2019-10-16 14:42:42 +01:00
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
"strings"
2021-09-24 19:32:56 +08:00
"code.gitea.io/gitea/models/db"
2021-09-25 21:00:12 +08:00
"code.gitea.io/gitea/models/login"
2021-11-24 17:49:20 +08:00
user_model "code.gitea.io/gitea/models/user"
2019-10-16 14:42:42 +01:00
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
)
type signingMode string
const (
never signingMode = "never"
always signingMode = "always"
pubkey signingMode = "pubkey"
twofa signingMode = "twofa"
parentSigned signingMode = "parentsigned"
baseSigned signingMode = "basesigned"
headSigned signingMode = "headsigned"
commitsSigned signingMode = "commitssigned"
2019-12-15 11:06:31 +00:00
approved signingMode = "approved"
2020-01-15 08:32:57 +00:00
noKey signingMode = "nokey"
2019-10-16 14:42:42 +01:00
)
func signingModeFromStrings ( modeStrings [ ] string ) [ ] signingMode {
returnable := make ( [ ] signingMode , 0 , len ( modeStrings ) )
for _ , mode := range modeStrings {
2020-09-19 17:44:55 +01:00
signMode := signingMode ( strings . ToLower ( strings . TrimSpace ( mode ) ) )
2019-10-16 14:42:42 +01:00
switch signMode {
case never :
return [ ] signingMode { never }
case always :
return [ ] signingMode { always }
case pubkey :
fallthrough
case twofa :
fallthrough
case parentSigned :
fallthrough
case baseSigned :
fallthrough
case headSigned :
fallthrough
2019-12-15 11:06:31 +00:00
case approved :
fallthrough
2019-10-16 14:42:42 +01:00
case commitsSigned :
returnable = append ( returnable , signMode )
}
}
if len ( returnable ) == 0 {
return [ ] signingMode { never }
}
return returnable
}
2020-09-19 17:44:55 +01:00
// SigningKey returns the KeyID and git Signature for the repo
func SigningKey ( repoPath string ) ( string , * git . Signature ) {
2019-10-16 14:42:42 +01:00
if setting . Repository . Signing . SigningKey == "none" {
2020-09-19 17:44:55 +01:00
return "" , nil
2019-10-16 14:42:42 +01:00
}
if setting . Repository . Signing . SigningKey == "default" || setting . Repository . Signing . SigningKey == "" {
// Can ignore the error here as it means that commit.gpgsign is not set
value , _ := git . NewCommand ( "config" , "--get" , "commit.gpgsign" ) . RunInDir ( repoPath )
sign , valid := git . ParseBool ( strings . TrimSpace ( value ) )
if ! sign || ! valid {
2020-09-19 17:44:55 +01:00
return "" , nil
2019-10-16 14:42:42 +01:00
}
signingKey , _ := git . NewCommand ( "config" , "--get" , "user.signingkey" ) . RunInDir ( repoPath )
2020-09-19 17:44:55 +01:00
signingName , _ := git . NewCommand ( "config" , "--get" , "user.name" ) . RunInDir ( repoPath )
signingEmail , _ := git . NewCommand ( "config" , "--get" , "user.email" ) . RunInDir ( repoPath )
return strings . TrimSpace ( signingKey ) , & git . Signature {
Name : strings . TrimSpace ( signingName ) ,
Email : strings . TrimSpace ( signingEmail ) ,
}
2019-10-16 14:42:42 +01:00
}
2020-09-19 17:44:55 +01:00
return setting . Repository . Signing . SigningKey , & git . Signature {
Name : setting . Repository . Signing . SigningName ,
Email : setting . Repository . Signing . SigningEmail ,
}
2019-10-16 14:42:42 +01:00
}
// PublicSigningKey gets the public signing key within a provided repository directory
func PublicSigningKey ( repoPath string ) ( string , error ) {
2020-09-19 17:44:55 +01:00
signingKey , _ := SigningKey ( repoPath )
2019-10-16 14:42:42 +01:00
if signingKey == "" {
return "" , nil
}
content , stderr , err := process . GetManager ( ) . ExecDir ( - 1 , repoPath ,
"gpg --export -a" , "gpg" , "--export" , "-a" , signingKey )
if err != nil {
log . Error ( "Unable to get default signing key in %s: %s, %s, %v" , repoPath , signingKey , stderr , err )
return "" , err
}
return content , nil
}
// SignInitialCommit determines if we should sign the initial commit to this repository
2021-11-24 17:49:20 +08:00
func SignInitialCommit ( repoPath string , u * user_model . User ) ( bool , string , * git . Signature , error ) {
2019-10-16 14:42:42 +01:00
rules := signingModeFromStrings ( setting . Repository . Signing . InitialCommit )
2020-09-19 17:44:55 +01:00
signingKey , sig := SigningKey ( repoPath )
2019-10-16 14:42:42 +01:00
if signingKey == "" {
2020-09-19 17:44:55 +01:00
return false , "" , nil , & ErrWontSign { noKey }
2019-10-16 14:42:42 +01:00
}
2020-07-06 05:07:07 -07:00
Loop :
2019-10-16 14:42:42 +01:00
for _ , rule := range rules {
switch rule {
case never :
2020-09-19 17:44:55 +01:00
return false , "" , nil , & ErrWontSign { never }
2019-10-16 14:42:42 +01:00
case always :
2020-07-06 05:07:07 -07:00
break Loop
2019-10-16 14:42:42 +01:00
case pubkey :
2021-09-24 19:32:56 +08:00
keys , err := ListGPGKeys ( u . ID , db . ListOptions { } )
2020-01-15 08:32:57 +00:00
if err != nil {
2020-09-19 17:44:55 +01:00
return false , "" , nil , err
2020-01-15 08:32:57 +00:00
}
if len ( keys ) == 0 {
2020-09-19 17:44:55 +01:00
return false , "" , nil , & ErrWontSign { pubkey }
2019-10-16 14:42:42 +01:00
}
case twofa :
2021-09-25 21:00:12 +08:00
twofaModel , err := login . GetTwoFactorByUID ( u . ID )
if err != nil && ! login . IsErrTwoFactorNotEnrolled ( err ) {
2020-09-19 17:44:55 +01:00
return false , "" , nil , err
2020-01-15 08:32:57 +00:00
}
if twofaModel == nil {
2020-09-19 17:44:55 +01:00
return false , "" , nil , & ErrWontSign { twofa }
2019-10-16 14:42:42 +01:00
}
}
}
2020-09-19 17:44:55 +01:00
return true , signingKey , sig , nil
2019-10-16 14:42:42 +01:00
}
// SignWikiCommit determines if we should sign the commits to this repository wiki
2021-11-24 17:49:20 +08:00
func ( repo * Repository ) SignWikiCommit ( u * user_model . User ) ( bool , string , * git . Signature , error ) {
2019-10-16 14:42:42 +01:00
rules := signingModeFromStrings ( setting . Repository . Signing . Wiki )
2020-09-19 17:44:55 +01:00
signingKey , sig := SigningKey ( repo . WikiPath ( ) )
2019-10-16 14:42:42 +01:00
if signingKey == "" {
2020-09-19 17:44:55 +01:00
return false , "" , nil , & ErrWontSign { noKey }
2019-10-16 14:42:42 +01:00
}
2020-07-06 05:07:07 -07:00
Loop :
2019-10-16 14:42:42 +01:00
for _ , rule := range rules {
switch rule {
case never :
2020-09-19 17:44:55 +01:00
return false , "" , nil , & ErrWontSign { never }
2019-10-16 14:42:42 +01:00
case always :
2020-07-06 05:07:07 -07:00
break Loop
2019-10-16 14:42:42 +01:00
case pubkey :
2021-09-24 19:32:56 +08:00
keys , err := ListGPGKeys ( u . ID , db . ListOptions { } )
2020-01-15 08:32:57 +00:00
if err != nil {
2020-09-19 17:44:55 +01:00
return false , "" , nil , err
2020-01-15 08:32:57 +00:00
}
if len ( keys ) == 0 {
2020-09-19 17:44:55 +01:00
return false , "" , nil , & ErrWontSign { pubkey }
2019-10-16 14:42:42 +01:00
}
case twofa :
2021-09-25 21:00:12 +08:00
twofaModel , err := login . GetTwoFactorByUID ( u . ID )
if err != nil && ! login . IsErrTwoFactorNotEnrolled ( err ) {
2020-09-19 17:44:55 +01:00
return false , "" , nil , err
2020-01-15 08:32:57 +00:00
}
if twofaModel == nil {
2020-09-19 17:44:55 +01:00
return false , "" , nil , & ErrWontSign { twofa }
2019-10-16 14:42:42 +01:00
}
case parentSigned :
gitRepo , err := git . OpenRepository ( repo . WikiPath ( ) )
if err != nil {
2020-09-19 17:44:55 +01:00
return false , "" , nil , err
2019-10-16 14:42:42 +01:00
}
2019-11-13 07:01:19 +00:00
defer gitRepo . Close ( )
2019-10-16 14:42:42 +01:00
commit , err := gitRepo . GetCommit ( "HEAD" )
if err != nil {
2020-09-19 17:44:55 +01:00
return false , "" , nil , err
2019-10-16 14:42:42 +01:00
}
if commit . Signature == nil {
2020-09-19 17:44:55 +01:00
return false , "" , nil , & ErrWontSign { parentSigned }
2019-10-16 14:42:42 +01:00
}
verification := ParseCommitWithSignature ( commit )
if ! verification . Verified {
2020-09-19 17:44:55 +01:00
return false , "" , nil , & ErrWontSign { parentSigned }
2019-10-16 14:42:42 +01:00
}
}
}
2020-09-19 17:44:55 +01:00
return true , signingKey , sig , nil
2019-10-16 14:42:42 +01:00
}
// SignCRUDAction determines if we should sign a CRUD commit to this repository
2021-11-24 17:49:20 +08:00
func ( repo * Repository ) SignCRUDAction ( u * user_model . User , tmpBasePath , parentCommit string ) ( bool , string , * git . Signature , error ) {
2019-10-16 14:42:42 +01:00
rules := signingModeFromStrings ( setting . Repository . Signing . CRUDActions )
2020-09-19 17:44:55 +01:00
signingKey , sig := SigningKey ( repo . RepoPath ( ) )
2019-10-16 14:42:42 +01:00
if signingKey == "" {
2020-09-19 17:44:55 +01:00
return false , "" , nil , & ErrWontSign { noKey }
2019-10-16 14:42:42 +01:00
}
2020-07-06 05:07:07 -07:00
Loop :
2019-10-16 14:42:42 +01:00
for _ , rule := range rules {
switch rule {
case never :
2020-09-19 17:44:55 +01:00
return false , "" , nil , & ErrWontSign { never }
2019-10-16 14:42:42 +01:00
case always :
2020-07-06 05:07:07 -07:00
break Loop
2019-10-16 14:42:42 +01:00
case pubkey :
2021-09-24 19:32:56 +08:00
keys , err := ListGPGKeys ( u . ID , db . ListOptions { } )
2020-01-15 08:32:57 +00:00
if err != nil {
2020-09-19 17:44:55 +01:00
return false , "" , nil , err
2020-01-15 08:32:57 +00:00
}
if len ( keys ) == 0 {
2020-09-19 17:44:55 +01:00
return false , "" , nil , & ErrWontSign { pubkey }
2019-10-16 14:42:42 +01:00
}
case twofa :
2021-09-25 21:00:12 +08:00
twofaModel , err := login . GetTwoFactorByUID ( u . ID )
if err != nil && ! login . IsErrTwoFactorNotEnrolled ( err ) {
2020-09-19 17:44:55 +01:00
return false , "" , nil , err
2020-01-15 08:32:57 +00:00
}
if twofaModel == nil {
2020-09-19 17:44:55 +01:00
return false , "" , nil , & ErrWontSign { twofa }
2019-10-16 14:42:42 +01:00
}
case parentSigned :
gitRepo , err := git . OpenRepository ( tmpBasePath )
if err != nil {
2020-09-19 17:44:55 +01:00
return false , "" , nil , err
2019-10-16 14:42:42 +01:00
}
2019-11-13 07:01:19 +00:00
defer gitRepo . Close ( )
2019-10-16 14:42:42 +01:00
commit , err := gitRepo . GetCommit ( parentCommit )
if err != nil {
2020-09-19 17:44:55 +01:00
return false , "" , nil , err
2019-10-16 14:42:42 +01:00
}
if commit . Signature == nil {
2020-09-19 17:44:55 +01:00
return false , "" , nil , & ErrWontSign { parentSigned }
2019-10-16 14:42:42 +01:00
}
verification := ParseCommitWithSignature ( commit )
if ! verification . Verified {
2020-09-19 17:44:55 +01:00
return false , "" , nil , & ErrWontSign { parentSigned }
2019-10-16 14:42:42 +01:00
}
}
}
2020-09-19 17:44:55 +01:00
return true , signingKey , sig , nil
2019-10-16 14:42:42 +01:00
}