mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-21 11:35:34 +01:00
[AC-2303] Implement approveAllRequests method (#9031)
* [AC-2302] Move organization-auth-request.service to bit-common folder * [AC-2302] Rename organization-auth-request.service to organization-auth-request-api.service * [AC-2302] Move logic from component to organization-auth-request.service * [AC-2302] Fix import path in OrganizationAuthRequestService * [AC-2302] Move imports to OrganizationsModule and delete unused CoreOrganizationModule * [AC-2302] Move the call to get userResetPasswordDetails into OrganizationAuthRequestService * [AC-2302] Remove @Injectable() and manually configure dependencies * [AC-2302] Add OrganizationAuthRequestService unit tests first draft * [AC-2302] Refactor device-approvals.component.ts to remove unused imports * [AC-2302] Set up jest on bit-common and add unit tests for OrganizationAuthRequestService * [AC-2302] Add bit-common to jest.config.js * [AC-2302] Update organizations.module.ts to include safeProviders declared in variable * [AC-2302] Remove services and views folders from bit-common * [AC-2302] Define path mapping * Adjust an import path The import path of `PendingAuthRequestView` in `OrganizationAuthRequestApiService` was pointing to the wrong place. I think this file was just recently moved, and the import didn't get updated. * Get paths working * Fix import * Update jest config to use ts-jest adn jsdom * Copy-paste path mappings from bit-web * Remove unnecessary test setup file * Undo unnecessary change * Fix remaining path mappings * Remove Bitwarden License mapping from OSS code * Fix bit-web so it uses its own tsconfig * Fix import path * Remove web-bit entrypoint from OSS tsconfig * Make DeviceApprovalsComponent standalone * Remove organization-auth-request-api.service export * Add BulkApproveAuthRequestsRequest class for bulk approval of authentication requests * Add api call for device bulk approvals * Add bulk device approval to OrganizationAuthRequestService * Add unit tests for bulk device approval method * Remove OrganizationsRoutingModule from DeviceApprovalsComponent imports * Remove CoreOrganizationModule from OrganizationsModule imports * Remove NoItemsModule from OrganizationsModule imports * Get keys for each item to approve * Update approvePendingRequests unit test * Use ApiService from JslibServicesModule * Update providers in device-approvals.component.ts * Add method to retrieve reset password details for multiple organization users * Add organizationUserId property to OrganizationUserResetPasswordDetailsResponse class * Use method to retrieve reset password details for multiple organization users * Rename ResetPasswordDetails to AccountRecoveryDetails * Update OrganizationAuthRequestService to use getManyOrganizationUserAccountRecoveryDetails * Update AdminAuthRequestUpdateWithIdRequest property names and imports * Refactor bulk approval functionality in organization auth requests * Rename update request AdminAuthRequestUpdateWithIdRequest to OrganizationAuthRequestUpdateRequest * Update organization-auth-request.service.spec.ts to use bulkUpdatePendingRequests method --------- Co-authored-by: Addison Beck <hello@addisonbeck.com> Co-authored-by: Thomas Rittson <trittson@bitwarden.com>
This commit is contained in:
parent
f3b82c30f2
commit
a49e7bb35f
@ -4,6 +4,7 @@ import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
|||||||
|
|
||||||
import { AdminAuthRequestUpdateRequest } from "./admin-auth-request-update.request";
|
import { AdminAuthRequestUpdateRequest } from "./admin-auth-request-update.request";
|
||||||
import { BulkDenyAuthRequestsRequest } from "./bulk-deny-auth-requests.request";
|
import { BulkDenyAuthRequestsRequest } from "./bulk-deny-auth-requests.request";
|
||||||
|
import { OrganizationAuthRequestUpdateRequest } from "./organization-auth-request-update.request";
|
||||||
import { PendingAuthRequestView } from "./pending-auth-request.view";
|
import { PendingAuthRequestView } from "./pending-auth-request.view";
|
||||||
import { PendingOrganizationAuthRequestResponse } from "./pending-organization-auth-request.response";
|
import { PendingOrganizationAuthRequestResponse } from "./pending-organization-auth-request.response";
|
||||||
|
|
||||||
@ -34,6 +35,19 @@ export class OrganizationAuthRequestApiService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async bulkUpdatePendingRequests(
|
||||||
|
organizationId: string,
|
||||||
|
items: OrganizationAuthRequestUpdateRequest[],
|
||||||
|
): Promise<void> {
|
||||||
|
await this.apiService.send(
|
||||||
|
"POST",
|
||||||
|
`/organizations/${organizationId}/auth-requests`,
|
||||||
|
items,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async approvePendingRequest(
|
async approvePendingRequest(
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
requestId: string,
|
requestId: string,
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
export class OrganizationAuthRequestUpdateRequest {
|
||||||
|
constructor(
|
||||||
|
public id: string,
|
||||||
|
public approved: boolean,
|
||||||
|
public key?: string,
|
||||||
|
) {}
|
||||||
|
}
|
@ -2,10 +2,12 @@ import { MockProxy, mock } from "jest-mock-extended";
|
|||||||
|
|
||||||
import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
|
import { OrganizationUserService } from "@bitwarden/common/admin-console/abstractions/organization-user/organization-user.service";
|
||||||
import { OrganizationUserResetPasswordDetailsResponse } from "@bitwarden/common/admin-console/abstractions/organization-user/responses";
|
import { OrganizationUserResetPasswordDetailsResponse } from "@bitwarden/common/admin-console/abstractions/organization-user/responses";
|
||||||
|
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||||
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
|
||||||
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
||||||
|
|
||||||
import { OrganizationAuthRequestApiService } from "./organization-auth-request-api.service";
|
import { OrganizationAuthRequestApiService } from "./organization-auth-request-api.service";
|
||||||
|
import { OrganizationAuthRequestUpdateRequest } from "./organization-auth-request-update.request";
|
||||||
import { OrganizationAuthRequestService } from "./organization-auth-request.service";
|
import { OrganizationAuthRequestService } from "./organization-auth-request.service";
|
||||||
import { PendingAuthRequestView } from "./pending-auth-request.view";
|
import { PendingAuthRequestView } from "./pending-auth-request.view";
|
||||||
|
|
||||||
@ -92,6 +94,55 @@ describe("OrganizationAuthRequestService", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("approvePendingRequests", () => {
|
||||||
|
it("should approve the specified pending auth requests", async () => {
|
||||||
|
jest.spyOn(organizationAuthRequestApiService, "bulkUpdatePendingRequests");
|
||||||
|
|
||||||
|
const organizationId = "organizationId";
|
||||||
|
|
||||||
|
const organizationUserResetPasswordDetailsResponse = new ListResponse(
|
||||||
|
{
|
||||||
|
Data: [
|
||||||
|
{
|
||||||
|
organizationUserId: "organizationUserId1",
|
||||||
|
resetPasswordKey: "resetPasswordKey",
|
||||||
|
encryptedPrivateKey: "encryptedPrivateKey",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
OrganizationUserResetPasswordDetailsResponse,
|
||||||
|
);
|
||||||
|
|
||||||
|
organizationUserService.getManyOrganizationUserAccountRecoveryDetails.mockResolvedValueOnce(
|
||||||
|
organizationUserResetPasswordDetailsResponse,
|
||||||
|
);
|
||||||
|
|
||||||
|
const encryptedUserKey = new EncString("encryptedUserKey");
|
||||||
|
cryptoService.rsaDecrypt.mockResolvedValue(new Uint8Array(32));
|
||||||
|
cryptoService.rsaEncrypt.mockResolvedValue(encryptedUserKey);
|
||||||
|
|
||||||
|
const mockPendingAuthRequest = new PendingAuthRequestView();
|
||||||
|
mockPendingAuthRequest.id = "requestId1";
|
||||||
|
mockPendingAuthRequest.organizationUserId = "organizationUserId1";
|
||||||
|
mockPendingAuthRequest.publicKey = "publicKey1";
|
||||||
|
|
||||||
|
await organizationAuthRequestService.approvePendingRequests(organizationId, [
|
||||||
|
mockPendingAuthRequest,
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(organizationAuthRequestApiService.bulkUpdatePendingRequests).toHaveBeenCalledWith(
|
||||||
|
organizationId,
|
||||||
|
[
|
||||||
|
new OrganizationAuthRequestUpdateRequest(
|
||||||
|
"requestId1",
|
||||||
|
true,
|
||||||
|
encryptedUserKey.encryptedString,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("approvePendingRequest", () => {
|
describe("approvePendingRequest", () => {
|
||||||
it("should approve the specified pending auth request", async () => {
|
it("should approve the specified pending auth request", async () => {
|
||||||
jest.spyOn(organizationAuthRequestApiService, "approvePendingRequest");
|
jest.spyOn(organizationAuthRequestApiService, "approvePendingRequest");
|
||||||
|
@ -6,6 +6,7 @@ import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";
|
|||||||
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
|
||||||
|
|
||||||
import { OrganizationAuthRequestApiService } from "./organization-auth-request-api.service";
|
import { OrganizationAuthRequestApiService } from "./organization-auth-request-api.service";
|
||||||
|
import { OrganizationAuthRequestUpdateRequest } from "./organization-auth-request-update.request";
|
||||||
import { PendingAuthRequestView } from "./pending-auth-request.view";
|
import { PendingAuthRequestView } from "./pending-auth-request.view";
|
||||||
|
|
||||||
export class OrganizationAuthRequestService {
|
export class OrganizationAuthRequestService {
|
||||||
@ -23,6 +24,42 @@ export class OrganizationAuthRequestService {
|
|||||||
await this.organizationAuthRequestApiService.denyPendingRequests(organizationId, ...requestIds);
|
await this.organizationAuthRequestApiService.denyPendingRequests(organizationId, ...requestIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async approvePendingRequests(
|
||||||
|
organizationId: string,
|
||||||
|
authRequests: PendingAuthRequestView[],
|
||||||
|
): Promise<void> {
|
||||||
|
const organizationUserIds = authRequests.map((r) => r.organizationUserId);
|
||||||
|
const details =
|
||||||
|
await this.organizationUserService.getManyOrganizationUserAccountRecoveryDetails(
|
||||||
|
organizationId,
|
||||||
|
organizationUserIds,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
details == null ||
|
||||||
|
details.data.length == 0 ||
|
||||||
|
details.data.some((d) => d.resetPasswordKey == null)
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
"All users must be enrolled in account recovery (password reset) in order for the requests to be approved.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestsToApprove = await Promise.all(
|
||||||
|
authRequests.map(async (r) => {
|
||||||
|
const detail = details.data.find((d) => d.organizationUserId === r.organizationUserId);
|
||||||
|
const encryptedKey = await this.getEncryptedUserKey(organizationId, r.publicKey, detail);
|
||||||
|
|
||||||
|
return new OrganizationAuthRequestUpdateRequest(r.id, true, encryptedKey.encryptedString);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.organizationAuthRequestApiService.bulkUpdatePendingRequests(
|
||||||
|
organizationId,
|
||||||
|
requestsToApprove,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async approvePendingRequest(organizationId: string, authRequest: PendingAuthRequestView) {
|
async approvePendingRequest(organizationId: string, authRequest: PendingAuthRequestView) {
|
||||||
const details = await this.organizationUserService.getOrganizationUserResetPasswordDetails(
|
const details = await this.organizationUserService.getOrganizationUserResetPasswordDetails(
|
||||||
organizationId,
|
organizationId,
|
||||||
|
Loading…
Reference in New Issue
Block a user