diff --git a/.gitignore b/.gitignore index f8d8a2869..65252f8c2 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ public/img/avatar/ *.o *.a *.so +dev # Folders _obj diff --git a/.gopmfile b/.gopmfile index 296d02367..c58f4299d 100644 --- a/.gopmfile +++ b/.gopmfile @@ -19,6 +19,7 @@ github.com/lib/pq = github.com/nfnt/resize = github.com/qiniu/log = github.com/robfig/cron = +github.com/juju2013/goldap = [res] include = templates|public|conf diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cfc6c14f2..6cc88515f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,6 +10,8 @@ Want to hack on Gogs? Awesome! Here are instructions to get you started. They ar ### Pull requests are always welcome +**ALL PULL REQUESTS MUST SEND TO `DEV` BRANCH** + We are always thrilled to receive pull requests, and do our best to process them as fast as possible. Not sure if that typo is worth a pull request? Do it! We will appreciate it. If your pull request is not accepted on the first try, don't be discouraged! If there's a problem with the implementation, hopefully you received feedback on what to improve. diff --git a/README.md b/README.md index 038780721..c16f721aa 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language ![Demo](http://gowalker.org/public/gogs_demo.gif) -##### Current version: 0.3.0 Alpha +##### Current version: 0.3.1 Alpha ### NOTICES @@ -42,6 +42,11 @@ More importantly, Gogs only needs one binary to setup your own project hosting o - Supports MySQL, PostgreSQL and SQLite3. - Social account login(GitHub, Google, QQ, Weibo) +## System Requirements + +- A cheap Raspberry Pi is powerful enough to match the minimal requirement. +- 4 CPU Cores and 1GB RAM would be the baseline for teamwork. + ## Installation Make sure you install [Prerequirements](https://github.com/gogits/gogs/wiki/Prerequirements) first. diff --git a/README_ZH.md b/README_ZH.md index 6d7553a68..b82782235 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -5,7 +5,7 @@ Gogs(Go Git Service) 是一个由 Go 语言编写的自助 Git 托管服务。 ![Demo](http://gowalker.org/public/gogs_demo.gif) -##### 当前版本:0.3.0 Alpha +##### 当前版本:0.3.1 Alpha ## 开发目的 @@ -33,6 +33,12 @@ Gogs 完全使用 Go 语言来实现对 Git 数据的操作,实现 **零** 依 - 支持 MySQL、PostgreSQL 以及 SQLite3 数据库 - 社交帐号登录(GitHub、Google、QQ、微博) +## 系统要求 + +- 最低的系统硬件要求为一个廉价的树莓派 +- 如果用于团队项目,建议使用 4 核 CPU 及 1GB 内存 + + ## 安装部署 在安装 Gogs 之前,您需要先安装 [基本环境](https://github.com/gogits/gogs/wiki/Prerequirements)。 diff --git a/conf/app.ini b/conf/app.ini index 25fd41091..e7174e225 100644 --- a/conf/app.ini +++ b/conf/app.ini @@ -16,6 +16,8 @@ LICENSES = Apache v2 License|GPL v2|MIT License|Affero GPL|Artistic License 2.0| PROTOCOL = http DOMAIN = localhost ROOT_URL = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/ +; Disable CDN even in "prod" mode +OFFLINE_MODE = false HTTP_ADDR = HTTP_PORT = 3000 ; Generate steps: diff --git a/doc/install_gogs_from_binary_on_ubuntu.md b/doc/install_gogs_from_binary_on_ubuntu.md new file mode 100644 index 000000000..3b406b364 --- /dev/null +++ b/doc/install_gogs_from_binary_on_ubuntu.md @@ -0,0 +1,26 @@ +### Binary install gogs on ubuntu 14.04 LTS + +### create user and install denpendency +- sudo adduser git +- sudo apt-get update +- sudo apt-get upgrade +- sudo apt-get install git +- sudo apt-get install mysql-server + +### create the database +- $mysql -u root -p +- mysql> SET GLOBAL storage_engine = 'InnoDB'; +- mysql> CREATE DATABASE gogs CHARACTER SET utf8 COLLATE utf8_bin; +- mysql> GRANT ALL PRIVILEGES ON gogs.* TO 'root'@'localhost' IDENTIFIED BY 'password'; +- mysql> FLUSH PRIVILEGES; +- mysql> QUIT + +### install the gogs +- mkdir gogs +- cd gogs +- curl -L http://gobuild.io/github.com/gogits/gogs/v0.3.0/linux/amd64 -o v0.3.0.zip +- unzip v0.3.0.zip +- ./start.sh + +> The up-to-date binary could be found at +> http://gobuild.io/download/github.com/gogits/gogs diff --git a/doc/install_gogs_from_source_on_ubuntu.md b/doc/install_gogs_from_source_on_ubuntu.md new file mode 100644 index 000000000..b8ae6fc79 --- /dev/null +++ b/doc/install_gogs_from_source_on_ubuntu.md @@ -0,0 +1,48 @@ +##Install gogs under ubuntu 14.04 LTS 32bit from source code + +###Requirements +- Go Programming Language: Version >= 1.2 +- git(bash): Version >= 1.6.6(both server and client) +- MySQL: Version >= 5.1 or PostgreSQL or NOTHING. + +### Create the user which will run git +- sudo adduser git +- su git + +### Install git and Mysql-server +- sudo apt-get install git +- sudo apt-get install mysql-server + +### Create database +- $ mysql -u root -p +- mysql> SET GLOBAL storage_engine = 'InnoDB'; +- mysql> CREATE DATABASE gogs CHARACTER SET utf8 COLLATE utf8_bin; +- mysql> GRANT ALL PRIVILEGES ON gogs.* TO 'root'@'localhost' IDENTIFIED BY 'pasword'; +- mysql> FLUSH PRIVILEGES; +- mysql> QUIT + +### install go from source +- sudo apt-get install build-essential +- sudo apt-get install mercurial +- hg clone -r release https://go.googlecode.com/hg/ /home/git/golang/ + + +- echo export GOROOT=/home/git/golang >>.bashrc +- echo export GOARCH=386 >>.bashrc +- echo export GOOS=linux >>.bashrc +- echo export GOBIN= /home/git/golang/bin >>.bashrc +- echo export GOPATH=$HOME/app/Go >>.bashrc +- echo PATH=${PATH}: /$HOME/golang/bin >>.bashrc +- cd $GOROOT/src +- ./make.bash + +### Download and install dependencies +- $ go get -u github.com/gogits/gogs + +### Build main program +- $ cd $GOPATH/src/github.com/gogits/gogs +- $ go build +- $ ./start.sh + +### At present, you could access gogs from http://localhost:3000 + diff --git a/dockerfiles/README.md b/dockerfiles/README.md index 83ca3e954..fbd9f7aa1 100644 --- a/dockerfiles/README.md +++ b/dockerfiles/README.md @@ -1,6 +1,6 @@ ### Install Gogs With Docker -Deplying gogs in [Docker](http://www.docker.io/) is just as easy as eating a pie, what you do is just open the `dockerfiles/build.sh` file, replace the confis: +Deploying gogs in [Docker](http://www.docker.io/) is just as easy as eating a pie, what you do is just open the `dockerfiles/build.sh` file, replace the configs: ``` DB_TYPE="YOUR_DB_TYPE" # type of database, support 'mysql' and 'postgres' diff --git a/dockerfiles/images/gogits/Dockerfile b/dockerfiles/images/gogits/Dockerfile index a460ca500..087168f5b 100644 --- a/dockerfiles/images/gogits/Dockerfile +++ b/dockerfiles/images/gogits/Dockerfile @@ -13,7 +13,7 @@ ENV GOROOT /usr/local/go ENV GOPATH /go RUN apt-get update && apt-get install --yes --force-yes curl git mercurial zip wget ca-certificates build-essential -RUN apt-get install -yq vim +RUN apt-get install -yq vim sudo RUN curl -s http://docker.u.qiniudn.com/go1.2.1.src.tar.gz | tar -v -C /usr/local -xz RUN cd /usr/local/go/src && ./make.bash --no-clean 2>&1 diff --git a/dockerfiles/images/postgres/Dockerfile b/dockerfiles/images/postgres/Dockerfile index 44e82b7d8..0188dd78f 100644 --- a/dockerfiles/images/postgres/Dockerfile +++ b/dockerfiles/images/postgres/Dockerfile @@ -9,7 +9,8 @@ RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys B97B0AFCAA # Add PostgreSQL's repository. It contains the most recent stable release # of PostgreSQL, ``9.3``. -RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main" > /etc/apt/sources.list.d/pgdg.list +# See http://apt.postgresql.org/pub/repos/apt/README +RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list # Update the Ubuntu and PostgreSQL repository indexes RUN apt-get update diff --git a/dockerfiles/images/redis/Dockerfile b/dockerfiles/images/redis/Dockerfile index 7dfcba334..cdbbea21a 100644 --- a/dockerfiles/images/redis/Dockerfile +++ b/dockerfiles/images/redis/Dockerfile @@ -1,8 +1,6 @@ FROM ubuntu MAINTAINER Meaglith Ma (@genedna), Lance Ju (@crystaldust) -ENV DEBIAN_FRONTEND noninteractive - ENV DEBIAN_FRONTEND noninteractive RUN apt-get update && apt-get install -y redis-server diff --git a/gogs.go b/gogs.go index 73555adab..d723aa33a 100644 --- a/gogs.go +++ b/gogs.go @@ -19,7 +19,7 @@ import ( // Test that go1.2 tag above is included in builds. main.go refers to this definition. const go12tag = true -const APP_VER = "0.3.0.0421 Alpha" +const APP_VER = "0.3.1.0427 Alpha" func init() { base.AppVer = APP_VER diff --git a/models/issue.go b/models/issue.go index f14030df5..26b8cecdd 100644 --- a/models/issue.go +++ b/models/issue.go @@ -71,7 +71,7 @@ func CreateIssue(userId, repoId, milestoneId, assigneeId int64, issueCount int, } if err = sess.Commit(); err != nil { - sess.Rollback() + //sess.Rollback() return nil, err } @@ -196,7 +196,7 @@ func CreateComment(userId, repoId, issueId, commitId, line int64, cmtType int, c defer sess.Close() sess.Begin() - if _, err := orm.Insert(&Comment{PosterId: userId, Type: cmtType, IssueId: issueId, + if _, err := sess.Insert(&Comment{PosterId: userId, Type: cmtType, IssueId: issueId, CommitId: commitId, Line: line, Content: content}); err != nil { sess.Rollback() return err diff --git a/models/ldap.go b/models/ldap.go new file mode 100644 index 000000000..cc9058765 --- /dev/null +++ b/models/ldap.go @@ -0,0 +1,38 @@ +// Copyright github.com/juju2013. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package models + +import ( + "strings" + + "github.com/gogits/gogs/modules/auth/ldap" + "github.com/gogits/gogs/modules/log" +) + +// Query if name/passwd can login against the LDAP direcotry pool +// Create a local user if success +// Return the same LoginUserPlain semantic +func LoginUserLdap(name, passwd string) (*User, error) { + mail, logged := ldap.LoginUser(name, passwd) + if !logged { + // user not in LDAP, do nothing + return nil, ErrUserNotExist + } + // fake a local user creation + user := User{ + LowerName: strings.ToLower(name), + Name: strings.ToLower(name), + LoginType: 389, + IsActive: true, + Passwd: passwd, + Email: mail} + _, err := RegisterUser(&user) + if err != nil { + log.Debug("LDAP local user %s fond (%s) ", name, err) + } + // simulate local user login + localUser, err2 := GetUserByName(user.Name) + return localUser, err2 +} diff --git a/models/publickey.go b/models/publickey.go index ed47ff209..b80412812 100644 --- a/models/publickey.go +++ b/models/publickey.go @@ -77,12 +77,12 @@ func init() { // PublicKey represents a SSH key of user. type PublicKey struct { Id int64 - OwnerId int64 `xorm:"unique(s) index not null"` - Name string `xorm:"unique(s) not null"` + OwnerId int64 `xorm:"UNIQUE(s) INDEX NOT NULL"` + Name string `xorm:"UNIQUE(s) NOT NULL"` Fingerprint string - Content string `xorm:"TEXT not null"` - Created time.Time `xorm:"created"` - Updated time.Time `xorm:"updated"` + Content string `xorm:"TEXT NOT NULL"` + Created time.Time `xorm:"CREATED"` + Updated time.Time `xorm:"UPDATED"` } // GenAuthorizedKey returns formatted public key string. @@ -107,9 +107,9 @@ func AddPublicKey(key *PublicKey) (err error) { if err = ioutil.WriteFile(tmpPath, []byte(key.Content), os.ModePerm); err != nil { return err } - stdout, _, err := com.ExecCmd("ssh-keygen", "-l", "-f", tmpPath) + stdout, stderr, err := com.ExecCmd("ssh-keygen", "-l", "-f", tmpPath) if err != nil { - return err + return errors.New("ssh-keygen -l -f: " + stderr) } else if len(stdout) < 2 { return errors.New("Not enough output for calculating fingerprint") } diff --git a/models/repo.go b/models/repo.go index 19fe6e288..5f66bca86 100644 --- a/models/repo.go +++ b/models/repo.go @@ -47,16 +47,16 @@ func NewRepoContext() { zip.Verbose = false // Check if server has basic git setting. - stdout, _, err := com.ExecCmd("git", "config", "--get", "user.name") - if err != nil { - fmt.Printf("repo.init(fail to get git user.name): %v", err) + stdout, stderr, err := com.ExecCmd("git", "config", "--get", "user.name") + if strings.Contains(stderr, "fatal:") { + fmt.Printf("repo.NewRepoContext(fail to get git user.name): %s", stderr) os.Exit(2) - } else if len(stdout) == 0 { - if _, _, err = com.ExecCmd("git", "config", "--global", "user.email", "gogitservice@gmail.com"); err != nil { - fmt.Printf("repo.init(fail to set git user.email): %v", err) + } else if err != nil || len(strings.TrimSpace(stdout)) == 0 { + if _, stderr, err = com.ExecCmd("git", "config", "--global", "user.email", "gogitservice@gmail.com"); err != nil { + fmt.Printf("repo.NewRepoContext(fail to set git user.email): %s", stderr) os.Exit(2) - } else if _, _, err = com.ExecCmd("git", "config", "--global", "user.name", "Gogs"); err != nil { - fmt.Printf("repo.init(fail to set git user.name): %v", err) + } else if _, stderr, err = com.ExecCmd("git", "config", "--global", "user.name", "Gogs"); err != nil { + fmt.Printf("repo.NewRepoContext(fail to set git user.name): %s", stderr) os.Exit(2) } } @@ -159,9 +159,7 @@ func MirrorUpdate() { repoPath := filepath.Join(base.RepoRootPath, m.RepoName+".git") _, stderr, err := com.ExecCmdDir(repoPath, "git", "remote", "update") if err != nil { - return err - } else if strings.Contains(stderr, "fatal:") { - return errors.New(stderr) + return errors.New("git remote update: " + stderr) } else if err = git.UnpackRefs(repoPath); err != nil { return err } @@ -177,9 +175,7 @@ func MirrorUpdate() { func MirrorRepository(repoId int64, userName, repoName, repoPath, url string) error { _, stderr, err := com.ExecCmd("git", "clone", "--mirror", url, repoPath) if err != nil { - return err - } else if strings.Contains(stderr, "fatal:") { - return errors.New(stderr) + return errors.New("git clone --mirror: " + stderr) } if _, err = orm.InsertOne(&Mirror{ @@ -219,23 +215,17 @@ func MigrateRepository(user *User, name, desc string, private, mirror bool, url // Clone from local repository. _, stderr, err := com.ExecCmd("git", "clone", repoPath, tmpDir) if err != nil { - return repo, err - } else if strings.Contains(stderr, "fatal:") { return repo, errors.New("git clone: " + stderr) } // Pull data from source. _, stderr, err = com.ExecCmdDir(tmpDir, "git", "pull", url) if err != nil { - return repo, err - } else if strings.Contains(stderr, "fatal:") { return repo, errors.New("git pull: " + stderr) } // Push data to local repository. if _, stderr, err = com.ExecCmdDir(tmpDir, "git", "push", "origin", "master"); err != nil { - return repo, err - } else if strings.Contains(stderr, "fatal:") { return repo, errors.New("git push: " + stderr) } @@ -352,7 +342,6 @@ func CreateRepository(user *User, name, desc, lang, license string, private, mir func extractGitBareZip(repoPath string) error { z, err := zip.Open("conf/content/git-bare.zip") if err != nil { - fmt.Println("shi?") return err } defer z.Close() @@ -364,21 +353,14 @@ func extractGitBareZip(repoPath string) error { func initRepoCommit(tmpPath string, sig *git.Signature) (err error) { var stderr string if _, stderr, err = com.ExecCmdDir(tmpPath, "git", "add", "--all"); err != nil { - return err - } else if strings.Contains(stderr, "fatal:") { return errors.New("git add: " + stderr) } - if _, stderr, err = com.ExecCmdDir(tmpPath, "git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", "Init commit"); err != nil { - return err - } else if strings.Contains(stderr, "fatal:") { return errors.New("git commit: " + stderr) } if _, stderr, err = com.ExecCmdDir(tmpPath, "git", "push", "origin", "master"); err != nil { - return err - } else if strings.Contains(stderr, "fatal:") { return errors.New("git push: " + stderr) } return nil @@ -411,10 +393,11 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep return err } + rp := strings.NewReplacer("\\", "/", " ", "\\ ") // hook/post-update if err := createHookUpdate(filepath.Join(repoPath, "hooks", "update"), fmt.Sprintf("#!/usr/bin/env %s\n%s update $1 $2 $3\n", base.ScriptType, - strings.Replace(appPath, "\\", "/", -1))); err != nil { + rp.Replace(appPath))); err != nil { return err } @@ -436,8 +419,6 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep _, stderr, err := com.ExecCmd("git", "clone", repoPath, tmpDir) if err != nil { - return err - } else if strings.Contains(stderr, "fatal:") { return errors.New("git clone: " + stderr) } diff --git a/models/update.go b/models/update.go index 2f59547b7..648c45f16 100644 --- a/models/update.go +++ b/models/update.go @@ -1,3 +1,7 @@ +// 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 models import ( @@ -5,9 +9,11 @@ import ( "os/exec" "strings" + qlog "github.com/qiniu/log" + "github.com/gogits/git" + "github.com/gogits/gogs/modules/base" - qlog "github.com/qiniu/log" ) func Update(refName, oldCommitId, newCommitId, userName, repoName string, userId int64) { diff --git a/models/user.go b/models/user.go index ab43df7a1..661eff2fd 100644 --- a/models/user.go +++ b/models/user.go @@ -125,6 +125,7 @@ func GetUserSalt() string { // RegisterUser creates record of a new user. func RegisterUser(user *User) (*User, error) { + if !IsLegalName(user.Name) { return nil, ErrUserNameIllegal } @@ -409,21 +410,27 @@ func GetUserByEmail(email string) (*User, error) { } // LoginUserPlain validates user by raw user name and password. -func LoginUserPlain(name, passwd string) (*User, error) { - user := User{LowerName: strings.ToLower(name)} - has, err := orm.Get(&user) +func LoginUserPlain(uname, passwd string) (*User, error) { + var u *User + if strings.Contains(uname, "@") { + u = &User{Email: uname} + } else { + u = &User{LowerName: strings.ToLower(uname)} + } + + has, err := orm.Get(u) if err != nil { return nil, err } else if !has { return nil, ErrUserNotExist } - newUser := &User{Passwd: passwd, Salt: user.Salt} + newUser := &User{Passwd: passwd, Salt: u.Salt} newUser.EncodePasswd() - if user.Passwd != newUser.Passwd { + if u.Passwd != newUser.Passwd { return nil, ErrUserNotExist } - return &user, nil + return u, nil } // Follow is connection request for receiving user notifycation. diff --git a/modules/auth/auth.go b/modules/auth/auth.go index 350ef4fcb..e493faefe 100644 --- a/modules/auth/auth.go +++ b/modules/auth/auth.go @@ -57,7 +57,7 @@ func (f *RegisterForm) Validate(errors *base.BindingErrors, req *http.Request, c } type LogInForm struct { - UserName string `form:"username" binding:"Required;AlphaDash;MaxSize(30)"` + UserName string `form:"username" binding:"Required;MaxSize(35)"` Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"` Remember string `form:"remember"` } diff --git a/modules/auth/ldap/README.md b/modules/auth/ldap/README.md new file mode 100644 index 000000000..8b508e0fe --- /dev/null +++ b/modules/auth/ldap/README.md @@ -0,0 +1,43 @@ +LDAP authentication +=================== + +## Goal + +Authenticat user against LDAP directories + +It will bind with the user's login/pasword and query attributs ("mail" for instance) in a pool of directory servers + +The first OK wins. + +If there's connection error, the server will be disabled and won't be checked again + +## Usage + +In the [security] section, set +> LDAP_AUTH = true + +then for each LDAP source, set + +> [LdapSource-someuniquename] +> name=canonicalName +> host=hostname-or-ip +> port=3268 # or regular LDAP port +> # the following settings depend highly how you've configured your AD +> basedn=dc=ACME,dc=COM +> MSADSAFORMAT=%s@ACME.COM +> filter=(&(objectClass=user)(sAMAccountName=%s)) + +### Limitation + +Only tested on an MS 2008R2 DC, using global catalog (TCP/3268) + +This MSAD is a mess. + +The way how one checks the directory (CN, DN etc...) may be highly depending local custom configuration + +### Todo +* Define a timeout per server +* Check servers marked as "Disabled" when they'll come back online +* Find a more flexible way to define filter/MSADSAFORMAT/Attributes etc... maybe text/template ? +* Check OpenLDAP server +* SSL support ? \ No newline at end of file diff --git a/modules/auth/ldap/ldap.go b/modules/auth/ldap/ldap.go new file mode 100644 index 000000000..29773cda5 --- /dev/null +++ b/modules/auth/ldap/ldap.go @@ -0,0 +1,86 @@ +// Copyright github.com/juju2013. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +// package ldap provide functions & structure to query a LDAP ldap directory +// For now, it's mainly tested again an MS Active Directory service, see README.md for more information +package ldap + +import ( + "fmt" + "github.com/gogits/gogs/modules/log" + goldap "github.com/juju2013/goldap" +) + +// Basic LDAP authentication service +type ldapsource struct { + Name string // canonical name (ie. corporate.ad) + Host string // LDAP host + Port int // port number + BaseDN string // Base DN + Attributes string // Attribut to search + Filter string // Query filter to validate entry + MsAdSAFormat string // in the case of MS AD Simple Authen, the format to use (see: http://msdn.microsoft.com/en-us/library/cc223499.aspx) + Enabled bool // if this source is disabled +} + +//Global LDAP directory pool +var ( + Authensource []ldapsource +) + +// Add a new source (LDAP directory) to the global pool +func AddSource(name string, host string, port int, basedn string, attributes string, filter string, msadsaformat string) { + ldaphost := ldapsource{name, host, port, basedn, attributes, filter, msadsaformat, true} + Authensource = append(Authensource, ldaphost) +} + +//LoginUser : try to login an user to LDAP sources, return requested (attribut,true) if ok, ("",false) other wise +//First match wins +//Returns first attribute if exists +func LoginUser(name, passwd string) (a string, r bool) { + r = false + for _, ls := range Authensource { + a, r = ls.searchEntry(name, passwd) + if r { + return + } + } + return +} + +// searchEntry : search an LDAP source if an entry (name, passwd) is valide and in the specific filter +func (ls ldapsource) searchEntry(name, passwd string) (string, bool) { + l, err := goldap.Dial("tcp", fmt.Sprintf("%s:%d", ls.Host, ls.Port)) + if err != nil { + log.Debug("LDAP Connect error, disabled source %s", ls.Host) + ls.Enabled = false + return "", false + } + defer l.Close() + + nx := fmt.Sprintf(ls.MsAdSAFormat, name) + err = l.Bind(nx, passwd) + if err != nil { + log.Debug("LDAP Authan failed for %s, reason: %s", nx, err.Error()) + return "", false + } + + search := goldap.NewSearchRequest( + ls.BaseDN, + goldap.ScopeWholeSubtree, goldap.NeverDerefAliases, 0, 0, false, + fmt.Sprintf(ls.Filter, name), + []string{ls.Attributes}, + nil) + sr, err := l.Search(search) + if err != nil { + log.Debug("LDAP Authen OK but not in filter %s", name) + return "", false + } + log.Debug("LDAP Authen OK: %s", name) + if len(sr.Entries) > 0 { + r := sr.Entries[0].GetAttributeValue(ls.Attributes) + return r, true + } + return "", true +} diff --git a/modules/base/base_redis.go b/modules/base/base_redis.go index 327af8415..42430df77 100644 --- a/modules/base/base_redis.go +++ b/modules/base/base_redis.go @@ -4,6 +4,7 @@ package base import ( _ "github.com/gogits/cache/redis" + _ "github.com/gogits/session/redis" ) func init() { diff --git a/modules/base/conf.go b/modules/base/conf.go index abb67f6d8..cfc85ff51 100644 --- a/modules/base/conf.go +++ b/modules/base/conf.go @@ -10,6 +10,7 @@ import ( "os/exec" "path" "path/filepath" + "regexp" "strings" "github.com/Unknwon/com" @@ -19,6 +20,7 @@ import ( "github.com/gogits/cache" "github.com/gogits/session" + "github.com/gogits/gogs/modules/auth/ldap" "github.com/gogits/gogs/modules/log" ) @@ -43,14 +45,15 @@ type Oauther struct { } var ( - AppVer string - AppName string - AppLogo string - AppUrl string - IsProdMode bool - Domain string - SecretKey string - RunUser string + AppVer string + AppName string + AppLogo string + AppUrl string + OfflineMode bool + ProdMode bool + Domain string + SecretKey string + RunUser string RepoRootPath string ScriptType string @@ -83,13 +86,14 @@ var ( ) var Service struct { - RegisterEmailConfirm bool - DisableRegistration bool - RequireSignInView bool - EnableCacheAvatar bool - NotifyMail bool - ActiveCodeLives int - ResetPwdCodeLives int + RegisterEmailConfirm bool + DisableRegistration bool + RequireSignInView bool + EnableCacheAvatar bool + NotifyMail bool + ActiveCodeLives int + ResetPwdCodeLives int + LdapAuth bool } func ExecDir() (string, error) { @@ -175,6 +179,36 @@ func newLogService() { log.Info("Log Mode: %s(%s)", strings.Title(LogMode), levelName) } +func newLdapService() { + Service.LdapAuth = Cfg.MustBool("security", "LDAP_AUTH", false) + if !Service.LdapAuth { + return + } + + nbsrc := 0 + for _, v := range Cfg.GetSectionList() { + if matched, _ := regexp.MatchString("(?i)^LDAPSOURCE.*", v); matched { + ldapname := Cfg.MustValue(v, "name", v) + ldaphost := Cfg.MustValue(v, "host") + ldapport := Cfg.MustInt(v, "port", 389) + ldapbasedn := Cfg.MustValue(v, "basedn", "dc=*,dc=*") + ldapattribute := Cfg.MustValue(v, "attribute", "mail") + ldapfilter := Cfg.MustValue(v, "filter", "(*)") + ldapmsadsaformat := Cfg.MustValue(v, "MSADSAFORMAT", "%s") + ldap.AddSource(ldapname, ldaphost, ldapport, ldapbasedn, ldapattribute, ldapfilter, ldapmsadsaformat) + nbsrc++ + log.Debug("%s added as LDAP source", ldapname) + } + } + if nbsrc == 0 { + log.Warn("No valide LDAP found, LDAP Authentication NOT enabled") + Service.LdapAuth = false + return + } + + log.Info("LDAP Authentication Enabled") +} + func newCacheService() { CacheAdapter = Cfg.MustValue("cache", "ADAPTER", "memory") if EnableRedis { @@ -292,6 +326,7 @@ func NewConfigContext() { AppLogo = Cfg.MustValue("", "APP_LOGO", "img/favicon.png") AppUrl = Cfg.MustValue("server", "ROOT_URL") Domain = Cfg.MustValue("server", "DOMAIN") + OfflineMode = Cfg.MustBool("server", "OFFLINE_MODE", false) SecretKey = Cfg.MustValue("security", "SECRET_KEY") InstallLock = Cfg.MustBool("security", "INSTALL_LOCK", false) @@ -309,7 +344,6 @@ func NewConfigContext() { LogInRememberDays = Cfg.MustInt("security", "LOGIN_REMEMBER_DAYS") CookieUserName = Cfg.MustValue("security", "COOKIE_USERNAME") CookieRememberName = Cfg.MustValue("security", "COOKIE_REMEMBER_NAME") - PictureService = Cfg.MustValue("picture", "SERVICE") // Determine and create root git reposiroty path. @@ -327,6 +361,7 @@ func NewConfigContext() { func NewBaseServices() { newService() newLogService() + newLdapService() newCacheService() newSessionService() newMailService() diff --git a/modules/base/template.go b/modules/base/template.go index 79aeeb9d7..dd98df75b 100644 --- a/modules/base/template.go +++ b/modules/base/template.go @@ -56,8 +56,8 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{ "AppDomain": func() string { return Domain }, - "IsProdMode": func() bool { - return IsProdMode + "CdnMode": func() bool { + return ProdMode && !OfflineMode }, "LoadTimes": func(startTime time.Time) string { return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms" @@ -124,11 +124,11 @@ func ActionIcon(opType int) string { const ( TPL_CREATE_REPO = `%s created repository %s` TPL_COMMIT_REPO = `%s pushed to %s at %s%s` - TPL_COMMIT_REPO_LI = `
user-avatar %s %s
` + TPL_COMMIT_REPO_LI = `
user-avatar %s %s
` TPL_CREATE_ISSUE = `%s opened issue %s#%s
user-avatar %s
` TPL_TRANSFER_REPO = `%s transfered repository %s to %s` - TPL_PUSH_TAG = `%s pushed tag %s at %s` + TPL_PUSH_TAG = `%s pushed tag %s at %s` ) type PushCommit struct { @@ -165,7 +165,7 @@ func ActionDesc(act Actioner) string { buf.WriteString(fmt.Sprintf(TPL_COMMIT_REPO_LI, AvatarLink(commit.AuthorEmail), repoLink, commit.Sha1, commit.Sha1[:7], commit.Message) + "\n") } if push.Len > 3 { - buf.WriteString(fmt.Sprintf(`
%d other commits >>
`, actUserName, repoName, branch, push.Len)) + buf.WriteString(fmt.Sprintf(`
%d other commits >>
`, actUserName, repoName, branch, push.Len)) } return fmt.Sprintf(TPL_COMMIT_REPO, actUserName, actUserName, repoLink, branch, branch, repoLink, repoLink, buf.String()) diff --git a/modules/middleware/repo.go b/modules/middleware/repo.go index 34144fe3d..2d2778cb0 100644 --- a/modules/middleware/repo.go +++ b/modules/middleware/repo.go @@ -26,11 +26,14 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { var displayBare bool if len(args) >= 1 { - validBranch = args[0] + // Note: argument has wrong value in Go1.3 martini. + // validBranch = args[0] + validBranch = true } if len(args) >= 2 { - displayBare = args[1] + // displayBare = args[1] + displayBare = true } var ( diff --git a/public/img/favicon.bak.png b/public/img/favicon.bak.png new file mode 100644 index 000000000..ba9bd0375 Binary files /dev/null and b/public/img/favicon.bak.png differ diff --git a/public/img/favicon.png b/public/img/favicon.png index ba9bd0375..87282f9a7 100644 Binary files a/public/img/favicon.png and b/public/img/favicon.png differ diff --git a/public/js/app.js b/public/js/app.js index a5c79a398..b7b5deb83 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -470,10 +470,10 @@ function initInstall() { (function () { $('#install-database').on("change", function () { var val = $(this).val(); - if (val != "sqlite") { + if (val != "SQLite3") { $('.server-sql').show(); $('.sqlite-setting').addClass("hide"); - if (val == "pgsql") { + if (val == "PostgreSQL") { $('.pgsql-setting').removeClass("hide"); } else { $('.pgsql-setting').addClass("hide"); diff --git a/routers/admin/admin.go b/routers/admin/admin.go index d0f737e64..fddd83018 100644 --- a/routers/admin/admin.go +++ b/routers/admin/admin.go @@ -139,9 +139,11 @@ func Config(ctx *middleware.Context) { ctx.Data["AppUrl"] = base.AppUrl ctx.Data["Domain"] = base.Domain + ctx.Data["OfflineMode"] = base.OfflineMode ctx.Data["RunUser"] = base.RunUser ctx.Data["RunMode"] = strings.Title(martini.Env) ctx.Data["RepoRootPath"] = base.RepoRootPath + ctx.Data["ScriptType"] = base.ScriptType ctx.Data["Service"] = base.Service diff --git a/routers/install.go b/routers/install.go index 12182ad30..38bf896f4 100644 --- a/routers/install.go +++ b/routers/install.go @@ -30,7 +30,7 @@ func checkRunMode() { switch base.Cfg.MustValue("", "RUN_MODE") { case "prod": martini.Env = martini.Prod - base.IsProdMode = true + base.ProdMode = true case "test": martini.Env = martini.Test } @@ -65,6 +65,10 @@ func GlobalInit() { checkRunMode() } +func renderDbOption(ctx *middleware.Context) { + ctx.Data["DbOptions"] = []string{"MySQL", "PostgreSQL", "SQLite3"} +} + func Install(ctx *middleware.Context, form auth.InstallForm) { if base.InstallLock { ctx.Handle(404, "install.Install", errors.New("Installation is prohibited")) @@ -104,6 +108,13 @@ func Install(ctx *middleware.Context, form auth.InstallForm) { form.AppUrl = base.AppUrl } + renderDbOption(ctx) + curDbValue := "" + if models.EnableSQLite3 { + curDbValue = "SQLite3" // Default when enabled. + } + ctx.Data["CurDbValue"] = curDbValue + auth.AssignForm(form, ctx.Data) ctx.HTML(200, "install") } @@ -117,6 +128,9 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) { ctx.Data["Title"] = "Install" ctx.Data["PageIsInstall"] = true + renderDbOption(ctx) + ctx.Data["CurDbValue"] = form.Database + if ctx.HasError() { ctx.HTML(200, "install") return @@ -129,7 +143,7 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) { // Pass basic check, now test configuration. // Test database setting. - dbTypes := map[string]string{"mysql": "mysql", "pgsql": "postgres", "sqlite": "sqlite3"} + dbTypes := map[string]string{"MySQL": "mysql", "PostgreSQL": "postgres", "SQLite3": "sqlite3"} models.DbCfg.Type = dbTypes[form.Database] models.DbCfg.Host = form.Host models.DbCfg.User = form.User diff --git a/routers/repo/commit.go b/routers/repo/commit.go index 6e20a7b7f..bc33fe447 100644 --- a/routers/repo/commit.go +++ b/routers/repo/commit.go @@ -91,10 +91,23 @@ func Diff(ctx *middleware.Context, params martini.Params) { return isImage } + parents := make([]string, commit.ParentCount()) + for i := 0; i < commit.ParentCount(); i++ { + sha, err := commit.ParentId(i) + parents[i] = sha.String() + if err != nil { + ctx.Handle(404, "repo.Diff", err) + return + } + } + + ctx.Data["Username"] = userName + ctx.Data["Reponame"] = repoName ctx.Data["IsImageFile"] = isImageFile - ctx.Data["Title"] = commit.Message() + " · " + base.ShortSha(commitId) + ctx.Data["Title"] = commit.Summary() + " · " + base.ShortSha(commitId) ctx.Data["Commit"] = commit ctx.Data["Diff"] = diff + ctx.Data["Parents"] = parents ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0 ctx.Data["IsRepoToolbarCommits"] = true ctx.Data["SourcePath"] = "/" + path.Join(userName, repoName, "src", commitId) diff --git a/routers/repo/release.go b/routers/repo/release.go index a4baa4792..b386bac80 100644 --- a/routers/repo/release.go +++ b/routers/repo/release.go @@ -87,7 +87,6 @@ func Releases(ctx *middleware.Context) { return } tags.rels[i].NumCommitsBehind = commitsCount - tags.rels[i].NumCommits - tags.rels[i].Created = commit.Author.When } } diff --git a/routers/repo/repo.go b/routers/repo/repo.go index f733378b3..76964dff1 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -134,7 +134,6 @@ func Single(ctx *middleware.Context, params martini.Params) { } entry, err := ctx.Repo.Commit.GetTreeEntryByPath(treename) - if err != nil && err != git.ErrNotExist { ctx.Handle(404, "repo.Single(GetTreeEntryByPath)", err) return diff --git a/routers/user/setting.go b/routers/user/setting.go index a8fdc116c..a55e617f4 100644 --- a/routers/user/setting.go +++ b/routers/user/setting.go @@ -66,7 +66,7 @@ func SettingPost(ctx *middleware.Context, form auth.UpdateProfileForm) { log.Trace("%s User setting updated: %s", ctx.Req.RequestURI, ctx.User.LowerName) ctx.Flash.Success("Your profile has been successfully updated.") - ctx.Redirect("/user/setting") + ctx.Redirect("/user/settings") } func SettingSocial(ctx *middleware.Context) { @@ -122,7 +122,7 @@ func SettingPasswordPost(ctx *middleware.Context, form auth.UpdatePasswdForm) { ctx.Flash.Success("Password is changed successfully. You can now sign in via new password.") } - ctx.Redirect("/user/setting/password") + ctx.Redirect("/user/settings/password") } func SettingSSHKeys(ctx *middleware.Context, form auth.AddSSHKeyForm) { @@ -166,7 +166,8 @@ func SettingSSHKeys(ctx *middleware.Context, form auth.AddSSHKeyForm) { return } - k := &models.PublicKey{OwnerId: ctx.User.Id, + k := &models.PublicKey{ + OwnerId: ctx.User.Id, Name: form.KeyName, Content: form.KeyContent, } @@ -181,7 +182,7 @@ func SettingSSHKeys(ctx *middleware.Context, form auth.AddSSHKeyForm) { } else { log.Trace("%s User SSH key added: %s", ctx.Req.RequestURI, ctx.User.LowerName) ctx.Flash.Success("New SSH Key has been added!") - ctx.Redirect("/user/setting/ssh") + ctx.Redirect("/user/settings/ssh") return } } diff --git a/routers/user/user.go b/routers/user/user.go index 7decd72d4..9cce4e719 100644 --- a/routers/user/user.go +++ b/routers/user/user.go @@ -89,7 +89,18 @@ func SignInPost(ctx *middleware.Context, form auth.LogInForm) { return } - user, err := models.LoginUserPlain(form.UserName, form.Password) + var user *models.User + var err error + if base.Service.LdapAuth { + user, err = models.LoginUserLdap(form.UserName, form.Password) + if err != nil { + log.Error("Fail to login through LDAP: %v", err) + } + } + // try local if not LDAP or it's failed + if !base.Service.LdapAuth || err != nil { + user, err = models.LoginUserPlain(form.UserName, form.Password) + } if err != nil { if err == models.ErrUserNotExist { log.Trace("%s Log in failed: %s/%s", ctx.Req.RequestURI, form.UserName, form.Password) @@ -133,27 +144,6 @@ func SignInPost(ctx *middleware.Context, form auth.LogInForm) { ctx.Redirect("/") } -func oauthSignInPost(ctx *middleware.Context, sid int64) { - ctx.Data["Title"] = "OAuth Sign Up" - ctx.Data["PageIsSignUp"] = true - - if _, err := models.GetOauth2ById(sid); err != nil { - if err == models.ErrOauth2RecordNotExist { - ctx.Handle(404, "user.oauthSignUp(GetOauth2ById)", err) - } else { - ctx.Handle(500, "user.oauthSignUp(GetOauth2ById)", err) - } - return - } - - ctx.Data["IsSocialLogin"] = true - ctx.Data["username"] = ctx.Session.Get("socialName") - ctx.Data["email"] = ctx.Session.Get("socialEmail") - log.Trace("user.oauthSignUp(social ID): %v", ctx.Session.Get("socialId")) - - ctx.HTML(200, "user/signup") -} - func SignOut(ctx *middleware.Context) { ctx.Session.Delete("userId") ctx.Session.Delete("userName") diff --git a/serve.go b/serve.go index 321227957..e3197a23d 100644 --- a/serve.go +++ b/serve.go @@ -53,6 +53,7 @@ func newLogger(execDir string) { } qlog.SetOutput(f) + //qlog.SetOutputLevel(qlog.Ldebug) qlog.Info("Start logging serv...") } @@ -159,7 +160,7 @@ func runServ(k *cli.Context) { qlog.Fatal(err) } if !has { - has, err = models.HasAccess(user.Name, repoPath, models.AU_WRITABLE) + has, err = models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.AU_WRITABLE) if err != nil { println("Internal error") qlog.Fatal(err) diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl index d25d40275..b2b25e90f 100644 --- a/templates/admin/config.tmpl +++ b/templates/admin/config.tmpl @@ -18,6 +18,8 @@
{{.AppUrl}}
Domain
{{.Domain}}
+
Offline Mode
+

Run User
{{.RunUser}}
@@ -26,6 +28,8 @@
Repository Root Path
{{.RepoRootPath}}
+
Script Type
+
{{.ScriptType}}
diff --git a/templates/base/footer.tmpl b/templates/base/footer.tmpl index 6c2da63e5..30f068913 100644 --- a/templates/base/footer.tmpl +++ b/templates/base/footer.tmpl @@ -13,7 +13,10 @@
-

+ +
+

+
diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl index 68231391c..6794b0171 100644 --- a/templates/base/head.tmpl +++ b/templates/base/head.tmpl @@ -12,7 +12,7 @@ {{if .Repository.IsGoget}}{{end}} - {{if IsProdMode}} + {{if CdnMode}} diff --git a/templates/base/navbar.tmpl b/templates/base/navbar.tmpl index e5b22192f..b8cba5faa 100644 --- a/templates/base/navbar.tmpl +++ b/templates/base/navbar.tmpl @@ -3,7 +3,7 @@