Sso link existing user (#616)

* created and applied link-sso component

* implemented linking existing user to sso

* removed an unused import

* created and applied link-sso component

* implemented linking existing user to sso

* removed an unused import

* merge

* added a token to the sso linking flow

* [jslib] Update (5d874d0 -> 6ab444a) (#618)

* Update jslib (5d874d0 -> 6ab444a)

* Update dependency flows

* created and applied link-sso component

* implemented linking existing user to sso

* removed an unused import

* merge

* added a token to the sso linking flow

* implemented linking existing user to sso

* removed an unused import

* account for some variable shakeup in jslib for link sso

* updated jslib

* updated jslib

* still trying to fix jslib

* finally, really, truly updated jslib

Co-authored-by: Vincent Salucci <26154748+vincentsalucci@users.noreply.github.com>
This commit is contained in:
Addison Beck 2020-08-27 11:44:04 -04:00 committed by GitHub
parent bc71ffa6f2
commit e17a49acd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 104 additions and 1 deletions

2
jslib

@ -1 +1 @@
Subproject commit e55528e61737635e7f8970b913bcc3f10bede85d
Subproject commit e07526a1b6cae520561e452a6695a1508d785585

View File

@ -109,6 +109,7 @@ import { CreateOrganizationComponent } from './settings/create-organization.comp
import { DeauthorizeSessionsComponent } from './settings/deauthorize-sessions.component';
import { DeleteAccountComponent } from './settings/delete-account.component';
import { DomainRulesComponent } from './settings/domain-rules.component';
import { LinkSsoComponent } from './settings/link-sso.component';
import { OptionsComponent } from './settings/options.component';
import { OrganizationPlansComponent } from './settings/organization-plans.component';
import { OrganizationsComponent } from './settings/organizations.component';
@ -295,6 +296,7 @@ registerLocaleData(localeZhTw, 'zh-TW');
ImportComponent,
InactiveTwoFactorReportComponent,
InputVerbatimDirective,
LinkSsoComponent,
LockComponent,
LoginComponent,
ModalComponent,

View File

@ -0,0 +1,4 @@
<a class="dropdown-item" href="#" appStopClick (click)="submit(returnUri, true)">
<i class="fa fa-fw fa-link" aria-hidden="true"></i>
Link SSO
</a>

View File

@ -0,0 +1,48 @@
import {
AfterContentInit,
Component,
Input,
} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ApiService } from 'jslib/abstractions/api.service';
import { AuthService } from 'jslib/abstractions/auth.service';
import { CryptoFunctionService } from 'jslib/abstractions/cryptoFunction.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service';
import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service';
import { StateService } from 'jslib/abstractions/state.service';
import { StorageService } from 'jslib/abstractions/storage.service';
import { SsoComponent } from 'jslib/angular/components/sso.component';
import { Organization } from 'jslib/models/domain/organization';
@Component({
selector: 'app-link-sso',
templateUrl: 'link-sso.component.html',
})
export class LinkSsoComponent extends SsoComponent implements AfterContentInit {
@Input() organization: Organization;
returnUri: string = '/settings/organizations'
constructor(platformUtilsService: PlatformUtilsService, i18nService: I18nService,
apiService: ApiService, authService: AuthService,
router: Router, route: ActivatedRoute,
cryptoFunctionService: CryptoFunctionService, passwordGenerationService: PasswordGenerationService,
storageService: StorageService, stateService: StateService) {
super(authService, router,
i18nService, route,
storageService, stateService,
platformUtilsService, apiService,
cryptoFunctionService, passwordGenerationService);
this.returnUri = '/settings/organizations';
this.redirectUri = window.location.origin + '/sso-connector.html';
this.clientId = 'web';
}
async ngAfterContentInit() {
this.identifier = this.organization.identifier;
}
}

View File

@ -74,6 +74,17 @@
<i class="fa fa-cog fa-lg" aria-hidden="true"></i>
</button>
<div class="dropdown-menu dropdown-menu-right">
<ng-container *ngIf="o.useSso && o.identifier">
<a *ngIf="o.ssoBound; else linkSso" class="dropdown-item" href="#" appStopClick
(click)="unlinkSso(o)">
<i class="fa fa-fw fa-chain-broken" aria-hidden="true"></i>
Unlink SSO
</a>
<ng-template #linkSso>
<app-link-sso [organization]="o">
</app-link-sso>
</ng-template>
</ng-container>
<a class="dropdown-item text-danger" href="#" appStopClick (click)="leave(o)">
<i class="fa fa-fw fa-sign-out" aria-hidden="true"></i>
{{'leave' | i18n}}

View File

@ -35,6 +35,7 @@ export class OrganizationsComponent implements OnInit {
async ngOnInit() {
if (!this.vault) {
await this.syncService.fullSync(true);
await this.load();
}
}
@ -46,6 +47,25 @@ export class OrganizationsComponent implements OnInit {
this.loaded = true;
}
async unlinkSso(org: Organization) {
const confirmed = await this.platformUtilsService.showDialog(
'Are you sure you want to unlink SSO for this organization?', org.name,
this.i18nService.t('yes'), this.i18nService.t('no'), 'warning');
if (!confirmed) {
return false;
}
try {
this.actionPromise = this.apiService.deleteSsoUser(org.id).then(() => {
return this.syncService.fullSync(true);
});
await this.actionPromise;
this.analytics.eventTrack.next({ action: 'Unlinked SSO' });
this.toasterService.popAsync('success', null, 'Unlinked SSO');
await this.load();
} catch { }
}
async leave(org: Organization) {
const confirmed = await this.platformUtilsService.showDialog(
this.i18nService.t('leaveOrganizationConfirmation'), org.name,

View File

@ -9,6 +9,13 @@ document.addEventListener('DOMContentLoaded', (event) => {
initiateBrowserSso(code, state);
} else {
window.location.href = window.location.origin + '/#/sso?code=' + code + '&state=' + state;
// Match any characters between "_returnUri='" and the next "'"
const returnUri = extractFromRegex(state, '(?<=_returnUri=\')(.*)(?=\')');
if (returnUri) {
window.location.href = window.location.origin + `/#${returnUri}`;
} else {
window.location.href = window.location.origin + '/#/sso?code=' + code + '&state=' + state;
}
}
});
@ -31,3 +38,14 @@ function getQsParam(name: string) {
function initiateBrowserSso(code: string, state: string) {
window.postMessage({ command: 'authResult', code: code, state: state }, '*');
}
function extractFromRegex(s: string, regexString: string) {
const regex = new RegExp(regexString);
const results = regex.exec(s);
if (!results) {
return null;
}
return results[0];
}