add ssh supports(unfinished)

release/v1.15
Lunny Xiao 2014-02-19 17:50:53 +08:00
parent 94311e187f
commit be0ba9ea88
8 changed files with 305 additions and 39 deletions

View File

@ -9,4 +9,4 @@ DB_TYPE = mysql
HOST = HOST =
NAME = gogs NAME = gogs
USER = root USER = root
PASSWD = root PASSWD =

65
gogs.go
View File

@ -1,44 +1,49 @@
// Copyright 2014 The Gogs Authors. All rights reserved. // Copyright 2013-2014 gogs authors.
// Use of this source code is governed by a MIT-style //
// license that can be found in the LICENSE file. // Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
// gogs(Go Git Service) is a Go clone of Github.
package main package main
import ( import (
"fmt" "os"
"net/http" "runtime"
"github.com/codegangsta/martini" "github.com/codegangsta/cli"
"github.com/martini-contrib/render"
"github.com/gogits/gogs/routers"
"github.com/gogits/gogs/routers/user"
"github.com/gogits/gogs/utils"
"github.com/gogits/gogs/utils/log"
) )
// +build go1.1
// Test that go1.1 tag above is included in builds. main.go refers to this definition.
const go11tag = true
const APP_VER = "0.0.0.0218" const APP_VER = "0.0.0.0218"
func init() { func init() {
runtime.GOMAXPROCS(runtime.NumCPU())
} }
func main() { func main() {
log.Info("%s %s", utils.Cfg.MustValue("", "APP_NAME"), APP_VER) app := cli.NewApp()
app.Name = "gogs"
m := martini.Classic() app.Usage = "Go Git Service"
app.Version = APP_VER
// Middleware. app.Commands = []cli.Command{
m.Use(render.Renderer()) CmdWeb,
CmdServ,
// Routers. }
m.Get("/", routers.Dashboard) app.Flags = append(app.Flags, []cli.Flag{
m.Get("/user/signin", user.SignIn) cli.BoolFlag{"noterm", "disable color output"},
m.Any("/user/signup", user.SignUp) }...)
app.Run(os.Args)
listenAddr := fmt.Sprintf("%s:%s",
utils.Cfg.MustValue("server", "HTTP_ADDR"),
utils.Cfg.MustValue("server", "HTTP_PORT", "3000"))
log.Info("Listen: %s", listenAddr)
http.ListenAndServe(listenAddr, m)
} }

View File

