mirror of
https://github.com/bitwarden/browser.git
synced 2025-04-07 18:57:06 +02:00
Remove Business Portal and add SSO configuration (#1213)
This commit is contained in:
parent
4ff38c7148
commit
3eda0aa2cd
@ -52,7 +52,6 @@ You can also manually adjusting your API endpoint settings by adding `config/loc
|
||||
"proxyIdentity": "http://your-identity-url",
|
||||
"proxyEvents": "http://your-events-url",
|
||||
"proxyNotifications": "http://your-notifications-url",
|
||||
"proxyPortal": "http://your-portal-url",
|
||||
"allowedHosts": ["hostnames-to-allow-in-webpack"],
|
||||
"urls": {
|
||||
|
||||
|
@ -9,13 +9,14 @@ import { RouterModule } from '@angular/router';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { OrganizationsModule } from './organizations/organizations.module';
|
||||
import { DisablePersonalVaultExportPolicyComponent } from './policies/disable-personal-vault-export.component';
|
||||
import { MaximumVaultTimeoutPolicyComponent } from './policies/maximum-vault-timeout.component';
|
||||
|
||||
import { OssRoutingModule } from 'src/app/oss-routing.module';
|
||||
import { OssModule } from 'src/app/oss.module';
|
||||
import { ServicesModule } from 'src/app/services/services.module';
|
||||
|
||||
import { WildcardRoutingModule } from 'src/app/wildcard-routing.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -29,7 +30,9 @@ import { ServicesModule } from 'src/app/services/services.module';
|
||||
DragDropModule,
|
||||
AppRoutingModule,
|
||||
OssRoutingModule,
|
||||
OrganizationsModule,
|
||||
RouterModule,
|
||||
WildcardRoutingModule, // Needs to be last to catch all non-existing routes
|
||||
],
|
||||
declarations: [
|
||||
AppComponent,
|
||||
|
@ -0,0 +1,284 @@
|
||||
<div class="page-header d-flex">
|
||||
<h1>{{'singleSignOn' | i18n}}</h1>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="loading">
|
||||
<i class="fa fa-spinner fa-spin text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{'loading' | i18n}}</span>
|
||||
</ng-container>
|
||||
|
||||
<form #form (ngSubmit)="submit()" [formGroup]="data" [appApiAction]="formPromise" *ngIf="!loading">
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="enabled" [formControl]="enabled" name="Enabled">
|
||||
<label class="form-check-label" for="enabled">{{'enabled' | i18n}}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="type">{{'type' | i18n}}</label>
|
||||
<select class="form-control" id="type" formControlName="configType">
|
||||
<option value="0" disabled>{{'selectType' | i18n}}</option>
|
||||
<option value="1">OpenID Connect</option>
|
||||
<option value="2">SAML 2.0</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- OIDC -->
|
||||
<div *ngIf="data.value.configType == 1">
|
||||
<div class="config-section">
|
||||
<h2>{{'openIdConnectConfig' | i18n}}</h2>
|
||||
<div class="form-group">
|
||||
<label>{{'callbackPath' | i18n}}</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" readonly [value]="callbackPath">
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{'copyValue' | i18n}}"
|
||||
(click)="copy(data.value.callbackPath)">
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'signedOutCallbackPath' | i18n}}</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" readonly [value]="signedOutCallbackPath">
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{'copyValue' | i18n}}"
|
||||
(click)="copy(data.value.signedOutCallbackPath)">
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'authority' | i18n}}</label>
|
||||
<input class="form-control" formControlName="authority">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'clientId' | i18n}}</label>
|
||||
<input class="form-control" formControlName="clientId">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'clientSecret' | i18n}}</label>
|
||||
<input class="form-control" formControlName="clientSecret">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'metadataAddress' | i18n}}</label>
|
||||
<input class="form-control" formControlName="metadataAddress">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'oidcRedirectBehavior' | i18n}}</label>
|
||||
<select class="form-control" formControlName="redirectBehavior">
|
||||
<option value="0">Redirect GET</option>
|
||||
<option value="1">Form POST</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="getClaimsFromUserInfoEndpoint"
|
||||
formControlName="getClaimsFromUserInfoEndpoint">
|
||||
<label class="form-check-label" for="getClaimsFromUserInfoEndpoint">
|
||||
{{'getClaimsFromUserInfoEndpoint' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'additionalScopes' | i18n}}</label>
|
||||
<input class="form-control" formControlName="additionalScopes">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'additionalUserIdClaimTypes' | i18n}}</label>
|
||||
<input class="form-control" formControlName="additionalUserIdClaimTypes">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'additionalEmailClaimTypes' | i18n}}</label>
|
||||
<input class="form-control" formControlName="additionalEmailClaimTypes">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'additionalNameClaimTypes' | i18n}}</label>
|
||||
<input class="form-control" formControlName="additionalNameClaimTypes">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'acrValues' | i18n}}</label>
|
||||
<input class="form-control" formControlName="acrValues">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'expectedReturnAcrValue' | i18n}}</label>
|
||||
<input class="form-control" formControlName="expectedReturnAcrValue">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="data.value.configType == 2">
|
||||
<!-- SAML2 SP -->
|
||||
<div class="config-section">
|
||||
<h2>{{'samlSpConfig' | i18n}}</h2>
|
||||
<div class="form-group">
|
||||
<label>{{'spEntityId' | i18n}}</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" readonly [value]="spEntityId" >
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{'copyValue' | i18n}}"
|
||||
(click)="copy(data.value.spEntityId)">
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'spMetadataUrl' | i18n}}</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" readonly [value]="spMetadataUrl">
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{'copyValue' | i18n}}"
|
||||
(click)="launchUri(data.value.spMetadataUrl)">
|
||||
<i class="fa fa-lg fa-external-link" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{'copyValue' | i18n}}"
|
||||
(click)="copy(data.value.spMetadataUrl)">
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'spAcsUrl' | i18n}}</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" readonly [value]="spAcsUrl">
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-outline-secondary"
|
||||
appA11yTitle="{{'copyValue' | i18n}}"
|
||||
(click)="copy(data.value.spAcsUrl)">
|
||||
<i class="fa fa-lg fa-clone" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'spNameIdFormat' | i18n}}</label>
|
||||
<select class="form-control" formControlName="spNameIdFormat">
|
||||
<option value="0">Not Configured</option>
|
||||
<option value="1">Unspecified</option>
|
||||
<option value="2">Email Address</option>
|
||||
<option value="3">X.509 Subject Name</option>
|
||||
<option value="4">Windows Domain Qualified Name</option>
|
||||
<option value="5">Kerberos Principal Name</option>
|
||||
<option value="6">Entity Identifier</option>
|
||||
<option value="7">Persistent</option>
|
||||
<option value="8">Transient</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'spOutboundSigningAlgorithm' | i18n}}</label>
|
||||
<select class="form-control" formControlName="spOutboundSigningAlgorithm">
|
||||
<option *ngFor="let o of samlSigningAlgorithms" [ngValue]="o">{{o}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'spSigningBehavior' | i18n}}</label>
|
||||
<select class="form-control" formControlName="spSigningBehavior">
|
||||
<option value="0">If IdP Wants Authn Requests Signed</option>
|
||||
<option value="1">Always</option>
|
||||
<option value="3">Never</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'spMinIncomingSigningAlgorithm' | i18n}}</label>
|
||||
<select class="form-control" formControlName="spMinIncomingSigningAlgorithm">
|
||||
<option *ngFor="let o of samlSigningAlgorithms" [ngValue]="o">{{o}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="spWantAssertionsSigned" formControlName="spWantAssertionsSigned">
|
||||
<label class="form-check-label" for="spWantAssertionsSigned">{{'spWantAssertionsSigned' | i18n}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="spValidateCertificates" formControlName="spValidateCertificates">
|
||||
<label class="form-check-label" for="spValidateCertificates">{{'spValidateCertificates' | i18n}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SAML2 IDP -->
|
||||
<div class="config-section">
|
||||
<h2>{{'samlIdpConfig' | i18n}}</h2>
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{'idpEntityId' | i18n}}</label>
|
||||
<input class="form-control" formControlName="idpEntityId">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'idpBindingType' | i18n}}</label>
|
||||
<select class="form-control" formControlName="idpBindingType">
|
||||
<option value="1">Redirect</option>
|
||||
<option value="2">HTTP POST</option>
|
||||
<option value="4">Artifact</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'idpSingleSignOnServiceUrl' | i18n}}</label>
|
||||
<input class="form-control" formControlName="idpSingleSignOnServiceUrl">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'idpSingleLogoutServiceUrl' | i18n}}</label>
|
||||
<input class="form-control" formControlName="idpSingleLogoutServiceUrl">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'idpArtifactResolutionServiceUrl' | i18n}}</label>
|
||||
<input class="form-control" formControlName="idpArtifactResolutionServiceUrl">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'idpX509PublicCert' | i18n}}</label>
|
||||
<textarea formControlName="idpX509PublicCert" class="form-control form-control-sm text-monospace" rows="6"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>{{'idpOutboundSigningAlgorithm' | i18n}}</label>
|
||||
<select class="form-control" formControlName="idpOutboundSigningAlgorithm">
|
||||
<option *ngFor="let o of samlSigningAlgorithms" [ngValue]="o">{{o}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="idpAllowUnsolicitedAuthnResponse"
|
||||
formControlName="idpAllowUnsolicitedAuthnResponse">
|
||||
<label class="form-check-label" for="idpAllowUnsolicitedAuthnResponse">
|
||||
{{'idpAllowUnsolicitedAuthnResponse' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="idpDisableOutboundLogoutRequests"
|
||||
formControlName="idpDisableOutboundLogoutRequests">
|
||||
<label class="form-check-label" for="idpDisableOutboundLogoutRequests">
|
||||
{{'idpDisableOutboundLogoutRequests' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="idpWantAuthnRequestsSigned"
|
||||
formControlName="idpWantAuthnRequestsSigned">
|
||||
<label class="form-check-label" for="idpWantAuthnRequestsSigned">
|
||||
{{'idpWantAuthnRequestsSigned' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-submit" [disabled]="form.loading">
|
||||
<i class="fa fa-spinner fa-spin" title="{{'loading' | i18n}}" aria-hidden="true"></i>
|
||||
<span>{{'save' | i18n}}</span>
|
||||
</button>
|
||||
</form>
|
121
bitwarden_license/src/app/organizations/manage/sso.component.ts
Normal file
121
bitwarden_license/src/app/organizations/manage/sso.component.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { FormBuilder } from '@angular/forms';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { OrganizationSsoRequest } from 'jslib-common/models/request/organization/organizationSsoRequest';
|
||||
|
||||
@Component({
|
||||
selector: 'app-org-manage-sso',
|
||||
templateUrl: 'sso.component.html',
|
||||
})
|
||||
export class SsoComponent implements OnInit {
|
||||
|
||||
samlSigningAlgorithms = [
|
||||
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
|
||||
'http://www.w3.org/2000/09/xmldsig#rsa-sha384',
|
||||
'http://www.w3.org/2000/09/xmldsig#rsa-sha512',
|
||||
'http://www.w3.org/2000/09/xmldsig#rsa-sha1',
|
||||
];
|
||||
|
||||
loading = true;
|
||||
organizationId: string;
|
||||
formPromise: Promise<any>;
|
||||
|
||||
callbackPath: string;
|
||||
signedOutCallbackPath: string;
|
||||
spEntityId: string;
|
||||
spMetadataUrl: string;
|
||||
spAcsUrl: string;
|
||||
|
||||
enabled = this.fb.control(false);
|
||||
data = this.fb.group({
|
||||
configType: [],
|
||||
|
||||
// OpenId
|
||||
authority: [],
|
||||
clientId: [],
|
||||
clientSecret: [],
|
||||
metadataAddress: [],
|
||||
redirectBehavior: [],
|
||||
getClaimsFromUserInfoEndpoint: [],
|
||||
additionalScopes: [],
|
||||
additionalUserIdClaimTypes: [],
|
||||
additionalEmailClaimTypes: [],
|
||||
additionalNameClaimTypes: [],
|
||||
acrValues: [],
|
||||
expectedReturnAcrValue: [],
|
||||
|
||||
// SAML
|
||||
spNameIdFormat: [],
|
||||
spOutboundSigningAlgorithm: [],
|
||||
spSigningBehavior: [],
|
||||
spMinIncomingSigningAlgorithm: [],
|
||||
spWantAssertionsSigned: [],
|
||||
spValidateCertificates: [],
|
||||
|
||||
idpEntityId: [],
|
||||
idpBindingType: [],
|
||||
idpSingleSignOnServiceUrl: [],
|
||||
idpSingleLogoutServiceUrl: [],
|
||||
idpArtifactResolutionServiceUrl: [],
|
||||
idpX509PublicCert: [],
|
||||
idpOutboundSigningAlgorithm: [],
|
||||
idpAllowUnsolicitedAuthnResponse: [],
|
||||
idpDisableOutboundLogoutRequests: [],
|
||||
idpWantAuthnRequestsSigned: [],
|
||||
});
|
||||
|
||||
constructor(private fb: FormBuilder, private route: ActivatedRoute, private apiService: ApiService,
|
||||
private platformUtilsService: PlatformUtilsService, private i18nService: I18nService) { }
|
||||
|
||||
async ngOnInit() {
|
||||
this.route.parent.parent.params.subscribe(async params => {
|
||||
this.organizationId = params.organizationId;
|
||||
await this.load();
|
||||
});
|
||||
}
|
||||
|
||||
async load() {
|
||||
const ssoSettings = await this.apiService.getOrganizationSso(this.organizationId);
|
||||
|
||||
this.data.patchValue(ssoSettings.data);
|
||||
this.enabled.setValue(ssoSettings.enabled);
|
||||
|
||||
this.callbackPath = ssoSettings.urls.callbackPath;
|
||||
this.signedOutCallbackPath = ssoSettings.urls.signedOutCallbackPath;
|
||||
this.spEntityId = ssoSettings.urls.spEntityId;
|
||||
this.spMetadataUrl = ssoSettings.urls.spMetadataUrl;
|
||||
this.spAcsUrl = ssoSettings.urls.spAcsUrl;
|
||||
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
copy(value: string) {
|
||||
this.platformUtilsService.copyToClipboard(value);
|
||||
}
|
||||
|
||||
launchUri(url: string) {
|
||||
this.platformUtilsService.launchUri(url);
|
||||
}
|
||||
|
||||
async submit() {
|
||||
const request = new OrganizationSsoRequest();
|
||||
request.enabled = this.enabled.value;
|
||||
request.data = this.data.value;
|
||||
|
||||
this.formPromise = this.apiService.postOrganizationSso(this.organizationId, request);
|
||||
|
||||
const response = await this.formPromise;
|
||||
this.data.patchValue(response.data);
|
||||
this.enabled.setValue(response.enabled);
|
||||
|
||||
this.formPromise = null;
|
||||
this.platformUtilsService.showToast('success', null, this.i18nService.t('ssoSettingsSaved'));
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { AuthGuardService } from 'jslib-angular/services/auth-guard.service';
|
||||
|
||||
import { Permissions } from 'jslib-common/enums/permissions';
|
||||
|
||||
import { OrganizationLayoutComponent } from 'src/app/layouts/organization-layout.component';
|
||||
import { ManageComponent } from 'src/app/organizations/manage/manage.component';
|
||||
import { OrganizationGuardService } from 'src/app/services/organization-guard.service';
|
||||
import { OrganizationTypeGuardService } from 'src/app/services/organization-type-guard.service';
|
||||
|
||||
import { SsoComponent } from './manage/sso.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'organizations/:organizationId',
|
||||
component: OrganizationLayoutComponent,
|
||||
canActivate: [AuthGuardService, OrganizationGuardService],
|
||||
children: [
|
||||
{
|
||||
path: 'manage',
|
||||
component: ManageComponent,
|
||||
canActivate: [OrganizationTypeGuardService],
|
||||
data: {
|
||||
permissions: [
|
||||
Permissions.ManageAssignedCollections,
|
||||
Permissions.ManageAllCollections,
|
||||
Permissions.AccessEventLogs,
|
||||
Permissions.ManageGroups,
|
||||
Permissions.ManageUsers,
|
||||
Permissions.ManagePolicies,
|
||||
Permissions.ManageSso,
|
||||
],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'sso',
|
||||
component: SsoComponent,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class OrganizationsRoutingModule { }
|
@ -0,0 +1,22 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
import { OssModule } from 'src/app/oss.module';
|
||||
|
||||
import { SsoComponent } from './manage/sso.component';
|
||||
import { OrganizationsRoutingModule } from './organizations-routing.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
OssModule,
|
||||
OrganizationsRoutingModule,
|
||||
],
|
||||
declarations: [
|
||||
SsoComponent,
|
||||
],
|
||||
})
|
||||
export class OrganizationsModule {}
|
@ -1,8 +1,7 @@
|
||||
{
|
||||
"urls": {
|
||||
"icons": "https://icons.bitwarden.net",
|
||||
"notifications": "https://notifications.bitwarden.com",
|
||||
"enterprise": "https://portal.bitwarden.com"
|
||||
"notifications": "https://notifications.bitwarden.com"
|
||||
},
|
||||
"stripeKey": "pk_live_bpN0P37nMxrMQkcaHXtAybJk",
|
||||
"braintreeKey": "production_qfbsv8kc_njj2zjtyngtjmbjd",
|
||||
|
@ -3,10 +3,8 @@
|
||||
"proxyIdentity": "http://localhost:33656",
|
||||
"proxyEvents": "http://localhost:46273",
|
||||
"proxyNotifications": "http://localhost:61840",
|
||||
"proxyEnterprise": "http://localhost:52313",
|
||||
"allowedHosts": [],
|
||||
"urls": {
|
||||
"notifications": "http://localhost:61840",
|
||||
"enterprise": "http://localhost:52313"
|
||||
"notifications": "http://localhost:61840"
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
{
|
||||
"urls": {
|
||||
"icons": "https://icons.qa.bitwarden.pw",
|
||||
"notifications": "https://notifications.qa.bitwarden.pw",
|
||||
"enterprise": "https://portal.qa.bitwarden.pw"
|
||||
"notifications": "https://notifications.qa.bitwarden.pw"
|
||||
}
|
||||
}
|
||||
|
2
jslib
2
jslib
@ -1 +1 @@
|
||||
Subproject commit 91c5393ae7a84e9f4d90391d072cae56e7a3ff41
|
||||
Subproject commit bfa9a1e1bc05fe96c121eb4709729e85dfb3b008
|
@ -10,6 +10,7 @@ import { AppComponent } from './app.component';
|
||||
import { OssRoutingModule } from './oss-routing.module';
|
||||
import { OssModule } from './oss.module';
|
||||
import { ServicesModule } from './services/services.module';
|
||||
import { WildcardRoutingModule } from './wildcard-routing.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -21,6 +22,7 @@ import { ServicesModule } from './services/services.module';
|
||||
InfiniteScrollModule,
|
||||
DragDropModule,
|
||||
OssRoutingModule,
|
||||
WildcardRoutingModule, // Needs to be last to catch all non-existing routes
|
||||
],
|
||||
declarations: [
|
||||
AppComponent,
|
||||
|
@ -48,15 +48,6 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="ml-auto d-flex align-items-center">
|
||||
<button class="btn btn-primary" (click)="goToBusinessPortal()" #businessBtn
|
||||
[appApiAction]="businessTokenPromise" *ngIf="showBusinessPortalButton">
|
||||
<i class="fa fa-bank fa-fw" [hidden]="businessBtn.loading" aria-hidden="true"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-fw" [hidden]="!businessBtn.loading" title="{{'loading' | i18n}}"
|
||||
aria-hidden="true"></i>
|
||||
{{'businessPortal' | i18n}} →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<router-outlet></router-outlet>
|
||||
|
@ -9,9 +9,6 @@ import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
import { BroadcasterService } from 'jslib-angular/services/broadcaster.service';
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
|
||||
import { Organization } from 'jslib-common/models/domain/organization';
|
||||
@ -26,16 +23,11 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
|
||||
organization: Organization;
|
||||
businessTokenPromise: Promise<any>;
|
||||
private organizationId: string;
|
||||
private businessUrl: string;
|
||||
|
||||
constructor(private route: ActivatedRoute, private userService: UserService,
|
||||
private broadcasterService: BroadcasterService, private ngZone: NgZone,
|
||||
private apiService: ApiService, private platformUtilsService: PlatformUtilsService,
|
||||
private environmentService: EnvironmentService) { }
|
||||
private broadcasterService: BroadcasterService, private ngZone: NgZone) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.businessUrl = this.environmentService.getEnterpriseUrl();
|
||||
|
||||
document.body.classList.remove('layout_frontend');
|
||||
this.route.params.subscribe(async params => {
|
||||
this.organizationId = params.organizationId;
|
||||
@ -60,22 +52,6 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
|
||||
this.organization = await this.userService.getOrganization(this.organizationId);
|
||||
}
|
||||
|
||||
async goToBusinessPortal() {
|
||||
if (this.businessTokenPromise != null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.businessTokenPromise = this.apiService.getEnterprisePortalSignInToken();
|
||||
const token = await this.businessTokenPromise;
|
||||
if (token != null) {
|
||||
const userId = await this.userService.getUserId();
|
||||
this.platformUtilsService.launchUri(this.businessUrl + '/login?userId=' + userId +
|
||||
'&token=' + (window as any).encodeURIComponent(token) + '&organizationId=' + this.organization.id);
|
||||
}
|
||||
} catch { }
|
||||
this.businessTokenPromise = null;
|
||||
}
|
||||
|
||||
get showMenuBar() {
|
||||
return this.showManageTab || this.showToolsTab || this.organization.isOwner;
|
||||
}
|
||||
@ -93,10 +69,6 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
|
||||
return this.organization.canAccessImportExport || this.organization.canAccessReports;
|
||||
}
|
||||
|
||||
get showBusinessPortalButton(): boolean {
|
||||
return this.organization.useBusinessPortal && this.organization.canAccessBusinessPortal;
|
||||
}
|
||||
|
||||
get toolsRoute(): string {
|
||||
return this.organization.canAccessImportExport ?
|
||||
'tools/import' :
|
||||
|
@ -20,6 +20,10 @@
|
||||
*ngIf="organization.canManagePolicies && accessPolicies">
|
||||
{{'policies' | i18n}}
|
||||
</a>
|
||||
<a routerLink="sso" class="list-group-item" routerLinkActive="active"
|
||||
*ngIf="organization.canManageSso && accessSso">
|
||||
{{'singleSignOn' | i18n}}
|
||||
</a>
|
||||
<a routerLink="events" class="list-group-item" routerLinkActive="active"
|
||||
*ngIf="organization.canAccessEventLogs && accessEvents">
|
||||
{{'eventLogs' | i18n}}
|
||||
|
@ -14,16 +14,18 @@ import { Organization } from 'jslib-common/models/domain/organization';
|
||||
})
|
||||
export class ManageComponent implements OnInit {
|
||||
organization: Organization;
|
||||
accessPolicies = false;
|
||||
accessGroups = false;
|
||||
accessEvents = false;
|
||||
accessPolicies: boolean = false;
|
||||
accessGroups: boolean = false;
|
||||
accessEvents: boolean = false;
|
||||
accessSso: boolean = false;
|
||||
|
||||
constructor(private route: ActivatedRoute, private userService: UserService) { }
|
||||
constructor(private route: ActivatedRoute, private userService: UserService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.route.parent.params.subscribe(async params => {
|
||||
this.organization = await this.userService.getOrganization(params.organizationId);
|
||||
this.accessPolicies = this.organization.usePolicies;
|
||||
this.accessSso = this.organization.useSso;
|
||||
this.accessEvents = this.organization.useEvents;
|
||||
this.accessGroups = this.organization.useGroups;
|
||||
});
|
||||
|
@ -12,9 +12,6 @@ import {
|
||||
import { PolicyType } from 'jslib-common/enums/policyType';
|
||||
|
||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||
|
||||
import { ModalService } from 'jslib-angular/services/modal.service';
|
||||
@ -25,7 +22,7 @@ import { Organization } from 'jslib-common/models/domain/organization';
|
||||
|
||||
import { PolicyEditComponent } from './policy-edit.component';
|
||||
|
||||
import { PolicyListService } from 'src/app/services/policy-list.service';
|
||||
import { PolicyListService } from '../../services/policy-list.service';
|
||||
import { BasePolicy } from '../policies/base-policy.component';
|
||||
|
||||
@Component({
|
||||
@ -40,19 +37,12 @@ export class PoliciesComponent implements OnInit {
|
||||
policies: BasePolicy[];
|
||||
organization: Organization;
|
||||
|
||||
// Remove when removing deprecation warning
|
||||
enterpriseTokenPromise: Promise<any>;
|
||||
|
||||
private enterpriseUrl: string;
|
||||
|
||||
private orgPolicies: PolicyResponse[];
|
||||
private policiesEnabledMap: Map<PolicyType, boolean> = new Map<PolicyType, boolean>();
|
||||
|
||||
constructor(private apiService: ApiService, private route: ActivatedRoute,
|
||||
private i18nService: I18nService, private modalService: ModalService,
|
||||
private platformUtilsService: PlatformUtilsService, private userService: UserService,
|
||||
private policyListService: PolicyListService, private router: Router,
|
||||
private environmentService: EnvironmentService) { }
|
||||
private modalService: ModalService, private userService: UserService,
|
||||
private policyListService: PolicyListService, private router: Router) { }
|
||||
|
||||
async ngOnInit() {
|
||||
this.route.parent.parent.params.subscribe(async params => {
|
||||
@ -89,9 +79,6 @@ export class PoliciesComponent implements OnInit {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Remove when removing deprecation warning
|
||||
this.enterpriseUrl = this.environmentService.getEnterpriseUrl();
|
||||
}
|
||||
|
||||
async load() {
|
||||
@ -115,21 +102,4 @@ export class PoliciesComponent implements OnInit {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Remove when removing deprecation warning
|
||||
async goToEnterprisePortal() {
|
||||
if (this.enterpriseTokenPromise != null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.enterpriseTokenPromise = this.apiService.getEnterprisePortalSignInToken();
|
||||
const token = await this.enterpriseTokenPromise;
|
||||
if (token != null) {
|
||||
const userId = await this.userService.getUserId();
|
||||
this.platformUtilsService.launchUri(this.enterpriseUrl + '/login?userId=' + userId +
|
||||
'&token=' + (window as any).encodeURIComponent(token) + '&organizationId=' + this.organizationId);
|
||||
}
|
||||
} catch { }
|
||||
this.enterpriseTokenPromise = null;
|
||||
}
|
||||
}
|
||||
|
@ -102,15 +102,6 @@
|
||||
<div class="mb-3">
|
||||
<label class="font-weight-bold mb-0">Admin Permissions</label>
|
||||
<hr class="my-0 mr-2" />
|
||||
<div class="form-group mb-0">
|
||||
<div class="form-check mt-1 form-check-block">
|
||||
<input class="form-check-input" type="checkbox" name="accessBusinessPortal"
|
||||
id="accessBusinessPortal" [(ngModel)]="permissions.accessBusinessPortal">
|
||||
<label class="form-check-label font-weight-normal" for="accessBusinessPortal">
|
||||
{{'accessBusinessPortal' | i18n}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<div class="form-check mt-1 form-check-block">
|
||||
<input class="form-check-input" type="checkbox" name="accessEventLogs"
|
||||
|
@ -216,7 +216,7 @@ export class OrganizationSubscriptionComponent implements OnInit {
|
||||
}
|
||||
|
||||
get subscriptionDesc() {
|
||||
if (this.sub.maxAutoscaleSeats == this.sub.seats && this.sub.seats != null) {
|
||||
if (this.sub.maxAutoscaleSeats === this.sub.seats && this.sub.seats != null) {
|
||||
return this.i18nService.t('subscriptionMaxReached', this.sub.seats.toString());
|
||||
} else if (this.sub.maxAutoscaleSeats == null) {
|
||||
return this.i18nService.t('subscriptionUserSeatsUnlimitedAutoscale');
|
||||
|
@ -443,7 +443,6 @@ const routes: Routes = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{ path: '**', redirectTo: '' },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@ -18,7 +18,6 @@ export class OrganizationTypeGuardService implements CanActivate {
|
||||
const permissions = route.data == null ? null : route.data.permissions as Permissions[];
|
||||
|
||||
if (
|
||||
(permissions.indexOf(Permissions.AccessBusinessPortal) !== -1 && org.canAccessBusinessPortal) ||
|
||||
(permissions.indexOf(Permissions.AccessEventLogs) !== -1 && org.canAccessEventLogs) ||
|
||||
(permissions.indexOf(Permissions.AccessImportExport) !== -1 && org.canAccessImportExport) ||
|
||||
(permissions.indexOf(Permissions.AccessReports) !== -1 && org.canAccessReports) ||
|
||||
@ -31,7 +30,8 @@ export class OrganizationTypeGuardService implements CanActivate {
|
||||
(permissions.indexOf(Permissions.ManageOrganization) !== -1 && org.isOwner) ||
|
||||
(permissions.indexOf(Permissions.ManagePolicies) !== -1 && org.canManagePolicies) ||
|
||||
(permissions.indexOf(Permissions.ManageUsers) !== -1 && org.canManageUsers) ||
|
||||
(permissions.indexOf(Permissions.ManageUsersPassword) !== -1 && org.canManageUsersPassword)
|
||||
(permissions.indexOf(Permissions.ManageUsersPassword) !== -1 && org.canManageUsersPassword) ||
|
||||
(permissions.indexOf(Permissions.ManageSso) !== -1 && org.canManageSso)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
12
src/app/wildcard-routing.module.ts
Normal file
12
src/app/wildcard-routing.module.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '**', redirectTo: '' },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class WildcardRoutingModule { }
|
@ -2154,6 +2154,9 @@
|
||||
"policies": {
|
||||
"message": "Policies"
|
||||
},
|
||||
"singleSignOn": {
|
||||
"message": "Single Sign-On"
|
||||
},
|
||||
"editPolicy": {
|
||||
"message": "Edit Policy"
|
||||
},
|
||||
@ -3396,10 +3399,6 @@
|
||||
"ssoHandOff": {
|
||||
"message": "You may now close this tab and continue in the extension."
|
||||
},
|
||||
"businessPortal": {
|
||||
"message": "Business Portal",
|
||||
"description": "The web portal used by business organizations for configuring certain features."
|
||||
},
|
||||
"includeAllTeamsFeatures": {
|
||||
"message": "All Teams features, plus:"
|
||||
},
|
||||
@ -3809,9 +3808,6 @@
|
||||
"permissions": {
|
||||
"message": "Permissions"
|
||||
},
|
||||
"accessBusinessPortal": {
|
||||
"message": "Access Business Portal"
|
||||
},
|
||||
"accessEventLogs": {
|
||||
"message": "Access Event Logs"
|
||||
},
|
||||
@ -4329,5 +4325,122 @@
|
||||
},
|
||||
"personalVaultExportPolicyInEffect": {
|
||||
"message": "One or more organization policies prevents you from exporting your personal vault."
|
||||
},
|
||||
"selectType": {
|
||||
"message": "Select SSO Type"
|
||||
},
|
||||
"type": {
|
||||
"message": "Type"
|
||||
},
|
||||
"openIdConnectConfig": {
|
||||
"message": "OpenID Connect Configuration"
|
||||
},
|
||||
"samlSpConfig": {
|
||||
"message": "SAML Service Provider Configuration"
|
||||
},
|
||||
"samlIdpConfig": {
|
||||
"message": "SAML Identity Provider Configuration"
|
||||
},
|
||||
"callbackPath": {
|
||||
"message": "Callback Path"
|
||||
},
|
||||
"signedOutCallbackPath": {
|
||||
"message": "Signed Out Callback Path"
|
||||
},
|
||||
"authority": {
|
||||
"message": "Authority"
|
||||
},
|
||||
"clientId": {
|
||||
"message": "Client ID"
|
||||
},
|
||||
"clientSecret": {
|
||||
"message": "Client Secret"
|
||||
},
|
||||
"metadataAddress": {
|
||||
"message": "Metadata Address"
|
||||
},
|
||||
"oidcRedirectBehavior": {
|
||||
"message": "OIDC Redirect Behavior"
|
||||
},
|
||||
"getClaimsFromUserInfoEndpoint": {
|
||||
"message": "Get Claims From User Info Endpoint"
|
||||
},
|
||||
"additionalScopes": {
|
||||
"message": "Additional/Custom Scopes (comma delimited)"
|
||||
},
|
||||
"additionalUserIdClaimTypes": {
|
||||
"message": "Additional/Custom User ID Claim Types (comma delimited)"
|
||||
},
|
||||
"additionalEmailClaimTypes": {
|
||||
"message": "Additional/Custom Email Claim Types (comma delimited)"
|
||||
},
|
||||
"additionalNameClaimTypes": {
|
||||
"message": "Additional/Custom Name Claim Types (comma delimited)"
|
||||
},
|
||||
"acrValues": {
|
||||
"message": "Requested Authentication Context Class Reference values (acr_values)"
|
||||
},
|
||||
"expectedReturnAcrValue": {
|
||||
"message": "Expected \"acr\" Claim Value In Response (acr validation)"
|
||||
},
|
||||
"spEntityId": {
|
||||
"message": "SP Entity ID"
|
||||
},
|
||||
"spMetadataUrl": {
|
||||
"message": "SAML 2.0 Metadata URL"
|
||||
},
|
||||
"spAcsUrl": {
|
||||
"message": "Assertion Consumer Service (ACS) URL"
|
||||
},
|
||||
"spNameIdFormat": {
|
||||
"message": "Name ID Format"
|
||||
},
|
||||
"spOutboundSigningAlgorithm": {
|
||||
"message": "Outbound Signing Algorithm"
|
||||
},
|
||||
"spSigningBehavior": {
|
||||
"message": "Signing Behavior"
|
||||
},
|
||||
"spMinIncomingSigningAlgorithm": {
|
||||
"message": "Minimum Incoming Signing Algorithm"
|
||||
},
|
||||
"spWantAssertionsSigned": {
|
||||
"message": "Want Assertions Signed"
|
||||
},
|
||||
"spValidateCertificates": {
|
||||
"message": "Validate Certificates"
|
||||
},
|
||||
"idpEntityId": {
|
||||
"message": "Entity ID"
|
||||
},
|
||||
"idpBindingType": {
|
||||
"message": "Binding Type"
|
||||
},
|
||||
"idpSingleSignOnServiceUrl": {
|
||||
"message": "Single Sign On Service URL"
|
||||
},
|
||||
"idpSingleLogoutServiceUrl": {
|
||||
"message": "Single Log Out Service URL"
|
||||
},
|
||||
"idpArtifactResolutionServiceUrl": {
|
||||
"message": "Artifact Resolution Service URL"
|
||||
},
|
||||
"idpX509PublicCert": {
|
||||
"message": "X509 Public Certificate"
|
||||
},
|
||||
"idpOutboundSigningAlgorithm": {
|
||||
"message": "Outbound Signing Algorithm"
|
||||
},
|
||||
"idpAllowUnsolicitedAuthnResponse": {
|
||||
"message": "Allow Unsolicited Authentication Response"
|
||||
},
|
||||
"idpDisableOutboundLogoutRequests": {
|
||||
"message": "Disable Outbound Logout Requests"
|
||||
},
|
||||
"idpWantAuthnRequestsSigned": {
|
||||
"message": "Want Authentication Requests Signed"
|
||||
},
|
||||
"ssoSettingsSaved": {
|
||||
"message": "Single Sign-On configuration was saved."
|
||||
}
|
||||
}
|
||||
|
@ -200,12 +200,6 @@ const devServer = ENV !== 'development' ? {} : {
|
||||
secure: false,
|
||||
changeOrigin: true
|
||||
},
|
||||
'/portal': {
|
||||
target: envConfig['proxyEnterprise'],
|
||||
pathRewrite: {'^/portal' : ''},
|
||||
secure: false,
|
||||
changeOrigin: true
|
||||
}
|
||||
},
|
||||
hot: false,
|
||||
allowedHosts: envConfig['allowedHosts']
|
||||
|
Loading…
Reference in New Issue
Block a user