Skip to content

Commit 151563f

Browse files
committed
Merge remote-tracking branch 'giteaofficial/main'
* giteaofficial/main: Prevent 500 when there is an error during new auth source post (go-gitea#19041) Update the webauthn_credential_id_sequence in Postgres (go-gitea#19048) If rendering has failed due to a net.OpError stop rendering (attempt 2) (go-gitea#19049) use xorm builder for models.getReviewers() (go-gitea#19033) RSS/Atom support for Orgs (go-gitea#17714) Fix flag validation (go-gitea#19046) Improve SyncMirrors logging (go-gitea#19045)
2 parents f59b171 + a223bc8 commit 151563f

File tree

17 files changed

+264
-174
lines changed

17 files changed

+264
-174
lines changed

cmd/cmd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func argsSet(c *cli.Context, args ...string) error {
3131
return errors.New(a + " is not set")
3232
}
3333

34-
if util.IsEmptyString(a) {
34+
if util.IsEmptyString(c.String(a)) {
3535
return errors.New(a + " is required")
3636
}
3737
}

docs/content/doc/developers/guidelines-backend.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ To maintain understandable code and avoid circular dependencies it is important
4242
- `modules/setting`: Store all system configurations read from ini files and has been referenced by everywhere. But they should be used as function parameters when possible.
4343
- `modules/git`: Package to interactive with `Git` command line or Gogit package.
4444
- `public`: Compiled frontend files (javascript, images, css, etc.)
45-
- `routers`: Handling of server requests. As it uses other Gitea packages to serve the request, other packages (models, modules or services) shall not depend on routers.
45+
- `routers`: Handling of server requests. As it uses other Gitea packages to serve the request, other packages (models, modules or services) must not depend on routers.
4646
- `routers/api` Contains routers for `/api/v1` aims to handle RESTful API requests.
4747
- `routers/install` Could only respond when system is in INSTALL mode (INSTALL_LOCK=false).
4848
- `routers/private` will only be invoked by internal sub commands, especially `serv` and `hooks`.
@@ -106,10 +106,20 @@ i.e. `servcies/user`, `models/repository`.
106106
Since there are some packages which use the same package name, it is possible that you find packages like `modules/user`, `models/user`, and `services/user`. When these packages are imported in one Go file, it's difficult to know which package we are using and if it's a variable name or an import name. So, we always recommend to use import aliases. To differ from package variables which are commonly in camelCase, just use **snake_case** for import aliases.
107107
i.e. `import user_service "code.gitea.io/gitea/services/user"`
108108

109+
### Important Gotchas
110+
111+
- Never write `x.Update(exemplar)` without an explicit `WHERE` clause:
112+
- This will cause all rows in the table to be updated with the non-zero values of the exemplar - including IDs.
113+
- You should usually write `x.ID(id).Update(exemplar)`.
114+
- If during a migration you are inserting into a table using `x.Insert(exemplar)` where the ID is preset:
115+
- You will need to ``SET IDENTITY_INSERT `table` ON`` for the MSSQL variant (the migration will fail otherwise)
116+
- However, you will also need to update the id sequence for postgres - the migration will silently pass here but later insertions will fail:
117+
``SELECT setval('table_name_id_seq', COALESCE((SELECT MAX(id)+1 FROM `table_name`), 1), false)``
118+
109119
### Future Tasks
110120

111121
Currently, we are creating some refactors to do the following things:
112122

113123
- Correct that codes which doesn't follow the rules.
114124
- There are too many files in `models`, so we are moving some of them into a sub package `models/xxx`.
115-
- Some `modules` sub packages should be moved to `services` because they depends on `models`.
125+
- Some `modules` sub packages should be moved to `services` because they depend on `models`.

models/action.go

Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"code.gitea.io/gitea/modules/git"
2323
"code.gitea.io/gitea/modules/log"
2424
"code.gitea.io/gitea/modules/setting"
25+
"code.gitea.io/gitea/modules/structs"
2526
"code.gitea.io/gitea/modules/timeutil"
2627
"code.gitea.io/gitea/modules/util"
2728

@@ -315,29 +316,36 @@ func (a *Action) GetIssueContent() string {
315316

316317
// GetFeedsOptions options for retrieving feeds
317318
type GetFeedsOptions struct {
318-
RequestedUser *user_model.User // the user we want activity for
319-
RequestedTeam *Team // the team we want activity for
320-
Actor *user_model.User // the user viewing the activity
321-
IncludePrivate bool // include private actions
322-
OnlyPerformedBy bool // only actions performed by requested user
323-
IncludeDeleted bool // include deleted actions
324-
Date string // the day we want activity for: YYYY-MM-DD
319+
db.ListOptions
320+
RequestedUser *user_model.User // the user we want activity for
321+
RequestedTeam *Team // the team we want activity for
322+
RequestedRepo *repo_model.Repository // the repo we want activity for
323+
Actor *user_model.User // the user viewing the activity
324+
IncludePrivate bool // include private actions
325+
OnlyPerformedBy bool // only actions performed by requested user
326+
IncludeDeleted bool // include deleted actions
327+
Date string // the day we want activity for: YYYY-MM-DD
325328
}
326329

327330
// GetFeeds returns actions according to the provided options
328331
func GetFeeds(opts GetFeedsOptions) ([]*Action, error) {
329-
if !activityReadable(opts.RequestedUser, opts.Actor) {
330-
return make([]*Action, 0), nil
332+
if opts.RequestedUser == nil && opts.RequestedTeam == nil && opts.RequestedRepo == nil {
333+
return nil, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
331334
}
332335

333336
cond, err := activityQueryCondition(opts)
334337
if err != nil {
335338
return nil, err
336339
}
337340

338-
actions := make([]*Action, 0, setting.UI.FeedPagingNum)
341+
sess := db.GetEngine(db.DefaultContext).Where(cond)
339342

340-
if err := db.GetEngine(db.DefaultContext).Limit(setting.UI.FeedPagingNum).Desc("created_unix").Where(cond).Find(&actions); err != nil {
343+
opts.SetDefaultValues()
344+
sess = db.SetSessionPagination(sess, &opts)
345+
346+
actions := make([]*Action, 0, opts.PageSize)
347+
348+
if err := sess.Desc("created_unix").Find(&actions); err != nil {
341349
return nil, fmt.Errorf("Find: %v", err)
342350
}
343351

@@ -349,41 +357,44 @@ func GetFeeds(opts GetFeedsOptions) ([]*Action, error) {
349357
}
350358

351359
func activityReadable(user, doer *user_model.User) bool {
352-
var doerID int64
353-
if doer != nil {
354-
doerID = doer.ID
355-
}
356-
if doer == nil || !doer.IsAdmin {
357-
if user.KeepActivityPrivate && doerID != user.ID {
358-
return false
359-
}
360-
}
361-
return true
360+
return !user.KeepActivityPrivate ||
361+
doer != nil && (doer.IsAdmin || user.ID == doer.ID)
362362
}
363363

364364
func activityQueryCondition(opts GetFeedsOptions) (builder.Cond, error) {
365365
cond := builder.NewCond()
366366

367-
var repoIDs []int64
368-
var actorID int64
369-
if opts.Actor != nil {
370-
actorID = opts.Actor.ID
367+
if opts.RequestedTeam != nil && opts.RequestedUser == nil {
368+
org, err := user_model.GetUserByID(opts.RequestedTeam.OrgID)
369+
if err != nil {
370+
return nil, err
371+
}
372+
opts.RequestedUser = org
373+
}
374+
375+
// check activity visibility for actor ( similar to activityReadable() )
376+
if opts.Actor == nil {
377+
cond = cond.And(builder.In("act_user_id",
378+
builder.Select("`user`.id").Where(
379+
builder.Eq{"keep_activity_private": false, "visibility": structs.VisibleTypePublic},
380+
).From("`user`"),
381+
))
382+
} else if !opts.Actor.IsAdmin {
383+
cond = cond.And(builder.In("act_user_id",
384+
builder.Select("`user`.id").Where(
385+
builder.Eq{"keep_activity_private": false}.
386+
And(builder.In("visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited))).
387+
Or(builder.Eq{"id": opts.Actor.ID}).From("`user`"),
388+
))
371389
}
372390

373391
// check readable repositories by doer/actor
374392
if opts.Actor == nil || !opts.Actor.IsAdmin {
375-
if opts.RequestedUser.IsOrganization() {
376-
env, err := OrgFromUser(opts.RequestedUser).AccessibleReposEnv(actorID)
377-
if err != nil {
378-
return nil, fmt.Errorf("AccessibleReposEnv: %v", err)
379-
}
380-
if repoIDs, err = env.RepoIDs(1, opts.RequestedUser.NumRepos); err != nil {
381-
return nil, fmt.Errorf("GetUserRepositories: %v", err)
382-
}
383-
cond = cond.And(builder.In("repo_id", repoIDs))
384-
} else {
385-
cond = cond.And(builder.In("repo_id", AccessibleRepoIDsQuery(opts.Actor)))
386-
}
393+
cond = cond.And(builder.In("repo_id", AccessibleRepoIDsQuery(opts.Actor)))
394+
}
395+
396+
if opts.RequestedRepo != nil {
397+
cond = cond.And(builder.Eq{"repo_id": opts.RequestedRepo.ID})
387398
}
388399

389400
if opts.RequestedTeam != nil {
@@ -395,11 +406,14 @@ func activityQueryCondition(opts GetFeedsOptions) (builder.Cond, error) {
395406
cond = cond.And(builder.In("repo_id", teamRepoIDs))
396407
}
397408

398-
cond = cond.And(builder.Eq{"user_id": opts.RequestedUser.ID})
409+
if opts.RequestedUser != nil {
410+
cond = cond.And(builder.Eq{"user_id": opts.RequestedUser.ID})
399411

400-
if opts.OnlyPerformedBy {
401-
cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID})
412+
if opts.OnlyPerformedBy {
413+
cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID})
414+
}
402415
}
416+
403417
if !opts.IncludePrivate {
404418
cond = cond.And(builder.Eq{"is_private": false})
405419
}

models/action_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,46 @@ func TestGetFeeds2(t *testing.T) {
9393
assert.Len(t, actions, 0)
9494
}
9595

96+
func TestActivityReadable(t *testing.T) {
97+
tt := []struct {
98+
desc string
99+
user *user_model.User
100+
doer *user_model.User
101+
result bool
102+
}{{
103+
desc: "user should see own activity",
104+
user: &user_model.User{ID: 1},
105+
doer: &user_model.User{ID: 1},
106+
result: true,
107+
}, {
108+
desc: "anon should see activity if public",
109+
user: &user_model.User{ID: 1},
110+
result: true,
111+
}, {
112+
desc: "anon should NOT see activity",
113+
user: &user_model.User{ID: 1, KeepActivityPrivate: true},
114+
result: false,
115+
}, {
116+
desc: "user should see own activity if private too",
117+
user: &user_model.User{ID: 1, KeepActivityPrivate: true},
118+
doer: &user_model.User{ID: 1},
119+
result: true,
120+
}, {
121+
desc: "other user should NOT see activity",
122+
user: &user_model.User{ID: 1, KeepActivityPrivate: true},
123+
doer: &user_model.User{ID: 2},
124+
result: false,
125+
}, {
126+
desc: "admin should see activity",
127+
user: &user_model.User{ID: 1, KeepActivityPrivate: true},
128+
doer: &user_model.User{ID: 2, IsAdmin: true},
129+
result: true,
130+
}}
131+
for _, test := range tt {
132+
assert.Equal(t, test.result, activityReadable(test.user, test.doer), test.desc)
133+
}
134+
}
135+
96136
func TestNotifyWatchers(t *testing.T) {
97137
assert.NoError(t, unittest.PrepareTestDatabase())
98138

models/lfs.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,12 +193,13 @@ func LFSAutoAssociate(metas []*LFSMetaObject, user *user_model.User, repoID int6
193193
// admin can associate any LFS object to any repository, and we do not care about errors (eg: duplicated unique key),
194194
// even if error occurs, it won't hurt users and won't make things worse
195195
for i := range metas {
196+
p := lfs.Pointer{Oid: metas[i].Oid, Size: metas[i].Size}
196197
_, err = sess.Insert(&LFSMetaObject{
197-
Pointer: lfs.Pointer{Oid: metas[i].Oid, Size: metas[i].Size},
198+
Pointer: p,
198199
RepositoryID: repoID,
199200
})
200201
if err != nil {
201-
log.Warn("failed to insert LFS meta object into database, err=%v", err)
202+
log.Warn("failed to insert LFS meta object %-v for repo_id: %d into database, err=%v", p, repoID, err)
202203
}
203204
}
204205
}

models/migrations/v210.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,5 +174,11 @@ func remigrateU2FCredentials(x *xorm.Engine) error {
174174
regs = regs[:0]
175175
}
176176

177+
if x.Dialect().URI().DBType == schemas.POSTGRES {
178+
if _, err := x.Exec("SELECT setval('webauthn_credential_id_seq', COALESCE((SELECT MAX(id)+1 FROM `webauthn_credential`), 1), false)"); err != nil {
179+
return err
180+
}
181+
}
182+
177183
return nil
178184
}

models/repo.go

Lines changed: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -218,50 +218,44 @@ func getReviewers(ctx context.Context, repo *repo_model.Repository, doerID, post
218218
return nil, err
219219
}
220220

221-
var users []*user_model.User
222-
e := db.GetEngine(ctx)
221+
cond := builder.And(builder.Neq{"`user`.id": posterID})
223222

224223
if repo.IsPrivate || repo.Owner.Visibility == api.VisibleTypePrivate {
225224
// This a private repository:
226225
// Anyone who can read the repository is a requestable reviewer
227-
if err := e.
228-
SQL("SELECT * FROM `user` WHERE id in ("+
229-
"SELECT user_id FROM `access` WHERE repo_id = ? AND mode >= ? AND user_id != ?"+ // private org repos
230-
") ORDER BY name",
231-
repo.ID, perm.AccessModeRead,
232-
posterID).
233-
Find(&users); err != nil {
234-
return nil, err
235-
}
226+
227+
cond = cond.And(builder.In("`user`.id",
228+
builder.Select("user_id").From("access").Where(
229+
builder.Eq{"repo_id": repo.ID}.
230+
And(builder.Gte{"mode": perm.AccessModeRead}),
231+
),
232+
))
236233

237234
if repo.Owner.Type == user_model.UserTypeIndividual && repo.Owner.ID != posterID {
238235
// as private *user* repos don't generate an entry in the `access` table,
239236
// the owner of a private repo needs to be explicitly added.
240-
users = append(users, repo.Owner)
237+
cond = cond.Or(builder.Eq{"`user`.id": repo.Owner.ID})
241238
}
242239

243-
return users, nil
244-
}
245-
246-
// This is a "public" repository:
247-
// Any user that has read access, is a watcher or organization member can be requested to review
248-
if err := e.
249-
SQL("SELECT * FROM `user` WHERE id IN ( "+
250-
"SELECT user_id FROM `access` WHERE repo_id = ? AND mode >= ? "+
251-
"UNION "+
252-
"SELECT user_id FROM `watch` WHERE repo_id = ? AND mode IN (?, ?) "+
253-
"UNION "+
254-
"SELECT uid AS user_id FROM `org_user` WHERE org_id = ? "+
255-
") AND id != ? ORDER BY name",
256-
repo.ID, perm.AccessModeRead,
257-
repo.ID, repo_model.WatchModeNormal, repo_model.WatchModeAuto,
258-
repo.OwnerID,
259-
posterID).
260-
Find(&users); err != nil {
261-
return nil, err
262-
}
263-
264-
return users, nil
240+
} else {
241+
// This is a "public" repository:
242+
// Any user that has read access, is a watcher or organization member can be requested to review
243+
cond = cond.And(builder.And(builder.In("`user`.id",
244+
builder.Select("user_id").From("access").
245+
Where(builder.Eq{"repo_id": repo.ID}.
246+
And(builder.Gte{"mode": perm.AccessModeRead})),
247+
).Or(builder.In("`user`.id",
248+
builder.Select("user_id").From("watch").
249+
Where(builder.Eq{"repo_id": repo.ID}.
250+
And(builder.In("mode", repo_model.WatchModeNormal, repo_model.WatchModeAuto))),
251+
).Or(builder.In("`user`.id",
252+
builder.Select("uid").From("org_user").
253+
Where(builder.Eq{"org_id": repo.OwnerID}),
254+
)))))
255+
}
256+
257+
users := make([]*user_model.User, 0, 8)
258+
return users, db.GetEngine(ctx).Where(cond).OrderBy("name").Find(&users)
265259
}
266260

267261
// GetReviewers get all users can be requested to review:

0 commit comments

Comments
 (0)