Browse Source

Merge pull request #964 from phsmit/access_rewriteserv

Rewrite/simplify gogs serve
master
无闻 10 years ago
parent
commit
8d17ff8ce7
2 changed files with 53 additions and 84 deletions
  1. +51
    -82
      cmd/serve.go
  2. +2
    -2
      models/user.go

+ 51
- 82
cmd/serve.go View File

@@ -21,6 +21,10 @@ import (
"github.com/gogits/gogs/modules/uuid" "github.com/gogits/gogs/modules/uuid"
) )


const (
ACCESS_DENIED_MESSAGE = "Repository does not exist or you do not have access"
)

var CmdServ = cli.Command{ var CmdServ = cli.Command{
Name: "serv", Name: "serv",
Usage: "This command should only be called by SSH shell", Usage: "This command should only be called by SSH shell",
@@ -51,72 +55,58 @@ func setup(logPath string) {
} }


func parseCmd(cmd string) (string, string) { func parseCmd(cmd string) (string, string) {

ss := strings.SplitN(cmd, " ", 2) ss := strings.SplitN(cmd, " ", 2)
if len(ss) != 2 { if len(ss) != 2 {
return "", "" 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)
return ss[0], strings.Replace(ss[1], "'/", "'", 1)
} }


var ( 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,
}

COMMANDS_WRITE = map[string]models.AccessMode{
"git-receive-pack": models.ACCESS_MODE_READ,
"git receive-pack": models.ACCESS_MODE_READ,
COMMANDS = map[string]models.AccessMode{
"git-upload-pack": models.ACCESS_MODE_READ,
"git-upload-archive": models.ACCESS_MODE_READ,
"git-receive-pack": models.ACCESS_MODE_WRITE,
} }
) )


func In(b string, sl map[string]models.AccessMode) bool {
_, e := sl[b]
return e
}

func runServ(c *cli.Context) { func runServ(c *cli.Context) {
if c.IsSet("config") { if c.IsSet("config") {
setting.CustomConf = c.String("config") setting.CustomConf = c.String("config")
} }
setup("serv.log") setup("serv.log")


fail := func(userMessage, logMessage string, args ...interface{}) {
fmt.Fprintln(os.Stderr, "Gogs: ", userMessage)
log.GitLogger.Fatal(2, logMessage, args...)
}

if len(c.Args()) < 1 { if len(c.Args()) < 1 {
log.GitLogger.Fatal(2, "Not enough arguments")
fail("Not enough arguments", "Not enough arugments")
} }

keys := strings.Split(c.Args()[0], "-") keys := strings.Split(c.Args()[0], "-")
if len(keys) != 2 { if len(keys) != 2 {
println("Gogs: auth file format error")
log.GitLogger.Fatal(2, "Invalid auth file format: %s", os.Args[2])
fail("key-id format error", "Invalid key id: %s", c.Args()[0])
} }


keyId, err := com.StrTo(keys[1]).Int64() keyId, err := com.StrTo(keys[1]).Int64()
if err != nil { if err != nil {
println("Gogs: auth file format error")
log.GitLogger.Fatal(2, "Invalid auth file format: %v", err)
fail("key-id format error", "Invalid key id: %s", err)
} }

user, err := models.GetUserByKeyId(keyId) user, err := models.GetUserByKeyId(keyId)
if err != nil { if err != nil {
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)
}
println("Gogs: internal error:", err.Error())
log.GitLogger.Fatal(2, "Fail to get user by key ID(%d): %v", keyId, err)
fail("internal error", "Fail to get user by key ID(%d): %v", keyId, err)
} }


cmd := os.Getenv("SSH_ORIGINAL_COMMAND") cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
if cmd == "" { if cmd == "" {
println("Hi", user.Name, "! You've successfully authenticated, but Gogs does not provide shell access.") 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.")
if user.IsAdmin {
println("If this is unexpected, please log in with password and setup Gogs under another user.")
}
return return
} }


