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:
parent
bc71ffa6f2
commit
e17a49acd5
2
jslib
2
jslib
|
@ -1 +1 @@
|
||||||
Subproject commit e55528e61737635e7f8970b913bcc3f10bede85d
|
Subproject commit e07526a1b6cae520561e452a6695a1508d785585
|
|
@ -109,6 +109,7 @@ import { CreateOrganizationComponent } from './settings/create-organization.comp
|
||||||
import { DeauthorizeSessionsComponent } from './settings/deauthorize-sessions.component';
|
import { DeauthorizeSessionsComponent } from './settings/deauthorize-sessions.component';
|
||||||
import { DeleteAccountComponent } from './settings/delete-account.component';
|
import { DeleteAccountComponent } from './settings/delete-account.component';
|
||||||
import { DomainRulesComponent } from './settings/domain-rules.component';
|
import { DomainRulesComponent } from './settings/domain-rules.component';
|
||||||
|
import { LinkSsoComponent } from './settings/link-sso.component';
|
||||||
import { OptionsComponent } from './settings/options.component';
|
import { OptionsComponent } from './settings/options.component';
|
||||||
import { OrganizationPlansComponent } from './settings/organization-plans.component';
|
import { OrganizationPlansComponent } from './settings/organization-plans.component';
|
||||||
import { OrganizationsComponent } from './settings/organizations.component';
|
import { OrganizationsComponent } from './settings/organizations.component';
|
||||||
|
@ -295,6 +296,7 @@ registerLocaleData(localeZhTw, 'zh-TW');
|
||||||
ImportComponent,
|
ImportComponent,
|
||||||
InactiveTwoFactorReportComponent,
|
InactiveTwoFactorReportComponent,
|
||||||
InputVerbatimDirective,
|
InputVerbatimDirective,
|
||||||
|
LinkSsoComponent,
|
||||||
LockComponent,
|
LockComponent,
|
||||||
LoginComponent,
|
LoginComponent,
|
||||||
ModalComponent,
|
ModalComponent,
|
||||||
|
|
|
@ -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>
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -74,6 +74,17 @@
|
||||||
<i class="fa fa-cog fa-lg" aria-hidden="true"></i>
|
<i class="fa fa-cog fa-lg" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown-menu dropdown-menu-right">
|
<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)">
|
<a class="dropdown-item text-danger" href="#" appStopClick (click)="leave(o)">
|
||||||
<i class="fa fa-fw fa-sign-out" aria-hidden="true"></i>
|
<i class="fa fa-fw fa-sign-out" aria-hidden="true"></i>
|
||||||
{{'leave' | i18n}}
|
{{'leave' | i18n}}
|
||||||
|
|
|
@ -35,6 +35,7 @@ export class OrganizationsComponent implements OnInit {
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
if (!this.vault) {
|
if (!this.vault) {
|
||||||
|
await this.syncService.fullSync(true);
|
||||||
await this.load();
|
await this.load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,6 +47,25 @@ export class OrganizationsComponent implements OnInit {
|
||||||
this.loaded = true;
|
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) {
|
async leave(org: Organization) {
|
||||||
const confirmed = await this.platformUtilsService.showDialog(
|
const confirmed = await this.platformUtilsService.showDialog(
|
||||||
this.i18nService.t('leaveOrganizationConfirmation'), org.name,
|
this.i18nService.t('leaveOrganizationConfirmation'), org.name,
|
||||||
|
|
|
@ -9,6 +9,13 @@ document.addEventListener('DOMContentLoaded', (event) => {
|
||||||
initiateBrowserSso(code, state);
|
initiateBrowserSso(code, state);
|
||||||
} else {
|
} else {
|
||||||
window.location.href = window.location.origin + '/#/sso?code=' + code + '&state=' + state;
|
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) {
|
function initiateBrowserSso(code: string, state: string) {
|
||||||
window.postMessage({ command: 'authResult', code: code, state: state }, '*');
|
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];
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue