mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 15:53:27 +00:00
[EC-7] Org Admin Vault Refresh Client V1 (#3925)
* [EC-8] Restructure Tabs (#3109) * Cherry pick pending PR for tabs component [CL-17] Tabs - Routing * Update organization tabs from 4 to 6 * Create initial 'Members' tab * Create initial 'Groups' tab * Add initial "Reporting" tab * Use correct report label/layout by product type * Create initial 'Billing' tab * Breakup billing payment and billing history pages * Cleanup org routing and nav permission service * More org tab permission cleanup * Refactor organization billing to use a module * Refactor organization reporting to use module * Cherry pick finished/merged tabs component [CL-17] Tabs - Router (#2952) * This partially reverts commit24bb775to fix tracking of people.component.html rename. * Fix people component file rename * Recover lost member page changes * Undo members component rename as it was causing difficult merge conflicts * Fix member and group page container * Remove unnecessary organization lookup * [EC-8] Some PR suggestions * [EC-8] Reuse user billing history for orgs * [EC-8] Renamed user billing history component * [EC-8] Repurpose payment method component Update end user payment method component to be usable for organizations. * [EC-8] Fix missing verify bank condition * [EC-8] Remove org payment method component * [EC-8] Use CL in payment method component * [EC-8] Extend maxWidth Tailwind theme config * [EC-8] Add lazy loading to org reports * [EC-8] Add lazy loading to org billing * [EC-8] Prettier * [EC-8] Cleanup org reporting component redundancy * [EC-8] Use different class for negative margin * [EC-8] Make billing history component "dumb" * Revert "[EC-8] Cleanup org reporting component redundancy" This reverts commiteca337e89b. * [EC-8] Create and export shared reports module * [EC-8] Use shared reports module in orgs * [EC-8] Use takeUntil pattern * [EC-8] Move org reporting module out of old modules folder * [EC-8] Move org billing module out of old modules folder * [EC-8] Fix some remaining merge conflicts * [EC-8] Move maxWidth into 'extend' key for Tailwind config * [EC-8] Remove unused module * [EC-8] Rename org report list component * Prettier Co-authored-by: Vincent Salucci <vincesalucci21@gmail.com> * [EC-451] Org Admin Refresh Permissions Refactor (#3320) * [EC-451] Update new org permissions for new tabs * [EC-451] Remove redudant route guards * [EC-451] Remove canAccessManageTab() * [EC-451] Use canAccess* callbacks in org routing module * Fix org api service refactor and linting after pulling in master * Fix broken org people and group pages after merge * [EC-18] Reporting side nav direction (#3420) * [EC-18] Re-order side nav for org reports according to Figma * [EC-18] Fix rxjs linter errors and redundant org flag * [EC-526] Default to Event Logs page for Reporting Tab (#3470) * [EC-526] Default to the Events Logs page when navigating to the Reporting tab * [EC-526] Undo default routing redirect when the child path is missing. Avoids defaulting to "/events" in case a user/org doesn't have access to event logs. * [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 * [EC-551] Update Event Logs Client Column (#3572) * [EC-551] Fix RxJS warnings * [EC-551] Update page to use CL components and Tailwind classes * [EC-551] Update Client column to use text instead of icon. Update language and i18n. * [EC-14] Refactor vault filter (#3440) * [EC-14] initial refactoring of vault filter * [EC-14] return observable trees for all filters with head node * [EC-14] Remove bindings on callbacks * [EC-14] fix formatting on disabled orgs * [EC-14] hide MyVault if personal org policy * [EC-14] add check for single org policy * [EC-14] add policies to org and change node constructor * [EC-14] don't show options if personal vault policy * [EC-14] default to all vaults * [EC-14] add default selection to filters * [EC-14] finish filter model callbacks * [EC-14] finish filter functionality and begin cleaning up * [EC-14] clean up old components and start on org vault * [EC-14] loop through filters for presentation * [EC-14] refactor VaultFilterService and put filter presentation data back into Vault Filter component. Remove VaultService * [EC-14] begin refactoring org vault * [EC-14] Refactor Vault Filter Service to use observables * [EC-14] finish org vault filter * [EC-14] fix vault model tests * [EC-14] fix org service calls * [EC-14] pull refactor out of shared code * [EC-14] include head node for collections even if collections aren't loaded yet * [EC-14] fix url params for vaults * [EC-14] remove comments * [EC-14] Remove unnecesary getter for org on vault filter * [EC-14] fix linter * [EC-14] fix prettier * [EC-14] add deprecated methods to collection service for desktop and browser * [EC-14] simplify cipher type node check * [EC-14] add getters to vault filter model * [EC-14] refactor how we build the filter list into methods * [EC-14] add getters to build filter method * [EC-14] remove param ids if false * [EC-14] fix collapsing nodes * [EC-14] add specific type to search placeholder * [EC-14] remove extra constructor and comment from org vault filter * [EC-14] extract subscription callback to methods * [EC-14] Remove unecessary await * [EC-14] Remove ternary operators while building org filter * [EC-14] remove unnecessary deps array in vault filter service declaration * [EC-14] consolidate new models into one file * [EC-14] initialize nested observable inside of service Signed-off-by: Jacob Fink <jfink@bitwarden.com> * [EC-14] change how we load orgs into the vault filter and select the default filter * [EC-14] remove get from getters name * [EC-14] remove eslint-disable comment * [EC-14] move vault filter service abstraction to angular folder and separate * [EC-14] rename filter types and delete VaultFilterLabel * [EC-14] remove changes to workspace file * [EC-14] remove deprecated service from jslib module * [EC-14] remove any remaining files from common code * [EC-14] consolidate vault filter components into components folder * [EC-14] simplify method call * [EC-14] refactor the vault filter service - orgs now have observable property - BehaviorSubjects have been migrated to ReplaySubjects if they don't need starting value - added unit tests - fix small error when selecting org badge of personal vault - renamed some properties * [EC-14] replace mergeMap with switchMap in vault filter service * [EC-14] early return to prevent nesting * [EC-14] clean up filterCollections method * [EC-14] use isDeleted helper in html * [EC-14] add jsdoc comments to ServiceUtils * [EC-14] fix linter * [EC-14] use array.slice instead of setting length * Update apps/web/src/app/vault/vault-filter/services/vault-filter.service.ts Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> * [EC-14] add missing high level jsdoc description * [EC-14] fix storybook absolute imports * [EC-14] delete vault-shared.module * [EC-14] change search placeholder text to getter and add missing strings * [EC-14] remove two way binding from search text in vault filter * [EC-14] removed all binding from search text and just use input event * [EC-14] remove async from apply vault filter * [EC-14] remove circular observable calls in vault filter service Co-authored-by: Thomas Rittson <eliykat@users.noreply.github.com> * [EC-14] move collapsed nodes to vault filter section * [EC-14] deconstruct filter section inside component * [EC-14] fix merge conflicts and introduce refactored organization service to vault filter service * [EC-14] remove mutation from filter builders * [EC-14] fix styling on buildFolderTree * [EC-14] remove leftover folder-filters reference and use ternary for collapse icon * [EC-14] remove unecessary checks * [EC-14] stop rebuilding filters when the organization changes * [EC-14] Move subscription out of setter in vault filter section * [EC-14] remove extra policy service methods from vault filter service * [EC-14] remove new methods from old vault-filter.service * [EC-14] Use vault filter service in vault components * [EC-14] reload collections from vault now that we have vault filter service * [EC-14] remove currentFilterCollections in vault filter component * [EC-14] change VaultFilterType to more specific OrganizationFilter in organization-options * [EC-14] include org check in isNodeSelected * [EC-14] add getters to filter function, fix storybook, and add test for All Collections * [EC-14] show org options even if there's a personal vault policy * [EC-14] use !"AllCollections" instead of just !null * [EC-14] Remove extra org Subject in vault filter service * [EC-14] remove null check from vault search text * [EC-14] replace store/build names with set/get. Remove extra call to setOrganizationFilter * [EC-14] add take(1) to subscribe in test * [EC-14] move init logic in org vault filter component to ngOnInit * [EC-14] Fix linter * [EC-14] revert change to vault filter model * [EC-14] be specific about ignoring All Collections * [EC-14] move observable init logic to beforeEach in test * [EC-14] make buildAllFilters return something to reduce side effects Signed-off-by: Jacob Fink <jfink@bitwarden.com> Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Co-authored-by: Thomas Rittson <eliykat@users.noreply.github.com> * [EC-97] Organization Billing Language / RxJS Warnings (#3688) * [EC-97] Update copy to use the word members in a few places * [EC-97] Cleanup RxJS warnings and unused properties in org billing components * [EC-599] Access Selector Component (#3717) * Add Access Selector Component and Stories * Cherry pick FormSelectionList * Fix some problems caused from cherry-pick * Fix some Web module problems caused from cherry-pick * Move AccessSelector out of the root components directory. Move UserType pipe to AccessSelectorModule * Fix broken member access selector story * Add organization feature module * Undo changes to messages.json * Fix messages.json * Remove redundant CommonModule * [EC-599] Fix avatar/icon sizing * [EC-599] Remove padding in permission column * [EC-599] Make FormSelectionList operations immutable * [EC-599] Integrate the multi-select component * [EC-599] Handle readonly/access all edge cases * [EC-599] Add initial unit tests Also cleans up public interface for the AccessSelectorComponent. Fixes a bug found during unit test creation. * [EC-599] Include item name in control labels * [EC-599] Cleanup member email display * [EC-599] Review suggestions - Change PermissionMode to Enum - Rename permControl to permissionControl to be more clear - Rename FormSelectionList file to kebab case. - Move permission row boolean logic to named function for readability * [EC-599] Cleanup AccessSelectorComponent tests - Clarify test states - Add tests for column rendering - Add tests for permission mode - Add id to column headers for testing - Fix small permissionControl bug found during testing * [EC-599] Add FormSelectionList unit tests * [EC-599] Fix unit test and linter * [EC-599] Update Enums to Pascal case * [EC-599] Undo change to Enum values * [EC-7] fix: broken build * [EC-646] Org Admin Vault Refresh November Release Prep (#3913) * [EC-646] Remove links from Manage component These links are no longer necessary as they are now located in the new OAVR tabs. * [EC-646] Re-introduce the canAccessManageTab helper * [EC-646] Re-introduce /manage route in Organization routing module - Add the parent /manage route - Add child routes for collections, people, and groups * [EC-646] Adjust Org admin tabs Re-introduce the Manage tab and remove Groups and Members tabs. * [EC-646] Change Members title back to People * [EC-646] Move missing billing components Some billing components were in the org settings module and needed to be moved the org billing module * [EC-646] Fix import file upload button -Update to use click event handler and tailwind class to hide input. Avoids inline styles/js blocked by CSP - Fix broken async pipe * [EC-646] Fix groups and people page overflow Remove the container and page-content wrapper as the pages are no longer on their own tab * [EC-646] Change People to Members Change the text regarding managing members from People to Members to more closely follow changes coming later in the OAVR. Also update the URL to use /manage/members * [EC-646] Cherry-pickae39afeto fix tab text color * [EC-646] Fix org routing permissions helpers - Add canAccessVaultTab helper - Update canAccessOrgAdmin include check for vault tab access - Simplify canManageCollections * [EC-646] Fix Manage tab conditional logic - Add *ngIf condition for rendering Manage tab - Re-introduce dynamic route for Manage tab * Revert "[EC-14] Refactor vault filter (#3440)" (#3926) This reverts commit4d83b81d82. * Remove old reference to bit-submit-button that no longer exists (#3927) * [EC-593] Top align event logs row content (#3813) * [EC-593] Top align event log row contents * [EC-593] Prevent event log timestamp from wrapping * [EC-593] Add alignContent input to bitRow directive * [EC-593] Remove ineffective inline styles (CSP) * [EC-593] Remove templated tailwind classes Tailwind minimizes the bundled stylesheet by removing classes that aren't used in code. Using a string template for the classes causes those classes to be ignored. * [EC-593] Introduce alignContent input to table story * [EC-657] Hide Billing History and Payment Method for selfhosted orgs (#3935) Signed-off-by: Jacob Fink <jfink@bitwarden.com> Co-authored-by: Vincent Salucci <vincesalucci21@gmail.com> Co-authored-by: Andreas Coroiu <andreas.coroiu@gmail.com> Co-authored-by: Jake Fink <jfink@bitwarden.com> Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Co-authored-by: Thomas Rittson <eliykat@users.noreply.github.com>
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
import { Directive, Input, OnInit, Self } from "@angular/core";
|
||||
import { ControlValueAccessor, UntypedFormControl, NgControl, Validators } from "@angular/forms";
|
||||
|
||||
import { dirtyRequired } from "@bitwarden/angular/validators/dirty.validator";
|
||||
|
||||
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
|
||||
@Directive()
|
||||
export abstract class BaseCvaComponent implements ControlValueAccessor, OnInit {
|
||||
@@ -15,10 +13,7 @@ export abstract class BaseCvaComponent implements ControlValueAccessor, OnInit {
|
||||
}
|
||||
|
||||
get isRequired() {
|
||||
return (
|
||||
this.controlDir.control.hasValidator(Validators.required) ||
|
||||
this.controlDir.control.hasValidator(dirtyRequired)
|
||||
);
|
||||
return this.controlDir.control.hasValidator(Validators.required);
|
||||
}
|
||||
|
||||
@Input() label: string;
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
<div class="form-group">
|
||||
<label>{{ label }}</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" readonly [value]="controlValue" />
|
||||
<div class="input-group-append" *ngIf="showLaunch">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{ 'launch' | i18n }}"
|
||||
(click)="launchUri(controlValue)"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-external-link" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="input-group-append" *ngIf="showCopy">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{ 'copyValue' | i18n }}"
|
||||
(click)="copy(controlValue)"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,25 +0,0 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
|
||||
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
|
||||
@Component({
|
||||
selector: "app-input-text-readonly",
|
||||
templateUrl: "input-text-readonly.component.html",
|
||||
})
|
||||
export class InputTextReadOnlyComponent {
|
||||
@Input() controlValue: string;
|
||||
@Input() label: string;
|
||||
@Input() showCopy = true;
|
||||
@Input() showLaunch = false;
|
||||
|
||||
constructor(private platformUtilsService: PlatformUtilsService) {}
|
||||
|
||||
copy(value: string) {
|
||||
this.platformUtilsService.copyToClipboard(value);
|
||||
}
|
||||
|
||||
launchUri(url: string) {
|
||||
this.platformUtilsService.launchUri(url);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
<div class="form-group">
|
||||
<label [attr.for]="controlId">
|
||||
{{ label }}
|
||||
<small *ngIf="isRequired" class="text-muted form-text d-inline"
|
||||
>({{ "required" | i18n }})</small
|
||||
>
|
||||
</label>
|
||||
<input
|
||||
[formControl]="internalControl"
|
||||
class="form-control"
|
||||
[attr.id]="controlId"
|
||||
[attr.aria-describedby]="describedById"
|
||||
[attr.aria-invalid]="controlDir.control.invalid"
|
||||
(blur)="onBlurInternal()"
|
||||
/>
|
||||
<div *ngIf="showDescribedBy" [attr.id]="describedById">
|
||||
<small
|
||||
*ngIf="helperText != null && !controlDir.control.hasError(helperTextSameAsError)"
|
||||
class="form-text text-muted"
|
||||
>
|
||||
{{ helperText }}
|
||||
</small>
|
||||
<small class="error-inline" *ngIf="controlDir.control.hasError('required')" role="alert">
|
||||
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ "error" | i18n }}:</span>
|
||||
{{
|
||||
controlDir.control.hasError(helperTextSameAsError)
|
||||
? helperText
|
||||
: ("fieldRequiredError" | i18n: label)
|
||||
}}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,48 +0,0 @@
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
|
||||
import { BaseCvaComponent } from "./base-cva.component";
|
||||
|
||||
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
|
||||
@Component({
|
||||
selector: "app-input-text[label][controlId]",
|
||||
templateUrl: "input-text.component.html",
|
||||
})
|
||||
export class InputTextComponent extends BaseCvaComponent implements OnInit {
|
||||
@Input() helperTextSameAsError: string;
|
||||
@Input() requiredErrorMessage: string;
|
||||
@Input() stripSpaces = false;
|
||||
|
||||
transformValue: (value: string) => string = null;
|
||||
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
if (this.stripSpaces) {
|
||||
this.transformValue = this.doStripSpaces;
|
||||
}
|
||||
}
|
||||
|
||||
writeValue(value: string) {
|
||||
this.internalControl.setValue(value == null ? "" : value);
|
||||
}
|
||||
|
||||
protected onValueChangesInternal: any = (value: string) => {
|
||||
let newValue = value;
|
||||
if (this.transformValue != null) {
|
||||
newValue = this.transformValue(value);
|
||||
this.internalControl.setValue(newValue, { emitEvent: false });
|
||||
}
|
||||
this.onChange(newValue);
|
||||
};
|
||||
|
||||
protected onValueChangeInternal(value: string) {
|
||||
let newValue = value;
|
||||
if (this.transformValue != null) {
|
||||
newValue = this.transformValue(value);
|
||||
this.internalControl.setValue(newValue, { emitEvent: false });
|
||||
}
|
||||
}
|
||||
|
||||
private doStripSpaces(value: string) {
|
||||
return value.replace(/ /g, "");
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<div class="form-group">
|
||||
<label [attr.for]="controlId">
|
||||
{{ label }}
|
||||
<small *ngIf="isRequired" class="text-muted form-text d-inline"
|
||||
>({{ "required" | i18n }})</small
|
||||
>
|
||||
</label>
|
||||
<select
|
||||
class="form-control"
|
||||
[attr.id]="controlId"
|
||||
[attr.aria-invalid]="controlDir.control.invalid"
|
||||
[formControl]="internalControl"
|
||||
(blur)="onBlurInternal()"
|
||||
>
|
||||
<option *ngFor="let o of selectOptions" [ngValue]="o.value" disabled="{{ o.disabled }}">
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -1,14 +0,0 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
|
||||
import { SelectOptions } from "@bitwarden/angular/interfaces/selectOptions";
|
||||
|
||||
import { BaseCvaComponent } from "./base-cva.component";
|
||||
|
||||
/** For use in the SSO Config Form only - will be deprecated by the Component Library */
|
||||
@Component({
|
||||
selector: "app-select",
|
||||
templateUrl: "select.component.html",
|
||||
})
|
||||
export class SelectComponent extends BaseCvaComponent {
|
||||
@Input() selectOptions: SelectOptions[];
|
||||
}
|
||||
@@ -35,6 +35,14 @@
|
||||
[helperText]="'allowSsoDesc' | i18n"
|
||||
></app-input-checkbox>
|
||||
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "ssoIdentifier" | i18n }}</bit-label>
|
||||
<input bitInput type="text" [formControl]="ssoIdentifier" />
|
||||
<bit-hint>{{ "ssoIdentifierHint" | i18n }}</bit-hint>
|
||||
</bit-form-field>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{ "memberDecryptionOption" | i18n }}</label>
|
||||
<div class="form-check form-check-block">
|
||||
@@ -80,66 +88,55 @@
|
||||
{{ "keyConnectorWarning" | i18n }}
|
||||
</app-callout>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="keyConnectorUrl">
|
||||
{{ "keyConnectorUrl" | i18n }}
|
||||
<small class="text-muted form-text d-inline">({{ "required" | i18n }})</small>
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<input
|
||||
class="form-control"
|
||||
formControlName="keyConnectorUrl"
|
||||
id="keyConnectorUrl"
|
||||
aria-describedby="keyConnectorUrlDesc"
|
||||
(change)="haveTestedKeyConnector = false"
|
||||
appInputStripSpaces
|
||||
appA11yInvalid
|
||||
/>
|
||||
<div class="input-group-append">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-secondary"
|
||||
(click)="validateKeyConnectorUrl()"
|
||||
[disabled]="!enableTestKeyConnector"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
*ngIf="keyConnectorUrl.pending"
|
||||
></i>
|
||||
<span *ngIf="!keyConnectorUrl.pending">
|
||||
{{ "keyConnectorTest" | i18n }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="haveTestedKeyConnector" id="keyConnectorUrlDesc" aria-live="polite">
|
||||
<small
|
||||
class="error-inline"
|
||||
*ngIf="keyConnectorUrl.hasError('invalidUrl'); else keyConnectorSuccess"
|
||||
>
|
||||
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ "error" | i18n }}:</span>
|
||||
{{ "keyConnectorTestFail" | i18n }}
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "keyConnectorUrl" | i18n }}</bit-label>
|
||||
<input
|
||||
bitInput
|
||||
type="text"
|
||||
required
|
||||
formControlName="keyConnectorUrl"
|
||||
appInputStripSpaces
|
||||
(input)="haveTestedKeyConnector = false"
|
||||
/>
|
||||
<button
|
||||
bitSuffix
|
||||
bitButton
|
||||
[disabled]="!enableTestKeyConnector"
|
||||
type="button"
|
||||
(click)="validateKeyConnectorUrl()"
|
||||
>
|
||||
<i
|
||||
class="bwi bwi-spinner bwi-spin"
|
||||
title="{{ 'loading' | i18n }}"
|
||||
aria-hidden="true"
|
||||
*ngIf="keyConnectorUrl.pending"
|
||||
></i>
|
||||
<span *ngIf="!keyConnectorUrl.pending">
|
||||
{{ "keyConnectorTest" | i18n }}
|
||||
</span>
|
||||
</button>
|
||||
<bit-hint
|
||||
aria-live="polite"
|
||||
*ngIf="haveTestedKeyConnector && !keyConnectorUrl.hasError('invalidUrl')"
|
||||
>
|
||||
<small class="text-success">
|
||||
<i class="bwi bwi-check-circle" aria-hidden="true"></i>
|
||||
{{ "keyConnectorTestSuccess" | i18n }}
|
||||
</small>
|
||||
<ng-template #keyConnectorSuccess>
|
||||
<small class="text-success">
|
||||
<i class="bwi bwi-check-circle" aria-hidden="true"></i>
|
||||
{{ "keyConnectorTestSuccess" | i18n }}
|
||||
</small>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</bit-hint>
|
||||
</bit-form-field>
|
||||
</ng-container>
|
||||
|
||||
<app-select
|
||||
controlId="type"
|
||||
[label]="'type' | i18n"
|
||||
[selectOptions]="ssoTypeOptions"
|
||||
formControlName="configType"
|
||||
>
|
||||
</app-select>
|
||||
<hr />
|
||||
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "type" | i18n }}</bit-label>
|
||||
<select bitInput formControlName="configType">
|
||||
<option *ngFor="let o of ssoTypeOptions" [ngValue]="o.value" disabled="{{ o.disabled }}">
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
</bit-form-field>
|
||||
</ng-container>
|
||||
|
||||
<!-- OIDC -->
|
||||
@@ -150,52 +147,67 @@
|
||||
<div class="config-section">
|
||||
<h2 class="secondary-header">{{ "openIdConnectConfig" | i18n }}</h2>
|
||||
|
||||
<app-input-text-readonly
|
||||
[label]="'callbackPath' | i18n"
|
||||
[controlValue]="callbackPath"
|
||||
></app-input-text-readonly>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "callbackPath" | i18n }}</bit-label>
|
||||
<input bitInput disabled [value]="callbackPath" />
|
||||
<button
|
||||
bitButton
|
||||
bitSuffix
|
||||
type="button"
|
||||
[appCopyClick]="callbackPath"
|
||||
[appA11yTitle]="'copyValue' | i18n"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</bit-form-field>
|
||||
|
||||
<app-input-text-readonly
|
||||
[label]="'signedOutCallbackPath' | i18n"
|
||||
[controlValue]="signedOutCallbackPath"
|
||||
></app-input-text-readonly>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "signedOutCallbackPath" | i18n }}</bit-label>
|
||||
<input bitInput disabled [value]="signedOutCallbackPath" />
|
||||
<button
|
||||
bitButton
|
||||
bitSuffix
|
||||
type="button"
|
||||
[appCopyClick]="signedOutCallbackPath"
|
||||
[appA11yTitle]="'copyValue' | i18n"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</bit-form-field>
|
||||
|
||||
<app-input-text
|
||||
[label]="'authority' | i18n"
|
||||
controlId="authority"
|
||||
[stripSpaces]="true"
|
||||
formControlName="authority"
|
||||
></app-input-text>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "authority" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="authority" appInputStripSpaces />
|
||||
</bit-form-field>
|
||||
|
||||
<app-input-text
|
||||
[label]="'clientId' | i18n"
|
||||
controlId="clientId"
|
||||
[stripSpaces]="true"
|
||||
formControlName="clientId"
|
||||
></app-input-text>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "clientId" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="clientId" appInputStripSpaces />
|
||||
</bit-form-field>
|
||||
|
||||
<app-input-text
|
||||
[label]="'clientSecret' | i18n"
|
||||
controlId="clientSecret"
|
||||
[stripSpaces]="true"
|
||||
formControlName="clientSecret"
|
||||
></app-input-text>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "clientSecret" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="clientSecret" appInputStripSpaces />
|
||||
</bit-form-field>
|
||||
|
||||
<app-input-text
|
||||
[label]="'metadataAddress' | i18n"
|
||||
controlId="metadataAddress"
|
||||
[stripSpaces]="true"
|
||||
[helperText]="'openIdAuthorityRequired' | i18n"
|
||||
formControlName="metadataAddress"
|
||||
></app-input-text>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "metadataAddress" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="metadataAddress" appInputStripSpaces />
|
||||
<bit-hint>{{ "openIdAuthorityRequired" | i18n }}</bit-hint>
|
||||
</bit-form-field>
|
||||
|
||||
<app-select
|
||||
controlId="redirectBehavior"
|
||||
[label]="'oidcRedirectBehavior' | i18n"
|
||||
[selectOptions]="connectRedirectOptions"
|
||||
formControlName="redirectBehavior"
|
||||
>
|
||||
</app-select>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "oidcRedirectBehavior" | i18n }}</bit-label>
|
||||
<select bitInput formControlName="redirectBehavior">
|
||||
<option
|
||||
*ngFor="let o of connectRedirectOptions"
|
||||
[ngValue]="o.value"
|
||||
disabled="{{ o.disabled }}"
|
||||
>
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
</bit-form-field>
|
||||
|
||||
<app-input-checkbox
|
||||
controlId="getClaimsFromUserInfoEndpoint"
|
||||
@@ -231,47 +243,41 @@
|
||||
</button>
|
||||
</div>
|
||||
<div id="customizations" [hidden]="!showOpenIdCustomizations">
|
||||
<app-input-text
|
||||
[label]="'additionalScopes' | i18n"
|
||||
controlId="additionalScopes"
|
||||
[helperText]="'separateMultipleWithComma' | i18n"
|
||||
formControlName="additionalScopes"
|
||||
></app-input-text>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "additionalScopes" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="additionalScopes" />
|
||||
<bit-hint>{{ "separateMultipleWithComma" | i18n }}</bit-hint>
|
||||
</bit-form-field>
|
||||
|
||||
<app-input-text
|
||||
[label]="'additionalUserIdClaimTypes' | i18n"
|
||||
controlId="additionalUserIdClaimTypes"
|
||||
[helperText]="'separateMultipleWithComma' | i18n"
|
||||
formControlName="additionalUserIdClaimTypes"
|
||||
></app-input-text>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "additionalUserIdClaimTypes" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="additionalUserIdClaimTypes" />
|
||||
<bit-hint>{{ "separateMultipleWithComma" | i18n }}</bit-hint>
|
||||
</bit-form-field>
|
||||
|
||||
<app-input-text
|
||||
[label]="'additionalEmailClaimTypes' | i18n"
|
||||
controlId="additionalEmailClaimTypes"
|
||||
[helperText]="'separateMultipleWithComma' | i18n"
|
||||
formControlName="additionalEmailClaimTypes"
|
||||
></app-input-text>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "additionalEmailClaimTypes" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="additionalEmailClaimTypes" />
|
||||
<bit-hint>{{ "separateMultipleWithComma" | i18n }}</bit-hint>
|
||||
</bit-form-field>
|
||||
|
||||
<app-input-text
|
||||
[label]="'additionalNameClaimTypes' | i18n"
|
||||
controlId="additionalNameClaimTypes"
|
||||
[helperText]="'separateMultipleWithComma' | i18n"
|
||||
formControlName="additionalNameClaimTypes"
|
||||
></app-input-text>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "additionalNameClaimTypes" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="additionalNameClaimTypes" />
|
||||
<bit-hint>{{ "separateMultipleWithComma" | i18n }}</bit-hint>
|
||||
</bit-form-field>
|
||||
|
||||
<app-input-text
|
||||
[label]="'acrValues' | i18n"
|
||||
controlId="acrValues"
|
||||
helperText="acr_values"
|
||||
formControlName="acrValues"
|
||||
></app-input-text>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "acrValues" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="acrValues" />
|
||||
<bit-hint>acr_values</bit-hint>
|
||||
</bit-form-field>
|
||||
|
||||
<app-input-text
|
||||
[label]="'expectedReturnAcrValue' | i18n"
|
||||
controlId="expectedReturnAcrValue"
|
||||
helperText="acr_validation"
|
||||
formControlName="expectedReturnAcrValue"
|
||||
></app-input-text>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "expectedReturnAcrValue" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="expectedReturnAcrValue" />
|
||||
<bit-hint>acr_validaton</bit-hint>
|
||||
</bit-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -282,53 +288,108 @@
|
||||
<div class="config-section">
|
||||
<h2 class="secondary-header">{{ "samlSpConfig" | i18n }}</h2>
|
||||
|
||||
<app-input-text-readonly
|
||||
[label]="'spEntityId' | i18n"
|
||||
[controlValue]="spEntityId"
|
||||
></app-input-text-readonly>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "spEntityId" | i18n }}</bit-label>
|
||||
<input bitInput disabled [value]="spEntityId" />
|
||||
<button
|
||||
bitButton
|
||||
bitSuffix
|
||||
type="button"
|
||||
[appCopyClick]="spEntityId"
|
||||
[appA11yTitle]="'copyValue' | i18n"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</bit-form-field>
|
||||
|
||||
<app-input-text-readonly
|
||||
[label]="'spMetadataUrl' | i18n"
|
||||
[controlValue]="spMetadataUrl"
|
||||
[showLaunch]="true"
|
||||
></app-input-text-readonly>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "spMetadataUrl" | i18n }}</bit-label>
|
||||
<input bitInput disabled [value]="spMetadataUrl" />
|
||||
<button
|
||||
bitButton
|
||||
bitSuffix
|
||||
type="button"
|
||||
[appLaunchClick]="spMetadataUrl"
|
||||
[appA11yTitle]="'launch' | i18n"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-external-link" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button
|
||||
bitButton
|
||||
bitSuffix
|
||||
type="button"
|
||||
[appCopyClick]="spMetadataUrl"
|
||||
[appA11yTitle]="'copyValue' | i18n"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</bit-form-field>
|
||||
|
||||
<app-input-text-readonly
|
||||
[label]="'spAcsUrl' | i18n"
|
||||
[controlValue]="spAcsUrl"
|
||||
></app-input-text-readonly>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "spAcsUrl" | i18n }}</bit-label>
|
||||
<input bitInput disabled [value]="spAcsUrl" />
|
||||
<button
|
||||
bitButton
|
||||
bitSuffix
|
||||
type="button"
|
||||
[appCopyClick]="spAcsUrl"
|
||||
[appA11yTitle]="'copyValue' | i18n"
|
||||
>
|
||||
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</bit-form-field>
|
||||
|
||||
<app-select
|
||||
controlId="spNameIdFormat"
|
||||
[label]="'spNameIdFormat' | i18n"
|
||||
[selectOptions]="saml2NameIdFormatOptions"
|
||||
formControlName="spNameIdFormat"
|
||||
>
|
||||
</app-select>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "spNameIdFormat" | i18n }}</bit-label>
|
||||
<select bitInput formControlName="spNameIdFormat">
|
||||
<option
|
||||
*ngFor="let o of saml2NameIdFormatOptions"
|
||||
[ngValue]="o.value"
|
||||
disabled="{{ o.disabled }}"
|
||||
>
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
</bit-form-field>
|
||||
|
||||
<app-select
|
||||
controlId="spOutboundSigningAlgorithm"
|
||||
[label]="'spOutboundSigningAlgorithm' | i18n"
|
||||
[selectOptions]="samlSigningAlgorithmOptions"
|
||||
formControlName="spOutboundSigningAlgorithm"
|
||||
>
|
||||
</app-select>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "spOutboundSigningAlgorithm" | i18n }}</bit-label>
|
||||
<select bitInput formControlName="spOutboundSigningAlgorithm">
|
||||
<option
|
||||
*ngFor="let o of samlSigningAlgorithmOptions"
|
||||
[ngValue]="o.value"
|
||||
disabled="{{ o.disabled }}"
|
||||
>
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
</bit-form-field>
|
||||
|
||||
<app-select
|
||||
controlId="spSigningBehavior"
|
||||
[label]="'spSigningBehavior' | i18n"
|
||||
[selectOptions]="saml2SigningBehaviourOptions"
|
||||
formControlName="spSigningBehavior"
|
||||
>
|
||||
</app-select>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "spSigningBehavior" | i18n }}</bit-label>
|
||||
<select bitInput formControlName="spSigningBehavior">
|
||||
<option
|
||||
*ngFor="let o of saml2SigningBehaviourOptions"
|
||||
[ngValue]="o.value"
|
||||
disabled="{{ o.disabled }}"
|
||||
>
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
</bit-form-field>
|
||||
|
||||
<app-select
|
||||
controlId="spMinIncomingSigningAlgorithm"
|
||||
[label]="'spMinIncomingSigningAlgorithm' | i18n"
|
||||
[selectOptions]="samlSigningAlgorithmOptions"
|
||||
formControlName="spMinIncomingSigningAlgorithm"
|
||||
>
|
||||
</app-select>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "spMinIncomingSigningAlgorithm" | i18n }}</bit-label>
|
||||
<select bitInput formControlName="spMinIncomingSigningAlgorithm">
|
||||
<option
|
||||
*ngFor="let o of samlSigningAlgorithmOptions"
|
||||
[ngValue]="o.value"
|
||||
disabled="{{ o.disabled }}"
|
||||
>
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
</bit-form-field>
|
||||
|
||||
<app-input-checkbox
|
||||
controlId="spWantAssertionsSigned"
|
||||
@@ -347,67 +408,62 @@
|
||||
<div class="config-section">
|
||||
<h2 class="secondary-header">{{ "samlIdpConfig" | i18n }}</h2>
|
||||
|
||||
<app-input-text
|
||||
[label]="'idpEntityId' | i18n"
|
||||
controlId="idpEntityId"
|
||||
formControlName="idpEntityId"
|
||||
></app-input-text>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "idpEntityId" | i18n }}</bit-label>
|
||||
<input bitInput type="text" formControlName="idpEntityId" />
|
||||
</bit-form-field>
|
||||
|
||||
<app-select
|
||||
controlId="idpBindingType"
|
||||
[label]="'idpBindingType' | i18n"
|
||||
[selectOptions]="saml2BindingTypeOptions"
|
||||
formControlName="idpBindingType"
|
||||
>
|
||||
</app-select>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "idpBindingType" | i18n }}</bit-label>
|
||||
<select bitInput formControlName="idpBindingType">
|
||||
<option
|
||||
*ngFor="let o of saml2BindingTypeOptions"
|
||||
[ngValue]="o.value"
|
||||
disabled="{{ o.disabled }}"
|
||||
>
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
</bit-form-field>
|
||||
|
||||
<app-input-text
|
||||
[label]="'idpSingleSignOnServiceUrl' | i18n"
|
||||
controlId="idpSingleSignOnServiceUrl"
|
||||
[helperText]="'idpSingleSignOnServiceUrlRequired' | i18n"
|
||||
[stripSpaces]="true"
|
||||
formControlName="idpSingleSignOnServiceUrl"
|
||||
></app-input-text>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "idpSingleSignOnServiceUrl" | i18n }}</bit-label>
|
||||
<input
|
||||
bitInput
|
||||
type="text"
|
||||
formControlName="idpSingleSignOnServiceUrl"
|
||||
appInputStripSpaces
|
||||
/>
|
||||
<bit-hint>{{ "idpSingleSignOnServiceUrlRequired" | i18n }}</bit-hint>
|
||||
</bit-form-field>
|
||||
|
||||
<app-input-text
|
||||
[label]="'idpSingleLogoutServiceUrl' | i18n"
|
||||
controlId="idpSingleLogoutServiceUrl"
|
||||
[stripSpaces]="true"
|
||||
formControlName="idpSingleLogoutServiceUrl"
|
||||
></app-input-text>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "idpSingleLogoutServiceUrl" | i18n }}</bit-label>
|
||||
<input
|
||||
bitInput
|
||||
type="text"
|
||||
formControlName="idpSingleLogoutServiceUrl"
|
||||
appInputStripSpaces
|
||||
/>
|
||||
</bit-form-field>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="idpX509PublicCert">
|
||||
{{ "idpX509PublicCert" | i18n }}
|
||||
<small class="text-muted form-text d-inline">({{ "required" | i18n }})</small>
|
||||
</label>
|
||||
<textarea
|
||||
formControlName="idpX509PublicCert"
|
||||
class="form-control form-control-sm text-monospace"
|
||||
rows="6"
|
||||
id="idpX509PublicCert"
|
||||
appA11yInvalid
|
||||
aria-describedby="idpX509PublicCertDesc"
|
||||
></textarea>
|
||||
<small
|
||||
id="idpX509PublicCertDesc"
|
||||
class="error-inline"
|
||||
role="alert"
|
||||
*ngIf="samlForm.get('idpX509PublicCert').hasError('required')"
|
||||
>
|
||||
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ "error" | i18n }}:</span>
|
||||
{{ "fieldRequiredError" | i18n: ("idpX509PublicCert" | i18n) }}
|
||||
</small>
|
||||
</div>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "idpX509PublicCert" | i18n }}</bit-label>
|
||||
<textarea bitInput rows="6" formControlName="idpX509PublicCert"></textarea>
|
||||
</bit-form-field>
|
||||
|
||||
<app-select
|
||||
controlId="idpOutboundSigningAlgorithm"
|
||||
[label]="'idpOutboundSigningAlgorithm' | i18n"
|
||||
[selectOptions]="samlSigningAlgorithmOptions"
|
||||
formControlName="idpOutboundSigningAlgorithm"
|
||||
>
|
||||
</app-select>
|
||||
<bit-form-field>
|
||||
<bit-label>{{ "idpOutboundSigningAlgorithm" | i18n }}</bit-label>
|
||||
<select bitInput formControlName="idpOutboundSigningAlgorithm">
|
||||
<option
|
||||
*ngFor="let o of samlSigningAlgorithmOptions"
|
||||
[ngValue]="o.value"
|
||||
disabled="{{ o.disabled }}"
|
||||
>
|
||||
{{ o.name }}
|
||||
</option>
|
||||
</select>
|
||||
</bit-form-field>
|
||||
|
||||
<!--TODO: Uncomment once Unsolicited IdP Response is supported-->
|
||||
<!-- <app-input-checkbox
|
||||
@@ -430,19 +486,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
|
||||
<i class="bwi bwi-spinner bwi-spin" title="{{ 'loading' | i18n }}" aria-hidden="true"></i>
|
||||
<span>{{ "save" | i18n }}</span>
|
||||
<button type="submit" buttonType="primary" bitButton [loading]="form.loading">
|
||||
{{ "save" | i18n }}
|
||||
</button>
|
||||
<div
|
||||
id="errorSummary"
|
||||
class="error-summary text-danger"
|
||||
*ngIf="this.getErrorCount(ssoConfigForm) as errorCount"
|
||||
>
|
||||
<i class="bwi bwi-exclamation-circle" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{ "error" | i18n }}:</span>
|
||||
{{
|
||||
(errorCount === 1 ? "formErrorSummarySingle" : "formErrorSummaryPlural") | i18n: errorCount
|
||||
}}
|
||||
</div>
|
||||
<bit-error-summary [formGroup]="ssoConfigForm"></bit-error-summary>
|
||||
</form>
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { AbstractControl, UntypedFormBuilder, UntypedFormGroup } from "@angular/forms";
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import {
|
||||
AbstractControl,
|
||||
FormBuilder,
|
||||
FormControl,
|
||||
UntypedFormGroup,
|
||||
Validators,
|
||||
} from "@angular/forms";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { concatMap, Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { SelectOptions } from "@bitwarden/angular/interfaces/selectOptions";
|
||||
import { dirtyRequired } from "@bitwarden/angular/validators/dirty.validator";
|
||||
import { ControlsOf } from "@bitwarden/angular/types/controls-of";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction";
|
||||
@@ -29,8 +36,7 @@ const defaultSigningAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha2
|
||||
selector: "app-org-manage-sso",
|
||||
templateUrl: "sso.component.html",
|
||||
})
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||
export class SsoComponent implements OnInit {
|
||||
export class SsoComponent implements OnInit, OnDestroy {
|
||||
readonly ssoType = SsoType;
|
||||
|
||||
readonly ssoTypeOptions: SelectOptions[] = [
|
||||
@@ -75,6 +81,8 @@ export class SsoComponent implements OnInit {
|
||||
{ name: "Form POST", value: OpenIdConnectRedirectBehavior.FormPost },
|
||||
];
|
||||
|
||||
private destory$ = new Subject<void>();
|
||||
|
||||
showOpenIdCustomizations = false;
|
||||
|
||||
loading = true;
|
||||
@@ -89,62 +97,69 @@ export class SsoComponent implements OnInit {
|
||||
spMetadataUrl: string;
|
||||
spAcsUrl: string;
|
||||
|
||||
enabled = this.formBuilder.control(false);
|
||||
private enabled = this.formBuilder.control(false);
|
||||
|
||||
openIdForm = this.formBuilder.group(
|
||||
private ssoIdentifier = this.formBuilder.control("", {
|
||||
validators: [Validators.maxLength(50), Validators.required],
|
||||
});
|
||||
|
||||
private openIdForm = this.formBuilder.group<ControlsOf<SsoConfigView["openId"]>>(
|
||||
{
|
||||
authority: ["", dirtyRequired],
|
||||
clientId: ["", dirtyRequired],
|
||||
clientSecret: ["", dirtyRequired],
|
||||
metadataAddress: [],
|
||||
redirectBehavior: [OpenIdConnectRedirectBehavior.RedirectGet, dirtyRequired],
|
||||
getClaimsFromUserInfoEndpoint: [],
|
||||
additionalScopes: [],
|
||||
additionalUserIdClaimTypes: [],
|
||||
additionalEmailClaimTypes: [],
|
||||
additionalNameClaimTypes: [],
|
||||
acrValues: [],
|
||||
expectedReturnAcrValue: [],
|
||||
authority: new FormControl("", Validators.required),
|
||||
clientId: new FormControl("", Validators.required),
|
||||
clientSecret: new FormControl("", Validators.required),
|
||||
metadataAddress: new FormControl(),
|
||||
redirectBehavior: new FormControl(
|
||||
OpenIdConnectRedirectBehavior.RedirectGet,
|
||||
Validators.required
|
||||
),
|
||||
getClaimsFromUserInfoEndpoint: new FormControl(),
|
||||
additionalScopes: new FormControl(),
|
||||
additionalUserIdClaimTypes: new FormControl(),
|
||||
additionalEmailClaimTypes: new FormControl(),
|
||||
additionalNameClaimTypes: new FormControl(),
|
||||
acrValues: new FormControl(),
|
||||
expectedReturnAcrValue: new FormControl(),
|
||||
},
|
||||
{
|
||||
updateOn: "blur",
|
||||
}
|
||||
);
|
||||
|
||||
samlForm = this.formBuilder.group(
|
||||
private samlForm = this.formBuilder.group<ControlsOf<SsoConfigView["saml"]>>(
|
||||
{
|
||||
spNameIdFormat: [Saml2NameIdFormat.NotConfigured],
|
||||
spOutboundSigningAlgorithm: [defaultSigningAlgorithm],
|
||||
spSigningBehavior: [Saml2SigningBehavior.IfIdpWantAuthnRequestsSigned],
|
||||
spMinIncomingSigningAlgorithm: [defaultSigningAlgorithm],
|
||||
spWantAssertionsSigned: [],
|
||||
spValidateCertificates: [],
|
||||
spNameIdFormat: new FormControl(Saml2NameIdFormat.NotConfigured),
|
||||
spOutboundSigningAlgorithm: new FormControl(defaultSigningAlgorithm),
|
||||
spSigningBehavior: new FormControl(Saml2SigningBehavior.IfIdpWantAuthnRequestsSigned),
|
||||
spMinIncomingSigningAlgorithm: new FormControl(defaultSigningAlgorithm),
|
||||
spWantAssertionsSigned: new FormControl(),
|
||||
spValidateCertificates: new FormControl(),
|
||||
|
||||
idpEntityId: ["", dirtyRequired],
|
||||
idpBindingType: [Saml2BindingType.HttpRedirect],
|
||||
idpSingleSignOnServiceUrl: [],
|
||||
idpSingleLogoutServiceUrl: [],
|
||||
idpX509PublicCert: ["", dirtyRequired],
|
||||
idpOutboundSigningAlgorithm: [defaultSigningAlgorithm],
|
||||
idpAllowUnsolicitedAuthnResponse: [],
|
||||
idpAllowOutboundLogoutRequests: [true],
|
||||
idpWantAuthnRequestsSigned: [],
|
||||
idpEntityId: new FormControl("", Validators.required),
|
||||
idpBindingType: new FormControl(Saml2BindingType.HttpRedirect),
|
||||
idpSingleSignOnServiceUrl: new FormControl(),
|
||||
idpSingleLogoutServiceUrl: new FormControl(),
|
||||
idpX509PublicCert: new FormControl("", Validators.required),
|
||||
idpOutboundSigningAlgorithm: new FormControl(defaultSigningAlgorithm),
|
||||
idpAllowUnsolicitedAuthnResponse: new FormControl(),
|
||||
idpAllowOutboundLogoutRequests: new FormControl(true),
|
||||
idpWantAuthnRequestsSigned: new FormControl(),
|
||||
},
|
||||
{
|
||||
updateOn: "blur",
|
||||
}
|
||||
);
|
||||
|
||||
ssoConfigForm = this.formBuilder.group({
|
||||
configType: [SsoType.None],
|
||||
keyConnectorEnabled: [false],
|
||||
keyConnectorUrl: [""],
|
||||
private ssoConfigForm = this.formBuilder.group<ControlsOf<SsoConfigView>>({
|
||||
configType: new FormControl(SsoType.None),
|
||||
keyConnectorEnabled: new FormControl(false),
|
||||
keyConnectorUrl: new FormControl(""),
|
||||
openId: this.openIdForm,
|
||||
saml: this.samlForm,
|
||||
});
|
||||
|
||||
constructor(
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
private formBuilder: FormBuilder,
|
||||
private route: ActivatedRoute,
|
||||
private apiService: ApiService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
@@ -154,32 +169,41 @@ export class SsoComponent implements OnInit {
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||
this.ssoConfigForm.get("configType").valueChanges.subscribe((newType: SsoType) => {
|
||||
if (newType === SsoType.OpenIdConnect) {
|
||||
this.openIdForm.enable();
|
||||
this.samlForm.disable();
|
||||
} else if (newType === SsoType.Saml2) {
|
||||
this.openIdForm.disable();
|
||||
this.samlForm.enable();
|
||||
} else {
|
||||
this.openIdForm.disable();
|
||||
this.samlForm.disable();
|
||||
}
|
||||
});
|
||||
this.ssoConfigForm
|
||||
.get("configType")
|
||||
.valueChanges.pipe(takeUntil(this.destory$))
|
||||
.subscribe((newType: SsoType) => {
|
||||
if (newType === SsoType.OpenIdConnect) {
|
||||
this.openIdForm.enable();
|
||||
this.samlForm.disable();
|
||||
} else if (newType === SsoType.Saml2) {
|
||||
this.openIdForm.disable();
|
||||
this.samlForm.enable();
|
||||
} else {
|
||||
this.openIdForm.disable();
|
||||
this.samlForm.disable();
|
||||
}
|
||||
});
|
||||
|
||||
this.samlForm
|
||||
.get("spSigningBehavior")
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
||||
.valueChanges.subscribe(() =>
|
||||
this.samlForm.get("idpX509PublicCert").updateValueAndValidity()
|
||||
);
|
||||
.valueChanges.pipe(takeUntil(this.destory$))
|
||||
.subscribe(() => this.samlForm.get("idpX509PublicCert").updateValueAndValidity());
|
||||
|
||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
|
||||
this.route.parent.parent.params.subscribe(async (params) => {
|
||||
this.organizationId = params.organizationId;
|
||||
await this.load();
|
||||
});
|
||||
this.route.params
|
||||
.pipe(
|
||||
concatMap(async (params) => {
|
||||
this.organizationId = params.organizationId;
|
||||
await this.load();
|
||||
}),
|
||||
takeUntil(this.destory$)
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destory$.next();
|
||||
this.destory$.complete();
|
||||
}
|
||||
|
||||
async load() {
|
||||
@@ -199,7 +223,8 @@ export class SsoComponent implements OnInit {
|
||||
async submit() {
|
||||
this.validateForm(this.ssoConfigForm);
|
||||
|
||||
if (this.ssoConfigForm.get("keyConnectorEnabled").value) {
|
||||
if (this.ssoConfigForm.value.keyConnectorEnabled) {
|
||||
this.haveTestedKeyConnector = false;
|
||||
await this.validateKeyConnectorUrl();
|
||||
}
|
||||
|
||||
@@ -210,7 +235,8 @@ export class SsoComponent implements OnInit {
|
||||
|
||||
const request = new OrganizationSsoRequest();
|
||||
request.enabled = this.enabled.value;
|
||||
request.data = SsoConfigApi.fromView(this.ssoConfigForm.value as SsoConfigView);
|
||||
request.identifier = this.ssoIdentifier.value;
|
||||
request.data = SsoConfigApi.fromView(this.ssoConfigForm.getRawValue());
|
||||
|
||||
this.formPromise = this.organizationApiService.updateSso(this.organizationId, request);
|
||||
|
||||
@@ -237,7 +263,7 @@ export class SsoComponent implements OnInit {
|
||||
this.keyConnectorUrl.updateValueAndValidity();
|
||||
} catch {
|
||||
this.keyConnectorUrl.setErrors({
|
||||
invalidUrl: true,
|
||||
invalidUrl: { message: this.i18nService.t("keyConnectorTestFail") },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -294,6 +320,7 @@ export class SsoComponent implements OnInit {
|
||||
|
||||
private populateForm(ssoSettings: OrganizationSsoResponse) {
|
||||
this.enabled.setValue(ssoSettings.enabled);
|
||||
this.ssoIdentifier.setValue(ssoSettings.identifier);
|
||||
if (ssoSettings.data != null) {
|
||||
const ssoConfigView = new SsoConfigView(ssoSettings.data);
|
||||
this.ssoConfigForm.patchValue(ssoConfigView);
|
||||
|
||||
@@ -2,11 +2,11 @@ import { NgModule } from "@angular/core";
|
||||
import { RouterModule, Routes } from "@angular/router";
|
||||
|
||||
import { AuthGuard } from "@bitwarden/angular/guards/auth.guard";
|
||||
import { canAccessManageTab } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
import { canAccessSettingsTab } from "@bitwarden/common/abstractions/organization/organization.service.abstraction";
|
||||
import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||
import { OrganizationPermissionsGuard } from "@bitwarden/web-vault/app/organizations/guards/org-permissions.guard";
|
||||
import { OrganizationLayoutComponent } from "@bitwarden/web-vault/app/organizations/layouts/organization-layout.component";
|
||||
import { ManageComponent } from "@bitwarden/web-vault/app/organizations/manage/manage.component";
|
||||
import { SettingsComponent } from "@bitwarden/web-vault/app/organizations/settings/settings.component";
|
||||
|
||||
import { ScimComponent } from "./manage/scim.component";
|
||||
import { SsoComponent } from "./manage/sso.component";
|
||||
@@ -18,11 +18,11 @@ const routes: Routes = [
|
||||
canActivate: [AuthGuard, OrganizationPermissionsGuard],
|
||||
children: [
|
||||
{
|
||||
path: "manage",
|
||||
component: ManageComponent,
|
||||
path: "settings",
|
||||
component: SettingsComponent,
|
||||
canActivate: [OrganizationPermissionsGuard],
|
||||
data: {
|
||||
organizationPermissions: canAccessManageTab,
|
||||
organizationPermissions: canAccessSettingsTab,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
|
||||
@@ -3,24 +3,12 @@ import { NgModule } from "@angular/core";
|
||||
import { SharedModule } from "@bitwarden/web-vault/app/shared/shared.module";
|
||||
|
||||
import { InputCheckboxComponent } from "./components/input-checkbox.component";
|
||||
import { InputTextReadOnlyComponent } from "./components/input-text-readonly.component";
|
||||
import { InputTextComponent } from "./components/input-text.component";
|
||||
import { SelectComponent } from "./components/select.component";
|
||||
import { ScimComponent } from "./manage/scim.component";
|
||||
import { SsoComponent } from "./manage/sso.component";
|
||||
import { OrganizationsRoutingModule } from "./organizations-routing.module";
|
||||
|
||||
// Form components are for use in the SSO Configuration Form only and should not be exported for use elsewhere.
|
||||
// They will be deprecated by the Component Library.
|
||||
@NgModule({
|
||||
imports: [SharedModule, OrganizationsRoutingModule],
|
||||
declarations: [
|
||||
InputCheckboxComponent,
|
||||
InputTextComponent,
|
||||
InputTextReadOnlyComponent,
|
||||
SelectComponent,
|
||||
SsoComponent,
|
||||
ScimComponent,
|
||||
],
|
||||
declarations: [InputCheckboxComponent, SsoComponent, ScimComponent],
|
||||
})
|
||||
export class OrganizationsModule {}
|
||||
|
||||
Reference in New Issue
Block a user