Browse Source

trivial PR feedback - missing files from web-vault patch

pull/1955/head
Stuart Heap 4 years ago
parent
commit
47d5320df4
No known key found for this signature in database GPG Key ID: C753450AB379AA25
  1. 1
      Cargo.toml
  2. 5
      src/api/core/organizations.rs
  3. 2
      src/api/identity.rs
  4. 224
      web-vault-sso.patch

1
Cargo.toml

@ -138,7 +138,6 @@ backtrace = "0.3.60"
# Macro ident concatenation # Macro ident concatenation
paste = "1.0.5" paste = "1.0.5"
openidconnect = "2.0.1" openidconnect = "2.0.1"
urlencoding = "1.1.1"
[patch.crates-io] [patch.crates-io]
# Use newest ring # Use newest ring

5
src/api/core/organizations.rs

@ -213,10 +213,7 @@ fn post_organization(
org.name = data.Name; org.name = data.Name;
org.billing_email = data.BillingEmail; org.billing_email = data.BillingEmail;
org.identifier = match data.Identifier { org.identifier = data.Identifier.unwrap_or_default();
Some(identifier) => identifier,
None => String::from(""),
};
org.save(&conn)?; org.save(&conn)?;
Ok(Json(org.to_json())) Ok(Json(org.to_json()))

2
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 // Check if org policy prevents password login
let user_orgs = UserOrganization::find_by_user_and_policy(&user.uuid, OrgPolicyType::RequireSso, &conn); 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 // if requires SSO is active, user is in exactly one org by policy rules
// policy only applies to "non-owner/non-admin" members // policy only applies to "non-owner/non-admin" members

224
web-vault-sso.patch

@ -121,6 +121,20 @@ index ac7ef04..5b1b774 100644
logInApiKey: (clientId: string, clientSecret: string) => Promise<AuthResult>; logInApiKey: (clientId: string, clientSecret: string) => Promise<AuthResult>;
logInTwoFactor: (twoFactorProvider: TwoFactorProviderType, twoFactorToken: string, logInTwoFactor: (twoFactorProvider: TwoFactorProviderType, twoFactorToken: string,
remember?: boolean) => Promise<AuthResult>; remember?: boolean) => Promise<AuthResult>;
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 diff --git a/jslib/common/src/models/request/tokenRequest.ts b/jslib/common/src/models/request/tokenRequest.ts
index 7578012..964364f 100644 index 7578012..964364f 100644
--- a/jslib/common/src/models/request/tokenRequest.ts --- a/jslib/common/src/models/request/tokenRequest.ts
@ -184,6 +198,82 @@ index 21d8d43..896b7a6 100644
+ this.clientSecret = this.getResponseProperty('ClientSecret'); + 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 diff --git a/jslib/common/src/services/api.service.ts b/jslib/common/src/services/api.service.ts
index 51c1c14..1a5b088 100644 index 51c1c14..1a5b088 100644
--- a/jslib/common/src/services/api.service.ts --- a/jslib/common/src/services/api.service.ts
@ -387,6 +477,140 @@ index 2dac5ac1..21ce9848 100644
<a routerLink="subscription" class="list-group-item" routerLinkActive="active"> <a routerLink="subscription" class="list-group-item" routerLinkActive="active">
{{'subscription' | i18n}} {{'subscription' | i18n}}
</a> </a>
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 @@
+<div class="page-header">
+ <h1>{{'singleSignOn' | i18n}}</h1>
+</div>
+<div *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>
+</div>
+<form *ngIf="org && !loading" #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate>
+ <div class="row">
+ <div class="col-12">
+ <div class="form-group">
+ <label for="enabled">{{'enabled' | i18n}}</label>
+ <input id="enabled" class="form-control" type="checkbox" name="Enabled" [(ngModel)]="org.useSso" [disabled]="selfHosted">
+ </div>
+ <h2>OpenId Connect Configuration</h2>
+ <div class="form-group">
+ <label for="callbackPath">{{'callbackPath' | i18n}}</label>
+ <input id="callbackPath" class="form-control" type="text" name="Callback Path" [(ngModel)]="org.callbackPath"
+ [disabled]="selfHosted">
+ </div>
+ <div class="form-group">
+ <label for="signedOutCallbackPath">{{'signedOutCallbackPath' | i18n}}</label>
+ <input id="signedOutCallbackPath" class="form-control" type="text" name="Signed Out Callback Path"
+ [(ngModel)]="org.signedOutCallbackPath" [disabled]="selfHosted">
+ </div>
+ <div class="form-group">
+ <label for="authority">{{'authority' | i18n}}</label>
+ <input id="authority" class="form-control" type="text" name="Authority"
+ [(ngModel)]="org.authority" [disabled]="selfHosted">
+ </div>
+ <div class="form-group">
+ <label for="clientId">{{'clientId' | i18n}}</label>
+ <input id="authority" class="form-control" type="text" name="Client ID"
+ [(ngModel)]="org.clientId" [disabled]="selfHosted">
+ </div>
+ <div class="form-group">
+ <label for="clientSecret">{{'clientSecret' | i18n}}</label>
+ <input id="clientSecret" class="form-control" type="password" name="Client Secret"
+ [(ngModel)]="org.clientSecret" [disabled]="selfHosted">
+ </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>
+<div *ngIf="!org || loading">
+ <i class="fa fa-spinner fa-spin text-muted" title="{{'loading' | i18n}}" aria-hidden="true"></i>
+ <span class="sr-only">{{'loading' | i18n}}</span>
+</div>
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<any>;
+
+ 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 diff --git a/src/app/send/access.component.html b/src/app/send/access.component.html
index 84944a2b..b736bbe4 100644 index 84944a2b..b736bbe4 100644
--- a/src/app/send/access.component.html --- a/src/app/send/access.component.html

Loading…
Cancel
Save