@@ -12,15 +12,17 @@ import (
1212 "github.com/gotify/server/v3/model"
1313)
1414
15+ var errCannotDeleteLastAdmin = errors .New ("cannot delete last admin" )
16+
1517// The UserDatabase interface for encapsulating database access.
16- type UserDatabase interface {
17- GetUsers () ([]* model.User , error )
18+ type UserDatabase [T UserDatabase [T ]] interface {
19+ Txn (fn func (txdb T ) error ) error
20+ GetUsers (condition ... any ) ([]* model.User , error )
1821 GetUserByID (id uint ) (* model.User , error )
1922 GetUserByName (name string ) (* model.User , error )
2023 DeleteUserByID (id uint ) error
2124 UpdateUser (user * model.User ) error
2225 CreateUser (user * model.User ) error
23- CountUser (condition ... any ) (int64 , error )
2426}
2527
2628// UserChangeNotifier notifies listeners for user changes.
@@ -58,8 +60,8 @@ func (c *UserChangeNotifier) fireUserAdded(uid uint) error {
5860}
5961
6062// The UserAPI provides handlers for managing users.
61- type UserAPI struct {
62- DB UserDatabase
63+ type UserAPI [ T UserDatabase [ T ]] struct {
64+ DB T
6365 PasswordStrength int
6466 UserChangeNotifier * UserChangeNotifier
6567 Registration bool
@@ -90,7 +92,7 @@ type UserAPI struct {
9092// description: Forbidden
9193// schema:
9294// $ref: "#/definitions/Error"
93- func (a * UserAPI ) GetUsers (ctx * gin.Context ) {
95+ func (a * UserAPI [ T ] ) GetUsers (ctx * gin.Context ) {
9496 users , err := a .DB .GetUsers ()
9597 if success := successOrAbort (ctx , 500 , err ); ! success {
9698 return
@@ -126,7 +128,7 @@ func (a *UserAPI) GetUsers(ctx *gin.Context) {
126128// description: Forbidden
127129// schema:
128130// $ref: "#/definitions/Error"
129- func (a * UserAPI ) GetCurrentUser (ctx * gin.Context ) {
131+ func (a * UserAPI [ T ] ) GetCurrentUser (ctx * gin.Context ) {
130132 user , err := a .DB .GetUserByID (auth .GetUserID (ctx ))
131133 if success := successOrAbort (ctx , 500 , err ); ! success {
132134 return
@@ -185,7 +187,7 @@ func (a *UserAPI) GetCurrentUser(ctx *gin.Context) {
185187// description: Forbidden
186188// schema:
187189// $ref: "#/definitions/Error"
188- func (a * UserAPI ) CreateUser (ctx * gin.Context ) {
190+ func (a * UserAPI [ T ] ) CreateUser (ctx * gin.Context ) {
189191 user := model.CreateUserExternal {}
190192 if err := ctx .Bind (& user ); err == nil {
191193 if err := password .ValidateNewPassword (user .Pass ); err != nil {
@@ -286,7 +288,7 @@ func (a *UserAPI) CreateUser(ctx *gin.Context) {
286288// description: Not Found
287289// schema:
288290// $ref: "#/definitions/Error"
289- func (a * UserAPI ) GetUserByID (ctx * gin.Context ) {
291+ func (a * UserAPI [ T ] ) GetUserByID (ctx * gin.Context ) {
290292 withID (ctx , "id" , func (id uint ) {
291293 user , err := a .DB .GetUserByID (id )
292294 if success := successOrAbort (ctx , 500 , err ); ! success {
@@ -336,26 +338,41 @@ func (a *UserAPI) GetUserByID(ctx *gin.Context) {
336338// description: Not Found
337339// schema:
338340// $ref: "#/definitions/Error"
339- func (a * UserAPI ) DeleteUserByID (ctx * gin.Context ) {
341+ func (a * UserAPI [ T ] ) DeleteUserByID (ctx * gin.Context ) {
340342 withID (ctx , "id" , func (id uint ) {
341343 user , err := a .DB .GetUserByID (id )
342344 if success := successOrAbort (ctx , 500 , err ); ! success {
343345 return
344346 }
345347 if user != nil {
346- adminCount , err := a .DB .CountUser (& model.User {Admin : true })
347- if success := successOrAbort (ctx , 500 , err ); ! success {
348- return
349- }
350- if user .Admin && adminCount == 1 {
351- ctx .AbortWithError (400 , errors .New ("cannot delete last admin" ))
352- return
353- }
354- if err := a .UserChangeNotifier .fireUserDeleted (id ); err != nil {
355- ctx .AbortWithError (500 , err )
356- return
348+ for range 3 {
349+ commitError := false
350+ err = a .DB .Txn (func (txdb T ) error {
351+ if success := successOrAbort (ctx , 500 , txdb .DeleteUserByID (id )); ! success {
352+ return err
353+ }
354+ anotherAdmin , err := txdb .GetUsers (& model.User {Admin : true })
355+ if success := successOrAbort (ctx , 500 , err ); ! success {
356+ return err
357+ }
358+ if user .Admin && len (anotherAdmin ) == 0 {
359+ ctx .AbortWithError (400 , errCannotDeleteLastAdmin )
360+ return errCannotDeleteLastAdmin
361+ }
362+ if success := successOrAbort (ctx , 500 , a .UserChangeNotifier .fireUserDeleted (id )); ! success {
363+ return err
364+ }
365+ commitError = true
366+ return nil
367+ })
368+ if ! commitError || err == nil {
369+ break
370+ }
371+ if err != nil {
372+ ctx .AbortWithError (500 , err )
373+ return
374+ }
357375 }
358- successOrAbort (ctx , 500 , a .DB .DeleteUserByID (id ))
359376 } else {
360377 ctx .AbortWithError (404 , errors .New ("user does not exist" ))
361378 }
@@ -395,7 +412,7 @@ func (a *UserAPI) DeleteUserByID(ctx *gin.Context) {
395412// description: Forbidden
396413// schema:
397414// $ref: "#/definitions/Error"
398- func (a * UserAPI ) ChangePassword (ctx * gin.Context ) {
415+ func (a * UserAPI [ T ] ) ChangePassword (ctx * gin.Context ) {
399416 pw := model.UserExternalPass {}
400417 if err := ctx .Bind (& pw ); err == nil {
401418 if err := password .ValidateNewPassword (pw .Pass ); err != nil {
@@ -461,7 +478,7 @@ func (a *UserAPI) ChangePassword(ctx *gin.Context) {
461478// description: Not Found
462479// schema:
463480// $ref: "#/definitions/Error"
464- func (a * UserAPI ) UpdateUserByID (ctx * gin.Context ) {
481+ func (a * UserAPI [ T ] ) UpdateUserByID (ctx * gin.Context ) {
465482 withID (ctx , "id" , func (id uint ) {
466483 var updatedUser * model.UpdateUserExternal
467484 if err := ctx .Bind (& updatedUser ); err == nil {
@@ -470,15 +487,7 @@ func (a *UserAPI) UpdateUserByID(ctx *gin.Context) {
470487 return
471488 }
472489 if dbUser != nil {
473- adminCount , err := a .DB .CountUser (& model.User {Admin : true })
474- if success := successOrAbort (ctx , 500 , err ); ! success {
475- return
476- }
477- if ! updatedUser .Admin && dbUser .Admin && adminCount == 1 {
478- ctx .AbortWithError (400 , errors .New ("cannot delete last admin" ))
479- return
480- }
481-
490+ dbUserWasAdmin := dbUser .Admin
482491 dbUser .Name = updatedUser .Name
483492 dbUser .Admin = updatedUser .Admin
484493
@@ -494,10 +503,37 @@ func (a *UserAPI) UpdateUserByID(ctx *gin.Context) {
494503 }
495504 dbUser .Pass = pw
496505 }
497- if success := successOrAbort (ctx , 500 , a .DB .UpdateUser (dbUser )); ! success {
498- return
506+
507+ for range 3 {
508+ commitError := false
509+
510+ err = a .DB .Txn (func (txdb T ) error {
511+ if success := successOrAbort (ctx , 500 , txdb .UpdateUser (dbUser )); ! success {
512+ return err
513+ }
514+
515+ anotherAdmin , err := txdb .GetUsers (& model.User {Admin : true })
516+ if success := successOrAbort (ctx , 500 , err ); ! success {
517+ return err
518+ }
519+ if ! updatedUser .Admin && dbUserWasAdmin && len (anotherAdmin ) == 0 {
520+ ctx .AbortWithError (400 , errCannotDeleteLastAdmin )
521+ return errCannotDeleteLastAdmin
522+ }
523+
524+ commitError = true
525+
526+ return nil
527+ })
528+
529+ if ! commitError || err == nil {
530+ break
531+ }
532+ }
533+
534+ if err == nil {
535+ ctx .JSON (200 , toExternalUser (dbUser ))
499536 }
500- ctx .JSON (200 , toExternalUser (dbUser ))
501537 } else {
502538 ctx .AbortWithError (404 , errors .New ("user does not exist" ))
503539 }
0 commit comments