Browse Source

add ssh supports(unfinished)

Lunny Xiao 11 years ago
parent
commit
be0ba9ea88
8 changed files with 306 additions and 40 deletions
  1. 1 1
      conf/app.ini
  2. 36 31
      gogs.go
  3. 6 2
      models/models.go
  4. 11 4
      models/repo.go
  5. 13 0
      models/user.go
  6. 164 0
      serve.go
  7. 24 2
      utils/conf.go
  8. 51 0
      web.go

+ 1 - 1
conf/app.ini

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

+ 36 - 31
gogs.go

@@ -1,44 +1,49 @@
-// 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.
-
+// Copyright 2013-2014 gogs authors.
+//
+// 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
 
 import (
-	"fmt"
-	"net/http"
-
-	"github.com/codegangsta/martini"
-	"github.com/martini-contrib/render"
+	"os"
+	"runtime"
 
-	"github.com/gogits/gogs/routers"
-	"github.com/gogits/gogs/routers/user"
-	"github.com/gogits/gogs/utils"
-	"github.com/gogits/gogs/utils/log"
+	"github.com/codegangsta/cli"
 )
 
+// +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"
 
 func init() {
-
+	runtime.GOMAXPROCS(runtime.NumCPU())
 }
 
 func main() {
-	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)
+	app := cli.NewApp()
+	app.Name = "gogs"
+	app.Usage = "Go Git Service"
+	app.Version = APP_VER
+	app.Commands = []cli.Command{
+		CmdWeb,
+		CmdServ,
+	}
+	app.Flags = append(app.Flags, []cli.Flag{
+		cli.BoolFlag{"noterm", "disable color output"},
+	}...)
+	app.Run(os.Args)
 }

+ 6 - 2
models/models.go

@@ -17,7 +17,7 @@ import (
 
 var (
 	orm          *xorm.Engine
-	repoRootPath string
+	RepoRootPath string
 )
 
 type Members struct {
@@ -71,5 +71,9 @@ func setEngine() {
 
 func init() {
 	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)
+	}
 }

+ 11 - 4
models/repo.go

@@ -29,15 +29,22 @@ type Repo struct {
 // check if repository is exist
 func IsRepositoryExist(user *User, reposName string) (bool, error) {
 	repo := Repo{OwnerId: user.Id}
-	// TODO: get repository by nocase name
-	return orm.Where("lower_name = ?", strings.ToLower(reposName)).Get(&repo)
+	has, err := 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
 //
 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)
 	f := filepath.Join(p, reposName+".git")
 	_, err := git.InitRepository(f, false)
@@ -108,7 +115,7 @@ func DeleteRepository(user *User, reposName string) (err error) {
 		session.Rollback()
 		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
 		return err
 	}

+ 13 - 0
models/user.go

@@ -123,6 +123,19 @@ func (user *User) EncodePasswd() error {
 	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.
 func LoginUserPlain(name, passwd string) (*User, error) {
 	user := User{Name: name, Passwd: passwd}

+ 164 - 0
serve.go

@@ -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
+}

+ 24 - 2
utils/conf.go

@@ -7,17 +7,39 @@ package utils
 import (
 	"fmt"
 	"os"
+	"os/exec"
+	"path"
+	"path/filepath"
 
 	"github.com/Unknwon/goconfig"
 )
 
 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() {
 	var err error
-	Cfg, err = goconfig.LoadConfigFile("conf/app.ini")
+	workDir, err := exeDir()
+	if err != nil {
+		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.Println("Cannot load config file 'app.ini'")
+		fmt.Printf("Cannot load config file '%s'\n", cfgPath)
 		os.Exit(2)
 	}
 	Cfg.BlockMode = false

+ 51 - 0
web.go

@@ -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)
+}