From 5cd1accdddce9710179881a4f74be9f019bdfb57 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Mon, 13 Jan 2025 13:53:25 -0500 Subject: [PATCH 1/7] Backend changes so org admins can invite guest users --- .../LexBoxApi/Controllers/LoginController.cs | 2 +- backend/LexBoxApi/GraphQL/ProjectMutations.cs | 7 +++++- backend/LexBoxApi/GraphQL/UserMutations.cs | 3 ++- .../LexBoxApi/Services/PermissionService.cs | 24 +++++++++++++++++++ .../ServiceInterfaces/IPermissionService.cs | 4 ++++ 5 files changed, 37 insertions(+), 3 deletions(-) diff --git a/backend/LexBoxApi/Controllers/LoginController.cs b/backend/LexBoxApi/Controllers/LoginController.cs index 3d4f6d21b..8c6f76e5a 100644 --- a/backend/LexBoxApi/Controllers/LoginController.cs +++ b/backend/LexBoxApi/Controllers/LoginController.cs @@ -170,7 +170,7 @@ public async Task> VerifyEmail( user.Email = loggedInContext.User.Email; user.EmailVerified = true; - // Guest ussers are promoted to "regular" users once they verify an email address + // Guest users are promoted to "regular" users once they verify an email address user.CreatedById = null; user.UpdateUpdatedDate(); await lexBoxDbContext.SaveChangesAsync(); diff --git a/backend/LexBoxApi/GraphQL/ProjectMutations.cs b/backend/LexBoxApi/GraphQL/ProjectMutations.cs index 55e2b8ed5..c57243ff5 100644 --- a/backend/LexBoxApi/GraphQL/ProjectMutations.cs +++ b/backend/LexBoxApi/GraphQL/ProjectMutations.cs @@ -134,9 +134,9 @@ public record BulkAddProjectMembersResult(List AddedMembers, Li [Error] [Error] [Error] - [AdminRequired] [UseMutationConvention] public async Task BulkAddProjectMembers( + IPermissionService permissionService, LoggedInContext loggedInContext, BulkAddProjectMembersInput input, LexBoxDbContext dbContext) @@ -145,6 +145,11 @@ public async Task BulkAddProjectMembers( { var projectExists = await dbContext.Projects.AnyAsync(p => p.Id == input.ProjectId.Value); if (!projectExists) throw new NotFoundException("Project not found", "project"); + await permissionService.AssertCanCreateGuestUserInProject(input.ProjectId.Value); + } + else + { + permissionService.AssertCanCreateGuestUserInAnyProject(); } List AddedMembers = []; List CreatedMembers = []; diff --git a/backend/LexBoxApi/GraphQL/UserMutations.cs b/backend/LexBoxApi/GraphQL/UserMutations.cs index 72e92252e..634138d03 100644 --- a/backend/LexBoxApi/GraphQL/UserMutations.cs +++ b/backend/LexBoxApi/GraphQL/UserMutations.cs @@ -96,8 +96,8 @@ IEmailService emailService [Error] [Error] [Error] - [AdminRequired] public async Task CreateGuestUserByAdmin( + IPermissionService permissionService, LoggedInContext loggedInContext, CreateGuestUserByAdminInput input, LexBoxDbContext dbContext, @@ -105,6 +105,7 @@ IEmailService emailService ) { using var createGuestUserActivity = LexBoxActivitySource.Get().StartActivity("CreateGuestUser"); + permissionService.AssertCanCreateGuestUserInAnyProject(); var hasExistingUser = input.Email is null && input.Username is null ? throw new RequiredException("Guest users must have either an email or a username") diff --git a/backend/LexBoxApi/Services/PermissionService.cs b/backend/LexBoxApi/Services/PermissionService.cs index 983461be7..656b773dd 100644 --- a/backend/LexBoxApi/Services/PermissionService.cs +++ b/backend/LexBoxApi/Services/PermissionService.cs @@ -141,6 +141,30 @@ public async ValueTask AssertCanManageProjectMemberRole(Guid projectId, Guid use throw new UnauthorizedAccessException("Not allowed to change own project role."); } + public async ValueTask CanCreateGuestUserInProject(Guid projectId) + { + if (User is null) return false; + if (User.Role == UserRole.admin) return true; + return await ManagesOrgThatOwnsProject(projectId); + } + + public async ValueTask AssertCanCreateGuestUserInProject(Guid projectId) + { + if (!await CanCreateGuestUserInProject(projectId)) throw new UnauthorizedAccessException(); + } + + public bool CanCreateGuestUserInAnyProject() + { + if (User is null) return false; + if (User.Role == UserRole.admin) return true; + return User.Orgs.Any(o => o.Role == OrgRole.Admin); + } + + public void AssertCanCreateGuestUserInAnyProject() + { + if (!CanCreateGuestUserInAnyProject()) throw new UnauthorizedAccessException(); + } + public async ValueTask CanAskToJoinProject(Guid projectId) { if (User is null) return false; diff --git a/backend/LexCore/ServiceInterfaces/IPermissionService.cs b/backend/LexCore/ServiceInterfaces/IPermissionService.cs index f575a4af3..f7e486382 100644 --- a/backend/LexCore/ServiceInterfaces/IPermissionService.cs +++ b/backend/LexCore/ServiceInterfaces/IPermissionService.cs @@ -20,6 +20,10 @@ public interface IPermissionService ValueTask AssertCanManageProject(Guid projectId); ValueTask AssertCanManageProject(string projectCode); ValueTask AssertCanManageProjectMemberRole(Guid projectId, Guid userId); + ValueTask CanCreateGuestUserInProject(Guid projectId); + ValueTask AssertCanCreateGuestUserInProject(Guid projectId); + bool CanCreateGuestUserInAnyProject(); + void AssertCanCreateGuestUserInAnyProject(); ValueTask CanAskToJoinProject(Guid projectId); ValueTask CanAskToJoinProject(string projectCode); ValueTask AssertCanAskToJoinProject(Guid projectId); From 6e6a0231b0111000cb70ebc61c04f2106aa19792 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Mon, 13 Jan 2025 15:57:20 -0500 Subject: [PATCH 2/7] Frontend changes so org admins can invite guest users --- .../(authenticated)/org/[org_id]/+page.svelte | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/frontend/src/routes/(authenticated)/org/[org_id]/+page.svelte b/frontend/src/routes/(authenticated)/org/[org_id]/+page.svelte index 9417d1d4e..f225a21f4 100644 --- a/frontend/src/routes/(authenticated)/org/[org_id]/+page.svelte +++ b/frontend/src/routes/(authenticated)/org/[org_id]/+page.svelte @@ -25,6 +25,10 @@ import BulkAddOrgMembers from './BulkAddOrgMembers.svelte'; import Dropdown from '$lib/components/Dropdown.svelte'; import AddMyProjectsToOrgModal from './AddMyProjectsToOrgModal.svelte'; + import CreateUserModal from '$lib/components/Users/CreateUserModal.svelte'; + import {createGuestUserByAdmin, type LexAuthUser} from '$lib/user'; + import {Duration} from '$lib/util/time'; + import {browser} from '$app/environment'; export let data: PageData; $: user = data.user; @@ -113,6 +117,11 @@ await goto('/'); } } + + let createUserModal: CreateUserModal; + function onUserCreated(user: LexAuthUser): void { + notifySuccess($t('admin_dashboard.notifications.user_created', { name: user.name }), Duration.Long); + } {$t('org.table.title')} @@ -130,6 +139,14 @@ + + createUserModal.open()}> + + {$t('admin_dashboard.create_user_modal.create_user')} + + + {/if}
@@ -223,3 +240,4 @@ + onUserCreated(e.detail)} bind:this={createUserModal}/> From f0bc6a7adede96d975f0beca50d85dc57c39e341 Mon Sep 17 00:00:00 2001 From: Tim Haasdyk Date: Tue, 14 Jan 2025 17:27:21 +0100 Subject: [PATCH 3/7] Move other org user/member buttons into menu --- .../(authenticated)/org/[org_id]/+page.svelte | 39 ++++++++++++------- .../org/[org_id]/BulkAddOrgMembers.svelte | 5 --- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/frontend/src/routes/(authenticated)/org/[org_id]/+page.svelte b/frontend/src/routes/(authenticated)/org/[org_id]/+page.svelte index f225a21f4..a8b5d3da5 100644 --- a/frontend/src/routes/(authenticated)/org/[org_id]/+page.svelte +++ b/frontend/src/routes/(authenticated)/org/[org_id]/+page.svelte @@ -28,7 +28,7 @@ import CreateUserModal from '$lib/components/Users/CreateUserModal.svelte'; import {createGuestUserByAdmin, type LexAuthUser} from '$lib/user'; import {Duration} from '$lib/util/time'; - import {browser} from '$app/environment'; + import IconButton from '$lib/components/IconButton.svelte'; export let data: PageData; $: user = data.user; @@ -67,6 +67,8 @@ await addOrgMemberModal.openModal(); } + let bulkAddMembersModal: BulkAddOrgMembers; + let changeMemberRoleModal: ChangeOrgMemberRoleModal; async function openChangeMemberRoleModal(member: OrgUser): Promise { await changeMemberRoleModal.open({ @@ -132,21 +134,33 @@ {/if} {#if canManage} - - - - - createUserModal.open()}> - - {$t('admin_dashboard.create_user_modal.create_user')} - - - + + + + +
+ onUserCreated(e.detail)} bind:this={createUserModal}/> + + {/if}
@@ -240,4 +254,3 @@ - onUserCreated(e.detail)} bind:this={createUserModal}/> diff --git a/frontend/src/routes/(authenticated)/org/[org_id]/BulkAddOrgMembers.svelte b/frontend/src/routes/(authenticated)/org/[org_id]/BulkAddOrgMembers.svelte index b3a03a145..54963fb26 100644 --- a/frontend/src/routes/(authenticated)/org/[org_id]/BulkAddOrgMembers.svelte +++ b/frontend/src/routes/(authenticated)/org/[org_id]/BulkAddOrgMembers.svelte @@ -79,11 +79,6 @@ } - - {$t('org_page.bulk_add_members.modal_title')} From 205c2da3d2aa6c80a0168ac57aa52950a4538ab8 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Tue, 14 Jan 2025 17:49:26 -0500 Subject: [PATCH 4/7] Fix compile and lint errors --- .../(authenticated)/org/[org_id]/BulkAddOrgMembers.svelte | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/routes/(authenticated)/org/[org_id]/BulkAddOrgMembers.svelte b/frontend/src/routes/(authenticated)/org/[org_id]/BulkAddOrgMembers.svelte index 54963fb26..2fb829259 100644 --- a/frontend/src/routes/(authenticated)/org/[org_id]/BulkAddOrgMembers.svelte +++ b/frontend/src/routes/(authenticated)/org/[org_id]/BulkAddOrgMembers.svelte @@ -1,5 +1,4 @@