Access Control config enhancements

- Check Github API and automatically remove users/orgs that don't exist
- Saving/Saved state on the authorization save button to show state
- Waiting state on test button to show state
- Size GitHub popup window bigger to show content
This commit is contained in:
Vincent Fiduccia 2015-01-13 15:21:23 -07:00
parent 1c0a29899b
commit b4940855e8
11 changed files with 127 additions and 54 deletions

View File

@ -8,8 +8,8 @@ export function initialize(container, application) {
// Find out if auth is enabled
store.rawRequest({
url: 'token', // Base url, which will be /v1
headers: { 'authorization': undefined }
url: 'token',
headers: { 'authorization': undefined } // Explicitly not send the auth token
})
.then(function(obj) {
var body = JSON.parse(obj.xhr.responseText);

View File

@ -5,21 +5,24 @@ export default Ember.Component.extend({
login: null,
classNames: ['gh-avatar'],
name: 'Loading...',
name: 'Checking...',
avatarUrl: null,
nameChanged: function() {
var self = this;
var url = 'https://api.github.com/' + this.get('type') + 's/' + this.get('login') + '?s=40';
var login = this.get('login');
var type = this.get('type');
var url = 'https://api.github.com/' + type + 's/' + login;
Ember.$.ajax({url: url, dataType: 'json'}).then(function(body) {
self.set('name', body.name);
self.set('avatarUrl', body.avatar_url);
}, function() {
var type = self.get('type');
type = type.substr(0,1).toUpperCase() + type.substr(1);
self.set('name', 'Warning: ' + type + ' not found');
var avatarUrl = body.avatar_url;
avatarUrl += (avatarUrl.indexOf('?') >= 0 ? '&' : '?') + 's=40';
self.set('avatarUrl', avatarUrl);
}, function() {
self.set('name', '(Not found)');
self.sendAction('notFound', login);
});
}.observes('login','type').on('init'),

View File

@ -12,5 +12,5 @@
<div class="clip">
<a {{bind-attr href=url}} target="_blank">{{login}}</a>
</div>
<div class="text-muted clip">{{name}}</div>
<div class="text-muted clip">{{#if name}}{{name}}{{else}}<i>No name</i>{{/if}}</div>
</div>

View File

@ -2,9 +2,12 @@
{{#if app.authenticationEnabled}}
<div class="pull-right">
<div class="dropdown">
<span class="fa-stack fa-lg hand" id="user-dropdown" data-toggle="dropdown" aria-expanded="true">
<i class="fa fa-circle-o fa-stack-2x"></i>
<i class="fa fa-user fa-stack-1x"></i>
<span class="hand" id="user-dropdown" data-toggle="dropdown" aria-expanded="true">
<span class="fa-stack fa-lg">
<i class="fa fa-circle-o fa-stack-2x"></i>
<i class="fa fa-user fa-stack-1x"></i>
</span>
<i class="fa fa-chevron-down"></i>
</span>
<ul class="dropdown-menu dropdown-menu-right" role="menu" aria-labelledby="user-dropdown">
<li role="presentation" class="disabled">

View File

@ -41,7 +41,7 @@ export default Ember.Controller.extend({
self.set('timedOut', false);
self.set('waiting', true);
self.get('torii').open('github-oauth2').then(function(github){
self.get('torii').open('github-oauth2',{width: 1024, height: 500}).then(function(github){
return self.get('store').rawRequest({
url: 'token',
method: 'POST',

View File

@ -3,12 +3,17 @@ import Ember from 'ember';
export default Ember.ObjectController.extend({
confirmDisable: false,
error: null,
testing: false,
saving: false,
saved: true,
createIncomplete: function() {
createDisabled: function() {
var id = (this.get('clientId')||'').trim();
var secret = (this.get('clientSecret')||'').trim();
return id.length < 20 ||secret.length < 40;
}.property('clientId','clientSecret'),
return id.length < 20 ||secret.length < 40 || this.get('testing');
}.property('clientId','clientSecret','testing'),
saveDisabled: Ember.computed.or('saving','saved'),
destinationUrl: function() {
return window.location.origin+'/';
@ -17,8 +22,10 @@ export default Ember.ObjectController.extend({
actions: {
test: function() {
var self = this;
self.send('clearError');
self.set('testing',true);
var model = this.get('model');
var model = self.get('model');
model.set('clientId', model.get('clientId').trim());
model.set('clientSecret', model.get('clientSecret').trim());
model.set('enabled',false); // It should already be, but just in case..
@ -34,7 +41,8 @@ export default Ember.ObjectController.extend({
authenticate: function() {
var self = this;
self.get('torii').open('github-oauth2').then(function(github){
self.send('clearError');
self.get('torii').open('github-oauth2',{width: 1024, height: 500}).then(function(github){
return self.get('store').rawRequest({
url: 'token',
method: 'POST',
@ -52,18 +60,21 @@ export default Ember.ObjectController.extend({
.catch(function(err) {
// Github auth failed.. try again
self.send('gotError', err);
})
.finally(function() {
self.set('testing',false);
});
},
authenticationSucceeded: function(auth) {
console.log('Authentication succeeded');
var self = this;
var session = self.get('session');
self.send('clearError');
var session = self.get('session');
session.set('token', auth.jwt);
session.set('isLoggedIn',1);
var model = this.get('model');
var model = self.get('model');
model.set('enabled',true);
model.set('allowOrganizations', auth.orgs||[]);
model.set('allowUsers', [auth.user]);
@ -75,7 +86,6 @@ export default Ember.ObjectController.extend({
},
waitAndRefresh: function(expect,limit) {
console.log('Wait and refresh',expect,limit);
var self = this;
if ( limit === undefined )
{
@ -114,6 +124,9 @@ export default Ember.ObjectController.extend({
},
addUser: function() {
this.send('clearError');
this.set('saved',false);
var str = (this.get('addUser')||'').trim();
if ( str )
{
@ -122,11 +135,15 @@ export default Ember.ObjectController.extend({
}
},
removeUser: function(user) {
this.get('allowUsers').removeObject(user);
removeUser: function(login) {
this.set('saved',false);
this.get('allowUsers').removeObject(login);
},
addOrg: function() {
this.send('clearError');
this.set('saved',false);
var str = (this.get('addOrg')||'').trim();
if ( str )
{
@ -135,17 +152,34 @@ export default Ember.ObjectController.extend({
}
},
removeOrg: function(org) {
this.get('allowOrganizations').removeObject(org);
removeOrg: function(login) {
this.set('saved',false);
this.get('allowOrganizations').removeObject(login);
},
userNotFound: function(login) {
this.send('showError',"User '"+ login + "' not found");
this.send('removeUser',login);
},
orgNotFound: function(login) {
this.send('showError',"Organization '"+ login + "' not found");
this.send('removeOrg',login);
},
saveAuthorization: function() {
var self = this;
self.send('clearError');
self.set('saving',true);
self.set('saved',false);
var model = self.get('model');
model.save().then(function() {
self.send('waitAndRefresh', true);
self.set('saved',true);
}).catch(function(err) {
self.send('gotError', err);
}).finally(function() {
self.set('saving',false);
});
},
@ -154,12 +188,21 @@ export default Ember.ObjectController.extend({
},
gotError: function(err) {
this.set('error', err.message);
this.send('showError', err.message);
},
showError: function(msg) {
this.set('error', msg);
window.scrollY = 0;
},
clearError: function() {
this.set('error', '');
},
disable: function() {
var self = this;
self.send('clearError');
var model = this.get('model');
model.set('allowOrganizations',[]);

View File

@ -11,5 +11,9 @@ export default Ember.Route.extend(AuthenticatedRouteMixin,{
setupController: function(controller, model) {
this._super(controller,model);
controller.set('confirmDisable',false);
controller.set('saving',false);
controller.set('saved',true);
controller.set('testing',false);
controller.set('error',null);
}
});

View File

@ -1,23 +1,29 @@
<div style="padding: 20px 20px 0 20px;">
<section>
{{#if error}}
<div class="alert alert-danger">
<i style="float: left;" class="fa fa-lg fa-exclamation-circle"></i>
<p style="margin-left: 50px">{{error}}</p>
</div>
{{/if}}
<div class="well">
<h2>Access Control is {{#if app.authenticationEnabled}}<b>enabled</b>{{else}}<b class="text-warning">not configured</b>{{/if}}</h2>
<p>
<div>
{{#if app.authenticationEnabled}}
Rancher is configured to restrict access to the GitHub users and organization members below.
{{else}}
Rancher can be configured to restrict access to a set of GitHub users and organization members. This is not currently set up, so anybody that reach this page (or the API) has full control over the system.
{{/if}}
</p>
</div>
</div>
{{#if error}}
<div class="alert alert-danger">
<i style="float: left;" class="fa fa-exclamation-circle"></i>
<p style="margin-left: 50px">{{error}}</p>
</div>
{{/if}}
{{#if app.authenticationEnabled}}
<div class="well">
<h4>Authentication</h4>
<hr/>
<p>To change the configured GitHub application, disable access control below and then set it up again.</p>
<div><b>Client ID: </b> <span class="text-muted">{{clientId}}</span></div>
</div>
<div class="well">
<h4>Configure Authorization</h4>
@ -27,7 +33,7 @@
<div class="col-sm-6">
<label>Users</label>
<form {{action "addUser"}}>
<form {{action "addUser" on="submit"}}>
<div class="input-group">
{{input type="text" value=addUser placeholder="Add GitHub username" class="form-control"}}
<div class="input-group-btn">
@ -41,7 +47,7 @@
<ul class="list-unstyled gh-avatar-list">
{{#each user in allowUsers}}
<li>
{{#github-avatar type="user" login=user}}
{{#github-avatar type="user" login=user notFound="userNotFound"}}
<button class="btn btn-danger btn-sm pull-right gh-action" {{action "removeUser" user}}>Remove</button>
{{/github-avatar}}
</li>
@ -54,7 +60,7 @@
<div class="col-sm-6">
<label>Organizations</label>
<form {{action "addOrg"}}>
<form {{action "addOrg" on="submit"}}>
<div class="input-group">
{{input type="text" value=addOrg placeholder="Add GitHub organization name" class="form-control"}}
<div class="input-group-btn">
@ -68,7 +74,7 @@
<ul class="list-unstyled gh-avatar-list">
{{#each org in allowOrganizations}}
<li>
{{#github-avatar type="org" login=org}}
{{#github-avatar type="org" login=org notFound="orgNotFound"}}
<button class="btn btn-danger btn-sm pull-right gh-action" {{action "removeOrg" org}}>Remove</button>
{{/github-avatar}}
</li>
@ -81,8 +87,16 @@
<hr/>
<button class="btn btn-primary" {{action "saveAuthorization"}}>
Save Authorization Configuration
<button class="btn btn-primary" {{bind-attr disabled=saveDisabled}} {{action "saveAuthorization"}}>
{{#if saved}}
Saved
{{else}}
{{#if saving}}
<i class="fa fa-spinner fa-spin"></i> Saving...</i>
{{else}}
Save authorization configuration
{{/if}}
{{/if}}
</button>
</div>
@ -96,11 +110,11 @@
{{#if confirmDisable}}
<button class="btn btn-danger" {{action "disable"}}>
<i class="fa fa-fire"></i> Are you sure? Click again to really disable Access Control
<i class="fa fa-fire"></i> Are you sure? Click again to really disable access control
</button>
{{else}}
<button class="btn btn-danger" {{action "promptDisable"}}>
<i class="fa fa-fire"></i> Disable Access Control
<i class="fa fa-fire"></i> Disable access control
</button>
{{/if}}
@ -160,11 +174,15 @@
<p>Check that your application is configured correctly by testing authentication with it:</p>
</div>
<div class="col-md-6">
<button {{bind-attr disabled=createIncomplete}} class="btn btn-primary" {{action "test"}}>
<i class="fa fa-github"></i> Authenticate with GitHub
<button {{bind-attr disabled=createDisabled}} class="btn btn-primary" {{action "test"}}>
{{#if testing}}
<i class="fa fa-spinner fa-spin"></i> Waiting to hear back from GitHub
{{else}}
<i class="fa fa-github"></i> Authenticate with GitHub
{{/if}}
</button>
</div>
</div>
</div>
{{/unless}}
</div>
</section>

View File

@ -1,4 +1,7 @@
import Ember from 'ember';
export default Ember.Route.extend({
enter: function() {
this.send('setPageName','Settings');
},
});

View File

@ -116,13 +116,12 @@ HEADER {
z-index: 2;
margin-left: $nav_width;
background-color: $header_bg;
padding: 0;
padding: 0 20px;
H2 {
font-weight: normal;
margin: 0;
line-height: $header_height;
padding-left: 20px;
}
}

View File

@ -19,6 +19,7 @@
"license": "Apache 2",
"devDependencies": {
"ember-api-store": "1.0.7",
"rancher-torii": "^0.2.2",
"body-parser": "^1.2.0",
"broccoli-asset-rev": "^1.0.0",
@ -38,7 +39,6 @@
"express": "^4.8.5",
"forever-agent": "^0.5.2",
"glob": "^4.0.5",
"http-proxy": "^1.6.2",
"torii": "^0.2.2"
"http-proxy": "^1.6.2"
}
}