fix: prevent user permission escalation
This commit is contained in:
+49
@@ -305,6 +305,55 @@ func TestDeleteUserRemovesOwnedFilesAndProtectsIDOne(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserManagerCannotGrantOrTakeOverPermissionsTheyDoNotHave(t *testing.T) {
|
||||
app, _ := testApp(t)
|
||||
passwordHash, _ := bcrypt.GenerateFromPassword([]byte("password-123"), bcrypt.DefaultCost)
|
||||
manager := User{Username: "manager", PasswordHash: string(passwordHash), CanManageUsers: true, CanUpload: true}
|
||||
higherUser := User{Username: "higher", PasswordHash: string(passwordHash), CanAdvancedUpload: true}
|
||||
basicUser := User{Username: "editable", PasswordHash: string(passwordHash), CanUpload: true}
|
||||
for _, user := range []*User{&manager, &higherUser, &basicUser} {
|
||||
if err := app.db.Create(user).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
router := app.router()
|
||||
cookie := loginAs(t, router, manager.Username, "password-123")
|
||||
|
||||
createBody, _ := json.Marshal(map[string]any{"username": "escalated", "canAdvancedUpload": true})
|
||||
created := request(t, router, http.MethodPost, "/api/users", bytes.NewReader(createBody), "application/json", cookie)
|
||||
if created.Code != http.StatusForbidden {
|
||||
t.Fatalf("create advanced user status = %d, body = %s", created.Code, created.Body.String())
|
||||
}
|
||||
|
||||
updateBody, _ := json.Marshal(map[string]any{"username": basicUser.Username, "canUpload": true, "canAdvancedUpload": true})
|
||||
updated := request(t, router, http.MethodPut, "/api/users/"+jsonNumber(basicUser.ID), bytes.NewReader(updateBody), "application/json", cookie)
|
||||
if updated.Code != http.StatusForbidden {
|
||||
t.Fatalf("grant advanced permission status = %d, body = %s", updated.Code, updated.Body.String())
|
||||
}
|
||||
|
||||
reset := request(t, router, http.MethodPost, "/api/users/"+jsonNumber(higherUser.ID)+"/reset-password", nil, "", cookie)
|
||||
if reset.Code != http.StatusForbidden {
|
||||
t.Fatalf("reset higher user status = %d, body = %s", reset.Code, reset.Body.String())
|
||||
}
|
||||
|
||||
downgradeBody, _ := json.Marshal(map[string]any{"username": higherUser.Username, "canUpload": true})
|
||||
downgraded := request(t, router, http.MethodPut, "/api/users/"+jsonNumber(higherUser.ID), bytes.NewReader(downgradeBody), "application/json", cookie)
|
||||
if downgraded.Code != http.StatusForbidden {
|
||||
t.Fatalf("edit higher user status = %d, body = %s", downgraded.Code, downgraded.Body.String())
|
||||
}
|
||||
|
||||
deleted := request(t, router, http.MethodDelete, "/api/users/"+jsonNumber(higherUser.ID), nil, "", cookie)
|
||||
if deleted.Code != http.StatusForbidden {
|
||||
t.Fatalf("delete higher user status = %d, body = %s", deleted.Code, deleted.Body.String())
|
||||
}
|
||||
|
||||
createBody, _ = json.Marshal(map[string]any{"username": "allowed", "canUpload": true})
|
||||
created = request(t, router, http.MethodPost, "/api/users", bytes.NewReader(createBody), "application/json", cookie)
|
||||
if created.Code != http.StatusCreated {
|
||||
t.Fatalf("create user with owned permission status = %d, body = %s", created.Code, created.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func jsonNumber(id uint) string {
|
||||
data, _ := json.Marshal(id)
|
||||
return string(data)
|
||||
|
||||
Reference in New Issue
Block a user