mirror of
https://github.com/bitwarden/jslib
synced 2025-12-06 00:03:29 +00:00
Merge branch 'master' of https://github.com/bitwarden/jslib into feature/additional-item-types-scaffold
This commit is contained in:
@@ -1,5 +1,11 @@
|
|||||||
[](https://github.com/bitwarden/jslib/actions/workflows/build.yml?query=branch:master)
|
[](https://github.com/bitwarden/jslib/actions/workflows/build.yml?query=branch:master)
|
||||||
|
|
||||||
|
> **Repository Reorganization in Progress**
|
||||||
|
>
|
||||||
|
> We are currently migrating some projects over to a mono repository. For existing PR's we will be providing documentation on how to move/migrate them. To minimize the overhead we are actively reviewing open PRs. If possible please ensure any pending comments are resolved as soon as possible.
|
||||||
|
>
|
||||||
|
> New pull requests created during this transition period may not get addressed —if needed, please create a new PR after the reorganization is complete.
|
||||||
|
|
||||||
# Bitwarden JavaScript Library
|
# Bitwarden JavaScript Library
|
||||||
|
|
||||||
Common code referenced across Bitwarden JavaScript projects.
|
Common code referenced across Bitwarden JavaScript projects.
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ module.exports = {
|
|||||||
name: "angular",
|
name: "angular",
|
||||||
displayName: "angular tests",
|
displayName: "angular tests",
|
||||||
preset: "jest-preset-angular",
|
preset: "jest-preset-angular",
|
||||||
roots: ["<rootDir>/spec/"],
|
|
||||||
testMatch: ["**/+(*.)+(spec).+(ts)"],
|
testMatch: ["**/+(*.)+(spec).+(ts)"],
|
||||||
setupFilesAfterEnv: ["<rootDir>/spec/test.ts"],
|
setupFilesAfterEnv: ["<rootDir>/spec/test.ts"],
|
||||||
collectCoverage: true,
|
collectCoverage: true,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { ActivatedRoute } from "@angular/router";
|
|||||||
import { first } from "rxjs/operators";
|
import { first } from "rxjs/operators";
|
||||||
|
|
||||||
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
import { I18nService } from "jslib-common/abstractions/i18n.service";
|
||||||
|
import { LogService } from "jslib-common/abstractions/log.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 { StateService } from "jslib-common/abstractions/state.service";
|
import { StateService } from "jslib-common/abstractions/state.service";
|
||||||
@@ -15,6 +16,7 @@ export class GeneratorComponent implements OnInit {
|
|||||||
@Input() type: string;
|
@Input() type: string;
|
||||||
@Output() onSelected = new EventEmitter<string>();
|
@Output() onSelected = new EventEmitter<string>();
|
||||||
|
|
||||||
|
usernameGeneratingPromise: Promise<string>;
|
||||||
typeOptions: any[];
|
typeOptions: any[];
|
||||||
passTypeOptions: any[];
|
passTypeOptions: any[];
|
||||||
usernameTypeOptions: any[];
|
usernameTypeOptions: any[];
|
||||||
@@ -36,6 +38,7 @@ export class GeneratorComponent implements OnInit {
|
|||||||
protected platformUtilsService: PlatformUtilsService,
|
protected platformUtilsService: PlatformUtilsService,
|
||||||
protected stateService: StateService,
|
protected stateService: StateService,
|
||||||
protected i18nService: I18nService,
|
protected i18nService: I18nService,
|
||||||
|
protected logService: LogService,
|
||||||
protected route: ActivatedRoute,
|
protected route: ActivatedRoute,
|
||||||
private win: Window
|
private win: Window
|
||||||
) {
|
) {
|
||||||
@@ -58,13 +61,20 @@ export class GeneratorComponent implements OnInit {
|
|||||||
value: "catchall",
|
value: "catchall",
|
||||||
desc: i18nService.t("catchallEmailDesc"),
|
desc: i18nService.t("catchallEmailDesc"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: i18nService.t("forwardedEmail"),
|
||||||
|
value: "forwarded",
|
||||||
|
desc: i18nService.t("forwardedEmailDesc"),
|
||||||
|
},
|
||||||
{ name: i18nService.t("randomWord"), value: "word" },
|
{ name: i18nService.t("randomWord"), value: "word" },
|
||||||
];
|
];
|
||||||
this.subaddressOptions = [{ name: i18nService.t("random"), value: "random" }];
|
this.subaddressOptions = [{ name: i18nService.t("random"), value: "random" }];
|
||||||
this.catchallOptions = [{ name: i18nService.t("random"), value: "random" }];
|
this.catchallOptions = [{ name: i18nService.t("random"), value: "random" }];
|
||||||
this.forwardOptions = [
|
this.forwardOptions = [
|
||||||
{ name: "SimpleLogin", value: "simplelogin" },
|
{ name: "SimpleLogin", value: "simplelogin" },
|
||||||
{ name: "FastMail", value: "fastmail" },
|
{ name: "AnonAddy", value: "anonaddy" },
|
||||||
|
{ name: "Firefox Relay", value: "firefoxrelay" },
|
||||||
|
// { name: "FastMail", value: "fastmail" },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,13 +114,17 @@ export class GeneratorComponent implements OnInit {
|
|||||||
this.type = generatorOptions?.type ?? "password";
|
this.type = generatorOptions?.type ?? "password";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await this.regenerate();
|
if (this.regenerateWithoutButtonPress()) {
|
||||||
|
await this.regenerate();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async typeChanged() {
|
async typeChanged() {
|
||||||
await this.stateService.setGeneratorOptions({ type: this.type });
|
await this.stateService.setGeneratorOptions({ type: this.type });
|
||||||
await this.regenerate();
|
if (this.regenerateWithoutButtonPress()) {
|
||||||
|
await this.regenerate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async regenerate() {
|
async regenerate() {
|
||||||
@@ -135,14 +149,17 @@ export class GeneratorComponent implements OnInit {
|
|||||||
this.normalizePasswordOptions();
|
this.normalizePasswordOptions();
|
||||||
await this.passwordGenerationService.saveOptions(this.passwordOptions);
|
await this.passwordGenerationService.saveOptions(this.passwordOptions);
|
||||||
|
|
||||||
if (regenerate) {
|
if (regenerate && this.regenerateWithoutButtonPress()) {
|
||||||
await this.regeneratePassword();
|
await this.regeneratePassword();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveUsernameOptions(regenerate = true) {
|
async saveUsernameOptions(regenerate = true) {
|
||||||
await this.usernameGenerationService.saveOptions(this.usernameOptions);
|
await this.usernameGenerationService.saveOptions(this.usernameOptions);
|
||||||
if (regenerate) {
|
if (this.usernameOptions.type === "forwarded") {
|
||||||
|
this.username = "-";
|
||||||
|
}
|
||||||
|
if (regenerate && this.regenerateWithoutButtonPress()) {
|
||||||
await this.regenerateUsername();
|
await this.regenerateUsername();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,9 +174,16 @@ export class GeneratorComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async generateUsername() {
|
async generateUsername() {
|
||||||
this.username = await this.usernameGenerationService.generateUsername(this.usernameOptions);
|
try {
|
||||||
if (this.username === "" || this.username === null) {
|
this.usernameGeneratingPromise = this.usernameGenerationService.generateUsername(
|
||||||
this.username = "-";
|
this.usernameOptions
|
||||||
|
);
|
||||||
|
this.username = await this.usernameGeneratingPromise;
|
||||||
|
if (this.username === "" || this.username === null) {
|
||||||
|
this.username = "-";
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.logService.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,6 +209,10 @@ export class GeneratorComponent implements OnInit {
|
|||||||
this.showOptions = !this.showOptions;
|
this.showOptions = !this.showOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
regenerateWithoutButtonPress() {
|
||||||
|
return this.type !== "username" || this.usernameOptions.type !== "forwarded";
|
||||||
|
}
|
||||||
|
|
||||||
private normalizePasswordOptions() {
|
private normalizePasswordOptions() {
|
||||||
// Application level normalize options depedent on class variables
|
// Application level normalize options depedent on class variables
|
||||||
this.passwordOptions.ambiguous = !this.avoidAmbiguous;
|
this.passwordOptions.ambiguous = !this.avoidAmbiguous;
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
|
|||||||
|
|
||||||
import { ModalRef } from "./modal/modal.ref";
|
import { ModalRef } from "./modal/modal.ref";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to verify the user's Master Password for the "Master Password Re-prompt" feature only.
|
||||||
|
* See UserVerificationComponent for any other situation where you need to verify the user's identity.
|
||||||
|
*/
|
||||||
@Directive()
|
@Directive()
|
||||||
export class PasswordRepromptComponent {
|
export class PasswordRepromptComponent {
|
||||||
showPassword = false;
|
showPassword = false;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import * as DuoWebSDK from "duo_web_sdk";
|
|||||||
import { first } from "rxjs/operators";
|
import { first } from "rxjs/operators";
|
||||||
|
|
||||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||||
|
import { AppIdService } from "jslib-common/abstractions/appId.service";
|
||||||
import { AuthService } from "jslib-common/abstractions/auth.service";
|
import { AuthService } from "jslib-common/abstractions/auth.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";
|
||||||
@@ -57,7 +58,8 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
|||||||
protected stateService: StateService,
|
protected stateService: StateService,
|
||||||
protected route: ActivatedRoute,
|
protected route: ActivatedRoute,
|
||||||
protected logService: LogService,
|
protected logService: LogService,
|
||||||
protected twoFactorService: TwoFactorService
|
protected twoFactorService: TwoFactorService,
|
||||||
|
protected appIdService: AppIdService
|
||||||
) {
|
) {
|
||||||
super(environmentService, i18nService, platformUtilsService);
|
super(environmentService, i18nService, platformUtilsService);
|
||||||
this.webAuthnSupported = this.platformUtilsService.supportsWebAuthn(win);
|
this.webAuthnSupported = this.platformUtilsService.supportsWebAuthn(win);
|
||||||
@@ -234,6 +236,7 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI
|
|||||||
const request = new TwoFactorEmailRequest();
|
const request = new TwoFactorEmailRequest();
|
||||||
request.email = this.authService.email;
|
request.email = this.authService.email;
|
||||||
request.masterPasswordHash = this.authService.masterPasswordHash;
|
request.masterPasswordHash = this.authService.masterPasswordHash;
|
||||||
|
request.deviceIdentifier = await this.appIdService.getAppId();
|
||||||
this.emailPromise = this.apiService.postTwoFactorEmail(request);
|
this.emailPromise = this.apiService.postTwoFactorEmail(request);
|
||||||
await this.emailPromise;
|
await this.emailPromise;
|
||||||
if (doToast) {
|
if (doToast) {
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ export class UpdatePasswordComponent extends BaseChangePasswordComponent {
|
|||||||
|
|
||||||
async cancel() {
|
async cancel() {
|
||||||
await this.stateService.setOrganizationInvitation(null);
|
await this.stateService.setOrganizationInvitation(null);
|
||||||
await this.stateService.setLoginRedirect(null);
|
|
||||||
this.router.navigate(["/vault"]);
|
this.router.navigate(["/vault"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,14 +7,20 @@ import { UserVerificationService } from "jslib-common/abstractions/userVerificat
|
|||||||
import { VerificationType } from "jslib-common/enums/verificationType";
|
import { VerificationType } from "jslib-common/enums/verificationType";
|
||||||
import { Verification } from "jslib-common/types/verification";
|
import { Verification } from "jslib-common/types/verification";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for general-purpose user verification throughout the app.
|
||||||
|
* Collects the user's master password, or if they are using Key Connector, prompts for an OTP via email.
|
||||||
|
* This is exposed to the parent component via the ControlValueAccessor interface (e.g. bind it to a FormControl).
|
||||||
|
* Use UserVerificationService to verify the user's input.
|
||||||
|
*/
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-verify-master-password",
|
selector: "app-user-verification",
|
||||||
templateUrl: "verify-master-password.component.html",
|
templateUrl: "user-verification.component.html",
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
provide: NG_VALUE_ACCESSOR,
|
provide: NG_VALUE_ACCESSOR,
|
||||||
multi: true,
|
multi: true,
|
||||||
useExisting: VerifyMasterPasswordComponent,
|
useExisting: UserVerificationComponent,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
animations: [
|
animations: [
|
||||||
@@ -23,7 +29,7 @@ import { Verification } from "jslib-common/types/verification";
|
|||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class VerifyMasterPasswordComponent implements ControlValueAccessor, OnInit {
|
export class UserVerificationComponent implements ControlValueAccessor, OnInit {
|
||||||
usesKeyConnector = false;
|
usesKeyConnector = false;
|
||||||
disableRequestOTP = false;
|
disableRequestOTP = false;
|
||||||
sentCode = false;
|
sentCode = false;
|
||||||
@@ -41,7 +47,7 @@ export class VerifyMasterPasswordComponent implements ControlValueAccessor, OnIn
|
|||||||
this.usesKeyConnector = await this.keyConnectorService.getUsesKeyConnector();
|
this.usesKeyConnector = await this.keyConnectorService.getUsesKeyConnector();
|
||||||
this.processChanges(this.secret.value);
|
this.processChanges(this.secret.value);
|
||||||
|
|
||||||
this.secret.valueChanges.subscribe((secret) => this.processChanges(secret));
|
this.secret.valueChanges.subscribe((secret: string) => this.processChanges(secret));
|
||||||
}
|
}
|
||||||
|
|
||||||
async requestOTP() {
|
async requestOTP() {
|
||||||
@@ -5,6 +5,12 @@ import { ErrorResponse } from "jslib-common/models/response/errorResponse";
|
|||||||
|
|
||||||
import { ValidationService } from "../services/validation.service";
|
import { ValidationService } from "../services/validation.service";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides error handling, in particular for any error returned by the server in an api call.
|
||||||
|
* Attach it to a <form> element and provide the name of the class property that will hold the api call promise.
|
||||||
|
* e.g. <form [appApiAction]="this.formPromise">
|
||||||
|
* Any errors/rejections that occur will be intercepted and displayed as error toasts.
|
||||||
|
*/
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: "[appApiAction]",
|
selector: "[appApiAction]",
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export class AuthGuardService implements CanActivate {
|
|||||||
async canActivate(route: ActivatedRouteSnapshot, routerState: RouterStateSnapshot) {
|
async canActivate(route: ActivatedRouteSnapshot, routerState: RouterStateSnapshot) {
|
||||||
const isAuthed = await this.stateService.getIsAuthenticated();
|
const isAuthed = await this.stateService.getIsAuthenticated();
|
||||||
if (!isAuthed) {
|
if (!isAuthed) {
|
||||||
this.messagingService.send("authBlocked");
|
this.messagingService.send("authBlocked", { url: routerState.url });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,16 +28,14 @@ export class AuthGuardService implements CanActivate {
|
|||||||
if (routerState != null) {
|
if (routerState != null) {
|
||||||
this.messagingService.send("lockedUrl", { url: routerState.url });
|
this.messagingService.send("lockedUrl", { url: routerState.url });
|
||||||
}
|
}
|
||||||
this.router.navigate(["lock"], { queryParams: { promptBiometric: true } });
|
return this.router.createUrlTree(["lock"], { queryParams: { promptBiometric: true } });
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!routerState.url.includes("remove-password") &&
|
!routerState.url.includes("remove-password") &&
|
||||||
(await this.keyConnectorService.getConvertAccountRequired())
|
(await this.keyConnectorService.getConvertAccountRequired())
|
||||||
) {
|
) {
|
||||||
this.router.navigate(["/remove-password"]);
|
return this.router.createUrlTree(["/remove-password"]);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Injector, LOCALE_ID, NgModule } from "@angular/core";
|
import { InjectionToken, Injector, LOCALE_ID, NgModule } from "@angular/core";
|
||||||
|
|
||||||
import { ApiService as ApiServiceAbstraction } from "jslib-common/abstractions/api.service";
|
import { ApiService as ApiServiceAbstraction } from "jslib-common/abstractions/api.service";
|
||||||
import { AppIdService as AppIdServiceAbstraction } from "jslib-common/abstractions/appId.service";
|
import { AppIdService as AppIdServiceAbstraction } from "jslib-common/abstractions/appId.service";
|
||||||
@@ -82,20 +82,60 @@ import { PasswordRepromptService } from "./passwordReprompt.service";
|
|||||||
import { UnauthGuardService } from "./unauth-guard.service";
|
import { UnauthGuardService } from "./unauth-guard.service";
|
||||||
import { ValidationService } from "./validation.service";
|
import { ValidationService } from "./validation.service";
|
||||||
|
|
||||||
|
export const WINDOW = new InjectionToken<Window>("WINDOW");
|
||||||
|
export const SECURE_STORAGE = new InjectionToken<StorageServiceAbstraction>("SECURE_STORAGE");
|
||||||
|
export const STATE_FACTORY = new InjectionToken<StateFactory>("STATE_FACTORY");
|
||||||
|
export const STATE_SERVICE_USE_CACHE = new InjectionToken<boolean>("STATE_SERVICE_USE_CACHE");
|
||||||
|
export const LOGOUT_CALLBACK = new InjectionToken<(expired: boolean, userId?: string) => void>(
|
||||||
|
"LOGOUT_CALLBACK"
|
||||||
|
);
|
||||||
|
export const LOCKED_CALLBACK = new InjectionToken<() => void>("LOCKED_CALLBACK");
|
||||||
|
export const CLIENT_TYPE = new InjectionToken<boolean>("CLIENT_TYPE");
|
||||||
|
export const LOCALES_DIRECTORY = new InjectionToken<string>("LOCALES_DIRECTORY");
|
||||||
|
export const SYSTEM_LANGUAGE = new InjectionToken<string>("SYSTEM_LANGUAGE");
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [],
|
declarations: [],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: "WINDOW", useValue: window },
|
|
||||||
{
|
|
||||||
provide: LOCALE_ID,
|
|
||||||
useFactory: (i18nService: I18nServiceAbstraction) => i18nService.translationLocale,
|
|
||||||
deps: [I18nServiceAbstraction],
|
|
||||||
},
|
|
||||||
ValidationService,
|
ValidationService,
|
||||||
AuthGuardService,
|
AuthGuardService,
|
||||||
UnauthGuardService,
|
UnauthGuardService,
|
||||||
LockGuardService,
|
LockGuardService,
|
||||||
ModalService,
|
ModalService,
|
||||||
|
{ provide: WINDOW, useValue: window },
|
||||||
|
{
|
||||||
|
provide: LOCALE_ID,
|
||||||
|
useFactory: (i18nService: I18nServiceAbstraction) => i18nService.translationLocale,
|
||||||
|
deps: [I18nServiceAbstraction],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: LOCALES_DIRECTORY,
|
||||||
|
useValue: "./locales",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: SYSTEM_LANGUAGE,
|
||||||
|
useFactory: (window: Window) => window.navigator.language,
|
||||||
|
deps: [WINDOW],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: STATE_FACTORY,
|
||||||
|
useValue: new StateFactory(GlobalState, Account),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: STATE_SERVICE_USE_CACHE,
|
||||||
|
useValue: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: LOGOUT_CALLBACK,
|
||||||
|
useFactory:
|
||||||
|
(messagingService: MessagingServiceAbstraction) => (expired: boolean, userId?: string) =>
|
||||||
|
messagingService.send("logout", { expired: expired, userId: userId }),
|
||||||
|
deps: [MessagingServiceAbstraction],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: LOCKED_CALLBACK,
|
||||||
|
useValue: null,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
provide: AppIdServiceAbstraction,
|
provide: AppIdServiceAbstraction,
|
||||||
useClass: AppIdService,
|
useClass: AppIdService,
|
||||||
@@ -203,30 +243,17 @@ import { ValidationService } from "./validation.service";
|
|||||||
{
|
{
|
||||||
provide: UsernameGenerationServiceAbstraction,
|
provide: UsernameGenerationServiceAbstraction,
|
||||||
useClass: UsernameGenerationService,
|
useClass: UsernameGenerationService,
|
||||||
deps: [CryptoServiceAbstraction, StateServiceAbstraction],
|
deps: [CryptoServiceAbstraction, StateServiceAbstraction, ApiServiceAbstraction],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: ApiServiceAbstraction,
|
provide: ApiServiceAbstraction,
|
||||||
useFactory: (
|
useClass: ApiService,
|
||||||
tokenService: TokenServiceAbstraction,
|
|
||||||
platformUtilsService: PlatformUtilsServiceAbstraction,
|
|
||||||
environmentService: EnvironmentServiceAbstraction,
|
|
||||||
messagingService: MessagingServiceAbstraction,
|
|
||||||
appIdService: AppIdServiceAbstraction
|
|
||||||
) =>
|
|
||||||
new ApiService(
|
|
||||||
tokenService,
|
|
||||||
platformUtilsService,
|
|
||||||
environmentService,
|
|
||||||
appIdService,
|
|
||||||
async (expired: boolean) => messagingService.send("logout", { expired: expired })
|
|
||||||
),
|
|
||||||
deps: [
|
deps: [
|
||||||
TokenServiceAbstraction,
|
TokenServiceAbstraction,
|
||||||
PlatformUtilsServiceAbstraction,
|
PlatformUtilsServiceAbstraction,
|
||||||
EnvironmentServiceAbstraction,
|
EnvironmentServiceAbstraction,
|
||||||
MessagingServiceAbstraction,
|
|
||||||
AppIdServiceAbstraction,
|
AppIdServiceAbstraction,
|
||||||
|
LOGOUT_CALLBACK,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -236,39 +263,7 @@ import { ValidationService } from "./validation.service";
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: SyncServiceAbstraction,
|
provide: SyncServiceAbstraction,
|
||||||
useFactory: (
|
useClass: SyncService,
|
||||||
apiService: ApiServiceAbstraction,
|
|
||||||
settingsService: SettingsServiceAbstraction,
|
|
||||||
folderService: FolderServiceAbstraction,
|
|
||||||
cipherService: CipherServiceAbstraction,
|
|
||||||
cryptoService: CryptoServiceAbstraction,
|
|
||||||
collectionService: CollectionServiceAbstraction,
|
|
||||||
messagingService: MessagingServiceAbstraction,
|
|
||||||
policyService: PolicyServiceAbstraction,
|
|
||||||
sendService: SendServiceAbstraction,
|
|
||||||
logService: LogService,
|
|
||||||
keyConnectorService: KeyConnectorServiceAbstraction,
|
|
||||||
stateService: StateServiceAbstraction,
|
|
||||||
organizationService: OrganizationServiceAbstraction,
|
|
||||||
providerService: ProviderServiceAbstraction
|
|
||||||
) =>
|
|
||||||
new SyncService(
|
|
||||||
apiService,
|
|
||||||
settingsService,
|
|
||||||
folderService,
|
|
||||||
cipherService,
|
|
||||||
cryptoService,
|
|
||||||
collectionService,
|
|
||||||
messagingService,
|
|
||||||
policyService,
|
|
||||||
sendService,
|
|
||||||
logService,
|
|
||||||
keyConnectorService,
|
|
||||||
stateService,
|
|
||||||
organizationService,
|
|
||||||
providerService,
|
|
||||||
async (expired: boolean) => messagingService.send("logout", { expired: expired })
|
|
||||||
),
|
|
||||||
deps: [
|
deps: [
|
||||||
ApiServiceAbstraction,
|
ApiServiceAbstraction,
|
||||||
SettingsServiceAbstraction,
|
SettingsServiceAbstraction,
|
||||||
@@ -284,6 +279,7 @@ import { ValidationService } from "./validation.service";
|
|||||||
StateServiceAbstraction,
|
StateServiceAbstraction,
|
||||||
OrganizationServiceAbstraction,
|
OrganizationServiceAbstraction,
|
||||||
ProviderServiceAbstraction,
|
ProviderServiceAbstraction,
|
||||||
|
LOGOUT_CALLBACK,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{ provide: BroadcasterServiceAbstraction, useClass: BroadcasterService },
|
{ provide: BroadcasterServiceAbstraction, useClass: BroadcasterService },
|
||||||
@@ -294,35 +290,7 @@ import { ValidationService } from "./validation.service";
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: VaultTimeoutServiceAbstraction,
|
provide: VaultTimeoutServiceAbstraction,
|
||||||
useFactory: (
|
useClass: VaultTimeoutService,
|
||||||
cipherService: CipherServiceAbstraction,
|
|
||||||
folderService: FolderServiceAbstraction,
|
|
||||||
collectionService: CollectionServiceAbstraction,
|
|
||||||
cryptoService: CryptoServiceAbstraction,
|
|
||||||
platformUtilsService: PlatformUtilsServiceAbstraction,
|
|
||||||
messagingService: MessagingServiceAbstraction,
|
|
||||||
searchService: SearchServiceAbstraction,
|
|
||||||
tokenService: TokenServiceAbstraction,
|
|
||||||
policyService: PolicyServiceAbstraction,
|
|
||||||
keyConnectorService: KeyConnectorServiceAbstraction,
|
|
||||||
stateService: StateServiceAbstraction
|
|
||||||
) =>
|
|
||||||
new VaultTimeoutService(
|
|
||||||
cipherService,
|
|
||||||
folderService,
|
|
||||||
collectionService,
|
|
||||||
cryptoService,
|
|
||||||
platformUtilsService,
|
|
||||||
messagingService,
|
|
||||||
searchService,
|
|
||||||
tokenService,
|
|
||||||
policyService,
|
|
||||||
keyConnectorService,
|
|
||||||
stateService,
|
|
||||||
null,
|
|
||||||
async (userId?: string) =>
|
|
||||||
messagingService.send("logout", { expired: false, userId: userId })
|
|
||||||
),
|
|
||||||
deps: [
|
deps: [
|
||||||
CipherServiceAbstraction,
|
CipherServiceAbstraction,
|
||||||
FolderServiceAbstraction,
|
FolderServiceAbstraction,
|
||||||
@@ -335,42 +303,26 @@ import { ValidationService } from "./validation.service";
|
|||||||
PolicyServiceAbstraction,
|
PolicyServiceAbstraction,
|
||||||
KeyConnectorServiceAbstraction,
|
KeyConnectorServiceAbstraction,
|
||||||
StateServiceAbstraction,
|
StateServiceAbstraction,
|
||||||
|
LOCKED_CALLBACK,
|
||||||
|
LOGOUT_CALLBACK,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: StateServiceAbstraction,
|
provide: StateServiceAbstraction,
|
||||||
useFactory: (
|
useClass: StateService,
|
||||||
storageService: StorageServiceAbstraction,
|
|
||||||
secureStorageService: StorageServiceAbstraction,
|
|
||||||
logService: LogService,
|
|
||||||
stateMigrationService: StateMigrationServiceAbstraction
|
|
||||||
) =>
|
|
||||||
new StateService(
|
|
||||||
storageService,
|
|
||||||
secureStorageService,
|
|
||||||
logService,
|
|
||||||
stateMigrationService,
|
|
||||||
new StateFactory(GlobalState, Account)
|
|
||||||
),
|
|
||||||
deps: [
|
deps: [
|
||||||
StorageServiceAbstraction,
|
StorageServiceAbstraction,
|
||||||
"SECURE_STORAGE",
|
SECURE_STORAGE,
|
||||||
LogService,
|
LogService,
|
||||||
StateMigrationServiceAbstraction,
|
StateMigrationServiceAbstraction,
|
||||||
|
STATE_FACTORY,
|
||||||
|
STATE_SERVICE_USE_CACHE,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: StateMigrationServiceAbstraction,
|
provide: StateMigrationServiceAbstraction,
|
||||||
useFactory: (
|
useClass: StateMigrationService,
|
||||||
storageService: StorageServiceAbstraction,
|
deps: [StorageServiceAbstraction, SECURE_STORAGE, STATE_FACTORY],
|
||||||
secureStorageService: StorageServiceAbstraction
|
|
||||||
) =>
|
|
||||||
new StateMigrationService(
|
|
||||||
storageService,
|
|
||||||
secureStorageService,
|
|
||||||
new StateFactory(GlobalState, Account)
|
|
||||||
),
|
|
||||||
deps: [StorageServiceAbstraction, "SECURE_STORAGE"],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: ExportServiceAbstraction,
|
provide: ExportServiceAbstraction,
|
||||||
@@ -389,33 +341,14 @@ import { ValidationService } from "./validation.service";
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: NotificationsServiceAbstraction,
|
provide: NotificationsServiceAbstraction,
|
||||||
useFactory: (
|
useClass: NotificationsService,
|
||||||
syncService: SyncServiceAbstraction,
|
|
||||||
appIdService: AppIdServiceAbstraction,
|
|
||||||
apiService: ApiServiceAbstraction,
|
|
||||||
vaultTimeoutService: VaultTimeoutServiceAbstraction,
|
|
||||||
environmentService: EnvironmentServiceAbstraction,
|
|
||||||
messagingService: MessagingServiceAbstraction,
|
|
||||||
logService: LogService,
|
|
||||||
stateService: StateServiceAbstraction
|
|
||||||
) =>
|
|
||||||
new NotificationsService(
|
|
||||||
syncService,
|
|
||||||
appIdService,
|
|
||||||
apiService,
|
|
||||||
vaultTimeoutService,
|
|
||||||
environmentService,
|
|
||||||
async () => messagingService.send("logout", { expired: true }),
|
|
||||||
logService,
|
|
||||||
stateService
|
|
||||||
),
|
|
||||||
deps: [
|
deps: [
|
||||||
SyncServiceAbstraction,
|
SyncServiceAbstraction,
|
||||||
AppIdServiceAbstraction,
|
AppIdServiceAbstraction,
|
||||||
ApiServiceAbstraction,
|
ApiServiceAbstraction,
|
||||||
VaultTimeoutServiceAbstraction,
|
VaultTimeoutServiceAbstraction,
|
||||||
EnvironmentServiceAbstraction,
|
EnvironmentServiceAbstraction,
|
||||||
MessagingServiceAbstraction,
|
LOGOUT_CALLBACK,
|
||||||
LogService,
|
LogService,
|
||||||
StateServiceAbstraction,
|
StateServiceAbstraction,
|
||||||
],
|
],
|
||||||
@@ -423,7 +356,7 @@ import { ValidationService } from "./validation.service";
|
|||||||
{
|
{
|
||||||
provide: CryptoFunctionServiceAbstraction,
|
provide: CryptoFunctionServiceAbstraction,
|
||||||
useClass: WebCryptoFunctionService,
|
useClass: WebCryptoFunctionService,
|
||||||
deps: ["WINDOW"],
|
deps: [WINDOW],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: EventServiceAbstraction,
|
provide: EventServiceAbstraction,
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ import { PasswordRepromptComponent } from "../components/password-reprompt.compo
|
|||||||
|
|
||||||
import { ModalService } from "./modal.service";
|
import { ModalService } from "./modal.service";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to verify the user's Master Password for the "Master Password Re-prompt" feature only.
|
||||||
|
* See UserVerificationService for any other situation where you need to verify the user's identity.
|
||||||
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PasswordRepromptService implements PasswordRepromptServiceAbstraction {
|
export class PasswordRepromptService implements PasswordRepromptServiceAbstraction {
|
||||||
protected component = PasswordRepromptComponent;
|
protected component = PasswordRepromptComponent;
|
||||||
|
|||||||
@@ -18,11 +18,9 @@ export class UnauthGuardService implements CanActivate {
|
|||||||
if (isAuthed) {
|
if (isAuthed) {
|
||||||
const locked = await this.vaultTimeoutService.isLocked();
|
const locked = await this.vaultTimeoutService.isLocked();
|
||||||
if (locked) {
|
if (locked) {
|
||||||
this.router.navigate(["lock"]);
|
return this.router.createUrlTree(["lock"]);
|
||||||
} else {
|
|
||||||
this.router.navigate([this.homepage]);
|
|
||||||
}
|
}
|
||||||
return false;
|
return this.router.createUrlTree([this.homepage]);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ module.exports = {
|
|||||||
displayName: "common jslib tests",
|
displayName: "common jslib tests",
|
||||||
preset: "ts-jest",
|
preset: "ts-jest",
|
||||||
testEnvironment: "jsdom",
|
testEnvironment: "jsdom",
|
||||||
roots: ["<rootDir>/spec/"],
|
|
||||||
testMatch: ["**/+(*.)+(spec).+(ts)"],
|
testMatch: ["**/+(*.)+(spec).+(ts)"],
|
||||||
setupFilesAfterEnv: ["<rootDir>/spec/test.ts"],
|
setupFilesAfterEnv: ["<rootDir>/spec/test.ts"],
|
||||||
collectCoverage: true,
|
collectCoverage: true,
|
||||||
|
|||||||
83
common/spec/domain/attachment.spec.ts
Normal file
83
common/spec/domain/attachment.spec.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import Substitute, { Arg } from "@fluffy-spoon/substitute";
|
||||||
|
|
||||||
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
|
import { AttachmentData } from "jslib-common/models/data/attachmentData";
|
||||||
|
import { Attachment } from "jslib-common/models/domain/attachment";
|
||||||
|
import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey";
|
||||||
|
import { ContainerService } from "jslib-common/services/container.service";
|
||||||
|
|
||||||
|
import { makeStaticByteArray, mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Attachment", () => {
|
||||||
|
let data: AttachmentData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
id: "id",
|
||||||
|
url: "url",
|
||||||
|
fileName: "fileName",
|
||||||
|
key: "key",
|
||||||
|
size: "1100",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new AttachmentData();
|
||||||
|
const attachment = new Attachment(data);
|
||||||
|
|
||||||
|
expect(attachment).toEqual({
|
||||||
|
id: null,
|
||||||
|
url: null,
|
||||||
|
size: undefined,
|
||||||
|
sizeName: null,
|
||||||
|
key: null,
|
||||||
|
fileName: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const attachment = new Attachment(data);
|
||||||
|
|
||||||
|
expect(attachment).toEqual({
|
||||||
|
size: "1100",
|
||||||
|
id: "id",
|
||||||
|
url: "url",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
fileName: { encryptedString: "fileName", encryptionType: 0 },
|
||||||
|
key: { encryptedString: "key", encryptionType: 0 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toAttachmentData", () => {
|
||||||
|
const attachment = new Attachment(data);
|
||||||
|
expect(attachment.toAttachmentData()).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const attachment = new Attachment();
|
||||||
|
attachment.id = "id";
|
||||||
|
attachment.url = "url";
|
||||||
|
attachment.size = "1100";
|
||||||
|
attachment.sizeName = "1.1 KB";
|
||||||
|
attachment.key = mockEnc("key");
|
||||||
|
attachment.fileName = mockEnc("fileName");
|
||||||
|
|
||||||
|
const cryptoService = Substitute.for<CryptoService>();
|
||||||
|
cryptoService.getOrgKey(null).resolves(null);
|
||||||
|
cryptoService.decryptToBytes(Arg.any(), Arg.any()).resolves(makeStaticByteArray(32));
|
||||||
|
|
||||||
|
(window as any).bitwardenContainerService = new ContainerService(cryptoService);
|
||||||
|
|
||||||
|
const view = await attachment.decrypt(null);
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
id: "id",
|
||||||
|
url: "url",
|
||||||
|
size: "1100",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
fileName: "fileName",
|
||||||
|
key: expect.any(SymmetricCryptoKey),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
73
common/spec/domain/card.spec.ts
Normal file
73
common/spec/domain/card.spec.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { CardData } from "jslib-common/models/data/cardData";
|
||||||
|
import { Card } from "jslib-common/models/domain/card";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Card", () => {
|
||||||
|
let data: CardData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
cardholderName: "encHolder",
|
||||||
|
brand: "encBrand",
|
||||||
|
number: "encNumber",
|
||||||
|
expMonth: "encMonth",
|
||||||
|
expYear: "encYear",
|
||||||
|
code: "encCode",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new CardData();
|
||||||
|
const card = new Card(data);
|
||||||
|
|
||||||
|
expect(card).toEqual({
|
||||||
|
cardholderName: null,
|
||||||
|
brand: null,
|
||||||
|
number: null,
|
||||||
|
expMonth: null,
|
||||||
|
expYear: null,
|
||||||
|
code: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const card = new Card(data);
|
||||||
|
|
||||||
|
expect(card).toEqual({
|
||||||
|
cardholderName: { encryptedString: "encHolder", encryptionType: 0 },
|
||||||
|
brand: { encryptedString: "encBrand", encryptionType: 0 },
|
||||||
|
number: { encryptedString: "encNumber", encryptionType: 0 },
|
||||||
|
expMonth: { encryptedString: "encMonth", encryptionType: 0 },
|
||||||
|
expYear: { encryptedString: "encYear", encryptionType: 0 },
|
||||||
|
code: { encryptedString: "encCode", encryptionType: 0 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toCardData", () => {
|
||||||
|
const card = new Card(data);
|
||||||
|
expect(card.toCardData()).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const card = new Card();
|
||||||
|
card.cardholderName = mockEnc("cardHolder");
|
||||||
|
card.brand = mockEnc("brand");
|
||||||
|
card.number = mockEnc("number");
|
||||||
|
card.expMonth = mockEnc("expMonth");
|
||||||
|
card.expYear = mockEnc("expYear");
|
||||||
|
card.code = mockEnc("code");
|
||||||
|
|
||||||
|
const view = await card.decrypt(null);
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
_brand: "brand",
|
||||||
|
_number: "number",
|
||||||
|
_subTitle: null,
|
||||||
|
cardholderName: "cardHolder",
|
||||||
|
code: "code",
|
||||||
|
expMonth: "expMonth",
|
||||||
|
expYear: "expYear",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
590
common/spec/domain/cipher.spec.ts
Normal file
590
common/spec/domain/cipher.spec.ts
Normal file
@@ -0,0 +1,590 @@
|
|||||||
|
import Substitute, { Arg } from "@fluffy-spoon/substitute";
|
||||||
|
|
||||||
|
import { CipherRepromptType } from "jslib-common/enums/cipherRepromptType";
|
||||||
|
import { CipherType } from "jslib-common/enums/cipherType";
|
||||||
|
import { FieldType } from "jslib-common/enums/fieldType";
|
||||||
|
import { SecureNoteType } from "jslib-common/enums/secureNoteType";
|
||||||
|
import { UriMatchType } from "jslib-common/enums/uriMatchType";
|
||||||
|
import { CipherData } from "jslib-common/models/data/cipherData";
|
||||||
|
import { Card } from "jslib-common/models/domain/card";
|
||||||
|
import { Cipher } from "jslib-common/models/domain/cipher";
|
||||||
|
import { Identity } from "jslib-common/models/domain/identity";
|
||||||
|
import { Login } from "jslib-common/models/domain/login";
|
||||||
|
import { SecureNote } from "jslib-common/models/domain/secureNote";
|
||||||
|
import { CardView } from "jslib-common/models/view/cardView";
|
||||||
|
import { IdentityView } from "jslib-common/models/view/identityView";
|
||||||
|
import { LoginView } from "jslib-common/models/view/loginView";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Cipher DTO", () => {
|
||||||
|
it("Convert from empty CipherData", () => {
|
||||||
|
const data = new CipherData();
|
||||||
|
const cipher = new Cipher(data);
|
||||||
|
|
||||||
|
expect(cipher).toEqual({
|
||||||
|
id: null,
|
||||||
|
organizationId: null,
|
||||||
|
folderId: null,
|
||||||
|
name: null,
|
||||||
|
notes: null,
|
||||||
|
type: undefined,
|
||||||
|
favorite: undefined,
|
||||||
|
organizationUseTotp: undefined,
|
||||||
|
edit: undefined,
|
||||||
|
viewPassword: true,
|
||||||
|
revisionDate: null,
|
||||||
|
collectionIds: undefined,
|
||||||
|
localData: null,
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: undefined,
|
||||||
|
attachments: null,
|
||||||
|
fields: null,
|
||||||
|
passwordHistory: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("LoginCipher", () => {
|
||||||
|
let cipherData: CipherData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cipherData = {
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
favorite: false,
|
||||||
|
revisionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
type: CipherType.Login,
|
||||||
|
name: "EncryptedString",
|
||||||
|
notes: "EncryptedString",
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: CipherRepromptType.None,
|
||||||
|
login: {
|
||||||
|
uris: [{ uri: "EncryptedString", match: UriMatchType.Domain }],
|
||||||
|
username: "EncryptedString",
|
||||||
|
password: "EncryptedString",
|
||||||
|
passwordRevisionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
totp: "EncryptedString",
|
||||||
|
autofillOnPageLoad: false,
|
||||||
|
},
|
||||||
|
passwordHistory: [
|
||||||
|
{ password: "EncryptedString", lastUsedDate: "2022-01-31T12:00:00.000Z" },
|
||||||
|
],
|
||||||
|
attachments: [
|
||||||
|
{
|
||||||
|
id: "a1",
|
||||||
|
url: "url",
|
||||||
|
size: "1100",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
fileName: "file",
|
||||||
|
key: "EncKey",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "a2",
|
||||||
|
url: "url",
|
||||||
|
size: "1100",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
fileName: "file",
|
||||||
|
key: "EncKey",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "EncryptedString",
|
||||||
|
value: "EncryptedString",
|
||||||
|
type: FieldType.Text,
|
||||||
|
linkedId: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "EncryptedString",
|
||||||
|
value: "EncryptedString",
|
||||||
|
type: FieldType.Hidden,
|
||||||
|
linkedId: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const cipher = new Cipher(cipherData);
|
||||||
|
|
||||||
|
expect(cipher).toEqual({
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
name: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
notes: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
type: 1,
|
||||||
|
favorite: false,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
collectionIds: undefined,
|
||||||
|
localData: null,
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: 0,
|
||||||
|
login: {
|
||||||
|
passwordRevisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
autofillOnPageLoad: false,
|
||||||
|
username: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
password: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
totp: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
uris: [{ match: 0, uri: { encryptedString: "EncryptedString", encryptionType: 0 } }],
|
||||||
|
},
|
||||||
|
attachments: [
|
||||||
|
{
|
||||||
|
fileName: { encryptedString: "file", encryptionType: 0 },
|
||||||
|
id: "a1",
|
||||||
|
key: { encryptedString: "EncKey", encryptionType: 0 },
|
||||||
|
size: "1100",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
url: "url",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fileName: { encryptedString: "file", encryptionType: 0 },
|
||||||
|
id: "a2",
|
||||||
|
key: { encryptedString: "EncKey", encryptionType: 0 },
|
||||||
|
size: "1100",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
url: "url",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
linkedId: null,
|
||||||
|
name: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
type: 0,
|
||||||
|
value: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
linkedId: null,
|
||||||
|
name: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
type: 1,
|
||||||
|
value: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
passwordHistory: [
|
||||||
|
{
|
||||||
|
lastUsedDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
password: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toCipherData", () => {
|
||||||
|
const cipher = new Cipher(cipherData);
|
||||||
|
expect(cipher.toCipherData()).toEqual(cipherData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const cipher = new Cipher();
|
||||||
|
cipher.id = "id";
|
||||||
|
cipher.organizationId = "orgId";
|
||||||
|
cipher.folderId = "folderId";
|
||||||
|
cipher.edit = true;
|
||||||
|
cipher.viewPassword = true;
|
||||||
|
cipher.organizationUseTotp = true;
|
||||||
|
cipher.favorite = false;
|
||||||
|
cipher.revisionDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
cipher.type = CipherType.Login;
|
||||||
|
cipher.name = mockEnc("EncryptedString");
|
||||||
|
cipher.notes = mockEnc("EncryptedString");
|
||||||
|
cipher.deletedDate = null;
|
||||||
|
cipher.reprompt = CipherRepromptType.None;
|
||||||
|
|
||||||
|
const loginView = new LoginView();
|
||||||
|
loginView.username = "username";
|
||||||
|
loginView.password = "password";
|
||||||
|
|
||||||
|
const login = Substitute.for<Login>();
|
||||||
|
login.decrypt(Arg.any(), Arg.any()).resolves(loginView);
|
||||||
|
cipher.login = login;
|
||||||
|
|
||||||
|
const cipherView = await cipher.decrypt();
|
||||||
|
|
||||||
|
expect(cipherView).toMatchObject({
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
name: "EncryptedString",
|
||||||
|
notes: "EncryptedString",
|
||||||
|
type: 1,
|
||||||
|
favorite: false,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
login: loginView,
|
||||||
|
attachments: null,
|
||||||
|
fields: null,
|
||||||
|
passwordHistory: null,
|
||||||
|
collectionIds: undefined,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: 0,
|
||||||
|
localData: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("SecureNoteCipher", () => {
|
||||||
|
let cipherData: CipherData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cipherData = {
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
favorite: false,
|
||||||
|
revisionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
type: CipherType.SecureNote,
|
||||||
|
name: "EncryptedString",
|
||||||
|
notes: "EncryptedString",
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: CipherRepromptType.None,
|
||||||
|
secureNote: {
|
||||||
|
type: SecureNoteType.Generic,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const cipher = new Cipher(cipherData);
|
||||||
|
|
||||||
|
expect(cipher).toEqual({
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
name: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
notes: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
type: 2,
|
||||||
|
favorite: false,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
collectionIds: undefined,
|
||||||
|
localData: null,
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: 0,
|
||||||
|
secureNote: { type: SecureNoteType.Generic },
|
||||||
|
attachments: null,
|
||||||
|
fields: null,
|
||||||
|
passwordHistory: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toCipherData", () => {
|
||||||
|
const cipher = new Cipher(cipherData);
|
||||||
|
expect(cipher.toCipherData()).toEqual(cipherData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const cipher = new Cipher();
|
||||||
|
cipher.id = "id";
|
||||||
|
cipher.organizationId = "orgId";
|
||||||
|
cipher.folderId = "folderId";
|
||||||
|
cipher.edit = true;
|
||||||
|
cipher.viewPassword = true;
|
||||||
|
cipher.organizationUseTotp = true;
|
||||||
|
cipher.favorite = false;
|
||||||
|
cipher.revisionDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
cipher.type = CipherType.SecureNote;
|
||||||
|
cipher.name = mockEnc("EncryptedString");
|
||||||
|
cipher.notes = mockEnc("EncryptedString");
|
||||||
|
cipher.deletedDate = null;
|
||||||
|
cipher.reprompt = CipherRepromptType.None;
|
||||||
|
cipher.secureNote = new SecureNote();
|
||||||
|
cipher.secureNote.type = SecureNoteType.Generic;
|
||||||
|
|
||||||
|
const cipherView = await cipher.decrypt();
|
||||||
|
|
||||||
|
expect(cipherView).toMatchObject({
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
name: "EncryptedString",
|
||||||
|
notes: "EncryptedString",
|
||||||
|
type: 2,
|
||||||
|
favorite: false,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
secureNote: { type: 0 },
|
||||||
|
attachments: null,
|
||||||
|
fields: null,
|
||||||
|
passwordHistory: null,
|
||||||
|
collectionIds: undefined,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: 0,
|
||||||
|
localData: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("CardCipher", () => {
|
||||||
|
let cipherData: CipherData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cipherData = {
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
favorite: false,
|
||||||
|
revisionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
type: CipherType.Card,
|
||||||
|
name: "EncryptedString",
|
||||||
|
notes: "EncryptedString",
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: CipherRepromptType.None,
|
||||||
|
card: {
|
||||||
|
cardholderName: "EncryptedString",
|
||||||
|
brand: "EncryptedString",
|
||||||
|
number: "EncryptedString",
|
||||||
|
expMonth: "EncryptedString",
|
||||||
|
expYear: "EncryptedString",
|
||||||
|
code: "EncryptedString",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const cipher = new Cipher(cipherData);
|
||||||
|
|
||||||
|
expect(cipher).toEqual({
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
name: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
notes: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
type: 3,
|
||||||
|
favorite: false,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
collectionIds: undefined,
|
||||||
|
localData: null,
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: 0,
|
||||||
|
card: {
|
||||||
|
cardholderName: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
brand: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
number: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
expMonth: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
expYear: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
code: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
},
|
||||||
|
attachments: null,
|
||||||
|
fields: null,
|
||||||
|
passwordHistory: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toCipherData", () => {
|
||||||
|
const cipher = new Cipher(cipherData);
|
||||||
|
expect(cipher.toCipherData()).toEqual(cipherData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const cipher = new Cipher();
|
||||||
|
cipher.id = "id";
|
||||||
|
cipher.organizationId = "orgId";
|
||||||
|
cipher.folderId = "folderId";
|
||||||
|
cipher.edit = true;
|
||||||
|
cipher.viewPassword = true;
|
||||||
|
cipher.organizationUseTotp = true;
|
||||||
|
cipher.favorite = false;
|
||||||
|
cipher.revisionDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
cipher.type = CipherType.Card;
|
||||||
|
cipher.name = mockEnc("EncryptedString");
|
||||||
|
cipher.notes = mockEnc("EncryptedString");
|
||||||
|
cipher.deletedDate = null;
|
||||||
|
cipher.reprompt = CipherRepromptType.None;
|
||||||
|
|
||||||
|
const cardView = new CardView();
|
||||||
|
cardView.cardholderName = "cardholderName";
|
||||||
|
cardView.number = "4111111111111111";
|
||||||
|
|
||||||
|
const card = Substitute.for<Card>();
|
||||||
|
card.decrypt(Arg.any(), Arg.any()).resolves(cardView);
|
||||||
|
cipher.card = card;
|
||||||
|
|
||||||
|
const cipherView = await cipher.decrypt();
|
||||||
|
|
||||||
|
expect(cipherView).toMatchObject({
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
name: "EncryptedString",
|
||||||
|
notes: "EncryptedString",
|
||||||
|
type: 3,
|
||||||
|
favorite: false,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
card: cardView,
|
||||||
|
attachments: null,
|
||||||
|
fields: null,
|
||||||
|
passwordHistory: null,
|
||||||
|
collectionIds: undefined,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: 0,
|
||||||
|
localData: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("IdentityCipher", () => {
|
||||||
|
let cipherData: CipherData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cipherData = {
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
favorite: false,
|
||||||
|
revisionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
type: CipherType.Identity,
|
||||||
|
name: "EncryptedString",
|
||||||
|
notes: "EncryptedString",
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: CipherRepromptType.None,
|
||||||
|
identity: {
|
||||||
|
title: "EncryptedString",
|
||||||
|
firstName: "EncryptedString",
|
||||||
|
middleName: "EncryptedString",
|
||||||
|
lastName: "EncryptedString",
|
||||||
|
address1: "EncryptedString",
|
||||||
|
address2: "EncryptedString",
|
||||||
|
address3: "EncryptedString",
|
||||||
|
city: "EncryptedString",
|
||||||
|
state: "EncryptedString",
|
||||||
|
postalCode: "EncryptedString",
|
||||||
|
country: "EncryptedString",
|
||||||
|
company: "EncryptedString",
|
||||||
|
email: "EncryptedString",
|
||||||
|
phone: "EncryptedString",
|
||||||
|
ssn: "EncryptedString",
|
||||||
|
username: "EncryptedString",
|
||||||
|
passportNumber: "EncryptedString",
|
||||||
|
licenseNumber: "EncryptedString",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const cipher = new Cipher(cipherData);
|
||||||
|
|
||||||
|
expect(cipher).toEqual({
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
name: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
notes: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
type: 4,
|
||||||
|
favorite: false,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
collectionIds: undefined,
|
||||||
|
localData: null,
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: 0,
|
||||||
|
identity: {
|
||||||
|
title: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
firstName: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
middleName: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
lastName: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
address1: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
address2: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
address3: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
city: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
state: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
postalCode: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
country: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
company: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
email: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
phone: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
ssn: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
username: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
passportNumber: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
licenseNumber: { encryptedString: "EncryptedString", encryptionType: 0 },
|
||||||
|
},
|
||||||
|
attachments: null,
|
||||||
|
fields: null,
|
||||||
|
passwordHistory: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toCipherData", () => {
|
||||||
|
const cipher = new Cipher(cipherData);
|
||||||
|
expect(cipher.toCipherData()).toEqual(cipherData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const cipher = new Cipher();
|
||||||
|
cipher.id = "id";
|
||||||
|
cipher.organizationId = "orgId";
|
||||||
|
cipher.folderId = "folderId";
|
||||||
|
cipher.edit = true;
|
||||||
|
cipher.viewPassword = true;
|
||||||
|
cipher.organizationUseTotp = true;
|
||||||
|
cipher.favorite = false;
|
||||||
|
cipher.revisionDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
cipher.type = CipherType.Identity;
|
||||||
|
cipher.name = mockEnc("EncryptedString");
|
||||||
|
cipher.notes = mockEnc("EncryptedString");
|
||||||
|
cipher.deletedDate = null;
|
||||||
|
cipher.reprompt = CipherRepromptType.None;
|
||||||
|
|
||||||
|
const identityView = new IdentityView();
|
||||||
|
identityView.firstName = "firstName";
|
||||||
|
identityView.lastName = "lastName";
|
||||||
|
|
||||||
|
const identity = Substitute.for<Identity>();
|
||||||
|
identity.decrypt(Arg.any(), Arg.any()).resolves(identityView);
|
||||||
|
cipher.identity = identity;
|
||||||
|
|
||||||
|
const cipherView = await cipher.decrypt();
|
||||||
|
|
||||||
|
expect(cipherView).toMatchObject({
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
folderId: "folderId",
|
||||||
|
name: "EncryptedString",
|
||||||
|
notes: "EncryptedString",
|
||||||
|
type: 4,
|
||||||
|
favorite: false,
|
||||||
|
organizationUseTotp: true,
|
||||||
|
edit: true,
|
||||||
|
viewPassword: true,
|
||||||
|
identity: identityView,
|
||||||
|
attachments: null,
|
||||||
|
fields: null,
|
||||||
|
passwordHistory: null,
|
||||||
|
collectionIds: undefined,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
deletedDate: null,
|
||||||
|
reprompt: 0,
|
||||||
|
localData: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
66
common/spec/domain/collection.spec.ts
Normal file
66
common/spec/domain/collection.spec.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { CollectionData } from "jslib-common/models/data/collectionData";
|
||||||
|
import { Collection } from "jslib-common/models/domain/collection";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Collection", () => {
|
||||||
|
let data: CollectionData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
name: "encName",
|
||||||
|
externalId: "extId",
|
||||||
|
readOnly: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new CollectionData({} as any);
|
||||||
|
const card = new Collection(data);
|
||||||
|
|
||||||
|
expect(card).toEqual({
|
||||||
|
externalId: null,
|
||||||
|
hidePasswords: null,
|
||||||
|
id: null,
|
||||||
|
name: null,
|
||||||
|
organizationId: null,
|
||||||
|
readOnly: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const collection = new Collection(data);
|
||||||
|
|
||||||
|
expect(collection).toEqual({
|
||||||
|
id: "id",
|
||||||
|
organizationId: "orgId",
|
||||||
|
name: { encryptedString: "encName", encryptionType: 0 },
|
||||||
|
externalId: "extId",
|
||||||
|
readOnly: true,
|
||||||
|
hidePasswords: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const collection = new Collection();
|
||||||
|
collection.id = "id";
|
||||||
|
collection.organizationId = "orgId";
|
||||||
|
collection.name = mockEnc("encName");
|
||||||
|
collection.externalId = "extId";
|
||||||
|
collection.readOnly = false;
|
||||||
|
collection.hidePasswords = false;
|
||||||
|
|
||||||
|
const view = await collection.decrypt();
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
externalId: "extId",
|
||||||
|
hidePasswords: false,
|
||||||
|
id: "id",
|
||||||
|
name: "encName",
|
||||||
|
organizationId: "orgId",
|
||||||
|
readOnly: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
195
common/spec/domain/encString.spec.ts
Normal file
195
common/spec/domain/encString.spec.ts
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
import Substitute, { Arg } from "@fluffy-spoon/substitute";
|
||||||
|
|
||||||
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
|
import { EncryptionType } from "jslib-common/enums/encryptionType";
|
||||||
|
import { EncString } from "jslib-common/models/domain/encString";
|
||||||
|
import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey";
|
||||||
|
import { ContainerService } from "jslib-common/services/container.service";
|
||||||
|
|
||||||
|
describe("EncString", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
(window as any).bitwardenContainerService = undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Rsa2048_OaepSha256_B64", () => {
|
||||||
|
it("constructor", () => {
|
||||||
|
const encString = new EncString(EncryptionType.Rsa2048_OaepSha256_B64, "data");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
data: "data",
|
||||||
|
encryptedString: "3.data",
|
||||||
|
encryptionType: 3,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("parse existing", () => {
|
||||||
|
it("valid", () => {
|
||||||
|
const encString = new EncString("3.data");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
data: "data",
|
||||||
|
encryptedString: "3.data",
|
||||||
|
encryptionType: 3,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("invalid", () => {
|
||||||
|
const encString = new EncString("3.data|test");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
encryptedString: "3.data|test",
|
||||||
|
encryptionType: 3,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("decrypt", () => {
|
||||||
|
const encString = new EncString(EncryptionType.Rsa2048_OaepSha256_B64, "data");
|
||||||
|
|
||||||
|
const cryptoService = Substitute.for<CryptoService>();
|
||||||
|
cryptoService.getOrgKey(null).resolves(null);
|
||||||
|
cryptoService.decryptToUtf8(encString, Arg.any()).resolves("decrypted");
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
(window as any).bitwardenContainerService = new ContainerService(cryptoService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("decrypts correctly", async () => {
|
||||||
|
const decrypted = await encString.decrypt(null);
|
||||||
|
|
||||||
|
expect(decrypted).toBe("decrypted");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("result should be cached", async () => {
|
||||||
|
const decrypted = await encString.decrypt(null);
|
||||||
|
cryptoService.received(1).decryptToUtf8(Arg.any(), Arg.any());
|
||||||
|
|
||||||
|
expect(decrypted).toBe("decrypted");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("AesCbc256_B64", () => {
|
||||||
|
it("constructor", () => {
|
||||||
|
const encString = new EncString(EncryptionType.AesCbc256_B64, "data", "iv");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
data: "data",
|
||||||
|
encryptedString: "0.iv|data",
|
||||||
|
encryptionType: 0,
|
||||||
|
iv: "iv",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("parse existing", () => {
|
||||||
|
it("valid", () => {
|
||||||
|
const encString = new EncString("0.iv|data");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
data: "data",
|
||||||
|
encryptedString: "0.iv|data",
|
||||||
|
encryptionType: 0,
|
||||||
|
iv: "iv",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("invalid", () => {
|
||||||
|
const encString = new EncString("0.iv|data|mac");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
encryptedString: "0.iv|data|mac",
|
||||||
|
encryptionType: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("AesCbc256_HmacSha256_B64", () => {
|
||||||
|
it("constructor", () => {
|
||||||
|
const encString = new EncString(EncryptionType.AesCbc256_HmacSha256_B64, "data", "iv", "mac");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
data: "data",
|
||||||
|
encryptedString: "2.iv|data|mac",
|
||||||
|
encryptionType: 2,
|
||||||
|
iv: "iv",
|
||||||
|
mac: "mac",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("valid", () => {
|
||||||
|
const encString = new EncString("2.iv|data|mac");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
data: "data",
|
||||||
|
encryptedString: "2.iv|data|mac",
|
||||||
|
encryptionType: 2,
|
||||||
|
iv: "iv",
|
||||||
|
mac: "mac",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("invalid", () => {
|
||||||
|
const encString = new EncString("2.iv|data");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
encryptedString: "2.iv|data",
|
||||||
|
encryptionType: 2,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Exit early if null", () => {
|
||||||
|
const encString = new EncString(null);
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
encryptedString: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("decrypt", () => {
|
||||||
|
it("throws exception when bitwarden container not initialized", async () => {
|
||||||
|
const encString = new EncString(null);
|
||||||
|
|
||||||
|
expect.assertions(1);
|
||||||
|
try {
|
||||||
|
await encString.decrypt(null);
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.message).toEqual("global bitwardenContainerService not initialized.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handles value it can't decrypt", async () => {
|
||||||
|
const encString = new EncString(null);
|
||||||
|
|
||||||
|
const cryptoService = Substitute.for<CryptoService>();
|
||||||
|
cryptoService.getOrgKey(null).resolves(null);
|
||||||
|
cryptoService.decryptToUtf8(encString, Arg.any()).throws("error");
|
||||||
|
|
||||||
|
(window as any).bitwardenContainerService = new ContainerService(cryptoService);
|
||||||
|
|
||||||
|
const decrypted = await encString.decrypt(null);
|
||||||
|
|
||||||
|
expect(decrypted).toBe("[error: cannot decrypt]");
|
||||||
|
|
||||||
|
expect(encString).toEqual({
|
||||||
|
decryptedValue: "[error: cannot decrypt]",
|
||||||
|
encryptedString: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes along key", async () => {
|
||||||
|
const encString = new EncString(null);
|
||||||
|
const key = Substitute.for<SymmetricCryptoKey>();
|
||||||
|
|
||||||
|
const cryptoService = Substitute.for<CryptoService>();
|
||||||
|
cryptoService.getOrgKey(null).resolves(null);
|
||||||
|
|
||||||
|
(window as any).bitwardenContainerService = new ContainerService(cryptoService);
|
||||||
|
|
||||||
|
await encString.decrypt(null, key);
|
||||||
|
|
||||||
|
cryptoService.received().decryptToUtf8(encString, key);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
64
common/spec/domain/field.spec.ts
Normal file
64
common/spec/domain/field.spec.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { FieldType } from "jslib-common/enums/fieldType";
|
||||||
|
import { FieldData } from "jslib-common/models/data/fieldData";
|
||||||
|
import { Field } from "jslib-common/models/domain/field";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Field", () => {
|
||||||
|
let data: FieldData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
type: FieldType.Text,
|
||||||
|
name: "encName",
|
||||||
|
value: "encValue",
|
||||||
|
linkedId: null,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new FieldData();
|
||||||
|
const field = new Field(data);
|
||||||
|
|
||||||
|
expect(field).toEqual({
|
||||||
|
type: undefined,
|
||||||
|
name: null,
|
||||||
|
value: null,
|
||||||
|
linkedId: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const field = new Field(data);
|
||||||
|
|
||||||
|
expect(field).toEqual({
|
||||||
|
type: FieldType.Text,
|
||||||
|
name: { encryptedString: "encName", encryptionType: 0 },
|
||||||
|
value: { encryptedString: "encValue", encryptionType: 0 },
|
||||||
|
linkedId: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toFieldData", () => {
|
||||||
|
const field = new Field(data);
|
||||||
|
expect(field.toFieldData()).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const field = new Field();
|
||||||
|
field.type = FieldType.Text;
|
||||||
|
field.name = mockEnc("encName");
|
||||||
|
field.value = mockEnc("encValue");
|
||||||
|
|
||||||
|
const view = await field.decrypt(null);
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
type: 0,
|
||||||
|
name: "encName",
|
||||||
|
value: "encValue",
|
||||||
|
newField: false,
|
||||||
|
showCount: false,
|
||||||
|
showValue: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
41
common/spec/domain/folder.spec.ts
Normal file
41
common/spec/domain/folder.spec.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { FolderData } from "jslib-common/models/data/folderData";
|
||||||
|
import { Folder } from "jslib-common/models/domain/folder";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Folder", () => {
|
||||||
|
let data: FolderData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
id: "id",
|
||||||
|
name: "encName",
|
||||||
|
revisionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const field = new Folder(data);
|
||||||
|
|
||||||
|
expect(field).toEqual({
|
||||||
|
id: "id",
|
||||||
|
name: { encryptedString: "encName", encryptionType: 0 },
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const folder = new Folder();
|
||||||
|
folder.id = "id";
|
||||||
|
folder.name = mockEnc("encName");
|
||||||
|
folder.revisionDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
|
||||||
|
const view = await folder.decrypt();
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
id: "id",
|
||||||
|
name: "encName",
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
134
common/spec/domain/identity.spec.ts
Normal file
134
common/spec/domain/identity.spec.ts
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
import { IdentityData } from "jslib-common/models/data/identityData";
|
||||||
|
import { Identity } from "jslib-common/models/domain/identity";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Identity", () => {
|
||||||
|
let data: IdentityData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
title: "enctitle",
|
||||||
|
firstName: "encfirstName",
|
||||||
|
middleName: "encmiddleName",
|
||||||
|
lastName: "enclastName",
|
||||||
|
address1: "encaddress1",
|
||||||
|
address2: "encaddress2",
|
||||||
|
address3: "encaddress3",
|
||||||
|
city: "enccity",
|
||||||
|
state: "encstate",
|
||||||
|
postalCode: "encpostalCode",
|
||||||
|
country: "enccountry",
|
||||||
|
company: "enccompany",
|
||||||
|
email: "encemail",
|
||||||
|
phone: "encphone",
|
||||||
|
ssn: "encssn",
|
||||||
|
username: "encusername",
|
||||||
|
passportNumber: "encpassportNumber",
|
||||||
|
licenseNumber: "enclicenseNumber",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new IdentityData();
|
||||||
|
const identity = new Identity(data);
|
||||||
|
|
||||||
|
expect(identity).toEqual({
|
||||||
|
address1: null,
|
||||||
|
address2: null,
|
||||||
|
address3: null,
|
||||||
|
city: null,
|
||||||
|
company: null,
|
||||||
|
country: null,
|
||||||
|
email: null,
|
||||||
|
firstName: null,
|
||||||
|
lastName: null,
|
||||||
|
licenseNumber: null,
|
||||||
|
middleName: null,
|
||||||
|
passportNumber: null,
|
||||||
|
phone: null,
|
||||||
|
postalCode: null,
|
||||||
|
ssn: null,
|
||||||
|
state: null,
|
||||||
|
title: null,
|
||||||
|
username: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const identity = new Identity(data);
|
||||||
|
|
||||||
|
expect(identity).toEqual({
|
||||||
|
title: { encryptedString: "enctitle", encryptionType: 0 },
|
||||||
|
firstName: { encryptedString: "encfirstName", encryptionType: 0 },
|
||||||
|
middleName: { encryptedString: "encmiddleName", encryptionType: 0 },
|
||||||
|
lastName: { encryptedString: "enclastName", encryptionType: 0 },
|
||||||
|
address1: { encryptedString: "encaddress1", encryptionType: 0 },
|
||||||
|
address2: { encryptedString: "encaddress2", encryptionType: 0 },
|
||||||
|
address3: { encryptedString: "encaddress3", encryptionType: 0 },
|
||||||
|
city: { encryptedString: "enccity", encryptionType: 0 },
|
||||||
|
state: { encryptedString: "encstate", encryptionType: 0 },
|
||||||
|
postalCode: { encryptedString: "encpostalCode", encryptionType: 0 },
|
||||||
|
country: { encryptedString: "enccountry", encryptionType: 0 },
|
||||||
|
company: { encryptedString: "enccompany", encryptionType: 0 },
|
||||||
|
email: { encryptedString: "encemail", encryptionType: 0 },
|
||||||
|
phone: { encryptedString: "encphone", encryptionType: 0 },
|
||||||
|
ssn: { encryptedString: "encssn", encryptionType: 0 },
|
||||||
|
username: { encryptedString: "encusername", encryptionType: 0 },
|
||||||
|
passportNumber: { encryptedString: "encpassportNumber", encryptionType: 0 },
|
||||||
|
licenseNumber: { encryptedString: "enclicenseNumber", encryptionType: 0 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toIdentityData", () => {
|
||||||
|
const identity = new Identity(data);
|
||||||
|
expect(identity.toIdentityData()).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const identity = new Identity();
|
||||||
|
|
||||||
|
identity.title = mockEnc("mockTitle");
|
||||||
|
identity.firstName = mockEnc("mockFirstName");
|
||||||
|
identity.middleName = mockEnc("mockMiddleName");
|
||||||
|
identity.lastName = mockEnc("mockLastName");
|
||||||
|
identity.address1 = mockEnc("mockAddress1");
|
||||||
|
identity.address2 = mockEnc("mockAddress2");
|
||||||
|
identity.address3 = mockEnc("mockAddress3");
|
||||||
|
identity.city = mockEnc("mockCity");
|
||||||
|
identity.state = mockEnc("mockState");
|
||||||
|
identity.postalCode = mockEnc("mockPostalCode");
|
||||||
|
identity.country = mockEnc("mockCountry");
|
||||||
|
identity.company = mockEnc("mockCompany");
|
||||||
|
identity.email = mockEnc("mockEmail");
|
||||||
|
identity.phone = mockEnc("mockPhone");
|
||||||
|
identity.ssn = mockEnc("mockSsn");
|
||||||
|
identity.username = mockEnc("mockUsername");
|
||||||
|
identity.passportNumber = mockEnc("mockPassportNumber");
|
||||||
|
identity.licenseNumber = mockEnc("mockLicenseNumber");
|
||||||
|
|
||||||
|
const view = await identity.decrypt(null);
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
_firstName: "mockFirstName",
|
||||||
|
_lastName: "mockLastName",
|
||||||
|
_subTitle: null,
|
||||||
|
address1: "mockAddress1",
|
||||||
|
address2: "mockAddress2",
|
||||||
|
address3: "mockAddress3",
|
||||||
|
city: "mockCity",
|
||||||
|
company: "mockCompany",
|
||||||
|
country: "mockCountry",
|
||||||
|
email: "mockEmail",
|
||||||
|
licenseNumber: "mockLicenseNumber",
|
||||||
|
middleName: "mockMiddleName",
|
||||||
|
passportNumber: "mockPassportNumber",
|
||||||
|
phone: "mockPhone",
|
||||||
|
postalCode: "mockPostalCode",
|
||||||
|
ssn: "mockSsn",
|
||||||
|
state: "mockState",
|
||||||
|
title: "mockTitle",
|
||||||
|
username: "mockUsername",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
101
common/spec/domain/login.spec.ts
Normal file
101
common/spec/domain/login.spec.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import Substitute, { Arg } from "@fluffy-spoon/substitute";
|
||||||
|
|
||||||
|
import { UriMatchType } from "jslib-common/enums/uriMatchType";
|
||||||
|
import { LoginData } from "jslib-common/models/data/loginData";
|
||||||
|
import { Login } from "jslib-common/models/domain/login";
|
||||||
|
import { LoginUri } from "jslib-common/models/domain/loginUri";
|
||||||
|
import { LoginUriView } from "jslib-common/models/view/loginUriView";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Login DTO", () => {
|
||||||
|
it("Convert from empty LoginData", () => {
|
||||||
|
const data = new LoginData();
|
||||||
|
const login = new Login(data);
|
||||||
|
|
||||||
|
expect(login).toEqual({
|
||||||
|
passwordRevisionDate: null,
|
||||||
|
autofillOnPageLoad: undefined,
|
||||||
|
username: null,
|
||||||
|
password: null,
|
||||||
|
totp: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from full LoginData", () => {
|
||||||
|
const data: LoginData = {
|
||||||
|
uris: [{ uri: "uri", match: UriMatchType.Domain }],
|
||||||
|
username: "username",
|
||||||
|
password: "password",
|
||||||
|
passwordRevisionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
totp: "123",
|
||||||
|
autofillOnPageLoad: false,
|
||||||
|
};
|
||||||
|
const login = new Login(data);
|
||||||
|
|
||||||
|
expect(login).toEqual({
|
||||||
|
passwordRevisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
autofillOnPageLoad: false,
|
||||||
|
username: { encryptedString: "username", encryptionType: 0 },
|
||||||
|
password: { encryptedString: "password", encryptionType: 0 },
|
||||||
|
totp: { encryptedString: "123", encryptionType: 0 },
|
||||||
|
uris: [{ match: 0, uri: { encryptedString: "uri", encryptionType: 0 } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Initialize without LoginData", () => {
|
||||||
|
const login = new Login();
|
||||||
|
|
||||||
|
expect(login).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypts correctly", async () => {
|
||||||
|
const loginUri = Substitute.for<LoginUri>();
|
||||||
|
const loginUriView = new LoginUriView();
|
||||||
|
loginUriView.uri = "decrypted uri";
|
||||||
|
loginUri.decrypt(Arg.any()).resolves(loginUriView);
|
||||||
|
|
||||||
|
const login = new Login();
|
||||||
|
login.uris = [loginUri];
|
||||||
|
login.username = mockEnc("encrypted username");
|
||||||
|
login.password = mockEnc("encrypted password");
|
||||||
|
login.passwordRevisionDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
login.totp = mockEnc("encrypted totp");
|
||||||
|
login.autofillOnPageLoad = true;
|
||||||
|
|
||||||
|
const loginView = await login.decrypt(null);
|
||||||
|
expect(loginView).toEqual({
|
||||||
|
username: "encrypted username",
|
||||||
|
password: "encrypted password",
|
||||||
|
passwordRevisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
totp: "encrypted totp",
|
||||||
|
uris: [
|
||||||
|
{
|
||||||
|
match: null,
|
||||||
|
_uri: "decrypted uri",
|
||||||
|
_domain: null,
|
||||||
|
_hostname: null,
|
||||||
|
_host: null,
|
||||||
|
_canLaunch: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
autofillOnPageLoad: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Converts from LoginData and back", () => {
|
||||||
|
const data: LoginData = {
|
||||||
|
uris: [{ uri: "uri", match: UriMatchType.Domain }],
|
||||||
|
username: "username",
|
||||||
|
password: "password",
|
||||||
|
passwordRevisionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
totp: "123",
|
||||||
|
autofillOnPageLoad: false,
|
||||||
|
};
|
||||||
|
const login = new Login(data);
|
||||||
|
|
||||||
|
const loginData = login.toLoginData();
|
||||||
|
|
||||||
|
expect(loginData).toEqual(data);
|
||||||
|
});
|
||||||
|
});
|
||||||
57
common/spec/domain/loginUri.spec.ts
Normal file
57
common/spec/domain/loginUri.spec.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { UriMatchType } from "jslib-common/enums/uriMatchType";
|
||||||
|
import { LoginUriData } from "jslib-common/models/data/loginUriData";
|
||||||
|
import { LoginUri } from "jslib-common/models/domain/loginUri";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("LoginUri", () => {
|
||||||
|
let data: LoginUriData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
uri: "encUri",
|
||||||
|
match: UriMatchType.Domain,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new LoginUriData();
|
||||||
|
const loginUri = new LoginUri(data);
|
||||||
|
|
||||||
|
expect(loginUri).toEqual({
|
||||||
|
match: null,
|
||||||
|
uri: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const loginUri = new LoginUri(data);
|
||||||
|
|
||||||
|
expect(loginUri).toEqual({
|
||||||
|
match: 0,
|
||||||
|
uri: { encryptedString: "encUri", encryptionType: 0 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toLoginUriData", () => {
|
||||||
|
const loginUri = new LoginUri(data);
|
||||||
|
expect(loginUri.toLoginUriData()).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const loginUri = new LoginUri();
|
||||||
|
loginUri.match = UriMatchType.Exact;
|
||||||
|
loginUri.uri = mockEnc("uri");
|
||||||
|
|
||||||
|
const view = await loginUri.decrypt(null);
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
_canLaunch: null,
|
||||||
|
_domain: null,
|
||||||
|
_host: null,
|
||||||
|
_hostname: null,
|
||||||
|
_uri: "uri",
|
||||||
|
match: 3,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
51
common/spec/domain/password.spec.ts
Normal file
51
common/spec/domain/password.spec.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { PasswordHistoryData } from "jslib-common/models/data/passwordHistoryData";
|
||||||
|
import { Password } from "jslib-common/models/domain/password";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Password", () => {
|
||||||
|
let data: PasswordHistoryData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
password: "encPassword",
|
||||||
|
lastUsedDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new PasswordHistoryData();
|
||||||
|
const password = new Password(data);
|
||||||
|
|
||||||
|
expect(password).toMatchObject({
|
||||||
|
password: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const password = new Password(data);
|
||||||
|
|
||||||
|
expect(password).toEqual({
|
||||||
|
password: { encryptedString: "encPassword", encryptionType: 0 },
|
||||||
|
lastUsedDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toPasswordHistoryData", () => {
|
||||||
|
const password = new Password(data);
|
||||||
|
expect(password.toPasswordHistoryData()).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const password = new Password();
|
||||||
|
password.password = mockEnc("password");
|
||||||
|
password.lastUsedDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
|
||||||
|
const view = await password.decrypt(null);
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
password: "password",
|
||||||
|
lastUsedDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
46
common/spec/domain/secureNote.spec.ts
Normal file
46
common/spec/domain/secureNote.spec.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { SecureNoteType } from "jslib-common/enums/secureNoteType";
|
||||||
|
import { SecureNoteData } from "jslib-common/models/data/secureNoteData";
|
||||||
|
import { SecureNote } from "jslib-common/models/domain/secureNote";
|
||||||
|
|
||||||
|
describe("SecureNote", () => {
|
||||||
|
let data: SecureNoteData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
type: SecureNoteType.Generic,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new SecureNoteData();
|
||||||
|
const secureNote = new SecureNote(data);
|
||||||
|
|
||||||
|
expect(secureNote).toEqual({
|
||||||
|
type: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const secureNote = new SecureNote(data);
|
||||||
|
|
||||||
|
expect(secureNote).toEqual({
|
||||||
|
type: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("toSecureNoteData", () => {
|
||||||
|
const secureNote = new SecureNote(data);
|
||||||
|
expect(secureNote.toSecureNoteData()).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const secureNote = new SecureNote();
|
||||||
|
secureNote.type = SecureNoteType.Generic;
|
||||||
|
|
||||||
|
const view = await secureNote.decrypt(null);
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
type: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
140
common/spec/domain/send.spec.ts
Normal file
140
common/spec/domain/send.spec.ts
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
import Substitute, { Arg, SubstituteOf } from "@fluffy-spoon/substitute";
|
||||||
|
|
||||||
|
import { CryptoService } from "jslib-common/abstractions/crypto.service";
|
||||||
|
import { SendType } from "jslib-common/enums/sendType";
|
||||||
|
import { SendData } from "jslib-common/models/data/sendData";
|
||||||
|
import { EncString } from "jslib-common/models/domain/encString";
|
||||||
|
import { Send } from "jslib-common/models/domain/send";
|
||||||
|
import { SendText } from "jslib-common/models/domain/sendText";
|
||||||
|
import { ContainerService } from "jslib-common/services/container.service";
|
||||||
|
|
||||||
|
import { makeStaticByteArray, mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("Send", () => {
|
||||||
|
let data: SendData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
id: "id",
|
||||||
|
accessId: "accessId",
|
||||||
|
type: SendType.Text,
|
||||||
|
name: "encName",
|
||||||
|
notes: "encNotes",
|
||||||
|
text: {
|
||||||
|
text: "encText",
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
file: null,
|
||||||
|
key: "encKey",
|
||||||
|
maxAccessCount: null,
|
||||||
|
accessCount: 10,
|
||||||
|
revisionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
expirationDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
deletionDate: "2022-01-31T12:00:00.000Z",
|
||||||
|
password: "password",
|
||||||
|
disabled: false,
|
||||||
|
hideEmail: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new SendData();
|
||||||
|
const send = new Send(data);
|
||||||
|
|
||||||
|
expect(send).toEqual({
|
||||||
|
id: null,
|
||||||
|
accessId: null,
|
||||||
|
type: undefined,
|
||||||
|
name: null,
|
||||||
|
notes: null,
|
||||||
|
text: undefined,
|
||||||
|
file: undefined,
|
||||||
|
key: null,
|
||||||
|
maxAccessCount: undefined,
|
||||||
|
accessCount: undefined,
|
||||||
|
revisionDate: null,
|
||||||
|
expirationDate: null,
|
||||||
|
deletionDate: null,
|
||||||
|
password: undefined,
|
||||||
|
disabled: undefined,
|
||||||
|
hideEmail: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const send = new Send(data);
|
||||||
|
|
||||||
|
expect(send).toEqual({
|
||||||
|
id: "id",
|
||||||
|
accessId: "accessId",
|
||||||
|
type: SendType.Text,
|
||||||
|
name: { encryptedString: "encName", encryptionType: 0 },
|
||||||
|
notes: { encryptedString: "encNotes", encryptionType: 0 },
|
||||||
|
text: {
|
||||||
|
text: { encryptedString: "encText", encryptionType: 0 },
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
key: { encryptedString: "encKey", encryptionType: 0 },
|
||||||
|
maxAccessCount: null,
|
||||||
|
accessCount: 10,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
expirationDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
deletionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
password: "password",
|
||||||
|
disabled: false,
|
||||||
|
hideEmail: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const text = Substitute.for<SendText>();
|
||||||
|
text.decrypt(Arg.any()).resolves("textView" as any);
|
||||||
|
|
||||||
|
const send = new Send();
|
||||||
|
send.id = "id";
|
||||||
|
send.accessId = "accessId";
|
||||||
|
send.type = SendType.Text;
|
||||||
|
send.name = mockEnc("name");
|
||||||
|
send.notes = mockEnc("notes");
|
||||||
|
send.text = text;
|
||||||
|
send.key = mockEnc("key");
|
||||||
|
send.accessCount = 10;
|
||||||
|
send.revisionDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
send.expirationDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
send.deletionDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
send.password = "password";
|
||||||
|
send.disabled = false;
|
||||||
|
send.hideEmail = true;
|
||||||
|
|
||||||
|
const cryptoService = Substitute.for<CryptoService>();
|
||||||
|
cryptoService.decryptToBytes(send.key, null).resolves(makeStaticByteArray(32));
|
||||||
|
cryptoService.makeSendKey(Arg.any()).resolves("cryptoKey" as any);
|
||||||
|
|
||||||
|
(window as any).bitwardenContainerService = new ContainerService(cryptoService);
|
||||||
|
|
||||||
|
const view = await send.decrypt();
|
||||||
|
|
||||||
|
text.received(1).decrypt("cryptoKey" as any);
|
||||||
|
(send.name as SubstituteOf<EncString>).received(1).decrypt(null, "cryptoKey" as any);
|
||||||
|
|
||||||
|
expect(view).toMatchObject({
|
||||||
|
id: "id",
|
||||||
|
accessId: "accessId",
|
||||||
|
name: "name",
|
||||||
|
notes: "notes",
|
||||||
|
type: 0,
|
||||||
|
key: expect.anything(),
|
||||||
|
cryptoKey: "cryptoKey",
|
||||||
|
file: expect.anything(),
|
||||||
|
text: "textView",
|
||||||
|
maxAccessCount: undefined,
|
||||||
|
accessCount: 10,
|
||||||
|
revisionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
expirationDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
deletionDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
password: "password",
|
||||||
|
disabled: false,
|
||||||
|
hideEmail: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
84
common/spec/domain/sendAccess.spec.ts
Normal file
84
common/spec/domain/sendAccess.spec.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import Substitute, { Arg } from "@fluffy-spoon/substitute";
|
||||||
|
|
||||||
|
import { SendType } from "jslib-common/enums/sendType";
|
||||||
|
import { SendAccess } from "jslib-common/models/domain/sendAccess";
|
||||||
|
import { SendText } from "jslib-common/models/domain/sendText";
|
||||||
|
import { SendAccessResponse } from "jslib-common/models/response/sendAccessResponse";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("SendAccess", () => {
|
||||||
|
let request: SendAccessResponse;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
request = {
|
||||||
|
id: "id",
|
||||||
|
type: SendType.Text,
|
||||||
|
name: "encName",
|
||||||
|
file: null,
|
||||||
|
text: {
|
||||||
|
text: "encText",
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
expirationDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
creatorIdentifier: "creatorIdentifier",
|
||||||
|
} as SendAccessResponse;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const request = new SendAccessResponse({});
|
||||||
|
const sendAccess = new SendAccess(request);
|
||||||
|
|
||||||
|
expect(sendAccess).toEqual({
|
||||||
|
id: null,
|
||||||
|
type: undefined,
|
||||||
|
name: null,
|
||||||
|
creatorIdentifier: null,
|
||||||
|
expirationDate: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const sendAccess = new SendAccess(request);
|
||||||
|
|
||||||
|
expect(sendAccess).toEqual({
|
||||||
|
id: "id",
|
||||||
|
type: 0,
|
||||||
|
name: { encryptedString: "encName", encryptionType: 0 },
|
||||||
|
text: {
|
||||||
|
hidden: true,
|
||||||
|
text: { encryptedString: "encText", encryptionType: 0 },
|
||||||
|
},
|
||||||
|
expirationDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
creatorIdentifier: "creatorIdentifier",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const sendAccess = new SendAccess();
|
||||||
|
sendAccess.id = "id";
|
||||||
|
sendAccess.type = SendType.Text;
|
||||||
|
sendAccess.name = mockEnc("name");
|
||||||
|
|
||||||
|
const text = Substitute.for<SendText>();
|
||||||
|
text.decrypt(Arg.any()).resolves({} as any);
|
||||||
|
sendAccess.text = text;
|
||||||
|
|
||||||
|
sendAccess.expirationDate = new Date("2022-01-31T12:00:00.000Z");
|
||||||
|
sendAccess.creatorIdentifier = "creatorIdentifier";
|
||||||
|
|
||||||
|
const view = await sendAccess.decrypt(null);
|
||||||
|
|
||||||
|
text.received(1).decrypt(Arg.any());
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
id: "id",
|
||||||
|
type: 0,
|
||||||
|
name: "name",
|
||||||
|
text: {},
|
||||||
|
file: expect.anything(),
|
||||||
|
expirationDate: new Date("2022-01-31T12:00:00.000Z"),
|
||||||
|
creatorIdentifier: "creatorIdentifier",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
57
common/spec/domain/sendFile.spec.ts
Normal file
57
common/spec/domain/sendFile.spec.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { SendFileData } from "jslib-common/models/data/sendFileData";
|
||||||
|
import { SendFile } from "jslib-common/models/domain/sendFile";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("SendFile", () => {
|
||||||
|
let data: SendFileData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
id: "id",
|
||||||
|
size: "1100",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
fileName: "encFileName",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new SendFileData();
|
||||||
|
const sendFile = new SendFile(data);
|
||||||
|
|
||||||
|
expect(sendFile).toEqual({
|
||||||
|
fileName: null,
|
||||||
|
id: null,
|
||||||
|
size: undefined,
|
||||||
|
sizeName: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const sendFile = new SendFile(data);
|
||||||
|
|
||||||
|
expect(sendFile).toEqual({
|
||||||
|
id: "id",
|
||||||
|
size: "1100",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
fileName: { encryptedString: "encFileName", encryptionType: 0 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const sendFile = new SendFile();
|
||||||
|
sendFile.id = "id";
|
||||||
|
sendFile.size = "1100";
|
||||||
|
sendFile.sizeName = "1.1 KB";
|
||||||
|
sendFile.fileName = mockEnc("fileName");
|
||||||
|
|
||||||
|
const view = await sendFile.decrypt(null);
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
fileName: "fileName",
|
||||||
|
id: "id",
|
||||||
|
size: "1100",
|
||||||
|
sizeName: "1.1 KB",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
47
common/spec/domain/sendText.spec.ts
Normal file
47
common/spec/domain/sendText.spec.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { SendTextData } from "jslib-common/models/data/sendTextData";
|
||||||
|
import { SendText } from "jslib-common/models/domain/sendText";
|
||||||
|
|
||||||
|
import { mockEnc } from "../utils";
|
||||||
|
|
||||||
|
describe("SendText", () => {
|
||||||
|
let data: SendTextData;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
data = {
|
||||||
|
text: "encText",
|
||||||
|
hidden: false,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert from empty", () => {
|
||||||
|
const data = new SendTextData();
|
||||||
|
const secureNote = new SendText(data);
|
||||||
|
|
||||||
|
expect(secureNote).toEqual({
|
||||||
|
hidden: undefined,
|
||||||
|
text: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Convert", () => {
|
||||||
|
const secureNote = new SendText(data);
|
||||||
|
|
||||||
|
expect(secureNote).toEqual({
|
||||||
|
hidden: false,
|
||||||
|
text: { encryptedString: "encText", encryptionType: 0 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Decrypt", async () => {
|
||||||
|
const secureNote = new SendText();
|
||||||
|
secureNote.text = mockEnc("text");
|
||||||
|
secureNote.hidden = true;
|
||||||
|
|
||||||
|
const view = await secureNote.decrypt(null);
|
||||||
|
|
||||||
|
expect(view).toEqual({
|
||||||
|
text: "text",
|
||||||
|
hidden: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
69
common/spec/domain/symmetricCryptoKey.spec.ts
Normal file
69
common/spec/domain/symmetricCryptoKey.spec.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { EncryptionType } from "jslib-common/enums/encryptionType";
|
||||||
|
import { SymmetricCryptoKey } from "jslib-common/models/domain/symmetricCryptoKey";
|
||||||
|
|
||||||
|
import { makeStaticByteArray } from "../utils";
|
||||||
|
|
||||||
|
describe("SymmetricCryptoKey", () => {
|
||||||
|
it("errors if no key", () => {
|
||||||
|
const t = () => {
|
||||||
|
new SymmetricCryptoKey(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(t).toThrowError("Must provide key");
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("guesses encKey from key length", () => {
|
||||||
|
it("AesCbc256_B64", () => {
|
||||||
|
const key = makeStaticByteArray(32);
|
||||||
|
const cryptoKey = new SymmetricCryptoKey(key);
|
||||||
|
|
||||||
|
expect(cryptoKey).toEqual({
|
||||||
|
encKey: key,
|
||||||
|
encKeyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=",
|
||||||
|
encType: 0,
|
||||||
|
key: key,
|
||||||
|
keyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=",
|
||||||
|
macKey: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("AesCbc128_HmacSha256_B64", () => {
|
||||||
|
const key = makeStaticByteArray(32);
|
||||||
|
const cryptoKey = new SymmetricCryptoKey(key, EncryptionType.AesCbc128_HmacSha256_B64);
|
||||||
|
|
||||||
|
expect(cryptoKey).toEqual({
|
||||||
|
encKey: key.slice(0, 16),
|
||||||
|
encKeyB64: "AAECAwQFBgcICQoLDA0ODw==",
|
||||||
|
encType: 1,
|
||||||
|
key: key,
|
||||||
|
keyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=",
|
||||||
|
macKey: key.slice(16, 32),
|
||||||
|
macKeyB64: "EBESExQVFhcYGRobHB0eHw==",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("AesCbc256_HmacSha256_B64", () => {
|
||||||
|
const key = makeStaticByteArray(64);
|
||||||
|
const cryptoKey = new SymmetricCryptoKey(key);
|
||||||
|
|
||||||
|
expect(cryptoKey).toEqual({
|
||||||
|
encKey: key.slice(0, 32),
|
||||||
|
encKeyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=",
|
||||||
|
encType: 2,
|
||||||
|
key: key,
|
||||||
|
keyB64:
|
||||||
|
"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+Pw==",
|
||||||
|
macKey: key.slice(32, 64),
|
||||||
|
macKeyB64: "ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8=",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("unknown length", () => {
|
||||||
|
const t = () => {
|
||||||
|
new SymmetricCryptoKey(makeStaticByteArray(30));
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(t).toThrowError("Unable to determine encType.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -11,6 +11,9 @@ import { CreditCardData } from "./testData/onePassword1Pux/CreditCard";
|
|||||||
import { DatabaseData } from "./testData/onePassword1Pux/Database";
|
import { DatabaseData } from "./testData/onePassword1Pux/Database";
|
||||||
import { DriversLicenseData } from "./testData/onePassword1Pux/DriversLicense";
|
import { DriversLicenseData } from "./testData/onePassword1Pux/DriversLicense";
|
||||||
import { EmailAccountData } from "./testData/onePassword1Pux/EmailAccount";
|
import { EmailAccountData } from "./testData/onePassword1Pux/EmailAccount";
|
||||||
|
import { EmailFieldData } from "./testData/onePassword1Pux/Emailfield";
|
||||||
|
import { EmailFieldOnIdentityData } from "./testData/onePassword1Pux/EmailfieldOnIdentity";
|
||||||
|
import { EmailFieldOnIdentityPrefilledData } from "./testData/onePassword1Pux/EmailfieldOnIdentity_Prefilled";
|
||||||
import { IdentityData } from "./testData/onePassword1Pux/IdentityData";
|
import { IdentityData } from "./testData/onePassword1Pux/IdentityData";
|
||||||
import { LoginData } from "./testData/onePassword1Pux/LoginData";
|
import { LoginData } from "./testData/onePassword1Pux/LoginData";
|
||||||
import { MedicalRecordData } from "./testData/onePassword1Pux/MedicalRecord";
|
import { MedicalRecordData } from "./testData/onePassword1Pux/MedicalRecord";
|
||||||
@@ -102,6 +105,25 @@ describe("1Password 1Pux Importer", () => {
|
|||||||
expect(cipher.fields[1].type).toBe(FieldType.Boolean);
|
expect(cipher.fields[1].type).toBe(FieldType.Boolean);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should add fields of type email as custom fields", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const EmailFieldDataJson = JSON.stringify(EmailFieldData);
|
||||||
|
const result = await importer.parse(EmailFieldDataJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const ciphers = result.ciphers;
|
||||||
|
expect(ciphers.length).toEqual(1);
|
||||||
|
const cipher = ciphers.shift();
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("reg_email");
|
||||||
|
expect(cipher.fields[0].value).toEqual("kriddler@nullvalue.test");
|
||||||
|
expect(cipher.fields[0].type).toBe(FieldType.Text);
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("provider");
|
||||||
|
expect(cipher.fields[1].value).toEqual("myEmailProvider");
|
||||||
|
expect(cipher.fields[1].type).toBe(FieldType.Text);
|
||||||
|
});
|
||||||
|
|
||||||
it('should create concealed field as "hidden" type', async () => {
|
it('should create concealed field as "hidden" type', async () => {
|
||||||
const importer = new Importer();
|
const importer = new Importer();
|
||||||
const result = await importer.parse(OnePuxExampleFileJson);
|
const result = await importer.parse(OnePuxExampleFileJson);
|
||||||
@@ -205,6 +227,46 @@ describe("1Password 1Pux Importer", () => {
|
|||||||
validateCustomField(cipher.fields, "forumsig", "super cool guy");
|
validateCustomField(cipher.fields, "forumsig", "super cool guy");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("emails fields on identity types should be added to the identity email field", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const EmailFieldOnIdentityDataJson = JSON.stringify(EmailFieldOnIdentityData);
|
||||||
|
const result = await importer.parse(EmailFieldOnIdentityDataJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const ciphers = result.ciphers;
|
||||||
|
expect(ciphers.length).toEqual(1);
|
||||||
|
const cipher = ciphers.shift();
|
||||||
|
|
||||||
|
const identity = cipher.identity;
|
||||||
|
expect(identity.email).toEqual("gengels@nullvalue.test");
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("provider");
|
||||||
|
expect(cipher.fields[0].value).toEqual("myEmailProvider");
|
||||||
|
expect(cipher.fields[0].type).toBe(FieldType.Text);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("emails fields on identity types should be added to custom fields if identity.email has been filled", async () => {
|
||||||
|
const importer = new Importer();
|
||||||
|
const EmailFieldOnIdentityPrefilledDataJson = JSON.stringify(EmailFieldOnIdentityPrefilledData);
|
||||||
|
const result = await importer.parse(EmailFieldOnIdentityPrefilledDataJson);
|
||||||
|
expect(result != null).toBe(true);
|
||||||
|
|
||||||
|
const ciphers = result.ciphers;
|
||||||
|
expect(ciphers.length).toEqual(1);
|
||||||
|
const cipher = ciphers.shift();
|
||||||
|
|
||||||
|
const identity = cipher.identity;
|
||||||
|
expect(identity.email).toEqual("gengels@nullvalue.test");
|
||||||
|
|
||||||
|
expect(cipher.fields[0].name).toEqual("2nd_email");
|
||||||
|
expect(cipher.fields[0].value).toEqual("kriddler@nullvalue.test");
|
||||||
|
expect(cipher.fields[0].type).toBe(FieldType.Text);
|
||||||
|
|
||||||
|
expect(cipher.fields[1].name).toEqual("provider");
|
||||||
|
expect(cipher.fields[1].value).toEqual("myEmailProvider");
|
||||||
|
expect(cipher.fields[1].type).toBe(FieldType.Text);
|
||||||
|
});
|
||||||
|
|
||||||
it("should parse category 005 - Password (Legacy)", async () => {
|
it("should parse category 005 - Password (Legacy)", async () => {
|
||||||
const importer = new Importer();
|
const importer = new Importer();
|
||||||
const jsonString = JSON.stringify(PasswordData);
|
const jsonString = JSON.stringify(PasswordData);
|
||||||
|
|||||||
91
common/spec/importers/testData/onePassword1Pux/Emailfield.ts
Normal file
91
common/spec/importers/testData/onePassword1Pux/Emailfield.ts
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes";
|
||||||
|
|
||||||
|
export const EmailFieldData: ExportData = {
|
||||||
|
accounts: [
|
||||||
|
{
|
||||||
|
attrs: {
|
||||||
|
accountName: "1Password Customer",
|
||||||
|
name: "1Password Customer",
|
||||||
|
avatar: "",
|
||||||
|
email: "username123123123@gmail.com",
|
||||||
|
uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E",
|
||||||
|
domain: "https://my.1password.com/",
|
||||||
|
},
|
||||||
|
vaults: [
|
||||||
|
{
|
||||||
|
attrs: {
|
||||||
|
uuid: "pqcgbqjxr4tng2hsqt5ffrgwju",
|
||||||
|
desc: "Just test entries",
|
||||||
|
avatar: "ke7i5rxnjrh3tj6uesstcosspu.png",
|
||||||
|
name: "T's Test Vault",
|
||||||
|
type: "U",
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
uuid: "47hvppiuwbanbza7bq6jpdjfxu",
|
||||||
|
favIndex: 1,
|
||||||
|
createdAt: 1619467985,
|
||||||
|
updatedAt: 1619468230,
|
||||||
|
trashed: false,
|
||||||
|
categoryUuid: "100",
|
||||||
|
details: {
|
||||||
|
loginFields: [],
|
||||||
|
notesPlain: "My Software License",
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
title: "",
|
||||||
|
fields: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Customer",
|
||||||
|
name: "customer",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
title: "registered email",
|
||||||
|
id: "reg_email",
|
||||||
|
value: {
|
||||||
|
email: {
|
||||||
|
email_address: "kriddler@nullvalue.test",
|
||||||
|
provider: "myEmailProvider",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
indexAtSource: 1,
|
||||||
|
guarded: false,
|
||||||
|
multiline: false,
|
||||||
|
dontGenerate: false,
|
||||||
|
inputTraits: {
|
||||||
|
keyboard: "emailAddress",
|
||||||
|
correction: "default",
|
||||||
|
capitalization: "default",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Publisher",
|
||||||
|
name: "publisher",
|
||||||
|
fields: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Order",
|
||||||
|
name: "order",
|
||||||
|
fields: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
passwordHistory: [],
|
||||||
|
},
|
||||||
|
overview: {
|
||||||
|
subtitle: "5.10.1000",
|
||||||
|
title: "Limux Product Key",
|
||||||
|
url: "",
|
||||||
|
ps: 0,
|
||||||
|
pbe: 0.0,
|
||||||
|
pgrng: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes";
|
||||||
|
|
||||||
|
export const EmailFieldOnIdentityData: ExportData = {
|
||||||
|
accounts: [
|
||||||
|
{
|
||||||
|
attrs: {
|
||||||
|
accountName: "1Password Customer",
|
||||||
|
name: "1Password Customer",
|
||||||
|
avatar: "",
|
||||||
|
email: "username123123123@gmail.com",
|
||||||
|
uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E",
|
||||||
|
domain: "https://my.1password.com/",
|
||||||
|
},
|
||||||
|
vaults: [
|
||||||
|
{
|
||||||
|
attrs: {
|
||||||
|
uuid: "pqcgbqjxr4tng2hsqt5ffrgwju",
|
||||||
|
desc: "Just test entries",
|
||||||
|
avatar: "ke7i5rxnjrh3tj6uesstcosspu.png",
|
||||||
|
name: "T's Test Vault",
|
||||||
|
type: "U",
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
uuid: "45mjttbbq3owgij2uis55pfrlq",
|
||||||
|
favIndex: 0,
|
||||||
|
createdAt: 1619465450,
|
||||||
|
updatedAt: 1619465789,
|
||||||
|
trashed: false,
|
||||||
|
categoryUuid: "004",
|
||||||
|
details: {
|
||||||
|
loginFields: [],
|
||||||
|
notesPlain: "",
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
title: "Identification",
|
||||||
|
name: "name",
|
||||||
|
fields: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Address",
|
||||||
|
name: "address",
|
||||||
|
fields: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Internet Details",
|
||||||
|
name: "internet",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
title: "E-mail",
|
||||||
|
id: "E-mail",
|
||||||
|
value: {
|
||||||
|
email: {
|
||||||
|
email_address: "gengels@nullvalue.test",
|
||||||
|
provider: "myEmailProvider",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
indexAtSource: 4,
|
||||||
|
guarded: false,
|
||||||
|
multiline: false,
|
||||||
|
dontGenerate: false,
|
||||||
|
inputTraits: {
|
||||||
|
keyboard: "emailAddress",
|
||||||
|
correction: "default",
|
||||||
|
capitalization: "default",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
passwordHistory: [],
|
||||||
|
},
|
||||||
|
overview: {
|
||||||
|
subtitle: "George Engels",
|
||||||
|
title: "George Engels",
|
||||||
|
url: "",
|
||||||
|
ps: 0,
|
||||||
|
pbe: 0.0,
|
||||||
|
pgrng: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import { ExportData } from "jslib-common/importers/onepasswordImporters/types/onepassword1PuxImporterTypes";
|
||||||
|
|
||||||
|
export const EmailFieldOnIdentityPrefilledData: ExportData = {
|
||||||
|
accounts: [
|
||||||
|
{
|
||||||
|
attrs: {
|
||||||
|
accountName: "1Password Customer",
|
||||||
|
name: "1Password Customer",
|
||||||
|
avatar: "",
|
||||||
|
email: "username123123123@gmail.com",
|
||||||
|
uuid: "TRIZ3XV4JJFRXJ3BARILLTUA6E",
|
||||||
|
domain: "https://my.1password.com/",
|
||||||
|
},
|
||||||
|
vaults: [
|
||||||
|
{
|
||||||
|
attrs: {
|
||||||
|
uuid: "pqcgbqjxr4tng2hsqt5ffrgwju",
|
||||||
|
desc: "Just test entries",
|
||||||
|
avatar: "ke7i5rxnjrh3tj6uesstcosspu.png",
|
||||||
|
name: "T's Test Vault",
|
||||||
|
type: "U",
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
uuid: "45mjttbbq3owgij2uis55pfrlq",
|
||||||
|
favIndex: 0,
|
||||||
|
createdAt: 1619465450,
|
||||||
|
updatedAt: 1619465789,
|
||||||
|
trashed: false,
|
||||||
|
categoryUuid: "004",
|
||||||
|
details: {
|
||||||
|
loginFields: [],
|
||||||
|
notesPlain: "",
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
title: "Identification",
|
||||||
|
name: "name",
|
||||||
|
fields: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Address",
|
||||||
|
name: "address",
|
||||||
|
fields: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Internet Details",
|
||||||
|
name: "internet",
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
title: "email",
|
||||||
|
id: "email",
|
||||||
|
value: {
|
||||||
|
string: "gengels@nullvalue.test",
|
||||||
|
},
|
||||||
|
indexAtSource: 4,
|
||||||
|
guarded: false,
|
||||||
|
multiline: false,
|
||||||
|
dontGenerate: false,
|
||||||
|
inputTraits: {
|
||||||
|
keyboard: "emailAddress",
|
||||||
|
correction: "default",
|
||||||
|
capitalization: "default",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "2nd email",
|
||||||
|
id: "2nd_email",
|
||||||
|
value: {
|
||||||
|
email: {
|
||||||
|
email_address: "kriddler@nullvalue.test",
|
||||||
|
provider: "myEmailProvider",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
indexAtSource: 1,
|
||||||
|
guarded: false,
|
||||||
|
multiline: false,
|
||||||
|
dontGenerate: false,
|
||||||
|
inputTraits: {
|
||||||
|
keyboard: "emailAddress",
|
||||||
|
correction: "default",
|
||||||
|
capitalization: "default",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
passwordHistory: [],
|
||||||
|
},
|
||||||
|
overview: {
|
||||||
|
subtitle: "George Engels",
|
||||||
|
title: "George Engels",
|
||||||
|
url: "",
|
||||||
|
ps: 0,
|
||||||
|
pbe: 0.0,
|
||||||
|
pgrng: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -344,7 +344,10 @@ export const SanitizedExport: ExportData = {
|
|||||||
title: "",
|
title: "",
|
||||||
id: "irpvnshg5kjpkmj5jwy4xxkfom",
|
id: "irpvnshg5kjpkmj5jwy4xxkfom",
|
||||||
value: {
|
value: {
|
||||||
email: "plexuser@nullvalue.test",
|
email: {
|
||||||
|
email_address: "plexuser@nullvalue.test",
|
||||||
|
provider: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
indexAtSource: 0,
|
indexAtSource: 0,
|
||||||
guarded: false,
|
guarded: false,
|
||||||
@@ -1434,7 +1437,10 @@ export const SanitizedExport: ExportData = {
|
|||||||
title: "registered email",
|
title: "registered email",
|
||||||
id: "reg_email",
|
id: "reg_email",
|
||||||
value: {
|
value: {
|
||||||
email: "kriddler@nullvalue.test",
|
email: {
|
||||||
|
email_address: "kriddler@nullvalue.test",
|
||||||
|
provider: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
indexAtSource: 1,
|
indexAtSource: 1,
|
||||||
guarded: false,
|
guarded: false,
|
||||||
@@ -1536,7 +1542,10 @@ export const SanitizedExport: ExportData = {
|
|||||||
title: "support email",
|
title: "support email",
|
||||||
id: "support_email",
|
id: "support_email",
|
||||||
value: {
|
value: {
|
||||||
email: "support@nullvalue.test",
|
email: {
|
||||||
|
email_address: "support@nullvalue.test",
|
||||||
|
provider: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
indexAtSource: 4,
|
indexAtSource: 4,
|
||||||
guarded: false,
|
guarded: false,
|
||||||
@@ -4014,7 +4023,10 @@ export const SanitizedExport: ExportData = {
|
|||||||
title: "registered email",
|
title: "registered email",
|
||||||
id: "reg_email",
|
id: "reg_email",
|
||||||
value: {
|
value: {
|
||||||
email: "",
|
email: {
|
||||||
|
email_address: "",
|
||||||
|
provider: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
indexAtSource: 1,
|
indexAtSource: 1,
|
||||||
guarded: false,
|
guarded: false,
|
||||||
@@ -4116,7 +4128,10 @@ export const SanitizedExport: ExportData = {
|
|||||||
title: "support email",
|
title: "support email",
|
||||||
id: "support_email",
|
id: "support_email",
|
||||||
value: {
|
value: {
|
||||||
email: "",
|
email: {
|
||||||
|
email_address: "",
|
||||||
|
provider: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
indexAtSource: 4,
|
indexAtSource: 4,
|
||||||
guarded: false,
|
guarded: false,
|
||||||
|
|||||||
@@ -93,7 +93,10 @@ export const SoftwareLicenseData: ExportData = {
|
|||||||
title: "registered email",
|
title: "registered email",
|
||||||
id: "reg_email",
|
id: "reg_email",
|
||||||
value: {
|
value: {
|
||||||
email: "kriddler@nullvalue.test",
|
email: {
|
||||||
|
email_address: "kriddler@nullvalue.test",
|
||||||
|
provider: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
indexAtSource: 1,
|
indexAtSource: 1,
|
||||||
guarded: false,
|
guarded: false,
|
||||||
@@ -195,7 +198,10 @@ export const SoftwareLicenseData: ExportData = {
|
|||||||
title: "support email",
|
title: "support email",
|
||||||
id: "support_email",
|
id: "support_email",
|
||||||
value: {
|
value: {
|
||||||
email: "support@nullvalue.test",
|
email: {
|
||||||
|
email_address: "support@nullvalue.test",
|
||||||
|
provider: null,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
indexAtSource: 4,
|
indexAtSource: 4,
|
||||||
guarded: false,
|
guarded: false,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { Utils } from "jslib-common/misc/utils";
|
|||||||
import { Cipher } from "jslib-common/models/domain/cipher";
|
import { Cipher } from "jslib-common/models/domain/cipher";
|
||||||
import { EncString } from "jslib-common/models/domain/encString";
|
import { EncString } from "jslib-common/models/domain/encString";
|
||||||
import { Login } from "jslib-common/models/domain/login";
|
import { Login } from "jslib-common/models/domain/login";
|
||||||
import { CipherWithIds as CipherExport } from "jslib-common/models/export/cipherWithIds";
|
import { CipherWithIdExport as CipherExport } from "jslib-common/models/export/cipherWithIdsExport";
|
||||||
import { CipherView } from "jslib-common/models/view/cipherView";
|
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||||
import { LoginView } from "jslib-common/models/view/loginView";
|
import { LoginView } from "jslib-common/models/view/loginView";
|
||||||
import { ExportService } from "jslib-common/services/export.service";
|
import { ExportService } from "jslib-common/services/export.service";
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
import Substitute, { Arg } from "@fluffy-spoon/substitute";
|
||||||
|
|
||||||
|
import { EncString } from "jslib-common/models/domain/encString";
|
||||||
|
|
||||||
function newGuid() {
|
function newGuid() {
|
||||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
||||||
const r = (Math.random() * 16) | 0;
|
const r = (Math.random() * 16) | 0;
|
||||||
@@ -16,3 +20,18 @@ export function BuildTestObject<T, K extends keyof T = keyof T>(
|
|||||||
): T {
|
): T {
|
||||||
return Object.assign(constructor === null ? {} : new constructor(), def) as T;
|
return Object.assign(constructor === null ? {} : new constructor(), def) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mockEnc(s: string): EncString {
|
||||||
|
const mock = Substitute.for<EncString>();
|
||||||
|
mock.decrypt(Arg.any(), Arg.any()).resolves(s);
|
||||||
|
|
||||||
|
return mock;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeStaticByteArray(length: number) {
|
||||||
|
const arr = new Uint8Array(length);
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
arr[i] = i;
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
export abstract class BiometricMain {
|
|
||||||
isError: boolean;
|
|
||||||
init: () => Promise<void>;
|
|
||||||
supportsBiometric: () => Promise<boolean>;
|
|
||||||
authenticateBiometric: () => Promise<boolean>;
|
|
||||||
}
|
|
||||||
@@ -242,8 +242,6 @@ export abstract class StateService<T extends Account = Account> {
|
|||||||
setLocalData: (value: string, options?: StorageOptions) => Promise<void>;
|
setLocalData: (value: string, options?: StorageOptions) => Promise<void>;
|
||||||
getLocale: (options?: StorageOptions) => Promise<string>;
|
getLocale: (options?: StorageOptions) => Promise<string>;
|
||||||
setLocale: (value: string, options?: StorageOptions) => Promise<void>;
|
setLocale: (value: string, options?: StorageOptions) => Promise<void>;
|
||||||
getLoginRedirect: (options?: StorageOptions) => Promise<any>;
|
|
||||||
setLoginRedirect: (value: any, options?: StorageOptions) => Promise<void>;
|
|
||||||
getMainWindowSize: (options?: StorageOptions) => Promise<number>;
|
getMainWindowSize: (options?: StorageOptions) => Promise<number>;
|
||||||
setMainWindowSize: (value: number, options?: StorageOptions) => Promise<void>;
|
setMainWindowSize: (value: number, options?: StorageOptions) => Promise<void>;
|
||||||
getMinimizeOnCopyToClipboard: (options?: StorageOptions) => Promise<boolean>;
|
getMinimizeOnCopyToClipboard: (options?: StorageOptions) => Promise<boolean>;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export abstract class UsernameGenerationService {
|
|||||||
generateWord: (options: any) => Promise<string>;
|
generateWord: (options: any) => Promise<string>;
|
||||||
generateSubaddress: (options: any) => Promise<string>;
|
generateSubaddress: (options: any) => Promise<string>;
|
||||||
generateCatchall: (options: any) => Promise<string>;
|
generateCatchall: (options: any) => Promise<string>;
|
||||||
|
generateForwarded: (options: any) => Promise<string>;
|
||||||
getOptions: () => Promise<any>;
|
getOptions: () => Promise<any>;
|
||||||
saveOptions: (options: any) => Promise<void>;
|
saveOptions: (options: any) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import { CryptoService } from "../abstractions/crypto.service";
|
|||||||
import { I18nService } from "../abstractions/i18n.service";
|
import { I18nService } from "../abstractions/i18n.service";
|
||||||
import { EncString } from "../models/domain/encString";
|
import { EncString } from "../models/domain/encString";
|
||||||
import { ImportResult } from "../models/domain/importResult";
|
import { ImportResult } from "../models/domain/importResult";
|
||||||
import { CipherWithIds } from "../models/export/cipherWithIds";
|
import { CipherWithIdExport } from "../models/export/cipherWithIdsExport";
|
||||||
import { CollectionWithId } from "../models/export/collectionWithId";
|
import { CollectionWithIdExport } from "../models/export/collectionWithIdExport";
|
||||||
import { FolderWithId } from "../models/export/folderWithId";
|
import { FolderWithIdExport } from "../models/export/folderWithIdExport";
|
||||||
|
|
||||||
import { BaseImporter } from "./baseImporter";
|
import { BaseImporter } from "./baseImporter";
|
||||||
import { Importer } from "./importer";
|
import { Importer } from "./importer";
|
||||||
@@ -59,8 +59,8 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
|
|||||||
const groupingsMap = new Map<string, number>();
|
const groupingsMap = new Map<string, number>();
|
||||||
|
|
||||||
if (this.organization && this.results.collections != null) {
|
if (this.organization && this.results.collections != null) {
|
||||||
for (const c of this.results.collections as CollectionWithId[]) {
|
for (const c of this.results.collections as CollectionWithIdExport[]) {
|
||||||
const collection = CollectionWithId.toDomain(c);
|
const collection = CollectionWithIdExport.toDomain(c);
|
||||||
if (collection != null) {
|
if (collection != null) {
|
||||||
collection.id = null;
|
collection.id = null;
|
||||||
collection.organizationId = this.organizationId;
|
collection.organizationId = this.organizationId;
|
||||||
@@ -70,8 +70,8 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!this.organization && this.results.folders != null) {
|
} else if (!this.organization && this.results.folders != null) {
|
||||||
for (const f of this.results.folders as FolderWithId[]) {
|
for (const f of this.results.folders as FolderWithIdExport[]) {
|
||||||
const folder = FolderWithId.toDomain(f);
|
const folder = FolderWithIdExport.toDomain(f);
|
||||||
if (folder != null) {
|
if (folder != null) {
|
||||||
folder.id = null;
|
folder.id = null;
|
||||||
const view = await folder.decrypt();
|
const view = await folder.decrypt();
|
||||||
@@ -81,8 +81,8 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const c of this.results.items as CipherWithIds[]) {
|
for (const c of this.results.items as CipherWithIdExport[]) {
|
||||||
const cipher = CipherWithIds.toDomain(c);
|
const cipher = CipherWithIdExport.toDomain(c);
|
||||||
// reset ids incase they were set for some reason
|
// reset ids incase they were set for some reason
|
||||||
cipher.id = null;
|
cipher.id = null;
|
||||||
cipher.folderId = null;
|
cipher.folderId = null;
|
||||||
@@ -121,8 +121,8 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
|
|||||||
private parseDecrypted() {
|
private parseDecrypted() {
|
||||||
const groupingsMap = new Map<string, number>();
|
const groupingsMap = new Map<string, number>();
|
||||||
if (this.organization && this.results.collections != null) {
|
if (this.organization && this.results.collections != null) {
|
||||||
this.results.collections.forEach((c: CollectionWithId) => {
|
this.results.collections.forEach((c: CollectionWithIdExport) => {
|
||||||
const collection = CollectionWithId.toView(c);
|
const collection = CollectionWithIdExport.toView(c);
|
||||||
if (collection != null) {
|
if (collection != null) {
|
||||||
collection.id = null;
|
collection.id = null;
|
||||||
collection.organizationId = null;
|
collection.organizationId = null;
|
||||||
@@ -131,8 +131,8 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (!this.organization && this.results.folders != null) {
|
} else if (!this.organization && this.results.folders != null) {
|
||||||
this.results.folders.forEach((f: FolderWithId) => {
|
this.results.folders.forEach((f: FolderWithIdExport) => {
|
||||||
const folder = FolderWithId.toView(f);
|
const folder = FolderWithIdExport.toView(f);
|
||||||
if (folder != null) {
|
if (folder != null) {
|
||||||
folder.id = null;
|
folder.id = null;
|
||||||
groupingsMap.set(f.id, this.result.folders.length);
|
groupingsMap.set(f.id, this.result.folders.length);
|
||||||
@@ -141,8 +141,8 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.results.items.forEach((c: CipherWithIds) => {
|
this.results.items.forEach((c: CipherWithIdExport) => {
|
||||||
const cipher = CipherWithIds.toView(c);
|
const cipher = CipherWithIdExport.toView(c);
|
||||||
// reset ids incase they were set for some reason
|
// reset ids incase they were set for some reason
|
||||||
cipher.id = null;
|
cipher.id = null;
|
||||||
cipher.folderId = null;
|
cipher.folderId = null;
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ export class OnePassword1PuxImporter extends BaseImporter implements Importer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (cipher.type === CipherType.Identity) {
|
} else if (cipher.type === CipherType.Identity) {
|
||||||
if (this.fillIdentity(field, fieldValue, cipher)) {
|
if (this.fillIdentity(field, fieldValue, cipher, valueKey)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (valueKey === "address") {
|
if (valueKey === "address") {
|
||||||
@@ -312,6 +312,14 @@ export class OnePassword1PuxImporter extends BaseImporter implements Importer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (valueKey === "email") {
|
||||||
|
// fieldValue is an object casted into a string, so access the plain value instead
|
||||||
|
const { email_address, provider } = field.value.email;
|
||||||
|
this.processKvp(cipher, fieldName, email_address, FieldType.Text);
|
||||||
|
this.processKvp(cipher, "provider", provider, FieldType.Text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Do not include a password field if it's already in the history
|
// Do not include a password field if it's already in the history
|
||||||
if (
|
if (
|
||||||
field.title === "password" &&
|
field.title === "password" &&
|
||||||
@@ -440,7 +448,12 @@ export class OnePassword1PuxImporter extends BaseImporter implements Importer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private fillIdentity(field: FieldsEntity, fieldValue: string, cipher: CipherView): boolean {
|
private fillIdentity(
|
||||||
|
field: FieldsEntity,
|
||||||
|
fieldValue: string,
|
||||||
|
cipher: CipherView,
|
||||||
|
valueKey: string
|
||||||
|
): boolean {
|
||||||
if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "firstname") {
|
if (this.isNullOrWhitespace(cipher.identity.firstName) && field.id === "firstname") {
|
||||||
cipher.identity.firstName = fieldValue;
|
cipher.identity.firstName = fieldValue;
|
||||||
return true;
|
return true;
|
||||||
@@ -466,9 +479,18 @@ export class OnePassword1PuxImporter extends BaseImporter implements Importer {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isNullOrWhitespace(cipher.identity.email) && field.id === "email") {
|
if (this.isNullOrWhitespace(cipher.identity.email)) {
|
||||||
cipher.identity.email = fieldValue;
|
if (valueKey === "email") {
|
||||||
return true;
|
const { email_address, provider } = field.value.email;
|
||||||
|
cipher.identity.email = this.getValueOrDefault(email_address);
|
||||||
|
this.processKvp(cipher, "provider", provider, FieldType.Text);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field.id === "email") {
|
||||||
|
cipher.identity.email = fieldValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isNullOrWhitespace(cipher.identity.username) && field.id === "username") {
|
if (this.isNullOrWhitespace(cipher.identity.username) && field.id === "username") {
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ export interface Value {
|
|||||||
date?: number | null;
|
date?: number | null;
|
||||||
string?: string | null;
|
string?: string | null;
|
||||||
concealed?: string | null;
|
concealed?: string | null;
|
||||||
email?: string | null;
|
email?: Email | null;
|
||||||
phone?: string | null;
|
phone?: string | null;
|
||||||
menu?: string | null;
|
menu?: string | null;
|
||||||
gender?: string | null;
|
gender?: string | null;
|
||||||
@@ -117,6 +117,12 @@ export interface Value {
|
|||||||
creditCardNumber?: string | null;
|
creditCardNumber?: string | null;
|
||||||
reference?: string | null;
|
reference?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Email {
|
||||||
|
email_address: string;
|
||||||
|
provider: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Address {
|
export interface Address {
|
||||||
street: string;
|
street: string;
|
||||||
city: string;
|
city: string;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { BaseResponse } from "../response/baseResponse";
|
|||||||
export class SendFileApi extends BaseResponse {
|
export class SendFileApi extends BaseResponse {
|
||||||
id: string;
|
id: string;
|
||||||
fileName: string;
|
fileName: string;
|
||||||
key: string;
|
|
||||||
size: string;
|
size: string;
|
||||||
sizeName: string;
|
sizeName: string;
|
||||||
|
|
||||||
@@ -14,7 +13,6 @@ export class SendFileApi extends BaseResponse {
|
|||||||
}
|
}
|
||||||
this.id = this.getResponseProperty("Id");
|
this.id = this.getResponseProperty("Id");
|
||||||
this.fileName = this.getResponseProperty("FileName");
|
this.fileName = this.getResponseProperty("FileName");
|
||||||
this.key = this.getResponseProperty("Key");
|
|
||||||
this.size = this.getResponseProperty("Size");
|
this.size = this.getResponseProperty("Size");
|
||||||
this.sizeName = this.getResponseProperty("SizeName");
|
this.sizeName = this.getResponseProperty("SizeName");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,14 +14,12 @@ export class CipherData {
|
|||||||
id: string;
|
id: string;
|
||||||
organizationId: string;
|
organizationId: string;
|
||||||
folderId: string;
|
folderId: string;
|
||||||
userId: string;
|
|
||||||
edit: boolean;
|
edit: boolean;
|
||||||
viewPassword: boolean;
|
viewPassword: boolean;
|
||||||
organizationUseTotp: boolean;
|
organizationUseTotp: boolean;
|
||||||
favorite: boolean;
|
favorite: boolean;
|
||||||
revisionDate: string;
|
revisionDate: string;
|
||||||
type: CipherType;
|
type: CipherType;
|
||||||
sizeName: string;
|
|
||||||
name: string;
|
name: string;
|
||||||
notes: string;
|
notes: string;
|
||||||
login?: LoginData;
|
login?: LoginData;
|
||||||
@@ -35,7 +33,7 @@ export class CipherData {
|
|||||||
deletedDate: string;
|
deletedDate: string;
|
||||||
reprompt: CipherRepromptType;
|
reprompt: CipherRepromptType;
|
||||||
|
|
||||||
constructor(response?: CipherResponse, userId?: string, collectionIds?: string[]) {
|
constructor(response?: CipherResponse, collectionIds?: string[]) {
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -43,7 +41,6 @@ export class CipherData {
|
|||||||
this.id = response.id;
|
this.id = response.id;
|
||||||
this.organizationId = response.organizationId;
|
this.organizationId = response.organizationId;
|
||||||
this.folderId = response.folderId;
|
this.folderId = response.folderId;
|
||||||
this.userId = userId;
|
|
||||||
this.edit = response.edit;
|
this.edit = response.edit;
|
||||||
this.viewPassword = response.viewPassword;
|
this.viewPassword = response.viewPassword;
|
||||||
this.organizationUseTotp = response.organizationUseTotp;
|
this.organizationUseTotp = response.organizationUseTotp;
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ import { FolderResponse } from "../response/folderResponse";
|
|||||||
|
|
||||||
export class FolderData {
|
export class FolderData {
|
||||||
id: string;
|
id: string;
|
||||||
userId: string;
|
|
||||||
name: string;
|
name: string;
|
||||||
revisionDate: string;
|
revisionDate: string;
|
||||||
|
|
||||||
constructor(response: FolderResponse, userId: string) {
|
constructor(response: FolderResponse) {
|
||||||
this.userId = userId;
|
|
||||||
this.name = response.name;
|
this.name = response.name;
|
||||||
this.id = response.id;
|
this.id = response.id;
|
||||||
this.revisionDate = response.revisionDate;
|
this.revisionDate = response.revisionDate;
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { SendTextData } from "./sendTextData";
|
|||||||
export class SendData {
|
export class SendData {
|
||||||
id: string;
|
id: string;
|
||||||
accessId: string;
|
accessId: string;
|
||||||
userId: string;
|
|
||||||
type: SendType;
|
type: SendType;
|
||||||
name: string;
|
name: string;
|
||||||
notes: string;
|
notes: string;
|
||||||
@@ -23,14 +22,13 @@ export class SendData {
|
|||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
hideEmail: boolean;
|
hideEmail: boolean;
|
||||||
|
|
||||||
constructor(response?: SendResponse, userId?: string) {
|
constructor(response?: SendResponse) {
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.id = response.id;
|
this.id = response.id;
|
||||||
this.accessId = response.accessId;
|
this.accessId = response.accessId;
|
||||||
this.userId = userId;
|
|
||||||
this.type = response.type;
|
this.type = response.type;
|
||||||
this.name = response.name;
|
this.name = response.name;
|
||||||
this.notes = response.notes;
|
this.notes = response.notes;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { SendFileApi } from "../api/sendFileApi";
|
|||||||
export class SendFileData {
|
export class SendFileData {
|
||||||
id: string;
|
id: string;
|
||||||
fileName: string;
|
fileName: string;
|
||||||
key: string;
|
|
||||||
size: string;
|
size: string;
|
||||||
sizeName: string;
|
sizeName: string;
|
||||||
|
|
||||||
@@ -14,7 +13,6 @@ export class SendFileData {
|
|||||||
|
|
||||||
this.id = data.id;
|
this.id = data.id;
|
||||||
this.fileName = data.fileName;
|
this.fileName = data.fileName;
|
||||||
this.key = data.key;
|
|
||||||
this.size = data.size;
|
this.size = data.size;
|
||||||
this.sizeName = data.sizeName;
|
this.sizeName = data.sizeName;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ export class Attachment extends Domain {
|
|||||||
id: string;
|
id: string;
|
||||||
url: string;
|
url: string;
|
||||||
size: string;
|
size: string;
|
||||||
sizeName: string;
|
sizeName: string; // Readable size, ex: "4.2 KB" or "1.43 GB"
|
||||||
key: EncString;
|
key: EncString;
|
||||||
fileName: EncString;
|
fileName: EncString;
|
||||||
|
|
||||||
constructor(obj?: AttachmentData, alreadyEncrypted = false) {
|
constructor(obj?: AttachmentData) {
|
||||||
super();
|
super();
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return;
|
return;
|
||||||
@@ -32,7 +32,6 @@ export class Attachment extends Domain {
|
|||||||
fileName: null,
|
fileName: null,
|
||||||
key: null,
|
key: null,
|
||||||
},
|
},
|
||||||
alreadyEncrypted,
|
|
||||||
["id", "url", "sizeName"]
|
["id", "url", "sizeName"]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export class Card extends Domain {
|
|||||||
expYear: EncString;
|
expYear: EncString;
|
||||||
code: EncString;
|
code: EncString;
|
||||||
|
|
||||||
constructor(obj?: CardData, alreadyEncrypted = false) {
|
constructor(obj?: CardData) {
|
||||||
super();
|
super();
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return;
|
return;
|
||||||
@@ -30,7 +30,6 @@ export class Card extends Domain {
|
|||||||
expYear: null,
|
expYear: null,
|
||||||
code: null,
|
code: null,
|
||||||
},
|
},
|
||||||
alreadyEncrypted,
|
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export class Cipher extends Domain {
|
|||||||
deletedDate: Date;
|
deletedDate: Date;
|
||||||
reprompt: CipherRepromptType;
|
reprompt: CipherRepromptType;
|
||||||
|
|
||||||
constructor(obj?: CipherData, alreadyEncrypted = false, localData: any = null) {
|
constructor(obj?: CipherData, localData: any = null) {
|
||||||
super();
|
super();
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return;
|
return;
|
||||||
@@ -49,14 +49,12 @@ export class Cipher extends Domain {
|
|||||||
obj,
|
obj,
|
||||||
{
|
{
|
||||||
id: null,
|
id: null,
|
||||||
userId: null,
|
|
||||||
organizationId: null,
|
organizationId: null,
|
||||||
folderId: null,
|
folderId: null,
|
||||||
name: null,
|
name: null,
|
||||||
notes: null,
|
notes: null,
|
||||||
},
|
},
|
||||||
alreadyEncrypted,
|
["id", "organizationId", "folderId"]
|
||||||
["id", "userId", "organizationId", "folderId"]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.type = obj.type;
|
this.type = obj.type;
|
||||||
@@ -76,35 +74,35 @@ export class Cipher extends Domain {
|
|||||||
|
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case CipherType.Login:
|
case CipherType.Login:
|
||||||
this.login = new Login(obj.login, alreadyEncrypted);
|
this.login = new Login(obj.login);
|
||||||
break;
|
break;
|
||||||
case CipherType.SecureNote:
|
case CipherType.SecureNote:
|
||||||
this.secureNote = new SecureNote(obj.secureNote);
|
this.secureNote = new SecureNote(obj.secureNote);
|
||||||
break;
|
break;
|
||||||
case CipherType.Card:
|
case CipherType.Card:
|
||||||
this.card = new Card(obj.card, alreadyEncrypted);
|
this.card = new Card(obj.card);
|
||||||
break;
|
break;
|
||||||
case CipherType.Identity:
|
case CipherType.Identity:
|
||||||
this.identity = new Identity(obj.identity, alreadyEncrypted);
|
this.identity = new Identity(obj.identity);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj.attachments != null) {
|
if (obj.attachments != null) {
|
||||||
this.attachments = obj.attachments.map((a) => new Attachment(a, alreadyEncrypted));
|
this.attachments = obj.attachments.map((a) => new Attachment(a));
|
||||||
} else {
|
} else {
|
||||||
this.attachments = null;
|
this.attachments = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj.fields != null) {
|
if (obj.fields != null) {
|
||||||
this.fields = obj.fields.map((f) => new Field(f, alreadyEncrypted));
|
this.fields = obj.fields.map((f) => new Field(f));
|
||||||
} else {
|
} else {
|
||||||
this.fields = null;
|
this.fields = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj.passwordHistory != null) {
|
if (obj.passwordHistory != null) {
|
||||||
this.passwordHistory = obj.passwordHistory.map((ph) => new Password(ph, alreadyEncrypted));
|
this.passwordHistory = obj.passwordHistory.map((ph) => new Password(ph));
|
||||||
} else {
|
} else {
|
||||||
this.passwordHistory = null;
|
this.passwordHistory = null;
|
||||||
}
|
}
|
||||||
@@ -187,12 +185,11 @@ export class Cipher extends Domain {
|
|||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
toCipherData(userId: string): CipherData {
|
toCipherData(): CipherData {
|
||||||
const c = new CipherData();
|
const c = new CipherData();
|
||||||
c.id = this.id;
|
c.id = this.id;
|
||||||
c.organizationId = this.organizationId;
|
c.organizationId = this.organizationId;
|
||||||
c.folderId = this.folderId;
|
c.folderId = this.folderId;
|
||||||
c.userId = this.organizationId != null ? userId : null;
|
|
||||||
c.edit = this.edit;
|
c.edit = this.edit;
|
||||||
c.viewPassword = this.viewPassword;
|
c.viewPassword = this.viewPassword;
|
||||||
c.organizationUseTotp = this.organizationUseTotp;
|
c.organizationUseTotp = this.organizationUseTotp;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export class Collection extends Domain {
|
|||||||
readOnly: boolean;
|
readOnly: boolean;
|
||||||
hidePasswords: boolean;
|
hidePasswords: boolean;
|
||||||
|
|
||||||
constructor(obj?: CollectionData, alreadyEncrypted = false) {
|
constructor(obj?: CollectionData) {
|
||||||
super();
|
super();
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return;
|
return;
|
||||||
@@ -29,7 +29,6 @@ export class Collection extends Domain {
|
|||||||
readOnly: null,
|
readOnly: null,
|
||||||
hidePasswords: null,
|
hidePasswords: null,
|
||||||
},
|
},
|
||||||
alreadyEncrypted,
|
|
||||||
["id", "organizationId", "externalId", "readOnly", "hidePasswords"]
|
["id", "organizationId", "externalId", "readOnly", "hidePasswords"]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ export default class Domain {
|
|||||||
domain: D,
|
domain: D,
|
||||||
dataObj: any,
|
dataObj: any,
|
||||||
map: any,
|
map: any,
|
||||||
alreadyEncrypted: boolean,
|
|
||||||
notEncList: any[] = []
|
notEncList: any[] = []
|
||||||
) {
|
) {
|
||||||
for (const prop in map) {
|
for (const prop in map) {
|
||||||
@@ -18,7 +17,7 @@ export default class Domain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const objProp = dataObj[map[prop] || prop];
|
const objProp = dataObj[map[prop] || prop];
|
||||||
if (alreadyEncrypted === true || notEncList.indexOf(prop) > -1) {
|
if (notEncList.indexOf(prop) > -1) {
|
||||||
(domain as any)[prop] = objProp ? objProp : null;
|
(domain as any)[prop] = objProp ? objProp : null;
|
||||||
} else {
|
} else {
|
||||||
(domain as any)[prop] = objProp ? new EncString(objProp) : null;
|
(domain as any)[prop] = objProp ? new EncString(objProp) : null;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export class Field extends Domain {
|
|||||||
type: FieldType;
|
type: FieldType;
|
||||||
linkedId: LinkedIdType;
|
linkedId: LinkedIdType;
|
||||||
|
|
||||||
constructor(obj?: FieldData, alreadyEncrypted = false) {
|
constructor(obj?: FieldData) {
|
||||||
super();
|
super();
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return;
|
return;
|
||||||
@@ -28,7 +28,6 @@ export class Field extends Domain {
|
|||||||
name: null,
|
name: null,
|
||||||
value: null,
|
value: null,
|
||||||
},
|
},
|
||||||
alreadyEncrypted,
|
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export class Folder extends Domain {
|
|||||||
name: EncString;
|
name: EncString;
|
||||||
revisionDate: Date;
|
revisionDate: Date;
|
||||||
|
|
||||||
constructor(obj?: FolderData, alreadyEncrypted = false) {
|
constructor(obj?: FolderData) {
|
||||||
super();
|
super();
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return;
|
return;
|
||||||
@@ -22,7 +22,6 @@ export class Folder extends Domain {
|
|||||||
id: null,
|
id: null,
|
||||||
name: null,
|
name: null,
|
||||||
},
|
},
|
||||||
alreadyEncrypted,
|
|
||||||
["id"]
|
["id"]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { WindowState } from "./windowState";
|
|||||||
export class GlobalState {
|
export class GlobalState {
|
||||||
enableAlwaysOnTop?: boolean;
|
enableAlwaysOnTop?: boolean;
|
||||||
installedVersion?: string;
|
installedVersion?: string;
|
||||||
locale?: string = "en";
|
locale?: string;
|
||||||
organizationInvitation?: any;
|
organizationInvitation?: any;
|
||||||
ssoCodeVerifier?: string;
|
ssoCodeVerifier?: string;
|
||||||
ssoOrganizationIdentifier?: string;
|
ssoOrganizationIdentifier?: string;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export class Identity extends Domain {
|
|||||||
passportNumber: EncString;
|
passportNumber: EncString;
|
||||||
licenseNumber: EncString;
|
licenseNumber: EncString;
|
||||||
|
|
||||||
constructor(obj?: IdentityData, alreadyEncrypted = false) {
|
constructor(obj?: IdentityData) {
|
||||||
super();
|
super();
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return;
|
return;
|
||||||
@@ -54,7 +54,6 @@ export class Identity extends Domain {
|
|||||||
passportNumber: null,
|
passportNumber: null,
|
||||||
licenseNumber: null,
|
licenseNumber: null,
|
||||||
},
|
},
|
||||||
alreadyEncrypted,
|
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export class Login extends Domain {
|
|||||||
totp: EncString;
|
totp: EncString;
|
||||||
autofillOnPageLoad: boolean;
|
autofillOnPageLoad: boolean;
|
||||||
|
|
||||||
constructor(obj?: LoginData, alreadyEncrypted = false) {
|
constructor(obj?: LoginData) {
|
||||||
super();
|
super();
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return;
|
return;
|
||||||
@@ -31,14 +31,13 @@ export class Login extends Domain {
|
|||||||
password: null,
|
password: null,
|
||||||
totp: null,
|
totp: null,
|
||||||
},
|
},
|
||||||
alreadyEncrypted,
|
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (obj.uris) {
|
if (obj.uris) {
|
||||||
this.uris = [];
|
this.uris = [];
|
||||||
obj.uris.forEach((u) => {
|
obj.uris.forEach((u) => {
|
||||||
this.uris.push(new LoginUri(u, alreadyEncrypted));
|
this.uris.push(new LoginUri(u));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export class LoginUri extends Domain {
|
|||||||
uri: EncString;
|
uri: EncString;
|
||||||
match: UriMatchType;
|
match: UriMatchType;
|
||||||
|
|
||||||
constructor(obj?: LoginUriData, alreadyEncrypted = false) {
|
constructor(obj?: LoginUriData) {
|
||||||
super();
|
super();
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return;
|
return;
|
||||||
@@ -23,7 +23,6 @@ export class LoginUri extends Domain {
|
|||||||
{
|
{
|
||||||
uri: null,
|
uri: null,
|
||||||
},
|
},
|
||||||
alreadyEncrypted,
|
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -46,6 +45,7 @@ export class LoginUri extends Domain {
|
|||||||
u,
|
u,
|
||||||
{
|
{
|
||||||
uri: null,
|
uri: null,
|
||||||
|
match: null,
|
||||||
},
|
},
|
||||||
["match"]
|
["match"]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,20 +9,15 @@ export class Password extends Domain {
|
|||||||
password: EncString;
|
password: EncString;
|
||||||
lastUsedDate: Date;
|
lastUsedDate: Date;
|
||||||
|
|
||||||
constructor(obj?: PasswordHistoryData, alreadyEncrypted = false) {
|
constructor(obj?: PasswordHistoryData) {
|
||||||
super();
|
super();
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.buildDomainModel(
|
this.buildDomainModel(this, obj, {
|
||||||
this,
|
password: null,
|
||||||
obj,
|
});
|
||||||
{
|
|
||||||
password: null,
|
|
||||||
},
|
|
||||||
alreadyEncrypted
|
|
||||||
);
|
|
||||||
this.lastUsedDate = new Date(obj.lastUsedDate);
|
this.lastUsedDate = new Date(obj.lastUsedDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import { SendText } from "./sendText";
|
|||||||
export class Send extends Domain {
|
export class Send extends Domain {
|
||||||
id: string;
|
id: string;
|
||||||
accessId: string;
|
accessId: string;
|
||||||
userId: string;
|
|
||||||
type: SendType;
|
type: SendType;
|
||||||
name: EncString;
|
name: EncString;
|
||||||
notes: EncString;
|
notes: EncString;
|
||||||
@@ -28,7 +27,7 @@ export class Send extends Domain {
|
|||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
hideEmail: boolean;
|
hideEmail: boolean;
|
||||||
|
|
||||||
constructor(obj?: SendData, alreadyEncrypted = false) {
|
constructor(obj?: SendData) {
|
||||||
super();
|
super();
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return;
|
return;
|
||||||
@@ -40,13 +39,11 @@ export class Send extends Domain {
|
|||||||
{
|
{
|
||||||
id: null,
|
id: null,
|
||||||
accessId: null,
|
accessId: null,
|
||||||
userId: null,
|
|
||||||
name: null,
|
name: null,
|
||||||
notes: null,
|
notes: null,
|
||||||
key: null,
|
key: null,
|
||||||
},
|
},
|
||||||
alreadyEncrypted,
|
["id", "accessId"]
|
||||||
["id", "accessId", "userId"]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.type = obj.type;
|
this.type = obj.type;
|
||||||
@@ -61,10 +58,10 @@ export class Send extends Domain {
|
|||||||
|
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case SendType.Text:
|
case SendType.Text:
|
||||||
this.text = new SendText(obj.text, alreadyEncrypted);
|
this.text = new SendText(obj.text);
|
||||||
break;
|
break;
|
||||||
case SendType.File:
|
case SendType.File:
|
||||||
this.file = new SendFile(obj.file, alreadyEncrypted);
|
this.file = new SendFile(obj.file);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export class SendAccess extends Domain {
|
|||||||
expirationDate: Date;
|
expirationDate: Date;
|
||||||
creatorIdentifier: string;
|
creatorIdentifier: string;
|
||||||
|
|
||||||
constructor(obj?: SendAccessResponse, alreadyEncrypted = false) {
|
constructor(obj?: SendAccessResponse) {
|
||||||
super();
|
super();
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return;
|
return;
|
||||||
@@ -32,7 +32,6 @@ export class SendAccess extends Domain {
|
|||||||
expirationDate: null,
|
expirationDate: null,
|
||||||
creatorIdentifier: null,
|
creatorIdentifier: null,
|
||||||
},
|
},
|
||||||
alreadyEncrypted,
|
|
||||||
["id", "expirationDate", "creatorIdentifier"]
|
["id", "expirationDate", "creatorIdentifier"]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -40,10 +39,10 @@ export class SendAccess extends Domain {
|
|||||||
|
|
||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case SendType.Text:
|
case SendType.Text:
|
||||||
this.text = new SendText(obj.text, alreadyEncrypted);
|
this.text = new SendText(obj.text);
|
||||||
break;
|
break;
|
||||||
case SendType.File:
|
case SendType.File:
|
||||||
this.file = new SendFile(obj.file, alreadyEncrypted);
|
this.file = new SendFile(obj.file);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export class SendFile extends Domain {
|
|||||||
sizeName: string;
|
sizeName: string;
|
||||||
fileName: EncString;
|
fileName: EncString;
|
||||||
|
|
||||||
constructor(obj?: SendFileData, alreadyEncrypted = false) {
|
constructor(obj?: SendFileData) {
|
||||||
super();
|
super();
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return;
|
return;
|
||||||
@@ -26,7 +26,6 @@ export class SendFile extends Domain {
|
|||||||
sizeName: null,
|
sizeName: null,
|
||||||
fileName: null,
|
fileName: null,
|
||||||
},
|
},
|
||||||
alreadyEncrypted,
|
|
||||||
["id", "sizeName"]
|
["id", "sizeName"]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export class SendText extends Domain {
|
|||||||
text: EncString;
|
text: EncString;
|
||||||
hidden: boolean;
|
hidden: boolean;
|
||||||
|
|
||||||
constructor(obj?: SendTextData, alreadyEncrypted = false) {
|
constructor(obj?: SendTextData) {
|
||||||
super();
|
super();
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return;
|
return;
|
||||||
@@ -22,7 +22,6 @@ export class SendText extends Domain {
|
|||||||
{
|
{
|
||||||
text: null,
|
text: null,
|
||||||
},
|
},
|
||||||
alreadyEncrypted,
|
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import { Card as CardDomain } from "../domain/card";
|
|||||||
import { EncString } from "../domain/encString";
|
import { EncString } from "../domain/encString";
|
||||||
import { CardView } from "../view/cardView";
|
import { CardView } from "../view/cardView";
|
||||||
|
|
||||||
export class Card {
|
export class CardExport {
|
||||||
static template(): Card {
|
static template(): CardExport {
|
||||||
const req = new Card();
|
const req = new CardExport();
|
||||||
req.cardholderName = "John Doe";
|
req.cardholderName = "John Doe";
|
||||||
req.brand = "visa";
|
req.brand = "visa";
|
||||||
req.number = "4242424242424242";
|
req.number = "4242424242424242";
|
||||||
@@ -14,7 +14,7 @@ export class Card {
|
|||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toView(req: Card, view = new CardView()) {
|
static toView(req: CardExport, view = new CardView()) {
|
||||||
view.cardholderName = req.cardholderName;
|
view.cardholderName = req.cardholderName;
|
||||||
view.brand = req.brand;
|
view.brand = req.brand;
|
||||||
view.number = req.number;
|
view.number = req.number;
|
||||||
@@ -24,7 +24,7 @@ export class Card {
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toDomain(req: Card, domain = new CardDomain()) {
|
static toDomain(req: CardExport, domain = new CardDomain()) {
|
||||||
domain.cardholderName = req.cardholderName != null ? new EncString(req.cardholderName) : null;
|
domain.cardholderName = req.cardholderName != null ? new EncString(req.cardholderName) : null;
|
||||||
domain.brand = req.brand != null ? new EncString(req.brand) : null;
|
domain.brand = req.brand != null ? new EncString(req.brand) : null;
|
||||||
domain.number = req.number != null ? new EncString(req.number) : null;
|
domain.number = req.number != null ? new EncString(req.number) : null;
|
||||||
@@ -4,15 +4,15 @@ import { Cipher as CipherDomain } from "../domain/cipher";
|
|||||||
import { EncString } from "../domain/encString";
|
import { EncString } from "../domain/encString";
|
||||||
import { CipherView } from "../view/cipherView";
|
import { CipherView } from "../view/cipherView";
|
||||||
|
|
||||||
import { Card } from "./card";
|
import { CardExport } from "./cardExport";
|
||||||
import { Field } from "./field";
|
import { FieldExport } from "./fieldExport";
|
||||||
import { Identity } from "./identity";
|
import { IdentityExport } from "./identityExport";
|
||||||
import { Login } from "./login";
|
import { LoginExport } from "./loginExport";
|
||||||
import { SecureNote } from "./secureNote";
|
import { SecureNoteExport } from "./secureNoteExport";
|
||||||
|
|
||||||
export class Cipher {
|
export class CipherExport {
|
||||||
static template(): Cipher {
|
static template(): CipherExport {
|
||||||
const req = new Cipher();
|
const req = new CipherExport();
|
||||||
req.organizationId = null;
|
req.organizationId = null;
|
||||||
req.collectionIds = null;
|
req.collectionIds = null;
|
||||||
req.folderId = null;
|
req.folderId = null;
|
||||||
@@ -29,7 +29,7 @@ export class Cipher {
|
|||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toView(req: Cipher, view = new CipherView()) {
|
static toView(req: CipherExport, view = new CipherView()) {
|
||||||
view.type = req.type;
|
view.type = req.type;
|
||||||
view.folderId = req.folderId;
|
view.folderId = req.folderId;
|
||||||
if (view.organizationId == null) {
|
if (view.organizationId == null) {
|
||||||
@@ -45,28 +45,28 @@ export class Cipher {
|
|||||||
view.reprompt = req.reprompt ?? CipherRepromptType.None;
|
view.reprompt = req.reprompt ?? CipherRepromptType.None;
|
||||||
|
|
||||||
if (req.fields != null) {
|
if (req.fields != null) {
|
||||||
view.fields = req.fields.map((f) => Field.toView(f));
|
view.fields = req.fields.map((f) => FieldExport.toView(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (req.type) {
|
switch (req.type) {
|
||||||
case CipherType.Login:
|
case CipherType.Login:
|
||||||
view.login = Login.toView(req.login);
|
view.login = LoginExport.toView(req.login);
|
||||||
break;
|
break;
|
||||||
case CipherType.SecureNote:
|
case CipherType.SecureNote:
|
||||||
view.secureNote = SecureNote.toView(req.secureNote);
|
view.secureNote = SecureNoteExport.toView(req.secureNote);
|
||||||
break;
|
break;
|
||||||
case CipherType.Card:
|
case CipherType.Card:
|
||||||
view.card = Card.toView(req.card);
|
view.card = CardExport.toView(req.card);
|
||||||
break;
|
break;
|
||||||
case CipherType.Identity:
|
case CipherType.Identity:
|
||||||
view.identity = Identity.toView(req.identity);
|
view.identity = IdentityExport.toView(req.identity);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toDomain(req: Cipher, domain = new CipherDomain()) {
|
static toDomain(req: CipherExport, domain = new CipherDomain()) {
|
||||||
domain.type = req.type;
|
domain.type = req.type;
|
||||||
domain.folderId = req.folderId;
|
domain.folderId = req.folderId;
|
||||||
if (domain.organizationId == null) {
|
if (domain.organizationId == null) {
|
||||||
@@ -78,21 +78,21 @@ export class Cipher {
|
|||||||
domain.reprompt = req.reprompt ?? CipherRepromptType.None;
|
domain.reprompt = req.reprompt ?? CipherRepromptType.None;
|
||||||
|
|
||||||
if (req.fields != null) {
|
if (req.fields != null) {
|
||||||
domain.fields = req.fields.map((f) => Field.toDomain(f));
|
domain.fields = req.fields.map((f) => FieldExport.toDomain(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (req.type) {
|
switch (req.type) {
|
||||||
case CipherType.Login:
|
case CipherType.Login:
|
||||||
domain.login = Login.toDomain(req.login);
|
domain.login = LoginExport.toDomain(req.login);
|
||||||
break;
|
break;
|
||||||
case CipherType.SecureNote:
|
case CipherType.SecureNote:
|
||||||
domain.secureNote = SecureNote.toDomain(req.secureNote);
|
domain.secureNote = SecureNoteExport.toDomain(req.secureNote);
|
||||||
break;
|
break;
|
||||||
case CipherType.Card:
|
case CipherType.Card:
|
||||||
domain.card = Card.toDomain(req.card);
|
domain.card = CardExport.toDomain(req.card);
|
||||||
break;
|
break;
|
||||||
case CipherType.Identity:
|
case CipherType.Identity:
|
||||||
domain.identity = Identity.toDomain(req.identity);
|
domain.identity = IdentityExport.toDomain(req.identity);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,11 +106,11 @@ export class Cipher {
|
|||||||
name: string;
|
name: string;
|
||||||
notes: string;
|
notes: string;
|
||||||
favorite: boolean;
|
favorite: boolean;
|
||||||
fields: Field[];
|
fields: FieldExport[];
|
||||||
login: Login;
|
login: LoginExport;
|
||||||
secureNote: SecureNote;
|
secureNote: SecureNoteExport;
|
||||||
card: Card;
|
card: CardExport;
|
||||||
identity: Identity;
|
identity: IdentityExport;
|
||||||
reprompt: CipherRepromptType;
|
reprompt: CipherRepromptType;
|
||||||
|
|
||||||
// Use build method instead of ctor so that we can control order of JSON stringify for pretty print
|
// Use build method instead of ctor so that we can control order of JSON stringify for pretty print
|
||||||
@@ -132,24 +132,24 @@ export class Cipher {
|
|||||||
|
|
||||||
if (o.fields != null) {
|
if (o.fields != null) {
|
||||||
if (o instanceof CipherView) {
|
if (o instanceof CipherView) {
|
||||||
this.fields = o.fields.map((f) => new Field(f));
|
this.fields = o.fields.map((f) => new FieldExport(f));
|
||||||
} else {
|
} else {
|
||||||
this.fields = o.fields.map((f) => new Field(f));
|
this.fields = o.fields.map((f) => new FieldExport(f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (o.type) {
|
switch (o.type) {
|
||||||
case CipherType.Login:
|
case CipherType.Login:
|
||||||
this.login = new Login(o.login);
|
this.login = new LoginExport(o.login);
|
||||||
break;
|
break;
|
||||||
case CipherType.SecureNote:
|
case CipherType.SecureNote:
|
||||||
this.secureNote = new SecureNote(o.secureNote);
|
this.secureNote = new SecureNoteExport(o.secureNote);
|
||||||
break;
|
break;
|
||||||
case CipherType.Card:
|
case CipherType.Card:
|
||||||
this.card = new Card(o.card);
|
this.card = new CardExport(o.card);
|
||||||
break;
|
break;
|
||||||
case CipherType.Identity:
|
case CipherType.Identity:
|
||||||
this.identity = new Identity(o.identity);
|
this.identity = new IdentityExport(o.identity);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Cipher as CipherDomain } from "../domain/cipher";
|
import { Cipher as CipherDomain } from "../domain/cipher";
|
||||||
import { CipherView } from "../view/cipherView";
|
import { CipherView } from "../view/cipherView";
|
||||||
|
|
||||||
import { Cipher } from "./cipher";
|
import { CipherExport } from "./cipherExport";
|
||||||
|
|
||||||
export class CipherWithIds extends Cipher {
|
export class CipherWithIdExport extends CipherExport {
|
||||||
id: string;
|
id: string;
|
||||||
collectionIds: string[];
|
collectionIds: string[];
|
||||||
|
|
||||||
@@ -2,16 +2,16 @@ import { Collection as CollectionDomain } from "../domain/collection";
|
|||||||
import { EncString } from "../domain/encString";
|
import { EncString } from "../domain/encString";
|
||||||
import { CollectionView } from "../view/collectionView";
|
import { CollectionView } from "../view/collectionView";
|
||||||
|
|
||||||
export class Collection {
|
export class CollectionExport {
|
||||||
static template(): Collection {
|
static template(): CollectionExport {
|
||||||
const req = new Collection();
|
const req = new CollectionExport();
|
||||||
req.organizationId = "00000000-0000-0000-0000-000000000000";
|
req.organizationId = "00000000-0000-0000-0000-000000000000";
|
||||||
req.name = "Collection name";
|
req.name = "Collection name";
|
||||||
req.externalId = null;
|
req.externalId = null;
|
||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toView(req: Collection, view = new CollectionView()) {
|
static toView(req: CollectionExport, view = new CollectionView()) {
|
||||||
view.name = req.name;
|
view.name = req.name;
|
||||||
view.externalId = req.externalId;
|
view.externalId = req.externalId;
|
||||||
if (view.organizationId == null) {
|
if (view.organizationId == null) {
|
||||||
@@ -20,7 +20,7 @@ export class Collection {
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toDomain(req: Collection, domain = new CollectionDomain()) {
|
static toDomain(req: CollectionExport, domain = new CollectionDomain()) {
|
||||||
domain.name = req.name != null ? new EncString(req.name) : null;
|
domain.name = req.name != null ? new EncString(req.name) : null;
|
||||||
domain.externalId = req.externalId;
|
domain.externalId = req.externalId;
|
||||||
if (domain.organizationId == null) {
|
if (domain.organizationId == null) {
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Collection as CollectionDomain } from "../domain/collection";
|
import { Collection as CollectionDomain } from "../domain/collection";
|
||||||
import { CollectionView } from "../view/collectionView";
|
import { CollectionView } from "../view/collectionView";
|
||||||
|
|
||||||
import { Collection } from "./collection";
|
import { CollectionExport } from "./collectionExport";
|
||||||
|
|
||||||
export class CollectionWithId extends Collection {
|
export class CollectionWithIdExport extends CollectionExport {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
// Use build method instead of ctor so that we can control order of JSON stringify for pretty print
|
// Use build method instead of ctor so that we can control order of JSON stringify for pretty print
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { EventType } from "../../enums/eventType";
|
import { EventType } from "../../enums/eventType";
|
||||||
import { EventView } from "../view/eventView";
|
import { EventView } from "../view/eventView";
|
||||||
|
|
||||||
export class Event {
|
export class EventExport {
|
||||||
message: string;
|
message: string;
|
||||||
appIcon: string;
|
appIcon: string;
|
||||||
appName: string;
|
appName: string;
|
||||||
@@ -4,16 +4,16 @@ import { EncString } from "../domain/encString";
|
|||||||
import { Field as FieldDomain } from "../domain/field";
|
import { Field as FieldDomain } from "../domain/field";
|
||||||
import { FieldView } from "../view/fieldView";
|
import { FieldView } from "../view/fieldView";
|
||||||
|
|
||||||
export class Field {
|
export class FieldExport {
|
||||||
static template(): Field {
|
static template(): FieldExport {
|
||||||
const req = new Field();
|
const req = new FieldExport();
|
||||||
req.name = "Field name";
|
req.name = "Field name";
|
||||||
req.value = "Some value";
|
req.value = "Some value";
|
||||||
req.type = FieldType.Text;
|
req.type = FieldType.Text;
|
||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toView(req: Field, view = new FieldView()) {
|
static toView(req: FieldExport, view = new FieldView()) {
|
||||||
view.type = req.type;
|
view.type = req.type;
|
||||||
view.value = req.value;
|
view.value = req.value;
|
||||||
view.name = req.name;
|
view.name = req.name;
|
||||||
@@ -21,7 +21,7 @@ export class Field {
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toDomain(req: Field, domain = new FieldDomain()) {
|
static toDomain(req: FieldExport, domain = new FieldDomain()) {
|
||||||
domain.type = req.type;
|
domain.type = req.type;
|
||||||
domain.value = req.value != null ? new EncString(req.value) : null;
|
domain.value = req.value != null ? new EncString(req.value) : null;
|
||||||
domain.name = req.name != null ? new EncString(req.name) : null;
|
domain.name = req.name != null ? new EncString(req.name) : null;
|
||||||
@@ -2,19 +2,19 @@ import { EncString } from "../domain/encString";
|
|||||||
import { Folder as FolderDomain } from "../domain/folder";
|
import { Folder as FolderDomain } from "../domain/folder";
|
||||||
import { FolderView } from "../view/folderView";
|
import { FolderView } from "../view/folderView";
|
||||||
|
|
||||||
export class Folder {
|
export class FolderExport {
|
||||||
static template(): Folder {
|
static template(): FolderExport {
|
||||||
const req = new Folder();
|
const req = new FolderExport();
|
||||||
req.name = "Folder name";
|
req.name = "Folder name";
|
||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toView(req: Folder, view = new FolderView()) {
|
static toView(req: FolderExport, view = new FolderView()) {
|
||||||
view.name = req.name;
|
view.name = req.name;
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toDomain(req: Folder, domain = new FolderDomain()) {
|
static toDomain(req: FolderExport, domain = new FolderDomain()) {
|
||||||
domain.name = req.name != null ? new EncString(req.name) : null;
|
domain.name = req.name != null ? new EncString(req.name) : null;
|
||||||
return domain;
|
return domain;
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Folder as FolderDomain } from "../domain/folder";
|
import { Folder as FolderDomain } from "../domain/folder";
|
||||||
import { FolderView } from "../view/folderView";
|
import { FolderView } from "../view/folderView";
|
||||||
|
|
||||||
import { Folder } from "./folder";
|
import { FolderExport } from "./folderExport";
|
||||||
|
|
||||||
export class FolderWithId extends Folder {
|
export class FolderWithIdExport extends FolderExport {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
// Use build method instead of ctor so that we can control order of JSON stringify for pretty print
|
// Use build method instead of ctor so that we can control order of JSON stringify for pretty print
|
||||||
@@ -2,9 +2,9 @@ import { EncString } from "../domain/encString";
|
|||||||
import { Identity as IdentityDomain } from "../domain/identity";
|
import { Identity as IdentityDomain } from "../domain/identity";
|
||||||
import { IdentityView } from "../view/identityView";
|
import { IdentityView } from "../view/identityView";
|
||||||
|
|
||||||
export class Identity {
|
export class IdentityExport {
|
||||||
static template(): Identity {
|
static template(): IdentityExport {
|
||||||
const req = new Identity();
|
const req = new IdentityExport();
|
||||||
req.title = "Mr";
|
req.title = "Mr";
|
||||||
req.firstName = "John";
|
req.firstName = "John";
|
||||||
req.middleName = "William";
|
req.middleName = "William";
|
||||||
@@ -26,7 +26,7 @@ export class Identity {
|
|||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toView(req: Identity, view = new IdentityView()) {
|
static toView(req: IdentityExport, view = new IdentityView()) {
|
||||||
view.title = req.title;
|
view.title = req.title;
|
||||||
view.firstName = req.firstName;
|
view.firstName = req.firstName;
|
||||||
view.middleName = req.middleName;
|
view.middleName = req.middleName;
|
||||||
@@ -48,7 +48,7 @@ export class Identity {
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toDomain(req: Identity, domain = new IdentityDomain()) {
|
static toDomain(req: IdentityExport, domain = new IdentityDomain()) {
|
||||||
domain.title = req.title != null ? new EncString(req.title) : null;
|
domain.title = req.title != null ? new EncString(req.title) : null;
|
||||||
domain.firstName = req.firstName != null ? new EncString(req.firstName) : null;
|
domain.firstName = req.firstName != null ? new EncString(req.firstName) : null;
|
||||||
domain.middleName = req.middleName != null ? new EncString(req.middleName) : null;
|
domain.middleName = req.middleName != null ? new EncString(req.middleName) : null;
|
||||||
@@ -2,11 +2,11 @@ import { EncString } from "../domain/encString";
|
|||||||
import { Login as LoginDomain } from "../domain/login";
|
import { Login as LoginDomain } from "../domain/login";
|
||||||
import { LoginView } from "../view/loginView";
|
import { LoginView } from "../view/loginView";
|
||||||
|
|
||||||
import { LoginUri } from "./loginUri";
|
import { LoginUriExport } from "./loginUriExport";
|
||||||
|
|
||||||
export class Login {
|
export class LoginExport {
|
||||||
static template(): Login {
|
static template(): LoginExport {
|
||||||
const req = new Login();
|
const req = new LoginExport();
|
||||||
req.uris = [];
|
req.uris = [];
|
||||||
req.username = "jdoe";
|
req.username = "jdoe";
|
||||||
req.password = "myp@ssword123";
|
req.password = "myp@ssword123";
|
||||||
@@ -14,9 +14,9 @@ export class Login {
|
|||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toView(req: Login, view = new LoginView()) {
|
static toView(req: LoginExport, view = new LoginView()) {
|
||||||
if (req.uris != null) {
|
if (req.uris != null) {
|
||||||
view.uris = req.uris.map((u) => LoginUri.toView(u));
|
view.uris = req.uris.map((u) => LoginUriExport.toView(u));
|
||||||
}
|
}
|
||||||
view.username = req.username;
|
view.username = req.username;
|
||||||
view.password = req.password;
|
view.password = req.password;
|
||||||
@@ -24,9 +24,9 @@ export class Login {
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toDomain(req: Login, domain = new LoginDomain()) {
|
static toDomain(req: LoginExport, domain = new LoginDomain()) {
|
||||||
if (req.uris != null) {
|
if (req.uris != null) {
|
||||||
domain.uris = req.uris.map((u) => LoginUri.toDomain(u));
|
domain.uris = req.uris.map((u) => LoginUriExport.toDomain(u));
|
||||||
}
|
}
|
||||||
domain.username = req.username != null ? new EncString(req.username) : null;
|
domain.username = req.username != null ? new EncString(req.username) : null;
|
||||||
domain.password = req.password != null ? new EncString(req.password) : null;
|
domain.password = req.password != null ? new EncString(req.password) : null;
|
||||||
@@ -34,7 +34,7 @@ export class Login {
|
|||||||
return domain;
|
return domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
uris: LoginUri[];
|
uris: LoginUriExport[];
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
totp: string;
|
totp: string;
|
||||||
@@ -46,9 +46,9 @@ export class Login {
|
|||||||
|
|
||||||
if (o.uris != null) {
|
if (o.uris != null) {
|
||||||
if (o instanceof LoginView) {
|
if (o instanceof LoginView) {
|
||||||
this.uris = o.uris.map((u) => new LoginUri(u));
|
this.uris = o.uris.map((u) => new LoginUriExport(u));
|
||||||
} else {
|
} else {
|
||||||
this.uris = o.uris.map((u) => new LoginUri(u));
|
this.uris = o.uris.map((u) => new LoginUriExport(u));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3,21 +3,21 @@ import { EncString } from "../domain/encString";
|
|||||||
import { LoginUri as LoginUriDomain } from "../domain/loginUri";
|
import { LoginUri as LoginUriDomain } from "../domain/loginUri";
|
||||||
import { LoginUriView } from "../view/loginUriView";
|
import { LoginUriView } from "../view/loginUriView";
|
||||||
|
|
||||||
export class LoginUri {
|
export class LoginUriExport {
|
||||||
static template(): LoginUri {
|
static template(): LoginUriExport {
|
||||||
const req = new LoginUri();
|
const req = new LoginUriExport();
|
||||||
req.uri = "https://google.com";
|
req.uri = "https://google.com";
|
||||||
req.match = null;
|
req.match = null;
|
||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toView(req: LoginUri, view = new LoginUriView()) {
|
static toView(req: LoginUriExport, view = new LoginUriView()) {
|
||||||
view.uri = req.uri;
|
view.uri = req.uri;
|
||||||
view.match = req.match;
|
view.match = req.match;
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toDomain(req: LoginUri, domain = new LoginUriDomain()) {
|
static toDomain(req: LoginUriExport, domain = new LoginUriDomain()) {
|
||||||
domain.uri = req.uri != null ? new EncString(req.uri) : null;
|
domain.uri = req.uri != null ? new EncString(req.uri) : null;
|
||||||
domain.match = req.match;
|
domain.match = req.match;
|
||||||
return domain;
|
return domain;
|
||||||
@@ -2,19 +2,19 @@ import { SecureNoteType } from "../../enums/secureNoteType";
|
|||||||
import { SecureNote as SecureNoteDomain } from "../domain/secureNote";
|
import { SecureNote as SecureNoteDomain } from "../domain/secureNote";
|
||||||
import { SecureNoteView } from "../view/secureNoteView";
|
import { SecureNoteView } from "../view/secureNoteView";
|
||||||
|
|
||||||
export class SecureNote {
|
export class SecureNoteExport {
|
||||||
static template(): SecureNote {
|
static template(): SecureNoteExport {
|
||||||
const req = new SecureNote();
|
const req = new SecureNoteExport();
|
||||||
req.type = SecureNoteType.Generic;
|
req.type = SecureNoteType.Generic;
|
||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toView(req: SecureNote, view = new SecureNoteView()) {
|
static toView(req: SecureNoteExport, view = new SecureNoteView()) {
|
||||||
view.type = req.type;
|
view.type = req.type;
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toDomain(req: SecureNote, view = new SecureNoteDomain()) {
|
static toDomain(req: SecureNoteExport, view = new SecureNoteDomain()) {
|
||||||
view.type = req.type;
|
view.type = req.type;
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
@@ -2,4 +2,5 @@ import { SecretVerificationRequest } from "./secretVerificationRequest";
|
|||||||
|
|
||||||
export class TwoFactorEmailRequest extends SecretVerificationRequest {
|
export class TwoFactorEmailRequest extends SecretVerificationRequest {
|
||||||
email: string;
|
email: string;
|
||||||
|
deviceIdentifier: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -306,7 +306,7 @@ export class CipherService implements CipherServiceAbstraction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const localData = await this.stateService.getLocalData();
|
const localData = await this.stateService.getLocalData();
|
||||||
return new Cipher(ciphers[id], false, localData ? localData[id] : null);
|
return new Cipher(ciphers[id], localData ? localData[id] : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAll(): Promise<Cipher[]> {
|
async getAll(): Promise<Cipher[]> {
|
||||||
@@ -316,7 +316,7 @@ export class CipherService implements CipherServiceAbstraction {
|
|||||||
for (const id in ciphers) {
|
for (const id in ciphers) {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
if (ciphers.hasOwnProperty(id)) {
|
if (ciphers.hasOwnProperty(id)) {
|
||||||
response.push(new Cipher(ciphers[id], false, localData ? localData[id] : null));
|
response.push(new Cipher(ciphers[id], localData ? localData[id] : null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
@@ -605,11 +605,7 @@ export class CipherService implements CipherServiceAbstraction {
|
|||||||
response = await this.apiService.putCipher(cipher.id, request);
|
response = await this.apiService.putCipher(cipher.id, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = new CipherData(
|
const data = new CipherData(response, cipher.collectionIds);
|
||||||
response,
|
|
||||||
await this.stateService.getUserId(),
|
|
||||||
cipher.collectionIds
|
|
||||||
);
|
|
||||||
await this.upsert(data);
|
await this.upsert(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -635,7 +631,7 @@ export class CipherService implements CipherServiceAbstraction {
|
|||||||
const encCipher = await this.encrypt(cipher);
|
const encCipher = await this.encrypt(cipher);
|
||||||
const request = new CipherShareRequest(encCipher);
|
const request = new CipherShareRequest(encCipher);
|
||||||
const response = await this.apiService.putShareCipher(cipher.id, request);
|
const response = await this.apiService.putShareCipher(cipher.id, request);
|
||||||
const data = new CipherData(response, await this.stateService.getUserId(), collectionIds);
|
const data = new CipherData(response, collectionIds);
|
||||||
await this.upsert(data);
|
await this.upsert(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -666,8 +662,7 @@ export class CipherService implements CipherServiceAbstraction {
|
|||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
const userId = await this.stateService.getUserId();
|
await this.upsert(encCiphers.map((c) => c.toCipherData()));
|
||||||
await this.upsert(encCiphers.map((c) => c.toCipherData(userId)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
saveAttachmentWithServer(cipher: Cipher, unencryptedFile: any, admin = false): Promise<Cipher> {
|
saveAttachmentWithServer(cipher: Cipher, unencryptedFile: any, admin = false): Promise<Cipher> {
|
||||||
@@ -741,11 +736,7 @@ export class CipherService implements CipherServiceAbstraction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cData = new CipherData(
|
const cData = new CipherData(response, cipher.collectionIds);
|
||||||
response,
|
|
||||||
await this.stateService.getUserId(),
|
|
||||||
cipher.collectionIds
|
|
||||||
);
|
|
||||||
if (!admin) {
|
if (!admin) {
|
||||||
await this.upsert(cData);
|
await this.upsert(cData);
|
||||||
}
|
}
|
||||||
@@ -801,7 +792,7 @@ export class CipherService implements CipherServiceAbstraction {
|
|||||||
async saveCollectionsWithServer(cipher: Cipher): Promise<any> {
|
async saveCollectionsWithServer(cipher: Cipher): Promise<any> {
|
||||||
const request = new CipherCollectionsRequest(cipher.collectionIds);
|
const request = new CipherCollectionsRequest(cipher.collectionIds);
|
||||||
await this.apiService.putCipherCollections(cipher.id, request);
|
await this.apiService.putCipherCollections(cipher.id, request);
|
||||||
const data = cipher.toCipherData(await this.stateService.getUserId());
|
const data = cipher.toCipherData();
|
||||||
await this.upsert(data);
|
await this.upsert(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ import { CollectionData } from "../models/data/collectionData";
|
|||||||
import { Cipher } from "../models/domain/cipher";
|
import { Cipher } from "../models/domain/cipher";
|
||||||
import { Collection } from "../models/domain/collection";
|
import { Collection } from "../models/domain/collection";
|
||||||
import { Folder } from "../models/domain/folder";
|
import { Folder } from "../models/domain/folder";
|
||||||
import { CipherWithIds as CipherExport } from "../models/export/cipherWithIds";
|
import { CipherWithIdExport as CipherExport } from "../models/export/cipherWithIdsExport";
|
||||||
import { CollectionWithId as CollectionExport } from "../models/export/collectionWithId";
|
import { CollectionWithIdExport as CollectionExport } from "../models/export/collectionWithIdExport";
|
||||||
import { Event } from "../models/export/event";
|
import { EventExport } from "../models/export/eventExport";
|
||||||
import { FolderWithId as FolderExport } from "../models/export/folderWithId";
|
import { FolderWithIdExport as FolderExport } from "../models/export/folderWithIdExport";
|
||||||
import { CollectionDetailsResponse } from "../models/response/collectionResponse";
|
import { CollectionDetailsResponse } from "../models/response/collectionResponse";
|
||||||
import { CipherView } from "../models/view/cipherView";
|
import { CipherView } from "../models/view/cipherView";
|
||||||
import { CollectionView } from "../models/view/collectionView";
|
import { CollectionView } from "../models/view/collectionView";
|
||||||
@@ -90,7 +90,7 @@ export class ExportService implements ExportServiceAbstraction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getEventExport(events: EventView[]): Promise<string> {
|
async getEventExport(events: EventView[]): Promise<string> {
|
||||||
return papa.unparse(events.map((e) => new Event(e)));
|
return papa.unparse(events.map((e) => new EventExport(e)));
|
||||||
}
|
}
|
||||||
|
|
||||||
getFileName(prefix: string = null, extension = "csv"): string {
|
getFileName(prefix: string = null, extension = "csv"): string {
|
||||||
|
|||||||
@@ -117,8 +117,7 @@ export class FolderService implements FolderServiceAbstraction {
|
|||||||
response = await this.apiService.putFolder(folder.id, request);
|
response = await this.apiService.putFolder(folder.id, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userId = await this.stateService.getUserId();
|
const data = new FolderData(response);
|
||||||
const data = new FolderData(response, userId);
|
|
||||||
await this.upsert(data);
|
await this.upsert(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export class NotificationsService implements NotificationsServiceAbstraction {
|
|||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
private vaultTimeoutService: VaultTimeoutService,
|
private vaultTimeoutService: VaultTimeoutService,
|
||||||
private environmentService: EnvironmentService,
|
private environmentService: EnvironmentService,
|
||||||
private logoutCallback: () => Promise<void>,
|
private logoutCallback: (expired: boolean) => Promise<void>,
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
private stateService: StateService
|
private stateService: StateService
|
||||||
) {
|
) {
|
||||||
@@ -169,7 +169,7 @@ export class NotificationsService implements NotificationsServiceAbstraction {
|
|||||||
break;
|
break;
|
||||||
case NotificationType.LogOut:
|
case NotificationType.LogOut:
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
this.logoutCallback();
|
this.logoutCallback(true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NotificationType.SyncSendCreate:
|
case NotificationType.SyncSendCreate:
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ export class PasswordGenerationService implements PasswordGenerationServiceAbstr
|
|||||||
async getOptions(): Promise<[any, PasswordGeneratorPolicyOptions]> {
|
async getOptions(): Promise<[any, PasswordGeneratorPolicyOptions]> {
|
||||||
let options = await this.stateService.getPasswordGenerationOptions();
|
let options = await this.stateService.getPasswordGenerationOptions();
|
||||||
if (options == null) {
|
if (options == null) {
|
||||||
options = DefaultOptions;
|
options = Object.assign({}, DefaultOptions);
|
||||||
} else {
|
} else {
|
||||||
options = Object.assign({}, DefaultOptions, options);
|
options = Object.assign({}, DefaultOptions, options);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -166,8 +166,7 @@ export class SendService implements SendServiceAbstraction {
|
|||||||
response = await this.apiService.putSend(sendData[0].id, request);
|
response = await this.apiService.putSend(sendData[0].id, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userId = await this.stateService.getUserId();
|
const data = new SendData(response);
|
||||||
const data = new SendData(response, userId);
|
|
||||||
await this.upsert(data);
|
await this.upsert(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,8 +256,7 @@ export class SendService implements SendServiceAbstraction {
|
|||||||
|
|
||||||
async removePasswordWithServer(id: string): Promise<any> {
|
async removePasswordWithServer(id: string): Promise<any> {
|
||||||
const response = await this.apiService.putSendRemovePassword(id);
|
const response = await this.apiService.putSendRemovePassword(id);
|
||||||
const userId = await this.stateService.getUserId();
|
const data = new SendData(response);
|
||||||
const data = new SendData(response, userId);
|
|
||||||
await this.upsert(data);
|
await this.upsert(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1633,19 +1633,6 @@ export class StateService<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getLoginRedirect(options?: StorageOptions): Promise<any> {
|
|
||||||
return (await this.getGlobals(this.reconcileOptions(options, this.defaultInMemoryOptions)))
|
|
||||||
?.loginRedirect;
|
|
||||||
}
|
|
||||||
|
|
||||||
async setLoginRedirect(value: any, options?: StorageOptions): Promise<void> {
|
|
||||||
const globals = await this.getGlobals(
|
|
||||||
this.reconcileOptions(options, this.defaultInMemoryOptions)
|
|
||||||
);
|
|
||||||
globals.loginRedirect = value;
|
|
||||||
await this.saveGlobals(globals, this.reconcileOptions(options, this.defaultInMemoryOptions));
|
|
||||||
}
|
|
||||||
|
|
||||||
async getMainWindowSize(options?: StorageOptions): Promise<number> {
|
async getMainWindowSize(options?: StorageOptions): Promise<number> {
|
||||||
return (await this.getGlobals(this.reconcileOptions(options, this.defaultInMemoryOptions)))
|
return (await this.getGlobals(this.reconcileOptions(options, this.defaultInMemoryOptions)))
|
||||||
?.mainWindowSize;
|
?.mainWindowSize;
|
||||||
|
|||||||
@@ -95,16 +95,15 @@ export class SyncService implements SyncServiceAbstraction {
|
|||||||
return this.syncCompleted(false);
|
return this.syncCompleted(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userId = await this.stateService.getUserId();
|
|
||||||
try {
|
try {
|
||||||
await this.apiService.refreshIdentityToken();
|
await this.apiService.refreshIdentityToken();
|
||||||
const response = await this.apiService.getSync();
|
const response = await this.apiService.getSync();
|
||||||
|
|
||||||
await this.syncProfile(response.profile);
|
await this.syncProfile(response.profile);
|
||||||
await this.syncFolders(userId, response.folders);
|
await this.syncFolders(response.folders);
|
||||||
await this.syncCollections(response.collections);
|
await this.syncCollections(response.collections);
|
||||||
await this.syncCiphers(userId, response.ciphers);
|
await this.syncCiphers(response.ciphers);
|
||||||
await this.syncSends(userId, response.sends);
|
await this.syncSends(response.sends);
|
||||||
await this.syncSettings(response.domains);
|
await this.syncSettings(response.domains);
|
||||||
await this.syncPolicies(response.policies);
|
await this.syncPolicies(response.policies);
|
||||||
|
|
||||||
@@ -130,8 +129,7 @@ export class SyncService implements SyncServiceAbstraction {
|
|||||||
) {
|
) {
|
||||||
const remoteFolder = await this.apiService.getFolder(notification.id);
|
const remoteFolder = await this.apiService.getFolder(notification.id);
|
||||||
if (remoteFolder != null) {
|
if (remoteFolder != null) {
|
||||||
const userId = await this.stateService.getUserId();
|
await this.folderService.upsert(new FolderData(remoteFolder));
|
||||||
await this.folderService.upsert(new FolderData(remoteFolder, userId));
|
|
||||||
this.messagingService.send("syncedUpsertedFolder", { folderId: notification.id });
|
this.messagingService.send("syncedUpsertedFolder", { folderId: notification.id });
|
||||||
return this.syncCompleted(true);
|
return this.syncCompleted(true);
|
||||||
}
|
}
|
||||||
@@ -200,8 +198,7 @@ export class SyncService implements SyncServiceAbstraction {
|
|||||||
if (shouldUpdate) {
|
if (shouldUpdate) {
|
||||||
const remoteCipher = await this.apiService.getCipher(notification.id);
|
const remoteCipher = await this.apiService.getCipher(notification.id);
|
||||||
if (remoteCipher != null) {
|
if (remoteCipher != null) {
|
||||||
const userId = await this.stateService.getUserId();
|
await this.cipherService.upsert(new CipherData(remoteCipher));
|
||||||
await this.cipherService.upsert(new CipherData(remoteCipher, userId));
|
|
||||||
this.messagingService.send("syncedUpsertedCipher", { cipherId: notification.id });
|
this.messagingService.send("syncedUpsertedCipher", { cipherId: notification.id });
|
||||||
return this.syncCompleted(true);
|
return this.syncCompleted(true);
|
||||||
}
|
}
|
||||||
@@ -238,8 +235,7 @@ export class SyncService implements SyncServiceAbstraction {
|
|||||||
) {
|
) {
|
||||||
const remoteSend = await this.apiService.getSend(notification.id);
|
const remoteSend = await this.apiService.getSend(notification.id);
|
||||||
if (remoteSend != null) {
|
if (remoteSend != null) {
|
||||||
const userId = await this.stateService.getUserId();
|
await this.sendService.upsert(new SendData(remoteSend));
|
||||||
await this.sendService.upsert(new SendData(remoteSend, userId));
|
|
||||||
this.messagingService.send("syncedUpsertedSend", { sendId: notification.id });
|
this.messagingService.send("syncedUpsertedSend", { sendId: notification.id });
|
||||||
return this.syncCompleted(true);
|
return this.syncCompleted(true);
|
||||||
}
|
}
|
||||||
@@ -339,10 +335,10 @@ export class SyncService implements SyncServiceAbstraction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async syncFolders(userId: string, response: FolderResponse[]) {
|
private async syncFolders(response: FolderResponse[]) {
|
||||||
const folders: { [id: string]: FolderData } = {};
|
const folders: { [id: string]: FolderData } = {};
|
||||||
response.forEach((f) => {
|
response.forEach((f) => {
|
||||||
folders[f.id] = new FolderData(f, userId);
|
folders[f.id] = new FolderData(f);
|
||||||
});
|
});
|
||||||
return await this.folderService.replace(folders);
|
return await this.folderService.replace(folders);
|
||||||
}
|
}
|
||||||
@@ -355,18 +351,18 @@ export class SyncService implements SyncServiceAbstraction {
|
|||||||
return await this.collectionService.replace(collections);
|
return await this.collectionService.replace(collections);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async syncCiphers(userId: string, response: CipherResponse[]) {
|
private async syncCiphers(response: CipherResponse[]) {
|
||||||
const ciphers: { [id: string]: CipherData } = {};
|
const ciphers: { [id: string]: CipherData } = {};
|
||||||
response.forEach((c) => {
|
response.forEach((c) => {
|
||||||
ciphers[c.id] = new CipherData(c, userId);
|
ciphers[c.id] = new CipherData(c);
|
||||||
});
|
});
|
||||||
return await this.cipherService.replace(ciphers);
|
return await this.cipherService.replace(ciphers);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async syncSends(userId: string, response: SendResponse[]) {
|
private async syncSends(response: SendResponse[]) {
|
||||||
const sends: { [id: string]: SendData } = {};
|
const sends: { [id: string]: SendData } = {};
|
||||||
response.forEach((s) => {
|
response.forEach((s) => {
|
||||||
sends[s.id] = new SendData(s, userId);
|
sends[s.id] = new SendData();
|
||||||
});
|
});
|
||||||
return await this.sendService.replace(sends);
|
return await this.sendService.replace(sends);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ import { VerifyOTPRequest } from "../models/request/account/verifyOTPRequest";
|
|||||||
import { SecretVerificationRequest } from "../models/request/secretVerificationRequest";
|
import { SecretVerificationRequest } from "../models/request/secretVerificationRequest";
|
||||||
import { Verification } from "../types/verification";
|
import { Verification } from "../types/verification";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for general-purpose user verification throughout the app.
|
||||||
|
* Use it to verify the input collected by UserVerificationComponent.
|
||||||
|
*/
|
||||||
export class UserVerificationService implements UserVerificationServiceAbstraction {
|
export class UserVerificationService implements UserVerificationServiceAbstraction {
|
||||||
constructor(
|
constructor(
|
||||||
private cryptoService: CryptoService,
|
private cryptoService: CryptoService,
|
||||||
@@ -14,6 +18,12 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
|
|||||||
private apiService: ApiService
|
private apiService: ApiService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new request model to be used for server-side verification
|
||||||
|
* @param verification User-supplied verification data (Master Password or OTP)
|
||||||
|
* @param requestClass The request model to create
|
||||||
|
* @param alreadyHashed Whether the master password is already hashed
|
||||||
|
*/
|
||||||
async buildRequest<T extends SecretVerificationRequest>(
|
async buildRequest<T extends SecretVerificationRequest>(
|
||||||
verification: Verification,
|
verification: Verification,
|
||||||
requestClass?: new () => T,
|
requestClass?: new () => T,
|
||||||
@@ -35,6 +45,11 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
|
|||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to verify the Master Password client-side, or send the OTP to the server for verification (with no other data)
|
||||||
|
* Generally used for client-side verification only.
|
||||||
|
* @param verification User-supplied verification data (Master Password or OTP)
|
||||||
|
*/
|
||||||
async verifyUser(verification: Verification): Promise<boolean> {
|
async verifyUser(verification: Verification): Promise<boolean> {
|
||||||
this.validateInput(verification);
|
this.validateInput(verification);
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { ApiService } from "../abstractions/api.service";
|
||||||
import { CryptoService } from "../abstractions/crypto.service";
|
import { CryptoService } from "../abstractions/crypto.service";
|
||||||
import { StateService } from "../abstractions/state.service";
|
import { StateService } from "../abstractions/state.service";
|
||||||
import { UsernameGenerationService as BaseUsernameGenerationService } from "../abstractions/usernameGeneration.service";
|
import { UsernameGenerationService as BaseUsernameGenerationService } from "../abstractions/usernameGeneration.service";
|
||||||
@@ -9,10 +10,16 @@ const DefaultOptions = {
|
|||||||
wordIncludeNumber: true,
|
wordIncludeNumber: true,
|
||||||
subaddressType: "random",
|
subaddressType: "random",
|
||||||
catchallType: "random",
|
catchallType: "random",
|
||||||
|
forwardedType: "simplelogin",
|
||||||
|
forwardedAnonAddyDomain: "anonaddy.me",
|
||||||
};
|
};
|
||||||
|
|
||||||
export class UsernameGenerationService implements BaseUsernameGenerationService {
|
export class UsernameGenerationService implements BaseUsernameGenerationService {
|
||||||
constructor(private cryptoService: CryptoService, private stateService: StateService) {}
|
constructor(
|
||||||
|
private cryptoService: CryptoService,
|
||||||
|
private stateService: StateService,
|
||||||
|
private apiService: ApiService
|
||||||
|
) {}
|
||||||
|
|
||||||
generateUsername(options: any): Promise<string> {
|
generateUsername(options: any): Promise<string> {
|
||||||
if (options.type === "catchall") {
|
if (options.type === "catchall") {
|
||||||
@@ -20,7 +27,7 @@ export class UsernameGenerationService implements BaseUsernameGenerationService
|
|||||||
} else if (options.type === "subaddress") {
|
} else if (options.type === "subaddress") {
|
||||||
return this.generateSubaddress(options);
|
return this.generateSubaddress(options);
|
||||||
} else if (options.type === "forwarded") {
|
} else if (options.type === "forwarded") {
|
||||||
return this.generateSubaddress(options);
|
return this.generateForwarded(options);
|
||||||
} else {
|
} else {
|
||||||
return this.generateWord(options);
|
return this.generateWord(options);
|
||||||
}
|
}
|
||||||
@@ -94,10 +101,50 @@ export class UsernameGenerationService implements BaseUsernameGenerationService
|
|||||||
return startString + "@" + o.catchallDomain;
|
return startString + "@" + o.catchallDomain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async generateForwarded(options: any): Promise<string> {
|
||||||
|
const o = Object.assign({}, DefaultOptions, options);
|
||||||
|
|
||||||
|
if (o.forwardedService == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o.forwardedService === "simplelogin") {
|
||||||
|
if (o.forwardedSimpleLoginApiKey == null || o.forwardedSimpleLoginApiKey === "") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.generateSimpleLoginAlias(
|
||||||
|
o.forwardedSimpleLoginApiKey,
|
||||||
|
o.forwardedSimpleLoginHostname,
|
||||||
|
o.website
|
||||||
|
);
|
||||||
|
} else if (o.forwardedService === "anonaddy") {
|
||||||
|
if (
|
||||||
|
o.forwardedAnonAddyApiToken == null ||
|
||||||
|
o.forwardedAnonAddyApiToken === "" ||
|
||||||
|
o.forwardedAnonAddyDomain == null ||
|
||||||
|
o.forwardedAnonAddyDomain == ""
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.generateAnonAddyAlias(
|
||||||
|
o.forwardedAnonAddyApiToken,
|
||||||
|
o.forwardedAnonAddyDomain,
|
||||||
|
o.website
|
||||||
|
);
|
||||||
|
} else if (o.forwardedService === "firefoxrelay") {
|
||||||
|
if (o.forwardedFirefoxApiToken == null || o.forwardedFirefoxApiToken === "") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.generateFirefoxRelayAlias(o.forwardedFirefoxApiToken, o.website);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
async getOptions(): Promise<any> {
|
async getOptions(): Promise<any> {
|
||||||
let options = await this.stateService.getUsernameGenerationOptions();
|
let options = await this.stateService.getUsernameGenerationOptions();
|
||||||
if (options == null) {
|
if (options == null) {
|
||||||
options = DefaultOptions;
|
options = Object.assign({}, DefaultOptions);
|
||||||
} else {
|
} else {
|
||||||
options = Object.assign({}, DefaultOptions, options);
|
options = Object.assign({}, DefaultOptions, options);
|
||||||
}
|
}
|
||||||
@@ -125,4 +172,112 @@ export class UsernameGenerationService implements BaseUsernameGenerationService
|
|||||||
? number
|
? number
|
||||||
: new Array(width - number.length + 1).join("0") + number;
|
: new Array(width - number.length + 1).join("0") + number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async generateSimpleLoginAlias(
|
||||||
|
apiKey: string,
|
||||||
|
hostname: string,
|
||||||
|
websiteNote: string
|
||||||
|
): Promise<string> {
|
||||||
|
if (apiKey == null || apiKey === "") {
|
||||||
|
throw "Invalid SimpleLogin API key.";
|
||||||
|
}
|
||||||
|
const requestInit: RequestInit = {
|
||||||
|
cache: "no-store",
|
||||||
|
method: "POST",
|
||||||
|
headers: new Headers({
|
||||||
|
Authentication: apiKey,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
let url = "https://app.simplelogin.io/api/alias/random/new";
|
||||||
|
if (hostname != null) {
|
||||||
|
url += "?hostname=" + hostname;
|
||||||
|
}
|
||||||
|
requestInit.body = JSON.stringify({
|
||||||
|
note:
|
||||||
|
(websiteNote != null ? "Website: " + websiteNote + ". " : "") + "Generated by Bitwarden.",
|
||||||
|
});
|
||||||
|
const request = new Request(url, requestInit);
|
||||||
|
const response = await this.apiService.nativeFetch(request);
|
||||||
|
if (response.status === 200 || response.status === 201) {
|
||||||
|
const json = await response.json();
|
||||||
|
return json.alias;
|
||||||
|
}
|
||||||
|
if (response.status === 401) {
|
||||||
|
throw "Invalid SimpleLogin API key.";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const json = await response.json();
|
||||||
|
if (json?.error != null) {
|
||||||
|
throw "SimpleLogin error:" + json.error;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Do nothing...
|
||||||
|
}
|
||||||
|
throw "Unknown SimpleLogin error occurred.";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async generateAnonAddyAlias(
|
||||||
|
apiToken: string,
|
||||||
|
domain: string,
|
||||||
|
websiteNote: string
|
||||||
|
): Promise<string> {
|
||||||
|
if (apiToken == null || apiToken === "") {
|
||||||
|
throw "Invalid AnonAddy API token.";
|
||||||
|
}
|
||||||
|
const requestInit: RequestInit = {
|
||||||
|
cache: "no-store",
|
||||||
|
method: "POST",
|
||||||
|
headers: new Headers({
|
||||||
|
Authorization: "Bearer " + apiToken,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
const url = "https://app.anonaddy.com/api/v1/aliases";
|
||||||
|
requestInit.body = JSON.stringify({
|
||||||
|
domain: domain,
|
||||||
|
description:
|
||||||
|
(websiteNote != null ? "Website: " + websiteNote + ". " : "") + "Generated by Bitwarden.",
|
||||||
|
});
|
||||||
|
const request = new Request(url, requestInit);
|
||||||
|
const response = await this.apiService.nativeFetch(request);
|
||||||
|
if (response.status === 200 || response.status === 201) {
|
||||||
|
const json = await response.json();
|
||||||
|
return json?.data?.email;
|
||||||
|
}
|
||||||
|
if (response.status === 401) {
|
||||||
|
throw "Invalid AnonAddy API token.";
|
||||||
|
}
|
||||||
|
throw "Unknown AnonAddy error occurred.";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async generateFirefoxRelayAlias(apiToken: string, website: string): Promise<string> {
|
||||||
|
if (apiToken == null || apiToken === "") {
|
||||||
|
throw "Invalid Firefox Relay API token.";
|
||||||
|
}
|
||||||
|
const requestInit: RequestInit = {
|
||||||
|
cache: "no-store",
|
||||||
|
method: "POST",
|
||||||
|
headers: new Headers({
|
||||||
|
Authorization: "Token " + apiToken,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
const url = "https://relay.firefox.com/api/v1/relayaddresses/";
|
||||||
|
requestInit.body = JSON.stringify({
|
||||||
|
enabled: true,
|
||||||
|
generated_for: website,
|
||||||
|
description: (website != null ? website + " - " : "") + "Generated by Bitwarden.",
|
||||||
|
});
|
||||||
|
const request = new Request(url, requestInit);
|
||||||
|
const response = await this.apiService.nativeFetch(request);
|
||||||
|
if (response.status === 200 || response.status === 201) {
|
||||||
|
const json = await response.json();
|
||||||
|
return json?.full_address;
|
||||||
|
}
|
||||||
|
if (response.status === 401) {
|
||||||
|
throw "Invalid Firefox Relay API token.";
|
||||||
|
}
|
||||||
|
throw "Unknown Firefox Relay error occurred.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
|
|||||||
private keyConnectorService: KeyConnectorService,
|
private keyConnectorService: KeyConnectorService,
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
private lockedCallback: (userId?: string) => Promise<void> = null,
|
private lockedCallback: (userId?: string) => Promise<void> = null,
|
||||||
private loggedOutCallback: (userId?: string) => Promise<void> = null
|
private loggedOutCallback: (expired: boolean, userId?: string) => Promise<void> = null
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
init(checkOnInterval: boolean) {
|
init(checkOnInterval: boolean) {
|
||||||
@@ -116,7 +116,7 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
|
|||||||
|
|
||||||
async logOut(userId?: string): Promise<void> {
|
async logOut(userId?: string): Promise<void> {
|
||||||
if (this.loggedOutCallback != null) {
|
if (this.loggedOutCallback != null) {
|
||||||
await this.loggedOutCallback(userId);
|
await this.loggedOutCallback(false, userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
|
stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
|
||||||
addons: ["@storybook/addon-links", "@storybook/addon-essentials", "@storybook/addon-a11y"],
|
addons: [
|
||||||
|
"@storybook/addon-links",
|
||||||
|
"@storybook/addon-essentials",
|
||||||
|
"@storybook/addon-a11y",
|
||||||
|
"storybook-addon-designs",
|
||||||
|
],
|
||||||
framework: "@storybook/angular",
|
framework: "@storybook/angular",
|
||||||
core: {
|
core: {
|
||||||
builder: "webpack5",
|
builder: "webpack5",
|
||||||
|
|||||||
92
components/package-lock.json
generated
92
components/package-lock.json
generated
@@ -47,6 +47,7 @@
|
|||||||
"karma-jasmine": "~4.0.0",
|
"karma-jasmine": "~4.0.0",
|
||||||
"karma-jasmine-html-reporter": "~1.7.0",
|
"karma-jasmine-html-reporter": "~1.7.0",
|
||||||
"postcss": "^8.4.6",
|
"postcss": "^8.4.6",
|
||||||
|
"storybook-addon-designs": "^6.2.1",
|
||||||
"tailwindcss": "^3.0.18",
|
"tailwindcss": "^3.0.18",
|
||||||
"typescript": "~4.3.5"
|
"typescript": "~4.3.5"
|
||||||
}
|
}
|
||||||
@@ -4036,6 +4037,29 @@
|
|||||||
"integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==",
|
"integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@figspec/components": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@figspec/components/-/components-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-a8sgP0YLJ3H0g0pdZPYecxfp9JNVQUTaaU3xcSci8duHXTGkJ7X8QPPCBbyhB+MoxMxnsAh8GjkfZHEr9oIoPQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"copy-to-clipboard": "^3.0.0",
|
||||||
|
"lit-element": "^2.4.0",
|
||||||
|
"lit-html": "^1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@figspec/react": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@figspec/react/-/react-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-BkOu3RsKF5vCtPoqsc6Oeyxw4wr9GesFrB9/wDHFqgjzhWsw8erFxCsPxsjdlJD8d8OWVHoM6SWxAaGe/pLdxg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@figspec/components": "^1.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.14.0 || ^17.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@foliojs-fork/fontkit": {
|
"node_modules/@foliojs-fork/fontkit": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/@foliojs-fork/fontkit/-/fontkit-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@foliojs-fork/fontkit/-/fontkit-1.9.1.tgz",
|
||||||
@@ -22451,6 +22475,21 @@
|
|||||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/lit-element": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lit-element/-/lit-element-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-ogu7PiJTA33bEK0xGu1dmaX5vhcRjBXCFexPja0e7P7jqLhTpNKYRPmE+GmiCaRVAbiQKGkUgkh/i6+bh++dPQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"lit-html": "^1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lit-html": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-B9btcSgPYb1q4oSOb/PrOT6Z/H+r6xuNzfH4lFli/AWhYwdtrgQkQWBbIc6mdnf6E2IL3gDXdkkqNktpU0OZQA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/loader-runner": {
|
"node_modules/loader-runner": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz",
|
||||||
@@ -30454,6 +30493,15 @@
|
|||||||
"integrity": "sha512-iJtHSGmNgAUx0b/MCS6ASGxb//hGrHHRgzvN+K5bvkBTN7A9RTpPSf1WSp+nPGvWCJ1jRnvY7MKnuqfoi3OEqg==",
|
"integrity": "sha512-iJtHSGmNgAUx0b/MCS6ASGxb//hGrHHRgzvN+K5bvkBTN7A9RTpPSf1WSp+nPGvWCJ1jRnvY7MKnuqfoi3OEqg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/storybook-addon-designs": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/storybook-addon-designs/-/storybook-addon-designs-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-ihsscab8185HnxqTNZlM4TfrCPVsO7AimVA8BapuqT/sfZQF9m5H9C0plT3kbECdIMh2cmzMBF1Tc9ckWRgpWg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@figspec/react": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/stream-browserify": {
|
"node_modules/stream-browserify": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
|
||||||
@@ -37238,6 +37286,26 @@
|
|||||||
"integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==",
|
"integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@figspec/components": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@figspec/components/-/components-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-a8sgP0YLJ3H0g0pdZPYecxfp9JNVQUTaaU3xcSci8duHXTGkJ7X8QPPCBbyhB+MoxMxnsAh8GjkfZHEr9oIoPQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"copy-to-clipboard": "^3.0.0",
|
||||||
|
"lit-element": "^2.4.0",
|
||||||
|
"lit-html": "^1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@figspec/react": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@figspec/react/-/react-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-BkOu3RsKF5vCtPoqsc6Oeyxw4wr9GesFrB9/wDHFqgjzhWsw8erFxCsPxsjdlJD8d8OWVHoM6SWxAaGe/pLdxg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@figspec/components": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@foliojs-fork/fontkit": {
|
"@foliojs-fork/fontkit": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/@foliojs-fork/fontkit/-/fontkit-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@foliojs-fork/fontkit/-/fontkit-1.9.1.tgz",
|
||||||
@@ -51668,6 +51736,21 @@
|
|||||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"lit-element": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lit-element/-/lit-element-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-ogu7PiJTA33bEK0xGu1dmaX5vhcRjBXCFexPja0e7P7jqLhTpNKYRPmE+GmiCaRVAbiQKGkUgkh/i6+bh++dPQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"lit-html": "^1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lit-html": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-B9btcSgPYb1q4oSOb/PrOT6Z/H+r6xuNzfH4lFli/AWhYwdtrgQkQWBbIc6mdnf6E2IL3gDXdkkqNktpU0OZQA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"loader-runner": {
|
"loader-runner": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz",
|
||||||
@@ -57893,6 +57976,15 @@
|
|||||||
"integrity": "sha512-iJtHSGmNgAUx0b/MCS6ASGxb//hGrHHRgzvN+K5bvkBTN7A9RTpPSf1WSp+nPGvWCJ1jRnvY7MKnuqfoi3OEqg==",
|
"integrity": "sha512-iJtHSGmNgAUx0b/MCS6ASGxb//hGrHHRgzvN+K5bvkBTN7A9RTpPSf1WSp+nPGvWCJ1jRnvY7MKnuqfoi3OEqg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"storybook-addon-designs": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/storybook-addon-designs/-/storybook-addon-designs-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-ihsscab8185HnxqTNZlM4TfrCPVsO7AimVA8BapuqT/sfZQF9m5H9C0plT3kbECdIMh2cmzMBF1Tc9ckWRgpWg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@figspec/react": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"stream-browserify": {
|
"stream-browserify": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
"karma-jasmine": "~4.0.0",
|
"karma-jasmine": "~4.0.0",
|
||||||
"karma-jasmine-html-reporter": "~1.7.0",
|
"karma-jasmine-html-reporter": "~1.7.0",
|
||||||
"postcss": "^8.4.6",
|
"postcss": "^8.4.6",
|
||||||
|
"storybook-addon-designs": "^6.2.1",
|
||||||
"tailwindcss": "^3.0.18",
|
"tailwindcss": "^3.0.18",
|
||||||
"typescript": "~4.3.5"
|
"typescript": "~4.3.5"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ export default {
|
|||||||
args: {
|
args: {
|
||||||
type: "primary",
|
type: "primary",
|
||||||
},
|
},
|
||||||
|
parameters: {
|
||||||
|
design: {
|
||||||
|
type: "figma",
|
||||||
|
url: "https://www.figma.com/file/f32LSg3jaegICkMu7rPARm/Tailwind-Component-Library-Update?node-id=1881%3A16956",
|
||||||
|
},
|
||||||
|
},
|
||||||
} as Meta;
|
} as Meta;
|
||||||
|
|
||||||
const Template: Story<BadgeComponent> = (args: BadgeComponent) => ({
|
const Template: Story<BadgeComponent> = (args: BadgeComponent) => ({
|
||||||
|
|||||||
14
components/src/banner/banner.component.html
Normal file
14
components/src/banner/banner.component.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<div
|
||||||
|
class="tw-py-2.5 tw-px-4 tw-text-contrast tw-flex tw-gap-2 tw-items-center"
|
||||||
|
[ngClass]="bannerClass"
|
||||||
|
[attr.role]="useAlertRole ? 'status' : null"
|
||||||
|
[attr.aria-live]="useAlertRole ? 'polite' : null"
|
||||||
|
>
|
||||||
|
<i class="bwi tw-align-middle" [ngClass]="icon" *ngIf="icon" aria-hidden="true"></i>
|
||||||
|
<span class="tw-text-base tw-grow">
|
||||||
|
<ng-content></ng-content>
|
||||||
|
</span>
|
||||||
|
<button class="tw-border-0 tw-bg-transparent tw-text-contrast tw-p-0" (click)="onClose.emit()">
|
||||||
|
<i class="bwi bwi-close tw-text-sm" *ngIf="icon" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
35
components/src/banner/banner.component.spec.ts
Normal file
35
components/src/banner/banner.component.spec.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
|
|
||||||
|
import { BannerComponent } from "./banner.component";
|
||||||
|
|
||||||
|
describe("BannerComponent", () => {
|
||||||
|
let component: BannerComponent;
|
||||||
|
let fixture: ComponentFixture<BannerComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [BannerComponent],
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(BannerComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create with alert", () => {
|
||||||
|
expect(component.useAlertRole).toBeTrue();
|
||||||
|
const el = fixture.nativeElement.children[0];
|
||||||
|
expect(el.getAttribute("role")).toEqual("status");
|
||||||
|
expect(el.getAttribute("aria-live")).toEqual("polite");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("useAlertRole=false", () => {
|
||||||
|
component.useAlertRole = false;
|
||||||
|
fixture.autoDetectChanges();
|
||||||
|
|
||||||
|
expect(component.useAlertRole).toBeFalse();
|
||||||
|
const el = fixture.nativeElement.children[0];
|
||||||
|
expect(el.getAttribute("role")).toBeNull();
|
||||||
|
expect(el.getAttribute("aria-live")).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
39
components/src/banner/banner.component.ts
Normal file
39
components/src/banner/banner.component.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { Component, Input, OnInit, Output, EventEmitter } from "@angular/core";
|
||||||
|
|
||||||
|
type BannerTypes = "premium" | "info" | "warning" | "danger";
|
||||||
|
|
||||||
|
const defaultIcon: Record<BannerTypes, string> = {
|
||||||
|
premium: "bwi-star",
|
||||||
|
info: "bwi-info-circle",
|
||||||
|
warning: "bwi-exclamation-triangle",
|
||||||
|
danger: "bwi-error",
|
||||||
|
};
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "bit-banner",
|
||||||
|
templateUrl: "./banner.component.html",
|
||||||
|
})
|
||||||
|
export class BannerComponent implements OnInit {
|
||||||
|
@Input("bannerType") bannerType: BannerTypes = "info";
|
||||||
|
@Input() icon: string;
|
||||||
|
@Input() useAlertRole = true;
|
||||||
|
|
||||||
|
@Output() onClose = new EventEmitter<void>();
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.icon ??= defaultIcon[this.bannerType];
|
||||||
|
}
|
||||||
|
|
||||||
|
get bannerClass() {
|
||||||
|
switch (this.bannerType) {
|
||||||
|
case "danger":
|
||||||
|
return "tw-bg-danger-500";
|
||||||
|
case "info":
|
||||||
|
return "tw-bg-info-500";
|
||||||
|
case "premium":
|
||||||
|
return "tw-bg-success-500";
|
||||||
|
case "warning":
|
||||||
|
return "tw-bg-warning-500";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
components/src/banner/banner.module.ts
Normal file
11
components/src/banner/banner.module.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { CommonModule } from "@angular/common";
|
||||||
|
import { NgModule } from "@angular/core";
|
||||||
|
|
||||||
|
import { BannerComponent } from "./banner.component";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
exports: [BannerComponent],
|
||||||
|
declarations: [BannerComponent],
|
||||||
|
})
|
||||||
|
export class BannerModule {}
|
||||||
38
components/src/banner/banner.stories.ts
Normal file
38
components/src/banner/banner.stories.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { Meta, Story } from "@storybook/angular";
|
||||||
|
|
||||||
|
import { BannerComponent } from "./banner.component";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Jslib/Banner",
|
||||||
|
component: BannerComponent,
|
||||||
|
args: {
|
||||||
|
bannerType: "warning",
|
||||||
|
},
|
||||||
|
} as Meta;
|
||||||
|
|
||||||
|
const Template: Story<BannerComponent> = (args: BannerComponent) => ({
|
||||||
|
props: args,
|
||||||
|
template: `
|
||||||
|
<bit-banner [bannerType]="bannerType">Content Really Long Text Lorem Ipsum Ipsum Ipsum <button>Button</button></bit-banner>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Premium = Template.bind({});
|
||||||
|
Premium.args = {
|
||||||
|
bannerType: "premium",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Info = Template.bind({});
|
||||||
|
Info.args = {
|
||||||
|
bannerType: "info",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Warning = Template.bind({});
|
||||||
|
Warning.args = {
|
||||||
|
bannerType: "warning",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Danger = Template.bind({});
|
||||||
|
Danger.args = {
|
||||||
|
bannerType: "danger",
|
||||||
|
};
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user