Skip to content

Commit

Permalink
CreateGuestUser now adds users to orgs
Browse files Browse the repository at this point in the history
  • Loading branch information
rmunn committed Jan 15, 2025
1 parent 4b02d26 commit a1de5f1
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 5 deletions.
8 changes: 6 additions & 2 deletions backend/LexBoxApi/GraphQL/UserMutations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public record CreateGuestUserByAdminInput(
string Locale,
string PasswordHash,
int PasswordStrength,
Guid? ProjectId);
Guid? OrgId);

[Error<NotFoundException>]
[Error<DbError>]
Expand Down Expand Up @@ -106,7 +106,7 @@ IEmailService emailService
)
{
using var createGuestUserActivity = LexBoxActivitySource.Get().StartActivity("CreateGuestUser");
permissionService.AssertCanCreateGuestUserInProject(input.ProjectId);
permissionService.AssertCanCreateGuestUserInOrg(input.OrgId);

var hasExistingUser = input.Email is null && input.Username is null
? throw new RequiredException("Guest users must have either an email or a username")
Expand Down Expand Up @@ -134,6 +134,10 @@ IEmailService emailService
CanCreateProjects = false
};
createGuestUserActivity?.AddTag("app.user.id", userEntity.Id);
if (input.OrgId is not null)
{
userEntity.Organizations.Add(new OrgMember() { OrgId = input.OrgId.Value, Role = OrgRole.User });
}
dbContext.Users.Add(userEntity);
await dbContext.SaveChangesAsync();
if (!string.IsNullOrEmpty(input.Email))
Expand Down
14 changes: 14 additions & 0 deletions backend/LexBoxApi/Services/PermissionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,20 @@ public async ValueTask AssertCanCreateGuestUserInProject(Guid? projectId)
if (!await CanCreateGuestUserInProject(projectId)) throw new UnauthorizedAccessException();
}

public bool CanCreateGuestUserInOrg(Guid? orgId)
{
if (User is null) return false;
if (User.Role == UserRole.admin) return true;
// Site admins can create guest users even with no org, anyone else (like org admins) must specify an org ID
if (orgId is null) return false;
return CanEditOrg(orgId.Value);
}

public void AssertCanCreateGuestUserInOrg(Guid? orgId)
{
if (!CanCreateGuestUserInOrg(orgId)) throw new UnauthorizedAccessException();
}

public async ValueTask<bool> CanAskToJoinProject(Guid projectId)
{
if (User is null) return false;
Expand Down
2 changes: 2 additions & 0 deletions backend/LexCore/ServiceInterfaces/IPermissionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public interface IPermissionService
ValueTask AssertCanManageProjectMemberRole(Guid projectId, Guid userId);
ValueTask<bool> CanCreateGuestUserInProject(Guid? projectId);
ValueTask AssertCanCreateGuestUserInProject(Guid? projectId);
bool CanCreateGuestUserInOrg(Guid? orgId);
void AssertCanCreateGuestUserInOrg(Guid? orgId);
ValueTask<bool> CanAskToJoinProject(Guid projectId);
ValueTask<bool> CanAskToJoinProject(string projectCode);
ValueTask AssertCanAskToJoinProject(Guid projectId);
Expand Down
2 changes: 1 addition & 1 deletion frontend/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,7 @@ input CreateGuestUserByAdminInput {
locale: String!
passwordHash: String!
passwordStrength: Int!
projectId: UUID
orgId: UUID
}

input CreateOrganizationInput {
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/lib/gql/gql-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
type MutationBulkAddProjectMembersArgs,
type MutationChangeOrgMemberRoleArgs,
type MutationChangeUserAccountBySelfArgs,
type MutationCreateGuestUserByAdminArgs,
type MutationCreateOrganizationArgs,
type MutationCreateProjectArgs,
type MutationDeleteDraftProjectArgs,
Expand Down Expand Up @@ -99,6 +100,11 @@ function createGqlClient(_gqlEndpoint?: string): Client {
cache.invalidate({__typename: 'Project', id: args.input.projectId});
}
},
createGuestUserByAdmin: (result, args: MutationCreateGuestUserByAdminArgs, cache, _info) => {
if (args.input.orgId) {
cache.invalidate({__typename: 'OrgById', id: args.input.orgId});
}
},
createOrganization: (result: CreateOrgMutation, args: MutationCreateOrganizationArgs, cache, _info) => {
cache.invalidate('Query', 'myOrgs');
},
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lib/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,14 @@ export function register(password: string, passwordStrength: number, name: strin
export function acceptInvitation(password: string, passwordStrength: number, name: string, email: string, locale: string, turnstileToken: string): Promise<RegisterResponse> {
return createUser('/api/User/acceptInvitation', password, passwordStrength, name, email, locale, turnstileToken);
}
export async function createGuestUserByAdmin(password: string, passwordStrength: number, name: string, email: string, locale: string, _turnstileToken: string): Promise<RegisterResponse> {
export async function createGuestUserByAdmin(password: string, passwordStrength: number, name: string, email: string, locale: string, _turnstileToken: string, orgId?: string): Promise<RegisterResponse> {
const passwordHash = await hash(password);
const gqlInput: CreateGuestUserByAdminInput = {
passwordHash,
passwordStrength,
name,
locale,
orgId,
};
if (email.includes('@')) {
gqlInput.email = email;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@
}
}
function createGuestUser(password: string, passwordStrength: number, name: string, email: string, locale: string, _turnstileToken: string): ReturnType<typeof createGuestUserByAdmin> {
return createGuestUserByAdmin(password, passwordStrength, name, email, locale, _turnstileToken, org.id);
}
let createUserModal: CreateUserModal;
function onUserCreated(user: LexAuthUser): void {
notifySuccess($t('admin_dashboard.notifications.user_created', { name: user.name }), Duration.Long);
Expand Down Expand Up @@ -158,7 +162,7 @@
</ul>
</Dropdown>
</div>
<CreateUserModal handleSubmit={createGuestUserByAdmin} on:submitted={(e) => onUserCreated(e.detail)} bind:this={createUserModal}/>
<CreateUserModal handleSubmit={createGuestUser} on:submitted={(e) => onUserCreated(e.detail)} bind:this={createUserModal}/>
<AddOrgMemberModal bind:this={addOrgMemberModal} {org} />
<BulkAddOrgMembers bind:this={bulkAddMembersModal} orgId={org.id} />
{/if}
Expand Down

0 comments on commit a1de5f1

Please sign in to comment.