2014-04-10 22:20:58 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2014-05-02 05:21:46 +04:00
package cmd
2014-04-10 22:20:58 +04:00
import (
"fmt"
"os"
"os/exec"
"path"
"strconv"
"strings"
"github.com/codegangsta/cli"
qlog "github.com/qiniu/log"
"github.com/gogits/gogs/models"
2014-05-26 04:11:25 +04:00
"github.com/gogits/gogs/modules/setting"
2014-04-10 22:20:58 +04:00
)
var CmdServ = cli . Command {
2014-05-05 08:55:17 +04:00
Name : "serv" ,
Usage : "This command should only be called by SSH shell" ,
Description : ` Serv provide access auth for repositories ` ,
Action : runServ ,
Flags : [ ] cli . Flag { } ,
2014-04-10 22:20:58 +04:00
}
2014-05-22 05:37:13 +04:00
func newLogger ( logPath string ) {
2014-04-10 22:20:58 +04:00
os . MkdirAll ( path . Dir ( logPath ) , os . ModePerm )
f , err := os . OpenFile ( logPath , os . O_WRONLY | os . O_APPEND | os . O_CREATE , os . ModePerm )
if err != nil {
qlog . Fatal ( err )
}
qlog . SetOutput ( f )
qlog . Info ( "Start logging serv..." )
}
2014-05-22 05:37:13 +04:00
func setup ( logPath string ) {
2014-05-26 04:11:25 +04:00
workDir , _ := setting . WorkDir ( )
newLogger ( path . Join ( workDir , logPath ) )
2014-05-22 05:37:13 +04:00
2014-05-26 04:11:25 +04:00
setting . NewConfigContext ( )
2014-05-22 05:37:13 +04:00
models . LoadModelsConfig ( )
if models . UseSQLite3 {
2014-05-26 04:11:25 +04:00
os . Chdir ( workDir )
2014-05-22 05:37:13 +04:00
}
models . SetEngine ( )
}
2014-04-10 22:20:58 +04:00
func parseCmd ( cmd string ) ( string , string ) {
ss := strings . SplitN ( cmd , " " , 2 )
if len ( ss ) != 2 {
return "" , ""
}
verb , args := ss [ 0 ] , ss [ 1 ]
if verb == "git" {
ss = strings . SplitN ( args , " " , 2 )
args = ss [ 1 ]
verb = fmt . Sprintf ( "%s %s" , verb , ss [ 0 ] )
}
2014-05-11 20:17:10 +04:00
return verb , strings . Replace ( args , "'/" , "'" , 1 )
2014-04-10 22:20:58 +04:00
}
2014-05-22 05:37:13 +04:00
var (
COMMANDS_READONLY = map [ string ] int {
"git-upload-pack" : models . AU_WRITABLE ,
"git upload-pack" : models . AU_WRITABLE ,
"git-upload-archive" : models . AU_WRITABLE ,
}
COMMANDS_WRITE = map [ string ] int {
"git-receive-pack" : models . AU_READABLE ,
"git receive-pack" : models . AU_READABLE ,
}
)
2014-04-10 22:20:58 +04:00
func In ( b string , sl map [ string ] int ) bool {
_ , e := sl [ b ]
return e
}
func runServ ( k * cli . Context ) {
2014-05-28 09:53:06 +04:00
setup ( path . Join ( setting . LogRootPath , "serv.log" ) )
2014-04-10 22:20:58 +04:00
keys := strings . Split ( os . Args [ 2 ] , "-" )
if len ( keys ) != 2 {
2014-05-22 05:37:13 +04:00
println ( "Gogs: auth file format error" )
qlog . Fatal ( "Invalid auth file format: %s" , os . Args [ 2 ] )
2014-04-10 22:20:58 +04:00
}
keyId , err := strconv . ParseInt ( keys [ 1 ] , 10 , 64 )
if err != nil {
2014-05-22 05:37:13 +04:00
println ( "Gogs: auth file format error" )
qlog . Fatalf ( "Invalid auth file format: %v" , err )
2014-04-10 22:20:58 +04:00
}
user , err := models . GetUserByKeyId ( keyId )
if err != nil {
2014-05-22 05:37:13 +04:00
if err == models . ErrUserNotKeyOwner {
println ( "Gogs: you are not the owner of SSH key" )
qlog . Fatalf ( "Invalid owner of SSH key: %d" , keyId )
}
println ( "Gogs: internal error:" , err )
qlog . Fatalf ( "Fail to get user by key ID(%d): %v" , keyId , err )
2014-04-10 22:20:58 +04:00
}
cmd := os . Getenv ( "SSH_ORIGINAL_COMMAND" )
if cmd == "" {
println ( "Hi" , user . Name , "! You've successfully authenticated, but Gogs does not provide shell access." )
return
}
verb , args := parseCmd ( cmd )
repoPath := strings . Trim ( args , "'" )
rr := strings . SplitN ( repoPath , "/" , 2 )
if len ( rr ) != 2 {
2014-05-22 05:37:13 +04:00
println ( "Gogs: unavailable repository" , args )
qlog . Fatalf ( "Unavailable repository: %v" , args )
2014-04-10 22:20:58 +04:00
}
repoUserName := rr [ 0 ]
2014-04-12 05:47:39 +04:00
repoName := strings . TrimSuffix ( rr [ 1 ] , ".git" )
2014-04-10 22:20:58 +04:00
isWrite := In ( verb , COMMANDS_WRITE )
isRead := In ( verb , COMMANDS_READONLY )
repoUser , err := models . GetUserByName ( repoUserName )
if err != nil {
2014-05-22 05:37:13 +04:00
if err == models . ErrUserNotExist {
println ( "Gogs: given repository owner are not registered" )
qlog . Fatalf ( "Unregistered owner: %s" , repoUserName )
}
println ( "Gogs: internal error:" , err )
qlog . Fatalf ( "Fail to get repository owner(%s): %v" , repoUserName , err )
2014-04-10 22:20:58 +04:00
}
2014-05-22 05:37:13 +04:00
// Access check.
2014-04-10 22:20:58 +04:00
switch {
case isWrite :
2014-05-22 05:37:13 +04:00
has , err := models . HasAccess ( user . Name , path . Join ( repoUserName , repoName ) , models . AU_WRITABLE )
2014-04-10 22:20:58 +04:00
if err != nil {
2014-05-22 05:37:13 +04:00
println ( "Gogs: internal error:" , err )
qlog . Fatal ( "Fail to check write access:" , err )
2014-04-10 22:20:58 +04:00
} else if ! has {
println ( "You have no right to write this repository" )
qlog . Fatalf ( "User %s has no right to write repository %s" , user . Name , repoPath )
}
case isRead :
repo , err := models . GetRepositoryByName ( repoUser . Id , repoName )
if err != nil {
2014-05-22 05:37:13 +04:00
if err == models . ErrRepoNotExist {
println ( "Gogs: given repository does not exist" )
qlog . Fatalf ( "Repository does not exist: %s/%s" , repoUser . Name , repoName )
}
println ( "Gogs: internal error:" , err )
qlog . Fatalf ( "Fail to get repository: %v" , err )
2014-04-10 22:20:58 +04:00
}
if ! repo . IsPrivate {
break
}
2014-04-12 05:47:39 +04:00
has , err := models . HasAccess ( user . Name , path . Join ( repoUserName , repoName ) , models . AU_READABLE )
2014-04-10 22:20:58 +04:00
if err != nil {
2014-05-22 05:37:13 +04:00
println ( "Gogs: internal error:" , err )
qlog . Fatal ( "Fail to check read access:" , err )
} else if ! has {
2014-04-10 22:20:58 +04:00
println ( "You have no right to access this repository" )
2014-05-22 05:37:13 +04:00
qlog . Fatalf ( "User %s has no right to read repository %s" , user . Name , repoPath )
2014-04-10 22:20:58 +04:00
}
default :
println ( "Unknown command" )
2014-05-22 05:37:13 +04:00
return
2014-04-10 22:20:58 +04:00
}
2014-05-03 09:37:49 +04:00
models . SetRepoEnvs ( user . Id , user . Name , repoName , repoUserName )
2014-04-10 22:20:58 +04:00
gitcmd := exec . Command ( verb , repoPath )
2014-05-26 04:11:25 +04:00
gitcmd . Dir = setting . RepoRootPath
2014-04-10 22:20:58 +04:00
gitcmd . Stdout = os . Stdout
gitcmd . Stdin = os . Stdin
gitcmd . Stderr = os . Stderr
if err = gitcmd . Run ( ) ; err != nil {
2014-05-22 05:37:13 +04:00
println ( "Gogs: internal error:" , err )
qlog . Fatalf ( "Fail to execute git command: %v" , err )
2014-04-10 22:20:58 +04:00
}
2014-04-11 06:27:13 +04:00
//refName := os.Getenv("refName")
//oldCommitId := os.Getenv("oldCommitId")
//newCommitId := os.Getenv("newCommitId")
//qlog.Error("get envs:", refName, oldCommitId, newCommitId)
// update
//models.Update(refName, oldCommitId, newCommitId, repoUserName, repoName, user.Id)
2014-04-10 22:20:58 +04:00
}