diff --git a/src/abstractions/api.service.ts b/src/abstractions/api.service.ts index 84e810bd2d..79503d49ff 100644 --- a/src/abstractions/api.service.ts +++ b/src/abstractions/api.service.ts @@ -31,6 +31,7 @@ import { ImportOrganizationCiphersRequest } from '../models/request/importOrgani import { KdfRequest } from '../models/request/kdfRequest'; import { KeysRequest } from '../models/request/keysRequest'; import { OrganizationCreateRequest } from '../models/request/organizationCreateRequest'; +import { OrganizationImportRequest } from '../models/request/organizationImportRequest'; import { OrganizationKeysRequest } from '../models/request/organizationKeysRequest'; import { OrganizationTaxInfoUpdateRequest } from '../models/request/organizationTaxInfoUpdateRequest'; import { OrganizationUpdateRequest } from '../models/request/organizationUpdateRequest'; @@ -296,6 +297,7 @@ export abstract class ApiService { getSync: () => Promise; postImportDirectory: (organizationId: string, request: ImportDirectoryRequest) => Promise; + postPublicImportDirectory: (request: OrganizationImportRequest) => Promise; getSettingsDomains: () => Promise; putSettingsDomains: (request: UpdateDomainsRequest) => Promise; diff --git a/src/abstractions/apiKey.service.ts b/src/abstractions/apiKey.service.ts new file mode 100644 index 0000000000..cf62765880 --- /dev/null +++ b/src/abstractions/apiKey.service.ts @@ -0,0 +1,7 @@ +export abstract class ApiKeyService { + setInformation: (clientId: string) => Promise; + clear: () => Promise; + getEntityType: () => Promise; + getEntityId: () => Promise; + isAuthenticated: () => Promise; +} diff --git a/src/models/request/organizationImportGroupRequest.ts b/src/models/request/organizationImportGroupRequest.ts new file mode 100644 index 0000000000..0cbb0faf77 --- /dev/null +++ b/src/models/request/organizationImportGroupRequest.ts @@ -0,0 +1,19 @@ +import { ImportDirectoryRequestGroup } from './importDirectoryRequestGroup'; + +export class OrganizationImportGroupRequest { + name: string; + externalId: string; + memberExternalIds: string[]; + + constructor(model: Required | ImportDirectoryRequestGroup) { + this.name = model.name; + this.externalId = model.externalId; + + if (model instanceof ImportDirectoryRequestGroup) { + this.memberExternalIds = model.users; + } + else { + this.memberExternalIds = model.memberExternalIds; + } + } +} diff --git a/src/models/request/organizationImportMemberRequest.ts b/src/models/request/organizationImportMemberRequest.ts new file mode 100644 index 0000000000..78b0949856 --- /dev/null +++ b/src/models/request/organizationImportMemberRequest.ts @@ -0,0 +1,13 @@ +import { ImportDirectoryRequestUser } from './importDirectoryRequestUser'; + +export class OrganizationImportMemberRequest { + email: string; + externalId: string; + deleted: boolean; + + constructor(model: Required | ImportDirectoryRequestUser) { + this.email = model.email; + this.externalId = model.externalId; + this.deleted = model.deleted; + } +} diff --git a/src/models/request/organizationImportRequest.ts b/src/models/request/organizationImportRequest.ts new file mode 100644 index 0000000000..6b1bc30442 --- /dev/null +++ b/src/models/request/organizationImportRequest.ts @@ -0,0 +1,25 @@ +import { ImportDirectoryRequest } from './importDirectoryRequest'; +import { OrganizationImportGroupRequest } from './organizationImportGroupRequest'; +import { OrganizationImportMemberRequest } from './organizationImportMemberRequest'; + +export class OrganizationImportRequest { + groups: OrganizationImportGroupRequest[] = []; + members: OrganizationImportMemberRequest[] = []; + overwriteExisting: boolean = false; + largeImport: boolean = false; + + constructor(model: { + groups: Required[], + users: Required[], overwriteExisting: boolean, largeImport: boolean; + } | ImportDirectoryRequest) { + if (model instanceof ImportDirectoryRequest) { + this.groups = model.groups.map(g => new OrganizationImportGroupRequest(g)); + this.members = model.users.map(u => new OrganizationImportMemberRequest(u)); + } else { + this.groups = model.groups.map(g => new OrganizationImportGroupRequest(g)); + this.members = model.users.map(u => new OrganizationImportMemberRequest(u)); + } + this.overwriteExisting = model.overwriteExisting; + this.largeImport = model.largeImport; + } +} diff --git a/src/models/request/tokenRequest.ts b/src/models/request/tokenRequest.ts index 1180b08120..877c663eb9 100644 --- a/src/models/request/tokenRequest.ts +++ b/src/models/request/tokenRequest.ts @@ -41,7 +41,7 @@ export class TokenRequest { }; if (this.clientSecret != null) { - obj.scope = 'api'; + obj.scope = clientId.startsWith('organization') ? 'api.organization' : 'api'; obj.grant_type = 'client_credentials'; obj.client_secret = this.clientSecret; } else if (this.masterPasswordHash != null && this.email != null) { diff --git a/src/services/api.service.ts b/src/services/api.service.ts index 0147a72018..55c0d65296 100644 --- a/src/services/api.service.ts +++ b/src/services/api.service.ts @@ -35,6 +35,7 @@ import { ImportOrganizationCiphersRequest } from '../models/request/importOrgani import { KdfRequest } from '../models/request/kdfRequest'; import { KeysRequest } from '../models/request/keysRequest'; import { OrganizationCreateRequest } from '../models/request/organizationCreateRequest'; +import { OrganizationImportRequest } from '../models/request/organizationImportRequest'; import { OrganizationKeysRequest } from '../models/request/organizationKeysRequest'; import { OrganizationTaxInfoUpdateRequest } from '../models/request/organizationTaxInfoUpdateRequest'; import { OrganizationUpdateRequest } from '../models/request/organizationUpdateRequest'; @@ -868,6 +869,10 @@ export class ApiService implements ApiServiceAbstraction { return this.send('POST', '/organizations/' + organizationId + '/import', request, true, false); } + async postPublicImportDirectory(request: OrganizationImportRequest): Promise { + return this.send('POST', '/public/organization/import', request, true, false); + } + async getTaxRates(): Promise> { const r = await this.send('GET', '/plans/sales-tax-rates/', null, true, true); return new ListResponse(r, TaxRateResponse); diff --git a/src/services/apiKey.service.ts b/src/services/apiKey.service.ts new file mode 100644 index 0000000000..1e09ac7e41 --- /dev/null +++ b/src/services/apiKey.service.ts @@ -0,0 +1,67 @@ +import { ApiKeyService as ApiKeyServiceAbstraction } from '../abstractions/apiKey.service'; +import { StorageService } from '../abstractions/storage.service'; +import { TokenService } from '../abstractions/token.service'; + +import { Utils } from '../misc/utils'; + +const Keys = { + clientId: 'clientId', + entityType: 'entityType', + entityId: 'entityId', +}; + + +export class ApiKeyService implements ApiKeyServiceAbstraction { + private clientId: string; + private entityType: string; + private entityId: string; + + constructor(private tokenService: TokenService, private storageService: StorageService) { } + + async setInformation(clientId: string) { + this.clientId = clientId; + const idParts = clientId.split('.'); + + if (idParts.length !== 2 || !Utils.isGuid(idParts[1])) { + throw Error('Invalid clientId'); + } + this.entityType = idParts[0]; + this.entityId = idParts[1]; + + await this.storageService.save(Keys.clientId, this.clientId); + await this.storageService.save(Keys.entityId, this.entityId); + await this.storageService.save(Keys.entityType, this.entityType); + } + + async getEntityType(): Promise { + if (this.entityType == null) { + this.entityType = await this.storageService.get(Keys.entityType); + } + return this.entityType; + } + + async getEntityId(): Promise { + if (this.entityId == null) { + this.entityId = await this.storageService.get(Keys.entityId); + } + return this.entityId; + } + + async clear(): Promise { + await this.storageService.remove(Keys.clientId); + await this.storageService.remove(Keys.entityId); + await this.storageService.remove(Keys.entityType); + + this.clientId = this.entityId = this.entityType = null; + } + + async isAuthenticated(): Promise { + const token = await this.tokenService.getToken(); + if (token == null) { + return false; + } + + const entityId = await this.getEntityId(); + return entityId != null; + } +} diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index 10048d0c69..2a60670bdc 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -88,10 +88,10 @@ export class AuthService implements AuthServiceAbstraction { private key: SymmetricCryptoKey; - constructor(private cryptoService: CryptoService, private apiService: ApiService, - private userService: UserService, private tokenService: TokenService, - private appIdService: AppIdService, private i18nService: I18nService, - private platformUtilsService: PlatformUtilsService, private messagingService: MessagingService, + constructor(private cryptoService: CryptoService, protected apiService: ApiService, + private userService: UserService, protected tokenService: TokenService, + protected appIdService: AppIdService, private i18nService: I18nService, + protected platformUtilsService: PlatformUtilsService, private messagingService: MessagingService, private vaultTimeoutService: VaultTimeoutService, private logService: LogService, private setCryptoKeys = true) { } diff --git a/src/services/crypto.service.ts b/src/services/crypto.service.ts index e930cf9dcc..26b96baf0c 100644 --- a/src/services/crypto.service.ts +++ b/src/services/crypto.service.ts @@ -294,15 +294,13 @@ export class CryptoService implements CryptoServiceAbstraction { return this.storageService.remove(ConstantsService.pinProtectedKey); } - clearKeys(): Promise { - return Promise.all([ - this.clearKey(), - this.clearKeyHash(), - this.clearOrgKeys(), - this.clearEncKey(), - this.clearKeyPair(), - this.clearPinProtectedKey(), - ]); + async clearKeys(): Promise { + await this.clearEncKey(); + await this.clearKeyHash(); + await this.clearOrgKeys(); + await this.clearEncKey(); + await this.clearKeyPair(); + await this.clearPinProtectedKey(); } async toggleKey(): Promise { diff --git a/src/services/token.service.ts b/src/services/token.service.ts index 709556f7b6..be1cf40c15 100644 --- a/src/services/token.service.ts +++ b/src/services/token.service.ts @@ -19,11 +19,9 @@ export class TokenService implements TokenServiceAbstraction { constructor(private storageService: StorageService) { } - setTokens(accessToken: string, refreshToken: string): Promise { - return Promise.all([ - this.setToken(accessToken), - this.setRefreshToken(refreshToken), - ]); + async setTokens(accessToken: string, refreshToken: string): Promise { + await this.setToken(accessToken); + await this.setRefreshToken(refreshToken); } async setToken(token: string): Promise { @@ -96,15 +94,13 @@ export class TokenService implements TokenServiceAbstraction { return this.storageService.remove(Keys.twoFactorTokenPrefix + email); } - clearToken(): Promise { + async clearToken(): Promise { this.token = null; this.decodedToken = null; this.refreshToken = null; - return Promise.all([ - this.storageService.remove(Keys.accessToken), - this.storageService.remove(Keys.refreshToken), - ]); + await this.storageService.remove(Keys.accessToken); + await this.storageService.remove(Keys.refreshToken); } // jwthelper methods diff --git a/src/services/user.service.ts b/src/services/user.service.ts index 99a81428eb..b62494e9d4 100644 --- a/src/services/user.service.ts +++ b/src/services/user.service.ts @@ -27,18 +27,16 @@ export class UserService implements UserServiceAbstraction { constructor(private tokenService: TokenService, private storageService: StorageService) { } - setInformation(userId: string, email: string, kdf: KdfType, kdfIterations: number): Promise { + async setInformation(userId: string, email: string, kdf: KdfType, kdfIterations: number): Promise { this.email = email; this.userId = userId; this.kdf = kdf; this.kdfIterations = kdfIterations; - return Promise.all([ - this.storageService.save(Keys.userEmail, email), - this.storageService.save(Keys.userId, userId), - this.storageService.save(Keys.kdf, kdf), - this.storageService.save(Keys.kdfIterations, kdfIterations), - ]); + await this.storageService.save(Keys.userEmail, email); + await this.storageService.save(Keys.userId, userId); + await this.storageService.save(Keys.kdf, kdf); + await this.storageService.save(Keys.kdfIterations, kdfIterations); } setSecurityStamp(stamp: string): Promise { @@ -96,14 +94,12 @@ export class UserService implements UserServiceAbstraction { async clear(): Promise { const userId = await this.getUserId(); - await Promise.all([ - this.storageService.remove(Keys.userId), - this.storageService.remove(Keys.userEmail), - this.storageService.remove(Keys.stamp), - this.storageService.remove(Keys.kdf), - this.storageService.remove(Keys.kdfIterations), - this.clearOrganizations(userId), - ]); + await this.storageService.remove(Keys.userId); + await this.storageService.remove(Keys.userEmail); + await this.storageService.remove(Keys.stamp); + await this.storageService.remove(Keys.kdf); + await this.storageService.remove(Keys.kdfIterations); + await this.clearOrganizations(userId); this.userId = this.email = this.stamp = null; this.kdf = null; diff --git a/src/services/vaultTimeout.service.ts b/src/services/vaultTimeout.service.ts index 15b9c67460..0039690fc9 100644 --- a/src/services/vaultTimeout.service.ts +++ b/src/services/vaultTimeout.service.ts @@ -113,12 +113,10 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction { } } - await Promise.all([ - this.cryptoService.clearKey(), - this.cryptoService.clearOrgKeys(true), - this.cryptoService.clearKeyPair(true), - this.cryptoService.clearEncKey(true), - ]); + await this.cryptoService.clearKey(); + await this.cryptoService.clearOrgKeys(true); + await this.cryptoService.clearKeyPair(true); + await this.cryptoService.clearEncKey(true); this.folderService.clearCache(); this.cipherService.clearCache();