initial commit
This commit is contained in:
1
lemur/static/app/.buildignore
Normal file
1
lemur/static/app/.buildignore
Normal file
@ -0,0 +1 @@
|
||||
*.coffee
|
157
lemur/static/app/404.html
Normal file
157
lemur/static/app/404.html
Normal file
@ -0,0 +1,157 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Page Not Found :(</title>
|
||||
<style>
|
||||
::-moz-selection {
|
||||
background: #b3d4fc;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: #b3d4fc;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
html {
|
||||
padding: 30px 10px;
|
||||
font-size: 20px;
|
||||
line-height: 1.4;
|
||||
color: #737373;
|
||||
background: #f0f0f0;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
html,
|
||||
input {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 500px;
|
||||
_width: 500px;
|
||||
padding: 30px 20px 50px;
|
||||
border: 1px solid #b3b3b3;
|
||||
border-radius: 4px;
|
||||
margin: 0 auto;
|
||||
box-shadow: 0 1px 10px #a7a7a7, inset 0 1px 0 #fff;
|
||||
background: #fcfcfc;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0 10px;
|
||||
font-size: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 span {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 1.5em 0 0.5em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0 0 0 40px;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 380px;
|
||||
_width: 380px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* google search */
|
||||
|
||||
#goog-fixurl ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#goog-fixurl form {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#goog-wm-qt,
|
||||
#goog-wm-sb {
|
||||
border: 1px solid #bbb;
|
||||
font-size: 16px;
|
||||
line-height: normal;
|
||||
vertical-align: top;
|
||||
color: #444;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
#goog-wm-qt {
|
||||
width: 220px;
|
||||
height: 20px;
|
||||
padding: 5px;
|
||||
margin: 5px 10px 0 0;
|
||||
box-shadow: inset 0 1px 1px #ccc;
|
||||
}
|
||||
|
||||
#goog-wm-sb {
|
||||
display: inline-block;
|
||||
height: 32px;
|
||||
padding: 0 10px;
|
||||
margin: 5px 0 0;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
background-color: #f5f5f5;
|
||||
background-image: -webkit-linear-gradient(rgba(255,255,255,0), #f1f1f1);
|
||||
background-image: -moz-linear-gradient(rgba(255,255,255,0), #f1f1f1);
|
||||
background-image: -ms-linear-gradient(rgba(255,255,255,0), #f1f1f1);
|
||||
background-image: -o-linear-gradient(rgba(255,255,255,0), #f1f1f1);
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
*overflow: visible;
|
||||
*display: inline;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
#goog-wm-sb:hover,
|
||||
#goog-wm-sb:focus {
|
||||
border-color: #aaa;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
#goog-wm-qt:hover,
|
||||
#goog-wm-qt:focus {
|
||||
border-color: #105cb6;
|
||||
outline: 0;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
input::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Not found <span>:(</span></h1>
|
||||
<p>Sorry, but the page you were trying to view does not exist.</p>
|
||||
<p>It looks like this was the result of either:</p>
|
||||
<ul>
|
||||
<li>a mistyped address</li>
|
||||
<li>an out-of-date link</li>
|
||||
</ul>
|
||||
<script>
|
||||
var GOOG_FIXURL_LANG = (navigator.language || '').slice(0,2),GOOG_FIXURL_SITE = location.host;
|
||||
</script>
|
||||
<script src="//linkhelp.clients.google.com/tbproxy/lh/wm/fixurl.js"></script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
27
lemur/static/app/angular/accounts/account/account.js
vendored
Normal file
27
lemur/static/app/angular/accounts/account/account.js
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
|
||||
.config(function config($routeProvider) {
|
||||
$routeProvider.when('/accounts/create', {
|
||||
templateUrl: '/angular/accounts/account/account.tpl.html',
|
||||
controller: 'AccountsCreateController'
|
||||
});
|
||||
$routeProvider.when('/accounts/:id/edit', {
|
||||
templateUrl: '/angular/accounts/account/account.tpl.html',
|
||||
controller: 'AccountsEditController'
|
||||
});
|
||||
})
|
||||
|
||||
.controller('AccountsCreateController', function ($scope, AccountService, LemurRestangular){
|
||||
$scope.account = LemurRestangular.restangularizeElement(null, {}, 'accounts');
|
||||
$scope.save = AccountService.create;
|
||||
})
|
||||
|
||||
.controller('AccountsEditController', function ($scope, $routeParams, AccountService, AccountApi) {
|
||||
AccountApi.get($routeParams.id).then(function (account) {
|
||||
$scope.account = account;
|
||||
});
|
||||
|
||||
$scope.save = AccountService.update;
|
||||
});
|
45
lemur/static/app/angular/accounts/account/account.tpl.html
Normal file
45
lemur/static/app/angular/accounts/account/account.tpl.html
Normal file
@ -0,0 +1,45 @@
|
||||
<h2 class="featurette-heading"><span ng-show="!account.fromServer">Create</span><span ng-show="account.fromServer">Edit</span> Account <span class="text-muted"><small>next in line please
|
||||
</small></span></h2>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<a href="/#/accounts/" class="btn btn-danger pull-right">Cancel</a>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form name="createForm" class="form-horizontal" role="form" novalidate>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': createForm.name.$invalid, 'has-success': !createForm.name.$invalid&&createForm.name.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Name
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="name" ng-model="account.label" placeholder="Name" class="form-control" required/>
|
||||
<p ng-show="createForm.name.$invalid && !createForm.name.$pristine" class="help-block">You must enter an account name</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': createForm.accountNumber.$invalid, 'has-success': !createForm.accountNumber.$invalid&&createForm.accountNumber.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Account Number
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" name="accountNumber" ng-model="account.accountNumber" placeholder="111111111111" class="form-control" ng-minlength="12" ng-maxlength="12" required/>
|
||||
<p ng-show="createForm.accountNumber.$invalid && !createForm.accountNumber.$pristine" class="help-block">You must enter an account number</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Comments
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea name="comments" ng-model="account.comments" placeholder="Something elegant" class="form-control" ></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<button ng-click="save(account)" type="submit" ng-disabled="createForm.$invalid" class="btn btn-primary pull-right">Save</button>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
53
lemur/static/app/angular/accounts/services.js
vendored
Normal file
53
lemur/static/app/angular/accounts/services.js
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
'use strict';
|
||||
angular.module('lemur')
|
||||
.service('AccountApi', function (LemurRestangular) {
|
||||
return LemurRestangular.all('accounts');
|
||||
})
|
||||
.service('AccountService', function ($location, AccountApi, toaster) {
|
||||
var AccountService = this;
|
||||
AccountService.findAccountsByName = function (filterValue) {
|
||||
return AccountApi.getList({'filter[label]': filterValue})
|
||||
.then(function (accounts) {
|
||||
return accounts;
|
||||
});
|
||||
};
|
||||
|
||||
AccountService.create = function (account) {
|
||||
AccountApi.post(account).then(
|
||||
function () {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: account.label,
|
||||
body: 'Successfully created!'
|
||||
});
|
||||
$location.path('accounts');
|
||||
},
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: account.label,
|
||||
body: 'Was not created! ' + response.data.message
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
AccountService.update = function (account) {
|
||||
account.put().then(
|
||||
function () {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: account.label,
|
||||
body: 'Successfully updated!'
|
||||
});
|
||||
$location.path('accounts');
|
||||
},
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: account.label,
|
||||
body: 'Was not updated! ' + response.data.message
|
||||
});
|
||||
});
|
||||
};
|
||||
return AccountService;
|
||||
});
|
52
lemur/static/app/angular/accounts/view/view.js
vendored
Normal file
52
lemur/static/app/angular/accounts/view/view.js
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
|
||||
.config(function config($routeProvider) {
|
||||
$routeProvider.when('/accounts', {
|
||||
templateUrl: '/angular/accounts/view/view.tpl.html',
|
||||
controller: 'AccountsViewController'
|
||||
});
|
||||
})
|
||||
|
||||
.controller('AccountsViewController', function ($scope, AccountApi, AccountService, ngTableParams, toaster) {
|
||||
$scope.filter = {};
|
||||
$scope.accountsTable = new ngTableParams({
|
||||
page: 1, // show first page
|
||||
count: 10, // count per page
|
||||
sorting: {
|
||||
id: 'desc' // initial sorting
|
||||
},
|
||||
filter: $scope.filter
|
||||
}, {
|
||||
total: 0, // length of data
|
||||
getData: function ($defer, params) {
|
||||
AccountApi.getList(params.url()).then(
|
||||
function (data) {
|
||||
params.total(data.total);
|
||||
$defer.resolve(data);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$scope.remove = function (account) {
|
||||
account.remove().then(
|
||||
function () {
|
||||
$scope.accountsTable.reload();
|
||||
},
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: 'Opps',
|
||||
body: 'I see what you did there' + response.data.message
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
$scope.toggleFilter = function (params) {
|
||||
params.settings().$scope.show_filter = !params.settings().$scope.show_filter;
|
||||
};
|
||||
|
||||
});
|
44
lemur/static/app/angular/accounts/view/view.tpl.html
Normal file
44
lemur/static/app/angular/accounts/view/view.tpl.html
Normal file
@ -0,0 +1,44 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2 class="featurette-heading">Accounts
|
||||
<span class="text-muted"><small>next in line please</small></span></h2>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<div class="btn-group pull-right">
|
||||
<a href="#/accounts/create" class="btn btn-primary">Create</a>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button ng-click="toggleFilter(accountsTable)" class="btn btn-default">Filter</button>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table ng-table="accountsTable" class="table table-striped" show-filter="false" template-pagination="angular/pager.html" >
|
||||
<tbody>
|
||||
<tr ng-repeat="account in $data track by $index">
|
||||
<td data-title="'Name'" sortable="'label'" filter="{ 'label': 'text' }">
|
||||
<ul class="list-unstyled">
|
||||
<li>{{ account.label }}</li>
|
||||
<li><span class="text-muted">{{ account.comments }}</span></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td data-title="'Account Number'" sortable="'account_number'" filter="{ 'account_number': 'text' }">
|
||||
{{ account.accountNumber }}
|
||||
</td>
|
||||
<td data-title="''">
|
||||
<div class="btn-group-vertical pull-right">
|
||||
<a tooltip="Edit Account" href="#/accounts/{{ account.id }}/edit" class="btn btn-sm btn-info">
|
||||
Edit
|
||||
</a>
|
||||
<button tooltip="Delete Account" ng-click="remove(account)" type="button" class="btn btn-sm btn-danger pull-left">
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
113
lemur/static/app/angular/app.js
vendored
Normal file
113
lemur/static/app/angular/app.js
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
'use strict';
|
||||
|
||||
var lemur = angular
|
||||
.module('lemur', [
|
||||
'ngRoute',
|
||||
'ngTable',
|
||||
'ngAnimate',
|
||||
'chart.js',
|
||||
'restangular',
|
||||
'angular-loading-bar',
|
||||
'ui.bootstrap',
|
||||
'angular-spinkit',
|
||||
'toaster',
|
||||
'uiSwitch',
|
||||
'mgo-angular-wizard',
|
||||
'satellizer'
|
||||
])
|
||||
.config(function ($routeProvider, $authProvider) {
|
||||
$routeProvider
|
||||
.when('/', {
|
||||
templateUrl: 'angular/welcome/welcome.html'
|
||||
})
|
||||
.otherwise({
|
||||
redirectTo: '/'
|
||||
});
|
||||
|
||||
$authProvider.oauth2({
|
||||
name: 'ping',
|
||||
url: 'http://localhost:5000/api/1/auth/ping',
|
||||
redirectUri: 'http://localhost:3000/',
|
||||
clientId: 'client-id',
|
||||
responseType: 'code',
|
||||
scope: ['openid', 'email', 'profile', 'address'],
|
||||
scopeDelimiter: ' ',
|
||||
authorizationEndpoint: 'https://example.com/as/authorization.oauth2',
|
||||
requiredUrlParams: ['scope']
|
||||
});
|
||||
});
|
||||
|
||||
lemur.service('MomentService', function () {
|
||||
this.diffMoment = function (start, end) {
|
||||
if (end !== 'None') {
|
||||
return moment(end, 'YYYY-MM-DD HH:mm Z').diff(moment(start, 'YYYY-MM-DD HH:mm Z'), 'minutes') + ' minutes';
|
||||
}
|
||||
return 'Unknown';
|
||||
};
|
||||
this.createMoment = function (date) {
|
||||
if (date !== 'None') {
|
||||
return moment(date, 'YYYY-MM-DD HH:mm Z').fromNow();
|
||||
}
|
||||
return 'Unknown';
|
||||
};
|
||||
});
|
||||
|
||||
lemur.controller('datePickerController', function ($scope, $timeout){
|
||||
$scope.open = function() {
|
||||
$timeout(function() {
|
||||
$scope.opened = true;
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
lemur.factory('LemurRestangular', function (Restangular, $location, $auth) {
|
||||
return Restangular.withConfig(function (RestangularConfigurer) {
|
||||
RestangularConfigurer.setBaseUrl('http://127.0.0.1:5000/api/1');
|
||||
RestangularConfigurer.setDefaultHttpFields({withCredentials: true});
|
||||
|
||||
RestangularConfigurer.addResponseInterceptor(function (data, operation, what, url, response, deferred) {
|
||||
var extractedData;
|
||||
|
||||
// .. to look for getList operations
|
||||
if (operation === "getList") {
|
||||
// .. and handle the data and meta data
|
||||
extractedData = data.items;
|
||||
extractedData.total = data.total;
|
||||
} else {
|
||||
extractedData = data;
|
||||
}
|
||||
return extractedData;
|
||||
});
|
||||
|
||||
RestangularConfigurer.addFullRequestInterceptor(function (element, operation, route, url, headers, params, httpConfig) {
|
||||
// We want to make sure the user is auth'd before any requests
|
||||
if (!$auth.isAuthenticated()) {
|
||||
$location.path('/login');
|
||||
return false;
|
||||
}
|
||||
|
||||
var regExp = /\[([^)]+)\]/;
|
||||
|
||||
var s = 'sorting';
|
||||
var f = 'filter';
|
||||
var newParams = {};
|
||||
for (var item in params) {
|
||||
if (item.indexOf(s) > -1) {
|
||||
newParams.sortBy = regExp.exec(item)[1];
|
||||
newParams.sortDir = params[item];
|
||||
} else if (item.indexOf(f) > -1) {
|
||||
var key = regExp.exec(item)[1];
|
||||
newParams['filter'] = key + ";" + params[item];
|
||||
} else {
|
||||
newParams[item] = params[item];
|
||||
}
|
||||
}
|
||||
return { params: newParams };
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
lemur.run(['$templateCache', function ($templateCache) {
|
||||
$templateCache.put('ng-table/pager.html', '<div class="ng-cloak ng-table-pager"> <div ng-if="params.settings().counts.length" class="ng-table-counts btn-group pull-left"> <button ng-repeat="count in params.settings().counts" type="button" ng-class="{\'active\':params.count()==count}" ng-click="params.count(count)" class="btn btn-default"> <span ng-bind="count"></span> </button></div><div class="pull-right"><ul style="margin: 0; padding: 0;" class="pagination ng-table-pagination"> <li ng-class="{\'disabled\': !page.active}" ng-repeat="page in pages" ng-switch="page.type"> <a ng-switch-when="prev" ng-click="params.page(page.number)" href="">«</a> <a ng-switch-when="first" ng-click="params.page(page.number)" href=""><span ng-bind="page.number"></span></a> <a ng-switch-when="page" ng-click="params.page(page.number)" href=""><span ng-bind="page.number"></span></a> <a ng-switch-when="more" ng-click="params.page(page.number)" href="">…</a> <a ng-switch-when="last" ng-click="params.page(page.number)" href=""><span ng-bind="page.number"></span></a> <a ng-switch-when="next" ng-click="params.page(page.number)" href="">»</a> </li> </ul> </div></div>');
|
||||
}]);
|
28
lemur/static/app/angular/authentication/login/login.js
vendored
Normal file
28
lemur/static/app/angular/authentication/login/login.js
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
.config(function config($routeProvider) {
|
||||
$routeProvider.when('/login', {
|
||||
templateUrl: '/angular/authentication/login/login.tpl.html',
|
||||
controller: 'LoginController'
|
||||
});
|
||||
})
|
||||
.controller('LoginController', function ($rootScope, $scope, AuthenticationService, UserService) {
|
||||
$scope.login = AuthenticationService.login;
|
||||
$scope.authenticate = AuthenticationService.authenticate;
|
||||
$scope.logout = AuthenticationService.logout;
|
||||
|
||||
UserService.getCurrentUser().then(function (user) {
|
||||
$scope.currentUser = user;
|
||||
});
|
||||
|
||||
$rootScope.$on('user:login', function () {
|
||||
UserService.getCurrentUser().then(function (user) {
|
||||
$scope.currentUser = user;
|
||||
});
|
||||
});
|
||||
|
||||
$rootScope.$on('user:logout', function () {
|
||||
$scope.currentUser = null;
|
||||
});
|
||||
});
|
27
lemur/static/app/angular/authentication/login/login.tpl.html
Normal file
27
lemur/static/app/angular/authentication/login/login.tpl.html
Normal file
@ -0,0 +1,27 @@
|
||||
<h2 class="featurette-heading">Login <span class="text-muted"><small>None shall pass</small></span></h2>
|
||||
<div class="row">
|
||||
<div class="login">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-12 col-md-12">
|
||||
<button class="btn btn-block btn-default" ng-click="authenticate('ping')">
|
||||
Login with Meechum
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="login-or">
|
||||
<hr class="hr-or">
|
||||
<span class="span-or">or</span>
|
||||
</div>
|
||||
<form role="form" _lpchecked="1">
|
||||
<div class="form-group">
|
||||
<input type="text" ng-model="username" placeholder="Username" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" ng-model="password" placeholder="Password" class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button ng-click="login(username, password)" class="btn btn-block btn-success">Login</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
12
lemur/static/app/angular/authentication/logout/logout.js
vendored
Normal file
12
lemur/static/app/angular/authentication/logout/logout.js
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
.config(function config($routeProvider) {
|
||||
$routeProvider.when('/logout', {
|
||||
controller: 'LogoutCtrl'
|
||||
});
|
||||
})
|
||||
.controller('LogoutCtrl', function ($scope, $location, lemurRestangular, userService) {
|
||||
userService.logout();
|
||||
$location.path('/');
|
||||
});
|
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head lang="en">
|
||||
<meta charset="UTF-8">
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
62
lemur/static/app/angular/authentication/services.js
vendored
Normal file
62
lemur/static/app/angular/authentication/services.js
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
'use strict';
|
||||
angular.module('lemur')
|
||||
.service('AuthenticationApi', function (LemurRestangular) {
|
||||
return LemurRestangular.all('auth');
|
||||
})
|
||||
.service('AuthenticationService', function ($location, $rootScope, AuthenticationApi, UserService, toaster, $auth) {
|
||||
var AuthenticationService = this;
|
||||
|
||||
AuthenticationService.login = function (username, password) {
|
||||
AuthenticationApi.customPOST({'username': username, 'password': password}, 'login')
|
||||
.then(
|
||||
function (user) {
|
||||
$auth.setToken(user.token, true);
|
||||
$rootScope.$emit('user:login');
|
||||
$location.url('/certificates');
|
||||
},
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: 'Whoa there',
|
||||
body: response.data.message,
|
||||
showCloseButton: true
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
AuthenticationService.authenticate = function (provider) {
|
||||
$auth.authenticate(provider)
|
||||
.then(
|
||||
function (user) {
|
||||
UserService.getCurrentUser();
|
||||
$rootScope.$emit('user:login');
|
||||
$location.url('/certificates');
|
||||
},
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: 'Something went wrong',
|
||||
body: response.data.message
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
AuthenticationService.logout = function () {
|
||||
if (!$auth.isAuthenticated()) {
|
||||
return;
|
||||
}
|
||||
$auth.logout()
|
||||
.then(function() {
|
||||
$rootScope.$emit('user:logout');
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: 'Good job!',
|
||||
body: 'You have been successfully logged out.'
|
||||
});
|
||||
$location.path('/');
|
||||
})
|
||||
};
|
||||
|
||||
});
|
18
lemur/static/app/angular/authentication/unlock/unlock.js
vendored
Normal file
18
lemur/static/app/angular/authentication/unlock/unlock.js
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
.config(function config($routeProvider) {
|
||||
$routeProvider.when('/unlock', {
|
||||
templateUrl: '/angular/authentication/unlock/unlock.tpl.html',
|
||||
controller: 'UnlockCtrl'
|
||||
});
|
||||
})
|
||||
.controller('UnlockCtrl', function ($scope, $location, lemurRestangular, messageService) {
|
||||
$scope.unlock = function () {
|
||||
lemurRestangular.one('unlock').customPOST({'password': $scope.password})
|
||||
.then(function (data) {
|
||||
messageService.addMessage(data);
|
||||
$location.path('/dashboard');
|
||||
});
|
||||
};
|
||||
});
|
@ -0,0 +1,16 @@
|
||||
<h2 class="featurette-heading">Unlock <span class="text-muted"><small>Assume 9 is twice 5; how will you write 6 times 5 in the same system of notation?</small></span></h2>
|
||||
<form class="form-horizontal" _lpchecked="1">
|
||||
<fieldset class="col-lg-offset-4">
|
||||
<div class="form-group">
|
||||
<div class="col-lg-4">
|
||||
<input type="password" ng-model="password" placeholder="Password" class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="featurette-divider">
|
||||
<div class="form-group">
|
||||
<div class="col-lg-4">
|
||||
<button ng-click="unlock()" class="btn btn-success">Unlock</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
55
lemur/static/app/angular/authorities/authority/authority.js
vendored
Normal file
55
lemur/static/app/angular/authorities/authority/authority.js
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
|
||||
.config(function config($routeProvider) {
|
||||
$routeProvider.when('/authorities/create', {
|
||||
templateUrl: '/angular/authorities/authority/authorityWizard.tpl.html',
|
||||
controller: 'AuthorityCreateController'
|
||||
});
|
||||
$routeProvider.when('/authorities/:id/edit', {
|
||||
templateUrl: '/angular/authorities/authority/authorityEdit.tpl.html',
|
||||
controller: 'AuthorityEditController'
|
||||
});
|
||||
})
|
||||
|
||||
.controller('AuthorityEditController', function ($scope, $routeParams, AuthorityApi, AuthorityService, RoleService){
|
||||
AuthorityApi.get($routeParams.id).then(function (authority) {
|
||||
AuthorityService.getRoles(authority);
|
||||
$scope.authority = authority;
|
||||
});
|
||||
|
||||
$scope.authorityService = AuthorityService;
|
||||
$scope.save = AuthorityService.update;
|
||||
$scope.roleService = RoleService;
|
||||
})
|
||||
|
||||
.controller('AuthorityCreateController', function ($scope, $modal, AuthorityService, LemurRestangular, RoleService) {
|
||||
$scope.authority = LemurRestangular.restangularizeElement(null, {}, 'authorities');
|
||||
|
||||
$scope.save = function (authority) {
|
||||
var loadingModal = $modal.open({backdrop: 'static', template: '<wave-spinner></wave-spinner>', windowTemplateUrl: 'angular/loadingModal.html', size: 'large'});
|
||||
return AuthorityService.create(authority).then(function (response) {
|
||||
loadingModal.close();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$scope.roleService = RoleService;
|
||||
|
||||
$scope.authorityService = AuthorityService;
|
||||
|
||||
$scope.open = function($event) {
|
||||
$event.preventDefault();
|
||||
$event.stopPropagation();
|
||||
|
||||
$scope.opened1 = true;
|
||||
};
|
||||
|
||||
$scope.open2 = function($event) {
|
||||
$event.preventDefault();
|
||||
$event.stopPropagation();
|
||||
|
||||
$scope.opened2 = true;
|
||||
};
|
||||
});
|
@ -0,0 +1,44 @@
|
||||
<h2 class="featurette-heading">Edit</span> Authority <span class="text-muted"><small>Chain of command
|
||||
</small></span></h2>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<a href="#/authorities" class="btn btn-danger pull-right">Cancel</a>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form name="createForm" class="form-horizontal" role="form" novalidate>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Roles
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group">
|
||||
<input type="text" ng-model="authority.selectedRole" placeholder="Role Name"
|
||||
typeahead="role.name for role in roleService.findRoleByName($viewValue)" typeahead-loading="loadingRoles"
|
||||
class="form-control input-md" typeahead-on-select="authority.attachRole($item)" typeahead-min-wait="50"
|
||||
tooltip="Roles control which authorities a user can issue certificates from"
|
||||
tooltip-trigger="focus" tooltip-placement="top">
|
||||
<span class="input-group-btn">
|
||||
<button ng-model="roles.show" class="btn btn-md btn-default" btn-checkbox btn-checkbox-true="1" btn-checkbox-false="0">
|
||||
<span class="badge">{{ authority.roles.length || 0 }}</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<table ng-show="authority.roles" class="table">
|
||||
<tr ng-repeat="role in authority.roles track by $index">
|
||||
<td><a class="btn btn-sm btn-info" href="#/roles/{{ role.id }}/edit">{{ role.name }}</a></td>
|
||||
<td><span class="text-muted">{{ role.description }}</span></td>
|
||||
<td>
|
||||
<button type="button" ng-click="authority.removeRole($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<button ng-click="save(authority)" class="btn btn-success pull-right">Save</button>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,17 @@
|
||||
<h2 class="featurette-heading"><span ng-show="!authority.id">Create</span><span ng-show="authority.id">Edit</span> Authority <span class="text-muted"><small>The nail that sticks out farthest gets hammered the hardest
|
||||
<div>
|
||||
<wizard on-finish="save(authority)" template="angular/wizard.html">
|
||||
<wz-step title="Tracking" canexit="exitTracking">
|
||||
<ng-include src="'angular/authorities/authority/tracking.tpl.html'"></ng-include>
|
||||
</wz-step>
|
||||
<wz-step title="Distinguished Name" canenter="exitTracking" canexit="exitDN">
|
||||
<ng-include src="'angular/authorities/authority/distinguishedName.tpl.html'"></ng-include>
|
||||
</wz-step>
|
||||
<wz-step title="Options" canenter="exitDN" canexit="exitOptions">
|
||||
<ng-include src="'angular/authorities/authority/options.tpl.html'"></ng-include>
|
||||
</wz-step>
|
||||
<wz-step title="Extensions" canenter="exitOptions" canexit="exitExtensions">
|
||||
<ng-include src="'angular/authorities/authority/extensions.tpl.html'"></ng-include>
|
||||
</wz-step>
|
||||
</wizard>
|
||||
</div>
|
@ -0,0 +1,55 @@
|
||||
<form name="dnForm" novalidate>
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': dnForm.country.$invalid, 'has-success': !dnForm.country.$invalid&&dnForm.country.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Country
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="country" ng-model="authority.caDN.country" placeholder="Country" class="form-control" ng-init="authority.caDN.country = 'US'" required/>
|
||||
<p ng-show="dnForm.country.$invalid && !dnForm.country.$pristine" class="help-block">You must enter a country</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': dnForm.state.$invalid, 'has-success': !dnForm.$invalid&&dnForm.state.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
State
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="state" ng-model="authority.caDN.state" placeholder="State" class="form-control" ng-init="authority.caDN.state = 'CA'" required/>
|
||||
<p ng-show="dnForm.state.$invalid && !dnForm.state.$pristine" class="help-block">You must enter a state</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': dnForm.location.$invalid, 'has-success': !dnForm.$invalid&&dnForm.location.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Location
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="location" ng-model="authority.caDN.location" placeholder="Location" class="form-control" ng-init="authority.caDN.location = 'Los Gatos'"required/>
|
||||
<p ng-show="dnForm.location.$invalid && !dnForm.location.$pristine" class="help-block">You must enter a location</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': dnForm.organization.$invalid, 'has-success': !dnForm.$invalid&&dnForm.organization.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Organization
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="organization" ng-model="authority.caDN.organization" placeholder="Organization" class="form-control" ng-init="authority.caDN.organization = 'Netflix'" required/>
|
||||
<p ng-show="dnForm.organization.$invalid && !dnForm.organization.$pristine" class="help-block">You must enter a organization</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': dnForm.organizationalUnit.$invalid, 'has-success': !dnForm.$invalid&&dnForm.organizationalUnit.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Organizational Unit
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="organizationalUnit" ng-model="authority.caDN.organizationalUnit" placeholder="Organizational Unit" class="form-control" ng-init="authority.caDN.organizationalUnit = 'Operations'"required/>
|
||||
<p ng-show="dnForm.organization.$invalid && !dnForm.organizationalUnit.$pristine" class="help-block">You must enter a organizational unit</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -0,0 +1,219 @@
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Subject Alternate Names
|
||||
</label>
|
||||
<div class="col-sm-3">
|
||||
<select class="form-control" ng-model="authority.subAltType" ng-init="null" ng-options="item for item in ['DNSName', 'IPAddress', 'uniformResourceIdentifier', 'directoryName','rfc822Name', 'registeredID', 'otherName', 'x400Address', 'EDIPartyName']"></select>
|
||||
</div>
|
||||
<div class="col-sm-5">
|
||||
<div class="input-group">
|
||||
<input tooltip-trigger="focus" tooltip-placement="top" tooltip="String or Base64-encoded DER ASN.1 structure for the value" class="form-control" name="value" ng-model="authority.subAltValue" placeholder="Value" class="form-control" required/>
|
||||
<span class="input-group-btn">
|
||||
<button ng-click="authority.attachSubAltName()" class="btn btn-info">Add</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-8 col-sm-offset-2">
|
||||
<table class="table">
|
||||
<tr ng-repeat="alt in authority.extensions.subAltNames.names track by $index">
|
||||
<td>{{ alt.nameType }}</td>
|
||||
<td>{{ alt.value }}</td>
|
||||
<td>
|
||||
<button type="button" ng-click="authority.removeSubAltName($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Key Usage
|
||||
</label>
|
||||
<div class="col-sm-3">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="authority.extensions.keyUsage.useDigitalSignature">Digital Signature
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="authority.extensions.keyUsage.useNonRepudiation">Non Repudiation
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="authority.extensions.keyUsage.useKeyEncipherment">Key Encipherment
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="authority.extensions.keyUsage.useDataEncipherment">Data Encipherment
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="authority.extensions.keyUsage.useKeyAgreement">Key Agreement
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="authority.extensions.keyUsage.useKeyCertSign">Key Certificate Signature
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="authority.extensions.keyUsage.useCRLSign">CRL Sign
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="authority.extensions.keyUsage.useEncipherOnly">Encipher Only
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="authority.extensions.keyUsage.useDecipherOnly">Decipher Only
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Extended Key Usage
|
||||
</label>
|
||||
<div class="col-sm-3">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="authority.extensions.extendedKeyUsage.useServerAuthentication">Server Authentication
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="authority.extensions.extendedKeyUsage.useEmail">Email
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="authority.extensions.extendedKeyUsage.useTimestamping">Timestamping
|
||||
</label>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="authority.extensions.extendedKeyUsage.useEapOverLAN">EAP Over LAN
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="authority.extensions.extendedKeyUsage.useEapOverPPP">EAP Over PPP
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="authority.extensions.extendedKeyUsage.useSmartCardLogon">Smartcard Logon
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="authority.extensions.extendedKeyUsage.useOCSPSigning">OCSP Signing
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Authority Key Identifier
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label tooltip-trigger="mouseenter" tooltip-placement="top" tooltip="Put Issuer's keyIdentifier in this extension" >
|
||||
<input type="checkbox" ng-model="authority.extensions.authorityKeyIdentifier.useKeyIdentifier">Key Identifier
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label tooltip-trigger="mouseenter" tooltip-placement="top" tooltip="Put Issuer's Name and Serial number" >
|
||||
<input type="checkbox" ng-model="authority.extensions.authorityKeyIdentifier.useAuthorityCert">Authority Certificate
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Authority Information Access
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label tooltip-trigger="mouseenter" tooltip-placement="top" tooltip="Ask CA to include/not include AIA extension" >
|
||||
<input type="checkbox" ng-model="authority.extensions.authorityInfoAccess.includeAIA">Include AIA
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Subject Key Identifier
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label tooltip-trigger="mouseenter" tooltip-placement="top" tooltip="Ask CA to include/not include Subject Key Identifier" >
|
||||
<input type="checkbox" ng-model="authority.extensions.subjectKeyIdentifier.includeSKI">Include SKI
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
cRL Distribution Points
|
||||
</label>
|
||||
<div class="col-sm-8">
|
||||
<select class="form-control" ng-model="authority.extensions.cRLDistributionPoints.includeCRLDP" ng-options="item for item in ['yes', 'no', 'default']"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Custom
|
||||
</label>
|
||||
<div class="col-sm-2">
|
||||
<input tooltip-trigger="focus" tooltip-placement="top" tooltip="OID for the custom extension e.g. 1.12.123.12.10" class="form-control" name="oid" ng-model="authority.customOid" placeholder="Oid" class="form-control" required/>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<select tooltip-trigger="focus" tooltip-placement="top" tooltip="Encoding for value" class="form-control col-sm-2" ng-model="authority.customEncoding" ng-options="item for item in ['b64asn1', 'string', 'ia5string']"></select>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="input-group">
|
||||
<input tooltip-trigger="focus" tooltip-placement="top" tooltip="String or Base64-encoded DER ASN.1 structure for the value" class="form-control" name="value" ng-model="authority.customValue" placeholder="Value" class="form-control" required/>
|
||||
<span class="input-group-btn">
|
||||
<button ng-click="authority.attachCustom()" class="btn btn-info">Add</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="authority.extensions.custom.isCritical">Critical
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-8 col-sm-offset-2">
|
||||
<table class="table">
|
||||
<tr ng-repeat="custom in authority.extensions.custom track by $index">
|
||||
<td>{{ custom.oid }}</td>
|
||||
<td>{{ custom.encoding }}</td>
|
||||
<td>{{ custom.value }}</td>
|
||||
<td>{{ custom.isCritical}}</td>
|
||||
<td>
|
||||
<button type="button" ng-click="authority.removeCustom($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,78 @@
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Type
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<select class="form-control" ng-model="authority.caType" ng-options="option for option in ['root', 'subca']" ng-init="authority.caType = 'root'"required></select>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="authority.caType == 'subca'" class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Parent Authority
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" ng-model="authority.caParent" placeholder="Parent Authority Name"
|
||||
typeahead="authority.name for authority in authorityService.findAuthorityByName($viewValue)" typeahead-loading="loadingAuthorities"
|
||||
class="form-control input-md" typeahead-min-wait="50"
|
||||
tooltip="When you specifiy a subordinate certificate authority you must specific the parent authority"
|
||||
tooltip-trigger="focus" tooltip-placement="top">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Signing Algorithm
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<select class="form-control" ng-model="authority.caSigningAlgo" ng-options="option for option in ['sha1WithRSA', 'sha256WithRSA']" ng-init="authority.caSigningAlgo = 'sha256WithRSA'"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Sensitivity
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<select class="form-control" ng-model="authority.caSensitivity" ng-options="option for option in ['medium', 'high']" ng-init="authority.caSensitivity = 'medium'"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Key Type
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<select class="form-control" ng-model="authority.keyType" ng-options="option for option in ['RSA2048', 'RSA4096']" ng-init="authority.keyType = 'RSA2048'"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="authority.caSensitivity == 'high'" class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Key Name
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" class="form-control" ng-model="authority.keyName" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Serial Number
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" name="serialNumber" ng-model="authority.caSerialNumber" placeholder="Serial Number" class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
First Serial Number
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="number" name="firstSerialNumber" ng-model="authority.caFirstSerial" placeholder="First Serial Number" class="form-control" ng-init="1000" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Plugin Name
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<select class="form-control" ng-model="authority.pluginName" ng-options="option for option in ['cloudca', 'verisign']" ng-init="authority.pluginName = 'cloudca'" required></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,28 @@
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Roles
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group">
|
||||
<input type="text" ng-model="authority.selectedRole" placeholder="Role Name"
|
||||
typeahead="role.name for role in roleService.findRoleByName($viewValue)" typeahead-loading="loadingAccounts"
|
||||
class="form-control input-md" typeahead-on-select="authority.attachRole($item)" typeahead-min-wait="50"
|
||||
tooltip="These are the User roles you wish to associated with your authority"
|
||||
tooltip-trigger="focus" tooltip-placement="top">
|
||||
<span class="input-group-btn">
|
||||
<button ng-model="roles.show" class="btn btn-md btn-default" btn-checkbox btn-checkbox-true="1" btn-checkbox-false="0">
|
||||
<span class="badge">{{ authority.roles.length || 0 }}</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<table ng-show="roles.show" class="table">
|
||||
<tr ng-repeat="role in authority.roles track by $index">
|
||||
<td><a class="btn btn-sm btn-info" href="#/accounts/{{ account.id }}/certificates">{{ role.name }}</a></td>
|
||||
<td><span class="text-muted">{{ role.description }}</span></td>
|
||||
<td>
|
||||
<button type="button" ng-click="authority.removeRole($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,3 @@
|
||||
<a tabindex="-1">
|
||||
{{ match.model.name }} - <span class="text-muted">{{match.model.description }}</span>
|
||||
</a>
|
@ -0,0 +1,71 @@
|
||||
<form name="trackingForm" novalidate>
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': trackingForm.caName.$invalid, 'has-success': !trackingForm.caName.$invalid&&trackingForm.caName.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Name
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="caName" ng-model="authority.caName" placeholder="Name" tooltip="This will be the name of your authority, it is the name you will reference when creating new certificates" class="form-control" ng-pattern="/^[A-Za-z0-9_-]+$/" required/>
|
||||
<p ng-show="trackingForm.caName.$invalid && !trackingForm.caName.$pristine" class="help-block">You must enter a valid authority name, spaces are not allowed</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': trackingForm.ownerEmail.$invalid, 'has-success': !trackingForm.$invalid&&trackingForm.ownerEmail.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Owner
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="email" name="ownerEmail" ng-model="authority.ownerEmail" placeholder="TeamDL@netflix.com" tooltip="This is the authorities team distribution list or the main point of contact for this authority" class="form-control" required/>
|
||||
<p ng-show="trackingForm.ownerEmail.$invalid && !trackingForm.ownerEmail.$pristine" class="help-block">You must enter an Certificate Authority owner</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': trackingForm.caDescription.$invalid, 'has-success': !trackingForm.$invalid&&trackingForm.caDescription.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Description
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea name="caDescription" ng-model="authority.caDescription" placeholder="Something elegant" class="form-control" ng-maxlength="250" ng-pattern="/^[\w\-\s]+$/" required></textarea>
|
||||
<p ng-show="trackingForm.caDescription.$invalid && !trackingForm.caDescription.$pristine" class="help-block">You must give a short description about this authority will be used for, it should contain only alphanumeric characters</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': trackingForm.commonName.$invalid, 'has-success': !trackingForm.$invalid&&trackingForm.commanName.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Common Name
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="commonName" ng-model="authority.caDN.commonName" placeholder="Common Name" class="form-control" required/>
|
||||
<p ng-show="trackingForm.commandName.$invalid && !trackingForm.commonName.$pristine" class="help-block">You must enter a common name</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Validity Range
|
||||
</label>
|
||||
<div class="col-sm-4">
|
||||
<div>
|
||||
<div class="input-group">
|
||||
<input tooltip="Starting Date" class="form-control" datepicker-popup="yyyy/MM/dd" is-open="opened1" ng-model="authority.validityStart" />
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-default" ng-click="open($event)"><i class="glyphicon glyphicon-calendar"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-center col-sm-2"><label><span class="glyphicon glyphicon-resize-horizontal"></span></label></span>
|
||||
<div class="col-sm-4">
|
||||
<div>
|
||||
<div class="input-group">
|
||||
<input tooltip="Ending Date" class="form-control" datepicker-popup="yyyy/MM/dd" is-open="opened2" ng-model="authority.validityEnd" />
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-default" ng-click="open2($event)"><i class="glyphicon glyphicon-calendar"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
130
lemur/static/app/angular/authorities/services.js
vendored
Normal file
130
lemur/static/app/angular/authorities/services.js
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
'use strict';
|
||||
angular.module('lemur')
|
||||
.service('AuthorityApi', function (LemurRestangular) {
|
||||
LemurRestangular.extendModel('authorities', function (obj) {
|
||||
return angular.extend(obj, {
|
||||
attachRole: function (role) {
|
||||
this.selectedRole = null;
|
||||
if (this.roles === undefined) {
|
||||
this.roles = [];
|
||||
}
|
||||
this.roles.push(role);
|
||||
},
|
||||
removeRole: function (index) {
|
||||
this.roles.splice(index, 1);
|
||||
},
|
||||
attachSubAltName: function () {
|
||||
if (this.extensions === undefined || this.extensions.subAltNames === undefined) {
|
||||
this.extensions = {'subAltNames': {'names': []}};
|
||||
}
|
||||
|
||||
if (angular.isString(this.subAltType) && angular.isString(this.subAltValue)) {
|
||||
this.extensions.subAltNames.names.push({'nameType': this.subAltType, 'value': this.subAltValue});
|
||||
}
|
||||
|
||||
this.subAltType = null;
|
||||
this.subAltValue = null;
|
||||
},
|
||||
removeSubAltName: function (index) {
|
||||
this.extensions.subAltNames.names.splice(index, 1);
|
||||
},
|
||||
attachCustom: function () {
|
||||
if (this.extensions === undefined || this.extensions.custom === undefined) {
|
||||
this.extensions = {'custom': []};
|
||||
}
|
||||
|
||||
if (angular.isString(this.customOid) && angular.isString(this.customEncoding) && angular.isString(this.customValue)) {
|
||||
this.extensions.custom.push(
|
||||
{
|
||||
'oid': this.customOid,
|
||||
'isCritical': this.customIsCritical,
|
||||
'encoding': this.customEncoding,
|
||||
'value': this.customValue
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
this.customOid = null;
|
||||
this.customIsCritical = null;
|
||||
this.customEncoding = null;
|
||||
this.customValue = null;
|
||||
},
|
||||
removeCustom: function (index) {
|
||||
this.extensions.custom.splice(index, 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
return LemurRestangular.all('authorities');
|
||||
})
|
||||
.service('AuthorityService', function ($location, AuthorityApi, toaster) {
|
||||
var AuthorityService = this;
|
||||
AuthorityService.findAuthorityByName = function (filterValue) {
|
||||
return AuthorityApi.getList({'filter[name]': filterValue})
|
||||
.then(function (authorites) {
|
||||
return authorites;
|
||||
});
|
||||
};
|
||||
|
||||
AuthorityService.create = function (authority) {
|
||||
authority.attachSubAltName();
|
||||
return AuthorityApi.post(authority).then(
|
||||
function () {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: authority.name,
|
||||
body: 'Successfully created!'
|
||||
});
|
||||
$location.path('/authorities');
|
||||
},
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: authority.name,
|
||||
body: 'Was not created! ' + response.data.message
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
AuthorityService.update = function (authority) {
|
||||
authority.put().then(
|
||||
function () {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: authority.name,
|
||||
body: 'Successfully updated!'
|
||||
});
|
||||
$location.path('/authorities');
|
||||
},
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: authority.name,
|
||||
body: 'Update Failed! ' + response.data.message
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
AuthorityService.getRoles = function (authority) {
|
||||
authority.getList('roles').then(function (roles) {
|
||||
authority.roles = roles;
|
||||
});
|
||||
};
|
||||
|
||||
AuthorityService.updateActive = function (authority) {
|
||||
authority.put().then(
|
||||
function () {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: authority.name,
|
||||
body: 'Successfully updated!'
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: authority.name,
|
||||
body: 'Update Failed! ' + response.data.message
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
46
lemur/static/app/angular/authorities/view/view.js
vendored
Normal file
46
lemur/static/app/angular/authorities/view/view.js
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
|
||||
.config(function config($routeProvider) {
|
||||
$routeProvider.when('/authorities', {
|
||||
templateUrl: '/angular/authorities/view/view.tpl.html',
|
||||
controller: 'AuthoritiesViewController'
|
||||
});
|
||||
})
|
||||
|
||||
.controller('AuthoritiesViewController', function ($scope, $q, AuthorityApi, AuthorityService, ngTableParams) {
|
||||
$scope.filter = {};
|
||||
$scope.authoritiesTable = new ngTableParams({
|
||||
page: 1, // show first page
|
||||
count: 10, // count per page
|
||||
sorting: {
|
||||
id: 'desc' // initial sorting
|
||||
},
|
||||
filter: $scope.filter
|
||||
}, {
|
||||
total: 0, // length of data
|
||||
getData: function ($defer, params) {
|
||||
AuthorityApi.getList(params.url()).then(function (data) {
|
||||
_.each(data, function(authority) {
|
||||
AuthorityService.getRoles(authority);
|
||||
});
|
||||
params.total(data.total);
|
||||
$defer.resolve(data);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.authorityService = AuthorityService;
|
||||
|
||||
$scope.getAuthorityStatus = function () {
|
||||
var def = $q.defer();
|
||||
def.resolve([{'title': 'Active', 'id': true}, {'title': 'Inactive', 'id': false}])
|
||||
return def;
|
||||
};
|
||||
|
||||
$scope.toggleFilter = function (params) {
|
||||
params.settings().$scope.show_filter = !params.settings().$scope.show_filter;
|
||||
};
|
||||
|
||||
});
|
50
lemur/static/app/angular/authorities/view/view.tpl.html
Normal file
50
lemur/static/app/angular/authorities/view/view.tpl.html
Normal file
@ -0,0 +1,50 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2 class="featurette-heading">Authorities
|
||||
<span class="text-muted"><small>The nail that sticks out farthest gets hammered the hardest</small></span></h2>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<div class="btn-group pull-right">
|
||||
<a href="#/authorities/create" class="btn btn-primary">Create</a>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button ng-click="toggleFilter(authoritiesTable)" class="btn btn-default">Filter</button>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table ng-table="authoritiesTable" class="table table-striped" template-pagination="angular/pager.html" show-filter="false">
|
||||
<tbody>
|
||||
<tr ng-repeat="authority in $data track by $index">
|
||||
<td data-title="'Name'" sortable="'name'" filter="{ 'name': 'text' }">
|
||||
<ul class="list-unstyled">
|
||||
<li>{{ authority.name }}</li>
|
||||
<li><span class="text-muted">{{ authority.description }}</span></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td data-title="'Active'" filter="{ 'active': 'select' }" filter-data="getAuthorityStatus()">
|
||||
<form>
|
||||
<switch ng-change="authorityService.updateActive(authority)" id="status" name="status" ng-model="authority.active" class="green small"></switch>
|
||||
</form>
|
||||
</td>
|
||||
<td data-title="'Roles'"> <!--filter="{ 'select': 'role' }" filter-data="roleService.getRoleDropDown()">-->
|
||||
<div class="btn-group">
|
||||
<button ng-repeat="role in authority.roles" class="btn btn-sm btn-danger">
|
||||
{{ role.name }}
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<td data-title="''">
|
||||
<div class="btn-group-vertical pull-right">
|
||||
<a tooltip="Edit Authority" href="#/authorities/{{ authority.id }}/edit" class="btn btn-sm btn-info">
|
||||
Edit
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
94
lemur/static/app/angular/certificates/certificate/certificate.js
vendored
Normal file
94
lemur/static/app/angular/certificates/certificate/certificate.js
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
.config(function config($routeProvider) {
|
||||
$routeProvider.when('/certificates/create', {
|
||||
templateUrl: '/angular/certificates/certificate/certificateWizard.tpl.html',
|
||||
controller: 'CertificateCreateController'
|
||||
});
|
||||
|
||||
$routeProvider.when('/certificates/:id/edit', {
|
||||
templateUrl: '/angular/certificates/certificate/edit.tpl.html',
|
||||
controller: 'CertificateEditController'
|
||||
});
|
||||
})
|
||||
|
||||
.controller('CertificateEditController', function ($scope, $routeParams, CertificateApi, CertificateService, MomentService) {
|
||||
CertificateApi.get($routeParams.id).then(function (certificate) {
|
||||
$scope.certificate = certificate;
|
||||
});
|
||||
|
||||
$scope.momentService = MomentService;
|
||||
$scope.save = CertificateService.update;
|
||||
|
||||
})
|
||||
|
||||
.controller('CertificateCreateController', function ($scope, $modal, CertificateApi, CertificateService, AccountService, ELBService, AuthorityService, MomentService, LemurRestangular) {
|
||||
$scope.certificate = LemurRestangular.restangularizeElement(null, {}, 'certificates');
|
||||
|
||||
$scope.save = function (certificate) {
|
||||
var loadingModal = $modal.open({backdrop: 'static', template: '<wave-spinner></wave-spinner>', windowTemplateUrl: 'angular/loadingModal.html', size: 'large'});
|
||||
CertificateService.create(certificate).then(function (response) {
|
||||
loadingModal.close();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.templates = [
|
||||
{
|
||||
'name': 'Client Certificate',
|
||||
'description': '',
|
||||
'extensions': {
|
||||
'basicConstraints': {},
|
||||
'keyUsage': {
|
||||
'isCritical': true,
|
||||
'useDigitalSignature': true
|
||||
},
|
||||
'extendedKeyUsage': {
|
||||
'isCritical': true,
|
||||
'useClientAuthentication': true
|
||||
},
|
||||
'subjectKeyIdentifier': {
|
||||
'includeSKI': true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
'name': 'Server Certificate',
|
||||
'description': '',
|
||||
'extensions' : {
|
||||
'basicConstraints': {},
|
||||
'keyUsage': {
|
||||
'isCritical': true,
|
||||
'useKeyEncipherment': true,
|
||||
'useDigitalSignature': true
|
||||
},
|
||||
'extendedKeyUsage': {
|
||||
'isCritical': true,
|
||||
'useServerAuthentication': true
|
||||
},
|
||||
'subjectKeyIdentifier': {
|
||||
'includeSKI': true
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
$scope.openNotBefore = function($event) {
|
||||
$event.preventDefault();
|
||||
$event.stopPropagation();
|
||||
|
||||
$scope.openNotBefore.isOpen = true;
|
||||
};
|
||||
|
||||
$scope.openNotAfter = function($event) {
|
||||
$event.preventDefault();
|
||||
$event.stopPropagation();
|
||||
|
||||
$scope.openNotAfter.isOpen = true;
|
||||
|
||||
};
|
||||
|
||||
$scope.elbService = ELBService;
|
||||
$scope.authorityService = AuthorityService;
|
||||
$scope.accountService = AccountService;
|
||||
});
|
@ -0,0 +1,20 @@
|
||||
<h2 class="featurette-heading">Create a certificate <span class="text-muted"><small>encrypt all the things
|
||||
</small></span></h2>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<a href="/#/certificates/" class="btn btn-danger pull-right">Cancel</a>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form name="createForm" class="form-horizontal" role="form" ng-submit="submitCreate()" novalidate>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<button type="submit" ng-disabled="createForm.$invalid" class="btn btn-success pull-right"><span
|
||||
ng-show="!loading"> Create </span><span ng-show="loading">Creating <i ng-show="loading"
|
||||
class="fa fa-cog fa-spin"></i></span>
|
||||
</button>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,17 @@
|
||||
<h2 class="featurette-heading"><span ng-show="!certificate.id">Create</span><span ng-show="certificate.id">Edit</span> Certificate <span class="text-muted"><small>encrypt all the things
|
||||
<div>
|
||||
<wizard on-finish="save(certificate)" template="angular/wizard.html">
|
||||
<wz-step title="Tracking" canexit="trackingForm.$valid">
|
||||
<ng-include src="'angular/certificates/certificate/tracking.tpl.html'"></ng-include>
|
||||
</wz-step>
|
||||
<wz-step title="Distinguished Name" canenter="exitTracking" canexit="exitDN">
|
||||
<ng-include src="'angular/certificates/certificate/distinguishedName.tpl.html'"></ng-include>
|
||||
</wz-step>
|
||||
<wz-step title="Options" canenter="enterValidation">
|
||||
<ng-include src="'angular/certificates/certificate/options.tpl.html'"></ng-include>
|
||||
</wz-step>
|
||||
<wz-step title="Destinations" canenter="enterValidation">
|
||||
<ng-include src="'angular/certificates/certificate/destinations.tpl.html'"></ng-include>
|
||||
</wz-step>
|
||||
</wizard>
|
||||
</div>
|
@ -0,0 +1,62 @@
|
||||
<p>Destinations are purely optional, if you think the created certificate will be used in AWS select one or more accounts and Lemur will upload it for you.</p>
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Accounts
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group">
|
||||
<input type="text" ng-model="certificate.selectedAccount" placeholder="Account Name"
|
||||
typeahead="account.label for account in accountService.findAccountsByName($viewValue)" typeahead-loading="loadingAccounts"
|
||||
class="form-control input-md" typeahead-on-select="certificate.attachAccount($item)" typeahead-min-wait="50"
|
||||
tooltip="Lemur can upload the certificate to any AWS account."
|
||||
tooltip-trigger="focus" tooltip-placement="top">
|
||||
<span class="input-group-btn">
|
||||
<button ng-model="accounts.show" class="btn btn-md btn-default" btn-checkbox btn-checkbox-true="1" btn-checkbox-false="0">
|
||||
<span class="badge">{{ certificate.accounts.length || 0 }}</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<table class="table">
|
||||
<tr ng-repeat="account in certificate.accounts track by $index">
|
||||
<td><a class="btn btn-sm btn-info" href="#/accounts/{{ account.id }}/certificates">{{ account.label }}</a></td>
|
||||
<td><span class="text-muted">{{ account.comments }}</span></td>
|
||||
<td>
|
||||
<button type="button" ng-click="certificate.removeAccount($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!--<div ng-show="certificate.accounts" class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
ELBs
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group">
|
||||
<input type="text" ng-model="certificate.selectedELB" placeholder="ELB Name"
|
||||
typeahead="elb.name for elb in elbService.findELBByName($viewValue)" typeahead-loading="loadingAccounts"
|
||||
class="form-control col-md-4" typeahead-min-wait="50"
|
||||
tooltip="Lemur can upload a certificate to one or more ELBs"
|
||||
tooltip-trigger="focus" tooltip-placement="top"/>
|
||||
<input class="form-control col-md-2" type="integer" ng-model="certificate.selectedPort" placeholder="Port"/>
|
||||
<select class="form-control col-md-2" ng-options="item for item in ['https', 'tcp']"></select>
|
||||
<span class="input-group-btn">
|
||||
<button ng-click="certificate.attachELB()" class="btn btn-info">Add</button>
|
||||
</span>
|
||||
</div>
|
||||
<table class="table">
|
||||
<tr ng-repeat="elb in certificate.elbs track by $index">
|
||||
<td><a class="btn btn-sm btn-info" href="#/accounts/{{ elb.id }}/elbs">{{ elb.name }}</a></td>
|
||||
<td>{{ elb.region }}</td>
|
||||
<td>{{ elb.scheme }}</td>
|
||||
<td>{{ elb.vpcId }}</td>
|
||||
<td>{{ elb.listener.scheme }}</td>
|
||||
<td>{{ elb.listener.port }}</td>
|
||||
<td>
|
||||
<button type="button" ng-click="certificate.removeELB($index)" class="btn btn-danger btn-sm pull-right">remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>-->
|
||||
</div>
|
@ -0,0 +1,55 @@
|
||||
<form name="dnForm" novalidate>
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': dnForm.country.$invalid, 'has-success': !dnForm.country.$invalid&&dnForm.country.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Country
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="country" ng-model="certificate.country" placeholder="Country" class="form-control" ng-init="certificate.country = 'US'" required/>
|
||||
<p ng-show="dnForm.country.$invalid && !dnForm.country.$pristine" class="help-block">You must enter a country</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': dnForm.state.$invalid, 'has-success': !dnForm.$invalid&&dnForm.state.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
State
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="state" ng-model="certificate.state" placeholder="State" class="form-control" ng-init="certificate.state = 'CA'" required/>
|
||||
<p ng-show="dnForm.state.$invalid && !dnForm.state.$pristine" class="help-block">You must enter a state</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': dnForm.location.$invalid, 'has-success': !dnForm.$invalid&&dnForm.location.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Location
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="location" ng-model="certificate.location" placeholder="Location" class="form-control" ng-init="certificate.location = 'Los Gatos'"required/>
|
||||
<p ng-show="dnForm.location.$invalid && !dnForm.location.$pristine" class="help-block">You must enter a location</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': dnForm.organization.$invalid, 'has-success': !dnForm.$invalid&&dnForm.organization.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Organization
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="organization" ng-model="certificate.organization" placeholder="Organization" class="form-control" ng-init="certificate.organization = 'Netflix'" required/>
|
||||
<p ng-show="dnForm.organization.$invalid && !dnForm.organization.$pristine" class="help-block">You must enter a organization</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': dnForm.organizationalUnit.$invalid, 'has-success': !dnForm.$invalid&&dnForm.organizationalUnit.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Organizational Unit
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="organizationalUnit" ng-model="certificate.organizationalUnit" placeholder="Organizational Unit" class="form-control" ng-init="certificate.organizationalUnit = 'Operations'"required/>
|
||||
<p ng-show="dnForm.organization.$invalid && !dnForm.organizationalUnit.$pristine" class="help-block">You must enter a organizational unit</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -0,0 +1,224 @@
|
||||
<form name="optionsForm" novalidate>
|
||||
<div class="form-horizontal">
|
||||
<div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Subject Alternate Names
|
||||
</label>
|
||||
<div class="col-sm-3">
|
||||
<select class="form-control" ng-model="certificate.subAltType" ng-options="item for item in ['DNSName', 'IPAddress', 'uniformResourceIdentifier', 'directoryName','rfc822Name', 'registeredID', 'otherName', 'x400Address', 'EDIPartyName']"></select>
|
||||
</div>
|
||||
<div class="col-sm-5">
|
||||
<div class="input-group">
|
||||
<input tooltip-trigger="focus" tooltip-placement="top" tooltip="String or Base64-encoded DER ASN.1 structure for the value" class="form-control" name="value" ng-model="certificate.subAltValue" placeholder="Value" class="form-control" required/>
|
||||
<span class="input-group-btn">
|
||||
<button ng-click="certificate.attachSubAltName()" class="btn btn-info">Add</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-8 col-sm-offset-2">
|
||||
<table class="table">
|
||||
<tr ng-repeat="alt in certificate.extensions.subAltNames.names track by $index">
|
||||
<td>{{ alt.nameType }}</td>
|
||||
<td>{{ alt.value }}</td>
|
||||
<td>
|
||||
<button type="button" ng-click="certificate.removeSubAltName($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Key Usage
|
||||
</label>
|
||||
<div class="col-sm-3">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="certificate.extensions.keyUsage.useDigitalSignature">Digital Signature
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="certificate.extensions.keyUsage.useNonRepudiation">Non Repudiation
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="certificate.extensions.keyUsage.useKeyEncipherment">Key Encipherment
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="certificate.extensions.keyUsage.useDataEncipherment">Data Encipherment
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="certificate.extensions.keyUsage.useKeyAgreement">Key Agreement
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="certificate.extensions.keyUsage.useKeyCertSign">Key Certificate Signature
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="certificate.extensions.keyUsage.useCRLSign">CRL Sign
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="certificate.extensions.keyUsage.useEncipherOnly">Encipher Only
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="certificate.extensions.keyUsage.useDecipherOnly">Decipher Only
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Extended Key Usage
|
||||
</label>
|
||||
<div class="col-sm-3">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="certificate.extensions.extendedKeyUsage.useServerAuthentication">Server Authentication
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="certificate.extensions.extendedKeyUsage.useEmail">Email
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="certificate.extensions.extendedKeyUsage.useTimestamping">Timestamping
|
||||
</label>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="certificate.extensions.extendedKeyUsage.useEapOverLAN">EAP Over LAN
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="certificate.extensions.extendedKeyUsage.useEapOverPPP">EAP Over PPP
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="certificate.extensions.extendedKeyUsage.useSmartCardLogon">Smartcard Logon
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="certificate.extensions.extendedKeyUsage.useOCSPSigning">OCSP Signing
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Authority Key Identifier
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label tooltip-trigger="mouseenter" tooltip-placement="top" tooltip="Put Issuer's keyIdentifier in this extension" >
|
||||
<input type="checkbox" ng-model="certificate.extensions.authorityKeyIdentifier.useKeyIdentifier">Key Identifier
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label tooltip-trigger="mouseenter" tooltip-placement="top" tooltip="Put Issuer's Name and Serial number" >
|
||||
<input type="checkbox" ng-model="certificate.extensions.authorityIdentifier.useAuthorityCert">Authority Certificate
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Authority Information Access
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label tooltip-trigger="mouseenter" tooltip-placement="top" tooltip="Ask CA to include/not include AIA extension" >
|
||||
<input type="checkbox" ng-model="certificate.extensions.certificateInfoAccess.includeAIA">Include AIA
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Subject Key Identifier
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="checkbox">
|
||||
<label tooltip-trigger="mouseenter" tooltip-placement="top" tooltip="Ask CA to include/not include Subject Key Identifier" >
|
||||
<input type="checkbox" ng-model="certificate.extensions.subjectKeyIdentifier.includeSKI">Include SKI
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
cRL Distribution Points
|
||||
</label>
|
||||
<div class="col-sm-8">
|
||||
<select class="form-control" ng-model="certificate.extensions.cRLDistributionPoints.includeCRLDP" ng-options="item for item in ['yes', 'no', 'default']"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Custom
|
||||
</label>
|
||||
<div class="col-sm-2">
|
||||
<input tooltip-trigger="focus" tooltip-placement="top" tooltip="OID for the custom extension e.g. 1.12.123.12.10" class="form-control" name="oid" ng-model="certificate.customOid" placeholder="Oid" class="form-control" required/>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<select tooltip-trigger="focus" tooltip-placement="top" tooltip="Encoding for value" class="form-control col-sm-2" ng-model="certificate.customEncoding" ng-options="item for item in ['b64asn1', 'string', 'ia5string']"></select>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="input-group">
|
||||
<input tooltip-trigger="focus" tooltip-placement="top" tooltip="String or Base64-encoded DER ASN.1 structure for the value" class="form-control" name="value" ng-model="certificate.customValue" placeholder="Value" class="form-control" required/>
|
||||
<span class="input-group-btn">
|
||||
<button ng-click="certificate.attachCustom()" class="btn btn-info">Add</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="certificate.extensions.custom.isCritical">Critical
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-8 col-sm-offset-2">
|
||||
<table class="table">
|
||||
<tr ng-repeat="custom in certificate.extensions.custom track by $index">
|
||||
<td>{{ custom.oid }}</td>
|
||||
<td>{{ custom.encoding }}</td>
|
||||
<td>{{ custom.value }}</td>
|
||||
<td>{{ custom.isCritical}}</td>
|
||||
<td>
|
||||
<button type="button" ng-click="certificate.removeCustom($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
@ -0,0 +1,103 @@
|
||||
<form name="trackingForm" novalidate>
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': trackingForm.ownerEmail.$invalid, 'has-success': !trackingForm.$invalid&&trackingForm.ownerEmail.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Owner
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="email" name="ownerEmail" ng-model="certificate.owner" placeholder="TeamDL@netflix.com" tooltip="This is the certificates team distribution list or main point of contact" class="form-control" required/>
|
||||
<p ng-show="trackingForm.ownerEmail.$invalid && !trackingForm.ownerEmail.$pristine" class="help-block">You must enter an Certificate owner</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': trackingForm.description.$invalid, 'has-success': !trackingForm.$invalid&&trackingForm.description.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Description
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea name="description" ng-model="certificate.description" placeholder="Something elegant" class="form-control" ng-pattern="/^[\w\-\s]+$/" required></textarea>
|
||||
<p ng-show="trackingForm.description.$invalid && !trackingForm.description.$pristine" class="help-block">You must give a short description about this authority will be used for, this description should only include alphanumeric characters</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Certificate Authority
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group col-sm-12">
|
||||
<input tooltip="If you are unsure which authority you need; you most likely want to use 'verisign'" type="text" ng-model="certificate.selectedAuthority" placeholder="Authority Name" typeahead-on-select="certificate.attachAuthority($item)"
|
||||
typeahead="authority.name for authority in authorityService.findAuthorityByName($viewValue)" typeahead-loading="loadingAuthorities"
|
||||
class="form-control" typeahead-wait-ms="100" typeahead-template-url="angular/authorities/authority/select.tpl.html">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="certificate.authority.name == 'verisign'">
|
||||
<div class="form-group" ng-class="{'has-error': !certificate.extensions.subAltNames.names.length&&!certificate.extensions.subAltNames.names , 'has-success': certificate.extensions.subAltNames.names||certificate.subAltValue}">
|
||||
<label class="control-label col-sm-2">Domain(s)</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group input-group-option">
|
||||
<input tooltip="If you request multiple domains a SAN certificate will be created" type="text" name="domain" ng-model="certificate.subAltValue" placeholder="Domain..." class="form-control" required/>
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-default" ng-click="certificate.attachSubAltName()">Add</button>
|
||||
</span>
|
||||
</div>
|
||||
<table ng-show="certificate.extensions.subAltNames.names" class="table">
|
||||
<tr ng-repeat="domain in certificate.extensions.subAltNames.names track by $index">
|
||||
<td>{{ domain.value }}</td>
|
||||
<td>
|
||||
<button type="button" ng-click="certificate.removeSubAltName($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="certificate.authority && certificate.authority.name != 'verisign'" class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Certificate Template
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<select class="form-control" ng-change="certificate.useTemplate()" name="certificateTemplate" ng-model="certificate.template" ng-options="template.name for template in templates"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="certificate.authority && certificate.authority.name != 'verisign'" class="form-group"
|
||||
ng-class="{'has-error': trackingForm.commonName.$invalid, 'has-success': !trackingForm.$invalid&&trackingForm.commonName.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Common Name
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="commonName" ng-model="certificate.commonName" placeholder="Common Name" class="form-control" required/>
|
||||
<p ng-show="trackingForm.commonName.$invalid && !trackingForm.commonName.$pristine" class="help-block">You must enter a common name</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Validity Range
|
||||
</label>
|
||||
<div class="col-sm-4">
|
||||
<div>
|
||||
<div class="input-group">
|
||||
<input tooltip="Starting Date (yyyy/MM/dd)" class="form-control" datepicker-popup="yyyy/MM/dd" is-open="$parent.openNotBefore.isOpen" min-date="certificate.authority.notBefore" max-date="certificate.authority.maxDate" ng-model="certificate.validityStart" />
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-default" ng-click="openNotBefore($event)"><i class="glyphicon glyphicon-calendar"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-center col-sm-2"><label><span class="glyphicon glyphicon-resize-horizontal"></span></label></span>
|
||||
<div class="col-sm-4">
|
||||
<div>
|
||||
<div class="input-group">
|
||||
<input tooltip="Ending Date (yyyy/MM/dd)" class="form-control" datepicker-popup="yyyy/MM/dd" is-open="$parent.openNotAfter.isOpen" min-date="certificate.authority.notBefore" max-date="certificate.authority.maxDate" ng-model="certificate.validityEnd" />
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-default" ng-click="openNotAfter($event)"><i class="glyphicon glyphicon-calendar"></i></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
26
lemur/static/app/angular/certificates/certificate/upload.js
vendored
Normal file
26
lemur/static/app/angular/certificates/certificate/upload.js
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
|
||||
.config(function config($routeProvider) {
|
||||
$routeProvider.when('/certificates/upload', {
|
||||
templateUrl: '/angular/certificates/certificate/upload.tpl.html',
|
||||
controller: 'CertificatesUploadController'
|
||||
});
|
||||
})
|
||||
|
||||
.controller('CertificatesUploadController', function ($scope, CertificateService, LemurRestangular, AccountService, ELBService) {
|
||||
$scope.certificate = LemurRestangular.restangularizeElement(null, {}, 'certificates');
|
||||
$scope.upload = CertificateService.upload;
|
||||
|
||||
$scope.accountService = AccountService;
|
||||
$scope.elbService = ELBService;
|
||||
|
||||
|
||||
$scope.attachELB = function (elb) {
|
||||
$scope.certificate.attachELB(elb);
|
||||
ELBService.getListeners(elb).then(function (listeners) {
|
||||
$scope.certificate.elb.listeners = listeners;
|
||||
});
|
||||
};
|
||||
});
|
@ -0,0 +1,136 @@
|
||||
<h2 class="featurette-heading">Upload a certificate <span class="text-muted"><small>encrypt all the things
|
||||
</small></span></h2>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<a href="/#/certificates/" class="btn btn-danger pull-right">Cancel</a>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form name="uploadForm" class="form-horizontal" role="form" novalidate>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': uploadForm.owner.$invalid, 'has-success': !uploadForm.owner.$invalid&&uploadForm.owner.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Owner
|
||||
</label>
|
||||
|
||||
<div class="col-sm-10">
|
||||
<input type="email" name="owner" ng-model="certificate.owner" placeholder="owner@netflix.com"
|
||||
class="form-control" required/>
|
||||
|
||||
<p ng-show="uploadForm.owner.$invalid && !uploadForm.owner.$pristine" class="help-block">Enter a valid
|
||||
email.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': uploadForm.publicCert.$invalid, 'has-success': !uploadForm.publicCert.$invalid&&uploadForm.publicCert.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Public Certificate
|
||||
</label>
|
||||
|
||||
<div class="col-sm-10">
|
||||
<textarea name="publicCert" ng-model="certificate.publicCert" placeholder="PEM encoded string..."
|
||||
class="form-control" ng-pattern="/^-----BEGIN CERTIFICATE-----/" required></textarea>
|
||||
|
||||
<p ng-show="uploadForm.publicCert.$invalid && !uploadForm.publicCert.$pristine" class="help-block">Enter
|
||||
a valid certificate.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': uploadForm.privateKey.$invalid&&uploadForm.privateKey.$dirty, 'has-success': !uploadForm.privateKey.$invalid&&uploadForm.privateKey.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Private Key
|
||||
</label>
|
||||
|
||||
<div class="col-sm-10">
|
||||
<textarea name="privateKey" ng-model="certificate.privateKey" placeholder="PEM encoded string..."
|
||||
class="form-control" ng-pattern="/^-----BEGIN RSA PRIVATE KEY-----/"></textarea>
|
||||
|
||||
<p ng-show="uploadForm.privateKey.$invalid && !uploadForm.privateKey.$pristine" class="help-block">Enter
|
||||
a valid certificate.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': uploadForm.owner.$invalid&&uploadform.intermediateCert.$dirty, 'has-success': !uploadForm.intermediateCert.$invalid&&uploadForm.intermediateCert.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Intermediate Certificate
|
||||
</label>
|
||||
|
||||
<div class="col-sm-10">
|
||||
<textarea name="intermediateCert" ng-model="certificate.intermediateCert"
|
||||
placeholder="PEM encoded string..." class="form-control"
|
||||
ng-pattern="/^-----BEGIN CERTIFICATE-----/"></textarea>
|
||||
|
||||
<p ng-show="uploadForm.intermediateCert.$invalid && !uploadForm.intemediateCert.$pristine"
|
||||
class="help-block">Enter a valid certificate.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Accounts
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group">
|
||||
<input type="text" ng-model="certificate.selectedAccount" placeholder="Account Name"
|
||||
typeahead="account.label for account in accountService.findAccountsByName($viewValue)" typeahead-loading="loadingAccounts"
|
||||
class="form-control" typeahead-on-select="certificate.attachAccount($item)" typeahead-min-wait="50"
|
||||
tooltip="Lemur can upload the certificate to any AWS account."
|
||||
tooltip-trigger="focus" tooltip-placement="top">
|
||||
<span class="input-group-btn">
|
||||
<button ng-model="certificate.accounts" class="btn btn-default" btn-checkbox btn-checkbox-true="1" btn-checkbox-false="0">
|
||||
<span class="badge">{{ certificate.accounts.length || 0 }}</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<table ng-show="certificate.accounts" class="table">
|
||||
<tr ng-repeat="account in certificate.accounts track by $index">
|
||||
<td><a class="btn btn-sm btn-info" href="#/accounts/{{ account.id }}/certificates">{{ account.label }}</a></td>
|
||||
<td><span class="text-muted">{{ account.comments }}</span></td>
|
||||
<td>
|
||||
<button type="button" ng-click="certificate.removeAccount($index)" class="btn btn-danger btn-sm pull-right">
|
||||
Remove
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!--<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
ELBs
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group">
|
||||
<input type="text" ng-model="certificate.selectedELB" placeholder="ELB Name"
|
||||
typeahead="elb.name for elb in elbService.findELBByName($viewValue)" typeahead-loading="loadingAccounts"
|
||||
class="form-control" typeahead-on-select="attachELB($item)" typeahead-min-wait="50"
|
||||
tooltip="Lemur can upload a certificate to one or more ELBs searching will be limited to the accounts selected above"
|
||||
tooltip-trigger="focus" tooltip-placement="top">
|
||||
<span class="input-group-btn">
|
||||
<button ng-model="certificate.elbs" class="btn btn-default" btn-checkbox btn-checkbox-true="1" btn-checkbox-false="0">
|
||||
<span class="badge">{{ certificate.elbs.length || 0 }}</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<table ng-show="certificate.elbs" class="table">
|
||||
<tr ng-repeat="elb in certificate.elbs track by $index">
|
||||
<td><a class="btn btn-sm btn-info" href="#/accounts/{{ elb.id }}/elbs">{{ elb.name }}</a></td>
|
||||
<td>{{ elb.region }}</td>
|
||||
<td>{{ elb.scheme }}</td>
|
||||
<td>{{ elb.vpcId }}</td>
|
||||
<td><a class="btn btn-info" ng-model="elb.showListeners" btn-checkbox btn-checkbox-true="1" btn-checkbox-false="0">View Listeners</a></td>
|
||||
<td>
|
||||
<button type="button" ng-click="certificate.removeAccount($index)" class="btn btn-danger btn-sm pull-right">
|
||||
Remove
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>-->
|
||||
</form>
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<button type="submit" ng-click="upload(certificate)" ng-disabled="uploadForm.$invalid" class="btn btn-success pull-right">Upload</button>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
238
lemur/static/app/angular/certificates/services.js
vendored
Normal file
238
lemur/static/app/angular/certificates/services.js
vendored
Normal file
@ -0,0 +1,238 @@
|
||||
/**
|
||||
* Created by kglisson on 1/19/15.
|
||||
*/
|
||||
angular.module('lemur')
|
||||
.service('CertificateApi', function (LemurRestangular, DomainService) {
|
||||
LemurRestangular.extendModel('certificates', function (obj) {
|
||||
return angular.extend(obj, {
|
||||
attachAuthority: function (authority) {
|
||||
this.authority = authority;
|
||||
this.authority.maxDate = moment(this.authority.notAfter).subtract(1, 'days').format('YYYY/MM/DD');
|
||||
},
|
||||
attachSubAltName: function () {
|
||||
if (this.extensions === undefined) {
|
||||
this.extensions = {};
|
||||
}
|
||||
|
||||
if (this.extensions.subAltNames === undefined) {
|
||||
this.extensions.subAltNames = {'names': []};
|
||||
}
|
||||
|
||||
if (!angular.isString(this.subAltType)) {
|
||||
this.subAltType = 'CNAME';
|
||||
}
|
||||
|
||||
if (angular.isString(this.subAltValue) && angular.isString(this.subAltType)) {
|
||||
this.extensions.subAltNames.names.push({'nameType': this.subAltType, 'value': this.subAltValue});
|
||||
this.findDuplicates();
|
||||
}
|
||||
|
||||
this.subAltType = null;
|
||||
this.subAltValue = null;
|
||||
},
|
||||
removeSubAltName: function (index) {
|
||||
this.extensions.subAltNames.names.splice(index, 1);
|
||||
this.findDuplicates();
|
||||
},
|
||||
attachCustom: function () {
|
||||
if (this.extensions === undefined || this.extensions.custom === undefined) {
|
||||
this.extensions = {'custom': []};
|
||||
}
|
||||
|
||||
if (angular.isString(this.customOid) && angular.isString(this.customEncoding) && angular.isString(this.customValue)) {
|
||||
this.extensions.custom.push(
|
||||
{
|
||||
'oid': this.customOid,
|
||||
'isCritical': this.customIsCritical,
|
||||
'encoding': this.customEncoding,
|
||||
'value': this.customValue
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
this.customOid = null;
|
||||
this.customIsCritical = null;
|
||||
this.customEncoding = null;
|
||||
this.customValue = null;
|
||||
},
|
||||
removeCustom: function (index) {
|
||||
this.extensions.custom.splice(index, 1);
|
||||
},
|
||||
attachAccount: function (account) {
|
||||
this.selectedAccount = null;
|
||||
if (this.accounts === undefined) {
|
||||
this.accounts = [];
|
||||
}
|
||||
this.accounts.push(account);
|
||||
},
|
||||
removeAccount: function (index) {
|
||||
this.accounts.splice(index, 1);
|
||||
},
|
||||
attachELB: function (elb) {
|
||||
this.selectedELB = null;
|
||||
if (this.elbs === undefined) {
|
||||
this.elbs = [];
|
||||
}
|
||||
this.elbs.push(elb);
|
||||
},
|
||||
removeELB: function (index) {
|
||||
this.elbs.splice(index, 1);
|
||||
},
|
||||
findDuplicates: function () {
|
||||
DomainService.findDomainByName(this.extensions.subAltNames[0]).then(function (domains) { //We should do a better job of searchin multiple domains
|
||||
this.duplicates = domains.total;
|
||||
});
|
||||
},
|
||||
useTemplate: function () {
|
||||
this.extensions = this.template.extensions;
|
||||
}
|
||||
});
|
||||
});
|
||||
return LemurRestangular.all('certificates');
|
||||
})
|
||||
.service('CertificateService', function ($location, CertificateApi, toaster) {
|
||||
var CertificateService = this;
|
||||
CertificateService.findCertificatesByName = function (filterValue) {
|
||||
return CertificateApi.getList({'filter[name]': filterValue})
|
||||
.then(function (certificates) {
|
||||
return certificates;
|
||||
});
|
||||
};
|
||||
|
||||
CertificateService.getARNs = function (certificate) {
|
||||
certificate.arns = [];
|
||||
_.each(certificate.accounts, function (account) {
|
||||
certificate.arns.push('arn:aws:iam::' + account.accountNumber + ':server-certificate/' + certificate.name);
|
||||
});
|
||||
};
|
||||
|
||||
CertificateService.create = function (certificate) {
|
||||
certificate.attachSubAltName();
|
||||
return CertificateApi.post(certificate).then(
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: certificate.name,
|
||||
body: 'Successfully created!'
|
||||
});
|
||||
$location.path('/certificates');
|
||||
},
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: certificate.name,
|
||||
body: 'Was not created! ' + response.data.message
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
CertificateService.update = function (certificate) {
|
||||
certificate.put().then(function () {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: certificate.name,
|
||||
body: 'Successfully updated!'
|
||||
});
|
||||
$location.path('certificates');
|
||||
});
|
||||
};
|
||||
|
||||
CertificateService.upload = function (certificate) {
|
||||
CertificateApi.customPOST(certificate, "upload").then(
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: certificate.name,
|
||||
body: 'Successfully uploaded!'
|
||||
});
|
||||
$location.path('/certificates');
|
||||
},
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: certificate.name,
|
||||
body: 'Failed to upload ' + response.data.message
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
CertificateService.loadPrivateKey = function (certificate) {
|
||||
certificate.customGET('key').then(
|
||||
function (response) {
|
||||
if (response.key === null) {
|
||||
toaster.pop({
|
||||
type: 'warning',
|
||||
title: certificate.name,
|
||||
body: 'No private key found!'
|
||||
});
|
||||
} else {
|
||||
certificate.privateKey = response.key;
|
||||
}
|
||||
},
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: certificate.name,
|
||||
body: 'You do not have permission to view this key!'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
CertificateService.getAuthority = function (certificate) {
|
||||
certificate.customGET('authority').then(function (authority) {
|
||||
certificate.authority = authority;
|
||||
});
|
||||
};
|
||||
|
||||
CertificateService.getCreator = function (certificate) {
|
||||
certificate.customGET('creator').then(function (creator) {
|
||||
certificate.creator = creator;
|
||||
});
|
||||
};
|
||||
|
||||
CertificateService.getAccounts = function (certificate) {
|
||||
certificate.getList('accounts').then(function (accounts) {
|
||||
certificate.accounts = accounts;
|
||||
CertificateService.getARNs(certificate);
|
||||
});
|
||||
};
|
||||
|
||||
CertificateService.getListeners = function (certificate) {
|
||||
certificate.getList('listeners').then(function (listeners) {
|
||||
certificate.listeners = listeners;
|
||||
});
|
||||
};
|
||||
|
||||
CertificateService.getELBs = function (certificate) {
|
||||
certificate.getList('listeners').then(function (elbs) {
|
||||
certificate.elbs = elbs;
|
||||
});
|
||||
};
|
||||
|
||||
CertificateService.getDomains = function (certificate) {
|
||||
certificate.getList('domains').then(function (domains) {
|
||||
certificate.domains = domains;
|
||||
});
|
||||
};
|
||||
|
||||
CertificateService.updateActive = function (certificate) {
|
||||
certificate.put().then(
|
||||
function () {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: certificate.name,
|
||||
body: 'Successfully updated!'
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: certificate.name,
|
||||
body: 'Was not updated! ' + response.data.message
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return CertificateService;
|
||||
});
|
63
lemur/static/app/angular/certificates/view/view.js
vendored
Normal file
63
lemur/static/app/angular/certificates/view/view.js
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
|
||||
.config(function config($routeProvider) {
|
||||
$routeProvider.when('/certificates', {
|
||||
templateUrl: '/angular/certificates/view/view.tpl.html',
|
||||
controller: 'CertificatesViewController'
|
||||
});
|
||||
})
|
||||
|
||||
.controller('CertificatesViewController', function ($q, $scope, CertificateApi, CertificateService, MomentService, ngTableParams) {
|
||||
$scope.filter = {};
|
||||
$scope.certificateTable = new ngTableParams({
|
||||
page: 1, // show first page
|
||||
count: 10, // count per page
|
||||
sorting: {
|
||||
id: 'desc' // initial sorting
|
||||
},
|
||||
filter: $scope.filter
|
||||
}, {
|
||||
total: 0, // length of data
|
||||
getData: function ($defer, params) {
|
||||
CertificateApi.getList(params.url())
|
||||
.then(function (data) {
|
||||
// TODO we should attempt to resolve all of these in parallel
|
||||
_.each(data, function (certificate) {
|
||||
CertificateService.getDomains(certificate);
|
||||
CertificateService.getAccounts(certificate);
|
||||
CertificateService.getListeners(certificate);
|
||||
CertificateService.getAuthority(certificate);
|
||||
CertificateService.getCreator(certificate);
|
||||
});
|
||||
params.total(data.total);
|
||||
$defer.resolve(data);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.certificateService = CertificateService;
|
||||
$scope.momentService = MomentService;
|
||||
|
||||
$scope.remove = function (certificate) {
|
||||
certificate.remove().then(function () {
|
||||
$scope.certificateTable.reload();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.getCertificateStatus = function () {
|
||||
var def = $q.defer();
|
||||
def.resolve([{'title': 'Active', 'id': true}, {'title': 'Inactive', 'id': false}])
|
||||
return def;
|
||||
};
|
||||
|
||||
$scope.show = {title: 'Current User', value: 'currentUser'};
|
||||
|
||||
$scope.fields = [{title: 'Current User', value: 'currentUser'}, {title: 'All', value: 'all'}];
|
||||
|
||||
|
||||
$scope.toggleFilter = function (params) {
|
||||
params.settings().$scope.show_filter = !params.settings().$scope.show_filter;
|
||||
};
|
||||
});
|
142
lemur/static/app/angular/certificates/view/view.tpl.html
Normal file
142
lemur/static/app/angular/certificates/view/view.tpl.html
Normal file
@ -0,0 +1,142 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2 class="featurette-heading">Certificates
|
||||
<span class="text-muted"><small>Cipher text says what?</small></span></h2>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<div class="btn-group pull-right">
|
||||
<a data-placement="left" data-title="Create Certificate" bs-tooltip href="#/certificates/create"
|
||||
class="btn btn-primary">
|
||||
Create
|
||||
</a>
|
||||
<a data-placement="left" data-title="Upload Certificate" bs-tooltip href="#/certificates/upload"
|
||||
class="btn btn-info">
|
||||
Upload
|
||||
</a>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button ng-click="toggleFilter(certificateTable)" class="btn btn-default">Filter</button>
|
||||
</div>
|
||||
<!--<select class="form-control" ng-model="show" ng-options="item.value as item.title for item in fields"></select>-->
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table ng-table="certificateTable" class="table" show-filter="false" template-pagination="angular/pager.html">
|
||||
<tbody>
|
||||
<tr ng-class="{'even-row': $even }" ng-repeat-start="certificate in $data track by $index">
|
||||
<td data-title="'Name'" sortable="'name'" filter="{ 'name': 'text' }">
|
||||
<ul class="list-unstyled">
|
||||
<li>{{ certificate.name }}</li>
|
||||
<li><span class="text-muted">{{ certificate.owner }}</span></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td data-title="'Accounts'" filter="{ 'account': 'select' }" filter-date="getAccountDropDown()">
|
||||
<div class="btn-group">
|
||||
<a href="#/accounts/{{ account.id }}/edit" class="btn btn-sm btn-default" ng-repeat="account in certificate.accounts">{{ account.label }}</a>
|
||||
</div>
|
||||
</td>
|
||||
<td data-title="'Active'" filter="{ 'active': 'select' }" filter-data="getCertificateStatus()">
|
||||
<form>
|
||||
<switch ng-change="certificateService.updateActive(certificate)" id="status" name="status" ng-model="certificate.active" class="green small"></switch>
|
||||
</form>
|
||||
</td>
|
||||
<td data-title="'Issuer'" sortable="'issuer'" filter="{ 'issuer': 'text' }">
|
||||
{{ certificate.authority.name || certificate.issuer }}
|
||||
</td>
|
||||
<td data-title="'Common Name'" filter="{ 'cn': 'text'}">
|
||||
{{ certificate.cn }}
|
||||
</td>
|
||||
<td data-title="''">
|
||||
<div class="btn-group-vertical pull-right">
|
||||
<button ng-model="certificate.toggle" class="btn btn-sm btn-info" btn-checkbox btn-checkbox-true="1" butn-checkbox-false="0">View</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="warning" ng-show="certificate.toggle" ng-repeat-end>
|
||||
<td colspan="5">
|
||||
<div class="col-md-6">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<strong>Creator</strong>
|
||||
<span class="pull-right">
|
||||
{{ certificate.creator.email }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<strong>Not Before</strong>
|
||||
<span class="pull-right" tooltip="{{ certificate.notBefore }}">
|
||||
{{ momentService.createMoment(certificate.notBefore) }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<strong>Not After</strong>
|
||||
<span class="pull-right" tooltip="{{ certificate.notAfter }}">
|
||||
{{ momentService.createMoment(certificate.notAfter) }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<strong>San</strong>
|
||||
<span class="pull-right">
|
||||
<i class="glyphicon glyphicon-ok" ng-show="certificate.san == 'true'"></i>
|
||||
<i class="glyphicon glyphicon-remove" ng-show="certificate.san == 'false'"></i>
|
||||
</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<strong>Bits</strong>
|
||||
<span class="pull-right">{{ certificate.bits }}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<strong>Serial</strong>
|
||||
<span class="pull-right">{{ certificate.serial }}</span>
|
||||
</li>
|
||||
<li tooltip="Lemur will attempt to check a certificates validity, this is used to track whether a certificate as been revoked" class="list-group-item">
|
||||
<strong>Validity</strong>
|
||||
<span class="pull-right">
|
||||
<span ng-show="!certificate.status" class="label label-warning">Unknown</span>
|
||||
<span ng-show="certificate.status == 'revoked'" class="label label-danger">Revoked</span>
|
||||
<span ng-show="certificate.status == 'valid'" class="label label-success">Valid</span>
|
||||
</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<strong>Description</strong>
|
||||
<span class="pull-right">{{ certificate.description }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<h4>Domains</h4>
|
||||
<div class="list-group">
|
||||
<a href="#/domains/{{ domain.id }}" class="list-group-item" ng-repeat="domain in certificate.domains">{{ domain.name }}</a>
|
||||
</div>
|
||||
<h4 ng-show="certificate.accounts.total">ARNs</h4>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item" ng-repeat="arn in certificate.arns">{{ arn }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<tabset justified="true" class="col-md-6">
|
||||
<tab heading="Chain">
|
||||
<p>
|
||||
<pre style="width: 550px">{{ certificate.chain }}</pre>
|
||||
</p>
|
||||
</tab>
|
||||
<tab heading="Public Certificate">
|
||||
<p>
|
||||
<pre style="width: 550px">{{ certificate.body }}</pre>
|
||||
</p>
|
||||
</tab>
|
||||
<tab ng-click="certificateService.loadPrivateKey(certificate)">
|
||||
<tab-heading>
|
||||
Private Key
|
||||
</tab-heading>
|
||||
<p>
|
||||
<pre style="width: 550px">{{ certificate.privateKey }}</pre>
|
||||
</p>
|
||||
</tab>
|
||||
</tabset>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
9
lemur/static/app/angular/components/filters.js
vendored
Normal file
9
lemur/static/app/angular/components/filters.js
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
angular.module('lemur').
|
||||
filter('titleCase', function () {
|
||||
return function (str) {
|
||||
return (str === undefined || str === null) ? '' : str.replace(/_|-/, ' ').replace(/\w\S*/g, function (txt) {
|
||||
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
||||
});
|
||||
};
|
||||
});
|
||||
|
93
lemur/static/app/angular/dashboard/dashboard.js
vendored
Normal file
93
lemur/static/app/angular/dashboard/dashboard.js
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
.config(function config($routeProvider) {
|
||||
$routeProvider.when('/dashboard', {
|
||||
templateUrl: '/angular/dashboard/dashboard.tpl.html',
|
||||
controller: 'DashboardController'
|
||||
});
|
||||
})
|
||||
.controller('DashboardController', function ($scope, $rootScope, $filter, $location, LemurRestangular, ngTableParams) {
|
||||
|
||||
var baseStats = LemurRestangular.all('stats');
|
||||
var baseAccounts = LemurRestangular.all('accounts');
|
||||
|
||||
baseAccounts.getList()
|
||||
.then(function (data) {
|
||||
$scope.accounts = data;
|
||||
});
|
||||
|
||||
$scope.colours = [
|
||||
{
|
||||
fillColor: 'rgba(41, 171, 224, 0.2)',
|
||||
strokeColor: 'rgba(41, 171, 224, 1)',
|
||||
pointColor: 'rgba(41, 171, 224, 0.2)',
|
||||
pointStrongColor: '#fff',
|
||||
pointHighlightFill: '#fff',
|
||||
pointHighlightStrokeColor: 'rgba(41, 171, 224, 0.8)'
|
||||
}, {
|
||||
fillColor: 'rgba(147, 197, 75, 0.2)',
|
||||
strokeColor: 'rgba(147, 197, 75, 1)',
|
||||
pointColor: 'rgba(147, 197, 75, 0.2)',
|
||||
pointStrongColor: '#fff',
|
||||
pointHighlightFill: '#fff',
|
||||
pointHighlightStrokeColor: 'rgba(147, 197, 75, 0.8)'
|
||||
}, {
|
||||
fillColor: 'rgba(217, 83, 79, 0.2)',
|
||||
strokeColor: 'rgba(217, 83, 79, 1)',
|
||||
pointColor: 'rgba(217, 83, 79, 0.2)',
|
||||
pointStrongColor: '#fff',
|
||||
pointHighlightFill: '#fff',
|
||||
pointHighlightStrokeColor: 'rgba(217, 83, 79, 0.8)'
|
||||
}, {
|
||||
fillColor: 'rgba(244, 124, 60, 0.2)',
|
||||
strokeColor: 'rgba(244, 124, 60, 1)',
|
||||
pointColor: 'rgba(244, 124, 60, 0.2)',
|
||||
pointStrongColor: '#fff',
|
||||
pointHighlightFill: '#fff',
|
||||
pointHighlightStrokeColor: 'rgba(244, 124, 60, 0.8)'
|
||||
}, {
|
||||
fillColor: 'rgba(243, 156, 18, 0.2)',
|
||||
strokeColor: 'rgba(243, 156, 18, 1)',
|
||||
pointColor: 'rgba(243, 156, 18, 0.2)',
|
||||
pointStrongColor: '#fff',
|
||||
pointHighlightFill: '#fff',
|
||||
pointHighlightStrokeColor: 'rgba(243, 156, 18, 0.8)'
|
||||
}, {
|
||||
fillColor: 'rgba(231, 76, 60, 0.2)',
|
||||
strokeColor: 'rgba(231, 76, 60, 1)',
|
||||
pointColor: 'rgba(231, 76, 60, 0.2)',
|
||||
pointStrongColor: '#fff',
|
||||
pointHighlightFill: '#fff',
|
||||
pointHighlightStrokeColor: 'rgba(231, 76, 60, 0.8)'
|
||||
}, {
|
||||
fillColor: 'rgba(255, 102, 102, 0.2)',
|
||||
strokeColor: 'rgba(255, 102, 102, 1)',
|
||||
pointColor: 'rgba(255, 102, 102, 0.2)',
|
||||
pointStrongColor: '#fff',
|
||||
pointHighlightFill: '#fff',
|
||||
pointHighlightStrokeColor: 'rgba(255, 102, 102, 0.8)'
|
||||
}, {
|
||||
fillColor: 'rgba(255, 230, 230, 0.2)',
|
||||
strokeColor: 'rgba(255, 230, 230, 1)',
|
||||
pointColor: 'rgba(255, 230, 230, 0.2)',
|
||||
pointStrongColor: '#fff',
|
||||
pointHighlightFill: '#fff',
|
||||
pointHighlightStrokeColor: 'rgba(255, 230, 230, 0.8)'
|
||||
}];
|
||||
|
||||
LemurRestangular.all('certificates').customGET('stats', {metric: 'issuer'})
|
||||
.then(function (data) {
|
||||
$scope.issuers = data['items'];
|
||||
});
|
||||
|
||||
LemurRestangular.all('certificates').customGET('stats', {metric: 'bits'})
|
||||
.then(function (data) {
|
||||
$scope.bits = data['items'];
|
||||
});
|
||||
|
||||
LemurRestangular.all('certificates').customGET('stats', {metric: 'not_after'})
|
||||
.then(function (data) {
|
||||
$scope.expiring = {labels: data['items']['labels'], values: [data['items']['values']]};
|
||||
});
|
||||
});
|
41
lemur/static/app/angular/dashboard/dashboard.tpl.html
Normal file
41
lemur/static/app/angular/dashboard/dashboard.tpl.html
Normal file
@ -0,0 +1,41 @@
|
||||
<div class="container-fluid">
|
||||
<!-- Three columns of text below the carousel -->
|
||||
<h2 class="featurette-heading">Status
|
||||
<span class="text-muted"><small>Information is power</small></span>
|
||||
</h2>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Expiring Certificates</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<canvas id="expiringBar" class="chart chart-bar" data="expiring.values" labels="expiring.labels" colours="colours"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row"></div>
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Issuers</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<canvas id="issuersPie" class="chart chart-pie" data="issuers.values" labels="issuers.labels" colours="colours" legend="true"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Bit Strength</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<canvas id="bitsPie" class="chart chart-pie" data="bits.values" labels="bits.labels" colours="colours" legend="true"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
</div>
|
13
lemur/static/app/angular/domains/services.js
vendored
Normal file
13
lemur/static/app/angular/domains/services.js
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
angular.module('lemur')
|
||||
.service('DomainApi', function (LemurRestangular) {
|
||||
return LemurRestangular.all('domains');
|
||||
})
|
||||
.service('DomainService', function ($location, DomainApi) {
|
||||
var DomainService = this;
|
||||
DomainService.findDomainByName = function (filterValue) {
|
||||
return DomainApi.getList({'filter[name]': filterValue})
|
||||
.then(function (domains) {
|
||||
return domains;
|
||||
});
|
||||
};
|
||||
});
|
35
lemur/static/app/angular/domains/view/view.js
vendored
Normal file
35
lemur/static/app/angular/domains/view/view.js
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
|
||||
.config(function config($routeProvider) {
|
||||
$routeProvider.when('/domains', {
|
||||
templateUrl: '/angular/domains/view/view.tpl.html',
|
||||
controller: 'DomainsViewController'
|
||||
});
|
||||
})
|
||||
|
||||
.controller('DomainsViewController', function ($scope, DomainApi, ngTableParams) {
|
||||
$scope.filter = {};
|
||||
$scope.domainsTable = new ngTableParams({
|
||||
page: 1, // show first page
|
||||
count: 10, // count per page
|
||||
sorting: {
|
||||
id: 'desc' // initial sorting
|
||||
},
|
||||
filter: $scope.filter
|
||||
}, {
|
||||
total: 0, // length of data
|
||||
getData: function ($defer, params) {
|
||||
DomainApi.getList().then(function (data) {
|
||||
params.total(data.total);
|
||||
$defer.resolve(data);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.toggleFilter = function (params) {
|
||||
params.settings().$scope.show_filter = !params.settings().$scope.show_filter;
|
||||
};
|
||||
|
||||
});
|
25
lemur/static/app/angular/domains/view/view.tpl.html
Normal file
25
lemur/static/app/angular/domains/view/view.tpl.html
Normal file
@ -0,0 +1,25 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2 class="featurette-heading">Domains
|
||||
<span class="text-muted"><small>Zone transfers as scary</small></span></h2>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<div class="btn-group">
|
||||
<button ng-click="toggleFilter(domainsTable)" class="btn btn-default">Filter</button>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table ng-table="domainsTable" class="table table-striped" show-filter="false" template-pagination="angular/pager.html">
|
||||
<tbody>
|
||||
<tr ng-repeat="domain in $data track by $index">
|
||||
<td data-title="'Name'" sortable="'name'" filter="{ 'name': 'text' }">
|
||||
{{ domain.name }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
3
lemur/static/app/angular/elbs/elb/elb.js
vendored
Normal file
3
lemur/static/app/angular/elbs/elb/elb.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/**
|
||||
* Created by kglisson on 1/19/15.
|
||||
*/
|
10
lemur/static/app/angular/elbs/elb/elb.tpl.html
Normal file
10
lemur/static/app/angular/elbs/elb/elb.tpl.html
Normal file
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head lang="en">
|
||||
<meta charset="UTF-8">
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
57
lemur/static/app/angular/elbs/services.js
vendored
Normal file
57
lemur/static/app/angular/elbs/services.js
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
angular.module('lemur')
|
||||
.service('ELBApi', function (LemurRestangular, ListenerService) {
|
||||
LemurRestangular.extendModel('elbs', function (obj) {
|
||||
return angular.extend(obj, {
|
||||
attachListener: function (listener) {
|
||||
if (this.listeners === undefined) {
|
||||
this.listeners = [];
|
||||
}
|
||||
this.listeners.push(listener);
|
||||
},
|
||||
removeListener: function (index) {
|
||||
this.listeners.splice(index, 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
return LemurRestangular.all('elbs');
|
||||
})
|
||||
.service('ELBService', function ($location, ELBApi, toaster) {
|
||||
var ELBService = this;
|
||||
ELBService.findELBByName = function (filterValue) {
|
||||
return ELBApi.getList({'filter[name]': filterValue})
|
||||
.then(function (elbs) {
|
||||
return elbs;
|
||||
});
|
||||
};
|
||||
|
||||
ELBService.getListeners = function (elb) {
|
||||
elb.getList('listeners').then(function (listeners) {
|
||||
elb.listeners = listeners;
|
||||
});
|
||||
return elb;
|
||||
};
|
||||
|
||||
ELBService.create = function (elb) {
|
||||
ELBApi.post(elb).then(function () {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: 'ELB ' + elb.name,
|
||||
body: 'Has been successfully created!'
|
||||
});
|
||||
$location.path('elbs');
|
||||
});
|
||||
};
|
||||
|
||||
ELBService.update = function (elb) {
|
||||
elb.put().then(function () {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: 'ELB ' + elb.name,
|
||||
body: 'Has been successfully updated!'
|
||||
});
|
||||
$location.path('elbs');
|
||||
});
|
||||
};
|
||||
|
||||
return ELBService;
|
||||
});
|
34
lemur/static/app/angular/elbs/view/view.js
vendored
Normal file
34
lemur/static/app/angular/elbs/view/view.js
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
.config(function config($routeProvider) {
|
||||
$routeProvider.when('/elbs', {
|
||||
templateUrl: '/angular/elbs/view/view.tpl.html',
|
||||
controller: 'ELBViewController'
|
||||
});
|
||||
})
|
||||
|
||||
.controller('ELBViewController', function ($scope, ELBApi, ELBService, ngTableParams) {
|
||||
$scope.filter = {};
|
||||
$scope.elbsTable = new ngTableParams({
|
||||
page: 1, // show first page
|
||||
count: 10, // count per page
|
||||
sorting: {
|
||||
id: 'desc' // initial sorting
|
||||
},
|
||||
filter: $scope.filter
|
||||
}, {
|
||||
total: 0, // length of data
|
||||
getData: function ($defer, params) {
|
||||
ELBApi.getList(params.url())
|
||||
.then(function (data) {
|
||||
params.total(data.total);
|
||||
$defer.resolve(data);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.toggleFilter = function (params) {
|
||||
params.settings().$scope.show_filter = !params.settings().$scope.show_filter;
|
||||
};
|
||||
});
|
128
lemur/static/app/angular/elbs/view/view.tpl.html
Normal file
128
lemur/static/app/angular/elbs/view/view.tpl.html
Normal file
@ -0,0 +1,128 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2 class="featurette-heading">ELBs
|
||||
<span class="text-muted"><small>Bring Balance to the Force</small></span></h2>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<div class="btn-group pull-right">
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button ng-click="toggleFilter(elbsTable)" class="btn btn-default">Filter</button>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table ng-table="elbsTable" show-filter="false" class="table" template-pagination="angular/pager.html" >
|
||||
<tbody>
|
||||
<tr ng-class="{'even-row': $even }" ng-repeat-start="elb in $data track by $index">
|
||||
<td data-title="'Name'" sortable="'name'" filter="{ 'name': 'text' }">
|
||||
<div class="text-center">{{ elb.name }}</div>
|
||||
</td>
|
||||
<td data-title="'Account'" sortable="'account_id'">
|
||||
<div class="text-center"><span class="label label-primary">{{ elb.account.label }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td data-title="'Region'" sortable="'region'" filter="{ 'region': 'text' }">
|
||||
<div class="text-center">{{ elb.region }}</div>
|
||||
</td>
|
||||
<td data-title="'VPC'">
|
||||
<div class="text-center">
|
||||
<i class="fa fa-check" ng-show="elb.vpcId" data-placement="top"
|
||||
data-title="{{ elb.vpcId }}" bs-tooltip></i>
|
||||
<i class="fa fa-times" ng-show="!elb.vpcId"></i>
|
||||
</div>
|
||||
</td>
|
||||
<td data-title="'Internet Accessible'" sortable="'scheme'">
|
||||
<div class="text-center">
|
||||
<i class="fa fa-check" ng-show="elb.scheme == 'internet-facing'"></i>
|
||||
<i class="fa fa-times" ng-show="elb.scheme == 'internal'"></i>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="text-center">
|
||||
<i ng-show="!button.toggle" class="fa fa-chevron-down" ng-model="button.toggle"
|
||||
bs-checkbox></i>
|
||||
<i ng-show="button.toggle" class="fa fa-chevron-up" ng-model="button.toggle"
|
||||
bs-checkbox></i>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="warning" ng-show="button.toggle" ng-repeat-end>
|
||||
<td colspan="6">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>
|
||||
<!--<button data-placement="right" data-title="Add Listener" class="btn btn-sm btn-default pull-left" ng-click="addListener()" bs-tooltip><i class="fa fa-plus"></i></button>--></th>
|
||||
<th>Certificate Name</th>
|
||||
<th>Instance Port</th>
|
||||
<th>Instance Protocol</th>
|
||||
<th>Load Balancer Port</th>
|
||||
<th>Load Balancer Protocol</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr class="warning" ng-repeat="listener in elb.listeners">
|
||||
<td>
|
||||
<div>
|
||||
<button data-title="Remove listener" data-placement="right"
|
||||
class="btn btn-sm btn-danger"
|
||||
ng-click="removeListener(elb, listener, $index)" bs-tooltip><i
|
||||
class="fa fa-remove"></i></button>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="text-center">
|
||||
<input type="text" class="form-control input-sm"
|
||||
ng-model="listener.certificate.name"
|
||||
ng-options="cert.name for cert in getCertificate($viewValue, elb)"
|
||||
placeholder="Certificate name..." bs-typeahead>
|
||||
|
||||
<div ng-show="showCert">
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="text-center">
|
||||
<input type="text" ng-model="listener.instancePort"
|
||||
placeholder="Port number..." class="form-control input-sm"/>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="text-center">
|
||||
<button type="button" class="btn btn-default btn-sm"
|
||||
ng-model="listener.instanceProtocol"
|
||||
ng-options="value for value in protocols" bs-select>
|
||||
Action <span class="caret"></span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="text-center">
|
||||
<input type="text" ng-model="listener.loadBalancerPort"
|
||||
placeholder="Port number..." class="form-control input-sm"/>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="text-center">
|
||||
<button type="button" class="btn btn-default btn-sm"
|
||||
ng-model="listener.loadBalancerProtocol"
|
||||
ng-options="value for value in protocols" bs-select>
|
||||
Action <span class="caret"></span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<button data-placement="right" data-title="Save" class="btn btn-sm btn-primary"
|
||||
ng-click="saveListener(listener)" bs-tooltip><i
|
||||
class="fa fa-floppy-o"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
35
lemur/static/app/angular/listeners/services.js
vendored
Normal file
35
lemur/static/app/angular/listeners/services.js
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
angular.module('lemur')
|
||||
.service('ListenerApi', function (LemurRestangular) {
|
||||
return LemurRestangular.all('listeners');
|
||||
})
|
||||
.service('ListenerService', function ($location, ListenerApi) {
|
||||
var ListenerService = this;
|
||||
ListenerService.findListenerByName = function (filterValue) {
|
||||
return ListenerApi.getList({'filter[name]': filterValue})
|
||||
.then(function (roles) {
|
||||
return roles;
|
||||
});
|
||||
};
|
||||
|
||||
ListenerService.create = function (role) {
|
||||
ListenerApi.post(role).then(function () {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: 'Listener ' + role.name,
|
||||
body: 'Has been successfully created!'
|
||||
});
|
||||
$location.path('roles/view');
|
||||
});
|
||||
};
|
||||
|
||||
ListenerService.update = function (role) {
|
||||
role.put().then(function () {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: 'Listener ' + role.name,
|
||||
body: 'Has been successfully updated!'
|
||||
});
|
||||
$location.path('roles/view');
|
||||
});
|
||||
};
|
||||
});
|
6
lemur/static/app/angular/loadingModal.html
Normal file
6
lemur/static/app/angular/loadingModal.html
Normal file
@ -0,0 +1,6 @@
|
||||
<div tabindex="-1" role="dialog" class="modal fade" ng-class="{in: animate}" ng-style="{'z-index': 1050 + index*10, display: 'block'}">
|
||||
<div class="modal-dialog-center">
|
||||
<div modal-transclude>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
19
lemur/static/app/angular/pager.html
Normal file
19
lemur/static/app/angular/pager.html
Normal file
@ -0,0 +1,19 @@
|
||||
<div class="panel-footer">
|
||||
<div class="ng-cloak ng-table-pager" ng-if="params.data.length">
|
||||
<div ng-if="params.settings().counts.length" class="ng-table-counts btn-group">
|
||||
<button ng-repeat="count in params.settings().counts" type="button" ng-class="{'active':params.count()==count}" ng-click="params.count(count)" class="btn btn-default">
|
||||
<span ng-bind="count"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="btn-group pull-right">
|
||||
<span ng-repeat="page in pages" ng-switch="page.type" ng-class="{'disabled': !page.active && !page.current, 'active': page.current}">
|
||||
<button type="button" class="btn btn-primary" ng-switch-when="prev" ng-click="params.page(page.number)" href="">Previous</button>
|
||||
<button type="button" class="btn btn-primary" ng-switch-when="first" ng-click="params.page(page.number)" href=""><span ng-bind="page.number"></span></button>
|
||||
<button type="button" class="btn btn-primary" ng-switch-when="page" ng-click="params.page(page.number)" href=""><span ng-bind="page.number"></span></button>
|
||||
<button type="button" class="btn btn-primary" ng-switch-when="more" ng-click="params.page(page.number)" href="">…</button>
|
||||
<button type="button" class="btn btn-primary" ng-switch-when="last" ng-click="params.page(page.number)" href=""><span ng-bind="page.number"></span></button>
|
||||
<button type="button" class="btn btn-primary" ng-switch-when="next" ng-click="params.page(page.number)" href="">Next</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
30
lemur/static/app/angular/roles/role/role.js
vendored
Normal file
30
lemur/static/app/angular/roles/role/role.js
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
|
||||
.config(function config($routeProvider) {
|
||||
$routeProvider.when('/roles/create', {
|
||||
templateUrl: '/angular/roles/role/role.tpl.html',
|
||||
controller: 'RoleCreateController'
|
||||
});
|
||||
$routeProvider.when('/roles/:id/edit', {
|
||||
templateUrl: '/angular/roles/role/role.tpl.html',
|
||||
controller: 'RoleEditController'
|
||||
});
|
||||
})
|
||||
.controller('RoleEditController', function ($scope, $routeParams, RoleApi, RoleService, UserService) {
|
||||
RoleApi.get($routeParams.id).then(function (role) {
|
||||
$scope.role = role;
|
||||
RoleService.getUsers(role);
|
||||
});
|
||||
|
||||
$scope.save = RoleService.update;
|
||||
$scope.userService = UserService;
|
||||
$scope.roleService = RoleService;
|
||||
})
|
||||
|
||||
.controller('RoleCreateController', function ($scope, RoleApi, RoleService, UserService, LemurRestangular ) {
|
||||
$scope.role = LemurRestangular.restangularizeElement(null, {}, 'roles');
|
||||
$scope.userService = UserService;
|
||||
$scope.save = RoleService.create;
|
||||
});
|
85
lemur/static/app/angular/roles/role/role.tpl.html
Normal file
85
lemur/static/app/angular/roles/role/role.tpl.html
Normal file
@ -0,0 +1,85 @@
|
||||
<h2 class="featurette-heading"><span ng-show="!role.fromServer">Create</span><span ng-show="role.fromServer">Edit</span> Role <span class="text-muted"><small>The nail that sticks out farthest gets hammered the hardest
|
||||
</small></span></h2>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<button ng-click="roleService.loadPassword(role)" class="btn btn-warning">Show Credentials</button>
|
||||
<a href="/#/roles" class="btn btn-danger pull-right">Cancel</a>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form name="createForm" class="form-horizontal" ng-submit="save(role)" role="form" novalidate>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': createForm.name.$invalid, 'has-success': !createForm.name.$invalid&&createForm.name.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Name
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="name" ng-model="role.name" placeholder="Name" class="form-control" required/>
|
||||
<p ng-show="createForm.name.$invalid && !createForm.name.$pristine" class="help-block">You must enter an role name</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Description
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<textarea name="description" ng-model="role.description" placeholder="Something elegant" class="form-control" ></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Username
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input ng-show="!role.fromServer" name="username" ng-model="role.username" placeholder="Username" class="form-control" required/>
|
||||
<div class="well">
|
||||
<span ng-show="role.password">
|
||||
{{ role.username }}
|
||||
</span>
|
||||
<span ng-show="!role.password">
|
||||
******************
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Password
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input ng-show="!role.fromServer" type="password" name="password" ng-model="role.password" placeholder="hunter2" class="form-control" required/>
|
||||
<p ng-show="createForm.password.$invalid && !createForm.password.$pristine" class="help-block">You must enter an password</p>
|
||||
<div class="well">
|
||||
<span ng-show="role.password">
|
||||
{{ role.password }}
|
||||
</span>
|
||||
<span ng-show="!role.password">
|
||||
*****************
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">User(s)</label>
|
||||
<div class="col-sm-10">
|
||||
<input tooltip="You can attach any user to this role, once attached they will have access as defined by this role"
|
||||
typeahead="user.username for user in userService.findUserByName($viewValue)" typeahead-loading="loadingUsers"
|
||||
typeahead-min-wait="100" typeahead-on-select="role.addUser($item)"
|
||||
type="text" name="user" ng-model="role.selectedUser" placeholder="Username..." class="form-control" required/>
|
||||
<table ng-show="role.users" class="table">
|
||||
<tr ng-repeat="user in role.users track by $index">
|
||||
<td>{{ user.username }}</td>
|
||||
<td>
|
||||
<button type="button" ng-click="role.removeUser($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<button ng-click="save(role)" class="btn btn-success pull-right">Save</button>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
119
lemur/static/app/angular/roles/services.js
vendored
Normal file
119
lemur/static/app/angular/roles/services.js
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
angular.module('lemur')
|
||||
.service('RoleApi', function (LemurRestangular) {
|
||||
LemurRestangular.extendModel('roles', function (obj) {
|
||||
return angular.extend(obj, {
|
||||
addUser: function (user) {
|
||||
this.selectedUser = null;
|
||||
if (this.users === undefined) {
|
||||
this.users = [];
|
||||
}
|
||||
this.users.push(user);
|
||||
},
|
||||
removeUser: function (index) {
|
||||
this.users.splice(index, 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
return LemurRestangular.all('roles');
|
||||
})
|
||||
.service('RoleService', function ($location, RoleApi, toaster) {
|
||||
var RoleService = this;
|
||||
RoleService.findRoleByName = function (filterValue) {
|
||||
return RoleApi.getList({'filter[name]': filterValue})
|
||||
.then(function (roles) {
|
||||
return roles;
|
||||
});
|
||||
};
|
||||
|
||||
RoleService.getRoleDropDown = function () {
|
||||
return RoleApi.getList().then(function (roles) {
|
||||
return roles;
|
||||
});
|
||||
};
|
||||
|
||||
RoleService.getUsers = function (role) {
|
||||
role.customGET('users').then(function (users) {
|
||||
role.users = users;
|
||||
});
|
||||
};
|
||||
|
||||
RoleService.create = function (role) {
|
||||
RoleApi.post(role).then(
|
||||
function () {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: role.name,
|
||||
body: 'Has been successfully created!'
|
||||
});
|
||||
$location.path('roles');
|
||||
},
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: role.name,
|
||||
body: 'Has not been created! ' + response.data.message
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
RoleService.update = function (role) {
|
||||
role.put().then(
|
||||
function () {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: role.name,
|
||||
body: 'Successfully updated!'
|
||||
});
|
||||
$location.path('roles');
|
||||
},
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: role.name,
|
||||
body: 'Was not updated!' + response.data.message
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
RoleService.remove = function (role) {
|
||||
return role.remove().then(
|
||||
function () {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: role.name,
|
||||
body: 'Successfully deleted!'
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: role.name,
|
||||
body: 'Was not deleted!' + response.data.message
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
RoleService.loadPassword = function (role) {
|
||||
return role.customGET('credentials').then(
|
||||
function (response) {
|
||||
if ( response.password === null) {
|
||||
toaster.pop({
|
||||
type: 'info',
|
||||
title: role.name,
|
||||
body: 'Has no password associated'
|
||||
});
|
||||
} else {
|
||||
role.password = response.password;
|
||||
role.username = response.username;
|
||||
}
|
||||
},
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: role.name,
|
||||
body: 'You do not have permission to view this password!'
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
42
lemur/static/app/angular/roles/view/view.js
vendored
Normal file
42
lemur/static/app/angular/roles/view/view.js
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
|
||||
.config(function config($routeProvider) {
|
||||
$routeProvider.when('/roles', {
|
||||
templateUrl: '/angular/roles/view/view.tpl.html',
|
||||
controller: 'RolesViewController'
|
||||
});
|
||||
})
|
||||
|
||||
.controller('RolesViewController', function ($scope, RoleApi, RoleService, ngTableParams) {
|
||||
$scope.filter = {};
|
||||
$scope.rolesTable = new ngTableParams({
|
||||
page: 1, // show first page
|
||||
count: 10, // count per page
|
||||
sorting: {
|
||||
id: 'desc' // initial sorting
|
||||
},
|
||||
filter: $scope.filter
|
||||
}, {
|
||||
total: 0, // length of data
|
||||
getData: function ($defer, params) {
|
||||
RoleApi.getList(params.url())
|
||||
.then(function (data) {
|
||||
params.total(data.total);
|
||||
$defer.resolve(data);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.remove = function (role) {
|
||||
RoleService.remove(role).then(function () {
|
||||
$scope.rolesTable.reload();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.toggleFilter = function (params) {
|
||||
params.settings().$scope.show_filter = !params.settings().$scope.show_filter;
|
||||
};
|
||||
|
||||
});
|
41
lemur/static/app/angular/roles/view/view.tpl.html
Normal file
41
lemur/static/app/angular/roles/view/view.tpl.html
Normal file
@ -0,0 +1,41 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2 class="featurette-heading">Roles
|
||||
<span class="text-muted"><small>Black Hat? Grey Hat? White Hat?</small></span></h2>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<div class="btn-group pull-right">
|
||||
<a data-placement="left" data-title="Create Role" bs-tooltip href="#/roles/create" class="btn btn-primary">Create</a>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button ng-click="toggleFilter(rolesTable)" class="btn btn-default">Filter</button>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table ng-table="rolesTable" class="table table-striped" show-filter="false" template-pagination="angular/pager.html">
|
||||
<tbody>
|
||||
<tr ng-repeat="role in $data track by $index">
|
||||
<td data-title="'Name'" sortable="'name'" filter="{ 'name': 'text' }">
|
||||
<ul class="list-unstyled">
|
||||
<li>{{ role.name }}</li>
|
||||
<li><span class="text-muted">{{ role.description }}</span></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td data-title="''">
|
||||
<div class="btn-group-vertical pull-right">
|
||||
<a href="#/roles/{{ role.id }}/edit" class="btn btn-sm btn-info">
|
||||
Edit
|
||||
</a>
|
||||
<a ng-click="remove(role)" class="btn btn-sm btn-danger">
|
||||
Remove
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
89
lemur/static/app/angular/users/services.js
vendored
Normal file
89
lemur/static/app/angular/users/services.js
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Created by kglisson on 1/19/15.
|
||||
*/
|
||||
'use strict';
|
||||
angular.module('lemur')
|
||||
.service('UserApi', function (LemurRestangular) {
|
||||
LemurRestangular.extendModel('users', function (obj) {
|
||||
return angular.extend(obj, {
|
||||
attachRole: function (role) {
|
||||
this.selectedRole = null;
|
||||
if (this.roles === undefined) {
|
||||
this.roles = [];
|
||||
}
|
||||
this.roles.push(role);
|
||||
},
|
||||
removeRole: function (index) {
|
||||
this.roles.splice(index, 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
return LemurRestangular.all('users');
|
||||
})
|
||||
.service('UserService', function ($location, UserApi, AuthenticationApi, toaster) {
|
||||
var UserService = this;
|
||||
UserService.getCurrentUser = function () {
|
||||
return AuthenticationApi.customGET('me').then(function (user) {
|
||||
return user;
|
||||
});
|
||||
};
|
||||
|
||||
UserService.findUserByName = function (filterValue) {
|
||||
return UserApi.getList({'filter[username]': filterValue})
|
||||
.then(function (users) {
|
||||
return users;
|
||||
});
|
||||
};
|
||||
|
||||
UserService.getRoles = function (user) {
|
||||
user.getList('roles').then(function (roles) {
|
||||
user.roles = roles;
|
||||
});
|
||||
};
|
||||
|
||||
UserService.loadMoreRoles = function (user, page) {
|
||||
user.getList('roles', {page: page}).then(function (roles) {
|
||||
_.each(roles, function (role) {
|
||||
user.roles.push(role);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
UserService.create = function (user) {
|
||||
UserApi.post(user).then(
|
||||
function () {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: user.username,
|
||||
body: 'Has been successfully created!'
|
||||
});
|
||||
$location.path('users');
|
||||
},
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: user.username,
|
||||
body: 'Has not been created!' + response.data.message
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
UserService.update = function (user) {
|
||||
user.put().then(
|
||||
function () {
|
||||
toaster.pop({
|
||||
type: 'success',
|
||||
title: user.username,
|
||||
body: 'Has been successfully updated!'
|
||||
});
|
||||
$location.path('users');
|
||||
},
|
||||
function (response) {
|
||||
toaster.pop({
|
||||
type: 'error',
|
||||
title: user.username,
|
||||
body: 'Has not been updated!' + response.data.message
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
38
lemur/static/app/angular/users/user/user.js
vendored
Normal file
38
lemur/static/app/angular/users/user/user.js
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
|
||||
.config(function config($routeProvider) {
|
||||
$routeProvider.when('/users/create', {
|
||||
templateUrl: '/angular/users/user/user.tpl.html',
|
||||
controller: 'UsersCreateController'
|
||||
});
|
||||
$routeProvider.when('/users/:id/edit', {
|
||||
templateUrl: '/angular/users/user/user.tpl.html',
|
||||
controller: 'UsersEditController'
|
||||
});
|
||||
})
|
||||
|
||||
.controller('UsersEditController', function ($scope, $routeParams, UserApi, UserService, RoleService) {
|
||||
UserApi.get($routeParams.id).then(function (user) {
|
||||
UserService.getRoles(user);
|
||||
$scope.user = user;
|
||||
});
|
||||
|
||||
$scope.save = UserService.update;
|
||||
$scope.roleService = RoleService;
|
||||
|
||||
$scope.rolePage = 1;
|
||||
|
||||
$scope.loadMoreRoles = function () {
|
||||
$scope.rolePage += 1;
|
||||
UserService.loadMoreRoles($scope.user, $scope.rolePage);
|
||||
};
|
||||
})
|
||||
|
||||
.controller('UsersCreateController', function ($scope, UserService, LemurRestangular, RoleService) {
|
||||
$scope.user = LemurRestangular.restangularizeElement(null, {}, 'users');
|
||||
$scope.save = UserService.create;
|
||||
$scope.roleService = RoleService;
|
||||
|
||||
});
|
89
lemur/static/app/angular/users/user/user.tpl.html
Normal file
89
lemur/static/app/angular/users/user/user.tpl.html
Normal file
@ -0,0 +1,89 @@
|
||||
<h2 class="featurette-heading"><span ng-show="!user.fromServer">Create</span><span ng-show="user.fromServer">Edit</span> User <span class="text-muted"><small>what was your name again?
|
||||
</small></span></h2>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<a href="#/users" class="btn btn-danger pull-right">Cancel</a>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form name="createForm" class="form-horizontal" role="form" novalidate>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': createForm.username.$invalid, 'has-success': !createForm.username.$invalid&&createForm.username.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Name
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input name="username" ng-model="user.username" placeholder="Name" class="form-control" required/>
|
||||
<p ng-show="createForm.username.$invalid && !createForm.username.$pristine" class="help-block">You must enter a username</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': createForm.email.$invalid, 'has-success': !createForm.email.$invalid&&createForm.email.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Email
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="email" name="email" ng-model="user.email" placeholder="hi@example.com" class="form-control" required/>
|
||||
<p ng-show="createForm.email.$invalid && !createForm.email.$pristine" class="help-block">You must enter an email</p>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="!user.id" class="form-group"
|
||||
ng-class="{'has-error': createForm.password.$invalid, 'has-success': !createForm.password.$invalid&&createForm.password.$dirty}">
|
||||
<label class="control-label col-sm-2">
|
||||
Password
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" name="password" ng-model="user.password" placeholder="hunter2" class="form-control" required/>
|
||||
<p ng-show="createForm.password.$invalid && !createForm.password.$pristine" class="help-block">You must enter an password</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Active
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="checkbox col-sm-10">
|
||||
<input ng-model="user.active" type="checkbox" value="true">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-sm-2">
|
||||
Roles
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group">
|
||||
<input type="text" ng-model="user.selectedRole" placeholder="Role Name"
|
||||
typeahead="role.name for role in roleService.findRoleByName($viewValue)" typeahead-loading="loadingRoles"
|
||||
class="form-control input-md" typeahead-on-select="user.attachRole($item)" typeahead-min-wait="50"
|
||||
tooltip="Roles control which authorities a user can issue certificates from"
|
||||
tooltip-trigger="focus" tooltip-placement="top">
|
||||
<span class="input-group-btn">
|
||||
<button ng-model="roles.show" class="btn btn-md btn-default" btn-checkbox btn-checkbox-true="1" btn-checkbox-false="0">
|
||||
<span class="badge">{{ user.roles.total || 0 }}</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<table ng-show="user.roles" class="table">
|
||||
<tr ng-repeat="role in user.roles track by $index">
|
||||
<td><a class="btn btn-sm btn-info" href="#/roles/{{ role.id }}/edit">{{ role.name }}</a></td>
|
||||
<td><span class="text-muted">{{ role.description }}</span></td>
|
||||
<td>
|
||||
<button type="button" ng-click="user.removeRole($index)" class="btn btn-danger btn-sm pull-right">Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td><a class="pull-right" ng-click="loadMoreRoles()"><strong>More</strong></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<button ng-click="save(user)" class="btn btn-success pull-right">Save</button>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
43
lemur/static/app/angular/users/view/view.js
vendored
Normal file
43
lemur/static/app/angular/users/view/view.js
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('lemur')
|
||||
|
||||
.config(function config($routeProvider) {
|
||||
$routeProvider.when('/users', {
|
||||
templateUrl: '/angular/users/view/view.tpl.html',
|
||||
controller: 'UsersViewController'
|
||||
});
|
||||
})
|
||||
|
||||
.controller('UsersViewController', function ($scope, UserApi, UserService, ngTableParams) {
|
||||
$scope.filter = {};
|
||||
$scope.usersTable = new ngTableParams({
|
||||
page: 1, // show first page
|
||||
count: 10, // count per page
|
||||
sorting: {
|
||||
id: 'desc' // initial sorting
|
||||
},
|
||||
filter: $scope.filter
|
||||
}, {
|
||||
total: 0, // length of data
|
||||
getData: function ($defer, params) {
|
||||
UserApi.getList(params.url()).then(
|
||||
function (data) {
|
||||
params.total(data.total);
|
||||
$defer.resolve(data);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$scope.remove = function (account) {
|
||||
account.remove().then(function () {
|
||||
$scope.usersTable.reload();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.toggleFilter = function (params) {
|
||||
params.settings().$scope.show_filter = !params.settings().$scope.show_filter;
|
||||
};
|
||||
|
||||
});
|
43
lemur/static/app/angular/users/view/view.tpl.html
Normal file
43
lemur/static/app/angular/users/view/view.tpl.html
Normal file
@ -0,0 +1,43 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2 class="featurette-heading">Users
|
||||
<span class="text-muted"><small>what was your name again?</small></span></h2>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<div class="btn-group pull-right">
|
||||
<a href="#/users/create" class="btn btn-primary">Create</a>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button ng-click="toggleFilter(usersTable)" class="btn btn-default">Filter</button>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table ng-table="usersTable" class="table table-striped" show-filter="false" template-pagination="angular/pager.html">
|
||||
<tbody>
|
||||
<tr ng-repeat="user in $data track by $index">
|
||||
<td data-title="'Name'" sortable="'user'" filter="{ 'username': 'text' }">
|
||||
<ul class="list-unstyled">
|
||||
<li>{{ user.username }}</li>
|
||||
<li><span class="text-muted">{{ user.email }}</span></li>
|
||||
</ul>
|
||||
</td>
|
||||
<td data-title="'Active'" sortable="'active'">
|
||||
<i class="glyphicon glyphicon-ok" ng-show="certificate.san == 'true'"></i>
|
||||
<i class="glyphicon glyphicon-remove" ng-show="certificate.san == 'false'"></i>
|
||||
<i ng-show="user.active" class="glyphicon glyphicon-ok"></i><i ng-show="!user.active" class="glyphicon gplyphinco-remove"></i>
|
||||
</td>
|
||||
<td data-title="''">
|
||||
<div class="btn-group-vertical pull-right">
|
||||
<a tooltip="Edit User" href="#/users/{{ user.id }}/edit" class="btn btn-sm btn-info">
|
||||
Edit
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
19
lemur/static/app/angular/welcome/welcome.html
Normal file
19
lemur/static/app/angular/welcome/welcome.html
Normal file
@ -0,0 +1,19 @@
|
||||
<div class="jumbotron">
|
||||
<h1>Hey there!</h1>
|
||||
|
||||
<p>Welcome to Lemur! A central portal for all (most) of your SSL needs.</p>
|
||||
|
||||
<p><a href="/#/certificates/create" class="btn btn-primary btn-lg" role="button">Create a Certificate</a></p>
|
||||
</div>
|
||||
<div class="row featurette">
|
||||
<div class="col-md-10">
|
||||
<h2 class="featurette-heading">SSL In The Cloud <span class="text-muted">Encrypt it all </span></h2>
|
||||
|
||||
<p class="lead">The Security Operations team manages all of the SSL certificate generation at Netflix. This
|
||||
portal was created to serve as both a self service application so that application owners can provision
|
||||
their own certificates and to help enforce some key naming and security conventions, in order provide
|
||||
Netflix with scalable and manageable SSL security.</p>
|
||||
|
||||
<p>See <a href="http://go/ssl">go/ssl</a> for more info.</p>
|
||||
</div>
|
||||
</div>
|
23
lemur/static/app/angular/wizard.html
Normal file
23
lemur/static/app/angular/wizard.html
Normal file
@ -0,0 +1,23 @@
|
||||
<div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<ul class="steps-indicator steps-{{steps.length}}" ng-if="!hideIndicators">
|
||||
<li ng-class="{default: !step.completed && !step.selected, current: step.selected && !step.completed, done: step.completed && !step.selected, editing: step.selected && step.completed}" ng-repeat="step in steps">
|
||||
<a ng-click="goTo(step)">{{step.title || step.wzTitle}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="steps" ng-transclude></div>
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<input ng-hide="currentStepNumber() == 1" class="btn btn-default pull-left" type="submit" wz-previous value="Previous" />
|
||||
<input ng-show="currentStepNumber() != steps.length" class="btn btn-default pull-right" type="submit" wz-next value="Next" />
|
||||
<button ng-show="currentStepNumber() == steps.length" class="btn btn-success pull-right" type="submit" wz-next>
|
||||
Create
|
||||
</button>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
BIN
lemur/static/app/favicon.ico
Normal file
BIN
lemur/static/app/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
86
lemur/static/app/index.html
Normal file
86
lemur/static/app/index.html
Normal file
@ -0,0 +1,86 @@
|
||||
<!doctype html>
|
||||
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
||||
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Lemur</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
|
||||
<!-- build:css(.) styles/main.css -->
|
||||
<!-- inject:css -->
|
||||
<!-- endinject -->
|
||||
<!-- endbuild -->
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
<script src="bower_components/es5-shim/es5-shim.js"></script>
|
||||
<script src="bower_components/json3/lib/json3.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<!-- build:js(.) scripts/vendor.js -->
|
||||
<!-- inject:bower:js -->
|
||||
<!-- endinject -->
|
||||
<!-- endbuild -->
|
||||
|
||||
<!-- build:js(.) scripts/main.js -->
|
||||
<!-- inject:js -->
|
||||
<!-- endinject -->
|
||||
<!-- inject:ngviews -->
|
||||
<!-- endinject -->
|
||||
<!-- endbuild -->
|
||||
|
||||
</head>
|
||||
<body ng-app="lemur" ng-csp>
|
||||
<toaster-container></toaster-container>
|
||||
<!--[if lt IE 7]>
|
||||
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
||||
<![endif]-->
|
||||
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="/#/">Lemur</a>
|
||||
</div>
|
||||
<div class="navbar-collapse collapse" ng-controller="LoginController">
|
||||
<ul class="nav navbar-nav navbar-left">
|
||||
<li data-match-route="/dashboard"><a href="/#/dashboard">Dashboard</a></li>
|
||||
<li data-match-route="/certificates"><a href="/#/certificates">Certificates</a></li>
|
||||
<li data-match-route="/authorities"><a href="/#/authorities">Authorities</a></li>
|
||||
<li data-match-route="/domains"><a href="/#/domains">Domains</a></li>
|
||||
<li><a href="/#/roles">Roles</a></li>
|
||||
<li><a href="/#/users">Users</a></li>
|
||||
<li><a href="/#/accounts">Accounts</a></li>
|
||||
</ul>
|
||||
<ul ng-show="!currentUser.username" class="nav navbar-nav navbar-right">
|
||||
<li><a href="/#/login">Login</a></li>
|
||||
</ul>
|
||||
<ul ng-show="currentUser.username" class="nav navbar-nav navbar-right">
|
||||
<li class="dropdown" dropdown on-toggle="toggled(open)">
|
||||
<a href class="dropdown-toggle profile-nav" dropdown-toggle>
|
||||
{{ currentUser.username }}<img ng-show="currentUser.profileImage" src="{{ currentUser.profileImage }}" class="profile img-circle">
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a ng-click="logout()">Logout</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Add your site or application content here -->
|
||||
<div class="container-fluid">
|
||||
<div ng-view></div>
|
||||
</div>
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">Lemur is maintained by <a href="mailto:secops@netflix.com">Security Operations</a>.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
3
lemur/static/app/robots.txt
Normal file
3
lemur/static/app/robots.txt
Normal file
@ -0,0 +1,3 @@
|
||||
# robotstxt.org
|
||||
|
||||
User-agent: *
|
161
lemur/static/app/styles/lemur.css
Normal file
161
lemur/static/app/styles/lemur.css
Normal file
@ -0,0 +1,161 @@
|
||||
body { padding-top: 70px; }
|
||||
|
||||
.steps-indicator {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.bs-callout {
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
border: 1px solid #eee;
|
||||
border-left-width: 5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.bs-callout h4 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.bs-callout p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.bs-callout code {
|
||||
border-radius: 3px;
|
||||
}
|
||||
.bs-callout+.bs-callout {
|
||||
margin-top: -5px;
|
||||
}
|
||||
.bs-callout-default {
|
||||
border-left-color: #777;
|
||||
}
|
||||
.bs-callout-default h4 {
|
||||
color: #777;
|
||||
}
|
||||
.bs-callout-primary {
|
||||
border-left-color: #428bca;
|
||||
}
|
||||
.bs-callout-primary h4 {
|
||||
color: #428bca;
|
||||
}
|
||||
.bs-callout-success {
|
||||
border-left-color: #5cb85c;
|
||||
}
|
||||
.bs-callout-success h4 {
|
||||
color: #5cb85c;
|
||||
}
|
||||
.bs-callout-danger {
|
||||
border-left-color: #d9534f;
|
||||
}
|
||||
.bs-callout-danger h4 {
|
||||
color: #d9534f;
|
||||
}
|
||||
.bs-callout-warning {
|
||||
border-left-color: #f0ad4e;
|
||||
}
|
||||
.bs-callout-warning h4 {
|
||||
color: #f0ad4e;
|
||||
}
|
||||
.bs-callout-info {
|
||||
border-left-color: #5bc0de;
|
||||
}
|
||||
.bs-callout-info h4 {
|
||||
color: #5bc0de;
|
||||
}
|
||||
|
||||
.modal-dialog-center { /* Edited classname 10/03/2014 */
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
.wave-spinner {
|
||||
margin: auto;
|
||||
width: 100px;
|
||||
height: 60px;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
html {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
}
|
||||
body {
|
||||
/* Margin bottom by footer height */
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
/* Set the fixed height of the footer here */
|
||||
height: 60px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.container .text-muted {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.footer > .container {
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.alert {
|
||||
position:absolute;
|
||||
z-index:1;
|
||||
}
|
||||
|
||||
.login {
|
||||
max-width: 320px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.login-or {
|
||||
position: relative;
|
||||
font-size: 18px;
|
||||
color: #aaa;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.span-or {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: -2px;
|
||||
margin-left: -25px;
|
||||
background-color: #fff;
|
||||
width: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hr-or {
|
||||
background-color: #cdcdcd;
|
||||
height: 1px;
|
||||
margin-top: 0px !important;
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
|
||||
.profile-nav {
|
||||
padding-top: 12px !important;
|
||||
padding-bottom: 12px !important;
|
||||
}
|
||||
|
||||
.profile {
|
||||
height: 35px;
|
||||
width: 35px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.message {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
Reference in New Issue
Block a user