From 3eda0aa2cd9b86ed36b60ccb5af0f48ab9cfede8 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Wed, 6 Oct 2021 20:45:45 +0200 Subject: [PATCH] Remove Business Portal and add SSO configuration (#1213) --- README.md | 1 - bitwarden_license/src/app/app.module.ts | 5 +- .../organizations/manage/sso.component.html | 284 ++++++++++++++++++ .../app/organizations/manage/sso.component.ts | 121 ++++++++ .../organizations-routing.module.ts | 51 ++++ .../app/organizations/organizations.module.ts | 22 ++ config/cloud.json | 3 +- config/development.json | 4 +- config/qa.json | 3 +- jslib | 2 +- src/app/app.module.ts | 2 + .../organization-layout.component.html | 9 - .../layouts/organization-layout.component.ts | 30 +- .../manage/manage.component.html | 4 + .../organizations/manage/manage.component.ts | 10 +- .../manage/policies.component.ts | 36 +-- .../manage/user-add-edit.component.html | 9 - .../organization-subscription.component.ts | 2 +- src/app/oss-routing.module.ts | 1 - .../organization-type-guard.service.ts | 4 +- src/app/wildcard-routing.module.ts | 12 + src/locales/en/messages.json | 127 +++++++- webpack.config.js | 6 - 23 files changed, 637 insertions(+), 111 deletions(-) create mode 100644 bitwarden_license/src/app/organizations/manage/sso.component.html create mode 100644 bitwarden_license/src/app/organizations/manage/sso.component.ts create mode 100644 bitwarden_license/src/app/organizations/organizations-routing.module.ts create mode 100644 bitwarden_license/src/app/organizations/organizations.module.ts create mode 100644 src/app/wildcard-routing.module.ts diff --git a/README.md b/README.md index 740d8ac1..a0b5367b 100644 --- a/README.md +++ b/README.md @@ -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": { diff --git a/bitwarden_license/src/app/app.module.ts b/bitwarden_license/src/app/app.module.ts index 77cee037..cd2c628c 100644 --- a/bitwarden_license/src/app/app.module.ts +++ b/bitwarden_license/src/app/app.module.ts @@ -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, diff --git a/bitwarden_license/src/app/organizations/manage/sso.component.html b/bitwarden_license/src/app/organizations/manage/sso.component.html new file mode 100644 index 00000000..cc9fca87 --- /dev/null +++ b/bitwarden_license/src/app/organizations/manage/sso.component.html @@ -0,0 +1,284 @@ + + + + + {{'loading' | i18n}} + + +
+
+
+ + +
+
+ +
+ + +
+ + +
+
+

{{'openIdConnectConfig' | i18n}}

+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ +
+

{{'samlSpConfig' | i18n}}

+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ + +
+
+
+
+ +
+ +
+ +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+ + +
+
+
+ + +
+

{{'samlIdpConfig' | i18n}}

+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
diff --git a/bitwarden_license/src/app/organizations/manage/sso.component.ts b/bitwarden_license/src/app/organizations/manage/sso.component.ts new file mode 100644 index 00000000..fa46e0d6 --- /dev/null +++ b/bitwarden_license/src/app/organizations/manage/sso.component.ts @@ -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; + + 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')); + } +} diff --git a/bitwarden_license/src/app/organizations/organizations-routing.module.ts b/bitwarden_license/src/app/organizations/organizations-routing.module.ts new file mode 100644 index 00000000..1ad83403 --- /dev/null +++ b/bitwarden_license/src/app/organizations/organizations-routing.module.ts @@ -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 { } diff --git a/bitwarden_license/src/app/organizations/organizations.module.ts b/bitwarden_license/src/app/organizations/organizations.module.ts new file mode 100644 index 00000000..81268126 --- /dev/null +++ b/bitwarden_license/src/app/organizations/organizations.module.ts @@ -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 {} diff --git a/config/cloud.json b/config/cloud.json index 5cbd8eca..d4cdc426 100644 --- a/config/cloud.json +++ b/config/cloud.json @@ -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", diff --git a/config/development.json b/config/development.json index b090a8b7..6c1aa1b7 100644 --- a/config/development.json +++ b/config/development.json @@ -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" } } diff --git a/config/qa.json b/config/qa.json index 7aee8be5..004613c3 100644 --- a/config/qa.json +++ b/config/qa.json @@ -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" } } diff --git a/jslib b/jslib index 91c5393a..bfa9a1e1 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit 91c5393ae7a84e9f4d90391d072cae56e7a3ff41 +Subproject commit bfa9a1e1bc05fe96c121eb4709729e85dfb3b008 diff --git a/src/app/app.module.ts b/src/app/app.module.ts index a8e2f88a..5ad3b62f 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -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, diff --git a/src/app/layouts/organization-layout.component.html b/src/app/layouts/organization-layout.component.html index 324e02e1..351ab3b3 100644 --- a/src/app/layouts/organization-layout.component.html +++ b/src/app/layouts/organization-layout.component.html @@ -48,15 +48,6 @@ -
- -
diff --git a/src/app/layouts/organization-layout.component.ts b/src/app/layouts/organization-layout.component.ts index c6b4b4bd..bb7a4880 100644 --- a/src/app/layouts/organization-layout.component.ts +++ b/src/app/layouts/organization-layout.component.ts @@ -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; 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' : diff --git a/src/app/organizations/manage/manage.component.html b/src/app/organizations/manage/manage.component.html index daa82b0f..2182f1fb 100644 --- a/src/app/organizations/manage/manage.component.html +++ b/src/app/organizations/manage/manage.component.html @@ -20,6 +20,10 @@ *ngIf="organization.canManagePolicies && accessPolicies"> {{'policies' | i18n}} + + {{'singleSignOn' | i18n}} + {{'eventLogs' | i18n}} diff --git a/src/app/organizations/manage/manage.component.ts b/src/app/organizations/manage/manage.component.ts index 30112010..fc760f63 100644 --- a/src/app/organizations/manage/manage.component.ts +++ b/src/app/organizations/manage/manage.component.ts @@ -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; }); diff --git a/src/app/organizations/manage/policies.component.ts b/src/app/organizations/manage/policies.component.ts index b2aaf2b6..16e562d7 100644 --- a/src/app/organizations/manage/policies.component.ts +++ b/src/app/organizations/manage/policies.component.ts @@ -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; - - private enterpriseUrl: string; - private orgPolicies: PolicyResponse[]; private policiesEnabledMap: Map = new Map(); 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; - } } diff --git a/src/app/organizations/manage/user-add-edit.component.html b/src/app/organizations/manage/user-add-edit.component.html index 8c442fdb..0a751dc5 100644 --- a/src/app/organizations/manage/user-add-edit.component.html +++ b/src/app/organizations/manage/user-add-edit.component.html @@ -102,15 +102,6 @@

-
-
- - -
-