From 47d5320df4161f333ce23660b9d1317763a81cd5 Mon Sep 17 00:00:00 2001 From: Stuart Heap Date: Wed, 15 Sep 2021 16:59:16 +0200 Subject: [PATCH] trivial PR feedback - missing files from web-vault patch --- Cargo.toml | 1 - src/api/core/organizations.rs | 5 +- src/api/identity.rs | 2 +- web-vault-sso.patch | 224 ++++++++++++++++++++++++++++++++++ 4 files changed, 226 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 80bf7d8d..bd583223 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -138,7 +138,6 @@ backtrace = "0.3.60" # Macro ident concatenation paste = "1.0.5" openidconnect = "2.0.1" -urlencoding = "1.1.1" [patch.crates-io] # Use newest ring diff --git a/src/api/core/organizations.rs b/src/api/core/organizations.rs index bc09d38c..2f993773 100644 --- a/src/api/core/organizations.rs +++ b/src/api/core/organizations.rs @@ -213,10 +213,7 @@ fn post_organization( org.name = data.Name; org.billing_email = data.BillingEmail; - org.identifier = match data.Identifier { - Some(identifier) => identifier, - None => String::from(""), - }; + org.identifier = data.Identifier.unwrap_or_default(); org.save(&conn)?; Ok(Json(org.to_json())) diff --git a/src/api/identity.rs b/src/api/identity.rs index f36b362b..b07f0a8d 100644 --- a/src/api/identity.rs +++ b/src/api/identity.rs @@ -171,7 +171,7 @@ fn _password_login(data: ConnectData, conn: DbConn, ip: &ClientIp) -> JsonResult // Check if org policy prevents password login let user_orgs = UserOrganization::find_by_user_and_policy(&user.uuid, OrgPolicyType::RequireSso, &conn); - if user_orgs.len() == 1 && user_orgs[0].atype >= 2 { + if user_orgs.len() >= 1 && user_orgs[0].atype != UserOrgType::Owner && user_orgs[0].atype != UserOrgType::Admin { // if requires SSO is active, user is in exactly one org by policy rules // policy only applies to "non-owner/non-admin" members diff --git a/web-vault-sso.patch b/web-vault-sso.patch index dde307ad..2c993df3 100644 --- a/web-vault-sso.patch +++ b/web-vault-sso.patch @@ -121,6 +121,20 @@ index ac7ef04..5b1b774 100644 logInApiKey: (clientId: string, clientSecret: string) => Promise; logInTwoFactor: (twoFactorProvider: TwoFactorProviderType, twoFactorToken: string, remember?: boolean) => Promise; +diff --git a/jslib/common/src/models/request/organizationSsoUpdateRequest.ts b/jslib/common/src/models/request/organizationSsoUpdateRequest.ts +new file mode 100644 +index 0000000..7075aec +--- /dev/null ++++ b/jslib/common/src/models/request/organizationSsoUpdateRequest.ts +@@ -0,0 +1,8 @@ ++export class OrganizationSsoUpdateRequest { ++ useSso: boolean; ++ callbackPath: string; ++ signedOutCallbackPath: string; ++ authority: string; ++ clientId: string; ++ clientSecret: string; ++} diff --git a/jslib/common/src/models/request/tokenRequest.ts b/jslib/common/src/models/request/tokenRequest.ts index 7578012..964364f 100644 --- a/jslib/common/src/models/request/tokenRequest.ts @@ -184,6 +198,82 @@ index 21d8d43..896b7a6 100644 + this.clientSecret = this.getResponseProperty('ClientSecret'); } } +diff --git a/jslib/common/src/models/response/organizationSsoResponse.ts b/jslib/common/src/models/response/organizationSsoResponse.ts +new file mode 100644 +index 0000000..896b7a6 +--- /dev/null ++++ b/jslib/common/src/models/response/organizationSsoResponse.ts +@@ -0,0 +1,70 @@ ++import { BaseResponse } from './baseResponse'; ++import { PlanResponse } from './planResponse'; ++ ++import { PlanType } from '../../enums/planType'; ++ ++export class OrganizationResponse extends BaseResponse { ++ id: string; ++ identifier: string; ++ name: string; ++ businessName: string; ++ businessAddress1: string; ++ businessAddress2: string; ++ businessAddress3: string; ++ businessCountry: string; ++ businessTaxNumber: string; ++ billingEmail: string; ++ plan: PlanResponse; ++ planType: PlanType; ++ seats: number; ++ maxCollections: number; ++ maxStorageGb: number; ++ useGroups: boolean; ++ useDirectory: boolean; ++ useEvents: boolean; ++ useTotp: boolean; ++ use2fa: boolean; ++ useApi: boolean; ++ useResetPassword: boolean; ++ hasPublicAndPrivateKeys: boolean; ++ useSso: boolean; ++ callbackPath: string; ++ signedOutCallbackPath: string; ++ authority: string; ++ clientId: string; ++ clientSecret: string; ++ ++ constructor(response: any) { ++ super(response); ++ this.id = this.getResponseProperty('Id'); ++ this.identifier = this.getResponseProperty('Identifier'); ++ this.name = this.getResponseProperty('Name'); ++ this.businessName = this.getResponseProperty('BusinessName'); ++ this.businessAddress1 = this.getResponseProperty('BusinessAddress1'); ++ this.businessAddress2 = this.getResponseProperty('BusinessAddress2'); ++ this.businessAddress3 = this.getResponseProperty('BusinessAddress3'); ++ this.businessCountry = this.getResponseProperty('BusinessCountry'); ++ this.businessTaxNumber = this.getResponseProperty('BusinessTaxNumber'); ++ this.billingEmail = this.getResponseProperty('BillingEmail'); ++ const plan = this.getResponseProperty('Plan'); ++ this.plan = plan == null ? null : new PlanResponse(plan); ++ this.planType = this.getResponseProperty('PlanType'); ++ this.seats = this.getResponseProperty('Seats'); ++ this.maxCollections = this.getResponseProperty('MaxCollections'); ++ this.maxStorageGb = this.getResponseProperty('MaxStorageGb'); ++ this.useGroups = this.getResponseProperty('UseGroups'); ++ this.useDirectory = this.getResponseProperty('UseDirectory'); ++ this.useEvents = this.getResponseProperty('UseEvents'); ++ this.useTotp = this.getResponseProperty('UseTotp'); ++ this.use2fa = this.getResponseProperty('Use2fa'); ++ this.useApi = this.getResponseProperty('UseApi'); ++ this.useResetPassword = this.getResponseProperty('UseResetPassword'); ++ this.hasPublicAndPrivateKeys = this.getResponseProperty('HasPublicAndPrivateKeys'); ++ this.useSso = this.getResponseProperty('UseSso'); ++ this.callbackPath = this.getResponseProperty('CallbackPath'); ++ this.signedOutCallbackPath = this.getResponseProperty('SignedOutCallbackPath'); ++ this.authority = this.getResponseProperty('Authority'); ++ this.clientId = this.getResponseProperty('ClientId'); ++ this.clientSecret = this.getResponseProperty('ClientSecret'); ++ } ++} diff --git a/jslib/common/src/services/api.service.ts b/jslib/common/src/services/api.service.ts index 51c1c14..1a5b088 100644 --- a/jslib/common/src/services/api.service.ts @@ -387,6 +477,140 @@ index 2dac5ac1..21ce9848 100644 {{'subscription' | i18n}} +diff --git a/src/app/organizations/settings/sso.component.html b/src/app/organizations/settings/sso.component.html +new file mode 100644 +index 00000000..41d0e89e +--- /dev/null ++++ b/src/app/organizations/settings/sso.component.html +@@ -0,0 +1,51 @@ ++ ++
++ ++ {{'loading' | i18n}} ++
++
++
++
++
++ ++ ++
++

