Bladeren bron

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.
Peter Smit 10 jaren geleden
bovenliggende
commit
4e79adf6b5
12 gewijzigde bestanden met toevoegingen van 235 en 681 verwijderingen
  1. 20 21
      cmd/serve.go
  2. 113 63
      models/access.go
  3. 6 0
      models/migrations/migrations.go
  4. 26 216
      models/org.go
  5. 13 190
      models/repo.go
  6. 2 54
      models/user.go
  7. 1 1
      modules/middleware/org.go
  8. 37 118
      modules/middleware/repo.go
  9. 1 1
      routers/api/v1/repo.go
  10. 8 8
      routers/org/teams.go
  11. 5 5
      routers/repo/http.go
  12. 3 4
      routers/user/home.go

+ 20 - 21
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)

+ 113 - 63
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)"` // <user name>/<repo name>
-	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 <username>/<reponame>.
-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
+
+}

+ 6 - 0
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
+}

+ 26 - 216
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.

+ 13 - 190
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 {

+ 2 - 54
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

+ 1 - 1
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 {

+ 37 - 118
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 {

+ 1 - 1
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) {

+ 8 - 8
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

+ 5 - 5
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

+ 3 - 4
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
 				}
 			}