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