diff options
Diffstat (limited to 'models/access.go')
-rw-r--r-- | models/access.go | 188 |
1 files changed, 135 insertions, 53 deletions
diff --git a/models/access.go b/models/access.go index 81aa43dc..1e1c0114 100644 --- a/models/access.go +++ b/models/access.go @@ -4,77 +4,159 @@ package models -import ( - "strings" - "time" - - "github.com/go-xorm/xorm" -) - -type AccessType int +type AccessMode int const ( - READABLE AccessType = iota + 1 - WRITABLE + ACCESS_MODE_NONE AccessMode = iota + ACCESS_MODE_READ + ACCESS_MODE_WRITE + ACCESS_MODE_ADMIN + ACCESS_MODE_OWNER ) -// Access represents the accessibility of user to repository. +// Access represents the highest access level of a user to the repository. The only access type +// that is not in this table is the real owner of a repository. In case of an organization +// repository, the members of the owners team are in this table. type Access struct { - Id int64 - UserName string `xorm:"UNIQUE(s)"` - RepoName string `xorm:"UNIQUE(s)"` // <user name>/<repo name> - Mode AccessType `xorm:"UNIQUE(s)"` - Created time.Time `xorm:"CREATED"` + ID int64 `xorm:"pk autoincr"` + UserID int64 `xorm:"UNIQUE(s)"` + RepoID int64 `xorm:"UNIQUE(s)"` + Mode AccessMode } -// 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 +// 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 := ACCESS_MODE_NONE + if !r.IsPrivate { + mode = ACCESS_MODE_READ + } + + if u != nil { + if u.Id == r.OwnerId { + return ACCESS_MODE_OWNER, 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 mode, nil } -// 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 +// 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 } -// DeleteAccess deletes access record. -func DeleteAccess(access *Access) error { - _, err := x.Delete(access) - return err +// GetAccessibleRepositories finds all repositories where a user has access to, +// besides his own. +func (u *User) GetAccessibleRepositories() (map[*Repository]AccessMode, error) { + accesses := make([]*Access, 0, 10) + if err := x.Find(&accesses, &Access{UserID: u.Id}); err != nil { + return nil, err + } + + repos := make(map[*Repository]AccessMode, len(accesses)) + for _, access := range accesses { + repo, err := GetRepositoryById(access.RepoID) + if err != nil { + return nil, err + } + if err = repo.GetOwner(); err != nil { + return nil, err + } else if repo.OwnerId == u.Id { + continue + } + repos[repo] = access.Mode + } + + return repos, nil } -// 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 +func maxAccessMode(modes ...AccessMode) AccessMode { + max := ACCESS_MODE_NONE + for _, mode := range modes { + if mode > max { + max = mode + } } + return max +} + +func (repo *Repository) recalculateTeamAccesses(e Engine, mode AccessMode) error { + 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 +func (repo *Repository) recalculateAccesses(e Engine) error { + accessMap := make(map[int64]AccessMode, 20) + + // Give all collaborators write access + collaborators, err := repo.getCollaborators(e) + if err != nil { + return err } - access := &Access{ - UserName: strings.ToLower(uname), - RepoName: strings.ToLower(repoName), + for _, c := range collaborators { + accessMap[c.Id] = ACCESS_MODE_WRITE } - 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 err := repo.getOwner(e); err != nil { + return err + } + if repo.Owner.IsOrganization() { + if err = repo.Owner.getTeams(e); err != nil { + return err + } + + for _, team := range repo.Owner.Teams { + if !(team.IsOwnerTeam() || team.HasRepository(repo)) { + continue + } else if team.IsOwnerTeam() { + team.Authorize = ACCESS_MODE_OWNER + } + + if err = team.getMembers(e); err != nil { + return err + } + for _, u := range team.Members { + accessMap[u.Id] = maxAccessMode(accessMap[u.Id], team.Authorize) + } + } + } + + minMode := ACCESS_MODE_READ + if !repo.IsPrivate { + minMode = ACCESS_MODE_WRITE + } + + newAccesses := make([]Access, 0, len(accessMap)) + for userID, mode := range accessMap { + if mode < minMode { + continue + } + newAccesses = append(newAccesses, Access{ + UserID: userID, + RepoID: repo.Id, + Mode: mode, + }) + } + + // Delete old accesses and insert new ones for repository. + if _, err = e.Delete(&Access{RepoID: repo.Id}); err != nil { + return err + } else if _, err = e.Insert(newAccesses); err != nil { + return err } - return true, nil + + return nil +} + +// RecalculateAccesses recalculates all accesses for repository. +func (r *Repository) RecalculateAccesses() error { + return r.recalculateAccesses(x) } |