You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gitea/cmd/serve.go

238 lines
6.4 KiB

11 years ago
// 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.
package cmd
11 years ago
import (
"fmt"
"os"
"os/exec"
11 years ago
"path/filepath"
11 years ago
"strings"
"time"
11 years ago
"github.com/Unknwon/com"
"github.com/codegangsta/cli"
11 years ago
"github.com/gogits/gogs/models"
11 years ago
"github.com/gogits/gogs/modules/log"
11 years ago
"github.com/gogits/gogs/modules/setting"
11 years ago
"github.com/gogits/gogs/modules/uuid"
11 years ago
)
var CmdServ = cli.Command{
11 years ago
Name: "serv",
Usage: "This command should only be called by SSH shell",
Description: `Serv provide access auth for repositories`,
Action: runServ,
Flags: []cli.Flag{
cli.StringFlag{"config, c", "custom/conf/app.ini", "Custom configuration file path", ""},
},
11 years ago
}
11 years ago
func setup(logPath string) {
11 years ago
setting.NewConfigContext()
11 years ago
log.NewGitLogger(filepath.Join(setting.LogRootPath, logPath))
if setting.DisableSSH {
println("Gogs: SSH has been disabled")
os.Exit(1)
}
11 years ago
models.LoadModelsConfig()
if setting.UseSQLite3 {
11 years ago
workDir, _ := setting.WorkDir()
11 years ago
os.Chdir(workDir)
11 years ago
}
models.SetEngine()
}
11 years ago
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])
}
return verb, strings.Replace(args, "'/", "'", 1)
11 years ago
}
11 years ago
var (
COMMANDS_READONLY = map[string]models.AccessMode{
"git-upload-pack": models.ACCESS_MODE_WRITE,
"git upload-pack": models.ACCESS_MODE_WRITE,
"git-upload-archive": models.ACCESS_MODE_WRITE,
11 years ago
}
COMMANDS_WRITE = map[string]models.AccessMode{
"git-receive-pack": models.ACCESS_MODE_READ,
"git receive-pack": models.ACCESS_MODE_READ,
11 years ago
}
)
func In(b string, sl map[string]models.AccessMode) bool {
11 years ago
_, e := sl[b]
return e
}
func runServ(c *cli.Context) {
if c.IsSet("config") {
setting.CustomConf = c.String("config")
}
11 years ago
setup("serv.log")
11 years ago
if len(c.Args()) < 1 {
log.GitLogger.Fatal(2, "Not enough arguments")
}
keys := strings.Split(c.Args()[0], "-")
11 years ago
if len(keys) != 2 {
11 years ago
println("Gogs: auth file format error")
log.GitLogger.Fatal(2, "Invalid auth file format: %s", os.Args[2])
11 years ago
}
keyId, err := com.StrTo(keys[1]).Int64()
11 years ago
if err != nil {
11 years ago
println("Gogs: auth file format error")
log.GitLogger.Fatal(2, "Invalid auth file format: %v", err)
11 years ago
}
user, err := models.GetUserByKeyId(keyId)
if err != nil {
11 years ago
if err == models.ErrUserNotKeyOwner {
println("Gogs: you are not the owner of SSH key")
log.GitLogger.Fatal(2, "Invalid owner of SSH key: %d", keyId)
11 years ago
}
10 years ago
println("Gogs: internal error:", err.Error())
log.GitLogger.Fatal(2, "Fail to get user by key ID(%d): %v", keyId, err)
11 years ago
}
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
if cmd == "" {
println("Hi", user.Name, "! You've successfully authenticated, but Gogs does not provide shell access.")
println("If this is what you do not expect, please log in with password and setup Gogs under another user.")
11 years ago
return
}
verb, args := parseCmd(cmd)
repoPath := strings.Trim(args, "'")
rr := strings.SplitN(repoPath, "/", 2)
if len(rr) != 2 {
11 years ago
println("Gogs: unavailable repository", args)
log.GitLogger.Fatal(2, "Unavailable repository: %v", args)
11 years ago
}
repoUserName := rr[0]
repoName := strings.TrimSuffix(rr[1], ".git")
11 years ago
isWrite := In(verb, COMMANDS_WRITE)
isRead := In(verb, COMMANDS_READONLY)
repoUser, err := models.GetUserByName(repoUserName)
if err != nil {
11 years ago
if err == models.ErrUserNotExist {
println("Gogs: given repository owner are not registered")
log.GitLogger.Fatal(2, "Unregistered owner: %s", repoUserName)
11 years ago
}
10 years ago
println("Gogs: internal error:", err.Error())
log.GitLogger.Fatal(2, "Fail to get repository owner(%s): %v", repoUserName, err)
11 years ago
}
11 years ago
// Access check.
repo, err := models.GetRepositoryByName(repoUser.Id, repoName)
if err != nil {
if err == models.ErrRepoNotExist {
println("Gogs: given repository does not exist")
log.GitLogger.Fatal(2, "Repository does not exist: %s/%s", repoUser.Name, repoName)
}
println("Gogs: internal error:", err.Error())
log.GitLogger.Fatal(2, "Fail to get repository: %v", err)
}
11 years ago
switch {
case isWrite:
has, err := models.HasAccess(user, repo, models.ACCESS_MODE_WRITE)
11 years ago
if err != nil {
10 years ago
println("Gogs: internal error:", err.Error())
log.GitLogger.Fatal(2, "Fail to check write access:", err)
11 years ago
} else if !has {
println("You have no right to write this repository")
log.GitLogger.Fatal(2, "User %s has no right to write repository %s", user.Name, repoPath)
11 years ago
}
if repo.IsMirror {
println("You can't write to a mirror repository")
log.GitLogger.Fatal(2, "User %s tried to write to a mirror repository %s", user.Name, repoPath)
}
11 years ago
case isRead:
if !repo.IsPrivate {
break
}
has, err := models.HasAccess(user, repo, models.ACCESS_MODE_READ)
11 years ago
if err != nil {
10 years ago
println("Gogs: internal error:", err.Error())
log.GitLogger.Fatal(2, "Fail to check read access:", err)
11 years ago
} else if !has {
11 years ago
println("You have no right to access this repository")
log.GitLogger.Fatal(2, "User %s has no right to read repository %s", user.Name, repoPath)
11 years ago
}
default:
println("Unknown command: " + cmd)
11 years ago
return
11 years ago
}
uuid := uuid.NewV4().String()
os.Setenv("uuid", uuid)
11 years ago
10 years ago
var gitcmd *exec.Cmd
verbs := strings.Split(verb, " ")
if len(verbs) == 2 {
gitcmd = exec.Command(verbs[0], verbs[1], repoPath)
} else {
gitcmd = exec.Command(verb, repoPath)
}
11 years ago
gitcmd.Dir = setting.RepoRootPath
11 years ago
gitcmd.Stdout = os.Stdout
gitcmd.Stdin = os.Stdin
gitcmd.Stderr = os.Stderr
if err = gitcmd.Run(); err != nil {
println("Gogs: internal error:", err.Error())
log.GitLogger.Fatal(2, "Fail to execute git command: %v", err)
11 years ago
}
if isWrite {
tasks, err := models.GetUpdateTasksByUuid(uuid)
if err != nil {
log.GitLogger.Fatal(2, "GetUpdateTasksByUuid: %v", err)
}
for _, task := range tasks {
err = models.Update(task.RefName, task.OldCommitId, task.NewCommitId,
user.Name, repoUserName, repoName, user.Id)
if err != nil {
log.GitLogger.Error(2, "Fail to update: %v", err)
}
}
if err = models.DelUpdateTasksByUuid(uuid); err != nil {
log.GitLogger.Fatal(2, "DelUpdateTasksByUuid: %v", err)
}
}
// Update key activity.
key, err := models.GetPublicKeyById(keyId)
if err != nil {
log.GitLogger.Fatal(2, "GetPublicKeyById: %v", err)
}
key.Updated = time.Now()
if err = models.UpdatePublicKey(key); err != nil {
log.GitLogger.Fatal(2, "UpdatePublicKey: %v", err)
}
11 years ago
}