@@ -124,67 +114,47 @@ func runServ(c *cli.Context) {
repoPath := strings.Trim(args, "'") repoPath := strings.Trim(args, "'")
rr := strings.SplitN(repoPath, "/", 2) rr := strings.SplitN(repoPath, "/", 2)
if len(rr) != 2 { if len(rr) != 2 {
println("Gogs: unavailable repository", args)
log.GitLogger.Fatal(2, "Unavailable repository: %v", args)
fail("Invalid repository path", "Invalide repository path: %v", args)
} }
repoUserName := rr[0] repoUserName := rr[0]
repoName := strings.TrimSuffix(rr[1], ".git") repoName := strings.TrimSuffix(rr[1], ".git")


isWrite := In(verb, COMMANDS_WRITE)
isRead := In(verb, COMMANDS_READONLY)

repoUser, err := models.GetUserByName(repoUserName) repoUser, err := models.GetUserByName(repoUserName)
if err != nil { if err != nil {
if err == models.ErrUserNotExist { if err == models.ErrUserNotExist {
println("Gogs: given repository owner are not registered")
log.GitLogger.Fatal(2, "Unregistered owner: %s", repoUserName)
fail("Repository owner does not exist", "Unregistered owner: %s", repoUserName)
} }
println("Gogs: internal error:", err.Error())
log.GitLogger.Fatal(2, "Fail to get repository owner(%s): %v", repoUserName, err)
fail("Internal error", "Fail to get repository owner(%s): %v", repoUserName, err)
} }


// Access check.
repo, err := models.GetRepositoryByName(repoUser.Id, repoName) repo, err := models.GetRepositoryByName(repoUser.Id, repoName)
if err != nil { if err != nil {
if err == models.ErrRepoNotExist { if err == models.ErrRepoNotExist {
println("Gogs: given repository does not exist")
log.GitLogger.Fatal(2, "Repository does not exist: %s/%s", repoUser.Name, repoName)
if user.Id == repoUser.Id || repoUser.IsOwnedBy(user.Id) {
fail("Repository does not exist", "Repository does not exist: %s/%s", repoUser.Name, repoName)
} else {
fail(ACCESS_DENIED_MESSAGE, "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)
fail("Internal error", "Fail to get repository: %v", err)
} }


switch {
case isWrite:
has, err := models.HasAccess(user, repo, models.ACCESS_MODE_WRITE)
if err != nil {
println("Gogs: internal error:", err.Error())
log.GitLogger.Fatal(2, "Fail to check write access:", err)
} 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)
}

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)
}
case isRead:
if !repo.IsPrivate {
break
}
requestedMode, has := COMMANDS[verb]
if !has {
fail("Unknown git command", "Unknown git command %s", verb)
}


has, err := models.HasAccess(user, repo, models.ACCESS_MODE_READ)
if err != nil {
println("Gogs: internal error:", err.Error())
log.GitLogger.Fatal(2, "Fail to check read access:", err)
} else if !has {
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)
mode, err := models.AccessLevel(user, repo)
if err != nil {
fail("Internal error", "HasAccess fail: %v", err)
} else if mode < requestedMode {
clientMessage := ACCESS_DENIED_MESSAGE
if mode >= models.ACCESS_MODE_READ {
clientMessage = "You do not have sufficient authorization for this action"
} }
default:
println("Unknown command: " + cmd)
return
fail(clientMessage,
"User %s does not have level %v access to repository %s",
user.Name, requestedMode, repoPath)
} }


uuid := uuid.NewV4().String() uuid := uuid.NewV4().String()
@@ -202,11 +172,10 @@ func runServ(c *cli.Context) {
gitcmd.Stdin = os.Stdin gitcmd.Stdin = os.Stdin
gitcmd.Stderr = os.Stderr gitcmd.Stderr = os.Stderr
if err = gitcmd.Run(); err != nil { if err = gitcmd.Run(); err != nil {
println("Gogs: internal error:", err.Error())
log.GitLogger.Fatal(2, "Fail to execute git command: %v", err)
fail("Internal error", "Fail to execute git command: %v", err)
} }


if isWrite {
if requestedMode == models.ACCESS_MODE_WRITE {
tasks, err := models.GetUpdateTasksByUuid(uuid) tasks, err := models.GetUpdateTasksByUuid(uuid)
if err != nil { if err != nil {
log.GitLogger.Fatal(2, "GetUpdateTasksByUuid: %v", err) log.GitLogger.Fatal(2, "GetUpdateTasksByUuid: %v", err)
@@ -228,10 +197,10 @@ func runServ(c *cli.Context) {
// Update key activity. // Update key activity.
key, err := models.GetPublicKeyById(keyId) key, err := models.GetPublicKeyById(keyId)
if err != nil { if err != nil {
log.GitLogger.Fatal(2, "GetPublicKeyById: %v", err)
fail("Internal error", "GetPublicKeyById: %v", err)
} }
key.Updated = time.Now() key.Updated = time.Now()
if err = models.UpdatePublicKey(key); err != nil { if err = models.UpdatePublicKey(key); err != nil {
log.GitLogger.Fatal(2, "UpdatePublicKey: %v", err)
fail("Internal error", "UpdatePublicKey: %v", err)
} }
} }

+ 2
- 2
models/user.go View File

@@ -40,7 +40,7 @@ var (
ErrUserHasOrgs = errors.New("User still have membership of organization") ErrUserHasOrgs = errors.New("User still have membership of organization")
ErrUserAlreadyExist = errors.New("User already exist") ErrUserAlreadyExist = errors.New("User already exist")
ErrUserNotExist = errors.New("User does not exist") ErrUserNotExist = errors.New("User does not exist")
ErrUserNotKeyOwner = errors.New("User does not the owner of public key")
ErrPublicKeyNotExist = errors.New("Public key does not exist")
ErrEmailAlreadyUsed = errors.New("E-mail already used") ErrEmailAlreadyUsed = errors.New("E-mail already used")
ErrEmailNotExist = errors.New("E-mail does not exist") ErrEmailNotExist = errors.New("E-mail does not exist")
ErrEmailNotActivated = errors.New("E-mail address has not been activated") ErrEmailNotActivated = errors.New("E-mail address has not been activated")
@@ -518,7 +518,7 @@ func GetUserByKeyId(keyId int64) (*User, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} else if !has { } else if !has {
return nil, ErrUserNotKeyOwner
return nil, ErrPublicKeyNotExist
} }
return user, nil return user, nil
} }


Loading…
Cancel
Save