initial commit

This commit is contained in:
Kevin Glisson
2015-06-22 13:47:27 -07:00
commit 4330ac9c05
228 changed files with 16656 additions and 0 deletions

View File

@ -0,0 +1 @@
*.coffee

157
lemur/static/app/404.html Normal file
View 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>

View 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;
});

View 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>

View 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;
});

View 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;
};
});

View 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
View 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="">&laquo;</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="">&#8230;</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="">&raquo;</a> </li> </ul> </div></div>');
}]);

View 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;
});
});

View 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>

View 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('/');
});

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
</body>
</html>

View 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('/');
})
};
});

View 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');
});
};
});

View File

@ -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>

View 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;
};
});

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,3 @@
<a tabindex="-1">
{{ match.model.name }} - <span class="text-muted">{{match.model.description }}</span>
</a>

View File

@ -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>

View 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
});
});
};
});

View 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;
};
});

View 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>

View 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;
});

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View 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;
});
};
});

View File

@ -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>

View 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;
});

View 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;
};
});

View 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>

View 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();
});
};
});

View 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']]};
});
});

View 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>

View 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;
});
};
});

View 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;
};
});

View 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>

View File

@ -0,0 +1,3 @@
/**
* Created by kglisson on 1/19/15.
*/

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
</body>
</html>

View 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;
});

View 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;
};
});

View 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>

View 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');
});
};
});

View 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>

View 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="">&#8230;</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>

View 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;
});

View 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>

View 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!'
});
});
};
});

View 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;
};
});

View 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>

View 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
});
});
};
});

View 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;
});

View 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>

View 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;
};
});

View 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>

View 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>

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View 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>

View File

@ -0,0 +1,3 @@
# robotstxt.org
User-agent: *

View 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;
}