OpenId Connect Configuration

++
++ ++ ++
++
++ ++ ++
++
++ ++ ++
++
++ ++ ++
++
++ ++ ++
++
++
++ ++
++
++ ++ {{'loading' | i18n}} ++
+diff --git a/src/app/organizations/settings/sso.component.ts b/src/app/organizations/settings/sso.component.ts +new file mode 100644 +index 00000000..f40a54f2 +--- /dev/null ++++ b/src/app/organizations/settings/sso.component.ts +@@ -0,0 +1,71 @@ ++import { ++ Component, ++ ComponentFactoryResolver, ++ ViewChild, ++ ViewContainerRef, ++} from '@angular/core'; ++ ++import { ActivatedRoute } from '@angular/router'; ++ ++import { ToasterService } from 'angular2-toaster'; ++ ++import { ApiService } from 'jslib-common/abstractions/api.service'; ++import { CryptoService } from 'jslib-common/abstractions/crypto.service'; ++import { I18nService } from 'jslib-common/abstractions/i18n.service'; ++import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; ++import { SyncService } from 'jslib-common/abstractions/sync.service'; ++ ++import { OrganizationSsoUpdateRequest } from 'jslib-common/models/request/organizationSsoUpdateRequest'; ++ ++import { OrganizationResponse } from 'jslib-common/models/response/organizationResponse'; ++ ++import { ModalComponent } from '../../modal.component'; ++ ++@Component({ ++ selector: 'app-org-sso', ++ templateUrl: 'sso.component.html', ++}) ++export class SsoComponent { ++ selfHosted = false; ++ loading = true; ++ org: OrganizationResponse; ++ formPromise: Promise; ++ ++ private organizationId: string; ++ private modal: ModalComponent = null; ++ ++ constructor(private componentFactoryResolver: ComponentFactoryResolver, ++ private apiService: ApiService, private i18nService: I18nService, ++ private toasterService: ToasterService, private route: ActivatedRoute, ++ private syncService: SyncService, private platformUtilsService: PlatformUtilsService, ++ private cryptoService: CryptoService) { } ++ ++ async ngOnInit() { ++ this.selfHosted = this.platformUtilsService.isSelfHost(); ++ this.route.parent.parent.params.subscribe(async params => { ++ this.organizationId = params.organizationId; ++ try { ++ this.org = await this.apiService.getOrganization(this.organizationId); ++ } catch { } ++ }); ++ this.loading = false; ++ } ++ ++ async submit() { ++ try { ++ const request = new OrganizationSsoUpdateRequest(); ++ request.useSso = this.org.useSso; ++ request.callbackPath = this.org.callbackPath; ++ request.signedOutCallbackPath = this.org.signedOutCallbackPath; ++ request.authority = this.org.authority; ++ request.clientId = this.org.clientId; ++ request.clientSecret = this.org.clientSecret; ++ ++ this.formPromise = this.apiService.putOrganizationSso(this.organizationId, request).then(() => { ++ return this.syncService.fullSync(true); ++ }); ++ await this.formPromise; ++ this.toasterService.popAsync('success', null, this.i18nService.t('organizationUpdated')); ++ } catch { } ++ } ++} diff --git a/src/app/send/access.component.html b/src/app/send/access.component.html index 84944a2b..b736bbe4 100644 --- a/src/app/send/access.component.html