fix: read privileged membership and the admin actions through a clean statement (T-12.1-38, T-12.1-39)
classes.fresh chained WithContext onto a lazy NewDB session, which clones the caller's statement with its conditions. A handle that carried a condition made IsPrivilegedMember answer false for a member of a privileged group, so the takeover guard would have let the request through. No caller passes such a handle today: the admin hooks hand over the write's transaction handle. - fresh builds the empty statement before anything is chained onto it - IsPrivilegedMember and PrivilegedGroupIDs read through fresh
This commit is contained in:
@@ -248,9 +248,13 @@ func ForceDeleteCleanup(ctx context.Context, db *gorm.DB, bucket *blob.Bucket, u
|
||||
}
|
||||
|
||||
// fresh returns db with an empty statement on ctx, so a query never continues
|
||||
// from conditions the caller's handle already carries.
|
||||
// from conditions the caller's handle already carries. A NewDB session is
|
||||
// lazy: GORM builds the empty statement on the next chain method, and a
|
||||
// WithContext chained directly onto it clones the caller's statement with its
|
||||
// conditions instead. Clauses() makes the empty statement exist first, so the
|
||||
// handle stays clean whatever is chained next.
|
||||
func fresh(ctx context.Context, db *gorm.DB) *gorm.DB {
|
||||
return db.Session(&gorm.Session{NewDB: true}).WithContext(ctx)
|
||||
return db.Session(&gorm.Session{NewDB: true, Context: ctx}).Clauses().Session(&gorm.Session{NewDB: true})
|
||||
}
|
||||
|
||||
// userIDs returns the ids of users, skipping nil and unsaved entries.
|
||||
|
||||
@@ -83,7 +83,7 @@ func PrivilegedGroupIDs(ctx context.Context, db *gorm.DB, codes []string) ([]uin
|
||||
if len(codes) == 0 {
|
||||
return ids, nil
|
||||
}
|
||||
err := db.Session(&gorm.Session{NewDB: true}).WithContext(ctx).
|
||||
err := fresh(ctx, db).
|
||||
Table("user_groups").
|
||||
Where("code IN ?", codes).
|
||||
Order("id").
|
||||
@@ -97,7 +97,8 @@ func PrivilegedGroupIDs(ctx context.Context, db *gorm.DB, codes []string) ([]uin
|
||||
// IsPrivilegedMember reports whether the user belongs to at least one group
|
||||
// whose code is in codes. A user in no group, and a group without a code,
|
||||
// give false. A deactivated user's membership counts: it survives the soft
|
||||
// delete.
|
||||
// delete. The membership is read through an empty statement on db's
|
||||
// connection, so conditions the caller's handle carries never narrow it.
|
||||
func IsPrivilegedMember(ctx context.Context, db *gorm.DB, codes []string, userID uint) (bool, error) {
|
||||
if db == nil {
|
||||
return false, fmt.Errorf("golem15.user: gorm handle is missing")
|
||||
@@ -105,7 +106,7 @@ func IsPrivilegedMember(ctx context.Context, db *gorm.DB, codes []string, userID
|
||||
if len(codes) == 0 || userID == 0 {
|
||||
return false, nil
|
||||
}
|
||||
held, err := UserGroupCodes(ctx, db.Session(&gorm.Session{NewDB: true}), userID)
|
||||
held, err := UserGroupCodes(ctx, fresh(ctx, db), userID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user