@ -17,7 +17,7 @@ import (
var ( var (
orm *xorm.Engine orm *xorm.Engine
repoRootPath string RepoRootPath string
) )
type Members struct { type Members struct {
@ -71,5 +71,9 @@ func setEngine() {
func init() { func init() {
setEngine() setEngine()
orm.Sync(new(User)) err := orm.Sync(new(User), new(PublicKey), new(Repo), new(Access))
if err != nil {
log.Error("sync database struct error: %s", err)
os.Exit(1)
}
} }

View File

@ -29,15 +29,22 @@ type Repo struct {
// check if repository is exist // check if repository is exist
func IsRepositoryExist(user *User, reposName string) (bool, error) { func IsRepositoryExist(user *User, reposName string) (bool, error) {
repo := Repo{OwnerId: user.Id} repo := Repo{OwnerId: user.Id}
// TODO: get repository by nocase name has, err := orm.Where("lower_name = ?", strings.ToLower(reposName)).Get(&repo)
return orm.Where("lower_name = ?", strings.ToLower(reposName)).Get(&repo) if err != nil {
return has, err
}
s, err := os.Stat(filepath.Join(RepoRootPath, user.Name, reposName))
if err != nil {
return false, err
}
return s.IsDir(), nil
} }
// //
// create a repository for a user or orgnaziation // create a repository for a user or orgnaziation
// //
func CreateRepository(user *User, reposName string) (*Repo, error) { func CreateRepository(user *User, reposName string) (*Repo, error) {
p := filepath.Join(repoRootPath, user.Name) p := filepath.Join(RepoRootPath, user.Name)
os.MkdirAll(p, os.ModePerm) os.MkdirAll(p, os.ModePerm)
f := filepath.Join(p, reposName+".git") f := filepath.Join(p, reposName+".git")
_, err := git.InitRepository(f, false) _, err := git.InitRepository(f, false)
@ -108,7 +115,7 @@ func DeleteRepository(user *User, reposName string) (err error) {
session.Rollback() session.Rollback()
return err return err
} }
if err = os.RemoveAll(filepath.Join(repoRootPath, user.Name, reposName+".git")); err != nil { if err = os.RemoveAll(filepath.Join(RepoRootPath, user.Name, reposName+".git")); err != nil {
// TODO: log and delete manully // TODO: log and delete manully
return err return err
} }

View File

@ -123,6 +123,19 @@ func (user *User) EncodePasswd() error {
return err return err
} }
func GetUserByKeyId(keyId int64) (*User, error) {
user := new(User)
has, err := orm.Sql("select a.* from user as a, public_key as b where a.id = b.owner_id and b.id=?", keyId).Get(user)
if err != nil {
return nil, err
}
if !has {
err = errors.New("not exist key owner")
return nil, err
}
return user, nil
}
// LoginUserPlain validates user by raw user name and password. // LoginUserPlain validates user by raw user name and password.
func LoginUserPlain(name, passwd string) (*User, error) { func LoginUserPlain(name, passwd string) (*User, error) {
user := User{Name: name, Passwd: passwd} user := User{Name: name, Passwd: passwd}

164
serve.go Normal file
View File

@ -0,0 +1,164 @@
package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"github.com/codegangsta/cli"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/utils/log"
)
var (
COMMANDS_READONLY = map[string]int{
"git-upload-pack": models.AU_WRITABLE,
"git upload-pack": models.AU_WRITABLE,
}
COMMANDS_WRITE = map[string]int{
"git-receive-pack": models.AU_READABLE,
"git receive-pack": models.AU_READABLE,
}
)
var CmdServ = cli.Command{
Name: "serv",
Usage: "just run",
Description: `
gogs serv`,
Action: runServ,
Flags: []cli.Flag{
//cli.BoolFlag{"update, u", "update pakcage(s) and dependencies if any"},
//cli.BoolFlag{"verbose, v", "show process details"},
},
}
func In(b string, sl map[string]int) bool {
_, e := sl[b]
return e
}
func runServ(*cli.Context) {
keys := strings.Split(os.Args[2], "-")
if len(keys) != 2 {
fmt.Println("auth file format error")
return
}
keyId, err := strconv.ParseInt(keys[1], 10, 64)
if err != nil {
fmt.Println("auth file format error")
return
}
user, err := models.GetUserByKeyId(keyId)
if err != nil {
fmt.Println("You have no right to access")
return
}
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
if cmd == "" {
fmt.Printf("Hi %s! You've successfully authenticated, but Gogits does not provide shell access.\n", user.Name)
return
}
f, _ := os.Create("test2.log")
f.WriteString(cmd)
f.Close()
log.Info("cmd is %s", cmd)
verb, args := parseCmd(cmd)
rr := strings.SplitN(strings.Trim(args, "'"), "/", 1)
if len(rr) != 2 {
fmt.Printf("Unavilable repository")
return
}
repoName := rr[1]
if strings.HasSuffix(repoName, ".git") {
repoName = repoName[:len(repoName)-4]
}
isWrite := In(verb, COMMANDS_WRITE)
isRead := In(verb, COMMANDS_READONLY)
switch {
case isWrite:
has, err := models.HasAccess(user.Name, repoName, COMMANDS_WRITE[verb])
if err != nil {
fmt.Println("Inernel error")
return
}
if !has {
fmt.Println("You have no right to access this repository")
return
}
case isRead:
has, err := models.HasAccess(user.Name, repoName, COMMANDS_READONLY[verb])
if err != nil {
fmt.Println("Inernel error")
return
}
if !has {
has, err = models.HasAccess(user.Name, repoName, COMMANDS_WRITE[verb])
if err != nil {
fmt.Println("Inernel error")
return
}
}
if !has {
fmt.Println("You have no right to access this repository")
return
}
default:
fmt.Println("Unknown command")
return
}
isExist, err := models.IsRepositoryExist(user, repoName)
if err != nil {
fmt.Println("Inernel error")
return
}
if !isExist {
if isRead {
fmt.Println("Repository is not exist")
return
} else if isWrite {
_, err := models.CreateRepository(user, repoName)
if err != nil {
fmt.Println("Create repository failed")
return
}
}
}
fullPath := filepath.Join(models.RepoRootPath, user.Name, repoName+".git")
newcmd := fmt.Sprintf("%s '%s'", verb, fullPath)
fmt.Println(newcmd)
gitcmd := exec.Command("git", "shell", "-c", newcmd)
gitcmd.Stdout = os.Stdout
gitcmd.Stderr = os.Stderr
err = gitcmd.Run()
if err != nil {
log.Error("execute command error: %s", err)
}
}
func parseCmd(cmd string) (string, string) {
ss := strings.SplitN(cmd, " ", 1)
if len(ss) != 2 {
return "", ""
}
verb, args := ss[0], ss[1]
if verb == "git" {
ss = strings.SplitN(args, " ", 1)
args = ss[1]
verb = fmt.Sprintf("%s %s", verb, ss[0])
}
return verb, args
}

View File

@ -7,17 +7,39 @@ package utils
import ( import (
"fmt" "fmt"
"os" "os"
"os/exec"
"path"
"path/filepath"
"github.com/Unknwon/goconfig" "github.com/Unknwon/goconfig"
) )
var Cfg *goconfig.ConfigFile var Cfg *goconfig.ConfigFile
func exeDir() (string, error) {
file, err := exec.LookPath(os.Args[0])
if err != nil {
return "", err
}
p, err := filepath.Abs(file)
if err != nil {
return "", err
}
return path.Dir(p), nil
}
func init() { func init() {
var err error var err error
Cfg, err = goconfig.LoadConfigFile("conf/app.ini") workDir, err := exeDir()
if err != nil { if err != nil {
fmt.Println("Cannot load config file 'app.ini'") fmt.Printf("Fail to get work directory: %s\n", err)
os.Exit(2)
}
cfgPath := filepath.Join(workDir, "conf", "app.ini")
Cfg, err = goconfig.LoadConfigFile(cfgPath)
if err != nil {
fmt.Printf("Cannot load config file '%s'\n", cfgPath)
os.Exit(2) os.Exit(2)
} }
Cfg.BlockMode = false Cfg.BlockMode = false

51
web.go Normal file
View File

@ -0,0 +1,51 @@
// 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 main
import (
"fmt"
"net/http"
"github.com/codegangsta/cli"
"github.com/codegangsta/martini"
"github.com/martini-contrib/render"
"github.com/gogits/gogs/routers"
"github.com/gogits/gogs/routers/user"
"github.com/gogits/gogs/utils"
"github.com/gogits/gogs/utils/log"
)
var CmdWeb = cli.Command{
Name: "web",
Usage: "just run",
Description: `
gogs web`,
Action: runWeb,
Flags: []cli.Flag{
//cli.BoolFlag{"update, u", "update pakcage(s) and dependencies if any"},
//cli.BoolFlag{"verbose, v", "show process details"},
},
}
func runWeb(*cli.Context) {
log.Info("%s %s", utils.Cfg.MustValue("", "APP_NAME"), APP_VER)
m := martini.Classic()
// Middleware.
m.Use(render.Renderer())
// Routers.
m.Get("/", routers.Dashboard)
m.Get("/user/signin", user.SignIn)
m.Any("/user/signup", user.SignUp)
listenAddr := fmt.Sprintf("%s:%s",
utils.Cfg.MustValue("server", "HTTP_ADDR"),
utils.Cfg.MustValue("server", "HTTP_PORT", "3000"))
log.Info("Listen: %s", listenAddr)
http.ListenAndServe(listenAddr, m)
}