Skip to content

Commit add5424

Browse files
committed
fix: prevent users from self-escalate to owners
Block non-owners from changing owner status Return 403 in the logged-account update handler when a non-owner submits the owner field, and add a regression test to ensure regular users cannot self-escalate privileges. Fixes #1196
1 parent 585ea34 commit add5424

2 files changed

Lines changed: 25 additions & 0 deletions

File tree

internal/http/handlers/api/v1/auth.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,14 @@ func HandleUpdateLoggedAccount(deps model.Dependencies, c model.WebContext) {
177177

178178
account := c.GetAccount()
179179

180+
// Only owners are allowed to change the owner flag. Without this check any
181+
// authenticated user could escalate their own privileges to administrator
182+
// through this self-service endpoint.
183+
if payload.Owner != nil && !account.IsOwner() {
184+
response.SendError(c, http.StatusForbidden, "Only owners can change the owner status")
185+
return
186+
}
187+
180188
if payload.NewPassword != "" {
181189
_, err := deps.Domains().Auth().GetAccountFromCredentials(c.Request().Context(), account.Username, payload.OldPassword)
182190
if err != nil {

internal/http/handlers/api/v1/auth_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,23 @@ func TestHandleUpdateLoggedAccount(t *testing.T) {
195195
require.Equal(t, http.StatusBadRequest, w.Code)
196196
})
197197

198+
t.Run("non-owner cannot escalate to owner", func(t *testing.T) {
199+
regularAccount, err := deps.Domains().Accounts().CreateAccount(context.Background(), model.AccountDTO{
200+
Username: "regular",
201+
Password: "gopher",
202+
Owner: model.Ptr(false),
203+
})
204+
require.NoError(t, err)
205+
206+
body := `{"owner": true}`
207+
w := testutil.PerformRequest(deps, HandleUpdateLoggedAccount, "PATCH", "/account", testutil.WithBody(body), testutil.WithAccount(regularAccount))
208+
require.Equal(t, http.StatusForbidden, w.Code)
209+
210+
stored, err := deps.Domains().Accounts().GetAccountByUsername(context.Background(), "regular")
211+
require.NoError(t, err)
212+
require.False(t, stored.IsOwner())
213+
})
214+
198215
t.Run("successful update", func(t *testing.T) {
199216
body := `{
200217
"old_password": "gopher",

0 commit comments

Comments
 (0)