@@ -1,6 +1,6 @@ | |||
[run] | |||
init_cmds = [ | |||
["grep", "-rn", "FIXME", "."], | |||
#["grep", "-rn", "FIXME", "."], | |||
["./gogs", "web"] | |||
] | |||
watch_all = true | |||
@@ -7,13 +7,13 @@ Gogs (Go Git Service) is a painless self-hosted Git service written in Go. | |||
 | |||
##### Current version: 0.5.13 Beta | |||
##### Current version: 0.5.16 Beta | |||
### NOTICES | |||
- Due to testing purpose, data of [try.gogs.io](https://try.gogs.io) has been reset in **Jan 28, 2015** and will reset multiple times after. Please do **NOT** put your important data on the site. | |||
- The demo site [try.gogs.io](https://try.gogs.io) is running under `dev` branch. | |||
- You **MUST** read [CONTRIBUTING.md](CONTRIBUTING.md) before you start filing a issue or making a Pull Request. | |||
- You **MUST** read [CONTRIBUTING.md](CONTRIBUTING.md) before you start filing an issue or making a Pull Request. | |||
- If you think there are vulnerabilities in the project, please talk privately to **u@gogs.io**. Thanks! | |||
#### Other language version | |||
@@ -57,7 +57,7 @@ The goal of this project is to make the easiest, fastest, and most painless way | |||
## System Requirements | |||
- A cheap Raspberry Pi is powerful enough for basic functionality. | |||
- At least 4 CPU cores and 1GB RAM would be the baseline for teamwork. | |||
- At least 2 CPU cores and 1GB RAM would be the baseline for teamwork. | |||
## Installation | |||
@@ -5,7 +5,7 @@ Gogs(Go Git Service) 是一个基于 Go 语言的自助 Git 服务。 | |||
 | |||
##### 当前版本:0.5.13 Beta | |||
##### 当前版本:0.5.16 Beta | |||
## 开发目的 | |||
@@ -44,7 +44,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自 | |||
## 系统要求 | |||
- 最低的系统硬件要求为一个廉价的树莓派 | |||
- 如果用于团队项目,建议使用 4 核 CPU 及 1GB 内存 | |||
- 如果用于团队项目,建议使用 2 核 CPU 及 1GB 内存 | |||
## 安装部署 | |||
@@ -75,4 +75,4 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自 | |||
## 授权许可 | |||
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/gogits/gogs/blob/master/LICENSE) 文件中。 | |||
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/gogits/gogs/blob/master/LICENSE) 文件中。 |
@@ -8,7 +8,6 @@ import ( | |||
"fmt" | |||
"os" | |||
"os/exec" | |||
"path" | |||
"path/filepath" | |||
"strings" | |||
"time" | |||
@@ -43,7 +42,7 @@ func setup(logPath string) { | |||
models.LoadModelsConfig() | |||
if models.UseSQLite3 { | |||
if setting.UseSQLite3 { | |||
workDir, _ := setting.WorkDir() | |||
os.Chdir(workDir) | |||
} | |||
@@ -67,33 +66,33 @@ func parseCmd(cmd string) (string, string) { | |||
} | |||
var ( | |||
COMMANDS_READONLY = map[string]models.AccessType{ | |||
"git-upload-pack": models.WRITABLE, | |||
"git upload-pack": models.WRITABLE, | |||
"git-upload-archive": models.WRITABLE, | |||
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.AccessType{ | |||
"git-receive-pack": models.READABLE, | |||
"git receive-pack": models.READABLE, | |||
COMMANDS_WRITE = map[string]models.AccessMode{ | |||
"git-receive-pack": models.ACCESS_MODE_READ, | |||
"git receive-pack": models.ACCESS_MODE_READ, | |||
} | |||
) | |||
func In(b string, sl map[string]models.AccessType) bool { | |||
func In(b string, sl map[string]models.AccessMode) bool { | |||
_, e := sl[b] | |||
return e | |||
} | |||
func runServ(k *cli.Context) { | |||
if k.IsSet("config") { | |||
setting.CustomConf = k.String("config") | |||
func runServ(c *cli.Context) { | |||
if c.IsSet("config") { | |||
setting.CustomConf = c.String("config") | |||
} | |||
setup("serv.log") | |||
if len(k.Args()) < 1 { | |||
if len(c.Args()) < 1 { | |||
log.GitLogger.Fatal(2, "Not enough arguments") | |||
} | |||
keys := strings.Split(k.Args()[0], "-") | |||
keys := strings.Split(c.Args()[0], "-") | |||
if len(keys) != 2 { | |||
println("Gogs: auth file format error") | |||
log.GitLogger.Fatal(2, "Invalid auth file format: %s", os.Args[2]) | |||
@@ -117,6 +116,7 @@ func runServ(k *cli.Context) { | |||
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.") | |||
return | |||
} | |||
@@ -144,9 +144,19 @@ func runServ(k *cli.Context) { | |||
} | |||
// 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) | |||
} | |||
switch { | |||
case isWrite: | |||
has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.WRITABLE) | |||
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) | |||
@@ -154,22 +164,17 @@ func runServ(k *cli.Context) { | |||
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) | |||
} | |||
case isRead: | |||
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) | |||
} | |||
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 | |||
} | |||
has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.READABLE) | |||
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) | |||
@@ -318,7 +318,7 @@ func runWeb(ctx *cli.Context) { | |||
m.Get("/template/*", dev.TemplatePreview) | |||
} | |||
reqTrueOwner := middleware.RequireTrueOwner() | |||
reqAdmin := middleware.RequireAdmin() | |||
// Organization. | |||
m.Group("/org", func() { | |||
@@ -393,7 +393,7 @@ func runWeb(ctx *cli.Context) { | |||
m.Post("/:name", repo.GitHooksEditPost) | |||
}, middleware.GitHookService()) | |||
}) | |||
}, reqSignIn, middleware.RepoAssignment(true), reqTrueOwner) | |||
}, reqSignIn, middleware.RepoAssignment(true), reqAdmin) | |||
m.Group("/:username/:reponame", func() { | |||
m.Get("/action/:action", repo.Action) | |||
@@ -17,7 +17,7 @@ import ( | |||
"github.com/gogits/gogs/modules/setting" | |||
) | |||
const APP_VER = "0.5.14.0222 Beta" | |||
const APP_VER = "0.5.16.0228 Beta" | |||
func init() { | |||
runtime.GOMAXPROCS(runtime.NumCPU()) | |||
@@ -5,76 +5,190 @@ | |||
package models | |||
import ( | |||
"strings" | |||
"time" | |||
"github.com/go-xorm/xorm" | |||
"fmt" | |||
) | |||
type AccessType int | |||
type AccessMode int | |||
const ( | |||
READABLE AccessType = iota + 1 | |||
WRITABLE | |||
ACCESS_MODE_NONE AccessMode = iota | |||
ACCESS_MODE_READ | |||
ACCESS_MODE_WRITE | |||
ACCESS_MODE_ADMIN | |||
ACCESS_MODE_OWNER | |||
) | |||
// Access represents the accessibility of user to repository. | |||
// Access represents the highest access level of a user to the repository. The only access type | |||
// that is not in this table is the real owner of a repository. In case of an organization | |||
// repository, the members of the owners team are in this table. | |||
type Access struct { | |||
Id int64 | |||
UserName string `xorm:"UNIQUE(s)"` | |||
RepoName string `xorm:"UNIQUE(s)"` // <user name>/<repo name> | |||
Mode AccessType `xorm:"UNIQUE(s)"` | |||
Created time.Time `xorm:"CREATED"` | |||
ID int64 `xorm:"pk autoincr"` | |||
UserID int64 `xorm:"UNIQUE(s)"` | |||
RepoID int64 `xorm:"UNIQUE(s)"` | |||
Mode AccessMode | |||
} | |||
func accessLevel(e Engine, u *User, repo *Repository) (AccessMode, error) { | |||
mode := ACCESS_MODE_NONE | |||
if !repo.IsPrivate { | |||
mode = ACCESS_MODE_READ | |||
} | |||
if u != nil { | |||
if u.Id == repo.OwnerId { | |||
return ACCESS_MODE_OWNER, nil | |||
} | |||
a := &Access{UserID: u.Id, RepoID: repo.Id} | |||
if has, err := e.Get(a); !has || err != nil { | |||
return mode, err | |||
} | |||
return a.Mode, nil | |||
} | |||
return mode, nil | |||
} | |||
// AddAccess adds new access record. | |||
func AddAccess(access *Access) error { | |||
access.UserName = strings.ToLower(access.UserName) | |||
access.RepoName = strings.ToLower(access.RepoName) | |||
_, err := x.Insert(access) | |||
return err | |||
// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the | |||
// user does not have access. User can be nil! | |||
func AccessLevel(u *User, repo *Repository) (AccessMode, error) { | |||
return accessLevel(x, u, repo) | |||
} | |||
// UpdateAccess updates access information. | |||
func UpdateAccess(access *Access) error { | |||
access.UserName = strings.ToLower(access.UserName) | |||
access.RepoName = strings.ToLower(access.RepoName) | |||
_, err := x.Id(access.Id).Update(access) | |||
return err | |||
func hasAccess(e Engine, u *User, repo *Repository, testMode AccessMode) (bool, error) { | |||
mode, err := accessLevel(e, u, repo) | |||
return testMode <= mode, err | |||
} | |||
// DeleteAccess deletes access record. | |||
func DeleteAccess(access *Access) error { | |||
_, err := x.Delete(access) | |||
return err | |||
// HasAccess returns true if someone has the request access level. User can be nil! | |||
func HasAccess(u *User, repo *Repository, testMode AccessMode) (bool, error) { | |||
return hasAccess(x, u, repo, testMode) | |||
} | |||
// UpdateAccess updates access information with session for rolling back. | |||
func UpdateAccessWithSession(sess *xorm.Session, access *Access) error { | |||
if _, err := sess.Id(access.Id).Update(access); err != nil { | |||
sess.Rollback() | |||
return err | |||
// GetAccessibleRepositories finds all repositories where a user has access to, | |||
// besides his own. | |||
func (u *User) GetAccessibleRepositories() (map[*Repository]AccessMode, error) { | |||
accesses := make([]*Access, 0, 10) | |||
if err := x.Find(&accesses, &Access{UserID: u.Id}); err != nil { | |||
return nil, err | |||
} | |||
return nil | |||
repos := make(map[*Repository]AccessMode, len(accesses)) | |||
for _, access := range accesses { | |||
repo, err := GetRepositoryById(access.RepoID) | |||
if err != nil { | |||
return nil, err | |||
} | |||
if err = repo.GetOwner(); err != nil { | |||
return nil, err | |||
} else if repo.OwnerId == u.Id { | |||
continue | |||
} | |||
repos[repo] = access.Mode | |||
} | |||
// FIXME: should we generate an ordered list here? Random looks weird. | |||
return repos, nil | |||
} | |||
func maxAccessMode(modes ...AccessMode) AccessMode { | |||
max := ACCESS_MODE_NONE | |||
for _, mode := range modes { | |||
if mode > max { | |||
max = mode | |||
} | |||
} | |||
return max | |||
} | |||
// HasAccess returns true if someone can read or write to given repository. | |||
// The repoName should be in format <username>/<reponame>. | |||
func HasAccess(uname, repoName string, mode AccessType) (bool, error) { | |||
if len(repoName) == 0 { | |||
return false, nil | |||
// FIXME: do corss-comparison so reduce deletions and additions to the minimum? | |||
func (repo *Repository) refreshAccesses(e Engine, accessMap map[int64]AccessMode) (err error) { | |||
minMode := ACCESS_MODE_READ | |||
if !repo.IsPrivate { | |||
minMode = ACCESS_MODE_WRITE | |||
} | |||
access := &Access{ | |||
UserName: strings.ToLower(uname), | |||
RepoName: strings.ToLower(repoName), | |||
newAccesses := make([]Access, 0, len(accessMap)) | |||
for userID, mode := range accessMap { | |||
if mode < minMode { | |||
continue | |||
} | |||
newAccesses = append(newAccesses, Access{ | |||
UserID: userID, | |||
RepoID: repo.Id, | |||
Mode: mode, | |||
}) | |||
} | |||
has, err := x.Get(access) | |||
// Delete old accesses and insert new ones for repository. | |||
if _, err = e.Delete(&Access{RepoID: repo.Id}); err != nil { | |||
return fmt.Errorf("delete old accesses: %v", err) | |||
} else if _, err = e.Insert(newAccesses); err != nil { | |||
return fmt.Errorf("insert new accesses: %v", err) | |||
} | |||
return nil | |||
} | |||
// FIXME: should be able to have read-only access. | |||
// Give all collaborators write access. | |||
func (repo *Repository) refreshCollaboratorAccesses(e Engine, accessMap map[int64]AccessMode) error { | |||
collaborators, err := repo.getCollaborators(e) | |||
if err != nil { | |||
return false, err | |||
} else if !has { | |||
return false, nil | |||
} else if mode > access.Mode { | |||
return false, nil | |||
return fmt.Errorf("getCollaborators: %v", err) | |||
} | |||
for _, c := range collaborators { | |||
accessMap[c.Id] = ACCESS_MODE_WRITE | |||
} | |||
return nil | |||
} | |||
// recalculateTeamAccesses recalculates new accesses for teams of an organization | |||
// except the team whose ID is given. It is used to assign a team ID when | |||
// remove repository from that team. | |||
func (repo *Repository) recalculateTeamAccesses(e Engine, ignTeamID int64) (err error) { | |||
accessMap := make(map[int64]AccessMode, 20) | |||
if err = repo.refreshCollaboratorAccesses(e, accessMap); err != nil { | |||
return fmt.Errorf("refreshCollaboratorAccesses: %v", err) | |||
} | |||
if err = repo.getOwner(e); err != nil { | |||
return err | |||
} | |||
if repo.Owner.IsOrganization() { | |||
if err = repo.Owner.getTeams(e); err != nil { | |||
return err | |||
} | |||
for _, t := range repo.Owner.Teams { | |||
if t.ID == ignTeamID { | |||
continue | |||
} | |||
if t.IsOwnerTeam() { | |||
t.Authorize = ACCESS_MODE_OWNER | |||
} | |||
if err = t.getMembers(e); err != nil { | |||
return fmt.Errorf("getMembers '%d': %v", t.ID, err) | |||
} | |||
for _, m := range t.Members { | |||
accessMap[m.Id] = maxAccessMode(accessMap[m.Id], t.Authorize) | |||
} | |||
} | |||
} | |||
return repo.refreshAccesses(e, accessMap) | |||
} | |||
func (repo *Repository) recalculateAccesses(e Engine) error { | |||
accessMap := make(map[int64]AccessMode, 20) | |||
if err := repo.refreshCollaboratorAccesses(e, accessMap); err != nil { | |||
return fmt.Errorf("refreshCollaboratorAccesses: %v", err) | |||
} | |||
return true, nil | |||
return repo.refreshAccesses(e, accessMap) | |||
} | |||
// RecalculateAccesses recalculates all accesses for repository. | |||
func (r *Repository) RecalculateAccesses() error { | |||
return r.recalculateAccesses(x) | |||
} |
@@ -434,46 +434,58 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string, | |||
return nil | |||
} | |||
// NewRepoAction adds new action for creating repository. | |||
func NewRepoAction(u *User, repo *Repository) (err error) { | |||
if err = NotifyWatchers(&Action{ActUserId: u.Id, ActUserName: u.Name, ActEmail: u.Email, | |||
OpType: CREATE_REPO, RepoId: repo.Id, RepoUserName: repo.Owner.Name, RepoName: repo.Name, | |||
IsPrivate: repo.IsPrivate}); err != nil { | |||
log.Error(4, "NotifyWatchers: %d/%s", u.Id, repo.Name) | |||
return err | |||
func newRepoAction(e Engine, u *User, repo *Repository) (err error) { | |||
if err = notifyWatchers(e, &Action{ | |||
ActUserId: u.Id, | |||
ActUserName: u.Name, | |||
ActEmail: u.Email, | |||
OpType: CREATE_REPO, | |||
RepoId: repo.Id, | |||
RepoUserName: repo.Owner.Name, | |||
RepoName: repo.Name, | |||
IsPrivate: repo.IsPrivate}); err != nil { | |||
return fmt.Errorf("notify watchers '%d/%s'", u.Id, repo.Id) | |||
} | |||
log.Trace("action.NewRepoAction: %s/%s", u.Name, repo.Name) | |||
return err | |||
} | |||
// TransferRepoAction adds new action for transferring repository. | |||
func TransferRepoAction(u, newUser *User, repo *Repository) (err error) { | |||
// NewRepoAction adds new action for creating repository. | |||
func NewRepoAction(u *User, repo *Repository) (err error) { | |||
return newRepoAction(x, u, repo) | |||
} | |||
func transferRepoAction(e Engine, actUser, oldOwner, newOwner *User, repo *Repository) (err error) { | |||
action := &Action{ | |||
ActUserId: u.Id, | |||
ActUserName: u.Name, | |||
ActEmail: u.Email, | |||
ActUserId: actUser.Id, | |||
ActUserName: actUser.Name, | |||
ActEmail: actUser.Email, | |||
OpType: TRANSFER_REPO, | |||
RepoId: repo.Id, | |||
RepoUserName: newUser.Name, | |||
RepoUserName: newOwner.Name, | |||
RepoName: repo.Name, | |||
IsPrivate: repo.IsPrivate, | |||
Content: path.Join(repo.Owner.LowerName, repo.LowerName), | |||
Content: path.Join(oldOwner.LowerName, repo.LowerName), | |||
} | |||
if err = NotifyWatchers(action); err != nil { | |||
log.Error(4, "NotifyWatchers: %d/%s", u.Id, repo.Name) | |||
return err | |||
if err = notifyWatchers(e, action); err != nil { | |||
return fmt.Errorf("notify watchers '%d/%s'", actUser.Id, repo.Id) | |||
} | |||
// Remove watch for organization. | |||
if repo.Owner.IsOrganization() { | |||
if err = WatchRepo(repo.Owner.Id, repo.Id, false); err != nil { | |||
log.Error(4, "WatchRepo", err) | |||
if err = watchRepo(e, repo.Owner.Id, repo.Id, false); err != nil { | |||
return fmt.Errorf("watch repository: %v", err) | |||
} | |||
} | |||
log.Trace("action.TransferRepoAction: %s/%s", u.Name, repo.Name) | |||
return err | |||
log.Trace("action.TransferRepoAction: %s/%s", actUser.Name, repo.Name) | |||
return nil | |||
} | |||
// TransferRepoAction adds new action for transferring repository. | |||
func TransferRepoAction(actUser, oldOwner, newOwner *User, repo *Repository) (err error) { | |||
return transferRepoAction(x, actUser, oldOwner, newOwner, repo) | |||
} | |||
// GetFeeds returns action list of given user in given context. | |||
@@ -282,30 +282,33 @@ type IssueUser struct { | |||
} | |||
// NewIssueUserPairs adds new issue-user pairs for new issue of repository. | |||
func NewIssueUserPairs(rid, iid, oid, pid, aid int64, repoName string) (err error) { | |||
iu := &IssueUser{IssueId: iid, RepoId: rid} | |||
us, err := GetCollaborators(repoName) | |||
func NewIssueUserPairs(repo *Repository, issueID, orgID, posterID, assigneeID int64) (err error) { | |||
users, err := repo.GetCollaborators() | |||
if err != nil { | |||
return err | |||
} | |||
iu := &IssueUser{ | |||
IssueId: issueID, | |||
RepoId: repo.Id, | |||
} | |||
isNeedAddPoster := true | |||
for _, u := range us { | |||
for _, u := range users { | |||
iu.Uid = u.Id | |||
iu.IsPoster = iu.Uid == pid | |||
iu.IsPoster = iu.Uid == posterID | |||
if isNeedAddPoster && iu.IsPoster { | |||
isNeedAddPoster = false | |||
} | |||
iu.IsAssigned = iu.Uid == aid | |||
iu.IsAssigned = iu.Uid == assigneeID | |||
if _, err = x.Insert(iu); err != nil { | |||
return err | |||
} | |||
} | |||
if isNeedAddPoster { | |||
iu.Uid = pid | |||
iu.Uid = posterID | |||
iu.IsPoster = true | |||
iu.IsAssigned = iu.Uid == aid | |||
iu.IsAssigned = iu.Uid == assigneeID | |||
if _, err = x.Insert(iu); err != nil { | |||
return err | |||
} | |||
@@ -1,12 +1,44 @@ | |||
// Copyright 2015 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 migrations | |||
import ( | |||
"errors" | |||
"fmt" | |||
"strings" | |||
"time" | |||
"github.com/Unknwon/com" | |||
"github.com/go-xorm/xorm" | |||
"github.com/gogits/gogs/modules/log" | |||
"github.com/gogits/gogs/modules/setting" | |||
) | |||
type migration func(*xorm.Engine) error | |||
const _MIN_DB_VER = 0 | |||
type Migration interface { | |||
Description() string | |||
Migrate(*xorm.Engine) error | |||
} | |||
type migration struct { | |||
description string | |||
migrate func(*xorm.Engine) error | |||
} | |||
func NewMigration(desc string, fn func(*xorm.Engine) error) Migration { | |||
return &migration{desc, fn} | |||
} | |||
func (m *migration) Description() string { | |||
return m.description | |||
} | |||
func (m *migration) Migrate(x *xorm.Engine) error { | |||
return m.migrate(x) | |||
} | |||
// The version table. Should have only one row with id==1 | |||
type Version struct { | |||
@@ -15,30 +47,55 @@ type Version struct { | |||
} | |||
// This is a sequence of migrations. Add new migrations to the bottom of the list. | |||
// If you want to "retire" a migration, replace it with "expiredMigration" | |||
var migrations = []migration{} | |||
// If you want to "retire" a migration, remove it from the top of the list and | |||
// update _MIN_VER_DB accordingly | |||
var migrations = []Migration{ | |||
NewMigration("generate collaboration from access", accessToCollaboration), // V0 -> V1 | |||
NewMigration("make authorize 4 if team is owners", ownerTeamUpdate), // V1 -> V2 | |||
NewMigration("refactor access table to use id's", accessRefactor), // V2 -> V3 | |||
NewMigration("generate team-repo from team", teamToTeamRepo), // V3 -> V4 | |||
} | |||
// Migrate database to current version | |||
func Migrate(x *xorm.Engine) error { | |||
if err := x.Sync(new(Version)); err != nil { | |||
return err | |||
return fmt.Errorf("sync: %v", err) | |||
} | |||
currentVersion := &Version{Id: 1} | |||
has, err := x.Get(currentVersion) | |||
if err != nil { | |||
return err | |||
return fmt.Errorf("get: %v", err) | |||
} else if !has { | |||
if _, err = x.InsertOne(currentVersion); err != nil { | |||
// If the user table does not exist it is a fresh installation and we | |||
// can skip all migrations. | |||
needsMigration, err := x.IsTableExist("user") | |||
if err != nil { | |||
return err | |||
} | |||
if needsMigration { | |||
isEmpty, err := x.IsTableEmpty("user") | |||
if err != nil { | |||
return err | |||
} | |||
// If the user table is empty it is a fresh installation and we can | |||
// skip all migrations. | |||
needsMigration = !isEmpty | |||
} | |||
if !needsMigration { | |||
currentVersion.Version = int64(_MIN_DB_VER + len(migrations)) | |||
} | |||
if _, err = x.InsertOne(currentVersion); err != nil { | |||
return fmt.Errorf("insert: %v", err) | |||
} | |||
} | |||
v := currentVersion.Version | |||
for i, migration := range migrations[v:] { | |||
if err = migration(x); err != nil { | |||
return err | |||
for i, m := range migrations[v-_MIN_DB_VER:] { | |||
log.Info("Migration: %s", m.Description()) | |||
if err = m.Migrate(x); err != nil { | |||
return fmt.Errorf("do migrate: %v", err) | |||
} | |||
currentVersion.Version = v + int64(i) + 1 | |||
if _, err = x.Id(1).Update(currentVersion); err != nil { | |||
@@ -48,6 +105,267 @@ func Migrate(x *xorm.Engine) error { | |||
return nil | |||
} | |||
func expiredMigration(x *xorm.Engine) error { | |||
return errors.New("You are migrating from a too old gogs version") | |||
func sessionRelease(sess *xorm.Session) { | |||
if !sess.IsCommitedOrRollbacked { | |||
sess.Rollback() | |||
} | |||
sess.Close() | |||
} | |||
func accessToCollaboration(x *xorm.Engine) (err error) { | |||
type Collaboration struct { | |||
ID int64 `xorm:"pk autoincr"` | |||
RepoID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"` | |||
UserID int64 `xorm:"UNIQUE(s) INDEX NOT NULL"` | |||
Created time.Time | |||
} | |||
if err = x.Sync(new(Collaboration)); err != nil { | |||
return fmt.Errorf("sync: %v", err) | |||
} | |||
results, err := x.Query("SELECT u.id AS `uid`, a.repo_name AS `repo`, a.mode AS `mode`, a.created as `created` FROM `access` a JOIN `user` u ON a.user_name=u.lower_name") | |||
if err != nil { | |||
return err | |||
} | |||
sess := x.NewSession() | |||
defer sessionRelease(sess) | |||
if err = sess.Begin(); err != nil { | |||
return err | |||
} | |||
offset := strings.Split(time.Now().String(), " ")[2] | |||
for _, result := range results { | |||
mode := com.StrTo(result["mode"]).MustInt64() | |||
// Collaborators must have write access. | |||
if mode < 2 { | |||
continue | |||
} | |||
userID := com.StrTo(result["uid"]).MustInt64() | |||
repoRefName := string(result["repo"]) | |||
var created time.Time | |||
switch { | |||
case setting.UseSQLite3: | |||
created, _ = time.Parse(time.RFC3339, string(result["created"])) | |||
case setting.UseMySQL: | |||
created, _ = time.Parse("2006-01-02 15:04:05-0700", string(result["created"])+offset) | |||
case setting.UsePostgreSQL: | |||
created, _ = time.Parse("2006-01-02T15:04:05Z-0700", string(result["created"])+offset) | |||
} | |||
// find owner of repository | |||
parts := strings.SplitN(repoRefName, "/", 2) | |||
ownerName := parts[0] | |||
repoName := parts[1] | |||
results, err := sess.Query("SELECT u.id as `uid`, ou.uid as `memberid` FROM `user` u LEFT JOIN org_user ou ON ou.org_id=u.id WHERE u.lower_name=?", ownerName) | |||
if err != nil { | |||
return err | |||
} | |||
if len(results) < 1 { | |||
continue | |||
} | |||
ownerID := com.StrTo(results[0]["uid"]).MustInt64() | |||
if ownerID == userID { | |||
continue | |||
} | |||
// test if user is member of owning organization | |||
isMember := false | |||
for _, member := range results { | |||
memberID := com.StrTo(member["memberid"]).MustInt64() | |||
// We can skip all cases that a user is member of the owning organization | |||
if memberID == userID { | |||
isMember = true | |||
} | |||
} | |||
if isMember { | |||
continue | |||
} | |||
results, err = sess.Query("SELECT id FROM `repository` WHERE owner_id=? AND lower_name=?", ownerID, repoName) | |||
if err != nil { | |||
return err | |||
} else if len(results) < 1 { | |||
continue | |||
} | |||
collaboration := &Collaboration{ | |||
UserID: userID, | |||
RepoID: com.StrTo(results[0]["id"]).MustInt64(), | |||
} | |||
has, err := sess.Get(collaboration) | |||
if err != nil { | |||
return err | |||
} else if has { | |||
continue | |||
} | |||
collaboration.Created = created | |||
if _, err = sess.InsertOne(collaboration); err != nil { | |||
return err | |||
} | |||
} | |||
return sess.Commit() | |||
} | |||
func ownerTeamUpdate(x *xorm.Engine) (err error) { | |||
if _, err := x.Exec("UPDATE `team` SET authorize=4 WHERE lower_name=?", "owners"); err != nil { | |||
return fmt.Errorf("update owner team table: %v", err) | |||
} | |||
return nil | |||
} | |||
func accessRefactor(x *xorm.Engine) (err error) { | |||
type ( | |||
AccessMode int | |||
Access struct { | |||
ID int64 `xorm:"pk autoincr"` | |||
UserID int64 `xorm:"UNIQUE(s)"` | |||
RepoID int64 `xorm:"UNIQUE(s)"` | |||
Mode AccessMode | |||
} | |||
UserRepo struct { | |||
UserID int64 | |||
RepoID int64 | |||
} | |||
) | |||
// We consiously don't start a session yet as we make only reads for now, no writes | |||
accessMap := make(map[UserRepo]AccessMode, 50) | |||
results, err := x.Query("SELECT r.id AS `repo_id`, r.is_private AS `is_private`, r.owner_id AS `owner_id`, u.type AS `owner_type` FROM `repository` r LEFT JOIN `user` u ON r.owner_id=u.id") | |||
if err != nil { | |||
return fmt.Errorf("select repositories: %v", err) | |||
} | |||
for _, repo := range results { | |||
repoID := com.StrTo(repo["repo_id"]).MustInt64() | |||
isPrivate := com.StrTo(repo["is_private"]).MustInt() > 0 | |||
ownerID := com.StrTo(repo["owner_id"]).MustInt64() | |||
ownerIsOrganization := com.StrTo(repo["owner_type"]).MustInt() > 0 | |||
results, err := x.Query("SELECT `user_id` FROM `collaboration` WHERE repo_id=?", repoID) | |||
if err != nil { | |||
return fmt.Errorf("select collaborators: %v", err) | |||
} | |||
for _, user := range results { | |||
userID := com.StrTo(user["user_id"]).MustInt64() | |||
accessMap[UserRepo{userID, repoID}] = 2 // WRITE ACCESS | |||
} | |||
if !ownerIsOrganization { | |||
continue | |||
} | |||
// The minimum level to add a new access record, | |||
// because public repository has implicit open access. | |||
minAccessLevel := AccessMode(0) | |||
if !isPrivate { | |||
minAccessLevel = 1 | |||
} | |||
repoString := "$" + string(repo["repo_id"]) + "|" | |||
results, err = x.Query("SELECT `id`,`authorize`,`repo_ids` FROM `team` WHERE org_id=? AND authorize>? ORDER BY `authorize` ASC", ownerID, int(minAccessLevel)) | |||
if err != nil { | |||
return fmt.Errorf("select teams from org: %v", err) | |||
} | |||
for _, team := range results { | |||
if !strings.Contains(string(team["repo_ids"]), repoString) { | |||
continue | |||
} | |||
teamID := com.StrTo(team["id"]).MustInt64() | |||
mode := AccessMode(com.StrTo(team["authorize"]).MustInt()) | |||
results, err := x.Query("SELECT `uid` FROM `team_user` WHERE team_id=?", teamID) | |||
if err != nil { | |||
return fmt.Errorf("select users from team: %v", err) | |||
} | |||
for _, user := range results { | |||
userID := com.StrTo(user["user_id"]).MustInt64() | |||
accessMap[UserRepo{userID, repoID}] = mode | |||
} | |||
} | |||
} | |||
// Drop table can't be in a session (at least not in sqlite) | |||
if _, err = x.Exec("DROP TABLE `access`"); err != nil { | |||
return fmt.Errorf("drop access table: %v", err) | |||
} | |||
// Now we start writing so we make a session | |||
sess := x.NewSession() | |||
defer sessionRelease(sess) | |||
if err = sess.Begin(); err != nil { | |||
return err | |||
} | |||
if err = sess.Sync2(new(Access)); err != nil { | |||
return fmt.Errorf("sync: %v", err) | |||
} | |||
accesses := make([]*Access, 0, len(accessMap)) | |||
for ur, mode := range accessMap { | |||
accesses = append(accesses, &Access{UserID: ur.UserID, RepoID: ur.RepoID, Mode: mode}) | |||
} | |||
if _, err = sess.Insert(accesses); err != nil { | |||
return fmt.Errorf("insert accesses: %v", err) | |||
} | |||
return sess.Commit() | |||
} | |||
func teamToTeamRepo(x *xorm.Engine) error { | |||
type TeamRepo struct { | |||
ID int64 `xorm:"pk autoincr"` | |||
OrgID int64 `xorm:"INDEX"` | |||
TeamID int64 `xorm:"UNIQUE(s)"` | |||
RepoID int64 `xorm:"UNIQUE(s)"` | |||
} | |||
teamRepos := make([]*TeamRepo, 0, 50) | |||
results, err := x.Query("SELECT `id`,`org_id`,`repo_ids` FROM `team`") | |||
if err != nil { | |||
return fmt.Errorf("select teams: %v", err) | |||
} | |||
for _, team := range results { | |||
orgID := com.StrTo(team["org_id"]).MustInt64() | |||
teamID := com.StrTo(team["id"]).MustInt64() | |||
for _, idStr := range strings.Split(string(team["repo_ids"]), "|") { | |||
repoID := com.StrTo(strings.TrimPrefix(idStr, "$")).MustInt64() | |||
if repoID == 0 { | |||
continue | |||
} | |||
teamRepos = append(teamRepos, &TeamRepo{ | |||
OrgID: orgID, | |||
TeamID: teamID, | |||
RepoID: repoID, | |||
}) | |||
} | |||
} | |||
sess := x.NewSession() | |||
defer sessionRelease(sess) | |||
if err = sess.Begin(); err != nil { | |||
return err | |||
} | |||
if err = sess.Sync2(new(TeamRepo)); err != nil { | |||
return fmt.Errorf("sync: %v", err) | |||
} else if _, err = sess.Insert(teamRepos); err != nil { | |||
return fmt.Errorf("insert team-repos: %v", err) | |||
} | |||
return sess.Commit() | |||
} |
@@ -12,10 +12,11 @@ import ( | |||
"strings" | |||
_ "github.com/go-sql-driver/mysql" | |||
"github.com/go-xorm/core" | |||
"github.com/go-xorm/xorm" | |||
_ "github.com/lib/pq" | |||
// "github.com/gogits/gogs/models/migrations" | |||
"github.com/gogits/gogs/models/migrations" | |||
"github.com/gogits/gogs/modules/setting" | |||
) | |||
@@ -23,12 +24,22 @@ import ( | |||
type Engine interface { | |||
Delete(interface{}) (int64, error) | |||
Exec(string, ...interface{}) (sql.Result, error) | |||
Find(interface{}, ...interface{}) error | |||
Get(interface{}) (bool, error) | |||
Insert(...interface{}) (int64, error) | |||
InsertOne(interface{}) (int64, error) | |||
Id(interface{}) *xorm.Session | |||
Sql(string, ...interface{}) *xorm.Session | |||
Where(string, ...interface{}) *xorm.Session | |||
} | |||
func sessionRelease(sess *xorm.Session) { | |||
if !sess.IsCommitedOrRollbacked { | |||
sess.Rollback() | |||
} | |||
sess.Close() | |||
} | |||
var ( | |||
x *xorm.Engine | |||
tables []interface{} | |||
@@ -39,24 +50,30 @@ var ( | |||
} | |||
EnableSQLite3 bool | |||
UseSQLite3 bool | |||
) | |||
func init() { | |||
tables = append(tables, | |||
new(User), new(PublicKey), new(Follow), new(Oauth2), new(AccessToken), | |||
new(Repository), new(Watch), new(Star), new(Action), new(Access), | |||
new(User), new(PublicKey), new(Oauth2), new(AccessToken), | |||
new(Repository), new(Collaboration), new(Access), | |||
new(Watch), new(Star), new(Follow), new(Action), | |||
new(Issue), new(Comment), new(Attachment), new(IssueUser), new(Label), new(Milestone), | |||
new(Mirror), new(Release), new(LoginSource), new(Webhook), | |||
new(UpdateTask), new(HookTask), new(Team), new(OrgUser), new(TeamUser), | |||
new(UpdateTask), new(HookTask), | |||
new(Team), new(OrgUser), new(TeamUser), new(TeamRepo), | |||
new(Notice), new(EmailAddress)) | |||
} | |||
func LoadModelsConfig() { | |||
sec := setting.Cfg.Section("database") | |||
DbCfg.Type = sec.Key("DB_TYPE").String() | |||
if DbCfg.Type == "sqlite3" { | |||
UseSQLite3 = true | |||
switch DbCfg.Type { | |||
case "sqlite3": | |||
setting.UseSQLite3 = true | |||
case "mysql": | |||
setting.UseMySQL = true | |||
case "postgres": | |||
setting.UsePostgreSQL = true | |||
} | |||
DbCfg.Host = sec.Key("HOST").String() | |||
DbCfg.Name = sec.Key("NAME").String() | |||
@@ -103,6 +120,7 @@ func NewTestEngine(x *xorm.Engine) (err error) { | |||
return fmt.Errorf("connect to database: %v", err) | |||
} | |||
x.SetMapper(core.GonicMapper{}) | |||
return x.Sync(tables...) | |||
} | |||
@@ -112,6 +130,8 @@ func SetEngine() (err error) { | |||
return fmt.Errorf("connect to database: %v", err) | |||
} | |||
x.SetMapper(core.GonicMapper{}) | |||
// WARNING: for serv command, MUST remove the output to os.stdout, | |||
// so use log file to instead print to stdout. | |||
logPath := path.Join(setting.LogRootPath, "xorm.log") | |||
@@ -136,13 +156,14 @@ func NewEngine() (err error) { | |||
return err | |||
} | |||
// if err = migrations.Migrate(x); err != nil { | |||
// return err | |||
// } | |||
if err = migrations.Migrate(x); err != nil { | |||
return fmt.Errorf("migrate: %v", err) | |||
} | |||
if err = x.StoreEngine("InnoDB").Sync2(tables...); err != nil { | |||
return fmt.Errorf("sync database struct error: %v\n", err) | |||
} | |||
return nil | |||
} | |||
@@ -231,7 +231,7 @@ func (u *User) GetOrganizations() error { | |||
u.Orgs = make([]*User, len(ous)) | |||
for i, ou := range ous { | |||
u.Orgs[i], err = GetUserById(ou.OrgId) | |||
u.Orgs[i], err = GetUserById(ou.OrgID) | |||
if err != nil { | |||
return err | |||
} | |||
@@ -398,63 +398,7 @@ func ChangeUserName(u *User, newUserName string) (err error) { | |||
return ErrUserNameIllegal | |||
} | |||
newUserName = strings.ToLower(newUserName) | |||
if u.LowerName == newUserName { | |||
// User only change letter cases. | |||
return nil | |||
} | |||
// Update accesses of user. | |||
accesses := make([]Access, 0, 10) | |||
if err = x.Find(&accesses, &Access{UserName: u.LowerName}); err != nil { | |||
return err | |||
} | |||
sess := x.NewSession() | |||
defer sess.Close() | |||
if err = sess.Begin(); err != nil { | |||
return err | |||
} | |||
for i := range accesses { | |||
accesses[i].UserName = newUserName | |||
if strings.HasPrefix(accesses[i].RepoName, u.LowerName+"/") { | |||
accesses[i].RepoName = strings.Replace(accesses[i].RepoName, u.LowerName, newUserName, 1) | |||
} | |||
if err = UpdateAccessWithSession(sess, &accesses[i]); err != nil { | |||
return err | |||
} | |||
} | |||
repos, err := GetRepositories(u.Id, true) | |||
if err != nil { | |||
return err | |||
} | |||
for i := range repos { | |||
accesses = make([]Access, 0, 10) | |||
// Update accesses of user repository. | |||
if err = x.Find(&accesses, &Access{RepoName: u.LowerName + "/" + repos[i].LowerName}); err != nil { | |||
return err | |||
} | |||
for j := range accesses { | |||
// if the access is not the user's access (already updated above) | |||
if accesses[j].UserName != u.LowerName { | |||
accesses[j].RepoName = newUserName + "/" + repos[i].LowerName | |||
if err = UpdateAccessWithSession(sess, &accesses[j]); err != nil { | |||
return err | |||
} | |||
} | |||
} | |||
} | |||
// Change user directory name. | |||
if err = os.Rename(UserPath(u.LowerName), UserPath(newUserName)); err != nil { | |||
sess.Rollback() | |||
return err | |||
} | |||
return sess.Commit() | |||
return os.Rename(UserPath(u.LowerName), UserPath(newUserName)) | |||
} | |||
// UpdateUser updates user's information. | |||
@@ -527,7 +471,7 @@ func DeleteUser(u *User) error { | |||
return err | |||
} | |||
// Delete all accesses. | |||
if _, err = x.Delete(&Access{UserName: u.LowerName}); err != nil { | |||
if _, err = x.Delete(&Access{UserID: u.Id}); err != nil { | |||
return err | |||
} | |||
// Delete all alternative email addresses | |||
@@ -570,8 +514,7 @@ func UserPath(userName string) string { | |||
func GetUserByKeyId(keyId int64) (*User, error) { | |||
user := new(User) | |||
rawSql := "SELECT a.* FROM `user` AS a, public_key AS b WHERE a.id = b.owner_id AND b.id=?" | |||
has, err := x.Sql(rawSql, keyId).Get(user) | |||
has, err := x.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 | |||
} else if !has { | |||
@@ -580,10 +523,9 @@ func GetUserByKeyId(keyId int64) (*User, error) { | |||
return user, nil | |||
} | |||
// GetUserById returns the user object by given ID if exists. | |||
func GetUserById(id int64) (*User, error) { | |||
func getUserById(e Engine, id int64) (*User, error) { | |||
u := new(User) | |||
has, err := x.Id(id).Get(u) | |||
has, err := e.Id(id).Get(u) | |||
if err != nil { | |||
return nil, err | |||
} else if !has { | |||
@@ -592,6 +534,11 @@ func GetUserById(id int64) (*User, error) { | |||
return u, nil | |||
} | |||
// GetUserById returns the user object by given ID if exists. | |||
func GetUserById(id int64) (*User, error) { | |||
return getUserById(x, id) | |||
} | |||
// GetUserByName returns user by given name. | |||
func GetUserByName(name string) (*User, error) { | |||
if len(name) == 0 { | |||
@@ -913,7 +860,7 @@ func UpdateMentions(userNames []string, issueId int64) error { | |||
} | |||
for _, orgUser := range orgUsers { | |||
tempIds = append(tempIds, orgUser.Id) | |||
tempIds = append(tempIds, orgUser.ID) | |||
} | |||
ids = append(ids, tempIds...) | |||
@@ -164,7 +164,7 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{ | |||
}, | |||
"DiffTypeToStr": DiffTypeToStr, | |||
"DiffLineTypeToStr": DiffLineTypeToStr, | |||
"Sha1": Sha1, | |||
"Sha1": Sha1, | |||
"ShortSha": ShortSha, | |||
"Md5": EncodeMd5, | |||
"ActionContent2Commits": ActionContent2Commits, | |||
@@ -38,29 +38,7 @@ type Context struct { | |||
IsSigned bool | |||
IsBasicAuth bool | |||
Repo struct { | |||
IsOwner bool | |||
IsTrueOwner bool | |||
IsWatching bool | |||
IsBranch bool | |||
IsTag bool | |||
IsCommit bool | |||
IsAdmin bool // Current user is admin level. | |||
HasAccess bool | |||
Repository *models.Repository | |||
Owner *models.User | |||
Commit *git.Commit | |||
Tag *git.Tag | |||
GitRepo *git.Repository | |||
BranchName string | |||
TagName string | |||
TreeName string | |||
CommitId string | |||
RepoLink string | |||
CloneLink models.CloneLink | |||
CommitsCount int | |||
Mirror *models.Mirror | |||
} | |||
Repo RepoContext | |||
Org struct { | |||
IsOwner bool | |||
@@ -73,6 +51,37 @@ type Context struct { | |||
} | |||
} | |||
type RepoContext struct { | |||
AccessMode models.AccessMode | |||
IsWatching bool | |||
IsBranch bool | |||
IsTag bool | |||
IsCommit bool | |||
Repository *models.Repository | |||
Owner *models.User | |||
Commit *git.Commit | |||
Tag *git.Tag | |||
GitRepo *git.Repository | |||
BranchName string | |||
TagName string | |||
TreeName string | |||
CommitId string | |||
RepoLink string | |||
CloneLink models.CloneLink | |||
CommitsCount int | |||
Mirror *models.Mirror | |||
} | |||
// Return if the current user has write access for this repository | |||
func (r RepoContext) IsOwner() bool { | |||
return r.AccessMode >= models.ACCESS_MODE_WRITE | |||
} | |||
// Return if the current user has read access for this repository | |||
func (r RepoContext) HasAccess() bool { | |||
return r.AccessMode >= models.ACCESS_MODE_READ | |||
} | |||
// HasError returns true if error occurs in form validation. | |||
func (ctx *Context) HasApiError() bool { | |||
hasErr, ok := ctx.Data["HasError"] | |||
@@ -87,7 +87,7 @@ func OrgAssignment(redirect bool, args ...bool) macaron.Handler { | |||
return | |||
} | |||
ctx.Data["Team"] = ctx.Org.Team | |||
ctx.Org.IsAdminTeam = ctx.Org.Team.IsOwnerTeam() || ctx.Org.Team.Authorize == models.ORG_ADMIN | |||
ctx.Org.IsAdminTeam = ctx.Org.Team.IsOwnerTeam() || ctx.Org.Team.Authorize >= models.ACCESS_MODE_ADMIN | |||
} | |||
ctx.Data["IsAdminTeam"] = ctx.Org.IsAdminTeam | |||
if requireAdminTeam && !ctx.Org.IsAdminTeam { | |||
@@ -5,7 +5,6 @@ | |||
package middleware | |||
import ( | |||
"errors" | |||
"fmt" | |||
"net/url" | |||
"strings" | |||
@@ -29,17 +28,10 @@ func ApiRepoAssignment() macaron.Handler { | |||
err error | |||
) | |||
// Collaborators who have write access can be seen as owners. | |||
if ctx.IsSigned { | |||
ctx.Repo.IsOwner, err = models.HasAccess(ctx.User.Name, userName+"/"+repoName, models.WRITABLE) | |||
if err != nil { | |||
ctx.JSON(500, &base.ApiJsonErr{"HasAccess: " + err.Error(), base.DOC_URL}) | |||
return | |||
} | |||
ctx.Repo.IsTrueOwner = ctx.User.LowerName == strings.ToLower(userName) | |||
} | |||
if !ctx.Repo.IsTrueOwner { | |||
// Check if the user is the same as the repository owner. | |||
if ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName) { | |||
u = ctx.User | |||
} else { | |||
u, err = models.GetUserByName(userName) | |||
if err != nil { | |||
if err == models.ErrUserNotExist { | |||
@@ -49,66 +41,36 @@ func ApiRepoAssignment() macaron.Handler { | |||
} | |||
return | |||
} | |||
} else { | |||
u = ctx.User | |||
} | |||
ctx.Repo.Owner = u | |||
// Organization owner team members are true owners as well. | |||
if ctx.IsSigned && ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOwnedBy(ctx.User.Id) { | |||
ctx.Repo.IsTrueOwner = true | |||
} | |||
// Get repository. | |||
repo, err := models.GetRepositoryByName(u.Id, repoName) | |||
if err != nil { | |||
if err == models.ErrRepoNotExist { | |||
ctx.Error(404) | |||
return | |||
} else { | |||
ctx.JSON(500, &base.ApiJsonErr{"GetRepositoryByName: " + err.Error(), base.DOC_URL}) | |||
} | |||
ctx.JSON(500, &base.ApiJsonErr{"GetRepositoryByName: " + err.Error(), base.DOC_URL}) | |||
return | |||
} else if err = repo.GetOwner(); err != nil { | |||
ctx.JSON(500, &base.ApiJsonErr{"GetOwner: " + err.Error(), base.DOC_URL}) | |||
return | |||
} | |||
// Check if the mirror repository owner(mirror repository doesn't have access). | |||
if ctx.IsSigned && !ctx.Repo.IsOwner { | |||
if repo.OwnerId == ctx.User.Id { | |||
ctx.Repo.IsOwner = true | |||
} | |||
// Check if current user has admin permission to repository. | |||
if u.IsOrganization() { | |||
auth, err := models.GetHighestAuthorize(u.Id, ctx.User.Id, repo.Id, 0) | |||
if err != nil { | |||
ctx.JSON(500, &base.ApiJsonErr{"GetHighestAuthorize: " + err.Error(), base.DOC_URL}) | |||
return | |||
} | |||
if auth == models.ORG_ADMIN { | |||
ctx.Repo.IsOwner = true | |||
ctx.Repo.IsAdmin = true | |||
} | |||
} | |||
mode, err := models.AccessLevel(ctx.User, repo) | |||
if err != nil { | |||
ctx.JSON(500, &base.ApiJsonErr{"AccessLevel: " + err.Error(), base.DOC_URL}) | |||
return | |||
} | |||
// Check access. | |||
if repo.IsPrivate && !ctx.Repo.IsOwner { | |||
if ctx.User == nil { | |||
ctx.Error(404) | |||
return | |||
} | |||
ctx.Repo.AccessMode = mode | |||
hasAccess, err := models.HasAccess(ctx.User.Name, ctx.Repo.Owner.Name+"/"+repo.Name, models.READABLE) | |||
if err != nil { | |||
ctx.JSON(500, &base.ApiJsonErr{"HasAccess: " + err.Error(), base.DOC_URL}) | |||
return | |||
} else if !hasAccess { | |||
ctx.Error(404) | |||
return | |||
} | |||
// Check access. | |||
if ctx.Repo.AccessMode == models.ACCESS_MODE_NONE { | |||
ctx.Error(404) | |||
return | |||
} | |||
ctx.Repo.HasAccess = true | |||
ctx.Repo.Repository = repo | |||
} | |||
@@ -242,101 +204,49 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler { | |||
refName = ctx.Params(":path") | |||
} | |||
// Collaborators who have write access can be seen as owners. | |||
if ctx.IsSigned { | |||
ctx.Repo.IsOwner, err = models.HasAccess(ctx.User.Name, userName+"/"+repoName, models.WRITABLE) | |||
if err != nil { | |||
ctx.Handle(500, "HasAccess", err) | |||
return | |||
} | |||
ctx.Repo.IsTrueOwner = ctx.User.LowerName == strings.ToLower(userName) | |||
} | |||
if !ctx.Repo.IsTrueOwner { | |||
// Check if the user is the same as the repository owner | |||
if ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName) { | |||
u = ctx.User | |||
} else { | |||
u, err = models.GetUserByName(userName) | |||
if err != nil { | |||
if err == models.ErrUserNotExist { | |||
ctx.Handle(404, "GetUserByName", err) | |||
} else if redirect { | |||
log.Error(4, "GetUserByName", err) | |||
ctx.Redirect(setting.AppSubUrl + "/") | |||
} else { | |||
ctx.Handle(500, "GetUserByName", err) | |||
} | |||
return | |||
} | |||
} else { | |||
u = ctx.User | |||
} | |||
if u == nil { | |||
if redirect { | |||
ctx.Redirect(setting.AppSubUrl + "/") | |||
return | |||
} | |||
ctx.Handle(404, "RepoAssignment", errors.New("invliad user account for single repository")) | |||
return | |||
} | |||
ctx.Repo.Owner = u | |||
// Organization owner team members are true owners as well. | |||
if ctx.IsSigned && ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOwnedBy(ctx.User.Id) { | |||
ctx.Repo.IsTrueOwner = true | |||
} | |||
// Get repository. | |||
repo, err := models.GetRepositoryByName(u.Id, repoName) | |||
if err != nil { | |||
if err == models.ErrRepoNotExist { | |||
ctx.Handle(404, "GetRepositoryByName", err) | |||
return | |||
} else if redirect { | |||
ctx.Redirect(setting.AppSubUrl + "/") | |||
return | |||
} else { | |||
ctx.Handle(500, "GetRepositoryByName", err) | |||
} | |||
ctx.Handle(500, "GetRepositoryByName", err) | |||
return | |||
} else if err = repo.GetOwner(); err != nil { | |||
ctx.Handle(500, "GetOwner", err) | |||
return | |||
} | |||
// Check if the mirror repository owner(mirror repository doesn't have access). | |||
if ctx.IsSigned && !ctx.Repo.IsOwner { | |||
if repo.OwnerId == ctx.User.Id { | |||
ctx.Repo.IsOwner = true | |||
} | |||
// Check if current user has admin permission to repository. | |||
if u.IsOrganization() { | |||
auth, err := models.GetHighestAuthorize(u.Id, ctx.User.Id, repo.Id, 0) | |||
if err != nil { | |||
ctx.Handle(500, "GetHighestAuthorize", err) | |||
return | |||
} | |||
if auth == models.ORG_ADMIN { | |||
ctx.Repo.IsOwner = true | |||
ctx.Repo.IsAdmin = true | |||
} | |||
} | |||
mode, err := models.AccessLevel(ctx.User, repo) | |||
if err != nil { | |||
ctx.Handle(500, "AccessLevel", err) | |||
return | |||
} | |||
ctx.Repo.AccessMode = mode | |||
// Check access. | |||
if repo.IsPrivate && !ctx.Repo.IsOwner { | |||
if ctx.User == nil { | |||
ctx.Handle(404, "HasAccess", nil) | |||
return | |||
} | |||
hasAccess, err := models.HasAccess(ctx.User.Name, ctx.Repo.Owner.Name+"/"+repo.Name, models.READABLE) | |||
if err != nil { | |||
ctx.Handle(500, "HasAccess", err) | |||
return | |||
} else if !hasAccess { | |||
ctx.Handle(404, "HasAccess", nil) | |||
return | |||
} | |||
if ctx.Repo.AccessMode == models.ACCESS_MODE_NONE { | |||
ctx.Handle(404, "no access right", err) | |||
return | |||
} | |||
ctx.Repo.HasAccess = true | |||
ctx.Data["HasAccess"] = true | |||
if repo.IsMirror { | |||
@@ -383,8 +293,8 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler { | |||
ctx.Data["Title"] = u.Name + "/" + repo.Name | |||
ctx.Data["Repository"] = repo | |||
ctx.Data["Owner"] = ctx.Repo.Repository.Owner | |||
ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner | |||
ctx.Data["IsRepositoryTrueOwner"] = ctx.Repo.IsTrueOwner | |||
ctx.Data["IsRepositoryOwner"] = ctx.Repo.AccessMode >= models.ACCESS_MODE_WRITE | |||
ctx.Data["IsRepositoryAdmin"] = ctx.Repo.AccessMode >= models.ACCESS_MODE_ADMIN | |||
ctx.Data["DisableSSH"] = setting.DisableSSH | |||
ctx.Repo.CloneLink, err = repo.CloneLink() | |||
@@ -438,9 +348,9 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler { | |||
} | |||
} | |||
func RequireTrueOwner() macaron.Handler { | |||
func RequireAdmin() macaron.Handler { | |||
return func(ctx *Context) { | |||
if !ctx.Repo.IsTrueOwner && !ctx.Repo.IsAdmin { | |||
if ctx.Repo.AccessMode < models.ACCESS_MODE_ADMIN { | |||
if !ctx.IsSigned { | |||
ctx.SetCookie("redirect_to", "/"+url.QueryEscape(setting.AppSubUrl+ctx.Req.RequestURI), 0, setting.AppSubUrl) | |||
ctx.Redirect(setting.AppSubUrl + "/user/login") | |||
@@ -67,6 +67,11 @@ var ( | |||
CookieRememberName string | |||
ReverseProxyAuthUser string | |||
// Database settings. | |||
UseSQLite3 bool | |||
UseMySQL bool | |||
UsePostgreSQL bool | |||
// Webhook settings. | |||
Webhook struct { | |||
TaskInterval int | |||
@@ -267,10 +272,6 @@ func NewConfigContext() { | |||
"StampNano": time.StampNano, | |||
}[Cfg.Section("time").Key("FORMAT").MustString("RFC1123")] | |||
if err = os.MkdirAll(AttachmentPath, os.ModePerm); err != nil { | |||
log.Fatal(4, "Could not create directory %s: %s", AttachmentPath, err) | |||
} | |||
RunUser = Cfg.Section("").Key("RUN_USER").String() | |||
curUser := os.Getenv("USER") | |||
if len(curUser) == 0 { | |||
@@ -293,9 +294,6 @@ func NewConfigContext() { | |||
} else { | |||
RepoRootPath = filepath.Clean(RepoRootPath) | |||
} | |||
if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil { | |||
log.Fatal(4, "Fail to create repository root path(%s): %v", RepoRootPath, err) | |||
} | |||
ScriptType = sec.Key("SCRIPT_TYPE").MustString("bash") | |||
sec = Cfg.Section("picture") | |||
@@ -304,7 +302,6 @@ func NewConfigContext() { | |||
if !filepath.IsAbs(AvatarUploadPath) { | |||
AvatarUploadPath = path.Join(workDir, AvatarUploadPath) | |||
} | |||
os.MkdirAll(AvatarUploadPath, os.ModePerm) | |||
switch sec.Key("GRAVATAR_SOURCE").MustString("gravatar") { | |||
case "duoshuo": | |||
GravatarSource = "http://gravatar.duoshuo.com/avatar/" | |||
@@ -369,9 +366,11 @@ func newLogService() { | |||
log.Fatal(4, "Unknown log mode: %s", mode) | |||
} | |||
validLevels := []string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"} | |||
// Log level. | |||
levelName := Cfg.Section("log."+mode).Key("LEVEL").In("Trace", | |||
[]string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"}) | |||
levelName := Cfg.Section("log."+mode).Key("LEVEL").In( | |||
Cfg.Section("log").Key("LEVEL").In("Trace", validLevels), | |||
validLevels) | |||
level, ok := logLevels[levelName] | |||
if !ok { | |||
log.Fatal(4, "Unknown log level: %s", levelName) | |||
@@ -238,28 +238,31 @@ func ListMyRepos(ctx *middleware.Context) { | |||
} | |||
numOwnRepos := len(ownRepos) | |||
collaRepos, err := models.GetCollaborativeRepos(ctx.User.Name) | |||
accessibleRepos, err := ctx.User.GetAccessibleRepositories() | |||
if err != nil { | |||
ctx.JSON(500, &base.ApiJsonErr{"GetCollaborativeRepos: " + err.Error(), base.DOC_URL}) | |||
ctx.JSON(500, &base.ApiJsonErr{"GetAccessibleRepositories: " + err.Error(), base.DOC_URL}) | |||
return | |||
} | |||
repos := make([]*api.Repository, numOwnRepos+len(collaRepos)) | |||
repos := make([]*api.Repository, numOwnRepos+len(accessibleRepos)) | |||
for i := range ownRepos { | |||
repos[i] = ToApiRepository(ctx.User, ownRepos[i], api.Permission{true, true, true}) | |||
} | |||
for i := range collaRepos { | |||
if err = collaRepos[i].GetOwner(); err != nil { | |||
i := numOwnRepos | |||
for repo, access := range accessibleRepos { | |||
if err = repo.GetOwner(); err != nil { | |||
ctx.JSON(500, &base.ApiJsonErr{"GetOwner: " + err.Error(), base.DOC_URL}) | |||
return | |||
} | |||
j := i + numOwnRepos | |||
repos[j] = ToApiRepository(collaRepos[i].Owner, collaRepos[i].Repository, api.Permission{false, collaRepos[i].CanPush, true}) | |||
repos[i] = ToApiRepository(repo.Owner, repo, api.Permission{false, access >= models.ACCESS_MODE_WRITE, true}) | |||
// FIXME: cache result to reduce DB query? | |||
if collaRepos[i].Owner.IsOrganization() && collaRepos[i].Owner.IsOwnedBy(ctx.User.Id) { | |||
repos[j].Permissions.Admin = true | |||
if repo.Owner.IsOrganization() && repo.Owner.IsOwnedBy(ctx.User.Id) { | |||
repos[i].Permissions.Admin = true | |||
} | |||
i++ | |||
} | |||
ctx.JSON(200, &repos) | |||
@@ -12,7 +12,7 @@ import ( | |||
) | |||
func GetRepoRawFile(ctx *middleware.Context) { | |||
if ctx.Repo.Repository.IsPrivate && !ctx.Repo.HasAccess { | |||
if !ctx.Repo.HasAccess() { | |||
ctx.Error(404) | |||
return | |||
} | |||
@@ -224,6 +224,7 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) { | |||
cfg.Section("session").Key("PROVIDER").SetValue("file") | |||
cfg.Section("log").Key("MODE").SetValue("file") | |||
cfg.Section("log").Key("LEVEL").SetValue("Info") | |||
cfg.Section("security").Key("INSTALL_LOCK").SetValue("true") | |||
cfg.Section("security").Key("SECRET_KEY").SetValue(base.GetRandomString(15)) | |||
@@ -165,14 +165,14 @@ func NewTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) { | |||
} | |||
// Validate permission level. | |||
var auth models.AuthorizeType | |||
var auth models.AccessMode | |||
switch form.Permission { | |||
case "read": | |||
auth = models.ORG_READABLE | |||
auth = models.ACCESS_MODE_READ | |||
case "write": | |||
auth = models.ORG_WRITABLE | |||
auth = models.ACCESS_MODE_WRITE | |||
case "admin": | |||
auth = models.ORG_ADMIN | |||
auth = models.ACCESS_MODE_ADMIN | |||
default: | |||
ctx.Error(401) | |||
return | |||
@@ -181,7 +181,7 @@ func NewTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) { | |||
org := ctx.Org.Organization | |||
t := &models.Team{ | |||
OrgId: org.Id, | |||
OrgID: org.Id, | |||
Name: form.TeamName, | |||
Description: form.Description, | |||
Authorize: auth, | |||
@@ -246,14 +246,14 @@ func EditTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) { | |||
isAuthChanged := false | |||
if !t.IsOwnerTeam() { | |||
// Validate permission level. | |||
var auth models.AuthorizeType | |||
var auth models.AccessMode | |||
switch form.Permission { | |||
case "read": | |||
auth = models.ORG_READABLE | |||
auth = models.ACCESS_MODE_READ | |||
case "write": | |||
auth = models.ORG_WRITABLE | |||
auth = models.ACCESS_MODE_WRITE | |||
case "admin": | |||
auth = models.ORG_ADMIN | |||
auth = models.ACCESS_MODE_ADMIN | |||
default: | |||
ctx.Error(401) | |||
return | |||
@@ -131,18 +131,18 @@ func Http(ctx *middleware.Context) { | |||
} | |||
if !isPublicPull { | |||
var tp = models.WRITABLE | |||
var tp = models.ACCESS_MODE_WRITE | |||
if isPull { | |||
tp = models.READABLE | |||
tp = models.ACCESS_MODE_READ | |||
} | |||
has, err := models.HasAccess(authUsername, username+"/"+reponame, tp) | |||
has, err := models.HasAccess(authUser, repo, tp) | |||
if err != nil { | |||
ctx.Handle(401, "no basic auth and digit auth", nil) | |||
return | |||
} else if !has { | |||
if tp == models.READABLE { | |||
has, err = models.HasAccess(authUsername, username+"/"+reponame, models.WRITABLE) | |||
if tp == models.ACCESS_MODE_READ { | |||
has, err = models.HasAccess(authUser, repo, models.ACCESS_MODE_WRITE) | |||
if err != nil || !has { | |||
ctx.Handle(401, "no basic auth and digit auth", nil) | |||
return | |||
@@ -152,6 +152,11 @@ func Http(ctx *middleware.Context) { | |||
return | |||
} | |||
} | |||
if !isPull && repo.IsMirror { | |||
ctx.Handle(401, "can't push to mirror", nil) | |||
return | |||
} | |||
} | |||
} | |||
@@ -174,7 +174,7 @@ func CreateIssue(ctx *middleware.Context) { | |||
return | |||
} | |||
us, err := models.GetCollaborators(strings.TrimPrefix(ctx.Repo.RepoLink, "/")) | |||
us, err := ctx.Repo.Repository.GetCollaborators() | |||
if err != nil { | |||
ctx.Handle(500, "issue.CreateIssue(GetCollaborators)", err) | |||
return | |||
@@ -218,7 +218,7 @@ func CreateIssuePost(ctx *middleware.Context, form auth.CreateIssueForm) { | |||
return | |||
} | |||
_, err = models.GetCollaborators(strings.TrimPrefix(ctx.Repo.RepoLink, "/")) | |||
_, err = ctx.Repo.Repository.GetCollaborators() | |||
if err != nil { | |||
send(500, nil, err) | |||
return | |||
@@ -230,7 +230,7 @@ func CreateIssuePost(ctx *middleware.Context, form auth.CreateIssueForm) { | |||
} | |||
// Only collaborators can assign. | |||
if !ctx.Repo.IsOwner { | |||
if !ctx.Repo.IsOwner() { | |||
form.AssigneeId = 0 | |||
} | |||
issue := &models.Issue{ | |||
@@ -246,8 +246,8 @@ func CreateIssuePost(ctx *middleware.Context, form auth.CreateIssueForm) { | |||
if err := models.NewIssue(issue); err != nil { | |||
send(500, nil, err) | |||
return | |||
} else if err := models.NewIssueUserPairs(issue.RepoId, issue.Id, ctx.Repo.Owner.Id, | |||
ctx.User.Id, form.AssigneeId, ctx.Repo.Repository.Name); err != nil { | |||
} else if err := models.NewIssueUserPairs(ctx.Repo.Repository, issue.Id, ctx.Repo.Owner.Id, | |||
ctx.User.Id, form.AssigneeId); err != nil { | |||
send(500, nil, err) | |||
return | |||
} | |||
@@ -384,7 +384,7 @@ func ViewIssue(ctx *middleware.Context) { | |||
} | |||
// Get all collaborators. | |||
ctx.Data["Collaborators"], err = models.GetCollaborators(strings.TrimPrefix(ctx.Repo.RepoLink, "/")) | |||
ctx.Data["Collaborators"], err = ctx.Repo.Repository.GetCollaborators() | |||
if err != nil { | |||
ctx.Handle(500, "issue.CreateIssue(GetCollaborators)", err) | |||
return | |||
@@ -434,7 +434,7 @@ func ViewIssue(ctx *middleware.Context) { | |||
ctx.Data["Title"] = issue.Name | |||
ctx.Data["Issue"] = issue | |||
ctx.Data["Comments"] = comments | |||
ctx.Data["IsIssueOwner"] = ctx.Repo.IsOwner || (ctx.IsSigned && issue.PosterId == ctx.User.Id) | |||
ctx.Data["IsIssueOwner"] = ctx.Repo.IsOwner() || (ctx.IsSigned && issue.PosterId == ctx.User.Id) | |||
ctx.Data["IsRepoToolbarIssues"] = true | |||
ctx.Data["IsRepoToolbarIssuesList"] = false | |||
ctx.HTML(200, ISSUE_VIEW) | |||
@@ -457,7 +457,7 @@ func UpdateIssue(ctx *middleware.Context, form auth.CreateIssueForm) { | |||
return | |||
} | |||
if ctx.User.Id != issue.PosterId && !ctx.Repo.IsOwner { | |||
if ctx.User.Id != issue.PosterId && !ctx.Repo.IsOwner() { | |||
ctx.Error(403) | |||
return | |||
} | |||
@@ -484,7 +484,7 @@ func UpdateIssue(ctx *middleware.Context, form auth.CreateIssueForm) { | |||
} | |||
func UpdateIssueLabel(ctx *middleware.Context) { | |||
if !ctx.Repo.IsOwner { | |||
if !ctx.Repo.IsOwner() { | |||
ctx.Error(403) | |||
return | |||
} | |||
@@ -561,7 +561,7 @@ func UpdateIssueLabel(ctx *middleware.Context) { | |||
} | |||
func UpdateIssueMilestone(ctx *middleware.Context) { | |||
if !ctx.Repo.IsOwner { | |||
if !ctx.Repo.IsOwner() { | |||
ctx.Error(403) | |||
return | |||
} | |||
@@ -607,7 +607,7 @@ func UpdateIssueMilestone(ctx *middleware.Context) { | |||
} | |||
func UpdateAssignee(ctx *middleware.Context) { | |||
if !ctx.Repo.IsOwner { | |||
if !ctx.Repo.IsOwner() { | |||
ctx.Error(403) | |||
return | |||
} | |||
@@ -753,7 +753,7 @@ func Comment(ctx *middleware.Context) { | |||
// Check if issue owner changes the status of issue. | |||
var newStatus string | |||
if ctx.Repo.IsOwner || issue.PosterId == ctx.User.Id { | |||
if ctx.Repo.IsOwner() || issue.PosterId == ctx.User.Id { | |||
newStatus = ctx.Query("change_status") | |||
} | |||
if len(newStatus) > 0 { | |||
@@ -41,7 +41,7 @@ func Releases(ctx *middleware.Context) { | |||
tags := make([]*models.Release, len(rawTags)) | |||
for i, rawTag := range rawTags { | |||
for j, rel := range rels { | |||
if rel == nil || (rel.IsDraft && !ctx.Repo.IsOwner) { | |||
if rel == nil || (rel.IsDraft && !ctx.Repo.IsOwner()) { | |||
continue | |||
} | |||
if rel.TagName == rawTag { | |||
@@ -140,7 +140,7 @@ func Releases(ctx *middleware.Context) { | |||
} | |||
func NewRelease(ctx *middleware.Context) { | |||
if !ctx.Repo.IsOwner { | |||
if !ctx.Repo.IsOwner() { | |||
ctx.Handle(403, "release.ReleasesNew", nil) | |||
return | |||
} | |||
@@ -153,7 +153,7 @@ func NewRelease(ctx *middleware.Context) { | |||
} | |||
func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) { | |||
if !ctx.Repo.IsOwner { | |||
if !ctx.Repo.IsOwner() { | |||
ctx.Handle(403, "release.ReleasesNew", nil) | |||
return | |||
} | |||
@@ -211,7 +211,7 @@ func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) { | |||
} | |||
func EditRelease(ctx *middleware.Context) { | |||
if !ctx.Repo.IsOwner { | |||
if !ctx.Repo.IsOwner() { | |||
ctx.Handle(403, "release.ReleasesEdit", nil) | |||
return | |||
} | |||
@@ -234,7 +234,7 @@ func EditRelease(ctx *middleware.Context) { | |||
} | |||
func EditReleasePost(ctx *middleware.Context, form auth.EditReleaseForm) { | |||
if !ctx.Repo.IsOwner { | |||
if !ctx.Repo.IsOwner() { | |||
ctx.Handle(403, "release.EditReleasePost", nil) | |||
return | |||
} | |||
@@ -349,7 +349,7 @@ func Action(ctx *middleware.Context) { | |||
case "unstar": | |||
err = models.StarRepo(ctx.User.Id, ctx.Repo.Repository.Id, false) | |||
case "desc": | |||
if !ctx.Repo.IsOwner { | |||
if !ctx.Repo.IsOwner() { | |||
ctx.Error(404) | |||
return | |||
} | |||
@@ -8,7 +8,6 @@ import ( | |||
"encoding/json" | |||
"errors" | |||
"fmt" | |||
"path" | |||
"strings" | |||
"time" | |||
@@ -54,15 +53,11 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) { | |||
newRepoName := form.RepoName | |||
// Check if repository name has been changed. | |||
if ctx.Repo.Repository.Name != newRepoName { | |||
isExist, err := models.IsRepositoryExist(ctx.Repo.Owner, newRepoName) | |||
if err != nil { | |||
ctx.Handle(500, "IsRepositoryExist", err) | |||
return | |||
} else if isExist { | |||
if models.IsRepositoryExist(ctx.Repo.Owner, newRepoName) { | |||
ctx.Data["Err_RepoName"] = true | |||
ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), SETTINGS_OPTIONS, nil) | |||
return | |||
} else if err = models.ChangeRepositoryName(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name, newRepoName); err != nil { | |||
} else if err := models.ChangeRepositoryName(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name, newRepoName); err != nil { | |||
if err == models.ErrRepoNameIllegal { | |||
ctx.Data["Err_RepoName"] = true | |||
ctx.RenderWithErr(ctx.Tr("form.illegal_repo_name"), SETTINGS_OPTIONS, nil) | |||
@@ -169,22 +164,12 @@ func SettingsCollaboration(ctx *middleware.Context) { | |||
ctx.Data["Title"] = ctx.Tr("repo.settings") | |||
ctx.Data["PageIsSettingsCollaboration"] = true | |||
repoLink := path.Join(ctx.Repo.Owner.LowerName, ctx.Repo.Repository.LowerName) | |||
if ctx.Req.Method == "POST" { | |||
name := strings.ToLower(ctx.Query("collaborator")) | |||
if len(name) == 0 || ctx.Repo.Owner.LowerName == name { | |||
ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path) | |||
return | |||
} | |||
has, err := models.HasAccess(name, repoLink, models.WRITABLE) | |||
if err != nil { | |||
ctx.Handle(500, "HasAccess", err) | |||
return | |||
} else if has { | |||
ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path) | |||
return | |||
} | |||
u, err := models.GetUserByName(name) | |||
if err != nil { | |||
@@ -204,9 +189,8 @@ func SettingsCollaboration(ctx *middleware.Context) { | |||
return | |||
} | |||
if err = models.AddAccess(&models.Access{UserName: name, RepoName: repoLink, | |||
Mode: models.WRITABLE}); err != nil { | |||
ctx.Handle(500, "AddAccess", err) | |||
if err = ctx.Repo.Repository.AddCollaborator(u); err != nil { | |||
ctx.Handle(500, "AddCollaborator", err) | |||
return | |||
} | |||
@@ -225,50 +209,27 @@ func SettingsCollaboration(ctx *middleware.Context) { | |||
// Delete collaborator. | |||
remove := strings.ToLower(ctx.Query("remove")) | |||
if len(remove) > 0 && remove != ctx.Repo.Owner.LowerName { | |||
needDelete := true | |||
if ctx.User.IsOrganization() { | |||
// Check if user belongs to a team that has access to this repository. | |||
auth, err := models.GetHighestAuthorize(ctx.Repo.Owner.Id, ctx.User.Id, ctx.Repo.Repository.Id, 0) | |||
if err != nil { | |||
ctx.Handle(500, "GetHighestAuthorize", err) | |||
return | |||
} | |||
if auth > 0 { | |||
needDelete = false | |||
} | |||
u, err := models.GetUserByName(remove) | |||
if err != nil { | |||
ctx.Handle(500, "GetUserByName", err) | |||
return | |||
} | |||
if needDelete { | |||
if err := models.DeleteAccess(&models.Access{UserName: remove, RepoName: repoLink}); err != nil { | |||
ctx.Handle(500, "DeleteAccess", err) | |||
return | |||
} | |||
if err := ctx.Repo.Repository.DeleteCollaborator(u); err != nil { | |||
ctx.Handle(500, "DeleteCollaborator", err) | |||
return | |||
} | |||
ctx.Flash.Success(ctx.Tr("repo.settings.remove_collaborator_success")) | |||
ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") | |||
return | |||
} | |||
names, err := models.GetCollaboratorNames(repoLink) | |||
users, err := ctx.Repo.Repository.GetCollaborators() | |||
if err != nil { | |||
ctx.Handle(500, "GetCollaborators", err) | |||
return | |||
} | |||
collaborators := make([]*models.User, 0, len(names)) | |||
for _, name := range names { | |||
u, err := models.GetUserByName(name) | |||
if err != nil { | |||
ctx.Handle(500, "GetUserByName", err) | |||
return | |||
} | |||
// Does not show organization members. | |||
if ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOrgMember(u.Id) { | |||
continue | |||
} | |||
collaborators = append(collaborators, u) | |||
} | |||
ctx.Data["Collaborators"] = collaborators | |||
ctx.Data["Collaborators"] = users | |||
ctx.HTML(200, COLLABORATION) | |||
} | |||
@@ -49,13 +49,19 @@ func Dashboard(ctx *middleware.Context) { | |||
} else { | |||
// Normal user. | |||
ctxUser = ctx.User | |||
collaborates, err := models.GetCollaborativeRepos(ctxUser.Name) | |||
collaborates, err := ctx.User.GetAccessibleRepositories() | |||
if err != nil { | |||
ctx.Handle(500, "GetCollaborativeRepos", err) | |||
ctx.Handle(500, "GetAccessibleRepositories", err) | |||
return | |||
} | |||
ctx.Data["CollaborateCount"] = len(collaborates) | |||
ctx.Data["CollaborativeRepos"] = collaborates | |||
repositories := make([]*models.Repository, 0, len(collaborates)) | |||
for repo := range collaborates { | |||
repositories = append(repositories, repo) | |||
} | |||
ctx.Data["CollaborateCount"] = len(repositories) | |||
ctx.Data["CollaborativeRepos"] = repositories | |||
} | |||
ctx.Data["ContextUser"] = ctxUser | |||
@@ -97,10 +103,14 @@ func Dashboard(ctx *middleware.Context) { | |||
feeds := make([]*models.Action, 0, len(actions)) | |||
for _, act := range actions { | |||
if act.IsPrivate { | |||
if has, _ := models.HasAccess(ctx.User.Name, act.RepoUserName+"/"+act.RepoName, | |||
models.READABLE); !has { | |||
continue | |||
// This prevents having to retrieve the repository for each action | |||
repo := &models.Repository{Id: act.RepoId, IsPrivate: true} | |||
if act.RepoUserName != ctx.User.LowerName { | |||
if has, _ := models.HasAccess(ctx.User, repo, models.ACCESS_MODE_READ); !has { | |||
continue | |||
} | |||
} | |||
} | |||
// FIXME: cache results? | |||
u, err := models.GetUserByName(act.ActUserName) | |||
@@ -205,10 +215,14 @@ func Profile(ctx *middleware.Context) { | |||
if !ctx.IsSigned { | |||
continue | |||
} | |||
if has, _ := models.HasAccess(ctx.User.Name, act.RepoUserName+"/"+act.RepoName, | |||
models.READABLE); !has { | |||
continue | |||
// This prevents having to retrieve the repository for each action | |||
repo := &models.Repository{Id: act.RepoId, IsPrivate: true} | |||
if act.RepoUserName != ctx.User.LowerName { | |||
if has, _ := models.HasAccess(ctx.User, repo, models.ACCESS_MODE_READ); !has { | |||
continue | |||
} | |||
} | |||
} | |||
// FIXME: cache results? | |||
u, err := models.GetUserByName(act.ActUserName) | |||
@@ -1 +1 @@ | |||
0.5.14.0222 Beta | |||
0.5.16.0228 Beta |
@@ -1,7 +1,7 @@ | |||
</div> | |||
<footer id="footer"> | |||
<div class="container clear"> | |||
<p class="left" id="footer-rights">© 2015 GoGits · {{.i18n.Tr "version"}}: {{AppVer}} · {{.i18n.Tr "page"}}: <strong>{{LoadTimes .PageStartTime}}</strong> · | |||
<p class="left" id="footer-rights">© 2015 Gogs · {{.i18n.Tr "version"}}: {{AppVer}} · {{.i18n.Tr "page"}}: <strong>{{LoadTimes .PageStartTime}}</strong> · | |||
{{.i18n.Tr "template"}}: <strong>{{call .TmplLoadTimes}}</strong></p> | |||
<div class="right" id="footer-links"> | |||
@@ -27,7 +27,7 @@ | |||
</div> | |||
<div id="org-repo-list"> | |||
{{range .Repos}} | |||
{{if or (not .IsPrivate) (.HasAccess $.SignedUser.Name)}} | |||
{{if or (not .IsPrivate) (.HasAccess $.SignedUser)}} | |||
<div class="org-repo-item"> | |||
<ul class="org-repo-status right"> | |||
<li><i class="octicon octicon-star"></i> {{.NumStars}}</li> | |||
@@ -49,7 +49,7 @@ | |||
</a> | |||
</li> | |||
<li id="repo-header-fork"> | |||
<a id="repo-header-fork-btn" {{if or (not $.IsRepositoryTrueOwner) $.Owner.IsOrganization}}href="{{AppSubUrl}}/repo/fork?fork_id={{.Id}}"{{end}}> | |||
<a id="repo-header-fork-btn" {{if or (not $.IsRepositoryAdmin) $.Owner.IsOrganization}}href="{{AppSubUrl}}/repo/fork?fork_id={{.Id}}"{{end}}> | |||
<button class="btn btn-gray text-bold btn-radius"> | |||
<i class="octicon octicon-repo-forked"></i>{{$.i18n.Tr "repo.fork"}} | |||
<span class="num">{{.NumForks}}</span> | |||
@@ -20,7 +20,7 @@ | |||
<!-- <li> | |||
<a class="radius" href="#"><i class="octicon octicon-organization"></i>contributors <span class="num right label label-gray label-radius">43</span></a> | |||
</li> --> | |||
{{if .IsRepositoryTrueOwner}} | |||
{{if .IsRepositoryAdmin}} | |||
<li class="border-bottom"></li> | |||
<li> | |||
<a class="radius" href="{{.RepoLink}}/settings"><i class="octicon octicon-tools"></i>{{.i18n.Tr "repo.settings"}}</a> | |||
@@ -35,7 +35,7 @@ | |||
<li><a href="#">Pulse</a></li> | |||
<li><a href="#">Network</a></li> | |||
</ul> | |||
</li> -->{{end}}{{if .IsRepositoryTrueOwner}} | |||
</li> -->{{end}}{{if .IsRepositoryAdmin}} | |||
<li class="{{if .IsRepoToolbarSetting}}active{{end}}"><a href="{{.RepoLink}}/settings">Settings</a> | |||
</li>{{end}} | |||
</ul> | |||
@@ -74,7 +74,7 @@ | |||
<div class="tab-pane active"> | |||
<div id="org-repo-list"> | |||
{{range .Repos}} | |||
{{if or (not .IsPrivate) (.HasAccess $.SignedUserName)}} | |||
{{if or (not .IsPrivate) (.HasAccess $.SignedUser)}} | |||
<div class="org-repo-item"> | |||
<ul class="org-repo-status right"> | |||
<li><i class="octicon octicon-star"></i> {{.NumStars}}</li> | |||