mirror of https://github.com/rancher/ui.git
Adds google auth
This commit is contained in:
parent
7752b38e9b
commit
dfac8fb538
|
|
@ -79,6 +79,7 @@ const App = Application.extend({
|
||||||
'digitalOcean',
|
'digitalOcean',
|
||||||
'endpoint',
|
'endpoint',
|
||||||
'github',
|
'github',
|
||||||
|
'google',
|
||||||
'globalStore',
|
'globalStore',
|
||||||
'intl',
|
'intl',
|
||||||
'modal',
|
'modal',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Resource from '@rancher/ember-api-store/models/resource';
|
||||||
|
|
||||||
|
var GoogleOauthConfig = Resource.extend({ type: 'googleOauthConfig', });
|
||||||
|
|
||||||
|
export default GoogleOauthConfig;
|
||||||
|
|
@ -67,6 +67,10 @@
|
||||||
&.freeipa {
|
&.freeipa {
|
||||||
background-image: url('images/providers/provider-freeipa.svg');
|
background-image: url('images/providers/provider-freeipa.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.googleoauth {
|
||||||
|
background-image: url('images/providers/provider-google.svg');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.advanced-setting {
|
.advanced-setting {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ const allowedForwards = ['localhost'];
|
||||||
|
|
||||||
export default Route.extend(VerifyAuth, {
|
export default Route.extend(VerifyAuth, {
|
||||||
github: service(),
|
github: service(),
|
||||||
|
google: service(),
|
||||||
intl: service(),
|
intl: service(),
|
||||||
language: service('user-language'),
|
language: service('user-language'),
|
||||||
|
|
||||||
|
|
@ -23,6 +24,7 @@ export default Route.extend(VerifyAuth, {
|
||||||
|
|
||||||
model(params/* , transition */) {
|
model(params/* , transition */) {
|
||||||
const github = get(this, 'github');
|
const github = get(this, 'github');
|
||||||
|
const google = get(this, 'google');
|
||||||
const code = get(params, 'code');
|
const code = get(params, 'code');
|
||||||
const forward = get(params, 'forward');
|
const forward = get(params, 'forward');
|
||||||
|
|
||||||
|
|
@ -50,11 +52,13 @@ export default Route.extend(VerifyAuth, {
|
||||||
|
|
||||||
if ( window.opener && !get(params, 'login') && !get(params, 'errorCode') ) {
|
if ( window.opener && !get(params, 'login') && !get(params, 'errorCode') ) {
|
||||||
let openersGithub = window.opener.ls('github');
|
let openersGithub = window.opener.ls('github');
|
||||||
|
let openersGoogle = window.opener.ls('google');
|
||||||
let openerStore = window.opener.ls('globalStore');
|
let openerStore = window.opener.ls('globalStore');
|
||||||
let qp = get(params, 'config') || get(params, 'authProvider');
|
let qp = get(params, 'config') || get(params, 'authProvider');
|
||||||
let type = `${ qp }Config`;
|
let type = `${ qp }Config`;
|
||||||
let config = openerStore.getById(type, qp);
|
let config = openerStore.getById(type, qp);
|
||||||
let gh = get(this, 'github');
|
let gh = get(this, 'github');
|
||||||
|
let go = get(this, 'google');
|
||||||
let stateMsg = 'Authorization state did not match, please try again.';
|
let stateMsg = 'Authorization state did not match, please try again.';
|
||||||
|
|
||||||
if ( get(params, 'config') === 'github' ) {
|
if ( get(params, 'config') === 'github' ) {
|
||||||
|
|
@ -63,6 +67,12 @@ export default Route.extend(VerifyAuth, {
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
this.send('gotError', err);
|
this.send('gotError', err);
|
||||||
});
|
});
|
||||||
|
} else if ( get(params, 'config') === 'googleoauth') {
|
||||||
|
return go.testConfig(config).then((resp) => {
|
||||||
|
go.authorize(resp, openersGoogle.get('state'));
|
||||||
|
}).catch((err) => {
|
||||||
|
this.send('gotError', err)
|
||||||
|
})
|
||||||
} else if ( samlProviders.includes(get(params, 'config')) ) {
|
} else if ( samlProviders.includes(get(params, 'config')) ) {
|
||||||
if ( window.opener.window.onAuthTest ) {
|
if ( window.opener.window.onAuthTest ) {
|
||||||
reply(null, config);
|
reply(null, config);
|
||||||
|
|
@ -72,7 +82,9 @@ export default Route.extend(VerifyAuth, {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( get(params, 'code') ) {
|
if ( get(params, 'code') ) {
|
||||||
if ( openersGithub.stateMatches(get(params, 'state')) ) {
|
const currentOpener = openersGithub.state ? openersGithub : openersGoogle;
|
||||||
|
|
||||||
|
if ( currentOpener.stateMatches(get(params, 'state')) ) {
|
||||||
reply(params.error_description, params.code);
|
reply(params.error_description, params.code);
|
||||||
} else {
|
} else {
|
||||||
reply(stateMsg);
|
reply(stateMsg);
|
||||||
|
|
@ -87,11 +99,13 @@ export default Route.extend(VerifyAuth, {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( code && get(params, 'login') ) {
|
if ( code && get(params, 'login') || get(params, 'state').includes('login') ) {
|
||||||
if ( github.stateMatches(get(params, 'state')) ) {
|
let currentProvider = github.stateMatches(get(params, 'state')) ? 'github' : 'googleoauth'
|
||||||
let ghProvider = get(this, 'access.providers').findBy('id', 'github');
|
|
||||||
|
|
||||||
return ghProvider.doAction('login', {
|
if ( github.stateMatches(get(params, 'state')) || google.stateMatches(get(params, 'state')) ) {
|
||||||
|
currentProvider = get(this, 'access.providers').findBy('id', currentProvider);
|
||||||
|
|
||||||
|
return currentProvider.doAction('login', {
|
||||||
code,
|
code,
|
||||||
responseType: 'cookie',
|
responseType: 'cookie',
|
||||||
description: C.SESSION.DESCRIPTION,
|
description: C.SESSION.DESCRIPTION,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ const Eng = Engine.extend({
|
||||||
'digitalOcean',
|
'digitalOcean',
|
||||||
'endpoint',
|
'endpoint',
|
||||||
'github',
|
'github',
|
||||||
|
'google',
|
||||||
'globalStore',
|
'globalStore',
|
||||||
'intl',
|
'intl',
|
||||||
'modal',
|
'modal',
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ export default Mixin.create({
|
||||||
this.send('clearError');
|
this.send('clearError');
|
||||||
|
|
||||||
const model = get(this, 'authConfig');
|
const model = get(this, 'authConfig');
|
||||||
const accessMode = get(model, 'id') === 'github' ? 'restricted' : 'unrestricted';
|
const accessMode = get(model, 'id') === 'github' || 'googleoauth' ? 'restricted' : 'unrestricted';
|
||||||
|
|
||||||
setProperties(model, {
|
setProperties(model, {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ export default buildRoutes(function() {
|
||||||
this.route('adfs');
|
this.route('adfs');
|
||||||
this.route('okta');
|
this.route('okta');
|
||||||
this.route('freeipa');
|
this.route('freeipa');
|
||||||
|
this.route('googleoauth');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.route('cloud-credentials', function() {
|
this.route('cloud-credentials', function() {
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,13 @@ export default Controller.extend({
|
||||||
available: this.hasRecord('openldapconfig'),
|
available: this.hasRecord('openldapconfig'),
|
||||||
providerType: 'ldap',
|
providerType: 'ldap',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
route: 'security.authentication.googleoauth',
|
||||||
|
label: 'Google',
|
||||||
|
css: 'googleoauth',
|
||||||
|
available: this.hasRecord('googleoauthconfig'),
|
||||||
|
providerType: null,
|
||||||
|
},
|
||||||
// {route: 'security.authentication.shibboleth', label: 'Shibboleth', css: 'shibboleth', available: this.hasRecord('shibbolethconfig') },
|
// {route: 'security.authentication.shibboleth', label: 'Shibboleth', css: 'shibboleth', available: this.hasRecord('shibbolethconfig') },
|
||||||
];
|
];
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { get, set, computed, setProperties } from '@ember/object';
|
||||||
|
import { alias } from '@ember/object/computed';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
import Controller from '@ember/controller';
|
||||||
|
import AuthMixin from 'global-admin/mixins/authentication';
|
||||||
|
|
||||||
|
export default Controller.extend(AuthMixin, {
|
||||||
|
google: service(),
|
||||||
|
endpoint: service(),
|
||||||
|
access: service(),
|
||||||
|
settings: service(),
|
||||||
|
|
||||||
|
confirmDisable: false,
|
||||||
|
errors: null,
|
||||||
|
testing: false,
|
||||||
|
error: null,
|
||||||
|
saved: false,
|
||||||
|
saving: false,
|
||||||
|
haveToken: false,
|
||||||
|
|
||||||
|
organizations: null,
|
||||||
|
secure: true,
|
||||||
|
|
||||||
|
authConfig: alias('model.googleConfig'),
|
||||||
|
isEnabled: alias('authConfig.enabled'),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
save() {
|
||||||
|
this.send('clearError');
|
||||||
|
set(this, 'saving', true);
|
||||||
|
|
||||||
|
const authConfig = get(this, 'authConfig');
|
||||||
|
const am = 'unrestricted';
|
||||||
|
|
||||||
|
setProperties(authConfig, {
|
||||||
|
'oauthCredential': (authConfig.get('oauthCredential') || '').trim(),
|
||||||
|
'serviceAccountCredential': (authConfig.get('serviceAccountCredential') || '').trim(),
|
||||||
|
'adminEmail': (authConfig.get('adminEmail') || '').trim(),
|
||||||
|
'hostname': (authConfig.get('hostname') || '').trim(),
|
||||||
|
'enabled': false,
|
||||||
|
'accessMode': am,
|
||||||
|
'tls': true,
|
||||||
|
'allowedPrincipalIds': [],
|
||||||
|
});
|
||||||
|
|
||||||
|
set(this, '_boundSucceed', this.authenticationApplied.bind(this));
|
||||||
|
get(this, 'google').test(authConfig, get(this, '_boundSucceed'));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
destinationUrl: computed(() => {
|
||||||
|
return `${ window.location.origin }/`;
|
||||||
|
}),
|
||||||
|
|
||||||
|
destinationDomain: computed(() => {
|
||||||
|
return `${ window.location.hostname }`
|
||||||
|
}),
|
||||||
|
|
||||||
|
redirectURI: computed(() => {
|
||||||
|
return `${ window.location.origin }/verify-auth`
|
||||||
|
}),
|
||||||
|
|
||||||
|
authenticationApplied(err) {
|
||||||
|
set(this, 'saving', false);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
set(this, 'isEnabled', false);
|
||||||
|
this.send('gotError', err);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.send('clearError');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
import Route from '@ember/routing/route';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
import { get } from '@ember/object';
|
||||||
|
import { hash } from 'rsvp';
|
||||||
|
|
||||||
|
export default Route.extend({
|
||||||
|
globalStore: service(),
|
||||||
|
|
||||||
|
model() {
|
||||||
|
let gs = get(this, 'globalStore');
|
||||||
|
|
||||||
|
return hash({
|
||||||
|
googleConfig: gs.find('authconfig', 'googleoauth', { forceReload: true }),
|
||||||
|
principals: gs.all('principal')
|
||||||
|
}).catch((e) => {
|
||||||
|
return e;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
setupController(controller, model) {
|
||||||
|
controller.setProperties({
|
||||||
|
model,
|
||||||
|
confirmDisable: false,
|
||||||
|
testing: false,
|
||||||
|
organizations: get(this, 'session.orgs') || [],
|
||||||
|
errors: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
controller.set('saved', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,210 @@
|
||||||
|
<section>
|
||||||
|
{{#unless isEnabled}}
|
||||||
|
<div class="banner bg-warning">
|
||||||
|
<div class="banner-icon"><span class="icon icon-alert"></span></div>
|
||||||
|
<div class="banner-message">
|
||||||
|
<p>{{t 'authPage.google.header.disabled.label'}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/unless}}
|
||||||
|
{{top-errors errors=errors}}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{{#accordion-list showExpandAll=false as |al expandFn|}}
|
||||||
|
|
||||||
|
{{#if isEnabled}}
|
||||||
|
{{#accordion-list-item
|
||||||
|
expand=(action expandFn)
|
||||||
|
expandAll=al.expandAll
|
||||||
|
expandOnInit=true
|
||||||
|
expanded=true
|
||||||
|
showExpand=false
|
||||||
|
title=(t 'authPage.google.authenticated.header.text')
|
||||||
|
}}
|
||||||
|
<section class="">
|
||||||
|
<div class="clearfix">
|
||||||
|
<div class="pull-right">
|
||||||
|
<button class="btn btn-sm right-divider-btn bg-error" {{action "disable"}}>
|
||||||
|
{{t 'authPage.google.authenticated.disableAccess.disable'}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div><b>{{t 'authPage.google.authenticated.header.adminEmail.text'}} </b> <span class="text-muted">{{authConfig.adminEmail}}</span></div>
|
||||||
|
</section>
|
||||||
|
{{/accordion-list-item}}
|
||||||
|
|
||||||
|
{{#accordion-list-item
|
||||||
|
classNames="mt-30"
|
||||||
|
detail=(t 'siteAccess.helpText' appName=settings.appName htmlSafe=true)
|
||||||
|
expand=(action expandFn)
|
||||||
|
expandAll=al.expandAll
|
||||||
|
expandOnInit=true
|
||||||
|
expanded=true
|
||||||
|
showExpand=false
|
||||||
|
title=(t 'siteAccess.header')
|
||||||
|
}}
|
||||||
|
{{site-access
|
||||||
|
model=authConfig
|
||||||
|
principals=model.principals
|
||||||
|
collection='siteAccess.organizations'
|
||||||
|
}}
|
||||||
|
{{/accordion-list-item}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#unless isEnabled}}
|
||||||
|
{{#accordion-list-item
|
||||||
|
expand=(action expandFn)
|
||||||
|
expandAll=al.expandAll
|
||||||
|
expandOnInit=true
|
||||||
|
expanded=true
|
||||||
|
showExpand=false
|
||||||
|
title=(t 'authPage.google.notAuthenticated.header')
|
||||||
|
}}
|
||||||
|
<section class="">
|
||||||
|
<p>
|
||||||
|
<ol class="alphalist ml-40">
|
||||||
|
<li>
|
||||||
|
{{t 'authPage.google.notAuthenticated.ul.li1.text' htmlSafe=true}}
|
||||||
|
<ul>
|
||||||
|
<li>{{t 'authPage.google.notAuthenticated.ul.li1.ul.li1'}}</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
{{t 'authPage.google.notAuthenticated.ul.li2.text'}}
|
||||||
|
<ul>
|
||||||
|
<li>{{t 'authPage.google.notAuthenticated.ul.li2.ul.li1' appName=settings.appName htmlSafe=true}}
|
||||||
|
<span>{{destinationDomain}}{{copy-to-clipboard size='small' clipboardText=destinationUrl htmlSafe=true}}</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>{{t 'authPage.google.notAuthenticated.ul.li2.ul.li2' htmlSafe=true}}</b> <span>{{destinationUrl}}{{copy-to-clipboard size='small' clipboardText=destinationUrl htmlSafe=true}}</span>
|
||||||
|
</li>
|
||||||
|
<li>{{t 'authPage.google.notAuthenticated.ul.li2.ul.li3'}}</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
{{t 'authPage.google.notAuthenticated.ul.li3.text'}}
|
||||||
|
<ul>
|
||||||
|
<li>{{t 'authPage.google.notAuthenticated.ul.li3.ul.li1'}}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>{{t 'authPage.google.notAuthenticated.ul.li3.ul.li2' htmlSafe=true}}</b> <span>{{destinationUrl}}{{copy-to-clipboard size='small' clipboardText=destinationUrl htmlSafe=true}}</span>
|
||||||
|
</li>
|
||||||
|
<li>{{t 'authPage.google.notAuthenticated.ul.li3.ul.li3' htmlSafe=true}}
|
||||||
|
<span>{{redirectURI}}{{copy-to-clipboard size='small' clipboardText=destinationUrl htmlSafe=true}}</span>
|
||||||
|
</li>
|
||||||
|
<li>{{t 'authPage.google.notAuthenticated.ul.li3.ul.li4'}}</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
{{t 'authPage.google.notAuthenticated.ul.li4.text'}}
|
||||||
|
<ul>
|
||||||
|
<li>{{t 'authPage.google.notAuthenticated.ul.li4.ul.li1'}}
|
||||||
|
</li>
|
||||||
|
<li>{{t 'authPage.google.notAuthenticated.ul.li4.ul.li2'}}</li>
|
||||||
|
<li>{{t 'authPage.google.notAuthenticated.ul.li4.ul.li3'}}</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
{{/accordion-list-item}}
|
||||||
|
|
||||||
|
{{#accordion-list-item
|
||||||
|
expand=(action expandFn)
|
||||||
|
expandAll=al.expandAll
|
||||||
|
expandOnInit=true
|
||||||
|
expanded=true
|
||||||
|
showExpand=false
|
||||||
|
title=(t 'authPage.google.notAuthenticated.form.header' appName=settings.appName)
|
||||||
|
}}
|
||||||
|
<form autcomplete="on">
|
||||||
|
<section class="">
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-6">
|
||||||
|
<div class="inline-form">
|
||||||
|
<label class="acc-label pb-5">{{t 'authPage.google.notAuthenticated.form.adminEmail.labelText'}}{{field-required}}</label>
|
||||||
|
{{input type="text" name="username" value=authConfig.adminEmail classNames="form-control"}}
|
||||||
|
<p class="help-block">{{t 'authPage.google.notAuthenticated.form.adminEmail.helperText'}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col span-6">
|
||||||
|
<div class="inline-form">
|
||||||
|
<label class="acc-label pb-5">{{t 'authPage.google.notAuthenticated.form.hostname.labelText'}}{{field-required}}</label>
|
||||||
|
{{input type="text" value=authConfig.hostname classNames="form-control"}}
|
||||||
|
<p class="help-block">{{t 'authPage.google.notAuthenticated.form.hostname.helperText'}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
{{#input-text-file
|
||||||
|
classNames="box"
|
||||||
|
label="authPage.google.notAuthenticated.form.oauthCredential.labelText"
|
||||||
|
value=authConfig.oauthCredential
|
||||||
|
accept="text/*, .json"
|
||||||
|
minHeight=60
|
||||||
|
canChangeName=false
|
||||||
|
nameRequired=true
|
||||||
|
placeholder="authPage.google.notAuthenticated.form.oauthCredential.labelText"
|
||||||
|
concealValue=true
|
||||||
|
as |section|
|
||||||
|
}}
|
||||||
|
{{#if (eq section "description")}}
|
||||||
|
<div class="row help">
|
||||||
|
<div class="col span-12 help-block wrap mb-0">
|
||||||
|
{{t "authPage.google.notAuthenticated.form.oauthCredential.helperText" htmlSafe=true}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/input-text-file}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-12">
|
||||||
|
{{#input-text-file
|
||||||
|
classNames="box"
|
||||||
|
label="authPage.google.notAuthenticated.form.serviceAccountCredential.labelText"
|
||||||
|
value=authConfig.serviceAccountCredential
|
||||||
|
accept="text/*, .json"
|
||||||
|
minHeight=60
|
||||||
|
canChangeName=false
|
||||||
|
nameRequired=true
|
||||||
|
placeholder="authPage.google.notAuthenticated.form.serviceAccountCredential.labelText"
|
||||||
|
concealValue=true
|
||||||
|
as |section|
|
||||||
|
}}
|
||||||
|
{{#if (eq section "description")}}
|
||||||
|
<div class="row help">
|
||||||
|
<div class="col span-12 help-block wrap mb-0">
|
||||||
|
{{t "authPage.google.notAuthenticated.form.serviceAccountCredential.helperText" htmlSafe=true}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{/input-text-file}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row text-center">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button class="btn bg-primary" {{action "save"}}>
|
||||||
|
{{#if saving}}
|
||||||
|
<i class="icon icon-spinner icon-spin"></i> {{t 'authPage.google.testAuth.buttonText.post'}}
|
||||||
|
{{else if doneSaving}}
|
||||||
|
{{t 'generic.saved'}}
|
||||||
|
{{else}}
|
||||||
|
<i class="icon icon-github"></i> {{t 'authPage.google.testAuth.buttonText.pre'}}
|
||||||
|
{{/if}}
|
||||||
|
</button>
|
||||||
|
<button {{action "cancel"}} class="btn bg-transparent">{{t 'saveCancel.cancel'}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
{{/accordion-list-item}}
|
||||||
|
{{/unless}}
|
||||||
|
{{/accordion-list}}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from 'global-admin/security/authentication/googleoauth/controller';
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from './global-admin/security/authentication/googleoauth/route';
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
import Component from '@ember/component';
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
google: service(),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
authenticate() {
|
||||||
|
this.get('google').login();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<form {{action "authenticate" on="submit"}} class="row pt-10 pb-10">
|
||||||
|
<button disabled={{waiting}} class="btn bg-primary" {{action "authenticate"}}>
|
||||||
|
<i class="icon icon-github"></i> {{t 'loginGoogle.buttonText'}}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
@ -150,6 +150,10 @@ export default Controller.extend({
|
||||||
return !!get(this, 'access.providers').findBy('id', 'github');
|
return !!get(this, 'access.providers').findBy('id', 'github');
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
isGoogle: computed('access.providers', function() {
|
||||||
|
return !!get(this, 'access.providers').findBy('id', 'googleoauth');
|
||||||
|
}),
|
||||||
|
|
||||||
isPing: computed('access.providers', function() {
|
isPing: computed('access.providers', function() {
|
||||||
return !!get(this, 'access.providers').findBy('id', 'ping');
|
return !!get(this, 'access.providers').findBy('id', 'ping');
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,10 @@
|
||||||
{{login-github action=(action "started")}}
|
{{login-github action=(action "started")}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isGoogle}}
|
||||||
|
{{login-google action=(action "started")}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{#if isShibboleth}}
|
{{#if isShibboleth}}
|
||||||
<p>{{t "loginPage.shibbolethMessage" appName=settings.appName}}</p>
|
<p>{{t "loginPage.shibbolethMessage" appName=settings.appName}}</p>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
import Service, { inject as service } from '@ember/service';
|
||||||
|
import { addQueryParam, addQueryParams, popupWindowOptions } from 'shared/utils/util';
|
||||||
|
import { get, set } from '@ember/object';
|
||||||
|
import C from 'shared/utils/constants';
|
||||||
|
|
||||||
|
const googleOauthScope = 'openid profile email https://www.googleapis.com/auth/admin.directory.user.readonly https://www.googleapis.com/auth/admin.directory.group.readonly'
|
||||||
|
|
||||||
|
export default Service.extend({
|
||||||
|
access: service(),
|
||||||
|
cookies: service(),
|
||||||
|
session: service(),
|
||||||
|
globalStore: service(),
|
||||||
|
app: service(),
|
||||||
|
intl: service(),
|
||||||
|
|
||||||
|
generateState() {
|
||||||
|
return set(this, 'session.googleState', `${ Math.random() }`);
|
||||||
|
},
|
||||||
|
|
||||||
|
generateLoginStateKey() {
|
||||||
|
return set(this, 'session.googleState', `${ Math.random() }login`)
|
||||||
|
},
|
||||||
|
|
||||||
|
stateMatches(actual) {
|
||||||
|
return actual && get(this, 'session.googleState') === actual;
|
||||||
|
},
|
||||||
|
|
||||||
|
testConfig(config) {
|
||||||
|
return config.doAction('configureTest', config);
|
||||||
|
},
|
||||||
|
|
||||||
|
saveConfig(config, opt) {
|
||||||
|
return config.doAction('testAndApply', opt);
|
||||||
|
},
|
||||||
|
|
||||||
|
authorize(auth, state) {
|
||||||
|
const url = addQueryParams(get(auth, 'redirectUrl'), {
|
||||||
|
scope: googleOauthScope,
|
||||||
|
redirect_uri: `${ window.location.origin }/verify-auth`,
|
||||||
|
state,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return window.location.href = url;
|
||||||
|
},
|
||||||
|
|
||||||
|
login(forwardUrl) {
|
||||||
|
const provider = get(this, 'access.providers').findBy('id', 'googleoauth');
|
||||||
|
const authRedirect = get(provider, 'redirectUrl');
|
||||||
|
let redirect = `${ window.location.origin }/verify-auth`;
|
||||||
|
|
||||||
|
if ( forwardUrl ) {
|
||||||
|
redirect = addQueryParam(redirect, 'forward', forwardUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = addQueryParams(authRedirect, {
|
||||||
|
scope: googleOauthScope,
|
||||||
|
state: this.generateLoginStateKey(),
|
||||||
|
redirect_uri: redirect,
|
||||||
|
});
|
||||||
|
|
||||||
|
window.location.href = url;
|
||||||
|
},
|
||||||
|
|
||||||
|
test(config, cb) {
|
||||||
|
let responded = false;
|
||||||
|
|
||||||
|
window.onAuthTest = (err, code) => {
|
||||||
|
if ( !responded ) {
|
||||||
|
let googleConfig = config;
|
||||||
|
|
||||||
|
responded = true;
|
||||||
|
|
||||||
|
this.finishTest(googleConfig, code, cb);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
set(this, 'state', this.generateState());
|
||||||
|
|
||||||
|
let url = addQueryParams(`${ window.location.origin }/verify-auth`, { config: 'googleoauth', });
|
||||||
|
|
||||||
|
const popup = window.open(url, 'rancherAuth', popupWindowOptions());
|
||||||
|
const intl = get(this, 'intl');
|
||||||
|
|
||||||
|
let timer = setInterval(() => {
|
||||||
|
if (popup && popup.closed ) {
|
||||||
|
clearInterval(timer);
|
||||||
|
|
||||||
|
if ( !responded ) {
|
||||||
|
responded = true;
|
||||||
|
cb({
|
||||||
|
type: 'error',
|
||||||
|
message: intl.t('authPage.google.testAuth.authError')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (popup === null || typeof (popup) === 'undefined') {
|
||||||
|
clearInterval(timer);
|
||||||
|
|
||||||
|
if ( !responded ) {
|
||||||
|
responded = true;
|
||||||
|
|
||||||
|
cb({
|
||||||
|
type: 'error',
|
||||||
|
message: intl.t('authPage.google.testAuth.popupError')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
},
|
||||||
|
|
||||||
|
finishTest(config, code, cb) {
|
||||||
|
const goConfig = config;
|
||||||
|
|
||||||
|
set(goConfig, 'enabled', true);
|
||||||
|
|
||||||
|
let out = {
|
||||||
|
code,
|
||||||
|
enabled: true,
|
||||||
|
googleOauthConfig: goConfig,
|
||||||
|
description: C.SESSION.DESCRIPTION,
|
||||||
|
ttl: C.SESSION.TTL,
|
||||||
|
};
|
||||||
|
|
||||||
|
const allowedPrincipalIds = get(config, 'allowedPrincipalIds') || [];
|
||||||
|
|
||||||
|
return this.saveConfig(config, out).then(() => {
|
||||||
|
let found = false;
|
||||||
|
const myPIds = get(this, 'access.me.principalIds');
|
||||||
|
|
||||||
|
myPIds.forEach( (id) => {
|
||||||
|
if (allowedPrincipalIds.indexOf(id) >= 0) {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if ( !found && !allowedPrincipalIds.length) {
|
||||||
|
allowedPrincipalIds.pushObject(get(this, 'access.principal.id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return goConfig.save().then(() => {
|
||||||
|
window.location.href = window.location.href;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
cb(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export { default } from 'shared/google/service';
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 48 48" version="1.1" width="48px" height="48px">
|
||||||
|
<g id="surface1">
|
||||||
|
<path style=" fill:#FFC107;" d="M 43.609375 20.082031 L 42 20.082031 L 42 20 L 24 20 L 24 28 L 35.304688 28 C 33.652344 32.65625 29.222656 36 24 36 C 17.371094 36 12 30.628906 12 24 C 12 17.371094 17.371094 12 24 12 C 27.058594 12 29.84375 13.152344 31.960938 15.039063 L 37.617188 9.382813 C 34.046875 6.054688 29.269531 4 24 4 C 12.953125 4 4 12.953125 4 24 C 4 35.046875 12.953125 44 24 44 C 35.046875 44 44 35.046875 44 24 C 44 22.660156 43.863281 21.351563 43.609375 20.082031 Z "/>
|
||||||
|
<path style=" fill:#FF3D00;" d="M 6.304688 14.691406 L 12.878906 19.511719 C 14.65625 15.109375 18.960938 12 24 12 C 27.058594 12 29.84375 13.152344 31.960938 15.039063 L 37.617188 9.382813 C 34.046875 6.054688 29.269531 4 24 4 C 16.316406 4 9.65625 8.335938 6.304688 14.691406 Z "/>
|
||||||
|
<path style=" fill:#4CAF50;" d="M 24 44 C 29.164063 44 33.859375 42.023438 37.410156 38.808594 L 31.21875 33.570313 C 29.210938 35.089844 26.714844 36 24 36 C 18.796875 36 14.382813 32.683594 12.71875 28.054688 L 6.195313 33.078125 C 9.503906 39.554688 16.226563 44 24 44 Z "/>
|
||||||
|
<path style=" fill:#1976D2;" d="M 43.609375 20.082031 L 42 20.082031 L 42 20 L 24 20 L 24 28 L 35.304688 28 C 34.511719 30.238281 33.070313 32.164063 31.214844 33.570313 C 31.21875 33.570313 31.21875 33.570313 31.21875 33.570313 L 37.410156 38.808594 C 36.972656 39.203125 44 34 44 24 C 44 22.660156 43.863281 21.351563 43.609375 20.082031 Z "/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
|
|
@ -520,6 +520,68 @@ authPage:
|
||||||
post: Waiting to hear back from GitHub
|
post: Waiting to hear back from GitHub
|
||||||
authError: 'Github access was not authorized'
|
authError: 'Github access was not authorized'
|
||||||
popupError: 'Please disable your pop-up blocker and click "Authenticate" again.'
|
popupError: 'Please disable your pop-up blocker and click "Authenticate" again.'
|
||||||
|
google:
|
||||||
|
header:
|
||||||
|
disabled:
|
||||||
|
label: 'Google is not configured'
|
||||||
|
authenticated:
|
||||||
|
header:
|
||||||
|
text: Authentication
|
||||||
|
adminEmail:
|
||||||
|
text: "Admin Email: "
|
||||||
|
disableAccess:
|
||||||
|
header: "Danger Zone™"
|
||||||
|
warning: '<b class="text-danger">Caution:</b> Disabling access control will give complete control over {appName} to anyone that can reach this page or the API.'
|
||||||
|
confirmDisable: "Are you sure? Click again to disable access control"
|
||||||
|
disable: Disable Google access
|
||||||
|
notAuthenticated:
|
||||||
|
header: "1. Configure your Google Application settings"
|
||||||
|
ul:
|
||||||
|
li1:
|
||||||
|
text: 'For standard Google, <a href="https://console.developers.google.com/apis/credentials" target="_blank">click here</a> to go to your Google developer console.'
|
||||||
|
ul:
|
||||||
|
li1: 'Login to your account. Navigate to "APIs & Services" and then select "Credentials".'
|
||||||
|
li2:
|
||||||
|
text: 'Navigate to the "OAuth consent screen" tab and fill in the form:'
|
||||||
|
ul:
|
||||||
|
li1: '<b>Authorized domains:</b> '
|
||||||
|
li2: '<b>Application homepage link:</b> '
|
||||||
|
li3: 'Enable "email", "profile", and "openid" to Scopes for Google APIs.'
|
||||||
|
li4: 'Authorization callback URL:'
|
||||||
|
li3:
|
||||||
|
text: 'Navigate to the "Credentials" tab to create your OAuth client ID:'
|
||||||
|
ul:
|
||||||
|
li1: 'Select "Create Credentials", select OAuth clientID, then select Web application.'
|
||||||
|
li2: '<b>Authorized Javascript origins:</b> '
|
||||||
|
li3: '<b>Authorized redirect URIs:</b> '
|
||||||
|
li4: 'Click "save" and then download JSON.'
|
||||||
|
li4:
|
||||||
|
text: 'Navigate to the "Credentials" tab again to create your Service account key:'
|
||||||
|
ul:
|
||||||
|
li1: 'Select your service account.'
|
||||||
|
li2: 'Select JSON for your Key type and then click "Create".'
|
||||||
|
li3: 'A JSON file will be automatically saved locally.'
|
||||||
|
form:
|
||||||
|
header: '2. Configure {appName} to use your application for authentication'
|
||||||
|
oauthCredential:
|
||||||
|
labelText: OAuth Credentials
|
||||||
|
helperText: Copy and paste in the OAuth Credentials JSON which can be found in your Google API developers console (Step 3 above).
|
||||||
|
serviceAccountCredential:
|
||||||
|
labelText: Service Account Credentials
|
||||||
|
helperText: Copy and paste in the Service Account Credentials JSON which can be found in the service accounts section of the Google API developers console (Step 4 above).
|
||||||
|
adminEmail:
|
||||||
|
labelText: Admin Email
|
||||||
|
helperText: Enter the Admin Email associated with your account.
|
||||||
|
hostname:
|
||||||
|
labelText: Host name
|
||||||
|
helperText: Enter the host name of your account.
|
||||||
|
testAuth:
|
||||||
|
buttonText:
|
||||||
|
pre: Authenticate with Google
|
||||||
|
post: Waiting to hear back from Google
|
||||||
|
authError: 'Google access was not authorized'
|
||||||
|
popupError: 'Please disable your pop-up blocker and click "Authenticate" again.'
|
||||||
|
|
||||||
azuread:
|
azuread:
|
||||||
header:
|
header:
|
||||||
disabled:
|
disabled:
|
||||||
|
|
@ -5622,6 +5684,9 @@ loginShibboleth:
|
||||||
loginGithub:
|
loginGithub:
|
||||||
buttonText: Log In with GitHub
|
buttonText: Log In with GitHub
|
||||||
|
|
||||||
|
loginGoogle:
|
||||||
|
buttonText: Log In with Google
|
||||||
|
|
||||||
loginAzure:
|
loginAzure:
|
||||||
buttonText: Log In with Azure AD
|
buttonText: Log In with Azure AD
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue