2019-06-01 20:30:21 +05:30
// Copyright 2019 The Gitea Authors. All rights reserved.
2022-11-27 23:50:29 +05:30
// SPDX-License-Identifier: MIT
2019-06-01 20:30:21 +05:30
// Package private includes all internal routes. The package name internal is ideal but Golang is not allowed, so we use private as package name instead.
package private
import (
"fmt"
"net/http"
"strings"
2021-12-10 13:44:24 +05:30
asymkey_model "code.gitea.io/gitea/models/asymkey"
2021-11-28 17:28:28 +05:30
"code.gitea.io/gitea/models/perm"
2022-05-11 15:39:36 +05:30
access_model "code.gitea.io/gitea/models/perm/access"
2021-12-10 06:57:50 +05:30
repo_model "code.gitea.io/gitea/models/repo"
2021-11-10 01:27:58 +05:30
"code.gitea.io/gitea/models/unit"
2021-11-24 15:19:20 +05:30
user_model "code.gitea.io/gitea/models/user"
2021-01-26 21:06:53 +05:30
"code.gitea.io/gitea/modules/context"
2021-07-28 15:12:56 +05:30
"code.gitea.io/gitea/modules/git"
2019-06-01 20:30:21 +05:30
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
2019-12-15 08:19:52 +05:30
repo_service "code.gitea.io/gitea/services/repository"
2020-01-07 23:57:36 +05:30
wiki_service "code.gitea.io/gitea/services/wiki"
2019-06-01 20:30:21 +05:30
)
// ServNoCommand returns information about the provided keyid
2021-01-26 21:06:53 +05:30
func ServNoCommand ( ctx * context . PrivateContext ) {
2019-06-01 20:30:21 +05:30
keyID := ctx . ParamsInt64 ( ":keyid" )
if keyID <= 0 {
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusBadRequest , private . Response {
Err : fmt . Sprintf ( "Bad key id: %d" , keyID ) ,
2019-06-01 20:30:21 +05:30
} )
}
results := private . KeyAndOwner { }
2021-12-10 13:44:24 +05:30
key , err := asymkey_model . GetPublicKeyByID ( keyID )
2019-06-01 20:30:21 +05:30
if err != nil {
2021-12-10 13:44:24 +05:30
if asymkey_model . IsErrKeyNotExist ( err ) {
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusUnauthorized , private . Response {
Err : fmt . Sprintf ( "Cannot find key: %d" , keyID ) ,
2019-06-01 20:30:21 +05:30
} )
return
}
log . Error ( "Unable to get public key: %d Error: %v" , keyID , err )
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusInternalServerError , private . Response {
Err : err . Error ( ) ,
2019-06-01 20:30:21 +05:30
} )
return
}
results . Key = key
2021-12-10 13:44:24 +05:30
if key . Type == asymkey_model . KeyTypeUser || key . Type == asymkey_model . KeyTypePrincipal {
2022-12-03 08:18:26 +05:30
user , err := user_model . GetUserByID ( ctx , key . OwnerID )
2019-06-01 20:30:21 +05:30
if err != nil {
2021-11-24 15:19:20 +05:30
if user_model . IsErrUserNotExist ( err ) {
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusUnauthorized , private . Response {
Err : fmt . Sprintf ( "Cannot find owner with id: %d for key: %d" , key . OwnerID , keyID ) ,
2019-06-01 20:30:21 +05:30
} )
return
}
log . Error ( "Unable to get owner with id: %d for public key: %d Error: %v" , key . OwnerID , keyID , err )
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusInternalServerError , private . Response {
Err : err . Error ( ) ,
2019-06-01 20:30:21 +05:30
} )
return
}
2020-11-13 04:59:11 +05:30
if ! user . IsActive || user . ProhibitLogin {
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusForbidden , private . Response {
Err : "Your account is disabled." ,
2020-11-13 04:59:11 +05:30
} )
return
}
2019-06-01 20:30:21 +05:30
results . Owner = user
}
ctx . JSON ( http . StatusOK , & results )
}
// ServCommand returns information about the provided keyid
2021-01-26 21:06:53 +05:30
func ServCommand ( ctx * context . PrivateContext ) {
2019-06-01 20:30:21 +05:30
keyID := ctx . ParamsInt64 ( ":keyid" )
ownerName := ctx . Params ( ":owner" )
repoName := ctx . Params ( ":repo" )
2021-11-28 17:28:28 +05:30
mode := perm . AccessMode ( ctx . FormInt ( "mode" ) )
2019-06-01 20:30:21 +05:30
// Set the basic parts of the results to return
results := private . ServCommandResults {
RepoName : repoName ,
OwnerName : ownerName ,
KeyID : keyID ,
}
2020-08-20 20:23:06 +05:30
// Now because we're not translating things properly let's just default some English strings here
2019-06-01 20:30:21 +05:30
modeString := "read"
2021-11-28 17:28:28 +05:30
if mode > perm . AccessModeRead {
2019-06-01 20:30:21 +05:30
modeString = "write to"
}
// The default unit we're trying to look at is code
2021-11-10 01:27:58 +05:30
unitType := unit . TypeCode
2019-06-01 20:30:21 +05:30
// Unless we're a wiki...
if strings . HasSuffix ( repoName , ".wiki" ) {
// in which case we need to look at the wiki
2021-11-10 01:27:58 +05:30
unitType = unit . TypeWiki
2019-06-01 20:30:21 +05:30
// And we'd better munge the reponame and tell downstream we're looking at a wiki
results . IsWiki = true
results . RepoName = repoName [ : len ( repoName ) - 5 ]
}
2022-05-20 19:38:52 +05:30
owner , err := user_model . GetUserByName ( ctx , results . OwnerName )
2020-11-13 04:59:11 +05:30
if err != nil {
2022-01-16 09:14:11 +05:30
if user_model . IsErrUserNotExist ( err ) {
// User is fetching/cloning a non-existent repository
log . Warn ( "Failed authentication attempt (cannot find repository: %s/%s) from %s" , results . OwnerName , results . RepoName , ctx . RemoteAddr ( ) )
ctx . JSON ( http . StatusNotFound , private . ErrServCommand {
Results : results ,
Err : fmt . Sprintf ( "Cannot find repository: %s/%s" , results . OwnerName , results . RepoName ) ,
} )
return
}
2020-11-13 04:59:11 +05:30
log . Error ( "Unable to get repository owner: %s/%s Error: %v" , results . OwnerName , results . RepoName , err )
2022-01-16 09:14:11 +05:30
ctx . JSON ( http . StatusForbidden , private . ErrServCommand {
2021-06-24 01:08:19 +05:30
Results : results ,
Err : fmt . Sprintf ( "Unable to get repository owner: %s/%s %v" , results . OwnerName , results . RepoName , err ) ,
2020-11-13 04:59:11 +05:30
} )
return
}
2020-11-18 15:28:25 +05:30
if ! owner . IsOrganization ( ) && ! owner . IsActive {
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusForbidden , private . ErrServCommand {
Results : results ,
Err : "Repository cannot be accessed, you could retry it later" ,
2020-11-13 04:59:11 +05:30
} )
return
}
2019-06-01 20:30:21 +05:30
// Now get the Repository and set the results section
2019-12-15 08:19:52 +05:30
repoExist := true
2021-12-10 06:57:50 +05:30
repo , err := repo_model . GetRepositoryByName ( owner . ID , results . RepoName )
2019-06-01 20:30:21 +05:30
if err != nil {
2021-12-10 06:57:50 +05:30
if repo_model . IsErrRepoNotExist ( err ) {
2019-12-15 08:19:52 +05:30
repoExist = false
2021-07-29 07:12:15 +05:30
for _ , verb := range ctx . FormStrings ( "verb" ) {
2022-06-20 15:32:49 +05:30
if verb == "git-upload-pack" {
2020-02-05 15:10:35 +05:30
// User is fetching/cloning a non-existent repository
2022-01-16 09:14:11 +05:30
log . Warn ( "Failed authentication attempt (cannot find repository: %s/%s) from %s" , results . OwnerName , results . RepoName , ctx . RemoteAddr ( ) )
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusNotFound , private . ErrServCommand {
Results : results ,
Err : fmt . Sprintf ( "Cannot find repository: %s/%s" , results . OwnerName , results . RepoName ) ,
2020-02-05 15:10:35 +05:30
} )
return
}
}
2019-12-15 08:19:52 +05:30
} else {
log . Error ( "Unable to get repository: %s/%s Error: %v" , results . OwnerName , results . RepoName , err )
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusInternalServerError , private . ErrServCommand {
Results : results ,
Err : fmt . Sprintf ( "Unable to get repository: %s/%s %v" , results . OwnerName , results . RepoName , err ) ,
2019-06-01 20:30:21 +05:30
} )
return
}
}
2019-12-15 08:19:52 +05:30
if repoExist {
2020-11-13 04:59:11 +05:30
repo . Owner = owner
2019-12-15 08:19:52 +05:30
repo . OwnerName = ownerName
results . RepoID = repo . ID
2019-10-13 18:53:14 +05:30
2019-12-15 08:19:52 +05:30
if repo . IsBeingCreated ( ) {
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusInternalServerError , private . ErrServCommand {
Results : results ,
Err : "Repository is being created, you could retry after it finished" ,
2019-12-15 08:19:52 +05:30
} )
return
}
2021-11-23 04:02:16 +05:30
if repo . IsBroken ( ) {
ctx . JSON ( http . StatusInternalServerError , private . ErrServCommand {
Results : results ,
Err : "Repository is in a broken state" ,
} )
return
}
2019-12-15 08:19:52 +05:30
// We can shortcut at this point if the repo is a mirror
2021-11-28 17:28:28 +05:30
if mode > perm . AccessModeRead && repo . IsMirror {
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusForbidden , private . ErrServCommand {
Results : results ,
Err : fmt . Sprintf ( "Mirror Repository %s/%s is read-only" , results . OwnerName , results . RepoName ) ,
2019-12-15 08:19:52 +05:30
} )
return
}
2019-06-01 20:30:21 +05:30
}
// Get the Public Key represented by the keyID
2021-12-10 13:44:24 +05:30
key , err := asymkey_model . GetPublicKeyByID ( keyID )
2019-06-01 20:30:21 +05:30
if err != nil {
2021-12-10 13:44:24 +05:30
if asymkey_model . IsErrKeyNotExist ( err ) {
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusNotFound , private . ErrServCommand {
Results : results ,
Err : fmt . Sprintf ( "Cannot find key: %d" , keyID ) ,
2019-06-01 20:30:21 +05:30
} )
return
}
log . Error ( "Unable to get public key: %d Error: %v" , keyID , err )
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusInternalServerError , private . ErrServCommand {
Results : results ,
Err : fmt . Sprintf ( "Unable to get key: %d Error: %v" , keyID , err ) ,
2019-06-01 20:30:21 +05:30
} )
return
}
results . KeyName = key . Name
results . KeyID = key . ID
results . UserID = key . OwnerID
2019-12-15 08:19:52 +05:30
// If repo doesn't exist, deploy key doesn't make sense
2021-12-10 13:44:24 +05:30
if ! repoExist && key . Type == asymkey_model . KeyTypeDeploy {
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusNotFound , private . ErrServCommand {
Results : results ,
Err : fmt . Sprintf ( "Cannot find repository %s/%s" , results . OwnerName , results . RepoName ) ,
2019-12-15 08:19:52 +05:30
} )
return
}
2019-06-01 20:30:21 +05:30
// Deploy Keys have ownerID set to 0 therefore we can't use the owner
// So now we need to check if the key is a deploy key
// We'll keep hold of the deploy key here for permissions checking
2021-12-10 13:44:24 +05:30
var deployKey * asymkey_model . DeployKey
2021-11-24 15:19:20 +05:30
var user * user_model . User
2021-12-10 13:44:24 +05:30
if key . Type == asymkey_model . KeyTypeDeploy {
2019-06-01 20:30:21 +05:30
var err error
2022-05-20 19:38:52 +05:30
deployKey , err = asymkey_model . GetDeployKeyByRepo ( ctx , key . ID , repo . ID )
2019-06-01 20:30:21 +05:30
if err != nil {
2021-12-10 13:44:24 +05:30
if asymkey_model . IsErrDeployKeyNotExist ( err ) {
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusNotFound , private . ErrServCommand {
Results : results ,
Err : fmt . Sprintf ( "Public (Deploy) Key: %d:%s is not authorized to %s %s/%s." , key . ID , key . Name , modeString , results . OwnerName , results . RepoName ) ,
2019-06-01 20:30:21 +05:30
} )
return
}
log . Error ( "Unable to get deploy for public (deploy) key: %d in %-v Error: %v" , key . ID , repo , err )
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusInternalServerError , private . ErrServCommand {
Results : results ,
Err : fmt . Sprintf ( "Unable to get Deploy Key for Public Key: %d:%s in %s/%s." , key . ID , key . Name , results . OwnerName , results . RepoName ) ,
2019-06-01 20:30:21 +05:30
} )
return
}
2022-03-22 14:59:07 +05:30
results . DeployKeyID = deployKey . ID
2019-06-01 20:30:21 +05:30
results . KeyName = deployKey . Name
// FIXME: Deploy keys aren't really the owner of the repo pushing changes
// however we don't have good way of representing deploy keys in hook.go
// so for now use the owner of the repository
results . UserName = results . OwnerName
results . UserID = repo . OwnerID
2020-08-30 12:54:39 +05:30
if ! repo . Owner . KeepEmailPrivate {
results . UserEmail = repo . Owner . Email
}
2019-06-01 20:30:21 +05:30
} else {
// Get the user represented by the Key
var err error
2022-12-03 08:18:26 +05:30
user , err = user_model . GetUserByID ( ctx , key . OwnerID )
2019-06-01 20:30:21 +05:30
if err != nil {
2021-11-24 15:19:20 +05:30
if user_model . IsErrUserNotExist ( err ) {
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusUnauthorized , private . ErrServCommand {
Results : results ,
Err : fmt . Sprintf ( "Public Key: %d:%s owner %d does not exist." , key . ID , key . Name , key . OwnerID ) ,
2019-06-01 20:30:21 +05:30
} )
return
}
log . Error ( "Unable to get owner: %d for public key: %d:%s Error: %v" , key . OwnerID , key . ID , key . Name , err )
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusInternalServerError , private . ErrServCommand {
Results : results ,
Err : fmt . Sprintf ( "Unable to get Owner: %d for Deploy Key: %d:%s in %s/%s." , key . OwnerID , key . ID , key . Name , ownerName , repoName ) ,
2019-06-01 20:30:21 +05:30
} )
return
}
2020-11-13 04:59:11 +05:30
if ! user . IsActive || user . ProhibitLogin {
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusForbidden , private . Response {
Err : "Your account is disabled." ,
2020-11-13 04:59:11 +05:30
} )
return
}
2019-06-01 20:30:21 +05:30
results . UserName = user . Name
2020-08-30 12:54:39 +05:30
if ! user . KeepEmailPrivate {
results . UserEmail = user . Email
}
2019-06-01 20:30:21 +05:30
}
// Don't allow pushing if the repo is archived
2021-11-28 17:28:28 +05:30
if repoExist && mode > perm . AccessModeRead && repo . IsArchived {
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusUnauthorized , private . ErrServCommand {
Results : results ,
Err : fmt . Sprintf ( "Repo: %s/%s is archived." , results . OwnerName , results . RepoName ) ,
2019-06-01 20:30:21 +05:30
} )
return
}
// Permissions checking:
2021-10-21 02:29:05 +05:30
if repoExist &&
2021-11-28 17:28:28 +05:30
( mode > perm . AccessModeRead ||
2021-10-21 02:29:05 +05:30
repo . IsPrivate ||
owner . Visibility . IsPrivate ( ) ||
2021-10-26 04:54:19 +05:30
( user != nil && user . IsRestricted ) || // user will be nil if the key is a deploykey
2021-10-21 02:29:05 +05:30
setting . Service . RequireSignInView ) {
2021-12-10 13:44:24 +05:30
if key . Type == asymkey_model . KeyTypeDeploy {
2019-06-01 20:30:21 +05:30
if deployKey . Mode < mode {
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusUnauthorized , private . ErrServCommand {
Results : results ,
Err : fmt . Sprintf ( "Deploy Key: %d:%s is not authorized to %s %s/%s." , key . ID , key . Name , modeString , results . OwnerName , results . RepoName ) ,
2019-06-01 20:30:21 +05:30
} )
return
}
} else {
2021-10-21 02:29:05 +05:30
// Because of the special ref "refs/for" we will need to delay write permission check
2021-11-10 01:27:58 +05:30
if git . SupportProcReceive && unitType == unit . TypeCode {
2021-11-28 17:28:28 +05:30
mode = perm . AccessModeRead
2021-07-28 15:12:56 +05:30
}
2022-05-11 15:39:36 +05:30
perm , err := access_model . GetUserRepoPermission ( ctx , repo , user )
2019-06-01 20:30:21 +05:30
if err != nil {
log . Error ( "Unable to get permissions for %-v with key %d in %-v Error: %v" , user , key . ID , repo , err )
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusInternalServerError , private . ErrServCommand {
Results : results ,
Err : fmt . Sprintf ( "Unable to get permissions for user %d:%s with key %d in %s/%s Error: %v" , user . ID , user . Name , key . ID , results . OwnerName , results . RepoName , err ) ,
2019-06-01 20:30:21 +05:30
} )
return
}
userMode := perm . UnitAccessMode ( unitType )
if userMode < mode {
2022-01-16 09:14:11 +05:30
log . Warn ( "Failed authentication attempt for %s with key %s (not authorized to %s %s/%s) from %s" , user . Name , key . Name , modeString , ownerName , repoName , ctx . RemoteAddr ( ) )
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusUnauthorized , private . ErrServCommand {
Results : results ,
Err : fmt . Sprintf ( "User: %d:%s with Key: %d:%s is not authorized to %s %s/%s." , user . ID , user . Name , key . ID , key . Name , modeString , ownerName , repoName ) ,
2019-06-01 20:30:21 +05:30
} )
return
}
}
}
2019-12-15 08:19:52 +05:30
// We already know we aren't using a deploy key
if ! repoExist {
2022-05-20 19:38:52 +05:30
owner , err := user_model . GetUserByName ( ctx , ownerName )
2019-12-15 08:19:52 +05:30
if err != nil {
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusInternalServerError , private . ErrServCommand {
Results : results ,
Err : fmt . Sprintf ( "Unable to get owner: %s %v" , results . OwnerName , err ) ,
2019-12-15 08:19:52 +05:30
} )
return
}
if owner . IsOrganization ( ) && ! setting . Repository . EnablePushCreateOrg {
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusForbidden , private . ErrServCommand {
Results : results ,
Err : "Push to create is not enabled for organizations." ,
2019-12-15 08:19:52 +05:30
} )
return
}
if ! owner . IsOrganization ( ) && ! setting . Repository . EnablePushCreateUser {
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusForbidden , private . ErrServCommand {
Results : results ,
Err : "Push to create is not enabled for users." ,
2019-12-15 08:19:52 +05:30
} )
return
}
2023-03-01 03:47:51 +05:30
repo , err = repo_service . PushCreateRepo ( ctx , user , owner , results . RepoName )
2019-12-15 08:19:52 +05:30
if err != nil {
log . Error ( "pushCreateRepo: %v" , err )
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusNotFound , private . ErrServCommand {
Results : results ,
Err : fmt . Sprintf ( "Cannot find repository: %s/%s" , results . OwnerName , results . RepoName ) ,
2019-12-15 08:19:52 +05:30
} )
return
}
results . RepoID = repo . ID
}
2019-06-01 20:30:21 +05:30
if results . IsWiki {
2020-04-19 19:56:58 +05:30
// Ensure the wiki is enabled before we allow access to it
2022-12-10 08:16:31 +05:30
if _ , err := repo . GetUnit ( ctx , unit . TypeWiki ) ; err != nil {
2021-12-10 06:57:50 +05:30
if repo_model . IsErrUnitTypeNotExist ( err ) {
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusForbidden , private . ErrServCommand {
Results : results ,
Err : "repository wiki is disabled" ,
2020-04-19 19:56:58 +05:30
} )
return
}
log . Error ( "Failed to get the wiki unit in %-v Error: %v" , repo , err )
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusInternalServerError , private . ErrServCommand {
Results : results ,
Err : fmt . Sprintf ( "Failed to get the wiki unit in %s/%s Error: %v" , ownerName , repoName , err ) ,
2020-04-19 19:56:58 +05:30
} )
return
}
// Finally if we're trying to touch the wiki we should init it
2022-01-20 04:56:57 +05:30
if err = wiki_service . InitWiki ( ctx , repo ) ; err != nil {
2019-06-01 20:30:21 +05:30
log . Error ( "Failed to initialize the wiki in %-v Error: %v" , repo , err )
2021-06-24 01:08:19 +05:30
ctx . JSON ( http . StatusInternalServerError , private . ErrServCommand {
Results : results ,
Err : fmt . Sprintf ( "Failed to initialize the wiki in %s/%s Error: %v" , ownerName , repoName , err ) ,
2019-06-01 20:30:21 +05:30
} )
return
}
}
2022-03-22 14:59:07 +05:30
log . Debug ( "Serv Results:\nIsWiki: %t\nDeployKeyID: %d\nKeyID: %d\tKeyName: %s\nUserName: %s\nUserID: %d\nOwnerName: %s\nRepoName: %s\nRepoID: %d" ,
2019-06-01 20:30:21 +05:30
results . IsWiki ,
2022-03-22 14:59:07 +05:30
results . DeployKeyID ,
2019-06-01 20:30:21 +05:30
results . KeyID ,
results . KeyName ,
results . UserName ,
results . UserID ,
results . OwnerName ,
results . RepoName ,
results . RepoID )
ctx . JSON ( http . StatusOK , results )
// We will update the keys in a different call.
}