From 76f89047183fc70a077ac37b4168d90539f12e09 Mon Sep 17 00:00:00 2001 From: Peter Smit Date: Fri, 23 Jan 2015 09:54:16 +0200 Subject: Introducing Collaboration Struct --- routers/api/v1/repo.go | 21 ++++++++++-------- routers/repo/issue.go | 26 +++++++++++----------- routers/repo/setting.go | 57 ++++++++++--------------------------------------- routers/user/home.go | 14 ++++++++---- 4 files changed, 46 insertions(+), 72 deletions(-) (limited to 'routers') diff --git a/routers/api/v1/repo.go b/routers/api/v1/repo.go index fbf9c73ea7..469e4808fe 100644 --- a/routers/api/v1/repo.go +++ b/routers/api/v1/repo.go @@ -237,28 +237,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.WRITABLE, 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) diff --git a/routers/repo/issue.go b/routers/repo/issue.go index 999fd0a892..921348dbd1 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -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 @@ -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 @@ -1122,18 +1122,18 @@ func IssueGetAttachment(ctx *middleware.Context) { // testing route handler for new issue ui page // todo : move to Issue() function -func Issues2(ctx *middleware.Context){ - ctx.HTML(200,"repo/issue2/list") +func Issues2(ctx *middleware.Context) { + ctx.HTML(200, "repo/issue2/list") } -func PullRequest2(ctx *middleware.Context){ - ctx.HTML(200,"repo/pr2/list") +func PullRequest2(ctx *middleware.Context) { + ctx.HTML(200, "repo/pr2/list") } -func Labels2(ctx *middleware.Context){ - ctx.HTML(200,"repo/issue2/labels") +func Labels2(ctx *middleware.Context) { + ctx.HTML(200, "repo/issue2/labels") } -func Milestones2(ctx *middleware.Context){ - ctx.HTML(200,"repo/milestone2/list") +func Milestones2(ctx *middleware.Context) { + ctx.HTML(200, "repo/milestone2/list") } diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 33bf1eab28..a6f50d3068 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -10,7 +10,6 @@ import ( "fmt" "strings" "time" - "path" "github.com/Unknwon/com" @@ -170,22 +169,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 { @@ -205,9 +194,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 } @@ -226,50 +214,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) } diff --git a/routers/user/home.go b/routers/user/home.go index 1aabe08774..5b02154c10 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -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 -- cgit v1.2.3 From 4e79adf6b5bf7ec7bc3b2b47469baafd1cb0b774 Mon Sep 17 00:00:00 2001 From: Peter Smit Date: Thu, 5 Feb 2015 15:29:08 +0200 Subject: Refactoring of the Access Table This commit does a lot of the work of refactoring the access table in a table with id's instead of strings. The result does compile, but has not been tested. It may eat your kittens. --- cmd/serve.go | 41 ++++--- models/access.go | 176 ++++++++++++++++++----------- models/migrations/migrations.go | 6 + models/org.go | 242 +++++----------------------------------- models/repo.go | 203 +++------------------------------ models/user.go | 56 +--------- modules/middleware/org.go | 2 +- modules/middleware/repo.go | 155 ++++++------------------- routers/api/v1/repo.go | 2 +- routers/org/teams.go | 16 +-- routers/repo/http.go | 10 +- routers/user/home.go | 7 +- 12 files changed, 235 insertions(+), 681 deletions(-) (limited to 'routers') diff --git a/cmd/serve.go b/cmd/serve.go index 2390962342..d9d1a06b4e 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -8,7 +8,6 @@ import ( "fmt" "os" "os/exec" - "path" "path/filepath" "strings" "time" @@ -59,19 +58,19 @@ 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.WriteAccess, + "git upload-pack": models.WriteAccess, + "git-upload-archive": models.WriteAccess, } - 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.ReadAccess, + "git receive-pack": models.ReadAccess, } ) -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 } @@ -130,9 +129,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.WriteAccess) if err != nil { println("Gogs: internal error:", err.Error()) log.GitLogger.Fatal(2, "Fail to check write access:", err) @@ -141,21 +150,11 @@ func runServ(k *cli.Context) { 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.IsPrivate { break } - has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.READABLE) + has, err := models.HasAccess(user, repo, models.ReadAccess) if err != nil { println("Gogs: internal error:", err.Error()) log.GitLogger.Fatal(2, "Fail to check read access:", err) diff --git a/models/access.go b/models/access.go index 64bb921409..ee678a0701 100644 --- a/models/access.go +++ b/models/access.go @@ -4,92 +4,80 @@ package models -import ( - "strings" - "time" +//import ( +// "github.com/go-xorm/xorm" +//) - "github.com/go-xorm/xorm" -) - -type AccessType int +type AccessMode int const ( - READABLE AccessType = iota + 1 - WRITABLE + NoAccess AccessMode = iota + ReadAccess + WriteAccess + AdminAccess + OwnerAccess ) -// Access represents the accessibility of user to repository. -type Access struct { - Id int64 - UserName string `xorm:"UNIQUE(s)"` - RepoName string `xorm:"UNIQUE(s)"` // / - Mode AccessType `xorm:"UNIQUE(s)"` - Created time.Time `xorm:"CREATED"` -} - -// 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 +func maxAccessMode(modes ...AccessMode) AccessMode { + max := NoAccess + for _, mode := range modes { + if mode > max { + max = mode + } + } + return max } -// 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 +// 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 `xorm:"pk autoincr"` + UserID int64 `xorm:"UNIQUE(s)"` + RepoID int64 `xorm:"UNIQUE(s)"` + Mode AccessMode } -// 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, r *Repository, testMode AccessMode) (bool, error) { + mode, err := AccessLevel(u, r) + return testMode <= mode, err } -// 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 +// Return 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, r *Repository) (AccessMode, error) { + mode := NoAccess + if !r.IsPrivate { + mode = ReadAccess } - return nil -} -// HasAccess returns true if someone can read or write to given repository. -// The repoName should be in format /. -func HasAccess(uname, repoName string, mode AccessType) (bool, error) { - if len(repoName) == 0 { - return false, nil - } - access := &Access{ - UserName: strings.ToLower(uname), - RepoName: strings.ToLower(repoName), - } - has, err := x.Get(access) - if err != nil { - return false, err - } else if !has { - return false, nil - } else if mode > access.Mode { - return false, nil + if u != nil { + if u.Id == r.OwnerId { + return OwnerAccess, nil + } + + a := &Access{UserID: u.Id, RepoID: r.Id} + if has, err := x.Get(a); !has || err != nil { + return mode, err + } + return a.Mode, nil } - return true, nil + + return mode, nil } // GetAccessibleRepositories finds all repositories where a user has access to, // besides his own. -func (u *User) GetAccessibleRepositories() (map[*Repository]AccessType, error) { +func (u *User) GetAccessibleRepositories() (map[*Repository]AccessMode, error) { accesses := make([]*Access, 0, 10) - if err := x.Find(&accesses, &Access{UserName: u.LowerName}); err != nil { + if err := x.Find(&accesses, &Access{UserID: u.Id}); err != nil { return nil, err } - repos := make(map[*Repository]AccessType, len(accesses)) + repos := make(map[*Repository]AccessMode, len(accesses)) for _, access := range accesses { - repo, err := GetRepositoryByRef(access.RepoName) + repo, err := GetRepositoryById(access.RepoID) if err != nil { return nil, err } @@ -102,3 +90,65 @@ func (u *User) GetAccessibleRepositories() (map[*Repository]AccessType, error) { return repos, nil } + +// Recalculate all accesses for repository +func (r *Repository) RecalcAccessSess() error { + accessMap := make(map[int64]AccessMode, 20) + + // Give all collaborators write access + collaborators, err := r.GetCollaborators() + if err != nil { + return err + } + for _, c := range collaborators { + accessMap[c.Id] = WriteAccess + } + + if err := r.GetOwner(); err != nil { + return err + } + if r.Owner.IsOrganization() { + if err = r.Owner.GetTeams(); err != nil { + return err + } + + for _, team := range r.Owner.Teams { + if !(team.IsOwnerTeam() || team.HasRepository(r)) { + continue + } + + if err = team.GetMembers(); err != nil { + return err + } + for _, u := range team.Members { + accessMap[u.Id] = maxAccessMode(accessMap[u.Id], team.Authorize) + } + } + } + + minMode := ReadAccess + if !r.IsPrivate { + minMode = WriteAccess + } + + newAccesses := make([]Access, 0, len(accessMap)) + for userID, mode := range accessMap { + if userID == r.OwnerId || mode <= minMode { + continue + } + newAccesses = append(newAccesses, Access{UserID: userID, RepoID: r.Id, Mode: mode}) + } + + // Delete old accesses for repository + if _, err = x.Delete(&Access{RepoID: r.Id}); err != nil { + return err + } + + // And insert the new ones + if _, err = x.Insert(newAccesses); err != nil { + return err + } + + return nil + +} diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index f0ed10b7aa..37986b68bd 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -21,6 +21,7 @@ type Version struct { // If you want to "retire" a migration, replace it with "expiredMigration" var migrations = []migration{ accessToCollaboration, + accessRefactor, } // Migrate database to current version @@ -158,3 +159,8 @@ func accessToCollaboration(x *xorm.Engine) error { } return nil } + +func accessRefactor(x *xorm.Engine) error { + //TODO + return nil +} diff --git a/models/org.go b/models/org.go index 5431a111c3..5e3bb0e070 100644 --- a/models/org.go +++ b/models/org.go @@ -6,9 +6,7 @@ package models import ( "errors" - "fmt" "os" - "path" "strings" "github.com/Unknwon/com" @@ -137,7 +135,7 @@ func CreateOrganization(org, owner *User) (*User, error) { OrgId: org.Id, LowerName: strings.ToLower(OWNER_TEAM), Name: OWNER_TEAM, - Authorize: ORG_ADMIN, + Authorize: OwnerAccess, NumMembers: 1, } if _, err = sess.Insert(t); err != nil { @@ -372,10 +370,10 @@ func RemoveOrgUser(orgId, uid int64) error { return err } access := &Access{ - UserName: u.LowerName, + UserID: u.Id, } for _, repo := range org.Repos { - access.RepoName = path.Join(org.LowerName, repo.LowerName) + access.RepoID = repo.Id if _, err = sess.Delete(access); err != nil { sess.Rollback() return err @@ -406,21 +404,6 @@ func RemoveOrgUser(orgId, uid int64) error { // |____| \___ >____ /__|_| / // \/ \/ \/ -type AuthorizeType int - -const ( - ORG_READABLE AuthorizeType = iota + 1 - ORG_WRITABLE - ORG_ADMIN -) - -func AuthorizeToAccessType(auth AuthorizeType) AccessType { - if auth == ORG_READABLE { - return READABLE - } - return WRITABLE -} - const OWNER_TEAM = "Owners" // Team represents a organization team. @@ -430,7 +413,7 @@ type Team struct { LowerName string Name string Description string - Authorize AuthorizeType + Authorize AccessMode RepoIds string `xorm:"TEXT"` Repos []*Repository `xorm:"-"` Members []*User `xorm:"-"` @@ -485,25 +468,6 @@ func (t *Team) RemoveMember(uid int64) error { return RemoveTeamMember(t.OrgId, t.Id, uid) } -// addAccessWithAuthorize inserts or updates access with given mode. -func addAccessWithAuthorize(sess *xorm.Session, access *Access, mode AccessType) error { - has, err := x.Get(access) - if err != nil { - return fmt.Errorf("fail to get access: %v", err) - } - access.Mode = mode - if has { - if _, err = sess.Id(access.Id).Update(access); err != nil { - return fmt.Errorf("fail to update access: %v", err) - } - } else { - if _, err = sess.Insert(access); err != nil { - return fmt.Errorf("fail to insert access: %v", err) - } - } - return nil -} - // AddRepository adds new repository to team of organization. func (t *Team) AddRepository(repo *Repository) (err error) { idStr := "$" + com.ToStr(repo.Id) + "|" @@ -532,26 +496,12 @@ func (t *Team) AddRepository(repo *Repository) (err error) { return err } - // Give access to team members. - mode := AuthorizeToAccessType(t.Authorize) + if err = repo.RecalcAccessSess(); err != nil { + sess.Rollback() + return err + } for _, u := range t.Members { - auth, err := GetHighestAuthorize(t.OrgId, u.Id, repo.Id, t.Id) - if err != nil { - sess.Rollback() - return err - } - - access := &Access{ - UserName: u.LowerName, - RepoName: path.Join(repo.Owner.LowerName, repo.LowerName), - } - if auth < t.Authorize { - if err = addAccessWithAuthorize(sess, access, mode); err != nil { - sess.Rollback() - return err - } - } if err = WatchRepo(u.Id, repo.Id, true); err != nil { sess.Rollback() return err @@ -560,6 +510,11 @@ func (t *Team) AddRepository(repo *Repository) (err error) { return sess.Commit() } +func (t *Team) HasRepository(r *Repository) bool { + idStr := "$" + com.ToStr(r.Id) + "|" + return strings.Contains(t.RepoIds, idStr) +} + // RemoveRepository removes repository from team of organization. func (t *Team) RemoveRepository(repoId int64) error { idStr := "$" + com.ToStr(repoId) + "|" @@ -591,32 +546,16 @@ func (t *Team) RemoveRepository(repoId int64) error { return err } - // Remove access to team members. + if err = repo.RecalcAccessSess(); err != nil { + sess.Rollback() + return err + } + for _, u := range t.Members { - auth, err := GetHighestAuthorize(t.OrgId, u.Id, repo.Id, t.Id) - if err != nil { + if err = WatchRepo(u.Id, repo.Id, false); err != nil { sess.Rollback() return err } - - access := &Access{ - UserName: u.LowerName, - RepoName: path.Join(repo.Owner.LowerName, repo.LowerName), - } - if auth == 0 { - if _, err = sess.Delete(access); err != nil { - sess.Rollback() - return fmt.Errorf("fail to delete access: %v", err) - } else if err = WatchRepo(u.Id, repo.Id, false); err != nil { - sess.Rollback() - return err - } - } else if auth < t.Authorize { - if err = addAccessWithAuthorize(sess, access, AuthorizeToAccessType(auth)); err != nil { - sess.Rollback() - return err - } - } } return sess.Commit() @@ -690,30 +629,6 @@ func GetTeamById(teamId int64) (*Team, error) { return t, nil } -// GetHighestAuthorize returns highest repository authorize level for given user and team. -func GetHighestAuthorize(orgId, uid, repoId, teamId int64) (AuthorizeType, error) { - ts, err := GetUserTeams(orgId, uid) - if err != nil { - return 0, err - } - - var auth AuthorizeType = 0 - for _, t := range ts { - // Not current team and has given repository. - if t.Id != teamId && strings.Contains(t.RepoIds, "$"+com.ToStr(repoId)+"|") { - // Fast return. - if t.Authorize == ORG_WRITABLE { - return ORG_WRITABLE, nil - } - if t.Authorize > auth { - auth = t.Authorize - } - } - } - - return auth, nil -} - // UpdateTeam updates information of team. func UpdateTeam(t *Team, authChanged bool) (err error) { if !IsLegalName(t.Name) { @@ -731,45 +646,14 @@ func UpdateTeam(t *Team, authChanged bool) (err error) { } // Update access for team members if needed. - if authChanged && !t.IsOwnerTeam() { + if authChanged { if err = t.GetRepositories(); err != nil { return err - } else if err = t.GetMembers(); err != nil { - return err - } - - // Get organization. - org, err := GetUserById(t.OrgId) - if err != nil { - return err } - // Update access. - mode := AuthorizeToAccessType(t.Authorize) - for _, repo := range t.Repos { - for _, u := range t.Members { - // ORG_WRITABLE is the highest authorize level for now. - // Skip checking others if current team has this level. - if t.Authorize < ORG_WRITABLE { - auth, err := GetHighestAuthorize(t.OrgId, u.Id, repo.Id, t.Id) - if err != nil { - sess.Rollback() - return err - } - if auth >= t.Authorize { - continue // Other team has higher or same authorize level. - } - } - - access := &Access{ - UserName: u.LowerName, - RepoName: path.Join(org.LowerName, repo.LowerName), - } - if err = addAccessWithAuthorize(sess, access, mode); err != nil { - sess.Rollback() - return err - } + if err = repo.RecalcAccessSess(); err != nil { + return err } } } @@ -805,29 +689,8 @@ func DeleteTeam(t *Team) error { // Delete all accesses. for _, repo := range t.Repos { - for _, u := range t.Members { - auth, err := GetHighestAuthorize(t.OrgId, u.Id, repo.Id, t.Id) - if err != nil { - sess.Rollback() - return err - } - - access := &Access{ - UserName: u.LowerName, - RepoName: path.Join(org.LowerName, repo.LowerName), - } - if auth == 0 { - if _, err = sess.Delete(access); err != nil { - sess.Rollback() - return fmt.Errorf("fail to delete access: %v", err) - } - } else if auth < t.Authorize { - // Downgrade authorize level. - if err = addAccessWithAuthorize(sess, access, AuthorizeToAccessType(auth)); err != nil { - sess.Rollback() - return err - } - } + if err = repo.RecalcAccessSess(); err != nil { + return err } } @@ -921,18 +784,6 @@ func AddTeamMember(orgId, teamId, uid int64) error { return err } - // Get organization. - org, err := GetUserById(orgId) - if err != nil { - return err - } - - // Get user. - u, err := GetUserById(uid) - if err != nil { - return err - } - sess := x.NewSession() defer sess.Close() if err = sess.Begin(); err != nil { @@ -954,24 +805,11 @@ func AddTeamMember(orgId, teamId, uid int64) error { } // Give access to team repositories. - mode := AuthorizeToAccessType(t.Authorize) for _, repo := range t.Repos { - auth, err := GetHighestAuthorize(t.OrgId, u.Id, repo.Id, teamId) - if err != nil { + if err = repo.RecalcAccessSess(); err != nil { sess.Rollback() return err } - - access := &Access{ - UserName: u.LowerName, - RepoName: path.Join(org.LowerName, repo.LowerName), - } - if auth < t.Authorize { - if err = addAccessWithAuthorize(sess, access, mode); err != nil { - sess.Rollback() - return err - } - } } // We make sure it exists before. @@ -1021,12 +859,6 @@ func removeTeamMemberWithSess(orgId, teamId, uid int64, sess *xorm.Session) erro return err } - // Get user. - u, err := GetUserById(uid) - if err != nil { - return err - } - tu := &TeamUser{ Uid: uid, OrgId: orgId, @@ -1043,32 +875,10 @@ func removeTeamMemberWithSess(orgId, teamId, uid int64, sess *xorm.Session) erro // Delete access to team repositories. for _, repo := range t.Repos { - auth, err := GetHighestAuthorize(t.OrgId, u.Id, repo.Id, teamId) - if err != nil { + if err = repo.RecalcAccessSess(); err != nil { sess.Rollback() return err } - - access := &Access{ - UserName: u.LowerName, - RepoName: path.Join(org.LowerName, repo.LowerName), - } - // Delete access if this is the last team user belongs to. - if auth == 0 { - if _, err = sess.Delete(access); err != nil { - sess.Rollback() - return fmt.Errorf("fail to delete access: %v", err) - } else if err = WatchRepo(u.Id, repo.Id, false); err != nil { - sess.Rollback() - return err - } - } else if auth < t.Authorize { - // Downgrade authorize level. - if err = addAccessWithAuthorize(sess, access, AuthorizeToAccessType(auth)); err != nil { - sess.Rollback() - return err - } - } } // This must exist. diff --git a/models/repo.go b/models/repo.go index a06f1d3e4e..5943dd31ad 100644 --- a/models/repo.go +++ b/models/repo.go @@ -206,14 +206,6 @@ func (repo *Repository) IsOwnedBy(u *User) bool { return repo.OwnerId == u.Id } -func (repo *Repository) HasAccess(uname string) bool { - if err := repo.GetOwner(); err != nil { - return false - } - has, _ := HasAccess(uname, path.Join(repo.Owner.Name, repo.Name), READABLE) - return has -} - // DescriptionHtml does special handles to description and return HTML string. func (repo *Repository) DescriptionHtml() template.HTML { sanitize := func(s string) string { @@ -553,36 +545,11 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror var t *Team // Owner team. - mode := WRITABLE - if mirror { - mode = READABLE - } - access := &Access{ - UserName: u.LowerName, - RepoName: path.Join(u.LowerName, repo.LowerName), - Mode: mode, - } + // TODO fix code for mirrors? + // Give access to all members in owner team. if u.IsOrganization() { - t, err = u.GetOwnerTeam() - if err != nil { - sess.Rollback() - return nil, err - } - if err = t.GetMembers(); err != nil { - sess.Rollback() - return nil, err - } - for _, u := range t.Members { - access.Id = 0 - access.UserName = u.LowerName - if _, err = sess.Insert(access); err != nil { - sess.Rollback() - return nil, err - } - } - } else { - if _, err = sess.Insert(access); err != nil { + if err = repo.RecalcAccessSess(); err != nil { sess.Rollback() return nil, err } @@ -712,37 +679,10 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error { } owner := repo.Owner - oldRepoLink := path.Join(owner.LowerName, repo.LowerName) - // Delete all access first if current owner is an organization. - if owner.IsOrganization() { - if _, err = sess.Where("repo_name=?", oldRepoLink).Delete(new(Access)); err != nil { - sess.Rollback() - return fmt.Errorf("fail to delete current accesses: %v", err) - } - } else { - // Delete current owner access. - if _, err = sess.Where("repo_name=?", oldRepoLink).And("user_name=?", owner.LowerName). - Delete(new(Access)); err != nil { - sess.Rollback() - return fmt.Errorf("fail to delete access(owner): %v", err) - } - // In case new owner has access. - if _, err = sess.Where("repo_name=?", oldRepoLink).And("user_name=?", newUser.LowerName). - Delete(new(Access)); err != nil { - sess.Rollback() - return fmt.Errorf("fail to delete access(new user): %v", err) - } - } - - // Change accesses to new repository path. - if _, err = sess.Where("repo_name=?", oldRepoLink). - Update(&Access{RepoName: path.Join(newUser.LowerName, repo.LowerName)}); err != nil { - sess.Rollback() - return fmt.Errorf("fail to update access(change reponame): %v", err) - } // Update repository. repo.OwnerId = newUser.Id + repo.Owner = newUser if _, err := sess.Id(repo.Id).Update(repo); err != nil { sess.Rollback() return err @@ -759,53 +699,8 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error { return err } - mode := WRITABLE - if repo.IsMirror { - mode = READABLE - } - // New owner is organization. - if newUser.IsOrganization() { - access := &Access{ - RepoName: path.Join(newUser.LowerName, repo.LowerName), - Mode: mode, - } - - // Give access to all members in owner team. - t, err := newUser.GetOwnerTeam() - if err != nil { - sess.Rollback() - return err - } - if err = t.GetMembers(); err != nil { - sess.Rollback() - return err - } - for _, u := range t.Members { - access.Id = 0 - access.UserName = u.LowerName - if _, err = sess.Insert(access); err != nil { - sess.Rollback() - return err - } - } - - // Update owner team info and count. - t.RepoIds += "$" + com.ToStr(repo.Id) + "|" - t.NumRepos++ - if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil { - sess.Rollback() - return err - } - } else { - access := &Access{ - RepoName: path.Join(newUser.LowerName, repo.LowerName), - UserName: newUser.LowerName, - Mode: mode, - } - if _, err = sess.Insert(access); err != nil { - sess.Rollback() - return fmt.Errorf("fail to insert access: %v", err) - } + if err = repo.RecalcAccessSess(); err != nil { + return err } // Change repository directory name. @@ -838,32 +733,8 @@ func ChangeRepositoryName(userName, oldRepoName, newRepoName string) (err error) return ErrRepoNameIllegal } - // Update accesses. - accesses := make([]Access, 0, 10) - if err = x.Find(&accesses, &Access{RepoName: userName + "/" + oldRepoName}); err != nil { - return err - } - - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { - return err - } - - for i := range accesses { - accesses[i].RepoName = userName + "/" + newRepoName - if err = UpdateAccessWithSession(sess, &accesses[i]); err != nil { - return err - } - } - // Change repository directory name. - if err = os.Rename(RepoPath(userName, oldRepoName), RepoPath(userName, newRepoName)); err != nil { - sess.Rollback() - return err - } - - return sess.Commit() + return os.Rename(RepoPath(userName, oldRepoName), RepoPath(userName, newRepoName)) } func UpdateRepository(repo *Repository) error { @@ -912,7 +783,7 @@ func DeleteRepository(uid, repoId int64, userName string) error { } // Delete all access. - if _, err := sess.Delete(&Access{RepoName: strings.ToLower(path.Join(userName, repo.Name))}); err != nil { + if _, err := sess.Delete(&Access{RepoID: repo.Id}); err != nil { sess.Rollback() return err } @@ -1105,7 +976,7 @@ func (r *Repository) AddCollaborator(u *User) error { return err } - return AddAccess(&Access{UserName: u.LowerName, RepoName: path.Join(r.Owner.LowerName, r.LowerName), Mode: WRITABLE}) + return r.RecalcAccessSess() } // Delete collaborator and accompanying access @@ -1116,25 +987,7 @@ func (r *Repository) DeleteCollaborator(u *User) error { return err } - if err := r.GetOwner(); err != nil { - return err - } - - needDelete := true - if r.Owner.IsOrganization() { - auth, err := GetHighestAuthorize(r.Owner.Id, u.Id, r.Id, 0) - if err != nil { - return err - } - if auth > 0 { - needDelete = false - } - } - if needDelete { - return DeleteAccess(&Access{UserName: u.LowerName, RepoName: path.Join(r.Owner.LowerName, r.LowerName), Mode: WRITABLE}) - } - - return nil + return r.RecalcAccessSess() } type SearchOption struct { @@ -1443,40 +1296,10 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (*Repositor return nil, err } - var t *Team // Owner team. - - mode := WRITABLE - - access := &Access{ - UserName: u.LowerName, - RepoName: path.Join(u.LowerName, repo.LowerName), - Mode: mode, - } - // Give access to all members in owner team. - if u.IsOrganization() { - t, err = u.GetOwnerTeam() - if err != nil { - sess.Rollback() - return nil, err - } - if err = t.GetMembers(); err != nil { - sess.Rollback() - return nil, err - } - for _, u := range t.Members { - access.Id = 0 - access.UserName = u.LowerName - if _, err = sess.Insert(access); err != nil { - sess.Rollback() - return nil, err - } - } - } else { - if _, err = sess.Insert(access); err != nil { - sess.Rollback() - return nil, err - } + if err = repo.RecalcAccessSess(); err != nil { + return nil, err } + var t *Team // Owner team. if _, err = sess.Exec( "UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", u.Id); err != nil { diff --git a/models/user.go b/models/user.go index 2da0881c81..b33e529aa6 100644 --- a/models/user.go +++ b/models/user.go @@ -396,59 +396,7 @@ func ChangeUserName(u *User, newUserName string) (err error) { return ErrUserNameIllegal } - newUserName = strings.ToLower(newUserName) - - // 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. @@ -521,7 +469,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 diff --git a/modules/middleware/org.go b/modules/middleware/org.go index e687258617..cbce54860d 100644 --- a/modules/middleware/org.go +++ b/modules/middleware/org.go @@ -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.AdminAccess } ctx.Data["IsAdminTeam"] = ctx.Org.IsAdminTeam if requireAdminTeam && !ctx.Org.IsAdminTeam { diff --git a/modules/middleware/repo.go b/modules/middleware/repo.go index d143d8a86b..66e6f3a570 100644 --- a/modules/middleware/repo.go +++ b/modules/middleware/repo.go @@ -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 && u.LowerName == strings.ToLower(userName) { + u = ctx.User + } else { u, err = models.GetUserByName(userName) if err != nil { if err == models.ErrUserNotExist { @@ -49,64 +41,38 @@ 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 - } + if ctx.IsSigned { + mode, err := models.AccessLevel(ctx.User, repo) + if err != nil { + ctx.JSON(500, &base.ApiJsonErr{"AccessLevel: " + err.Error(), base.DOC_URL}) + return } + ctx.Repo.IsOwner = mode >= models.WriteAccess + ctx.Repo.IsAdmin = mode >= models.ReadAccess + ctx.Repo.IsTrueOwner = mode >= models.OwnerAccess } // Check access. if repo.IsPrivate && !ctx.Repo.IsOwner { - if ctx.User == nil { - ctx.Error(404) - return - } - - 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 - } + ctx.Error(404) + return } ctx.Repo.HasAccess = true @@ -242,101 +208,54 @@ 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 && u.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 + "/") + ctx.Error(404) } else { - ctx.Handle(500, "GetUserByName", err) + ctx.JSON(500, &base.ApiJsonErr{"GetUserByName: " + err.Error(), base.DOC_URL}) } 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 + ctx.Error(404) + } else { + ctx.JSON(500, &base.ApiJsonErr{"GetRepositoryByName: " + err.Error(), base.DOC_URL}) } - ctx.Handle(500, "GetRepositoryByName", err) return } else if err = repo.GetOwner(); err != nil { - ctx.Handle(500, "GetOwner", err) + 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.Handle(500, "GetHighestAuthorize", err) - return - } - if auth == models.ORG_ADMIN { - ctx.Repo.IsOwner = true - ctx.Repo.IsAdmin = true - } + if ctx.IsSigned { + mode, err := models.AccessLevel(ctx.User, repo) + if err != nil { + ctx.JSON(500, &base.ApiJsonErr{"AccessLevel: " + err.Error(), base.DOC_URL}) + return } + ctx.Repo.IsOwner = mode >= models.WriteAccess + ctx.Repo.IsAdmin = mode >= models.ReadAccess + ctx.Repo.IsTrueOwner = mode >= models.OwnerAccess } // 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 - } + ctx.Error(404) + return } ctx.Repo.HasAccess = true + ctx.Data["HasAccess"] = true if repo.IsMirror { diff --git a/routers/api/v1/repo.go b/routers/api/v1/repo.go index 469e4808fe..78c9f9a6a3 100644 --- a/routers/api/v1/repo.go +++ b/routers/api/v1/repo.go @@ -255,7 +255,7 @@ func ListMyRepos(ctx *middleware.Context) { return } - repos[i] = ToApiRepository(repo.Owner, repo, api.Permission{false, access >= models.WRITABLE, true}) + repos[i] = ToApiRepository(repo.Owner, repo, api.Permission{false, access >= models.WriteAccess, true}) // FIXME: cache result to reduce DB query? if repo.Owner.IsOrganization() && repo.Owner.IsOwnedBy(ctx.User.Id) { diff --git a/routers/org/teams.go b/routers/org/teams.go index 77a7b6e13c..4fef02c987 100644 --- a/routers/org/teams.go +++ b/routers/org/teams.go @@ -168,14 +168,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.ReadAccess case "write": - auth = models.ORG_WRITABLE + auth = models.WriteAccess case "admin": - auth = models.ORG_ADMIN + auth = models.AdminAccess default: ctx.Error(401) return @@ -249,14 +249,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.ReadAccess case "write": - auth = models.ORG_WRITABLE + auth = models.WriteAccess case "admin": - auth = models.ORG_ADMIN + auth = models.AdminAccess default: ctx.Error(401) return diff --git a/routers/repo/http.go b/routers/repo/http.go index a5e01efc8f..716c71272a 100644 --- a/routers/repo/http.go +++ b/routers/repo/http.go @@ -115,18 +115,18 @@ func Http(ctx *middleware.Context) { } if !isPublicPull { - var tp = models.WRITABLE + var tp = models.WriteAccess if isPull { - tp = models.READABLE + tp = models.ReadAccess } - 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.ReadAccess { + has, err = models.HasAccess(authUser, repo, models.WriteAccess) if err != nil || !has { ctx.Handle(401, "no basic auth and digit auth", nil) return diff --git a/routers/user/home.go b/routers/user/home.go index 5b02154c10..82325cb747 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -103,8 +103,7 @@ 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 { + if has, _ := models.HasAccess(ctx.User, &models.Repository{Id: act.RepoId, IsPrivate: true}, models.ReadAccess); !has { continue } } @@ -211,8 +210,8 @@ func Profile(ctx *middleware.Context) { if !ctx.IsSigned { continue } - if has, _ := models.HasAccess(ctx.User.Name, act.RepoUserName+"/"+act.RepoName, - models.READABLE); !has { + if has, _ := models.HasAccess(ctx.User, &models.Repository{Id: act.RepoId, IsPrivate: true}, + models.ReadAccess); !has { continue } } -- cgit v1.2.3 From 0d158e569b0c19614b5e946849e8b7a8e4a75015 Mon Sep 17 00:00:00 2001 From: Peter Smit Date: Mon, 9 Feb 2015 13:36:33 +0200 Subject: Change constants to UPPERCASE_WITH_UNDERSCORE style --- cmd/serve.go | 14 +++++++------- models/access.go | 24 ++++++++++++------------ models/org.go | 2 +- modules/middleware/org.go | 2 +- modules/middleware/repo.go | 12 ++++++------ routers/api/v1/repo.go | 2 +- routers/org/teams.go | 12 ++++++------ routers/repo/http.go | 12 ++++++------ routers/user/home.go | 4 ++-- 9 files changed, 42 insertions(+), 42 deletions(-) (limited to 'routers') diff --git a/cmd/serve.go b/cmd/serve.go index d9d1a06b4e..90e1045c8e 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -59,14 +59,14 @@ func parseCmd(cmd string) (string, string) { var ( COMMANDS_READONLY = map[string]models.AccessMode{ - "git-upload-pack": models.WriteAccess, - "git upload-pack": models.WriteAccess, - "git-upload-archive": models.WriteAccess, + "git-upload-pack": models.ACCESS_MODE_WRITE, + "git upload-pack": models.ACCESS_MODE_WRITE, + "git-upload-archive": models.ACCESS_MODE_WRITE, } COMMANDS_WRITE = map[string]models.AccessMode{ - "git-receive-pack": models.ReadAccess, - "git receive-pack": models.ReadAccess, + "git-receive-pack": models.ACCESS_MODE_READ, + "git receive-pack": models.ACCESS_MODE_READ, } ) @@ -141,7 +141,7 @@ func runServ(k *cli.Context) { switch { case isWrite: - has, err := models.HasAccess(user, repo, models.WriteAccess) + 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,7 +154,7 @@ func runServ(k *cli.Context) { break } - has, err := models.HasAccess(user, repo, models.ReadAccess) + 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) diff --git a/models/access.go b/models/access.go index ee678a0701..916711f786 100644 --- a/models/access.go +++ b/models/access.go @@ -11,15 +11,15 @@ package models type AccessMode int const ( - NoAccess AccessMode = iota - ReadAccess - WriteAccess - AdminAccess - OwnerAccess + ACCESS_MODE_NONE AccessMode = iota + ACCESS_MODE_READ + ACCESS_MODE_WRITE + ACCESS_MODE_ADMIN + ACCESS_MODE_OWNER ) func maxAccessMode(modes ...AccessMode) AccessMode { - max := NoAccess + max := ACCESS_MODE_NONE for _, mode := range modes { if mode > max { max = mode @@ -47,14 +47,14 @@ func HasAccess(u *User, r *Repository, testMode AccessMode) (bool, error) { // Return 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, r *Repository) (AccessMode, error) { - mode := NoAccess + mode := ACCESS_MODE_NONE if !r.IsPrivate { - mode = ReadAccess + mode = ACCESS_MODE_READ } if u != nil { if u.Id == r.OwnerId { - return OwnerAccess, nil + return ACCESS_MODE_OWNER, nil } a := &Access{UserID: u.Id, RepoID: r.Id} @@ -101,7 +101,7 @@ func (r *Repository) RecalcAccessSess() error { return err } for _, c := range collaborators { - accessMap[c.Id] = WriteAccess + accessMap[c.Id] = ACCESS_MODE_WRITE } if err := r.GetOwner(); err != nil { @@ -126,9 +126,9 @@ func (r *Repository) RecalcAccessSess() error { } } - minMode := ReadAccess + minMode := ACCESS_MODE_READ if !r.IsPrivate { - minMode = WriteAccess + minMode = ACCESS_MODE_WRITE } newAccesses := make([]Access, 0, len(accessMap)) diff --git a/models/org.go b/models/org.go index 5e3bb0e070..f6d472a6d9 100644 --- a/models/org.go +++ b/models/org.go @@ -135,7 +135,7 @@ func CreateOrganization(org, owner *User) (*User, error) { OrgId: org.Id, LowerName: strings.ToLower(OWNER_TEAM), Name: OWNER_TEAM, - Authorize: OwnerAccess, + Authorize: ACCESS_MODE_OWNER, NumMembers: 1, } if _, err = sess.Insert(t); err != nil { diff --git a/modules/middleware/org.go b/modules/middleware/org.go index cbce54860d..0e544fe4a2 100644 --- a/modules/middleware/org.go +++ b/modules/middleware/org.go @@ -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.AdminAccess + 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 { diff --git a/modules/middleware/repo.go b/modules/middleware/repo.go index 66e6f3a570..bc1d1f9692 100644 --- a/modules/middleware/repo.go +++ b/modules/middleware/repo.go @@ -64,9 +64,9 @@ func ApiRepoAssignment() macaron.Handler { ctx.JSON(500, &base.ApiJsonErr{"AccessLevel: " + err.Error(), base.DOC_URL}) return } - ctx.Repo.IsOwner = mode >= models.WriteAccess - ctx.Repo.IsAdmin = mode >= models.ReadAccess - ctx.Repo.IsTrueOwner = mode >= models.OwnerAccess + ctx.Repo.IsOwner = mode >= models.ACCESS_MODE_WRITE + ctx.Repo.IsAdmin = mode >= models.ACCESS_MODE_READ + ctx.Repo.IsTrueOwner = mode >= models.ACCESS_MODE_OWNER } // Check access. @@ -244,9 +244,9 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler { ctx.JSON(500, &base.ApiJsonErr{"AccessLevel: " + err.Error(), base.DOC_URL}) return } - ctx.Repo.IsOwner = mode >= models.WriteAccess - ctx.Repo.IsAdmin = mode >= models.ReadAccess - ctx.Repo.IsTrueOwner = mode >= models.OwnerAccess + ctx.Repo.IsOwner = mode >= models.ACCESS_MODE_WRITE + ctx.Repo.IsAdmin = mode >= models.ACCESS_MODE_READ + ctx.Repo.IsTrueOwner = mode >= models.ACCESS_MODE_OWNER } // Check access. diff --git a/routers/api/v1/repo.go b/routers/api/v1/repo.go index 78c9f9a6a3..f5128e4746 100644 --- a/routers/api/v1/repo.go +++ b/routers/api/v1/repo.go @@ -255,7 +255,7 @@ func ListMyRepos(ctx *middleware.Context) { return } - repos[i] = ToApiRepository(repo.Owner, repo, api.Permission{false, access >= models.WriteAccess, true}) + repos[i] = ToApiRepository(repo.Owner, repo, api.Permission{false, access >= models.ACCESS_MODE_WRITE, true}) // FIXME: cache result to reduce DB query? if repo.Owner.IsOrganization() && repo.Owner.IsOwnedBy(ctx.User.Id) { diff --git a/routers/org/teams.go b/routers/org/teams.go index 4fef02c987..a315abe03e 100644 --- a/routers/org/teams.go +++ b/routers/org/teams.go @@ -171,11 +171,11 @@ func NewTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) { var auth models.AccessMode switch form.Permission { case "read": - auth = models.ReadAccess + auth = models.ACCESS_MODE_READ case "write": - auth = models.WriteAccess + auth = models.ACCESS_MODE_WRITE case "admin": - auth = models.AdminAccess + auth = models.ACCESS_MODE_ADMIN default: ctx.Error(401) return @@ -252,11 +252,11 @@ func EditTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) { var auth models.AccessMode switch form.Permission { case "read": - auth = models.ReadAccess + auth = models.ACCESS_MODE_READ case "write": - auth = models.WriteAccess + auth = models.ACCESS_MODE_WRITE case "admin": - auth = models.AdminAccess + auth = models.ACCESS_MODE_ADMIN default: ctx.Error(401) return diff --git a/routers/repo/http.go b/routers/repo/http.go index 716c71272a..4173c7a91f 100644 --- a/routers/repo/http.go +++ b/routers/repo/http.go @@ -115,9 +115,9 @@ func Http(ctx *middleware.Context) { } if !isPublicPull { - var tp = models.WriteAccess + var tp = models.ACCESS_MODE_WRITE if isPull { - tp = models.ReadAccess + tp = models.ACCESS_MODE_READ } has, err := models.HasAccess(authUser, repo, tp) @@ -125,8 +125,8 @@ func Http(ctx *middleware.Context) { ctx.Handle(401, "no basic auth and digit auth", nil) return } else if !has { - if tp == models.ReadAccess { - has, err = models.HasAccess(authUser, repo, models.WriteAccess) + 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 @@ -268,7 +268,7 @@ func serviceRpc(rpc string, hr handler) { access := hasAccess(r, hr.Config, dir, rpc, true) if access == false { - renderNoAccess(w) + renderACCESS_MODE_NONE(w) return } @@ -495,7 +495,7 @@ func renderNotFound(w http.ResponseWriter) { w.Write([]byte("Not Found")) } -func renderNoAccess(w http.ResponseWriter) { +func renderACCESS_MODE_NONE(w http.ResponseWriter) { w.WriteHeader(http.StatusForbidden) w.Write([]byte("Forbidden")) } diff --git a/routers/user/home.go b/routers/user/home.go index 82325cb747..ce82ae772c 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -103,7 +103,7 @@ 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, &models.Repository{Id: act.RepoId, IsPrivate: true}, models.ReadAccess); !has { + if has, _ := models.HasAccess(ctx.User, &models.Repository{Id: act.RepoId, IsPrivate: true}, models.ACCESS_MODE_READ); !has { continue } } @@ -211,7 +211,7 @@ func Profile(ctx *middleware.Context) { continue } if has, _ := models.HasAccess(ctx.User, &models.Repository{Id: act.RepoId, IsPrivate: true}, - models.ReadAccess); !has { + models.ACCESS_MODE_READ); !has { continue } } -- cgit v1.2.3 From 0a4cda0dd4d7c906668d9d2e283fbe9ebe2e1608 Mon Sep 17 00:00:00 2001 From: Peter Smit Date: Thu, 5 Feb 2015 15:29:08 +0200 Subject: Refactoring of the Access Table This commit does a lot of the work of refactoring the access table in a table with id's instead of strings. The result does compile, but has not been tested. It may eat your kittens. --- cmd/serve.go | 41 ++++--- models/access.go | 175 +++++++++++++++++----------- models/migrations/migrations.go | 10 +- models/org.go | 245 +++++---------------------------------- models/repo.go | 251 ++++------------------------------------ models/user.go | 59 +--------- modules/middleware/org.go | 2 +- modules/middleware/repo.go | 155 ++++++------------------- routers/api/v1/repo.go | 2 +- routers/org/teams.go | 16 +-- routers/repo/http.go | 10 +- routers/user/home.go | 7 +- 12 files changed, 243 insertions(+), 730 deletions(-) (limited to 'routers') diff --git a/cmd/serve.go b/cmd/serve.go index e957d45de7..62e5d14374 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -8,7 +8,6 @@ import ( "fmt" "os" "os/exec" - "path" "path/filepath" "strings" "time" @@ -67,19 +66,19 @@ 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.WriteAccess, + "git upload-pack": models.WriteAccess, + "git-upload-archive": models.WriteAccess, } - 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.ReadAccess, + "git receive-pack": models.ReadAccess, } ) -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 } @@ -144,9 +143,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.WriteAccess) if err != nil { println("Gogs: internal error:", err.Error()) log.GitLogger.Fatal(2, "Fail to check write access:", err) @@ -155,21 +164,11 @@ func runServ(k *cli.Context) { 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.IsPrivate { break } - has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.READABLE) + has, err := models.HasAccess(user, repo, models.ReadAccess) if err != nil { println("Gogs: internal error:", err.Error()) log.GitLogger.Fatal(2, "Fail to check read access:", err) diff --git a/models/access.go b/models/access.go index 6df1da29bc..d85f24cd06 100644 --- a/models/access.go +++ b/models/access.go @@ -4,95 +4,76 @@ package models -import ( - "strings" - "time" -) - -type AccessType int +type AccessMode int const ( - READABLE AccessType = iota + 1 - WRITABLE + NoAccess AccessMode = iota + ReadAccess + WriteAccess + AdminAccess + OwnerAccess ) -// Access represents the accessibility of user to repository. -type Access struct { - Id int64 - UserName string `xorm:"UNIQUE(s)"` - RepoName string `xorm:"UNIQUE(s)"` // / - Mode AccessType `xorm:"UNIQUE(s)"` - Created time.Time `xorm:"CREATED"` +func maxAccessMode(modes ...AccessMode) AccessMode { + max := NoAccess + for _, mode := range modes { + if mode > max { + max = mode + } + } + return max } -func addAccess(e Engine, access *Access) error { - access.UserName = strings.ToLower(access.UserName) - access.RepoName = strings.ToLower(access.RepoName) - _, err := e.Insert(access) - return err +// 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 `xorm:"pk autoincr"` + UserID int64 `xorm:"UNIQUE(s)"` + RepoID int64 `xorm:"UNIQUE(s)"` + Mode AccessMode } -// AddAccess adds new access record. -func AddAccess(access *Access) error { - return addAccess(x, access) +// HasAccess returns true if someone has the request access level. User can be nil! +func HasAccess(u *User, r *Repository, testMode AccessMode) (bool, error) { + mode, err := AccessLevel(u, r) + return testMode <= mode, err } -func updateAccess(e Engine, access *Access) error { - if _, err := e.Id(access.Id).Update(access); err != nil { - return err +// Return 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, r *Repository) (AccessMode, error) { + mode := NoAccess + if !r.IsPrivate { + mode = ReadAccess } - return nil -} - -// UpdateAccess updates access information. -func UpdateAccess(access *Access) error { - access.UserName = strings.ToLower(access.UserName) - access.RepoName = strings.ToLower(access.RepoName) - return updateAccess(x, access) -} - -func deleteAccess(e Engine, access *Access) error { - _, err := e.Delete(access) - return err -} -// DeleteAccess deletes access record. -func DeleteAccess(access *Access) error { - return deleteAccess(x, access) -} + if u != nil { + if u.Id == r.OwnerId { + return OwnerAccess, nil + } -// HasAccess returns true if someone can read or write to given repository. -// The repoName should be in format /. -func HasAccess(uname, repoName string, mode AccessType) (bool, error) { - if len(repoName) == 0 { - return false, nil - } - access := &Access{ - UserName: strings.ToLower(uname), - RepoName: strings.ToLower(repoName), - } - has, err := x.Get(access) - if err != nil { - return false, err - } else if !has { - return false, nil - } else if mode > access.Mode { - return false, nil + a := &Access{UserID: u.Id, RepoID: r.Id} + if has, err := x.Get(a); !has || err != nil { + return mode, err + } + return a.Mode, nil } - return true, nil + + return mode, nil } // GetAccessibleRepositories finds all repositories where a user has access to, // besides his own. -func (u *User) GetAccessibleRepositories() (map[*Repository]AccessType, error) { +func (u *User) GetAccessibleRepositories() (map[*Repository]AccessMode, error) { accesses := make([]*Access, 0, 10) - if err := x.Find(&accesses, &Access{UserName: u.LowerName}); err != nil { + if err := x.Find(&accesses, &Access{UserID: u.Id}); err != nil { return nil, err } - repos := make(map[*Repository]AccessType, len(accesses)) + repos := make(map[*Repository]AccessMode, len(accesses)) for _, access := range accesses { - repo, err := GetRepositoryByRef(access.RepoName) + repo, err := GetRepositoryById(access.RepoID) if err != nil { return nil, err } @@ -106,3 +87,65 @@ func (u *User) GetAccessibleRepositories() (map[*Repository]AccessType, error) { return repos, nil } + +// Recalculate all accesses for repository +func (r *Repository) RecalcAccessSess() error { + accessMap := make(map[int64]AccessMode, 20) + + // Give all collaborators write access + collaborators, err := r.GetCollaborators() + if err != nil { + return err + } + for _, c := range collaborators { + accessMap[c.Id] = WriteAccess + } + + if err := r.GetOwner(); err != nil { + return err + } + if r.Owner.IsOrganization() { + if err = r.Owner.GetTeams(); err != nil { + return err + } + + for _, team := range r.Owner.Teams { + if !(team.IsOwnerTeam() || team.HasRepository(r)) { + continue + } + + if err = team.GetMembers(); err != nil { + return err + } + for _, u := range team.Members { + accessMap[u.Id] = maxAccessMode(accessMap[u.Id], team.Authorize) + } + } + } + + minMode := ReadAccess + if !r.IsPrivate { + minMode = WriteAccess + } + + newAccesses := make([]Access, 0, len(accessMap)) + for userID, mode := range accessMap { + if userID == r.OwnerId || mode <= minMode { + continue + } + newAccesses = append(newAccesses, Access{UserID: userID, RepoID: r.Id, Mode: mode}) + } + + // Delete old accesses for repository + if _, err = x.Delete(&Access{RepoID: r.Id}); err != nil { + return err + } + + // And insert the new ones + if _, err = x.Insert(newAccesses); err != nil { + return err + } + + return nil + +} diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 814564e9af..1510bafdb6 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -47,10 +47,11 @@ 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, remove it from the top of the list and -// update _MIN_VER_DB accordingly +// 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("refactor access table to use id's", accessRefactor), // V1 -> V2 } // Migrate database to current version @@ -206,3 +207,8 @@ func accessToCollaboration(x *xorm.Engine) error { return sess.Commit() } + +func accessRefactor(x *xorm.Engine) error { + //TODO + return nil +} diff --git a/models/org.go b/models/org.go index 3d37a37d69..775caa8639 100644 --- a/models/org.go +++ b/models/org.go @@ -6,9 +6,7 @@ package models import ( "errors" - "fmt" "os" - "path" "strings" "github.com/Unknwon/com" @@ -136,7 +134,7 @@ func CreateOrganization(org, owner *User) (*User, error) { OrgId: org.Id, LowerName: strings.ToLower(OWNER_TEAM), Name: OWNER_TEAM, - Authorize: ORG_ADMIN, + Authorize: OwnerAccess, NumMembers: 1, } if _, err = sess.Insert(t); err != nil { @@ -371,10 +369,10 @@ func RemoveOrgUser(orgId, uid int64) error { return err } access := &Access{ - UserName: u.LowerName, + UserID: u.Id, } for _, repo := range org.Repos { - access.RepoName = path.Join(org.LowerName, repo.LowerName) + access.RepoID = repo.Id if _, err = sess.Delete(access); err != nil { sess.Rollback() return err @@ -405,21 +403,6 @@ func RemoveOrgUser(orgId, uid int64) error { // |____| \___ >____ /__|_| / // \/ \/ \/ -type AuthorizeType int - -const ( - ORG_READABLE AuthorizeType = iota + 1 - ORG_WRITABLE - ORG_ADMIN -) - -func AuthorizeToAccessType(auth AuthorizeType) AccessType { - if auth == ORG_READABLE { - return READABLE - } - return WRITABLE -} - const OWNER_TEAM = "Owners" // Team represents a organization team. @@ -429,7 +412,7 @@ type Team struct { LowerName string Name string Description string - Authorize AuthorizeType + Authorize AccessMode RepoIds string `xorm:"TEXT"` Repos []*Repository `xorm:"-"` Members []*User `xorm:"-"` @@ -484,25 +467,6 @@ func (t *Team) RemoveMember(uid int64) error { return RemoveTeamMember(t.OrgId, t.Id, uid) } -// addAccessWithAuthorize inserts or updates access with given mode. -func addAccessWithAuthorize(e Engine, access *Access, mode AccessType) error { - has, err := e.Get(access) - if err != nil { - return fmt.Errorf("fail to get access: %v", err) - } - access.Mode = mode - if has { - if _, err = e.Id(access.Id).Update(access); err != nil { - return fmt.Errorf("fail to update access: %v", err) - } - } else { - if _, err = e.Insert(access); err != nil { - return fmt.Errorf("fail to insert access: %v", err) - } - } - return nil -} - // AddRepository adds new repository to team of organization. func (t *Team) AddRepository(repo *Repository) (err error) { idStr := "$" + com.ToStr(repo.Id) + "|" @@ -531,27 +495,13 @@ func (t *Team) AddRepository(repo *Repository) (err error) { return err } - // Give access to team members. - mode := AuthorizeToAccessType(t.Authorize) + if err = repo.RecalcAccessSess(); err != nil { + sess.Rollback() + return err + } for _, u := range t.Members { - auth, err := getHighestAuthorize(sess, t.OrgId, u.Id, repo.Id, t.Id) - if err != nil { - sess.Rollback() - return err - } - - access := &Access{ - UserName: u.LowerName, - RepoName: path.Join(repo.Owner.LowerName, repo.LowerName), - } - if auth < t.Authorize { - if err = addAccessWithAuthorize(sess, access, mode); err != nil { - sess.Rollback() - return err - } - } - if err = watchRepo(sess, u.Id, repo.Id, true); err != nil { + if err = WatchRepo(u.Id, repo.Id, true); err != nil { sess.Rollback() return err } @@ -559,6 +509,11 @@ func (t *Team) AddRepository(repo *Repository) (err error) { return sess.Commit() } +func (t *Team) HasRepository(r *Repository) bool { + idStr := "$" + com.ToStr(r.Id) + "|" + return strings.Contains(t.RepoIds, idStr) +} + // RemoveRepository removes repository from team of organization. func (t *Team) RemoveRepository(repoId int64) error { idStr := "$" + com.ToStr(repoId) + "|" @@ -590,32 +545,16 @@ func (t *Team) RemoveRepository(repoId int64) error { return err } - // Remove access to team members. + if err = repo.RecalcAccessSess(); err != nil { + sess.Rollback() + return err + } + for _, u := range t.Members { - auth, err := getHighestAuthorize(sess, t.OrgId, u.Id, repo.Id, t.Id) - if err != nil { + if err = WatchRepo(u.Id, repo.Id, false); err != nil { sess.Rollback() return err } - - access := &Access{ - UserName: u.LowerName, - RepoName: path.Join(repo.Owner.LowerName, repo.LowerName), - } - if auth == 0 { - if _, err = sess.Delete(access); err != nil { - sess.Rollback() - return fmt.Errorf("fail to delete access: %v", err) - } else if err = watchRepo(sess, u.Id, repo.Id, false); err != nil { - sess.Rollback() - return err - } - } else if auth < t.Authorize { - if err = addAccessWithAuthorize(sess, access, AuthorizeToAccessType(auth)); err != nil { - sess.Rollback() - return err - } - } } return sess.Commit() @@ -693,34 +632,6 @@ func GetTeamById(teamId int64) (*Team, error) { return getTeamById(x, teamId) } -func getHighestAuthorize(e Engine, orgId, uid, repoId, teamId int64) (AuthorizeType, error) { - ts, err := getUserTeams(e, orgId, uid) - if err != nil { - return 0, err - } - - var auth AuthorizeType = 0 - for _, t := range ts { - // Not current team and has given repository. - if t.Id != teamId && strings.Contains(t.RepoIds, "$"+com.ToStr(repoId)+"|") { - // Fast return. - if t.Authorize == ORG_WRITABLE { - return ORG_WRITABLE, nil - } - if t.Authorize > auth { - auth = t.Authorize - } - } - } - - return auth, nil -} - -// GetHighestAuthorize returns highest repository authorize level for given user and team. -func GetHighestAuthorize(orgId, uid, repoId, teamId int64) (AuthorizeType, error) { - return getHighestAuthorize(x, orgId, uid, repoId, teamId) -} - // UpdateTeam updates information of team. func UpdateTeam(t *Team, authChanged bool) (err error) { if !IsLegalName(t.Name) { @@ -738,45 +649,14 @@ func UpdateTeam(t *Team, authChanged bool) (err error) { } // Update access for team members if needed. - if authChanged && !t.IsOwnerTeam() { + if authChanged { if err = t.GetRepositories(); err != nil { return err - } else if err = t.GetMembers(); err != nil { - return err } - // Get organization. - org, err := GetUserById(t.OrgId) - if err != nil { - return err - } - - // Update access. - mode := AuthorizeToAccessType(t.Authorize) - for _, repo := range t.Repos { - for _, u := range t.Members { - // ORG_WRITABLE is the highest authorize level for now. - // Skip checking others if current team has this level. - if t.Authorize < ORG_WRITABLE { - auth, err := GetHighestAuthorize(t.OrgId, u.Id, repo.Id, t.Id) - if err != nil { - sess.Rollback() - return err - } - if auth >= t.Authorize { - continue // Other team has higher or same authorize level. - } - } - - access := &Access{ - UserName: u.LowerName, - RepoName: path.Join(org.LowerName, repo.LowerName), - } - if err = addAccessWithAuthorize(sess, access, mode); err != nil { - sess.Rollback() - return err - } + if err = repo.RecalcAccessSess(); err != nil { + return err } } } @@ -812,29 +692,8 @@ func DeleteTeam(t *Team) error { // Delete all accesses. for _, repo := range t.Repos { - for _, u := range t.Members { - auth, err := GetHighestAuthorize(t.OrgId, u.Id, repo.Id, t.Id) - if err != nil { - sess.Rollback() - return err - } - - access := &Access{ - UserName: u.LowerName, - RepoName: path.Join(org.LowerName, repo.LowerName), - } - if auth == 0 { - if _, err = sess.Delete(access); err != nil { - sess.Rollback() - return fmt.Errorf("fail to delete access: %v", err) - } - } else if auth < t.Authorize { - // Downgrade authorize level. - if err = addAccessWithAuthorize(sess, access, AuthorizeToAccessType(auth)); err != nil { - sess.Rollback() - return err - } - } + if err = repo.RecalcAccessSess(); err != nil { + return err } } @@ -936,18 +795,6 @@ func AddTeamMember(orgId, teamId, uid int64) error { return err } - // Get organization. - org, err := GetUserById(orgId) - if err != nil { - return err - } - - // Get user. - u, err := GetUserById(uid) - if err != nil { - return err - } - sess := x.NewSession() defer sess.Close() if err = sess.Begin(); err != nil { @@ -969,24 +816,11 @@ func AddTeamMember(orgId, teamId, uid int64) error { } // Give access to team repositories. - mode := AuthorizeToAccessType(t.Authorize) for _, repo := range t.Repos { - auth, err := getHighestAuthorize(sess, t.OrgId, u.Id, repo.Id, teamId) - if err != nil { + if err = repo.RecalcAccessSess(); err != nil { sess.Rollback() return err } - - access := &Access{ - UserName: u.LowerName, - RepoName: path.Join(org.LowerName, repo.LowerName), - } - if auth < t.Authorize { - if err = addAccessWithAuthorize(sess, access, mode); err != nil { - sess.Rollback() - return err - } - } } // We make sure it exists before. @@ -1036,12 +870,6 @@ func removeTeamMember(e Engine, orgId, teamId, uid int64) error { return err } - // Get user. - u, err := GetUserById(uid) - if err != nil { - return err - } - tu := &TeamUser{ Uid: uid, OrgId: orgId, @@ -1056,28 +884,9 @@ func removeTeamMember(e Engine, orgId, teamId, uid int64) error { // Delete access to team repositories. for _, repo := range t.Repos { - auth, err := getHighestAuthorize(e, t.OrgId, u.Id, repo.Id, teamId) - if err != nil { + if err = repo.RecalcAccessSess(); err != nil { return err } - - access := &Access{ - UserName: u.LowerName, - RepoName: path.Join(org.LowerName, repo.LowerName), - } - // Delete access if this is the last team user belongs to. - if auth == 0 { - if _, err = e.Delete(access); err != nil { - return fmt.Errorf("fail to delete access: %v", err) - } else if err = watchRepo(e, u.Id, repo.Id, false); err != nil { - return err - } - } else if auth < t.Authorize { - // Downgrade authorize level. - if err = addAccessWithAuthorize(e, access, AuthorizeToAccessType(auth)); err != nil { - return err - } - } } // This must exist. diff --git a/models/repo.go b/models/repo.go index 35ee871fd4..5a669d9dd8 100644 --- a/models/repo.go +++ b/models/repo.go @@ -206,14 +206,6 @@ func (repo *Repository) IsOwnedBy(u *User) bool { return repo.OwnerId == u.Id } -func (repo *Repository) HasAccess(uname string) bool { - if err := repo.GetOwner(); err != nil { - return false - } - has, _ := HasAccess(uname, path.Join(repo.Owner.Name, repo.Name), READABLE) - return has -} - // DescriptionHtml does special handles to description and return HTML string. func (repo *Repository) DescriptionHtml() template.HTML { sanitize := func(s string) string { @@ -548,36 +540,11 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror var t *Team // Owner team. - mode := WRITABLE - if mirror { - mode = READABLE - } - access := &Access{ - UserName: u.LowerName, - RepoName: path.Join(u.LowerName, repo.LowerName), - Mode: mode, - } + // TODO fix code for mirrors? + // Give access to all members in owner team. if u.IsOrganization() { - t, err = u.GetOwnerTeam() - if err != nil { - sess.Rollback() - return nil, err - } - if err = t.GetMembers(); err != nil { - sess.Rollback() - return nil, err - } - for _, u := range t.Members { - access.Id = 0 - access.UserName = u.LowerName - if _, err = sess.Insert(access); err != nil { - sess.Rollback() - return nil, err - } - } - } else { - if _, err = sess.Insert(access); err != nil { + if err = repo.RecalcAccessSess(); err != nil { sess.Rollback() return nil, err } @@ -707,37 +674,10 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error { } owner := repo.Owner - oldRepoLink := path.Join(owner.LowerName, repo.LowerName) - // Delete all access first if current owner is an organization. - if owner.IsOrganization() { - if _, err = sess.Where("repo_name=?", oldRepoLink).Delete(new(Access)); err != nil { - sess.Rollback() - return fmt.Errorf("fail to delete current accesses: %v", err) - } - } else { - // Delete current owner access. - if _, err = sess.Where("repo_name=?", oldRepoLink).And("user_name=?", owner.LowerName). - Delete(new(Access)); err != nil { - sess.Rollback() - return fmt.Errorf("fail to delete access(owner): %v", err) - } - // In case new owner has access. - if _, err = sess.Where("repo_name=?", oldRepoLink).And("user_name=?", newUser.LowerName). - Delete(new(Access)); err != nil { - sess.Rollback() - return fmt.Errorf("fail to delete access(new user): %v", err) - } - } - - // Change accesses to new repository path. - if _, err = sess.Where("repo_name=?", oldRepoLink). - Update(&Access{RepoName: path.Join(newUser.LowerName, repo.LowerName)}); err != nil { - sess.Rollback() - return fmt.Errorf("fail to update access(change reponame): %v", err) - } // Update repository. repo.OwnerId = newUser.Id + repo.Owner = newUser if _, err := sess.Id(repo.Id).Update(repo); err != nil { sess.Rollback() return err @@ -754,53 +694,8 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error { return err } - mode := WRITABLE - if repo.IsMirror { - mode = READABLE - } - // New owner is organization. - if newUser.IsOrganization() { - access := &Access{ - RepoName: path.Join(newUser.LowerName, repo.LowerName), - Mode: mode, - } - - // Give access to all members in owner team. - t, err := newUser.GetOwnerTeam() - if err != nil { - sess.Rollback() - return err - } - if err = t.GetMembers(); err != nil { - sess.Rollback() - return err - } - for _, u := range t.Members { - access.Id = 0 - access.UserName = u.LowerName - if _, err = sess.Insert(access); err != nil { - sess.Rollback() - return err - } - } - - // Update owner team info and count. - t.RepoIds += "$" + com.ToStr(repo.Id) + "|" - t.NumRepos++ - if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil { - sess.Rollback() - return err - } - } else { - access := &Access{ - RepoName: path.Join(newUser.LowerName, repo.LowerName), - UserName: newUser.LowerName, - Mode: mode, - } - if _, err = sess.Insert(access); err != nil { - sess.Rollback() - return fmt.Errorf("fail to insert access: %v", err) - } + if err = repo.RecalcAccessSess(); err != nil { + return err } // Change repository directory name. @@ -833,33 +728,8 @@ func ChangeRepositoryName(userName, oldRepoName, newRepoName string) (err error) return ErrRepoNameIllegal } - // Update accesses. - accesses := make([]Access, 0, 10) - if err = x.Find(&accesses, &Access{RepoName: userName + "/" + oldRepoName}); err != nil { - return err - } - - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { - return err - } - - for i := range accesses { - accesses[i].RepoName = userName + "/" + newRepoName - if err = updateAccess(sess, &accesses[i]); err != nil { - sess.Rollback() - return err - } - } - // Change repository directory name. - if err = os.Rename(RepoPath(userName, oldRepoName), RepoPath(userName, newRepoName)); err != nil { - sess.Rollback() - return err - } - - return sess.Commit() + return os.Rename(RepoPath(userName, oldRepoName), RepoPath(userName, newRepoName)) } func UpdateRepository(repo *Repository) error { @@ -908,7 +778,7 @@ func DeleteRepository(uid, repoId int64, userName string) error { } // Delete all access. - if _, err := sess.Delete(&Access{RepoName: strings.ToLower(path.Join(userName, repo.Name))}); err != nil { + if _, err := sess.Delete(&Access{RepoID: repo.Id}); err != nil { sess.Rollback() return err } @@ -1228,41 +1098,30 @@ type Collaboration struct { // Add collaborator and accompanying access func (r *Repository) AddCollaborator(u *User) error { collaboration := &Collaboration{RepoID: r.Id, UserID: u.Id} + has, err := x.Get(collaboration) if err != nil { return err - } else if has { - return nil } - - if err = r.GetOwner(); err != nil { - return err + if has { + return nil } - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { + if _, err = x.InsertOne(collaboration); err != nil { return err } - if _, err = sess.InsertOne(collaboration); err != nil { - sess.Rollback() - return err - } else if err = addAccess(sess, &Access{ - UserName: u.LowerName, - RepoName: path.Join(r.Owner.LowerName, r.LowerName), - Mode: WRITABLE}); err != nil { - sess.Rollback() + if err = r.GetOwner(); err != nil { return err } - return sess.Commit() + return r.RecalcAccessSess() } // GetCollaborators returns the collaborators for a repository func (r *Repository) GetCollaborators() ([]*User, error) { - collaborations := make([]*Collaboration, 0, 5) - if err := x.Where("repo_id=?", r.Id).Find(&collaborations); err != nil { + collaborations := make([]*Collaboration, 0) + if err := x.Find(&collaborations, &Collaboration{RepoID: r.Id}); err != nil { return nil, err } @@ -1278,50 +1137,14 @@ func (r *Repository) GetCollaborators() ([]*User, error) { } // Delete collaborator and accompanying access -func (r *Repository) DeleteCollaborator(u *User) (err error) { +func (r *Repository) DeleteCollaborator(u *User) error { collaboration := &Collaboration{RepoID: r.Id, UserID: u.Id} - has, err := x.Get(collaboration) - if err != nil { - return err - } else if !has { - return nil - } - if err = r.GetOwner(); err != nil { - return err - } - - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { + if has, err := x.Delete(collaboration); err != nil || has == 0 { return err } - needDelete := true - if r.Owner.IsOrganization() { - auth, err := getHighestAuthorize(sess, r.Owner.Id, u.Id, r.Id, 0) - if err != nil { - sess.Rollback() - return err - } - if auth > 0 { - needDelete = false - } - } - if needDelete { - if err = deleteAccess(sess, &Access{ - UserName: u.LowerName, - RepoName: path.Join(r.Owner.LowerName, r.LowerName), - Mode: WRITABLE}); err != nil { - sess.Rollback() - return err - } else if _, err = sess.Delete(collaboration); err != nil { - sess.Rollback() - return err - } - } - - return sess.Commit() + return r.RecalcAccessSess() } // __ __ __ .__ @@ -1495,40 +1318,10 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (*Repositor return nil, err } - var t *Team // Owner team. - - mode := WRITABLE - - access := &Access{ - UserName: u.LowerName, - RepoName: path.Join(u.LowerName, repo.LowerName), - Mode: mode, - } - // Give access to all members in owner team. - if u.IsOrganization() { - t, err = u.GetOwnerTeam() - if err != nil { - sess.Rollback() - return nil, err - } - if err = t.GetMembers(); err != nil { - sess.Rollback() - return nil, err - } - for _, u := range t.Members { - access.Id = 0 - access.UserName = u.LowerName - if _, err = sess.Insert(access); err != nil { - sess.Rollback() - return nil, err - } - } - } else { - if _, err = sess.Insert(access); err != nil { - sess.Rollback() - return nil, err - } + if err = repo.RecalcAccessSess(); err != nil { + return nil, err } + var t *Team // Owner team. if _, err = sess.Exec( "UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", u.Id); err != nil { diff --git a/models/user.go b/models/user.go index 5606cea379..9a6f93a474 100644 --- a/models/user.go +++ b/models/user.go @@ -395,62 +395,7 @@ func ChangeUserName(u *User, newUserName string) (err error) { if !IsLegalName(newUserName) { return ErrUserNameIllegal } - - newUserName = strings.ToLower(newUserName) - - // 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 = updateAccess(sess, &accesses[i]); err != nil { - sess.Rollback() - 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 = updateAccess(sess, &accesses[j]); err != nil { - sess.Rollback() - 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. @@ -523,7 +468,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 diff --git a/modules/middleware/org.go b/modules/middleware/org.go index e687258617..cbce54860d 100644 --- a/modules/middleware/org.go +++ b/modules/middleware/org.go @@ -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.AdminAccess } ctx.Data["IsAdminTeam"] = ctx.Org.IsAdminTeam if requireAdminTeam && !ctx.Org.IsAdminTeam { diff --git a/modules/middleware/repo.go b/modules/middleware/repo.go index 1ab158dd6d..8cc6290460 100644 --- a/modules/middleware/repo.go +++ b/modules/middleware/repo.go @@ -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 && u.LowerName == strings.ToLower(userName) { + u = ctx.User + } else { u, err = models.GetUserByName(userName) if err != nil { if err == models.ErrUserNotExist { @@ -49,64 +41,38 @@ 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 - } + if ctx.IsSigned { + mode, err := models.AccessLevel(ctx.User, repo) + if err != nil { + ctx.JSON(500, &base.ApiJsonErr{"AccessLevel: " + err.Error(), base.DOC_URL}) + return } + ctx.Repo.IsOwner = mode >= models.WriteAccess + ctx.Repo.IsAdmin = mode >= models.ReadAccess + ctx.Repo.IsTrueOwner = mode >= models.OwnerAccess } // Check access. if repo.IsPrivate && !ctx.Repo.IsOwner { - if ctx.User == nil { - ctx.Error(404) - return - } - - 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 - } + ctx.Error(404) + return } ctx.Repo.HasAccess = true @@ -242,101 +208,54 @@ 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 && u.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 + "/") + ctx.Error(404) } else { - ctx.Handle(500, "GetUserByName", err) + ctx.JSON(500, &base.ApiJsonErr{"GetUserByName: " + err.Error(), base.DOC_URL}) } 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 + ctx.Error(404) + } else { + ctx.JSON(500, &base.ApiJsonErr{"GetRepositoryByName: " + err.Error(), base.DOC_URL}) } - ctx.Handle(500, "GetRepositoryByName", err) return } else if err = repo.GetOwner(); err != nil { - ctx.Handle(500, "GetOwner", err) + 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.Handle(500, "GetHighestAuthorize", err) - return - } - if auth == models.ORG_ADMIN { - ctx.Repo.IsOwner = true - ctx.Repo.IsAdmin = true - } + if ctx.IsSigned { + mode, err := models.AccessLevel(ctx.User, repo) + if err != nil { + ctx.JSON(500, &base.ApiJsonErr{"AccessLevel: " + err.Error(), base.DOC_URL}) + return } + ctx.Repo.IsOwner = mode >= models.WriteAccess + ctx.Repo.IsAdmin = mode >= models.ReadAccess + ctx.Repo.IsTrueOwner = mode >= models.OwnerAccess } // 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 - } + ctx.Error(404) + return } ctx.Repo.HasAccess = true + ctx.Data["HasAccess"] = true if repo.IsMirror { diff --git a/routers/api/v1/repo.go b/routers/api/v1/repo.go index 469e4808fe..78c9f9a6a3 100644 --- a/routers/api/v1/repo.go +++ b/routers/api/v1/repo.go @@ -255,7 +255,7 @@ func ListMyRepos(ctx *middleware.Context) { return } - repos[i] = ToApiRepository(repo.Owner, repo, api.Permission{false, access >= models.WRITABLE, true}) + repos[i] = ToApiRepository(repo.Owner, repo, api.Permission{false, access >= models.WriteAccess, true}) // FIXME: cache result to reduce DB query? if repo.Owner.IsOrganization() && repo.Owner.IsOwnedBy(ctx.User.Id) { diff --git a/routers/org/teams.go b/routers/org/teams.go index 9dd9b8e229..f5f94be0f4 100644 --- a/routers/org/teams.go +++ b/routers/org/teams.go @@ -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.ReadAccess case "write": - auth = models.ORG_WRITABLE + auth = models.WriteAccess case "admin": - auth = models.ORG_ADMIN + auth = models.AdminAccess default: ctx.Error(401) return @@ -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.ReadAccess case "write": - auth = models.ORG_WRITABLE + auth = models.WriteAccess case "admin": - auth = models.ORG_ADMIN + auth = models.AdminAccess default: ctx.Error(401) return diff --git a/routers/repo/http.go b/routers/repo/http.go index f5dc0c9d6a..c6742af3ec 100644 --- a/routers/repo/http.go +++ b/routers/repo/http.go @@ -137,18 +137,18 @@ func Http(ctx *middleware.Context) { } if !isPublicPull { - var tp = models.WRITABLE + var tp = models.WriteAccess if isPull { - tp = models.READABLE + tp = models.ReadAccess } - 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.ReadAccess { + has, err = models.HasAccess(authUser, repo, models.WriteAccess) if err != nil || !has { ctx.Handle(401, "no basic auth and digit auth", nil) return diff --git a/routers/user/home.go b/routers/user/home.go index 5b02154c10..82325cb747 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -103,8 +103,7 @@ 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 { + if has, _ := models.HasAccess(ctx.User, &models.Repository{Id: act.RepoId, IsPrivate: true}, models.ReadAccess); !has { continue } } @@ -211,8 +210,8 @@ func Profile(ctx *middleware.Context) { if !ctx.IsSigned { continue } - if has, _ := models.HasAccess(ctx.User.Name, act.RepoUserName+"/"+act.RepoName, - models.READABLE); !has { + if has, _ := models.HasAccess(ctx.User, &models.Repository{Id: act.RepoId, IsPrivate: true}, + models.ReadAccess); !has { continue } } -- cgit v1.2.3 From 6d0f3a07d4fa3189b0b7c4e366bddb6d72ef7e68 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Fri, 13 Feb 2015 00:58:46 -0500 Subject: code fix #941 caution: undertest --- cmd/serve.go | 11 +- models/access.go | 66 ++++++----- models/action.go | 42 ++++--- models/models.go | 10 ++ models/org.go | 110 +++++++++-------- models/repo.go | 286 +++++++++++++++++++++++---------------------- models/user.go | 10 +- modules/middleware/repo.go | 16 +-- routers/repo/http.go | 4 +- routers/user/home.go | 7 +- 10 files changed, 306 insertions(+), 256 deletions(-) (limited to 'routers') diff --git a/cmd/serve.go b/cmd/serve.go index 54b3714806..e8e5c186c7 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -83,16 +83,16 @@ func In(b string, sl map[string]models.AccessMode) bool { 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]) @@ -116,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 } diff --git a/models/access.go b/models/access.go index 174aca987f..e9742b318d 100644 --- a/models/access.go +++ b/models/access.go @@ -14,16 +14,6 @@ const ( ACCESS_MODE_OWNER ) -func maxAccessMode(modes ...AccessMode) AccessMode { - max := ACCESS_MODE_NONE - for _, mode := range modes { - if mode > max { - max = mode - } - } - return max -} - // 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. @@ -34,12 +24,6 @@ type Access struct { Mode AccessMode } -// HasAccess returns true if someone has the request access level. User can be nil! -func HasAccess(u *User, r *Repository, testMode AccessMode) (bool, error) { - mode, err := AccessLevel(u, r) - return testMode <= mode, err -} - // Return 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, r *Repository) (AccessMode, error) { @@ -63,6 +47,12 @@ func AccessLevel(u *User, r *Repository) (AccessMode, error) { return mode, nil } +// HasAccess returns true if someone has the request access level. User can be nil! +func HasAccess(u *User, r *Repository, testMode AccessMode) (bool, error) { + mode, err := AccessLevel(u, r) + return testMode <= mode, err +} + // GetAccessibleRepositories finds all repositories where a user has access to, // besides his own. func (u *User) GetAccessibleRepositories() (map[*Repository]AccessMode, error) { @@ -88,12 +78,21 @@ func (u *User) GetAccessibleRepositories() (map[*Repository]AccessMode, error) { return repos, nil } -// Recalculate all accesses for repository -func (r *Repository) RecalcAccessSess() error { +func maxAccessMode(modes ...AccessMode) AccessMode { + max := ACCESS_MODE_NONE + for _, mode := range modes { + if mode > max { + max = mode + } + } + return max +} + +func (repo *Repository) recalculateAccesses(e Engine) error { accessMap := make(map[int64]AccessMode, 20) // Give all collaborators write access - collaborators, err := r.GetCollaborators() + collaborators, err := repo.getCollaborators(e) if err != nil { return err } @@ -101,20 +100,20 @@ func (r *Repository) RecalcAccessSess() error { accessMap[c.Id] = ACCESS_MODE_WRITE } - if err := r.GetOwner(); err != nil { + if err := repo.getOwner(e); err != nil { return err } - if r.Owner.IsOrganization() { - if err = r.Owner.GetTeams(); err != nil { + if repo.Owner.IsOrganization() { + if err = repo.Owner.getTeams(e); err != nil { return err } - for _, team := range r.Owner.Teams { - if !(team.IsOwnerTeam() || team.HasRepository(r)) { + for _, team := range repo.Owner.Teams { + if !(team.IsOwnerTeam() || team.HasRepository(repo)) { continue } - if err = team.GetMembers(); err != nil { + if err = team.getMembers(e); err != nil { return err } for _, u := range team.Members { @@ -124,28 +123,35 @@ func (r *Repository) RecalcAccessSess() error { } minMode := ACCESS_MODE_READ - if !r.IsPrivate { + if !repo.IsPrivate { minMode = ACCESS_MODE_WRITE } newAccesses := make([]Access, 0, len(accessMap)) for userID, mode := range accessMap { - if userID == r.OwnerId || mode <= minMode { + if userID == repo.OwnerId || mode <= minMode { continue } - newAccesses = append(newAccesses, Access{UserID: userID, RepoID: r.Id, Mode: mode}) + newAccesses = append(newAccesses, Access{ + UserID: userID, + RepoID: repo.Id, + Mode: mode}) } // Delete old accesses for repository - if _, err = x.Delete(&Access{RepoID: r.Id}); err != nil { + if _, err = e.Delete(&Access{RepoID: repo.Id}); err != nil { return err } // And insert the new ones - if _, err = x.Insert(newAccesses); err != nil { + if _, err = e.Insert(newAccesses); err != nil { return err } return nil +} +// RecalculateAccesses recalculates all accesses for repository. +func (r *Repository) RecalculateAccesses() error { + return r.recalculateAccesses(x) } diff --git a/models/action.go b/models/action.go index 5cba2f515a..9ba661f1e2 100644 --- a/models/action.go +++ b/models/action.go @@ -412,21 +412,29 @@ 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, u, newUser *User, repo *Repository) (err error) { action := &Action{ ActUserId: u.Id, ActUserName: u.Name, @@ -438,20 +446,24 @@ func TransferRepoAction(u, newUser *User, repo *Repository) (err error) { IsPrivate: repo.IsPrivate, Content: path.Join(repo.Owner.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'", u.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 + return nil +} + +// TransferRepoAction adds new action for transferring repository. +func TransferRepoAction(u, newUser *User, repo *Repository) (err error) { + return transferRepoAction(x, u, newUser, repo) } // GetFeeds returns action list of given user in given context. diff --git a/models/models.go b/models/models.go index 141e3ac497..1cb7229645 100644 --- a/models/models.go +++ b/models/models.go @@ -24,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{} diff --git a/models/org.go b/models/org.go index d667fb26dc..42b14bf072 100644 --- a/models/org.go +++ b/models/org.go @@ -32,19 +32,31 @@ func (org *User) IsOrgMember(uid int64) bool { return IsOrganizationMember(org.Id, uid) } +func (org *User) getTeam(e Engine, name string) (*Team, error) { + return getTeam(e, org.Id, name) +} + // GetTeam returns named team of organization. func (org *User) GetTeam(name string) (*Team, error) { - return GetTeam(org.Id, name) + return org.getTeam(x, name) +} + +func (org *User) getOwnerTeam(e Engine) (*Team, error) { + return org.getTeam(e, OWNER_TEAM) } // GetOwnerTeam returns owner team of organization. func (org *User) GetOwnerTeam() (*Team, error) { - return org.GetTeam(OWNER_TEAM) + return org.getOwnerTeam(x) +} + +func (org *User) getTeams(e Engine) error { + return e.Where("org_id=?", org.Id).Find(&org.Teams) } // GetTeams returns all teams that belong to organization. func (org *User) GetTeams() error { - return x.Where("org_id=?", org.Id).Find(&org.Teams) + return org.getTeams(x) } // GetMembers returns all members of organization. @@ -430,8 +442,7 @@ func (t *Team) IsMember(uid int64) bool { return IsTeamMember(t.OrgId, t.Id, uid) } -// GetRepositories returns all repositories in team of organization. -func (t *Team) GetRepositories() error { +func (t *Team) getRepositories(e Engine) error { idStrs := strings.Split(t.RepoIds, "|") t.Repos = make([]*Repository, 0, len(idStrs)) for _, str := range idStrs { @@ -442,7 +453,7 @@ func (t *Team) GetRepositories() error { if id == 0 { continue } - repo, err := GetRepositoryById(id) + repo, err := getRepositoryById(e, id) if err != nil { return err } @@ -451,10 +462,19 @@ func (t *Team) GetRepositories() error { return nil } +// GetRepositories returns all repositories in team of organization. +func (t *Team) GetRepositories() error { + return t.getRepositories(x) +} + +func (t *Team) getMembers(e Engine) (err error) { + t.Members, err = getTeamMembers(e, t.Id) + return err +} + // GetMembers returns all members in team of organization. func (t *Team) GetMembers() (err error) { - t.Members, err = GetTeamMembers(t.OrgId, t.Id) - return err + return t.getMembers(x) } // AddMember adds new member to team of organization. @@ -483,7 +503,7 @@ func (t *Team) AddRepository(repo *Repository) (err error) { } sess := x.NewSession() - defer sess.Close() + defer sessionRelease(sess) if err = sess.Begin(); err != nil { return err } @@ -491,26 +511,23 @@ func (t *Team) AddRepository(repo *Repository) (err error) { t.NumRepos++ t.RepoIds += idStr if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil { - sess.Rollback() return err } - if err = repo.RecalcAccessSess(); err != nil { - sess.Rollback() + if err = repo.recalculateAccesses(sess); err != nil { return err } for _, u := range t.Members { - if err = WatchRepo(u.Id, repo.Id, true); err != nil { - sess.Rollback() + if err = watchRepo(sess, u.Id, repo.Id, true); err != nil { return err } } return sess.Commit() } -func (t *Team) HasRepository(r *Repository) bool { - idStr := "$" + com.ToStr(r.Id) + "|" +func (t *Team) HasRepository(repo *Repository) bool { + idStr := "$" + com.ToStr(repo.Id) + "|" return strings.Contains(t.RepoIds, idStr) } @@ -533,7 +550,7 @@ func (t *Team) RemoveRepository(repoId int64) error { } sess := x.NewSession() - defer sess.Close() + defer sessionRelease(sess) if err = sess.Begin(); err != nil { return err } @@ -541,18 +558,15 @@ func (t *Team) RemoveRepository(repoId int64) error { t.NumRepos-- t.RepoIds = strings.Replace(t.RepoIds, idStr, "", 1) if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil { - sess.Rollback() return err } - if err = repo.RecalcAccessSess(); err != nil { - sess.Rollback() + if err = repo.recalculateAccesses(sess); err != nil { return err } for _, u := range t.Members { - if err = WatchRepo(u.Id, repo.Id, false); err != nil { - sess.Rollback() + if err = watchRepo(sess, u.Id, repo.Id, false); err != nil { return err } } @@ -601,13 +615,12 @@ func NewTeam(t *Team) error { return sess.Commit() } -// GetTeam returns team by given team name and organization. -func GetTeam(orgId int64, name string) (*Team, error) { +func getTeam(e Engine, orgId int64, name string) (*Team, error) { t := &Team{ OrgId: orgId, LowerName: strings.ToLower(name), } - has, err := x.Get(t) + has, err := e.Get(t) if err != nil { return nil, err } else if !has { @@ -616,6 +629,11 @@ func GetTeam(orgId int64, name string) (*Team, error) { return t, nil } +// GetTeam returns team by given team name and organization. +func GetTeam(orgId int64, name string) (*Team, error) { + return getTeam(x, orgId, name) +} + func getTeamById(e Engine, teamId int64) (*Team, error) { t := new(Team) has, err := e.Id(teamId).Get(t) @@ -643,19 +661,19 @@ func UpdateTeam(t *Team, authChanged bool) (err error) { } sess := x.NewSession() - defer sess.Close() + defer sessionRelease(sess) if err = sess.Begin(); err != nil { return err } // Update access for team members if needed. if authChanged { - if err = t.GetRepositories(); err != nil { + if err = t.getRepositories(sess); err != nil { return err } for _, repo := range t.Repos { - if err = repo.RecalcAccessSess(); err != nil { + if err = repo.recalculateAccesses(sess); err != nil { return err } } @@ -663,7 +681,6 @@ func UpdateTeam(t *Team, authChanged bool) (err error) { t.LowerName = strings.ToLower(t.Name) if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil { - sess.Rollback() return err } return sess.Commit() @@ -685,32 +702,29 @@ func DeleteTeam(t *Team) error { } sess := x.NewSession() - defer sess.Close() + defer sessionRelease(sess) if err = sess.Begin(); err != nil { return err } // Delete all accesses. for _, repo := range t.Repos { - if err = repo.RecalcAccessSess(); err != nil { + if err = repo.recalculateAccesses(sess); err != nil { return err } } // Delete team-user. if _, err = sess.Where("org_id=?", org.Id).Where("team_id=?", t.Id).Delete(new(TeamUser)); err != nil { - sess.Rollback() return err } // Delete team. if _, err = sess.Id(t.Id).Delete(new(Team)); err != nil { - sess.Rollback() return err } // Update organization number of teams. if _, err = sess.Exec("UPDATE `user` SET num_teams = num_teams - 1 WHERE id = ?", t.OrgId); err != nil { - sess.Rollback() return err } @@ -742,13 +756,17 @@ func IsTeamMember(orgId, teamId, uid int64) bool { return isTeamMember(x, orgId, teamId, uid) } -// GetTeamMembers returns all members in given team of organization. -func GetTeamMembers(orgId, teamId int64) ([]*User, error) { +func getTeamMembers(e Engine, teamID int64) ([]*User, error) { us := make([]*User, 0, 10) - err := x.Sql("SELECT * FROM `user` JOIN `team_user` ON `team_user`.`team_id` = ? AND `team_user`.`uid` = `user`.`id`", teamId).Find(&us) + err := e.Sql("SELECT * FROM `user` JOIN `team_user` ON `team_user`.`team_id` = ? AND `team_user`.`uid` = `user`.`id`", teamID).Find(&us) return us, err } +// GetTeamMembers returns all members in given team of organization. +func GetTeamMembers(teamID int64) ([]*User, error) { + return getTeamMembers(x, teamID) +} + func getUserTeams(e Engine, orgId, uid int64) ([]*Team, error) { tus := make([]*TeamUser, 0, 5) if err := e.Where("uid=?", uid).And("org_id=?", orgId).Find(&tus); err != nil { @@ -796,7 +814,7 @@ func AddTeamMember(orgId, teamId, uid int64) error { } sess := x.NewSession() - defer sess.Close() + defer sessionRelease(sess) if err = sess.Begin(); err != nil { return err } @@ -808,26 +826,21 @@ func AddTeamMember(orgId, teamId, uid int64) error { } if _, err = sess.Insert(tu); err != nil { - sess.Rollback() return err } else if _, err = sess.Id(t.Id).Update(t); err != nil { - sess.Rollback() return err } // Give access to team repositories. for _, repo := range t.Repos { - if err = repo.RecalcAccessSess(); err != nil { - sess.Rollback() + if err = repo.recalculateAccesses(sess); err != nil { return err } } // We make sure it exists before. ou := new(OrgUser) - _, err = sess.Where("uid=?", uid).And("org_id=?", orgId).Get(ou) - if err != nil { - sess.Rollback() + if _, err = sess.Where("uid=?", uid).And("org_id=?", orgId).Get(ou); err != nil { return err } ou.NumTeams++ @@ -835,7 +848,6 @@ func AddTeamMember(orgId, teamId, uid int64) error { ou.IsOwner = true } if _, err = sess.Id(ou.Id).AllCols().Update(ou); err != nil { - sess.Rollback() return err } @@ -860,12 +872,12 @@ func removeTeamMember(e Engine, orgId, teamId, uid int64) error { t.NumMembers-- - if err = t.GetRepositories(); err != nil { + if err = t.getRepositories(e); err != nil { return err } // Get organization. - org, err := GetUserById(orgId) + org, err := getUserById(e, orgId) if err != nil { return err } @@ -884,7 +896,7 @@ func removeTeamMember(e Engine, orgId, teamId, uid int64) error { // Delete access to team repositories. for _, repo := range t.Repos { - if err = repo.RecalcAccessSess(); err != nil { + if err = repo.recalculateAccesses(e); err != nil { return err } } diff --git a/models/repo.go b/models/repo.go index 5a669d9dd8..75e29b0934 100644 --- a/models/repo.go +++ b/models/repo.go @@ -167,13 +167,17 @@ type Repository struct { Updated time.Time `xorm:"UPDATED"` } -func (repo *Repository) GetOwner() (err error) { +func (repo *Repository) getOwner(e Engine) (err error) { if repo.Owner == nil { - repo.Owner, err = GetUserById(repo.OwnerId) + repo.Owner, err = getUserById(e, repo.OwnerId) } return err } +func (repo *Repository) GetOwner() (err error) { + return repo.getOwner(x) +} + func (repo *Repository) GetMirror() (err error) { repo.Mirror, err = GetMirror(repo.Id) return err @@ -403,7 +407,7 @@ func createUpdateHook(repoPath string) error { } // InitRepository initializes README and .gitignore if needed. -func initRepository(f string, u *User, repo *Repository, initReadme bool, repoLang, license string) error { +func initRepository(e Engine, f string, u *User, repo *Repository, initReadme bool, repoLang, license string) error { repoPath := RepoPath(u.Name, repo.Name) // Create bare new repository. @@ -493,12 +497,12 @@ func initRepository(f string, u *User, repo *Repository, initReadme bool, repoLa if len(fileName) == 0 { // Re-fetch the repository from database before updating it (else it would // override changes that were done earlier with sql) - if repo, err = GetRepositoryById(repo.Id); err != nil { + if repo, err = getRepositoryById(e, repo.Id); err != nil { return err } repo.IsBare = true repo.DefaultBranch = "master" - return UpdateRepository(repo) + return updateRepository(e, repo) } // Apply changes and commit. @@ -518,12 +522,6 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror return nil, ErrRepoAlreadyExist } - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { - return nil, err - } - repo := &Repository{ OwnerId: u.Id, Owner: u, @@ -533,8 +531,13 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror IsPrivate: private, } + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return nil, err + } + if _, err = sess.Insert(repo); err != nil { - sess.Rollback() return nil, err } @@ -544,15 +547,12 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror // Give access to all members in owner team. if u.IsOrganization() { - if err = repo.RecalcAccessSess(); err != nil { - sess.Rollback() + if err = repo.recalculateAccesses(sess); err != nil { return nil, err } } - if _, err = sess.Exec( - "UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", u.Id); err != nil { - sess.Rollback() + if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", u.Id); err != nil { return nil, err } @@ -561,63 +561,54 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror t.RepoIds += "$" + com.ToStr(repo.Id) + "|" t.NumRepos++ if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil { - sess.Rollback() return nil, err } } - if err = sess.Commit(); err != nil { - return nil, err - } - if u.IsOrganization() { - t, err := u.GetOwnerTeam() + t, err := u.getOwnerTeam(sess) if err != nil { - log.Error(4, "GetOwnerTeam: %v", err) - } else { - if err = t.GetMembers(); err != nil { - log.Error(4, "GetMembers: %v", err) - } else { - for _, u := range t.Members { - if err = WatchRepo(u.Id, repo.Id, true); err != nil { - log.Error(4, "WatchRepo2: %v", err) - } - } + return nil, fmt.Errorf("get owner team: %v", err) + } else if err = t.getMembers(sess); err != nil { + return nil, fmt.Errorf("get team members: %v", err) + } + + for _, u := range t.Members { + if err = watchRepo(sess, u.Id, repo.Id, true); err != nil { + return nil, fmt.Errorf("watch repository: %v", err) } } } else { - if err = WatchRepo(u.Id, repo.Id, true); err != nil { - log.Error(4, "WatchRepo3: %v", err) + if err = watchRepo(sess, u.Id, repo.Id, true); err != nil { + return nil, fmt.Errorf("watch repository 2: %v", err) } } - if err = NewRepoAction(u, repo); err != nil { - log.Error(4, "NewRepoAction: %v", err) + if err = newRepoAction(sess, u, repo); err != nil { + return nil, fmt.Errorf("new repository action: %v", err) } // No need for init mirror. - if mirror { - return repo, nil - } - - repoPath := RepoPath(u.Name, repo.Name) - if err = initRepository(repoPath, u, repo, initReadme, lang, license); err != nil { - if err2 := os.RemoveAll(repoPath); err2 != nil { - log.Error(4, "initRepository: %v", err) - return nil, fmt.Errorf( - "delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2) + if !mirror { + repoPath := RepoPath(u.Name, repo.Name) + if err = initRepository(sess, repoPath, u, repo, initReadme, lang, license); err != nil { + if err2 := os.RemoveAll(repoPath); err2 != nil { + log.Error(4, "initRepository: %v", err) + return nil, fmt.Errorf( + "delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2) + } + return nil, fmt.Errorf("initRepository: %v", err) } - return nil, fmt.Errorf("initRepository: %v", err) - } - _, stderr, err := process.ExecDir(-1, - repoPath, fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath), - "git", "update-server-info") - if err != nil { - return nil, errors.New("CreateRepository(git update-server-info): " + stderr) + _, stderr, err := process.ExecDir(-1, + repoPath, fmt.Sprintf("CreateRepository(git update-server-info): %s", repoPath), + "git", "update-server-info") + if err != nil { + return nil, errors.New("CreateRepository(git update-server-info): " + stderr) + } } - return repo, nil + return repo, sess.Commit() } // CountRepositories returns number of repositories. @@ -668,7 +659,7 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error { } sess := x.NewSession() - defer sess.Close() + defer sessionRelease(sess) if err = sess.Begin(); err != nil { return err } @@ -679,44 +670,28 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error { repo.OwnerId = newUser.Id repo.Owner = newUser if _, err := sess.Id(repo.Id).Update(repo); err != nil { - sess.Rollback() return err } // Update user repository number. if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", newUser.Id); err != nil { - sess.Rollback() return err - } - - if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?", owner.Id); err != nil { - sess.Rollback() + } else if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?", owner.Id); err != nil { return err - } - - if err = repo.RecalcAccessSess(); err != nil { + } else if err = repo.recalculateAccesses(sess); err != nil { return err - } - - // Change repository directory name. - if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newUser.Name, repo.Name)); err != nil { - sess.Rollback() + } else if err = watchRepo(sess, newUser.Id, repo.Id, true); err != nil { return err - } - - if err = sess.Commit(); err != nil { + } else if err = transferRepoAction(sess, u, newUser, repo); err != nil { return err } - if err = WatchRepo(newUser.Id, repo.Id, true); err != nil { - log.Error(4, "WatchRepo", err) - } - - if err = TransferRepoAction(u, newUser, repo); err != nil { + // Change repository directory name. + if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newUser.Name, repo.Name)); err != nil { return err } - return nil + return sess.Commit() } // ChangeRepositoryName changes all corresponding setting from old repository name to new one. @@ -732,7 +707,7 @@ func ChangeRepositoryName(userName, oldRepoName, newRepoName string) (err error) return os.Rename(RepoPath(userName, oldRepoName), RepoPath(userName, newRepoName)) } -func UpdateRepository(repo *Repository) error { +func updateRepository(e Engine, repo *Repository) error { repo.LowerName = strings.ToLower(repo.Name) if len(repo.Description) > 255 { @@ -741,10 +716,14 @@ func UpdateRepository(repo *Repository) error { if len(repo.Website) > 255 { repo.Website = repo.Website[:255] } - _, err := x.Id(repo.Id).AllCols().Update(repo) + _, err := e.Id(repo.Id).AllCols().Update(repo) return err } +func UpdateRepository(repo *Repository) error { + return updateRepository(x, repo) +} + // DeleteRepository deletes a repository for a user or organization. func DeleteRepository(uid, repoId int64, userName string) error { repo := &Repository{Id: repoId, OwnerId: uid} @@ -898,10 +877,9 @@ func GetRepositoryByName(uid int64, repoName string) (*Repository, error) { return repo, err } -// GetRepositoryById returns the repository by given id if exists. -func GetRepositoryById(id int64) (*Repository, error) { +func getRepositoryById(e Engine, id int64) (*Repository, error) { repo := &Repository{} - has, err := x.Id(id).Get(repo) + has, err := e.Id(id).Get(repo) if err != nil { return nil, err } else if !has { @@ -910,6 +888,11 @@ func GetRepositoryById(id int64) (*Repository, error) { return repo, nil } +// GetRepositoryById returns the repository by given id if exists. +func GetRepositoryById(id int64) (*Repository, error) { + return getRepositoryById(x, id) +} + // GetRepositories returns a list of repositories of given user. func GetRepositories(uid int64, private bool) ([]*Repository, error) { repos := make([]*Repository, 0, 10) @@ -1096,38 +1079,43 @@ type Collaboration struct { } // Add collaborator and accompanying access -func (r *Repository) AddCollaborator(u *User) error { - collaboration := &Collaboration{RepoID: r.Id, UserID: u.Id} +func (repo *Repository) AddCollaborator(u *User) error { + collaboration := &Collaboration{ + RepoID: repo.Id, + UserID: u.Id, + } has, err := x.Get(collaboration) if err != nil { return err - } - if has { + } else if has { return nil } - if _, err = x.InsertOne(collaboration); err != nil { + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { return err } - if err = r.GetOwner(); err != nil { + if _, err = sess.InsertOne(collaboration); err != nil { + return err + } else if err = repo.recalculateAccesses(sess); err != nil { return err } - return r.RecalcAccessSess() + return sess.Commit() } -// GetCollaborators returns the collaborators for a repository -func (r *Repository) GetCollaborators() ([]*User, error) { +func (repo *Repository) getCollaborators(e Engine) ([]*User, error) { collaborations := make([]*Collaboration, 0) - if err := x.Find(&collaborations, &Collaboration{RepoID: r.Id}); err != nil { + if err := e.Find(&collaborations, &Collaboration{RepoID: repo.Id}); err != nil { return nil, err } users := make([]*User, len(collaborations)) for i, c := range collaborations { - user, err := GetUserById(c.UserID) + user, err := getUserById(e, c.UserID) if err != nil { return nil, err } @@ -1136,15 +1124,31 @@ func (r *Repository) GetCollaborators() ([]*User, error) { return users, nil } +// GetCollaborators returns the collaborators for a repository +func (repo *Repository) GetCollaborators() ([]*User, error) { + return repo.getCollaborators(x) +} + // Delete collaborator and accompanying access -func (r *Repository) DeleteCollaborator(u *User) error { - collaboration := &Collaboration{RepoID: r.Id, UserID: u.Id} +func (repo *Repository) DeleteCollaborator(u *User) (err error) { + collaboration := &Collaboration{ + RepoID: repo.Id, + UserID: u.Id, + } + + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } - if has, err := x.Delete(collaboration); err != nil || has == 0 { + if has, err := sess.Delete(collaboration); err != nil || has == 0 { + return err + } else if err = repo.recalculateAccesses(sess); err != nil { return err } - return r.RecalcAccessSess() + return sess.Commit() } // __ __ __ .__ @@ -1193,25 +1197,28 @@ func WatchRepo(uid, repoId int64, watch bool) (err error) { return watchRepo(x, uid, repoId, watch) } -// GetWatchers returns all watchers of given repository. -func GetWatchers(rid int64) ([]*Watch, error) { +func getWatchers(e Engine, rid int64) ([]*Watch, error) { watches := make([]*Watch, 0, 10) - err := x.Find(&watches, &Watch{RepoId: rid}) + err := e.Find(&watches, &Watch{RepoId: rid}) return watches, err } -// NotifyWatchers creates batch of actions for every watcher. -func NotifyWatchers(act *Action) error { +// GetWatchers returns all watchers of given repository. +func GetWatchers(rid int64) ([]*Watch, error) { + return getWatchers(x, rid) +} + +func notifyWatchers(e Engine, act *Action) error { // Add feeds for user self and all watchers. - watches, err := GetWatchers(act.RepoId) + watches, err := getWatchers(e, act.RepoId) if err != nil { - return errors.New("repo.NotifyWatchers(get watches): " + err.Error()) + return fmt.Errorf("get watchers: %v", err) } // Add feed for actioner. act.UserId = act.ActUserId - if _, err = x.InsertOne(act); err != nil { - return errors.New("repo.NotifyWatchers(create action): " + err.Error()) + if _, err = e.InsertOne(act); err != nil { + return fmt.Errorf("insert new actioner: %v", err) } for i := range watches { @@ -1221,13 +1228,18 @@ func NotifyWatchers(act *Action) error { act.Id = 0 act.UserId = watches[i].UserId - if _, err = x.InsertOne(act); err != nil { - return errors.New("repo.NotifyWatchers(create action): " + err.Error()) + if _, err = e.InsertOne(act); err != nil { + return fmt.Errorf("insert new action: %v", err) } } return nil } +// NotifyWatchers creates batch of actions for every watcher. +func NotifyWatchers(act *Action) error { + return notifyWatchers(x, act) +} + // _________ __ // / _____// |______ _______ // \_____ \\ __\__ \\_ __ \ @@ -1296,12 +1308,6 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (*Repositor } } - sess := x.NewSession() - defer sess.Close() - if err = sess.Begin(); err != nil { - return nil, err - } - repo := &Repository{ OwnerId: u.Id, Owner: u, @@ -1313,19 +1319,23 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (*Repositor ForkId: oldRepo.Id, } + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return nil, err + } + if _, err = sess.Insert(repo); err != nil { - sess.Rollback() return nil, err } - if err = repo.RecalcAccessSess(); err != nil { + if err = repo.recalculateAccesses(sess); err != nil { return nil, err } + var t *Team // Owner team. - if _, err = sess.Exec( - "UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", u.Id); err != nil { - sess.Rollback() + if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", u.Id); err != nil { return nil, err } @@ -1334,50 +1344,42 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (*Repositor t.RepoIds += "$" + com.ToStr(repo.Id) + "|" t.NumRepos++ if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil { - sess.Rollback() return nil, err } } if u.IsOrganization() { - t, err := u.GetOwnerTeam() + t, err := u.getOwnerTeam(sess) if err != nil { - log.Error(4, "GetOwnerTeam: %v", err) + return nil, fmt.Errorf("get owner team: %v", err) } else { - if err = t.GetMembers(); err != nil { - log.Error(4, "GetMembers: %v", err) + if err = t.getMembers(sess); err != nil { + return nil, fmt.Errorf("get team members: %v", err) } else { for _, u := range t.Members { if err = watchRepo(sess, u.Id, repo.Id, true); err != nil { - log.Error(4, "WatchRepo2: %v", err) + return nil, fmt.Errorf("watch repository: %v", err) } } } } } else { if err = watchRepo(sess, u.Id, repo.Id, true); err != nil { - log.Error(4, "WatchRepo3: %v", err) + return nil, fmt.Errorf("watch repository 2: %v", err) } } - if err = NewRepoAction(u, repo); err != nil { - log.Error(4, "NewRepoAction: %v", err) + if err = newRepoAction(sess, u, repo); err != nil { + return nil, fmt.Errorf("new repository action: %v", err) } - if _, err = sess.Exec( - "UPDATE `repository` SET num_forks = num_forks + 1 WHERE id = ?", oldRepo.Id); err != nil { - sess.Rollback() + if _, err = sess.Exec("UPDATE `repository` SET num_forks = num_forks + 1 WHERE id = ?", oldRepo.Id); err != nil { return nil, err } oldRepoPath, err := oldRepo.RepoPath() if err != nil { - sess.Rollback() - return nil, fmt.Errorf("fail to get repo path(%s): %v", oldRepo.Name, err) - } - - if err = sess.Commit(); err != nil { - return nil, err + return nil, fmt.Errorf("get old repository path: %v", err) } repoPath := RepoPath(u.Name, repo.Name) @@ -1385,15 +1387,15 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (*Repositor fmt.Sprintf("ForkRepository(git clone): %s/%s", u.Name, repo.Name), "git", "clone", "--bare", oldRepoPath, repoPath) if err != nil { - return nil, errors.New("ForkRepository(git clone): " + stderr) + return nil, fmt.Errorf("git clone: %v", stderr) } _, stderr, err = process.ExecDir(-1, repoPath, fmt.Sprintf("ForkRepository(git update-server-info): %s", repoPath), "git", "update-server-info") if err != nil { - return nil, errors.New("ForkRepository(git update-server-info): " + stderr) + return nil, fmt.Errorf("git update-server-info: %v", err) } - return repo, nil + return repo, sess.Commit() } diff --git a/models/user.go b/models/user.go index 7e3dc260e5..37e77dd3ba 100644 --- a/models/user.go +++ b/models/user.go @@ -521,10 +521,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 { @@ -533,6 +532,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 { diff --git a/modules/middleware/repo.go b/modules/middleware/repo.go index 8465af83ef..ea41b0dddd 100644 --- a/modules/middleware/repo.go +++ b/modules/middleware/repo.go @@ -28,7 +28,7 @@ func ApiRepoAssignment() macaron.Handler { err error ) - // Check if the user is the same as the repository owner + // Check if the user is the same as the repository owner. if ctx.IsSigned && u.LowerName == strings.ToLower(userName) { u = ctx.User } else { @@ -216,9 +216,9 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler { u, err = models.GetUserByName(userName) if err != nil { if err == models.ErrUserNotExist { - ctx.Error(404) + ctx.Handle(404, "GetUserByName", err) } else { - ctx.JSON(500, &base.ApiJsonErr{"GetUserByName: " + err.Error(), base.DOC_URL}) + ctx.Handle(500, "GetUserByName", err) } return } @@ -229,20 +229,20 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler { repo, err := models.GetRepositoryByName(u.Id, repoName) if err != nil { if err == models.ErrRepoNotExist { - ctx.Error(404) + ctx.Handle(404, "GetRepositoryByName", err) } else { - ctx.JSON(500, &base.ApiJsonErr{"GetRepositoryByName: " + err.Error(), base.DOC_URL}) + ctx.Handle(500, "GetRepositoryByName", err) } return } else if err = repo.GetOwner(); err != nil { - ctx.JSON(500, &base.ApiJsonErr{"GetOwner: " + err.Error(), base.DOC_URL}) + ctx.Handle(500, "GetOwner", err) return } if ctx.IsSigned { mode, err := models.AccessLevel(ctx.User, repo) if err != nil { - ctx.JSON(500, &base.ApiJsonErr{"AccessLevel: " + err.Error(), base.DOC_URL}) + ctx.Handle(500, "AccessLevel", err) return } ctx.Repo.IsOwner = mode >= models.ACCESS_MODE_WRITE @@ -252,7 +252,7 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler { // Check access. if repo.IsPrivate && !ctx.Repo.IsOwner { - ctx.Error(404) + ctx.Handle(404, "no access right", err) return } ctx.Repo.HasAccess = true diff --git a/routers/repo/http.go b/routers/repo/http.go index a209c2b254..034b5a7b5e 100644 --- a/routers/repo/http.go +++ b/routers/repo/http.go @@ -288,7 +288,7 @@ func serviceRpc(rpc string, hr handler) { access := hasAccess(r, hr.Config, dir, rpc, true) if access == false { - renderACCESS_MODE_NONE(w) + renderNoAccess(w) return } @@ -515,7 +515,7 @@ func renderNotFound(w http.ResponseWriter) { w.Write([]byte("Not Found")) } -func renderACCESS_MODE_NONE(w http.ResponseWriter) { +func renderNoAccess(w http.ResponseWriter) { w.WriteHeader(http.StatusForbidden) w.Write([]byte("Forbidden")) } diff --git a/routers/user/home.go b/routers/user/home.go index ce82ae772c..35407534f9 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -210,8 +210,11 @@ func Profile(ctx *middleware.Context) { if !ctx.IsSigned { continue } - if has, _ := models.HasAccess(ctx.User, &models.Repository{Id: act.RepoId, IsPrivate: true}, - models.ACCESS_MODE_READ); !has { + if has, _ := models.HasAccess(ctx.User, + &models.Repository{ + Id: act.RepoId, + IsPrivate: true, + }, models.ACCESS_MODE_READ); !has { continue } } -- cgit v1.2.3 From f9454cc32c94780eb4c49753fc0ccd9b60b1deb7 Mon Sep 17 00:00:00 2001 From: Peter Smit Date: Mon, 16 Feb 2015 12:00:06 +0200 Subject: Make sure that a mirror can't be written to by http or ssh --- cmd/serve.go | 5 +++++ routers/repo/http.go | 5 +++++ 2 files changed, 10 insertions(+) (limited to 'routers') diff --git a/cmd/serve.go b/cmd/serve.go index e8e5c186c7..9e34b95c5a 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -164,6 +164,11 @@ func runServ(c *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) } + + 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 diff --git a/routers/repo/http.go b/routers/repo/http.go index 034b5a7b5e..d47d73ef05 100644 --- a/routers/repo/http.go +++ b/routers/repo/http.go @@ -158,6 +158,11 @@ func Http(ctx *middleware.Context) { return } } + + if !isPull && repo.IsMirror { + ctx.Handle(401, "can't push to mirror", nil) + return + } } } -- cgit v1.2.3 From ed89b39984a9191380263eaf357c3a9c71770674 Mon Sep 17 00:00:00 2001 From: Peter Smit Date: Mon, 16 Feb 2015 12:51:56 +0200 Subject: Updating context and fixing permission issues The boolean flags in the repo context have been replaced with mode and two methods Also, the permissions have been brought more in line with https://help.github.com/articles/permission-levels-for-an-organization-repository/ , Admin Team members are able to change settings of their repositories. --- cmd/web.go | 4 ++-- modules/middleware/context.go | 55 +++++++++++++++++++++++++------------------ modules/middleware/repo.go | 47 +++++++++++++----------------------- routers/api/v1/repo_file.go | 2 +- routers/repo/issue.go | 14 +++++------ routers/repo/release.go | 10 ++++---- routers/repo/repo.go | 2 +- templates/repo/header.tmpl | 2 +- templates/repo/sidebar.tmpl | 2 +- templates/repo/toolbar.tmpl | 2 +- 10 files changed, 68 insertions(+), 72 deletions(-) (limited to 'routers') diff --git a/cmd/web.go b/cmd/web.go index 3284acb9df..8b3b03c45e 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -319,7 +319,7 @@ func runWeb(ctx *cli.Context) { m.Get("/template/*", dev.TemplatePreview) } - reqTrueOwner := middleware.RequireTrueOwner() + reqAdmin := middleware.RequireAdmin() // Organization. m.Group("/org", func() { @@ -394,7 +394,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) diff --git a/modules/middleware/context.go b/modules/middleware/context.go index 28be3a3025..a266109691 100644 --- a/modules/middleware/context.go +++ b/modules/middleware/context.go @@ -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"] diff --git a/modules/middleware/repo.go b/modules/middleware/repo.go index bd298819d7..5c863dc01f 100644 --- a/modules/middleware/repo.go +++ b/modules/middleware/repo.go @@ -58,24 +58,19 @@ func ApiRepoAssignment() macaron.Handler { return } - if ctx.IsSigned { - mode, err := models.AccessLevel(ctx.User, repo) - if err != nil { - ctx.JSON(500, &base.ApiJsonErr{"AccessLevel: " + err.Error(), base.DOC_URL}) - return - } - - ctx.Repo.IsOwner = mode >= models.ACCESS_MODE_WRITE - ctx.Repo.IsAdmin = mode >= models.ACCESS_MODE_READ - ctx.Repo.IsTrueOwner = mode >= models.ACCESS_MODE_OWNER + mode, err := models.AccessLevel(ctx.User, repo) + if err != nil { + ctx.JSON(500, &base.ApiJsonErr{"AccessLevel: " + err.Error(), base.DOC_URL}) + return } + ctx.Repo.AccessMode = mode + // Check access. - if repo.IsPrivate && !ctx.Repo.IsOwner { + if ctx.Repo.AccessMode == models.ACCESS_MODE_NONE { ctx.Error(404) return } - ctx.Repo.HasAccess = true ctx.Repo.Repository = repo } @@ -239,26 +234,18 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler { return } - if ctx.IsSigned { - mode, err := models.AccessLevel(ctx.User, repo) - if err != nil { - ctx.Handle(500, "AccessLevel", err) - return - } - ctx.Repo.IsOwner = mode >= models.ACCESS_MODE_WRITE - ctx.Repo.IsAdmin = mode >= models.ACCESS_MODE_READ - ctx.Repo.IsTrueOwner = mode >= models.ACCESS_MODE_OWNER - if !ctx.Repo.IsTrueOwner && ctx.Repo.Owner.IsOrganization() { - ctx.Repo.IsTrueOwner = ctx.Repo.Owner.IsOwnedBy(ctx.User.Id) - } + 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.Repo.AccessMode == models.ACCESS_MODE_NONE { ctx.Handle(404, "no access right", err) return } - ctx.Repo.HasAccess = true ctx.Data["HasAccess"] = true @@ -306,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() @@ -362,9 +349,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") diff --git a/routers/api/v1/repo_file.go b/routers/api/v1/repo_file.go index a049904f95..73f97b2cae 100644 --- a/routers/api/v1/repo_file.go +++ b/routers/api/v1/repo_file.go @@ -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 } diff --git a/routers/repo/issue.go b/routers/repo/issue.go index bf39d9aba6..40e9338970 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -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{ @@ -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 } @@ -560,7 +560,7 @@ func UpdateIssueLabel(ctx *middleware.Context) { } func UpdateIssueMilestone(ctx *middleware.Context) { - if !ctx.Repo.IsOwner { + if !ctx.Repo.IsOwner() { ctx.Error(403) return } @@ -606,7 +606,7 @@ func UpdateIssueMilestone(ctx *middleware.Context) { } func UpdateAssignee(ctx *middleware.Context) { - if !ctx.Repo.IsOwner { + if !ctx.Repo.IsOwner() { ctx.Error(403) return } @@ -752,7 +752,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 { diff --git a/routers/repo/release.go b/routers/repo/release.go index 591810cc5f..52d78b1967 100644 --- a/routers/repo/release.go +++ b/routers/repo/release.go @@ -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 } diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 48f7b09bc0..0053720030 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -343,7 +343,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 } diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index a0b927be60..21f9cea882 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -49,7 +49,7 @@
  • - +
  • -->{{end}}{{if .IsRepositoryTrueOwner}} + -->{{end}}{{if .IsRepositoryAdmin}}
  • Settings
  • {{end}} -- cgit v1.2.3 From 455fad0fbd85137af5d3efec93d2b974e9383b98 Mon Sep 17 00:00:00 2001 From: Peter Smit Date: Mon, 16 Feb 2015 13:16:24 +0200 Subject: Fix that owners also see actions on their repositories This is a balance between speed and nice code, where speed has won. To prevent a repository query for each action the ownername is match with the current user. It would be "cleaner" or "better" if we fetch the repository each time. Another option is to add the RepoOwnerID to action --- routers/user/home.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'routers') diff --git a/routers/user/home.go b/routers/user/home.go index 35407534f9..574c6387dc 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -103,7 +103,12 @@ 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, &models.Repository{Id: act.RepoId, IsPrivate: true}, models.ACCESS_MODE_READ); !has { + repo := &models.Repository{Id: act.RepoId, IsPrivate: true} + // This prevents having to retrieve the repository for each action + if act.RepoUserName == ctx.User.LowerName { + repo.OwnerId = ctx.User.Id + } + if has, _ := models.HasAccess(ctx.User, repo, models.ACCESS_MODE_READ); !has { continue } } @@ -210,11 +215,12 @@ func Profile(ctx *middleware.Context) { if !ctx.IsSigned { continue } - if has, _ := models.HasAccess(ctx.User, - &models.Repository{ - Id: act.RepoId, - IsPrivate: true, - }, models.ACCESS_MODE_READ); !has { + repo := &models.Repository{Id: act.RepoId, IsPrivate: true} + // This prevents having to retrieve the repository for each action + if act.RepoUserName == ctx.User.LowerName { + repo.OwnerId = ctx.User.Id + } + if has, _ := models.HasAccess(ctx.User, repo, models.ACCESS_MODE_READ); !has { continue } } -- cgit v1.2.3 From 0fa209d07b6155943bfd235257a4ed6a484a9a85 Mon Sep 17 00:00:00 2001 From: Peter Smit Date: Wed, 18 Feb 2015 08:59:22 +0200 Subject: Update/simplify fix that owners also see actions on their repositories --- routers/user/home.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'routers') diff --git a/routers/user/home.go b/routers/user/home.go index 574c6387dc..0a1d9dd217 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -103,14 +103,14 @@ func Dashboard(ctx *middleware.Context) { feeds := make([]*models.Action, 0, len(actions)) for _, act := range actions { if act.IsPrivate { - repo := &models.Repository{Id: act.RepoId, IsPrivate: true} // This prevents having to retrieve the repository for each action - if act.RepoUserName == ctx.User.LowerName { - repo.OwnerId = ctx.User.Id - } - if has, _ := models.HasAccess(ctx.User, repo, models.ACCESS_MODE_READ); !has { - continue + 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) @@ -215,14 +215,14 @@ func Profile(ctx *middleware.Context) { if !ctx.IsSigned { continue } - repo := &models.Repository{Id: act.RepoId, IsPrivate: true} // This prevents having to retrieve the repository for each action - if act.RepoUserName == ctx.User.LowerName { - repo.OwnerId = ctx.User.Id - } - if has, _ := models.HasAccess(ctx.User, repo, models.ACCESS_MODE_READ); !has { - continue + 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) -- cgit v1.2.3 From ee68a826a55c6a4305e7f609db57501a54a5bc47 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Mon, 23 Feb 2015 02:15:53 -0500 Subject: v4 migration, merge 'dev', clean code and mirror fix --- gogs.go | 2 +- models/access.go | 37 +++-- models/action.go | 20 +-- models/migrations/migrations.go | 76 +++++++++-- models/models.go | 5 +- models/org.go | 296 ++++++++++++++++++++++++---------------- models/repo.go | 132 ++++++++---------- models/user.go | 4 +- routers/org/teams.go | 2 +- routers/repo/setting.go | 1 - templates/.VERSION | 2 +- 11 files changed, 343 insertions(+), 234 deletions(-) (limited to 'routers') diff --git a/gogs.go b/gogs.go index b2366b6341..58cbb1a05c 100644 --- a/gogs.go +++ b/gogs.go @@ -17,7 +17,7 @@ import ( "github.com/gogits/gogs/modules/setting" ) -const APP_VER = "0.5.14.0222 Beta" +const APP_VER = "0.5.15.0223 Beta" func init() { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/models/access.go b/models/access.go index 1e1c0114ed..29d1a730b7 100644 --- a/models/access.go +++ b/models/access.go @@ -24,21 +24,19 @@ type Access struct { Mode AccessMode } -// Return 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, r *Repository) (AccessMode, error) { +func accessLevel(e Engine, u *User, repo *Repository) (AccessMode, error) { mode := ACCESS_MODE_NONE - if !r.IsPrivate { + if !repo.IsPrivate { mode = ACCESS_MODE_READ } if u != nil { - if u.Id == r.OwnerId { + if u.Id == repo.OwnerId { return ACCESS_MODE_OWNER, nil } - a := &Access{UserID: u.Id, RepoID: r.Id} - if has, err := x.Get(a); !has || err != 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 @@ -47,12 +45,22 @@ func AccessLevel(u *User, r *Repository) (AccessMode, error) { return mode, nil } -// HasAccess returns true if someone has the request access level. User can be nil! -func HasAccess(u *User, r *Repository, testMode AccessMode) (bool, error) { - mode, err := AccessLevel(u, r) +// 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) +} + +func hasAccess(e Engine, u *User, repo *Repository, testMode AccessMode) (bool, error) { + mode, err := accessLevel(e, u, repo) return testMode <= mode, 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) +} + // GetAccessibleRepositories finds all repositories where a user has access to, // besides his own. func (u *User) GetAccessibleRepositories() (map[*Repository]AccessMode, error) { @@ -96,7 +104,8 @@ func (repo *Repository) recalculateTeamAccesses(e Engine, mode AccessMode) error func (repo *Repository) recalculateAccesses(e Engine) error { accessMap := make(map[int64]AccessMode, 20) - // Give all collaborators write access + // FIXME: should be able to have read-only access. + // Give all collaborators write access. collaborators, err := repo.getCollaborators(e) if err != nil { return err @@ -114,9 +123,7 @@ func (repo *Repository) recalculateAccesses(e Engine) error { } for _, team := range repo.Owner.Teams { - if !(team.IsOwnerTeam() || team.HasRepository(repo)) { - continue - } else if team.IsOwnerTeam() { + if team.IsOwnerTeam() { team.Authorize = ACCESS_MODE_OWNER } @@ -129,6 +136,8 @@ func (repo *Repository) recalculateAccesses(e Engine) error { } } + // FIXME: do corss-comparison so reduce deletions and additions to the minimum? + minMode := ACCESS_MODE_READ if !repo.IsPrivate { minMode = ACCESS_MODE_WRITE diff --git a/models/action.go b/models/action.go index 9ba661f1e2..fee5e5e676 100644 --- a/models/action.go +++ b/models/action.go @@ -434,20 +434,20 @@ func NewRepoAction(u *User, repo *Repository) (err error) { return newRepoAction(x, u, repo) } -func transferRepoAction(e Engine, u, newUser *User, repo *Repository) (err error) { +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(e, action); err != nil { - return fmt.Errorf("notify watchers '%d/%s'", u.Id, repo.Id) + return fmt.Errorf("notify watchers '%d/%s'", actUser.Id, repo.Id) } // Remove watch for organization. @@ -457,13 +457,13 @@ func transferRepoAction(e Engine, u, newUser *User, repo *Repository) (err error } } - log.Trace("action.TransferRepoAction: %s/%s", u.Name, repo.Name) + log.Trace("action.TransferRepoAction: %s/%s", actUser.Name, repo.Name) return nil } // TransferRepoAction adds new action for transferring repository. -func TransferRepoAction(u, newUser *User, repo *Repository) (err error) { - return transferRepoAction(x, u, newUser, repo) +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. diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index f6ef513207..25c130a926 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -53,6 +53,7 @@ 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 @@ -214,8 +215,8 @@ func accessToCollaboration(x *xorm.Engine) (err error) { } 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("drop table: %v", err) + 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 } @@ -239,9 +240,9 @@ func accessRefactor(x *xorm.Engine) (err error) { 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") + 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 err + return fmt.Errorf("select repositories: %v", err) } for _, repo := range results { repoID := com.StrTo(repo["repo_id"]).MustInt64() @@ -249,9 +250,9 @@ func accessRefactor(x *xorm.Engine) (err error) { 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) + results, err := x.Query("SELECT `user_id` FROM `collaboration` WHERE repo_id=?", repoID) if err != nil { - return fmt.Errorf("select repos: %v", err) + return fmt.Errorf("select collaborators: %v", err) } for _, user := range results { userID := com.StrTo(user["user_id"]).MustInt64() @@ -262,6 +263,8 @@ func accessRefactor(x *xorm.Engine) (err error) { continue } + // The minimum level to add a new access record, + // because public repository has implicit open access. minAccessLevel := AccessMode(0) if !isPrivate { minAccessLevel = 1 @@ -269,7 +272,7 @@ func accessRefactor(x *xorm.Engine) (err error) { 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)) + 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) } @@ -281,20 +284,20 @@ func accessRefactor(x *xorm.Engine) (err error) { 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) + 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["uid"]).MustInt64() + 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 table: %v", err) + 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 @@ -313,7 +316,56 @@ func accessRefactor(x *xorm.Engine) (err error) { accesses = append(accesses, &Access{UserID: ur.UserID, RepoID: ur.RepoID, Mode: mode}) } - _, err = sess.Insert(accesses) + 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() } diff --git a/models/models.go b/models/models.go index b0c3b8978f..a9436fca85 100644 --- a/models/models.go +++ b/models/models.go @@ -16,7 +16,7 @@ import ( "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" ) @@ -59,7 +59,8 @@ func init() { 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)) } diff --git a/models/org.go b/models/org.go index 8cb17b6054..629e9345c3 100644 --- a/models/org.go +++ b/models/org.go @@ -6,11 +6,10 @@ package models import ( "errors" + "fmt" "os" "strings" - "github.com/Unknwon/com" - "github.com/gogits/gogs/modules/base" ) @@ -126,53 +125,48 @@ func CreateOrganization(org, owner *User) (*User, error) { org.NumMembers = 1 sess := x.NewSession() - defer sess.Close() + defer sessionRelease(sess) if err = sess.Begin(); err != nil { return nil, err } if _, err = sess.Insert(org); err != nil { - sess.Rollback() - return nil, err - } - - if err = os.MkdirAll(UserPath(org.Name), os.ModePerm); err != nil { - sess.Rollback() return nil, err } // Create default owner team. t := &Team{ - OrgId: org.Id, + OrgID: org.Id, LowerName: strings.ToLower(OWNER_TEAM), Name: OWNER_TEAM, Authorize: ACCESS_MODE_OWNER, NumMembers: 1, } if _, err = sess.Insert(t); err != nil { - sess.Rollback() return nil, err } // Add initial creator to organization and owner team. ou := &OrgUser{ Uid: owner.Id, - OrgId: org.Id, + OrgID: org.Id, IsOwner: true, NumTeams: 1, } if _, err = sess.Insert(ou); err != nil { - sess.Rollback() return nil, err } tu := &TeamUser{ Uid: owner.Id, - OrgId: org.Id, - TeamId: t.Id, + OrgID: org.Id, + TeamID: t.ID, } if _, err = sess.Insert(tu); err != nil { - sess.Rollback() + return nil, err + } + + if err = os.MkdirAll(UserPath(org.Name), os.ModePerm); err != nil { return nil, err } @@ -223,15 +217,15 @@ func DeleteOrganization(org *User) (err error) { return err } - if _, err = sess.Delete(&Team{OrgId: org.Id}); err != nil { + if _, err = sess.Delete(&Team{OrgID: org.Id}); err != nil { sess.Rollback() return err } - if _, err = sess.Delete(&OrgUser{OrgId: org.Id}); err != nil { + if _, err = sess.Delete(&OrgUser{OrgID: org.Id}); err != nil { sess.Rollback() return err } - if _, err = sess.Delete(&TeamUser{OrgId: org.Id}); err != nil { + if _, err = sess.Delete(&TeamUser{OrgID: org.Id}); err != nil { sess.Rollback() return err } @@ -247,9 +241,9 @@ func DeleteOrganization(org *User) (err error) { // OrgUser represents an organization-user relation. type OrgUser struct { - Id int64 + ID int64 `xorm:"pk autoincr"` Uid int64 `xorm:"INDEX UNIQUE(s)"` - OrgId int64 `xorm:"INDEX UNIQUE(s)"` + OrgID int64 `xorm:"INDEX UNIQUE(s)"` IsPublic bool IsOwner bool NumTeams int @@ -298,7 +292,7 @@ func ChangeOrgUserStatus(orgId, uid int64, public bool) error { } ou.IsPublic = public - _, err = x.Id(ou.Id).AllCols().Update(ou) + _, err = x.Id(ou.ID).AllCols().Update(ou) return err } @@ -316,7 +310,7 @@ func AddOrgUser(orgId, uid int64) error { ou := &OrgUser{ Uid: uid, - OrgId: orgId, + OrgID: orgId, } if _, err := sess.Insert(ou); err != nil { @@ -367,7 +361,7 @@ func RemoveOrgUser(orgId, uid int64) error { return err } - if _, err := sess.Id(ou.Id).Delete(ou); err != nil { + if _, err := sess.Id(ou.ID).Delete(ou); err != nil { sess.Rollback() return err } else if _, err = sess.Exec("UPDATE `user` SET num_members = num_members - 1 WHERE id = ?", orgId); err != nil { @@ -400,7 +394,7 @@ func RemoveOrgUser(orgId, uid int64) error { return err } for _, t := range ts { - if err = removeTeamMember(sess, org.Id, t.Id, u.Id); err != nil { + if err = removeTeamMember(sess, org.Id, t.ID, u.Id); err != nil { return err } } @@ -419,13 +413,12 @@ const OWNER_TEAM = "Owners" // Team represents a organization team. type Team struct { - Id int64 - OrgId int64 `xorm:"INDEX"` + ID int64 `xorm:"pk autoincr"` + OrgID int64 `xorm:"INDEX"` LowerName string Name string Description string Authorize AccessMode - RepoIds string `xorm:"TEXT"` Repos []*Repository `xorm:"-"` Members []*User `xorm:"-"` NumRepos int @@ -439,23 +432,20 @@ func (t *Team) IsOwnerTeam() bool { // IsTeamMember returns true if given user is a member of team. func (t *Team) IsMember(uid int64) bool { - return IsTeamMember(t.OrgId, t.Id, uid) + return IsTeamMember(t.OrgID, t.ID, uid) } -func (t *Team) getRepositories(e Engine) error { - idStrs := strings.Split(t.RepoIds, "|") - t.Repos = make([]*Repository, 0, len(idStrs)) - for _, str := range idStrs { - if len(str) == 0 { - continue - } - id := com.StrTo(str[1:]).MustInt64() - if id == 0 { - continue - } - repo, err := getRepositoryById(e, id) +func (t *Team) getRepositories(e Engine) (err error) { + teamRepos := make([]*TeamRepo, 0, t.NumRepos) + if err = x.Where("team_id=?", t.ID).Find(&teamRepos); err != nil { + return fmt.Errorf("get team-repos: %v", err) + } + + t.Repos = make([]*Repository, 0, len(teamRepos)) + for i := range teamRepos { + repo, err := getRepositoryById(e, teamRepos[i].RepoID) if err != nil { - return err + return fmt.Errorf("getRepositoryById(%d): %v", teamRepos[i].RepoID, err) } t.Repos = append(t.Repos, repo) } @@ -468,7 +458,7 @@ func (t *Team) GetRepositories() error { } func (t *Team) getMembers(e Engine) (err error) { - t.Members, err = getTeamMembers(e, t.Id) + t.Members, err = getTeamMembers(e, t.ID) return err } @@ -479,96 +469,121 @@ func (t *Team) GetMembers() (err error) { // AddMember adds new member to team of organization. func (t *Team) AddMember(uid int64) error { - return AddTeamMember(t.OrgId, t.Id, uid) + return AddTeamMember(t.OrgID, t.ID, uid) } // RemoveMember removes member from team of organization. func (t *Team) RemoveMember(uid int64) error { - return RemoveTeamMember(t.OrgId, t.Id, uid) + return RemoveTeamMember(t.OrgID, t.ID, uid) } -// AddRepository adds new repository to team of organization. -func (t *Team) AddRepository(repo *Repository) (err error) { - idStr := "$" + com.ToStr(repo.Id) + "|" - if repo.OwnerId != t.OrgId { - return errors.New("Repository not belong to organization") - } else if strings.Contains(t.RepoIds, idStr) { - return nil - } +func (t *Team) hasRepository(e Engine, repoID int64) bool { + return hasTeamRepo(e, t.OrgID, t.ID, repoID) +} - if err = repo.GetOwner(); err != nil { - return err - } else if err = t.GetMembers(); err != nil { - return err - } +// HasRepository returns true if given repository belong to team. +func (t *Team) HasRepository(repoID int64) bool { + return HasTeamRepo(t.OrgID, t.ID, repoID) +} - sess := x.NewSession() - defer sessionRelease(sess) - if err = sess.Begin(); err != nil { +func (t *Team) addRepository(e Engine, repo *Repository) (err error) { + if err = addTeamRepo(e, t.OrgID, t.ID, repo.Id); err != nil { return err } t.NumRepos++ - t.RepoIds += idStr - if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil { + if _, err = e.Id(t.ID).AllCols().Update(t); err != nil { return err } - if err = repo.recalculateAccesses(sess); err != nil { + if err = repo.recalculateAccesses(e); err != nil { return err } + if err = t.getMembers(e); err != nil { + return fmt.Errorf("get team members: %v", err) + } for _, u := range t.Members { - if err = watchRepo(sess, u.Id, repo.Id, true); err != nil { + if err = watchRepo(e, u.Id, repo.Id, true); err != nil { return err } } - return sess.Commit() -} - -func (t *Team) HasRepository(repo *Repository) bool { - idStr := "$" + com.ToStr(repo.Id) + "|" - return strings.Contains(t.RepoIds, idStr) + return nil } -// RemoveRepository removes repository from team of organization. -func (t *Team) RemoveRepository(repoId int64) error { - idStr := "$" + com.ToStr(repoId) + "|" - if !strings.Contains(t.RepoIds, idStr) { +// AddRepository adds new repository to team of organization. +func (t *Team) AddRepository(repo *Repository) (err error) { + if repo.OwnerId != t.OrgID { + return errors.New("Repository does not belong to organization") + } else if t.HasRepository(repo.Id) { return nil } - repo, err := GetRepositoryById(repoId) - if err != nil { + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { return err } - if err = repo.GetOwner(); err != nil { - return err - } else if err = t.GetMembers(); err != nil { + if err = t.addRepository(sess, repo); err != nil { return err } - sess := x.NewSession() - defer sessionRelease(sess) - if err = sess.Begin(); err != nil { + return sess.Commit() +} + +func (t *Team) removeRepository(e Engine, repo *Repository) (err error) { + if err = removeTeamRepo(e, t.ID, repo.Id); err != nil { return err } t.NumRepos-- - t.RepoIds = strings.Replace(t.RepoIds, idStr, "", 1) - if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil { + if _, err = e.Id(t.ID).AllCols().Update(t); err != nil { return err } - if err = repo.recalculateAccesses(sess); err != nil { + if err = repo.recalculateAccesses(e); err != nil { return err } + if err = t.getMembers(e); err != nil { + return fmt.Errorf("get team members: %v", err) + } for _, u := range t.Members { - if err = watchRepo(sess, u.Id, repo.Id, false); err != nil { + has, err := hasAccess(e, u, repo, ACCESS_MODE_READ) + if err != nil { return err + } else if has { + continue } + + if err = watchRepo(e, u.Id, repo.Id, false); err != nil { + return err + } + } + + return nil +} + +// RemoveRepository removes repository from team of organization. +func (t *Team) RemoveRepository(repoID int64) error { + if !t.HasRepository(repoID) { + return nil + } + + repo, err := GetRepositoryById(repoID) + if err != nil { + return err + } + + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + if err = t.removeRepository(sess, repo); err != nil { + return err } return sess.Commit() @@ -581,7 +596,7 @@ func NewTeam(t *Team) error { return ErrTeamNameIllegal } - has, err := x.Id(t.OrgId).Get(new(User)) + has, err := x.Id(t.OrgID).Get(new(User)) if err != nil { return err } else if !has { @@ -589,7 +604,7 @@ func NewTeam(t *Team) error { } t.LowerName = strings.ToLower(t.Name) - has, err = x.Where("org_id=?", t.OrgId).And("lower_name=?", t.LowerName).Get(new(Team)) + has, err = x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).Get(new(Team)) if err != nil { return err } else if has { @@ -608,7 +623,7 @@ func NewTeam(t *Team) error { } // Update organization number of teams. - if _, err = sess.Exec("UPDATE `user` SET num_teams = num_teams + 1 WHERE id = ?", t.OrgId); err != nil { + if _, err = sess.Exec("UPDATE `user` SET num_teams = num_teams + 1 WHERE id = ?", t.OrgID); err != nil { sess.Rollback() return err } @@ -617,7 +632,7 @@ func NewTeam(t *Team) error { func getTeam(e Engine, orgId int64, name string) (*Team, error) { t := &Team{ - OrgId: orgId, + OrgID: orgId, LowerName: strings.ToLower(name), } has, err := e.Get(t) @@ -667,7 +682,7 @@ func UpdateTeam(t *Team, authChanged bool) (err error) { } t.LowerName = strings.ToLower(t.Name) - if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil { + if _, err = sess.Id(t.ID).AllCols().Update(t); err != nil { return err } @@ -697,7 +712,7 @@ func DeleteTeam(t *Team) error { } // Get organization. - org, err := GetUserById(t.OrgId) + org, err := GetUserById(t.OrgID) if err != nil { return err } @@ -716,16 +731,16 @@ func DeleteTeam(t *Team) error { } // Delete team-user. - if _, err = sess.Where("org_id=?", org.Id).Where("team_id=?", t.Id).Delete(new(TeamUser)); err != nil { + if _, err = sess.Where("org_id=?", org.Id).Where("team_id=?", t.ID).Delete(new(TeamUser)); err != nil { return err } // Delete team. - if _, err = sess.Id(t.Id).Delete(new(Team)); err != nil { + if _, err = sess.Id(t.ID).Delete(new(Team)); err != nil { return err } // Update organization number of teams. - if _, err = sess.Exec("UPDATE `user` SET num_teams = num_teams - 1 WHERE id = ?", t.OrgId); err != nil { + if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil { return err } @@ -741,20 +756,20 @@ func DeleteTeam(t *Team) error { // TeamUser represents an team-user relation. type TeamUser struct { - Id int64 - Uid int64 - OrgId int64 `xorm:"INDEX"` - TeamId int64 + ID int64 `xorm:"pk autoincr"` + OrgID int64 `xorm:"INDEX"` + TeamID int64 `xorm:"UNIQUE(s)"` + Uid int64 `xorm:"UNIQUE(s)"` } -func isTeamMember(e Engine, orgId, teamId, uid int64) bool { - has, _ := e.Where("uid=?", uid).And("org_id=?", orgId).And("team_id=?", teamId).Get(new(TeamUser)) +func isTeamMember(e Engine, orgID, teamID, uid int64) bool { + has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("uid=?", uid).Get(new(TeamUser)) return has } // IsTeamMember returns true if given user is a member of team. -func IsTeamMember(orgId, teamId, uid int64) bool { - return isTeamMember(x, orgId, teamId, uid) +func IsTeamMember(orgID, teamID, uid int64) bool { + return isTeamMember(x, orgID, teamID, uid) } func getTeamMembers(e Engine, teamID int64) ([]*User, error) { @@ -777,7 +792,7 @@ func getUserTeams(e Engine, orgId, uid int64) ([]*Team, error) { ts := make([]*Team, len(tus)) for i, tu := range tus { t := new(Team) - has, err := e.Id(tu.TeamId).Get(t) + has, err := e.Id(tu.TeamID).Get(t) if err != nil { return nil, err } else if !has { @@ -822,13 +837,13 @@ func AddTeamMember(orgId, teamId, uid int64) error { tu := &TeamUser{ Uid: uid, - OrgId: orgId, - TeamId: teamId, + OrgID: orgId, + TeamID: teamId, } if _, err = sess.Insert(tu); err != nil { return err - } else if _, err = sess.Id(t.Id).Update(t); err != nil { + } else if _, err = sess.Id(t.ID).Update(t); err != nil { return err } @@ -848,7 +863,7 @@ func AddTeamMember(orgId, teamId, uid int64) error { if t.IsOwnerTeam() { ou.IsOwner = true } - if _, err = sess.Id(ou.Id).AllCols().Update(ou); err != nil { + if _, err = sess.Id(ou.ID).AllCols().Update(ou); err != nil { return err } @@ -885,13 +900,13 @@ func removeTeamMember(e Engine, orgId, teamId, uid int64) error { tu := &TeamUser{ Uid: uid, - OrgId: orgId, - TeamId: teamId, + OrgID: orgId, + TeamID: teamId, } if _, err := e.Delete(tu); err != nil { return err - } else if _, err = e.Id(t.Id).AllCols().Update(t); err != nil { + } else if _, err = e.Id(t.ID).AllCols().Update(t); err != nil { return err } @@ -912,7 +927,7 @@ func removeTeamMember(e Engine, orgId, teamId, uid int64) error { if t.IsOwnerTeam() { ou.IsOwner = false } - if _, err = e.Id(ou.Id).AllCols().Update(ou); err != nil { + if _, err = e.Id(ou.ID).AllCols().Update(ou); err != nil { return err } return nil @@ -921,13 +936,64 @@ func removeTeamMember(e Engine, orgId, teamId, uid int64) error { // RemoveTeamMember removes member from given team of given organization. func RemoveTeamMember(orgId, teamId, uid int64) error { sess := x.NewSession() - defer sess.Close() + defer sessionRelease(sess) if err := sess.Begin(); err != nil { return err } if err := removeTeamMember(sess, orgId, teamId, uid); err != nil { - sess.Rollback() return err } return sess.Commit() } + +// ___________ __________ +// \__ ___/___ _____ _____\______ \ ____ ______ ____ +// | |_/ __ \\__ \ / \| _// __ \\____ \ / _ \ +// | |\ ___/ / __ \| Y Y \ | \ ___/| |_> > <_> ) +// |____| \___ >____ /__|_| /____|_ /\___ > __/ \____/ +// \/ \/ \/ \/ \/|__| + +// TeamRepo represents an team-repository relation. +type TeamRepo struct { + ID int64 `xorm:"pk autoincr"` + OrgID int64 `xorm:"INDEX"` + TeamID int64 `xorm:"UNIQUE(s)"` + RepoID int64 `xorm:"UNIQUE(s)"` +} + +func hasTeamRepo(e Engine, orgID, teamID, repoID int64) bool { + has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("repo_id=?", repoID).Get(new(TeamRepo)) + return has +} + +// HasTeamRepo returns true if given repository belongs to team. +func HasTeamRepo(orgID, teamID, repoID int64) bool { + return hasTeamRepo(x, orgID, teamID, repoID) +} + +func addTeamRepo(e Engine, orgID, teamID, repoID int64) error { + _, err := e.InsertOne(&TeamRepo{ + OrgID: orgID, + TeamID: teamID, + RepoID: repoID, + }) + return err +} + +// AddTeamRepo adds new repository relation to team. +func AddTeamRepo(orgID, teamID, repoID int64) error { + return addTeamRepo(x, orgID, teamID, repoID) +} + +func removeTeamRepo(e Engine, teamID, repoID int64) error { + _, err := e.Delete(&TeamRepo{ + TeamID: teamID, + RepoID: repoID, + }) + return err +} + +// RemoveTeamRepo deletes repository relation to team. +func RemoveTeamRepo(teamID, repoID int64) error { + return removeTeamRepo(x, teamID, repoID) +} diff --git a/models/repo.go b/models/repo.go index bfa5925c35..7f87bbb2a7 100644 --- a/models/repo.go +++ b/models/repo.go @@ -514,7 +514,7 @@ func initRepository(e Engine, f string, u *User, repo *Repository, initReadme bo } // CreateRepository creates a repository for given user or organization. -func CreateRepository(u *User, name, desc, lang, license string, private, mirror, initReadme bool) (*Repository, error) { +func CreateRepository(u *User, name, desc, lang, license string, isPrivate, isMirror, initReadme bool) (*Repository, error) { if !IsLegalName(name) { return nil, ErrRepoNameIllegal } @@ -532,7 +532,7 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror Name: name, LowerName: strings.ToLower(name), Description: desc, - IsPrivate: private, + IsPrivate: isPrivate, } sess := x.NewSession() @@ -558,34 +558,22 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror // Update owner team info and count. t, err := u.getOwnerTeam(sess) if err != nil { - return nil, fmt.Errorf("get owner team: %v", err) - } else if err = t.getMembers(sess); err != nil { - return nil, fmt.Errorf("get team members: %v", err) - } - - for _, u := range t.Members { - if err = watchRepo(sess, u.Id, repo.Id, true); err != nil { - return nil, fmt.Errorf("watch repository: %v", err) - } - } - - t.RepoIds += "$" + com.ToStr(repo.Id) + "|" - t.NumRepos++ - if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil { - return nil, err + return nil, fmt.Errorf("getOwnerTeam: %v", err) + } else if err = t.addRepository(sess, repo); err != nil { + return nil, fmt.Errorf("addRepository: %v", err) } } else { if err = watchRepo(sess, u.Id, repo.Id, true); err != nil { - return nil, fmt.Errorf("watch repository 2: %v", err) + return nil, fmt.Errorf("watchRepo: %v", err) } } if err = newRepoAction(sess, u, repo); err != nil { - return nil, fmt.Errorf("new repository action: %v", err) + return nil, fmt.Errorf("newRepoAction: %v", err) } // No need for init mirror. - if !mirror { + if !isMirror { repoPath := RepoPath(u.Name, repo.Name) if err = initRepository(sess, repoPath, u, repo, initReadme, lang, license); err != nil { if err2 := os.RemoveAll(repoPath); err2 != nil { @@ -640,14 +628,14 @@ func RepoPath(userName, repoName string) string { } // TransferOwnership transfers all corresponding setting from old user to new one. -func TransferOwnership(u *User, newOwner string, repo *Repository) error { - newUser, err := GetUserByName(newOwner) +func TransferOwnership(u *User, newOwnerName string, repo *Repository) error { + newOwner, err := GetUserByName(newOwnerName) if err != nil { - return fmt.Errorf("fail to get new owner(%s): %v", newOwner, err) + return fmt.Errorf("get new owner(%s): %v", newOwnerName, err) } // Check if new owner has repository with same name. - has, err := IsRepositoryExist(newUser, repo.Name) + has, err := IsRepositoryExist(newOwner, repo.Name) if err != nil { return err } else if has { @@ -663,8 +651,8 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error { owner := repo.Owner // Update repository. - repo.OwnerId = newUser.Id - repo.Owner = newUser + repo.OwnerId = newOwner.Id + repo.Owner = newOwner if _, err := sess.Id(repo.Id).Update(repo); err != nil { return err } @@ -675,28 +663,38 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error { return err } for _, c := range collaborators { - if c.Id == newUser.Id || newUser.IsOrgMember(c.Id) { + if c.Id == newOwner.Id || newOwner.IsOrgMember(c.Id) { if _, err = sess.Delete(&Collaboration{RepoID: repo.Id, UserID: c.Id}); err != nil { return err } } } + if newOwner.IsOrganization() { + // Update owner team info and count. + t, err := newOwner.GetOwnerTeam() + if err != nil { + return err + } else if err = t.addRepository(sess, repo); err != nil { + return err + } + } + // Update user repository number. - if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", newUser.Id); err != nil { + if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", newOwner.Id); err != nil { return err } else if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?", owner.Id); err != nil { return err } else if err = repo.recalculateAccesses(sess); err != nil { return err - } else if err = watchRepo(sess, newUser.Id, repo.Id, true); err != nil { + } else if err = watchRepo(sess, newOwner.Id, repo.Id, true); err != nil { return err - } else if err = transferRepoAction(sess, u, newUser, repo); err != nil { + } else if err = transferRepoAction(sess, u, owner, newOwner, repo); err != nil { return err } // Change repository directory name. - if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newUser.Name, repo.Name)); err != nil { + if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil { return err } @@ -734,8 +732,8 @@ func UpdateRepository(repo *Repository) error { } // DeleteRepository deletes a repository for a user or organization. -func DeleteRepository(uid, repoId int64, userName string) error { - repo := &Repository{Id: repoId, OwnerId: uid} +func DeleteRepository(uid, repoID int64, userName string) error { + repo := &Repository{Id: repoID, OwnerId: uid} has, err := x.Get(repo) if err != nil { return err @@ -761,70 +759,66 @@ func DeleteRepository(uid, repoId int64, userName string) error { } if org.IsOrganization() { - idStr := "$" + com.ToStr(repoId) + "|" for _, t := range org.Teams { - if !strings.Contains(t.RepoIds, idStr) { + if !t.hasRepository(sess, repoID) { continue - } - t.NumRepos-- - t.RepoIds = strings.Replace(t.RepoIds, idStr, "", 1) - if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil { + } else if err = t.removeRepository(sess, repo); err != nil { return err } } } - if _, err = sess.Delete(&Repository{Id: repoId}); err != nil { + if _, err = sess.Delete(&Repository{Id: repoID}); err != nil { return err } else if _, err := sess.Delete(&Access{RepoID: repo.Id}); err != nil { return err } else if _, err := sess.Delete(&Action{RepoId: repo.Id}); err != nil { return err - } else if _, err = sess.Delete(&Watch{RepoId: repoId}); err != nil { + } else if _, err = sess.Delete(&Watch{RepoId: repoID}); err != nil { return err - } else if _, err = sess.Delete(&Mirror{RepoId: repoId}); err != nil { + } else if _, err = sess.Delete(&Mirror{RepoId: repoID}); err != nil { return err - } else if _, err = sess.Delete(&IssueUser{RepoId: repoId}); err != nil { + } else if _, err = sess.Delete(&IssueUser{RepoId: repoID}); err != nil { return err - } else if _, err = sess.Delete(&Milestone{RepoId: repoId}); err != nil { + } else if _, err = sess.Delete(&Milestone{RepoId: repoID}); err != nil { return err - } else if _, err = sess.Delete(&Release{RepoId: repoId}); err != nil { + } else if _, err = sess.Delete(&Release{RepoId: repoID}); err != nil { return err - } else if _, err = sess.Delete(&Collaboration{RepoID: repoId}); err != nil { + } else if _, err = sess.Delete(&Collaboration{RepoID: repoID}); err != nil { return err } // Delete comments. - if err = x.Iterate(&Issue{RepoId: repoId}, func(idx int, bean interface{}) error { - issue := bean.(*Issue) - if _, err = sess.Delete(&Comment{IssueId: issue.Id}); err != nil { + issues := make([]*Issue, 0, 25) + if err = sess.Where("repo_id=?", repoID).Find(&issues); err != nil { + return err + } + for i := range issues { + if _, err = sess.Delete(&Comment{IssueId: issues[i].Id}); err != nil { return err } - return nil - }); err != nil { - return err } - if _, err = sess.Delete(&Issue{RepoId: repoId}); err != nil { + if _, err = sess.Delete(&Issue{RepoId: repoID}); err != nil { return err } if repo.IsFork { - if _, err = sess.Exec("UPDATE `repository` SET num_forks = num_forks - 1 WHERE id = ?", repo.ForkId); err != nil { + if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?", repo.ForkId); err != nil { return err } } - if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?", uid); err != nil { + if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos-1 WHERE id=?", uid); err != nil { return err } // Remove repository files. if err = os.RemoveAll(RepoPath(userName, repo.Name)); err != nil { - desc := fmt.Sprintf("Fail to delete repository files(%s/%s): %v", userName, repo.Name, err) + desc := fmt.Sprintf("delete repository files(%s/%s): %v", userName, repo.Name, err) log.Warn(desc) if err = CreateRepositoryNotice(desc); err != nil { - log.Error(4, "Fail to add notice: %v", err) + log.Error(4, "add notice: %v", err) } } @@ -1328,33 +1322,21 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (*Repositor // Update owner team info and count. t, err := u.getOwnerTeam(sess) if err != nil { - return nil, fmt.Errorf("get owner team: %v", err) - } else if err = t.getMembers(sess); err != nil { - return nil, fmt.Errorf("get team members: %v", err) - } - - for _, u := range t.Members { - if err = watchRepo(sess, u.Id, repo.Id, true); err != nil { - return nil, fmt.Errorf("watch repository: %v", err) - } - } - - t.RepoIds += "$" + com.ToStr(repo.Id) + "|" - t.NumRepos++ - if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil { - return nil, err + return nil, fmt.Errorf("getOwnerTeam: %v", err) + } else if err = t.addRepository(sess, repo); err != nil { + return nil, fmt.Errorf("addRepository: %v", err) } } else { if err = watchRepo(sess, u.Id, repo.Id, true); err != nil { - return nil, fmt.Errorf("watch repository 2: %v", err) + return nil, fmt.Errorf("watchRepo: %v", err) } } if err = newRepoAction(sess, u, repo); err != nil { - return nil, fmt.Errorf("new repository action: %v", err) + return nil, fmt.Errorf("newRepoAction: %v", err) } - if _, err = sess.Exec("UPDATE `repository` SET num_forks = num_forks + 1 WHERE id = ?", oldRepo.Id); err != nil { + if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?", oldRepo.Id); err != nil { return nil, err } diff --git a/models/user.go b/models/user.go index 387ab6555b..ea5041bdff 100644 --- a/models/user.go +++ b/models/user.go @@ -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 } @@ -860,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...) diff --git a/routers/org/teams.go b/routers/org/teams.go index 2fbb1480d1..69f2734c64 100644 --- a/routers/org/teams.go +++ b/routers/org/teams.go @@ -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, diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 0f8acfa5ba..e2b8968375 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -8,7 +8,6 @@ import ( "encoding/json" "errors" "fmt" - "path" "strings" "time" diff --git a/templates/.VERSION b/templates/.VERSION index 056f3141af..4758bb670f 100644 --- a/templates/.VERSION +++ b/templates/.VERSION @@ -1 +1 @@ -0.5.14.0222 Beta +0.5.15.0223 Beta \ No newline at end of file -- cgit v1.2.3 From 8896c82d34ec5d86af1eeafb8c55a06f81bd3af1 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Tue, 24 Feb 2015 00:27:22 -0500 Subject: models: fix issue with transfer repository README: fix typo --- README.md | 6 ++-- README_ZH.md | 6 ++-- gogs.go | 2 +- models/access.go | 10 ++++-- models/org.go | 28 ++++++++++----- models/repo.go | 90 ++++++++++++++++++++++++------------------------- routers/repo/setting.go | 8 ++--- templates/.VERSION | 2 +- 8 files changed, 80 insertions(+), 72 deletions(-) (limited to 'routers') diff --git a/README.md b/README.md index 150aa64de8..ff4edd0935 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,13 @@ Gogs (Go Git Service) is a painless self-hosted Git service written in Go. ![Demo](http://gogs.qiniudn.com/gogs_demo.gif) -##### Current version: 0.5.13 Beta +##### Current version: 0.5.15 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 diff --git a/README_ZH.md b/README_ZH.md index b6d74e7b14..ffbbf0dd71 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -5,7 +5,7 @@ Gogs(Go Git Service) 是一个基于 Go 语言的自助 Git 服务。 ![Demo](http://gogs.qiniudn.com/gogs_demo.gif) -##### 当前版本:0.5.13 Beta +##### 当前版本:0.5.15 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) 文件中。 \ No newline at end of file +本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/gogits/gogs/blob/master/LICENSE) 文件中。 diff --git a/gogs.go b/gogs.go index 58cbb1a05c..930203afa3 100644 --- a/gogs.go +++ b/gogs.go @@ -17,7 +17,7 @@ import ( "github.com/gogits/gogs/modules/setting" ) -const APP_VER = "0.5.15.0223 Beta" +const APP_VER = "0.5.15.0224 Beta" func init() { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/models/access.go b/models/access.go index 29d1a730b7..683282f336 100644 --- a/models/access.go +++ b/models/access.go @@ -4,6 +4,10 @@ package models +import ( + "fmt" +) + type AccessMode int const ( @@ -128,7 +132,7 @@ func (repo *Repository) recalculateAccesses(e Engine) error { } if err = team.getMembers(e); err != nil { - return err + return fmt.Errorf("getMembers '%d': %v", team.ID, err) } for _, u := range team.Members { accessMap[u.Id] = maxAccessMode(accessMap[u.Id], team.Authorize) @@ -157,9 +161,9 @@ func (repo *Repository) recalculateAccesses(e Engine) error { // Delete old accesses and insert new ones for repository. if _, err = e.Delete(&Access{RepoID: repo.Id}); err != nil { - return err + return fmt.Errorf("delete old accesses: %v", err) } else if _, err = e.Insert(newAccesses); err != nil { - return err + return fmt.Errorf("insert new accesses: %v", err) } return nil diff --git a/models/org.go b/models/org.go index 629e9345c3..b47fbced5f 100644 --- a/models/org.go +++ b/models/org.go @@ -28,7 +28,7 @@ func (org *User) IsOwnedBy(uid int64) bool { // IsOrgMember returns true if given user is member of organization. func (org *User) IsOrgMember(uid int64) bool { - return IsOrganizationMember(org.Id, uid) + return org.IsOrganization() && IsOrganizationMember(org.Id, uid) } func (org *User) getTeam(e Engine, name string) (*Team, error) { @@ -493,19 +493,19 @@ func (t *Team) addRepository(e Engine, repo *Repository) (err error) { t.NumRepos++ if _, err = e.Id(t.ID).AllCols().Update(t); err != nil { - return err + return fmt.Errorf("update team: %v", err) } if err = repo.recalculateAccesses(e); err != nil { - return err + return fmt.Errorf("recalculateAccesses: %v", err) } if err = t.getMembers(e); err != nil { - return fmt.Errorf("get team members: %v", err) + return fmt.Errorf("getMembers: %v", err) } for _, u := range t.Members { if err = watchRepo(e, u.Id, repo.Id, true); err != nil { - return err + return fmt.Errorf("watchRepo: %v", err) } } return nil @@ -772,10 +772,20 @@ func IsTeamMember(orgID, teamID, uid int64) bool { return isTeamMember(x, orgID, teamID, uid) } -func getTeamMembers(e Engine, teamID int64) ([]*User, error) { - us := make([]*User, 0, 10) - err := e.Sql("SELECT * FROM `user` JOIN `team_user` ON `team_user`.`team_id` = ? AND `team_user`.`uid` = `user`.`id`", teamID).Find(&us) - return us, err +func getTeamMembers(e Engine, teamID int64) (_ []*User, err error) { + teamUsers := make([]*TeamUser, 0, 10) + if err = e.Where("team_id=?", teamID).Find(&teamUsers); err != nil { + return nil, fmt.Errorf("get team-users: %v", err) + } + members := make([]*User, 0, len(teamUsers)) + for i := range teamUsers { + member := new(User) + if _, err = e.Id(teamUsers[i].Uid).Get(member); err != nil { + return nil, fmt.Errorf("get user '%d': %v", teamUsers[i].Uid, err) + } + members = append(members, member) + } + return members, nil } // GetTeamMembers returns all members in given team of organization. diff --git a/models/repo.go b/models/repo.go index 7f87bbb2a7..0f75029096 100644 --- a/models/repo.go +++ b/models/repo.go @@ -223,16 +223,12 @@ func (repo *Repository) DescriptionHtml() template.HTML { } // IsRepositoryExist returns true if the repository with given name under user has already existed. -func IsRepositoryExist(u *User, repoName string) (bool, error) { - repo := Repository{OwnerId: u.Id} - has, err := x.Where("lower_name = ?", strings.ToLower(repoName)).Get(&repo) - if err != nil { - return has, err - } else if !has { - return false, nil - } - - return com.IsDir(RepoPath(u.Name, repoName)), nil +func IsRepositoryExist(u *User, repoName string) bool { + has, _ := x.Get(&Repository{ + OwnerId: u.Id, + LowerName: strings.ToLower(repoName), + }) + return has && com.IsDir(RepoPath(u.Name, repoName)) } // CloneLink represents different types of clone URLs of repository. @@ -514,15 +510,12 @@ func initRepository(e Engine, f string, u *User, repo *Repository, initReadme bo } // CreateRepository creates a repository for given user or organization. -func CreateRepository(u *User, name, desc, lang, license string, isPrivate, isMirror, initReadme bool) (*Repository, error) { +func CreateRepository(u *User, name, desc, lang, license string, isPrivate, isMirror, initReadme bool) (_ *Repository, err error) { if !IsLegalName(name) { return nil, ErrRepoNameIllegal } - isExist, err := IsRepositoryExist(u, name) - if err != nil { - return nil, err - } else if isExist { + if IsRepositoryExist(u, name) { return nil, ErrRepoAlreadyExist } @@ -631,71 +624,79 @@ func RepoPath(userName, repoName string) string { func TransferOwnership(u *User, newOwnerName string, repo *Repository) error { newOwner, err := GetUserByName(newOwnerName) if err != nil { - return fmt.Errorf("get new owner(%s): %v", newOwnerName, err) + return fmt.Errorf("get new owner '%s': %v", newOwnerName, err) } // Check if new owner has repository with same name. - has, err := IsRepositoryExist(newOwner, repo.Name) - if err != nil { - return err - } else if has { + if IsRepositoryExist(newOwner, repo.Name) { return ErrRepoAlreadyExist } sess := x.NewSession() defer sessionRelease(sess) if err = sess.Begin(); err != nil { - return err + return fmt.Errorf("sess.Begin: %v", err) } owner := repo.Owner - // Update repository. + // Note: we have to set value here to make sure recalculate accesses is based on + // new owner. repo.OwnerId = newOwner.Id repo.Owner = newOwner + + // Update repository. if _, err := sess.Id(repo.Id).Update(repo); err != nil { - return err + return fmt.Errorf("update owner: %v", err) } - // Remove redundant collaborators + // Remove redundant collaborators. collaborators, err := repo.GetCollaborators() if err != nil { - return err + return fmt.Errorf("GetCollaborators: %v", err) } + + // Dummy object. + collaboration := &Collaboration{RepoID: repo.Id} for _, c := range collaborators { + collaboration.UserID = c.Id if c.Id == newOwner.Id || newOwner.IsOrgMember(c.Id) { - if _, err = sess.Delete(&Collaboration{RepoID: repo.Id, UserID: c.Id}); err != nil { - return err + if _, err = sess.Delete(collaboration); err != nil { + return fmt.Errorf("remove collaborator '%d': %v", c.Id, err) } } } if newOwner.IsOrganization() { - // Update owner team info and count. t, err := newOwner.GetOwnerTeam() if err != nil { - return err + return fmt.Errorf("GetOwnerTeam: %v", err) } else if err = t.addRepository(sess, repo); err != nil { - return err + return fmt.Errorf("add to owner team: %v", err) + } + } else { + // Organization called this in addRepository method. + if err = repo.recalculateAccesses(sess); err != nil { + return fmt.Errorf("recalculateAccesses: %v", err) } } - // Update user repository number. - if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", newOwner.Id); err != nil { - return err - } else if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?", owner.Id); err != nil { - return err - } else if err = repo.recalculateAccesses(sess); err != nil { - return err - } else if err = watchRepo(sess, newOwner.Id, repo.Id, true); err != nil { - return err + // Update repository count. + if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos+1 WHERE id=?", newOwner.Id); err != nil { + return fmt.Errorf("increase new owner repository count: %v", err) + } else if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos-1 WHERE id=?", owner.Id); err != nil { + return fmt.Errorf("decrease old owner repository count: %v", err) + } + + if err = watchRepo(sess, newOwner.Id, repo.Id, true); err != nil { + return fmt.Errorf("watchRepo: %v", err) } else if err = transferRepoAction(sess, u, owner, newOwner, repo); err != nil { - return err + return fmt.Errorf("transferRepoAction: %v", err) } // Change repository directory name. if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil { - return err + return fmt.Errorf("rename directory: %v", err) } return sess.Commit() @@ -1275,11 +1276,8 @@ func IsStaring(uid, repoId int64) bool { // \___ / \____/|__| |__|_ \ // \/ \/ -func ForkRepository(u *User, oldRepo *Repository, name, desc string) (*Repository, error) { - isExist, err := IsRepositoryExist(u, name) - if err != nil { - return nil, err - } else if isExist { +func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Repository, err error) { + if IsRepositoryExist(u, name) { return nil, ErrRepoAlreadyExist } diff --git a/routers/repo/setting.go b/routers/repo/setting.go index e2b8968375..5cd39ada2c 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -53,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) diff --git a/templates/.VERSION b/templates/.VERSION index 4758bb670f..64dc6626d5 100644 --- a/templates/.VERSION +++ b/templates/.VERSION @@ -1 +1 @@ -0.5.15.0223 Beta \ No newline at end of file +0.5.15.0224 Beta \ No newline at end of file -- cgit v1.2.3 From e3dece1371f38f5af72c200bc4c4684ec2fc396b Mon Sep 17 00:00:00 2001 From: Unknwon Date: Thu, 26 Feb 2015 19:45:38 -0500 Subject: modules/setting: simple behave change in startup --- .bra.toml | 2 +- modules/base/template.go | 2 +- modules/setting/setting.go | 14 ++++---------- routers/install.go | 1 + 4 files changed, 7 insertions(+), 12 deletions(-) (limited to 'routers') diff --git a/.bra.toml b/.bra.toml index 65b3988445..0f52fcdde5 100644 --- a/.bra.toml +++ b/.bra.toml @@ -1,6 +1,6 @@ [run] init_cmds = [ - ["grep", "-rn", "FIXME", "."], + #["grep", "-rn", "FIXME", "."], ["./gogs", "web"] ] watch_all = true diff --git a/modules/base/template.go b/modules/base/template.go index cfcabb71a2..196b935107 100644 --- a/modules/base/template.go +++ b/modules/base/template.go @@ -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, diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 6a36105691..6db43b16b3 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -272,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 { @@ -298,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") @@ -309,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/" @@ -374,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) diff --git a/routers/install.go b/routers/install.go index a7828e351c..4aa3ca9306 100644 --- a/routers/install.go +++ b/routers/install.go @@ -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)) -- cgit v1.2.3