Skip to content

Commit a93f138

Browse files
daviiantechknowlogick
authored andcommitted
Fix not removed watches on unallowed repositories (#4201)
1 parent 467ff4d commit a93f138

File tree

6 files changed

+233
-0
lines changed

6 files changed

+233
-0
lines changed

models/issue_watch.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,15 @@ func getIssueWatchers(e Engine, issueID int64) (watches []*IssueWatch, err error
7171
Find(&watches)
7272
return
7373
}
74+
75+
func removeIssueWatchersByRepoID(e Engine, userID int64, repoID int64) error {
76+
iw := &IssueWatch{
77+
IsWatching: false,
78+
}
79+
_, err := e.
80+
Join("INNER", "issue", "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?", repoID).
81+
Cols("is_watching", "updated_unix").
82+
Where("`issue_watch`.user_id = ?", userID).
83+
Update(iw)
84+
return err
85+
}

models/migrations/migrations.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ var migrations = []Migration{
186186
NewMigration("add u2f", addU2FReg),
187187
// v66 -> v67
188188
NewMigration("add login source id column for public_key table", addLoginSourceIDToPublicKeyTable),
189+
// v67 -> v68
190+
NewMigration("remove stale watches", removeStaleWatches),
189191
}
190192

191193
// Migrate database to current version

models/migrations/v67.go

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
// Copyright 2018 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package migrations
6+
7+
import (
8+
"code.gitea.io/gitea/modules/setting"
9+
10+
"github.com/go-xorm/xorm"
11+
)
12+
13+
func removeStaleWatches(x *xorm.Engine) error {
14+
type Watch struct {
15+
ID int64
16+
UserID int64
17+
RepoID int64
18+
}
19+
20+
type IssueWatch struct {
21+
ID int64
22+
UserID int64
23+
RepoID int64
24+
IsWatching bool
25+
}
26+
27+
type Repository struct {
28+
ID int64
29+
IsPrivate bool
30+
OwnerID int64
31+
}
32+
33+
type Access struct {
34+
UserID int64
35+
RepoID int64
36+
Mode int
37+
}
38+
39+
const (
40+
// AccessModeNone no access
41+
AccessModeNone int = iota // 0
42+
// AccessModeRead read access
43+
AccessModeRead // 1
44+
)
45+
46+
accessLevel := func(userID int64, repo *Repository) (int, error) {
47+
mode := AccessModeNone
48+
if !repo.IsPrivate {
49+
mode = AccessModeRead
50+
}
51+
52+
if userID == 0 {
53+
return mode, nil
54+
}
55+
56+
if userID == repo.OwnerID {
57+
return 4, nil
58+
}
59+
60+
a := &Access{UserID: userID, RepoID: repo.ID}
61+
if has, err := x.Get(a); !has || err != nil {
62+
return mode, err
63+
}
64+
return a.Mode, nil
65+
}
66+
67+
sess := x.NewSession()
68+
defer sess.Close()
69+
if err := sess.Begin(); err != nil {
70+
return err
71+
}
72+
73+
repoCache := make(map[int64]*Repository)
74+
err := x.BufferSize(setting.IterateBufferSize).Iterate(new(Watch),
75+
func(idx int, bean interface{}) error {
76+
watch := bean.(*Watch)
77+
78+
repo := repoCache[watch.RepoID]
79+
if repo == nil {
80+
repo = &Repository{
81+
ID: watch.RepoID,
82+
}
83+
if _, err := x.Get(repo); err != nil {
84+
return err
85+
}
86+
repoCache[watch.RepoID] = repo
87+
}
88+
89+
// Remove watches from now unaccessible repositories
90+
mode, err := accessLevel(watch.UserID, repo)
91+
if err != nil {
92+
return err
93+
}
94+
has := AccessModeRead <= mode
95+
if has {
96+
return nil
97+
}
98+
99+
if _, err = sess.Delete(&Watch{0, watch.UserID, repo.ID}); err != nil {
100+
return err
101+
}
102+
_, err = sess.Exec("UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?", repo.ID)
103+
104+
return err
105+
})
106+
if err != nil {
107+
return err
108+
}
109+
110+
repoCache = make(map[int64]*Repository)
111+
err = x.BufferSize(setting.IterateBufferSize).
112+
Distinct("issue_watch.user_id", "issue.repo_id").
113+
Join("INNER", "issue", "issue_watch.issue_id = issue.id").
114+
Where("issue_watch.is_watching = ?", true).
115+
Iterate(new(IssueWatch),
116+
func(idx int, bean interface{}) error {
117+
watch := bean.(*IssueWatch)
118+
119+
repo := repoCache[watch.RepoID]
120+
if repo == nil {
121+
repo = &Repository{
122+
ID: watch.RepoID,
123+
}
124+
if _, err := x.Get(repo); err != nil {
125+
return err
126+
}
127+
repoCache[watch.RepoID] = repo
128+
}
129+
130+
// Remove issue watches from now unaccssible repositories
131+
mode, err := accessLevel(watch.UserID, repo)
132+
if err != nil {
133+
return err
134+
}
135+
has := AccessModeRead <= mode
136+
if has {
137+
return nil
138+
}
139+
140+
iw := &IssueWatch{
141+
IsWatching: false,
142+
}
143+
144+
_, err = sess.
145+
Join("INNER", "issue", "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?", watch.RepoID).
146+
Cols("is_watching", "updated_unix").
147+
Where("`issue_watch`.user_id = ?", watch.UserID).
148+
Update(iw)
149+
150+
return err
151+
152+
})
153+
if err != nil {
154+
return err
155+
}
156+
157+
return sess.Commit()
158+
}

models/org_team.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,11 @@ func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (e
178178
if err = watchRepo(e, teamUser.UID, repo.ID, false); err != nil {
179179
return err
180180
}
181+
182+
// Remove all IssueWatches a user has subscribed to in the repositories
183+
if err := removeIssueWatchersByRepoID(e, teamUser.UID, repo.ID); err != nil {
184+
return err
185+
}
181186
}
182187

183188
return nil
@@ -374,11 +379,34 @@ func DeleteTeam(t *Team) error {
374379
return err
375380
}
376381

382+
if err := t.getMembers(sess); err != nil {
383+
return err
384+
}
385+
377386
// Delete all accesses.
378387
for _, repo := range t.Repos {
379388
if err := repo.recalculateTeamAccesses(sess, t.ID); err != nil {
380389
return err
381390
}
391+
392+
// Remove watches from all users and now unaccessible repos
393+
for _, user := range t.Members {
394+
has, err := hasAccess(sess, user.ID, repo, AccessModeRead)
395+
if err != nil {
396+
return err
397+
} else if has {
398+
continue
399+
}
400+
401+
if err = watchRepo(sess, user.ID, repo.ID, false); err != nil {
402+
return err
403+
}
404+
405+
// Remove all IssueWatches a user has subscribed to in the repositories
406+
if err = removeIssueWatchersByRepoID(sess, user.ID, repo.ID); err != nil {
407+
return err
408+
}
409+
}
382410
}
383411

384412
// Delete team-repo
@@ -518,6 +546,10 @@ func AddTeamMember(team *Team, userID int64) error {
518546
if err := repo.recalculateTeamAccesses(sess, 0); err != nil {
519547
return err
520548
}
549+
550+
if err = watchRepo(sess, userID, repo.ID, true); err != nil {
551+
return err
552+
}
521553
}
522554

523555
return sess.Commit()
@@ -558,6 +590,23 @@ func removeTeamMember(e *xorm.Session, team *Team, userID int64) error {
558590
if err := repo.recalculateTeamAccesses(e, 0); err != nil {
559591
return err
560592
}
593+
594+
// Remove watches from now unaccessible
595+
has, err := hasAccess(e, userID, repo, AccessModeRead)
596+
if err != nil {
597+
return err
598+
} else if has {
599+
continue
600+
}
601+
602+
if err = watchRepo(e, userID, repo.ID, false); err != nil {
603+
return err
604+
}
605+
606+
// Remove all IssueWatches a user has subscribed to in the repositories
607+
if err := removeIssueWatchersByRepoID(e, userID, repo.ID); err != nil {
608+
return err
609+
}
561610
}
562611

563612
// Check if the user is a member of any team in the organization.

models/repo.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1851,6 +1851,9 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
18511851
if _, err = sess.In("issue_id", issueIDs).Delete(&Reaction{}); err != nil {
18521852
return err
18531853
}
1854+
if _, err = sess.In("issue_id", issueIDs).Delete(&IssueWatch{}); err != nil {
1855+
return err
1856+
}
18541857

18551858
attachments := make([]*Attachment, 0, 5)
18561859
if err = sess.

models/repo_collaboration.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,5 +172,14 @@ func (repo *Repository) DeleteCollaboration(uid int64) (err error) {
172172
return err
173173
}
174174

175+
if err = watchRepo(sess, uid, repo.ID, false); err != nil {
176+
return err
177+
}
178+
179+
// Remove all IssueWatches a user has subscribed to in the repository
180+
if err := removeIssueWatchersByRepoID(sess, uid, repo.ID); err != nil {
181+
return err
182+
}
183+
175184
return sess.Commit()
176185
}

0 commit comments

Comments
 (0)