From cba0f319370d343409f45d751d3a833dfdd0b6a9 Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Thu, 17 Nov 2022 12:50:37 +0100 Subject: [PATCH 1/6] [PS 1045] bw login with apikey argument fails on cli (#3959) * Add fix for bw login with apikey argument fails bug * Changes after running the prettier * Revert chnages on the launch.json file * Changes after running a lint * Renaming a filename to remove capital letters * Resolving the error on test run * Renaming file names due lint errors * Renaming new files to conform to snake case * Remove the test for user api login strategy * Adding the user api login test and file renaming * Rename file name to organization-api-login.spec.ts * Fixing the lint error on PR * Adding the apiLogIn.strategy to whitelist-capital-letters * Removing all the apiLogIn.strategy in whitelist-capital-letters. * Fixing PR comment relating OrganizationApiTokenRequest * Resolve PR comment on OrganizationApiTokenRequest model * Fixing PR comment of separating organization token model * fixing the lint error message * Fixing the lint error * Reverting the changes on lunch.js * revert the actual content on launch.json * Reverting changes relating to organization api login * Removing the OrganizationIdentityTokenResponse file * Removing OrganizationIdentityTokenResponse file Co-authored-by: dynwee --- .github/whitelist-capital-letters.txt | 2 -- .../src/components/two-factor.component.ts | 4 ++-- ...pec.ts => user-api-login.strategy.spec.ts} | 14 ++++++------- libs/common/src/abstractions/api.service.ts | 4 ++-- libs/common/src/abstractions/auth.service.ts | 6 +++--- libs/common/src/enums/authenticationType.ts | 2 +- .../misc/logInStrategies/logIn.strategy.ts | 8 ++++---- ...strategy.ts => user-api-login.strategy.ts} | 12 +++++------ .../src/models/domain/log-in-credentials.ts | 4 ++-- .../request/identity-token/token.request.ts | 10 ++++++---- ...n.request.ts => user-api-token.request.ts} | 2 +- libs/common/src/services/api.service.ts | 9 ++++----- libs/common/src/services/auth.service.ts | 20 +++++++++---------- libs/node/src/cli/commands/login.command.ts | 9 +++++++-- 14 files changed, 55 insertions(+), 51 deletions(-) rename libs/common/spec/misc/logInStrategies/{apiLogIn.strategy.spec.ts => user-api-login.strategy.spec.ts} (90%) rename libs/common/src/misc/logInStrategies/{apiLogin.strategy.ts => user-api-login.strategy.ts} (85%) rename libs/common/src/models/request/identity-token/{api-token.request.ts => user-api-token.request.ts} (91%) diff --git a/.github/whitelist-capital-letters.txt b/.github/whitelist-capital-letters.txt index a8ce05f180e..65d19a8b809 100644 --- a/.github/whitelist-capital-letters.txt +++ b/.github/whitelist-capital-letters.txt @@ -36,7 +36,6 @@ ./libs/angular/src/interfaces/selectOptions.ts ./libs/components/src/stories/Introduction.stories.mdx ./libs/common/spec/misc/logInStrategies/logIn.strategy.spec.ts -./libs/common/spec/misc/logInStrategies/apiLogIn.strategy.spec.ts ./libs/common/spec/misc/logInStrategies/passwordLogIn.strategy.spec.ts ./libs/common/spec/misc/logInStrategies/ssoLogIn.strategy.spec.ts ./libs/common/spec/web/services/webCryptoFunction.service.spec.ts @@ -60,7 +59,6 @@ ./libs/common/spec/services/consoleLog.service.spec.ts ./libs/common/src/misc/logInStrategies/ssoLogin.strategy.ts ./libs/common/src/misc/logInStrategies/passwordLogin.strategy.ts -./libs/common/src/misc/logInStrategies/apiLogin.strategy.ts ./libs/common/src/misc/logInStrategies/passwordlessLogin.strategy.ts ./libs/common/src/misc/logInStrategies/logIn.strategy.ts ./libs/common/src/misc/nodeUtils.ts diff --git a/libs/angular/src/components/two-factor.component.ts b/libs/angular/src/components/two-factor.component.ts index f61e54710b2..28799df6600 100644 --- a/libs/angular/src/components/two-factor.component.ts +++ b/libs/angular/src/components/two-factor.component.ts @@ -281,12 +281,12 @@ export class TwoFactorComponent extends CaptchaProtectedComponent implements OnI return ( this.authService.authingWithPassword() || this.authService.authingWithSso() || - this.authService.authingWithApiKey() || + this.authService.authingWithUserApiKey() || this.authService.authingWithPasswordless() ); } get needsLock(): boolean { - return this.authService.authingWithSso() || this.authService.authingWithApiKey(); + return this.authService.authingWithSso() || this.authService.authingWithUserApiKey(); } } diff --git a/libs/common/spec/misc/logInStrategies/apiLogIn.strategy.spec.ts b/libs/common/spec/misc/logInStrategies/user-api-login.strategy.spec.ts similarity index 90% rename from libs/common/spec/misc/logInStrategies/apiLogIn.strategy.spec.ts rename to libs/common/spec/misc/logInStrategies/user-api-login.strategy.spec.ts index 6e80ffa2916..bc61d2c7e22 100644 --- a/libs/common/spec/misc/logInStrategies/apiLogIn.strategy.spec.ts +++ b/libs/common/spec/misc/logInStrategies/user-api-login.strategy.spec.ts @@ -12,13 +12,13 @@ import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUti import { StateService } from "@bitwarden/common/abstractions/state.service"; import { TokenService } from "@bitwarden/common/abstractions/token.service"; import { TwoFactorService } from "@bitwarden/common/abstractions/twoFactor.service"; -import { ApiLogInStrategy } from "@bitwarden/common/misc/logInStrategies/apiLogin.strategy"; +import { UserApiLogInStrategy } from "@bitwarden/common/misc/logInStrategies/user-api-login.strategy"; import { Utils } from "@bitwarden/common/misc/utils"; -import { ApiLogInCredentials } from "@bitwarden/common/models/domain/log-in-credentials"; +import { UserApiLogInCredentials } from "@bitwarden/common/models/domain/log-in-credentials"; import { identityTokenResponseFactory } from "./logIn.strategy.spec"; -describe("ApiLogInStrategy", () => { +describe("UserApiLogInStrategy", () => { let cryptoService: SubstituteOf; let apiService: SubstituteOf; let tokenService: SubstituteOf; @@ -31,8 +31,8 @@ describe("ApiLogInStrategy", () => { let stateService: SubstituteOf; let twoFactorService: SubstituteOf; - let apiLogInStrategy: ApiLogInStrategy; - let credentials: ApiLogInCredentials; + let apiLogInStrategy: UserApiLogInStrategy; + let credentials: UserApiLogInCredentials; const deviceId = Utils.newGuid(); const keyConnectorUrl = "KEY_CONNECTOR_URL"; @@ -55,7 +55,7 @@ describe("ApiLogInStrategy", () => { appIdService.getAppId().resolves(deviceId); tokenService.getTwoFactorToken().resolves(null); - apiLogInStrategy = new ApiLogInStrategy( + apiLogInStrategy = new UserApiLogInStrategy( cryptoService, apiService, tokenService, @@ -69,7 +69,7 @@ describe("ApiLogInStrategy", () => { keyConnectorService ); - credentials = new ApiLogInCredentials(apiClientId, apiClientSecret); + credentials = new UserApiLogInCredentials(apiClientId, apiClientSecret); }); it("sends api key credentials to the server", async () => { diff --git a/libs/common/src/abstractions/api.service.ts b/libs/common/src/abstractions/api.service.ts index 74f9edebd6f..a8e2f45283c 100644 --- a/libs/common/src/abstractions/api.service.ts +++ b/libs/common/src/abstractions/api.service.ts @@ -24,9 +24,9 @@ import { EmergencyAccessUpdateRequest } from "../models/request/emergency-access import { EventRequest } from "../models/request/event.request"; import { GroupRequest } from "../models/request/group.request"; import { IapCheckRequest } from "../models/request/iap-check.request"; -import { ApiTokenRequest } from "../models/request/identity-token/api-token.request"; import { PasswordTokenRequest } from "../models/request/identity-token/password-token.request"; import { SsoTokenRequest } from "../models/request/identity-token/sso-token.request"; +import { UserApiTokenRequest } from "../models/request/identity-token/user-api-token.request"; import { ImportCiphersRequest } from "../models/request/import-ciphers.request"; import { ImportOrganizationCiphersRequest } from "../models/request/import-organization-ciphers.request"; import { KdfRequest } from "../models/request/kdf.request"; @@ -175,7 +175,7 @@ export abstract class ApiService { ) => Promise; postIdentityToken: ( - request: PasswordTokenRequest | SsoTokenRequest | ApiTokenRequest + request: PasswordTokenRequest | SsoTokenRequest | UserApiTokenRequest ) => Promise; refreshIdentityToken: () => Promise; diff --git a/libs/common/src/abstractions/auth.service.ts b/libs/common/src/abstractions/auth.service.ts index cadc7bb60b3..ca75e273b5c 100644 --- a/libs/common/src/abstractions/auth.service.ts +++ b/libs/common/src/abstractions/auth.service.ts @@ -3,7 +3,7 @@ import { Observable } from "rxjs"; import { AuthenticationStatus } from "../enums/authenticationStatus"; import { AuthResult } from "../models/domain/auth-result"; import { - ApiLogInCredentials, + UserApiLogInCredentials, PasswordLogInCredentials, SsoLogInCredentials, PasswordlessLogInCredentials, @@ -20,7 +20,7 @@ export abstract class AuthService { logIn: ( credentials: - | ApiLogInCredentials + | UserApiLogInCredentials | PasswordLogInCredentials | SsoLogInCredentials | PasswordlessLogInCredentials @@ -31,7 +31,7 @@ export abstract class AuthService { ) => Promise; logOut: (callback: () => void) => void; makePreloginKey: (masterPassword: string, email: string) => Promise; - authingWithApiKey: () => boolean; + authingWithUserApiKey: () => boolean; authingWithSso: () => boolean; authingWithPassword: () => boolean; authingWithPasswordless: () => boolean; diff --git a/libs/common/src/enums/authenticationType.ts b/libs/common/src/enums/authenticationType.ts index 5133c4f648e..531c571b469 100644 --- a/libs/common/src/enums/authenticationType.ts +++ b/libs/common/src/enums/authenticationType.ts @@ -1,6 +1,6 @@ export enum AuthenticationType { Password = 0, Sso = 1, - Api = 2, + UserApi = 2, Passwordless = 3, } diff --git a/libs/common/src/misc/logInStrategies/logIn.strategy.ts b/libs/common/src/misc/logInStrategies/logIn.strategy.ts index 8ac3ea2b55b..b0cf06e9f9a 100644 --- a/libs/common/src/misc/logInStrategies/logIn.strategy.ts +++ b/libs/common/src/misc/logInStrategies/logIn.strategy.ts @@ -11,23 +11,23 @@ import { TwoFactorProviderType } from "../../enums/twoFactorProviderType"; import { Account, AccountProfile, AccountTokens } from "../../models/domain/account"; import { AuthResult } from "../../models/domain/auth-result"; import { - ApiLogInCredentials, + UserApiLogInCredentials, PasswordLogInCredentials, SsoLogInCredentials, PasswordlessLogInCredentials, } from "../../models/domain/log-in-credentials"; import { DeviceRequest } from "../../models/request/device.request"; -import { ApiTokenRequest } from "../../models/request/identity-token/api-token.request"; import { PasswordTokenRequest } from "../../models/request/identity-token/password-token.request"; import { SsoTokenRequest } from "../../models/request/identity-token/sso-token.request"; import { TokenTwoFactorRequest } from "../../models/request/identity-token/token-two-factor.request"; +import { UserApiTokenRequest } from "../../models/request/identity-token/user-api-token.request"; import { KeysRequest } from "../../models/request/keys.request"; import { IdentityCaptchaResponse } from "../../models/response/identity-captcha.response"; import { IdentityTokenResponse } from "../../models/response/identity-token.response"; import { IdentityTwoFactorResponse } from "../../models/response/identity-two-factor.response"; export abstract class LogInStrategy { - protected abstract tokenRequest: ApiTokenRequest | PasswordTokenRequest | SsoTokenRequest; + protected abstract tokenRequest: UserApiTokenRequest | PasswordTokenRequest | SsoTokenRequest; protected captchaBypassToken: string = null; constructor( @@ -44,7 +44,7 @@ export abstract class LogInStrategy { abstract logIn( credentials: - | ApiLogInCredentials + | UserApiLogInCredentials | PasswordLogInCredentials | SsoLogInCredentials | PasswordlessLogInCredentials diff --git a/libs/common/src/misc/logInStrategies/apiLogin.strategy.ts b/libs/common/src/misc/logInStrategies/user-api-login.strategy.ts similarity index 85% rename from libs/common/src/misc/logInStrategies/apiLogin.strategy.ts rename to libs/common/src/misc/logInStrategies/user-api-login.strategy.ts index a60735343ad..7b435c74fd4 100644 --- a/libs/common/src/misc/logInStrategies/apiLogin.strategy.ts +++ b/libs/common/src/misc/logInStrategies/user-api-login.strategy.ts @@ -9,14 +9,14 @@ import { PlatformUtilsService } from "../../abstractions/platformUtils.service"; import { StateService } from "../../abstractions/state.service"; import { TokenService } from "../../abstractions/token.service"; import { TwoFactorService } from "../../abstractions/twoFactor.service"; -import { ApiLogInCredentials } from "../../models/domain/log-in-credentials"; -import { ApiTokenRequest } from "../../models/request/identity-token/api-token.request"; +import { UserApiLogInCredentials } from "../../models/domain/log-in-credentials"; +import { UserApiTokenRequest } from "../../models/request/identity-token/user-api-token.request"; import { IdentityTokenResponse } from "../../models/response/identity-token.response"; import { LogInStrategy } from "./logIn.strategy"; -export class ApiLogInStrategy extends LogInStrategy { - tokenRequest: ApiTokenRequest; +export class UserApiLogInStrategy extends LogInStrategy { + tokenRequest: UserApiTokenRequest; constructor( cryptoService: CryptoService, @@ -51,8 +51,8 @@ export class ApiLogInStrategy extends LogInStrategy { } } - async logIn(credentials: ApiLogInCredentials) { - this.tokenRequest = new ApiTokenRequest( + async logIn(credentials: UserApiLogInCredentials) { + this.tokenRequest = new UserApiTokenRequest( credentials.clientId, credentials.clientSecret, await this.buildTwoFactor(), diff --git a/libs/common/src/models/domain/log-in-credentials.ts b/libs/common/src/models/domain/log-in-credentials.ts index 46f2fe5f82e..81323934bf0 100644 --- a/libs/common/src/models/domain/log-in-credentials.ts +++ b/libs/common/src/models/domain/log-in-credentials.ts @@ -26,8 +26,8 @@ export class SsoLogInCredentials { ) {} } -export class ApiLogInCredentials { - readonly type = AuthenticationType.Api; +export class UserApiLogInCredentials { + readonly type = AuthenticationType.UserApi; constructor(public clientId: string, public clientSecret: string) {} } diff --git a/libs/common/src/models/request/identity-token/token.request.ts b/libs/common/src/models/request/identity-token/token.request.ts index 91007ce3424..32becd222a9 100644 --- a/libs/common/src/models/request/identity-token/token.request.ts +++ b/libs/common/src/models/request/identity-token/token.request.ts @@ -42,10 +42,12 @@ export abstract class TokenRequest { obj.authRequest = this.passwordlessAuthRequest; } - if (this.twoFactor.token && this.twoFactor.provider != null) { - obj.twoFactorToken = this.twoFactor.token; - obj.twoFactorProvider = this.twoFactor.provider; - obj.twoFactorRemember = this.twoFactor.remember ? "1" : "0"; + if (this.twoFactor) { + if (this.twoFactor.token && this.twoFactor.provider != null) { + obj.twoFactorToken = this.twoFactor.token; + obj.twoFactorProvider = this.twoFactor.provider; + obj.twoFactorRemember = this.twoFactor.remember ? "1" : "0"; + } } return obj; diff --git a/libs/common/src/models/request/identity-token/api-token.request.ts b/libs/common/src/models/request/identity-token/user-api-token.request.ts similarity index 91% rename from libs/common/src/models/request/identity-token/api-token.request.ts rename to libs/common/src/models/request/identity-token/user-api-token.request.ts index 60c8b6d3817..15a9bbfe681 100644 --- a/libs/common/src/models/request/identity-token/api-token.request.ts +++ b/libs/common/src/models/request/identity-token/user-api-token.request.ts @@ -3,7 +3,7 @@ import { DeviceRequest } from "../device.request"; import { TokenTwoFactorRequest } from "./token-two-factor.request"; import { TokenRequest } from "./token.request"; -export class ApiTokenRequest extends TokenRequest { +export class UserApiTokenRequest extends TokenRequest { constructor( public clientId: string, public clientSecret: string, diff --git a/libs/common/src/services/api.service.ts b/libs/common/src/services/api.service.ts index 6c15d85c0d1..6b660bb367e 100644 --- a/libs/common/src/services/api.service.ts +++ b/libs/common/src/services/api.service.ts @@ -31,10 +31,10 @@ import { EmergencyAccessUpdateRequest } from "../models/request/emergency-access import { EventRequest } from "../models/request/event.request"; import { GroupRequest } from "../models/request/group.request"; import { IapCheckRequest } from "../models/request/iap-check.request"; -import { ApiTokenRequest } from "../models/request/identity-token/api-token.request"; import { PasswordTokenRequest } from "../models/request/identity-token/password-token.request"; import { SsoTokenRequest } from "../models/request/identity-token/sso-token.request"; import { TokenTwoFactorRequest } from "../models/request/identity-token/token-two-factor.request"; +import { UserApiTokenRequest } from "../models/request/identity-token/user-api-token.request"; import { ImportCiphersRequest } from "../models/request/import-ciphers.request"; import { ImportOrganizationCiphersRequest } from "../models/request/import-organization-ciphers.request"; import { KdfRequest } from "../models/request/kdf.request"; @@ -206,7 +206,7 @@ export class ApiService implements ApiServiceAbstraction { // Auth APIs async postIdentityToken( - request: ApiTokenRequest | PasswordTokenRequest | SsoTokenRequest + request: UserApiTokenRequest | PasswordTokenRequest | SsoTokenRequest ): Promise { const headers = new Headers({ "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", @@ -219,7 +219,7 @@ export class ApiService implements ApiServiceAbstraction { request.alterIdentityTokenHeaders(headers); const identityToken = - request instanceof ApiTokenRequest + request instanceof UserApiTokenRequest ? request.toIdentityToken() : request.toIdentityToken(this.platformUtilsService.getClientType()); @@ -2271,8 +2271,7 @@ export class ApiService implements ApiServiceAbstraction { const appId = await this.appIdService.getAppId(); const deviceRequest = new DeviceRequest(appId, this.platformUtilsService); - - const tokenRequest = new ApiTokenRequest( + const tokenRequest = new UserApiTokenRequest( clientId, clientSecret, new TokenTwoFactorRequest(), diff --git a/libs/common/src/services/auth.service.ts b/libs/common/src/services/auth.service.ts index 4a851fb13df..4e97f701113 100644 --- a/libs/common/src/services/auth.service.ts +++ b/libs/common/src/services/auth.service.ts @@ -17,13 +17,13 @@ import { AuthenticationStatus } from "../enums/authenticationStatus"; import { AuthenticationType } from "../enums/authenticationType"; import { KdfType } from "../enums/kdfType"; import { KeySuffixOptions } from "../enums/keySuffixOptions"; -import { ApiLogInStrategy } from "../misc/logInStrategies/apiLogin.strategy"; import { PasswordLogInStrategy } from "../misc/logInStrategies/passwordLogin.strategy"; import { PasswordlessLogInStrategy } from "../misc/logInStrategies/passwordlessLogin.strategy"; import { SsoLogInStrategy } from "../misc/logInStrategies/ssoLogin.strategy"; +import { UserApiLogInStrategy } from "../misc/logInStrategies/user-api-login.strategy"; import { AuthResult } from "../models/domain/auth-result"; import { - ApiLogInCredentials, + UserApiLogInCredentials, PasswordLogInCredentials, SsoLogInCredentials, PasswordlessLogInCredentials, @@ -67,7 +67,7 @@ export class AuthService implements AuthServiceAbstraction { } private logInStrategy: - | ApiLogInStrategy + | UserApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy | PasswordlessLogInStrategy; @@ -92,7 +92,7 @@ export class AuthService implements AuthServiceAbstraction { async logIn( credentials: - | ApiLogInCredentials + | UserApiLogInCredentials | PasswordLogInCredentials | SsoLogInCredentials | PasswordlessLogInCredentials @@ -100,7 +100,7 @@ export class AuthService implements AuthServiceAbstraction { this.clearState(); let strategy: - | ApiLogInStrategy + | UserApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy | PasswordlessLogInStrategy; @@ -134,8 +134,8 @@ export class AuthService implements AuthServiceAbstraction { this.keyConnectorService ); break; - case AuthenticationType.Api: - strategy = new ApiLogInStrategy( + case AuthenticationType.UserApi: + strategy = new UserApiLogInStrategy( this.cryptoService, this.apiService, this.tokenService, @@ -203,8 +203,8 @@ export class AuthService implements AuthServiceAbstraction { this.messagingService.send("loggedOut"); } - authingWithApiKey(): boolean { - return this.logInStrategy instanceof ApiLogInStrategy; + authingWithUserApiKey(): boolean { + return this.logInStrategy instanceof UserApiLogInStrategy; } authingWithSso(): boolean { @@ -272,7 +272,7 @@ export class AuthService implements AuthServiceAbstraction { private saveState( strategy: - | ApiLogInStrategy + | UserApiLogInStrategy | PasswordLogInStrategy | SsoLogInStrategy | PasswordlessLogInStrategy diff --git a/libs/node/src/cli/commands/login.command.ts b/libs/node/src/cli/commands/login.command.ts index dfff7b9ad20..413b5add5ee 100644 --- a/libs/node/src/cli/commands/login.command.ts +++ b/libs/node/src/cli/commands/login.command.ts @@ -21,7 +21,7 @@ import { NodeUtils } from "@bitwarden/common/misc/nodeUtils"; import { Utils } from "@bitwarden/common/misc/utils"; import { AuthResult } from "@bitwarden/common/models/domain/auth-result"; import { - ApiLogInCredentials, + UserApiLogInCredentials, PasswordLogInCredentials, SsoLogInCredentials, } from "@bitwarden/common/models/domain/log-in-credentials"; @@ -160,7 +160,12 @@ export class LoginCommand { let response: AuthResult = null; if (clientId != null && clientSecret != null) { - response = await this.authService.logIn(new ApiLogInCredentials(clientId, clientSecret)); + if (!clientId.startsWith("user")) { + return Response.error("Invalid API Key; Organization API Key currently not supported"); + } + response = await this.authService.logIn( + new UserApiLogInCredentials(clientId, clientSecret) + ); } else if (ssoCode != null && ssoCodeVerifier != null) { response = await this.authService.logIn( new SsoLogInCredentials( From df0a148ed8719cc7e37d82812d5caf8da24a726a Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Thu, 17 Nov 2022 12:52:48 +0100 Subject: [PATCH 2/6] [PS 1624]CLI get and delete folder command, fails if using GUID and Session key (#4026) * Resolve get&delete folder cmd on cli using GUID&Sessionkey * Using the getFromState on the edit.command.ts Co-authored-by: dynwee --- apps/cli/src/commands/delete.command.ts | 2 +- apps/cli/src/commands/edit.command.ts | 2 +- apps/cli/src/commands/get.command.ts | 2 +- .../folder/folder.service.abstraction.ts | 4 ++++ libs/common/src/services/folder/folder.service.ts | 14 ++++++++++++++ 5 files changed, 21 insertions(+), 3 deletions(-) diff --git a/apps/cli/src/commands/delete.command.ts b/apps/cli/src/commands/delete.command.ts index 9a4480df687..5866e2a6fd6 100644 --- a/apps/cli/src/commands/delete.command.ts +++ b/apps/cli/src/commands/delete.command.ts @@ -88,7 +88,7 @@ export class DeleteCommand { } private async deleteFolder(id: string) { - const folder = await this.folderService.get(id); + const folder = await this.folderService.getFromState(id); if (folder == null) { return Response.notFound(); } diff --git a/apps/cli/src/commands/edit.command.ts b/apps/cli/src/commands/edit.command.ts index a8ed1b0f281..f486acbe991 100644 --- a/apps/cli/src/commands/edit.command.ts +++ b/apps/cli/src/commands/edit.command.ts @@ -118,7 +118,7 @@ export class EditCommand { } private async editFolder(id: string, req: FolderExport) { - const folder = await this.folderService.get(id); + const folder = await this.folderService.getFromState(id); if (folder == null) { return Response.notFound(); } diff --git a/apps/cli/src/commands/get.command.ts b/apps/cli/src/commands/get.command.ts index d2ca9edbd48..594dc5c183d 100644 --- a/apps/cli/src/commands/get.command.ts +++ b/apps/cli/src/commands/get.command.ts @@ -353,7 +353,7 @@ export class GetCommand extends DownloadCommand { private async getFolder(id: string) { let decFolder: FolderView = null; if (Utils.isGuid(id)) { - const folder = await this.folderService.get(id); + const folder = await this.folderService.getFromState(id); if (folder != null) { decFolder = await folder.decrypt(); } diff --git a/libs/common/src/abstractions/folder/folder.service.abstraction.ts b/libs/common/src/abstractions/folder/folder.service.abstraction.ts index f8df1f7db63..44db4a985b4 100644 --- a/libs/common/src/abstractions/folder/folder.service.abstraction.ts +++ b/libs/common/src/abstractions/folder/folder.service.abstraction.ts @@ -12,6 +12,10 @@ export abstract class FolderService { clearCache: () => Promise; encrypt: (model: FolderView, key?: SymmetricCryptoKey) => Promise; get: (id: string) => Promise; + /** + * @deprecated Only use in CLI! + */ + getFromState: (id: string) => Promise; /** * @deprecated Only use in CLI! */ diff --git a/libs/common/src/services/folder/folder.service.ts b/libs/common/src/services/folder/folder.service.ts index 4d8981c4eae..096d89cea9f 100644 --- a/libs/common/src/services/folder/folder.service.ts +++ b/libs/common/src/services/folder/folder.service.ts @@ -64,6 +64,20 @@ export class FolderService implements InternalFolderServiceAbstraction { return folders.find((folder) => folder.id === id); } + /** + * @deprecated For the CLI only + * @param id id of the folder + */ + async getFromState(id: string): Promise { + const foldersMap = await this.stateService.getEncryptedFolders(); + const folder = foldersMap[id]; + if (folder == null) { + return null; + } + + return new Folder(folder); + } + /** * @deprecated Only use in CLI! */ From 6049e588e43c35fc1756243ee78a8f47ed38b3f7 Mon Sep 17 00:00:00 2001 From: Daniel James Smith Date: Thu, 17 Nov 2022 15:46:49 +0100 Subject: [PATCH 3/6] Rename nodeCryptoFunctionService (#4070) --- .github/whitelist-capital-letters.txt | 2 -- apps/cli/src/bw.ts | 2 +- apps/desktop/native-messaging-test-runner/package.json | 2 +- .../native-messaging-test-runner/src/nativeMessageService.ts | 2 +- ...ion.service.spec.ts => node-crypto-function.service.spec.ts} | 2 +- ...ryptoFunction.service.ts => node-crypto-function.service.ts} | 0 6 files changed, 4 insertions(+), 6 deletions(-) rename libs/node/spec/services/{nodeCryptoFunction.service.spec.ts => node-crypto-function.service.spec.ts} (99%) rename libs/node/src/services/{nodeCryptoFunction.service.ts => node-crypto-function.service.ts} (100%) diff --git a/.github/whitelist-capital-letters.txt b/.github/whitelist-capital-letters.txt index 65d19a8b809..e1c74a70943 100644 --- a/.github/whitelist-capital-letters.txt +++ b/.github/whitelist-capital-letters.txt @@ -163,7 +163,6 @@ ./libs/common/src/services/webCryptoFunction.service.ts ./libs/common/src/interfaces/IEncrypted.ts ./libs/node/spec/cli/consoleLog.service.spec.ts -./libs/node/spec/services/nodeCryptoFunction.service.spec.ts ./libs/node/src/cli/models/response/baseResponse.ts ./libs/node/src/cli/models/response/stringResponse.ts ./libs/node/src/cli/models/response/fileResponse.ts @@ -173,7 +172,6 @@ ./libs/node/src/cli/services/consoleLog.service.ts ./libs/node/src/cli/services/cliPlatformUtils.service.ts ./libs/node/src/services/nodeApi.service.ts -./libs/node/src/services/nodeCryptoFunction.service.ts ./libs/node/src/services/lowdbStorage.service.ts ./libs/electron/spec/services/electronLog.service.spec.ts ./libs/electron/src/baseMenu.ts diff --git a/apps/cli/src/bw.ts b/apps/cli/src/bw.ts index 42738b7dfa7..7efee8eb3e5 100644 --- a/apps/cli/src/bw.ts +++ b/apps/cli/src/bw.ts @@ -51,8 +51,8 @@ import { VaultTimeoutService } from "@bitwarden/common/services/vaultTimeout/vau import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vaultTimeout/vaultTimeoutSettings.service"; import { CliPlatformUtilsService } from "@bitwarden/node/cli/services/cliPlatformUtils.service"; import { ConsoleLogService } from "@bitwarden/node/cli/services/consoleLog.service"; +import { NodeCryptoFunctionService } from "@bitwarden/node/services/node-crypto-function.service"; import { NodeApiService } from "@bitwarden/node/services/nodeApi.service"; -import { NodeCryptoFunctionService } from "@bitwarden/node/services/nodeCryptoFunction.service"; import { Program } from "./program"; import { SendProgram } from "./send.program"; diff --git a/apps/desktop/native-messaging-test-runner/package.json b/apps/desktop/native-messaging-test-runner/package.json index 85d4a1ab6a6..b9c437f591d 100644 --- a/apps/desktop/native-messaging-test-runner/package.json +++ b/apps/desktop/native-messaging-test-runner/package.json @@ -30,6 +30,6 @@ }, "_moduleAliases": { "@bitwarden/common": "dist/libs/common/src", - "@bitwarden/node/services/nodeCryptoFunction.service": "dist/libs/node/src/services/nodeCryptoFunction.service" + "@bitwarden/node/services/node-crypto-function.service": "dist/libs/node/src/services/node-crypto-function.service" } } diff --git a/apps/desktop/native-messaging-test-runner/src/nativeMessageService.ts b/apps/desktop/native-messaging-test-runner/src/nativeMessageService.ts index 916ec3c286e..d1f29493fc0 100644 --- a/apps/desktop/native-messaging-test-runner/src/nativeMessageService.ts +++ b/apps/desktop/native-messaging-test-runner/src/nativeMessageService.ts @@ -7,7 +7,7 @@ import { EncString } from "@bitwarden/common/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service"; import { EncryptServiceImplementation } from "@bitwarden/common/services/cryptography/encrypt.service.implementation"; -import { NodeCryptoFunctionService } from "@bitwarden/node/services/nodeCryptoFunction.service"; +import { NodeCryptoFunctionService } from "@bitwarden/node/services/node-crypto-function.service"; import { DecryptedCommandData } from "../../src/models/nativeMessaging/decryptedCommandData"; import { EncryptedMessage } from "../../src/models/nativeMessaging/encryptedMessage"; diff --git a/libs/node/spec/services/nodeCryptoFunction.service.spec.ts b/libs/node/spec/services/node-crypto-function.service.spec.ts similarity index 99% rename from libs/node/spec/services/nodeCryptoFunction.service.spec.ts rename to libs/node/spec/services/node-crypto-function.service.spec.ts index d0097c29304..1dbdcfb93ae 100644 --- a/libs/node/spec/services/nodeCryptoFunction.service.spec.ts +++ b/libs/node/spec/services/node-crypto-function.service.spec.ts @@ -1,6 +1,6 @@ import { Utils } from "@bitwarden/common/misc/utils"; import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; -import { NodeCryptoFunctionService } from "@bitwarden/node/services/nodeCryptoFunction.service"; +import { NodeCryptoFunctionService } from "@bitwarden/node/services/node-crypto-function.service"; const RsaPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl0Vawl/toXzkEvB82FEtqHP" + diff --git a/libs/node/src/services/nodeCryptoFunction.service.ts b/libs/node/src/services/node-crypto-function.service.ts similarity index 100% rename from libs/node/src/services/nodeCryptoFunction.service.ts rename to libs/node/src/services/node-crypto-function.service.ts From 3c0beef3a5a10a406e4f9566ea9c0bcbe0c1e5a2 Mon Sep 17 00:00:00 2001 From: Shane Melton Date: Thu, 17 Nov 2022 08:10:01 -0800 Subject: [PATCH 4/6] [CL-62] Fix Content Tab Keyboard Navigation (#3944) * [CL-62] Add missing modules to Dialog Service story The IconButtonModule and SharedModule need to be available for the service to properly open the dialog. Also fix type error for dialogSize attribute * [CL-62] Add new tabbed dialog service story - Update StoryDialogComponent to support different content components and button text for re-use in multiple stories - Update the story module metadata to include Tabs and FormsField modules for the new tab story - Add StoryTabbedDialogComponent that has tabbed content with input fields which provide tabbing targets - Add storybook actions to provide an example of getting a result from the dialog service * [CL-62] Remove tab panel tabIndex from tab group component The tabIndex attribute broke keyboard navigation in Firefox and is only required on the tab labels. * [CL-62] Introduce contentTabIndex input for bit-tab contentTabIndex provides an interface for setting the tabPanel's tabIndex so that the tabPanel is still included in the tab sequence of the page in case it has no focusable content of its own * [CL-62] Add tab keyboard navigation story * Revert "[CL-62] Add new tabbed dialog service story" This reverts commit e19216f0310fe0fdbb827be25db7aafc15d95f2f. --- .../src/dialog/dialog.service.stories.ts | 8 +++-- .../tabs/tab-group/tab-group.component.html | 2 +- .../src/tabs/tab-group/tab.component.ts | 11 +++++- libs/components/src/tabs/tabs.stories.ts | 34 +++++++++++++++++++ 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/libs/components/src/dialog/dialog.service.stories.ts b/libs/components/src/dialog/dialog.service.stories.ts index 942891ba061..678494cd9f8 100644 --- a/libs/components/src/dialog/dialog.service.stories.ts +++ b/libs/components/src/dialog/dialog.service.stories.ts @@ -1,10 +1,12 @@ -import { DialogModule, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; +import { DIALOG_DATA, DialogModule, DialogRef } from "@angular/cdk/dialog"; import { Component, Inject } from "@angular/core"; import { Meta, moduleMetadata, Story } from "@storybook/angular"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { ButtonModule } from "../button"; +import { IconButtonModule } from "../icon-button"; +import { SharedModule } from "../shared"; import { I18nMockService } from "../utils/i18n-mock.service"; import { DialogService } from "./dialog.service"; @@ -35,7 +37,7 @@ class StoryDialogComponent { @Component({ selector: "story-dialog-content", template: ` - + Dialog Title Dialog body text goes here. @@ -68,7 +70,7 @@ export default { DialogTitleContainerDirective, StoryDialogContentComponent, ], - imports: [ButtonModule, DialogModule], + imports: [SharedModule, ButtonModule, DialogModule, IconButtonModule], providers: [ DialogService, { diff --git a/libs/components/src/tabs/tab-group/tab-group.component.html b/libs/components/src/tabs/tab-group/tab-group.component.html index dbe71fb4b99..071f5c2259f 100644 --- a/libs/components/src/tabs/tab-group/tab-group.component.html +++ b/libs/components/src/tabs/tab-group/tab-group.component.html @@ -34,7 +34,7 @@ role="tabpanel" *ngFor="let tab of tabs; let i = index" [id]="getTabContentId(i)" - [attr.tabindex]="selectedIndex === i ? 0 : -1" + [attr.tabindex]="tab.contentTabIndex" [attr.labeledby]="getTabLabelId(i)" [active]="tab.isActive" [content]="tab.content" diff --git a/libs/components/src/tabs/tab-group/tab.component.ts b/libs/components/src/tabs/tab-group/tab.component.ts index 05e61ffacc2..9a3a9380031 100644 --- a/libs/components/src/tabs/tab-group/tab.component.ts +++ b/libs/components/src/tabs/tab-group/tab.component.ts @@ -20,9 +20,18 @@ import { TabLabelDirective } from "./tab-label.directive"; }) export class TabComponent implements OnInit { @Input() disabled = false; - @Input("label") textLabel = ""; + /** + * Optional tabIndex for the tabPanel that contains this tab's content. + * + * If the tabpanel does not contain any focusable elements or the first element with content is not focusable, + * this should be set to 0 to include it in the tab sequence of the page. + * + * @remarks See note 4 of https://www.w3.org/WAI/ARIA/apg/patterns/tabpanel/ + */ + @Input() contentTabIndex: number | undefined; + @ViewChild(TemplateRef, { static: true }) implicitContent: TemplateRef; @ContentChild(TabLabelDirective) templateLabel: TabLabelDirective; diff --git a/libs/components/src/tabs/tabs.stories.ts b/libs/components/src/tabs/tabs.stories.ts index a520b50e17a..32c7e950e73 100644 --- a/libs/components/src/tabs/tabs.stories.ts +++ b/libs/components/src/tabs/tabs.stories.ts @@ -3,6 +3,9 @@ import { Component } from "@angular/core"; import { RouterModule } from "@angular/router"; import { Meta, moduleMetadata, Story } from "@storybook/angular"; +import { ButtonModule } from "../button"; +import { FormFieldModule } from "../form-field"; + import { TabGroupComponent } from "./tab-group/tab-group.component"; import { TabsModule } from "./tabs.module"; @@ -44,6 +47,8 @@ export default { imports: [ CommonModule, TabsModule, + ButtonModule, + FormFieldModule, RouterModule.forRoot( [ { path: "", redirectTo: "active", pathMatch: "full" }, @@ -125,3 +130,32 @@ const PreserveContentTabGroupTemplate: Story = (args: any) => }); export const PreserveContentTabs = PreserveContentTabGroupTemplate.bind({}); + +const KeyboardNavTabGroupTemplate: Story = (args: any) => ({ + props: args, + template: ` + + +

+ You can navigate through all tab labels, form inputs, and the button that is outside the tab group via + the keyboard. +

+ + First Input + + + + Second Input + + +
+ + +

This tab has no focusable content, but the panel should still be focusable

+
+
+ +`, +}); + +export const KeyboardNavigation = KeyboardNavTabGroupTemplate.bind({}); From a57424df75e9abfc61b5b386782faca868a76490 Mon Sep 17 00:00:00 2001 From: Justin Baur <19896123+justindbaur@users.noreply.github.com> Date: Thu, 17 Nov 2022 15:15:34 -0500 Subject: [PATCH 5/6] [PS-1829] Fix Service Worker Startup in MV3 (#4000) * Fix Service Worker Startup in MV3 * Removed unneeded plugin from shared main config * Added plugin back behind MV2 check * Added background entry behind MV2 check * Added totally new config for MV3 behind check * Target `webworker` for MV3 background * Export array of configs * Address PR feedback * Remove comment --- apps/browser/webpack.config.js | 100 ++++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 21 deletions(-) diff --git a/apps/browser/webpack.config.js b/apps/browser/webpack.config.js index 3dc4085d3d0..e9224a8919a 100644 --- a/apps/browser/webpack.config.js +++ b/apps/browser/webpack.config.js @@ -6,6 +6,7 @@ const CopyWebpackPlugin = require("copy-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const { AngularWebpackPlugin } = require("@ngtools/webpack"); const TerserPlugin = require("terser-webpack-plugin"); +const { TsconfigPathsPlugin } = require("tsconfig-paths-webpack-plugin"); const configurator = require("./config/config"); if (process.env.NODE_ENV == null) { @@ -67,17 +68,24 @@ const moduleRules = [ }, ]; +const requiredPlugins = [ + new webpack.DefinePlugin({ + "process.env": { + ENV: JSON.stringify(ENV), + }, + }), + new webpack.EnvironmentPlugin({ + FLAGS: envConfig.flags, + DEV_FLAGS: ENV === "development" ? envConfig.devFlags : {}, + }), +]; + const plugins = [ new HtmlWebpackPlugin({ template: "./src/popup/index.html", filename: "popup/index.html", chunks: ["popup/polyfills", "popup/vendor-angular", "popup/vendor", "popup/main"], }), - new HtmlWebpackPlugin({ - template: "./src/background.html", - filename: "background.html", - chunks: ["vendor", "background"], - }), new HtmlWebpackPlugin({ template: "./src/notification/bar.html", filename: "notification/bar.html", @@ -99,11 +107,6 @@ const plugins = [ filename: "[name].css", chunkFilename: "chunk-[id].css", }), - new webpack.DefinePlugin({ - "process.env": { - ENV: JSON.stringify(ENV), - }, - }), new AngularWebpackPlugin({ tsConfigPath: "tsconfig.json", entryModule: "src/popup/app.module#AppModule", @@ -119,19 +122,20 @@ const plugins = [ exclude: [/content\/.*/, /notification\/.*/], filename: "[file].map", }), - new webpack.EnvironmentPlugin({ - FLAGS: envConfig.flags, - DEV_FLAGS: ENV === "development" ? envConfig.devFlags : {}, - }), + ...requiredPlugins, ]; -const config = { +/** + * @type {import("webpack").Configuration} + * This config compiles everything but the background + */ +const mainConfig = { + name: "main", mode: ENV, devtool: false, entry: { "popup/polyfills": "./src/popup/polyfills.ts", "popup/main": "./src/popup/main.ts", - background: "./src/background.ts", "content/autofill": "./src/content/autofill.js", "content/autofiller": "./src/content/autofiller.ts", "content/notificationBar": "./src/content/notificationBar.ts", @@ -209,18 +213,72 @@ const config = { plugins: plugins, }; +/** + * @type {import("webpack").Configuration[]} + */ +const configs = []; + if (manifestVersion == 2) { - // We can't use this in manifest v3 - // Ideally we understand why this breaks it and we don't have to do this - config.optimization.splitChunks.cacheGroups.commons2 = { + mainConfig.optimization.splitChunks.cacheGroups.commons2 = { test: /[\\/]node_modules[\\/]/, name: "vendor", chunks: (chunk) => { return chunk.name === "background"; }, }; + + // Manifest V2 uses Background Pages which requires a html page. + mainConfig.plugins.push( + new HtmlWebpackPlugin({ + template: "./src/background.html", + filename: "background.html", + chunks: ["vendor", "background"], + }) + ); + + // Manifest V2 background pages can be run through the regular build pipeline. + // Since it's a standard webpage. + mainConfig.entry.background = "./src/background.ts"; + + configs.push(mainConfig); } else { - config.entry["content/misc-utils"] = "./src/content/misc-utils.ts"; + // Manifest v3 needs an extra helper for utilities in the content script. + // The javascript output of this should be added to manifest.v3.json + mainConfig.entry["content/misc-utils"] = "./src/content/misc-utils.ts"; + + /** + * @type {import("webpack").Configuration} + */ + const backgroundConfig = { + name: "background", + mode: ENV, + devtool: false, + entry: "./src/background.ts", + target: "webworker", + output: { + filename: "background.js", + path: path.resolve(__dirname, "build"), + }, + module: { + rules: [ + { + test: /\.tsx?$/, + loader: "ts-loader", + }, + ], + }, + resolve: { + extensions: [".ts", ".js"], + symlinks: false, + modules: [path.resolve("../../node_modules")], + plugins: [new TsconfigPathsPlugin()], + }, + dependencies: ["main"], + plugins: [...requiredPlugins], + }; + + configs.push(mainConfig); + configs.push(backgroundConfig); } -module.exports = config; +module.exports = configs; From dc84a54928abf34bf47e0c7004b54f447836a5e6 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Fri, 18 Nov 2022 07:33:54 +1000 Subject: [PATCH 6/6] [CL-63] Color password component (#4018) --- .../color-password.component.ts | 79 +++++++++++++++++++ .../color-password/color-password.module.ts | 11 +++ .../color-password/color-password.stories.ts | 52 ++++++++++++ libs/components/src/color-password/index.ts | 1 + libs/components/src/index.ts | 1 + 5 files changed, 144 insertions(+) create mode 100644 libs/components/src/color-password/color-password.component.ts create mode 100644 libs/components/src/color-password/color-password.module.ts create mode 100644 libs/components/src/color-password/color-password.stories.ts create mode 100644 libs/components/src/color-password/index.ts diff --git a/libs/components/src/color-password/color-password.component.ts b/libs/components/src/color-password/color-password.component.ts new file mode 100644 index 00000000000..b55384dea1f --- /dev/null +++ b/libs/components/src/color-password/color-password.component.ts @@ -0,0 +1,79 @@ +import { Component, HostBinding, Input } from "@angular/core"; + +import { Utils } from "@bitwarden/common/misc/utils"; + +enum CharacterType { + Letter, + Emoji, + Special, + Number, +} + +@Component({ + selector: "bit-color-password", + template: `
+ {{ character }} + {{ + i + 1 + }} +
`, +}) +export class ColorPasswordComponent { + @Input() private password: string = null; + @Input() showCount = false; + + characterStyles: Record = { + [CharacterType.Emoji]: [], + [CharacterType.Letter]: ["tw-text-main"], + [CharacterType.Special]: ["tw-text-danger"], + [CharacterType.Number]: ["tw-text-primary-500"], + }; + + @HostBinding("class") + get classList() { + return ["tw-min-w-0", "tw-whitespace-pre-wrap", "tw-break-all"]; + } + + get passwordArray() { + // Convert to an array to handle cases that strings have special characters, i.e.: emoji. + return Array.from(this.password); + } + + getCharacterClass(character: string) { + const charType = this.getCharacterType(character); + const charClass = this.characterStyles[charType].concat("tw-inline-flex"); + + if (this.showCount) { + return charClass.concat([ + "tw-inline-flex", + "tw-flex-col", + "tw-items-center", + "tw-w-7", + "tw-py-1", + "odd:tw-bg-secondary-100", + ]); + } + + return charClass; + } + + private getCharacterType(character: string): CharacterType { + if (character.match(Utils.regexpEmojiPresentation)) { + return CharacterType.Emoji; + } + + if (character.match(/\d/)) { + return CharacterType.Number; + } + + const specials = ["&", "<", ">", " "]; + if (specials.includes(character) || character.match(/[^\w ]/)) { + return CharacterType.Special; + } + + return CharacterType.Letter; + } +} diff --git a/libs/components/src/color-password/color-password.module.ts b/libs/components/src/color-password/color-password.module.ts new file mode 100644 index 00000000000..692c206bb4c --- /dev/null +++ b/libs/components/src/color-password/color-password.module.ts @@ -0,0 +1,11 @@ +import { CommonModule } from "@angular/common"; +import { NgModule } from "@angular/core"; + +import { ColorPasswordComponent } from "./color-password.component"; + +@NgModule({ + imports: [CommonModule], + exports: [ColorPasswordComponent], + declarations: [ColorPasswordComponent], +}) +export class ColorPasswordModule {} diff --git a/libs/components/src/color-password/color-password.stories.ts b/libs/components/src/color-password/color-password.stories.ts new file mode 100644 index 00000000000..87565b81273 --- /dev/null +++ b/libs/components/src/color-password/color-password.stories.ts @@ -0,0 +1,52 @@ +import { Meta, Story } from "@storybook/angular"; + +import { ColorPasswordComponent } from "./color-password.component"; + +const examplePassword = "Wq$Jk😀7jDX#rS5Sdi!z"; + +export default { + title: "Component Library/Color Password", + component: ColorPasswordComponent, + args: { + password: examplePassword, + showCount: false, + }, + parameters: { + design: { + type: "figma", + url: "https://www.figma.com/file/6fvTDa3zfvgWdizLQ7nSTP/Numbered-Password", + }, + }, +} as Meta; + +const Template: Story = (args: ColorPasswordComponent) => ({ + props: args, + template: ` + + `, +}); + +const WrappedTemplate: Story = (args: ColorPasswordComponent) => ({ + props: args, + template: ` +
+ +
+ `, +}); + +export const ColorPassword = Template.bind({}); + +export const WrappedColorPassword = WrappedTemplate.bind({}); + +export const ColorPasswordCount = Template.bind({}); +ColorPasswordCount.args = { + password: examplePassword, + showCount: true, +}; + +export const WrappedColorPasswordCount = WrappedTemplate.bind({}); +WrappedColorPasswordCount.args = { + password: examplePassword, + showCount: true, +}; diff --git a/libs/components/src/color-password/index.ts b/libs/components/src/color-password/index.ts new file mode 100644 index 00000000000..86718f037f7 --- /dev/null +++ b/libs/components/src/color-password/index.ts @@ -0,0 +1 @@ +export * from "./color-password.module"; diff --git a/libs/components/src/index.ts b/libs/components/src/index.ts index e217c6bf026..8abb6bade3d 100644 --- a/libs/components/src/index.ts +++ b/libs/components/src/index.ts @@ -14,4 +14,5 @@ export * from "./multi-select"; export * from "./tabs"; export * from "./table"; export * from "./toggle-group"; +export * from "./color-password"; export * from "./utils/i18n-mock.service";