Skip to content

Commit c5a96c0

Browse files
committed
fix: enforce sort_key is unique per user
1 parent 17f4875 commit c5a96c0

File tree

4 files changed

+31
-4
lines changed

4 files changed

+31
-4
lines changed

api/application.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/gotify/server/v2/auth"
1313
"github.com/gotify/server/v2/model"
1414
"github.com/h2non/filetype"
15+
"gorm.io/gorm"
1516
)
1617

1718
// The ApplicationDatabase interface for encapsulating database access.
@@ -102,7 +103,8 @@ func (a *ApplicationAPI) CreateApplication(ctx *gin.Context) {
102103
Internal: false,
103104
}
104105

105-
if success := successOrAbort(ctx, 500, a.DB.CreateApplication(&app)); !success {
106+
if err := a.DB.CreateApplication(&app); err != nil {
107+
handleApplicationError(ctx, err)
106108
return
107109
}
108110
ctx.JSON(200, withResolvedImage(&app))
@@ -262,7 +264,8 @@ func (a *ApplicationAPI) UpdateApplication(ctx *gin.Context) {
262264
app.SortKey = applicationParams.SortKey
263265
}
264266

265-
if success := successOrAbort(ctx, 500, a.DB.UpdateApplication(app)); !success {
267+
if err := a.DB.UpdateApplication(app); err != nil {
268+
handleApplicationError(ctx, err)
266269
return
267270
}
268271
ctx.JSON(200, withResolvedImage(app))
@@ -477,3 +480,11 @@ func ValidApplicationImageExt(ext string) bool {
477480
return false
478481
}
479482
}
483+
484+
func handleApplicationError(ctx *gin.Context, err error) {
485+
if errors.Is(err, gorm.ErrDuplicatedKey) {
486+
ctx.AbortWithError(400, errors.New("sort key is not unique"))
487+
} else {
488+
ctx.AbortWithError(500, err)
489+
}
490+
}

api/application_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,21 @@ func (s *ApplicationSuite) Test_UpdateApplication_WithoutPermission_expectNotFou
647647
assert.Equal(s.T(), 404, s.recorder.Code)
648648
}
649649

650+
func (s *ApplicationSuite) Test_UpdateApplication_duplicateSortKey() {
651+
user := s.db.User(5)
652+
user.App(1) // sortKey=a0
653+
user.App(2) // sortKey=a1
654+
655+
s.withFormData("name=new_name&sortKey=a0")
656+
test.WithUser(s.ctx, 5)
657+
s.ctx.Params = gin.Params{{Key: "id", Value: "2"}}
658+
659+
s.a.UpdateApplication(s.ctx)
660+
661+
assert.EqualError(s.T(), s.ctx.Errors[0].Err, "sort key is not unique")
662+
assert.Equal(s.T(), 400, s.recorder.Code)
663+
}
664+
650665
func (s *ApplicationSuite) withFormData(formData string) {
651666
s.ctx.Request = httptest.NewRequest("POST", "/token", strings.NewReader(formData))
652667
s.ctx.Request.Header.Set("Content-Type", "application/x-www-form-urlencoded")

database/database.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func New(dialect, connection, defaultUser, defaultPass string, strength int, cre
3636
gormConfig := &gorm.Config{
3737
Logger: dbLogger,
3838
DisableForeignKeyConstraintWhenMigrating: true,
39+
TranslateError: true,
3940
}
4041

4142
var db *gorm.DB

model/application.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type Application struct {
2020
// required: true
2121
// example: AWH0wZ5r0Mbac.r
2222
Token string `gorm:"type:varchar(180);uniqueIndex:uix_applications_token" json:"token"`
23-
UserID uint `gorm:"index" json:"-"`
23+
UserID uint `gorm:"index;uniqueIndex:idx_id_sortkey,priority:1" json:"-"`
2424
// The application name. This is how the application should be displayed to the user.
2525
//
2626
// required: true
@@ -58,5 +58,5 @@ type Application struct {
5858
//
5959
// required: false
6060
// example: a1
61-
SortKey string `form:"sortKey" query:"sortKey" json:"sortKey"`
61+
SortKey string `gorm:"uniqueIndex:idx_id_sortkey,priority:2" form:"sortKey" query:"sortKey" json:"sortKey"`
6262
}

0 commit comments

Comments
 (0)