diff --git a/server/router/api/v1/test/user_service_registration_test.go b/server/router/api/v1/test/user_service_registration_test.go index a6c6ccad4..ac9dba6cb 100644 --- a/server/router/api/v1/test/user_service_registration_test.go +++ b/server/router/api/v1/test/user_service_registration_test.go @@ -34,8 +34,12 @@ func TestCreateUserRegistration(t *testing.T) { ts := NewTestService(t) defer ts.Cleanup() + // Create a host user first so we're not in first-user setup mode + _, err := ts.CreateHostUser(ctx, "admin") + require.NoError(t, err) + // Disable user registration - _, err := ts.Store.UpsertInstanceSetting(ctx, &storepb.InstanceSetting{ + _, err = ts.Store.UpsertInstanceSetting(ctx, &storepb.InstanceSetting{ Key: storepb.InstanceSettingKey_GENERAL, Value: &storepb.InstanceSetting_GeneralSetting{ GeneralSetting: &storepb.InstanceGeneralSetting{ @@ -147,6 +151,10 @@ func TestCreateUserRegistration(t *testing.T) { ts := NewTestService(t) defer ts.Cleanup() + // Create a host user first so we're not in first-user setup mode + _, err := ts.CreateHostUser(ctx, "admin") + require.NoError(t, err) + // User registration is enabled by default // Unauthenticated user tries to create admin user - role should be ignored diff --git a/server/router/api/v1/user_service.go b/server/router/api/v1/user_service.go index b5e452fc3..8ca617734 100644 --- a/server/router/api/v1/user_service.go +++ b/server/router/api/v1/user_service.go @@ -140,36 +140,19 @@ func (s *APIV1Service) GetUserAvatar(ctx context.Context, request *v1pb.GetUserA } func (s *APIV1Service) CreateUser(ctx context.Context, request *v1pb.CreateUserRequest) (*v1pb.User, error) { - // Check if there are any existing host users (for first-time setup detection) - hostUserType := store.RoleHost - existedHostUsers, err := s.Store.ListUsers(ctx, &store.FindUser{ - Role: &hostUserType, - }) + // Get current user (might be nil for unauthenticated requests) + currentUser, _ := s.GetCurrentUser(ctx) + + // Check if there are any existing users (for first-time setup detection) + limitOne := 1 + allUsers, err := s.Store.ListUsers(ctx, &store.FindUser{Limit: &limitOne}) if err != nil { - return nil, status.Errorf(codes.Internal, "failed to list host users: %v", err) + return nil, status.Errorf(codes.Internal, "failed to list users: %v", err) } + isFirstUser := len(allUsers) == 0 - // Determine the role to assign and check permissions - var roleToAssign store.Role - if len(existedHostUsers) == 0 { - // First-time setup: create the first user as HOST (no authentication required) - roleToAssign = store.RoleHost - } else { - // Regular user creation: allow unauthenticated creation of normal users - // But if authenticated, check if user has HOST permission for any role - currentUser, err := s.GetCurrentUser(ctx) - if err == nil && currentUser != nil && currentUser.Role == store.RoleHost { - // Authenticated HOST user can create users with any role specified in request - if request.User.Role != v1pb.User_ROLE_UNSPECIFIED { - roleToAssign = convertUserRoleToStore(request.User.Role) - } else { - roleToAssign = store.RoleUser - } - } else { - // Unauthenticated or non-HOST users can only create normal users - roleToAssign = store.RoleUser - } - + // Check registration settings FIRST (unless it's the very first user) + if !isFirstUser { // Only allow user registration if it is enabled in the settings, or if the user is a superuser if currentUser == nil || !isSuperUser(currentUser) { instanceGeneralSetting, err := s.Store.GetInstanceGeneralSetting(ctx) @@ -182,6 +165,23 @@ func (s *APIV1Service) CreateUser(ctx context.Context, request *v1pb.CreateUserR } } + // Determine the role to assign + var roleToAssign store.Role + if isFirstUser { + // First-time setup: create the first user as HOST (no authentication required) + roleToAssign = store.RoleHost + } else if currentUser != nil && currentUser.Role == store.RoleHost { + // Authenticated HOST user can create users with any role specified in request + if request.User.Role != v1pb.User_ROLE_UNSPECIFIED { + roleToAssign = convertUserRoleToStore(request.User.Role) + } else { + roleToAssign = store.RoleUser + } + } else { + // Unauthenticated or non-HOST users can only create normal users + roleToAssign = store.RoleUser + } + if !base.UIDMatcher.MatchString(strings.ToLower(request.User.Username)) { return nil, status.Errorf(codes.InvalidArgument, "invalid username: %s", request.User.Username) }