2022-12-20 17:07:13 +08:00
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package secret
import (
"context"
2023-06-21 06:54:15 +08:00
"errors"
2023-08-24 10:07:00 +08:00
"fmt"
2022-12-20 17:07:13 +08:00
"strings"
2024-03-08 14:14:35 +08:00
actions_model "code.gitea.io/gitea/models/actions"
2022-12-20 17:07:13 +08:00
"code.gitea.io/gitea/models/db"
2024-03-08 14:14:35 +08:00
actions_module "code.gitea.io/gitea/modules/actions"
"code.gitea.io/gitea/modules/log"
2022-12-20 17:07:13 +08:00
secret_module "code.gitea.io/gitea/modules/secret"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
2023-08-24 10:07:00 +08:00
"code.gitea.io/gitea/modules/util"
2022-12-20 17:07:13 +08:00
"xorm.io/builder"
)
// Secret represents a secret
type Secret struct {
ID int64
OwnerID int64 ` xorm:"INDEX UNIQUE(owner_repo_name) NOT NULL" `
RepoID int64 ` xorm:"INDEX UNIQUE(owner_repo_name) NOT NULL DEFAULT 0" `
Name string ` xorm:"UNIQUE(owner_repo_name) NOT NULL" `
Data string ` xorm:"LONGTEXT" ` // encrypted data
CreatedUnix timeutil . TimeStamp ` xorm:"created NOT NULL" `
}
2023-08-24 10:07:00 +08:00
// ErrSecretNotFound represents a "secret not found" error.
type ErrSecretNotFound struct {
Name string
}
func ( err ErrSecretNotFound ) Error ( ) string {
return fmt . Sprintf ( "secret was not found [name: %s]" , err . Name )
}
func ( err ErrSecretNotFound ) Unwrap ( ) error {
return util . ErrNotExist
}
2022-12-20 17:07:13 +08:00
// InsertEncryptedSecret Creates, encrypts, and validates a new secret with yet unencrypted data and insert into database
func InsertEncryptedSecret ( ctx context . Context , ownerID , repoID int64 , name , data string ) ( * Secret , error ) {
2023-05-16 14:49:40 +08:00
encrypted , err := secret_module . EncryptSecret ( setting . SecretKey , data )
2022-12-20 17:07:13 +08:00
if err != nil {
return nil , err
}
2023-09-05 17:21:02 +02:00
secret := & Secret {
OwnerID : ownerID ,
RepoID : repoID ,
Name : strings . ToUpper ( name ) ,
Data : encrypted ,
}
2022-12-20 17:07:13 +08:00
if err := secret . Validate ( ) ; err != nil {
return secret , err
}
return secret , db . Insert ( ctx , secret )
}
func init ( ) {
db . RegisterModel ( new ( Secret ) )
}
func ( s * Secret ) Validate ( ) error {
2023-06-21 06:54:15 +08:00
if s . OwnerID == 0 && s . RepoID == 0 {
return errors . New ( "the secret is not bound to any scope" )
2022-12-20 17:07:13 +08:00
}
2023-06-21 06:54:15 +08:00
return nil
2022-12-20 17:07:13 +08:00
}
type FindSecretsOptions struct {
db . ListOptions
2023-09-05 17:21:02 +02:00
OwnerID int64
RepoID int64
SecretID int64
Name string
2022-12-20 17:07:13 +08:00
}
2023-11-24 11:49:41 +08:00
func ( opts FindSecretsOptions ) ToConds ( ) builder . Cond {
2022-12-20 17:07:13 +08:00
cond := builder . NewCond ( )
if opts . OwnerID > 0 {
cond = cond . And ( builder . Eq { "owner_id" : opts . OwnerID } )
}
if opts . RepoID > 0 {
cond = cond . And ( builder . Eq { "repo_id" : opts . RepoID } )
}
2023-09-05 17:21:02 +02:00
if opts . SecretID != 0 {
cond = cond . And ( builder . Eq { "id" : opts . SecretID } )
}
if opts . Name != "" {
cond = cond . And ( builder . Eq { "name" : strings . ToUpper ( opts . Name ) } )
}
2022-12-20 17:07:13 +08:00
return cond
}
2023-08-24 10:07:00 +08:00
// UpdateSecret changes org or user reop secret.
2023-09-05 17:21:02 +02:00
func UpdateSecret ( ctx context . Context , secretID int64 , data string ) error {
2023-08-24 10:07:00 +08:00
encrypted , err := secret_module . EncryptSecret ( setting . SecretKey , data )
if err != nil {
return err
}
2023-09-05 17:21:02 +02:00
s := & Secret {
Data : encrypted ,
2023-08-30 04:54:49 +08:00
}
2023-09-05 17:21:02 +02:00
affected , err := db . GetEngine ( ctx ) . ID ( secretID ) . Cols ( "data" ) . Update ( s )
if affected != 1 {
return ErrSecretNotFound { }
2023-08-30 04:54:49 +08:00
}
2023-09-05 17:21:02 +02:00
return err
2023-08-30 04:54:49 +08:00
}
2024-03-08 14:14:35 +08:00
func GetSecretsOfTask ( ctx context . Context , task * actions_model . ActionTask ) ( map [ string ] string , error ) {
secrets := map [ string ] string { }
secrets [ "GITHUB_TOKEN" ] = task . Token
secrets [ "GITEA_TOKEN" ] = task . Token
if task . Job . Run . IsForkPullRequest && task . Job . Run . TriggerEvent != actions_module . GithubEventPullRequestTarget {
// ignore secrets for fork pull request, except GITHUB_TOKEN and GITEA_TOKEN which are automatically generated.
// for the tasks triggered by pull_request_target event, they could access the secrets because they will run in the context of the base branch
// see the documentation: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
return secrets , nil
}
ownerSecrets , err := db . Find [ Secret ] ( ctx , FindSecretsOptions { OwnerID : task . Job . Run . Repo . OwnerID } )
if err != nil {
log . Error ( "find secrets of owner %v: %v" , task . Job . Run . Repo . OwnerID , err )
return nil , err
}
repoSecrets , err := db . Find [ Secret ] ( ctx , FindSecretsOptions { RepoID : task . Job . Run . RepoID } )
if err != nil {
log . Error ( "find secrets of repo %v: %v" , task . Job . Run . RepoID , err )
return nil , err
}
for _ , secret := range append ( ownerSecrets , repoSecrets ... ) {
v , err := secret_module . DecryptSecret ( setting . SecretKey , secret . Data )
if err != nil {
log . Error ( "decrypt secret %v %q: %v" , secret . ID , secret . Name , err )
return nil , err
}
secrets [ secret . Name ] = v
}
return secrets , nil
}