]> source.dussan.org Git - gitea.git/commitdiff
Restrict permission check on repositories and fix some problems (#5314)
authorLunny Xiao <xiaolunwen@gmail.com>
Wed, 28 Nov 2018 11:26:14 +0000 (19:26 +0800)
committerGitHub <noreply@github.com>
Wed, 28 Nov 2018 11:26:14 +0000 (19:26 +0800)
* fix units permission problems

* fix some bugs and merge LoadUnits to repoAssignment

* refactor permission struct and add some copyright heads

* remove unused codes

* fix routes units check

* improve permission check

* add unit tests for permission

* fix typo

* fix tests

* fix some routes

* fix api permission check

* improve permission check

* fix some permission check

* fix tests

* fix tests

* improve some permission check

* fix some permission check

* refactor AccessLevel

* fix bug

* fix tests

* fix tests

* fix tests

* fix AccessLevel

* rename CanAccess

* fix tests

* fix comment

* fix bug

* add missing unit for test repos

* fix bug

* rename some functions

* fix routes check

80 files changed:
cmd/serv.go
integrations/api_repo_test.go
models/access.go
models/access_test.go
models/branches.go
models/fixtures/repo_unit.yml
models/fixtures/repository.yml
models/fixtures/team.yml
models/fixtures/team_repo.yml
models/fixtures/team_unit.yml
models/fixtures/team_user.yml
models/fixtures/user.yml
models/issue.go
models/issue_assignees.go
models/issue_comment.go
models/issue_milestone.go
models/lfs_lock.go
models/org_team.go
models/org_team_test.go
models/org_test.go
models/pull.go
models/release.go
models/repo.go
models/repo_permission.go [new file with mode: 0644]
models/repo_permission_test.go [new file with mode: 0644]
models/repo_unit.go
models/ssh_key.go
models/user.go
models/user_test.go
modules/context/permission.go [new file with mode: 0644]
modules/context/repo.go
modules/lfs/server.go
modules/private/internal.go
modules/test/context_tests.go
routers/api/v1/api.go
routers/api/v1/org/team.go
routers/api/v1/repo/collaborators.go
routers/api/v1/repo/file.go
routers/api/v1/repo/fork.go
routers/api/v1/repo/issue.go
routers/api/v1/repo/issue_label.go
routers/api/v1/repo/label.go
routers/api/v1/repo/pull.go
routers/api/v1/repo/release.go
routers/api/v1/repo/repo.go
routers/api/v1/user/repo.go
routers/api/v1/user/star.go
routers/api/v1/user/watch.go
routers/private/internal.go
routers/repo/activity.go
routers/repo/branch.go
routers/repo/http.go
routers/repo/issue.go
routers/repo/issue_watch.go
routers/repo/pull.go
routers/repo/release.go
routers/repo/setting_protected_branch.go
routers/repo/view.go
routers/repo/wiki.go
routers/routes/routes.go
routers/user/home.go
templates/repo/activity.tmpl
templates/repo/bare.tmpl
templates/repo/diff/comments.tmpl
templates/repo/header.tmpl
templates/repo/home.tmpl
templates/repo/issue/labels.tmpl
templates/repo/issue/milestone_new.tmpl
templates/repo/issue/milestones.tmpl
templates/repo/issue/view_content.tmpl
templates/repo/issue/view_content/comments.tmpl
templates/repo/issue/view_content/sidebar.tmpl
templates/repo/issue/view_title.tmpl
templates/repo/release/list.tmpl
templates/repo/settings/options.tmpl
templates/repo/settings/webhook/history.tmpl
templates/repo/sub_menu.tmpl
templates/repo/wiki/pages.tmpl
templates/repo/wiki/start.tmpl
templates/repo/wiki/view.tmpl

index ca042e2b2b70d1d9b3def44c9191016a8d9068d4..51b0b4984b39df3e89d64f2c82328ec9102f1571 100644 (file)
@@ -193,7 +193,7 @@ func runServ(c *cli.Context) error {
                keyID int64
                user  *models.User
        )
-       if requestedMode == models.AccessModeWrite || repo.IsPrivate {
+       if requestedMode == models.AccessModeWrite || repo.IsPrivate || setting.Service.RequireSignInView {
                keys := strings.Split(c.Args()[0], "-")
                if len(keys) != 2 {
                        fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
@@ -236,7 +236,7 @@ func runServ(c *cli.Context) error {
                                        user.Name, repoPath)
                        }
 
-                       mode, err := private.AccessLevel(user.ID, repo.ID)
+                       mode, err := private.CheckUnitUser(user.ID, repo.ID, user.IsAdmin, unitType)
                        if err != nil {
                                fail("Internal error", "Failed to check access: %v", err)
                        } else if *mode < requestedMode {
@@ -249,16 +249,6 @@ func runServ(c *cli.Context) error {
                                        user.Name, requestedMode, repoPath)
                        }
 
-                       check, err := private.CheckUnitUser(user.ID, repo.ID, user.IsAdmin, unitType)
-                       if err != nil {
-                               fail("You do not have allowed for this action", "Failed to access internal api: [user.Name: %s, repoPath: %s]", user.Name, repoPath)
-                       }
-                       if !check {
-                               fail("You do not have allowed for this action",
-                                       "User %s does not have allowed access to repository %s 's code",
-                                       user.Name, repoPath)
-                       }
-
                        os.Setenv(models.EnvPusherName, user.Name)
                        os.Setenv(models.EnvPusherID, fmt.Sprintf("%d", user.ID))
                }
index da748942f6435503ed7d50e5ef8b694b3f1417b4..237c4eea9abb137b7596f7041d5f7d45f336e2f1 100644 (file)
@@ -164,7 +164,7 @@ func TestAPISearchRepo(t *testing.T) {
                                        assert.Len(t, body.Data, expected.count)
                                        for _, repo := range body.Data {
                                                r := getRepo(t, repo.ID)
-                                               hasAccess, err := models.HasAccess(userID, r, models.AccessModeRead)
+                                               hasAccess, err := models.HasAccess(userID, r)
                                                assert.NoError(t, err)
                                                assert.True(t, hasAccess)
 
index 98ead19a0beb218bcd5b1b41a59a34978fb05d12..34d76953f57b2c0be5ffc3911625c2aef94e8d96 100644 (file)
@@ -80,22 +80,6 @@ func accessLevel(e Engine, userID int64, repo *Repository) (AccessMode, error) {
        return a.Mode, nil
 }
 
-// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
-// user does not have access.
-func AccessLevel(userID int64, repo *Repository) (AccessMode, error) {
-       return accessLevel(x, userID, repo)
-}
-
-func hasAccess(e Engine, userID int64, repo *Repository, testMode AccessMode) (bool, error) {
-       mode, err := accessLevel(e, userID, repo)
-       return testMode <= mode, err
-}
-
-// HasAccess returns true if user has access to repo
-func HasAccess(userID int64, repo *Repository, testMode AccessMode) (bool, error) {
-       return hasAccess(x, userID, repo, testMode)
-}
-
 type repoAccess struct {
        Access     `xorm:"extends"`
        Repository `xorm:"extends"`
index 46d6f723ea8cfa6e5c5ce5500fe6bb39ee454edc..d6a1c92b902026717b63922a75c6677a684420a8 100644 (file)
@@ -20,28 +20,28 @@ var accessModes = []AccessMode{
 func TestAccessLevel(t *testing.T) {
        assert.NoError(t, PrepareTestDatabase())
 
-       user1 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
-       user2 := AssertExistsAndLoadBean(t, &User{ID: 5}).(*User)
+       user2 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
+       user5 := AssertExistsAndLoadBean(t, &User{ID: 5}).(*User)
        // A public repository owned by User 2
        repo1 := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
        assert.False(t, repo1.IsPrivate)
        // A private repository owned by Org 3
-       repo2 := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository)
-       assert.True(t, repo2.IsPrivate)
+       repo3 := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository)
+       assert.True(t, repo3.IsPrivate)
 
-       level, err := AccessLevel(user1.ID, repo1)
+       level, err := AccessLevel(user2, repo1)
        assert.NoError(t, err)
        assert.Equal(t, AccessModeOwner, level)
 
-       level, err = AccessLevel(user1.ID, repo2)
+       level, err = AccessLevel(user2, repo3)
        assert.NoError(t, err)
-       assert.Equal(t, AccessModeWrite, level)
+       assert.Equal(t, AccessModeOwner, level)
 
-       level, err = AccessLevel(user2.ID, repo1)
+       level, err = AccessLevel(user5, repo1)
        assert.NoError(t, err)
        assert.Equal(t, AccessModeRead, level)
 
-       level, err = AccessLevel(user2.ID, repo2)
+       level, err = AccessLevel(user5, repo3)
        assert.NoError(t, err)
        assert.Equal(t, AccessModeNone, level)
 }
@@ -58,23 +58,18 @@ func TestHasAccess(t *testing.T) {
        repo2 := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository)
        assert.True(t, repo2.IsPrivate)
 
-       for _, accessMode := range accessModes {
-               has, err := HasAccess(user1.ID, repo1, accessMode)
-               assert.NoError(t, err)
-               assert.True(t, has)
+       has, err := HasAccess(user1.ID, repo1)
+       assert.NoError(t, err)
+       assert.True(t, has)
 
-               has, err = HasAccess(user1.ID, repo2, accessMode)
-               assert.NoError(t, err)
-               assert.Equal(t, accessMode <= AccessModeWrite, has)
+       has, err = HasAccess(user1.ID, repo2)
+       assert.NoError(t, err)
 
-               has, err = HasAccess(user2.ID, repo1, accessMode)
-               assert.NoError(t, err)
-               assert.Equal(t, accessMode <= AccessModeRead, has)
+       has, err = HasAccess(user2.ID, repo1)
+       assert.NoError(t, err)
 
-               has, err = HasAccess(user2.ID, repo2, accessMode)
-               assert.NoError(t, err)
-               assert.Equal(t, accessMode <= AccessModeNone, has)
-       }
+       has, err = HasAccess(user2.ID, repo2)
+       assert.NoError(t, err)
 }
 
 func TestUser_GetRepositoryAccesses(t *testing.T) {
index 3de76a5cc16c33e0cecc2bd4212978fc13d98ef0..bbcd342baae44e5817493010437889b94c871f67 100644 (file)
@@ -243,10 +243,16 @@ func updateUserWhitelist(repo *Repository, currentWhitelist, newWhitelist []int6
 
        whitelist = make([]int64, 0, len(newWhitelist))
        for _, userID := range newWhitelist {
-               has, err := hasAccess(x, userID, repo, AccessModeWrite)
+               user, err := GetUserByID(userID)
                if err != nil {
-                       return nil, fmt.Errorf("HasAccess [user_id: %d, repo_id: %d]: %v", userID, repo.ID, err)
-               } else if !has {
+                       return nil, fmt.Errorf("GetUserByID [user_id: %d, repo_id: %d]: %v", userID, repo.ID, err)
+               }
+               perm, err := GetUserRepoPermission(repo, user)
+               if err != nil {
+                       return nil, fmt.Errorf("GetUserRepoPermission [user_id: %d, repo_id: %d]: %v", userID, repo.ID, err)
+               }
+
+               if !perm.CanWrite(UnitTypeCode) {
                        continue // Drop invalid user ID
                }
 
index 581f9d6ed5600cee1d2d8df05ddaff188a8fa652..24d77b9997e09d4a934a6fa17860ab8035221b15 100644 (file)
   repo_id: 33
   type: 5
   config: "{}"
-  created_unix: 1535593231
\ No newline at end of file
+  created_unix: 1535593231
+
+-
+  id: 17
+  repo_id: 4
+  type: 4
+  config: "{}"
+  created_unix: 946684810
+
+-
+  id: 18
+  repo_id: 4
+  type: 5
+  config: "{}"
+  created_unix: 946684810
+
+-
+  id: 19
+  repo_id: 4
+  type: 1
+  config: "{}"
+  created_unix: 946684810
+
+-
+  id: 20
+  repo_id: 4
+  type: 2
+  config: "{\"EnableTimetracker\":true,\"AllowOnlyContributorsToTrackTime\":true}"
+  created_unix: 946684810
+
+-
+  id: 21
+  repo_id: 4
+  type: 3
+  config: "{\"IgnoreWhitespaceConflicts\":false,\"AllowMerge\":true,\"AllowRebase\":true,\"AllowSquash\":true}"
+  created_unix: 946684810
+
+-
+  id: 22
+  repo_id: 2
+  type: 4
+  config: "{}"
+  created_unix: 946684810
+
+-
+  id: 23
+  repo_id: 2
+  type: 5
+  config: "{}"
+  created_unix: 946684810
+
+-
+  id: 24
+  repo_id: 2
+  type: 1
+  config: "{}"
+  created_unix: 946684810
+
+-
+  id: 25
+  repo_id: 32
+  type: 1
+  config: "{}"
+  created_unix: 1524304355
+
+-
+  id: 26
+  repo_id: 32
+  type: 2
+  config: "{}"
+  created_unix: 1524304355
+
+-
+  id: 27
+  repo_id: 24
+  type: 1
+  config: "{}"
+  created_unix: 1524304355
+
+-
+  id: 28
+  repo_id: 24
+  type: 2
+  config: "{}"
+  created_unix: 1524304355
+
+-
+  id: 29
+  repo_id: 16
+  type: 1
+  config: "{}"
+  created_unix: 1524304355
+
+-
+  id: 30
+  repo_id: 23
+  type: 1
+  config: "{}"
+  created_unix: 1524304355
+
+-
+  id: 31
+  repo_id: 27
+  type: 1
+  config: "{}"
+  created_unix: 1524304355
+
+-
+  id: 32
+  repo_id: 28
+  type: 1
+  config: "{}"
+  created_unix: 1524304355
\ No newline at end of file
index c2987b9658678a98585dbc1fb28ef44597cc4a17..aa96656530aec3ae3ed7fd3c931ac58185006cd6 100644 (file)
   is_mirror: false
 
 -
-  id: 32
+  id: 32 # org public repo
   owner_id: 3
   lower_name: repo21
   name: repo21
index 4b4a1d798b4e67580f3f2fa00ed606b7daa4d6a1..2d0dd9cd56ca11dc1c79327d4af41f38d1723a7b 100644 (file)
   authorize: 4 # owner
   num_repos: 2
   num_members: 1
+
+-
+  id: 7
+  org_id: 3
+  lower_name: test_team
+  name: test_team
+  authorize: 2 # write
+  num_repos: 1
+  num_members: 1
+
+-
+  id: 8
+  org_id: 17
+  lower_name: test_team
+  name: test_team
+  authorize: 2 # write
+  num_repos: 1
+  num_members: 1
+
+-
+  id: 9
+  org_id: 17
+  lower_name: review_team
+  name: review_team
+  authorize: 1 # read
+  num_repos: 1
+  num_members: 1
\ No newline at end of file
index b324e094151f4614432ae9a0192b7a759b4cbcd7..a523a90b204d25068729cdef34390657d1a8cf18 100644 (file)
   org_id: 3
   team_id: 1
   repo_id: 32
+
+-
+  id: 9
+  org_id: 3
+  team_id: 7
+  repo_id: 32
+
+-
+  id: 10
+  org_id: 17
+  team_id: 8
+  repo_id: 24
+
+-
+  id: 11
+  org_id: 17
+  team_id: 9
+  repo_id: 24
\ No newline at end of file
index ad5466a5c13364408499b6b2967207103f0000a0..943745c000f9b0a086c98ed46ebfb3dc4bf72712 100644 (file)
   id: 42
   team_id: 6
   type: 7
+
+-
+  id: 43
+  team_id: 7
+  type: 2 # issues
+
+-
+  id: 44
+  team_id: 8
+  type: 2 # issues
+
+-
+  id: 45
+  team_id: 9
+  type: 1 # code
\ No newline at end of file
index b1dfcdfdef8aa4c31efd03d31d8ce15c8b0664fc..e20b5c9684bfffeac55bd695cfeaab2640c20fb3 100644 (file)
   id: 8
   org_id: 19
   team_id: 6
+  uid: 20
+
+-
+  id: 9
+  org_id: 3
+  team_id: 7
+  uid: 15
+
+-
+  id: 10
+  org_id: 17
+  team_id: 8
+  uid: 2
+
+-
+  id: 11
+  org_id: 17
+  team_id: 9
   uid: 20
\ No newline at end of file
index b3850e35991c12623374cbed293ff3e2650470d7..dc3de2a2e185187f76fd557a2e484a718f019907 100644 (file)
@@ -47,7 +47,7 @@
   avatar_email: user3@example.com
   num_repos: 3
   num_members: 2
-  num_teams: 2
+  num_teams: 3
 
 -
   id: 4
   num_repos: 2
   is_active: true
   num_members: 2
-  num_teams: 1
+  num_teams: 3
 
 -
   id: 18
index ab4fe3107bac706cfdc192329b940e76d204bd88..26196274fe2b7ae8f1d02421968f13ad79c40036 100644 (file)
@@ -385,7 +385,7 @@ func (issue *Issue) sendLabelUpdatedWebhook(doer *User) {
                return
        }
 
-       mode, _ := AccessLevel(issue.Poster.ID, issue.Repo)
+       mode, _ := AccessLevel(issue.Poster, issue.Repo)
        if issue.IsPull {
                if err = issue.loadPullRequest(x); err != nil {
                        log.Error(4, "loadPullRequest: %v", err)
@@ -468,9 +468,11 @@ func (issue *Issue) RemoveLabel(doer *User, label *Label) error {
                return err
        }
 
-       if has, err := HasAccess(doer.ID, issue.Repo, AccessModeWrite); err != nil {
+       perm, err := GetUserRepoPermission(issue.Repo, doer)
+       if err != nil {
                return err
-       } else if !has {
+       }
+       if !perm.CanWriteIssuesOrPulls(issue.IsPull) {
                return ErrLabelNotExist{}
        }
 
@@ -511,9 +513,11 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
                return err
        }
 
-       if has, err := hasAccess(sess, doer.ID, issue.Repo, AccessModeWrite); err != nil {
+       perm, err := getUserRepoPermission(sess, issue.Repo, doer)
+       if err != nil {
                return err
-       } else if !has {
+       }
+       if !perm.CanWriteIssuesOrPulls(issue.IsPull) {
                return ErrLabelNotExist{}
        }
 
@@ -529,7 +533,7 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
                return fmt.Errorf("loadPoster: %v", err)
        }
 
-       mode, _ := AccessLevel(issue.Poster.ID, issue.Repo)
+       mode, _ := AccessLevel(issue.Poster, issue.Repo)
        if issue.IsPull {
                err = issue.PullRequest.LoadIssue()
                if err != nil {
@@ -723,7 +727,7 @@ func (issue *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (e
        }
        sess.Close()
 
-       mode, _ := AccessLevel(issue.Poster.ID, issue.Repo)
+       mode, _ := AccessLevel(issue.Poster, issue.Repo)
        if issue.IsPull {
                // Merge pull request calls issue.changeStatus so we need to handle separately.
                issue.PullRequest.Issue = issue
@@ -785,7 +789,7 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
                return err
        }
 
-       mode, _ := AccessLevel(issue.Poster.ID, issue.Repo)
+       mode, _ := AccessLevel(issue.Poster, issue.Repo)
        if issue.IsPull {
                issue.PullRequest.Issue = issue
                err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
@@ -851,7 +855,7 @@ func (issue *Issue) ChangeContent(doer *User, content string) (err error) {
                return fmt.Errorf("UpdateIssueCols: %v", err)
        }
 
-       mode, _ := AccessLevel(issue.Poster.ID, issue.Repo)
+       mode, _ := AccessLevel(issue.Poster, issue.Repo)
        if issue.IsPull {
                issue.PullRequest.Issue = issue
                err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
@@ -946,9 +950,13 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
        // Check for and validate assignees
        if len(opts.AssigneeIDs) > 0 {
                for _, assigneeID := range opts.AssigneeIDs {
-                       valid, err := hasAccess(e, assigneeID, opts.Repo, AccessModeWrite)
+                       user, err := getUserByID(e, assigneeID)
+                       if err != nil {
+                               return fmt.Errorf("getUserByID [user_id: %d, repo_id: %d]: %v", assigneeID, opts.Repo.ID, err)
+                       }
+                       valid, err := canBeAssigned(e, user, opts.Repo)
                        if err != nil {
-                               return fmt.Errorf("hasAccess [user_id: %d, repo_id: %d]: %v", assigneeID, opts.Repo.ID, err)
+                               return fmt.Errorf("canBeAssigned [user_id: %d, repo_id: %d]: %v", assigneeID, opts.Repo.ID, err)
                        }
                        if !valid {
                                return ErrUserDoesNotHaveAccessToRepo{UserID: assigneeID, RepoName: opts.Repo.Name}
@@ -1071,7 +1079,7 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, assigneeIDs []in
                log.Error(4, "MailParticipants: %v", err)
        }
 
-       mode, _ := AccessLevel(issue.Poster.ID, issue.Repo)
+       mode, _ := AccessLevel(issue.Poster, issue.Repo)
        if err = PrepareWebhooks(repo, HookEventIssues, &api.IssuePayload{
                Action:     api.HookIssueOpened,
                Index:      issue.Index,
index 09d4d310dc6bd8cceffafbf03fe63f52b7845162..f330ade1c8eec6961f35bea1279065a0d6af2538 100644 (file)
@@ -159,13 +159,14 @@ func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID in
                return fmt.Errorf("createAssigneeComment: %v", err)
        }
 
-       // if issue/pull is in the middle of creation - don't call webhook
+       // if pull request is in the middle of creation - don't call webhook
        if isCreate {
                return nil
        }
 
-       mode, _ := accessLevel(sess, doer.ID, issue.Repo)
        if issue.IsPull {
+               mode, _ := accessLevelUnit(sess, doer, issue.Repo, UnitTypePullRequests)
+
                if err = issue.loadPullRequest(sess); err != nil {
                        return fmt.Errorf("loadPullRequest: %v", err)
                }
@@ -186,6 +187,8 @@ func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID in
                        return nil
                }
        } else {
+               mode, _ := accessLevelUnit(sess, doer, issue.Repo, UnitTypeIssues)
+
                apiIssue := &api.IssuePayload{
                        Index:      issue.Index,
                        Issue:      issue.APIFormat(),
index 0085c7a732ae56f4eefb06804a735b00f3233d8e..96b162ca196b2b8c92fc8339ab3093353b944f59 100644 (file)
@@ -795,7 +795,7 @@ func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content stri
                return nil, fmt.Errorf("CreateComment: %v", err)
        }
 
-       mode, _ := AccessLevel(doer.ID, repo)
+       mode, _ := AccessLevel(doer, repo)
        if err = PrepareWebhooks(repo, HookEventIssueComment, &api.IssueCommentPayload{
                Action:     api.HookIssueCommentCreated,
                Issue:      issue.APIFormat(),
@@ -990,7 +990,7 @@ func UpdateComment(doer *User, c *Comment, oldContent string) error {
                return err
        }
 
-       mode, _ := AccessLevel(doer.ID, c.Issue.Repo)
+       mode, _ := AccessLevel(doer, c.Issue.Repo)
        if err := PrepareWebhooks(c.Issue.Repo, HookEventIssueComment, &api.IssueCommentPayload{
                Action:  api.HookIssueCommentEdited,
                Issue:   c.Issue.APIFormat(),
@@ -1047,7 +1047,7 @@ func DeleteComment(doer *User, comment *Comment) error {
                return err
        }
 
-       mode, _ := AccessLevel(doer.ID, comment.Issue.Repo)
+       mode, _ := AccessLevel(doer, comment.Issue.Repo)
 
        if err := PrepareWebhooks(comment.Issue.Repo, HookEventIssueComment, &api.IssueCommentPayload{
                Action:     api.HookIssueCommentDeleted,
index ead3e5a4f65df1455821171230f2c4182aa4e99d..2e512d7ba474c1980dfda261f37bf026a3ab8b10 100644 (file)
@@ -377,7 +377,7 @@ func ChangeMilestoneAssign(issue *Issue, doer *User, oldMilestoneID int64) (err
                return err
        }
 
-       mode, _ := AccessLevel(doer.ID, issue.Repo)
+       mode, _ := AccessLevel(doer, issue.Repo)
        if issue.IsPull {
                err = issue.PullRequest.LoadIssue()
                if err != nil {
index 52e877b156aecaee33517391b2a31fdec26f6174..66fc2b2ec4fbf9cb4a6d85e8b5a833330a157c8b 100644 (file)
@@ -139,10 +139,11 @@ func CheckLFSAccessForRepo(u *User, repo *Repository, mode AccessMode) error {
        if u == nil {
                return ErrLFSUnauthorizedAction{repo.ID, "undefined", mode}
        }
-       has, err := HasAccess(u.ID, repo, mode)
+       perm, err := GetUserRepoPermission(repo, u)
        if err != nil {
                return err
-       } else if !has {
+       }
+       if !perm.CanAccess(mode, UnitTypeCode) {
                return ErrLFSUnauthorizedAction{repo.ID, u.DisplayName(), mode}
        }
        return nil
index 53c1ec34d8bdb68fb0ff31eb5307b5ccdafb4cd7..cad4af25066ea68ab0257abcb09d88312a1d5eb4 100644 (file)
@@ -177,7 +177,7 @@ func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (e
                return fmt.Errorf("getTeamUsersByTeamID: %v", err)
        }
        for _, teamUser := range teamUsers {
-               has, err := hasAccess(e, teamUser.UID, repo, AccessModeRead)
+               has, err := hasAccess(e, teamUser.UID, repo)
                if err != nil {
                        return err
                } else if has {
@@ -434,7 +434,7 @@ func DeleteTeam(t *Team) error {
 
                // Remove watches from all users and now unaccessible repos
                for _, user := range t.Members {
-                       has, err := hasAccess(sess, user.ID, repo, AccessModeRead)
+                       has, err := hasAccess(sess, user.ID, repo)
                        if err != nil {
                                return err
                        } else if has {
@@ -652,7 +652,7 @@ func removeTeamMember(e *xorm.Session, team *Team, userID int64) error {
                }
 
                // Remove watches from now unaccessible
-               has, err := hasAccess(e, userID, repo, AccessModeRead)
+               has, err := hasAccess(e, userID, repo)
                if err != nil {
                        return err
                } else if has {
index 05429c7cc236c0d75eae49be7da5c595c153f0a2..f9f1f289ec7c039b16cd91411c19802e266e327e 100644 (file)
@@ -243,7 +243,7 @@ func TestDeleteTeam(t *testing.T) {
        // check that team members don't have "leftover" access to repos
        user := AssertExistsAndLoadBean(t, &User{ID: 4}).(*User)
        repo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository)
-       accessMode, err := AccessLevel(user.ID, repo)
+       accessMode, err := AccessLevel(user, repo)
        assert.NoError(t, err)
        assert.True(t, accessMode < AccessModeWrite)
 }
index c54e7a93bf19d64dd32c756181172259329d9815..4790f6997137e72264bb78544fa57281d33611c8 100644 (file)
@@ -84,9 +84,10 @@ func TestUser_GetTeams(t *testing.T) {
        assert.NoError(t, PrepareTestDatabase())
        org := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User)
        assert.NoError(t, org.GetTeams())
-       if assert.Len(t, org.Teams, 2) {
+       if assert.Len(t, org.Teams, 3) {
                assert.Equal(t, int64(1), org.Teams[0].ID)
                assert.Equal(t, int64(2), org.Teams[1].ID)
+               assert.Equal(t, int64(7), org.Teams[2].ID)
        }
 }
 
index 79f6d7005d56ae4c66a8543527e6ca62fecd54a4..e97faa8f519de0a6b1dd020665aa8306252a1360 100644 (file)
@@ -458,7 +458,7 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
                return nil
        }
 
-       mode, _ := AccessLevel(doer.ID, pr.Issue.Repo)
+       mode, _ := AccessLevel(doer, pr.Issue.Repo)
        if err = PrepareWebhooks(pr.Issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
                Action:      api.HookIssueClosed,
                Index:       pr.Index,
@@ -787,7 +787,7 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
 
        pr.Issue = pull
        pull.PullRequest = pr
-       mode, _ := AccessLevel(pull.Poster.ID, repo)
+       mode, _ := AccessLevel(pull.Poster, repo)
        if err = PrepareWebhooks(repo, HookEventPullRequest, &api.PullRequestPayload{
                Action:      api.HookIssueOpened,
                Index:       pull.Index,
index 44d028a26f3b5db2b719dca5d63504f4bc15e22c..c18e152293029089d8f7d8f1713523351e7c7c3e 100644 (file)
@@ -200,7 +200,7 @@ func CreateRelease(gitRepo *git.Repository, rel *Release, attachmentUUIDs []stri
                if err := rel.LoadAttributes(); err != nil {
                        log.Error(2, "LoadAttributes: %v", err)
                } else {
-                       mode, _ := AccessLevel(rel.PublisherID, rel.Repo)
+                       mode, _ := AccessLevel(rel.Publisher, rel.Repo)
                        if err := PrepareWebhooks(rel.Repo, HookEventRelease, &api.ReleasePayload{
                                Action:     api.HookReleasePublished,
                                Release:    rel.APIFormat(),
@@ -392,7 +392,7 @@ func UpdateRelease(doer *User, gitRepo *git.Repository, rel *Release, attachment
 
        err = addReleaseAttachments(rel.ID, attachmentUUIDs)
 
-       mode, _ := accessLevel(x, doer.ID, rel.Repo)
+       mode, _ := AccessLevel(doer, rel.Repo)
        if err1 := PrepareWebhooks(rel.Repo, HookEventRelease, &api.ReleasePayload{
                Action:     api.HookReleaseUpdated,
                Release:    rel.APIFormat(),
@@ -419,13 +419,6 @@ func DeleteReleaseByID(id int64, u *User, delTag bool) error {
                return fmt.Errorf("GetRepositoryByID: %v", err)
        }
 
-       has, err := HasAccess(u.ID, repo, AccessModeWrite)
-       if err != nil {
-               return fmt.Errorf("HasAccess: %v", err)
-       } else if !has {
-               return fmt.Errorf("DeleteReleaseByID: permission denied")
-       }
-
        if delTag {
                _, stderr, err := process.GetManager().ExecDir(-1, repo.RepoPath(),
                        fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID),
@@ -454,7 +447,7 @@ func DeleteReleaseByID(id int64, u *User, delTag bool) error {
                return fmt.Errorf("LoadAttributes: %v", err)
        }
 
-       mode, _ := accessLevel(x, u.ID, rel.Repo)
+       mode, _ := AccessLevel(u, rel.Repo)
        if err := PrepareWebhooks(rel.Repo, HookEventRelease, &api.ReleasePayload{
                Action:     api.HookReleaseDeleted,
                Release:    rel.APIFormat(),
index b7be50e9d17a5895cb5919c60d3e5b50b46967ea..b86226ec829d95e949b0e3aafd5438af0cdb6e4d 100644 (file)
@@ -325,63 +325,19 @@ func (repo *Repository) CheckUnitUser(userID int64, isAdmin bool, unitType UnitT
 }
 
 func (repo *Repository) checkUnitUser(e Engine, userID int64, isAdmin bool, unitType UnitType) bool {
-       if err := repo.getUnitsByUserID(e, userID, isAdmin); err != nil {
-               return false
-       }
-
-       for _, unit := range repo.Units {
-               if unit.Type == unitType {
-                       return true
-               }
-       }
-       return false
-}
-
-// LoadUnitsByUserID loads units according userID's permissions
-func (repo *Repository) LoadUnitsByUserID(userID int64, isAdmin bool) error {
-       return repo.getUnitsByUserID(x, userID, isAdmin)
-}
-
-func (repo *Repository) getUnitsByUserID(e Engine, userID int64, isAdmin bool) (err error) {
-       if repo.Units != nil {
-               return nil
-       }
-
-       if err = repo.getUnits(e); err != nil {
-               return err
-       } else if err = repo.getOwner(e); err != nil {
-               return err
-       }
-
-       if !repo.Owner.IsOrganization() || userID == 0 || isAdmin || !repo.IsPrivate {
-               return nil
-       }
-
-       // Collaborators will not be limited
-       if isCollaborator, err := repo.isCollaborator(e, userID); err != nil {
-               return err
-       } else if isCollaborator {
-               return nil
+       if isAdmin {
+               return true
        }
-
-       teams, err := getUserRepoTeams(e, repo.OwnerID, userID, repo.ID)
+       user, err := getUserByID(e, userID)
        if err != nil {
-               return err
+               return false
        }
-
-       // unique
-       var newRepoUnits = make([]*RepoUnit, 0, len(repo.Units))
-       for _, u := range repo.Units {
-               for _, team := range teams {
-                       if team.unitEnabled(e, u.Type) {
-                               newRepoUnits = append(newRepoUnits, u)
-                               break
-                       }
-               }
+       perm, err := getUserRepoPermission(e, repo, user)
+       if err != nil {
+               return false
        }
 
-       repo.Units = newRepoUnits
-       return nil
+       return perm.CanRead(unitType)
 }
 
 // UnitEnabled if this repository has the given unit enabled
@@ -397,21 +353,6 @@ func (repo *Repository) UnitEnabled(tp UnitType) bool {
        return false
 }
 
-// AnyUnitEnabled if this repository has the any of the given units enabled
-func (repo *Repository) AnyUnitEnabled(tps ...UnitType) bool {
-       if err := repo.getUnits(x); err != nil {
-               log.Warn("Error loading repository (ID: %d) units: %s", repo.ID, err.Error())
-       }
-       for _, unit := range repo.Units {
-               for _, tp := range tps {
-                       if unit.Type == tp {
-                               return true
-                       }
-               }
-       }
-       return false
-}
-
 var (
        // ErrUnitNotExist organization does not exist
        ErrUnitNotExist = errors.New("Unit does not exist")
@@ -600,11 +541,6 @@ func (repo *Repository) GetAssignees() (_ []*User, err error) {
        return repo.getAssignees(x)
 }
 
-// GetUserIfHasWriteAccess returns the user that has write access of repository by given ID.
-func (repo *Repository) GetUserIfHasWriteAccess(userID int64) (*User, error) {
-       return GetUserIfHasWriteAccess(repo, userID)
-}
-
 // GetMilestoneByID returns the milestone belongs to repository by given ID.
 func (repo *Repository) GetMilestoneByID(milestoneID int64) (*Milestone, error) {
        return GetMilestoneByRepoID(repo.ID, milestoneID)
@@ -671,12 +607,6 @@ func (repo *Repository) ComposeCompareURL(oldCommitID, newCommitID string) strin
        return fmt.Sprintf("%s/%s/compare/%s...%s", repo.MustOwner().Name, repo.Name, oldCommitID, newCommitID)
 }
 
-// HasAccess returns true when user has access to this repository
-func (repo *Repository) HasAccess(u *User) bool {
-       has, _ := HasAccess(u.ID, repo, AccessModeRead)
-       return has
-}
-
 // UpdateDefaultBranch updates the default branch
 func (repo *Repository) UpdateDefaultBranch() error {
        _, err := x.ID(repo.ID).Cols("default_branch").Update(repo)
@@ -704,11 +634,6 @@ func (repo *Repository) UpdateSize() error {
        return repo.updateSize(x)
 }
 
-// CanBeForked returns true if repository meets the requirements of being forked.
-func (repo *Repository) CanBeForked() bool {
-       return !repo.IsBare && repo.UnitEnabled(UnitTypeCode)
-}
-
 // CanUserFork returns true if specified user can fork repository.
 func (repo *Repository) CanUserFork(user *User) (bool, error) {
        if user == nil {
@@ -2486,8 +2411,8 @@ func ForkRepository(doer, u *User, oldRepo *Repository, name, desc string) (_ *R
                return nil, err
        }
 
-       oldMode, _ := AccessLevel(doer.ID, oldRepo)
-       mode, _ := AccessLevel(doer.ID, repo)
+       oldMode, _ := AccessLevel(doer, oldRepo)
+       mode, _ := AccessLevel(doer, repo)
 
        if err = PrepareWebhooks(oldRepo, HookEventFork, &api.ForkPayload{
                Forkee: oldRepo.APIFormat(oldMode),
diff --git a/models/repo_permission.go b/models/repo_permission.go
new file mode 100644 (file)
index 0000000..9dd7cc5
--- /dev/null
@@ -0,0 +1,270 @@
+// Copyright 2018 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package models
+
+// Permission contains all the permissions related variables to a repository for a user
+type Permission struct {
+       AccessMode AccessMode
+       Units      []*RepoUnit
+       UnitsMode  map[UnitType]AccessMode
+}
+
+// IsOwner returns true if current user is the owner of repository.
+func (p *Permission) IsOwner() bool {
+       return p.AccessMode >= AccessModeOwner
+}
+
+// IsAdmin returns true if current user has admin or higher access of repository.
+func (p *Permission) IsAdmin() bool {
+       return p.AccessMode >= AccessModeAdmin
+}
+
+// HasAccess returns true if the current user has at least read access to any unit of this repository
+func (p *Permission) HasAccess() bool {
+       if p.UnitsMode == nil {
+               return p.AccessMode >= AccessModeRead
+       }
+       return len(p.UnitsMode) > 0
+}
+
+// UnitAccessMode returns current user accessmode to the specify unit of the repository
+func (p *Permission) UnitAccessMode(unitType UnitType) AccessMode {
+       if p.UnitsMode == nil {
+               for _, u := range p.Units {
+                       if u.Type == unitType {
+                               return p.AccessMode
+                       }
+               }
+               return AccessModeNone
+       }
+       return p.UnitsMode[unitType]
+}
+
+// CanAccess returns true if user has mode access to the unit of the repository
+func (p *Permission) CanAccess(mode AccessMode, unitType UnitType) bool {
+       return p.UnitAccessMode(unitType) >= mode
+}
+
+// CanAccessAny returns true if user has mode access to any of the units of the repository
+func (p *Permission) CanAccessAny(mode AccessMode, unitTypes ...UnitType) bool {
+       for _, u := range unitTypes {
+               if p.CanAccess(mode, u) {
+                       return true
+               }
+       }
+       return false
+}
+
+// CanRead returns true if user could read to this unit
+func (p *Permission) CanRead(unitType UnitType) bool {
+       return p.CanAccess(AccessModeRead, unitType)
+}
+
+// CanReadAny returns true if user has read access to any of the units of the repository
+func (p *Permission) CanReadAny(unitTypes ...UnitType) bool {
+       return p.CanAccessAny(AccessModeRead, unitTypes...)
+}
+
+// CanReadIssuesOrPulls returns true if isPull is true and user could read pull requests and
+// returns true if isPull is false and user could read to issues
+func (p *Permission) CanReadIssuesOrPulls(isPull bool) bool {
+       if isPull {
+               return p.CanRead(UnitTypePullRequests)
+       }
+       return p.CanRead(UnitTypeIssues)
+}
+
+// CanWrite returns true if user could write to this unit
+func (p *Permission) CanWrite(unitType UnitType) bool {
+       return p.CanAccess(AccessModeWrite, unitType)
+}
+
+// CanWriteIssuesOrPulls returns true if isPull is true and user could write to pull requests and
+// returns true if isPull is false and user could write to issues
+func (p *Permission) CanWriteIssuesOrPulls(isPull bool) bool {
+       if isPull {
+               return p.CanWrite(UnitTypePullRequests)
+       }
+       return p.CanWrite(UnitTypeIssues)
+}
+
+// GetUserRepoPermission returns the user permissions to the repository
+func GetUserRepoPermission(repo *Repository, user *User) (Permission, error) {
+       return getUserRepoPermission(x, repo, user)
+}
+
+func getUserRepoPermission(e Engine, repo *Repository, user *User) (perm Permission, err error) {
+       // anonymous user visit private repo.
+       // TODO: anonymous user visit public unit of private repo???
+       if user == nil && repo.IsPrivate {
+               perm.AccessMode = AccessModeNone
+               return
+       }
+
+       if err = repo.getUnits(e); err != nil {
+               return
+       }
+
+       perm.Units = repo.Units
+
+       // anonymous visit public repo
+       if user == nil {
+               perm.AccessMode = AccessModeRead
+               return
+       }
+
+       // Admin or the owner has super access to the repository
+       if user.IsAdmin || user.ID == repo.OwnerID {
+               perm.AccessMode = AccessModeOwner
+               return
+       }
+
+       // plain user
+       perm.AccessMode, err = accessLevel(e, user.ID, repo)
+       if err != nil {
+               return
+       }
+
+       if err = repo.getOwner(e); err != nil {
+               return
+       }
+       if !repo.Owner.IsOrganization() {
+               return
+       }
+
+       perm.UnitsMode = make(map[UnitType]AccessMode)
+
+       // Collaborators on organization
+       if isCollaborator, err := repo.isCollaborator(e, user.ID); err != nil {
+               return perm, err
+       } else if isCollaborator {
+               for _, u := range repo.Units {
+                       perm.UnitsMode[u.Type] = perm.AccessMode
+               }
+       }
+
+       // get units mode from teams
+       teams, err := getUserRepoTeams(e, repo.OwnerID, user.ID, repo.ID)
+       if err != nil {
+               return
+       }
+
+       for _, u := range repo.Units {
+               var found bool
+               for _, team := range teams {
+                       if team.unitEnabled(e, u.Type) {
+                               m := perm.UnitsMode[u.Type]
+                               if m < team.Authorize {
+                                       perm.UnitsMode[u.Type] = team.Authorize
+                               }
+                               found = true
+                       }
+               }
+
+               // for a public repo on an organization, user have read permission on non-team defined units.
+               if !found && !repo.IsPrivate {
+                       if _, ok := perm.UnitsMode[u.Type]; !ok {
+                               perm.UnitsMode[u.Type] = AccessModeRead
+                       }
+               }
+       }
+
+       // remove no permission units
+       perm.Units = make([]*RepoUnit, 0, len(repo.Units))
+       for t := range perm.UnitsMode {
+               for _, u := range repo.Units {
+                       if u.Type == t {
+                               perm.Units = append(perm.Units, u)
+                       }
+               }
+       }
+
+       return
+}
+
+// IsUserRepoAdmin return ture if user has admin right of a repo
+func IsUserRepoAdmin(repo *Repository, user *User) (bool, error) {
+       return isUserRepoAdmin(x, repo, user)
+}
+
+func isUserRepoAdmin(e Engine, repo *Repository, user *User) (bool, error) {
+       if user == nil || repo == nil {
+               return false, nil
+       }
+       if user.IsAdmin {
+               return true, nil
+       }
+
+       mode, err := accessLevel(e, user.ID, repo)
+       if err != nil {
+               return false, err
+       }
+       if mode >= AccessModeAdmin {
+               return true, nil
+       }
+
+       teams, err := getUserRepoTeams(e, repo.OwnerID, user.ID, repo.ID)
+       if err != nil {
+               return false, err
+       }
+
+       for _, team := range teams {
+               if team.Authorize >= AccessModeAdmin {
+                       return true, nil
+               }
+       }
+       return false, nil
+}
+
+// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
+// user does not have access.
+func AccessLevel(user *User, repo *Repository) (AccessMode, error) {
+       return accessLevelUnit(x, user, repo, UnitTypeCode)
+}
+
+func accessLevelUnit(e Engine, user *User, repo *Repository, unitType UnitType) (AccessMode, error) {
+       perm, err := getUserRepoPermission(e, repo, user)
+       if err != nil {
+               return AccessModeNone, err
+       }
+       return perm.UnitAccessMode(UnitTypeCode), nil
+}
+
+func hasAccessUnit(e Engine, user *User, repo *Repository, unitType UnitType, testMode AccessMode) (bool, error) {
+       mode, err := accessLevelUnit(e, user, repo, unitType)
+       return testMode <= mode, err
+}
+
+// HasAccessUnit returns ture if user has testMode to the unit of the repository
+func HasAccessUnit(user *User, repo *Repository, unitType UnitType, testMode AccessMode) (bool, error) {
+       return hasAccessUnit(x, user, repo, unitType, testMode)
+}
+
+// canBeAssigned return true if user could be assigned to a repo
+// FIXME: user could send PullRequest also could be assigned???
+func canBeAssigned(e Engine, user *User, repo *Repository) (bool, error) {
+       return hasAccessUnit(e, user, repo, UnitTypeCode, AccessModeWrite)
+}
+
+func hasAccess(e Engine, userID int64, repo *Repository) (bool, error) {
+       var user *User
+       var err error
+       if userID > 0 {
+               user, err = getUserByID(e, userID)
+               if err != nil {
+                       return false, err
+               }
+       }
+       perm, err := getUserRepoPermission(e, repo, user)
+       if err != nil {
+               return false, err
+       }
+       return perm.HasAccess(), nil
+}
+
+// HasAccess returns true if user has access to repo
+func HasAccess(userID int64, repo *Repository) (bool, error) {
+       return hasAccess(x, userID, repo)
+}
diff --git a/models/repo_permission_test.go b/models/repo_permission_test.go
new file mode 100644 (file)
index 0000000..fd55ae5
--- /dev/null
@@ -0,0 +1,246 @@
+// Copyright 2018 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package models
+
+import (
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+)
+
+func TestRepoPermissionPublicNonOrgRepo(t *testing.T) {
+       assert.NoError(t, PrepareTestDatabase())
+
+       // public non-organization repo
+       repo := AssertExistsAndLoadBean(t, &Repository{ID: 4}).(*Repository)
+       assert.NoError(t, repo.getUnits(x))
+
+       // plain user
+       user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
+       perm, err := GetUserRepoPermission(repo, user)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.True(t, perm.CanRead(unit.Type))
+               assert.False(t, perm.CanWrite(unit.Type))
+       }
+
+       // change to collaborator
+       assert.NoError(t, repo.AddCollaborator(user))
+       perm, err = GetUserRepoPermission(repo, user)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.True(t, perm.CanRead(unit.Type))
+               assert.True(t, perm.CanWrite(unit.Type))
+       }
+
+       // collaborator
+       collaborator := AssertExistsAndLoadBean(t, &User{ID: 4}).(*User)
+       perm, err = GetUserRepoPermission(repo, collaborator)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.True(t, perm.CanRead(unit.Type))
+               assert.True(t, perm.CanWrite(unit.Type))
+       }
+
+       // owner
+       owner := AssertExistsAndLoadBean(t, &User{ID: 5}).(*User)
+       perm, err = GetUserRepoPermission(repo, owner)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.True(t, perm.CanRead(unit.Type))
+               assert.True(t, perm.CanWrite(unit.Type))
+       }
+
+       // admin
+       admin := AssertExistsAndLoadBean(t, &User{ID: 1}).(*User)
+       perm, err = GetUserRepoPermission(repo, admin)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.True(t, perm.CanRead(unit.Type))
+               assert.True(t, perm.CanWrite(unit.Type))
+       }
+}
+
+func TestRepoPermissionPrivateNonOrgRepo(t *testing.T) {
+       assert.NoError(t, PrepareTestDatabase())
+
+       // private non-organization repo
+       repo := AssertExistsAndLoadBean(t, &Repository{ID: 2}).(*Repository)
+       assert.NoError(t, repo.getUnits(x))
+
+       // plain user
+       user := AssertExistsAndLoadBean(t, &User{ID: 4}).(*User)
+       perm, err := GetUserRepoPermission(repo, user)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.False(t, perm.CanRead(unit.Type))
+               assert.False(t, perm.CanWrite(unit.Type))
+       }
+
+       // change to collaborator to default write access
+       assert.NoError(t, repo.AddCollaborator(user))
+       perm, err = GetUserRepoPermission(repo, user)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.True(t, perm.CanRead(unit.Type))
+               assert.True(t, perm.CanWrite(unit.Type))
+       }
+
+       assert.NoError(t, repo.ChangeCollaborationAccessMode(user.ID, AccessModeRead))
+       perm, err = GetUserRepoPermission(repo, user)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.True(t, perm.CanRead(unit.Type))
+               assert.False(t, perm.CanWrite(unit.Type))
+       }
+
+       // owner
+       owner := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
+       perm, err = GetUserRepoPermission(repo, owner)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.True(t, perm.CanRead(unit.Type))
+               assert.True(t, perm.CanWrite(unit.Type))
+       }
+
+       // admin
+       admin := AssertExistsAndLoadBean(t, &User{ID: 1}).(*User)
+       perm, err = GetUserRepoPermission(repo, admin)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.True(t, perm.CanRead(unit.Type))
+               assert.True(t, perm.CanWrite(unit.Type))
+       }
+}
+
+func TestRepoPermissionPublicOrgRepo(t *testing.T) {
+       assert.NoError(t, PrepareTestDatabase())
+
+       // public organization repo
+       repo := AssertExistsAndLoadBean(t, &Repository{ID: 32}).(*Repository)
+       assert.NoError(t, repo.getUnits(x))
+
+       // plain user
+       user := AssertExistsAndLoadBean(t, &User{ID: 5}).(*User)
+       perm, err := GetUserRepoPermission(repo, user)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.True(t, perm.CanRead(unit.Type))
+               assert.False(t, perm.CanWrite(unit.Type))
+       }
+
+       // change to collaborator to default write access
+       assert.NoError(t, repo.AddCollaborator(user))
+       perm, err = GetUserRepoPermission(repo, user)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.True(t, perm.CanRead(unit.Type))
+               assert.True(t, perm.CanWrite(unit.Type))
+       }
+
+       assert.NoError(t, repo.ChangeCollaborationAccessMode(user.ID, AccessModeRead))
+       perm, err = GetUserRepoPermission(repo, user)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.True(t, perm.CanRead(unit.Type))
+               assert.False(t, perm.CanWrite(unit.Type))
+       }
+
+       // org member team owner
+       owner := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
+       perm, err = GetUserRepoPermission(repo, owner)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.True(t, perm.CanRead(unit.Type))
+               assert.True(t, perm.CanWrite(unit.Type))
+       }
+
+       // org member team tester
+       member := AssertExistsAndLoadBean(t, &User{ID: 15}).(*User)
+       perm, err = GetUserRepoPermission(repo, member)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.True(t, perm.CanRead(unit.Type))
+       }
+       assert.True(t, perm.CanWrite(UnitTypeIssues))
+       assert.False(t, perm.CanWrite(UnitTypeCode))
+
+       // admin
+       admin := AssertExistsAndLoadBean(t, &User{ID: 1}).(*User)
+       perm, err = GetUserRepoPermission(repo, admin)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.True(t, perm.CanRead(unit.Type))
+               assert.True(t, perm.CanWrite(unit.Type))
+       }
+}
+
+func TestRepoPermissionPrivateOrgRepo(t *testing.T) {
+       assert.NoError(t, PrepareTestDatabase())
+
+       // private organization repo
+       repo := AssertExistsAndLoadBean(t, &Repository{ID: 24}).(*Repository)
+       assert.NoError(t, repo.getUnits(x))
+
+       // plain user
+       user := AssertExistsAndLoadBean(t, &User{ID: 5}).(*User)
+       perm, err := GetUserRepoPermission(repo, user)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.False(t, perm.CanRead(unit.Type))
+               assert.False(t, perm.CanWrite(unit.Type))
+       }
+
+       // change to collaborator to default write access
+       assert.NoError(t, repo.AddCollaborator(user))
+       perm, err = GetUserRepoPermission(repo, user)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.True(t, perm.CanRead(unit.Type))
+               assert.True(t, perm.CanWrite(unit.Type))
+       }
+
+       assert.NoError(t, repo.ChangeCollaborationAccessMode(user.ID, AccessModeRead))
+       perm, err = GetUserRepoPermission(repo, user)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.True(t, perm.CanRead(unit.Type))
+               assert.False(t, perm.CanWrite(unit.Type))
+       }
+
+       // org member team owner
+       owner := AssertExistsAndLoadBean(t, &User{ID: 15}).(*User)
+       perm, err = GetUserRepoPermission(repo, owner)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.True(t, perm.CanRead(unit.Type))
+               assert.True(t, perm.CanWrite(unit.Type))
+       }
+
+       // org member team tester
+       tester := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
+       perm, err = GetUserRepoPermission(repo, tester)
+       assert.NoError(t, err)
+       assert.True(t, perm.CanWrite(UnitTypeIssues))
+       assert.False(t, perm.CanWrite(UnitTypeCode))
+       assert.False(t, perm.CanRead(UnitTypeCode))
+
+       // org member team reviewer
+       reviewer := AssertExistsAndLoadBean(t, &User{ID: 20}).(*User)
+       perm, err = GetUserRepoPermission(repo, reviewer)
+       assert.NoError(t, err)
+       assert.False(t, perm.CanRead(UnitTypeIssues))
+       assert.False(t, perm.CanWrite(UnitTypeCode))
+       assert.True(t, perm.CanRead(UnitTypeCode))
+
+       // admin
+       admin := AssertExistsAndLoadBean(t, &User{ID: 1}).(*User)
+       perm, err = GetUserRepoPermission(repo, admin)
+       assert.NoError(t, err)
+       for _, unit := range repo.Units {
+               assert.True(t, perm.CanRead(unit.Type))
+               assert.True(t, perm.CanWrite(unit.Type))
+       }
+}
index 1e1778356a06d04a5095790053e82ce49a4e4e5c..9eaec884bb11aa473dd10ad5c65829c3e66ee4a3 100644 (file)
@@ -166,10 +166,7 @@ func (r *RepoUnit) IssuesConfig() *IssuesConfig {
 func (r *RepoUnit) ExternalTrackerConfig() *ExternalTrackerConfig {
        return r.Config.(*ExternalTrackerConfig)
 }
+
 func getUnitsByRepoID(e Engine, repoID int64) (units []*RepoUnit, err error) {
        return units, e.Where("repo_id = ?", repoID).Find(&units)
 }
-
-func getUnitsByRepoIDAndIDs(e Engine, repoID int64, types []UnitType) (units []*RepoUnit, err error) {
-       return units, e.Where("repo_id = ?", repoID).In("`type`", types).Find(&units)
-}
index 5d046c020113ba66759ca42a9383b81219fe70ef..b7dd81b49b0b3a508225e66ccf8f78c185972a45 100644 (file)
@@ -807,10 +807,10 @@ func DeleteDeployKey(doer *User, id int64) error {
                if err != nil {
                        return fmt.Errorf("GetRepositoryByID: %v", err)
                }
-               yes, err := HasAccess(doer.ID, repo, AccessModeAdmin)
+               has, err := IsUserRepoAdmin(repo, doer)
                if err != nil {
-                       return fmt.Errorf("HasAccess: %v", err)
-               } else if !yes {
+                       return fmt.Errorf("GetUserRepoPermission: %v", err)
+               } else if !has {
                        return ErrKeyAccessDenied{doer.ID, key.ID, "deploy"}
                }
        }
index 9becee7760bd154eac0e3f0da6382e81a1c61e09..8f1b170b0d5acf8dbc72780ea42cd9a9bb71485a 100644 (file)
@@ -496,24 +496,6 @@ func (u *User) DeleteAvatar() error {
        return nil
 }
 
-// IsAdminOfRepo returns true if user has admin or higher access of repository.
-func (u *User) IsAdminOfRepo(repo *Repository) bool {
-       has, err := HasAccess(u.ID, repo, AccessModeAdmin)
-       if err != nil {
-               log.Error(3, "HasAccess: %v", err)
-       }
-       return has
-}
-
-// IsWriterOfRepo returns true if user has write access to given repository.
-func (u *User) IsWriterOfRepo(repo *Repository) bool {
-       has, err := HasAccess(u.ID, repo, AccessModeWrite)
-       if err != nil {
-               log.Error(3, "HasAccess: %v", err)
-       }
-       return has
-}
-
 // IsOrganization returns true if user is actually a organization.
 func (u *User) IsOrganization() bool {
        return u.Type == UserTypeOrganization
@@ -1170,17 +1152,6 @@ func GetUserByID(id int64) (*User, error) {
        return getUserByID(x, id)
 }
 
-// GetUserIfHasWriteAccess returns the user with write access of repository by given ID.
-func GetUserIfHasWriteAccess(repo *Repository, userID int64) (*User, error) {
-       has, err := HasAccess(userID, repo, AccessModeWrite)
-       if err != nil {
-               return nil, err
-       } else if !has {
-               return nil, ErrUserNotExist{userID, "", 0}
-       }
-       return GetUserByID(userID)
-}
-
 // GetUserByName returns user by given name.
 func GetUserByName(name string) (*User, error) {
        return getUserByName(x, name)
index 4713b6864c45f0e2c726982f2fe5c014da275ec7..ba700d36599da644390284ca8b0e394baf596f4a 100644 (file)
@@ -169,7 +169,7 @@ func TestGetOrgRepositoryIDs(t *testing.T) {
        accessibleRepos, err := user2.GetOrgRepositoryIDs()
        assert.NoError(t, err)
        // User 2's team has access to private repos 3, 5, repo 32 is a public repo of the organization
-       assert.Equal(t, []int64{3, 5, 32}, accessibleRepos)
+       assert.Equal(t, []int64{3, 5, 23, 24, 32}, accessibleRepos)
 
        accessibleRepos, err = user4.GetOrgRepositoryIDs()
        assert.NoError(t, err)
diff --git a/modules/context/permission.go b/modules/context/permission.go
new file mode 100644 (file)
index 0000000..70f8695
--- /dev/null
@@ -0,0 +1,64 @@
+// Copyright 2018 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package context
+
+import (
+       "code.gitea.io/gitea/models"
+       macaron "gopkg.in/macaron.v1"
+)
+
+// RequireRepoAdmin returns a macaron middleware for requiring repository admin permission
+func RequireRepoAdmin() macaron.Handler {
+       return func(ctx *Context) {
+               if !ctx.IsSigned || !ctx.Repo.IsAdmin() {
+                       ctx.NotFound(ctx.Req.RequestURI, nil)
+                       return
+               }
+       }
+}
+
+// RequireRepoWriter returns a macaron middleware for requiring repository write to the specify unitType
+func RequireRepoWriter(unitType models.UnitType) macaron.Handler {
+       return func(ctx *Context) {
+               if !ctx.Repo.CanWrite(unitType) {
+                       ctx.NotFound(ctx.Req.RequestURI, nil)
+                       return
+               }
+       }
+}
+
+// RequireRepoWriterOr returns a macaron middleware for requiring repository write to one of the unit permission
+func RequireRepoWriterOr(unitTypes ...models.UnitType) macaron.Handler {
+       return func(ctx *Context) {
+               for _, unitType := range unitTypes {
+                       if ctx.Repo.CanWrite(unitType) {
+                               return
+                       }
+               }
+               ctx.NotFound(ctx.Req.RequestURI, nil)
+       }
+}
+
+// RequireRepoReader returns a macaron middleware for requiring repository read to the specify unitType
+func RequireRepoReader(unitType models.UnitType) macaron.Handler {
+       return func(ctx *Context) {
+               if !ctx.Repo.CanRead(unitType) {
+                       ctx.NotFound(ctx.Req.RequestURI, nil)
+                       return
+               }
+       }
+}
+
+// RequireRepoReaderOr returns a macaron middleware for requiring repository write to one of the unit permission
+func RequireRepoReaderOr(unitTypes ...models.UnitType) macaron.Handler {
+       return func(ctx *Context) {
+               for _, unitType := range unitTypes {
+                       if ctx.Repo.CanRead(unitType) {
+                               return
+                       }
+               }
+               ctx.NotFound(ctx.Req.RequestURI, nil)
+       }
+}
index be08bc4c77f9f2d38b5b88123c46727430d9d536..55d607a28ac4d8a28e5c27e030d58289f16015fc 100644 (file)
@@ -32,7 +32,7 @@ type PullRequest struct {
 
 // Repository contains information to operate a repository
 type Repository struct {
-       AccessMode   models.AccessMode
+       models.Permission
        IsWatching   bool
        IsViewBranch bool
        IsViewTag    bool
@@ -54,34 +54,14 @@ type Repository struct {
        PullRequest *PullRequest
 }
 
-// IsOwner returns true if current user is the owner of repository.
-func (r *Repository) IsOwner() bool {
-       return r.AccessMode >= models.AccessModeOwner
-}
-
-// IsAdmin returns true if current user has admin or higher access of repository.
-func (r *Repository) IsAdmin() bool {
-       return r.AccessMode >= models.AccessModeAdmin
-}
-
-// IsWriter returns true if current user has write or higher access of repository.
-func (r *Repository) IsWriter() bool {
-       return r.AccessMode >= models.AccessModeWrite
-}
-
-// HasAccess returns true if the current user has at least read access for this repository
-func (r *Repository) HasAccess() bool {
-       return r.AccessMode >= models.AccessModeRead
-}
-
 // CanEnableEditor returns true if repository is editable and user has proper access level.
 func (r *Repository) CanEnableEditor() bool {
-       return r.Repository.CanEnableEditor() && r.IsViewBranch && r.IsWriter()
+       return r.Permission.CanWrite(models.UnitTypeCode) && r.Repository.CanEnableEditor() && r.IsViewBranch
 }
 
 // CanCreateBranch returns true if repository is editable and user has proper access level.
 func (r *Repository) CanCreateBranch() bool {
-       return r.Repository.CanCreateBranch() && r.IsWriter()
+       return r.Permission.CanWrite(models.UnitTypeCode) && r.Repository.CanCreateBranch()
 }
 
 // CanCommitToBranch returns true if repository is editable and user has proper access level
@@ -101,12 +81,12 @@ func (r *Repository) CanUseTimetracker(issue *models.Issue, user *models.User) b
        // 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this?
        isAssigned, _ := models.IsUserAssignedToIssue(issue, user)
        return r.Repository.IsTimetrackerEnabled() && (!r.Repository.AllowOnlyContributorsToTrackTime() ||
-               r.IsWriter() || issue.IsPoster(user.ID) || isAssigned)
+               r.Permission.CanWrite(models.UnitTypeIssues) || issue.IsPoster(user.ID) || isAssigned)
 }
 
 // CanCreateIssueDependencies returns whether or not a user can create dependencies.
 func (r *Repository) CanCreateIssueDependencies(user *models.User) bool {
-       return r.Repository.IsDependenciesEnabled() && r.IsWriter()
+       return r.Permission.CanWrite(models.UnitTypeIssues) && r.Repository.IsDependenciesEnabled()
 }
 
 // GetCommitsCount returns cached commit count for current view
@@ -221,24 +201,15 @@ func RedirectToRepo(ctx *Context, redirectRepoID int64) {
 }
 
 func repoAssignment(ctx *Context, repo *models.Repository) {
-       // Admin has super access.
-       if ctx.IsSigned && ctx.User.IsAdmin {
-               ctx.Repo.AccessMode = models.AccessModeOwner
-       } else {
-               var userID int64
-               if ctx.User != nil {
-                       userID = ctx.User.ID
-               }
-               mode, err := models.AccessLevel(userID, repo)
-               if err != nil {
-                       ctx.ServerError("AccessLevel", err)
-                       return
-               }
-               ctx.Repo.AccessMode = mode
+       var err error
+       ctx.Repo.Permission, err = models.GetUserRepoPermission(repo, ctx.User)
+       if err != nil {
+               ctx.ServerError("GetUserRepoPermission", err)
+               return
        }
 
        // Check access.
-       if ctx.Repo.AccessMode == models.AccessModeNone {
+       if ctx.Repo.Permission.AccessMode == models.AccessModeNone {
                if ctx.Query("go-get") == "1" {
                        EarlyResponseForGoGetMeta(ctx)
                        return
@@ -247,6 +218,7 @@ func repoAssignment(ctx *Context, repo *models.Repository) {
                return
        }
        ctx.Data["HasAccess"] = true
+       ctx.Data["Permission"] = &ctx.Repo.Permission
 
        if repo.IsMirror {
                var err error
@@ -281,10 +253,6 @@ func RepoIDAssignment() macaron.Handler {
                        return
                }
 
-               if err = repo.GetOwner(); err != nil {
-                       ctx.ServerError("GetOwner", err)
-                       return
-               }
                repoAssignment(ctx, repo)
        }
 }
@@ -381,7 +349,9 @@ func RepoAssignment() macaron.Handler {
                ctx.Data["Owner"] = ctx.Repo.Repository.Owner
                ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner()
                ctx.Data["IsRepositoryAdmin"] = ctx.Repo.IsAdmin()
-               ctx.Data["IsRepositoryWriter"] = ctx.Repo.IsWriter()
+               ctx.Data["CanWriteCode"] = ctx.Repo.CanWrite(models.UnitTypeCode)
+               ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(models.UnitTypeIssues)
+               ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(models.UnitTypePullRequests)
 
                if ctx.Data["CanSignedUserFork"], err = ctx.Repo.Repository.CanUserFork(ctx.User); err != nil {
                        ctx.ServerError("CanUserFork", err)
@@ -435,7 +405,7 @@ func RepoAssignment() macaron.Handler {
                }
 
                // People who have push access or have forked repository can propose a new pull request.
-               if ctx.Repo.IsWriter() || (ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID)) {
+               if ctx.Repo.CanWrite(models.UnitTypeCode) || (ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID)) {
                        // Pull request is allowed if this is a fork repository
                        // and base repository accepts pull requests.
                        if repo.BaseRepo != nil && repo.BaseRepo.AllowsPulls() {
@@ -453,9 +423,6 @@ func RepoAssignment() macaron.Handler {
                                        ctx.Repo.PullRequest.HeadInfo = ctx.Repo.BranchName
                                }
                        }
-
-                       // Reset repo units as otherwise user specific units wont be loaded later
-                       ctx.Repo.Repository.Units = nil
                }
                ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest
 
@@ -661,64 +628,6 @@ func RepoRefByType(refType RepoRefType) macaron.Handler {
        }
 }
 
-// RequireRepoAdmin returns a macaron middleware for requiring repository admin permission
-func RequireRepoAdmin() macaron.Handler {
-       return func(ctx *Context) {
-               if !ctx.IsSigned || (!ctx.Repo.IsAdmin() && !ctx.User.IsAdmin) {
-                       ctx.NotFound(ctx.Req.RequestURI, nil)
-                       return
-               }
-       }
-}
-
-// RequireRepoWriter returns a macaron middleware for requiring repository write permission
-func RequireRepoWriter() macaron.Handler {
-       return func(ctx *Context) {
-               if !ctx.IsSigned || (!ctx.Repo.IsWriter() && !ctx.User.IsAdmin) {
-                       ctx.NotFound(ctx.Req.RequestURI, nil)
-                       return
-               }
-       }
-}
-
-// LoadRepoUnits loads repsitory's units, it should be called after repository and user loaded
-func LoadRepoUnits() macaron.Handler {
-       return func(ctx *Context) {
-               var isAdmin bool
-               if ctx.User != nil && ctx.User.IsAdmin {
-                       isAdmin = true
-               }
-
-               var userID int64
-               if ctx.User != nil {
-                       userID = ctx.User.ID
-               }
-               err := ctx.Repo.Repository.LoadUnitsByUserID(userID, isAdmin)
-               if err != nil {
-                       ctx.ServerError("LoadUnitsByUserID", err)
-                       return
-               }
-       }
-}
-
-// CheckUnit will check whether unit type is enabled
-func CheckUnit(unitType models.UnitType) macaron.Handler {
-       return func(ctx *Context) {
-               if !ctx.Repo.Repository.UnitEnabled(unitType) {
-                       ctx.NotFound("CheckUnit", fmt.Errorf("%s: %v", ctx.Tr("units.error.unit_not_allowed"), unitType))
-               }
-       }
-}
-
-// CheckAnyUnit will check whether any of the unit types are enabled
-func CheckAnyUnit(unitTypes ...models.UnitType) macaron.Handler {
-       return func(ctx *Context) {
-               if !ctx.Repo.Repository.AnyUnitEnabled(unitTypes...) {
-                       ctx.NotFound("CheckAnyUnit", fmt.Errorf("%s: %v", ctx.Tr("units.error.unit_not_allowed"), unitTypes))
-               }
-       }
-}
-
 // GitHookService checks if repository Git hooks service has been enabled.
 func GitHookService() macaron.Handler {
        return func(ctx *Context) {
index d6543816b92686da0750b05440189e842170c1e9..f0f2d4bf4402683f2405d2edcb96548189166d71 100644 (file)
@@ -497,12 +497,12 @@ func authenticate(ctx *context.Context, repository *models.Repository, authoriza
                accessMode = models.AccessModeWrite
        }
 
-       if !repository.IsPrivate && !requireWrite {
-               return true
+       perm, err := models.GetUserRepoPermission(repository, ctx.User)
+       if err != nil {
+               return false
        }
        if ctx.IsSigned {
-               accessCheck, _ := models.HasAccess(ctx.User.ID, repository, accessMode)
-               return accessCheck
+               return perm.CanAccess(accessMode, models.UnitTypeCode)
        }
 
        user, repo, opStr, err := parseToken(authorization)
@@ -511,8 +511,11 @@ func authenticate(ctx *context.Context, repository *models.Repository, authoriza
        }
        ctx.User = user
        if opStr == "basic" {
-               accessCheck, _ := models.HasAccess(ctx.User.ID, repository, accessMode)
-               return accessCheck
+               perm, err = models.GetUserRepoPermission(repository, ctx.User)
+               if err != nil {
+                       return false
+               }
+               return perm.CanAccess(accessMode, models.UnitTypeCode)
        }
        if repository.ID == repo.ID {
                if requireWrite && opStr != "upload" {
index f4ac1c515a4bf4e8c54b755e357b909293963c6c..a230bc744ca366512d3d48b81c55fd5364473ba2 100644 (file)
@@ -51,27 +51,9 @@ func newInternalRequest(url, method string) *httplib.Request {
 }
 
 // CheckUnitUser check whether user could visit the unit of this repository
-func CheckUnitUser(userID, repoID int64, isAdmin bool, unitType models.UnitType) (bool, error) {
+func CheckUnitUser(userID, repoID int64, isAdmin bool, unitType models.UnitType) (*models.AccessMode, error) {
        reqURL := setting.LocalURL + fmt.Sprintf("api/internal/repositories/%d/user/%d/checkunituser?isAdmin=%t&unitType=%d", repoID, userID, isAdmin, unitType)
-       log.GitLogger.Trace("AccessLevel: %s", reqURL)
-
-       resp, err := newInternalRequest(reqURL, "GET").Response()
-       if err != nil {
-               return false, err
-       }
-       defer resp.Body.Close()
-
-       if resp.StatusCode == 200 {
-               return true, nil
-       }
-       return false, nil
-}
-
-// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
-// user does not have access.
-func AccessLevel(userID, repoID int64) (*models.AccessMode, error) {
-       reqURL := setting.LocalURL + fmt.Sprintf("api/internal/repositories/%d/user/%d/accesslevel", repoID, userID)
-       log.GitLogger.Trace("AccessLevel: %s", reqURL)
+       log.GitLogger.Trace("CheckUnitUser: %s", reqURL)
 
        resp, err := newInternalRequest(reqURL, "GET").Response()
        if err != nil {
@@ -80,7 +62,7 @@ func AccessLevel(userID, repoID int64) (*models.AccessMode, error) {
        defer resp.Body.Close()
 
        if resp.StatusCode != 200 {
-               return nil, fmt.Errorf("Failed to get user access level: %s", decodeJSONError(resp).Err)
+               return nil, fmt.Errorf("Failed to CheckUnitUser: %s", decodeJSONError(resp).Err)
        }
 
        var a models.AccessMode
index b2b96fff9d44f27088b0c3c90c08631a1bef235f..af986001ae3e0e8a385b70adabec0a3732de00fb 100644 (file)
@@ -47,6 +47,9 @@ func LoadRepo(t *testing.T, ctx *context.Context, repoID int64) {
        ctx.Repo = &context.Repository{}
        ctx.Repo.Repository = models.AssertExistsAndLoadBean(t, &models.Repository{ID: repoID}).(*models.Repository)
        ctx.Repo.RepoLink = ctx.Repo.Repository.Link()
+       var err error
+       ctx.Repo.Permission, err = models.GetUserRepoPermission(ctx.Repo.Repository, ctx.User)
+       assert.NoError(t, err)
 }
 
 // LoadRepoCommit loads a repo's commit into a test context.
index 0be8f84836f3c73a05f341012c5c56368a3c40b0..47e556c8815025dd8d704599141475dc809584e2 100644 (file)
@@ -1,5 +1,5 @@
 // Copyright 2015 The Gogs Authors. All rights reserved.
-// Copyright 2018 The Gitea Authors. All rights reserved.
+// Copyright 2016 The Gitea Authors. All rights reserved.
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
@@ -71,7 +71,6 @@ import (
        "code.gitea.io/gitea/routers/api/v1/repo"
        _ "code.gitea.io/gitea/routers/api/v1/swagger" // for swagger generation
        "code.gitea.io/gitea/routers/api/v1/user"
-       "code.gitea.io/gitea/routers/api/v1/utils"
        api "code.gitea.io/sdk/gitea"
 
        "github.com/go-macaron/binding"
@@ -152,24 +151,18 @@ func repoAssignment() macaron.Handler {
                        return
                }
                repo.Owner = owner
+               ctx.Repo.Repository = repo
 
-               if ctx.IsSigned && ctx.User.IsAdmin {
-                       ctx.Repo.AccessMode = models.AccessModeOwner
-               } else {
-                       mode, err := models.AccessLevel(utils.UserID(ctx), repo)
-                       if err != nil {
-                               ctx.Error(500, "AccessLevel", err)
-                               return
-                       }
-                       ctx.Repo.AccessMode = mode
+               ctx.Repo.Permission, err = models.GetUserRepoPermission(repo, ctx.User)
+               if err != nil {
+                       ctx.Error(500, "GetUserRepoPermission", err)
+                       return
                }
 
                if !ctx.Repo.HasAccess() {
                        ctx.Status(404)
                        return
                }
-
-               ctx.Repo.Repository = repo
        }
 }
 
@@ -196,7 +189,8 @@ func reqBasicAuth() macaron.Handler {
        }
 }
 
-func reqAdmin() macaron.Handler {
+// reqSiteAdmin user should be the site admin
+func reqSiteAdmin() macaron.Handler {
        return func(ctx *context.Context) {
                if !ctx.IsSigned || !ctx.User.IsAdmin {
                        ctx.Error(403)
@@ -205,15 +199,56 @@ func reqAdmin() macaron.Handler {
        }
 }
 
-func reqRepoWriter() macaron.Handler {
+// reqOwner user should be the owner of the repo.
+func reqOwner() macaron.Handler {
+       return func(ctx *context.Context) {
+               if !ctx.Repo.IsOwner() {
+                       ctx.Error(403)
+                       return
+               }
+       }
+}
+
+// reqAdmin user should be an owner or a collaborator with admin write of a repository
+func reqAdmin() macaron.Handler {
+       return func(ctx *context.Context) {
+               if !ctx.Repo.IsAdmin() {
+                       ctx.Error(403)
+                       return
+               }
+       }
+}
+
+func reqRepoReader(unitType models.UnitType) macaron.Handler {
+       return func(ctx *context.Context) {
+               if !ctx.Repo.CanRead(unitType) {
+                       ctx.Error(403)
+                       return
+               }
+       }
+}
+
+func reqAnyRepoReader() macaron.Handler {
        return func(ctx *context.Context) {
-               if !ctx.Repo.IsWriter() {
+               if !ctx.Repo.HasAccess() {
                        ctx.Error(403)
                        return
                }
        }
 }
 
+func reqRepoWriter(unitTypes ...models.UnitType) macaron.Handler {
+       return func(ctx *context.Context) {
+               for _, unitType := range unitTypes {
+                       if ctx.Repo.CanWrite(unitType) {
+                               return
+                       }
+               }
+
+               ctx.Error(403)
+       }
+}
+
 func reqOrgMembership() macaron.Handler {
        return func(ctx *context.APIContext) {
                var orgID int64
@@ -308,22 +343,22 @@ func orgAssignment(args ...bool) macaron.Handler {
 }
 
 func mustEnableIssues(ctx *context.APIContext) {
-       if !ctx.Repo.Repository.UnitEnabled(models.UnitTypeIssues) {
+       if !ctx.Repo.CanRead(models.UnitTypeIssues) {
                ctx.Status(404)
                return
        }
 }
 
 func mustAllowPulls(ctx *context.Context) {
-       if !ctx.Repo.Repository.AllowsPulls() {
+       if !(ctx.Repo.Repository.CanEnablePulls() && ctx.Repo.CanRead(models.UnitTypePullRequests)) {
                ctx.Status(404)
                return
        }
 }
 
 func mustEnableIssuesOrPulls(ctx *context.Context) {
-       if !ctx.Repo.Repository.UnitEnabled(models.UnitTypeIssues) &&
-               !ctx.Repo.Repository.AllowsPulls() {
+       if !ctx.Repo.CanRead(models.UnitTypeIssues) &&
+               !(ctx.Repo.Repository.CanEnablePulls() && ctx.Repo.CanRead(models.UnitTypePullRequests)) {
                ctx.Status(404)
                return
        }
@@ -443,7 +478,8 @@ func RegisterRoutes(m *macaron.Macaron) {
                        m.Post("/migrate", reqToken(), bind(auth.MigrateRepoForm{}), repo.Migrate)
 
                        m.Group("/:username/:reponame", func() {
-                               m.Combo("").Get(repo.Get).Delete(reqToken(), repo.Delete)
+                               m.Combo("").Get(reqAnyRepoReader(), repo.Get).
+                                       Delete(reqToken(), reqOwner(), repo.Delete)
                                m.Group("/hooks", func() {
                                        m.Combo("").Get(repo.ListHooks).
                                                Post(bind(api.CreateHookOption{}), repo.CreateHook)
@@ -453,31 +489,30 @@ func RegisterRoutes(m *macaron.Macaron) {
                                                        Delete(repo.DeleteHook)
                                                m.Post("/tests", context.RepoRef(), repo.TestHook)
                                        })
-                               }, reqToken(), reqRepoWriter())
+                               }, reqToken(), reqAdmin())
                                m.Group("/collaborators", func() {
                                        m.Get("", repo.ListCollaborators)
                                        m.Combo("/:collaborator").Get(repo.IsCollaborator).
                                                Put(bind(api.AddCollaboratorOption{}), repo.AddCollaborator).
                                                Delete(repo.DeleteCollaborator)
-                               }, reqToken())
-                               m.Get("/raw/*", context.RepoRefByType(context.RepoRefAny), repo.GetRawFile)
-                               m.Get("/archive/*", repo.GetArchive)
+                               }, reqToken(), reqAdmin())
+                               m.Get("/raw/*", context.RepoRefByType(context.RepoRefAny), reqRepoReader(models.UnitTypeCode), repo.GetRawFile)
+                               m.Get("/archive/*", reqRepoReader(models.UnitTypeCode), repo.GetArchive)
                                m.Combo("/forks").Get(repo.ListForks).
-                                       Post(reqToken(), bind(api.CreateForkOption{}), repo.CreateFork)
+                                       Post(reqToken(), reqRepoReader(models.UnitTypeCode), bind(api.CreateForkOption{}), repo.CreateFork)
                                m.Group("/branches", func() {
                                        m.Get("", repo.ListBranches)
                                        m.Get("/*", context.RepoRefByType(context.RepoRefBranch), repo.GetBranch)
-                               })
+                               }, reqRepoReader(models.UnitTypeCode))
                                m.Group("/keys", func() {
                                        m.Combo("").Get(repo.ListDeployKeys).
                                                Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey)
                                        m.Combo("/:id").Get(repo.GetDeployKey).
                                                Delete(repo.DeleteDeploykey)
-                               }, reqToken(), reqRepoWriter())
+                               }, reqToken(), reqAdmin())
                                m.Group("/times", func() {
                                        m.Combo("").Get(repo.ListTrackedTimesByRepository)
                                        m.Combo("/:timetrackingusername").Get(repo.ListTrackedTimesByUser)
-
                                }, mustEnableIssues)
                                m.Group("/issues", func() {
                                        m.Combo("").Get(repo.ListIssues).
@@ -517,17 +552,17 @@ func RegisterRoutes(m *macaron.Macaron) {
                                }, mustEnableIssuesOrPulls)
                                m.Group("/labels", func() {
                                        m.Combo("").Get(repo.ListLabels).
-                                               Post(reqToken(), bind(api.CreateLabelOption{}), repo.CreateLabel)
+                                               Post(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.CreateLabelOption{}), repo.CreateLabel)
                                        m.Combo("/:id").Get(repo.GetLabel).
-                                               Patch(reqToken(), bind(api.EditLabelOption{}), repo.EditLabel).
-                                               Delete(reqToken(), repo.DeleteLabel)
+                                               Patch(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.EditLabelOption{}), repo.EditLabel).
+                                               Delete(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), repo.DeleteLabel)
                                })
                                m.Group("/milestones", func() {
                                        m.Combo("").Get(repo.ListMilestones).
-                                               Post(reqToken(), reqRepoWriter(), bind(api.CreateMilestoneOption{}), repo.CreateMilestone)
+                                               Post(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.CreateMilestoneOption{}), repo.CreateMilestone)
                                        m.Combo("/:id").Get(repo.GetMilestone).
-                                               Patch(reqToken(), reqRepoWriter(), bind(api.EditMilestoneOption{}), repo.EditMilestone).
-                                               Delete(reqToken(), reqRepoWriter(), repo.DeleteMilestone)
+                                               Patch(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone).
+                                               Delete(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), repo.DeleteMilestone)
                                })
                                m.Get("/stargazers", repo.ListStargazers)
                                m.Get("/subscribers", repo.ListSubscribers)
@@ -538,45 +573,44 @@ func RegisterRoutes(m *macaron.Macaron) {
                                })
                                m.Group("/releases", func() {
                                        m.Combo("").Get(repo.ListReleases).
-                                               Post(reqToken(), reqRepoWriter(), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease)
+                                               Post(reqToken(), reqRepoWriter(models.UnitTypeReleases), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease)
                                        m.Group("/:id", func() {
                                                m.Combo("").Get(repo.GetRelease).
-                                                       Patch(reqToken(), reqRepoWriter(), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease).
-                                                       Delete(reqToken(), reqRepoWriter(), repo.DeleteRelease)
+                                                       Patch(reqToken(), reqRepoWriter(models.UnitTypeReleases), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease).
+                                                       Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteRelease)
                                                m.Group("/assets", func() {
                                                        m.Combo("").Get(repo.ListReleaseAttachments).
-                                                               Post(reqToken(), reqRepoWriter(), repo.CreateReleaseAttachment)
+                                                               Post(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.CreateReleaseAttachment)
                                                        m.Combo("/:asset").Get(repo.GetReleaseAttachment).
-                                                               Patch(reqToken(), reqRepoWriter(), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment).
-                                                               Delete(reqToken(), reqRepoWriter(), repo.DeleteReleaseAttachment)
+                                                               Patch(reqToken(), reqRepoWriter(models.UnitTypeReleases), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment).
+                                                               Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteReleaseAttachment)
                                                })
                                        })
-                               })
-                               m.Post("/mirror-sync", reqToken(), reqRepoWriter(), repo.MirrorSync)
-                               m.Get("/editorconfig/:filename", context.RepoRef(), repo.GetEditorconfig)
+                               }, reqRepoReader(models.UnitTypeReleases))
+                               m.Post("/mirror-sync", reqToken(), reqRepoWriter(models.UnitTypeCode), repo.MirrorSync)
+                               m.Get("/editorconfig/:filename", context.RepoRef(), reqRepoReader(models.UnitTypeCode), repo.GetEditorconfig)
                                m.Group("/pulls", func() {
                                        m.Combo("").Get(bind(api.ListPullRequestsOptions{}), repo.ListPullRequests).
-                                               Post(reqToken(), reqRepoWriter(), bind(api.CreatePullRequestOption{}), repo.CreatePullRequest)
+                                               Post(reqToken(), bind(api.CreatePullRequestOption{}), repo.CreatePullRequest)
                                        m.Group("/:index", func() {
                                                m.Combo("").Get(repo.GetPullRequest).
-                                                       Patch(reqToken(), reqRepoWriter(), bind(api.EditPullRequestOption{}), repo.EditPullRequest)
+                                                       Patch(reqToken(), reqRepoWriter(models.UnitTypePullRequests), bind(api.EditPullRequestOption{}), repo.EditPullRequest)
                                                m.Combo("/merge").Get(repo.IsPullRequestMerged).
-                                                       Post(reqToken(), reqRepoWriter(), bind(auth.MergePullRequestForm{}), repo.MergePullRequest)
+                                                       Post(reqToken(), reqRepoWriter(models.UnitTypePullRequests), bind(auth.MergePullRequestForm{}), repo.MergePullRequest)
                                        })
-
-                               }, mustAllowPulls, context.ReferencesGitRepo())
+                               }, mustAllowPulls, reqRepoReader(models.UnitTypeCode), context.ReferencesGitRepo())
                                m.Group("/statuses", func() {
                                        m.Combo("/:sha").Get(repo.GetCommitStatuses).
-                                               Post(reqToken(), reqRepoWriter(), bind(api.CreateStatusOption{}), repo.NewCommitStatus)
-                               })
+                                               Post(reqToken(), bind(api.CreateStatusOption{}), repo.NewCommitStatus)
+                               }, reqRepoReader(models.UnitTypeCode))
                                m.Group("/commits/:ref", func() {
                                        m.Get("/status", repo.GetCombinedCommitStatusByRef)
                                        m.Get("/statuses", repo.GetCommitStatusesByRef)
-                               })
+                               }, reqRepoReader(models.UnitTypeCode))
                                m.Group("/git", func() {
                                        m.Get("/refs", repo.GetGitAllRefs)
                                        m.Get("/refs/*", repo.GetGitRefs)
-                               })
+                               }, reqRepoReader(models.UnitTypeCode))
                        }, repoAssignment())
                })
 
@@ -645,7 +679,7 @@ func RegisterRoutes(m *macaron.Macaron) {
                                        m.Post("/repos", bind(api.CreateRepoOption{}), admin.CreateRepo)
                                })
                        })
-               }, reqToken(), reqAdmin())
+               }, reqToken(), reqSiteAdmin())
 
                m.Group("/topics", func() {
                        m.Get("/search", repo.TopicSearch)
index 8b67eda42fdf5d813eb6096a3c5a3f260b9e3424..a22d25eae3018f88b9d40555c415f836b037820e 100644 (file)
@@ -311,7 +311,7 @@ func GetTeamRepos(ctx *context.APIContext) {
        }
        repos := make([]*api.Repository, len(team.Repos))
        for i, repo := range team.Repos {
-               access, err := models.AccessLevel(ctx.User.ID, repo)
+               access, err := models.AccessLevel(ctx.User, repo)
                if err != nil {
                        ctx.Error(500, "GetTeamRepos", err)
                        return
@@ -366,7 +366,7 @@ func AddTeamRepository(ctx *context.APIContext) {
        if ctx.Written() {
                return
        }
-       if access, err := models.AccessLevel(ctx.User.ID, repo); err != nil {
+       if access, err := models.AccessLevel(ctx.User, repo); err != nil {
                ctx.Error(500, "AccessLevel", err)
                return
        } else if access < models.AccessModeAdmin {
@@ -413,7 +413,7 @@ func RemoveTeamRepository(ctx *context.APIContext) {
        if ctx.Written() {
                return
        }
-       if access, err := models.AccessLevel(ctx.User.ID, repo); err != nil {
+       if access, err := models.AccessLevel(ctx.User, repo); err != nil {
                ctx.Error(500, "AccessLevel", err)
                return
        } else if access < models.AccessModeAdmin {
index c52472b0f87244a0764e4947c38152e2157ecdeb..2f254f71033de7854623d250e465815aaa12abe7 100644 (file)
@@ -1,4 +1,5 @@
 // Copyright 2016 The Gogs Authors. All rights reserved.
+// Copyright 2018 The Gitea Authors. All rights reserved.
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
@@ -34,10 +35,6 @@ func ListCollaborators(ctx *context.APIContext) {
        // responses:
        //   "200":
        //     "$ref": "#/responses/UserList"
-       if !ctx.Repo.IsWriter() {
-               ctx.Error(403, "", "User does not have push access")
-               return
-       }
        collaborators, err := ctx.Repo.Repository.GetCollaborators()
        if err != nil {
                ctx.Error(500, "ListCollaborators", err)
@@ -78,10 +75,6 @@ func IsCollaborator(ctx *context.APIContext) {
        //     "$ref": "#/responses/empty"
        //   "404":
        //     "$ref": "#/responses/empty"
-       if !ctx.Repo.IsWriter() {
-               ctx.Error(403, "", "User does not have push access")
-               return
-       }
        user, err := models.GetUserByName(ctx.Params(":collaborator"))
        if err != nil {
                if models.IsErrUserNotExist(err) {
@@ -133,10 +126,6 @@ func AddCollaborator(ctx *context.APIContext, form api.AddCollaboratorOption) {
        // responses:
        //   "204":
        //     "$ref": "#/responses/empty"
-       if !ctx.Repo.IsWriter() {
-               ctx.Error(403, "", "User does not have push access")
-               return
-       }
        collaborator, err := models.GetUserByName(ctx.Params(":collaborator"))
        if err != nil {
                if models.IsErrUserNotExist(err) {
@@ -193,11 +182,6 @@ func DeleteCollaborator(ctx *context.APIContext) {
        // responses:
        //   "204":
        //     "$ref": "#/responses/empty"
-       if !ctx.Repo.IsWriter() {
-               ctx.Error(403, "", "User does not have push access")
-               return
-       }
-
        collaborator, err := models.GetUserByName(ctx.Params(":collaborator"))
        if err != nil {
                if models.IsErrUserNotExist(err) {
index 610247bc27af556676cb882ae1b388451c3b6b58..81f2332089d95b0cfd0a197882fd4c410a0575f4 100644 (file)
@@ -1,4 +1,5 @@
 // Copyright 2014 The Gogs Authors. All rights reserved.
+// Copyright 2018 The Gitea Authors. All rights reserved.
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
@@ -38,11 +39,6 @@ func GetRawFile(ctx *context.APIContext) {
        // responses:
        //   200:
        //     description: success
-       if !ctx.Repo.HasAccess() {
-               ctx.Status(404)
-               return
-       }
-
        if ctx.Repo.Repository.IsBare {
                ctx.Status(404)
                return
index 843559d523bd610dadede4af31e51d22d265a835..d10f668712ab25ce587e0db06cd2227ea5a47467 100644 (file)
@@ -7,7 +7,6 @@ package repo
 import (
        "code.gitea.io/gitea/models"
        "code.gitea.io/gitea/modules/context"
-       "code.gitea.io/gitea/routers/api/v1/utils"
 
        api "code.gitea.io/sdk/gitea"
 )
@@ -40,7 +39,7 @@ func ListForks(ctx *context.APIContext) {
        }
        apiForks := make([]*api.Repository, len(forks))
        for i, fork := range forks {
-               access, err := models.AccessLevel(utils.UserID(ctx), fork)
+               access, err := models.AccessLevel(ctx.User, fork)
                if err != nil {
                        ctx.Error(500, "AccessLevel", err)
                        return
index cd78135a6216085916b49bf6d025ca11e97c49c4..7a8ed09b4864704f210d8a4daa748cfc801f07bc 100644 (file)
@@ -169,7 +169,7 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
        //     "$ref": "#/responses/Issue"
 
        var deadlineUnix util.TimeStamp
-       if form.Deadline != nil && ctx.Repo.IsWriter() {
+       if form.Deadline != nil && ctx.Repo.CanWrite(models.UnitTypeIssues) {
                deadlineUnix = util.TimeStamp(form.Deadline.Unix())
        }
 
@@ -184,7 +184,7 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
 
        var assigneeIDs = make([]int64, 0)
        var err error
-       if ctx.Repo.IsWriter() {
+       if ctx.Repo.CanWrite(models.UnitTypeIssues) {
                issue.MilestoneID = form.Milestone
                assigneeIDs, err = models.MakeIDsFromAPIAssigneesToAdd(form.Assignee, form.Assignees)
                if err != nil {
@@ -274,7 +274,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
                return
        }
 
-       if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.IsWriter() {
+       if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.CanWrite(models.UnitTypeIssues) {
                ctx.Status(403)
                return
        }
@@ -288,7 +288,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
 
        // Update the deadline
        var deadlineUnix util.TimeStamp
-       if form.Deadline != nil && !form.Deadline.IsZero() && ctx.Repo.IsWriter() {
+       if form.Deadline != nil && !form.Deadline.IsZero() && ctx.Repo.CanWrite(models.UnitTypeIssues) {
                deadlineUnix = util.TimeStamp(form.Deadline.Unix())
        }
 
@@ -305,8 +305,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
        // Pass one or more user logins to replace the set of assignees on this Issue.
        // Send an empty array ([]) to clear all assignees from the Issue.
 
-       if ctx.Repo.IsWriter() && (form.Assignees != nil || form.Assignee != nil) {
-
+       if ctx.Repo.CanWrite(models.UnitTypeIssues) && (form.Assignees != nil || form.Assignee != nil) {
                oneAssignee := ""
                if form.Assignee != nil {
                        oneAssignee = *form.Assignee
@@ -319,7 +318,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
                }
        }
 
-       if ctx.Repo.IsWriter() && form.Milestone != nil &&
+       if ctx.Repo.CanWrite(models.UnitTypeIssues) && form.Milestone != nil &&
                issue.MilestoneID != *form.Milestone {
                oldMilestoneID := issue.MilestoneID
                issue.MilestoneID = *form.Milestone
@@ -403,7 +402,7 @@ func UpdateIssueDeadline(ctx *context.APIContext, form api.EditDeadlineOption) {
                return
        }
 
-       if !ctx.Repo.IsWriter() {
+       if !ctx.Repo.CanWrite(models.UnitTypeIssues) {
                ctx.Status(403)
                return
        }
index 35defa25b57d1c546f3a4246849f43f7c88e7e64..715dd0ed771fcbfb360cbee10c096534c2c13089 100644 (file)
@@ -1,4 +1,5 @@
 // Copyright 2016 The Gogs Authors. All rights reserved.
+// Copyright 2018 The Gitea Authors. All rights reserved.
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
@@ -90,11 +91,6 @@ func AddIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
        // responses:
        //   "200":
        //     "$ref": "#/responses/LabelList"
-       if !ctx.Repo.IsWriter() {
-               ctx.Status(403)
-               return
-       }
-
        issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
        if err != nil {
                if models.IsErrIssueNotExist(err) {
@@ -105,6 +101,11 @@ func AddIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
                return
        }
 
+       if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
+               ctx.Status(403)
+               return
+       }
+
        labels, err := models.GetLabelsInRepoByIDs(ctx.Repo.Repository.ID, form.Labels)
        if err != nil {
                ctx.Error(500, "GetLabelsInRepoByIDs", err)
@@ -162,11 +163,6 @@ func DeleteIssueLabel(ctx *context.APIContext) {
        // responses:
        //   "204":
        //     "$ref": "#/responses/empty"
-       if !ctx.Repo.IsWriter() {
-               ctx.Status(403)
-               return
-       }
-
        issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
        if err != nil {
                if models.IsErrIssueNotExist(err) {
@@ -177,6 +173,11 @@ func DeleteIssueLabel(ctx *context.APIContext) {
                return
        }
 
+       if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
+               ctx.Status(403)
+               return
+       }
+
        label, err := models.GetLabelInRepoByID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
        if err != nil {
                if models.IsErrLabelNotExist(err) {
@@ -228,11 +229,6 @@ func ReplaceIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
        // responses:
        //   "200":
        //     "$ref": "#/responses/LabelList"
-       if !ctx.Repo.IsWriter() {
-               ctx.Status(403)
-               return
-       }
-
        issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
        if err != nil {
                if models.IsErrIssueNotExist(err) {
@@ -243,6 +239,11 @@ func ReplaceIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
                return
        }
 
+       if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
+               ctx.Status(403)
+               return
+       }
+
        labels, err := models.GetLabelsInRepoByIDs(ctx.Repo.Repository.ID, form.Labels)
        if err != nil {
                ctx.Error(500, "GetLabelsInRepoByIDs", err)
@@ -294,11 +295,6 @@ func ClearIssueLabels(ctx *context.APIContext) {
        // responses:
        //   "204":
        //     "$ref": "#/responses/empty"
-       if !ctx.Repo.IsWriter() {
-               ctx.Status(403)
-               return
-       }
-
        issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
        if err != nil {
                if models.IsErrIssueNotExist(err) {
@@ -309,6 +305,11 @@ func ClearIssueLabels(ctx *context.APIContext) {
                return
        }
 
+       if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
+               ctx.Status(403)
+               return
+       }
+
        if err := issue.ClearLabels(ctx.User); err != nil {
                ctx.Error(500, "ClearLabels", err)
                return
index 359ccf0e41eca0a56ed2217354ee052c4f7d8001..df7a37004191e317d654cec8b7ce72d8bb2bac19 100644 (file)
@@ -1,4 +1,5 @@
 // Copyright 2016 The Gogs Authors. All rights reserved.
+// Copyright 2018 The Gitea Authors. All rights reserved.
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
@@ -123,11 +124,6 @@ func CreateLabel(ctx *context.APIContext, form api.CreateLabelOption) {
        // responses:
        //   "201":
        //     "$ref": "#/responses/Label"
-       if !ctx.Repo.IsWriter() {
-               ctx.Status(403)
-               return
-       }
-
        label := &models.Label{
                Name:   form.Name,
                Color:  form.Color,
@@ -173,11 +169,6 @@ func EditLabel(ctx *context.APIContext, form api.EditLabelOption) {
        // responses:
        //   "200":
        //     "$ref": "#/responses/Label"
-       if !ctx.Repo.IsWriter() {
-               ctx.Status(403)
-               return
-       }
-
        label, err := models.GetLabelInRepoByID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
        if err != nil {
                if models.IsErrLabelNotExist(err) {
@@ -226,11 +217,6 @@ func DeleteLabel(ctx *context.APIContext) {
        // responses:
        //   "204":
        //     "$ref": "#/responses/empty"
-       if !ctx.Repo.IsWriter() {
-               ctx.Status(403)
-               return
-       }
-
        if err := models.DeleteLabel(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")); err != nil {
                ctx.Error(500, "DeleteLabel", err)
                return
index 83ef886d7d319dfef0fe6731e5cb19b7e337cc50..a0117794ad54f4599ac19e51e59e7e70cfbf27b8 100644 (file)
@@ -351,7 +351,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
        pr.LoadIssue()
        issue := pr.Issue
 
-       if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.IsWriter() {
+       if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.CanWrite(models.UnitTypePullRequests) {
                ctx.Status(403)
                return
        }
@@ -382,7 +382,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
        // Pass one or more user logins to replace the set of assignees on this Issue.
        // Send an empty array ([]) to clear all assignees from the Issue.
 
-       if ctx.Repo.IsWriter() && (form.Assignees != nil || len(form.Assignee) > 0) {
+       if ctx.Repo.CanWrite(models.UnitTypePullRequests) && (form.Assignees != nil || len(form.Assignee) > 0) {
 
                err = models.UpdateAPIAssignee(issue, form.Assignee, form.Assignees, ctx.User)
                if err != nil {
@@ -395,7 +395,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
                }
        }
 
-       if ctx.Repo.IsWriter() && form.Milestone != 0 &&
+       if ctx.Repo.CanWrite(models.UnitTypePullRequests) && form.Milestone != 0 &&
                issue.MilestoneID != form.Milestone {
                oldMilestoneID := issue.MilestoneID
                issue.MilestoneID = form.Milestone
@@ -405,7 +405,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
                }
        }
 
-       if ctx.Repo.IsWriter() && (form.Labels != nil && len(form.Labels) > 0) {
+       if ctx.Repo.CanWrite(models.UnitTypePullRequests) && (form.Labels != nil && len(form.Labels) > 0) {
                labels, err := models.GetLabelsInRepoByIDs(ctx.Repo.Repository.ID, form.Labels)
                if err != nil {
                        ctx.Error(500, "GetLabelsInRepoByIDsError", err)
@@ -663,7 +663,12 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
                }
        }
 
-       if !ctx.User.IsWriterOfRepo(headRepo) && !ctx.User.IsAdmin {
+       perm, err := models.GetUserRepoPermission(headRepo, ctx.User)
+       if err != nil {
+               ctx.ServerError("GetUserRepoPermission", err)
+               return nil, nil, nil, nil, "", ""
+       }
+       if !perm.CanWrite(models.UnitTypeCode) {
                log.Trace("ParseCompareInfo[%d]: does not have write access or site admin", baseRepo.ID)
                ctx.Status(404)
                return nil, nil, nil, nil, "", ""
index 282ff582c509f5179803866dfce80014c1509e99..caa2905e93076df95a8719df10fa48924238feb1 100644 (file)
@@ -122,10 +122,6 @@ func CreateRelease(ctx *context.APIContext, form api.CreateReleaseOption) {
        // responses:
        //   "201":
        //     "$ref": "#/responses/Release"
-       if ctx.Repo.AccessMode < models.AccessModeWrite {
-               ctx.Status(403)
-               return
-       }
        rel, err := models.GetRelease(ctx.Repo.Repository.ID, form.TagName)
        if err != nil {
                if !models.IsErrReleaseNotExist(err) {
@@ -209,10 +205,6 @@ func EditRelease(ctx *context.APIContext, form api.EditReleaseOption) {
        // responses:
        //   "200":
        //     "$ref": "#/responses/Release"
-       if ctx.Repo.AccessMode < models.AccessModeWrite {
-               ctx.Status(403)
-               return
-       }
        id := ctx.ParamsInt64(":id")
        rel, err := models.GetReleaseByID(id)
        if err != nil && !models.IsErrReleaseNotExist(err) {
@@ -285,10 +277,6 @@ func DeleteRelease(ctx *context.APIContext) {
        // responses:
        //   "204":
        //     "$ref": "#/responses/empty"
-       if ctx.Repo.AccessMode < models.AccessModeWrite {
-               ctx.Status(403)
-               return
-       }
        id := ctx.ParamsInt64(":id")
        rel, err := models.GetReleaseByID(id)
        if err != nil && !models.IsErrReleaseNotExist(err) {
index bf6346eebdfcadef3a7d8332e8855ea0758f9e5a..6d8125a77fafb640d70fecf61ae2979c323782fe 100644 (file)
@@ -1,4 +1,5 @@
 // Copyright 2014 The Gogs Authors. All rights reserved.
+// Copyright 2018 The Gitea Authors. All rights reserved.
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
@@ -183,11 +184,6 @@ func Search(ctx *context.APIContext) {
                return
        }
 
-       var userID int64
-       if ctx.IsSigned {
-               userID = ctx.User.ID
-       }
-
        results := make([]*api.Repository, len(repos))
        for i, repo := range repos {
                if err = repo.GetOwner(); err != nil {
@@ -197,7 +193,7 @@ func Search(ctx *context.APIContext) {
                        })
                        return
                }
-               accessMode, err := models.AccessLevel(userID, repo)
+               accessMode, err := models.AccessLevel(ctx.User, repo)
                if err != nil {
                        ctx.JSON(500, api.SearchError{
                                OK:    false,
@@ -469,15 +465,15 @@ func GetByID(ctx *context.APIContext) {
                return
        }
 
-       access, err := models.AccessLevel(ctx.User.ID, repo)
+       perm, err := models.GetUserRepoPermission(repo, ctx.User)
        if err != nil {
                ctx.Error(500, "AccessLevel", err)
                return
-       } else if access < models.AccessModeRead {
+       } else if !perm.HasAccess() {
                ctx.Status(404)
                return
        }
-       ctx.JSON(200, repo.APIFormat(access))
+       ctx.JSON(200, repo.APIFormat(perm.AccessMode))
 }
 
 // Delete one repository
@@ -503,10 +499,6 @@ func Delete(ctx *context.APIContext) {
        //     "$ref": "#/responses/empty"
        //   "403":
        //     "$ref": "#/responses/forbidden"
-       if !ctx.Repo.IsAdmin() {
-               ctx.Error(403, "", "Must have admin rights")
-               return
-       }
        owner := ctx.Repo.Owner
        repo := ctx.Repo.Repository
 
@@ -553,7 +545,7 @@ func MirrorSync(ctx *context.APIContext) {
        //     "$ref": "#/responses/empty"
        repo := ctx.Repo.Repository
 
-       if !ctx.Repo.IsWriter() {
+       if !ctx.Repo.CanWrite(models.UnitTypeCode) {
                ctx.Error(403, "MirrorSync", "Must have write access")
        }
 
index 5dccfac96090766d57de91fe1917097998dad39b..1ddb3bd57bafffac89cf8ef747e1aafed70333ed 100644 (file)
@@ -17,13 +17,10 @@ func listUserRepos(ctx *context.APIContext, u *models.User, private bool) {
                ctx.Error(500, "GetUserRepositories", err)
                return
        }
+
        apiRepos := make([]*api.Repository, 0, len(repos))
-       var ctxUserID int64
-       if ctx.User != nil {
-               ctxUserID = ctx.User.ID
-       }
        for i := range repos {
-               access, err := models.AccessLevel(ctxUserID, repos[i])
+               access, err := models.AccessLevel(ctx.User, repos[i])
                if err != nil {
                        ctx.Error(500, "AccessLevel", err)
                        return
index 1cf4f5239cc594f6cc610120f42f2dee2e528b45..b0016399c8db622143474d70c80dc7189a76fda2 100644 (file)
@@ -13,15 +13,15 @@ import (
 
 // getStarredRepos returns the repos that the user with the specified userID has
 // starred
-func getStarredRepos(userID int64, private bool) ([]*api.Repository, error) {
-       starredRepos, err := models.GetStarredRepos(userID, private)
+func getStarredRepos(user *models.User, private bool) ([]*api.Repository, error) {
+       starredRepos, err := models.GetStarredRepos(user.ID, private)
        if err != nil {
                return nil, err
        }
 
        repos := make([]*api.Repository, len(starredRepos))
        for i, starred := range starredRepos {
-               access, err := models.AccessLevel(userID, starred)
+               access, err := models.AccessLevel(user, starred)
                if err != nil {
                        return nil, err
                }
@@ -48,7 +48,7 @@ func GetStarredRepos(ctx *context.APIContext) {
        //     "$ref": "#/responses/RepositoryList"
        user := GetUserByParams(ctx)
        private := user.ID == ctx.User.ID
-       repos, err := getStarredRepos(user.ID, private)
+       repos, err := getStarredRepos(user, private)
        if err != nil {
                ctx.Error(500, "getStarredRepos", err)
        }
@@ -65,7 +65,7 @@ func GetMyStarredRepos(ctx *context.APIContext) {
        // responses:
        //   "200":
        //     "$ref": "#/responses/RepositoryList"
-       repos, err := getStarredRepos(ctx.User.ID, true)
+       repos, err := getStarredRepos(ctx.User, true)
        if err != nil {
                ctx.Error(500, "getStarredRepos", err)
        }
index 2971bf6869c8d09d0712221d0fbd606daec8b616..4afa18be2a75d9ae9d2f8c4a973a8c497cd9dc61 100644 (file)
@@ -14,15 +14,15 @@ import (
 
 // getWatchedRepos returns the repos that the user with the specified userID is
 // watching
-func getWatchedRepos(userID int64, private bool) ([]*api.Repository, error) {
-       watchedRepos, err := models.GetWatchedRepos(userID, private)
+func getWatchedRepos(user *models.User, private bool) ([]*api.Repository, error) {
+       watchedRepos, err := models.GetWatchedRepos(user.ID, private)
        if err != nil {
                return nil, err
        }
 
        repos := make([]*api.Repository, len(watchedRepos))
        for i, watched := range watchedRepos {
-               access, err := models.AccessLevel(userID, watched)
+               access, err := models.AccessLevel(user, watched)
                if err != nil {
                        return nil, err
                }
@@ -49,7 +49,7 @@ func GetWatchedRepos(ctx *context.APIContext) {
        //     "$ref": "#/responses/RepositoryList"
        user := GetUserByParams(ctx)
        private := user.ID == ctx.User.ID
-       repos, err := getWatchedRepos(user.ID, private)
+       repos, err := getWatchedRepos(user, private)
        if err != nil {
                ctx.Error(500, "getWatchedRepos", err)
        }
@@ -66,7 +66,7 @@ func GetMyWatchedRepos(ctx *context.APIContext) {
        // responses:
        //   "200":
        //     "$ref": "#/responses/RepositoryList"
-       repos, err := getWatchedRepos(ctx.User.ID, true)
+       repos, err := getWatchedRepos(ctx.User, true)
        if err != nil {
                ctx.Error(500, "getWatchedRepos", err)
        }
index 23e0122642b37f97b7090cdc4bca13b52e42847f..0221b1fee86a897f0b6fbabbf2209c2fe8a7d526 100644 (file)
@@ -38,8 +38,8 @@ func GetRepositoryByOwnerAndName(ctx *macaron.Context) {
        ctx.JSON(200, repo)
 }
 
-//AccessLevel chainload to models.AccessLevel
-func AccessLevel(ctx *macaron.Context) {
+//CheckUnitUser chainload to models.CheckUnitUser
+func CheckUnitUser(ctx *macaron.Context) {
        repoID := ctx.ParamsInt64(":repoid")
        userID := ctx.ParamsInt64(":userid")
        repo, err := models.GetRepositoryByID(repoID)
@@ -49,32 +49,27 @@ func AccessLevel(ctx *macaron.Context) {
                })
                return
        }
-       al, err := models.AccessLevel(userID, repo)
-       if err != nil {
-               ctx.JSON(500, map[string]interface{}{
-                       "err": err.Error(),
-               })
-               return
+
+       var user *models.User
+       if userID > 0 {
+               user, err = models.GetUserByID(userID)
+               if err != nil {
+                       ctx.JSON(500, map[string]interface{}{
+                               "err": err.Error(),
+                       })
+                       return
+               }
        }
-       ctx.JSON(200, al)
-}
 
-//CheckUnitUser chainload to models.CheckUnitUser
-func CheckUnitUser(ctx *macaron.Context) {
-       repoID := ctx.ParamsInt64(":repoid")
-       userID := ctx.ParamsInt64(":userid")
-       repo, err := models.GetRepositoryByID(repoID)
+       perm, err := models.GetUserRepoPermission(repo, user)
        if err != nil {
                ctx.JSON(500, map[string]interface{}{
                        "err": err.Error(),
                })
                return
        }
-       if repo.CheckUnitUser(userID, ctx.QueryBool("isAdmin"), models.UnitType(ctx.QueryInt("unitType"))) {
-               ctx.PlainText(200, []byte("success"))
-               return
-       }
-       ctx.PlainText(404, []byte("no access"))
+
+       ctx.JSON(200, perm.UnitAccessMode(models.UnitType(ctx.QueryInt("unitType"))))
 }
 
 // RegisterRoutes registers all internal APIs routes to web application.
@@ -85,7 +80,6 @@ func RegisterRoutes(m *macaron.Macaron) {
                m.Get("/ssh/:id/user", GetUserByKeyID)
                m.Post("/ssh/:id/update", UpdatePublicKey)
                m.Post("/repositories/:repoid/keys/:keyid/update", UpdateDeployKey)
-               m.Get("/repositories/:repoid/user/:userid/accesslevel", AccessLevel)
                m.Get("/repositories/:repoid/user/:userid/checkunituser", CheckUnitUser)
                m.Get("/repositories/:repoid/has-keys/:keyid", HasDeployKey)
                m.Post("/push/update", PushUpdate)
index a0a25dc936b2a68dbac18f12efdb8068a16d0cbf..5d90d73506db28a9ed43f4968ebdffc63b047623 100644 (file)
@@ -45,9 +45,9 @@ func Activity(ctx *context.Context) {
 
        var err error
        if ctx.Data["Activity"], err = models.GetActivityStats(ctx.Repo.Repository.ID, timeFrom,
-               ctx.Repo.Repository.UnitEnabled(models.UnitTypeReleases),
-               ctx.Repo.Repository.UnitEnabled(models.UnitTypeIssues),
-               ctx.Repo.Repository.UnitEnabled(models.UnitTypePullRequests)); err != nil {
+               ctx.Repo.CanRead(models.UnitTypeReleases),
+               ctx.Repo.CanRead(models.UnitTypeIssues),
+               ctx.Repo.CanRead(models.UnitTypePullRequests)); err != nil {
                ctx.ServerError("GetActivityStats", err)
                return
        }
index 7b1e2a8bbe7bcc400b444d72c388a15fa1bc0faa..295aeaa24ba604b36b69f1c1b9d001d6387bc46c 100644 (file)
@@ -1,4 +1,5 @@
 // Copyright 2014 The Gogs Authors. All rights reserved.
+// Copyright 2018 The Gitea Authors. All rights reserved.
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
@@ -33,7 +34,7 @@ func Branches(ctx *context.Context) {
        ctx.Data["Title"] = "Branches"
        ctx.Data["IsRepoToolbarBranches"] = true
        ctx.Data["DefaultBranch"] = ctx.Repo.Repository.DefaultBranch
-       ctx.Data["IsWriter"] = ctx.Repo.IsWriter()
+       ctx.Data["IsWriter"] = ctx.Repo.CanWrite(models.UnitTypeCode)
        ctx.Data["IsMirror"] = ctx.Repo.Repository.IsMirror
        ctx.Data["PageIsViewCode"] = true
        ctx.Data["PageIsBranches"] = true
@@ -161,7 +162,7 @@ func loadBranches(ctx *context.Context) []*Branch {
                }
        }
 
-       if ctx.Repo.IsWriter() {
+       if ctx.Repo.CanWrite(models.UnitTypeCode) {
                deletedBranches, err := getDeletedBranches(ctx)
                if err != nil {
                        ctx.ServerError("getDeletedBranches", err)
index 5b469754adf52edf7927da5c54ca499fbeef4f9d..ec5fbe6c0d33652a846bbeadd013b71cb5b89b9d 100644 (file)
@@ -182,36 +182,19 @@ func HTTP(ctx *context.Context) {
                        }
                }
 
-               if !isPublicPull {
-                       has, err := models.HasAccess(authUser.ID, repo, accessMode)
-                       if err != nil {
-                               ctx.ServerError("HasAccess", err)
-                               return
-                       } else if !has {
-                               if accessMode == models.AccessModeRead {
-                                       has, err = models.HasAccess(authUser.ID, repo, models.AccessModeWrite)
-                                       if err != nil {
-                                               ctx.ServerError("HasAccess2", err)
-                                               return
-                                       } else if !has {
-                                               ctx.HandleText(http.StatusForbidden, "User permission denied")
-                                               return
-                                       }
-                               } else {
-                                       ctx.HandleText(http.StatusForbidden, "User permission denied")
-                                       return
-                               }
-                       }
+               perm, err := models.GetUserRepoPermission(repo, authUser)
+               if err != nil {
+                       ctx.ServerError("GetUserRepoPermission", err)
+                       return
+               }
 
-                       if !isPull && repo.IsMirror {
-                               ctx.HandleText(http.StatusForbidden, "mirror repository is read-only")
-                               return
-                       }
+               if !perm.CanAccess(accessMode, unitType) {
+                       ctx.HandleText(http.StatusForbidden, "User permission denied")
+                       return
                }
 
-               if !repo.CheckUnitUser(authUser.ID, authUser.IsAdmin, unitType) {
-                       ctx.HandleText(http.StatusForbidden, fmt.Sprintf("User %s does not have allowed access to repository %s 's code",
-                               authUser.Name, repo.RepoPath()))
+               if !isPull && repo.IsMirror {
+                       ctx.HandleText(http.StatusForbidden, "mirror repository is read-only")
                        return
                }
 
index 08bdf311f912c6794a0f53cba1eb5f5e5b40ca12..8d95f3394086bec2ce9c9280d69a5e418f6c0572 100644 (file)
@@ -1,4 +1,5 @@
 // Copyright 2014 The Gogs Authors. All rights reserved.
+// Copyright 2018 The Gitea Authors. All rights reserved.
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
@@ -63,8 +64,8 @@ var (
 
 // MustEnableIssues check if repository enable internal issues
 func MustEnableIssues(ctx *context.Context) {
-       if !ctx.Repo.Repository.UnitEnabled(models.UnitTypeIssues) &&
-               !ctx.Repo.Repository.UnitEnabled(models.UnitTypeExternalTracker) {
+       if !ctx.Repo.CanRead(models.UnitTypeIssues) &&
+               !ctx.Repo.CanRead(models.UnitTypeExternalTracker) {
                ctx.NotFound("MustEnableIssues", nil)
                return
        }
@@ -76,9 +77,9 @@ func MustEnableIssues(ctx *context.Context) {
        }
 }
 
-// MustAllowPulls check if repository enable pull requests
+// MustAllowPulls check if repository enable pull requests and user have right to do that
 func MustAllowPulls(ctx *context.Context) {
-       if !ctx.Repo.Repository.AllowsPulls() {
+       if !ctx.Repo.Repository.CanEnablePulls() || !ctx.Repo.CanRead(models.UnitTypePullRequests) {
                ctx.NotFound("MustAllowPulls", nil)
                return
        }
@@ -280,7 +281,7 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *models.Repos
 
 // RetrieveRepoMetas find all the meta information of a repository
 func RetrieveRepoMetas(ctx *context.Context, repo *models.Repository) []*models.Label {
-       if !ctx.Repo.IsWriter() {
+       if !ctx.Repo.CanWrite(models.UnitTypeIssues) {
                return nil
        }
 
@@ -369,7 +370,7 @@ func NewIssue(ctx *context.Context) {
 }
 
 // ValidateRepoMetas check and returns repository's meta informations
-func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64, []int64, int64) {
+func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm, isPull bool) ([]int64, []int64, int64) {
        var (
                repo = ctx.Repo.Repository
                err  error
@@ -380,10 +381,6 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64
                return nil, nil, 0
        }
 
-       if !ctx.Repo.IsWriter() {
-               return nil, nil, 0
-       }
-
        var labelIDs []int64
        hasSelected := false
        // Check labels.
@@ -427,9 +424,19 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64
 
                // Check if the passed assignees actually exists and has write access to the repo
                for _, aID := range assigneeIDs {
-                       _, err = repo.GetUserIfHasWriteAccess(aID)
+                       user, err := models.GetUserByID(aID)
+                       if err != nil {
+                               ctx.ServerError("GetUserByID", err)
+                               return nil, nil, 0
+                       }
+
+                       perm, err := models.GetUserRepoPermission(repo, user)
                        if err != nil {
-                               ctx.ServerError("GetUserIfHasWriteAccess", err)
+                               ctx.ServerError("GetUserRepoPermission", err)
+                               return nil, nil, 0
+                       }
+                       if !perm.CanWriteIssuesOrPulls(isPull) {
+                               ctx.ServerError("CanWriteIssuesOrPulls", fmt.Errorf("No permission for %s", user.Name))
                                return nil, nil, 0
                        }
                }
@@ -458,7 +465,7 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) {
                attachments []string
        )
 
-       labelIDs, assigneeIDs, milestoneID := ValidateRepoMetas(ctx, form)
+       labelIDs, assigneeIDs, milestoneID := ValidateRepoMetas(ctx, form, false)
        if ctx.Written() {
                return
        }
@@ -498,31 +505,23 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) {
 
 // commentTag returns the CommentTag for a comment in/with the given repo, poster and issue
 func commentTag(repo *models.Repository, poster *models.User, issue *models.Issue) (models.CommentTag, error) {
-       if repo.IsOwnedBy(poster.ID) {
-               return models.CommentTagOwner, nil
-       } else if repo.Owner.IsOrganization() {
-               isOwner, err := repo.Owner.IsOwnedBy(poster.ID)
-               if err != nil {
-                       return models.CommentTagNone, err
-               } else if isOwner {
-                       return models.CommentTagOwner, nil
-               }
+       perm, err := models.GetUserRepoPermission(repo, poster)
+       if err != nil {
+               return models.CommentTagNone, err
        }
-       if poster.IsWriterOfRepo(repo) {
-               return models.CommentTagWriter, nil
+       if perm.IsOwner() {
+               return models.CommentTagOwner, nil
        } else if poster.ID == issue.PosterID {
                return models.CommentTagPoster, nil
+       } else if perm.CanWrite(models.UnitTypeCode) {
+               return models.CommentTagWriter, nil
        }
+
        return models.CommentTagNone, nil
 }
 
 // ViewIssue render issue view page
 func ViewIssue(ctx *context.Context) {
-       ctx.Data["RequireHighlightJS"] = true
-       ctx.Data["RequireDropzone"] = true
-       ctx.Data["RequireTribute"] = true
-       renderAttachmentSettings(ctx)
-
        issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
        if err != nil {
                if models.IsErrIssueNotExist(err) {
@@ -532,25 +531,6 @@ func ViewIssue(ctx *context.Context) {
                }
                return
        }
-       ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, issue.Title)
-
-       var iw *models.IssueWatch
-       var exists bool
-       if ctx.User != nil {
-               iw, exists, err = models.GetIssueWatch(ctx.User.ID, issue.ID)
-               if err != nil {
-                       ctx.ServerError("GetIssueWatch", err)
-                       return
-               }
-               if !exists {
-                       iw = &models.IssueWatch{
-                               UserID:     ctx.User.ID,
-                               IssueID:    issue.ID,
-                               IsWatching: models.IsWatching(ctx.User.ID, ctx.Repo.Repository.ID),
-                       }
-               }
-       }
-       ctx.Data["IssueWatch"] = iw
 
        // Make sure type and URL matches.
        if ctx.Params(":type") == "issues" && issue.IsPull {
@@ -576,6 +556,31 @@ func ViewIssue(ctx *context.Context) {
                ctx.Data["PageIsIssueList"] = true
        }
 
+       ctx.Data["RequireHighlightJS"] = true
+       ctx.Data["RequireDropzone"] = true
+       ctx.Data["RequireTribute"] = true
+       renderAttachmentSettings(ctx)
+
+       ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, issue.Title)
+
+       var iw *models.IssueWatch
+       var exists bool
+       if ctx.User != nil {
+               iw, exists, err = models.GetIssueWatch(ctx.User.ID, issue.ID)
+               if err != nil {
+                       ctx.ServerError("GetIssueWatch", err)
+                       return
+               }
+               if !exists {
+                       iw = &models.IssueWatch{
+                               UserID:     ctx.User.ID,
+                               IssueID:    issue.ID,
+                               IsWatching: models.IsWatching(ctx.User.ID, ctx.Repo.Repository.ID),
+                       }
+               }
+       }
+       ctx.Data["IssueWatch"] = iw
+
        issue.RenderedContent = string(markdown.Render([]byte(issue.Content), ctx.Repo.RepoLink,
                ctx.Repo.Repository.ComposeMetas()))
 
@@ -616,7 +621,7 @@ func ViewIssue(ctx *context.Context) {
        ctx.Data["Labels"] = labels
 
        // Check milestone and assignee.
-       if ctx.Repo.IsWriter() {
+       if ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
                RetrieveRepoMilestonesAndAssignees(ctx, repo)
                if ctx.Written() {
                        return
@@ -761,13 +766,20 @@ func ViewIssue(ctx *context.Context) {
                if ctx.IsSigned {
                        if err := pull.GetHeadRepo(); err != nil {
                                log.Error(4, "GetHeadRepo: %v", err)
-                       } else if pull.HeadRepo != nil && pull.HeadBranch != pull.HeadRepo.DefaultBranch && ctx.User.IsWriterOfRepo(pull.HeadRepo) {
-                               // Check if branch is not protected
-                               if protected, err := pull.HeadRepo.IsProtectedBranch(pull.HeadBranch, ctx.User); err != nil {
-                                       log.Error(4, "IsProtectedBranch: %v", err)
-                               } else if !protected {
-                                       canDelete = true
-                                       ctx.Data["DeleteBranchLink"] = ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index) + "/cleanup"
+                       } else if pull.HeadRepo != nil && pull.HeadBranch != pull.HeadRepo.DefaultBranch {
+                               perm, err := models.GetUserRepoPermission(pull.HeadRepo, ctx.User)
+                               if err != nil {
+                                       ctx.ServerError("GetUserRepoPermission", err)
+                                       return
+                               }
+                               if perm.CanWrite(models.UnitTypeCode) {
+                                       // Check if branch is not protected
+                                       if protected, err := pull.HeadRepo.IsProtectedBranch(pull.HeadBranch, ctx.User); err != nil {
+                                               log.Error(4, "IsProtectedBranch: %v", err)
+                                       } else if !protected {
+                                               canDelete = true
+                                               ctx.Data["DeleteBranchLink"] = ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index) + "/cleanup"
+                                       }
                                }
                        }
                }
@@ -779,7 +791,7 @@ func ViewIssue(ctx *context.Context) {
                }
                prConfig := prUnit.PullRequestsConfig()
 
-               ctx.Data["AllowMerge"] = ctx.Data["IsRepositoryWriter"]
+               ctx.Data["AllowMerge"] = ctx.Repo.CanWrite(models.UnitTypeCode)
                if err := pull.CheckUserAllowedToMerge(ctx.User); err != nil {
                        if !models.IsErrNotAllowedToMerge(err) {
                                ctx.ServerError("CheckUserAllowedToMerge", err)
@@ -818,8 +830,9 @@ func ViewIssue(ctx *context.Context) {
        ctx.Data["NumParticipants"] = len(participants)
        ctx.Data["Issue"] = issue
        ctx.Data["ReadOnly"] = true
-       ctx.Data["IsIssueOwner"] = ctx.Repo.IsWriter() || (ctx.IsSigned && issue.IsPoster(ctx.User.ID))
        ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login?redirect_to=" + ctx.Data["Link"].(string)
+       ctx.Data["IsIssuePoster"] = ctx.IsSigned && issue.IsPoster(ctx.User.ID)
+       ctx.Data["IsIssueWriter"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)
        ctx.HTML(200, tplIssueView)
 }
 
@@ -842,8 +855,8 @@ func GetActionIssue(ctx *context.Context) *models.Issue {
 }
 
 func checkIssueRights(ctx *context.Context, issue *models.Issue) {
-       if issue.IsPull && !ctx.Repo.Repository.UnitEnabled(models.UnitTypePullRequests) ||
-               !issue.IsPull && !ctx.Repo.Repository.UnitEnabled(models.UnitTypeIssues) {
+       if issue.IsPull && !ctx.Repo.CanRead(models.UnitTypePullRequests) ||
+               !issue.IsPull && !ctx.Repo.CanRead(models.UnitTypeIssues) {
                ctx.NotFound("IssueOrPullRequestUnitNotAllowed", nil)
        }
 }
@@ -868,8 +881,8 @@ func getActionIssues(ctx *context.Context) []*models.Issue {
                return nil
        }
        // Check access rights for all issues
-       issueUnitEnabled := ctx.Repo.Repository.UnitEnabled(models.UnitTypeIssues)
-       prUnitEnabled := ctx.Repo.Repository.UnitEnabled(models.UnitTypePullRequests)
+       issueUnitEnabled := ctx.Repo.CanRead(models.UnitTypeIssues)
+       prUnitEnabled := ctx.Repo.CanRead(models.UnitTypePullRequests)
        for _, issue := range issues {
                if issue.IsPull && !prUnitEnabled || !issue.IsPull && !issueUnitEnabled {
                        ctx.NotFound("IssueOrPullRequestUnitNotAllowed", nil)
@@ -890,7 +903,7 @@ func UpdateIssueTitle(ctx *context.Context) {
                return
        }
 
-       if !ctx.IsSigned || (!issue.IsPoster(ctx.User.ID) && !ctx.Repo.IsWriter()) {
+       if !ctx.IsSigned || (!issue.IsPoster(ctx.User.ID) && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) {
                ctx.Error(403)
                return
        }
@@ -918,7 +931,7 @@ func UpdateIssueContent(ctx *context.Context) {
                return
        }
 
-       if !ctx.IsSigned || (ctx.User.ID != issue.PosterID && !ctx.Repo.IsWriter()) {
+       if !ctx.IsSigned || (ctx.User.ID != issue.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) {
                ctx.Error(403)
                return
        }
@@ -1037,6 +1050,11 @@ func NewComment(ctx *context.Context, form auth.CreateCommentForm) {
                return
        }
 
+       if !ctx.IsSigned || (ctx.User.ID != issue.PosterID && !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull)) {
+               ctx.Error(403)
+               return
+       }
+
        var attachments []string
        if setting.AttachmentEnabled {
                attachments = form.Files
@@ -1051,7 +1069,7 @@ func NewComment(ctx *context.Context, form auth.CreateCommentForm) {
        var comment *models.Comment
        defer func() {
                // Check if issue admin/poster changes the status of issue.
-               if (ctx.Repo.IsWriter() || (ctx.IsSigned && issue.IsPoster(ctx.User.ID))) &&
+               if (ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) || (ctx.IsSigned && issue.IsPoster(ctx.User.ID))) &&
                        (form.Status == "reopen" || form.Status == "close") &&
                        !(issue.IsPull && issue.PullRequest.HasMerged) {
 
@@ -1140,7 +1158,12 @@ func UpdateCommentContent(ctx *context.Context) {
                return
        }
 
-       if !ctx.IsSigned || (ctx.User.ID != comment.PosterID && !ctx.Repo.IsAdmin()) {
+       if err := comment.LoadIssue(); err != nil {
+               ctx.NotFoundOrServerError("LoadIssue", models.IsErrIssueNotExist, err)
+               return
+       }
+
+       if !ctx.IsSigned || (ctx.User.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
                ctx.Error(403)
                return
        } else if comment.Type != models.CommentTypeComment && comment.Type != models.CommentTypeCode {
@@ -1174,7 +1197,12 @@ func DeleteComment(ctx *context.Context) {
                return
        }
 
-       if !ctx.IsSigned || (ctx.User.ID != comment.PosterID && !ctx.Repo.IsAdmin()) {
+       if err := comment.LoadIssue(); err != nil {
+               ctx.NotFoundOrServerError("LoadIssue", models.IsErrIssueNotExist, err)
+               return
+       }
+
+       if !ctx.IsSigned || (ctx.User.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
                ctx.Error(403)
                return
        } else if comment.Type != models.CommentTypeComment && comment.Type != models.CommentTypeCode {
@@ -1417,6 +1445,11 @@ func ChangeIssueReaction(ctx *context.Context, form auth.ReactionForm) {
                return
        }
 
+       if !ctx.IsSigned || (ctx.User.ID != issue.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)) {
+               ctx.Error(403)
+               return
+       }
+
        if ctx.HasError() {
                ctx.ServerError("ChangeIssueReaction", errors.New(ctx.GetErrMsg()))
                return
@@ -1486,20 +1519,22 @@ func ChangeCommentReaction(ctx *context.Context, form auth.ReactionForm) {
                return
        }
 
-       issue, err := models.GetIssueByID(comment.IssueID)
-       checkIssueRights(ctx, issue)
-       if ctx.Written() {
+       if err := comment.LoadIssue(); err != nil {
+               ctx.NotFoundOrServerError("LoadIssue", models.IsErrIssueNotExist, err)
                return
        }
 
-       if ctx.HasError() {
-               ctx.ServerError("ChangeCommentReaction", errors.New(ctx.GetErrMsg()))
+       if !ctx.IsSigned || (ctx.User.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
+               ctx.Error(403)
+               return
+       } else if comment.Type != models.CommentTypeComment && comment.Type != models.CommentTypeCode {
+               ctx.Error(204)
                return
        }
 
        switch ctx.Params(":action") {
        case "react":
-               reaction, err := models.CreateCommentReaction(ctx.User, issue, comment, form.Content)
+               reaction, err := models.CreateCommentReaction(ctx.User, comment.Issue, comment, form.Content)
                if err != nil {
                        log.Info("CreateCommentReaction: %s", err)
                        break
@@ -1511,9 +1546,9 @@ func ChangeCommentReaction(ctx *context.Context, form auth.ReactionForm) {
                        break
                }
 
-               log.Trace("Reaction for comment created: %d/%d/%d/%d", ctx.Repo.Repository.ID, issue.ID, comment.ID, reaction.ID)
+               log.Trace("Reaction for comment created: %d/%d/%d/%d", ctx.Repo.Repository.ID, comment.Issue.ID, comment.ID, reaction.ID)
        case "unreact":
-               if err := models.DeleteCommentReaction(ctx.User, issue, comment, form.Content); err != nil {
+               if err := models.DeleteCommentReaction(ctx.User, comment.Issue, comment, form.Content); err != nil {
                        ctx.ServerError("DeleteCommentReaction", err)
                        return
                }
@@ -1525,7 +1560,7 @@ func ChangeCommentReaction(ctx *context.Context, form auth.ReactionForm) {
                        break
                }
 
-               log.Trace("Reaction for comment removed: %d/%d/%d", ctx.Repo.Repository.ID, issue.ID, comment.ID)
+               log.Trace("Reaction for comment removed: %d/%d/%d", ctx.Repo.Repository.ID, comment.Issue.ID, comment.ID)
        default:
                ctx.NotFound(fmt.Sprintf("Unknown action %s", ctx.Params(":action")), nil)
                return
index a499b70d9c31f4621b782600e2815ff54c812ed2..c6a436801a1eea73f7575f8e5d431e2df81033e8 100644 (file)
@@ -14,23 +14,28 @@ import (
 )
 
 // IssueWatch sets issue watching
-func IssueWatch(c *context.Context) {
-       watch, err := strconv.ParseBool(c.Req.PostForm.Get("watch"))
-       if err != nil {
-               c.ServerError("watch is not bool", err)
+func IssueWatch(ctx *context.Context) {
+       issue := GetActionIssue(ctx)
+       if ctx.Written() {
                return
        }
 
-       issue := GetActionIssue(c)
-       if c.Written() {
+       if !ctx.IsSigned || (ctx.User.ID != issue.PosterID && !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull)) {
+               ctx.Error(403)
+               return
+       }
+
+       watch, err := strconv.ParseBool(ctx.Req.PostForm.Get("watch"))
+       if err != nil {
+               ctx.ServerError("watch is not bool", err)
                return
        }
 
-       if err := models.CreateOrUpdateIssueWatch(c.User.ID, issue.ID, watch); err != nil {
-               c.ServerError("CreateOrUpdateIssueWatch", err)
+       if err := models.CreateOrUpdateIssueWatch(ctx.User.ID, issue.ID, watch); err != nil {
+               ctx.ServerError("CreateOrUpdateIssueWatch", err)
                return
        }
 
-       url := fmt.Sprintf("%s/issues/%d", c.Repo.RepoLink, issue.Index)
-       c.Redirect(url, http.StatusSeeOther)
+       url := fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issue.Index)
+       ctx.Redirect(url, http.StatusSeeOther)
 }
index 4ec1c27cea0550f61a8d71a92841e1100d27fb29..4adfb96e748be4931049144e649d2781b147d978 100644 (file)
@@ -57,7 +57,13 @@ func getForkRepository(ctx *context.Context) *models.Repository {
                return nil
        }
 
-       if !forkRepo.CanBeForked() || !forkRepo.HasAccess(ctx.User) {
+       perm, err := models.GetUserRepoPermission(forkRepo, ctx.User)
+       if err != nil {
+               ctx.ServerError("GetUserRepoPermission", err)
+               return nil
+       }
+
+       if forkRepo.IsBare || !perm.CanRead(models.UnitTypeCode) {
                ctx.NotFound("getForkRepository", nil)
                return nil
        }
@@ -669,7 +675,12 @@ func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *
                }
        }
 
-       if !ctx.User.IsWriterOfRepo(headRepo) && !ctx.User.IsAdmin {
+       perm, err := models.GetUserRepoPermission(headRepo, ctx.User)
+       if err != nil {
+               ctx.ServerError("GetUserRepoPermission", err)
+               return nil, nil, nil, nil, "", ""
+       }
+       if !perm.CanWrite(models.UnitTypeCode) {
                log.Trace("ParseCompareInfo[%d]: does not have write access or site admin", baseRepo.ID)
                ctx.NotFound("ParseCompareInfo", nil)
                return nil, nil, nil, nil, "", ""
@@ -823,7 +834,7 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm)
                return
        }
 
-       labelIDs, assigneeIDs, milestoneID := ValidateRepoMetas(ctx, form)
+       labelIDs, assigneeIDs, milestoneID := ValidateRepoMetas(ctx, form, true)
        if ctx.Written() {
                return
        }
@@ -969,7 +980,12 @@ func CleanUpPullRequest(ctx *context.Context) {
                return
        }
 
-       if !ctx.User.IsWriterOfRepo(pr.HeadRepo) {
+       perm, err := models.GetUserRepoPermission(pr.HeadRepo, ctx.User)
+       if err != nil {
+               ctx.ServerError("GetUserRepoPermission", err)
+               return
+       }
+       if !perm.CanWrite(models.UnitTypeCode) {
                ctx.NotFound("CleanUpPullRequest", nil)
                return
        }
index bae87efdcdc814faf375ca577e5e02c81bee4a29..5a869520f0b0ac3164652fdd223a0ffaf4bf2e63 100644 (file)
@@ -1,4 +1,5 @@
 // Copyright 2014 The Gogs Authors. All rights reserved.
+// Copyright 2018 The Gitea Authors. All rights reserved.
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
@@ -65,8 +66,11 @@ func Releases(ctx *context.Context) {
                limit = 10
        }
 
+       writeAccess := ctx.Repo.CanWrite(models.UnitTypeReleases)
+       ctx.Data["CanCreateRelease"] = writeAccess
+
        opts := models.FindReleasesOptions{
-               IncludeDrafts: ctx.Repo.IsWriter(),
+               IncludeDrafts: writeAccess,
                IncludeTags:   true,
        }
 
index db12f0afcdf9ecd4d7d9cf5aff34b2c3ef836f28..c8f6f843da3b4a35461a2cc19002d67dc9f6f1b5 100644 (file)
@@ -165,12 +165,21 @@ func SettingsProtectedBranchPost(ctx *context.Context, f auth.ProtectBranchForm)
                        }
                }
 
+               var whitelistUsers, whitelistTeams, mergeWhitelistUsers, mergeWhitelistTeams []int64
                protectBranch.EnableWhitelist = f.EnableWhitelist
-               whitelistUsers, _ := base.StringsToInt64s(strings.Split(f.WhitelistUsers, ","))
-               whitelistTeams, _ := base.StringsToInt64s(strings.Split(f.WhitelistTeams, ","))
+               if strings.TrimSpace(f.WhitelistUsers) != "" {
+                       whitelistUsers, _ = base.StringsToInt64s(strings.Split(f.WhitelistUsers, ","))
+               }
+               if strings.TrimSpace(f.WhitelistTeams) != "" {
+                       whitelistTeams, _ = base.StringsToInt64s(strings.Split(f.WhitelistTeams, ","))
+               }
                protectBranch.EnableMergeWhitelist = f.EnableMergeWhitelist
-               mergeWhitelistUsers, _ := base.StringsToInt64s(strings.Split(f.MergeWhitelistUsers, ","))
-               mergeWhitelistTeams, _ := base.StringsToInt64s(strings.Split(f.MergeWhitelistTeams, ","))
+               if strings.TrimSpace(f.MergeWhitelistUsers) != "" {
+                       mergeWhitelistUsers, _ = base.StringsToInt64s(strings.Split(f.MergeWhitelistUsers, ","))
+               }
+               if strings.TrimSpace(f.MergeWhitelistTeams) != "" {
+                       mergeWhitelistTeams, _ = base.StringsToInt64s(strings.Split(f.MergeWhitelistTeams, ","))
+               }
                err = models.UpdateProtectBranch(ctx.Repo.Repository, protectBranch, whitelistUsers, whitelistTeams, mergeWhitelistUsers, mergeWhitelistTeams)
                if err != nil {
                        ctx.ServerError("UpdateProtectBranch", err)
index 657fe315a2a7316f58b9512859b48794a1254138..78a305aa28aa379fc72a9134dec2f8a43c75325d 100644 (file)
@@ -137,7 +137,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
        ctx.Data["LatestCommitStatus"] = models.CalcCommitStatus(statuses)
 
        // Check permission to add or upload new file.
-       if ctx.Repo.IsWriter() && ctx.Repo.IsViewBranch {
+       if ctx.Repo.CanWrite(models.UnitTypeCode) && ctx.Repo.IsViewBranch {
                ctx.Data["CanAddFile"] = true
                ctx.Data["CanUploadFile"] = setting.Repository.Upload.Enabled
        }
@@ -256,7 +256,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
                        ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.edit_this_file")
                } else if !ctx.Repo.IsViewBranch {
                        ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch")
-               } else if !ctx.Repo.IsWriter() {
+               } else if !ctx.Repo.CanWrite(models.UnitTypeCode) {
                        ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.fork_before_edit")
                }
 
@@ -275,16 +275,16 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
                ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.delete_this_file")
        } else if !ctx.Repo.IsViewBranch {
                ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_be_on_a_branch")
-       } else if !ctx.Repo.IsWriter() {
+       } else if !ctx.Repo.CanWrite(models.UnitTypeCode) {
                ctx.Data["DeleteFileTooltip"] = ctx.Tr("repo.editor.must_have_write_access")
        }
 }
 
 // Home render repository home page
 func Home(ctx *context.Context) {
-       if len(ctx.Repo.Repository.Units) > 0 {
+       if len(ctx.Repo.Units) > 0 {
                var firstUnit *models.Unit
-               for _, repoUnit := range ctx.Repo.Repository.Units {
+               for _, repoUnit := range ctx.Repo.Units {
                        if repoUnit.Type == models.UnitTypeCode {
                                renderCode(ctx)
                                return
index 3220ab134d46a53868681eee35d65d76b74f58e6..23492363580eda36e5ecd5832d0f620c3e0030bd 100644 (file)
@@ -1,4 +1,5 @@
 // Copyright 2015 The Gogs Authors. All rights reserved.
+// Copyright 2018 The Gitea Authors. All rights reserved.
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
@@ -30,8 +31,8 @@ const (
 
 // MustEnableWiki check if wiki is enabled, if external then redirect
 func MustEnableWiki(ctx *context.Context) {
-       if !ctx.Repo.Repository.UnitEnabled(models.UnitTypeWiki) &&
-               !ctx.Repo.Repository.UnitEnabled(models.UnitTypeExternalWiki) {
+       if !ctx.Repo.CanRead(models.UnitTypeWiki) &&
+               !ctx.Repo.CanRead(models.UnitTypeExternalWiki) {
                ctx.NotFound("MustEnableWiki", nil)
                return
        }
@@ -200,6 +201,7 @@ func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, *gi
 // Wiki renders single wiki page
 func Wiki(ctx *context.Context) {
        ctx.Data["PageIsWiki"] = true
+       ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(models.UnitTypeWiki)
 
        if !ctx.Repo.Repository.HasWiki() {
                ctx.Data["Title"] = ctx.Tr("repo.wiki")
@@ -235,14 +237,15 @@ func Wiki(ctx *context.Context) {
 
 // WikiPages render wiki pages list page
 func WikiPages(ctx *context.Context) {
-       ctx.Data["Title"] = ctx.Tr("repo.wiki.pages")
-       ctx.Data["PageIsWiki"] = true
-
        if !ctx.Repo.Repository.HasWiki() {
                ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
                return
        }
 
+       ctx.Data["Title"] = ctx.Tr("repo.wiki.pages")
+       ctx.Data["PageIsWiki"] = true
+       ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(models.UnitTypeWiki)
+
        wikiRepo, commit, err := findWikiRepoCommit(ctx)
        if err != nil {
                return
index 1c1bcd8f953541e202f0cc0f8f4bbdc7bf42baaf..eb5841f5931096f10fa34caea0fc4097ca36feef 100644 (file)
@@ -393,7 +393,16 @@ func RegisterRoutes(m *macaron.Macaron) {
        }
 
        reqRepoAdmin := context.RequireRepoAdmin()
-       reqRepoWriter := context.RequireRepoWriter()
+       reqRepoCodeWriter := context.RequireRepoWriter(models.UnitTypeCode)
+       reqRepoCodeReader := context.RequireRepoReader(models.UnitTypeCode)
+       reqRepoReleaseWriter := context.RequireRepoWriter(models.UnitTypeReleases)
+       reqRepoReleaseReader := context.RequireRepoReader(models.UnitTypeReleases)
+       reqRepoWikiWriter := context.RequireRepoWriter(models.UnitTypeWiki)
+       reqRepoIssueReader := context.RequireRepoReader(models.UnitTypeIssues)
+       reqRepoPullsWriter := context.RequireRepoWriter(models.UnitTypePullRequests)
+       reqRepoPullsReader := context.RequireRepoReader(models.UnitTypePullRequests)
+       reqRepoIssuesOrPullsWriter := context.RequireRepoWriterOr(models.UnitTypeIssues, models.UnitTypePullRequests)
+       reqRepoIssuesOrPullsReader := context.RequireRepoReaderOr(models.UnitTypeIssues, models.UnitTypePullRequests)
 
        // ***** START: Organization *****
        m.Group("/org", func() {
@@ -463,7 +472,7 @@ func RegisterRoutes(m *macaron.Macaron) {
                m.Group("/fork", func() {
                        m.Combo("/:repoid").Get(repo.Fork).
                                Post(bindIgnErr(auth.CreateRepoForm{}), repo.ForkPost)
-               }, context.RepoIDAssignment(), context.UnitTypes(), context.LoadRepoUnits(), context.CheckUnit(models.UnitTypeCode))
+               }, context.RepoIDAssignment(), context.UnitTypes(), reqRepoCodeReader)
        }, reqSignIn)
 
        m.Group("/:username/:reponame", func() {
@@ -514,7 +523,7 @@ func RegisterRoutes(m *macaron.Macaron) {
                }, func(ctx *context.Context) {
                        ctx.Data["PageIsSettings"] = true
                })
-       }, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.UnitTypes(), context.LoadRepoUnits(), context.RepoRef())
+       }, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.UnitTypes(), context.RepoRef())
 
        m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), repo.Action)
 
@@ -522,7 +531,7 @@ func RegisterRoutes(m *macaron.Macaron) {
                m.Group("/issues", func() {
                        m.Combo("/new").Get(context.RepoRef(), repo.NewIssue).
                                Post(bindIgnErr(auth.CreateIssueForm{}), repo.NewIssuePost)
-               }, context.CheckUnit(models.UnitTypeIssues))
+               }, reqRepoIssueReader)
                // FIXME: should use different URLs but mostly same logic for comments of issue and pull reuqest.
                // So they can apply their own enable/disable logic on routers.
                m.Group("/issues", func() {
@@ -545,22 +554,22 @@ func RegisterRoutes(m *macaron.Macaron) {
                                m.Post("/reactions/:action", bindIgnErr(auth.ReactionForm{}), repo.ChangeIssueReaction)
                        })
 
-                       m.Post("/labels", reqRepoWriter, repo.UpdateIssueLabel)
-                       m.Post("/milestone", reqRepoWriter, repo.UpdateIssueMilestone)
-                       m.Post("/assignee", reqRepoWriter, repo.UpdateIssueAssignee)
-                       m.Post("/status", reqRepoWriter, repo.UpdateIssueStatus)
+                       m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel)
+                       m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone)
+                       m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee)
+                       m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus)
                })
                m.Group("/comments/:id", func() {
                        m.Post("", repo.UpdateCommentContent)
                        m.Post("/delete", repo.DeleteComment)
                        m.Post("/reactions/:action", bindIgnErr(auth.ReactionForm{}), repo.ChangeCommentReaction)
-               }, context.CheckAnyUnit(models.UnitTypeIssues, models.UnitTypePullRequests))
+               })
                m.Group("/labels", func() {
                        m.Post("/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel)
                        m.Post("/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel)
                        m.Post("/delete", repo.DeleteLabel)
                        m.Post("/initialize", bindIgnErr(auth.InitializeLabelsForm{}), repo.InitializeLabels)
-               }, reqRepoWriter, context.RepoRef(), context.CheckAnyUnit(models.UnitTypeIssues, models.UnitTypePullRequests))
+               }, reqRepoIssuesOrPullsWriter, context.RepoRef())
                m.Group("/milestones", func() {
                        m.Combo("/new").Get(repo.NewMilestone).
                                Post(bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost)
@@ -568,9 +577,9 @@ func RegisterRoutes(m *macaron.Macaron) {
                        m.Post("/:id/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.EditMilestonePost)
                        m.Get("/:id/:action", repo.ChangeMilestonStatus)
                        m.Post("/delete", repo.DeleteMilestone)
-               }, reqRepoWriter, context.RepoRef(), context.CheckAnyUnit(models.UnitTypeIssues, models.UnitTypePullRequests))
+               }, reqRepoIssuesOrPullsWriter, context.RepoRef())
 
-               m.Combo("/compare/*", repo.MustAllowPulls, repo.SetEditorconfigIfExists).
+               m.Combo("/compare/*", reqRepoCodeReader, reqRepoPullsReader, repo.MustAllowPulls, repo.SetEditorconfigIfExists).
                        Get(repo.SetDiffViewStyle, repo.CompareAndPullRequest).
                        Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
 
@@ -591,7 +600,7 @@ func RegisterRoutes(m *macaron.Macaron) {
                                m.Post("/upload-file", repo.UploadFileToServer)
                                m.Post("/upload-remove", bindIgnErr(auth.RemoveUploadFileForm{}), repo.RemoveUploadFileFromServer)
                        }, context.RepoRef(), repo.MustBeEditable, repo.MustBeAbleToUpload)
-               }, repo.MustBeNotBare, reqRepoWriter)
+               }, reqRepoCodeWriter, repo.MustBeNotBare)
 
                m.Group("/branches", func() {
                        m.Group("/_new/", func() {
@@ -601,9 +610,9 @@ func RegisterRoutes(m *macaron.Macaron) {
                        }, bindIgnErr(auth.NewBranchForm{}))
                        m.Post("/delete", repo.DeleteBranchPost)
                        m.Post("/restore", repo.RestoreBranchPost)
-               }, reqRepoWriter, repo.MustBeNotBare, context.CheckUnit(models.UnitTypeCode))
+               }, reqRepoCodeWriter, repo.MustBeNotBare)
 
-       }, reqSignIn, context.RepoAssignment(), context.UnitTypes(), context.LoadRepoUnits())
+       }, reqSignIn, context.RepoAssignment(), context.UnitTypes())
 
        // Releases
        m.Group("/:username/:reponame", func() {
@@ -614,11 +623,11 @@ func RegisterRoutes(m *macaron.Macaron) {
                        m.Get("/new", repo.NewRelease)
                        m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost)
                        m.Post("/delete", repo.DeleteRelease)
-               }, reqSignIn, repo.MustBeNotBare, reqRepoWriter, context.RepoRef())
+               }, reqSignIn, repo.MustBeNotBare, reqRepoReleaseWriter, context.RepoRef())
                m.Group("/releases", func() {
                        m.Get("/edit/*", repo.EditRelease)
                        m.Post("/edit/*", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
-               }, reqSignIn, repo.MustBeNotBare, reqRepoWriter, func(ctx *context.Context) {
+               }, reqSignIn, repo.MustBeNotBare, reqRepoReleaseWriter, func(ctx *context.Context) {
                        var err error
                        ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
                        if err != nil {
@@ -632,7 +641,7 @@ func RegisterRoutes(m *macaron.Macaron) {
                        }
                        ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
                })
-       }, context.RepoAssignment(), context.UnitTypes(), context.LoadRepoUnits(), context.CheckUnit(models.UnitTypeReleases))
+       }, context.RepoAssignment(), context.UnitTypes(), reqRepoReleaseReader)
 
        m.Group("/:username/:reponame", func() {
                m.Post("/topics", repo.TopicsPost)
@@ -642,8 +651,8 @@ func RegisterRoutes(m *macaron.Macaron) {
                m.Group("", func() {
                        m.Get("/^:type(issues|pulls)$", repo.RetrieveLabels, repo.Issues)
                        m.Get("/^:type(issues|pulls)$/:index", repo.ViewIssue)
-                       m.Get("/labels/", context.CheckAnyUnit(models.UnitTypeIssues, models.UnitTypePullRequests), repo.RetrieveLabels, repo.Labels)
-                       m.Get("/milestones", context.CheckAnyUnit(models.UnitTypeIssues, models.UnitTypePullRequests), repo.Milestones)
+                       m.Get("/labels/", reqRepoIssuesOrPullsReader, repo.RetrieveLabels, repo.Labels)
+                       m.Get("/milestones", reqRepoIssuesOrPullsReader, repo.Milestones)
                }, context.RepoRef())
 
                m.Group("/wiki", func() {
@@ -656,7 +665,7 @@ func RegisterRoutes(m *macaron.Macaron) {
                                m.Combo("/:page/_edit").Get(repo.EditWiki).
                                        Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost)
                                m.Post("/:page/delete", repo.DeleteWikiPagePost)
-                       }, reqSignIn, reqRepoWriter)
+                       }, reqSignIn, reqRepoWikiWriter)
                }, repo.MustEnableWiki, context.RepoRef())
 
                m.Group("/wiki", func() {
@@ -666,19 +675,19 @@ func RegisterRoutes(m *macaron.Macaron) {
                m.Group("/activity", func() {
                        m.Get("", repo.Activity)
                        m.Get("/:period", repo.Activity)
-               }, context.RepoRef(), repo.MustBeNotBare, context.CheckAnyUnit(models.UnitTypePullRequests, models.UnitTypeIssues, models.UnitTypeReleases))
+               }, context.RepoRef(), repo.MustBeNotBare, context.RequireRepoReaderOr(models.UnitTypePullRequests, models.UnitTypeIssues, models.UnitTypeReleases))
 
-               m.Get("/archive/*", repo.MustBeNotBare, context.CheckUnit(models.UnitTypeCode), repo.Download)
+               m.Get("/archive/*", repo.MustBeNotBare, reqRepoCodeReader, repo.Download)
 
                m.Group("/branches", func() {
                        m.Get("", repo.Branches)
-               }, repo.MustBeNotBare, context.RepoRef(), context.CheckUnit(models.UnitTypeCode))
+               }, repo.MustBeNotBare, context.RepoRef(), reqRepoCodeReader)
 
                m.Group("/pulls/:index", func() {
                        m.Get(".diff", repo.DownloadPullDiff)
                        m.Get(".patch", repo.DownloadPullPatch)
                        m.Get("/commits", context.RepoRef(), repo.ViewPullCommits)
-                       m.Post("/merge", reqRepoWriter, bindIgnErr(auth.MergePullRequestForm{}), repo.MergePullRequest)
+                       m.Post("/merge", reqRepoPullsWriter, bindIgnErr(auth.MergePullRequestForm{}), repo.MergePullRequest)
                        m.Post("/cleanup", context.RepoRef(), repo.CleanUpPullRequest)
                        m.Group("/files", func() {
                                m.Get("", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.ViewPullFiles)
@@ -696,7 +705,7 @@ func RegisterRoutes(m *macaron.Macaron) {
                        m.Get("/blob/:sha", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByID)
                        // "/*" route is deprecated, and kept for backward compatibility
                        m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownload)
-               }, repo.MustBeNotBare, context.CheckUnit(models.UnitTypeCode))
+               }, repo.MustBeNotBare, reqRepoCodeReader)
 
                m.Group("/commits", func() {
                        m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.RefCommits)
@@ -704,12 +713,12 @@ func RegisterRoutes(m *macaron.Macaron) {
                        m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RefCommits)
                        // "/*" route is deprecated, and kept for backward compatibility
                        m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.RefCommits)
-               }, repo.MustBeNotBare, context.CheckUnit(models.UnitTypeCode))
+               }, repo.MustBeNotBare, reqRepoCodeReader)
 
                m.Group("", func() {
                        m.Get("/graph", repo.Graph)
                        m.Get("/commit/:sha([a-f0-9]{7,40})$", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff)
-               }, repo.MustBeNotBare, context.RepoRef(), context.CheckUnit(models.UnitTypeCode))
+               }, repo.MustBeNotBare, context.RepoRef(), reqRepoCodeReader)
 
                m.Group("/src", func() {
                        m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home)
@@ -721,24 +730,24 @@ func RegisterRoutes(m *macaron.Macaron) {
 
                m.Group("", func() {
                        m.Get("/forks", repo.Forks)
-               }, context.RepoRef(), context.CheckUnit(models.UnitTypeCode))
+               }, context.RepoRef(), reqRepoCodeReader)
                m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)",
-                       repo.MustBeNotBare, context.CheckUnit(models.UnitTypeCode), repo.RawDiff)
+                       repo.MustBeNotBare, reqRepoCodeReader, repo.RawDiff)
 
                m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", repo.SetEditorconfigIfExists,
-                       repo.SetDiffViewStyle, repo.MustBeNotBare, context.CheckUnit(models.UnitTypeCode), repo.CompareDiff)
-       }, ignSignIn, context.RepoAssignment(), context.UnitTypes(), context.LoadRepoUnits())
+                       repo.SetDiffViewStyle, repo.MustBeNotBare, reqRepoCodeReader, repo.CompareDiff)
+       }, ignSignIn, context.RepoAssignment(), context.UnitTypes())
        m.Group("/:username/:reponame", func() {
                m.Get("/stars", repo.Stars)
                m.Get("/watchers", repo.Watchers)
-               m.Get("/search", context.CheckUnit(models.UnitTypeCode), repo.Search)
-       }, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes(), context.LoadRepoUnits())
+               m.Get("/search", reqRepoCodeReader, repo.Search)
+       }, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes())
 
        m.Group("/:username", func() {
                m.Group("/:reponame", func() {
                        m.Get("", repo.SetEditorconfigIfExists, repo.Home)
                        m.Get("\\.git$", repo.SetEditorconfigIfExists, repo.Home)
-               }, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes(), context.LoadRepoUnits())
+               }, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes())
 
                m.Group("/:reponame", func() {
                        m.Group("\\.git/info/lfs", func() {
index 49c65795564ae10a8f3fe57de2de330ce50b4704..4c6a9f6cd09576f083bfaad1841cdbd6df30b940 100644 (file)
@@ -286,7 +286,12 @@ func Issues(ctx *context.Context) {
                repo := showReposMap[repoID]
 
                // Check if user has access to given repository.
-               if !repo.IsOwnedBy(ctxUser.ID) && !repo.HasAccess(ctxUser) {
+               perm, err := models.GetUserRepoPermission(repo, ctxUser)
+               if err != nil {
+                       ctx.ServerError("GetUserRepoPermission", fmt.Errorf("[%d]%v", repoID, err))
+                       return
+               }
+               if !perm.CanRead(models.UnitTypeIssues) {
                        ctx.Status(404)
                        return
                }
index 19866d53d77b415e742b2a9642e592bb8d6a798b..2b8fbc6c1c872800bec0d8d508e972deb49ecc57 100644 (file)
                </h2>
                <div class="ui divider"></div>
 
-               {{if (or (.Repository.UnitEnabled $.UnitTypeIssues) (.Repository.UnitEnabled $.UnitTypePullRequests))}}
+               {{if (or (.Permission.CanRead $.UnitTypeIssues) (.Permission.CanRead $.UnitTypePullRequests))}}
                <h4 class="ui top attached header">{{.i18n.Tr "repo.activity.overview"}}</h4>
                <div class="ui attached segment two column grid">
-                       {{if .Repository.UnitEnabled $.UnitTypePullRequests}}
+                       {{if .Permission.CanRead $.UnitTypePullRequests}}
                                <div class="column">
                                        {{if gt .Activity.ActivePRCount 0}}
                                        <div class="stats-table">
@@ -41,7 +41,7 @@
                                        {{.i18n.Tr (TrN .i18n.Lang .Activity.ActivePRCount "repo.activity.active_prs_count_1" "repo.activity.active_prs_count_n") .Activity.ActivePRCount | Safe }}
                                </div>
                        {{end}}
-                       {{if .Repository.UnitEnabled $.UnitTypeIssues}}
+                       {{if .Permission.CanRead $.UnitTypeIssues}}
                                <div class="column">
                                        {{if gt .Activity.ActiveIssueCount 0}}
                                        <div class="stats-table">
@@ -58,7 +58,7 @@
                        {{end}}
                </div>
                <div class="ui attached segment horizontal segments">
-                       {{if .Repository.UnitEnabled $.UnitTypePullRequests}}
+                       {{if .Permission.CanRead $.UnitTypePullRequests}}
                                <a href="#merged-pull-requests" class="ui attached segment text center">
                                        <i class="text purple octicon octicon-git-pull-request"></i> <strong>{{.Activity.MergedPRCount}}</strong><br>
                                        {{.i18n.Tr (TrN .i18n.Lang .Activity.MergedPRCount "repo.activity.merged_prs_count_1" "repo.activity.merged_prs_count_n") }}
@@ -68,7 +68,7 @@
                                        {{.i18n.Tr (TrN .i18n.Lang .Activity.OpenedPRCount "repo.activity.opened_prs_count_1" "repo.activity.opened_prs_count_n") }}
                                </a>
                        {{end}}
-                       {{if .Repository.UnitEnabled $.UnitTypeIssues}}
+                       {{if .Permission.CanRead $.UnitTypeIssues}}
                                <a href="#closed-issues" class="ui attached segment text center">
                                        <i class="text red octicon octicon-issue-closed"></i> <strong>{{.Activity.ClosedIssueCount}}</strong><br>
                                        {{.i18n.Tr (TrN .i18n.Lang .Activity.ClosedIssueCount "repo.activity.closed_issues_count_1" "repo.activity.closed_issues_count_n") }}
index f1e1d585425c5b36d17d4c43cce11dca0b75db77..ec4be2bdea689bed8d2b4f7109e405b360a48ce7 100644 (file)
@@ -5,7 +5,7 @@
                <div class="ui grid">
                        <div class="sixteen wide column content">
                                {{template "base/alert" .}}
-                               {{if .IsRepositoryWriter}}
+                               {{if .CanWriteCode}}
                                        <h4 class="ui top attached header">
                                                {{.i18n.Tr "repo.quick_guide"}}
                                        </h4>
index 18221e36ed3717f5557412c58a18bc4a7075c429..7e8588e60c8484fc19e6e76b267f900fc5ccf1ec 100644 (file)
@@ -21,7 +21,7 @@
                                {{end}}
                        {{end}}
                        {{template "repo/issue/view_content/add_reaction" Dict "ctx" $ "ActionURL" (Printf "%s/comments/%d/reactions" $.root.RepoLink .ID) }}
-                       {{if or $.root.IsRepositoryAdmin (eq .Poster.ID $.root.SignedUserID)}}
+                       {{if or $.root.Permission.IsAdmin (eq .Poster.ID $.root.SignedUserID)}}
                                <div class="item action">
                                        <a class="edit-content" href="#"><i class="octicon octicon-pencil"></i></a>
                                        <a class="delete-comment" href="#" data-comment-id={{.HashTag}} data-url="{{$.root.RepoLink}}/comments/{{.ID}}/delete" data-locale="{{$.root.i18n.Tr "repo.issues.delete_comment_confirm"}}"><i class="octicon octicon-x"></i></a>
index bf052301555b2a5f059582854d3de1978e8b1b7c..c8ef4bced71f2e83521b505eebe9f4146c51d9fa 100644 (file)
@@ -30,7 +30,7 @@
                                                {{.NumStars}}
                                        </a>
                                </div>
-                               {{if .CanBeForked}}
+                               {{if and (not .IsBare) ($.Permission.CanRead $.UnitTypeCode)}}
                                        <div class="ui compact labeled button" tabindex="0">
                                                <a class="ui compact button {{if not $.CanSignedUserFork}}poping up{{end}}" {{if $.CanSignedUserFork}}href="{{AppSubUrl}}/repo/fork/{{.ID}}"{{else if $.IsSigned}} data-content="{{$.i18n.Tr "repo.fork_from_self"}}" data-position="top center" data-variation="tiny"{{end}}>
                                                        <i class="octicon octicon-repo-forked"></i>{{$.i18n.Tr "repo.fork"}}
 {{if not .IsDiffCompare}}
        <div class="ui tabs container">
                <div class="ui tabular stackable menu navbar">
-                       {{if .Repository.UnitEnabled $.UnitTypeCode}}
+                       {{if .Permission.CanRead $.UnitTypeCode}}
                        <a class="{{if .PageIsViewCode}}active{{end}} item" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}">
                                <i class="octicon octicon-code"></i> {{.i18n.Tr "repo.code"}}
                        </a>
                        {{end}}
 
-                       {{if .Repository.UnitEnabled $.UnitTypeIssues}}
+                       {{if .Permission.CanRead $.UnitTypeIssues}}
                                <a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoLink}}/issues">
                                        <i class="octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues"}} <span class="ui {{if not .Repository.NumOpenIssues}}gray{{else}}blue{{end}} small label">{{.Repository.NumOpenIssues}}</span>
                                </a>
                        {{end}}
 
-                       {{if .Repository.UnitEnabled $.UnitTypeExternalTracker}}
+                       {{if .Permission.CanRead $.UnitTypeExternalTracker}}
                                <a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoLink}}/issues" target="_blank" rel="noopener noreferrer">
                                        <i class="octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues"}} </span>
                                </a>
                        {{end}}
 
-                       {{if .Repository.AllowsPulls}}
+                       {{if and .Repository.CanEnablePulls (.Permission.CanRead $.UnitTypePullRequests)}}
                                <a class="{{if .PageIsPullList}}active{{end}} item" href="{{.RepoLink}}/pulls">
                                        <i class="octicon octicon-git-pull-request"></i> {{.i18n.Tr "repo.pulls"}} <span class="ui {{if not .Repository.NumOpenPulls}}gray{{else}}blue{{end}} small label">{{.Repository.NumOpenPulls}}</span>
                                </a>
                        {{end}}
 
-                       {{if and (.Repository.UnitEnabled $.UnitTypeReleases) (not .IsBareRepo) }}
+                       {{if and (.Permission.CanRead $.UnitTypeReleases) (not .IsBareRepo) }}
                        <a class="{{if .PageIsReleaseList}}active{{end}} item" href="{{.RepoLink}}/releases">
                                <i class="octicon octicon-tag"></i> {{.i18n.Tr "repo.releases"}} <span class="ui {{if not .Repository.NumReleases}}gray{{else}}blue{{end}} small label">{{.Repository.NumReleases}}</span>
                        </a>
                        {{end}}
 
-                       {{if or (.Repository.UnitEnabled $.UnitTypeWiki) (.Repository.UnitEnabled $.UnitTypeExternalWiki)}}
-                               <a class="{{if .PageIsWiki}}active{{end}} item" href="{{.RepoLink}}/wiki" {{if (.Repository.UnitEnabled $.UnitTypeExternalWiki)}} target="_blank" rel="noopener noreferrer" {{end}}>
+                       {{if or (.Permission.CanRead $.UnitTypeWiki) (.Permission.CanRead $.UnitTypeExternalWiki)}}
+                               <a class="{{if .PageIsWiki}}active{{end}} item" href="{{.RepoLink}}/wiki" {{if (.Permission.CanRead $.UnitTypeExternalWiki)}} target="_blank" rel="noopener noreferrer" {{end}}>
                                        <i class="octicon octicon-book"></i> {{.i18n.Tr "repo.wiki"}}
                                </a>
                        {{end}}
 
-                       {{if and (.Repository.AnyUnitEnabled $.UnitTypePullRequests $.UnitTypeIssues $.UnitTypeReleases) (not .IsBareRepo)}}
+                       {{if and (.Permission.CanReadAny $.UnitTypePullRequests $.UnitTypeIssues $.UnitTypeReleases) (not .IsBareRepo)}}
                                <a class="{{if .PageIsActivity}}active{{end}} item" href="{{.RepoLink}}/activity">
                                        <i class="octicon octicon-pulse"></i> {{.i18n.Tr "repo.activity"}}
                                </a>
@@ -91,7 +91,7 @@
 
                        {{template "custom/extra_tabs" .}}
 
-                       {{if .IsRepositoryAdmin}}
+                       {{if .Permission.IsAdmin}}
                                <div class="right menu">
                                        <a class="{{if .PageIsSettings}}active{{end}} item" href="{{.RepoLink}}/settings">
                                                <i class="octicon octicon-tools"></i> {{.i18n.Tr "repo.settings"}}
index 3e69506c0a05e633de650a80521cbbe2ccdfca07..0a5886381d4209535f475348a503664fb9d596b1 100644 (file)
@@ -25,9 +25,9 @@
                </div>
                <div class="ui repo-topic" id="repo-topic">
                {{range .Topics}}<a class="ui green basic label topic" style="cursor:pointer;" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}}
-               {{if .IsRepositoryAdmin}}<a id="manage_topic" style="cursor:pointer;margin-left:10px;">{{.i18n.Tr "repo.topic.manage_topics"}}</a>{{end}}
+               {{if .Permission.IsAdmin}}<a id="manage_topic" style="cursor:pointer;margin-left:10px;">{{.i18n.Tr "repo.topic.manage_topics"}}</a>{{end}}
                </div>
-               {{if .IsRepositoryAdmin}}
+               {{if .Permission.IsAdmin}}
                <div class="ui repo-topic-edit grid form segment error" id="topic_edit" style="display:none">
                        <div class="fourteen wide column">
                                <div class="field">
index 38d914520635bde44f851989a024943f1ab98d09..c89d3740ee7f6a14bc88d2da9fca65df54617b16 100644 (file)
@@ -4,7 +4,7 @@
        <div class="ui container">
                <div class="navbar">
                        {{template "repo/issue/navbar" .}}
-                       {{if .IsRepositoryWriter}}
+                       {{if or .CanWriteIssues .CanWritePulls}}
                                <div class="ui right">
                                        <div class="ui green new-label button">{{.i18n.Tr "repo.issues.new_label"}}</div>
                                </div>
@@ -57,7 +57,7 @@
                {{template "base/alert" .}}
                <div class="ui black label">{{.i18n.Tr "repo.issues.label_count" .NumLabels}}</div>
                <div class="label list">
-                       {{if and $.IsRepositoryWriter (eq .NumLabels 0)}}
+                       {{if and (or $.CanWriteIssues $.CanWritePulls) (eq .NumLabels 0)}}
                                <div class="ui centered grid">
                                        <div class="twelve wide column eight wide computer column">
                                                <div class="ui attached left aligned segment">
                                                        <a class="ui right open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}"><i class="octicon octicon-issue-opened"></i> {{$.i18n.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a>
                                                </div>
                                                <div class="three wide column">
-                                               {{if $.IsRepositoryWriter}}
+                                               {{if or $.CanWriteIssues $.CanWritePulls}}
                                                        <a class="ui right delete-button" href="#" data-url="{{$.RepoLink}}/labels/delete" data-id="{{.ID}}"><i class="octicon octicon-trashcan"></i> {{$.i18n.Tr "repo.issues.label_delete"}}</a>
                                                        <a class="ui right edit-label-button" href="#" data-id="{{.ID}}" data-title="{{.Name}}" data-description="{{.Description}}" data-color={{.Color}}><i class="octicon octicon-pencil"></i> {{$.i18n.Tr "repo.issues.label_edit"}}</a>
                                                {{end}}
        </div>
 </div>
 
-{{if .IsRepositoryWriter}}
+{{if or $.CanWriteIssues $.CanWritePulls}}
        <div class="ui small basic delete modal">
                <div class="ui icon header">
                        <i class="trash icon"></i>
index bd2f79389ac0a9b7a0c9d3209db93b8536a07040..30a7d7ebbb3d0f65fb38b5fc372d5e684fd1ed55 100644 (file)
@@ -4,7 +4,7 @@
        <div class="ui container">
                <div class="navbar">
                        {{template "repo/issue/navbar" .}}
-                       {{if and .IsRepositoryWriter .PageIsEditMilestone}}
+                       {{if and (or .CanWriteIssues .CanWritePulls) .PageIsEditMilestone}}
                                <div class="ui right floated secondary menu">
                                        <a class="ui green button" href="{{$.RepoLink}}/milestones/new">{{.i18n.Tr "repo.milestones.new"}}</a>
                                </div>
index 75bd8db8ed35c20211935840a165e1331fd1b7d7..7b98c0cf56f468ed4dc0f7f44fae397c68e8e448 100644 (file)
@@ -4,7 +4,7 @@
        <div class="ui container">
                <div class="navbar">
                        {{template "repo/issue/navbar" .}}
-                       {{if .IsRepositoryWriter}}
+                       {{if or .CanWriteIssues .CanWritePulls}}
                                <div class="ui right">
                                        <a class="ui green button" href="{{$.Link}}/new">{{.i18n.Tr "repo.milestones.new"}}</a>
                                </div>
@@ -67,7 +67,7 @@
                                                        {{if .TotalTrackedTime}}<i class="octicon octicon-clock"></i> {{.TotalTrackedTime|Sec2Time}}{{end}}
                                                </span>
                                        </div>
-                                       {{if $.IsRepositoryWriter}}
+                                       {{if or $.CanWriteIssues $.CanWritePulls}}
                                                <div class="ui right operate">
                                                        <a href="{{$.Link}}/{{.ID}}/edit" data-id={{.ID}} data-title={{.Name}}><i class="octicon octicon-pencil"></i> {{$.i18n.Tr "repo.issues.label_edit"}}</a>
                                                        {{if .IsClosed}}
        </div>
 </div>
 
-{{if .IsRepositoryWriter}}
+{{if or .CanWriteIssues .CanWritePulls}}
        <div class="ui small basic delete modal">
                <div class="ui icon header">
                        <i class="trash icon"></i>
index e133ee9dc3700ac36504ee7b53e35bc7c2e77cb0..54531cf4d8a11159be6415f3660ad86ba90c367a 100644 (file)
@@ -20,7 +20,7 @@
                                                <span class="text grey"><a {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>{{.Issue.Poster.Name}}</a> {{.i18n.Tr "repo.issues.commented_at" .Issue.HashTag $createdStr | Safe}}</span>
                                                <div class="ui right actions">
                                                        {{template "repo/issue/view_content/add_reaction" Dict "ctx" $ "ActionURL" (Printf "%s/issues/%d/reactions" $.RepoLink .Issue.Index) }}
-                                                       {{if .IsIssueOwner}}
+                                                       {{if or .IsIssueWriter .IsIssuePoster}}
                                                                <div class="item action">
                                                                        <a class="edit-content" href="#"><i class="octicon octicon-pencil"></i></a>
                                                                </div>
@@ -79,7 +79,7 @@
                                                        {{.CsrfTokenHtml}}
                                                        <input id="status" name="status" type="hidden">
                                                        <div class="text right">
-                                                               {{if and .IsIssueOwner (not .DisableStatusChange)}}
+                                                               {{if and (or .IsIssueWriter .IsIssuePoster) (not .DisableStatusChange)}}
                                                                        {{if .Issue.IsClosed}}
                                                                                <div id="status-button" class="ui green basic button" tabindex="6" data-status="{{.i18n.Tr "repo.issues.reopen_issue"}}" data-status-and-comment="{{.i18n.Tr "repo.issues.reopen_comment_issue"}}" data-status-val="reopen">
                                                                                        {{.i18n.Tr "repo.issues.reopen_issue"}}
index 4e79fd866c19e1444e613bbd765cd5ab8cbcaa85..953aa4c2f02dbda7d317d583287473e10928d0ea 100644 (file)
@@ -23,7 +23,7 @@
                                                        </div>
                                                {{end}}
                                                {{template "repo/issue/view_content/add_reaction" Dict "ctx" $ "ActionURL" (Printf "%s/comments/%d/reactions" $.RepoLink .ID) }}
-                                               {{if or $.IsRepositoryAdmin (eq .Poster.ID $.SignedUserID)}}
+                                               {{if or $.Permission.IsAdmin (eq .Poster.ID $.SignedUserID)}}
                                                        <div class="item action">
                                                                <a class="edit-content" href="#"><i class="octicon octicon-pencil"></i></a>
                                                                <a class="delete-comment" href="#" data-comment-id={{.HashTag}} data-url="{{$.RepoLink}}/comments/{{.ID}}/delete" data-locale="{{$.i18n.Tr "repo.issues.delete_comment_confirm"}}"><i class="octicon octicon-x"></i></a>
index fa7a05512ddec4641bda67a6833bf2b7c9fd34e9..ee8ed0e1a1e9426a7043b0f35dae5e473f35fb71 100644 (file)
@@ -2,7 +2,7 @@
        <div class="ui segment metas">
                {{template "repo/issue/branch_selector_field" .}}
 
-               <div class="ui {{if not .IsRepositoryWriter}}disabled{{end}} floating jump select-label dropdown">
+               <div class="ui {{if not .IsIssueWriter}}disabled{{end}} floating jump select-label dropdown">
                        <span class="text">
                                <strong>{{.i18n.Tr "repo.issues.new.labels"}}</strong>
                                <span class="octicon octicon-gear"></span>
@@ -27,7 +27,7 @@
 
                <div class="ui divider"></div>
 
-               <div class="ui {{if not .IsRepositoryWriter}}disabled{{end}} floating jump select-milestone dropdown">
+               <div class="ui {{if not .IsIssueWriter}}disabled{{end}} floating jump select-milestone dropdown">
                        <span class="text">
                                <strong>{{.i18n.Tr "repo.issues.new.milestone"}}</strong>
                                <span class="octicon octicon-gear"></span>
@@ -68,7 +68,7 @@
                <div class="ui divider"></div>
 
                <input id="assignee_id" name="assignee_id" type="hidden" value="{{.assignee_id}}">
-               <div class="ui {{if not .IsRepositoryWriter}}disabled{{end}} floating jump select-assignees-modify dropdown">
+               <div class="ui {{if not .IsIssueWriter}}disabled{{end}} floating jump select-assignees-modify dropdown">
                        <span class="text">
                                <strong>{{.i18n.Tr "repo.issues.new.assignees"}}</strong>
                                <span class="octicon octicon-gear"></span>
                                        {{if .Issue.IsOverdue}}
                                                <span style="color: red;">{{.i18n.Tr "repo.issues.due_date_overdue"}}</span>
                                        {{end}}
-                                       {{if and .IsSigned .IsRepositoryWriter}}
+                                       {{if .IsIssueWriter}}
                                                <br/>
                                                <a style="cursor:pointer;" onclick="toggleDeadlineForm();"><i class="edit icon"></i>{{$.i18n.Tr "repo.issues.due_date_form_edit"}}</a> -
                                                <a style="cursor:pointer;" onclick="updateDeadline('');"><i class="remove icon"></i>{{$.i18n.Tr "repo.issues.due_date_form_remove"}}</a>
                                <p><i>{{.i18n.Tr "repo.issues.due_date_not_set"}}</i></p>
                        {{end}}
 
-                       {{if and .IsSigned .IsRepositoryWriter}}
+                       {{if .IsIssueWriter}}
                                <div {{if ne .Issue.DeadlineUnix 0}} style="display: none;"{{end}} id="deadlineForm">
                                        <form class="ui fluid action input" action="{{AppSubUrl}}/api/v1/repos/{{.Repository.Owner.Name}}/{{.Repository.Name}}/issues/{{.Issue.Index}}" method="post" id="update-issue-deadline-form" onsubmit="setDeadline();return false;">
                                                {{$.CsrfTokenHtml}}
index d69f699ac18abfa73778c70d22ff6c03ffaa6ed9..b9edd0b562063fb19069be0ebfadf767872681a3 100644 (file)
@@ -6,7 +6,7 @@
                                <input value="{{.Issue.Title}}">
                        </div>
                </h1>
-               {{if .IsIssueOwner}}
+               {{if or .IsIssueWriter .IsIssuePoster}}
                        <div class="four wide column">
                                <div class="edit-zone text right">
                                        <div id="edit-title" class="ui basic green not-in-edit button">{{.i18n.Tr "repo.issues.edit"}}</div>
index 7337b8ca0ed2331fbad92de6483d85d4e65ac0c6..7ee58c6b107f298aa98d1f3b85c7816fde1a7649 100644 (file)
@@ -5,7 +5,7 @@
                {{template "base/alert" .}}
                <h2 class="ui header">
                        {{.i18n.Tr "repo.release.releases"}}
-                       {{if .IsRepositoryWriter}}
+                       {{if .CanCreateRelease}}
                                <div class="ui right">
                                        <a class="ui small green button" href="{{$.RepoLink}}/releases/new">
                                                {{.i18n.Tr "repo.release.new_release"}}
@@ -41,7 +41,7 @@
                                                                <a href="{{$.RepoLink}}/src/tag/{{.TagName | EscapePound}}" rel="nofollow"><i class="tag icon"></i> {{.TagName}}</a>
                                                        </h4>
                                                        <div class="download">
-                                                       {{if $.Repository.UnitEnabled $.UnitTypeCode}}
+                                                       {{if $.Permission.CanRead $.UnitTypeCode}}
                                                                <a href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow"><i class="code icon"></i> {{ShortSha .Sha1}}</a>
                                                                <a href="{{$.RepoLink}}/archive/{{.TagName | EscapePound}}.zip" rel="nofollow"><i class="octicon octicon-file-zip"></i> ZIP</a>
                                                                <a href="{{$.RepoLink}}/archive/{{.TagName | EscapePound}}.tar.gz"><i class="octicon octicon-file-zip"></i> TAR.GZ</a>
@@ -50,7 +50,7 @@
                                                {{else}}
                                                        <h3>
                                                                <a href="{{$.RepoLink}}/src/tag/{{.TagName | EscapePound}}">{{.Title}}</a>
-                                                               {{if $.IsRepositoryWriter}}<small>(<a href="{{$.RepoLink}}/releases/edit/{{.TagName | EscapePound}}" rel="nofollow">{{$.i18n.Tr "repo.release.edit"}}</a>)</small>{{end}}
+                                                               {{if $.CanCreateRelease}}<small>(<a href="{{$.RepoLink}}/releases/edit/{{.TagName | EscapePound}}" rel="nofollow">{{$.i18n.Tr "repo.release.edit"}}</a>)</small>{{end}}
                                                        </h3>
                                                        <p class="text grey">
                                                                <span class="author">
@@ -66,7 +66,7 @@
                                                        <div class="download">
                                                                <h2>{{$.i18n.Tr "repo.release.downloads"}}</h2>
                                                                <ul class="list">
-                                                                       {{if $.Repository.UnitEnabled $.UnitTypeCode}}
+                                                                       {{if $.Permission.CanRead $.UnitTypeCode}}
                                                                        <li>
                                                                                <a href="{{$.RepoLink}}/archive/{{.TagName | EscapePound}}.zip" rel="nofollow"><strong><i class="octicon octicon-file-zip"></i> {{$.i18n.Tr "repo.release.source_code"}} (ZIP)</strong></a>
                                                                        </li>
index 744fa820620a742832e5c7f7ff14a43b7e078933..7994260f6ecaa32573b09f86fbcc48833a535e10 100644 (file)
                </div>
                {{end}}
 
-               {{if .IsRepositoryOwner}}
+               {{if .Permission.IsOwner}}
                <h4 class="ui top attached warning header">
                        {{.i18n.Tr "repo.settings.danger_zone"}}
                </h4>
                                </div>
                        </div>
 
-                       {{if .Repository.UnitEnabled $.UnitTypeWiki}}
+                       {{if .Permission.CanRead $.UnitTypeWiki}}
                                <div class="ui divider"></div>
 
                                <div class="item">
        </div>
 </div>
 
-{{if .IsRepositoryOwner}}
+{{if .Permission.IsOwner}}
        {{if .Repository.IsMirror}}
        <div class="ui small modal" id="convert-repo-modal">
                <div class="header">
index a08481aaface1f76919703b8a57489e7288a0066..7f85c702b5440e1ed3dc295a83c7332b5c58d561 100644 (file)
@@ -1,7 +1,7 @@
 {{if .PageIsSettingsHooksEdit}}
        <h4 class="ui top attached header">
                {{.i18n.Tr "repo.settings.recent_deliveries"}}
-               {{if .IsRepositoryAdmin}}
+               {{if .Permission.IsAdmin}}
                        <div class="ui right">
                                <button class="ui teal tiny button poping up" id="test-delivery" data-content=
                                "{{.i18n.Tr "repo.settings.webhook.test_delivery_desc"}}" data-variation="inverted tiny" data-link="{{.Link}}/test" data-redirect="{{.Link}}">{{.i18n.Tr "repo.settings.webhook.test_delivery"}}</button>
index cd37f80b70e81ee7f06355f52150678947a35055..4ca1aa5cbb310030cef01f4143145c2211629cfd 100644 (file)
@@ -1,11 +1,11 @@
 <div class="ui segment sub-menu">
        <div class="ui two horizontal center link list">
-               {{if and (.Repository.UnitEnabled $.UnitTypeCode) (not .IsBareRepo)}}
+               {{if and (.Permission.CanRead $.UnitTypeCode) (not .IsBareRepo)}}
                        <div class="item{{if .PageIsCommits}} active{{end}}">
                                <a href="{{.RepoLink}}/commits{{if .IsViewBranch}}/branch{{else if .IsViewTag}}/tag{{else if .IsViewCommit}}/commit{{end}}/{{EscapePound .BranchName}}"><i class="octicon octicon-history"></i> <b>{{.CommitsCount}}</b> {{.i18n.Tr (TrN .i18n.Lang .CommitsCount "repo.commit" "repo.commits") }}</a>
                        </div>
                {{end}}
-               {{if and (.Repository.UnitEnabled $.UnitTypeCode) (not .IsBareRepo) }}
+               {{if and (.Permission.CanRead $.UnitTypeCode) (not .IsBareRepo) }}
                        <div class="item{{if .PageIsBranches}} active{{end}}">
                                <a href="{{.RepoLink}}/branches/"><i class="octicon octicon-git-branch"></i> <b>{{.BranchesCount}}</b> {{.i18n.Tr (TrN .i18n.Lang .BranchesCount "repo.branch" "repo.branches") }}</a>
                        </div>
index 2829b6137473172cf3387cab5663abc704880a52..61903423f390a7531382e8904114dded4a5d53bb 100644 (file)
@@ -4,7 +4,7 @@
        <div class="ui container">
                <div class="ui header">
                        {{.i18n.Tr "repo.wiki.pages"}}
-                       {{if and .IsRepositoryWriter (not .IsRepositoryMirror)}}
+                       {{if and .CanWriteWiki (not .IsRepositoryMirror)}}
                        <div class="ui right">
                                <a class="ui green small button" href="{{.RepoLink}}/wiki/_new">{{.i18n.Tr "repo.wiki.new_page_button"}}</a>
                        </div>
index 1b8d52a587b8663e221a5ac4527395db80acbded..0341e8067b1f26c580ea5f9ffc3b5353ca168f47 100644 (file)
@@ -6,7 +6,7 @@
                        <span class="mega-octicon octicon-book"></span>
                        <h2>{{.i18n.Tr "repo.wiki.welcome"}}</h2>
                        <p>{{.i18n.Tr "repo.wiki.welcome_desc"}}</p>
-                       {{if and .IsRepositoryWriter (not .Repository.IsMirror)}}
+                       {{if and .CanWriteWiki (not .Repository.IsMirror)}}
                                <a class="ui green button" href="{{.RepoLink}}/wiki/_new">{{.i18n.Tr "repo.wiki.create_first_page"}}</a>
                        {{end}}
                </div>
index 93b7c10640110aa3ebe06ad6bfc3af74a96c3b6d..dd2de2a041380256e2d03d897d16fb914c6d9934 100644 (file)
@@ -63,7 +63,7 @@
                                        </div>
                                </div>
                                <div class="eight wide right aligned column">
-                                       {{if and .IsRepositoryWriter (not .Repository.IsMirror)}}
+                                       {{if and .CanWriteWiki (not .Repository.IsMirror)}}
                                                <div class="ui right">
                                                        <a class="ui small button" href="{{.RepoLink}}/wiki/{{.PageURL}}/_edit">{{.i18n.Tr "repo.wiki.edit_page_button"}}</a>
                                                        <a class="ui green small button" href="{{.RepoLink}}/wiki/_new">{{.i18n.Tr "repo.wiki.new_page_button"}}</a>