2019-10-16 16:42:42 +03: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"
"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 14:06:31 +03:00
approved signingMode = "approved"
2020-01-15 11:32:57 +03:00
noKey signingMode = "nokey"
2019-10-16 16:42:42 +03:00
)
func signingModeFromStrings ( modeStrings [ ] string ) [ ] signingMode {
returnable := make ( [ ] signingMode , 0 , len ( modeStrings ) )
for _ , mode := range modeStrings {
signMode := signingMode ( strings . ToLower ( mode ) )
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 14:06:31 +03:00
case approved :
fallthrough
2019-10-16 16:42:42 +03:00
case commitsSigned :
returnable = append ( returnable , signMode )
}
}
if len ( returnable ) == 0 {
return [ ] signingMode { never }
}
return returnable
}
func signingKey ( repoPath string ) string {
if setting . Repository . Signing . SigningKey == "none" {
return ""
}
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 {
return ""
}
signingKey , _ := git . NewCommand ( "config" , "--get" , "user.signingkey" ) . RunInDir ( repoPath )
return strings . TrimSpace ( signingKey )
}
return setting . Repository . Signing . SigningKey
}
// PublicSigningKey gets the public signing key within a provided repository directory
func PublicSigningKey ( repoPath string ) ( string , error ) {
signingKey := signingKey ( repoPath )
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
2020-01-15 11:32:57 +03:00
func SignInitialCommit ( repoPath string , u * User ) ( bool , string , error ) {
2019-10-16 16:42:42 +03:00
rules := signingModeFromStrings ( setting . Repository . Signing . InitialCommit )
signingKey := signingKey ( repoPath )
if signingKey == "" {
2020-01-15 11:32:57 +03:00
return false , "" , & ErrWontSign { noKey }
2019-10-16 16:42:42 +03:00
}
2020-07-06 15:07:07 +03:00
Loop :
2019-10-16 16:42:42 +03:00
for _ , rule := range rules {
switch rule {
case never :
2020-01-15 11:32:57 +03:00
return false , "" , & ErrWontSign { never }
2019-10-16 16:42:42 +03:00
case always :
2020-07-06 15:07:07 +03:00
break Loop
2019-10-16 16:42:42 +03:00
case pubkey :
2020-01-24 22:00:29 +03:00
keys , err := ListGPGKeys ( u . ID , ListOptions { } )
2020-01-15 11:32:57 +03:00
if err != nil {
return false , "" , err
}
if len ( keys ) == 0 {
return false , "" , & ErrWontSign { pubkey }
2019-10-16 16:42:42 +03:00
}
case twofa :
2020-01-15 11:32:57 +03:00
twofaModel , err := GetTwoFactorByUID ( u . ID )
2020-01-27 02:44:12 +03:00
if err != nil && ! IsErrTwoFactorNotEnrolled ( err ) {
2020-01-15 11:32:57 +03:00
return false , "" , err
}
if twofaModel == nil {
return false , "" , & ErrWontSign { twofa }
2019-10-16 16:42:42 +03:00
}
}
}
2020-01-15 11:32:57 +03:00
return true , signingKey , nil
2019-10-16 16:42:42 +03:00
}
// SignWikiCommit determines if we should sign the commits to this repository wiki
2020-01-15 11:32:57 +03:00
func ( repo * Repository ) SignWikiCommit ( u * User ) ( bool , string , error ) {
2019-10-16 16:42:42 +03:00
rules := signingModeFromStrings ( setting . Repository . Signing . Wiki )
signingKey := signingKey ( repo . WikiPath ( ) )
if signingKey == "" {
2020-01-15 11:32:57 +03:00
return false , "" , & ErrWontSign { noKey }
2019-10-16 16:42:42 +03:00
}
2020-07-06 15:07:07 +03:00
Loop :
2019-10-16 16:42:42 +03:00
for _ , rule := range rules {
switch rule {
case never :
2020-01-15 11:32:57 +03:00
return false , "" , & ErrWontSign { never }
2019-10-16 16:42:42 +03:00
case always :
2020-07-06 15:07:07 +03:00
break Loop
2019-10-16 16:42:42 +03:00
case pubkey :
2020-01-24 22:00:29 +03:00
keys , err := ListGPGKeys ( u . ID , ListOptions { } )
2020-01-15 11:32:57 +03:00
if err != nil {
return false , "" , err
}
if len ( keys ) == 0 {
return false , "" , & ErrWontSign { pubkey }
2019-10-16 16:42:42 +03:00
}
case twofa :
2020-01-15 11:32:57 +03:00
twofaModel , err := GetTwoFactorByUID ( u . ID )
2020-01-27 02:44:12 +03:00
if err != nil && ! IsErrTwoFactorNotEnrolled ( err ) {
2020-01-15 11:32:57 +03:00
return false , "" , err
}
if twofaModel == nil {
return false , "" , & ErrWontSign { twofa }
2019-10-16 16:42:42 +03:00
}
case parentSigned :
gitRepo , err := git . OpenRepository ( repo . WikiPath ( ) )
if err != nil {
2020-01-15 11:32:57 +03:00
return false , "" , err
2019-10-16 16:42:42 +03:00
}
2019-11-13 10:01:19 +03:00
defer gitRepo . Close ( )
2019-10-16 16:42:42 +03:00
commit , err := gitRepo . GetCommit ( "HEAD" )
if err != nil {
2020-01-15 11:32:57 +03:00
return false , "" , err
2019-10-16 16:42:42 +03:00
}
if commit . Signature == nil {
2020-01-15 11:32:57 +03:00
return false , "" , & ErrWontSign { parentSigned }
2019-10-16 16:42:42 +03:00
}
verification := ParseCommitWithSignature ( commit )
if ! verification . Verified {
2020-01-15 11:32:57 +03:00
return false , "" , & ErrWontSign { parentSigned }
2019-10-16 16:42:42 +03:00
}
}
}
2020-01-15 11:32:57 +03:00
return true , signingKey , nil
2019-10-16 16:42:42 +03:00
}
// SignCRUDAction determines if we should sign a CRUD commit to this repository
2020-01-15 11:32:57 +03:00
func ( repo * Repository ) SignCRUDAction ( u * User , tmpBasePath , parentCommit string ) ( bool , string , error ) {
2019-10-16 16:42:42 +03:00
rules := signingModeFromStrings ( setting . Repository . Signing . CRUDActions )
signingKey := signingKey ( repo . RepoPath ( ) )
if signingKey == "" {
2020-01-15 11:32:57 +03:00
return false , "" , & ErrWontSign { noKey }
2019-10-16 16:42:42 +03:00
}
2020-07-06 15:07:07 +03:00
Loop :
2019-10-16 16:42:42 +03:00
for _ , rule := range rules {
switch rule {
case never :
2020-01-15 11:32:57 +03:00
return false , "" , & ErrWontSign { never }
2019-10-16 16:42:42 +03:00
case always :
2020-07-06 15:07:07 +03:00
break Loop
2019-10-16 16:42:42 +03:00
case pubkey :
2020-01-24 22:00:29 +03:00
keys , err := ListGPGKeys ( u . ID , ListOptions { } )
2020-01-15 11:32:57 +03:00
if err != nil {
return false , "" , err
}
if len ( keys ) == 0 {
return false , "" , & ErrWontSign { pubkey }
2019-10-16 16:42:42 +03:00
}
case twofa :
2020-01-15 11:32:57 +03:00
twofaModel , err := GetTwoFactorByUID ( u . ID )
2020-01-27 02:44:12 +03:00
if err != nil && ! IsErrTwoFactorNotEnrolled ( err ) {
2020-01-15 11:32:57 +03:00
return false , "" , err
}
if twofaModel == nil {
return false , "" , & ErrWontSign { twofa }
2019-10-16 16:42:42 +03:00
}
case parentSigned :
gitRepo , err := git . OpenRepository ( tmpBasePath )
if err != nil {
2020-01-15 11:32:57 +03:00
return false , "" , err
2019-10-16 16:42:42 +03:00
}
2019-11-13 10:01:19 +03:00
defer gitRepo . Close ( )
2019-10-16 16:42:42 +03:00
commit , err := gitRepo . GetCommit ( parentCommit )
if err != nil {
2020-01-15 11:32:57 +03:00
return false , "" , err
2019-10-16 16:42:42 +03:00
}
if commit . Signature == nil {
2020-01-15 11:32:57 +03:00
return false , "" , & ErrWontSign { parentSigned }
2019-10-16 16:42:42 +03:00
}
verification := ParseCommitWithSignature ( commit )
if ! verification . Verified {
2020-01-15 11:32:57 +03:00
return false , "" , & ErrWontSign { parentSigned }
2019-10-16 16:42:42 +03:00
}
}
}
2020-01-15 11:32:57 +03:00
return true , signingKey , nil
2019-10-16 16:42:42 +03:00
}