1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-21 18:53:29 +00:00

[EC-19] Update Organization Settings Page (#3251)

* [EC-19] Refactor existing organization settings components to its own module

* [EC-19] Move SSO page to settings tab

* [EC-19] Move Policies page to Settings tab

Refactor Policy components into its own module

* [EC-19] Move ImageSubscriptionHiddenComponent

* [EC-19] Lazy load org settings module

* [EC-19] Add SSO Id to SSO config view

* [EC-19] Remove SSO identfier from org info page

* [EC-19] Update org settings/policies to follow ADR-0011

* [EC-19] Update two-step login setup description

* [EC-19] Revert nested policy components folder

* [EC-19] Revert nested org setting components folder

* [EC-19] Remove left over image component

* [EC-19] Prettier

* [EC-19] Fix missing i18n

* [EC-19] Update SSO form to use CL

* [EC-19] Remove unused SSO input components

* [EC-19] Fix bad SSO locale identifier

* [EC-19] Fix import order linting

* [EC-19] Add explicit whitespace check for launch click directive

* [EC-19] Add restricted import paths to eslint config

* [EC-19] Tag deprecated field with Jira issue to cleanup in future release

* [EC-19] Remove out of date comment

* [EC-19] Move policy components to policies module

* [EC-19] Remove dityRequired validator

* [EC-19] Use explicit type for SSO config form

* [EC-19] Fix rxjs linter errors

* [EC-19] Fix RxJS eslint comments in org settings component

* [EC-19] Use explicit ControlsOf<T> helper for nested SSO form groups.

* [EC-19] Attribute source of ControlsOf<T> helper

* [EC-19] Fix missing settings side nav links

* [EC-19] Fix member/user language for policy modals
This commit is contained in:
Shane Melton
2022-09-27 14:40:04 -07:00
committed by GitHub
parent 38204bf4dd
commit 75965b080f
48 changed files with 750 additions and 641 deletions

View File

@@ -0,0 +1,16 @@
import { Directive, HostListener, Input } from "@angular/core";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
@Directive({
selector: "[appCopyClick]",
})
export class CopyClickDirective {
constructor(private platformUtilsService: PlatformUtilsService) {}
@Input("appCopyClick") valueToCopy = "";
@HostListener("click") onClick() {
this.platformUtilsService.copyToClipboard(this.valueToCopy);
}
}

View File

@@ -0,0 +1,19 @@
import { Directive, HostListener, Input } from "@angular/core";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { Utils } from "@bitwarden/common/misc/utils";
@Directive({
selector: "[appLaunchClick]",
})
export class LaunchClickDirective {
constructor(private platformUtilsService: PlatformUtilsService) {}
@Input("appLaunchClick") uriToLaunch = "";
@HostListener("click") onClick() {
if (!Utils.isNullOrWhitespace(this.uriToLaunch)) {
this.platformUtilsService.launchUri(this.uriToLaunch);
}
}
}

View File

@@ -12,9 +12,11 @@ import { A11yTitleDirective } from "./directives/a11y-title.directive";
import { ApiActionDirective } from "./directives/api-action.directive";
import { AutofocusDirective } from "./directives/autofocus.directive";
import { BoxRowDirective } from "./directives/box-row.directive";
import { CopyClickDirective } from "./directives/copy-click.directive";
import { FallbackSrcDirective } from "./directives/fallback-src.directive";
import { InputStripSpacesDirective } from "./directives/input-strip-spaces.directive";
import { InputVerbatimDirective } from "./directives/input-verbatim.directive";
import { LaunchClickDirective } from "./directives/launch-click.directive";
import { NotPremiumDirective } from "./directives/not-premium.directive";
import { SelectCopyDirective } from "./directives/select-copy.directive";
import { StopClickDirective } from "./directives/stop-click.directive";
@@ -66,6 +68,8 @@ import { PasswordStrengthComponent } from "./shared/components/password-strength
StopClickDirective,
StopPropDirective,
TrueFalseValueDirective,
CopyClickDirective,
LaunchClickDirective,
UserNamePipe,
PasswordStrengthComponent,
],
@@ -95,6 +99,8 @@ import { PasswordStrengthComponent } from "./shared/components/password-strength
StopClickDirective,
StopPropDirective,
TrueFalseValueDirective,
CopyClickDirective,
LaunchClickDirective,
UserNamePipe,
PasswordStrengthComponent,
],

