2021-07-24 13:16:34 +03:00
// Copyright 2021 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2021-07-24 13:16:34 +03:00
2021-12-10 11:14:24 +03:00
package asymkey
2021-07-24 13:16:34 +03:00
import (
2021-12-10 04:27:50 +03:00
"context"
2021-07-24 13:16:34 +03:00
"fmt"
"time"
2021-09-19 14:49:59 +03:00
"code.gitea.io/gitea/models/db"
2021-11-28 14:58:28 +03:00
"code.gitea.io/gitea/models/perm"
2021-07-24 13:16:34 +03:00
"code.gitea.io/gitea/modules/timeutil"
2021-11-17 15:34:35 +03:00
2021-07-24 13:16:34 +03:00
"xorm.io/builder"
)
// ________ .__ ____ __.
// \______ \ ____ ______ | | ____ ___.__.| |/ _|____ ___.__.
// | | \_/ __ \\____ \| | / _ < | || <_/ __ < | |
// | ` \ ___/| |_> > |_( <_> )___ || | \ ___/\___ |
// /_______ /\___ > __/|____/\____// ____||____|__ \___ > ____|
// \/ \/|__| \/ \/ \/\/
//
// This file contains functions specific to DeployKeys
// DeployKey represents deploy key information and its relation with repository.
type DeployKey struct {
ID int64 ` xorm:"pk autoincr" `
KeyID int64 ` xorm:"UNIQUE(s) INDEX" `
RepoID int64 ` xorm:"UNIQUE(s) INDEX" `
Name string
Fingerprint string
Content string ` xorm:"-" `
2021-11-28 14:58:28 +03:00
Mode perm . AccessMode ` xorm:"NOT NULL DEFAULT 1" `
2021-07-24 13:16:34 +03:00
CreatedUnix timeutil . TimeStamp ` xorm:"created" `
UpdatedUnix timeutil . TimeStamp ` xorm:"updated" `
HasRecentActivity bool ` xorm:"-" `
HasUsed bool ` xorm:"-" `
}
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
func ( key * DeployKey ) AfterLoad ( ) {
key . HasUsed = key . UpdatedUnix > key . CreatedUnix
key . HasRecentActivity = key . UpdatedUnix . AddDuration ( 7 * 24 * time . Hour ) > timeutil . TimeStampNow ( )
}
// GetContent gets associated public key content.
2023-10-11 07:24:07 +03:00
func ( key * DeployKey ) GetContent ( ctx context . Context ) error {
pkey , err := GetPublicKeyByID ( ctx , key . KeyID )
2021-07-24 13:16:34 +03:00
if err != nil {
return err
}
key . Content = pkey . Content
return nil
}
2022-03-22 12:29:07 +03:00
// IsReadOnly checks if the key can only be used for read operations, used by template
2021-07-24 13:16:34 +03:00
func ( key * DeployKey ) IsReadOnly ( ) bool {
2021-11-28 14:58:28 +03:00
return key . Mode == perm . AccessModeRead
2021-07-24 13:16:34 +03:00
}
2021-09-19 14:49:59 +03:00
func init ( ) {
db . RegisterModel ( new ( DeployKey ) )
}
2022-05-20 17:08:52 +03:00
func checkDeployKey ( ctx context . Context , keyID , repoID int64 , name string ) error {
2021-07-24 13:16:34 +03:00
// Note: We want error detail, not just true or false here.
2022-05-20 17:08:52 +03:00
has , err := db . GetEngine ( ctx ) .
2021-07-24 13:16:34 +03:00
Where ( "key_id = ? AND repo_id = ?" , keyID , repoID ) .
Get ( new ( DeployKey ) )
if err != nil {
return err
} else if has {
return ErrDeployKeyAlreadyExist { keyID , repoID }
}
2022-05-20 17:08:52 +03:00
has , err = db . GetEngine ( ctx ) .
2021-07-24 13:16:34 +03:00
Where ( "repo_id = ? AND name = ?" , repoID , name ) .
Get ( new ( DeployKey ) )
if err != nil {
return err
} else if has {
return ErrDeployKeyNameAlreadyUsed { repoID , name }
}
return nil
}
// addDeployKey adds new key-repo relation.
2022-05-20 17:08:52 +03:00
func addDeployKey ( ctx context . Context , keyID , repoID int64 , name , fingerprint string , mode perm . AccessMode ) ( * DeployKey , error ) {
if err := checkDeployKey ( ctx , keyID , repoID , name ) ; err != nil {
2021-07-24 13:16:34 +03:00
return nil , err
}
key := & DeployKey {
KeyID : keyID ,
RepoID : repoID ,
Name : name ,
Fingerprint : fingerprint ,
Mode : mode ,
}
2022-05-20 17:08:52 +03:00
return key , db . Insert ( ctx , key )
2021-07-24 13:16:34 +03:00
}
// HasDeployKey returns true if public key is a deploy key of given repository.
2023-10-14 11:37:24 +03:00
func HasDeployKey ( ctx context . Context , keyID , repoID int64 ) bool {
has , _ := db . GetEngine ( ctx ) .
2021-07-24 13:16:34 +03:00
Where ( "key_id = ? AND repo_id = ?" , keyID , repoID ) .
Get ( new ( DeployKey ) )
return has
}
// AddDeployKey add new deploy key to database and authorized_keys file.
2023-10-14 11:37:24 +03:00
func AddDeployKey ( ctx context . Context , repoID int64 , name , content string , readOnly bool ) ( * DeployKey , error ) {
2022-06-04 22:18:50 +03:00
fingerprint , err := CalcFingerprint ( content )
2021-07-24 13:16:34 +03:00
if err != nil {
return nil , err
}
2021-11-28 14:58:28 +03:00
accessMode := perm . AccessModeRead
2021-07-24 13:16:34 +03:00
if ! readOnly {
2021-11-28 14:58:28 +03:00
accessMode = perm . AccessModeWrite
2021-07-24 13:16:34 +03:00
}
2023-10-14 11:37:24 +03:00
ctx , committer , err := db . TxContext ( ctx )
2021-11-21 18:41:00 +03:00
if err != nil {
2021-07-24 13:16:34 +03:00
return nil , err
}
2021-11-21 18:41:00 +03:00
defer committer . Close ( )
2023-12-07 10:27:36 +03:00
pkey , exist , err := db . Get [ PublicKey ] ( ctx , builder . Eq { "fingerprint" : fingerprint } )
2021-07-24 13:16:34 +03:00
if err != nil {
return nil , err
2023-12-07 10:27:36 +03:00
} else if exist {
2021-07-24 13:16:34 +03:00
if pkey . Type != KeyTypeDeploy {
return nil , ErrKeyAlreadyExist { 0 , fingerprint , "" }
}
} else {
// First time use this deploy key.
2023-12-07 10:27:36 +03:00
pkey = & PublicKey {
Fingerprint : fingerprint ,
Mode : accessMode ,
Type : KeyTypeDeploy ,
Content : content ,
Name : name ,
}
2022-05-20 17:08:52 +03:00
if err = addKey ( ctx , pkey ) ; err != nil {
2022-10-24 22:29:17 +03:00
return nil , fmt . Errorf ( "addKey: %w" , err )
2021-07-24 13:16:34 +03:00
}
}
2022-05-20 17:08:52 +03:00
key , err := addDeployKey ( ctx , pkey . ID , repoID , name , pkey . Fingerprint , accessMode )
2021-07-24 13:16:34 +03:00
if err != nil {
return nil , err
}
2021-11-21 18:41:00 +03:00
return key , committer . Commit ( )
2021-07-24 13:16:34 +03:00
}
// GetDeployKeyByID returns deploy key by given ID.
2021-12-10 11:14:24 +03:00
func GetDeployKeyByID ( ctx context . Context , id int64 ) ( * DeployKey , error ) {
2023-12-07 10:27:36 +03:00
key , exist , err := db . GetByID [ DeployKey ] ( ctx , id )
2021-07-24 13:16:34 +03:00
if err != nil {
return nil , err
2023-12-07 10:27:36 +03:00
} else if ! exist {
2021-07-24 13:16:34 +03:00
return nil , ErrDeployKeyNotExist { id , 0 , 0 }
}
return key , nil
}
// GetDeployKeyByRepo returns deploy key by given public key ID and repository ID.
2022-05-20 17:08:52 +03:00
func GetDeployKeyByRepo ( ctx context . Context , keyID , repoID int64 ) ( * DeployKey , error ) {
2023-12-07 10:27:36 +03:00
key , exist , err := db . Get [ DeployKey ] ( ctx , builder . Eq { "key_id" : keyID , "repo_id" : repoID } )
2021-07-24 13:16:34 +03:00
if err != nil {
return nil , err
2023-12-07 10:27:36 +03:00
} else if ! exist {
2021-07-24 13:16:34 +03:00
return nil , ErrDeployKeyNotExist { 0 , keyID , repoID }
}
return key , nil
}
2022-06-06 11:01:49 +03:00
// IsDeployKeyExistByKeyID return true if there is at least one deploykey with the key id
func IsDeployKeyExistByKeyID ( ctx context . Context , keyID int64 ) ( bool , error ) {
return db . GetEngine ( ctx ) .
Where ( "key_id = ?" , keyID ) .
Get ( new ( DeployKey ) )
}
2021-07-24 13:16:34 +03:00
// UpdateDeployKeyCols updates deploy key information in the specified columns.
2023-10-14 11:37:24 +03:00
func UpdateDeployKeyCols ( ctx context . Context , key * DeployKey , cols ... string ) error {
_ , err := db . GetEngine ( ctx ) . ID ( key . ID ) . Cols ( cols ... ) . Update ( key )
2021-07-24 13:16:34 +03:00
return err
}
2021-08-12 15:43:08 +03:00
// ListDeployKeysOptions are options for ListDeployKeys
type ListDeployKeysOptions struct {
2021-09-24 14:32:56 +03:00
db . ListOptions
2021-08-12 15:43:08 +03:00
RepoID int64
KeyID int64
Fingerprint string
}
2023-11-24 06:49:41 +03:00
func ( opt ListDeployKeysOptions ) ToConds ( ) builder . Cond {
2021-08-12 15:43:08 +03:00
cond := builder . NewCond ( )
if opt . RepoID != 0 {
cond = cond . And ( builder . Eq { "repo_id" : opt . RepoID } )
}
if opt . KeyID != 0 {
cond = cond . And ( builder . Eq { "key_id" : opt . KeyID } )
}
if opt . Fingerprint != "" {
cond = cond . And ( builder . Eq { "fingerprint" : opt . Fingerprint } )
}
return cond
2021-07-24 13:16:34 +03:00
}