mirror of
https://github.com/bitwarden/jslib
synced 2025-12-22 11:13:17 +00:00
Merge branch 'master' into AccountService
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
Directive,
|
Directive,
|
||||||
Input,
|
Input,
|
||||||
|
OnChanges,
|
||||||
|
SimpleChanges,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -18,13 +20,17 @@ import { CipherType } from 'jslib-common/enums/cipherType';
|
|||||||
import { EventType } from 'jslib-common/enums/eventType';
|
import { EventType } from 'jslib-common/enums/eventType';
|
||||||
import { FieldType } from 'jslib-common/enums/fieldType';
|
import { FieldType } from 'jslib-common/enums/fieldType';
|
||||||
|
|
||||||
|
import { Utils } from 'jslib-common/misc/utils';
|
||||||
|
|
||||||
@Directive()
|
@Directive()
|
||||||
export class AddEditCustomFieldsComponent {
|
export class AddEditCustomFieldsComponent implements OnChanges {
|
||||||
@Input() cipher: CipherView;
|
@Input() cipher: CipherView;
|
||||||
|
@Input() thisCipherType: CipherType;
|
||||||
@Input() editMode: boolean;
|
@Input() editMode: boolean;
|
||||||
|
|
||||||
addFieldType: FieldType = FieldType.Text;
|
addFieldType: FieldType = FieldType.Text;
|
||||||
addFieldTypeOptions: any[];
|
addFieldTypeOptions: any[];
|
||||||
|
addFieldLinkedTypeOption: any;
|
||||||
linkedFieldOptions: any[] = [];
|
linkedFieldOptions: any[] = [];
|
||||||
|
|
||||||
cipherType = CipherType;
|
cipherType = CipherType;
|
||||||
@@ -37,6 +43,17 @@ export class AddEditCustomFieldsComponent {
|
|||||||
{ name: i18nService.t('cfTypeHidden'), value: FieldType.Hidden },
|
{ name: i18nService.t('cfTypeHidden'), value: FieldType.Hidden },
|
||||||
{ name: i18nService.t('cfTypeBoolean'), value: FieldType.Boolean },
|
{ name: i18nService.t('cfTypeBoolean'), value: FieldType.Boolean },
|
||||||
];
|
];
|
||||||
|
this.addFieldLinkedTypeOption = { name: this.i18nService.t('cfTypeLinked'), value: FieldType.Linked };
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
|
if (changes.thisCipherType != null) {
|
||||||
|
this.setLinkedFieldOptions();
|
||||||
|
|
||||||
|
if (!changes.thisCipherType.firstChange) {
|
||||||
|
this.resetCipherLinkedFields();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addField() {
|
addField() {
|
||||||
@@ -48,6 +65,10 @@ export class AddEditCustomFieldsComponent {
|
|||||||
f.type = this.addFieldType;
|
f.type = this.addFieldType;
|
||||||
f.newField = true;
|
f.newField = true;
|
||||||
|
|
||||||
|
if (f.type === FieldType.Linked) {
|
||||||
|
f.linkedId = this.linkedFieldOptions[0].value;
|
||||||
|
}
|
||||||
|
|
||||||
this.cipher.fields.push(f);
|
this.cipher.fields.push(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,4 +94,31 @@ export class AddEditCustomFieldsComponent {
|
|||||||
drop(event: CdkDragDrop<string[]>) {
|
drop(event: CdkDragDrop<string[]>) {
|
||||||
moveItemInArray(this.cipher.fields, event.previousIndex, event.currentIndex);
|
moveItemInArray(this.cipher.fields, event.previousIndex, event.currentIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setLinkedFieldOptions() {
|
||||||
|
if (this.cipher.linkedFieldOptions == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options: any = [];
|
||||||
|
this.cipher.linkedFieldOptions.forEach((linkedFieldOption, id) =>
|
||||||
|
options.push({ name: this.i18nService.t(linkedFieldOption.i18nKey), value: id }));
|
||||||
|
this.linkedFieldOptions = options.sort(Utils.getSortFunction(this.i18nService, 'name'));
|
||||||
|
}
|
||||||
|
|
||||||
|
private resetCipherLinkedFields() {
|
||||||
|
if (this.cipher.fields == null || this.cipher.fields.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete any Linked custom fields if the item type does not support them
|
||||||
|
if (this.cipher.linkedFieldOptions == null) {
|
||||||
|
this.cipher.fields = this.cipher.fields.filter(f => f.type !== FieldType.Linked);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cipher.fields
|
||||||
|
.filter(f => f.type = FieldType.Linked)
|
||||||
|
.forEach(f => f.linkedId = this.linkedFieldOptions[0].value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
|||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||||
import { OrganizationService } from 'jslib-common/abstractions/organization.service';
|
import { OrganizationService } from 'jslib-common/abstractions/organization.service';
|
||||||
|
import { PasswordRepromptService } from 'jslib-common/abstractions/passwordReprompt.service';
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||||
@@ -80,6 +81,7 @@ export class AddEditComponent implements OnInit {
|
|||||||
currentDate = new Date();
|
currentDate = new Date();
|
||||||
allowPersonal = true;
|
allowPersonal = true;
|
||||||
reprompt: boolean = false;
|
reprompt: boolean = false;
|
||||||
|
canUseReprompt: boolean = true;
|
||||||
|
|
||||||
protected writeableCollections: CollectionView[];
|
protected writeableCollections: CollectionView[];
|
||||||
private previousCipherId: string;
|
private previousCipherId: string;
|
||||||
@@ -89,7 +91,8 @@ export class AddEditComponent implements OnInit {
|
|||||||
protected auditService: AuditService, protected stateService: StateService,
|
protected auditService: AuditService, protected stateService: StateService,
|
||||||
protected collectionService: CollectionService, protected messagingService: MessagingService,
|
protected collectionService: CollectionService, protected messagingService: MessagingService,
|
||||||
protected eventService: EventService, protected policyService: PolicyService,
|
protected eventService: EventService, protected policyService: PolicyService,
|
||||||
private logService: LogService, private organizationService: OrganizationService) {
|
private logService: LogService, protected passwordRepromptService: PasswordRepromptService,
|
||||||
|
private organizationService: OrganizationService) {
|
||||||
this.typeOptions = [
|
this.typeOptions = [
|
||||||
{ name: i18nService.t('typeLogin'), value: CipherType.Login },
|
{ name: i18nService.t('typeLogin'), value: CipherType.Login },
|
||||||
{ name: i18nService.t('typeCard'), value: CipherType.Card },
|
{ name: i18nService.t('typeCard'), value: CipherType.Card },
|
||||||
@@ -169,6 +172,8 @@ export class AddEditComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.writeableCollections = await this.loadCollections();
|
this.writeableCollections = await this.loadCollections();
|
||||||
|
|
||||||
|
this.canUseReprompt = await this.passwordRepromptService.enabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
import { FormBuilder } from '@angular/forms';
|
||||||
|
|
||||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||||
import { EventService } from 'jslib-common/abstractions/event.service';
|
import { EventService } from 'jslib-common/abstractions/event.service';
|
||||||
@@ -12,6 +13,7 @@ import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
|||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||||
|
import { UserVerificationService } from 'jslib-common/abstractions/userVerification.service';
|
||||||
|
|
||||||
import { EventType } from 'jslib-common/enums/eventType';
|
import { EventType } from 'jslib-common/enums/eventType';
|
||||||
import { PolicyType } from 'jslib-common/enums/policyType';
|
import { PolicyType } from 'jslib-common/enums/policyType';
|
||||||
@@ -21,15 +23,24 @@ export class ExportComponent implements OnInit {
|
|||||||
@Output() onSaved = new EventEmitter();
|
@Output() onSaved = new EventEmitter();
|
||||||
|
|
||||||
formPromise: Promise<string>;
|
formPromise: Promise<string>;
|
||||||
masterPassword: string;
|
|
||||||
format: 'json' | 'encrypted_json' | 'csv' = 'json';
|
|
||||||
showPassword = false;
|
|
||||||
disabledByPolicy: boolean = false;
|
disabledByPolicy: boolean = false;
|
||||||
|
|
||||||
|
exportForm = this.fb.group({
|
||||||
|
format: ['json'],
|
||||||
|
secret: [''],
|
||||||
|
});
|
||||||
|
|
||||||
|
formatOptions = [
|
||||||
|
{ name: '.json', value: 'json' },
|
||||||
|
{ name: '.csv', value: 'csv' },
|
||||||
|
{ name: '.json (Encrypted)', value: 'encrypted_json' },
|
||||||
|
];
|
||||||
|
|
||||||
constructor(protected cryptoService: CryptoService, protected i18nService: I18nService,
|
constructor(protected cryptoService: CryptoService, protected i18nService: I18nService,
|
||||||
protected platformUtilsService: PlatformUtilsService, protected exportService: ExportService,
|
protected platformUtilsService: PlatformUtilsService, protected exportService: ExportService,
|
||||||
protected eventService: EventService, private policyService: PolicyService, protected win: Window,
|
protected eventService: EventService, private policyService: PolicyService, protected win: Window,
|
||||||
private logService: LogService) { }
|
private logService: LogService, private userVerificationService: UserVerificationService,
|
||||||
|
private fb: FormBuilder) { }
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
await this.checkExportDisabled();
|
await this.checkExportDisabled();
|
||||||
@@ -37,6 +48,9 @@ export class ExportComponent implements OnInit {
|
|||||||
|
|
||||||
async checkExportDisabled() {
|
async checkExportDisabled() {
|
||||||
this.disabledByPolicy = await this.policyService.policyAppliesToUser(PolicyType.DisablePersonalVaultExport);
|
this.disabledByPolicy = await this.policyService.policyAppliesToUser(PolicyType.DisablePersonalVaultExport);
|
||||||
|
if (this.disabledByPolicy) {
|
||||||
|
this.exportForm.disable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get encryptedFormat() {
|
get encryptedFormat() {
|
||||||
@@ -49,31 +63,25 @@ export class ExportComponent implements OnInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.masterPassword == null || this.masterPassword === '') {
|
|
||||||
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'),
|
|
||||||
this.i18nService.t('invalidMasterPassword'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const acceptedWarning = await this.warningDialog();
|
const acceptedWarning = await this.warningDialog();
|
||||||
if (!acceptedWarning) {
|
if (!acceptedWarning) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const passwordValid = await this.cryptoService.compareAndUpdateKeyHash(this.masterPassword, null);
|
const secret = this.exportForm.get('secret').value;
|
||||||
if (passwordValid) {
|
if (!await this.userVerificationService.verifyUser(secret)) {
|
||||||
try {
|
return;
|
||||||
this.formPromise = this.getExportData();
|
}
|
||||||
const data = await this.formPromise;
|
|
||||||
this.downloadFile(data);
|
try {
|
||||||
this.saved();
|
this.formPromise = this.getExportData();
|
||||||
await this.collectEvent();
|
const data = await this.formPromise;
|
||||||
} catch (e) {
|
this.downloadFile(data);
|
||||||
this.logService.error(e);
|
this.saved();
|
||||||
}
|
await this.collectEvent();
|
||||||
} else {
|
this.exportForm.get('secret').setValue('');
|
||||||
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'),
|
} catch (e) {
|
||||||
this.i18nService.t('invalidMasterPassword'));
|
this.logService.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,11 +101,6 @@ export class ExportComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
togglePassword() {
|
|
||||||
this.showPassword = !this.showPassword;
|
|
||||||
document.getElementById('masterPassword').focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected saved() {
|
protected saved() {
|
||||||
this.onSaved.emit();
|
this.onSaved.emit();
|
||||||
}
|
}
|
||||||
@@ -123,6 +126,10 @@ export class ExportComponent implements OnInit {
|
|||||||
await this.eventService.collect(EventType.User_ClientExportedVault);
|
await this.eventService.collect(EventType.User_ClientExportedVault);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get format() {
|
||||||
|
return this.exportForm.get('format').value;
|
||||||
|
}
|
||||||
|
|
||||||
private downloadFile(csv: string): void {
|
private downloadFile(csv: string): void {
|
||||||
const fileName = this.getFileName();
|
const fileName = this.getFileName();
|
||||||
this.platformUtilsService.saveFile(this.win, csv, { type: 'text/plain' }, fileName);
|
this.platformUtilsService.saveFile(this.win, csv, { type: 'text/plain' }, fileName);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { ApiService } from 'jslib-common/abstractions/api.service';
|
|||||||
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||||
|
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
|
||||||
import { LogService } from 'jslib-common/abstractions/log.service';
|
import { LogService } from 'jslib-common/abstractions/log.service';
|
||||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||||
@@ -14,7 +15,7 @@ import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.serv
|
|||||||
import { EncString } from 'jslib-common/models/domain/encString';
|
import { EncString } from 'jslib-common/models/domain/encString';
|
||||||
import { SymmetricCryptoKey } from 'jslib-common/models/domain/symmetricCryptoKey';
|
import { SymmetricCryptoKey } from 'jslib-common/models/domain/symmetricCryptoKey';
|
||||||
|
|
||||||
import { PasswordVerificationRequest } from 'jslib-common/models/request/passwordVerificationRequest';
|
import { SecretVerificationRequest } from 'jslib-common/models/request/secretVerificationRequest';
|
||||||
|
|
||||||
import { Utils } from 'jslib-common/misc/utils';
|
import { Utils } from 'jslib-common/misc/utils';
|
||||||
|
|
||||||
@@ -44,7 +45,8 @@ export class LockComponent implements OnInit {
|
|||||||
protected platformUtilsService: PlatformUtilsService, protected messagingService: MessagingService,
|
protected platformUtilsService: PlatformUtilsService, protected messagingService: MessagingService,
|
||||||
protected cryptoService: CryptoService, protected vaultTimeoutService: VaultTimeoutService,
|
protected cryptoService: CryptoService, protected vaultTimeoutService: VaultTimeoutService,
|
||||||
protected environmentService: EnvironmentService, protected stateService: StateService,
|
protected environmentService: EnvironmentService, protected stateService: StateService,
|
||||||
protected apiService: ApiService, private logService: LogService) { }
|
protected apiService: ApiService, private logService: LogService,
|
||||||
|
private keyConnectorService: KeyConnectorService) { }
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.stateService.accounts.subscribe(async _accounts => {
|
this.stateService.accounts.subscribe(async _accounts => {
|
||||||
@@ -56,6 +58,11 @@ export class LockComponent implements OnInit {
|
|||||||
this.biometricText = await this.stateService.getBiometricText();
|
this.biometricText = await this.stateService.getBiometricText();
|
||||||
this.email = await this.stateService.getEmail();
|
this.email = await this.stateService.getEmail();
|
||||||
|
|
||||||
|
// Users with key connector and without biometric or pin has no MP to unlock using
|
||||||
|
if (await this.keyConnectorService.getUsesKeyConnector() && !(this.biometricLock || this.pinLock)) {
|
||||||
|
await this.vaultTimeoutService.logOut();
|
||||||
|
}
|
||||||
|
|
||||||
const webVaultUrl = this.environmentService.getWebVaultUrl();
|
const webVaultUrl = this.environmentService.getWebVaultUrl();
|
||||||
const vaultUrl = webVaultUrl === 'https://vault.bitwarden.com' ? 'https://bitwarden.com' : webVaultUrl;
|
const vaultUrl = webVaultUrl === 'https://vault.bitwarden.com' ? 'https://bitwarden.com' : webVaultUrl;
|
||||||
this.webVaultHostname = Utils.getHostname(vaultUrl);
|
this.webVaultHostname = Utils.getHostname(vaultUrl);
|
||||||
@@ -117,7 +124,7 @@ export class LockComponent implements OnInit {
|
|||||||
if (storedKeyHash != null) {
|
if (storedKeyHash != null) {
|
||||||
passwordValid = await this.cryptoService.compareAndUpdateKeyHash(this.masterPassword, key);
|
passwordValid = await this.cryptoService.compareAndUpdateKeyHash(this.masterPassword, key);
|
||||||
} else {
|
} else {
|
||||||
const request = new PasswordVerificationRequest();
|
const request = new SecretVerificationRequest();
|
||||||
const serverKeyHash = await this.cryptoService.hashPassword(this.masterPassword, key,
|
const serverKeyHash = await this.cryptoService.hashPassword(this.masterPassword, key,
|
||||||
HashPurpose.ServerAuthorization);
|
HashPurpose.ServerAuthorization);
|
||||||
request.masterPasswordHash = serverKeyHash;
|
request.masterPasswordHash = serverKeyHash;
|
||||||
|
|||||||
77
angular/src/components/remove-password.component.ts
Normal file
77
angular/src/components/remove-password.component.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import {
|
||||||
|
Directive,
|
||||||
|
OnInit,
|
||||||
|
} from '@angular/core';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||||
|
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||||
|
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
|
||||||
|
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||||
|
import { StorageService } from 'jslib-common/abstractions/storage.service';
|
||||||
|
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||||
|
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||||
|
|
||||||
|
import { ConstantsService } from 'jslib-common/services/constants.service';
|
||||||
|
|
||||||
|
import { Organization } from 'jslib-common/models/domain/organization';
|
||||||
|
|
||||||
|
@Directive()
|
||||||
|
export class RemovePasswordComponent implements OnInit {
|
||||||
|
|
||||||
|
actionPromise: Promise<any>;
|
||||||
|
continuing: boolean = false;
|
||||||
|
leaving: boolean = false;
|
||||||
|
|
||||||
|
loading: boolean = true;
|
||||||
|
organization: Organization;
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
constructor(private router: Router, private userService: UserService,
|
||||||
|
private apiService: ApiService, private syncService: SyncService,
|
||||||
|
private platformUtilsService: PlatformUtilsService, private i18nService: I18nService,
|
||||||
|
private keyConnectorService: KeyConnectorService, private storageService: StorageService) { }
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
this.organization = await this.keyConnectorService.getManagingOrganization();
|
||||||
|
this.email = await this.userService.getEmail();
|
||||||
|
await this.syncService.fullSync(false);
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async convert() {
|
||||||
|
this.continuing = true;
|
||||||
|
this.actionPromise = this.keyConnectorService.migrateUser();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.actionPromise;
|
||||||
|
this.platformUtilsService.showToast('success', null, this.i18nService.t('removedMasterPassword'));
|
||||||
|
await this.keyConnectorService.removeConvertAccountRequired();
|
||||||
|
this.router.navigate(['']);
|
||||||
|
} catch (e) {
|
||||||
|
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'), e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async leave() {
|
||||||
|
const confirmed = await this.platformUtilsService.showDialog(
|
||||||
|
this.i18nService.t('leaveOrganizationConfirmation'), this.organization.name,
|
||||||
|
this.i18nService.t('yes'), this.i18nService.t('no'), 'warning');
|
||||||
|
if (!confirmed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.leaving = true;
|
||||||
|
this.actionPromise = this.apiService.postLeaveOrganization(this.organization.id).then(() => {
|
||||||
|
return this.syncService.fullSync(true);
|
||||||
|
});
|
||||||
|
await this.actionPromise;
|
||||||
|
this.platformUtilsService.showToast('success', null, this.i18nService.t('leftOrganization'));
|
||||||
|
await this.keyConnectorService.removeConvertAccountRequired();
|
||||||
|
this.router.navigate(['']);
|
||||||
|
} catch (e) {
|
||||||
|
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -57,7 +57,7 @@ export class SsoComponent {
|
|||||||
await this.stateService.setSsoCodeVerifier(null);
|
await this.stateService.setSsoCodeVerifier(null);
|
||||||
await this.stateService.setSsoState(null);
|
await this.stateService.setSsoState(null);
|
||||||
if (qParams.code != null && codeVerifier != null && state != null && this.checkState(state, qParams.state)) {
|
if (qParams.code != null && codeVerifier != null && state != null && this.checkState(state, qParams.state)) {
|
||||||
await this.logIn(qParams.code, codeVerifier, this.getOrgIdentiferFromState(qParams.state));
|
await this.logIn(qParams.code, codeVerifier, this.getOrgIdentifierFromState(qParams.state));
|
||||||
}
|
}
|
||||||
} else if (qParams.clientId != null && qParams.redirectUri != null && qParams.state != null &&
|
} else if (qParams.clientId != null && qParams.redirectUri != null && qParams.state != null &&
|
||||||
qParams.codeChallenge != null) {
|
qParams.codeChallenge != null) {
|
||||||
@@ -180,14 +180,14 @@ export class SsoComponent {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logService.error(e);
|
this.logService.error(e);
|
||||||
if (e.message === 'Unable to reach crypto agent') {
|
if (e.message === 'Unable to reach key connector') {
|
||||||
this.platformUtilsService.showToast('error', null, this.i18nService.t('ssoCryptoAgentUnavailable'));
|
this.platformUtilsService.showToast('error', null, this.i18nService.t('ssoKeyConnectorUnavailable'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.loggingIn = false;
|
this.loggingIn = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getOrgIdentiferFromState(state: string): string {
|
private getOrgIdentifierFromState(state: string): string {
|
||||||
if (state === null || state === undefined) {
|
if (state === null || state === undefined) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,7 +208,9 @@ export class TwoFactorComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const request = new TwoFactorEmailRequest(this.authService.email, this.authService.masterPasswordHash);
|
const request = new TwoFactorEmailRequest();
|
||||||
|
request.email = this.authService.email;
|
||||||
|
request.masterPasswordHash = this.authService.masterPasswordHash;
|
||||||
this.emailPromise = this.apiService.postTwoFactorEmail(request);
|
this.emailPromise = this.apiService.postTwoFactorEmail(request);
|
||||||
await this.emailPromise;
|
await this.emailPromise;
|
||||||
if (doToast) {
|
if (doToast) {
|
||||||
|
|||||||
18
angular/src/components/verify-master-password.component.html
Normal file
18
angular/src/components/verify-master-password.component.html
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<ng-container *ngIf="!usesKeyConnector">
|
||||||
|
<label for="masterPassword">{{'masterPass' | i18n}}</label>
|
||||||
|
<input id="masterPassword" type="password" name="MasterPasswordHash" class="form-control"
|
||||||
|
[formControl]="secret" required appAutofocus appInputVerbatim>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="usesKeyConnector">
|
||||||
|
<div class="form-group">
|
||||||
|
<button type="button" class="btn btn-primary" (click)="requestOTP()" [disabled]="disableRequestOTP">
|
||||||
|
{{'requestVerificationCode' | i18n}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="verificationCode">{{'verificationCode' | i18n}}</label>
|
||||||
|
<input id="verificationCode" type="input" name="verificationCode" class="form-control"
|
||||||
|
[formControl]="secret" required appAutofocus appInputVerbatim>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
81
angular/src/components/verify-master-password.component.ts
Normal file
81
angular/src/components/verify-master-password.component.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
} from '@angular/core';
|
||||||
|
import {
|
||||||
|
ControlValueAccessor,
|
||||||
|
FormControl,
|
||||||
|
NG_VALUE_ACCESSOR,
|
||||||
|
} from '@angular/forms';
|
||||||
|
|
||||||
|
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||||
|
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
|
||||||
|
|
||||||
|
import { VerificationType } from 'jslib-common/enums/verificationType';
|
||||||
|
|
||||||
|
import { Verification } from 'jslib-common/types/verification';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-verify-master-password',
|
||||||
|
templateUrl: 'verify-master-password.component.html',
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
multi: true,
|
||||||
|
useExisting: VerifyMasterPasswordComponent,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class VerifyMasterPasswordComponent implements ControlValueAccessor, OnInit {
|
||||||
|
usesKeyConnector: boolean = false;
|
||||||
|
disableRequestOTP: boolean = false;
|
||||||
|
|
||||||
|
secret = new FormControl('');
|
||||||
|
|
||||||
|
private onChange: (value: Verification) => void;
|
||||||
|
|
||||||
|
constructor(private keyConnectorService: KeyConnectorService, private apiService: ApiService) { }
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
this.usesKeyConnector = await this.keyConnectorService.getUsesKeyConnector();
|
||||||
|
|
||||||
|
this.secret.valueChanges.subscribe(secret => {
|
||||||
|
if (this.onChange == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onChange({
|
||||||
|
type: this.usesKeyConnector ? VerificationType.OTP : VerificationType.MasterPassword,
|
||||||
|
secret: secret,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async requestOTP() {
|
||||||
|
if (this.usesKeyConnector) {
|
||||||
|
this.disableRequestOTP = true;
|
||||||
|
await this.apiService.postAccountRequestOTP();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeValue(obj: any): void {
|
||||||
|
this.secret.setValue(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: any): void {
|
||||||
|
this.onChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(fn: any): void {
|
||||||
|
// Not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisabledState?(isDisabled: boolean): void {
|
||||||
|
this.disableRequestOTP = isDisabled;
|
||||||
|
if (isDisabled) {
|
||||||
|
this.secret.disable();
|
||||||
|
} else {
|
||||||
|
this.secret.enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
|
|
||||||
|
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
|
||||||
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
import { MessagingService } from 'jslib-common/abstractions/messaging.service';
|
||||||
import { StateService } from 'jslib-common/abstractions/state.service';
|
import { StateService } from 'jslib-common/abstractions/state.service';
|
||||||
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.service';
|
||||||
@@ -13,7 +14,8 @@ import { VaultTimeoutService } from 'jslib-common/abstractions/vaultTimeout.serv
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthGuardService implements CanActivate {
|
export class AuthGuardService implements CanActivate {
|
||||||
constructor(private vaultTimeoutService: VaultTimeoutService, private router: Router,
|
constructor(private vaultTimeoutService: VaultTimeoutService, private router: Router,
|
||||||
private messagingService: MessagingService, private stateService: StateService) { }
|
private messagingService: MessagingService, private keyConnectorService: KeyConnectorService,
|
||||||
|
private stateService: StateService) { }
|
||||||
|
|
||||||
async canActivate(route: ActivatedRouteSnapshot, routerState: RouterStateSnapshot) {
|
async canActivate(route: ActivatedRouteSnapshot, routerState: RouterStateSnapshot) {
|
||||||
const isAuthed = await this.stateService.getIsAuthenticated();
|
const isAuthed = await this.stateService.getIsAuthenticated();
|
||||||
@@ -31,6 +33,11 @@ export class AuthGuardService implements CanActivate {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!routerState.url.includes('remove-password') && await this.keyConnectorService.getConvertAccountRequired()) {
|
||||||
|
this.router.navigate(['/remove-password']);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
|
||||||
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from 'jslib-common/abstractions/passwordReprompt.service';
|
import { PasswordRepromptService as PasswordRepromptServiceAbstraction } from 'jslib-common/abstractions/passwordReprompt.service';
|
||||||
|
|
||||||
import { PasswordRepromptComponent } from '../components/password-reprompt.component';
|
import { PasswordRepromptComponent } from '../components/password-reprompt.component';
|
||||||
@@ -9,13 +10,17 @@ import { ModalService } from './modal.service';
|
|||||||
export class PasswordRepromptService implements PasswordRepromptServiceAbstraction {
|
export class PasswordRepromptService implements PasswordRepromptServiceAbstraction {
|
||||||
protected component = PasswordRepromptComponent;
|
protected component = PasswordRepromptComponent;
|
||||||
|
|
||||||
constructor(private modalService: ModalService) { }
|
constructor(private modalService: ModalService, private keyConnectorService: KeyConnectorService) { }
|
||||||
|
|
||||||
protectedFields() {
|
protectedFields() {
|
||||||
return ['TOTP', 'Password', 'H_Field', 'Card Number', 'Security Code'];
|
return ['TOTP', 'Password', 'H_Field', 'Card Number', 'Security Code'];
|
||||||
}
|
}
|
||||||
|
|
||||||
async showPasswordPrompt() {
|
async showPasswordPrompt() {
|
||||||
|
if (!await this.enabled()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const ref = this.modalService.open(this.component, {allowMultipleModals: true});
|
const ref = this.modalService.open(this.component, {allowMultipleModals: true});
|
||||||
|
|
||||||
if (ref == null) {
|
if (ref == null) {
|
||||||
@@ -25,4 +30,8 @@ export class PasswordRepromptService implements PasswordRepromptServiceAbstracti
|
|||||||
const result = await ref.onClosedPromise();
|
const result = await ref.onClosedPromise();
|
||||||
return result === true;
|
return result === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async enabled() {
|
||||||
|
return !await this.keyConnectorService.getUsesKeyConnector();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { PolicyType } from '../enums/policyType';
|
import { PolicyType } from '../enums/policyType';
|
||||||
import { SetCryptoAgentKeyRequest } from '../models/request/account/setCryptoAgentKeyRequest';
|
import { SetKeyConnectorKeyRequest } from '../models/request/account/setKeyConnectorKeyRequest';
|
||||||
|
import { VerifyOTPRequest } from '../models/request/account/verifyOTPRequest';
|
||||||
|
|
||||||
import { AttachmentRequest } from '../models/request/attachmentRequest';
|
import { AttachmentRequest } from '../models/request/attachmentRequest';
|
||||||
|
|
||||||
@@ -13,7 +14,6 @@ import { CipherCreateRequest } from '../models/request/cipherCreateRequest';
|
|||||||
import { CipherRequest } from '../models/request/cipherRequest';
|
import { CipherRequest } from '../models/request/cipherRequest';
|
||||||
import { CipherShareRequest } from '../models/request/cipherShareRequest';
|
import { CipherShareRequest } from '../models/request/cipherShareRequest';
|
||||||
import { CollectionRequest } from '../models/request/collectionRequest';
|
import { CollectionRequest } from '../models/request/collectionRequest';
|
||||||
import { CryptoAgentUserKeyRequest } from '../models/request/cryptoAgentUserKeyRequest';
|
|
||||||
import { DeleteRecoverRequest } from '../models/request/deleteRecoverRequest';
|
import { DeleteRecoverRequest } from '../models/request/deleteRecoverRequest';
|
||||||
import { EmailRequest } from '../models/request/emailRequest';
|
import { EmailRequest } from '../models/request/emailRequest';
|
||||||
import { EmailTokenRequest } from '../models/request/emailTokenRequest';
|
import { EmailTokenRequest } from '../models/request/emailTokenRequest';
|
||||||
@@ -30,6 +30,7 @@ import { ImportCiphersRequest } from '../models/request/importCiphersRequest';
|
|||||||
import { ImportDirectoryRequest } from '../models/request/importDirectoryRequest';
|
import { ImportDirectoryRequest } from '../models/request/importDirectoryRequest';
|
||||||
import { ImportOrganizationCiphersRequest } from '../models/request/importOrganizationCiphersRequest';
|
import { ImportOrganizationCiphersRequest } from '../models/request/importOrganizationCiphersRequest';
|
||||||
import { KdfRequest } from '../models/request/kdfRequest';
|
import { KdfRequest } from '../models/request/kdfRequest';
|
||||||
|
import { KeyConnectorUserKeyRequest } from '../models/request/keyConnectorUserKeyRequest';
|
||||||
import { KeysRequest } from '../models/request/keysRequest';
|
import { KeysRequest } from '../models/request/keysRequest';
|
||||||
import { OrganizationSsoRequest } from '../models/request/organization/organizationSsoRequest';
|
import { OrganizationSsoRequest } from '../models/request/organization/organizationSsoRequest';
|
||||||
import { OrganizationCreateRequest } from '../models/request/organizationCreateRequest';
|
import { OrganizationCreateRequest } from '../models/request/organizationCreateRequest';
|
||||||
@@ -50,7 +51,6 @@ import { OrganizationUserUpdateGroupsRequest } from '../models/request/organizat
|
|||||||
import { OrganizationUserUpdateRequest } from '../models/request/organizationUserUpdateRequest';
|
import { OrganizationUserUpdateRequest } from '../models/request/organizationUserUpdateRequest';
|
||||||
import { PasswordHintRequest } from '../models/request/passwordHintRequest';
|
import { PasswordHintRequest } from '../models/request/passwordHintRequest';
|
||||||
import { PasswordRequest } from '../models/request/passwordRequest';
|
import { PasswordRequest } from '../models/request/passwordRequest';
|
||||||
import { PasswordVerificationRequest } from '../models/request/passwordVerificationRequest';
|
|
||||||
import { PaymentRequest } from '../models/request/paymentRequest';
|
import { PaymentRequest } from '../models/request/paymentRequest';
|
||||||
import { PolicyRequest } from '../models/request/policyRequest';
|
import { PolicyRequest } from '../models/request/policyRequest';
|
||||||
import { PreloginRequest } from '../models/request/preloginRequest';
|
import { PreloginRequest } from '../models/request/preloginRequest';
|
||||||
@@ -66,6 +66,7 @@ import { ProviderUserInviteRequest } from '../models/request/provider/providerUs
|
|||||||
import { ProviderUserUpdateRequest } from '../models/request/provider/providerUserUpdateRequest';
|
import { ProviderUserUpdateRequest } from '../models/request/provider/providerUserUpdateRequest';
|
||||||
import { RegisterRequest } from '../models/request/registerRequest';
|
import { RegisterRequest } from '../models/request/registerRequest';
|
||||||
import { SeatRequest } from '../models/request/seatRequest';
|
import { SeatRequest } from '../models/request/seatRequest';
|
||||||
|
import { SecretVerificationRequest } from '../models/request/secretVerificationRequest';
|
||||||
import { SelectionReadOnlyRequest } from '../models/request/selectionReadOnlyRequest';
|
import { SelectionReadOnlyRequest } from '../models/request/selectionReadOnlyRequest';
|
||||||
import { SendAccessRequest } from '../models/request/sendAccessRequest';
|
import { SendAccessRequest } from '../models/request/sendAccessRequest';
|
||||||
import { SendRequest } from '../models/request/sendRequest';
|
import { SendRequest } from '../models/request/sendRequest';
|
||||||
@@ -100,7 +101,6 @@ import {
|
|||||||
CollectionGroupDetailsResponse,
|
CollectionGroupDetailsResponse,
|
||||||
CollectionResponse,
|
CollectionResponse,
|
||||||
} from '../models/response/collectionResponse';
|
} from '../models/response/collectionResponse';
|
||||||
import { CryptoAgentUserKeyResponse } from '../models/response/cryptoAgentUserKeyResponse';
|
|
||||||
import { DomainsResponse } from '../models/response/domainsResponse';
|
import { DomainsResponse } from '../models/response/domainsResponse';
|
||||||
import {
|
import {
|
||||||
EmergencyAccessGranteeDetailsResponse,
|
EmergencyAccessGranteeDetailsResponse,
|
||||||
@@ -117,6 +117,7 @@ import {
|
|||||||
import { IdentityCaptchaResponse } from '../models/response/identityCaptchaResponse';
|
import { IdentityCaptchaResponse } from '../models/response/identityCaptchaResponse';
|
||||||
import { IdentityTokenResponse } from '../models/response/identityTokenResponse';
|
import { IdentityTokenResponse } from '../models/response/identityTokenResponse';
|
||||||
import { IdentityTwoFactorResponse } from '../models/response/identityTwoFactorResponse';
|
import { IdentityTwoFactorResponse } from '../models/response/identityTwoFactorResponse';
|
||||||
|
import { KeyConnectorUserKeyResponse } from '../models/response/keyConnectorUserKeyResponse';
|
||||||
import { ListResponse } from '../models/response/listResponse';
|
import { ListResponse } from '../models/response/listResponse';
|
||||||
import { OrganizationSsoResponse } from '../models/response/organization/organizationSsoResponse';
|
import { OrganizationSsoResponse } from '../models/response/organization/organizationSsoResponse';
|
||||||
import { OrganizationAutoEnrollStatusResponse } from '../models/response/organizationAutoEnrollStatusResponse';
|
import { OrganizationAutoEnrollStatusResponse } from '../models/response/organizationAutoEnrollStatusResponse';
|
||||||
@@ -175,9 +176,9 @@ export abstract class ApiService {
|
|||||||
postEmail: (request: EmailRequest) => Promise<any>;
|
postEmail: (request: EmailRequest) => Promise<any>;
|
||||||
postPassword: (request: PasswordRequest) => Promise<any>;
|
postPassword: (request: PasswordRequest) => Promise<any>;
|
||||||
setPassword: (request: SetPasswordRequest) => Promise<any>;
|
setPassword: (request: SetPasswordRequest) => Promise<any>;
|
||||||
postSetCryptoAgentKey: (request: SetCryptoAgentKeyRequest) => Promise<any>;
|
postSetKeyConnectorKey: (request: SetKeyConnectorKeyRequest) => Promise<any>;
|
||||||
postSecurityStamp: (request: PasswordVerificationRequest) => Promise<any>;
|
postSecurityStamp: (request: SecretVerificationRequest) => Promise<any>;
|
||||||
deleteAccount: (request: PasswordVerificationRequest) => Promise<any>;
|
deleteAccount: (request: SecretVerificationRequest) => Promise<any>;
|
||||||
getAccountRevisionDate: () => Promise<number>;
|
getAccountRevisionDate: () => Promise<number>;
|
||||||
postPasswordHint: (request: PasswordHintRequest) => Promise<any>;
|
postPasswordHint: (request: PasswordHintRequest) => Promise<any>;
|
||||||
postRegister: (request: RegisterRequest) => Promise<any>;
|
postRegister: (request: RegisterRequest) => Promise<any>;
|
||||||
@@ -192,13 +193,16 @@ export abstract class ApiService {
|
|||||||
postAccountKeys: (request: KeysRequest) => Promise<any>;
|
postAccountKeys: (request: KeysRequest) => Promise<any>;
|
||||||
postAccountVerifyEmail: () => Promise<any>;
|
postAccountVerifyEmail: () => Promise<any>;
|
||||||
postAccountVerifyEmailToken: (request: VerifyEmailRequest) => Promise<any>;
|
postAccountVerifyEmailToken: (request: VerifyEmailRequest) => Promise<any>;
|
||||||
postAccountVerifyPassword: (request: PasswordVerificationRequest) => Promise<any>;
|
postAccountVerifyPassword: (request: SecretVerificationRequest) => Promise<any>;
|
||||||
postAccountRecoverDelete: (request: DeleteRecoverRequest) => Promise<any>;
|
postAccountRecoverDelete: (request: DeleteRecoverRequest) => Promise<any>;
|
||||||
postAccountRecoverDeleteToken: (request: VerifyDeleteRecoverRequest) => Promise<any>;
|
postAccountRecoverDeleteToken: (request: VerifyDeleteRecoverRequest) => Promise<any>;
|
||||||
postAccountKdf: (request: KdfRequest) => Promise<any>;
|
postAccountKdf: (request: KdfRequest) => Promise<any>;
|
||||||
postUserApiKey: (id: string, request: PasswordVerificationRequest) => Promise<ApiKeyResponse>;
|
postUserApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
|
||||||
postUserRotateApiKey: (id: string, request: PasswordVerificationRequest) => Promise<ApiKeyResponse>;
|
postUserRotateApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
|
||||||
putUpdateTempPassword: (request: UpdateTempPasswordRequest) => Promise<any>;
|
putUpdateTempPassword: (request: UpdateTempPasswordRequest) => Promise<any>;
|
||||||
|
postAccountRequestOTP: () => Promise<void>;
|
||||||
|
postAccountVerifyOTP: (request: VerifyOTPRequest) => Promise<void>;
|
||||||
|
postConvertToKeyConnector: () => Promise<void>;
|
||||||
|
|
||||||
getFolder: (id: string) => Promise<FolderResponse>;
|
getFolder: (id: string) => Promise<FolderResponse>;
|
||||||
postFolder: (request: FolderRequest) => Promise<FolderResponse>;
|
postFolder: (request: FolderRequest) => Promise<FolderResponse>;
|
||||||
@@ -240,7 +244,7 @@ export abstract class ApiService {
|
|||||||
putShareCiphers: (request: CipherBulkShareRequest) => Promise<any>;
|
putShareCiphers: (request: CipherBulkShareRequest) => Promise<any>;
|
||||||
putCipherCollections: (id: string, request: CipherCollectionsRequest) => Promise<any>;
|
putCipherCollections: (id: string, request: CipherCollectionsRequest) => Promise<any>;
|
||||||
putCipherCollectionsAdmin: (id: string, request: CipherCollectionsRequest) => Promise<any>;
|
putCipherCollectionsAdmin: (id: string, request: CipherCollectionsRequest) => Promise<any>;
|
||||||
postPurgeCiphers: (request: PasswordVerificationRequest, organizationId?: string) => Promise<any>;
|
postPurgeCiphers: (request: SecretVerificationRequest, organizationId?: string) => Promise<any>;
|
||||||
postImportCiphers: (request: ImportCiphersRequest) => Promise<any>;
|
postImportCiphers: (request: ImportCiphersRequest) => Promise<any>;
|
||||||
postImportOrganizationCiphers: (organizationId: string, request: ImportOrganizationCiphersRequest) => Promise<any>;
|
postImportOrganizationCiphers: (organizationId: string, request: ImportOrganizationCiphersRequest) => Promise<any>;
|
||||||
putDeleteCipher: (id: string) => Promise<any>;
|
putDeleteCipher: (id: string) => Promise<any>;
|
||||||
@@ -329,15 +333,15 @@ export abstract class ApiService {
|
|||||||
|
|
||||||
getTwoFactorProviders: () => Promise<ListResponse<TwoFactorProviderResponse>>;
|
getTwoFactorProviders: () => Promise<ListResponse<TwoFactorProviderResponse>>;
|
||||||
getTwoFactorOrganizationProviders: (organizationId: string) => Promise<ListResponse<TwoFactorProviderResponse>>;
|
getTwoFactorOrganizationProviders: (organizationId: string) => Promise<ListResponse<TwoFactorProviderResponse>>;
|
||||||
getTwoFactorAuthenticator: (request: PasswordVerificationRequest) => Promise<TwoFactorAuthenticatorResponse>;
|
getTwoFactorAuthenticator: (request: SecretVerificationRequest) => Promise<TwoFactorAuthenticatorResponse>;
|
||||||
getTwoFactorEmail: (request: PasswordVerificationRequest) => Promise<TwoFactorEmailResponse>;
|
getTwoFactorEmail: (request: SecretVerificationRequest) => Promise<TwoFactorEmailResponse>;
|
||||||
getTwoFactorDuo: (request: PasswordVerificationRequest) => Promise<TwoFactorDuoResponse>;
|
getTwoFactorDuo: (request: SecretVerificationRequest) => Promise<TwoFactorDuoResponse>;
|
||||||
getTwoFactorOrganizationDuo: (organizationId: string,
|
getTwoFactorOrganizationDuo: (organizationId: string,
|
||||||
request: PasswordVerificationRequest) => Promise<TwoFactorDuoResponse>;
|
request: SecretVerificationRequest) => Promise<TwoFactorDuoResponse>;
|
||||||
getTwoFactorYubiKey: (request: PasswordVerificationRequest) => Promise<TwoFactorYubiKeyResponse>;
|
getTwoFactorYubiKey: (request: SecretVerificationRequest) => Promise<TwoFactorYubiKeyResponse>;
|
||||||
getTwoFactorWebAuthn: (request: PasswordVerificationRequest) => Promise<TwoFactorWebAuthnResponse>;
|
getTwoFactorWebAuthn: (request: SecretVerificationRequest) => Promise<TwoFactorWebAuthnResponse>;
|
||||||
getTwoFactorWebAuthnChallenge: (request: PasswordVerificationRequest) => Promise<ChallengeResponse>;
|
getTwoFactorWebAuthnChallenge: (request: SecretVerificationRequest) => Promise<ChallengeResponse>;
|
||||||
getTwoFactorRecover: (request: PasswordVerificationRequest) => Promise<TwoFactorRecoverResponse>;
|
getTwoFactorRecover: (request: SecretVerificationRequest) => Promise<TwoFactorRecoverResponse>;
|
||||||
putTwoFactorAuthenticator: (
|
putTwoFactorAuthenticator: (
|
||||||
request: UpdateTwoFactorAuthenticatorRequest) => Promise<TwoFactorAuthenticatorResponse>;
|
request: UpdateTwoFactorAuthenticatorRequest) => Promise<TwoFactorAuthenticatorResponse>;
|
||||||
putTwoFactorEmail: (request: UpdateTwoFactorEmailRequest) => Promise<TwoFactorEmailResponse>;
|
putTwoFactorEmail: (request: UpdateTwoFactorEmailRequest) => Promise<TwoFactorEmailResponse>;
|
||||||
@@ -384,8 +388,8 @@ export abstract class ApiService {
|
|||||||
postLeaveOrganization: (id: string) => Promise<any>;
|
postLeaveOrganization: (id: string) => Promise<any>;
|
||||||
postOrganizationLicense: (data: FormData) => Promise<OrganizationResponse>;
|
postOrganizationLicense: (data: FormData) => Promise<OrganizationResponse>;
|
||||||
postOrganizationLicenseUpdate: (id: string, data: FormData) => Promise<any>;
|
postOrganizationLicenseUpdate: (id: string, data: FormData) => Promise<any>;
|
||||||
postOrganizationApiKey: (id: string, request: PasswordVerificationRequest) => Promise<ApiKeyResponse>;
|
postOrganizationApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
|
||||||
postOrganizationRotateApiKey: (id: string, request: PasswordVerificationRequest) => Promise<ApiKeyResponse>;
|
postOrganizationRotateApiKey: (id: string, request: SecretVerificationRequest) => Promise<ApiKeyResponse>;
|
||||||
postOrganizationSso: (id: string, request: OrganizationSsoRequest) => Promise<OrganizationSsoResponse>;
|
postOrganizationSso: (id: string, request: OrganizationSsoRequest) => Promise<OrganizationSsoResponse>;
|
||||||
postOrganizationUpgrade: (id: string, request: OrganizationUpgradeRequest) => Promise<PaymentResponse>;
|
postOrganizationUpgrade: (id: string, request: OrganizationUpgradeRequest) => Promise<PaymentResponse>;
|
||||||
postOrganizationUpdateSubscription: (id: string, request: OrganizationSubscriptionUpdateRequest) => Promise<void>;
|
postOrganizationUpdateSubscription: (id: string, request: OrganizationSubscriptionUpdateRequest) => Promise<void>;
|
||||||
@@ -395,7 +399,7 @@ export abstract class ApiService {
|
|||||||
postOrganizationVerifyBank: (id: string, request: VerifyBankRequest) => Promise<any>;
|
postOrganizationVerifyBank: (id: string, request: VerifyBankRequest) => Promise<any>;
|
||||||
postOrganizationCancel: (id: string) => Promise<any>;
|
postOrganizationCancel: (id: string) => Promise<any>;
|
||||||
postOrganizationReinstate: (id: string) => Promise<any>;
|
postOrganizationReinstate: (id: string) => Promise<any>;
|
||||||
deleteOrganization: (id: string, request: PasswordVerificationRequest) => Promise<any>;
|
deleteOrganization: (id: string, request: SecretVerificationRequest) => Promise<any>;
|
||||||
getPlans: () => Promise<ListResponse<PlanResponse>>;
|
getPlans: () => Promise<ListResponse<PlanResponse>>;
|
||||||
getTaxRates: () => Promise<ListResponse<TaxRateResponse>>;
|
getTaxRates: () => Promise<ListResponse<TaxRateResponse>>;
|
||||||
getOrganizationKeys: (id: string) => Promise<OrganizationKeysResponse>;
|
getOrganizationKeys: (id: string) => Promise<OrganizationKeysResponse>;
|
||||||
@@ -449,6 +453,6 @@ export abstract class ApiService {
|
|||||||
|
|
||||||
preValidateSso: (identifier: string) => Promise<boolean>;
|
preValidateSso: (identifier: string) => Promise<boolean>;
|
||||||
|
|
||||||
getUserKeyFromCryptoAgent: (cryptoAgentUrl: string) => Promise<CryptoAgentUserKeyResponse>;
|
getUserKeyFromKeyConnector: (keyConnectorUrl: string) => Promise<KeyConnectorUserKeyResponse>;
|
||||||
postUserKeyToCryptoAgent: (cryptoAgentUrl: string, request: CryptoAgentUserKeyRequest) => Promise<void>;
|
postUserKeyToKeyConnector: (keyConnectorUrl: string, request: KeyConnectorUserKeyRequest) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export type Urls = {
|
|||||||
icons?: string;
|
icons?: string;
|
||||||
notifications?: string;
|
notifications?: string;
|
||||||
events?: string;
|
events?: string;
|
||||||
|
keyConnector?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PayPalConfig = {
|
export type PayPalConfig = {
|
||||||
@@ -26,6 +27,7 @@ export abstract class EnvironmentService {
|
|||||||
getApiUrl: () => string;
|
getApiUrl: () => string;
|
||||||
getIdentityUrl: () => string;
|
getIdentityUrl: () => string;
|
||||||
getEventsUrl: () => string;
|
getEventsUrl: () => string;
|
||||||
|
getKeyConnectorUrl: () => string;
|
||||||
setUrlsFromStorage: () => Promise<void>;
|
setUrlsFromStorage: () => Promise<void>;
|
||||||
setUrls: (urls: any, saveSettings?: boolean) => Promise<Urls>;
|
setUrls: (urls: any, saveSettings?: boolean) => Promise<Urls>;
|
||||||
getUrls: () => Urls;
|
getUrls: () => Urls;
|
||||||
|
|||||||
14
common/src/abstractions/keyConnector.service.ts
Normal file
14
common/src/abstractions/keyConnector.service.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { Organization } from '../models/domain/organization';
|
||||||
|
|
||||||
|
export abstract class KeyConnectorService {
|
||||||
|
getAndSetKey: (url?: string) => Promise<void>;
|
||||||
|
getManagingOrganization: () => Promise<Organization>;
|
||||||
|
getUsesKeyConnector: () => Promise<boolean>;
|
||||||
|
migrateUser: () => Promise<void>;
|
||||||
|
userNeedsMigration: () => Promise<boolean>;
|
||||||
|
setUsesKeyConnector: (enabled: boolean) => Promise<void>;
|
||||||
|
setConvertAccountRequired: (status: boolean) => Promise<void>;
|
||||||
|
getConvertAccountRequired: () => Promise<boolean>;
|
||||||
|
removeConvertAccountRequired: () => Promise<void>;
|
||||||
|
clear: () => Promise<void>;
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
export abstract class PasswordRepromptService {
|
export abstract class PasswordRepromptService {
|
||||||
protectedFields: () => string[];
|
protectedFields: () => string[];
|
||||||
showPasswordPrompt: () => Promise<boolean>;
|
showPasswordPrompt: () => Promise<boolean>;
|
||||||
|
enabled: () => Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,4 +23,5 @@ export abstract class TokenService {
|
|||||||
getName: () => Promise<string>;
|
getName: () => Promise<string>;
|
||||||
getPremium: () => Promise<boolean>;
|
getPremium: () => Promise<boolean>;
|
||||||
getIssuer: () => Promise<string>;
|
getIssuer: () => Promise<string>;
|
||||||
|
getIsExternal: () => boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
9
common/src/abstractions/userVerification.service.ts
Normal file
9
common/src/abstractions/userVerification.service.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { SecretVerificationRequest } from '../models/request/secretVerificationRequest';
|
||||||
|
|
||||||
|
import { Verification } from '../types/verification';
|
||||||
|
|
||||||
|
export abstract class UserVerificationService {
|
||||||
|
buildRequest: <T extends SecretVerificationRequest> (verification: Verification,
|
||||||
|
requestClass?: new () => T, alreadyHashed?: boolean) => Promise<T>;
|
||||||
|
verifyUser: (verification: Verification) => Promise<boolean>;
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ export enum EventType {
|
|||||||
User_FailedLogIn2fa = 1006,
|
User_FailedLogIn2fa = 1006,
|
||||||
User_ClientExportedVault = 1007,
|
User_ClientExportedVault = 1007,
|
||||||
User_UpdatedTempPassword = 1008,
|
User_UpdatedTempPassword = 1008,
|
||||||
|
User_MigratedKeyToKeyConnector = 1009,
|
||||||
|
|
||||||
Cipher_Created = 1100,
|
Cipher_Created = 1100,
|
||||||
Cipher_Updated = 1101,
|
Cipher_Updated = 1101,
|
||||||
@@ -51,6 +52,10 @@ export enum EventType {
|
|||||||
Organization_PurgedVault = 1601,
|
Organization_PurgedVault = 1601,
|
||||||
// Organization_ClientExportedVault = 1602,
|
// Organization_ClientExportedVault = 1602,
|
||||||
Organization_VaultAccessed = 1603,
|
Organization_VaultAccessed = 1603,
|
||||||
|
Organization_EnabledSso = 1604,
|
||||||
|
Organization_DisabledSso = 1605,
|
||||||
|
Organization_EnabledKeyConnector = 1606,
|
||||||
|
Organization_DisabledKeyConnector = 1607,
|
||||||
|
|
||||||
Policy_Updated = 1700,
|
Policy_Updated = 1700,
|
||||||
|
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ export enum FieldType {
|
|||||||
Text = 0,
|
Text = 0,
|
||||||
Hidden = 1,
|
Hidden = 1,
|
||||||
Boolean = 2,
|
Boolean = 2,
|
||||||
|
Linked = 3,
|
||||||
}
|
}
|
||||||
|
|||||||
40
common/src/enums/linkedIdType.ts
Normal file
40
common/src/enums/linkedIdType.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
export type LinkedIdType = LoginLinkedId | CardLinkedId | IdentityLinkedId;
|
||||||
|
|
||||||
|
// LoginView
|
||||||
|
export enum LoginLinkedId {
|
||||||
|
Username = 100,
|
||||||
|
Password = 101,
|
||||||
|
}
|
||||||
|
|
||||||
|
// CardView
|
||||||
|
export enum CardLinkedId {
|
||||||
|
CardholderName = 300,
|
||||||
|
ExpMonth = 301,
|
||||||
|
ExpYear = 302,
|
||||||
|
Code = 303,
|
||||||
|
Brand = 304,
|
||||||
|
Number = 305,
|
||||||
|
}
|
||||||
|
|
||||||
|
// IdentityView
|
||||||
|
export enum IdentityLinkedId {
|
||||||
|
Title = 400,
|
||||||
|
MiddleName = 401,
|
||||||
|
Address1 = 402,
|
||||||
|
Address2 = 403,
|
||||||
|
Address3 = 404,
|
||||||
|
City = 405,
|
||||||
|
State = 406,
|
||||||
|
PostalCode = 407,
|
||||||
|
Country = 408,
|
||||||
|
Company = 409,
|
||||||
|
Email = 410,
|
||||||
|
Phone = 411,
|
||||||
|
Ssn = 412,
|
||||||
|
Username = 413,
|
||||||
|
PassportNumber = 414,
|
||||||
|
LicenseNumber = 415,
|
||||||
|
FirstName = 416,
|
||||||
|
LastName = 417,
|
||||||
|
FullName = 418,
|
||||||
|
}
|
||||||
4
common/src/enums/verificationType.ts
Normal file
4
common/src/enums/verificationType.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export enum VerificationType {
|
||||||
|
MasterPassword = 0,
|
||||||
|
OTP = 1,
|
||||||
|
}
|
||||||
28
common/src/misc/linkedFieldOption.decorator.ts
Normal file
28
common/src/misc/linkedFieldOption.decorator.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { ItemView } from '../models/view/itemView';
|
||||||
|
|
||||||
|
import { LinkedIdType } from '../enums/linkedIdType';
|
||||||
|
|
||||||
|
export class LinkedMetadata {
|
||||||
|
constructor(readonly propertyKey: string, private readonly _i18nKey?: string) { }
|
||||||
|
|
||||||
|
get i18nKey() {
|
||||||
|
return this._i18nKey ?? this.propertyKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A decorator used to set metadata used by Linked custom fields. Apply it to a class property or getter to make it
|
||||||
|
* available as a Linked custom field option.
|
||||||
|
* @param id - A unique value that is saved in the Field model. It is used to look up the decorated class property.
|
||||||
|
* @param i18nKey - The i18n key used to describe the decorated class property in the UI. If it is null, then the name
|
||||||
|
* of the class property will be used as the i18n key.
|
||||||
|
*/
|
||||||
|
export function linkedFieldOption(id: LinkedIdType, i18nKey?: string) {
|
||||||
|
return (prototype: ItemView, propertyKey: string) => {
|
||||||
|
if (prototype.linkedFieldOptions == null) {
|
||||||
|
prototype.linkedFieldOptions = new Map<LinkedIdType, LinkedMetadata>();
|
||||||
|
}
|
||||||
|
|
||||||
|
prototype.linkedFieldOptions.set(id, new LinkedMetadata(propertyKey, i18nKey));
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -219,7 +219,8 @@ export class Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let httpUrl = uriString.startsWith('http://') || uriString.startsWith('https://');
|
let httpUrl = uriString.startsWith('http://') || uriString.startsWith('https://');
|
||||||
if (!httpUrl && uriString.indexOf('://') < 0 && Utils.tldEndingRegex.test(uriString)) {
|
if (!httpUrl && uriString.indexOf('://') < 0 && Utils.tldEndingRegex.test(uriString) &&
|
||||||
|
uriString.indexOf('@') < 0) {
|
||||||
uriString = 'http://' + uriString;
|
uriString = 'http://' + uriString;
|
||||||
httpUrl = true;
|
httpUrl = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { BaseResponse } from '../response/baseResponse';
|
import { BaseResponse } from '../response/baseResponse';
|
||||||
|
|
||||||
import { FieldType } from '../../enums/fieldType';
|
import { FieldType } from '../../enums/fieldType';
|
||||||
|
import { LinkedIdType } from '../../enums/linkedIdType';
|
||||||
|
|
||||||
export class FieldApi extends BaseResponse {
|
export class FieldApi extends BaseResponse {
|
||||||
name: string;
|
name: string;
|
||||||
value: string;
|
value: string;
|
||||||
type: FieldType;
|
type: FieldType;
|
||||||
|
linkedId: LinkedIdType;
|
||||||
|
|
||||||
constructor(data: any = null) {
|
constructor(data: any = null) {
|
||||||
super(data);
|
super(data);
|
||||||
@@ -15,5 +17,6 @@ export class FieldApi extends BaseResponse {
|
|||||||
this.type = this.getResponseProperty('Type');
|
this.type = this.getResponseProperty('Type');
|
||||||
this.name = this.getResponseProperty('Name');
|
this.name = this.getResponseProperty('Name');
|
||||||
this.value = this.getResponseProperty('Value');
|
this.value = this.getResponseProperty('Value');
|
||||||
|
this.linkedId = this.getResponseProperty('linkedId');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ enum Saml2SigningBehavior {
|
|||||||
export class SsoConfigApi extends BaseResponse {
|
export class SsoConfigApi extends BaseResponse {
|
||||||
configType: SsoType;
|
configType: SsoType;
|
||||||
|
|
||||||
useCryptoAgent: boolean;
|
useKeyConnector: boolean;
|
||||||
cryptoAgentUrl: string;
|
keyConnectorUrl: string;
|
||||||
|
|
||||||
// OpenId
|
// OpenId
|
||||||
authority: string;
|
authority: string;
|
||||||
@@ -81,8 +81,8 @@ export class SsoConfigApi extends BaseResponse {
|
|||||||
|
|
||||||
this.configType = this.getResponseProperty('ConfigType');
|
this.configType = this.getResponseProperty('ConfigType');
|
||||||
|
|
||||||
this.useCryptoAgent = this.getResponseProperty('UseCryptoAgent');
|
this.useKeyConnector = this.getResponseProperty('UseKeyConnector');
|
||||||
this.cryptoAgentUrl = this.getResponseProperty('CryptoAgentUrl');
|
this.keyConnectorUrl = this.getResponseProperty('KeyConnectorUrl');
|
||||||
|
|
||||||
this.authority = this.getResponseProperty('Authority');
|
this.authority = this.getResponseProperty('Authority');
|
||||||
this.clientId = this.getResponseProperty('ClientId');
|
this.clientId = this.getResponseProperty('ClientId');
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { FieldType } from '../../enums/fieldType';
|
import { FieldType } from '../../enums/fieldType';
|
||||||
|
import { LinkedIdType } from '../../enums/linkedIdType';
|
||||||
|
|
||||||
import { FieldApi } from '../api/fieldApi';
|
import { FieldApi } from '../api/fieldApi';
|
||||||
|
|
||||||
@@ -6,6 +7,7 @@ export class FieldData {
|
|||||||
type: FieldType;
|
type: FieldType;
|
||||||
name: string;
|
name: string;
|
||||||
value: string;
|
value: string;
|
||||||
|
linkedId: LinkedIdType;
|
||||||
|
|
||||||
constructor(response?: FieldApi) {
|
constructor(response?: FieldApi) {
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
@@ -14,5 +16,6 @@ export class FieldData {
|
|||||||
this.type = response.type;
|
this.type = response.type;
|
||||||
this.name = response.name;
|
this.name = response.name;
|
||||||
this.value = response.value;
|
this.value = response.value;
|
||||||
|
this.linkedId = response.linkedId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ export class OrganizationData {
|
|||||||
providerId: string;
|
providerId: string;
|
||||||
providerName: string;
|
providerName: string;
|
||||||
isProviderUser: boolean;
|
isProviderUser: boolean;
|
||||||
|
usesKeyConnector: boolean;
|
||||||
|
keyConnectorUrl: string;
|
||||||
|
|
||||||
constructor(response: ProfileOrganizationResponse) {
|
constructor(response: ProfileOrganizationResponse) {
|
||||||
this.id = response.id;
|
this.id = response.id;
|
||||||
@@ -62,5 +64,7 @@ export class OrganizationData {
|
|||||||
this.hasPublicAndPrivateKeys = response.hasPublicAndPrivateKeys;
|
this.hasPublicAndPrivateKeys = response.hasPublicAndPrivateKeys;
|
||||||
this.providerId = response.providerId;
|
this.providerId = response.providerId;
|
||||||
this.providerName = response.providerName;
|
this.providerName = response.providerName;
|
||||||
|
this.usesKeyConnector = response.usesKeyConnector;
|
||||||
|
this.keyConnectorUrl = response.keyConnectorUrl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { FieldType } from '../../enums/fieldType';
|
import { FieldType } from '../../enums/fieldType';
|
||||||
|
import { LinkedIdType } from '../../enums/linkedIdType';
|
||||||
|
|
||||||
import { FieldData } from '../data/fieldData';
|
import { FieldData } from '../data/fieldData';
|
||||||
|
|
||||||
@@ -12,6 +13,7 @@ export class Field extends Domain {
|
|||||||
name: EncString;
|
name: EncString;
|
||||||
value: EncString;
|
value: EncString;
|
||||||
type: FieldType;
|
type: FieldType;
|
||||||
|
linkedId: LinkedIdType;
|
||||||
|
|
||||||
constructor(obj?: FieldData, alreadyEncrypted: boolean = false) {
|
constructor(obj?: FieldData, alreadyEncrypted: boolean = false) {
|
||||||
super();
|
super();
|
||||||
@@ -20,6 +22,7 @@ export class Field extends Domain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.type = obj.type;
|
this.type = obj.type;
|
||||||
|
this.linkedId = obj.linkedId;
|
||||||
this.buildDomainModel(this, obj, {
|
this.buildDomainModel(this, obj, {
|
||||||
name: null,
|
name: null,
|
||||||
value: null,
|
value: null,
|
||||||
@@ -39,7 +42,8 @@ export class Field extends Domain {
|
|||||||
name: null,
|
name: null,
|
||||||
value: null,
|
value: null,
|
||||||
type: null,
|
type: null,
|
||||||
}, ['type']);
|
linkedId: null,
|
||||||
|
}, ['type', 'linkedId']);
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ export class Organization {
|
|||||||
providerId: string;
|
providerId: string;
|
||||||
providerName: string;
|
providerName: string;
|
||||||
isProviderUser: boolean;
|
isProviderUser: boolean;
|
||||||
|
usesKeyConnector: boolean;
|
||||||
|
keyConnectorUrl: string;
|
||||||
|
|
||||||
constructor(obj?: OrganizationData) {
|
constructor(obj?: OrganizationData) {
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
@@ -68,6 +70,8 @@ export class Organization {
|
|||||||
this.providerId = obj.providerId;
|
this.providerId = obj.providerId;
|
||||||
this.providerName = obj.providerName;
|
this.providerName = obj.providerName;
|
||||||
this.isProviderUser = obj.isProviderUser;
|
this.isProviderUser = obj.isProviderUser;
|
||||||
|
this.usesKeyConnector = obj.usesKeyConnector;
|
||||||
|
this.keyConnectorUrl = obj.keyConnectorUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
get canAccess() {
|
get canAccess() {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { KeysRequest } from '../keysRequest';
|
|||||||
|
|
||||||
import { KdfType } from '../../../enums/kdfType';
|
import { KdfType } from '../../../enums/kdfType';
|
||||||
|
|
||||||
export class SetCryptoAgentKeyRequest {
|
export class SetKeyConnectorKeyRequest {
|
||||||
key: string;
|
key: string;
|
||||||
keys: KeysRequest;
|
keys: KeysRequest;
|
||||||
kdf: KdfType;
|
kdf: KdfType;
|
||||||
7
common/src/models/request/account/verifyOTPRequest.ts
Normal file
7
common/src/models/request/account/verifyOTPRequest.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export class VerifyOTPRequest {
|
||||||
|
OTP: string;
|
||||||
|
|
||||||
|
constructor(OTP: string) {
|
||||||
|
this.OTP = OTP;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -119,6 +119,7 @@ export class CipherRequest {
|
|||||||
field.type = f.type;
|
field.type = f.type;
|
||||||
field.name = f.name ? f.name.encryptedString : null;
|
field.name = f.name ? f.name.encryptedString : null;
|
||||||
field.value = f.value ? f.value.encryptedString : null;
|
field.value = f.value ? f.value.encryptedString : null;
|
||||||
|
field.linkedId = f.linkedId;
|
||||||
return field;
|
return field;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { PasswordVerificationRequest } from './passwordVerificationRequest';
|
import { SecretVerificationRequest } from './secretVerificationRequest';
|
||||||
|
|
||||||
export class EmailTokenRequest extends PasswordVerificationRequest {
|
export class EmailTokenRequest extends SecretVerificationRequest {
|
||||||
newEmail: string;
|
newEmail: string;
|
||||||
masterPasswordHash: string;
|
masterPasswordHash: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export class CryptoAgentUserKeyRequest {
|
export class KeyConnectorUserKeyRequest {
|
||||||
key: string;
|
key: string;
|
||||||
|
|
||||||
constructor(key: string) {
|
constructor(key: string) {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { PasswordVerificationRequest } from './passwordVerificationRequest';
|
import { SecretVerificationRequest } from './secretVerificationRequest';
|
||||||
|
|
||||||
export class PasswordRequest extends PasswordVerificationRequest {
|
export class PasswordRequest extends SecretVerificationRequest {
|
||||||
newMasterPasswordHash: string;
|
newMasterPasswordHash: string;
|
||||||
key: string;
|
key: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
export class PasswordVerificationRequest {
|
|
||||||
masterPasswordHash: string;
|
|
||||||
}
|
|
||||||
4
common/src/models/request/secretVerificationRequest.ts
Normal file
4
common/src/models/request/secretVerificationRequest.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export class SecretVerificationRequest {
|
||||||
|
masterPasswordHash: string;
|
||||||
|
otp: string;
|
||||||
|
}
|
||||||
@@ -1,11 +1,5 @@
|
|||||||
import { PasswordVerificationRequest } from './passwordVerificationRequest';
|
import { SecretVerificationRequest } from './secretVerificationRequest';
|
||||||
|
|
||||||
export class TwoFactorEmailRequest extends PasswordVerificationRequest {
|
export class TwoFactorEmailRequest extends SecretVerificationRequest {
|
||||||
email: string;
|
email: string;
|
||||||
|
|
||||||
constructor(email: string, masterPasswordHash: string) {
|
|
||||||
super();
|
|
||||||
this.masterPasswordHash = masterPasswordHash;
|
|
||||||
this.email = email;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { PasswordVerificationRequest } from './passwordVerificationRequest';
|
import { SecretVerificationRequest } from './secretVerificationRequest';
|
||||||
|
|
||||||
import { TwoFactorProviderType } from '../../enums/twoFactorProviderType';
|
import { TwoFactorProviderType } from '../../enums/twoFactorProviderType';
|
||||||
|
|
||||||
export class TwoFactorProviderRequest extends PasswordVerificationRequest {
|
export class TwoFactorProviderRequest extends SecretVerificationRequest {
|
||||||
type: TwoFactorProviderType;
|
type: TwoFactorProviderType;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { PasswordVerificationRequest } from './passwordVerificationRequest';
|
import { SecretVerificationRequest } from './secretVerificationRequest';
|
||||||
|
|
||||||
export class TwoFactorRecoveryRequest extends PasswordVerificationRequest {
|
export class TwoFactorRecoveryRequest extends SecretVerificationRequest {
|
||||||
recoveryCode: string;
|
recoveryCode: string;
|
||||||
email: string;
|
email: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { PasswordVerificationRequest } from './passwordVerificationRequest';
|
import { SecretVerificationRequest } from './secretVerificationRequest';
|
||||||
|
|
||||||
export class UpdateTwoFactorAuthenticatorRequest extends PasswordVerificationRequest {
|
export class UpdateTwoFactorAuthenticatorRequest extends SecretVerificationRequest {
|
||||||
token: string;
|
token: string;
|
||||||
key: string;
|
key: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { PasswordVerificationRequest } from './passwordVerificationRequest';
|
import { SecretVerificationRequest } from './secretVerificationRequest';
|
||||||
|
|
||||||
export class UpdateTwoFactorDuoRequest extends PasswordVerificationRequest {
|
export class UpdateTwoFactorDuoRequest extends SecretVerificationRequest {
|
||||||
integrationKey: string;
|
integrationKey: string;
|
||||||
secretKey: string;
|
secretKey: string;
|
||||||
host: string;
|
host: string;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { PasswordVerificationRequest } from './passwordVerificationRequest';
|
import { SecretVerificationRequest } from './secretVerificationRequest';
|
||||||
|
|
||||||
export class UpdateTwoFactorEmailRequest extends PasswordVerificationRequest {
|
export class UpdateTwoFactorEmailRequest extends SecretVerificationRequest {
|
||||||
token: string;
|
token: string;
|
||||||
email: string;
|
email: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { PasswordVerificationRequest } from './passwordVerificationRequest';
|
import { SecretVerificationRequest } from './secretVerificationRequest';
|
||||||
|
|
||||||
export class UpdateTwoFactorWebAuthnDeleteRequest extends PasswordVerificationRequest {
|
export class UpdateTwoFactorWebAuthnDeleteRequest extends SecretVerificationRequest {
|
||||||
id: number;
|
id: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { PasswordVerificationRequest } from './passwordVerificationRequest';
|
import { SecretVerificationRequest } from './secretVerificationRequest';
|
||||||
|
|
||||||
export class UpdateTwoFactorWebAuthnRequest extends PasswordVerificationRequest {
|
export class UpdateTwoFactorWebAuthnRequest extends SecretVerificationRequest {
|
||||||
deviceResponse: PublicKeyCredential;
|
deviceResponse: PublicKeyCredential;
|
||||||
name: string;
|
name: string;
|
||||||
id: number;
|
id: number;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { PasswordVerificationRequest } from './passwordVerificationRequest';
|
import { SecretVerificationRequest } from './secretVerificationRequest';
|
||||||
|
|
||||||
export class UpdateTwoFactorYubioOtpRequest extends PasswordVerificationRequest {
|
export class UpdateTwoFactorYubioOtpRequest extends SecretVerificationRequest {
|
||||||
key1: string;
|
key1: string;
|
||||||
key2: string;
|
key2: string;
|
||||||
key3: string;
|
key3: string;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export class IdentityTokenResponse extends BaseResponse {
|
|||||||
kdf: KdfType;
|
kdf: KdfType;
|
||||||
kdfIterations: number;
|
kdfIterations: number;
|
||||||
forcePasswordReset: boolean;
|
forcePasswordReset: boolean;
|
||||||
cryptoAgentUrl: string;
|
keyConnectorUrl: string;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
@@ -31,6 +31,6 @@ export class IdentityTokenResponse extends BaseResponse {
|
|||||||
this.kdf = this.getResponseProperty('Kdf');
|
this.kdf = this.getResponseProperty('Kdf');
|
||||||
this.kdfIterations = this.getResponseProperty('KdfIterations');
|
this.kdfIterations = this.getResponseProperty('KdfIterations');
|
||||||
this.forcePasswordReset = this.getResponseProperty('ForcePasswordReset');
|
this.forcePasswordReset = this.getResponseProperty('ForcePasswordReset');
|
||||||
this.cryptoAgentUrl = this.getResponseProperty('CryptoAgentUrl');
|
this.keyConnectorUrl = this.getResponseProperty('KeyConnectorUrl');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { BaseResponse } from './baseResponse';
|
import { BaseResponse } from './baseResponse';
|
||||||
|
|
||||||
export class CryptoAgentUserKeyResponse extends BaseResponse {
|
export class KeyConnectorUserKeyResponse extends BaseResponse {
|
||||||
key: string;
|
key: string;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
@@ -33,6 +33,8 @@ export class ProfileOrganizationResponse extends BaseResponse {
|
|||||||
userId: string;
|
userId: string;
|
||||||
providerId: string;
|
providerId: string;
|
||||||
providerName: string;
|
providerName: string;
|
||||||
|
usesKeyConnector: boolean;
|
||||||
|
keyConnectorUrl: string;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
@@ -64,5 +66,7 @@ export class ProfileOrganizationResponse extends BaseResponse {
|
|||||||
this.userId = this.getResponseProperty('UserId');
|
this.userId = this.getResponseProperty('UserId');
|
||||||
this.providerId = this.getResponseProperty('ProviderId');
|
this.providerId = this.getResponseProperty('ProviderId');
|
||||||
this.providerName = this.getResponseProperty('ProviderName');
|
this.providerName = this.getResponseProperty('ProviderName');
|
||||||
|
this.usesKeyConnector = this.getResponseProperty('UsesKeyConnector') ?? false;
|
||||||
|
this.keyConnectorUrl = this.getResponseProperty('KeyConnectorUrl');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,8 @@
|
|||||||
import { BaseResponse } from './baseResponse';
|
import { ProfileOrganizationResponse } from './profileOrganizationResponse';
|
||||||
|
|
||||||
import { OrganizationUserStatusType } from '../../enums/organizationUserStatusType';
|
|
||||||
import { OrganizationUserType } from '../../enums/organizationUserType';
|
|
||||||
import { PermissionsApi } from '../api/permissionsApi';
|
|
||||||
|
|
||||||
export class ProfileProviderOrganizationResponse extends BaseResponse {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
usePolicies: boolean;
|
|
||||||
useGroups: boolean;
|
|
||||||
useDirectory: boolean;
|
|
||||||
useEvents: boolean;
|
|
||||||
useTotp: boolean;
|
|
||||||
use2fa: boolean;
|
|
||||||
useApi: boolean;
|
|
||||||
useSso: boolean;
|
|
||||||
useResetPassword: boolean;
|
|
||||||
selfHost: boolean;
|
|
||||||
usersGetPremium: boolean;
|
|
||||||
seats: number;
|
|
||||||
maxCollections: number;
|
|
||||||
maxStorageGb?: number;
|
|
||||||
key: string;
|
|
||||||
hasPublicAndPrivateKeys: boolean;
|
|
||||||
status: OrganizationUserStatusType;
|
|
||||||
type: OrganizationUserType;
|
|
||||||
enabled: boolean;
|
|
||||||
ssoBound: boolean;
|
|
||||||
identifier: string;
|
|
||||||
permissions: PermissionsApi;
|
|
||||||
resetPasswordEnrolled: boolean;
|
|
||||||
userId: string;
|
|
||||||
providerId: string;
|
|
||||||
providerName: string;
|
|
||||||
|
|
||||||
|
export class ProfileProviderOrganizationResponse extends ProfileOrganizationResponse {
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
super(response);
|
super(response);
|
||||||
this.id = this.getResponseProperty('Id');
|
this.usesKeyConnector = false;
|
||||||
this.name = this.getResponseProperty('Name');
|
|
||||||
this.usePolicies = this.getResponseProperty('UsePolicies');
|
|
||||||
this.useGroups = this.getResponseProperty('UseGroups');
|
|
||||||
this.useDirectory = this.getResponseProperty('UseDirectory');
|
|
||||||
this.useEvents = this.getResponseProperty('UseEvents');
|
|
||||||
this.useTotp = this.getResponseProperty('UseTotp');
|
|
||||||
this.use2fa = this.getResponseProperty('Use2fa');
|
|
||||||
this.useApi = this.getResponseProperty('UseApi');
|
|
||||||
this.useSso = this.getResponseProperty('UseSso');
|
|
||||||
this.useResetPassword = this.getResponseProperty('UseResetPassword');
|
|
||||||
this.selfHost = this.getResponseProperty('SelfHost');
|
|
||||||
this.usersGetPremium = this.getResponseProperty('UsersGetPremium');
|
|
||||||
this.seats = this.getResponseProperty('Seats');
|
|
||||||
this.maxCollections = this.getResponseProperty('MaxCollections');
|
|
||||||
this.maxStorageGb = this.getResponseProperty('MaxStorageGb');
|
|
||||||
this.key = this.getResponseProperty('Key');
|
|
||||||
this.hasPublicAndPrivateKeys = this.getResponseProperty('HasPublicAndPrivateKeys');
|
|
||||||
this.status = this.getResponseProperty('Status');
|
|
||||||
this.type = this.getResponseProperty('Type');
|
|
||||||
this.enabled = this.getResponseProperty('Enabled');
|
|
||||||
this.ssoBound = this.getResponseProperty('SsoBound');
|
|
||||||
this.identifier = this.getResponseProperty('Identifier');
|
|
||||||
this.permissions = new PermissionsApi(this.getResponseProperty('permissions'));
|
|
||||||
this.resetPasswordEnrolled = this.getResponseProperty('ResetPasswordEnrolled');
|
|
||||||
this.userId = this.getResponseProperty('UserId');
|
|
||||||
this.providerId = this.getResponseProperty('ProviderId');
|
|
||||||
this.providerName = this.getResponseProperty('ProviderName');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export class ProfileResponse extends BaseResponse {
|
|||||||
privateKey: string;
|
privateKey: string;
|
||||||
securityStamp: string;
|
securityStamp: string;
|
||||||
forcePasswordReset: boolean;
|
forcePasswordReset: boolean;
|
||||||
|
usesKeyConnector: boolean;
|
||||||
organizations: ProfileOrganizationResponse[] = [];
|
organizations: ProfileOrganizationResponse[] = [];
|
||||||
providers: ProfileProviderResponse[] = [];
|
providers: ProfileProviderResponse[] = [];
|
||||||
providerOrganizations: ProfileProviderOrganizationResponse[] = [];
|
providerOrganizations: ProfileProviderOrganizationResponse[] = [];
|
||||||
@@ -34,6 +35,7 @@ export class ProfileResponse extends BaseResponse {
|
|||||||
this.privateKey = this.getResponseProperty('PrivateKey');
|
this.privateKey = this.getResponseProperty('PrivateKey');
|
||||||
this.securityStamp = this.getResponseProperty('SecurityStamp');
|
this.securityStamp = this.getResponseProperty('SecurityStamp');
|
||||||
this.forcePasswordReset = this.getResponseProperty('ForcePasswordReset') ?? false;
|
this.forcePasswordReset = this.getResponseProperty('ForcePasswordReset') ?? false;
|
||||||
|
this.usesKeyConnector = this.getResponseProperty('UsesKeyConnector') ?? false;
|
||||||
|
|
||||||
const organizations = this.getResponseProperty('Organizations');
|
const organizations = this.getResponseProperty('Organizations');
|
||||||
if (organizations != null) {
|
if (organizations != null) {
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
import { View } from './view';
|
import { ItemView } from './itemView';
|
||||||
|
|
||||||
import { Card } from '../domain/card';
|
import { Card } from '../domain/card';
|
||||||
|
|
||||||
export class CardView implements View {
|
import { CardLinkedId as LinkedId } from '../../enums/linkedIdType';
|
||||||
|
|
||||||
|
import { linkedFieldOption } from '../../misc/linkedFieldOption.decorator';
|
||||||
|
|
||||||
|
export class CardView extends ItemView {
|
||||||
|
@linkedFieldOption(LinkedId.CardholderName)
|
||||||
cardholderName: string = null;
|
cardholderName: string = null;
|
||||||
|
@linkedFieldOption(LinkedId.ExpMonth, 'expirationMonth')
|
||||||
expMonth: string = null;
|
expMonth: string = null;
|
||||||
|
@linkedFieldOption(LinkedId.ExpYear, 'expirationYear')
|
||||||
expYear: string = null;
|
expYear: string = null;
|
||||||
|
@linkedFieldOption(LinkedId.Code, 'securityCode')
|
||||||
code: string = null;
|
code: string = null;
|
||||||
|
|
||||||
// tslint:disable
|
// tslint:disable
|
||||||
@@ -15,7 +23,7 @@ export class CardView implements View {
|
|||||||
// tslint:enable
|
// tslint:enable
|
||||||
|
|
||||||
constructor(c?: Card) {
|
constructor(c?: Card) {
|
||||||
// ctor
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
get maskedCode(): string {
|
get maskedCode(): string {
|
||||||
@@ -26,6 +34,7 @@ export class CardView implements View {
|
|||||||
return this.number != null ? '•'.repeat(this.number.length) : null;
|
return this.number != null ? '•'.repeat(this.number.length) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@linkedFieldOption(LinkedId.Brand)
|
||||||
get brand(): string {
|
get brand(): string {
|
||||||
return this._brand;
|
return this._brand;
|
||||||
}
|
}
|
||||||
@@ -34,6 +43,7 @@ export class CardView implements View {
|
|||||||
this._subTitle = null;
|
this._subTitle = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@linkedFieldOption(LinkedId.Number)
|
||||||
get number(): string {
|
get number(): string {
|
||||||
return this._number;
|
return this._number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { CipherRepromptType } from '../../enums/cipherRepromptType';
|
import { CipherRepromptType } from '../../enums/cipherRepromptType';
|
||||||
import { CipherType } from '../../enums/cipherType';
|
import { CipherType } from '../../enums/cipherType';
|
||||||
|
import { LinkedIdType } from '../../enums/linkedIdType';
|
||||||
|
|
||||||
import { Cipher } from '../domain/cipher';
|
import { Cipher } from '../domain/cipher';
|
||||||
|
|
||||||
@@ -7,6 +8,7 @@ import { AttachmentView } from './attachmentView';
|
|||||||
import { CardView } from './cardView';
|
import { CardView } from './cardView';
|
||||||
import { FieldView } from './fieldView';
|
import { FieldView } from './fieldView';
|
||||||
import { IdentityView } from './identityView';
|
import { IdentityView } from './identityView';
|
||||||
|
import { ItemView } from './itemView';
|
||||||
import { LoginView } from './loginView';
|
import { LoginView } from './loginView';
|
||||||
import { PasswordHistoryView } from './passwordHistoryView';
|
import { PasswordHistoryView } from './passwordHistoryView';
|
||||||
import { SecureNoteView } from './secureNoteView';
|
import { SecureNoteView } from './secureNoteView';
|
||||||
@@ -57,16 +59,16 @@ export class CipherView implements View {
|
|||||||
this.reprompt = c.reprompt ?? CipherRepromptType.None;
|
this.reprompt = c.reprompt ?? CipherRepromptType.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
get subTitle(): string {
|
private get item() {
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case CipherType.Login:
|
case CipherType.Login:
|
||||||
return this.login.subTitle;
|
return this.login;
|
||||||
case CipherType.SecureNote:
|
case CipherType.SecureNote:
|
||||||
return this.secureNote.subTitle;
|
return this.secureNote;
|
||||||
case CipherType.Card:
|
case CipherType.Card:
|
||||||
return this.card.subTitle;
|
return this.card;
|
||||||
case CipherType.Identity:
|
case CipherType.Identity:
|
||||||
return this.identity.subTitle;
|
return this.identity;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -74,6 +76,10 @@ export class CipherView implements View {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get subTitle(): string {
|
||||||
|
return this.item.subTitle;
|
||||||
|
}
|
||||||
|
|
||||||
get hasPasswordHistory(): boolean {
|
get hasPasswordHistory(): boolean {
|
||||||
return this.passwordHistory && this.passwordHistory.length > 0;
|
return this.passwordHistory && this.passwordHistory.length > 0;
|
||||||
}
|
}
|
||||||
@@ -109,4 +115,22 @@ export class CipherView implements View {
|
|||||||
get isDeleted(): boolean {
|
get isDeleted(): boolean {
|
||||||
return this.deletedDate != null;
|
return this.deletedDate != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get linkedFieldOptions() {
|
||||||
|
return this.item.linkedFieldOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
linkedFieldValue(id: LinkedIdType) {
|
||||||
|
const linkedFieldOption = this.linkedFieldOptions?.get(id);
|
||||||
|
if (linkedFieldOption == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = this.item;
|
||||||
|
return this.item[linkedFieldOption.propertyKey as keyof typeof item];
|
||||||
|
}
|
||||||
|
|
||||||
|
linkedFieldI18nKey(id: LinkedIdType): string {
|
||||||
|
return this.linkedFieldOptions.get(id)?.i18nKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { FieldType } from '../../enums/fieldType';
|
import { FieldType } from '../../enums/fieldType';
|
||||||
|
import { LinkedIdType } from '../../enums/linkedIdType';
|
||||||
|
|
||||||
import { View } from './view';
|
import { View } from './view';
|
||||||
|
|
||||||
@@ -10,6 +11,7 @@ export class FieldView implements View {
|
|||||||
type: FieldType = null;
|
type: FieldType = null;
|
||||||
newField: boolean = false; // Marks if the field is new and hasn't been saved
|
newField: boolean = false; // Marks if the field is new and hasn't been saved
|
||||||
showValue: boolean = false;
|
showValue: boolean = false;
|
||||||
|
linkedId: LinkedIdType = null;
|
||||||
|
|
||||||
constructor(f?: Field) {
|
constructor(f?: Field) {
|
||||||
if (!f) {
|
if (!f) {
|
||||||
@@ -17,6 +19,7 @@ export class FieldView implements View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.type = f.type;
|
this.type = f.type;
|
||||||
|
this.linkedId = f.linkedId;
|
||||||
}
|
}
|
||||||
|
|
||||||
get maskedValue(): string {
|
get maskedValue(): string {
|
||||||
|
|||||||
@@ -1,25 +1,45 @@
|
|||||||
import { View } from './view';
|
import { ItemView } from './itemView';
|
||||||
|
|
||||||
import { Identity } from '../domain/identity';
|
import { Identity } from '../domain/identity';
|
||||||
|
|
||||||
import { Utils } from '../../misc/utils';
|
import { Utils } from '../../misc/utils';
|
||||||
|
|
||||||
export class IdentityView implements View {
|
import { IdentityLinkedId as LinkedId } from '../../enums/linkedIdType';
|
||||||
|
|
||||||
|
import { linkedFieldOption } from '../../misc/linkedFieldOption.decorator';
|
||||||
|
|
||||||
|
export class IdentityView extends ItemView {
|
||||||
|
@linkedFieldOption(LinkedId.Title)
|
||||||
title: string = null;
|
title: string = null;
|
||||||
|
@linkedFieldOption(LinkedId.MiddleName)
|
||||||
middleName: string = null;
|
middleName: string = null;
|
||||||
|
@linkedFieldOption(LinkedId.Address1)
|
||||||
address1: string = null;
|
address1: string = null;
|
||||||
|
@linkedFieldOption(LinkedId.Address2)
|
||||||
address2: string = null;
|
address2: string = null;
|
||||||
|
@linkedFieldOption(LinkedId.Address3)
|
||||||
address3: string = null;
|
address3: string = null;
|
||||||
|
@linkedFieldOption(LinkedId.City, 'cityTown')
|
||||||
city: string = null;
|
city: string = null;
|
||||||
|
@linkedFieldOption(LinkedId.State, 'stateProvince')
|
||||||
state: string = null;
|
state: string = null;
|
||||||
|
@linkedFieldOption(LinkedId.PostalCode, 'zipPostalCode')
|
||||||
postalCode: string = null;
|
postalCode: string = null;
|
||||||
|
@linkedFieldOption(LinkedId.Country)
|
||||||
country: string = null;
|
country: string = null;
|
||||||
|
@linkedFieldOption(LinkedId.Company)
|
||||||
company: string = null;
|
company: string = null;
|
||||||
|
@linkedFieldOption(LinkedId.Email)
|
||||||
email: string = null;
|
email: string = null;
|
||||||
|
@linkedFieldOption(LinkedId.Phone)
|
||||||
phone: string = null;
|
phone: string = null;
|
||||||
|
@linkedFieldOption(LinkedId.Ssn)
|
||||||
ssn: string = null;
|
ssn: string = null;
|
||||||
|
@linkedFieldOption(LinkedId.Username)
|
||||||
username: string = null;
|
username: string = null;
|
||||||
|
@linkedFieldOption(LinkedId.PassportNumber)
|
||||||
passportNumber: string = null;
|
passportNumber: string = null;
|
||||||
|
@linkedFieldOption(LinkedId.LicenseNumber)
|
||||||
licenseNumber: string = null;
|
licenseNumber: string = null;
|
||||||
|
|
||||||
// tslint:disable
|
// tslint:disable
|
||||||
@@ -29,9 +49,10 @@ export class IdentityView implements View {
|
|||||||
// tslint:enable
|
// tslint:enable
|
||||||
|
|
||||||
constructor(i?: Identity) {
|
constructor(i?: Identity) {
|
||||||
// ctor
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@linkedFieldOption(LinkedId.FirstName)
|
||||||
get firstName(): string {
|
get firstName(): string {
|
||||||
return this._firstName;
|
return this._firstName;
|
||||||
}
|
}
|
||||||
@@ -40,6 +61,7 @@ export class IdentityView implements View {
|
|||||||
this._subTitle = null;
|
this._subTitle = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@linkedFieldOption(LinkedId.LastName)
|
||||||
get lastName(): string {
|
get lastName(): string {
|
||||||
return this._lastName;
|
return this._lastName;
|
||||||
}
|
}
|
||||||
@@ -65,6 +87,7 @@ export class IdentityView implements View {
|
|||||||
return this._subTitle;
|
return this._subTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@linkedFieldOption(LinkedId.FullName)
|
||||||
get fullName(): string {
|
get fullName(): string {
|
||||||
if (this.title != null || this.firstName != null || this.middleName != null || this.lastName != null) {
|
if (this.title != null || this.firstName != null || this.middleName != null || this.lastName != null) {
|
||||||
let name = '';
|
let name = '';
|
||||||
|
|||||||
8
common/src/models/view/itemView.ts
Normal file
8
common/src/models/view/itemView.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { View } from './view';
|
||||||
|
|
||||||
|
import { LinkedMetadata } from '../../misc/linkedFieldOption.decorator';
|
||||||
|
|
||||||
|
export abstract class ItemView implements View {
|
||||||
|
linkedFieldOptions: Map<number, LinkedMetadata>;
|
||||||
|
abstract get subTitle(): string;
|
||||||
|
}
|
||||||
@@ -1,18 +1,27 @@
|
|||||||
|
import { ItemView } from './itemView';
|
||||||
import { LoginUriView } from './loginUriView';
|
import { LoginUriView } from './loginUriView';
|
||||||
import { View } from './view';
|
|
||||||
|
|
||||||
import { Utils } from '../../misc/utils';
|
import { Utils } from '../../misc/utils';
|
||||||
|
|
||||||
import { Login } from '../domain/login';
|
import { Login } from '../domain/login';
|
||||||
|
|
||||||
export class LoginView implements View {
|
import { LoginLinkedId as LinkedId } from '../../enums/linkedIdType';
|
||||||
|
|
||||||
|
import { linkedFieldOption } from '../../misc/linkedFieldOption.decorator';
|
||||||
|
|
||||||
|
export class LoginView extends ItemView {
|
||||||
|
@linkedFieldOption(LinkedId.Username)
|
||||||
username: string = null;
|
username: string = null;
|
||||||
|
@linkedFieldOption(LinkedId.Password)
|
||||||
password: string = null;
|
password: string = null;
|
||||||
|
|
||||||
passwordRevisionDate?: Date = null;
|
passwordRevisionDate?: Date = null;
|
||||||
totp: string = null;
|
totp: string = null;
|
||||||
uris: LoginUriView[] = null;
|
uris: LoginUriView[] = null;
|
||||||
autofillOnPageLoad: boolean = null;
|
autofillOnPageLoad: boolean = null;
|
||||||
|
|
||||||
constructor(l?: Login) {
|
constructor(l?: Login) {
|
||||||
|
super();
|
||||||
if (!l) {
|
if (!l) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { SecureNoteType } from '../../enums/secureNoteType';
|
import { SecureNoteType } from '../../enums/secureNoteType';
|
||||||
|
|
||||||
import { View } from './view';
|
import { ItemView } from './itemView';
|
||||||
|
|
||||||
import { SecureNote } from '../domain/secureNote';
|
import { SecureNote } from '../domain/secureNote';
|
||||||
|
|
||||||
export class SecureNoteView implements View {
|
export class SecureNoteView extends ItemView {
|
||||||
type: SecureNoteType = null;
|
type: SecureNoteType = null;
|
||||||
|
|
||||||
constructor(n?: SecureNote) {
|
constructor(n?: SecureNote) {
|
||||||
|
super();
|
||||||
if (!n) {
|
if (!n) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ import { OrganizationUserUpdateGroupsRequest } from '../models/request/organizat
|
|||||||
import { OrganizationUserUpdateRequest } from '../models/request/organizationUserUpdateRequest';
|
import { OrganizationUserUpdateRequest } from '../models/request/organizationUserUpdateRequest';
|
||||||
import { PasswordHintRequest } from '../models/request/passwordHintRequest';
|
import { PasswordHintRequest } from '../models/request/passwordHintRequest';
|
||||||
import { PasswordRequest } from '../models/request/passwordRequest';
|
import { PasswordRequest } from '../models/request/passwordRequest';
|
||||||
import { PasswordVerificationRequest } from '../models/request/passwordVerificationRequest';
|
|
||||||
import { PaymentRequest } from '../models/request/paymentRequest';
|
import { PaymentRequest } from '../models/request/paymentRequest';
|
||||||
import { PolicyRequest } from '../models/request/policyRequest';
|
import { PolicyRequest } from '../models/request/policyRequest';
|
||||||
import { PreloginRequest } from '../models/request/preloginRequest';
|
import { PreloginRequest } from '../models/request/preloginRequest';
|
||||||
@@ -68,6 +67,7 @@ import { ProviderUserInviteRequest } from '../models/request/provider/providerUs
|
|||||||
import { ProviderUserUpdateRequest } from '../models/request/provider/providerUserUpdateRequest';
|
import { ProviderUserUpdateRequest } from '../models/request/provider/providerUserUpdateRequest';
|
||||||
import { RegisterRequest } from '../models/request/registerRequest';
|
import { RegisterRequest } from '../models/request/registerRequest';
|
||||||
import { SeatRequest } from '../models/request/seatRequest';
|
import { SeatRequest } from '../models/request/seatRequest';
|
||||||
|
import { SecretVerificationRequest } from '../models/request/secretVerificationRequest';
|
||||||
import { SelectionReadOnlyRequest } from '../models/request/selectionReadOnlyRequest';
|
import { SelectionReadOnlyRequest } from '../models/request/selectionReadOnlyRequest';
|
||||||
import { SendAccessRequest } from '../models/request/sendAccessRequest';
|
import { SendAccessRequest } from '../models/request/sendAccessRequest';
|
||||||
import { SendRequest } from '../models/request/sendRequest';
|
import { SendRequest } from '../models/request/sendRequest';
|
||||||
@@ -166,9 +166,10 @@ import { ChallengeResponse } from '../models/response/twoFactorWebAuthnResponse'
|
|||||||
import { TwoFactorYubiKeyResponse } from '../models/response/twoFactorYubiKeyResponse';
|
import { TwoFactorYubiKeyResponse } from '../models/response/twoFactorYubiKeyResponse';
|
||||||
import { UserKeyResponse } from '../models/response/userKeyResponse';
|
import { UserKeyResponse } from '../models/response/userKeyResponse';
|
||||||
|
|
||||||
import { SetCryptoAgentKeyRequest } from '../models/request/account/setCryptoAgentKeyRequest';
|
import { SetKeyConnectorKeyRequest } from '../models/request/account/setKeyConnectorKeyRequest';
|
||||||
import { CryptoAgentUserKeyRequest } from '../models/request/cryptoAgentUserKeyRequest';
|
import { VerifyOTPRequest } from '../models/request/account/verifyOTPRequest';
|
||||||
import { CryptoAgentUserKeyResponse } from '../models/response/cryptoAgentUserKeyResponse';
|
import { KeyConnectorUserKeyRequest } from '../models/request/keyConnectorUserKeyRequest';
|
||||||
|
import { KeyConnectorUserKeyResponse } from '../models/response/keyConnectorUserKeyResponse';
|
||||||
import { SendAccessView } from '../models/view/sendAccessView';
|
import { SendAccessView } from '../models/view/sendAccessView';
|
||||||
|
|
||||||
export class ApiService implements ApiServiceAbstraction {
|
export class ApiService implements ApiServiceAbstraction {
|
||||||
@@ -292,15 +293,15 @@ export class ApiService implements ApiServiceAbstraction {
|
|||||||
return this.send('POST', '/accounts/set-password', request, true, false);
|
return this.send('POST', '/accounts/set-password', request, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
postSetCryptoAgentKey(request: SetCryptoAgentKeyRequest): Promise<any> {
|
postSetKeyConnectorKey(request: SetKeyConnectorKeyRequest): Promise<any> {
|
||||||
return this.send('POST', '/accounts/set-crypto-agent-key', request, true, false);
|
return this.send('POST', '/accounts/set-key-connector-key', request, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
postSecurityStamp(request: PasswordVerificationRequest): Promise<any> {
|
postSecurityStamp(request: SecretVerificationRequest): Promise<any> {
|
||||||
return this.send('POST', '/accounts/security-stamp', request, true, false);
|
return this.send('POST', '/accounts/security-stamp', request, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteAccount(request: PasswordVerificationRequest): Promise<any> {
|
deleteAccount(request: SecretVerificationRequest): Promise<any> {
|
||||||
return this.send('DELETE', '/accounts', request, true, false);
|
return this.send('DELETE', '/accounts', request, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,7 +364,7 @@ export class ApiService implements ApiServiceAbstraction {
|
|||||||
return this.send('POST', '/accounts/verify-email-token', request, false, false);
|
return this.send('POST', '/accounts/verify-email-token', request, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
postAccountVerifyPassword(request: PasswordVerificationRequest): Promise<any> {
|
postAccountVerifyPassword(request: SecretVerificationRequest): Promise<any> {
|
||||||
return this.send('POST', '/accounts/verify-password', request, true, false);
|
return this.send('POST', '/accounts/verify-password', request, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,12 +388,12 @@ export class ApiService implements ApiServiceAbstraction {
|
|||||||
return this.send('GET', '/accounts/sso/user-identifier', null, true, true);
|
return this.send('GET', '/accounts/sso/user-identifier', null, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async postUserApiKey(id: string, request: PasswordVerificationRequest): Promise<ApiKeyResponse> {
|
async postUserApiKey(id: string, request: SecretVerificationRequest): Promise<ApiKeyResponse> {
|
||||||
const r = await this.send('POST', '/accounts/api-key', request, true, true);
|
const r = await this.send('POST', '/accounts/api-key', request, true, true);
|
||||||
return new ApiKeyResponse(r);
|
return new ApiKeyResponse(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
async postUserRotateApiKey(id: string, request: PasswordVerificationRequest): Promise<ApiKeyResponse> {
|
async postUserRotateApiKey(id: string, request: SecretVerificationRequest): Promise<ApiKeyResponse> {
|
||||||
const r = await this.send('POST', '/accounts/rotate-api-key', request, true, true);
|
const r = await this.send('POST', '/accounts/rotate-api-key', request, true, true);
|
||||||
return new ApiKeyResponse(r);
|
return new ApiKeyResponse(r);
|
||||||
}
|
}
|
||||||
@@ -401,6 +402,18 @@ export class ApiService implements ApiServiceAbstraction {
|
|||||||
return this.send('PUT', '/accounts/update-temp-password', request, true, false);
|
return this.send('PUT', '/accounts/update-temp-password', request, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
postAccountRequestOTP(): Promise<void> {
|
||||||
|
return this.send('POST', '/accounts/request-otp', null, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
postAccountVerifyOTP(request: VerifyOTPRequest): Promise<void> {
|
||||||
|
return this.send('POST', '/accounts/verify-otp', request, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
postConvertToKeyConnector(): Promise<void> {
|
||||||
|
return this.send('POST', '/accounts/convert-to-key-connector', null, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
// Folder APIs
|
// Folder APIs
|
||||||
|
|
||||||
async getFolder(id: string): Promise<FolderResponse> {
|
async getFolder(id: string): Promise<FolderResponse> {
|
||||||
@@ -573,7 +586,7 @@ export class ApiService implements ApiServiceAbstraction {
|
|||||||
return this.send('PUT', '/ciphers/' + id + '/collections-admin', request, true, false);
|
return this.send('PUT', '/ciphers/' + id + '/collections-admin', request, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
postPurgeCiphers(request: PasswordVerificationRequest, organizationId: string = null): Promise<any> {
|
postPurgeCiphers(request: SecretVerificationRequest, organizationId: string = null): Promise<any> {
|
||||||
let path = '/ciphers/purge';
|
let path = '/ciphers/purge';
|
||||||
if (organizationId != null) {
|
if (organizationId != null) {
|
||||||
path += '?organizationId=' + organizationId;
|
path += '?organizationId=' + organizationId;
|
||||||
@@ -939,44 +952,44 @@ export class ApiService implements ApiServiceAbstraction {
|
|||||||
return new ListResponse(r, TwoFactorProviderResponse);
|
return new ListResponse(r, TwoFactorProviderResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTwoFactorAuthenticator(request: PasswordVerificationRequest): Promise<TwoFactorAuthenticatorResponse> {
|
async getTwoFactorAuthenticator(request: SecretVerificationRequest): Promise<TwoFactorAuthenticatorResponse> {
|
||||||
const r = await this.send('POST', '/two-factor/get-authenticator', request, true, true);
|
const r = await this.send('POST', '/two-factor/get-authenticator', request, true, true);
|
||||||
return new TwoFactorAuthenticatorResponse(r);
|
return new TwoFactorAuthenticatorResponse(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTwoFactorEmail(request: PasswordVerificationRequest): Promise<TwoFactorEmailResponse> {
|
async getTwoFactorEmail(request: SecretVerificationRequest): Promise<TwoFactorEmailResponse> {
|
||||||
const r = await this.send('POST', '/two-factor/get-email', request, true, true);
|
const r = await this.send('POST', '/two-factor/get-email', request, true, true);
|
||||||
return new TwoFactorEmailResponse(r);
|
return new TwoFactorEmailResponse(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTwoFactorDuo(request: PasswordVerificationRequest): Promise<TwoFactorDuoResponse> {
|
async getTwoFactorDuo(request: SecretVerificationRequest): Promise<TwoFactorDuoResponse> {
|
||||||
const r = await this.send('POST', '/two-factor/get-duo', request, true, true);
|
const r = await this.send('POST', '/two-factor/get-duo', request, true, true);
|
||||||
return new TwoFactorDuoResponse(r);
|
return new TwoFactorDuoResponse(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTwoFactorOrganizationDuo(organizationId: string,
|
async getTwoFactorOrganizationDuo(organizationId: string,
|
||||||
request: PasswordVerificationRequest): Promise<TwoFactorDuoResponse> {
|
request: SecretVerificationRequest): Promise<TwoFactorDuoResponse> {
|
||||||
const r = await this.send('POST', '/organizations/' + organizationId + '/two-factor/get-duo',
|
const r = await this.send('POST', '/organizations/' + organizationId + '/two-factor/get-duo',
|
||||||
request, true, true);
|
request, true, true);
|
||||||
return new TwoFactorDuoResponse(r);
|
return new TwoFactorDuoResponse(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTwoFactorYubiKey(request: PasswordVerificationRequest): Promise<TwoFactorYubiKeyResponse> {
|
async getTwoFactorYubiKey(request: SecretVerificationRequest): Promise<TwoFactorYubiKeyResponse> {
|
||||||
const r = await this.send('POST', '/two-factor/get-yubikey', request, true, true);
|
const r = await this.send('POST', '/two-factor/get-yubikey', request, true, true);
|
||||||
return new TwoFactorYubiKeyResponse(r);
|
return new TwoFactorYubiKeyResponse(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTwoFactorWebAuthn(request: PasswordVerificationRequest): Promise<TwoFactorWebAuthnResponse> {
|
async getTwoFactorWebAuthn(request: SecretVerificationRequest): Promise<TwoFactorWebAuthnResponse> {
|
||||||
const r = await this.send('POST', '/two-factor/get-webauthn', request, true, true);
|
const r = await this.send('POST', '/two-factor/get-webauthn', request, true, true);
|
||||||
return new TwoFactorWebAuthnResponse(r);
|
return new TwoFactorWebAuthnResponse(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTwoFactorWebAuthnChallenge(request: PasswordVerificationRequest): Promise<ChallengeResponse> {
|
async getTwoFactorWebAuthnChallenge(request: SecretVerificationRequest): Promise<ChallengeResponse> {
|
||||||
const r = await this.send('POST', '/two-factor/get-webauthn-challenge', request, true, true);
|
const r = await this.send('POST', '/two-factor/get-webauthn-challenge', request, true, true);
|
||||||
return new ChallengeResponse(r);
|
return new ChallengeResponse(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTwoFactorRecover(request: PasswordVerificationRequest): Promise<TwoFactorRecoverResponse> {
|
async getTwoFactorRecover(request: SecretVerificationRequest): Promise<TwoFactorRecoverResponse> {
|
||||||
const r = await this.send('POST', '/two-factor/get-recover', request, true, true);
|
const r = await this.send('POST', '/two-factor/get-recover', request, true, true);
|
||||||
return new TwoFactorRecoverResponse(r);
|
return new TwoFactorRecoverResponse(r);
|
||||||
}
|
}
|
||||||
@@ -1187,12 +1200,12 @@ export class ApiService implements ApiServiceAbstraction {
|
|||||||
return this.send('POST', '/organizations/' + id + '/license', data, true, false);
|
return this.send('POST', '/organizations/' + id + '/license', data, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async postOrganizationApiKey(id: string, request: PasswordVerificationRequest): Promise<ApiKeyResponse> {
|
async postOrganizationApiKey(id: string, request: SecretVerificationRequest): Promise<ApiKeyResponse> {
|
||||||
const r = await this.send('POST', '/organizations/' + id + '/api-key', request, true, true);
|
const r = await this.send('POST', '/organizations/' + id + '/api-key', request, true, true);
|
||||||
return new ApiKeyResponse(r);
|
return new ApiKeyResponse(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
async postOrganizationRotateApiKey(id: string, request: PasswordVerificationRequest): Promise<ApiKeyResponse> {
|
async postOrganizationRotateApiKey(id: string, request: SecretVerificationRequest): Promise<ApiKeyResponse> {
|
||||||
const r = await this.send('POST', '/organizations/' + id + '/rotate-api-key', request, true, true);
|
const r = await this.send('POST', '/organizations/' + id + '/rotate-api-key', request, true, true);
|
||||||
return new ApiKeyResponse(r);
|
return new ApiKeyResponse(r);
|
||||||
}
|
}
|
||||||
@@ -1237,7 +1250,7 @@ export class ApiService implements ApiServiceAbstraction {
|
|||||||
return this.send('POST', '/organizations/' + id + '/reinstate', null, true, false);
|
return this.send('POST', '/organizations/' + id + '/reinstate', null, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteOrganization(id: string, request: PasswordVerificationRequest): Promise<any> {
|
deleteOrganization(id: string, request: SecretVerificationRequest): Promise<any> {
|
||||||
return this.send('DELETE', '/organizations/' + id, request, true, false);
|
return this.send('DELETE', '/organizations/' + id, request, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1436,12 +1449,12 @@ export class ApiService implements ApiServiceAbstraction {
|
|||||||
return r as string;
|
return r as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crypto Agent
|
// Key Connector
|
||||||
|
|
||||||
async getUserKeyFromCryptoAgent(cryptoAgentUrl: string): Promise<CryptoAgentUserKeyResponse> {
|
async getUserKeyFromKeyConnector(keyConnectorUrl: string): Promise<KeyConnectorUserKeyResponse> {
|
||||||
const authHeader = await this.getActiveBearerToken();
|
const authHeader = await this.getActiveBearerToken();
|
||||||
|
|
||||||
const response = await this.fetch(new Request(cryptoAgentUrl + '/user-keys', {
|
const response = await this.fetch(new Request(keyConnectorUrl + '/user-keys', {
|
||||||
cache: 'no-store',
|
cache: 'no-store',
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: new Headers({
|
headers: new Headers({
|
||||||
@@ -1455,13 +1468,13 @@ export class ApiService implements ApiServiceAbstraction {
|
|||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CryptoAgentUserKeyResponse(await response.json());
|
return new KeyConnectorUserKeyResponse(await response.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
async postUserKeyToCryptoAgent(cryptoAgentUrl: string, request: CryptoAgentUserKeyRequest): Promise<void> {
|
async postUserKeyToKeyConnector(keyConnectorUrl: string, request: KeyConnectorUserKeyRequest): Promise<void> {
|
||||||
const authHeader = await this.getActiveBearerToken();
|
const authHeader = await this.getActiveBearerToken();
|
||||||
|
|
||||||
const response = await this.fetch(new Request(cryptoAgentUrl + '/user-keys', {
|
const response = await this.fetch(new Request(keyConnectorUrl + '/user-keys', {
|
||||||
cache: 'no-store',
|
cache: 'no-store',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: new Headers({
|
headers: new Headers({
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import { Account } from '../models/domain/account';
|
|||||||
import { AuthResult } from '../models/domain/authResult';
|
import { AuthResult } from '../models/domain/authResult';
|
||||||
import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey';
|
import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey';
|
||||||
|
|
||||||
import { SetCryptoAgentKeyRequest } from '../models/request/account/setCryptoAgentKeyRequest';
|
import { SetKeyConnectorKeyRequest } from '../models/request/account/setKeyConnectorKeyRequest';
|
||||||
import { CryptoAgentUserKeyRequest } from '../models/request/cryptoAgentUserKeyRequest';
|
|
||||||
import { DeviceRequest } from '../models/request/deviceRequest';
|
import { DeviceRequest } from '../models/request/deviceRequest';
|
||||||
|
import { KeyConnectorUserKeyRequest } from '../models/request/keyConnectorUserKeyRequest';
|
||||||
import { KeysRequest } from '../models/request/keysRequest';
|
import { KeysRequest } from '../models/request/keysRequest';
|
||||||
import { PreloginRequest } from '../models/request/preloginRequest';
|
import { PreloginRequest } from '../models/request/preloginRequest';
|
||||||
import { TokenRequest } from '../models/request/tokenRequest';
|
import { TokenRequest } from '../models/request/tokenRequest';
|
||||||
@@ -21,7 +21,9 @@ import { AppIdService } from '../abstractions/appId.service';
|
|||||||
import { AuthService as AuthServiceAbstraction } from '../abstractions/auth.service';
|
import { AuthService as AuthServiceAbstraction } from '../abstractions/auth.service';
|
||||||
import { CryptoService } from '../abstractions/crypto.service';
|
import { CryptoService } from '../abstractions/crypto.service';
|
||||||
import { CryptoFunctionService } from '../abstractions/cryptoFunction.service';
|
import { CryptoFunctionService } from '../abstractions/cryptoFunction.service';
|
||||||
|
import { EnvironmentService } from '../abstractions/environment.service';
|
||||||
import { I18nService } from '../abstractions/i18n.service';
|
import { I18nService } from '../abstractions/i18n.service';
|
||||||
|
import { KeyConnectorService } from '../abstractions/keyConnector.service';
|
||||||
import { LogService } from '../abstractions/log.service';
|
import { LogService } from '../abstractions/log.service';
|
||||||
import { MessagingService } from '../abstractions/messaging.service';
|
import { MessagingService } from '../abstractions/messaging.service';
|
||||||
import { PlatformUtilsService } from '../abstractions/platformUtils.service';
|
import { PlatformUtilsService } from '../abstractions/platformUtils.service';
|
||||||
@@ -102,6 +104,7 @@ export class AuthService implements AuthServiceAbstraction {
|
|||||||
private i18nService: I18nService, protected platformUtilsService: PlatformUtilsService,
|
private i18nService: I18nService, protected platformUtilsService: PlatformUtilsService,
|
||||||
private messagingService: MessagingService, private vaultTimeoutService: VaultTimeoutService,
|
private messagingService: MessagingService, private vaultTimeoutService: VaultTimeoutService,
|
||||||
private logService: LogService, protected cryptoFunctionService: CryptoFunctionService,
|
private logService: LogService, protected cryptoFunctionService: CryptoFunctionService,
|
||||||
|
private keyConnectorService: KeyConnectorService,
|
||||||
protected stateService: StateService, private setCryptoKeys = true) {
|
protected stateService: StateService, private setCryptoKeys = true) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,16 +374,10 @@ export class AuthService implements AuthServiceAbstraction {
|
|||||||
// Skip this step during SSO new user flow. No key is returned from server.
|
// Skip this step during SSO new user flow. No key is returned from server.
|
||||||
if (code == null || tokenResponse.key != null) {
|
if (code == null || tokenResponse.key != null) {
|
||||||
|
|
||||||
if (tokenResponse.cryptoAgentUrl != null) {
|
if (tokenResponse.keyConnectorUrl != null) {
|
||||||
try {
|
await this.keyConnectorService.getAndSetKey(tokenResponse.keyConnectorUrl);
|
||||||
const userKeyResponse = await this.apiService.getUserKeyFromCryptoAgent(tokenResponse.cryptoAgentUrl);
|
} else if (this.environmentService.getKeyConnectorUrl() != null) {
|
||||||
const keyArr = Utils.fromB64ToArray(userKeyResponse.key);
|
await this.keyConnectorService.getAndSetKey();
|
||||||
const k = new SymmetricCryptoKey(keyArr);
|
|
||||||
await this.cryptoService.setKey(k);
|
|
||||||
} catch (e) {
|
|
||||||
this.logService.error(e);
|
|
||||||
throw new Error('Unable to reach crypto agent');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.cryptoService.setEncKey(tokenResponse.key);
|
await this.cryptoService.setEncKey(tokenResponse.key);
|
||||||
@@ -397,11 +394,11 @@ export class AuthService implements AuthServiceAbstraction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await this.cryptoService.setEncPrivateKey(tokenResponse.privateKey);
|
await this.cryptoService.setEncPrivateKey(tokenResponse.privateKey);
|
||||||
} else if (tokenResponse.cryptoAgentUrl != null) {
|
} else if (tokenResponse.keyConnectorUrl != null) {
|
||||||
const password = await this.cryptoFunctionService.randomBytes(64);
|
const password = await this.cryptoFunctionService.randomBytes(64);
|
||||||
|
|
||||||
const k = await this.cryptoService.makeKey(Utils.fromBufferToB64(password), await this.tokenService.getEmail(), tokenResponse.kdf, tokenResponse.kdfIterations);
|
const k = await this.cryptoService.makeKey(Utils.fromBufferToB64(password), await this.tokenService.getEmail(), tokenResponse.kdf, tokenResponse.kdfIterations);
|
||||||
const cryptoAgentRequest = new CryptoAgentUserKeyRequest(k.encKeyB64);
|
const keyConnectorRequest = new KeyConnectorUserKeyRequest(k.encKeyB64);
|
||||||
await this.cryptoService.setKey(k);
|
await this.cryptoService.setKey(k);
|
||||||
|
|
||||||
const encKey = await this.cryptoService.makeEncKey(k);
|
const encKey = await this.cryptoService.makeEncKey(k);
|
||||||
@@ -410,16 +407,16 @@ export class AuthService implements AuthServiceAbstraction {
|
|||||||
const [pubKey, privKey] = await this.cryptoService.makeKeyPair();
|
const [pubKey, privKey] = await this.cryptoService.makeKeyPair();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.apiService.postUserKeyToCryptoAgent(tokenResponse.cryptoAgentUrl, cryptoAgentRequest);
|
await this.apiService.postUserKeyToKeyConnector(tokenResponse.keyConnectorUrl, keyConnectorRequest);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error('Unable to reach crypto agent');
|
throw new Error('Unable to reach key connector');
|
||||||
}
|
}
|
||||||
|
|
||||||
const keys = new KeysRequest(pubKey, privKey.encryptedString);
|
const keys = new KeysRequest(pubKey, privKey.encryptedString);
|
||||||
const setPasswordRequest = new SetCryptoAgentKeyRequest(
|
const setPasswordRequest = new SetKeyConnectorKeyRequest(
|
||||||
encKey[1].encryptedString, tokenResponse.kdf, tokenResponse.kdfIterations, orgId, keys
|
encKey[1].encryptedString, tokenResponse.kdf, tokenResponse.kdfIterations, orgId, keys
|
||||||
);
|
);
|
||||||
await this.apiService.postSetCryptoAgentKey(setPasswordRequest);
|
await this.apiService.postSetKeyConnectorKey(setPasswordRequest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -212,6 +212,7 @@ export class CipherService implements CipherServiceAbstraction {
|
|||||||
async encryptField(fieldModel: FieldView, key: SymmetricCryptoKey): Promise<Field> {
|
async encryptField(fieldModel: FieldView, key: SymmetricCryptoKey): Promise<Field> {
|
||||||
const field = new Field();
|
const field = new Field();
|
||||||
field.type = fieldModel.type;
|
field.type = fieldModel.type;
|
||||||
|
field.linkedId = fieldModel.linkedId;
|
||||||
// normalize boolean type field values
|
// normalize boolean type field values
|
||||||
if (fieldModel.type === FieldType.Boolean && fieldModel.value !== 'true') {
|
if (fieldModel.type === FieldType.Boolean && fieldModel.value !== 'true') {
|
||||||
fieldModel.value = 'false';
|
fieldModel.value = 'false';
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
|||||||
private iconsUrl: string;
|
private iconsUrl: string;
|
||||||
private notificationsUrl: string;
|
private notificationsUrl: string;
|
||||||
private eventsUrl: string;
|
private eventsUrl: string;
|
||||||
|
private keyConnectorUrl: string;
|
||||||
|
|
||||||
constructor(private stateService: StateService) {}
|
constructor(private stateService: StateService) {}
|
||||||
|
|
||||||
@@ -101,6 +102,10 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
|||||||
return 'https://events.bitwarden.com';
|
return 'https://events.bitwarden.com';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getKeyConnectorUrl() {
|
||||||
|
return this.keyConnectorUrl;
|
||||||
|
}
|
||||||
|
|
||||||
async setUrlsFromStorage(): Promise<void> {
|
async setUrlsFromStorage(): Promise<void> {
|
||||||
const urlsObj: any = await this.stateService.getEnvironmentUrls();
|
const urlsObj: any = await this.stateService.getEnvironmentUrls();
|
||||||
const urls = urlsObj || {
|
const urls = urlsObj || {
|
||||||
@@ -111,6 +116,7 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
|||||||
notifications: null,
|
notifications: null,
|
||||||
events: null,
|
events: null,
|
||||||
webVault: null,
|
webVault: null,
|
||||||
|
keyConnector: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const envUrls = new EnvironmentUrls();
|
const envUrls = new EnvironmentUrls();
|
||||||
@@ -126,6 +132,7 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
|||||||
this.iconsUrl = urls.icons;
|
this.iconsUrl = urls.icons;
|
||||||
this.notificationsUrl = urls.notifications;
|
this.notificationsUrl = urls.notifications;
|
||||||
this.eventsUrl = envUrls.events = urls.events;
|
this.eventsUrl = envUrls.events = urls.events;
|
||||||
|
this.keyConnectorUrl = urls.keyConnector;
|
||||||
}
|
}
|
||||||
|
|
||||||
async setUrls(urls: Urls, saveSettings: boolean = true): Promise<any> {
|
async setUrls(urls: Urls, saveSettings: boolean = true): Promise<any> {
|
||||||
@@ -136,6 +143,7 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
|||||||
urls.icons = this.formatUrl(urls.icons);
|
urls.icons = this.formatUrl(urls.icons);
|
||||||
urls.notifications = this.formatUrl(urls.notifications);
|
urls.notifications = this.formatUrl(urls.notifications);
|
||||||
urls.events = this.formatUrl(urls.events);
|
urls.events = this.formatUrl(urls.events);
|
||||||
|
urls.keyConnector = this.formatUrl(urls.keyConnector);
|
||||||
|
|
||||||
if (saveSettings) {
|
if (saveSettings) {
|
||||||
await this.stateService.setEnvironmentUrls({
|
await this.stateService.setEnvironmentUrls({
|
||||||
@@ -146,6 +154,7 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
|||||||
icons: urls.icons,
|
icons: urls.icons,
|
||||||
notifications: urls.notifications,
|
notifications: urls.notifications,
|
||||||
events: urls.events,
|
events: urls.events,
|
||||||
|
keyConnector: urls.keyConnector,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,6 +165,7 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
|||||||
this.iconsUrl = urls.icons;
|
this.iconsUrl = urls.icons;
|
||||||
this.notificationsUrl = urls.notifications;
|
this.notificationsUrl = urls.notifications;
|
||||||
this.eventsUrl = urls.events;
|
this.eventsUrl = urls.events;
|
||||||
|
this.keyConnectorUrl = urls.keyConnector;
|
||||||
|
|
||||||
this.urlsSubject.next(urls);
|
this.urlsSubject.next(urls);
|
||||||
|
|
||||||
@@ -171,6 +181,7 @@ export class EnvironmentService implements EnvironmentServiceAbstraction {
|
|||||||
icons: this.iconsUrl,
|
icons: this.iconsUrl,
|
||||||
notifications: this.notificationsUrl,
|
notifications: this.notificationsUrl,
|
||||||
events: this.eventsUrl,
|
events: this.eventsUrl,
|
||||||
|
keyConnector: this.keyConnectorUrl,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
106
common/src/services/keyConnector.service.ts
Normal file
106
common/src/services/keyConnector.service.ts
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import { ApiService } from '../abstractions/api.service';
|
||||||
|
import { CryptoService } from '../abstractions/crypto.service';
|
||||||
|
import { EnvironmentService } from '../abstractions/environment.service';
|
||||||
|
import { KeyConnectorService as KeyConnectorServiceAbstraction } from '../abstractions/keyConnector.service';
|
||||||
|
import { LogService } from '../abstractions/log.service';
|
||||||
|
import { StorageService } from '../abstractions/storage.service';
|
||||||
|
import { TokenService } from '../abstractions/token.service';
|
||||||
|
import { UserService } from '../abstractions/user.service';
|
||||||
|
|
||||||
|
import { OrganizationUserType } from '../enums/organizationUserType';
|
||||||
|
|
||||||
|
import { Utils } from '../misc/utils';
|
||||||
|
|
||||||
|
import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey';
|
||||||
|
|
||||||
|
import { KeyConnectorUserKeyRequest } from '../models/request/keyConnectorUserKeyRequest';
|
||||||
|
|
||||||
|
const Keys = {
|
||||||
|
usesKeyConnector: 'usesKeyConnector',
|
||||||
|
convertAccountToKeyConnector: 'convertAccountToKeyConnector',
|
||||||
|
};
|
||||||
|
|
||||||
|
export class KeyConnectorService implements KeyConnectorServiceAbstraction {
|
||||||
|
private usesKeyConnector?: boolean = null;
|
||||||
|
|
||||||
|
constructor(private storageService: StorageService, private userService: UserService,
|
||||||
|
private cryptoService: CryptoService, private apiService: ApiService,
|
||||||
|
private environmentService: EnvironmentService, private tokenService: TokenService,
|
||||||
|
private logService: LogService) { }
|
||||||
|
|
||||||
|
setUsesKeyConnector(usesKeyConnector: boolean) {
|
||||||
|
this.usesKeyConnector = usesKeyConnector;
|
||||||
|
return this.storageService.save(Keys.usesKeyConnector, usesKeyConnector);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUsesKeyConnector(): Promise<boolean> {
|
||||||
|
return this.usesKeyConnector ??= await this.storageService.get<boolean>(Keys.usesKeyConnector);
|
||||||
|
}
|
||||||
|
|
||||||
|
async userNeedsMigration() {
|
||||||
|
const loggedInUsingSso = this.tokenService.getIsExternal();
|
||||||
|
const requiredByOrganization = await this.getManagingOrganization() != null;
|
||||||
|
const userIsNotUsingKeyConnector = !await this.getUsesKeyConnector();
|
||||||
|
|
||||||
|
return loggedInUsingSso && requiredByOrganization && userIsNotUsingKeyConnector;
|
||||||
|
}
|
||||||
|
|
||||||
|
async migrateUser() {
|
||||||
|
const organization = await this.getManagingOrganization();
|
||||||
|
const key = await this.cryptoService.getKey();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const keyConnectorRequest = new KeyConnectorUserKeyRequest(key.encKeyB64);
|
||||||
|
await this.apiService.postUserKeyToKeyConnector(organization.keyConnectorUrl, keyConnectorRequest);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Unable to reach key connector');
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.apiService.postConvertToKeyConnector();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAndSetKey(url?: string) {
|
||||||
|
if (url == null) {
|
||||||
|
url = this.environmentService.getKeyConnectorUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url == null) {
|
||||||
|
throw new Error('No Key Connector URL found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const userKeyResponse = await this.apiService.getUserKeyFromKeyConnector(url);
|
||||||
|
const keyArr = Utils.fromB64ToArray(userKeyResponse.key);
|
||||||
|
const k = new SymmetricCryptoKey(keyArr);
|
||||||
|
await this.cryptoService.setKey(k);
|
||||||
|
} catch (e) {
|
||||||
|
this.logService.error(e);
|
||||||
|
throw new Error('Unable to reach key connector');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getManagingOrganization() {
|
||||||
|
const orgs = await this.userService.getAllOrganizations();
|
||||||
|
return orgs.find(o =>
|
||||||
|
o.usesKeyConnector &&
|
||||||
|
o.type !== OrganizationUserType.Admin &&
|
||||||
|
o.type !== OrganizationUserType.Owner &&
|
||||||
|
!o.isProviderUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setConvertAccountRequired(status: boolean) {
|
||||||
|
await this.storageService.save(Keys.convertAccountToKeyConnector, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getConvertAccountRequired(): Promise<boolean> {
|
||||||
|
return await this.storageService.get(Keys.convertAccountToKeyConnector);
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeConvertAccountRequired() {
|
||||||
|
await this.storageService.remove(Keys.convertAccountToKeyConnector);
|
||||||
|
}
|
||||||
|
|
||||||
|
async clear() {
|
||||||
|
await this.removeConvertAccountRequired();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import { CipherService } from '../abstractions/cipher.service';
|
|||||||
import { CollectionService } from '../abstractions/collection.service';
|
import { CollectionService } from '../abstractions/collection.service';
|
||||||
import { CryptoService } from '../abstractions/crypto.service';
|
import { CryptoService } from '../abstractions/crypto.service';
|
||||||
import { FolderService } from '../abstractions/folder.service';
|
import { FolderService } from '../abstractions/folder.service';
|
||||||
|
import { KeyConnectorService } from '../abstractions/keyConnector.service';
|
||||||
import { LogService } from '../abstractions/log.service';
|
import { LogService } from '../abstractions/log.service';
|
||||||
import { MessagingService } from '../abstractions/messaging.service';
|
import { MessagingService } from '../abstractions/messaging.service';
|
||||||
import { OrganizationService } from '../abstractions/organization.service';
|
import { OrganizationService } from '../abstractions/organization.service';
|
||||||
@@ -12,6 +13,11 @@ import { SendService } from '../abstractions/send.service';
|
|||||||
import { SettingsService } from '../abstractions/settings.service';
|
import { SettingsService } from '../abstractions/settings.service';
|
||||||
import { StateService } from '../abstractions/state.service';
|
import { StateService } from '../abstractions/state.service';
|
||||||
import { SyncService as SyncServiceAbstraction } from '../abstractions/sync.service';
|
import { SyncService as SyncServiceAbstraction } from '../abstractions/sync.service';
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
import { TokenService } from '../abstractions/token.service';
|
||||||
|
import { UserService } from '../abstractions/user.service';
|
||||||
|
>>>>>>> master
|
||||||
|
|
||||||
import { CipherData } from '../models/data/cipherData';
|
import { CipherData } from '../models/data/cipherData';
|
||||||
import { CollectionData } from '../models/data/collectionData';
|
import { CollectionData } from '../models/data/collectionData';
|
||||||
@@ -42,8 +48,13 @@ export class SyncService implements SyncServiceAbstraction {
|
|||||||
private cryptoService: CryptoService, private collectionService: CollectionService,
|
private cryptoService: CryptoService, private collectionService: CollectionService,
|
||||||
private messagingService: MessagingService, private policyService: PolicyService,
|
private messagingService: MessagingService, private policyService: PolicyService,
|
||||||
private sendService: SendService, private logService: LogService,
|
private sendService: SendService, private logService: LogService,
|
||||||
|
<<<<<<< HEAD
|
||||||
private logoutCallback: (expired: boolean) => Promise<void>, private stateService: StateService,
|
private logoutCallback: (expired: boolean) => Promise<void>, private stateService: StateService,
|
||||||
private organizationService: OrganizationService, private providerService: ProviderService) {
|
private organizationService: OrganizationService, private providerService: ProviderService) {
|
||||||
|
=======
|
||||||
|
private tokenService: TokenService, private keyConnectorService: KeyConnectorService,
|
||||||
|
private logoutCallback: (expired: boolean) => Promise<void>) {
|
||||||
|
>>>>>>> master
|
||||||
}
|
}
|
||||||
|
|
||||||
async getLastSync(): Promise<Date> {
|
async getLastSync(): Promise<Date> {
|
||||||
@@ -287,9 +298,16 @@ export class SyncService implements SyncServiceAbstraction {
|
|||||||
await this.cryptoService.setEncPrivateKey(response.privateKey);
|
await this.cryptoService.setEncPrivateKey(response.privateKey);
|
||||||
await this.cryptoService.setProviderKeys(response.providers);
|
await this.cryptoService.setProviderKeys(response.providers);
|
||||||
await this.cryptoService.setOrgKeys(response.organizations, response.providerOrganizations);
|
await this.cryptoService.setOrgKeys(response.organizations, response.providerOrganizations);
|
||||||
|
<<<<<<< HEAD
|
||||||
await this.stateService.setSecurityStamp(response.securityStamp);
|
await this.stateService.setSecurityStamp(response.securityStamp);
|
||||||
await this.stateService.setEmailVerified(response.emailVerified);
|
await this.stateService.setEmailVerified(response.emailVerified);
|
||||||
await this.stateService.setForcePasswordReset(response.forcePasswordReset);
|
await this.stateService.setForcePasswordReset(response.forcePasswordReset);
|
||||||
|
=======
|
||||||
|
await this.userService.setSecurityStamp(response.securityStamp);
|
||||||
|
await this.userService.setEmailVerified(response.emailVerified);
|
||||||
|
await this.userService.setForcePasswordReset(response.forcePasswordReset);
|
||||||
|
await this.keyConnectorService.setUsesKeyConnector(response.usesKeyConnector);
|
||||||
|
>>>>>>> master
|
||||||
|
|
||||||
const organizations: { [id: string]: OrganizationData; } = {};
|
const organizations: { [id: string]: OrganizationData; } = {};
|
||||||
response.organizations.forEach(o => {
|
response.organizations.forEach(o => {
|
||||||
@@ -307,6 +325,13 @@ export class SyncService implements SyncServiceAbstraction {
|
|||||||
organizations[o.id].isProviderUser = true;
|
organizations[o.id].isProviderUser = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (await this.keyConnectorService.userNeedsMigration()) {
|
||||||
|
this.messagingService.send('convertAccountToKeyConnector');
|
||||||
|
} else {
|
||||||
|
this.keyConnectorService.removeConvertAccountRequired();
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
this.organizationService.save(organizations),
|
this.organizationService.save(organizations),
|
||||||
this.providerService.save(providers),
|
this.providerService.save(providers),
|
||||||
|
|||||||
@@ -209,6 +209,27 @@ export class TokenService implements TokenServiceAbstraction {
|
|||||||
return decoded.iss as string;
|
return decoded.iss as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
getIsExternal(): boolean {
|
||||||
|
const decoded = this.decodeToken();
|
||||||
|
if (!Array.isArray(decoded.amr)) {
|
||||||
|
throw new Error('No amr found');
|
||||||
|
}
|
||||||
|
|
||||||
|
return decoded.amr.includes('external');
|
||||||
|
}
|
||||||
|
|
||||||
|
private async storeTokenValue(key: string, value: string) {
|
||||||
|
if (await this.skipTokenStorage()) {
|
||||||
|
// if we have a vault timeout and the action is log out, don't store token
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.storageService.save(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
>>>>>>> master
|
||||||
private async skipTokenStorage(): Promise<boolean> {
|
private async skipTokenStorage(): Promise<boolean> {
|
||||||
const timeout = await this.stateService.getVaultTimeout();
|
const timeout = await this.stateService.getVaultTimeout();
|
||||||
const action = await this.stateService.getVaultTimeoutAction();
|
const action = await this.stateService.getVaultTimeoutAction();
|
||||||
|
|||||||
233
common/src/services/user.service.ts
Normal file
233
common/src/services/user.service.ts
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
import { StorageService } from '../abstractions/storage.service';
|
||||||
|
import { TokenService } from '../abstractions/token.service';
|
||||||
|
import { UserService as UserServiceAbstraction } from '../abstractions/user.service';
|
||||||
|
|
||||||
|
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',
|
||||||
|
userEmail: 'userEmail',
|
||||||
|
stamp: 'securityStamp',
|
||||||
|
kdf: 'kdf',
|
||||||
|
kdfIterations: 'kdfIterations',
|
||||||
|
organizationsPrefix: 'organizations_',
|
||||||
|
providersPrefix: 'providers_',
|
||||||
|
emailVerified: 'emailVerified',
|
||||||
|
forcePasswordReset: 'forcePasswordReset',
|
||||||
|
};
|
||||||
|
|
||||||
|
export class UserService implements UserServiceAbstraction {
|
||||||
|
private userId: string;
|
||||||
|
private email: string;
|
||||||
|
private stamp: string;
|
||||||
|
private kdf: KdfType;
|
||||||
|
private kdfIterations: number;
|
||||||
|
private emailVerified: boolean;
|
||||||
|
private forcePasswordReset: boolean;
|
||||||
|
|
||||||
|
constructor(private tokenService: TokenService, private storageService: StorageService) { }
|
||||||
|
|
||||||
|
async setInformation(userId: string, email: string, kdf: KdfType, kdfIterations: number): Promise<any> {
|
||||||
|
this.email = email;
|
||||||
|
this.userId = userId;
|
||||||
|
this.kdf = kdf;
|
||||||
|
this.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<any> {
|
||||||
|
this.stamp = stamp;
|
||||||
|
return this.storageService.save(Keys.stamp, stamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
setEmailVerified(emailVerified: boolean) {
|
||||||
|
this.emailVerified = emailVerified;
|
||||||
|
return this.storageService.save(Keys.emailVerified, emailVerified);
|
||||||
|
}
|
||||||
|
|
||||||
|
setForcePasswordReset(forcePasswordReset: boolean) {
|
||||||
|
this.forcePasswordReset = forcePasswordReset;
|
||||||
|
return this.storageService.save(Keys.forcePasswordReset, forcePasswordReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserId(): Promise<string> {
|
||||||
|
if (this.userId == null) {
|
||||||
|
this.userId = await this.storageService.get<string>(Keys.userId);
|
||||||
|
}
|
||||||
|
return this.userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getEmail(): Promise<string> {
|
||||||
|
if (this.email == null) {
|
||||||
|
this.email = await this.storageService.get<string>(Keys.userEmail);
|
||||||
|
}
|
||||||
|
return this.email;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getSecurityStamp(): Promise<string> {
|
||||||
|
if (this.stamp == null) {
|
||||||
|
this.stamp = await this.storageService.get<string>(Keys.stamp);
|
||||||
|
}
|
||||||
|
return this.stamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getKdf(): Promise<KdfType> {
|
||||||
|
if (this.kdf == null) {
|
||||||
|
this.kdf = await this.storageService.get<KdfType>(Keys.kdf);
|
||||||
|
}
|
||||||
|
return this.kdf;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getKdfIterations(): Promise<number> {
|
||||||
|
if (this.kdfIterations == null) {
|
||||||
|
this.kdfIterations = await this.storageService.get<number>(Keys.kdfIterations);
|
||||||
|
}
|
||||||
|
return this.kdfIterations;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getEmailVerified(): Promise<boolean> {
|
||||||
|
if (this.emailVerified == null) {
|
||||||
|
this.emailVerified = await this.storageService.get<boolean>(Keys.emailVerified);
|
||||||
|
}
|
||||||
|
return this.emailVerified;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getForcePasswordReset(): Promise<boolean> {
|
||||||
|
if (this.forcePasswordReset == null) {
|
||||||
|
this.forcePasswordReset = await this.storageService.get<boolean>(Keys.forcePasswordReset);
|
||||||
|
}
|
||||||
|
return this.forcePasswordReset;
|
||||||
|
}
|
||||||
|
|
||||||
|
async clear(): Promise<any> {
|
||||||
|
const userId = await this.getUserId();
|
||||||
|
|
||||||
|
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.storageService.remove(Keys.forcePasswordReset);
|
||||||
|
await this.clearOrganizations(userId);
|
||||||
|
await this.clearProviders(userId);
|
||||||
|
|
||||||
|
this.userId = this.email = this.stamp = null;
|
||||||
|
this.kdf = null;
|
||||||
|
this.kdfIterations = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async isAuthenticated(): Promise<boolean> {
|
||||||
|
const token = await this.tokenService.getToken();
|
||||||
|
if (token == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userId = await this.getUserId();
|
||||||
|
return userId != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async canAccessPremium(): Promise<boolean> {
|
||||||
|
const authed = await this.isAuthenticated();
|
||||||
|
if (!authed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokenPremium = this.tokenService.getPremium();
|
||||||
|
if (tokenPremium) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const orgs = await this.getAllOrganizations();
|
||||||
|
for (let i = 0; i < orgs.length; i++) {
|
||||||
|
if (orgs[i].usersGetPremium && orgs[i].enabled) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getOrganization(id: string): Promise<Organization> {
|
||||||
|
const userId = await this.getUserId();
|
||||||
|
const organizations = await this.storageService.get<{ [id: string]: OrganizationData; }>(
|
||||||
|
Keys.organizationsPrefix + userId);
|
||||||
|
if (organizations == null || !organizations.hasOwnProperty(id)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Organization(organizations[id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getOrganizationByIdentifier(identifier: string): Promise<Organization> {
|
||||||
|
const organizations = await this.getAllOrganizations();
|
||||||
|
if (organizations == null || organizations.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return organizations.find(o => o.identifier === identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllOrganizations(): Promise<Organization[]> {
|
||||||
|
const userId = await this.getUserId();
|
||||||
|
const organizations = await this.storageService.get<{ [id: string]: OrganizationData; }>(
|
||||||
|
Keys.organizationsPrefix + userId);
|
||||||
|
const response: Organization[] = [];
|
||||||
|
for (const id in organizations) {
|
||||||
|
if (organizations.hasOwnProperty(id) && !organizations[id].isProviderUser) {
|
||||||
|
response.push(new Organization(organizations[id]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
async replaceOrganizations(organizations: { [id: string]: OrganizationData; }): Promise<any> {
|
||||||
|
const userId = await this.getUserId();
|
||||||
|
await this.storageService.save(Keys.organizationsPrefix + userId, organizations);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
70
common/src/services/userVerification.service.ts
Normal file
70
common/src/services/userVerification.service.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { UserVerificationService as UserVerificationServiceAbstraction } from '../abstractions/userVerification.service';
|
||||||
|
|
||||||
|
import { ApiService } from '../abstractions/api.service';
|
||||||
|
import { CryptoService } from '../abstractions/crypto.service';
|
||||||
|
import { I18nService } from '../abstractions/i18n.service';
|
||||||
|
import { LogService } from '../abstractions/log.service';
|
||||||
|
import { PlatformUtilsService } from '../abstractions/platformUtils.service';
|
||||||
|
|
||||||
|
import { VerificationType } from '../enums/verificationType';
|
||||||
|
|
||||||
|
import { VerifyOTPRequest } from '../models/request/account/verifyOTPRequest';
|
||||||
|
import { SecretVerificationRequest } from '../models/request/secretVerificationRequest';
|
||||||
|
|
||||||
|
import { Verification } from '../types/verification';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UserVerificationService implements UserVerificationServiceAbstraction {
|
||||||
|
constructor(private cryptoService: CryptoService, private i18nService: I18nService,
|
||||||
|
private platformUtilsService: PlatformUtilsService, private apiService: ApiService,
|
||||||
|
private logService: LogService) { }
|
||||||
|
|
||||||
|
async buildRequest<T extends SecretVerificationRequest>(verification: Verification,
|
||||||
|
requestClass?: new () => T, alreadyHashed?: boolean) {
|
||||||
|
if (verification?.secret == null || verification.secret === '') {
|
||||||
|
throw new Error('No secret provided for verification.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const request = requestClass != null
|
||||||
|
? new requestClass()
|
||||||
|
: new SecretVerificationRequest() as T;
|
||||||
|
|
||||||
|
if (verification.type === VerificationType.OTP) {
|
||||||
|
request.otp = verification.secret;
|
||||||
|
} else {
|
||||||
|
request.masterPasswordHash = alreadyHashed
|
||||||
|
? verification.secret
|
||||||
|
: await this.cryptoService.hashPassword(verification.secret, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyUser(verification: Verification): Promise<boolean> {
|
||||||
|
if (verification?.secret == null || verification.secret === '') {
|
||||||
|
throw new Error('No secret provided for verification.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verification.type === VerificationType.OTP) {
|
||||||
|
const request = new VerifyOTPRequest(verification.secret);
|
||||||
|
try {
|
||||||
|
await this.apiService.postAccountVerifyOTP(request);
|
||||||
|
} catch (e) {
|
||||||
|
this.logService.error(e);
|
||||||
|
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'),
|
||||||
|
this.i18nService.t('invalidVerificationCode'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const passwordValid = await this.cryptoService.compareAndUpdateKeyHash(verification.secret, null);
|
||||||
|
if (!passwordValid) {
|
||||||
|
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'),
|
||||||
|
this.i18nService.t('invalidMasterPassword'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import { CipherService } from '../abstractions/cipher.service';
|
|||||||
import { CollectionService } from '../abstractions/collection.service';
|
import { CollectionService } from '../abstractions/collection.service';
|
||||||
import { CryptoService } from '../abstractions/crypto.service';
|
import { CryptoService } from '../abstractions/crypto.service';
|
||||||
import { FolderService } from '../abstractions/folder.service';
|
import { FolderService } from '../abstractions/folder.service';
|
||||||
|
import { KeyConnectorService } from '../abstractions/keyConnector.service';
|
||||||
import { MessagingService } from '../abstractions/messaging.service';
|
import { MessagingService } from '../abstractions/messaging.service';
|
||||||
import { PlatformUtilsService } from '../abstractions/platformUtils.service';
|
import { PlatformUtilsService } from '../abstractions/platformUtils.service';
|
||||||
import { PolicyService } from '../abstractions/policy.service';
|
import { PolicyService } from '../abstractions/policy.service';
|
||||||
@@ -18,10 +19,18 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
|
|||||||
|
|
||||||
constructor(private cipherService: CipherService, private folderService: FolderService,
|
constructor(private cipherService: CipherService, private folderService: FolderService,
|
||||||
private collectionService: CollectionService, private cryptoService: CryptoService,
|
private collectionService: CollectionService, private cryptoService: CryptoService,
|
||||||
|
<<<<<<< HEAD
|
||||||
protected platformUtilsService: PlatformUtilsService, private messagingService: MessagingService,
|
protected platformUtilsService: PlatformUtilsService, private messagingService: MessagingService,
|
||||||
private searchService: SearchService, private tokenService: TokenService,
|
private searchService: SearchService, private tokenService: TokenService,
|
||||||
private policyService: PolicyService, private stateService: StateService,
|
private policyService: PolicyService, private stateService: StateService,
|
||||||
private lockedCallback: () => Promise<void> = null, private loggedOutCallback: (userId?: string) => Promise<void> = null) {
|
private lockedCallback: () => Promise<void> = null, private loggedOutCallback: (userId?: string) => Promise<void> = null) {
|
||||||
|
=======
|
||||||
|
protected platformUtilsService: PlatformUtilsService, private storageService: StorageService,
|
||||||
|
private messagingService: MessagingService, private searchService: SearchService,
|
||||||
|
private userService: UserService, private tokenService: TokenService, private policyService: PolicyService,
|
||||||
|
private keyConnectorService: KeyConnectorService,
|
||||||
|
private lockedCallback: () => Promise<void> = null, private loggedOutCallback: () => Promise<void> = null) {
|
||||||
|
>>>>>>> master
|
||||||
}
|
}
|
||||||
|
|
||||||
init(checkOnInterval: boolean) {
|
init(checkOnInterval: boolean) {
|
||||||
@@ -94,6 +103,7 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
if (userId == null || userId === await this.stateService.getUserId()) {
|
if (userId == null || userId === await this.stateService.getUserId()) {
|
||||||
this.searchService.clearIndex();
|
this.searchService.clearIndex();
|
||||||
}
|
}
|
||||||
@@ -109,6 +119,29 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
|
|||||||
await this.stateService.setBiometricLocked(true, { userId: userId });
|
await this.stateService.setBiometricLocked(true, { userId: userId });
|
||||||
|
|
||||||
this.messagingService.send('locked', { userId: userId });
|
this.messagingService.send('locked', { userId: userId });
|
||||||
|
=======
|
||||||
|
if (await this.keyConnectorService.getUsesKeyConnector()) {
|
||||||
|
const pinSet = await this.isPinLockSet();
|
||||||
|
const pinLock = (pinSet[0] && this.pinProtectedKey != null) || pinSet[1];
|
||||||
|
|
||||||
|
if (!pinLock && !await this.isBiometricLockSet()) {
|
||||||
|
await this.logOut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.biometricLocked = true;
|
||||||
|
this.everBeenUnlocked = true;
|
||||||
|
await this.cryptoService.clearKey(false);
|
||||||
|
await this.cryptoService.clearOrgKeys(true);
|
||||||
|
await this.cryptoService.clearKeyPair(true);
|
||||||
|
await this.cryptoService.clearEncKey(true);
|
||||||
|
|
||||||
|
this.folderService.clearCache();
|
||||||
|
this.cipherService.clearCache();
|
||||||
|
this.collectionService.clearCache();
|
||||||
|
this.searchService.clearIndex();
|
||||||
|
this.messagingService.send('locked');
|
||||||
|
>>>>>>> master
|
||||||
if (this.lockedCallback != null) {
|
if (this.lockedCallback != null) {
|
||||||
await this.lockedCallback();
|
await this.lockedCallback();
|
||||||
}
|
}
|
||||||
|
|||||||
6
common/src/types/verification.ts
Normal file
6
common/src/types/verification.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { VerificationType } from '../enums/verificationType';
|
||||||
|
|
||||||
|
export type Verification = {
|
||||||
|
type: VerificationType,
|
||||||
|
secret: string,
|
||||||
|
};
|
||||||
16
electron/package-lock.json
generated
16
electron/package-lock.json
generated
@@ -11,7 +11,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bitwarden/jslib-common": "file:../common",
|
"@bitwarden/jslib-common": "file:../common",
|
||||||
"@nodert-win10-rs4/windows.security.credentials.ui": "^0.4.4",
|
"@nodert-win10-rs4/windows.security.credentials.ui": "^0.4.4",
|
||||||
"electron": "14.0.1",
|
"electron": "14.2.0",
|
||||||
"electron-log": "4.4.1",
|
"electron-log": "4.4.1",
|
||||||
"electron-store": "8.0.1",
|
"electron-store": "8.0.1",
|
||||||
"electron-updater": "4.3.9",
|
"electron-updater": "4.3.9",
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"../common": {
|
"../common": {
|
||||||
|
"name": "@bitwarden/jslib-common",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -555,10 +556,11 @@
|
|||||||
"integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
|
"integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
|
||||||
},
|
},
|
||||||
"node_modules/electron": {
|
"node_modules/electron": {
|
||||||
"version": "14.0.1",
|
"version": "14.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/electron/-/electron-14.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/electron/-/electron-14.2.0.tgz",
|
||||||
"integrity": "sha512-1XILvfE5mQEBz5L/QeNfcwC3PxAIjwMyA3GR8Naw5C0IKAnHl3lAdjczbtGX8nqbcEpOAVo+4TMSpcPD3zxe8Q==",
|
"integrity": "sha512-6CmAv1P0xcwK3FQOSA27fHI36/wctSFVgj46VODn56srXXQWeolkK1VzeAFNE613iAuuH9jJdHvE3gz+c7XkNA==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/get": "^1.0.1",
|
"@electron/get": "^1.0.1",
|
||||||
"@types/node": "^14.6.2",
|
"@types/node": "^14.6.2",
|
||||||
@@ -2286,9 +2288,9 @@
|
|||||||
"integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
|
"integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
|
||||||
},
|
},
|
||||||
"electron": {
|
"electron": {
|
||||||
"version": "14.0.1",
|
"version": "14.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/electron/-/electron-14.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/electron/-/electron-14.2.0.tgz",
|
||||||
"integrity": "sha512-1XILvfE5mQEBz5L/QeNfcwC3PxAIjwMyA3GR8Naw5C0IKAnHl3lAdjczbtGX8nqbcEpOAVo+4TMSpcPD3zxe8Q==",
|
"integrity": "sha512-6CmAv1P0xcwK3FQOSA27fHI36/wctSFVgj46VODn56srXXQWeolkK1VzeAFNE613iAuuH9jJdHvE3gz+c7XkNA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@electron/get": "^1.0.1",
|
"@electron/get": "^1.0.1",
|
||||||
"@types/node": "^14.6.2",
|
"@types/node": "^14.6.2",
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bitwarden/jslib-common": "file:../common",
|
"@bitwarden/jslib-common": "file:../common",
|
||||||
"@nodert-win10-rs4/windows.security.credentials.ui": "^0.4.4",
|
"@nodert-win10-rs4/windows.security.credentials.ui": "^0.4.4",
|
||||||
"electron": "14.0.1",
|
"electron": "14.2.0",
|
||||||
"electron-log": "4.4.1",
|
"electron-log": "4.4.1",
|
||||||
"electron-store": "8.0.1",
|
"electron-store": "8.0.1",
|
||||||
"electron-updater": "4.3.9",
|
"electron-updater": "4.3.9",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
|||||||
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
|
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
|
||||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||||
|
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
|
||||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||||
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||||
@@ -22,6 +23,7 @@ import { SyncService } from 'jslib-common/abstractions/sync.service';
|
|||||||
|
|
||||||
import { Response } from '../models/response';
|
import { Response } from '../models/response';
|
||||||
|
|
||||||
|
import { KeyConnectorUserKeyRequest } from 'jslib-common/models/request/keyConnectorUserKeyRequest';
|
||||||
import { UpdateTempPasswordRequest } from 'jslib-common/models/request/updateTempPasswordRequest';
|
import { UpdateTempPasswordRequest } from 'jslib-common/models/request/updateTempPasswordRequest';
|
||||||
|
|
||||||
import { MessageResponse } from '../models/response/messageResponse';
|
import { MessageResponse } from '../models/response/messageResponse';
|
||||||
@@ -47,8 +49,14 @@ export class LoginCommand {
|
|||||||
protected i18nService: I18nService, protected environmentService: EnvironmentService,
|
protected i18nService: I18nService, protected environmentService: EnvironmentService,
|
||||||
protected passwordGenerationService: PasswordGenerationService,
|
protected passwordGenerationService: PasswordGenerationService,
|
||||||
protected cryptoFunctionService: CryptoFunctionService, protected platformUtilsService: PlatformUtilsService,
|
protected cryptoFunctionService: CryptoFunctionService, protected platformUtilsService: PlatformUtilsService,
|
||||||
|
<<<<<<< HEAD
|
||||||
protected stateService: StateService, protected cryptoService: CryptoService,
|
protected stateService: StateService, protected cryptoService: CryptoService,
|
||||||
protected policyService: PolicyService, clientId: string, private syncService: SyncService) {
|
protected policyService: PolicyService, clientId: string, private syncService: SyncService) {
|
||||||
|
=======
|
||||||
|
protected userService: UserService, protected cryptoService: CryptoService,
|
||||||
|
protected policyService: PolicyService, clientId: string, private syncService: SyncService,
|
||||||
|
protected keyConnectorService: KeyConnectorService) {
|
||||||
|
>>>>>>> master
|
||||||
this.clientId = clientId;
|
this.clientId = clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +65,7 @@ export class LoginCommand {
|
|||||||
|
|
||||||
let ssoCodeVerifier: string = null;
|
let ssoCodeVerifier: string = null;
|
||||||
let ssoCode: string = null;
|
let ssoCode: string = null;
|
||||||
|
let orgIdentifier: string = null;
|
||||||
|
|
||||||
let clientId: string = null;
|
let clientId: string = null;
|
||||||
let clientSecret: string = null;
|
let clientSecret: string = null;
|
||||||
@@ -79,7 +88,9 @@ export class LoginCommand {
|
|||||||
const codeVerifierHash = await this.cryptoFunctionService.hash(ssoCodeVerifier, 'sha256');
|
const codeVerifierHash = await this.cryptoFunctionService.hash(ssoCodeVerifier, 'sha256');
|
||||||
const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash);
|
const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash);
|
||||||
try {
|
try {
|
||||||
ssoCode = await this.getSsoCode(codeChallenge, state);
|
const ssoParams = await this.openSsoPrompt(codeChallenge, state);
|
||||||
|
ssoCode = ssoParams.ssoCode;
|
||||||
|
orgIdentifier = ssoParams.orgIdentifier;
|
||||||
} catch {
|
} catch {
|
||||||
return Response.badRequest('Something went wrong. Try again.');
|
return Response.badRequest('Something went wrong. Try again.');
|
||||||
}
|
}
|
||||||
@@ -151,7 +162,8 @@ export class LoginCommand {
|
|||||||
if (clientId != null && clientSecret != null) {
|
if (clientId != null && clientSecret != null) {
|
||||||
response = await this.authService.logInApiKey(clientId, clientSecret);
|
response = await this.authService.logInApiKey(clientId, clientSecret);
|
||||||
} else if (ssoCode != null && ssoCodeVerifier != null) {
|
} else if (ssoCode != null && ssoCodeVerifier != null) {
|
||||||
response = await this.authService.logInSso(ssoCode, ssoCodeVerifier, this.ssoRedirectUri, null);
|
response = await this.authService.logInSso(ssoCode, ssoCodeVerifier, this.ssoRedirectUri,
|
||||||
|
orgIdentifier);
|
||||||
} else {
|
} else {
|
||||||
response = await this.authService.logIn(email, password);
|
response = await this.authService.logIn(email, password);
|
||||||
}
|
}
|
||||||
@@ -220,8 +232,9 @@ export class LoginCommand {
|
|||||||
|
|
||||||
if (twoFactorToken == null && response.twoFactorProviders.size > 1 &&
|
if (twoFactorToken == null && response.twoFactorProviders.size > 1 &&
|
||||||
selectedProvider.type === TwoFactorProviderType.Email) {
|
selectedProvider.type === TwoFactorProviderType.Email) {
|
||||||
const emailReq = new TwoFactorEmailRequest(this.authService.email,
|
const emailReq = new TwoFactorEmailRequest();
|
||||||
this.authService.masterPasswordHash);
|
emailReq.email = this.authService.email;
|
||||||
|
emailReq.masterPasswordHash = this.authService.masterPasswordHash;
|
||||||
await this.apiService.postTwoFactorEmail(emailReq);
|
await this.apiService.postTwoFactorEmail(emailReq);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,9 +267,16 @@ export class LoginCommand {
|
|||||||
' through the web vault to set your master password.');
|
' through the web vault to set your master password.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Full sync required for the reset password and key connector checks
|
||||||
|
await this.syncService.fullSync(true);
|
||||||
|
|
||||||
|
// Handle converting to Key Connector if required
|
||||||
|
if (await this.keyConnectorService.userNeedsMigration()) {
|
||||||
|
return await this.migrateToKeyConnector();
|
||||||
|
}
|
||||||
|
|
||||||
// Handle Updating Temp Password if NOT using an API Key for authentication
|
// Handle Updating Temp Password if NOT using an API Key for authentication
|
||||||
if (response.forcePasswordReset && (clientId == null && clientSecret == null)) {
|
if (response.forcePasswordReset && (clientId == null && clientSecret == null)) {
|
||||||
await this.syncService.fullSync(true);
|
|
||||||
return await this.updateTempPassword();
|
return await this.updateTempPassword();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,6 +403,57 @@ export class LoginCommand {
|
|||||||
return userInput;
|
return userInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async migrateToKeyConnector() {
|
||||||
|
// If no interaction available, alert user to use web vault
|
||||||
|
if (!this.canInteract) {
|
||||||
|
await this.logout();
|
||||||
|
this.authService.logOut(() => { /* Do nothing */ });
|
||||||
|
return Response.error(new MessageResponse('An organization you are a member of is using Key Connector. ' +
|
||||||
|
'In order to access the vault, you must opt-in to Key Connector now via the web vault. You have been logged out.', null));
|
||||||
|
}
|
||||||
|
|
||||||
|
const organization = await this.keyConnectorService.getManagingOrganization();
|
||||||
|
|
||||||
|
const answer: inquirer.Answers = await inquirer.createPromptModule({ output: process.stderr })({
|
||||||
|
type: 'list',
|
||||||
|
name: 'convert',
|
||||||
|
message: organization.name + ' is using a self-hosted key server. A master password is no longer required to log in for members of this organization. ',
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
name: 'Remove master password and log in',
|
||||||
|
value: 'remove',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Leave organization and log in',
|
||||||
|
value: 'leave',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Exit',
|
||||||
|
value: 'exit',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (answer.convert === 'remove') {
|
||||||
|
await this.keyConnectorService.migrateUser();
|
||||||
|
|
||||||
|
// Update environment URL - required for api key login
|
||||||
|
const urls = this.environmentService.getUrls();
|
||||||
|
urls.keyConnector = organization.keyConnectorUrl;
|
||||||
|
await this.environmentService.setUrls(urls, true);
|
||||||
|
|
||||||
|
return await this.handleSuccessResponse();
|
||||||
|
} else if (answer.convert === 'leave') {
|
||||||
|
await this.apiService.postLeaveOrganization(organization.id);
|
||||||
|
await this.syncService.fullSync(true);
|
||||||
|
return await this.handleSuccessResponse();
|
||||||
|
} else {
|
||||||
|
await this.logout();
|
||||||
|
this.authService.logOut(() => { /* Do nothing */ });
|
||||||
|
return Response.error('You have been logged out.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async apiClientId(): Promise<string> {
|
private async apiClientId(): Promise<string> {
|
||||||
let clientId: string = null;
|
let clientId: string = null;
|
||||||
|
|
||||||
@@ -432,13 +503,14 @@ export class LoginCommand {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getSsoCode(codeChallenge: string, state: string): Promise<string> {
|
private async openSsoPrompt(codeChallenge: string, state: string): Promise<{ ssoCode: string, orgIdentifier: string }> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const callbackServer = http.createServer((req, res) => {
|
const callbackServer = http.createServer((req, res) => {
|
||||||
const urlString = 'http://localhost' + req.url;
|
const urlString = 'http://localhost' + req.url;
|
||||||
const url = new URL(urlString);
|
const url = new URL(urlString);
|
||||||
const code = url.searchParams.get('code');
|
const code = url.searchParams.get('code');
|
||||||
const receivedState = url.searchParams.get('state');
|
const receivedState = url.searchParams.get('state');
|
||||||
|
const orgIdentifier = this.getOrgIdentifierFromState(receivedState);
|
||||||
res.setHeader('Content-Type', 'text/html');
|
res.setHeader('Content-Type', 'text/html');
|
||||||
if (code != null && receivedState != null && this.checkState(receivedState, state)) {
|
if (code != null && receivedState != null && this.checkState(receivedState, state)) {
|
||||||
res.writeHead(200);
|
res.writeHead(200);
|
||||||
@@ -446,7 +518,10 @@ export class LoginCommand {
|
|||||||
'<h1>Successfully authenticated with the Bitwarden CLI</h1>' +
|
'<h1>Successfully authenticated with the Bitwarden CLI</h1>' +
|
||||||
'<p>You may now close this tab and return to the terminal.</p>' +
|
'<p>You may now close this tab and return to the terminal.</p>' +
|
||||||
'</body></html>');
|
'</body></html>');
|
||||||
callbackServer.close(() => resolve(code));
|
callbackServer.close(() => resolve({
|
||||||
|
ssoCode: code,
|
||||||
|
orgIdentifier: orgIdentifier,
|
||||||
|
}));
|
||||||
} else {
|
} else {
|
||||||
res.writeHead(400);
|
res.writeHead(400);
|
||||||
res.end('<html><head><title>Failed | Bitwarden CLI</title></head><body>' +
|
res.end('<html><head><title>Failed | Bitwarden CLI</title></head><body>' +
|
||||||
@@ -478,6 +553,15 @@ export class LoginCommand {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getOrgIdentifierFromState(state: string): string {
|
||||||
|
if (state === null || state === undefined) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stateSplit = state.split('_identifier=');
|
||||||
|
return stateSplit.length > 1 ? stateSplit[1] : null;
|
||||||
|
}
|
||||||
|
|
||||||
private checkState(state: string, checkState: string): boolean {
|
private checkState(state: string, checkState: string): boolean {
|
||||||
if (state === null || state === undefined) {
|
if (state === null || state === undefined) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
19
package-lock.json
generated
19
package-lock.json
generated
@@ -74,6 +74,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
|
"name": "@bitwarden/jslib-common",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -100,12 +101,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"electron": {
|
"electron": {
|
||||||
|
"name": "@bitwarden/jslib-electron",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bitwarden/jslib-common": "file:../common",
|
"@bitwarden/jslib-common": "file:../common",
|
||||||
"@nodert-win10-rs4/windows.security.credentials.ui": "^0.4.4",
|
"@nodert-win10-rs4/windows.security.credentials.ui": "^0.4.4",
|
||||||
"electron": "14.0.1",
|
"electron": "14.2.0",
|
||||||
"electron-log": "4.4.1",
|
"electron-log": "4.4.1",
|
||||||
"electron-store": "8.0.1",
|
"electron-store": "8.0.1",
|
||||||
"electron-updater": "4.3.9",
|
"electron-updater": "4.3.9",
|
||||||
@@ -119,6 +121,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node": {
|
"node": {
|
||||||
|
"name": "@bitwarden/jslib-node",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -2791,9 +2794,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/electron": {
|
"node_modules/electron": {
|
||||||
"version": "14.0.1",
|
"version": "14.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/electron/-/electron-14.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/electron/-/electron-14.2.0.tgz",
|
||||||
"integrity": "sha512-1XILvfE5mQEBz5L/QeNfcwC3PxAIjwMyA3GR8Naw5C0IKAnHl3lAdjczbtGX8nqbcEpOAVo+4TMSpcPD3zxe8Q==",
|
"integrity": "sha512-6CmAv1P0xcwK3FQOSA27fHI36/wctSFVgj46VODn56srXXQWeolkK1VzeAFNE613iAuuH9jJdHvE3gz+c7XkNA==",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/get": "^1.0.1",
|
"@electron/get": "^1.0.1",
|
||||||
@@ -9832,7 +9835,7 @@
|
|||||||
"@bitwarden/jslib-common": "file:../common",
|
"@bitwarden/jslib-common": "file:../common",
|
||||||
"@nodert-win10-rs4/windows.security.credentials.ui": "^0.4.4",
|
"@nodert-win10-rs4/windows.security.credentials.ui": "^0.4.4",
|
||||||
"@types/node": "^14.17.1",
|
"@types/node": "^14.17.1",
|
||||||
"electron": "14.0.1",
|
"electron": "14.2.0",
|
||||||
"electron-log": "4.4.1",
|
"electron-log": "4.4.1",
|
||||||
"electron-store": "8.0.1",
|
"electron-store": "8.0.1",
|
||||||
"electron-updater": "4.3.9",
|
"electron-updater": "4.3.9",
|
||||||
@@ -11854,9 +11857,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"electron": {
|
"electron": {
|
||||||
"version": "14.0.1",
|
"version": "14.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/electron/-/electron-14.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/electron/-/electron-14.2.0.tgz",
|
||||||
"integrity": "sha512-1XILvfE5mQEBz5L/QeNfcwC3PxAIjwMyA3GR8Naw5C0IKAnHl3lAdjczbtGX8nqbcEpOAVo+4TMSpcPD3zxe8Q==",
|
"integrity": "sha512-6CmAv1P0xcwK3FQOSA27fHI36/wctSFVgj46VODn56srXXQWeolkK1VzeAFNE613iAuuH9jJdHvE3gz+c7XkNA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@electron/get": "^1.0.1",
|
"@electron/get": "^1.0.1",
|
||||||
"@types/node": "^14.6.2",
|
"@types/node": "^14.6.2",
|
||||||
|
|||||||
Reference in New Issue
Block a user