summaryrefslogtreecommitdiff
path: root/player-server/internal/service
diff options
context:
space:
mode:
Diffstat (limited to 'player-server/internal/service')
-rw-r--r--player-server/internal/service/admin_test.go2
-rw-r--r--player-server/internal/service/service.go1
-rw-r--r--player-server/internal/service/user.go8
-rw-r--r--player-server/internal/service/user_test.go24
4 files changed, 29 insertions, 6 deletions
diff --git a/player-server/internal/service/admin_test.go b/player-server/internal/service/admin_test.go
index 0e4d309..1a93356 100644
--- a/player-server/internal/service/admin_test.go
+++ b/player-server/internal/service/admin_test.go
@@ -200,7 +200,7 @@ func TestAdminService_CreateUser(t *testing.T) {
}
hasher := &fakeHasher{fixed: "hashed", err: tt.hashErr}
svc := NewAdminService(store, newMockClock(), hasher, nil, "", ctx)
- user, err := svc.CreateUser(ctx, "alice", "secret", false)
+ user, err := svc.CreateUser(ctx, "alice", "strongpass", false)
if tt.wantErr {
if err == nil {
t.Fatal("expected error")
diff --git a/player-server/internal/service/service.go b/player-server/internal/service/service.go
index beeb5aa..9792f95 100644
--- a/player-server/internal/service/service.go
+++ b/player-server/internal/service/service.go
@@ -48,6 +48,7 @@ var (
ErrInvalidCredentials = &apiError{msg: "invalid credentials", status: http.StatusUnauthorized}
ErrInvalidFeed = &apiError{msg: "invalid feed", status: http.StatusBadRequest}
ErrCannotDeleteSelf = &apiError{msg: "cannot delete self", status: http.StatusBadRequest}
+ ErrWeakPassword = &apiError{msg: "password must be at least 8 characters", status: http.StatusBadRequest}
// ErrShareExpired is handled directly by share handlers (not via
// handleError); it stays a plain sentinel because no dispatch metadata
diff --git a/player-server/internal/service/user.go b/player-server/internal/service/user.go
index 01467ed..ba3e541 100644
--- a/player-server/internal/service/user.go
+++ b/player-server/internal/service/user.go
@@ -10,6 +10,9 @@ import (
"codeberg.org/snonux/player/internal/repository"
)
+// minPasswordLen is the minimum acceptable password length for new accounts.
+const minPasswordLen = 8
+
// userAdminService handles user account management.
type userAdminService struct {
store repository.UserAdminServiceStore
@@ -27,6 +30,11 @@ func (s *userAdminService) ListUsers(ctx context.Context) ([]model.User, error)
}
func (s *userAdminService) CreateUser(ctx context.Context, username, password string, isAdmin bool) (*model.User, error) {
+ // Reject blank or short passwords before hashing to prevent weak account creation.
+ if len(password) < minPasswordLen {
+ return nil, ErrWeakPassword
+ }
+
hash, err := s.hasher.Hash(password)
if err != nil {
return nil, fmt.Errorf("hash password: %w", err)
diff --git a/player-server/internal/service/user_test.go b/player-server/internal/service/user_test.go
index 1e0ceb3..b470119 100644
--- a/player-server/internal/service/user_test.go
+++ b/player-server/internal/service/user_test.go
@@ -30,23 +30,37 @@ func TestUserAdminService_CreateUser(t *testing.T) {
tests := []struct {
name string
+ password string
hashErr error
createErr error
wantErr bool
}{
{
- name: "ok",
+ name: "ok",
+ password: "strongpass", // 10 chars, meets 8-char minimum
},
{
- name: "hash error",
- hashErr: errors.New("boom"),
- wantErr: true,
+ name: "hash error",
+ password: "strongpass",
+ hashErr: errors.New("boom"),
+ wantErr: true,
},
{
name: "create error",
+ password: "strongpass",
createErr: errors.New("boom"),
wantErr: true,
},
+ {
+ name: "empty password rejected",
+ password: "",
+ wantErr: true,
+ },
+ {
+ name: "short password rejected",
+ password: "short",
+ wantErr: true,
+ },
}
for _, tt := range tests {
@@ -60,7 +74,7 @@ func TestUserAdminService_CreateUser(t *testing.T) {
}
hasher := &fakeUserHasher{fixed: "hashed", err: tt.hashErr}
svc := NewUserAdminService(store, clock.RealClock{}, hasher)
- user, err := svc.CreateUser(ctx, "alice", "secret", false)
+ user, err := svc.CreateUser(ctx, "alice", tt.password, false)
if tt.wantErr {
if err == nil {
t.Fatal("expected error")