Merge pull request #1908 from westlywright/auth

Azure AD Fixes and Bugs
This commit is contained in:
Vincent Fiduccia 2018-05-16 10:51:54 -07:00 committed by GitHub
commit fa751a9ebf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 518 additions and 266 deletions

View File

@ -57,6 +57,11 @@ $ember-basic-dropdown-content-z-index: 1500;
background: $accent-bg;
}
&.lang-select {
max-height: 300px;
overflow: scroll;
}
// Links within the dropdown menu
> li > a {
display: block;

View File

@ -90,6 +90,12 @@ $tooltip-arrow-color : $tooltip-bg !default;
background: lighten($primary-dark, 15%);
}
.icon-vertical-ellipsis {
position: relative;
top: 50%;
transform: translateY(-50%);
}
.icon-chevron-down {
transition: ease all 350ms;
}

View File

@ -3,6 +3,7 @@ import Component from '@ember/component';
import Errors from 'ui/utils/errors';
import layout from './template';
import { get, set, observer } from '@ember/object';
import { on } from '@ember/object/evented';
export default Component.extend({
layout,
@ -20,11 +21,6 @@ export default Component.extend({
saved: true,
errors: null,
init() {
this._super(...arguments);
this.accessModeChanged();
},
actions: {
addAuthorized(principal) {
if ( !principal ) {
@ -76,7 +72,7 @@ export default Component.extend({
return get(this, 'model.accessMode') !== 'unrestricted';
}.property('model.accessMode'),
accessModeChanged: observer('model.accessMode', function() {
accessModeChanged: on('init', observer('model.accessMode', function() {
set(this, 'saved',false);
let allowedPrincipals = get(this, 'model.allowedPrincipalIds') || []; // ['princ_id1://yada']
@ -93,6 +89,6 @@ export default Component.extend({
}
set(this, 'model.allowedPrincipalIds', allowedPrincipals);
}),
})),
});

View File

@ -1,127 +1,183 @@
import { later } from '@ember/runloop';
import { inject as service } from '@ember/service';
import Controller from '@ember/controller';
import { alias } from '@ember/object/computed';
import {
get, set, setProperties, computed
} from '@ember/object';
import C from 'ui/utils/constants';
export default Controller.extend({
access : service(),
settings : service(),
intl : service(),
access: service(),
settings: service(),
intl: service(),
confirmDisable : false,
errors : null,
testing : false,
error : null,
confirmDisable: false,
errors: null,
testing: false,
error: null,
loginUsername : null,
loginPassword : null,
loginUsername: null,
loginPassword: null,
//new
azureADConfig: alias('model.azureADConfig'),
isEnabled: alias('azureADConfig.enabled'),
editing: false,
mode: 'global',
modeClass: 'span-4',
numUsers: computed('azureADConfig.allowedPrincipalIds.[]','userType','groupType', function() {
return ( get(this, 'azureADConfig.allowedPrincipalIds') || [] ).filter(principal => principal.includes(C.PROJECT.TYPE_AZURE_USER)).get('length');
}),
numGroups: computed('azureADConfig.allowedPrincipalIds.[]','userType','groupType', function() {
return ( get(this, 'azureADConfig.allowedPrincipalIds') || [] ).filter(principal => principal.includes(C.PROJECT.TYPE_AZURE_GROUP)).get('length');
}),
actions: {
edit() {
set(this, 'editing', true);
},
toggleMode() {
if (get(this, 'mode') === 'global') {
setProperties(this, {
mode: 'china',
modeClass: 'span-3'
});
} else {
setProperties(this, {
mode: 'global',
modeClass: 'span-4'
});
}
},
test: function() {
this.send('clearError');
var model = this.get('model');
const model = get(this, 'azureADConfig');
const enabled = get(this, 'azureADConfig.enabled');
model.setProperties({
enabled: false,
accessMode: 'unrestricted',
});
var errors = model.validationErrors();
if ( errors.get('length') )
{
this.set('errors', errors);
}
else
{
this.set('testing', true);
model.save().then(() => {
this.send('authenticate');
}).catch(err => {
if ( errors.get('length') ) {
set(this, 'errors', errors);
set(this, 'testing', false);
model.set('enabled', enabled);
} else {
set(this, 'testing', true);
// delete model.enabled;
model.doAction('testAndApply', {
azureAdConfig: model,
enabled: true,
username: get(this, 'loginUsername'),
password: get(this, 'loginPassword'),
}).then( () => {
this.send('waitAndRefresh');
}).catch((err) => {
set(model, 'enabled', enabled);
this.send('gotError', err);
});
}
},
authenticate: function() {
this.send('clearError');
var code = this.get('loginUsername')+':'+this.get('loginPassword');
this.get('access').login(code).then(res => {
this.send('authenticationSucceeded', res.body);
}).catch(err => {
this.send('gotError', err);
});
},
authenticationSucceeded: function(/*auth*/) {
this.send('clearError');
// Set this to true so the token will be sent with the request
this.set('access.enabled', true);
var model = this.get('model');
model.setProperties({
enabled: true,
});
model.save().then(() => {
this.send('waitAndRefresh');
}).catch((err) => {
this.set('access.enabled', false);
this.send('gotError', err);
});
},
waitAndRefresh: function(url) {
$('#loading-underlay, #loading-overlay').removeClass('hide').show(); // eslint-disable-line
setTimeout(function() {
window.location.href = url || window.location.href;
}, 1000);
},
promptDisable: function() {
this.set('confirmDisable', true);
set(this, 'confirmDisable', true);
later(this, function() {
this.set('confirmDisable', false);
set(this, 'confirmDisable', false);
}, 10000);
},
gotError: function(err) {
if ( err.message )
{
if ( err.message ) {
this.send('showError', err.message + (err.detail? '('+err.detail+')' : ''));
}
else
{
} else {
this.send('showError', 'Error ('+err.status + ' - ' + err.code+')');
}
this.set('testing', false);
this.set('saving', false);
setProperties(this, {
testing: false,
saving: false,
});
},
showError: function(msg) {
this.set('errors', [msg]);
set(this, 'errors', [msg]);
window.scrollY = 0;
},
clearError: function() {
this.set('errors', null);
set(this, 'errors', null);
},
disable: function() {
this.send('clearError');
var model = this.get('model');
model.setProperties({
enabled: false,
username: "",
password: "",
const model = get(this, 'azureADConfig');
setProperties(model, {
enabled: false,
});
model.save().then(() => {
model.doAction('disable').then(() => {
this.send('waitAndRefresh');
}).catch((err) => {
this.send('gotError', err);
}).finally(() => {
this.set('confirmDisable', false);
set(this, 'confirmDisable', false);
});
},
},

View File

@ -1,11 +1,17 @@
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({
model: function() {
return this.get('globalStore').find('azureadconfig', null, {forceReload: true}).then((collection) => {
let obj = collection.get('firstObject');
obj.set('accessMode','unrestricted');
return obj;
});
globalStore: service(),
model() {
let gs = get(this, 'globalStore');
return hash({
azureADConfig: gs.find('authconfig', 'azuread'),
principals: gs.all('principal')
}).catch( e => e);
},
});

View File

@ -1,117 +1,187 @@
<section>
{{#if access.enabled}}
<p>{{t 'authPage.azuread.header.enabled'}}</p>
{{t 'authPage.azuread.subtext.enabled' appName=settings.appName}}
{{#if isEnabled}}
<p>{{t (concat 'authPage.azuread.header.enabled.' azureADConfig.accessMode)
appName=settings.appName
groups=numGroups
users=numUsers
htmlSafe=true
}}</p>
{{else}}
<div class="banner bg-warning">
<div class="banner-icon">
<span class="icon icon-alert"></span>
</div>
<div class="banner-message">
<p>{{t 'authPage.azuread.header.disabled'}}</p>
<p>{{t 'authPage.azuread.header.disabled.label' htmlSafe=true}}</p>
</div>
</div>
<p>{{t 'authPage.azuread.subtext.disabled' appName=settings.appName}}</p>
<p>{{t 'authPage.azuread.header.disabled.warning' appName=settings.appName}}</p>
{{/if}}
</section>
{{#if access.enabled}}
<section class="box mt-30">
<h3>{{t 'authPage.azuread.enabled.header' htmlSafe=true}}</h3>
<hr/>
<p>
{{t 'authPage.azuread.enabled.warning' appName=settings.appName htmlSafe=true}}
</p>
{{#accordion-list showExpandAll=false as |al expandFn|}}
{{#if confirmDisable}}
<button class="btn bg-error" {{action "disable"}}>
<i class="icon icon-alert"></i> {{t 'authPage.azuread.enabled.reallyDisable'}}
</button>
{{else}}
<button class="btn bg-error" {{action "promptDisable"}}>
<i class="icon icon-umbrella"></i> {{t 'authPage.azuread.enabled.promptDisable'}}
</button>
{{/if}}
{{#if isEnabled}}
</section>
{{/if}}
{{#accordion-list-item
detail=(t 'authPage.azuread.configure.help')
expand=(action expandFn)
expandAll=al.expandAll
expandOnInit=true
expanded=true
showExpand=false
title=(t 'authPage.github.authenticated.header.text')
}}
{{#unless access.enabled}}
<section class="box mt-30">
<h3>{{t 'authPage.azuread.configure.header'}}</h3>
<hr/>
<section class="">
<div class="clearfix">
<div class="pull-right">
<button class="btn btn-sm bg-primary" {{action "edit"}}>
{{t 'generic.edit'}}
</button>
<button class="btn btn-sm right-divider-btn bg-error" {{action "disable"}}>
{{t 'authPage.azuread.enabled.promptDisable'}}
</button>
</div>
<span class="help-block">{{t 'authPage.azuread.configure.help'}}</span>
</div>
<hr/>
<div class="row">
<div class="col span-12">
<h3>{{t 'authPage.azuread.enabled.general.header'}}</h3>
<div>
<b>{{t 'authPage.azuread.configure.tenantId.label'}}: </b> <span class="text-muted">{{azureADConfig.tenantId}}</span>
</div>
<div>
<b>{{t 'authPage.azuread.configure.clientId.label'}}: </b> <span class="text-muted">{{azureADConfig.clientId}}</span>
</div>
<div>
<b>{{t 'authPage.azuread.configure.domain.label'}}: </b> <span class="text-muted">{{azureADConfig.domain}}</span>
</div>
</div>
</div>
</section>
<div class="row">
<div class="col span-4">
<div class="inline-form">
<label class="acc-label pb-5">{{t 'authPage.azuread.configure.tenantId.label'}}{{field-required}}</label>
{{input type="text" value=model.tenantId placeholder=(t 'authPage.azuread.configure.tenantId.placeholder') classNames="form-control"}}
<p class="help-block">{{t 'authPage.azuread.configure.tenantId.help'}}</p>
</div>
</div>
<div class="col span-4">
<div class="inline-form">
<label class="acc-label pb-5">{{t 'authPage.azuread.configure.clientId.label'}}{{field-required}}</label>
{{input type="text" value=model.clientId placeholder=(t 'authPage.azuread.configure.clientId.placeholder') classNames="form-control"}}
</div>
</div>
<div class="col span-4">
<div class="inline-form">
<label class="acc-label pb-5">{{t 'authPage.azuread.configure.domain.label'}}{{field-required}}</label>
{{input type="text" value=model.domain placeholder=(t 'authPage.azuread.configure.domain.placeholder') classNames="form-control"}}
</div>
</div>
</div>
{{/accordion-list-item}}
<div class="row">
<div class="col span-6">
<div class="inline-form">
<label class="acc-label pb-5">{{t 'authPage.azuread.configure.adminAccountUsername.label'}}{{field-required}}</label>
{{input type="text" value=model.adminAccountUsername placeholder=(t 'authPage.azuread.configure.adminAccountUsername.placeholder') classNames="form-control"}}
<p class="help-block">{{t 'authPage.azuread.configure.adminAccountUsername.help'}}</p>
</div>
</div>
<div class="col span-6">
<div class="inline-form">
<label class="acc-label pb-5">{{t 'authPage.azuread.configure.adminAccountPassword.label'}}{{field-required}}</label>
{{input type="password" value=model.adminAccountPassword classNames="form-control"}}
</div>
</div>
</div>
</section>
{{#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')
}}
<section class="box mt-30">
<h3>{{t 'authPage.azuread.test.header'}}</h3>
<hr/>
<p class="text-info">{{t 'authPage.azuread.test.help'}}</p>
{{top-errors errors=errors}}
{{site-access
model=azureADConfig
principals=model.principals
collection='siteAccess.organizations'
}}
<div class="row">
<div class="col span-4">
<div class="inline-form">
<label class="acc-label pb-5">{{t 'authPage.azuread.test.username.label'}}{{field-required}}</label>
{{input type="text" value=loginUsername prompt=(t 'authPage.azuread.test.username.placeholder') classNames="form-control"}}
{{/accordion-list-item}}
{{/if}}
{{#if (or (not isEnabled) editing)}}
{{#accordion-list-item
classNames="mt-30"
detail=(t 'authPage.azuread.configure.help')
expand=(action expandFn)
expandAll=al.expandAll
expandOnInit=true
expanded=true
showExpand=false
title=(t 'authPage.azuread.configure.header')
}}
<section>
<div class="row">
<div class="pull-right btn-group no-inline-space p-0">
<button class="btn btn-link btn-sm {{if (eq mode 'global') 'bg-primary' 'bg-deafult'}}" {{action 'toggleMode'}}>Global</button>
<button class="btn btn-link btn-sm bg-defualt {{if (eq mode 'china') 'bg-primary' 'bg-deafult'}}" {{action 'toggleMode'}}>China</button>
</div>
</div>
</div>
<div class="col span-4">
<div class="inline-form">
<label class="acc-label pb-5">{{t 'authPage.azuread.test.password.label'}}{{field-required}}</label>
{{input type="password" value=loginPassword classNames="form-control"}}
<hr/>
<div class="row">
<div class="col {{modeClass}}">
<div class="inline-form">
<label class="acc-label pb-5">{{t 'authPage.azuread.configure.tenantId.label'}}{{field-required}}</label>
{{input type="text" value=azureADConfig.tenantId placeholder=(t 'authPage.azuread.configure.tenantId.placeholder') classNames="form-control"}}
<p class="help-block">{{t 'authPage.azuread.configure.tenantId.help'}}</p>
</div>
</div>
<div class="col {{modeClass}}">
<div class="inline-form">
<label class="acc-label pb-5">{{t 'authPage.azuread.configure.clientId.label'}}{{field-required}}</label>
{{input type="text" value=azureADConfig.clientId placeholder=(t 'authPage.azuread.configure.clientId.placeholder') classNames="form-control"}}
</div>
</div>
{{#unless (eq mode 'global')}}
<div class="col span-3">
<div class="inline-form">
<label class="acc-label pb-5">{{t 'authPage.azuread.configure.clientSecret.label'}}{{field-required}}</label>
{{input type="password" value=azureADConfig.clientSecret placeholder=(t 'authPage.azuread.configure.clientSecret.placeholder') classNames="form-control"}}
</div>
</div>
{{/unless}}
<div class="col {{modeClass}}">
<div class="inline-form">
<label class="acc-label pb-5">{{t 'authPage.azuread.configure.domain.label'}}{{field-required}}</label>
{{input type="text" value=azureADConfig.domain placeholder=(t 'authPage.azuread.configure.domain.placeholder') classNames="form-control"}}
</div>
</div>
</div>
</div>
<div class="col span-4">
<div class="inline-form">
<label class="acc-label pb-5">&nbsp;</label>
<button class="btn bg-primary mt-25" style="height: 38px; line-height: 28px;" {{action "test"}}>
{{#if testing}}
<i class="icon icon-spinner icon-spin"></i> {{t 'authPage.azuread.test.post'}}
{{else}}
{{t 'authPage.azuread.test.pre'}}
{{/if}}
</button>
</section>
{{/accordion-list-item}}
{{#accordion-list-item
classNames="mt-30"
detail=(t 'authPage.azuread.test.help')
expand=(action expandFn)
expandAll=al.expandAll
expandOnInit=true
expanded=true
showExpand=false
title=(t 'authPage.azuread.test.header')
}}
<section>
{{top-errors errors=errors}}
<div class="row">
<div class="col span-6">
<div class="inline-form">
<label class="acc-label pb-5">{{t 'authPage.azuread.test.username.label'}}{{field-required}}</label>
{{input type="text" value=loginUsername prompt=(t 'authPage.azuread.test.username.placeholder') classNames="form-control"}}
</div>
</div>
<div class="col span-6">
<div class="inline-form">
<label class="acc-label pb-5">{{t 'authPage.azuread.test.password.label'}}{{field-required}}</label>
{{input type="password" value=loginPassword classNames="form-control"}}
</div>
</div>
</div>
</div>
</div>
</section>
{{/unless}}
<div class="row mt-10">
<div class="inline-form">
<button class="btn bg-primary" style="display: block;margin: 0 auto;" {{action "test"}}>
{{#if testing}}
<i class="icon icon-spinner icon-spin"></i> {{t 'authPage.azuread.test.post'}}
{{else}}
{{t 'authPage.azuread.test.pre'}}
{{/if}}
</button>
</div>
</div>
</section>
{{/accordion-list-item}}
{{/if}}
{{/accordion-list}}

View File

@ -10,9 +10,8 @@ export default Controller.extend({
drivers: computed(function() {
return [
{route: 'security.authentication.activedirectory', label: 'Active Directory', css: 'activedirectory', available: this.hasRecord('activedirectoryconfig') },
// {route: 'security.authentication.azuread', label: 'Azure AD', css: 'azuread', available: this.hasRecord('azureadconfig') },
{route: 'security.authentication.azuread', label: 'Azure AD', css: 'azuread', available: this.hasRecord('azureadconfig') },
{route: 'security.authentication.github', label: 'GitHub', css: 'github', available: this.hasRecord('githubconfig') },
// {route: 'security.authentication.localauth', label: 'Local', css: 'local', available: this.hasRecord('localconfig') }, // always on
// {route: 'security.authentication.openldap', label: 'OpenLDAP', css: 'openldap', available: this.hasRecord('openldapconfig') },
// {route: 'security.authentication.shibboleth', label: 'Shibboleth', css: 'shibboleth', available: this.hasRecord('shibbolethconfig') },
];

View File

@ -1,24 +1,72 @@
import { get, set, computed } from '@ember/object';
import { get, set, computed, setProperties } from '@ember/object';
import { next } from '@ember/runloop';
import { inject as service } from '@ember/service';
import Component from '@ember/component';
import C from 'ui/utils/constants';
export default Component.extend({
access: service(),
access: service(),
cookies: service(),
isCaas: computed('app.mode', function() {
return this.get('app.mode') === 'caas' ? true : false;
}),
waiting: null,
intl: service(),
username: null,
waiting: null,
username: null,
rememberUsername: false,
password: null,
shown: false,
provider: null,
password: null,
shown: false,
provider: null,
readableProvider: null,
onlyLocal: null,
onlyLocal: null,
init() {
this._super(...arguments);
let username = null;
if (get(this, 'provider') === 'local') {
username = get(this, `cookies.${C.COOKIE.USERNAME}`);
} else {
username = get(this, `cookies.${get(this, 'provider').toUpperCase()}-USERNAME`);
}
if ( username ) {
setProperties(this, {
username: username,
rememberUsername: true,
});
}
if (get(this, 'provider') && !get(this,'onlyLocal')) {
let pv = null;
switch(get(this, 'provider')) {
case 'activedirectory':
pv = get(this, 'intl').t('loginPage.readableProviders.ad');
break;
case 'azuread':
pv = get(this, 'intl').t('loginPage.readableProviders.azureAd');
break;
case 'local':
default:
pv = get(this, 'intl').t('loginPage.readableProviders.local');
break;
}
set(this, 'readableProvider', pv);
// console.log(this.get('provider'));
}
},
actions: {
showLocal() {
@ -56,39 +104,9 @@ export default Component.extend({
}
},
init() {
this._super(...arguments);
var username = null;
if (get(this, 'provider') === 'local') {
username = get(this, `cookies.${C.COOKIE.USERNAME}`);
} else {
username = get(this, `cookies.${get(this, 'provider').toUpperCase()}-USERNAME`);
}
if ( username ) {
set(this, 'username', username);
set(this, 'rememberUsername', true);
}
if (get(this, 'provider') && !get(this,'onlyLocal')) {
let pv = null;
switch(get(this, 'provider')) {
case 'activedirectory':
pv = 'Active Directory';
break;
case 'local':
default:
pv = 'a Local User';
break;
}
set(this, 'readableProvider', pv);
// console.log(this.get('provider'));
}
},
isCaas: computed('app.mode', function() {
return this.get('app.mode') === 'caas' ? true : false;
}),
focusSomething() {
if ( this.isDestroyed || this.isDestroying ) {

View File

@ -101,6 +101,9 @@ export default Controller.extend({
case 'activedirectory':
this.toggleProperty('adWaiting');
break;
case 'azuread':
this.toggleProperty('azureadWaiting');
break;
case 'shibboleth':
this.toggleProperty('shibbolethWaiting');
break;

View File

@ -36,6 +36,16 @@
}}
{{/if}}
{{#if isAzureAd}}
{{login-user-pass
action="authenticate"
classNames="row"
provider="azuread"
shown=true
waiting=azureadWaiting
}}
{{/if}}
{{#if isLocal}}
{{login-user-pass
action="authenticate"

View File

@ -1,7 +1,7 @@
import Component from '@ember/component';
import layout from './template';
import { inject as service } from '@ember/service'
import { computed } from '@ember/object';
import { computed, get } from '@ember/object';
export default Component.extend({
@ -17,16 +17,29 @@ export default Component.extend({
size: 'xs',
actions: {
clickedAction: function(actionName) {
this.get('resourceActions').triggerAction(actionName);
},
closeLater(dd) {
dd.actions.close();
return true;
},
preload() {
this.get('resourceActions').setActionItems(this.get('model'), this.get('context'));
}
},
actionsOpen() {
get(this, 'tooltipService').set('childOpened', true);
},
actionsClosed() {
get(this, 'tooltipService').set('childOpened', false);
get(this, 'tooltipService').hide();
},
},
sizeClass: computed('size', function() {

View File

@ -1,4 +1,10 @@
{{#basic-dropdown as |dd|}}
{{#basic-dropdown
horizontalPosition="right"
verticalPosition="below"
onOpen=(action 'actionsOpen')
onClose=(action 'actionsClosed')
as |dd|
}}
{{#dd.trigger
ariaLabel=(t 'generic.moreActions')

View File

@ -18,6 +18,10 @@
localizedPrompt=true
}}
</div>
<div class="col span-6">
<label class="acc-label">{{t 'clusterNew.azureaks.dns.label'}}</label>
{{input type="text" value=config.masterDnsPrefix classNames="form-control" placeholder=(t 'clusterNew.azureaks.dns.placeholder')}}
</div>
</div>
{{/accordion-list-item}}
{{#accordion-list-item
@ -70,7 +74,7 @@
{{t 'generic.na'}}
</div>
{{else}}
{{input type="text" value=config.clientSecret classNames="form-control" placeholder=(t 'clusterNew.azureaks.clientSecret.placeholder')}}
{{input type="password" value=config.clientSecret classNames="form-control" placeholder=(t 'clusterNew.azureaks.clientSecret.placeholder')}}
{{/if}}
</div>
</div>
@ -111,7 +115,7 @@
{{/if}}
</div>
<div class="col span-6">
<label class="acc-label">{{t 'clusterNew.azureaks.resourceGroup.label'}}</label>
<label class="acc-label">{{t 'clusterNew.azureaks.resourceGroup.label'}}{{field-required}}</label>
{{#if editing}}
<div>
{{config.resourceGroup}}

View File

@ -1,5 +1,4 @@
{{#basic-dropdown
verticalPosition="above"
horizontalPosition="right"
as |dd|
}}
@ -10,7 +9,7 @@
<i class="icon icon-globe"></i> {{selectedLabel}} <i class="icon icon-chevron-down"></i>
{{/dd.trigger}}
{{#dd.content class="text-right"}}
{{#dd.content class="text-right lang-select"}}
{{#if settings.isRancher}}
<li><a href="http://translate.rancher.com" target="_blank" rel="noopener nofollow">{{t 'languageContribute'}}</a></li>
<li class="divider"></li>

View File

@ -1,27 +1,44 @@
import Mixin from '@ember/object/mixin';
import { cancel, later } from '@ember/runloop';
import { get, set } from '@ember/object';
export default Mixin.create({
closeTimer: null,
actions: {
prevent() {
return false;
},
open(dropdown) {
if (this.closeTimer) {
cancel(this.closeTimer);
this.closeTimer = null;
const ct = get(this, 'closeTimer');
console.log('open closeTimer: ', this.closeTimer, get(dropdown, 'uniqueId'));
if (ct) {
cancel(ct);
set(this, 'closeTimer', null);
} else {
dropdown.actions.open();
}
},
closeLater(dropdown) {
this.closeTimer = later(() => {
this.closeTimer = null;
set(this, 'closeTimer', later(() => {
dropdown.actions.close();
}, 200);
set(this, 'closeTimer', null);
}, 200));
console.log('close closeTimer: ', this.closeTimer, get(dropdown, 'uniqueId'));
}
},
});

View File

@ -1,19 +1,24 @@
import { later, cancel } from '@ember/runloop';
import Service, { inject as service } from '@ember/service';
import { get } from '@ember/object';
const DELAY = 250;
export default Service.extend({
mouseLeaveTimer: null,
requireClick: false,
tooltipOpts: null,
app: service(),
mouseLeaveTimer: null,
requireClick: false,
tooltipOpts: null,
openedViaContextClick: false,
app: service(),
childOpened: false,
startTimer() {
this.set('mouseLeaveTimer', later(() => {
this.hide();
}, DELAY));
},
cancelTimer() {
@ -21,13 +26,19 @@ export default Service.extend({
},
hide() {
this.set('tooltipOpts', null);
if (!get(this, 'childOpened')) {
this.set('tooltipOpts', null);
}
},
leave() {
if ( !this.get('requireClick') )
{
if ( !this.get('requireClick') ) {
this.startTimer();
}
},
});

View File

@ -449,36 +449,62 @@ authPage:
post: Waiting to hear back from GitHub
azuread:
header:
enabled: 'Azure AD Authentication is <b>enabled</b>'
disabled: 'Azure AD Authentication is not configured'
subtext:
enabled: '{appName} is configured to allow access to accounts in Azure AD'
disabled: '{appName} can be configured to restrict access to a set of accounts defined in the {appName} database. This is not currently set up, so anybody that reach this page (or the API) has full control over the system.'
enabled:
label: "Azure AD is enabled"
required: |
{appName} is configured to allow access to {groups, plural,
=0 {no groups}
=1 {# group}
other {# groups}
} and {users, plural,
=0 {no users}
=1 {# user}
other {# users}
}.
restricted: |
{appName} is configured to allow access to environment members, {groups, plural,
=0 {no groups}
=1 {# group}
other {# groups}
} and {users, plural,
=0 {no users}
=1 {# user}
other {# users}
}.
unrestricted: "{appName} is configured to allow access to any Azure AD user."
disabled:
label: "Azure AD is not configured"
warning: "{appName} can be configured to restrict access to a set of Azure AD users and groups"
enabled:
header: 'Danger Zone&trade;'
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.'
reallyDisable: 'Are you sure? Click again to really disable access control'
promptDisable: Disable access control
promptDisable: Disable Azure AD
general:
header: General
configure:
header: '1. Configure Azure AD Account'
tenantId:
label: Tenant ID
placeholder: A long UUID string
help: From the Azure AD portal
clientId:
label: Client ID
placeholder: A long UUID string
domain:
label: Domain
placeholder: e.g. youcompany.onmicrosoft.com
header: 'Configure Azure AD Account'
help: Enter the Tenant ID, Client ID, and Admin user details to connect to your Azure AD auth account.
adminAccountUsername:
label: Admin Account Username
placeholder: e.g. rancher-admin
help: A user that can read information about other users
adminAccountPassword:
label: Admin Account Password
clientId:
label: Client ID
placeholder: A long UUID string
clientSecret:
label: Client Secret
placeholder: Your Client Secret
domain:
label: Domain
placeholder: e.g. youcompany.onmicrosoft.com
tenantId:
label: Tenant ID
placeholder: A long UUID string
help: From the Azure AD portal
test:
header: '2. Test and enable authentication'
header: 'Test and enable authentication'
help: 'Check that everything is configured correctly by testing authentication with your account:'
username:
label: Login Username
@ -1076,6 +1102,10 @@ loginPage:
error:
authFailedCreds: "Logging in failed: Check credentials, or your account may not be authorized to log in."
authFailed: "Logging in failed: Your account may not be authorized to log in."
readableProviders:
ad: Active Directory
azureAd: Azure AD
local: a Local User
machinePage:
header: Node Drivers
@ -1757,6 +1787,9 @@ clusterNew:
prompt: Choose a size...
diskSizeGb:
label: OS Disk Size
dns:
label: DNS Prfix
placeholder: "e.g. example"
ssh:
label: SSH Public Key
security:

View File

@ -2604,9 +2604,9 @@ elliptic@^6.0.0:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.0"
ember-api-store@^2.6.6:
version "2.6.6"
resolved "https://registry.yarnpkg.com/ember-api-store/-/ember-api-store-2.6.6.tgz#c405ce94f21ca7c5d80568cbf33f99b42403dce0"
ember-api-store@2.6.8:
version "2.6.8"
resolved "https://registry.yarnpkg.com/ember-api-store/-/ember-api-store-2.6.8.tgz#fdcc950ea1c8cab7633e0480eb5061534c650d0c"
dependencies:
broccoli-file-creator "^1.1.1"
ember-cli-babel "^6.8.2"