diff --git a/apps/web/src/app/organizations/core/services/index.ts b/apps/web/src/app/organizations/core/services/index.ts index 7864ce90ac4..1e670faccd6 100644 --- a/apps/web/src/app/organizations/core/services/index.ts +++ b/apps/web/src/app/organizations/core/services/index.ts @@ -1,2 +1,3 @@ export * from "./group/group.service"; export * from "./collection-admin.service"; +export * from "./user-admin.service"; diff --git a/apps/web/src/app/organizations/core/services/user-admin.service.ts b/apps/web/src/app/organizations/core/services/user-admin.service.ts new file mode 100644 index 00000000000..76e48f1c5d9 --- /dev/null +++ b/apps/web/src/app/organizations/core/services/user-admin.service.ts @@ -0,0 +1,82 @@ +import { Injectable } from "@angular/core"; + +import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service"; +import { + OrganizationUserInviteRequest, + OrganizationUserUpdateRequest, +} from "@bitwarden/common/abstractions/organization-user/requests"; +import { OrganizationUserDetailsResponse } from "@bitwarden/common/abstractions/organization-user/responses"; + +import { CoreOrganizationModule } from "../core-organization.module"; +import { OrganizationUserAdminView } from "../views/user-admin-view"; + +@Injectable({ providedIn: CoreOrganizationModule }) +export class UserAdminService { + constructor(private organizationUserService: OrganizationUserService) {} + + async get( + organizationId: string, + organizationUserId: string + ): Promise { + const userResponse = await this.organizationUserService.getOrganizationUser( + organizationId, + organizationUserId + ); + + if (userResponse == null) { + return undefined; + } + + const [view] = await this.decryptMany(organizationId, [userResponse]); + + return view; + } + + async save(user: OrganizationUserAdminView): Promise { + const request = new OrganizationUserUpdateRequest(); + request.accessAll = user.accessAll; + request.permissions = user.permissions; + request.type = user.type; + request.collections = user.collections; + + await this.organizationUserService.putOrganizationUser(user.organizationId, user.id, request); + } + + async invite(emails: string[], user: OrganizationUserAdminView): Promise { + const request = new OrganizationUserInviteRequest(); + request.emails = emails; + request.accessAll = user.accessAll; + request.permissions = user.permissions; + request.type = user.type; + request.collections = user.collections; + + await this.organizationUserService.postOrganizationUserInvite(user.organizationId, request); + } + + private async decryptMany( + organizationId: string, + users: OrganizationUserDetailsResponse[] + ): Promise { + const promises = users.map(async (u) => { + const view = new OrganizationUserAdminView(); + + view.id = u.id; + view.organizationId = organizationId; + view.userId = u.userId; + view.type = u.type; + view.status = u.status; + view.accessAll = u.accessAll; + view.permissions = u.permissions; + view.resetPasswordEnrolled = u.resetPasswordEnrolled; + view.collections = u.collections.map((c) => ({ + id: c.id, + hidePasswords: c.hidePasswords, + readOnly: c.readOnly, + })); + + return view; + }); + + return await Promise.all(promises); + } +} diff --git a/apps/web/src/app/organizations/core/views/index.ts b/apps/web/src/app/organizations/core/views/index.ts index 5ecfed1efdf..1ac450d8904 100644 --- a/apps/web/src/app/organizations/core/views/index.ts +++ b/apps/web/src/app/organizations/core/views/index.ts @@ -1,3 +1,5 @@ export * from "./collection-access-selection.view"; export * from "./collection-admin.view"; export * from "./group.view"; +export * from "./organization-user.view"; +export * from "./user-admin-view"; diff --git a/apps/web/src/app/organizations/core/views/user-admin-view.ts b/apps/web/src/app/organizations/core/views/user-admin-view.ts new file mode 100644 index 00000000000..5496d3475bb --- /dev/null +++ b/apps/web/src/app/organizations/core/views/user-admin-view.ts @@ -0,0 +1,18 @@ +import { OrganizationUserStatusType } from "@bitwarden/common/enums/organizationUserStatusType"; +import { OrganizationUserType } from "@bitwarden/common/enums/organizationUserType"; +import { PermissionsApi } from "@bitwarden/common/models/api/permissions.api"; + +import { CollectionAccessSelectionView } from "./collection-access-selection.view"; + +export class OrganizationUserAdminView { + id: string; + userId: string; + organizationId: string; + type: OrganizationUserType; + status: OrganizationUserStatusType; + accessAll: boolean; + permissions: PermissionsApi; + resetPasswordEnrolled: boolean; + + collections: CollectionAccessSelectionView[] = []; +} diff --git a/apps/web/src/app/organizations/manage/user-add-edit.component.ts b/apps/web/src/app/organizations/manage/user-add-edit.component.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/apps/web/src/app/organizations/members/components/member-dialog/member-dialog.component.html b/apps/web/src/app/organizations/members/components/member-dialog/member-dialog.component.html index f4d53b4ce22..55c60e88519 100644 --- a/apps/web/src/app/organizations/members/components/member-dialog/member-dialog.component.html +++ b/apps/web/src/app/organizations/members/components/member-dialog/member-dialog.component.html @@ -1,5 +1,5 @@
- + {{ title }} {{ @@ -114,6 +114,7 @@ id="userTypeCustom" [value]="organizationUserType.Custom" [(ngModel)]="type" + [ngModelOptions]="{ standalone: true }" [attr.disabled]="!canUseCustomPermissions || null" />