summaryrefslogtreecommitdiffstats
path: root/models
diff options
context:
space:
mode:
authorDavid Schneiderbauer <daviian@users.noreply.github.com>2018-06-19 21:44:33 +0200
committertechknowlogick <techknowlogick@users.noreply.github.com>2018-06-19 15:44:33 -0400
commita93f13849cd7f54029f1dc17b642d024b98ee71e (patch)
tree99340a1daec2fa3a3db14ed630b57c6bc3984fbe /models
parent467ff4d34302f6ecab959d61bf3944a2bdf125d0 (diff)
downloadgitea-a93f13849cd7f54029f1dc17b642d024b98ee71e.tar.gz
gitea-a93f13849cd7f54029f1dc17b642d024b98ee71e.zip
Fix not removed watches on unallowed repositories (#4201)
Diffstat (limited to 'models')
-rw-r--r--models/issue_watch.go12
-rw-r--r--models/migrations/migrations.go2
-rw-r--r--models/migrations/v67.go158
-rw-r--r--models/org_team.go49
-rw-r--r--models/repo.go3
-rw-r--r--models/repo_collaboration.go9
6 files changed, 233 insertions, 0 deletions
diff --git a/models/issue_watch.go b/models/issue_watch.go
index 69e218af01..3e7d24821b 100644
--- a/models/issue_watch.go
+++ b/models/issue_watch.go
@@ -71,3 +71,15 @@ func getIssueWatchers(e Engine, issueID int64) (watches []*IssueWatch, err error
Find(&watches)
return
}
+
+func removeIssueWatchersByRepoID(e Engine, userID int64, repoID int64) error {
+ iw := &IssueWatch{
+ IsWatching: false,
+ }
+ _, err := e.
+ Join("INNER", "issue", "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?", repoID).
+ Cols("is_watching", "updated_unix").
+ Where("`issue_watch`.user_id = ?", userID).
+ Update(iw)
+ return err
+}
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 1300065ab4..2537e5712b 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -186,6 +186,8 @@ var migrations = []Migration{
NewMigration("add u2f", addU2FReg),
// v66 -> v67
NewMigration("add login source id column for public_key table", addLoginSourceIDToPublicKeyTable),
+ // v67 -> v68
+ NewMigration("remove stale watches", removeStaleWatches),
}
// Migrate database to current version
diff --git a/models/migrations/v67.go b/models/migrations/v67.go
new file mode 100644
index 0000000000..2782219191
--- /dev/null
+++ b/models/migrations/v67.go
@@ -0,0 +1,158 @@
+// 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 migrations
+
+import (
+ "code.gitea.io/gitea/modules/setting"
+
+ "github.com/go-xorm/xorm"
+)
+
+func removeStaleWatches(x *xorm.Engine) error {
+ type Watch struct {
+ ID int64
+ UserID int64
+ RepoID int64
+ }
+
+ type IssueWatch struct {
+ ID int64
+ UserID int64
+ RepoID int64
+ IsWatching bool
+ }
+
+ type Repository struct {
+ ID int64
+ IsPrivate bool
+ OwnerID int64
+ }
+
+ type Access struct {
+ UserID int64
+ RepoID int64
+ Mode int
+ }
+
+ const (
+ // AccessModeNone no access
+ AccessModeNone int = iota // 0
+ // AccessModeRead read access
+ AccessModeRead // 1
+ )
+
+ accessLevel := func(userID int64, repo *Repository) (int, error) {
+ mode := AccessModeNone
+ if !repo.IsPrivate {
+ mode = AccessModeRead
+ }
+
+ if userID == 0 {
+ return mode, nil
+ }
+
+ if userID == repo.OwnerID {
+ return 4, nil
+ }
+
+ a := &Access{UserID: userID, RepoID: repo.ID}
+ if has, err := x.Get(a); !has || err != nil {
+ return mode, err
+ }
+ return a.Mode, nil
+ }
+
+ sess := x.NewSession()
+ defer sess.Close()
+ if err := sess.Begin(); err != nil {
+ return err
+ }
+
+ repoCache := make(map[int64]*Repository)
+ err := x.BufferSize(setting.IterateBufferSize).Iterate(new(Watch),
+ func(idx int, bean interface{}) error {
+ watch := bean.(*Watch)
+
+ repo := repoCache[watch.RepoID]
+ if repo == nil {
+ repo = &Repository{
+ ID: watch.RepoID,
+ }
+ if _, err := x.Get(repo); err != nil {
+ return err
+ }
+ repoCache[watch.RepoID] = repo
+ }
+
+ // Remove watches from now unaccessible repositories
+ mode, err := accessLevel(watch.UserID, repo)
+ if err != nil {
+ return err
+ }
+ has := AccessModeRead <= mode
+ if has {
+ return nil
+ }
+
+ if _, err = sess.Delete(&Watch{0, watch.UserID, repo.ID}); err != nil {
+ return err
+ }
+ _, err = sess.Exec("UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?", repo.ID)
+
+ return err
+ })
+ if err != nil {
+ return err
+ }
+
+ repoCache = make(map[int64]*Repository)
+ err = x.BufferSize(setting.IterateBufferSize).
+ Distinct("issue_watch.user_id", "issue.repo_id").
+ Join("INNER", "issue", "issue_watch.issue_id = issue.id").
+ Where("issue_watch.is_watching = ?", true).
+ Iterate(new(IssueWatch),
+ func(idx int, bean interface{}) error {
+ watch := bean.(*IssueWatch)
+
+ repo := repoCache[watch.RepoID]
+ if repo == nil {
+ repo = &Repository{
+ ID: watch.RepoID,
+ }
+ if _, err := x.Get(repo); err != nil {
+ return err
+ }
+ repoCache[watch.RepoID] = repo
+ }
+
+ // Remove issue watches from now unaccssible repositories
+ mode, err := accessLevel(watch.UserID, repo)
+ if err != nil {
+ return err
+ }
+ has := AccessModeRead <= mode
+ if has {
+ return nil
+ }
+
+ iw := &IssueWatch{
+ IsWatching: false,
+ }
+
+ _, err = sess.
+ Join("INNER", "issue", "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?", watch.RepoID).
+ Cols("is_watching", "updated_unix").
+ Where("`issue_watch`.user_id = ?", watch.UserID).
+ Update(iw)
+
+ return err
+
+ })
+ if err != nil {
+ return err
+ }
+
+ return sess.Commit()
+}
diff --git a/models/org_team.go b/models/org_team.go
index 9d8a031418..5ea6e76cd9 100644
--- a/models/org_team.go
+++ b/models/org_team.go
@@ -178,6 +178,11 @@ func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (e
if err = watchRepo(e, teamUser.UID, repo.ID, false); err != nil {
return err
}
+
+ // Remove all IssueWatches a user has subscribed to in the repositories
+ if err := removeIssueWatchersByRepoID(e, teamUser.UID, repo.ID); err != nil {
+ return err
+ }
}
return nil
@@ -374,11 +379,34 @@ func DeleteTeam(t *Team) error {
return err
}
+ if err := t.getMembers(sess); err != nil {
+ return err
+ }
+
// Delete all accesses.
for _, repo := range t.Repos {
if err := repo.recalculateTeamAccesses(sess, t.ID); err != nil {
return err
}
+
+ // Remove watches from all users and now unaccessible repos
+ for _, user := range t.Members {
+ has, err := hasAccess(sess, user.ID, repo, AccessModeRead)
+ if err != nil {
+ return err
+ } else if has {
+ continue
+ }
+
+ if err = watchRepo(sess, user.ID, repo.ID, false); err != nil {
+ return err
+ }
+
+ // Remove all IssueWatches a user has subscribed to in the repositories
+ if err = removeIssueWatchersByRepoID(sess, user.ID, repo.ID); err != nil {
+ return err
+ }
+ }
}
// Delete team-repo
@@ -518,6 +546,10 @@ func AddTeamMember(team *Team, userID int64) error {
if err := repo.recalculateTeamAccesses(sess, 0); err != nil {
return err
}
+
+ if err = watchRepo(sess, userID, repo.ID, true); err != nil {
+ return err
+ }
}
return sess.Commit()
@@ -558,6 +590,23 @@ func removeTeamMember(e *xorm.Session, team *Team, userID int64) error {
if err := repo.recalculateTeamAccesses(e, 0); err != nil {
return err
}
+
+ // Remove watches from now unaccessible
+ has, err := hasAccess(e, userID, repo, AccessModeRead)
+ if err != nil {
+ return err
+ } else if has {
+ continue
+ }
+
+ if err = watchRepo(e, userID, repo.ID, false); err != nil {
+ return err
+ }
+
+ // Remove all IssueWatches a user has subscribed to in the repositories
+ if err := removeIssueWatchersByRepoID(e, userID, repo.ID); err != nil {
+ return err
+ }
}
// Check if the user is a member of any team in the organization.
diff --git a/models/repo.go b/models/repo.go
index f4923cf4a9..7f2be502a4 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -1851,6 +1851,9 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
if _, err = sess.In("issue_id", issueIDs).Delete(&Reaction{}); err != nil {
return err
}
+ if _, err = sess.In("issue_id", issueIDs).Delete(&IssueWatch{}); err != nil {
+ return err
+ }
attachments := make([]*Attachment, 0, 5)
if err = sess.
diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go
index 0448149e6a..9d2935d581 100644
--- a/models/repo_collaboration.go
+++ b/models/repo_collaboration.go
@@ -172,5 +172,14 @@ func (repo *Repository) DeleteCollaboration(uid int64) (err error) {
return err
}
+ if err = watchRepo(sess, uid, repo.ID, false); err != nil {
+ return err
+ }
+
+ // Remove all IssueWatches a user has subscribed to in the repository
+ if err := removeIssueWatchersByRepoID(sess, uid, repo.ID); err != nil {
+ return err
+ }
+
return sess.Commit()
}