mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 08:13:42 +00:00
Merge branch 'master' into EC-598-beeep-properly-store-passkeys-in-bitwarden
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ProfileResponse } from "../../models/response/profile.response";
|
||||
export abstract class AvatarUpdateService {
|
||||
avatarUpdate$ = new Observable<string | null>();
|
||||
abstract pushUpdate(color: string): Promise<ProfileResponse | void>;
|
||||
abstract loadColorFromState(): Promise<string | null>;
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import { CipherCreateRequest } from "../models/request/cipher-create.request";
|
||||
import { CipherPartialRequest } from "../models/request/cipher-partial.request";
|
||||
import { CipherShareRequest } from "../models/request/cipher-share.request";
|
||||
import { CipherRequest } from "../models/request/cipher.request";
|
||||
import { CollectionBulkDeleteRequest } from "../models/request/collection-bulk-delete.request";
|
||||
import { CollectionRequest } from "../models/request/collection.request";
|
||||
import { DeleteRecoverRequest } from "../models/request/delete-recover.request";
|
||||
import { DeviceVerificationRequest } from "../models/request/device-verification.request";
|
||||
@@ -22,7 +23,6 @@ import { EmergencyAccessInviteRequest } from "../models/request/emergency-access
|
||||
import { EmergencyAccessPasswordRequest } from "../models/request/emergency-access-password.request";
|
||||
import { EmergencyAccessUpdateRequest } from "../models/request/emergency-access-update.request";
|
||||
import { EventRequest } from "../models/request/event.request";
|
||||
import { GroupRequest } from "../models/request/group.request";
|
||||
import { IapCheckRequest } from "../models/request/iap-check.request";
|
||||
import { PasswordTokenRequest } from "../models/request/identity-token/password-token.request";
|
||||
import { SsoTokenRequest } from "../models/request/identity-token/sso-token.request";
|
||||
@@ -34,15 +34,6 @@ import { KeyConnectorUserKeyRequest } from "../models/request/key-connector-user
|
||||
import { KeysRequest } from "../models/request/keys.request";
|
||||
import { OrganizationConnectionRequest } from "../models/request/organization-connection.request";
|
||||
import { OrganizationImportRequest } from "../models/request/organization-import.request";
|
||||
import { OrganizationUserAcceptRequest } from "../models/request/organization-user-accept.request";
|
||||
import { OrganizationUserBulkConfirmRequest } from "../models/request/organization-user-bulk-confirm.request";
|
||||
import { OrganizationUserBulkRequest } from "../models/request/organization-user-bulk.request";
|
||||
import { OrganizationUserConfirmRequest } from "../models/request/organization-user-confirm.request";
|
||||
import { OrganizationUserInviteRequest } from "../models/request/organization-user-invite.request";
|
||||
import { OrganizationUserResetPasswordEnrollmentRequest } from "../models/request/organization-user-reset-password-enrollment.request";
|
||||
import { OrganizationUserResetPasswordRequest } from "../models/request/organization-user-reset-password.request";
|
||||
import { OrganizationUserUpdateGroupsRequest } from "../models/request/organization-user-update-groups.request";
|
||||
import { OrganizationUserUpdateRequest } from "../models/request/organization-user-update.request";
|
||||
import { OrganizationSponsorshipCreateRequest } from "../models/request/organization/organization-sponsorship-create.request";
|
||||
import { OrganizationSponsorshipRedeemRequest } from "../models/request/organization/organization-sponsorship-redeem.request";
|
||||
import { PasswordHintRequest } from "../models/request/password-hint.request";
|
||||
@@ -71,6 +62,7 @@ import { TaxInfoUpdateRequest } from "../models/request/tax-info-update.request"
|
||||
import { TwoFactorEmailRequest } from "../models/request/two-factor-email.request";
|
||||
import { TwoFactorProviderRequest } from "../models/request/two-factor-provider.request";
|
||||
import { TwoFactorRecoveryRequest } from "../models/request/two-factor-recovery.request";
|
||||
import { UpdateAvatarRequest } from "../models/request/update-avatar.request";
|
||||
import { UpdateDomainsRequest } from "../models/request/update-domains.request";
|
||||
import { UpdateKeyRequest } from "../models/request/update-key.request";
|
||||
import { UpdateProfileRequest } from "../models/request/update-profile.request";
|
||||
@@ -93,7 +85,7 @@ import { BillingPaymentResponse } from "../models/response/billing-payment.respo
|
||||
import { BreachAccountResponse } from "../models/response/breach-account.response";
|
||||
import { CipherResponse } from "../models/response/cipher.response";
|
||||
import {
|
||||
CollectionGroupDetailsResponse,
|
||||
CollectionAccessDetailsResponse,
|
||||
CollectionResponse,
|
||||
} from "../models/response/collection.response";
|
||||
import { DeviceVerificationResponse } from "../models/response/device-verification.response";
|
||||
@@ -105,7 +97,6 @@ import {
|
||||
EmergencyAccessViewResponse,
|
||||
} from "../models/response/emergency-access.response";
|
||||
import { EventResponse } from "../models/response/event.response";
|
||||
import { GroupDetailsResponse, GroupResponse } from "../models/response/group.response";
|
||||
import { IdentityCaptchaResponse } from "../models/response/identity-captcha.response";
|
||||
import { IdentityTokenResponse } from "../models/response/identity-token.response";
|
||||
import { IdentityTwoFactorResponse } from "../models/response/identity-two-factor.response";
|
||||
@@ -117,13 +108,6 @@ import {
|
||||
} from "../models/response/organization-connection.response";
|
||||
import { OrganizationExportResponse } from "../models/response/organization-export.response";
|
||||
import { OrganizationSponsorshipSyncStatusResponse } from "../models/response/organization-sponsorship-sync-status.response";
|
||||
import { OrganizationUserBulkPublicKeyResponse } from "../models/response/organization-user-bulk-public-key.response";
|
||||
import { OrganizationUserBulkResponse } from "../models/response/organization-user-bulk.response";
|
||||
import {
|
||||
OrganizationUserDetailsResponse,
|
||||
OrganizationUserUserDetailsResponse,
|
||||
OrganizationUserResetPasswordDetailsReponse,
|
||||
} from "../models/response/organization-user.response";
|
||||
import { PaymentResponse } from "../models/response/payment.response";
|
||||
import { PlanResponse } from "../models/response/plan.response";
|
||||
import { PolicyResponse } from "../models/response/policy.response";
|
||||
@@ -136,8 +120,8 @@ import {
|
||||
import { ProviderUserBulkPublicKeyResponse } from "../models/response/provider/provider-user-bulk-public-key.response";
|
||||
import { ProviderUserBulkResponse } from "../models/response/provider/provider-user-bulk.response";
|
||||
import {
|
||||
ProviderUserUserDetailsResponse,
|
||||
ProviderUserResponse,
|
||||
ProviderUserUserDetailsResponse,
|
||||
} from "../models/response/provider/provider-user.response";
|
||||
import { ProviderResponse } from "../models/response/provider/provider.response";
|
||||
import { SelectionReadOnlyResponse } from "../models/response/selection-read-only.response";
|
||||
@@ -156,13 +140,18 @@ import { TwoFactorEmailResponse } from "../models/response/two-factor-email.resp
|
||||
import { TwoFactorProviderResponse } from "../models/response/two-factor-provider.response";
|
||||
import { TwoFactorRecoverResponse } from "../models/response/two-factor-recover.response";
|
||||
import {
|
||||
TwoFactorWebAuthnResponse,
|
||||
ChallengeResponse,
|
||||
TwoFactorWebAuthnResponse,
|
||||
} from "../models/response/two-factor-web-authn.response";
|
||||
import { TwoFactorYubiKeyResponse } from "../models/response/two-factor-yubi-key.response";
|
||||
import { UserKeyResponse } from "../models/response/user-key.response";
|
||||
import { SendAccessView } from "../models/view/send-access.view";
|
||||
|
||||
/**
|
||||
* @deprecated The `ApiService` class is deprecated and calls should be extracted into individual
|
||||
* api services. The `send` method is still allowed to be used within api services. For background
|
||||
* of this decision please read https://contributing.bitwarden.com/architecture/adr/refactor-api-service.
|
||||
*/
|
||||
export abstract class ApiService {
|
||||
send: (
|
||||
method: "GET" | "POST" | "PUT" | "DELETE",
|
||||
@@ -183,6 +172,7 @@ export abstract class ApiService {
|
||||
getUserSubscription: () => Promise<SubscriptionResponse>;
|
||||
getTaxInfo: () => Promise<TaxInfoResponse>;
|
||||
putProfile: (request: UpdateProfileRequest) => Promise<ProfileResponse>;
|
||||
putAvatar: (request: UpdateAvatarRequest) => Promise<ProfileResponse>;
|
||||
putTaxInfo: (request: TaxInfoUpdateRequest) => Promise<any>;
|
||||
postPrelogin: (request: PreloginRequest) => Promise<PreloginResponse>;
|
||||
postEmailToken: (request: EmailTokenRequest) => Promise<any>;
|
||||
@@ -313,13 +303,16 @@ export abstract class ApiService {
|
||||
) => Promise<AttachmentUploadDataResponse>;
|
||||
postAttachmentFile: (id: string, attachmentId: string, data: FormData) => Promise<any>;
|
||||
|
||||
getCollectionDetails: (
|
||||
organizationId: string,
|
||||
id: string
|
||||
) => Promise<CollectionGroupDetailsResponse>;
|
||||
getUserCollections: () => Promise<ListResponse<CollectionResponse>>;
|
||||
getCollections: (organizationId: string) => Promise<ListResponse<CollectionResponse>>;
|
||||
getCollectionUsers: (organizationId: string, id: string) => Promise<SelectionReadOnlyResponse[]>;
|
||||
getCollectionAccessDetails: (
|
||||
organizationId: string,
|
||||
id: string
|
||||
) => Promise<CollectionAccessDetailsResponse>;
|
||||
getManyCollectionsWithAccessDetails: (
|
||||
orgId: string
|
||||
) => Promise<ListResponse<CollectionAccessDetailsResponse>>;
|
||||
postCollection: (
|
||||
organizationId: string,
|
||||
request: CollectionRequest
|
||||
@@ -335,97 +328,17 @@ export abstract class ApiService {
|
||||
request: CollectionRequest
|
||||
) => Promise<CollectionResponse>;
|
||||
deleteCollection: (organizationId: string, id: string) => Promise<any>;
|
||||
deleteManyCollections: (request: CollectionBulkDeleteRequest) => Promise<any>;
|
||||
deleteCollectionUser: (
|
||||
organizationId: string,
|
||||
id: string,
|
||||
organizationUserId: string
|
||||
) => Promise<any>;
|
||||
|
||||
getGroupDetails: (organizationId: string, id: string) => Promise<GroupDetailsResponse>;
|
||||
getGroups: (organizationId: string) => Promise<ListResponse<GroupResponse>>;
|
||||
getGroupUsers: (organizationId: string, id: string) => Promise<string[]>;
|
||||
postGroup: (organizationId: string, request: GroupRequest) => Promise<GroupResponse>;
|
||||
putGroup: (organizationId: string, id: string, request: GroupRequest) => Promise<GroupResponse>;
|
||||
putGroupUsers: (organizationId: string, id: string, request: string[]) => Promise<any>;
|
||||
deleteGroup: (organizationId: string, id: string) => Promise<any>;
|
||||
deleteGroupUser: (organizationId: string, id: string, organizationUserId: string) => Promise<any>;
|
||||
|
||||
getOrganizationUser: (
|
||||
organizationId: string,
|
||||
id: string
|
||||
) => Promise<OrganizationUserDetailsResponse>;
|
||||
getOrganizationUserGroups: (organizationId: string, id: string) => Promise<string[]>;
|
||||
getOrganizationUsers: (
|
||||
organizationId: string
|
||||
) => Promise<ListResponse<OrganizationUserUserDetailsResponse>>;
|
||||
getOrganizationUserResetPasswordDetails: (
|
||||
organizationId: string,
|
||||
id: string
|
||||
) => Promise<OrganizationUserResetPasswordDetailsReponse>;
|
||||
postOrganizationUserInvite: (
|
||||
organizationId: string,
|
||||
request: OrganizationUserInviteRequest
|
||||
) => Promise<any>;
|
||||
postOrganizationUserReinvite: (organizationId: string, id: string) => Promise<any>;
|
||||
postManyOrganizationUserReinvite: (
|
||||
organizationId: string,
|
||||
request: OrganizationUserBulkRequest
|
||||
) => Promise<ListResponse<OrganizationUserBulkResponse>>;
|
||||
postOrganizationUserAccept: (
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserAcceptRequest
|
||||
) => Promise<any>;
|
||||
postOrganizationUserConfirm: (
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserConfirmRequest
|
||||
) => Promise<any>;
|
||||
postOrganizationUsersPublicKey: (
|
||||
organizationId: string,
|
||||
request: OrganizationUserBulkRequest
|
||||
) => Promise<ListResponse<OrganizationUserBulkPublicKeyResponse>>;
|
||||
postOrganizationUserBulkConfirm: (
|
||||
organizationId: string,
|
||||
request: OrganizationUserBulkConfirmRequest
|
||||
) => Promise<ListResponse<OrganizationUserBulkResponse>>;
|
||||
|
||||
putOrganizationUser: (
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserUpdateRequest
|
||||
) => Promise<any>;
|
||||
putOrganizationUserGroups: (
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserUpdateGroupsRequest
|
||||
) => Promise<any>;
|
||||
putOrganizationUserResetPasswordEnrollment: (
|
||||
organizationId: string,
|
||||
userId: string,
|
||||
request: OrganizationUserResetPasswordEnrollmentRequest
|
||||
) => Promise<void>;
|
||||
putOrganizationUserResetPassword: (
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserResetPasswordRequest
|
||||
) => Promise<any>;
|
||||
deleteOrganizationUser: (organizationId: string, id: string) => Promise<any>;
|
||||
deleteManyOrganizationUsers: (
|
||||
organizationId: string,
|
||||
request: OrganizationUserBulkRequest
|
||||
) => Promise<ListResponse<OrganizationUserBulkResponse>>;
|
||||
revokeOrganizationUser: (organizationId: string, id: string) => Promise<any>;
|
||||
revokeManyOrganizationUsers: (
|
||||
organizationId: string,
|
||||
request: OrganizationUserBulkRequest
|
||||
) => Promise<ListResponse<OrganizationUserBulkResponse>>;
|
||||
restoreOrganizationUser: (organizationId: string, id: string) => Promise<any>;
|
||||
restoreManyOrganizationUsers: (
|
||||
organizationId: string,
|
||||
request: OrganizationUserBulkRequest
|
||||
) => Promise<ListResponse<OrganizationUserBulkResponse>>;
|
||||
|
||||
getSync: () => Promise<SyncResponse>;
|
||||
postPublicImportDirectory: (request: OrganizationImportRequest) => Promise<any>;
|
||||
|
||||
|
||||
@@ -66,8 +66,8 @@ export abstract class CipherService {
|
||||
deleteManyWithServer: (ids: string[]) => Promise<any>;
|
||||
deleteAttachment: (id: string, attachmentId: string) => Promise<void>;
|
||||
deleteAttachmentWithServer: (id: string, attachmentId: string) => Promise<void>;
|
||||
sortCiphersByLastUsed: (a: any, b: any) => number;
|
||||
sortCiphersByLastUsedThenName: (a: any, b: any) => number;
|
||||
sortCiphersByLastUsed: (a: CipherView, b: CipherView) => number;
|
||||
sortCiphersByLastUsedThenName: (a: CipherView, b: CipherView) => number;
|
||||
getLocaleSortingFunction: () => (a: CipherView, b: CipherView) => number;
|
||||
softDelete: (id: string | string[]) => Promise<any>;
|
||||
softDeleteWithServer: (id: string) => Promise<any>;
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { EventType } from "../enums/eventType";
|
||||
|
||||
export abstract class EventService {
|
||||
collect: (
|
||||
eventType: EventType,
|
||||
cipherId?: string,
|
||||
uploadImmediately?: boolean,
|
||||
organizationId?: string
|
||||
) => Promise<any>;
|
||||
uploadEvents: (userId?: string) => Promise<any>;
|
||||
clearEvents: (userId?: string) => Promise<any>;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { EventType } from "../../enums/eventType";
|
||||
|
||||
export abstract class EventCollectionService {
|
||||
collect: (
|
||||
eventType: EventType,
|
||||
cipherId?: string,
|
||||
uploadImmediately?: boolean,
|
||||
organizationId?: string
|
||||
) => Promise<any>;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export abstract class EventUploadService {
|
||||
uploadEvents: (userId?: string) => Promise<void>;
|
||||
}
|
||||
@@ -12,6 +12,7 @@ export abstract class FolderService {
|
||||
clearCache: () => Promise<void>;
|
||||
encrypt: (model: FolderView, key?: SymmetricCryptoKey) => Promise<Folder>;
|
||||
get: (id: string) => Promise<Folder>;
|
||||
getAllFromState: () => Promise<Folder[]>;
|
||||
/**
|
||||
* @deprecated Only use in CLI!
|
||||
*/
|
||||
|
||||
@@ -6,6 +6,6 @@ export abstract class I18nService {
|
||||
translationLocale: string;
|
||||
collator: Intl.Collator;
|
||||
localeNames: Map<string, string>;
|
||||
t: (id: string, p1?: string, p2?: string, p3?: string) => string;
|
||||
t: (id: string, p1?: string | number, p2?: string | number, p3?: string | number) => string;
|
||||
translate: (id: string, p1?: string, p2?: string, p3?: string) => string;
|
||||
}
|
||||
|
||||
@@ -4,4 +4,5 @@ export abstract class LoginService {
|
||||
setEmail: (value: string) => void;
|
||||
setRememberEmail: (value: boolean) => void;
|
||||
clearValues: () => void;
|
||||
saveEmailSettings: () => Promise<void>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
import { ListResponse } from "../../models/response/list.response";
|
||||
|
||||
import {
|
||||
OrganizationUserAcceptRequest,
|
||||
OrganizationUserBulkConfirmRequest,
|
||||
OrganizationUserConfirmRequest,
|
||||
OrganizationUserInviteRequest,
|
||||
OrganizationUserResetPasswordEnrollmentRequest,
|
||||
OrganizationUserResetPasswordRequest,
|
||||
OrganizationUserUpdateGroupsRequest,
|
||||
OrganizationUserUpdateRequest,
|
||||
} from "./requests";
|
||||
import {
|
||||
OrganizationUserBulkPublicKeyResponse,
|
||||
OrganizationUserBulkResponse,
|
||||
OrganizationUserDetailsResponse,
|
||||
OrganizationUserResetPasswordDetailsReponse,
|
||||
OrganizationUserUserDetailsResponse,
|
||||
} from "./responses";
|
||||
|
||||
/**
|
||||
* Service for interacting with Organization Users via the API
|
||||
*/
|
||||
export abstract class OrganizationUserService {
|
||||
/**
|
||||
* Retrieve a single organization user by Id
|
||||
* @param organizationId - Identifier for the user's organization
|
||||
* @param id - Organization user identifier
|
||||
* @param options - Options for the request
|
||||
*/
|
||||
abstract getOrganizationUser(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
options?: {
|
||||
includeGroups?: boolean;
|
||||
}
|
||||
): Promise<OrganizationUserDetailsResponse>;
|
||||
|
||||
/**
|
||||
* Retrieve a list of groups Ids the specified organization user belongs to
|
||||
* @param organizationId - Identifier for the user's organization
|
||||
* @param id - Organization user identifier
|
||||
*/
|
||||
abstract getOrganizationUserGroups(organizationId: string, id: string): Promise<string[]>;
|
||||
|
||||
/**
|
||||
* Retrieve a list of all users that belong to the specified organization
|
||||
* @param organizationId - Identifier for the organization
|
||||
* @param options - Options for the request
|
||||
*/
|
||||
abstract getAllUsers(
|
||||
organizationId: string,
|
||||
options?: {
|
||||
includeCollections?: boolean;
|
||||
includeGroups?: boolean;
|
||||
}
|
||||
): Promise<ListResponse<OrganizationUserUserDetailsResponse>>;
|
||||
|
||||
/**
|
||||
* Retrieve reset password details for the specified organization user
|
||||
* @param organizationId - Identifier for the user's organization
|
||||
* @param id - Organization user identifier
|
||||
*/
|
||||
abstract getOrganizationUserResetPasswordDetails(
|
||||
organizationId: string,
|
||||
id: string
|
||||
): Promise<OrganizationUserResetPasswordDetailsReponse>;
|
||||
|
||||
/**
|
||||
* Create new organization user invite(s) for the specified organization
|
||||
* @param organizationId - Identifier for the organization
|
||||
* @param request - New user invitation request details
|
||||
*/
|
||||
abstract postOrganizationUserInvite(
|
||||
organizationId: string,
|
||||
request: OrganizationUserInviteRequest
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Re-invite the specified organization user
|
||||
* @param organizationId - Identifier for the user's organization
|
||||
* @param id - Organization user identifier
|
||||
*/
|
||||
abstract postOrganizationUserReinvite(organizationId: string, id: string): Promise<any>;
|
||||
|
||||
/**
|
||||
* Re-invite many organization users for the specified organization
|
||||
* @param organizationId - Identifier for the organization
|
||||
* @param ids - A list of organization user identifiers
|
||||
* @return List of user ids, including both those that were successfully re-invited and those that had an error
|
||||
*/
|
||||
abstract postManyOrganizationUserReinvite(
|
||||
organizationId: string,
|
||||
ids: string[]
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>>;
|
||||
|
||||
/**
|
||||
* Accept an organization user invitation
|
||||
* @param organizationId - Identifier for the organization to accept
|
||||
* @param id - Organization user identifier
|
||||
* @param request - Request details for accepting the invitation
|
||||
*/
|
||||
abstract postOrganizationUserAccept(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserAcceptRequest
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Confirm an organization user that has accepted their invitation
|
||||
* @param organizationId - Identifier for the organization to confirm
|
||||
* @param id - Organization user identifier
|
||||
* @param request - Request details for confirming the user
|
||||
*/
|
||||
abstract postOrganizationUserConfirm(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserConfirmRequest
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Retrieve a list of the specified users' public keys
|
||||
* @param organizationId - Identifier for the organization to accept
|
||||
* @param ids - A list of organization user identifiers to retrieve public keys for
|
||||
*/
|
||||
abstract postOrganizationUsersPublicKey(
|
||||
organizationId: string,
|
||||
ids: string[]
|
||||
): Promise<ListResponse<OrganizationUserBulkPublicKeyResponse>>;
|
||||
|
||||
/**
|
||||
* Confirm many organization users that have accepted their invitations
|
||||
* @param organizationId - Identifier for the organization to confirm users
|
||||
* @param request - Bulk request details for confirming the user
|
||||
*/
|
||||
abstract postOrganizationUserBulkConfirm(
|
||||
organizationId: string,
|
||||
request: OrganizationUserBulkConfirmRequest
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>>;
|
||||
|
||||
/**
|
||||
* Update an organization users
|
||||
* @param organizationId - Identifier for the organization the user belongs to
|
||||
* @param id - Organization user identifier
|
||||
* @param request - Request details for updating the user
|
||||
*/
|
||||
abstract putOrganizationUser(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserUpdateRequest
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Update an organization user's groups
|
||||
* @param organizationId - Identifier for the organization the user belongs to
|
||||
* @param id - Organization user identifier
|
||||
* @param groupIds - List of group ids to associate the user with
|
||||
*/
|
||||
abstract putOrganizationUserGroups(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
groupIds: OrganizationUserUpdateGroupsRequest
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Update an organization user's reset password enrollment
|
||||
* @param organizationId - Identifier for the organization the user belongs to
|
||||
* @param userId - Organization user identifier
|
||||
* @param request - Reset password enrollment details
|
||||
*/
|
||||
abstract putOrganizationUserResetPasswordEnrollment(
|
||||
organizationId: string,
|
||||
userId: string,
|
||||
request: OrganizationUserResetPasswordEnrollmentRequest
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Reset an organization user's password
|
||||
* @param organizationId - Identifier for the organization the user belongs to
|
||||
* @param id - Organization user identifier
|
||||
* @param request - Reset password details
|
||||
*/
|
||||
abstract putOrganizationUserResetPassword(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserResetPasswordRequest
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Delete an organization user
|
||||
* @param organizationId - Identifier for the organization the user belongs to
|
||||
* @param id - Organization user identifier
|
||||
*/
|
||||
abstract deleteOrganizationUser(organizationId: string, id: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Delete many organization users
|
||||
* @param organizationId - Identifier for the organization the users belongs to
|
||||
* @param ids - List of organization user identifiers to delete
|
||||
* @return List of user ids, including both those that were successfully deleted and those that had an error
|
||||
*/
|
||||
abstract deleteManyOrganizationUsers(
|
||||
organizationId: string,
|
||||
ids: string[]
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>>;
|
||||
|
||||
/**
|
||||
* Revoke an organization user's access to the organization
|
||||
* @param organizationId - Identifier for the organization the user belongs to
|
||||
* @param id - Organization user identifier
|
||||
*/
|
||||
abstract revokeOrganizationUser(organizationId: string, id: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Revoke many organization users' access to the organization
|
||||
* @param organizationId - Identifier for the organization the users belongs to
|
||||
* @param ids - List of organization user identifiers to revoke
|
||||
* @return List of user ids, including both those that were successfully revoked and those that had an error
|
||||
*/
|
||||
abstract revokeManyOrganizationUsers(
|
||||
organizationId: string,
|
||||
ids: string[]
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>>;
|
||||
|
||||
/**
|
||||
* Restore an organization user's access to the organization
|
||||
* @param organizationId - Identifier for the organization the user belongs to
|
||||
* @param id - Organization user identifier
|
||||
*/
|
||||
abstract restoreOrganizationUser(organizationId: string, id: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Restore many organization users' access to the organization
|
||||
* @param organizationId - Identifier for the organization the users belongs to
|
||||
* @param ids - List of organization user identifiers to restore
|
||||
* @return List of user ids, including both those that were successfully restored and those that had an error
|
||||
*/
|
||||
abstract restoreManyOrganizationUsers(
|
||||
organizationId: string,
|
||||
ids: string[]
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>>;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export * from "./organization-user-accept.request";
|
||||
export * from "./organization-user-bulk-confirm.request";
|
||||
export * from "./organization-user-confirm.request";
|
||||
export * from "./organization-user-invite.request";
|
||||
export * from "./organization-user-reset-password.request";
|
||||
export * from "./organization-user-reset-password-enrollment.request";
|
||||
export * from "./organization-user-update.request";
|
||||
export * from "./organization-user-update-groups.request";
|
||||
@@ -0,0 +1,12 @@
|
||||
import { OrganizationUserType } from "../../../enums/organizationUserType";
|
||||
import { PermissionsApi } from "../../../models/api/permissions.api";
|
||||
import { SelectionReadOnlyRequest } from "../../../models/request/selection-read-only.request";
|
||||
|
||||
export class OrganizationUserInviteRequest {
|
||||
emails: string[] = [];
|
||||
type: OrganizationUserType;
|
||||
accessAll: boolean;
|
||||
collections: SelectionReadOnlyRequest[] = [];
|
||||
groups: string[];
|
||||
permissions: PermissionsApi;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SecretVerificationRequest } from "./secret-verification.request";
|
||||
import { SecretVerificationRequest } from "../../../models/request/secret-verification.request";
|
||||
|
||||
export class OrganizationUserResetPasswordEnrollmentRequest extends SecretVerificationRequest {
|
||||
resetPasswordKey: string;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { OrganizationUserType } from "../../../enums/organizationUserType";
|
||||
import { PermissionsApi } from "../../../models/api/permissions.api";
|
||||
import { SelectionReadOnlyRequest } from "../../../models/request/selection-read-only.request";
|
||||
|
||||
export class OrganizationUserUpdateRequest {
|
||||
type: OrganizationUserType;
|
||||
accessAll: boolean;
|
||||
collections: SelectionReadOnlyRequest[] = [];
|
||||
groups: string[] = [];
|
||||
permissions: PermissionsApi;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from "./organization-user.response";
|
||||
export * from "./organization-user-bulk.response";
|
||||
export * from "./organization-user-bulk-public-key.response";
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BaseResponse } from "./base.response";
|
||||
import { BaseResponse } from "../../../models/response/base.response";
|
||||
|
||||
export class OrganizationUserBulkPublicKeyResponse extends BaseResponse {
|
||||
id: string;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BaseResponse } from "./base.response";
|
||||
import { BaseResponse } from "../../../models/response/base.response";
|
||||
|
||||
export class OrganizationUserBulkResponse extends BaseResponse {
|
||||
id: string;
|
||||
@@ -1,10 +1,9 @@
|
||||
import { KdfType } from "../../enums/kdfType";
|
||||
import { OrganizationUserStatusType } from "../../enums/organizationUserStatusType";
|
||||
import { OrganizationUserType } from "../../enums/organizationUserType";
|
||||
import { PermissionsApi } from "../api/permissions.api";
|
||||
|
||||
import { BaseResponse } from "./base.response";
|
||||
import { SelectionReadOnlyResponse } from "./selection-read-only.response";
|
||||
import { KdfType } from "../../../enums/kdfType";
|
||||
import { OrganizationUserStatusType } from "../../../enums/organizationUserStatusType";
|
||||
import { OrganizationUserType } from "../../../enums/organizationUserType";
|
||||
import { PermissionsApi } from "../../../models/api/permissions.api";
|
||||
import { BaseResponse } from "../../../models/response/base.response";
|
||||
import { SelectionReadOnlyResponse } from "../../../models/response/selection-read-only.response";
|
||||
|
||||
export class OrganizationUserResponse extends BaseResponse {
|
||||
id: string;
|
||||
@@ -14,6 +13,8 @@ export class OrganizationUserResponse extends BaseResponse {
|
||||
accessAll: boolean;
|
||||
permissions: PermissionsApi;
|
||||
resetPasswordEnrolled: boolean;
|
||||
collections: SelectionReadOnlyResponse[] = [];
|
||||
groups: string[] = [];
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
@@ -24,6 +25,15 @@ export class OrganizationUserResponse extends BaseResponse {
|
||||
this.permissions = new PermissionsApi(this.getResponseProperty("Permissions"));
|
||||
this.accessAll = this.getResponseProperty("AccessAll");
|
||||
this.resetPasswordEnrolled = this.getResponseProperty("ResetPasswordEnrolled");
|
||||
|
||||
const collections = this.getResponseProperty("Collections");
|
||||
if (collections != null) {
|
||||
this.collections = collections.map((c: any) => new SelectionReadOnlyResponse(c));
|
||||
}
|
||||
const groups = this.getResponseProperty("Groups");
|
||||
if (groups != null) {
|
||||
this.groups = groups;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,14 +53,8 @@ export class OrganizationUserUserDetailsResponse extends OrganizationUserRespons
|
||||
}
|
||||
|
||||
export class OrganizationUserDetailsResponse extends OrganizationUserResponse {
|
||||
collections: SelectionReadOnlyResponse[] = [];
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
const collections = this.getResponseProperty("Collections");
|
||||
if (collections != null) {
|
||||
this.collections = collections.map((c: any) => new SelectionReadOnlyResponse(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { map, Observable } from "rxjs";
|
||||
|
||||
import { Utils } from "../../misc/utils";
|
||||
import { OrganizationData } from "../../models/data/organization.data";
|
||||
import { Organization } from "../../models/domain/organization";
|
||||
import { I18nService } from "../i18n.service";
|
||||
|
||||
export function canAccessVaultTab(org: Organization): boolean {
|
||||
return org.isManager;
|
||||
return org.canViewAssignedCollections || org.canViewAllCollections || org.canManageGroups;
|
||||
}
|
||||
|
||||
export function canAccessSettingsTab(org: Organization): boolean {
|
||||
@@ -34,19 +35,6 @@ export function canAccessBillingTab(org: Organization): boolean {
|
||||
return org.canManageBilling;
|
||||
}
|
||||
|
||||
export function canManageCollections(org: Organization): boolean {
|
||||
return (
|
||||
org.canCreateNewCollections ||
|
||||
org.canEditAnyCollection ||
|
||||
org.canDeleteAnyCollection ||
|
||||
org.canViewAssignedCollections
|
||||
);
|
||||
}
|
||||
|
||||
export function canAccessManageTab(org: Organization): boolean {
|
||||
return canAccessMembersTab(org) || canAccessGroupsTab(org) || canManageCollections(org);
|
||||
}
|
||||
|
||||
export function canAccessOrgAdmin(org: Organization): boolean {
|
||||
return (
|
||||
canAccessMembersTab(org) ||
|
||||
@@ -54,8 +42,7 @@ export function canAccessOrgAdmin(org: Organization): boolean {
|
||||
canAccessReportingTab(org) ||
|
||||
canAccessBillingTab(org) ||
|
||||
canAccessSettingsTab(org) ||
|
||||
canAccessVaultTab(org) ||
|
||||
canAccessManageTab(org)
|
||||
canAccessVaultTab(org)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -69,6 +56,10 @@ export function canAccessAdmin(i18nService: I18nService) {
|
||||
);
|
||||
}
|
||||
|
||||
export function isNotProviderUser(org: Organization): boolean {
|
||||
return !org.isProviderUser;
|
||||
}
|
||||
|
||||
export abstract class OrganizationService {
|
||||
organizations$: Observable<Organization[]>;
|
||||
|
||||
@@ -83,3 +74,7 @@ export abstract class OrganizationService {
|
||||
canManageSponsorships: () => Promise<boolean>;
|
||||
hasOrganizations: () => boolean;
|
||||
}
|
||||
|
||||
export abstract class InternalOrganizationService extends OrganizationService {
|
||||
replace: (organizations: { [id: string]: OrganizationData }) => Promise<void>;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ export abstract class PlatformUtilsService {
|
||||
isViewOpen: () => Promise<boolean>;
|
||||
launchUri: (uri: string, options?: any) => void;
|
||||
getApplicationVersion: () => Promise<string>;
|
||||
getApplicationVersionNumber: () => Promise<string>;
|
||||
supportsWebAuthn: (win: Window) => boolean;
|
||||
supportsDuo: () => boolean;
|
||||
showToast: (
|
||||
@@ -33,7 +34,8 @@ export abstract class PlatformUtilsService {
|
||||
confirmText?: string,
|
||||
cancelText?: string,
|
||||
type?: string,
|
||||
bodyIsHtml?: boolean
|
||||
bodyIsHtml?: boolean,
|
||||
target?: string
|
||||
) => Promise<boolean>;
|
||||
isDev: () => boolean;
|
||||
isSelfHost: () => boolean;
|
||||
|
||||
@@ -349,4 +349,7 @@ export abstract class StateService<T extends Account = Account> {
|
||||
* @deprecated Do not call this directly, use ConfigService
|
||||
*/
|
||||
setServerConfig: (value: ServerConfigData, options?: StorageOptions) => Promise<void>;
|
||||
|
||||
getAvatarColor: (options?: StorageOptions) => Promise<string | null | undefined>;
|
||||
setAvatarColor: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
}
|
||||
|
||||
@@ -7,10 +7,11 @@ export abstract class AbstractStorageService {
|
||||
abstract remove(key: string, options?: StorageOptions): Promise<void>;
|
||||
}
|
||||
|
||||
export abstract class AbstractCachedStorageService extends AbstractStorageService {
|
||||
export abstract class AbstractMemoryStorageService extends AbstractStorageService {
|
||||
// Used to identify the service in the session sync decorator framework
|
||||
static readonly TYPE = "MemoryStorageService";
|
||||
readonly type = AbstractMemoryStorageService.TYPE;
|
||||
|
||||
abstract get<T>(key: string, options?: MemoryStorageOptions<T>): Promise<T>;
|
||||
abstract getBypassCache<T>(key: string, options?: MemoryStorageOptions<T>): Promise<T>;
|
||||
}
|
||||
|
||||
export interface MemoryStorageServiceInterface {
|
||||
get<T>(key: string, options?: MemoryStorageOptions<T>): Promise<T>;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ApiService } from "../abstractions/api.service";
|
||||
|
||||
import { Forwarder } from "./forwarder";
|
||||
import { ForwarderOptions } from "./forwarderOptions";
|
||||
import { ForwarderOptions } from "./forwarder-options";
|
||||
|
||||
export class AnonAddyForwarder implements Forwarder {
|
||||
async generate(apiService: ApiService, options: ForwarderOptions): Promise<string> {
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ApiService } from "../abstractions/api.service";
|
||||
|
||||
import { Forwarder } from "./forwarder";
|
||||
import { ForwarderOptions } from "./forwarderOptions";
|
||||
import { ForwarderOptions } from "./forwarder-options";
|
||||
|
||||
export class DuckDuckGoForwarder implements Forwarder {
|
||||
async generate(apiService: ApiService, options: ForwarderOptions): Promise<string> {
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ApiService } from "../abstractions/api.service";
|
||||
|
||||
import { Forwarder } from "./forwarder";
|
||||
import { ForwarderOptions } from "./forwarderOptions";
|
||||
import { ForwarderOptions } from "./forwarder-options";
|
||||
|
||||
export class FastmailForwarder implements Forwarder {
|
||||
async generate(apiService: ApiService, options: ForwarderOptions): Promise<string> {
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ApiService } from "../abstractions/api.service";
|
||||
|
||||
import { Forwarder } from "./forwarder";
|
||||
import { ForwarderOptions } from "./forwarderOptions";
|
||||
import { ForwarderOptions } from "./forwarder-options";
|
||||
|
||||
export class FirefoxRelayForwarder implements Forwarder {
|
||||
async generate(apiService: ApiService, options: ForwarderOptions): Promise<string> {
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ApiService } from "../abstractions/api.service";
|
||||
|
||||
import { ForwarderOptions } from "./forwarderOptions";
|
||||
import { ForwarderOptions } from "./forwarder-options";
|
||||
|
||||
export interface Forwarder {
|
||||
generate(apiService: ApiService, options: ForwarderOptions): Promise<string>;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ApiService } from "../abstractions/api.service";
|
||||
|
||||
import { Forwarder } from "./forwarder";
|
||||
import { ForwarderOptions } from "./forwarderOptions";
|
||||
import { ForwarderOptions } from "./forwarder-options";
|
||||
|
||||
export class SimpleLoginForwarder implements Forwarder {
|
||||
async generate(apiService: ApiService, options: ForwarderOptions): Promise<string> {
|
||||
@@ -67,6 +67,7 @@ export const regularImportOptions = [
|
||||
{ id: "encryptrcsv", name: "Encryptr (csv)" },
|
||||
{ id: "yoticsv", name: "Yoti (csv)" },
|
||||
{ id: "nordpasscsv", name: "Nordpass (csv)" },
|
||||
{ id: "passkyjson", name: "Passky (json)" },
|
||||
] as const;
|
||||
|
||||
export type ImportType =
|
||||
|
||||
@@ -3,5 +3,5 @@ export enum KdfType {
|
||||
}
|
||||
|
||||
export const DEFAULT_KDF_TYPE = KdfType.PBKDF2_SHA256;
|
||||
export const DEFAULT_KDF_ITERATIONS = 100000;
|
||||
export const DEFAULT_KDF_ITERATIONS = 600000;
|
||||
export const SEND_KDF_ITERATIONS = 100000;
|
||||
|
||||
@@ -137,8 +137,7 @@ export class DashlaneCsvImporter extends BaseImporter implements Importer {
|
||||
cipher.card.number = row.cc_number;
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
cipher.card.code = row.code;
|
||||
cipher.card.expMonth = row.expiration_month;
|
||||
cipher.card.expYear = row.expiration_year.substring(2, 4);
|
||||
this.setCardExpiration(cipher, `${row.expiration_month}/${row.expiration_year}`);
|
||||
|
||||
// If you add more mapped fields please extend this
|
||||
mappedValues = [
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { CipherType } from "../enums/cipherType";
|
||||
import { SecureNoteType } from "../enums/secureNoteType";
|
||||
import { ImportResult } from "../models/domain/import-result";
|
||||
import { CardView } from "../models/view/card.view";
|
||||
import { SecureNoteView } from "../models/view/secure-note.view";
|
||||
|
||||
import { BaseImporter } from "./base-importer";
|
||||
import { Importer } from "./importer";
|
||||
import { CipherType } from "../../enums/cipherType";
|
||||
import { SecureNoteType } from "../../enums/secureNoteType";
|
||||
import { ImportResult } from "../../models/domain/import-result";
|
||||
import { CardView } from "../../models/view/card.view";
|
||||
import { SecureNoteView } from "../../models/view/secure-note.view";
|
||||
import { BaseImporter } from "../base-importer";
|
||||
import { Importer } from "../importer";
|
||||
|
||||
export class EnpassCsvImporter extends BaseImporter implements Importer {
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
@@ -1,17 +1,21 @@
|
||||
import { CipherType } from "../enums/cipherType";
|
||||
import { FieldType } from "../enums/fieldType";
|
||||
import { ImportResult } from "../models/domain/import-result";
|
||||
import { CardView } from "../models/view/card.view";
|
||||
import { CipherView } from "../models/view/cipher.view";
|
||||
import { FolderView } from "../models/view/folder.view";
|
||||
import { CipherType } from "../../enums/cipherType";
|
||||
import { FieldType } from "../../enums/fieldType";
|
||||
import { ImportResult } from "../../models/domain/import-result";
|
||||
import { CardView } from "../../models/view/card.view";
|
||||
import { CipherView } from "../../models/view/cipher.view";
|
||||
import { FolderView } from "../../models/view/folder.view";
|
||||
import { BaseImporter } from "../base-importer";
|
||||
import { Importer } from "../importer";
|
||||
|
||||
import { BaseImporter } from "./base-importer";
|
||||
import { Importer } from "./importer";
|
||||
import { EnpassJsonFile, EnpassFolder, EnpassField } from "./types/enpass-json-type";
|
||||
|
||||
type EnpassFolderTreeItem = EnpassFolder & { children: EnpassFolderTreeItem[] };
|
||||
const androidUrlRegex = new RegExp("androidapp://.*==@", "g");
|
||||
|
||||
export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
const result = new ImportResult();
|
||||
const results = JSON.parse(data);
|
||||
const results: EnpassJsonFile = JSON.parse(data);
|
||||
if (results == null || results.items == null || results.items.length === 0) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
@@ -28,7 +32,7 @@ export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
result.folders.push(f);
|
||||
});
|
||||
|
||||
results.items.forEach((item: any) => {
|
||||
results.items.forEach((item) => {
|
||||
if (item.folders != null && item.folders.length > 0 && foldersIndexMap.has(item.folders[0])) {
|
||||
result.folderRelationships.push([
|
||||
result.ciphers.length,
|
||||
@@ -50,7 +54,7 @@ export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
this.processCard(cipher, item.fields);
|
||||
} else if (
|
||||
item.template_type.indexOf("identity.") < 0 &&
|
||||
item.fields.some((f: any) => f.type === "password" && !this.isNullOrWhitespace(f.value))
|
||||
item.fields.some((f) => f.type === "password" && !this.isNullOrWhitespace(f.value))
|
||||
) {
|
||||
this.processLogin(cipher, item.fields);
|
||||
} else {
|
||||
@@ -68,9 +72,9 @@ export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
private processLogin(cipher: CipherView, fields: any[]) {
|
||||
private processLogin(cipher: CipherView, fields: EnpassField[]) {
|
||||
const urls: string[] = [];
|
||||
fields.forEach((field: any) => {
|
||||
fields.forEach((field) => {
|
||||
if (this.isNullOrWhitespace(field.value) || field.type === "section") {
|
||||
return;
|
||||
}
|
||||
@@ -86,6 +90,13 @@ export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
cipher.login.totp = field.value;
|
||||
} else if (field.type === "url") {
|
||||
urls.push(field.value);
|
||||
} else if (field.type === ".Android#") {
|
||||
let cleanedValue = field.value.startsWith("androidapp://")
|
||||
? field.value
|
||||
: "androidapp://" + field.value;
|
||||
cleanedValue = cleanedValue.replace("android://", "");
|
||||
cleanedValue = cleanedValue.replace(androidUrlRegex, "androidapp://");
|
||||
urls.push(cleanedValue);
|
||||
} else {
|
||||
this.processKvp(
|
||||
cipher,
|
||||
@@ -98,10 +109,10 @@ export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
cipher.login.uris = this.makeUriArray(urls);
|
||||
}
|
||||
|
||||
private processCard(cipher: CipherView, fields: any[]) {
|
||||
private processCard(cipher: CipherView, fields: EnpassField[]) {
|
||||
cipher.card = new CardView();
|
||||
cipher.type = CipherType.Card;
|
||||
fields.forEach((field: any) => {
|
||||
fields.forEach((field) => {
|
||||
if (
|
||||
this.isNullOrWhitespace(field.value) ||
|
||||
field.type === "section" ||
|
||||
@@ -137,8 +148,8 @@ export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
});
|
||||
}
|
||||
|
||||
private processNote(cipher: CipherView, fields: any[]) {
|
||||
fields.forEach((field: any) => {
|
||||
private processNote(cipher: CipherView, fields: EnpassField[]) {
|
||||
fields.forEach((field) => {
|
||||
if (this.isNullOrWhitespace(field.value) || field.type === "section") {
|
||||
return;
|
||||
}
|
||||
@@ -151,17 +162,17 @@ export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
});
|
||||
}
|
||||
|
||||
private buildFolderTree(folders: any[]): any[] {
|
||||
private buildFolderTree(folders: EnpassFolder[]): EnpassFolderTreeItem[] {
|
||||
if (folders == null) {
|
||||
return [];
|
||||
}
|
||||
const folderTree: any[] = [];
|
||||
const map = new Map<string, any>([]);
|
||||
folders.forEach((obj: any) => {
|
||||
const folderTree: EnpassFolderTreeItem[] = [];
|
||||
const map = new Map<string, EnpassFolderTreeItem>([]);
|
||||
folders.forEach((obj: EnpassFolderTreeItem) => {
|
||||
map.set(obj.uuid, obj);
|
||||
obj.children = [];
|
||||
});
|
||||
folders.forEach((obj: any) => {
|
||||
folders.forEach((obj: EnpassFolderTreeItem) => {
|
||||
if (obj.parent_uuid != null && obj.parent_uuid !== "" && map.has(obj.parent_uuid)) {
|
||||
map.get(obj.parent_uuid).children.push(obj);
|
||||
} else {
|
||||
@@ -171,11 +182,15 @@ export class EnpassJsonImporter extends BaseImporter implements Importer {
|
||||
return folderTree;
|
||||
}
|
||||
|
||||
private flattenFolderTree(titlePrefix: string, tree: any[], map: Map<string, string>) {
|
||||
private flattenFolderTree(
|
||||
titlePrefix: string,
|
||||
tree: EnpassFolderTreeItem[],
|
||||
map: Map<string, string>
|
||||
) {
|
||||
if (tree == null) {
|
||||
return;
|
||||
}
|
||||
tree.forEach((f: any) => {
|
||||
tree.forEach((f) => {
|
||||
if (f.title != null && f.title.trim() !== "") {
|
||||
let title = f.title.trim();
|
||||
if (titlePrefix != null && titlePrefix.trim() !== "") {
|
||||
@@ -0,0 +1,79 @@
|
||||
type Login = "login.default";
|
||||
|
||||
type CreditCard = "creditcard.default";
|
||||
|
||||
type Identity = "identity.default";
|
||||
|
||||
type Note = "note.default";
|
||||
|
||||
type Password = "password.default";
|
||||
|
||||
type Finance =
|
||||
| "finance.stock"
|
||||
| "finance.bankaccount"
|
||||
| "finance.loan"
|
||||
| "finance.mutualfund"
|
||||
| "finance.insurance"
|
||||
| "finance.other";
|
||||
|
||||
type License = "license.driving" | "license.hunting" | "license.software" | "license.other";
|
||||
|
||||
type Travel =
|
||||
| "travel.passport"
|
||||
| "travel.flightdetails"
|
||||
| "travel.hotelreservation"
|
||||
| "travel.visa"
|
||||
| "travel.freqflyer"
|
||||
| "travel.other";
|
||||
|
||||
type Computer =
|
||||
| "computer.database"
|
||||
| "computer.emailaccount"
|
||||
| "computer.ftp"
|
||||
| "computer.messaging"
|
||||
| "computer.internetprovider"
|
||||
| "computer.server"
|
||||
| "computer.wifi"
|
||||
| "computer.hosting"
|
||||
| "computer.other";
|
||||
|
||||
type Misc =
|
||||
| "misc.Aadhar"
|
||||
| "misc.address"
|
||||
| "misc.library"
|
||||
| "misc.rewardprogram"
|
||||
| "misc.lens"
|
||||
| "misc.service"
|
||||
| "misc.vehicleinfo"
|
||||
| "misc.itic"
|
||||
| "misc.itz"
|
||||
| "misc.propertyinfo"
|
||||
| "misc.clothsize"
|
||||
| "misc.contact"
|
||||
| "misc.membership"
|
||||
| "misc.cellphone"
|
||||
| "misc.emergencyno"
|
||||
| "misc.pan"
|
||||
| "misc.identity"
|
||||
| "misc.regcode"
|
||||
| "misc.prescription"
|
||||
| "misc.serial"
|
||||
| "misc.socialsecurityno"
|
||||
| "misc.isic"
|
||||
| "misc.calling"
|
||||
| "misc.voicemail"
|
||||
| "misc.voter"
|
||||
| "misc.combilock"
|
||||
| "misc.other";
|
||||
|
||||
export type EnpassItemTemplate =
|
||||
| Login
|
||||
| CreditCard
|
||||
| Identity
|
||||
| Note
|
||||
| Password
|
||||
| Finance
|
||||
| License
|
||||
| Travel
|
||||
| Computer
|
||||
| Misc;
|
||||
85
libs/common/src/importers/enpass/types/enpass-json-type.ts
Normal file
85
libs/common/src/importers/enpass/types/enpass-json-type.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { EnpassItemTemplate } from "./enpass-item-templates";
|
||||
|
||||
export type EnpassJsonFile = {
|
||||
folders: EnpassFolder[];
|
||||
items: EnpassItem[];
|
||||
};
|
||||
|
||||
export type EnpassFolder = {
|
||||
icon: string;
|
||||
parent_uuid: string;
|
||||
title: string;
|
||||
updated_at: number;
|
||||
uuid: string;
|
||||
};
|
||||
|
||||
export type EnpassItem = {
|
||||
archived: number;
|
||||
auto_submit: number;
|
||||
category: string;
|
||||
createdAt: number;
|
||||
favorite: number;
|
||||
fields?: EnpassField[];
|
||||
icon: Icon;
|
||||
note: string;
|
||||
subtitle: string;
|
||||
template_type: EnpassItemTemplate;
|
||||
title: string;
|
||||
trashed: number;
|
||||
updated_at: number;
|
||||
uuid: string;
|
||||
folders?: string[];
|
||||
};
|
||||
|
||||
export type EnpassFieldType =
|
||||
| "text"
|
||||
| "password"
|
||||
| "pin"
|
||||
| "numeric"
|
||||
| "date"
|
||||
| "email"
|
||||
| "url"
|
||||
| "phone"
|
||||
| "username"
|
||||
| "totp"
|
||||
| "multiline"
|
||||
| "ccName"
|
||||
| "ccNumber"
|
||||
| "ccCvc"
|
||||
| "ccPin"
|
||||
| "ccExpiry"
|
||||
| "ccBankname"
|
||||
| "ccTxnpassword"
|
||||
| "ccType"
|
||||
| "ccValidfrom"
|
||||
| "section"
|
||||
| ".Android#";
|
||||
|
||||
export type EnpassField = {
|
||||
deleted: number;
|
||||
history?: History[];
|
||||
label: string;
|
||||
order: number;
|
||||
sensitive: number;
|
||||
type: EnpassFieldType;
|
||||
uid: number;
|
||||
updated_at: number;
|
||||
value: string;
|
||||
value_updated_at: number;
|
||||
};
|
||||
|
||||
export type History = {
|
||||
updated_at: number;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type Icon = {
|
||||
fav: string;
|
||||
image: Image;
|
||||
type: number;
|
||||
uuid: string;
|
||||
};
|
||||
|
||||
export type Image = {
|
||||
file: string;
|
||||
};
|
||||
@@ -1,59 +0,0 @@
|
||||
import { CipherType } from "../enums/cipherType";
|
||||
import { ImportResult } from "../models/domain/import-result";
|
||||
import { CardView } from "../models/view/card.view";
|
||||
|
||||
import { BaseImporter } from "./base-importer";
|
||||
import { Importer } from "./importer";
|
||||
|
||||
export class FSecureFskImporter extends BaseImporter implements Importer {
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
const result = new ImportResult();
|
||||
const results = JSON.parse(data);
|
||||
if (results == null || results.data == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
for (const key in results.data) {
|
||||
// eslint-disable-next-line
|
||||
if (!results.data.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value = results.data[key];
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(value.service);
|
||||
cipher.notes = this.getValueOrDefault(value.notes);
|
||||
|
||||
if (value.style === "website" || value.style === "globe") {
|
||||
cipher.login.username = this.getValueOrDefault(value.username);
|
||||
cipher.login.password = this.getValueOrDefault(value.password);
|
||||
cipher.login.uris = this.makeUriArray(value.url);
|
||||
} else if (value.style === "creditcard") {
|
||||
cipher.type = CipherType.Card;
|
||||
cipher.card = new CardView();
|
||||
cipher.card.cardholderName = this.getValueOrDefault(value.username);
|
||||
cipher.card.number = this.getValueOrDefault(value.creditNumber);
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
cipher.card.code = this.getValueOrDefault(value.creditCvv);
|
||||
if (!this.isNullOrWhitespace(value.creditExpiry)) {
|
||||
if (!this.setCardExpiration(cipher, value.creditExpiry)) {
|
||||
this.processKvp(cipher, "Expiration", value.creditExpiry);
|
||||
}
|
||||
}
|
||||
if (!this.isNullOrWhitespace(value.password)) {
|
||||
this.processKvp(cipher, "PIN", value.password);
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.convertToNoteIfNeeded(cipher);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { CipherType } from "../../enums/cipherType";
|
||||
|
||||
import { FSecureFskImporter as Importer } from "./fsecure-fsk-importer";
|
||||
import { CreditCardTestEntry, LoginTestEntry } from "./fsk-test-data";
|
||||
|
||||
describe("FSecure FSK Importer", () => {
|
||||
it("should import data of type login", async () => {
|
||||
const importer = new Importer();
|
||||
const LoginTestEntryStringified = JSON.stringify(LoginTestEntry);
|
||||
const result = await importer.parse(LoginTestEntryStringified);
|
||||
expect(result != null).toBe(true);
|
||||
|
||||
const cipher = result.ciphers.shift();
|
||||
|
||||
expect(cipher.name).toEqual("example.com");
|
||||
expect(cipher.favorite).toBe(true);
|
||||
expect(cipher.notes).toEqual("some note for example.com");
|
||||
|
||||
expect(cipher.type).toBe(CipherType.Login);
|
||||
expect(cipher.login.username).toEqual("jdoe");
|
||||
expect(cipher.login.password).toEqual("somePassword");
|
||||
|
||||
expect(cipher.login.uris.length).toEqual(1);
|
||||
const uriView = cipher.login.uris.shift();
|
||||
expect(uriView.uri).toEqual("https://www.example.com");
|
||||
});
|
||||
|
||||
it("should import data of type creditCard", async () => {
|
||||
const importer = new Importer();
|
||||
const CreditCardTestEntryStringified = JSON.stringify(CreditCardTestEntry);
|
||||
const result = await importer.parse(CreditCardTestEntryStringified);
|
||||
expect(result != null).toBe(true);
|
||||
|
||||
const cipher = result.ciphers.shift();
|
||||
|
||||
expect(cipher.name).toEqual("My credit card");
|
||||
expect(cipher.favorite).toBe(false);
|
||||
expect(cipher.notes).toEqual("some notes to my card");
|
||||
|
||||
expect(cipher.type).toBe(CipherType.Card);
|
||||
expect(cipher.card.cardholderName).toEqual("John Doe");
|
||||
expect(cipher.card.number).toEqual("4242424242424242");
|
||||
expect(cipher.card.code).toEqual("123");
|
||||
|
||||
expect(cipher.fields.length).toBe(2);
|
||||
expect(cipher.fields[0].name).toEqual("Expiration");
|
||||
expect(cipher.fields[0].value).toEqual("22.10.2026");
|
||||
|
||||
expect(cipher.fields[1].name).toEqual("PIN");
|
||||
expect(cipher.fields[1].value).toEqual("1234");
|
||||
});
|
||||
});
|
||||
79
libs/common/src/importers/fsecure/fsecure-fsk-importer.ts
Normal file
79
libs/common/src/importers/fsecure/fsecure-fsk-importer.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { CipherType } from "../../enums/cipherType";
|
||||
import { ImportResult } from "../../models/domain/import-result";
|
||||
import { CardView } from "../../models/view/card.view";
|
||||
import { CipherView } from "../../models/view/cipher.view";
|
||||
import { BaseImporter } from "../base-importer";
|
||||
import { Importer } from "../importer";
|
||||
|
||||
import { FskEntry, FskEntryTypesEnum, FskFile } from "./fsecure-fsk-types";
|
||||
|
||||
export class FSecureFskImporter extends BaseImporter implements Importer {
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
const result = new ImportResult();
|
||||
const results: FskFile = JSON.parse(data);
|
||||
if (results == null || results.data == null) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
for (const key in results.data) {
|
||||
// eslint-disable-next-line
|
||||
if (!results.data.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value = results.data[key];
|
||||
const cipher = this.parseEntry(value);
|
||||
result.ciphers.push(cipher);
|
||||
}
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
private parseEntry(entry: FskEntry): CipherView {
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = this.getValueOrDefault(entry.service);
|
||||
cipher.notes = this.getValueOrDefault(entry.notes);
|
||||
cipher.favorite = entry.favorite > 0;
|
||||
|
||||
switch (entry.type) {
|
||||
case FskEntryTypesEnum.Login:
|
||||
this.handleLoginEntry(entry, cipher);
|
||||
break;
|
||||
case FskEntryTypesEnum.CreditCard:
|
||||
this.handleCreditCardEntry(entry, cipher);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
this.convertToNoteIfNeeded(cipher);
|
||||
this.cleanupCipher(cipher);
|
||||
return cipher;
|
||||
}
|
||||
|
||||
private handleLoginEntry(entry: FskEntry, cipher: CipherView) {
|
||||
cipher.login.username = this.getValueOrDefault(entry.username);
|
||||
cipher.login.password = this.getValueOrDefault(entry.password);
|
||||
cipher.login.uris = this.makeUriArray(entry.url);
|
||||
}
|
||||
|
||||
private handleCreditCardEntry(entry: FskEntry, cipher: CipherView) {
|
||||
cipher.type = CipherType.Card;
|
||||
cipher.card = new CardView();
|
||||
cipher.card.cardholderName = this.getValueOrDefault(entry.username);
|
||||
cipher.card.number = this.getValueOrDefault(entry.creditNumber);
|
||||
cipher.card.brand = this.getCardBrand(cipher.card.number);
|
||||
cipher.card.code = this.getValueOrDefault(entry.creditCvv);
|
||||
if (!this.isNullOrWhitespace(entry.creditExpiry)) {
|
||||
if (!this.setCardExpiration(cipher, entry.creditExpiry)) {
|
||||
this.processKvp(cipher, "Expiration", entry.creditExpiry);
|
||||
}
|
||||
}
|
||||
if (!this.isNullOrWhitespace(entry.password)) {
|
||||
this.processKvp(cipher, "PIN", entry.password);
|
||||
}
|
||||
}
|
||||
}
|
||||
37
libs/common/src/importers/fsecure/fsecure-fsk-types.ts
Normal file
37
libs/common/src/importers/fsecure/fsecure-fsk-types.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
export interface FskFile {
|
||||
data: Data;
|
||||
}
|
||||
|
||||
export interface Data {
|
||||
[key: string]: FskEntry;
|
||||
}
|
||||
|
||||
export enum FskEntryTypesEnum {
|
||||
Login = 1,
|
||||
CreditCard = 2,
|
||||
}
|
||||
|
||||
export interface FskEntry {
|
||||
color: string;
|
||||
creditCvv: string;
|
||||
creditExpiry: string;
|
||||
creditNumber: string;
|
||||
favorite: number; // UNIX timestamp
|
||||
notes: string;
|
||||
password: string;
|
||||
passwordList: PasswordList[];
|
||||
passwordModifiedDate: number; // UNIX timestamp
|
||||
rev: string | number;
|
||||
service: string;
|
||||
style: string;
|
||||
type: FskEntryTypesEnum;
|
||||
url: string;
|
||||
username: string;
|
||||
createdDate: number; // UNIX timestamp
|
||||
modifiedDate: number; // UNIX timestamp
|
||||
}
|
||||
|
||||
export interface PasswordList {
|
||||
changedate: string;
|
||||
password: string;
|
||||
}
|
||||
49
libs/common/src/importers/fsecure/fsk-test-data.ts
Normal file
49
libs/common/src/importers/fsecure/fsk-test-data.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { FskFile } from "./fsecure-fsk-types";
|
||||
|
||||
export const LoginTestEntry: FskFile = {
|
||||
data: {
|
||||
"1c3a2e31dcaa8459edd70a9d895ce298": {
|
||||
color: "#00A34D",
|
||||
createdDate: 0,
|
||||
creditCvv: "",
|
||||
creditExpiry: "",
|
||||
creditNumber: "",
|
||||
favorite: 1666440874,
|
||||
modifiedDate: 0,
|
||||
notes: "some note for example.com",
|
||||
password: "somePassword",
|
||||
passwordList: [],
|
||||
passwordModifiedDate: 0,
|
||||
rev: 1,
|
||||
service: "example.com",
|
||||
style: "website",
|
||||
type: 1,
|
||||
url: "https://www.example.com",
|
||||
username: "jdoe",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const CreditCardTestEntry: FskFile = {
|
||||
data: {
|
||||
"156498a46a3254f16035cbbbd09c2b8f": {
|
||||
color: "#00baff",
|
||||
createdDate: 1666438977,
|
||||
creditCvv: "123",
|
||||
creditExpiry: "22.10.2026",
|
||||
creditNumber: "4242424242424242",
|
||||
favorite: 0,
|
||||
modifiedDate: 1666438977,
|
||||
notes: "some notes to my card",
|
||||
password: "1234",
|
||||
passwordList: [],
|
||||
passwordModifiedDate: 1666438977,
|
||||
rev: 1,
|
||||
service: "My credit card",
|
||||
style: "creditcard",
|
||||
type: 2,
|
||||
url: "mybank",
|
||||
username: "John Doe",
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -15,7 +15,23 @@ export class KeePass2XmlImporter extends BaseImporter implements Importer {
|
||||
return Promise.resolve(this.result);
|
||||
}
|
||||
|
||||
const rootGroup = doc.querySelector("KeePassFile > Root > Group");
|
||||
//Note: The doc.querySelector("KeePassFile > Root > Group") no longers works on node and we have to breakdown the query by nodes
|
||||
const KeePassFileNode = doc.querySelector("KeePassFile");
|
||||
|
||||
if (KeePassFileNode == null) {
|
||||
this.result.errorMessage = "Missing `KeePassFile` node.";
|
||||
this.result.success = false;
|
||||
return Promise.resolve(this.result);
|
||||
}
|
||||
|
||||
const RootNode = KeePassFileNode.querySelector("Root");
|
||||
if (RootNode == null) {
|
||||
this.result.errorMessage = "Missing `KeePassFile > Root` node.";
|
||||
this.result.success = false;
|
||||
return Promise.resolve(this.result);
|
||||
}
|
||||
|
||||
const rootGroup = RootNode.querySelector("Group");
|
||||
if (rootGroup == null) {
|
||||
this.result.errorMessage = "Missing `KeePassFile > Root > Group` node.";
|
||||
this.result.success = false;
|
||||
|
||||
@@ -18,7 +18,12 @@ export class KeeperCsvImporter extends BaseImporter implements Importer {
|
||||
|
||||
this.processFolder(result, value[0]);
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.notes = this.getValueOrDefault(value[5]) + "\n";
|
||||
|
||||
const notes = this.getValueOrDefault(value[5]);
|
||||
if (notes) {
|
||||
cipher.notes = `${notes}\n`;
|
||||
}
|
||||
|
||||
cipher.name = this.getValueOrDefault(value[1], "--");
|
||||
cipher.login.username = this.getValueOrDefault(value[2]);
|
||||
cipher.login.password = this.getValueOrDefault(value[3]);
|
||||
@@ -27,7 +32,11 @@ export class KeeperCsvImporter extends BaseImporter implements Importer {
|
||||
if (value.length > 7) {
|
||||
// we have some custom fields.
|
||||
for (let i = 7; i < value.length; i = i + 2) {
|
||||
this.processKvp(cipher, value[i], value[i + 1]);
|
||||
if (value[i] == "TFC:Keeper") {
|
||||
cipher.login.totp = value[i + 1];
|
||||
} else {
|
||||
this.processKvp(cipher, value[i], value[i + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
43
libs/common/src/importers/passky/passky-json-importer.ts
Normal file
43
libs/common/src/importers/passky/passky-json-importer.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { ImportResult } from "../../models/domain/import-result";
|
||||
import { BaseImporter } from "../base-importer";
|
||||
import { Importer } from "../importer";
|
||||
|
||||
import { PasskyJsonExport } from "./passky-json-types";
|
||||
|
||||
export class PasskyJsonImporter extends BaseImporter implements Importer {
|
||||
parse(data: string): Promise<ImportResult> {
|
||||
const result = new ImportResult();
|
||||
const passkyExport: PasskyJsonExport = JSON.parse(data);
|
||||
if (
|
||||
passkyExport == null ||
|
||||
passkyExport.passwords == null ||
|
||||
passkyExport.passwords.length === 0
|
||||
) {
|
||||
result.success = false;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
if (passkyExport.encrypted == true) {
|
||||
result.success = false;
|
||||
result.errorMessage = "Unable to import an encrypted passky backup.";
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
passkyExport.passwords.forEach((record) => {
|
||||
const cipher = this.initLoginCipher();
|
||||
cipher.name = record.website;
|
||||
cipher.login.username = record.username;
|
||||
cipher.login.password = record.password;
|
||||
|
||||
cipher.login.uris = this.makeUriArray(record.website);
|
||||
cipher.notes = record.message;
|
||||
|
||||
this.convertToNoteIfNeeded(cipher);
|
||||
this.cleanupCipher(cipher);
|
||||
result.ciphers.push(cipher);
|
||||
});
|
||||
|
||||
result.success = true;
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
}
|
||||
11
libs/common/src/importers/passky/passky-json-types.ts
Normal file
11
libs/common/src/importers/passky/passky-json-types.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export interface PasskyJsonExport {
|
||||
encrypted: boolean;
|
||||
passwords: LoginEntry[];
|
||||
}
|
||||
|
||||
export interface LoginEntry {
|
||||
website: string;
|
||||
username: string;
|
||||
password: string;
|
||||
message: string;
|
||||
}
|
||||
@@ -50,6 +50,9 @@ export abstract class LogInStrategy {
|
||||
| PasswordlessLogInCredentials
|
||||
): Promise<AuthResult>;
|
||||
|
||||
// The user key comes from different sources depending on the login strategy
|
||||
protected abstract setUserKey(response: IdentityTokenResponse): Promise<void>;
|
||||
|
||||
async logInTwoFactor(
|
||||
twoFactor: TokenTwoFactorRequest,
|
||||
captchaResponse: string = null
|
||||
@@ -74,11 +77,6 @@ export abstract class LogInStrategy {
|
||||
throw new Error("Invalid response object.");
|
||||
}
|
||||
|
||||
protected onSuccessfulLogin(response: IdentityTokenResponse): Promise<void> {
|
||||
// Implemented in subclass if required
|
||||
return null;
|
||||
}
|
||||
|
||||
protected async buildDeviceRequest() {
|
||||
const appId = await this.appIdService.getAppId();
|
||||
return new DeviceRequest(appId, this.platformUtilsService);
|
||||
@@ -134,6 +132,9 @@ export abstract class LogInStrategy {
|
||||
await this.tokenService.setTwoFactorToken(response);
|
||||
}
|
||||
|
||||
await this.setUserKey(response);
|
||||
|
||||
// Must come after the user Key is set, otherwise createKeyPairForOldAccount will fail
|
||||
const newSsoUser = response.key == null;
|
||||
if (!newSsoUser) {
|
||||
await this.cryptoService.setEncKey(response.key);
|
||||
@@ -142,8 +143,6 @@ export abstract class LogInStrategy {
|
||||
);
|
||||
}
|
||||
|
||||
await this.onSuccessfulLogin(response);
|
||||
|
||||
this.messagingService.send("loggedIn");
|
||||
|
||||
return result;
|
||||
|
||||
@@ -56,7 +56,7 @@ export class PasswordLogInStrategy extends LogInStrategy {
|
||||
);
|
||||
}
|
||||
|
||||
async onSuccessfulLogin() {
|
||||
async setUserKey() {
|
||||
await this.cryptoService.setKey(this.key);
|
||||
await this.cryptoService.setKeyHash(this.localHashedPassword);
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ export class PasswordlessLogInStrategy extends LogInStrategy {
|
||||
);
|
||||
}
|
||||
|
||||
async onSuccessfulLogin() {
|
||||
async setUserKey() {
|
||||
await this.cryptoService.setKey(this.passwordlessCredentials.decKey);
|
||||
await this.cryptoService.setKeyHash(this.passwordlessCredentials.localPasswordHash);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ export class SsoLogInStrategy extends LogInStrategy {
|
||||
);
|
||||
}
|
||||
|
||||
async onSuccessfulLogin(tokenResponse: IdentityTokenResponse) {
|
||||
async setUserKey(tokenResponse: IdentityTokenResponse) {
|
||||
const newSsoUser = tokenResponse.key == null;
|
||||
|
||||
if (tokenResponse.keyConnectorUrl != null) {
|
||||
|
||||
@@ -44,7 +44,7 @@ export class UserApiLogInStrategy extends LogInStrategy {
|
||||
);
|
||||
}
|
||||
|
||||
async onSuccessfulLogin(tokenResponse: IdentityTokenResponse) {
|
||||
async setUserKey(tokenResponse: IdentityTokenResponse) {
|
||||
if (tokenResponse.apiUseKeyConnector) {
|
||||
const keyConnectorUrl = this.environmentService.getKeyConnectorUrl();
|
||||
await this.keyConnectorService.getAndSetKey(keyConnectorUrl);
|
||||
|
||||
72
libs/common/src/misc/serviceUtils.spec.ts
Normal file
72
libs/common/src/misc/serviceUtils.spec.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { ITreeNodeObject, TreeNode } from "../models/domain/tree-node";
|
||||
|
||||
import { ServiceUtils } from "./serviceUtils";
|
||||
|
||||
type FakeObject = { id: string; name: string };
|
||||
|
||||
describe("serviceUtils", () => {
|
||||
let nodeTree: TreeNode<FakeObject>[];
|
||||
beforeEach(() => {
|
||||
nodeTree = [
|
||||
createTreeNode({ id: "1", name: "1" }, [
|
||||
createTreeNode({ id: "1.1", name: "1.1" }, [
|
||||
createTreeNode({ id: "1.1.1", name: "1.1.1" }),
|
||||
]),
|
||||
createTreeNode({ id: "1.2", name: "1.2" }),
|
||||
])(null),
|
||||
createTreeNode({ id: "2", name: "2" }, [createTreeNode({ id: "2.1", name: "2.1" })])(null),
|
||||
createTreeNode({ id: "3", name: "3" }, [])(null),
|
||||
];
|
||||
});
|
||||
|
||||
describe("nestedTraverse", () => {
|
||||
it("should traverse a tree and add a node at the correct position given a valid path", () => {
|
||||
const nodeToBeAdded: FakeObject = { id: "1.2.1", name: "1.2.1" };
|
||||
const path = ["1", "1.2", "1.2.1"];
|
||||
|
||||
ServiceUtils.nestedTraverse(nodeTree, 0, path, nodeToBeAdded, null, "/");
|
||||
expect(nodeTree[0].children[1].children[0].node).toEqual(nodeToBeAdded);
|
||||
});
|
||||
|
||||
it("should combine the path for missing nodes and use as the added node name given an invalid path", () => {
|
||||
const nodeToBeAdded: FakeObject = { id: "blank", name: "blank" };
|
||||
const path = ["3", "3.1", "3.1.1"];
|
||||
|
||||
ServiceUtils.nestedTraverse(nodeTree, 0, path, nodeToBeAdded, null, "/");
|
||||
expect(nodeTree[2].children[0].node.name).toEqual("3.1/3.1.1");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getTreeNodeObject", () => {
|
||||
it("should return a matching node given a single tree branch and a valid id", () => {
|
||||
const id = "1.1.1";
|
||||
const given = ServiceUtils.getTreeNodeObject(nodeTree[0], id);
|
||||
expect(given.node.id).toEqual(id);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getTreeNodeObjectFromList", () => {
|
||||
it("should return a matching node given a list of branches and a valid id", () => {
|
||||
const id = "1.1.1";
|
||||
const given = ServiceUtils.getTreeNodeObjectFromList(nodeTree, id);
|
||||
expect(given.node.id).toEqual(id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
type TreeNodeFactory<T extends ITreeNodeObject> = (
|
||||
obj: T,
|
||||
children?: TreeNodeFactoryWithoutParent<T>[]
|
||||
) => TreeNodeFactoryWithoutParent<T>;
|
||||
|
||||
type TreeNodeFactoryWithoutParent<T extends ITreeNodeObject> = (
|
||||
parent?: TreeNode<T>
|
||||
) => TreeNode<T>;
|
||||
|
||||
const createTreeNode: TreeNodeFactory<FakeObject> =
|
||||
(obj, children = []) =>
|
||||
(parent) => {
|
||||
const node = new TreeNode<FakeObject>(obj, parent, obj.name, obj.id);
|
||||
node.children = children.map((childFunc) => childFunc(node));
|
||||
return node;
|
||||
};
|
||||
@@ -1,47 +1,62 @@
|
||||
import { ITreeNodeObject, TreeNode } from "../models/domain/tree-node";
|
||||
|
||||
export class ServiceUtils {
|
||||
/**
|
||||
* Recursively adds a node to nodeTree
|
||||
* @param {TreeNode<ITreeNodeObject>[]} nodeTree - An array of TreeNodes that the node will be added to
|
||||
* @param {number} partIndex - Index of the `parts` array that is being processed
|
||||
* @param {string[]} parts - Array of strings that represent the path to the `obj` node
|
||||
* @param {ITreeNodeObject} obj - The node to be added to the tree
|
||||
* @param {ITreeNodeObject} parent - The parent node of the `obj` node
|
||||
* @param {string} delimiter - The delimiter used to split the path string, will be used to combine the path for missing nodes
|
||||
*/
|
||||
static nestedTraverse(
|
||||
nodeTree: TreeNode<ITreeNodeObject>[],
|
||||
partIndex: number,
|
||||
parts: string[],
|
||||
obj: ITreeNodeObject,
|
||||
parent: ITreeNodeObject,
|
||||
parent: TreeNode<ITreeNodeObject> | undefined,
|
||||
delimiter: string
|
||||
) {
|
||||
if (parts.length <= partIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
const end = partIndex === parts.length - 1;
|
||||
const partName = parts[partIndex];
|
||||
const end: boolean = partIndex === parts.length - 1;
|
||||
const partName: string = parts[partIndex];
|
||||
|
||||
for (let i = 0; i < nodeTree.length; i++) {
|
||||
if (nodeTree[i].node.name !== parts[partIndex]) {
|
||||
if (nodeTree[i].node.name !== partName) {
|
||||
continue;
|
||||
}
|
||||
if (end && nodeTree[i].node.id !== obj.id) {
|
||||
// Another node with the same name.
|
||||
nodeTree.push(new TreeNode(obj, partName, parent));
|
||||
// Another node exists with the same name as the node being added
|
||||
nodeTree.push(new TreeNode(obj, parent, partName));
|
||||
return;
|
||||
}
|
||||
// Move down the tree to the next level
|
||||
ServiceUtils.nestedTraverse(
|
||||
nodeTree[i].children,
|
||||
partIndex + 1,
|
||||
parts,
|
||||
obj,
|
||||
nodeTree[i].node,
|
||||
nodeTree[i],
|
||||
delimiter
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// If there's no node here with the same name...
|
||||
if (nodeTree.filter((n) => n.node.name === partName).length === 0) {
|
||||
// And we're at the end of the path given, add the node
|
||||
if (end) {
|
||||
nodeTree.push(new TreeNode(obj, partName, parent));
|
||||
nodeTree.push(new TreeNode(obj, parent, partName));
|
||||
return;
|
||||
}
|
||||
const newPartName = parts[partIndex] + delimiter + parts[partIndex + 1];
|
||||
// And we're not at the end of the path, combine the current name with the next name
|
||||
// 1, *1.2, 1.2.1 becomes
|
||||
// 1, *1.2/1.2.1
|
||||
const newPartName = partName + delimiter + parts[partIndex + 1];
|
||||
ServiceUtils.nestedTraverse(
|
||||
nodeTree,
|
||||
0,
|
||||
@@ -53,7 +68,37 @@ export class ServiceUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches a tree for a node with a matching `id`
|
||||
* @param {TreeNode<ITreeNodeObject>} nodeTree - A single TreeNode branch that will be searched
|
||||
* @param {string} id - The id of the node to be found
|
||||
* @returns {TreeNode<ITreeNodeObject>} The node with a matching `id`
|
||||
*/
|
||||
static getTreeNodeObject(
|
||||
nodeTree: TreeNode<ITreeNodeObject>,
|
||||
id: string
|
||||
): TreeNode<ITreeNodeObject> {
|
||||
if (nodeTree.node.id === id) {
|
||||
return nodeTree;
|
||||
}
|
||||
for (let i = 0; i < nodeTree.children.length; i++) {
|
||||
if (nodeTree.children[i].children != null) {
|
||||
const node = ServiceUtils.getTreeNodeObject(nodeTree.children[i], id);
|
||||
if (node !== null) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches an array of tree nodes for a node with a matching `id`
|
||||
* @param {TreeNode<ITreeNodeObject>} nodeTree - An array of TreeNode branches that will be searched
|
||||
* @param {string} id - The id of the node to be found
|
||||
* @returns {TreeNode<ITreeNodeObject>} The node with a matching `id`
|
||||
*/
|
||||
static getTreeNodeObjectFromList(
|
||||
nodeTree: TreeNode<ITreeNodeObject>[],
|
||||
id: string
|
||||
): TreeNode<ITreeNodeObject> {
|
||||
@@ -61,7 +106,7 @@ export class ServiceUtils {
|
||||
if (nodeTree[i].node.id === id) {
|
||||
return nodeTree[i];
|
||||
} else if (nodeTree[i].children != null) {
|
||||
const node = ServiceUtils.getTreeNodeObject(nodeTree[i].children, id);
|
||||
const node = ServiceUtils.getTreeNodeObjectFromList(nodeTree[i].children, id);
|
||||
if (node !== null) {
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* eslint-disable no-useless-escape */
|
||||
import { getHostname, parse } from "tldts";
|
||||
import { Merge } from "type-fest";
|
||||
|
||||
import { CryptoService } from "../abstractions/crypto.service";
|
||||
import { EncryptService } from "../abstractions/encrypt.service";
|
||||
@@ -55,6 +56,10 @@ export class Utils {
|
||||
}
|
||||
|
||||
static fromB64ToArray(str: string): Uint8Array {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Utils.isNode) {
|
||||
return new Uint8Array(Buffer.from(str, "base64"));
|
||||
} else {
|
||||
@@ -108,6 +113,9 @@ export class Utils {
|
||||
}
|
||||
|
||||
static fromBufferToB64(buffer: ArrayBuffer): string {
|
||||
if (buffer == null) {
|
||||
return null;
|
||||
}
|
||||
if (Utils.isNode) {
|
||||
return Buffer.from(buffer).toString("base64");
|
||||
} else {
|
||||
@@ -423,6 +431,73 @@ export class Utils {
|
||||
return this.global.bitwardenContainerService;
|
||||
}
|
||||
|
||||
static validateHexColor(color: string) {
|
||||
return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts map to a Record<string, V> with the same data. Inverse of recordToMap
|
||||
* Useful in toJSON methods, since Maps are not serializable
|
||||
* @param map
|
||||
* @returns
|
||||
*/
|
||||
static mapToRecord<K extends string | number, V>(map: Map<K, V>): Record<string, V> {
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
if (!(map instanceof Map)) {
|
||||
return map;
|
||||
}
|
||||
return Object.fromEntries(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts record to a Map<string, V> with the same data. Inverse of mapToRecord
|
||||
* Useful in fromJSON methods, since Maps are not serializable
|
||||
*
|
||||
* Warning: If the record has string keys that are numbers, they will be converted to numbers in the map
|
||||
* @param record
|
||||
* @returns
|
||||
*/
|
||||
static recordToMap<K extends string | number, V>(record: Record<K, V>): Map<K, V> {
|
||||
if (record == null) {
|
||||
return null;
|
||||
} else if (record instanceof Map) {
|
||||
return record;
|
||||
}
|
||||
|
||||
const entries = Object.entries(record);
|
||||
if (entries.length === 0) {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
if (isNaN(Number(entries[0][0]))) {
|
||||
return new Map(entries) as Map<K, V>;
|
||||
} else {
|
||||
return new Map(entries.map((e) => [Number(e[0]), e[1]])) as Map<K, V>;
|
||||
}
|
||||
}
|
||||
|
||||
/** Applies Object.assign, but converts the type nicely using Type-Fest Merge<Destination, Source> */
|
||||
static merge<Destination, Source>(
|
||||
destination: Destination,
|
||||
source: Source
|
||||
): Merge<Destination, Source> {
|
||||
return Object.assign(destination, source) as unknown as Merge<Destination, Source>;
|
||||
}
|
||||
|
||||
/**
|
||||
* encodeURIComponent escapes all characters except the following:
|
||||
* alphabetic, decimal digits, - _ . ! ~ * ' ( )
|
||||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#encoding_for_rfc3986
|
||||
*/
|
||||
static encodeRFC3986URIComponent(str: string): string {
|
||||
return encodeURIComponent(str).replace(
|
||||
/[!'()*]/g,
|
||||
(c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
|
||||
);
|
||||
}
|
||||
|
||||
private static isMobile(win: Window) {
|
||||
let mobile = false;
|
||||
((a) => {
|
||||
@@ -440,6 +515,10 @@ export class Utils {
|
||||
return mobile || win.navigator.userAgent.match(/iPad/i) != null;
|
||||
}
|
||||
|
||||
static delay(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
private static isAppleMobile(win: Window) {
|
||||
return (
|
||||
win.navigator.userAgent.match(/iPhone/i) != null ||
|
||||
|
||||
@@ -4,19 +4,9 @@ export class PermissionsApi extends BaseResponse {
|
||||
accessEventLogs: boolean;
|
||||
accessImportExport: boolean;
|
||||
accessReports: boolean;
|
||||
/**
|
||||
* @deprecated Sep 29 2021: This permission has been split out to `createNewCollections`, `editAnyCollection`, and
|
||||
* `deleteAnyCollection`. It exists here for backwards compatibility with Server versions <= 1.43.0
|
||||
*/
|
||||
manageAllCollections: boolean;
|
||||
createNewCollections: boolean;
|
||||
editAnyCollection: boolean;
|
||||
deleteAnyCollection: boolean;
|
||||
/**
|
||||
* @deprecated Sep 29 2021: This permission has been split out to `editAssignedCollections` and
|
||||
* `deleteAssignedCollections`. It exists here for backwards compatibility with Server versions <= 1.43.0
|
||||
*/
|
||||
manageAssignedCollections: boolean;
|
||||
editAssignedCollections: boolean;
|
||||
deleteAssignedCollections: boolean;
|
||||
manageCiphers: boolean;
|
||||
@@ -36,10 +26,6 @@ export class PermissionsApi extends BaseResponse {
|
||||
this.accessImportExport = this.getResponseProperty("AccessImportExport");
|
||||
this.accessReports = this.getResponseProperty("AccessReports");
|
||||
|
||||
// For backwards compatibility with Server <= 1.43.0
|
||||
this.manageAllCollections = this.getResponseProperty("ManageAllCollections");
|
||||
this.manageAssignedCollections = this.getResponseProperty("ManageAssignedCollections");
|
||||
|
||||
this.createNewCollections = this.getResponseProperty("CreateNewCollections");
|
||||
this.editAnyCollection = this.getResponseProperty("EditAnyCollection");
|
||||
this.deleteAnyCollection = this.getResponseProperty("DeleteAnyCollection");
|
||||
|
||||
@@ -20,7 +20,9 @@ export class OrganizationData {
|
||||
useSso: boolean;
|
||||
useKeyConnector: boolean;
|
||||
useScim: boolean;
|
||||
useCustomPermissions: boolean;
|
||||
useResetPassword: boolean;
|
||||
useSecretsManager: boolean;
|
||||
selfHost: boolean;
|
||||
usersGetPremium: boolean;
|
||||
seats: number;
|
||||
@@ -60,7 +62,9 @@ export class OrganizationData {
|
||||
this.useSso = response.useSso;
|
||||
this.useKeyConnector = response.useKeyConnector;
|
||||
this.useScim = response.useScim;
|
||||
this.useCustomPermissions = response.useCustomPermissions;
|
||||
this.useResetPassword = response.useResetPassword;
|
||||
this.useSecretsManager = response.useSecretsManager;
|
||||
this.selfHost = response.selfHost;
|
||||
this.usersGetPremium = response.usersGetPremium;
|
||||
this.seats = response.seats;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Except, Jsonify } from "type-fest";
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { AuthenticationStatus } from "../../enums/authenticationStatus";
|
||||
import { KdfType } from "../../enums/kdfType";
|
||||
@@ -40,7 +40,7 @@ export class EncryptionPair<TEncrypted, TDecrypted> {
|
||||
}
|
||||
|
||||
static fromJSON<TEncrypted, TDecrypted>(
|
||||
obj: Jsonify<EncryptionPair<Jsonify<TEncrypted>, Jsonify<TDecrypted>>>,
|
||||
obj: { encrypted?: Jsonify<TEncrypted>; decrypted?: string | Jsonify<TDecrypted> },
|
||||
decryptedFromJson?: (decObj: Jsonify<TDecrypted> | string) => TDecrypted,
|
||||
encryptedFromJson?: (encObj: Jsonify<TEncrypted>) => TEncrypted
|
||||
) {
|
||||
@@ -123,7 +123,7 @@ export class AccountKeys {
|
||||
apiKeyClientSecret?: string;
|
||||
|
||||
toJSON() {
|
||||
return Object.assign(this as Except<AccountKeys, "publicKey">, {
|
||||
return Utils.merge(this, {
|
||||
publicKey: Utils.fromBufferToByteString(this.publicKey),
|
||||
});
|
||||
}
|
||||
@@ -233,6 +233,7 @@ export class AccountSettings {
|
||||
vaultTimeout?: number;
|
||||
vaultTimeoutAction?: string = "lock";
|
||||
serverConfig?: ServerConfigData;
|
||||
avatarColor?: string;
|
||||
|
||||
static fromJSON(obj: Jsonify<AccountSettings>): AccountSettings {
|
||||
if (obj == null) {
|
||||
@@ -251,7 +252,7 @@ export class AccountSettings {
|
||||
}
|
||||
|
||||
export type AccountSettingsSettings = {
|
||||
equivalentDomains?: { [id: string]: any };
|
||||
equivalentDomains?: string[][];
|
||||
};
|
||||
|
||||
export class AccountTokens {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { OrganizationUserStatusType } from "../../enums/organizationUserStatusType";
|
||||
import { OrganizationUserType } from "../../enums/organizationUserType";
|
||||
import { ProductType } from "../../enums/productType";
|
||||
@@ -20,7 +22,9 @@ export class Organization {
|
||||
useSso: boolean;
|
||||
useKeyConnector: boolean;
|
||||
useScim: boolean;
|
||||
useCustomPermissions: boolean;
|
||||
useResetPassword: boolean;
|
||||
useSecretsManager: boolean;
|
||||
selfHost: boolean;
|
||||
usersGetPremium: boolean;
|
||||
seats: number;
|
||||
@@ -64,7 +68,9 @@ export class Organization {
|
||||
this.useSso = obj.useSso;
|
||||
this.useKeyConnector = obj.useKeyConnector;
|
||||
this.useScim = obj.useScim;
|
||||
this.useCustomPermissions = obj.useCustomPermissions;
|
||||
this.useResetPassword = obj.useResetPassword;
|
||||
this.useSecretsManager = obj.useSecretsManager;
|
||||
this.selfHost = obj.selfHost;
|
||||
this.usersGetPremium = obj.usersGetPremium;
|
||||
this.seats = obj.seats;
|
||||
@@ -125,23 +131,19 @@ export class Organization {
|
||||
}
|
||||
|
||||
get canCreateNewCollections() {
|
||||
return (
|
||||
this.isManager ||
|
||||
(this.permissions.createNewCollections ?? this.permissions.manageAllCollections)
|
||||
);
|
||||
return this.isManager || this.permissions.createNewCollections;
|
||||
}
|
||||
|
||||
get canEditAnyCollection() {
|
||||
return (
|
||||
this.isAdmin || (this.permissions.editAnyCollection ?? this.permissions.manageAllCollections)
|
||||
);
|
||||
return this.isAdmin || this.permissions.editAnyCollection;
|
||||
}
|
||||
|
||||
get canUseAdminCollections() {
|
||||
return this.canEditAnyCollection;
|
||||
}
|
||||
|
||||
get canDeleteAnyCollection() {
|
||||
return (
|
||||
this.isAdmin ||
|
||||
(this.permissions.deleteAnyCollection ?? this.permissions.manageAllCollections)
|
||||
);
|
||||
return this.isAdmin || this.permissions.deleteAnyCollection;
|
||||
}
|
||||
|
||||
get canViewAllCollections() {
|
||||
@@ -149,17 +151,11 @@ export class Organization {
|
||||
}
|
||||
|
||||
get canEditAssignedCollections() {
|
||||
return (
|
||||
this.isManager ||
|
||||
(this.permissions.editAssignedCollections ?? this.permissions.manageAssignedCollections)
|
||||
);
|
||||
return this.isManager || this.permissions.editAssignedCollections;
|
||||
}
|
||||
|
||||
get canDeleteAssignedCollections() {
|
||||
return (
|
||||
this.isManager ||
|
||||
(this.permissions.deleteAssignedCollections ?? this.permissions.manageAssignedCollections)
|
||||
);
|
||||
return this.isManager || this.permissions.deleteAssignedCollections;
|
||||
}
|
||||
|
||||
get canViewAssignedCollections() {
|
||||
@@ -201,4 +197,19 @@ export class Organization {
|
||||
get hasProvider() {
|
||||
return this.providerId != null || this.providerName != null;
|
||||
}
|
||||
|
||||
get canAccessSecretsManager() {
|
||||
return this.useSecretsManager;
|
||||
}
|
||||
|
||||
static fromJSON(json: Jsonify<Organization>) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Object.assign(new Organization(), json, {
|
||||
familySponsorshipLastSyncDate: new Date(json.familySponsorshipLastSyncDate),
|
||||
familySponsorshipValidUntil: new Date(json.familySponsorshipValidUntil),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,22 +4,25 @@ import { State } from "./state";
|
||||
describe("state", () => {
|
||||
describe("fromJSON", () => {
|
||||
it("should deserialize to an instance of itself", () => {
|
||||
expect(State.fromJSON({})).toBeInstanceOf(State);
|
||||
expect(State.fromJSON({}, () => new Account({}))).toBeInstanceOf(State);
|
||||
});
|
||||
|
||||
it("should always assign an object to accounts", () => {
|
||||
const state = State.fromJSON({});
|
||||
const state = State.fromJSON({}, () => new Account({}));
|
||||
expect(state.accounts).not.toBeNull();
|
||||
expect(state.accounts).toEqual({});
|
||||
});
|
||||
|
||||
it("should build an account map", () => {
|
||||
const accountsSpy = jest.spyOn(Account, "fromJSON");
|
||||
const state = State.fromJSON({
|
||||
accounts: {
|
||||
userId: {},
|
||||
const state = State.fromJSON(
|
||||
{
|
||||
accounts: {
|
||||
userId: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
Account.fromJSON
|
||||
);
|
||||
|
||||
expect(state.accounts["userId"]).toBeInstanceOf(Account);
|
||||
expect(accountsSpy).toHaveBeenCalled();
|
||||
|
||||
@@ -19,26 +19,28 @@ export class State<
|
||||
|
||||
// TODO, make Jsonify<State,TGlobalState,TAccount> work. It currently doesn't because Globals doesn't implement Jsonify.
|
||||
static fromJSON<TGlobalState extends GlobalState, TAccount extends Account>(
|
||||
obj: any
|
||||
obj: any,
|
||||
accountDeserializer: (json: Jsonify<TAccount>) => TAccount
|
||||
): State<TGlobalState, TAccount> {
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Object.assign(new State(null), obj, {
|
||||
accounts: State.buildAccountMapFromJSON(obj?.accounts),
|
||||
accounts: State.buildAccountMapFromJSON(obj?.accounts, accountDeserializer),
|
||||
});
|
||||
}
|
||||
|
||||
private static buildAccountMapFromJSON(
|
||||
jsonAccounts: Jsonify<{ [userId: string]: Jsonify<Account> }>
|
||||
private static buildAccountMapFromJSON<TAccount extends Account>(
|
||||
jsonAccounts: { [userId: string]: Jsonify<TAccount> },
|
||||
accountDeserializer: (json: Jsonify<TAccount>) => TAccount
|
||||
) {
|
||||
if (!jsonAccounts) {
|
||||
return {};
|
||||
}
|
||||
const accounts: { [userId: string]: Account } = {};
|
||||
const accounts: { [userId: string]: TAccount } = {};
|
||||
for (const userId in jsonAccounts) {
|
||||
accounts[userId] = Account.fromJSON(jsonAccounts[userId]);
|
||||
accounts[userId] = accountDeserializer(jsonAccounts[userId]);
|
||||
}
|
||||
return accounts;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
export class TreeNode<T extends ITreeNodeObject> {
|
||||
parent: T;
|
||||
node: T;
|
||||
parent: TreeNode<T>;
|
||||
children: TreeNode<T>[] = [];
|
||||
|
||||
constructor(node: T, name: string, parent: T) {
|
||||
constructor(node: T, parent: TreeNode<T>, name?: string, id?: string) {
|
||||
this.parent = parent;
|
||||
this.node = node;
|
||||
this.node.name = name;
|
||||
if (name) {
|
||||
this.node.name = name;
|
||||
}
|
||||
if (id) {
|
||||
this.node.id = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,4 +7,5 @@ export class WindowState {
|
||||
displayBounds: any;
|
||||
x?: number;
|
||||
y?: number;
|
||||
zoomFactor?: number;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
export class CollectionBulkDeleteRequest {
|
||||
ids: string[];
|
||||
organizationId: string;
|
||||
|
||||
constructor(ids: string[], organizationId?: string) {
|
||||
this.ids = ids == null ? [] : ids;
|
||||
this.organizationId = organizationId;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ export class CollectionRequest {
|
||||
name: string;
|
||||
externalId: string;
|
||||
groups: SelectionReadOnlyRequest[] = [];
|
||||
users: SelectionReadOnlyRequest[] = [];
|
||||
|
||||
constructor(collection?: Collection) {
|
||||
if (collection == null) {
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
import { SelectionReadOnlyRequest } from "./selection-read-only.request";
|
||||
|
||||
export class GroupRequest {
|
||||
name: string;
|
||||
accessAll: boolean;
|
||||
externalId: string;
|
||||
collections: SelectionReadOnlyRequest[] = [];
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { OrganizationUserType } from "../../enums/organizationUserType";
|
||||
import { PermissionsApi } from "../api/permissions.api";
|
||||
|
||||
import { SelectionReadOnlyRequest } from "./selection-read-only.request";
|
||||
|
||||
export class OrganizationUserInviteRequest {
|
||||
emails: string[] = [];
|
||||
type: OrganizationUserType;
|
||||
accessAll: boolean;
|
||||
collections: SelectionReadOnlyRequest[] = [];
|
||||
permissions: PermissionsApi;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { OrganizationUserType } from "../../enums/organizationUserType";
|
||||
import { PermissionsApi } from "../api/permissions.api";
|
||||
|
||||
import { SelectionReadOnlyRequest } from "./selection-read-only.request";
|
||||
|
||||
export class OrganizationUserUpdateRequest {
|
||||
type: OrganizationUserType;
|
||||
accessAll: boolean;
|
||||
collections: SelectionReadOnlyRequest[] = [];
|
||||
permissions: PermissionsApi;
|
||||
}
|
||||
7
libs/common/src/models/request/update-avatar.request.ts
Normal file
7
libs/common/src/models/request/update-avatar.request.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export class UpdateAvatarRequest {
|
||||
avatarColor: string;
|
||||
|
||||
constructor(avatarColor: string) {
|
||||
this.avatarColor = avatarColor;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OrganizationUserResetPasswordRequest } from "./organization-user-reset-password.request";
|
||||
import { OrganizationUserResetPasswordRequest } from "../../abstractions/organization-user/requests";
|
||||
|
||||
export class UpdateTempPasswordRequest extends OrganizationUserResetPasswordRequest {
|
||||
masterPasswordHint: string;
|
||||
|
||||
@@ -25,14 +25,27 @@ export class CollectionDetailsResponse extends CollectionResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export class CollectionGroupDetailsResponse extends CollectionResponse {
|
||||
export class CollectionAccessDetailsResponse extends CollectionResponse {
|
||||
groups: SelectionReadOnlyResponse[] = [];
|
||||
users: SelectionReadOnlyResponse[] = [];
|
||||
|
||||
/**
|
||||
* Flag indicating the user has been explicitly assigned to this Collection
|
||||
*/
|
||||
assigned: boolean;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.assigned = this.getResponseProperty("Assigned") || false;
|
||||
|
||||
const groups = this.getResponseProperty("Groups");
|
||||
if (groups != null) {
|
||||
this.groups = groups.map((g: any) => new SelectionReadOnlyResponse(g));
|
||||
}
|
||||
|
||||
const users = this.getResponseProperty("Users");
|
||||
if (users != null) {
|
||||
this.users = users.map((g: any) => new SelectionReadOnlyResponse(g));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ export class EmergencyAccessGranteeDetailsResponse extends BaseResponse {
|
||||
status: EmergencyAccessStatusType;
|
||||
waitTimeDays: number;
|
||||
creationDate: string;
|
||||
avatarColor: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
@@ -25,6 +26,7 @@ export class EmergencyAccessGranteeDetailsResponse extends BaseResponse {
|
||||
this.status = this.getResponseProperty("Status");
|
||||
this.waitTimeDays = this.getResponseProperty("WaitTimeDays");
|
||||
this.creationDate = this.getResponseProperty("CreationDate");
|
||||
this.avatarColor = this.getResponseProperty("AvatarColor");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +39,7 @@ export class EmergencyAccessGrantorDetailsResponse extends BaseResponse {
|
||||
status: EmergencyAccessStatusType;
|
||||
waitTimeDays: number;
|
||||
creationDate: string;
|
||||
avatarColor: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
@@ -48,6 +51,7 @@ export class EmergencyAccessGrantorDetailsResponse extends BaseResponse {
|
||||
this.status = this.getResponseProperty("Status");
|
||||
this.waitTimeDays = this.getResponseProperty("WaitTimeDays");
|
||||
this.creationDate = this.getResponseProperty("CreationDate");
|
||||
this.avatarColor = this.getResponseProperty("AvatarColor");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import { BaseResponse } from "./base.response";
|
||||
import { SelectionReadOnlyResponse } from "./selection-read-only.response";
|
||||
|
||||
export class GroupResponse extends BaseResponse {
|
||||
id: string;
|
||||
organizationId: string;
|
||||
name: string;
|
||||
accessAll: boolean;
|
||||
externalId: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.organizationId = this.getResponseProperty("OrganizationId");
|
||||
this.name = this.getResponseProperty("Name");
|
||||
this.accessAll = this.getResponseProperty("AccessAll");
|
||||
this.externalId = this.getResponseProperty("ExternalId");
|
||||
}
|
||||
}
|
||||
|
||||
export class GroupDetailsResponse extends GroupResponse {
|
||||
collections: SelectionReadOnlyResponse[] = [];
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
const collections = this.getResponseProperty("Collections");
|
||||
if (collections != null) {
|
||||
this.collections = collections.map((c: any) => new SelectionReadOnlyResponse(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,9 @@ export class ProfileOrganizationResponse extends BaseResponse {
|
||||
useSso: boolean;
|
||||
useKeyConnector: boolean;
|
||||
useScim: boolean;
|
||||
useCustomPermissions: boolean;
|
||||
useResetPassword: boolean;
|
||||
useSecretsManager: boolean;
|
||||
selfHost: boolean;
|
||||
usersGetPremium: boolean;
|
||||
seats: number;
|
||||
@@ -59,7 +61,9 @@ export class ProfileOrganizationResponse extends BaseResponse {
|
||||
this.useSso = this.getResponseProperty("UseSso");
|
||||
this.useKeyConnector = this.getResponseProperty("UseKeyConnector") ?? false;
|
||||
this.useScim = this.getResponseProperty("UseScim") ?? false;
|
||||
this.useCustomPermissions = this.getResponseProperty("UseCustomPermissions") ?? false;
|
||||
this.useResetPassword = this.getResponseProperty("UseResetPassword");
|
||||
this.useSecretsManager = this.getResponseProperty("UseSecretsManager");
|
||||
this.selfHost = this.getResponseProperty("SelfHost");
|
||||
this.usersGetPremium = this.getResponseProperty("UsersGetPremium");
|
||||
this.seats = this.getResponseProperty("Seats");
|
||||
|
||||
@@ -14,6 +14,7 @@ export class ProfileResponse extends BaseResponse {
|
||||
culture: string;
|
||||
twoFactorEnabled: boolean;
|
||||
key: string;
|
||||
avatarColor: string;
|
||||
privateKey: string;
|
||||
securityStamp: string;
|
||||
forcePasswordReset: boolean;
|
||||
@@ -34,6 +35,7 @@ export class ProfileResponse extends BaseResponse {
|
||||
this.culture = this.getResponseProperty("Culture");
|
||||
this.twoFactorEnabled = this.getResponseProperty("TwoFactorEnabled");
|
||||
this.key = this.getResponseProperty("Key");
|
||||
this.avatarColor = this.getResponseProperty("AvatarColor");
|
||||
this.privateKey = this.getResponseProperty("PrivateKey");
|
||||
this.securityStamp = this.getResponseProperty("SecurityStamp");
|
||||
this.forcePasswordReset = this.getResponseProperty("ForcePasswordReset") ?? false;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { OrganizationUserBulkPublicKeyResponse } from "../organization-user-bulk-public-key.response";
|
||||
import { OrganizationUserBulkPublicKeyResponse } from "../../../abstractions/organization-user/responses";
|
||||
|
||||
export class ProviderUserBulkPublicKeyResponse extends OrganizationUserBulkPublicKeyResponse {}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Collection } from "../domain/collection";
|
||||
import { ITreeNodeObject } from "../domain/tree-node";
|
||||
import { CollectionGroupDetailsResponse } from "../response/collection.response";
|
||||
import { CollectionAccessDetailsResponse } from "../response/collection.response";
|
||||
|
||||
import { View } from "./view";
|
||||
|
||||
@@ -12,7 +12,7 @@ export class CollectionView implements View, ITreeNodeObject {
|
||||
readOnly: boolean = null;
|
||||
hidePasswords: boolean = null;
|
||||
|
||||
constructor(c?: Collection | CollectionGroupDetailsResponse) {
|
||||
constructor(c?: Collection | CollectionAccessDetailsResponse) {
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { DeepJsonify } from "../../types/deep-jsonify";
|
||||
import { SendFile } from "../domain/send-file";
|
||||
|
||||
import { View } from "./view";
|
||||
@@ -28,4 +29,12 @@ export class SendFileView implements View {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static fromJSON(json: DeepJsonify<SendFileView>) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Object.assign(new SendFileView(), json);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { DeepJsonify } from "../../types/deep-jsonify";
|
||||
import { SendText } from "../domain/send-text";
|
||||
|
||||
import { View } from "./view";
|
||||
@@ -17,4 +18,12 @@ export class SendTextView implements View {
|
||||
get maskedText(): string {
|
||||
return this.text != null ? "••••••••" : null;
|
||||
}
|
||||
|
||||
static fromJSON(json: DeepJsonify<SendTextView>) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Object.assign(new SendTextView(), json);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { SendType } from "../../enums/sendType";
|
||||
import { Utils } from "../../misc/utils";
|
||||
import { DeepJsonify } from "../../types/deep-jsonify";
|
||||
import { Send } from "../domain/send";
|
||||
import { SymmetricCryptoKey } from "../domain/symmetric-crypto-key";
|
||||
|
||||
@@ -65,4 +66,26 @@ export class SendView implements View {
|
||||
get pendingDelete(): boolean {
|
||||
return this.deletionDate <= new Date();
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return Utils.merge(this, {
|
||||
key: Utils.fromBufferToB64(this.key),
|
||||
});
|
||||
}
|
||||
|
||||
static fromJSON(json: DeepJsonify<SendView>) {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Object.assign(new SendView(), json, {
|
||||
key: Utils.fromB64ToArray(json.key)?.buffer,
|
||||
cryptoKey: SymmetricCryptoKey.fromJSON(json.cryptoKey),
|
||||
text: SendTextView.fromJSON(json.text),
|
||||
file: SendFileView.fromJSON(json.file),
|
||||
revisionDate: json.revisionDate == null ? null : new Date(json.revisionDate),
|
||||
deletionDate: json.deletionDate == null ? null : new Date(json.deletionDate),
|
||||
expirationDate: json.expirationDate == null ? null : new Date(json.expirationDate),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
30
libs/common/src/services/account/avatar-update.service.ts
Normal file
30
libs/common/src/services/account/avatar-update.service.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { BehaviorSubject, Observable } from "rxjs";
|
||||
|
||||
import { AvatarUpdateService as AvatarUpdateServiceAbstraction } from "../../abstractions/account/avatar-update.service";
|
||||
import { ApiService } from "../../abstractions/api.service";
|
||||
import { StateService } from "../../abstractions/state.service";
|
||||
import { UpdateAvatarRequest } from "../../models/request/update-avatar.request";
|
||||
import { ProfileResponse } from "../../models/response/profile.response";
|
||||
|
||||
export class AvatarUpdateService implements AvatarUpdateServiceAbstraction {
|
||||
private _avatarUpdate$ = new BehaviorSubject<string | null>(null);
|
||||
avatarUpdate$: Observable<string | null> = this._avatarUpdate$.asObservable();
|
||||
|
||||
constructor(private apiService: ApiService, private stateService: StateService) {
|
||||
this.loadColorFromState();
|
||||
}
|
||||
|
||||
loadColorFromState(): Promise<string | null> {
|
||||
return this.stateService.getAvatarColor().then((color) => {
|
||||
this._avatarUpdate$.next(color);
|
||||
return color;
|
||||
});
|
||||
}
|
||||
|
||||
pushUpdate(color: string | null): Promise<ProfileResponse | void> {
|
||||
return this.apiService.putAvatar(new UpdateAvatarRequest(color)).then((response) => {
|
||||
this.stateService.setAvatarColor(response.avatarColor);
|
||||
this._avatarUpdate$.next(response.avatarColor);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import { CipherCreateRequest } from "../models/request/cipher-create.request";
|
||||
import { CipherPartialRequest } from "../models/request/cipher-partial.request";
|
||||
import { CipherShareRequest } from "../models/request/cipher-share.request";
|
||||
import { CipherRequest } from "../models/request/cipher.request";
|
||||
import { CollectionBulkDeleteRequest } from "../models/request/collection-bulk-delete.request";
|
||||
import { CollectionRequest } from "../models/request/collection.request";
|
||||
import { DeleteRecoverRequest } from "../models/request/delete-recover.request";
|
||||
import { DeviceVerificationRequest } from "../models/request/device-verification.request";
|
||||
@@ -29,7 +30,6 @@ import { EmergencyAccessInviteRequest } from "../models/request/emergency-access
|
||||
import { EmergencyAccessPasswordRequest } from "../models/request/emergency-access-password.request";
|
||||
import { EmergencyAccessUpdateRequest } from "../models/request/emergency-access-update.request";
|
||||
import { EventRequest } from "../models/request/event.request";
|
||||
import { GroupRequest } from "../models/request/group.request";
|
||||
import { IapCheckRequest } from "../models/request/iap-check.request";
|
||||
import { PasswordTokenRequest } from "../models/request/identity-token/password-token.request";
|
||||
import { SsoTokenRequest } from "../models/request/identity-token/sso-token.request";
|
||||
@@ -42,15 +42,6 @@ import { KeyConnectorUserKeyRequest } from "../models/request/key-connector-user
|
||||
import { KeysRequest } from "../models/request/keys.request";
|
||||
import { OrganizationConnectionRequest } from "../models/request/organization-connection.request";
|
||||
import { OrganizationImportRequest } from "../models/request/organization-import.request";
|
||||
import { OrganizationUserAcceptRequest } from "../models/request/organization-user-accept.request";
|
||||
import { OrganizationUserBulkConfirmRequest } from "../models/request/organization-user-bulk-confirm.request";
|
||||
import { OrganizationUserBulkRequest } from "../models/request/organization-user-bulk.request";
|
||||
import { OrganizationUserConfirmRequest } from "../models/request/organization-user-confirm.request";
|
||||
import { OrganizationUserInviteRequest } from "../models/request/organization-user-invite.request";
|
||||
import { OrganizationUserResetPasswordEnrollmentRequest } from "../models/request/organization-user-reset-password-enrollment.request";
|
||||
import { OrganizationUserResetPasswordRequest } from "../models/request/organization-user-reset-password.request";
|
||||
import { OrganizationUserUpdateGroupsRequest } from "../models/request/organization-user-update-groups.request";
|
||||
import { OrganizationUserUpdateRequest } from "../models/request/organization-user-update.request";
|
||||
import { OrganizationSponsorshipCreateRequest } from "../models/request/organization/organization-sponsorship-create.request";
|
||||
import { OrganizationSponsorshipRedeemRequest } from "../models/request/organization/organization-sponsorship-redeem.request";
|
||||
import { PasswordHintRequest } from "../models/request/password-hint.request";
|
||||
@@ -79,6 +70,7 @@ import { TaxInfoUpdateRequest } from "../models/request/tax-info-update.request"
|
||||
import { TwoFactorEmailRequest } from "../models/request/two-factor-email.request";
|
||||
import { TwoFactorProviderRequest } from "../models/request/two-factor-provider.request";
|
||||
import { TwoFactorRecoveryRequest } from "../models/request/two-factor-recovery.request";
|
||||
import { UpdateAvatarRequest } from "../models/request/update-avatar.request";
|
||||
import { UpdateDomainsRequest } from "../models/request/update-domains.request";
|
||||
import { UpdateKeyRequest } from "../models/request/update-key.request";
|
||||
import { UpdateProfileRequest } from "../models/request/update-profile.request";
|
||||
@@ -101,7 +93,7 @@ import { BillingPaymentResponse } from "../models/response/billing-payment.respo
|
||||
import { BreachAccountResponse } from "../models/response/breach-account.response";
|
||||
import { CipherResponse } from "../models/response/cipher.response";
|
||||
import {
|
||||
CollectionGroupDetailsResponse,
|
||||
CollectionAccessDetailsResponse,
|
||||
CollectionResponse,
|
||||
} from "../models/response/collection.response";
|
||||
import { DeviceVerificationResponse } from "../models/response/device-verification.response";
|
||||
@@ -114,7 +106,6 @@ import {
|
||||
} from "../models/response/emergency-access.response";
|
||||
import { ErrorResponse } from "../models/response/error.response";
|
||||
import { EventResponse } from "../models/response/event.response";
|
||||
import { GroupDetailsResponse, GroupResponse } from "../models/response/group.response";
|
||||
import { IdentityCaptchaResponse } from "../models/response/identity-captcha.response";
|
||||
import { IdentityTokenResponse } from "../models/response/identity-token.response";
|
||||
import { IdentityTwoFactorResponse } from "../models/response/identity-two-factor.response";
|
||||
@@ -126,13 +117,6 @@ import {
|
||||
} from "../models/response/organization-connection.response";
|
||||
import { OrganizationExportResponse } from "../models/response/organization-export.response";
|
||||
import { OrganizationSponsorshipSyncStatusResponse } from "../models/response/organization-sponsorship-sync-status.response";
|
||||
import { OrganizationUserBulkPublicKeyResponse } from "../models/response/organization-user-bulk-public-key.response";
|
||||
import { OrganizationUserBulkResponse } from "../models/response/organization-user-bulk.response";
|
||||
import {
|
||||
OrganizationUserDetailsResponse,
|
||||
OrganizationUserUserDetailsResponse,
|
||||
OrganizationUserResetPasswordDetailsReponse,
|
||||
} from "../models/response/organization-user.response";
|
||||
import { PaymentResponse } from "../models/response/payment.response";
|
||||
import { PlanResponse } from "../models/response/plan.response";
|
||||
import { PolicyResponse } from "../models/response/policy.response";
|
||||
@@ -165,13 +149,18 @@ import { TwoFactorEmailResponse } from "../models/response/two-factor-email.resp
|
||||
import { TwoFactorProviderResponse } from "../models/response/two-factor-provider.response";
|
||||
import { TwoFactorRecoverResponse } from "../models/response/two-factor-recover.response";
|
||||
import {
|
||||
TwoFactorWebAuthnResponse,
|
||||
ChallengeResponse,
|
||||
TwoFactorWebAuthnResponse,
|
||||
} from "../models/response/two-factor-web-authn.response";
|
||||
import { TwoFactorYubiKeyResponse } from "../models/response/two-factor-yubi-key.response";
|
||||
import { UserKeyResponse } from "../models/response/user-key.response";
|
||||
import { SendAccessView } from "../models/view/send-access.view";
|
||||
|
||||
/**
|
||||
* @deprecated The `ApiService` class is deprecated and calls should be extracted into individual
|
||||
* api services. The `send` method is still allowed to be used within api services. For background
|
||||
* of this decision please read https://contributing.bitwarden.com/architecture/adr/refactor-api-service.
|
||||
*/
|
||||
export class ApiService implements ApiServiceAbstraction {
|
||||
private device: DeviceType;
|
||||
private deviceType: string;
|
||||
@@ -301,6 +290,11 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
return new ProfileResponse(r);
|
||||
}
|
||||
|
||||
async putAvatar(request: UpdateAvatarRequest): Promise<ProfileResponse> {
|
||||
const r = await this.send("PUT", "/accounts/avatar", request, true, true);
|
||||
return new ProfileResponse(r);
|
||||
}
|
||||
|
||||
putTaxInfo(request: TaxInfoUpdateRequest): Promise<any> {
|
||||
return this.send("PUT", "/accounts/tax", request, true, false);
|
||||
}
|
||||
@@ -809,10 +803,10 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
|
||||
// Collections APIs
|
||||
|
||||
async getCollectionDetails(
|
||||
async getCollectionAccessDetails(
|
||||
organizationId: string,
|
||||
id: string
|
||||
): Promise<CollectionGroupDetailsResponse> {
|
||||
): Promise<CollectionAccessDetailsResponse> {
|
||||
const r = await this.send(
|
||||
"GET",
|
||||
"/organizations/" + organizationId + "/collections/" + id + "/details",
|
||||
@@ -820,7 +814,7 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new CollectionGroupDetailsResponse(r);
|
||||
return new CollectionAccessDetailsResponse(r);
|
||||
}
|
||||
|
||||
async getUserCollections(): Promise<ListResponse<CollectionResponse>> {
|
||||
@@ -839,6 +833,19 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
return new ListResponse(r, CollectionResponse);
|
||||
}
|
||||
|
||||
async getManyCollectionsWithAccessDetails(
|
||||
organizationId: string
|
||||
): Promise<ListResponse<CollectionAccessDetailsResponse>> {
|
||||
const r = await this.send(
|
||||
"GET",
|
||||
"/organizations/" + organizationId + "/collections/details",
|
||||
null,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new ListResponse(r, CollectionAccessDetailsResponse);
|
||||
}
|
||||
|
||||
async getCollectionUsers(
|
||||
organizationId: string,
|
||||
id: string
|
||||
@@ -906,6 +913,16 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
);
|
||||
}
|
||||
|
||||
deleteManyCollections(request: CollectionBulkDeleteRequest): Promise<any> {
|
||||
return this.send(
|
||||
"DELETE",
|
||||
"/organizations/" + request.organizationId + "/collections",
|
||||
request,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
deleteCollectionUser(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
@@ -922,28 +939,6 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
|
||||
// Groups APIs
|
||||
|
||||
async getGroupDetails(organizationId: string, id: string): Promise<GroupDetailsResponse> {
|
||||
const r = await this.send(
|
||||
"GET",
|
||||
"/organizations/" + organizationId + "/groups/" + id + "/details",
|
||||
null,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new GroupDetailsResponse(r);
|
||||
}
|
||||
|
||||
async getGroups(organizationId: string): Promise<ListResponse<GroupResponse>> {
|
||||
const r = await this.send(
|
||||
"GET",
|
||||
"/organizations/" + organizationId + "/groups",
|
||||
null,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new ListResponse(r, GroupResponse);
|
||||
}
|
||||
|
||||
async getGroupUsers(organizationId: string, id: string): Promise<string[]> {
|
||||
const r = await this.send(
|
||||
"GET",
|
||||
@@ -955,32 +950,6 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
return r;
|
||||
}
|
||||
|
||||
async postGroup(organizationId: string, request: GroupRequest): Promise<GroupResponse> {
|
||||
const r = await this.send(
|
||||
"POST",
|
||||
"/organizations/" + organizationId + "/groups",
|
||||
request,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new GroupResponse(r);
|
||||
}
|
||||
|
||||
async putGroup(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: GroupRequest
|
||||
): Promise<GroupResponse> {
|
||||
const r = await this.send(
|
||||
"PUT",
|
||||
"/organizations/" + organizationId + "/groups/" + id,
|
||||
request,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new GroupResponse(r);
|
||||
}
|
||||
|
||||
async putGroupUsers(organizationId: string, id: string, request: string[]): Promise<any> {
|
||||
await this.send(
|
||||
"PUT",
|
||||
@@ -991,16 +960,6 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
);
|
||||
}
|
||||
|
||||
deleteGroup(organizationId: string, id: string): Promise<any> {
|
||||
return this.send(
|
||||
"DELETE",
|
||||
"/organizations/" + organizationId + "/groups/" + id,
|
||||
null,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
deleteGroupUser(organizationId: string, id: string, organizationUserId: string): Promise<any> {
|
||||
return this.send(
|
||||
"DELETE",
|
||||
@@ -1011,281 +970,6 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
);
|
||||
}
|
||||
|
||||
// Organization User APIs
|
||||
|
||||
async getOrganizationUser(
|
||||
organizationId: string,
|
||||
id: string
|
||||
): Promise<OrganizationUserDetailsResponse> {
|
||||
const r = await this.send(
|
||||
"GET",
|
||||
"/organizations/" + organizationId + "/users/" + id,
|
||||
null,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new OrganizationUserDetailsResponse(r);
|
||||
}
|
||||
|
||||
async getOrganizationUserGroups(organizationId: string, id: string): Promise<string[]> {
|
||||
const r = await this.send(
|
||||
"GET",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/groups",
|
||||
null,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return r;
|
||||
}
|
||||
|
||||
async getOrganizationUsers(
|
||||
organizationId: string
|
||||
): Promise<ListResponse<OrganizationUserUserDetailsResponse>> {
|
||||
const r = await this.send(
|
||||
"GET",
|
||||
"/organizations/" + organizationId + "/users",
|
||||
null,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new ListResponse(r, OrganizationUserUserDetailsResponse);
|
||||
}
|
||||
|
||||
async getOrganizationUserResetPasswordDetails(
|
||||
organizationId: string,
|
||||
id: string
|
||||
): Promise<OrganizationUserResetPasswordDetailsReponse> {
|
||||
const r = await this.send(
|
||||
"GET",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/reset-password-details",
|
||||
null,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new OrganizationUserResetPasswordDetailsReponse(r);
|
||||
}
|
||||
|
||||
postOrganizationUserInvite(
|
||||
organizationId: string,
|
||||
request: OrganizationUserInviteRequest
|
||||
): Promise<any> {
|
||||
return this.send(
|
||||
"POST",
|
||||
"/organizations/" + organizationId + "/users/invite",
|
||||
request,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
postOrganizationUserReinvite(organizationId: string, id: string): Promise<any> {
|
||||
return this.send(
|
||||
"POST",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/reinvite",
|
||||
null,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
async postManyOrganizationUserReinvite(
|
||||
organizationId: string,
|
||||
request: OrganizationUserBulkRequest
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>> {
|
||||
const r = await this.send(
|
||||
"POST",
|
||||
"/organizations/" + organizationId + "/users/reinvite",
|
||||
request,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new ListResponse(r, OrganizationUserBulkResponse);
|
||||
}
|
||||
|
||||
postOrganizationUserAccept(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserAcceptRequest
|
||||
): Promise<any> {
|
||||
return this.send(
|
||||
"POST",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/accept",
|
||||
request,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
postOrganizationUserConfirm(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserConfirmRequest
|
||||
): Promise<any> {
|
||||
return this.send(
|
||||
"POST",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/confirm",
|
||||
request,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
async postOrganizationUsersPublicKey(
|
||||
organizationId: string,
|
||||
request: OrganizationUserBulkRequest
|
||||
): Promise<ListResponse<OrganizationUserBulkPublicKeyResponse>> {
|
||||
const r = await this.send(
|
||||
"POST",
|
||||
"/organizations/" + organizationId + "/users/public-keys",
|
||||
request,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new ListResponse(r, OrganizationUserBulkPublicKeyResponse);
|
||||
}
|
||||
|
||||
async postOrganizationUserBulkConfirm(
|
||||
organizationId: string,
|
||||
request: OrganizationUserBulkConfirmRequest
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>> {
|
||||
const r = await this.send(
|
||||
"POST",
|
||||
"/organizations/" + organizationId + "/users/confirm",
|
||||
request,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new ListResponse(r, OrganizationUserBulkResponse);
|
||||
}
|
||||
|
||||
putOrganizationUser(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserUpdateRequest
|
||||
): Promise<any> {
|
||||
return this.send(
|
||||
"PUT",
|
||||
"/organizations/" + organizationId + "/users/" + id,
|
||||
request,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
putOrganizationUserGroups(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserUpdateGroupsRequest
|
||||
): Promise<any> {
|
||||
return this.send(
|
||||
"PUT",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/groups",
|
||||
request,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
putOrganizationUserResetPasswordEnrollment(
|
||||
organizationId: string,
|
||||
userId: string,
|
||||
request: OrganizationUserResetPasswordEnrollmentRequest
|
||||
): Promise<void> {
|
||||
return this.send(
|
||||
"PUT",
|
||||
"/organizations/" + organizationId + "/users/" + userId + "/reset-password-enrollment",
|
||||
request,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
putOrganizationUserResetPassword(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserResetPasswordRequest
|
||||
): Promise<any> {
|
||||
return this.send(
|
||||
"PUT",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/reset-password",
|
||||
request,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
deleteOrganizationUser(organizationId: string, id: string): Promise<any> {
|
||||
return this.send(
|
||||
"DELETE",
|
||||
"/organizations/" + organizationId + "/users/" + id,
|
||||
null,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
async deleteManyOrganizationUsers(
|
||||
organizationId: string,
|
||||
request: OrganizationUserBulkRequest
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>> {
|
||||
const r = await this.send(
|
||||
"DELETE",
|
||||
"/organizations/" + organizationId + "/users",
|
||||
request,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new ListResponse(r, OrganizationUserBulkResponse);
|
||||
}
|
||||
|
||||
revokeOrganizationUser(organizationId: string, id: string): Promise<any> {
|
||||
return this.send(
|
||||
"PUT",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/revoke",
|
||||
null,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
async revokeManyOrganizationUsers(
|
||||
organizationId: string,
|
||||
request: OrganizationUserBulkRequest
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>> {
|
||||
const r = await this.send(
|
||||
"PUT",
|
||||
"/organizations/" + organizationId + "/users/revoke",
|
||||
request,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new ListResponse(r, OrganizationUserBulkResponse);
|
||||
}
|
||||
|
||||
restoreOrganizationUser(organizationId: string, id: string): Promise<any> {
|
||||
return this.send(
|
||||
"PUT",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/restore",
|
||||
null,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
async restoreManyOrganizationUsers(
|
||||
organizationId: string,
|
||||
request: OrganizationUserBulkRequest
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>> {
|
||||
const r = await this.send(
|
||||
"PUT",
|
||||
"/organizations/" + organizationId + "/users/restore",
|
||||
request,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new ListResponse(r, OrganizationUserBulkResponse);
|
||||
}
|
||||
|
||||
// Plan APIs
|
||||
|
||||
async getPlans(): Promise<ListResponse<PlanResponse>> {
|
||||
@@ -2081,7 +1765,7 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
request.headers.set("Bitwarden-Client-Name", this.platformUtilsService.getClientType());
|
||||
request.headers.set(
|
||||
"Bitwarden-Client-Version",
|
||||
await this.platformUtilsService.getApplicationVersion()
|
||||
await this.platformUtilsService.getApplicationVersionNumber()
|
||||
);
|
||||
return this.nativeFetch(request);
|
||||
}
|
||||
|
||||
@@ -413,7 +413,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
: firstValueFrom(this.settingsService.settings$).then(
|
||||
(settings: AccountSettingsSettings) => {
|
||||
let matches: any[] = [];
|
||||
settings.equivalentDomains?.forEach((eqDomain: any) => {
|
||||
settings?.equivalentDomains?.forEach((eqDomain: any) => {
|
||||
if (eqDomain.length && eqDomain.indexOf(domain) >= 0) {
|
||||
matches = matches.concat(eqDomain);
|
||||
}
|
||||
|
||||
@@ -106,9 +106,13 @@ export class CollectionService implements CollectionServiceAbstraction {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated August 30 2022: Moved to new Vault Filter Service
|
||||
* Remove when Desktop and Browser are updated
|
||||
*/
|
||||
async getNested(id: string): Promise<TreeNode<CollectionView>> {
|
||||
const collections = await this.getAllNested();
|
||||
return ServiceUtils.getTreeNodeObject(collections, id) as TreeNode<CollectionView>;
|
||||
return ServiceUtils.getTreeNodeObjectFromList(collections, id) as TreeNode<CollectionView>;
|
||||
}
|
||||
|
||||
async upsert(collection: CollectionData | CollectionData[]): Promise<any> {
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
import { ApiService } from "../abstractions/api.service";
|
||||
import { CipherService } from "../abstractions/cipher.service";
|
||||
import { EventService as EventServiceAbstraction } from "../abstractions/event.service";
|
||||
import { LogService } from "../abstractions/log.service";
|
||||
import { OrganizationService } from "../abstractions/organization/organization.service.abstraction";
|
||||
import { StateService } from "../abstractions/state.service";
|
||||
import { EventType } from "../enums/eventType";
|
||||
import { EventData } from "../models/data/event.data";
|
||||
import { EventRequest } from "../models/request/event.request";
|
||||
|
||||
export class EventService implements EventServiceAbstraction {
|
||||
private inited = false;
|
||||
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
private cipherService: CipherService,
|
||||
private stateService: StateService,
|
||||
private logService: LogService,
|
||||
private organizationService: OrganizationService
|
||||
) {}
|
||||
|
||||
init(checkOnInterval: boolean) {
|
||||
if (this.inited) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.inited = true;
|
||||
if (checkOnInterval) {
|
||||
this.uploadEvents();
|
||||
setInterval(() => this.uploadEvents(), 60 * 1000); // check every 60 seconds
|
||||
}
|
||||
}
|
||||
|
||||
async collect(
|
||||
eventType: EventType,
|
||||
cipherId: string = null,
|
||||
uploadImmediately = false,
|
||||
organizationId: string = null
|
||||
): Promise<any> {
|
||||
const authed = await this.stateService.getIsAuthenticated();
|
||||
if (!authed) {
|
||||
return;
|
||||
}
|
||||
const organizations = await this.organizationService.getAll();
|
||||
if (organizations == null) {
|
||||
return;
|
||||
}
|
||||
const orgIds = new Set<string>(organizations.filter((o) => o.useEvents).map((o) => o.id));
|
||||
if (orgIds.size === 0) {
|
||||
return;
|
||||
}
|
||||
if (cipherId != null) {
|
||||
const cipher = await this.cipherService.get(cipherId);
|
||||
if (cipher == null || cipher.organizationId == null || !orgIds.has(cipher.organizationId)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (organizationId != null) {
|
||||
if (!orgIds.has(organizationId)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let eventCollection = await this.stateService.getEventCollection();
|
||||
if (eventCollection == null) {
|
||||
eventCollection = [];
|
||||
}
|
||||
const event = new EventData();
|
||||
event.type = eventType;
|
||||
event.cipherId = cipherId;
|
||||
event.date = new Date().toISOString();
|
||||
event.organizationId = organizationId;
|
||||
eventCollection.push(event);
|
||||
await this.stateService.setEventCollection(eventCollection);
|
||||
if (uploadImmediately) {
|
||||
await this.uploadEvents();
|
||||
}
|
||||
}
|
||||
|
||||
async uploadEvents(userId?: string): Promise<any> {
|
||||
const authed = await this.stateService.getIsAuthenticated({ userId: userId });
|
||||
if (!authed) {
|
||||
return;
|
||||
}
|
||||
const eventCollection = await this.stateService.getEventCollection({ userId: userId });
|
||||
if (eventCollection == null || eventCollection.length === 0) {
|
||||
return;
|
||||
}
|
||||
const request = eventCollection.map((e) => {
|
||||
const req = new EventRequest();
|
||||
req.type = e.type;
|
||||
req.cipherId = e.cipherId;
|
||||
req.date = e.date;
|
||||
req.organizationId = e.organizationId;
|
||||
return req;
|
||||
});
|
||||
try {
|
||||
await this.apiService.postEventsCollect(request);
|
||||
this.clearEvents(userId);
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
async clearEvents(userId?: string): Promise<any> {
|
||||
await this.stateService.setEventCollection(null, { userId: userId });
|
||||
}
|
||||
}
|
||||
61
libs/common/src/services/event/event-collection.service.ts
Normal file
61
libs/common/src/services/event/event-collection.service.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { CipherService } from "../../abstractions/cipher.service";
|
||||
import { EventCollectionService as EventCollectionServiceAbstraction } from "../../abstractions/event/event-collection.service";
|
||||
import { EventUploadService } from "../../abstractions/event/event-upload.service";
|
||||
import { OrganizationService } from "../../abstractions/organization/organization.service.abstraction";
|
||||
import { StateService } from "../../abstractions/state.service";
|
||||
import { EventType } from "../../enums/eventType";
|
||||
import { EventData } from "../../models/data/event.data";
|
||||
|
||||
export class EventCollectionService implements EventCollectionServiceAbstraction {
|
||||
constructor(
|
||||
private cipherService: CipherService,
|
||||
private stateService: StateService,
|
||||
private organizationService: OrganizationService,
|
||||
private eventUploadService: EventUploadService
|
||||
) {}
|
||||
|
||||
async collect(
|
||||
eventType: EventType,
|
||||
cipherId: string = null,
|
||||
uploadImmediately = false,
|
||||
organizationId: string = null
|
||||
): Promise<any> {
|
||||
const authed = await this.stateService.getIsAuthenticated();
|
||||
if (!authed) {
|
||||
return;
|
||||
}
|
||||
const organizations = await this.organizationService.getAll();
|
||||
if (organizations == null) {
|
||||
return;
|
||||
}
|
||||
const orgIds = new Set<string>(organizations.filter((o) => o.useEvents).map((o) => o.id));
|
||||
if (orgIds.size === 0) {
|
||||
return;
|
||||
}
|
||||
if (cipherId != null) {
|
||||
const cipher = await this.cipherService.get(cipherId);
|
||||
if (cipher == null || cipher.organizationId == null || !orgIds.has(cipher.organizationId)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (organizationId != null) {
|
||||
if (!orgIds.has(organizationId)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let eventCollection = await this.stateService.getEventCollection();
|
||||
if (eventCollection == null) {
|
||||
eventCollection = [];
|
||||
}
|
||||
const event = new EventData();
|
||||
event.type = eventType;
|
||||
event.cipherId = cipherId;
|
||||
event.date = new Date().toISOString();
|
||||
event.organizationId = organizationId;
|
||||
eventCollection.push(event);
|
||||
await this.stateService.setEventCollection(eventCollection);
|
||||
if (uploadImmediately) {
|
||||
await this.eventUploadService.uploadEvents();
|
||||
}
|
||||
}
|
||||
}
|
||||
55
libs/common/src/services/event/event-upload.service.ts
Normal file
55
libs/common/src/services/event/event-upload.service.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { ApiService } from "../../abstractions/api.service";
|
||||
import { EventUploadService as EventUploadServiceAbstraction } from "../../abstractions/event/event-upload.service";
|
||||
import { LogService } from "../../abstractions/log.service";
|
||||
import { StateService } from "../../abstractions/state.service";
|
||||
import { EventRequest } from "../../models/request/event.request";
|
||||
|
||||
export class EventUploadService implements EventUploadServiceAbstraction {
|
||||
private inited = false;
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
private stateService: StateService,
|
||||
private logService: LogService
|
||||
) {}
|
||||
|
||||
init(checkOnInterval: boolean) {
|
||||
if (this.inited) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.inited = true;
|
||||
if (checkOnInterval) {
|
||||
this.uploadEvents();
|
||||
setInterval(() => this.uploadEvents(), 60 * 1000); // check every 60 seconds
|
||||
}
|
||||
}
|
||||
|
||||
async uploadEvents(userId?: string): Promise<void> {
|
||||
const authed = await this.stateService.getIsAuthenticated({ userId: userId });
|
||||
if (!authed) {
|
||||
return;
|
||||
}
|
||||
const eventCollection = await this.stateService.getEventCollection({ userId: userId });
|
||||
if (eventCollection == null || eventCollection.length === 0) {
|
||||
return;
|
||||
}
|
||||
const request = eventCollection.map((e) => {
|
||||
const req = new EventRequest();
|
||||
req.type = e.type;
|
||||
req.cipherId = e.cipherId;
|
||||
req.date = e.date;
|
||||
req.organizationId = e.organizationId;
|
||||
return req;
|
||||
});
|
||||
try {
|
||||
await this.apiService.postEventsCollect(request);
|
||||
this.clearEvents(userId);
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private async clearEvents(userId?: string): Promise<any> {
|
||||
await this.stateService.setEventCollection(null, { userId: userId });
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as papa from "papaparse";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { ApiService } from "../abstractions/api.service";
|
||||
import { CipherService } from "../abstractions/cipher.service";
|
||||
@@ -116,7 +115,7 @@ export class ExportService implements ExportServiceAbstraction {
|
||||
const promises = [];
|
||||
|
||||
promises.push(
|
||||
firstValueFrom(this.folderService.folderViews$).then((folders) => {
|
||||
this.folderService.getAllDecryptedFromState().then((folders) => {
|
||||
decFolders = folders;
|
||||
})
|
||||
);
|
||||
@@ -192,7 +191,7 @@ export class ExportService implements ExportServiceAbstraction {
|
||||
const promises = [];
|
||||
|
||||
promises.push(
|
||||
firstValueFrom(this.folderService.folders$).then((f) => {
|
||||
this.folderService.getAllFromState().then((f) => {
|
||||
folders = f;
|
||||
})
|
||||
);
|
||||
|
||||
@@ -64,6 +64,18 @@ export class FolderService implements InternalFolderServiceAbstraction {
|
||||
return folders.find((folder) => folder.id === id);
|
||||
}
|
||||
|
||||
async getAllFromState(): Promise<Folder[]> {
|
||||
const folders = await this.stateService.getEncryptedFolders();
|
||||
const response: Folder[] = [];
|
||||
for (const id in folders) {
|
||||
// eslint-disable-next-line
|
||||
if (folders.hasOwnProperty(id)) {
|
||||
response.push(new Folder(folders[id]));
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated For the CLI only
|
||||
* @param id id of the folder
|
||||
|
||||
@@ -7,6 +7,7 @@ export class I18nService implements I18nServiceAbstraction {
|
||||
locale$: Observable<string> = this._locale.asObservable();
|
||||
// First locale is the default (English)
|
||||
supportedTranslationLocales: string[] = ["en"];
|
||||
defaultLocale = "en";
|
||||
translationLocale: string;
|
||||
collator: Intl.Collator;
|
||||
localeNames = new Map<string, string>([
|
||||
@@ -106,14 +107,14 @@ export class I18nService implements I18nServiceAbstraction {
|
||||
this.translationLocale = this.translationLocale.slice(0, 2);
|
||||
|
||||
if (this.supportedTranslationLocales.indexOf(this.translationLocale) === -1) {
|
||||
this.translationLocale = this.supportedTranslationLocales[0];
|
||||
this.translationLocale = this.defaultLocale;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.localesDirectory != null) {
|
||||
await this.loadMessages(this.translationLocale, this.localeMessages);
|
||||
if (this.translationLocale !== this.supportedTranslationLocales[0]) {
|
||||
await this.loadMessages(this.supportedTranslationLocales[0], this.defaultMessages);
|
||||
if (this.translationLocale !== this.defaultLocale) {
|
||||
await this.loadMessages(this.defaultLocale, this.defaultMessages);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,7 +123,7 @@ export class I18nService implements I18nServiceAbstraction {
|
||||
return this.translate(id, p1, p2, p3);
|
||||
}
|
||||
|
||||
translate(id: string, p1?: string, p2?: string, p3?: string): string {
|
||||
translate(id: string, p1?: string | number, p2?: string | number, p3?: string | number): string {
|
||||
let result: string;
|
||||
// eslint-disable-next-line
|
||||
if (this.localeMessages.hasOwnProperty(id) && this.localeMessages[id]) {
|
||||
@@ -136,13 +137,13 @@ export class I18nService implements I18nServiceAbstraction {
|
||||
|
||||
if (result !== "") {
|
||||
if (p1 != null) {
|
||||
result = result.split("__$1__").join(p1);
|
||||
result = result.split("__$1__").join(p1.toString());
|
||||
}
|
||||
if (p2 != null) {
|
||||
result = result.split("__$2__").join(p2);
|
||||
result = result.split("__$2__").join(p2.toString());
|
||||
}
|
||||
if (p3 != null) {
|
||||
result = result.split("__$3__").join(p3);
|
||||
result = result.split("__$3__").join(p3.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,10 +28,10 @@ import { CodebookCsvImporter } from "../importers/codebook-csv-importer";
|
||||
import { DashlaneCsvImporter } from "../importers/dashlane/dashlane-csv-importer";
|
||||
import { DashlaneJsonImporter } from "../importers/dashlane/dashlane-json-importer";
|
||||
import { EncryptrCsvImporter } from "../importers/encryptr-csv-importer";
|
||||
import { EnpassCsvImporter } from "../importers/enpass-csv-importer";
|
||||
import { EnpassJsonImporter } from "../importers/enpass-json-importer";
|
||||
import { EnpassCsvImporter } from "../importers/enpass/enpass-csv-importer";
|
||||
import { EnpassJsonImporter } from "../importers/enpass/enpass-json-importer";
|
||||
import { FirefoxCsvImporter } from "../importers/firefox-csv-importer";
|
||||
import { FSecureFskImporter } from "../importers/fsecure-fsk-importer";
|
||||
import { FSecureFskImporter } from "../importers/fsecure/fsecure-fsk-importer";
|
||||
import { GnomeJsonImporter } from "../importers/gnome-json-importer";
|
||||
import { ImportError } from "../importers/import-error";
|
||||
import { Importer } from "../importers/importer";
|
||||
@@ -51,6 +51,7 @@ import { OnePasswordMacCsvImporter } from "../importers/onepassword/onepassword-
|
||||
import { OnePasswordWinCsvImporter } from "../importers/onepassword/onepassword-win-csv-importer";
|
||||
import { PadlockCsvImporter } from "../importers/padlock-csv-importer";
|
||||
import { PassKeepCsvImporter } from "../importers/passkeep-csv-importer";
|
||||
import { PasskyJsonImporter } from "../importers/passky/passky-json-importer";
|
||||
import { PassmanJsonImporter } from "../importers/passman-json-importer";
|
||||
import { PasspackCsvImporter } from "../importers/passpack-csv-importer";
|
||||
import { PasswordAgentCsvImporter } from "../importers/passwordagent-csv-importer";
|
||||
@@ -279,6 +280,8 @@ export class ImportService implements ImportServiceAbstraction {
|
||||
return new YotiCsvImporter();
|
||||
case "nordpasscsv":
|
||||
return new NordPassCsvImporter();
|
||||
case "passkyjson":
|
||||
return new PasskyJsonImporter();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { LoginService as LoginServiceAbstraction } from "../abstractions/login.service";
|
||||
import { StateService } from "../abstractions/state.service";
|
||||
|
||||
export class LoginService implements LoginServiceAbstraction {
|
||||
private _email: string;
|
||||
private _rememberEmail: boolean;
|
||||
|
||||
constructor(private stateService: StateService) {}
|
||||
|
||||
getEmail() {
|
||||
return this._email;
|
||||
}
|
||||
@@ -24,4 +27,9 @@ export class LoginService implements LoginServiceAbstraction {
|
||||
this._email = null;
|
||||
this._rememberEmail = null;
|
||||
}
|
||||
|
||||
async saveEmailSettings() {
|
||||
await this.stateService.setRememberedEmail(this._rememberEmail ? this._email : null);
|
||||
this.clearValues();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import {
|
||||
AbstractStorageService,
|
||||
MemoryStorageServiceInterface,
|
||||
} from "../abstractions/storage.service";
|
||||
import { AbstractMemoryStorageService } from "../abstractions/storage.service";
|
||||
|
||||
export class MemoryStorageService
|
||||
extends AbstractStorageService
|
||||
implements MemoryStorageServiceInterface
|
||||
{
|
||||
export class MemoryStorageService extends AbstractMemoryStorageService {
|
||||
private store = new Map<string, any>();
|
||||
|
||||
get<T>(key: string): Promise<T> {
|
||||
@@ -18,7 +12,7 @@ export class MemoryStorageService
|
||||
}
|
||||
|
||||
async has(key: string): Promise<boolean> {
|
||||
return this.get(key) != null;
|
||||
return (await this.get(key)) != null;
|
||||
}
|
||||
|
||||
save(key: string, obj: any): Promise<any> {
|
||||
@@ -33,4 +27,8 @@ export class MemoryStorageService
|
||||
this.store.delete(key);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
getBypassCache<T>(key: string): Promise<T> {
|
||||
return this.get<T>(key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,321 @@
|
||||
import { ApiService } from "../../abstractions/api.service";
|
||||
import { OrganizationUserService } from "../../abstractions/organization-user/organization-user.service";
|
||||
import {
|
||||
OrganizationUserAcceptRequest,
|
||||
OrganizationUserBulkConfirmRequest,
|
||||
OrganizationUserConfirmRequest,
|
||||
OrganizationUserInviteRequest,
|
||||
OrganizationUserResetPasswordEnrollmentRequest,
|
||||
OrganizationUserResetPasswordRequest,
|
||||
OrganizationUserUpdateGroupsRequest,
|
||||
OrganizationUserUpdateRequest,
|
||||
} from "../../abstractions/organization-user/requests";
|
||||
import {
|
||||
OrganizationUserBulkPublicKeyResponse,
|
||||
OrganizationUserBulkResponse,
|
||||
OrganizationUserDetailsResponse,
|
||||
OrganizationUserResetPasswordDetailsReponse,
|
||||
OrganizationUserUserDetailsResponse,
|
||||
} from "../../abstractions/organization-user/responses";
|
||||
import { ListResponse } from "../../models/response/list.response";
|
||||
|
||||
import { OrganizationUserBulkRequest } from "./requests";
|
||||
|
||||
export class OrganizationUserServiceImplementation implements OrganizationUserService {
|
||||
constructor(private apiService: ApiService) {}
|
||||
|
||||
async getOrganizationUser(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
options?: {
|
||||
includeGroups?: boolean;
|
||||
}
|
||||
): Promise<OrganizationUserDetailsResponse> {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (options?.includeGroups) {
|
||||
params.set("includeGroups", "true");
|
||||
}
|
||||
|
||||
const r = await this.apiService.send(
|
||||
"GET",
|
||||
`/organizations/${organizationId}/users/${id}?${params.toString()}`,
|
||||
null,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new OrganizationUserDetailsResponse(r);
|
||||
}
|
||||
|
||||
async getOrganizationUserGroups(organizationId: string, id: string): Promise<string[]> {
|
||||
const r = await this.apiService.send(
|
||||
"GET",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/groups",
|
||||
null,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return r;
|
||||
}
|
||||
|
||||
async getAllUsers(
|
||||
organizationId: string,
|
||||
options?: {
|
||||
includeCollections?: boolean;
|
||||
includeGroups?: boolean;
|
||||
}
|
||||
): Promise<ListResponse<OrganizationUserUserDetailsResponse>> {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (options?.includeCollections) {
|
||||
params.set("includeCollections", "true");
|
||||
}
|
||||
if (options?.includeGroups) {
|
||||
params.set("includeGroups", "true");
|
||||
}
|
||||
|
||||
const r = await this.apiService.send(
|
||||
"GET",
|
||||
`/organizations/${organizationId}/users?${params.toString()}`,
|
||||
null,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new ListResponse(r, OrganizationUserUserDetailsResponse);
|
||||
}
|
||||
|
||||
async getOrganizationUserResetPasswordDetails(
|
||||
organizationId: string,
|
||||
id: string
|
||||
): Promise<OrganizationUserResetPasswordDetailsReponse> {
|
||||
const r = await this.apiService.send(
|
||||
"GET",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/reset-password-details",
|
||||
null,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new OrganizationUserResetPasswordDetailsReponse(r);
|
||||
}
|
||||
|
||||
postOrganizationUserInvite(
|
||||
organizationId: string,
|
||||
request: OrganizationUserInviteRequest
|
||||
): Promise<void> {
|
||||
return this.apiService.send(
|
||||
"POST",
|
||||
"/organizations/" + organizationId + "/users/invite",
|
||||
request,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
postOrganizationUserReinvite(organizationId: string, id: string): Promise<any> {
|
||||
return this.apiService.send(
|
||||
"POST",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/reinvite",
|
||||
null,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
async postManyOrganizationUserReinvite(
|
||||
organizationId: string,
|
||||
ids: string[]
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>> {
|
||||
const r = await this.apiService.send(
|
||||
"POST",
|
||||
"/organizations/" + organizationId + "/users/reinvite",
|
||||
new OrganizationUserBulkRequest(ids),
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new ListResponse(r, OrganizationUserBulkResponse);
|
||||
}
|
||||
|
||||
postOrganizationUserAccept(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserAcceptRequest
|
||||
): Promise<void> {
|
||||
return this.apiService.send(
|
||||
"POST",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/accept",
|
||||
request,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
postOrganizationUserConfirm(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserConfirmRequest
|
||||
): Promise<void> {
|
||||
return this.apiService.send(
|
||||
"POST",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/confirm",
|
||||
request,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
async postOrganizationUsersPublicKey(
|
||||
organizationId: string,
|
||||
ids: string[]
|
||||
): Promise<ListResponse<OrganizationUserBulkPublicKeyResponse>> {
|
||||
const r = await this.apiService.send(
|
||||
"POST",
|
||||
"/organizations/" + organizationId + "/users/public-keys",
|
||||
new OrganizationUserBulkRequest(ids),
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new ListResponse(r, OrganizationUserBulkPublicKeyResponse);
|
||||
}
|
||||
|
||||
async postOrganizationUserBulkConfirm(
|
||||
organizationId: string,
|
||||
request: OrganizationUserBulkConfirmRequest
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>> {
|
||||
const r = await this.apiService.send(
|
||||
"POST",
|
||||
"/organizations/" + organizationId + "/users/confirm",
|
||||
request,
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new ListResponse(r, OrganizationUserBulkResponse);
|
||||
}
|
||||
|
||||
putOrganizationUser(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserUpdateRequest
|
||||
): Promise<void> {
|
||||
return this.apiService.send(
|
||||
"PUT",
|
||||
"/organizations/" + organizationId + "/users/" + id,
|
||||
request,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
putOrganizationUserGroups(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserUpdateGroupsRequest
|
||||
): Promise<void> {
|
||||
return this.apiService.send(
|
||||
"PUT",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/groups",
|
||||
request,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
putOrganizationUserResetPasswordEnrollment(
|
||||
organizationId: string,
|
||||
userId: string,
|
||||
request: OrganizationUserResetPasswordEnrollmentRequest
|
||||
): Promise<void> {
|
||||
return this.apiService.send(
|
||||
"PUT",
|
||||
"/organizations/" + organizationId + "/users/" + userId + "/reset-password-enrollment",
|
||||
request,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
putOrganizationUserResetPassword(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserResetPasswordRequest
|
||||
): Promise<void> {
|
||||
return this.apiService.send(
|
||||
"PUT",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/reset-password",
|
||||
request,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
deleteOrganizationUser(organizationId: string, id: string): Promise<any> {
|
||||
return this.apiService.send(
|
||||
"DELETE",
|
||||
"/organizations/" + organizationId + "/users/" + id,
|
||||
null,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
async deleteManyOrganizationUsers(
|
||||
organizationId: string,
|
||||
ids: string[]
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>> {
|
||||
const r = await this.apiService.send(
|
||||
"DELETE",
|
||||
"/organizations/" + organizationId + "/users",
|
||||
new OrganizationUserBulkRequest(ids),
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new ListResponse(r, OrganizationUserBulkResponse);
|
||||
}
|
||||
|
||||
revokeOrganizationUser(organizationId: string, id: string): Promise<void> {
|
||||
return this.apiService.send(
|
||||
"PUT",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/revoke",
|
||||
null,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
async revokeManyOrganizationUsers(
|
||||
organizationId: string,
|
||||
ids: string[]
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>> {
|
||||
const r = await this.apiService.send(
|
||||
"PUT",
|
||||
"/organizations/" + organizationId + "/users/revoke",
|
||||
new OrganizationUserBulkRequest(ids),
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new ListResponse(r, OrganizationUserBulkResponse);
|
||||
}
|
||||
|
||||
restoreOrganizationUser(organizationId: string, id: string): Promise<void> {
|
||||
return this.apiService.send(
|
||||
"PUT",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/restore",
|
||||
null,
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
async restoreManyOrganizationUsers(
|
||||
organizationId: string,
|
||||
ids: string[]
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>> {
|
||||
const r = await this.apiService.send(
|
||||
"PUT",
|
||||
"/organizations/" + organizationId + "/users/restore",
|
||||
new OrganizationUserBulkRequest(ids),
|
||||
true,
|
||||
true
|
||||
);
|
||||
return new ListResponse(r, OrganizationUserBulkResponse);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./organization-user-bulk.request";
|
||||
@@ -1,21 +1,16 @@
|
||||
import { BehaviorSubject, concatMap, filter } from "rxjs";
|
||||
import { BehaviorSubject, concatMap } from "rxjs";
|
||||
|
||||
import { OrganizationService as OrganizationServiceAbstraction } from "../../abstractions/organization/organization.service.abstraction";
|
||||
import { InternalOrganizationService as InternalOrganizationServiceAbstraction } from "../../abstractions/organization/organization.service.abstraction";
|
||||
import { StateService } from "../../abstractions/state.service";
|
||||
import { SyncNotifierService } from "../../abstractions/sync/syncNotifier.service.abstraction";
|
||||
import { OrganizationData } from "../../models/data/organization.data";
|
||||
import { Organization } from "../../models/domain/organization";
|
||||
import { isSuccessfullyCompleted } from "../../types/syncEventArgs";
|
||||
|
||||
export class OrganizationService implements OrganizationServiceAbstraction {
|
||||
private _organizations = new BehaviorSubject<Organization[]>([]);
|
||||
export class OrganizationService implements InternalOrganizationServiceAbstraction {
|
||||
protected _organizations = new BehaviorSubject<Organization[]>([]);
|
||||
|
||||
organizations$ = this._organizations.asObservable();
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private syncNotifierService: SyncNotifierService
|
||||
) {
|
||||
constructor(private stateService: StateService) {
|
||||
this.stateService.activeAccountUnlocked$
|
||||
.pipe(
|
||||
concatMap(async (unlocked) => {
|
||||
@@ -29,28 +24,6 @@ export class OrganizationService implements OrganizationServiceAbstraction {
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
this.syncNotifierService.sync$
|
||||
.pipe(
|
||||
filter(isSuccessfullyCompleted),
|
||||
concatMap(async ({ data }) => {
|
||||
const { profile } = data;
|
||||
const organizations: { [id: string]: OrganizationData } = {};
|
||||
profile.organizations.forEach((o) => {
|
||||
organizations[o.id] = new OrganizationData(o);
|
||||
});
|
||||
|
||||
profile.providerOrganizations.forEach((o) => {
|
||||
if (organizations[o.id] == null) {
|
||||
organizations[o.id] = new OrganizationData(o);
|
||||
organizations[o.id].isProviderUser = true;
|
||||
}
|
||||
});
|
||||
|
||||
await this.updateStateAndObservables(organizations);
|
||||
})
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
async getAll(userId?: string): Promise<Organization[]> {
|
||||
@@ -78,7 +51,7 @@ export class OrganizationService implements OrganizationServiceAbstraction {
|
||||
|
||||
organizations[organization.id] = organization;
|
||||
|
||||
await this.updateStateAndObservables(organizations);
|
||||
await this.replace(organizations);
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<void> {
|
||||
@@ -92,7 +65,7 @@ export class OrganizationService implements OrganizationServiceAbstraction {
|
||||
}
|
||||
|
||||
delete organizations[id];
|
||||
await this.updateStateAndObservables(organizations);
|
||||
await this.replace(organizations);
|
||||
}
|
||||
|
||||
get(id: string): Organization {
|
||||
@@ -121,9 +94,9 @@ export class OrganizationService implements OrganizationServiceAbstraction {
|
||||
return organizations.find((organization) => organization.identifier === identifier);
|
||||
}
|
||||
|
||||
private async updateStateAndObservables(organizationsMap: { [id: string]: OrganizationData }) {
|
||||
await this.stateService.setOrganizations(organizationsMap);
|
||||
this.updateObservables(organizationsMap);
|
||||
async replace(organizations: { [id: string]: OrganizationData }) {
|
||||
await this.stateService.setOrganizations(organizations);
|
||||
this.updateObservables(organizations);
|
||||
}
|
||||
|
||||
private updateObservables(organizationsMap: { [id: string]: OrganizationData }) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user