summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/api/handlers.go3
-rw-r--r--internal/api/handlers_admin.go8
-rw-r--r--internal/api/handlers_more_test.go4
-rw-r--r--internal/api/handlers_test.go7
-rw-r--r--internal/service/admin.go4
-rw-r--r--internal/service/admin_test.go2
-rw-r--r--internal/service/mock.go6
-rw-r--r--internal/service/mock_test.go6
-rw-r--r--internal/service/service.go3
-rw-r--r--internal/service/user.go5
10 files changed, 29 insertions, 19 deletions
diff --git a/internal/api/handlers.go b/internal/api/handlers.go
index 029d78b..b276b10 100644
--- a/internal/api/handlers.go
+++ b/internal/api/handlers.go
@@ -56,7 +56,8 @@ func handleError(w http.ResponseWriter, err error) {
case errors.Is(err, service.ErrInvalidCredentials):
writeJSON(w, http.StatusUnauthorized, map[string]string{"error": "invalid credentials"})
case errors.Is(err, service.ErrUnsupportedExtension),
- errors.Is(err, service.ErrInvalidFeed):
+ errors.Is(err, service.ErrInvalidFeed),
+ errors.Is(err, service.ErrCannotDeleteSelf):
badRequest(w, err.Error())
default:
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
diff --git a/internal/api/handlers_admin.go b/internal/api/handlers_admin.go
index 6d10c67..d68b07c 100644
--- a/internal/api/handlers_admin.go
+++ b/internal/api/handlers_admin.go
@@ -83,11 +83,11 @@ func (s *Server) handleDeleteUser(w http.ResponseWriter, r *http.Request) {
return
}
adminUser, _ := r.Context().Value(userCtxKey).(*model.User)
- if adminUser != nil && adminUser.ID == id {
- badRequest(w, "cannot delete self")
- return
+ var callerID int64
+ if adminUser != nil {
+ callerID = adminUser.ID
}
- if err := s.adminSvc.DeleteUser(r.Context(), id); err != nil {
+ if err := s.adminSvc.DeleteUser(r.Context(), callerID, id); err != nil {
handleError(w, err)
return
}
diff --git a/internal/api/handlers_more_test.go b/internal/api/handlers_more_test.go
index 57158fc..8f7817b 100644
--- a/internal/api/handlers_more_test.go
+++ b/internal/api/handlers_more_test.go
@@ -1730,7 +1730,7 @@ func TestServer_AdminDeleteUser(t *testing.T) {
wantCode int
}{
{"nil service", "2", true, nil, http.StatusNotImplemented},
- {"self delete", "1", false, nil, http.StatusBadRequest},
+ {"self delete", "1", false, service.ErrCannotDeleteSelf, http.StatusBadRequest},
{"invalid id zero", "0", false, nil, http.StatusBadRequest},
{"service error", "2", false, errors.New("boom"), http.StatusInternalServerError},
{"ok", "2", false, nil, http.StatusOK},
@@ -1741,7 +1741,7 @@ func TestServer_AdminDeleteUser(t *testing.T) {
var as service.AdminService
if !tt.svcNil {
as = &service.MockAdminService{
- DeleteUserFunc: func(ctx context.Context, id int64) error { return tt.svcErr },
+ DeleteUserFunc: func(ctx context.Context, callerID, id int64) error { return tt.svcErr },
}
}
srv := newTestServer(t, store, nil, sm, cfg, nil, nil, nil, nil, nil, nil, as, nil, nil, nil)
diff --git a/internal/api/handlers_test.go b/internal/api/handlers_test.go
index 363b6ba..cf109c3 100644
--- a/internal/api/handlers_test.go
+++ b/internal/api/handlers_test.go
@@ -1207,7 +1207,12 @@ func TestServer_AdminRoutes(t *testing.T) {
CreateUserFunc: func(ctx context.Context, username, password string, isAdmin bool) (*model.User, error) {
return &model.User{ID: 2, Username: username, IsAdmin: isAdmin}, nil
},
- DeleteUserFunc: func(ctx context.Context, id int64) error { return nil },
+ DeleteUserFunc: func(ctx context.Context, callerID, id int64) error {
+ if callerID == id {
+ return service.ErrCannotDeleteSelf
+ }
+ return nil
+ },
ListPermissionsFunc: func(ctx context.Context) (*service.PermissionsMatrix, error) { return nil, nil },
GrantPermissionFunc: func(ctx context.Context, setID, userID int64, role model.Role) error { return nil },
RevokePermissionFunc: func(ctx context.Context, setID, userID int64) error { return nil },
diff --git a/internal/service/admin.go b/internal/service/admin.go
index 1b1a352..37fda19 100644
--- a/internal/service/admin.go
+++ b/internal/service/admin.go
@@ -61,8 +61,8 @@ func (s *adminService) CreateUser(ctx context.Context, username, password string
}
// DeleteUser delegates to userAdminService.
-func (s *adminService) DeleteUser(ctx context.Context, id int64) error {
- return s.userAdminService.DeleteUser(ctx, id)
+func (s *adminService) DeleteUser(ctx context.Context, callerID, id int64) error {
+ return s.userAdminService.DeleteUser(ctx, callerID, id)
}
// ListPermissions delegates to permissionAdminService.
diff --git a/internal/service/admin_test.go b/internal/service/admin_test.go
index 2ab2157..0e4d309 100644
--- a/internal/service/admin_test.go
+++ b/internal/service/admin_test.go
@@ -229,7 +229,7 @@ func TestAdminService_DeleteUser(t *testing.T) {
},
}
svc := NewAdminService(store, newMockClock(), &fakeHasher{fixed: "hash"}, nil, "", ctx)
- if err := svc.DeleteUser(ctx, 1); err != nil {
+ if err := svc.DeleteUser(ctx, 2, 1); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !called {
diff --git a/internal/service/mock.go b/internal/service/mock.go
index 5b3c96b..f585c52 100644
--- a/internal/service/mock.go
+++ b/internal/service/mock.go
@@ -283,7 +283,7 @@ type MockAdminService struct {
ScanProgressFunc func(ctx context.Context) model.ScanProgress
ListUsersFunc func(ctx context.Context) ([]model.User, error)
CreateUserFunc func(ctx context.Context, username, password string, isAdmin bool) (*model.User, error)
- DeleteUserFunc func(ctx context.Context, id int64) error
+ DeleteUserFunc func(ctx context.Context, callerID, id int64) error
ListPermissionsFunc func(ctx context.Context) (*PermissionsMatrix, error)
GrantPermissionFunc func(ctx context.Context, setID, userID int64, role model.Role) error
RevokePermissionFunc func(ctx context.Context, setID, userID int64) error
@@ -330,9 +330,9 @@ func (m *MockAdminService) CreateUser(ctx context.Context, username, password st
}
// DeleteUser calls DeleteUserFunc or returns nil.
-func (m *MockAdminService) DeleteUser(ctx context.Context, id int64) error {
+func (m *MockAdminService) DeleteUser(ctx context.Context, callerID, id int64) error {
if m.DeleteUserFunc != nil {
- return m.DeleteUserFunc(ctx, id)
+ return m.DeleteUserFunc(ctx, callerID, id)
}
return nil
}
diff --git a/internal/service/mock_test.go b/internal/service/mock_test.go
index bb2525c..c90bac6 100644
--- a/internal/service/mock_test.go
+++ b/internal/service/mock_test.go
@@ -117,7 +117,7 @@ func TestMockAdminService_Defaults(t *testing.T) {
m.ListTrash(ctx)
m.TriggerRescan(ctx)
m.ListUsers(ctx)
- m.DeleteUser(ctx, 1)
+ m.DeleteUser(ctx, 2, 1)
m.ListPermissions(ctx)
m.GrantPermission(ctx, 1, 2, model.RoleViewer)
m.RevokePermission(ctx, 1, 2)
@@ -136,7 +136,7 @@ func TestMockAdminService_WithFuncs(t *testing.T) {
CreateUserFunc: func(ctx context.Context, username, password string, isAdmin bool) (*model.User, error) {
return nil, nil
},
- DeleteUserFunc: func(ctx context.Context, id int64) error { return nil },
+ DeleteUserFunc: func(ctx context.Context, callerID, id int64) error { return nil },
ListPermissionsFunc: func(ctx context.Context) (*PermissionsMatrix, error) { return nil, nil },
GrantPermissionFunc: func(ctx context.Context, setID, userID int64, role model.Role) error { return nil },
RevokePermissionFunc: func(ctx context.Context, setID, userID int64) error { return nil },
@@ -146,7 +146,7 @@ func TestMockAdminService_WithFuncs(t *testing.T) {
m.TriggerRescan(ctx)
m.ListUsers(ctx)
m.CreateUser(ctx, "alice", "secret", false)
- m.DeleteUser(ctx, 1)
+ m.DeleteUser(ctx, 2, 1)
m.ListPermissions(ctx)
m.GrantPermission(ctx, 1, 2, model.RoleViewer)
m.RevokePermission(ctx, 1, 2)
diff --git a/internal/service/service.go b/internal/service/service.go
index 6a02d98..ba826be 100644
--- a/internal/service/service.go
+++ b/internal/service/service.go
@@ -21,6 +21,7 @@ var (
ErrAlreadyBootstrapped = errors.New("already bootstrapped")
ErrInvalidCredentials = errors.New("invalid credentials")
ErrInvalidFeed = errors.New("invalid feed")
+ ErrCannotDeleteSelf = errors.New("cannot delete self")
)
// MediaQueryFilter defines query parameters for listing media from the API layer.
@@ -191,7 +192,7 @@ type AdminService interface {
// CreateUser creates a user account.
CreateUser(ctx context.Context, username, password string, isAdmin bool) (*model.User, error)
// DeleteUser removes a user account.
- DeleteUser(ctx context.Context, id int64) error
+ DeleteUser(ctx context.Context, callerID, id int64) error
// ListPermissions returns the set permission matrix.
ListPermissions(ctx context.Context) (*PermissionsMatrix, error)
// GrantPermission grants a user access to a set.
diff --git a/internal/service/user.go b/internal/service/user.go
index be2ac71..01467ed 100644
--- a/internal/service/user.go
+++ b/internal/service/user.go
@@ -47,6 +47,9 @@ func (s *userAdminService) CreateUser(ctx context.Context, username, password st
return user, nil
}
-func (s *userAdminService) DeleteUser(ctx context.Context, id int64) error {
+func (s *userAdminService) DeleteUser(ctx context.Context, callerID, id int64) error {
+ if callerID == id {
+ return ErrCannotDeleteSelf
+ }
return s.store.DeleteUser(ctx, id)
}