fix(api): correct user registration logic and first-user detection

- Changed first-user detection to check for any users instead of only HOST users
- Moved registration setting check before role assignment to properly block unauthorized registrations
- Fixed role assignment logic to ensure unauthenticated users always get USER role
- Updated test cases to create host user first when not testing first-user scenario

This ensures the first user is always created as HOST and registration settings are properly enforced for subsequent user creation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
pull/5231/head
Steven 2 weeks ago
parent 21d31e3609
commit 8f136ffa75

@ -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

@ -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)
}

Loading…
Cancel
Save