mirror of
https://github.com/bitwarden/browser
synced 2025-12-31 07:33:23 +00:00
[Provider] Add initial support for providers (#399)
This commit is contained in:
@@ -55,6 +55,15 @@ import { PasswordVerificationRequest } from '../models/request/passwordVerificat
|
||||
import { PaymentRequest } from '../models/request/paymentRequest';
|
||||
import { PolicyRequest } from '../models/request/policyRequest';
|
||||
import { PreloginRequest } from '../models/request/preloginRequest';
|
||||
import { ProviderAddOrganizationRequest } from '../models/request/provider/providerAddOrganizationRequest';
|
||||
import { ProviderSetupRequest } from '../models/request/provider/providerSetupRequest';
|
||||
import { ProviderUpdateRequest } from '../models/request/provider/providerUpdateRequest';
|
||||
import { ProviderUserAcceptRequest } from '../models/request/provider/providerUserAcceptRequest';
|
||||
import { ProviderUserBulkConfirmRequest } from '../models/request/provider/providerUserBulkConfirmRequest';
|
||||
import { ProviderUserBulkRequest } from '../models/request/provider/providerUserBulkRequest';
|
||||
import { ProviderUserConfirmRequest } from '../models/request/provider/providerUserConfirmRequest';
|
||||
import { ProviderUserInviteRequest } from '../models/request/provider/providerUserInviteRequest';
|
||||
import { ProviderUserUpdateRequest } from '../models/request/provider/providerUserUpdateRequest';
|
||||
import { RegisterRequest } from '../models/request/registerRequest';
|
||||
import { SeatRequest } from '../models/request/seatRequest';
|
||||
import { SelectionReadOnlyRequest } from '../models/request/selectionReadOnlyRequest';
|
||||
@@ -81,6 +90,7 @@ import { VerifyDeleteRecoverRequest } from '../models/request/verifyDeleteRecove
|
||||
import { VerifyEmailRequest } from '../models/request/verifyEmailRequest';
|
||||
|
||||
import { Utils } from '../misc/utils';
|
||||
|
||||
import { ApiKeyResponse } from '../models/response/apiKeyResponse';
|
||||
import { AttachmentResponse } from '../models/response/attachmentResponse';
|
||||
import { AttachmentUploadDataResponse } from '../models/response/attachmentUploadDataResponse';
|
||||
@@ -123,6 +133,14 @@ import { PlanResponse } from '../models/response/planResponse';
|
||||
import { PolicyResponse } from '../models/response/policyResponse';
|
||||
import { PreloginResponse } from '../models/response/preloginResponse';
|
||||
import { ProfileResponse } from '../models/response/profileResponse';
|
||||
import { ProviderOrganizationOrganizationDetailsResponse } from '../models/response/provider/providerOrganizationResponse';
|
||||
import { ProviderResponse } from '../models/response/provider/providerResponse';
|
||||
import { ProviderUserBulkPublicKeyResponse } from '../models/response/provider/providerUserBulkPublicKeyResponse';
|
||||
import { ProviderUserBulkResponse } from '../models/response/provider/providerUserBulkResponse';
|
||||
import {
|
||||
ProviderUserResponse,
|
||||
ProviderUserUserDetailsResponse
|
||||
} from '../models/response/provider/providerUserResponse';
|
||||
import { SelectionReadOnlyResponse } from '../models/response/selectionReadOnlyResponse';
|
||||
import { SendAccessResponse } from '../models/response/sendAccessResponse';
|
||||
import { SendFileDownloadDataResponse } from '../models/response/sendFileDownloadDataResponse';
|
||||
@@ -1230,6 +1248,101 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
return new OrganizationKeysResponse(r);
|
||||
}
|
||||
|
||||
// Provider APIs
|
||||
|
||||
async postProviderSetup(id: string, request: ProviderSetupRequest) {
|
||||
const r = await this.send('POST', '/providers/' + id + '/setup', request, true, true);
|
||||
return new ProviderResponse(r);
|
||||
}
|
||||
|
||||
async getProvider(id: string) {
|
||||
const r = await this.send('GET', '/providers/' + id, null, true, true);
|
||||
return new ProviderResponse(r);
|
||||
}
|
||||
|
||||
async putProvider(id: string, request: ProviderUpdateRequest) {
|
||||
const r = await this.send('PUT', '/providers/' + id, request, true, true);
|
||||
return new ProviderResponse(r);
|
||||
}
|
||||
|
||||
// Provider User APIs
|
||||
|
||||
async getProviderUsers(providerId: string): Promise<ListResponse<ProviderUserUserDetailsResponse>> {
|
||||
const r = await this.send('GET', '/providers/' + providerId + '/users', null, true, true);
|
||||
return new ListResponse(r, ProviderUserUserDetailsResponse);
|
||||
}
|
||||
|
||||
async getProviderUser(providerId: string, id: string): Promise<ProviderUserResponse> {
|
||||
const r = await this.send('GET', '/providers/' + providerId + '/users/' + id, null, true, true);
|
||||
return new ProviderUserResponse(r);
|
||||
}
|
||||
|
||||
postProviderUserInvite(providerId: string, request: ProviderUserInviteRequest): Promise<any> {
|
||||
return this.send('POST', '/providers/' + providerId + '/users/invite', request, true, false);
|
||||
}
|
||||
|
||||
postProviderUserReinvite(providerId: string, id: string): Promise<any> {
|
||||
return this.send('POST', '/providers/' + providerId + '/users/' + id + '/reinvite', null, true, false);
|
||||
}
|
||||
|
||||
async postManyProviderUserReinvite(providerId: string, request: ProviderUserBulkRequest): Promise<ListResponse<ProviderUserBulkResponse>> {
|
||||
const r = await this.send('POST', '/providers/' + providerId + '/users/reinvite', request, true, true);
|
||||
return new ListResponse(r, ProviderUserBulkResponse);
|
||||
}
|
||||
|
||||
async postProviderUserBulkConfirm(providerId: string, request: ProviderUserBulkConfirmRequest): Promise<ListResponse<ProviderUserBulkResponse>> {
|
||||
const r = await this.send('POST', '/providers/' + providerId + '/users/confirm', request, true, true);
|
||||
return new ListResponse(r, ProviderUserBulkResponse);
|
||||
}
|
||||
|
||||
async deleteManyProviderUsers(providerId: string, request: ProviderUserBulkRequest): Promise<ListResponse<ProviderUserBulkResponse>> {
|
||||
const r = await this.send('DELETE', '/providers/' + providerId + '/users', request, true, true);
|
||||
return new ListResponse(r, ProviderUserBulkResponse);
|
||||
}
|
||||
|
||||
postProviderUserAccept(providerId: string, id: string, request: ProviderUserAcceptRequest): Promise<any> {
|
||||
return this.send('POST', '/providers/' + providerId + '/users/' + id + '/accept', request, true, false);
|
||||
}
|
||||
|
||||
postProviderUserConfirm(providerId: string, id: string, request: ProviderUserConfirmRequest): Promise<any> {
|
||||
return this.send('POST', '/providers/' + providerId + '/users/' + id + '/confirm',
|
||||
request, true, false);
|
||||
}
|
||||
|
||||
async postProviderUsersPublicKey(providerId: string, request: ProviderUserBulkRequest): Promise<ListResponse<ProviderUserBulkPublicKeyResponse>> {
|
||||
const r = await this.send('POST', '/providers/' + providerId + '/users/public-keys', request, true, true);
|
||||
return new ListResponse(r, ProviderUserBulkPublicKeyResponse);
|
||||
}
|
||||
|
||||
|
||||
putProviderUser(providerId: string, id: string, request: ProviderUserUpdateRequest): Promise<any> {
|
||||
return this.send('PUT', '/providers/' + providerId + '/users/' + id, request, true, false);
|
||||
}
|
||||
|
||||
deleteProviderUser(providerId: string, id: string): Promise<any> {
|
||||
return this.send('DELETE', '/providers/' + providerId + '/users/' + id, null, true, false);
|
||||
}
|
||||
|
||||
// Provider Organization APIs
|
||||
|
||||
async getProviderClients(providerId: string): Promise<ListResponse<ProviderOrganizationOrganizationDetailsResponse>> {
|
||||
const r = await this.send('GET', '/providers/' + providerId + '/organizations', null, true, true);
|
||||
return new ListResponse(r, ProviderOrganizationOrganizationDetailsResponse);
|
||||
}
|
||||
|
||||
postProviderAddOrganization(providerId: string, request: ProviderAddOrganizationRequest): Promise<any> {
|
||||
return this.send('POST', '/providers/' + providerId + '/organizations/add', request, true, false);
|
||||
}
|
||||
|
||||
async postProviderCreateOrganization(providerId: string, request: OrganizationCreateRequest): Promise<OrganizationResponse> {
|
||||
const r = await this.send('POST', '/providers/' + providerId + '/organizations', request, true, true);
|
||||
return new OrganizationResponse(r);
|
||||
}
|
||||
|
||||
deleteProviderOrganization(providerId: string, id: string): Promise<any> {
|
||||
return this.send('DELETE', '/providers/' + providerId + '/organizations/' + id, null, true, false);
|
||||
}
|
||||
|
||||
// Event APIs
|
||||
|
||||
async getEvents(start: string, end: string, token: string): Promise<ListResponse<EventResponse>> {
|
||||
@@ -1259,6 +1372,19 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
return new ListResponse(r, EventResponse);
|
||||
}
|
||||
|
||||
async getEventsProvider(id: string, start: string, end: string, token: string): Promise<ListResponse<EventResponse>> {
|
||||
const r = await this.send('GET', this.addEventParameters('/providers/' + id + '/events', start, end, token), null, true, true);
|
||||
return new ListResponse(r, EventResponse);
|
||||
}
|
||||
|
||||
async getEventsProviderUser(providerId: string, id: string,
|
||||
start: string, end: string, token: string): Promise<ListResponse<EventResponse>> {
|
||||
const r = await this.send('GET',
|
||||
this.addEventParameters('/providers/' + providerId + '/users/' + id + '/events', start, end, token),
|
||||
null, true, true);
|
||||
return new ListResponse(r, EventResponse);
|
||||
}
|
||||
|
||||
async postEventsCollect(request: EventRequest[]): Promise<any> {
|
||||
const authHeader = await this.getActiveBearerToken();
|
||||
const headers = new Headers({
|
||||
|
||||
@@ -24,10 +24,13 @@ import { ConstantsService } from './constants.service';
|
||||
import { sequentialize } from '../misc/sequentialize';
|
||||
import { Utils } from '../misc/utils';
|
||||
import { EEFLongWordList } from '../misc/wordlist';
|
||||
import { ProfileProviderOrganizationResponse } from '../models/response/profileProviderOrganizationResponse';
|
||||
import { ProfileProviderResponse } from '../models/response/profileProviderResponse';
|
||||
|
||||
export const Keys = {
|
||||
key: 'key', // Master Key
|
||||
encOrgKeys: 'encOrgKeys',
|
||||
encProviderKeys: 'encProviderKeys',
|
||||
encPrivateKey: 'encPrivateKey',
|
||||
encKey: 'encKey', // Generated Symmetric Key
|
||||
keyHash: 'keyHash',
|
||||
@@ -41,6 +44,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
private publicKey: ArrayBuffer;
|
||||
private privateKey: ArrayBuffer;
|
||||
private orgKeys: Map<string, SymmetricCryptoKey>;
|
||||
private providerKeys: Map<string, SymmetricCryptoKey>;
|
||||
|
||||
constructor(private storageService: StorageService, protected secureStorageService: StorageService,
|
||||
private cryptoFunctionService: CryptoFunctionService, protected platformUtilService: PlatformUtilsService,
|
||||
@@ -76,16 +80,33 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
this.privateKey = null;
|
||||
}
|
||||
|
||||
setOrgKeys(orgs: ProfileOrganizationResponse[]): Promise<{}> {
|
||||
async setOrgKeys(orgs: ProfileOrganizationResponse[], providerOrgs: ProfileProviderOrganizationResponse[]): Promise<{}> {
|
||||
const orgKeys: any = {};
|
||||
orgs.forEach(org => {
|
||||
orgKeys[org.id] = org.key;
|
||||
});
|
||||
|
||||
for (const providerOrg of providerOrgs) {
|
||||
// Convert provider encrypted keys to user encrypted.
|
||||
const providerKey = await this.getProviderKey(providerOrg.providerId);
|
||||
const decValue = await this.decryptToBytes(new EncString(providerOrg.key), providerKey);
|
||||
orgKeys[providerOrg.id] = await (await this.rsaEncrypt(decValue)).encryptedString;
|
||||
}
|
||||
|
||||
this.orgKeys = null;
|
||||
return this.storageService.save(Keys.encOrgKeys, orgKeys);
|
||||
}
|
||||
|
||||
setProviderKeys(providers: ProfileProviderResponse[]): Promise<{}> {
|
||||
const providerKeys: any = {};
|
||||
providers.forEach(provider => {
|
||||
providerKeys[provider.id] = provider.key;
|
||||
});
|
||||
|
||||
this.providerKeys = null;
|
||||
return this.storageService.save(Keys.encProviderKeys, providerKeys);
|
||||
}
|
||||
|
||||
async getKey(keySuffix?: KeySuffixOptions): Promise<SymmetricCryptoKey> {
|
||||
if (this.key != null) {
|
||||
return this.key;
|
||||
@@ -270,6 +291,50 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return orgKeys.get(orgId);
|
||||
}
|
||||
|
||||
@sequentialize(() => 'getProviderKeys')
|
||||
async getProviderKeys(): Promise<Map<string, SymmetricCryptoKey>> {
|
||||
if (this.providerKeys != null && this.providerKeys.size > 0) {
|
||||
return this.providerKeys;
|
||||
}
|
||||
|
||||
const encProviderKeys = await this.storageService.get<any>(Keys.encProviderKeys);
|
||||
if (encProviderKeys == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const providerKeys: Map<string, SymmetricCryptoKey> = new Map<string, SymmetricCryptoKey>();
|
||||
let setKey = false;
|
||||
|
||||
for (const orgId in encProviderKeys) {
|
||||
if (!encProviderKeys.hasOwnProperty(orgId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const decValue = await this.rsaDecrypt(encProviderKeys[orgId]);
|
||||
providerKeys.set(orgId, new SymmetricCryptoKey(decValue));
|
||||
setKey = true;
|
||||
}
|
||||
|
||||
if (setKey) {
|
||||
this.providerKeys = providerKeys;
|
||||
}
|
||||
|
||||
return this.providerKeys;
|
||||
}
|
||||
|
||||
async getProviderKey(providerId: string): Promise<SymmetricCryptoKey> {
|
||||
if (providerId == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const providerKeys = await this.getProviderKeys();
|
||||
if (providerKeys == null || !providerKeys.has(providerId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return providerKeys.get(providerId);
|
||||
}
|
||||
|
||||
async hasKey(): Promise<boolean> {
|
||||
return this.hasKeyInMemory() || await this.hasKeyStored('auto') || await this.hasKeyStored('biometric');
|
||||
}
|
||||
@@ -329,6 +394,14 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
return this.storageService.remove(Keys.encOrgKeys);
|
||||
}
|
||||
|
||||
clearProviderKeys(memoryOnly?: boolean): Promise<any> {
|
||||
this.providerKeys = null;
|
||||
if (memoryOnly) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return this.storageService.remove(Keys.encOrgKeys);
|
||||
}
|
||||
|
||||
clearPinProtectedKey(): Promise<any> {
|
||||
return this.storageService.remove(ConstantsService.pinProtectedKey);
|
||||
}
|
||||
@@ -337,6 +410,7 @@ export class CryptoService implements CryptoServiceAbstraction {
|
||||
await this.clearKey();
|
||||
await this.clearKeyHash();
|
||||
await this.clearOrgKeys();
|
||||
await this.clearProviderKeys();
|
||||
await this.clearEncKey();
|
||||
await this.clearKeyPair();
|
||||
await this.clearPinProtectedKey();
|
||||
|
||||
@@ -16,6 +16,7 @@ import { CollectionData } from '../models/data/collectionData';
|
||||
import { FolderData } from '../models/data/folderData';
|
||||
import { OrganizationData } from '../models/data/organizationData';
|
||||
import { PolicyData } from '../models/data/policyData';
|
||||
import { ProviderData } from '../models/data/providerData';
|
||||
import { SendData } from '../models/data/sendData';
|
||||
|
||||
import { CipherResponse } from '../models/response/cipherResponse';
|
||||
@@ -286,7 +287,8 @@ export class SyncService implements SyncServiceAbstraction {
|
||||
|
||||
await this.cryptoService.setEncKey(response.key);
|
||||
await this.cryptoService.setEncPrivateKey(response.privateKey);
|
||||
await this.cryptoService.setOrgKeys(response.organizations);
|
||||
await this.cryptoService.setProviderKeys(response.providers);
|
||||
await this.cryptoService.setOrgKeys(response.organizations, response.providerOrganizations);
|
||||
await this.userService.setSecurityStamp(response.securityStamp);
|
||||
await this.userService.setEmailVerified(response.emailVerified);
|
||||
|
||||
@@ -294,7 +296,22 @@ export class SyncService implements SyncServiceAbstraction {
|
||||
response.organizations.forEach(o => {
|
||||
organizations[o.id] = new OrganizationData(o);
|
||||
});
|
||||
return await this.userService.replaceOrganizations(organizations);
|
||||
|
||||
const providers: { [id: string]: ProviderData; } = {};
|
||||
response.providers.forEach(p => {
|
||||
providers[p.id] = new ProviderData(p);
|
||||
});
|
||||
|
||||
response.providerOrganizations.forEach(o => {
|
||||
if (organizations[o.id] == null) {
|
||||
organizations[o.id] = new OrganizationData(o);
|
||||
organizations[o.id].isProviderUser = true;
|
||||
}
|
||||
});
|
||||
return Promise.all([
|
||||
this.userService.replaceOrganizations(organizations),
|
||||
this.userService.replaceProviders(providers),
|
||||
]);
|
||||
}
|
||||
|
||||
private async syncFolders(userId: string, response: FolderResponse[]) {
|
||||
|
||||
@@ -6,6 +6,8 @@ import { OrganizationData } from '../models/data/organizationData';
|
||||
import { Organization } from '../models/domain/organization';
|
||||
|
||||
import { KdfType } from '../enums/kdfType';
|
||||
import { ProviderData } from '../models/data/providerData';
|
||||
import { Provider } from '../models/domain/provider';
|
||||
|
||||
const Keys = {
|
||||
userId: 'userId',
|
||||
@@ -14,6 +16,7 @@ const Keys = {
|
||||
kdf: 'kdf',
|
||||
kdfIterations: 'kdfIterations',
|
||||
organizationsPrefix: 'organizations_',
|
||||
providersPrefix: 'providers_',
|
||||
emailVerified: 'emailVerified',
|
||||
};
|
||||
|
||||
@@ -100,6 +103,7 @@ export class UserService implements UserServiceAbstraction {
|
||||
await this.storageService.remove(Keys.kdf);
|
||||
await this.storageService.remove(Keys.kdfIterations);
|
||||
await this.clearOrganizations(userId);
|
||||
await this.clearProviders(userId);
|
||||
|
||||
this.userId = this.email = this.stamp = null;
|
||||
this.kdf = null;
|
||||
@@ -153,7 +157,7 @@ export class UserService implements UserServiceAbstraction {
|
||||
Keys.organizationsPrefix + userId);
|
||||
const response: Organization[] = [];
|
||||
for (const id in organizations) {
|
||||
if (organizations.hasOwnProperty(id)) {
|
||||
if (organizations.hasOwnProperty(id) && !organizations[id].isProviderUser) {
|
||||
response.push(new Organization(organizations[id]));
|
||||
}
|
||||
}
|
||||
@@ -168,4 +172,37 @@ export class UserService implements UserServiceAbstraction {
|
||||
async clearOrganizations(userId: string): Promise<any> {
|
||||
await this.storageService.remove(Keys.organizationsPrefix + userId);
|
||||
}
|
||||
|
||||
async getProvider(id: string): Promise<Provider> {
|
||||
const userId = await this.getUserId();
|
||||
const providers = await this.storageService.get<{ [id: string]: ProviderData; }>(
|
||||
Keys.providersPrefix + userId);
|
||||
if (providers == null || !providers.hasOwnProperty(id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Provider(providers[id]);
|
||||
}
|
||||
|
||||
async getAllProviders(): Promise<Provider[]> {
|
||||
const userId = await this.getUserId();
|
||||
const providers = await this.storageService.get<{ [id: string]: ProviderData; }>(
|
||||
Keys.providersPrefix + userId);
|
||||
const response: Provider[] = [];
|
||||
for (const id in providers) {
|
||||
if (providers.hasOwnProperty(id)) {
|
||||
response.push(new Provider(providers[id]));
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
async replaceProviders(providers: { [id: string]: ProviderData; }): Promise<any> {
|
||||
const userId = await this.getUserId();
|
||||
await this.storageService.save(Keys.providersPrefix + userId, providers);
|
||||
}
|
||||
|
||||
async clearProviders(userId: string): Promise<any> {
|
||||
await this.storageService.remove(Keys.providersPrefix + userId);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user