@@ -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,33 @@ 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+ err = a .DB .Txn (func (txdb T ) error {
350+ if err := txdb .DeleteUserByID (id ); err != nil {
351+ return err
352+ }
353+ anotherAdmin , err := txdb .GetUsers (& model.User {Admin : true })
354+ if err != nil {
355+ return err
356+ }
357+ if user .Admin && len (anotherAdmin ) == 0 {
358+ ctx .AbortWithError (400 , errCannotDeleteLastAdmin )
359+ return errCannotDeleteLastAdmin
360+ }
361+ return a .UserChangeNotifier .fireUserDeleted (id )
362+ })
363+ if err == nil || ctx .IsAborted () {
364+ return
365+ }
357366 }
358- successOrAbort (ctx , 500 , a . DB . DeleteUserByID ( id ) )
367+ successOrAbort (ctx , 500 , err )
359368 } else {
360369 ctx .AbortWithError (404 , errors .New ("user does not exist" ))
361370 }
@@ -395,7 +404,7 @@ func (a *UserAPI) DeleteUserByID(ctx *gin.Context) {
395404// description: Forbidden
396405// schema:
397406// $ref: "#/definitions/Error"
398- func (a * UserAPI ) ChangePassword (ctx * gin.Context ) {
407+ func (a * UserAPI [ T ] ) ChangePassword (ctx * gin.Context ) {
399408 pw := model.UserExternalPass {}
400409 if err := ctx .Bind (& pw ); err == nil {
401410 if err := password .ValidateNewPassword (pw .Pass ); err != nil {
@@ -461,7 +470,7 @@ func (a *UserAPI) ChangePassword(ctx *gin.Context) {
461470// description: Not Found
462471// schema:
463472// $ref: "#/definitions/Error"
464- func (a * UserAPI ) UpdateUserByID (ctx * gin.Context ) {
473+ func (a * UserAPI [ T ] ) UpdateUserByID (ctx * gin.Context ) {
465474 withID (ctx , "id" , func (id uint ) {
466475 var updatedUser * model.UpdateUserExternal
467476 if err := ctx .Bind (& updatedUser ); err == nil {
@@ -470,15 +479,7 @@ func (a *UserAPI) UpdateUserByID(ctx *gin.Context) {
470479 return
471480 }
472481 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-
482+ dbUserWasAdmin := dbUser .Admin
482483 dbUser .Name = updatedUser .Name
483484 dbUser .Admin = updatedUser .Admin
484485
@@ -494,10 +495,30 @@ func (a *UserAPI) UpdateUserByID(ctx *gin.Context) {
494495 }
495496 dbUser .Pass = pw
496497 }
497- if success := successOrAbort (ctx , 500 , a .DB .UpdateUser (dbUser )); ! success {
498- return
498+
499+ for range 3 {
500+ err = a .DB .Txn (func (txdb T ) error {
501+ if err := txdb .UpdateUser (dbUser ); err != nil {
502+ return err
503+ }
504+
505+ anotherAdmin , err := txdb .GetUsers (& model.User {Admin : true })
506+ if err != nil {
507+ return err
508+ }
509+ if ! updatedUser .Admin && dbUserWasAdmin && len (anotherAdmin ) == 0 {
510+ ctx .AbortWithError (400 , errCannotDeleteLastAdmin )
511+ return errCannotDeleteLastAdmin
512+ }
513+
514+ return nil
515+ })
516+
517+ if err == nil || ctx .IsAborted () {
518+ return
519+ }
499520 }
500- ctx . JSON ( 200 , toExternalUser ( dbUser ) )
521+ successOrAbort ( ctx , 500 , err )
501522 } else {
502523 ctx .AbortWithError (404 , errors .New ("user does not exist" ))
503524 }
0 commit comments