feat(users): admin promote/demote endpoints

POST /users/{id}/promote and /demote set a user's role (admin/user), admin-only
(403 otherwise). Mirrors the verify endpoints: SetRole use case + repo method,
validated role. Unit, admin-flow, and integration tests included.
This commit is contained in:
Efremenko Arhip
2026-07-26 21:14:32 +03:00
parent 49eb662798
commit 9a458ec4ef
13 changed files with 167 additions and 0 deletions
@@ -32,6 +32,9 @@ func (s stubRepo) GetByID(context.Context, uuid.UUID) (*domain.User, error) {
func (s stubRepo) SetVerified(context.Context, uuid.UUID, bool) error {
return usecase.ErrUserNotFound
}
func (s stubRepo) SetRole(context.Context, uuid.UUID, domain.Role) error {
return usecase.ErrUserNotFound
}
type stubHasher struct {
hashErr error
+1
View File
@@ -15,4 +15,5 @@ var (
ErrInvalidCredentials = errors.New("invalid email or password")
ErrPasswordTooShort = errors.New("password too short")
ErrPasswordTooLong = errors.New("password too long")
ErrInvalidRole = errors.New("invalid role")
)
+3
View File
@@ -25,6 +25,9 @@ type UserRepository interface {
// SetVerified toggles the verified flag, returning ErrUserNotFound if no
// such user exists.
SetVerified(ctx context.Context, id uuid.UUID, verified bool) error
// SetRole changes a user's role, returning ErrUserNotFound if no such user
// exists.
SetRole(ctx context.Context, id uuid.UUID, role domain.Role) error
}
// PasswordHasher hashes and verifies passwords. The bcrypt adapter satisfies it.
+28
View File
@@ -0,0 +1,28 @@
package usecase
import (
"context"
"github.com/google/uuid"
"github.com/emil28092005/SciMesh/users/internal/domain"
)
// SetRole promotes or demotes a user. Only an admin may call this (enforced in
// the transport layer); the use case validates the target role and applies it.
type SetRole struct {
users UserRepository
}
func NewSetRole(users UserRepository) *SetRole {
return &SetRole{users: users}
}
// Execute assigns role to the user, returning ErrInvalidRole for an unknown role
// or ErrUserNotFound if the user does not exist.
func (uc *SetRole) Execute(ctx context.Context, id uuid.UUID, role domain.Role) error {
if !role.Valid() {
return ErrInvalidRole
}
return uc.users.SetRole(ctx, id, role)
}