View File

@@ -0,0 +1,20 @@
import { FormControl, FormGroup } from "@angular/forms";
/**
* Helper type to map a model to a strictly typed from group. Allows specifying a formGroup's
* type explicity using an existing model/interface instead of a type being inferred
* indirectly by control structure.
* Source: https://netbasal.com/typed-reactive-forms-in-angular-no-longer-a-type-dream-bf6982b0af28
* @example
* interface UserData {
* name: string;
* age: number;
* }
* const strictlyTypedForm = this.formBuilder.group<Controls<UserData>>({
* name: new FormControl("John"),
* age: new FormControl(24),
* });
*/
export type ControlsOf<T extends Record<string, any>> = {
[K in keyof T]: T[K] extends Record<any, any> ? FormGroup<ControlsOf<T[K]>> : FormControl<T[K]>;
};

View File

@@ -1,9 +0,0 @@
import { AbstractControl, ValidationErrors, Validators } from "@angular/forms";
/**
* Runs Validators.required on a field only if it's dirty. This prevents error messages from being displayed
* to the user prematurely.
*/
export function dirtyRequired(control: AbstractControl): ValidationErrors | null {
return control.dirty ? Validators.required(control) : null;
}

View File

@@ -74,7 +74,7 @@ export class SsoConfigApi extends BaseResponse {
spNameIdFormat: Saml2NameIdFormat;
spOutboundSigningAlgorithm: string;
spSigningBehavior: Saml2SigningBehavior;
spMinIncomingSigningAlgorithm: boolean;
spMinIncomingSigningAlgorithm: string;
spWantAssertionsSigned: boolean;
spValidateCertificates: boolean;

View File

@@ -2,5 +2,6 @@ import { SsoConfigApi } from "../../api/ssoConfigApi";
export class OrganizationSsoRequest {
enabled = false;
identifier: string;
data: SsoConfigApi;
}

View File

@@ -2,6 +2,10 @@ import { OrganizationKeysRequest } from "./organizationKeysRequest";
export class OrganizationUpdateRequest {
name: string;
/**
* @deprecated 2022-08-03 Moved to OrganizationSsoRequest, left for backwards compatability.
* https://bitwarden.atlassian.net/browse/EC-489
*/
identifier: string;
businessName: string;
billingEmail: string;

View File

@@ -3,12 +3,14 @@ import { BaseResponse } from "../baseResponse";
export class OrganizationSsoResponse extends BaseResponse {
enabled: boolean;
identifier: string;
data: SsoConfigApi;
urls: SsoUrls;
constructor(response: any) {
super(response);
this.enabled = this.getResponseProperty("Enabled");
this.identifier = this.getResponseProperty("Identifier");
this.data =
this.getResponseProperty("Data") != null
? new SsoConfigApi(this.getResponseProperty("Data"))

View File

@@ -34,7 +34,7 @@ export class SsoConfigView extends View {
spNameIdFormat: Saml2NameIdFormat;
spOutboundSigningAlgorithm: string;
spSigningBehavior: Saml2SigningBehavior;
spMinIncomingSigningAlgorithm: boolean;
spMinIncomingSigningAlgorithm: string;
spWantAssertionsSigned: boolean;
spValidateCertificates: boolean;

View File

@@ -33,6 +33,10 @@ export class BitErrorSummary {
return acc;
}
if (!control.dirty && control.untouched) {
return acc;
}
return acc + Object.keys(control.errors).length;
}, 0);
}

View File

@@ -28,6 +28,8 @@ export class BitErrorComponent {
return this.i18nService.t("inputEmail");
case "minlength":
return this.i18nService.t("inputMinLength", this.error[1]?.requiredLength);
case "maxlength":
return this.i18nService.t("inputMaxLength", this.error[1]?.requiredLength);
default:
// Attempt to show a custom error message.
if (this.error[1]?.message) {