"Fossies" - the Fresh Open Source Software Archive

Member "gogs-0.12.3/internal/db/access.go" (7 Oct 2020, 6678 Bytes) of package /linux/misc/gogs-0.12.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // Copyright 2014 The Gogs Authors. All rights reserved.
    2 // Use of this source code is governed by a MIT-style
    3 // license that can be found in the LICENSE file.
    4 
    5 package db
    6 
    7 import (
    8     "fmt"
    9 
   10     log "unknwon.dev/clog/v2"
   11 )
   12 
   13 type AccessMode int
   14 
   15 const (
   16     AccessModeNone  AccessMode = iota // 0
   17     AccessModeRead                    // 1
   18     AccessModeWrite                   // 2
   19     AccessModeAdmin                   // 3
   20     AccessModeOwner                   // 4
   21 )
   22 
   23 func (mode AccessMode) String() string {
   24     switch mode {
   25     case AccessModeRead:
   26         return "read"
   27     case AccessModeWrite:
   28         return "write"
   29     case AccessModeAdmin:
   30         return "admin"
   31     case AccessModeOwner:
   32         return "owner"
   33     default:
   34         return "none"
   35     }
   36 }
   37 
   38 // ParseAccessMode returns corresponding access mode to given permission string.
   39 func ParseAccessMode(permission string) AccessMode {
   40     switch permission {
   41     case "write":
   42         return AccessModeWrite
   43     case "admin":
   44         return AccessModeAdmin
   45     default:
   46         return AccessModeRead
   47     }
   48 }
   49 
   50 // Access represents the highest access level of a user to a repository. The only access type
   51 // that is not in this table is the real owner of a repository. In case of an organization
   52 // repository, the members of the owners team are in this table.
   53 type Access struct {
   54     ID     int64
   55     UserID int64 `xorm:"UNIQUE(s)"`
   56     RepoID int64 `xorm:"UNIQUE(s)"`
   57     Mode   AccessMode
   58 }
   59 
   60 func userAccessMode(e Engine, userID int64, repo *Repository) (AccessMode, error) {
   61     mode := AccessModeNone
   62     // Everyone has read access to public repository
   63     if !repo.IsPrivate {
   64         mode = AccessModeRead
   65     }
   66 
   67     if userID <= 0 {
   68         return mode, nil
   69     }
   70 
   71     if userID == repo.OwnerID {
   72         return AccessModeOwner, nil
   73     }
   74 
   75     access := &Access{
   76         UserID: userID,
   77         RepoID: repo.ID,
   78     }
   79     if has, err := e.Get(access); !has || err != nil {
   80         return mode, err
   81     }
   82     return access.Mode, nil
   83 }
   84 
   85 // UserAccessMode returns the access mode of given user to the repository.
   86 func UserAccessMode(userID int64, repo *Repository) (AccessMode, error) {
   87     return userAccessMode(x, userID, repo)
   88 }
   89 
   90 func hasAccess(e Engine, userID int64, repo *Repository, testMode AccessMode) (bool, error) {
   91     mode, err := userAccessMode(e, userID, repo)
   92     return mode >= testMode, err
   93 }
   94 
   95 // HasAccess returns true if someone has the request access level. User can be nil!
   96 // Deprecated: Use Perms.Authorize instead.
   97 func HasAccess(userID int64, repo *Repository, testMode AccessMode) (bool, error) {
   98     return hasAccess(x, userID, repo, testMode)
   99 }
  100 
  101 // GetRepositoryAccesses finds all repositories with their access mode where a user has access but does not own.
  102 func (u *User) GetRepositoryAccesses() (map[*Repository]AccessMode, error) {
  103     accesses := make([]*Access, 0, 10)
  104     if err := x.Find(&accesses, &Access{UserID: u.ID}); err != nil {
  105         return nil, err
  106     }
  107 
  108     repos := make(map[*Repository]AccessMode, len(accesses))
  109     for _, access := range accesses {
  110         repo, err := GetRepositoryByID(access.RepoID)
  111         if err != nil {
  112             if IsErrRepoNotExist(err) {
  113                 log.Error("Failed to get repository by ID: %v", err)
  114                 continue
  115             }
  116             return nil, err
  117         }
  118         if repo.OwnerID == u.ID {
  119             continue
  120         }
  121         repos[repo] = access.Mode
  122     }
  123     return repos, nil
  124 }
  125 
  126 // GetAccessibleRepositories finds repositories which the user has access but does not own.
  127 // If limit is smaller than 1 means returns all found results.
  128 func (user *User) GetAccessibleRepositories(limit int) (repos []*Repository, _ error) {
  129     sess := x.Where("owner_id !=? ", user.ID).Desc("updated_unix")
  130     if limit > 0 {
  131         sess.Limit(limit)
  132         repos = make([]*Repository, 0, limit)
  133     } else {
  134         repos = make([]*Repository, 0, 10)
  135     }
  136     return repos, sess.Join("INNER", "access", "access.user_id = ? AND access.repo_id = repository.id", user.ID).Find(&repos)
  137 }
  138 
  139 func maxAccessMode(modes ...AccessMode) AccessMode {
  140     max := AccessModeNone
  141     for _, mode := range modes {
  142         if mode > max {
  143             max = mode
  144         }
  145     }
  146     return max
  147 }
  148 
  149 // Deprecated: Use Perms.SetRepoPerms instead.
  150 func (repo *Repository) refreshAccesses(e Engine, accessMap map[int64]AccessMode) (err error) {
  151     newAccesses := make([]Access, 0, len(accessMap))
  152     for userID, mode := range accessMap {
  153         newAccesses = append(newAccesses, Access{
  154             UserID: userID,
  155             RepoID: repo.ID,
  156             Mode:   mode,
  157         })
  158     }
  159 
  160     // Delete old accesses and insert new ones for repository.
  161     if _, err = e.Delete(&Access{RepoID: repo.ID}); err != nil {
  162         return fmt.Errorf("delete old accesses: %v", err)
  163     } else if _, err = e.Insert(newAccesses); err != nil {
  164         return fmt.Errorf("insert new accesses: %v", err)
  165     }
  166     return nil
  167 }
  168 
  169 // refreshCollaboratorAccesses retrieves repository collaborations with their access modes.
  170 func (repo *Repository) refreshCollaboratorAccesses(e Engine, accessMap map[int64]AccessMode) error {
  171     collaborations, err := repo.getCollaborations(e)
  172     if err != nil {
  173         return fmt.Errorf("getCollaborations: %v", err)
  174     }
  175     for _, c := range collaborations {
  176         accessMap[c.UserID] = c.Mode
  177     }
  178     return nil
  179 }
  180 
  181 // recalculateTeamAccesses recalculates new accesses for teams of an organization
  182 // except the team whose ID is given. It is used to assign a team ID when
  183 // remove repository from that team.
  184 func (repo *Repository) recalculateTeamAccesses(e Engine, ignTeamID int64) (err error) {
  185     accessMap := make(map[int64]AccessMode, 20)
  186 
  187     if err = repo.getOwner(e); err != nil {
  188         return err
  189     } else if !repo.Owner.IsOrganization() {
  190         return fmt.Errorf("owner is not an organization: %d", repo.OwnerID)
  191     }
  192 
  193     if err = repo.refreshCollaboratorAccesses(e, accessMap); err != nil {
  194         return fmt.Errorf("refreshCollaboratorAccesses: %v", err)
  195     }
  196 
  197     if err = repo.Owner.getTeams(e); err != nil {
  198         return err
  199     }
  200 
  201     for _, t := range repo.Owner.Teams {
  202         if t.ID == ignTeamID {
  203             continue
  204         }
  205 
  206         // Owner team gets owner access, and skip for teams that do not
  207         // have relations with repository.
  208         if t.IsOwnerTeam() {
  209             t.Authorize = AccessModeOwner
  210         } else if !t.hasRepository(e, repo.ID) {
  211             continue
  212         }
  213 
  214         if err = t.getMembers(e); err != nil {
  215             return fmt.Errorf("getMembers '%d': %v", t.ID, err)
  216         }
  217         for _, m := range t.Members {
  218             accessMap[m.ID] = maxAccessMode(accessMap[m.ID], t.Authorize)
  219         }
  220     }
  221 
  222     return repo.refreshAccesses(e, accessMap)
  223 }
  224 
  225 func (repo *Repository) recalculateAccesses(e Engine) error {
  226     if repo.Owner.IsOrganization() {
  227         return repo.recalculateTeamAccesses(e, 0)
  228     }
  229 
  230     accessMap := make(map[int64]AccessMode, 10)
  231     if err := repo.refreshCollaboratorAccesses(e, accessMap); err != nil {
  232         return fmt.Errorf("refreshCollaboratorAccesses: %v", err)
  233     }
  234     return repo.refreshAccesses(e, accessMap)
  235 }
  236 
  237 // RecalculateAccesses recalculates all accesses for repository.
  238 func (repo *Repository) RecalculateAccesses() error {
  239     return repo.recalculateAccesses(x)
  240 }