diff options
author | Dixsie Wolmers <dixsiew@gmail.com> | 2019-09-11 15:26:38 -0500 |
---|---|---|
committer | Gunnar Mills <gmills@us.ibm.com> | 2019-10-21 18:41:51 +0000 |
commit | c15f66b0d52e855ca0c03e20f875f7c4bd67282f (patch) | |
tree | 327202e6525858a0154d6231d08e7c4a73fc2273 /app | |
parent | 2ac4eda38aff10e8647ded62569f102290f3331a (diff) | |
download | phosphor-webui-c15f66b0d52e855ca0c03e20f875f7c4bd67282f.tar.gz phosphor-webui-c15f66b0d52e855ca0c03e20f875f7c4bd67282f.zip |
Update certificate management page consistency
This change applies global styles to improve
page layout consistency and modal management
- Adds page and section styles
- Removes unused styles
- Creates individual html files for modals
- Updates certificate modals to bootstrap modal
- Updates global styles for input file field in file-upload.scss
TODO:
- Update certificate table with table component in separate commit
- Update CSR modal to use global form-field styles in separate commit
Signed-off-by: Dixsie Wolmers <dixsiew@gmail.com>
Change-Id: I9b800cb684740da1a9168294433e726efb0f9d0e
Diffstat (limited to 'app')
-rw-r--r-- | app/access-control/controllers/certificate-controller.html | 312 | ||||
-rw-r--r-- | app/access-control/controllers/certificate-controller.js | 79 | ||||
-rw-r--r-- | app/access-control/controllers/certificate-modal-add-cert.html | 86 | ||||
-rw-r--r-- | app/access-control/controllers/certificate-modal-csr-download.html | 41 | ||||
-rw-r--r-- | app/access-control/controllers/certificate-modal-csr-gen.html | 351 | ||||
-rw-r--r-- | app/access-control/styles/certificate.scss | 72 | ||||
-rw-r--r-- | app/common/directives/certificate.html | 75 | ||||
-rw-r--r-- | app/common/styles/elements/file-upload.scss | 56 | ||||
-rw-r--r-- | app/common/styles/elements/index.scss | 2 | ||||
-rw-r--r-- | app/common/styles/elements/input-file.scss | 25 | ||||
-rw-r--r-- | app/common/styles/elements/modals.scss | 42 |
11 files changed, 736 insertions, 405 deletions
diff --git a/app/access-control/controllers/certificate-controller.html b/app/access-control/controllers/certificate-controller.html index c1f64fa..d2db65c 100644 --- a/app/access-control/controllers/certificate-controller.html +++ b/app/access-control/controllers/certificate-controller.html @@ -1,32 +1,44 @@ <loader loading="loading"></loader> -<div id="configuration-cert"> - <div class="row column"> - <h1>SSL certificates</h1> - </div> - <div ng-repeat="certificate in certificates | filter:{isExpiring:true}" class="row column"> - <div class="small-12 alert alert-warning" role="alert"> - <icon file="icon-warning.svg" aria-hidden="true"></icon> - The uploaded {{certificate.name}} is expiring in {{getDays(certificate.ValidNotAfter) === 0 ? 'less than one day!' : getDays(certificate.ValidNotAfter) - + ' days!'}} Consider replacing it with a new certificate. +<div id="configuration-cert" class="page"> + <h1 class="page-title">SSL certificates</h1> + <section class="section"> + <div class="section-content"> + <div ng-repeat="certificate in certificates | filter:{isExpiring:true}"> + <div class="alert alert-warning" role="alert"> + <icon file="icon-warning.svg" aria-hidden="true"></icon> + The uploaded {{ certificate.name }} is expiring in + {{ getDays(certificate.ValidNotAfter) === 0 + ? "less than one day!" + : getDays(certificate.ValidNotAfter) + " days!" }} + Consider replacing it with a new certificate. + </div> + </div> + <div ng-repeat="certificate in certificates | filter:{isExpired:true}"> + <div class="alert alert-danger" role="alert"> + <icon file="icon-critical.svg" aria-hidden="true"></icon> + The uploaded {{ certificate.name }} has expired! Consider replacing it + with a new certificate. + </div> + </div> </div> - </div> - <div ng-repeat="certificate in certificates|filter:{isExpired:true}" class="row column"> - <div class="small-12 alert alert-danger" role="alert"> - <icon file="icon-critical.svg" aria-hidden="true"></icon> The uploaded {{certificate.name}} has expired! Consider replacing it with a new certificate. + </section> + <section class="section"> + <div class="section-content"> + <button + type="button" + class="btn btn-tertiary" + ng-disabled="availableCertificateTypes.length === 0" + ng-click="addCertModal()"> + <icon class="icon-add" file="icon-plus.svg" aria-hidden="true"></icon> + Add new certificate + </button> + <button type="button" class="btn btn-tertiary" ng-click="addCsrModal()"> + <icon class="icon-add" file="icon-plus.svg" aria-hidden="true"></icon> + Generate CSR + </button> </div> - </div> - <div class="row column"> - <button type="button" class="btn btn-tertiary" ng-disabled="availableCertificateTypes.length === 0" ng-click="addCertificateModal=true"> - <icon class="icon-add" file="icon-plus.svg"></icon> - Add new certificate - </button> - <button type="button" class="btn btn-tertiary" ng-click="addCSRModal=true"> - <icon class="icon-add" file="icon-plus.svg"></icon> - Generate CSR - </button> - </div> - <div class="row column"> - <div class="small-12 certificate__table"> + <!-- TODO: Replace table with resusable table component --> + <div class="section-content certificate__table"> <div class="table__row-header"> <div class="row column"> <div class="certificate__type-header"> @@ -41,254 +53,18 @@ <div class="certificate__date-header"> Valid from </div> - <div class="certificate__status-header"> - </div> + <div class="certificate__status-header"></div> <div class="certificate__date-header"> Valid until </div> </div> </div> - <div ng-if="certificates.length < 1" class="empty__logs">There have been no certificates added.</div> - <div ng-repeat="certificate in certificates"> - <certificate cert="certificate" reload="loadCertificates()" )></certificate> + <div ng-if="certificates.length < 1" class="empty__logs"> + There have been no certificates added. </div> - </div> - </div> - <section class="modal add__certificate__modal" aria-hidden="true" role="dialog" ng-class="{'active': addCertificateModal}"> - <form name="add__cert__form" id="add__cert__form" ng-class="{'submitted': submitted}"> - <div class="modal__content"> - <button class="certificate__close-modal" ng-click="addCertificateModal = false; add__cert__form.$setUntouched()"> - <icon aria-hidden="true" file="icon-close.svg"> - </button> - <h2 class="page-header">Add new certificate</h2> - <div class="row column add-certificate__section "> - <div class="small-12"> - <label for="cert__type">Certificate type</label> - <select id="cert__type" name="cert__type" ng-model="newCertificate.selectedType" required> - <option class="courier-bold" ng-value="">Select an option</option> - <option class="courier-bold" ng-value="type" ng-repeat="type in availableCertificateTypes"> - {{type.name}}</option> - </select> - <div ng-messages="add__cert__form.cert__type.$error" class="form-error" ng-class="{'visible' : add__cert__form.cert__type.$touched || submitted }"> - <p ng-message="required">Field is required</p> - </div> - </div> - </div> - <div class="row column add-certificate__section"> - <div class="small-12"> - <label class="select__new-label" for="upload_cert_new">Certificate file</label> - </div> - <div class="row column file__upload add-certificate__section "> - <label for='upload_cert_new'> - <input name="upload_cert_new" id="upload_cert_new" type="file" file="newCertificate.file" class="hide" /> - <span class="btn btn-secondary select__new-button">Choose file</span> - </label> - </div> - <div class="row column add-certificate__section "> - <div ng-if="newCertificate.file" class="small-7 file__name"> - <span>{{newCertificate.file.name}}</span> - <icon file="icon-close.svg" ng-if="newCertificate.file.name" ng-click="newCertificate.file = '';" class="float-right"></icon> - </div> - </div> - </div> - </div> - <div class="modal__button-wrapper"> - <button class="btn btn-secondary" ng-click="addCertificateModal = false; newCertificate={};add__cert__form.$setUntouched();">Cancel</button> - <button class="btn btn-primary" ng-disabled="add__cert__form.$invalid || !newCertificate.file" ng-click="submitted = true; uploadCertificate();">Save</button> - </div> - </form> - </section> - - <section class="modal add-csr__modal" aria-hidden="true" role="dialog" ng-class="{'active': addCSRModal}"> - <!--Close button for displaying CSR Code, we need a close button within form to reset form validation correctly--> - <button class="certificate__close-modal" ng-click="resetCSRModal();" ng-if="displayCSRCode==true"> - <icon aria-hidden="true" file="icon-close.svg"> - </button> - - <!-- CSR Code display content--> - - <div ng-if="displayCSRCode==true"> - <h2 class="page-header">Certificate Signing Request (CSR)</h2> - <div class="modal__content add-csr__container"> - <span id="csrCode" class="add-csr__container-csr-code">{{csrCode}}</span> - </div> - <div class="modal__button-wrapper"> - <button class="btn btn-secondary" clipboard text="csrCode" on-copied="copySuccess(event)" on-error="copyfailed(err)"> - <span ng-if="!copied">Copy</span> - <span ng-if="copied"> - <icon aria-hidden="true" file="icon-check.svg"></icon> - <span>Copied</span> - </span> - </button> - <button class="btn btn-primary" ng-click="addCSRModal = false;"> - <a ng-href="data:text/json;charset=utf-8,{{csrCode}}" download="csrCode.txt" class="add-csr__text-download"> - Download</a> - </button> + <div ng-repeat="certificate in certificates"> + <certificate cert="certificate" reload="loadCertificates()"></certificate> </div> - </div> - - - - <form name="add__csr__form" id="add__csr__form" novalidate ng-if="displayCSRCode==false"> - <div class="modal__content add-csr__container"> - <button class="certificate__close-modal" ng-click="resetCSRModal(); add__csr__form.$setUntouched()"> - <icon aria-hidden="true" file="icon-close.svg"> - </button> - <h2 class="page-header">Generate a Certificate Signing Request (CSR)</h2> - <div class="row"> - <fieldset class="column medium-8 add-csr__section"> - <legend class="add-csr__section-title">General</legend> - <div class="row"> - <div class="column medium-6"> - <label for="cert__type" class="add-csr__label">Certificate Type *</label> - <select class="add-csr__select" id="cert__type" name="cert__type" ng-model="newCSR.certificateCollection" required> - <option class="courier-bold" ng-value="default" ng-model="selectOption">Select an option</option> - <!-- Do not show CA certificate as an option. Only a certificate authority can generate a CA certificate (known as TrustStore Certificate in Redfish) --> - <option class="courier-bold" ng-value="type" ng-repeat="type in allCertificateTypes" ng-if="type.Description !== 'TrustStore Certificate'"> - {{type.name}}</option> - </select> - <div ng-messages="add__csr__form.cert__type.$error" class="form-error" ng-class="{'visible' : add__csr__form.cert__type.$touched}"> - <p ng-message="required">Field is required</p> - </div> - </div> - <div class="column medium-6"> - <label for="countryCode" class="add-csr__label">Country *</label> - <select class="add-csr__select" id="countryCode" name="countryCode" ng-model="newCSR.countryCode" required> - <option class="courier-bold" ng-value="" ng-model="selectOption">Select an option</option> - <option class="courier-bold" ng-value="country" ng-repeat="country in countryList">{{country.Name}} - </option> - </select> - <div ng-messages="add__csr__form.countryCode.$error" class="form-error" ng-class="{'visible' : add__csr__form.countryCode.$touched}"> - <p ng-message="required">Field is required</p> - </div> - </div> - - <div class="column medium-6"> - <label for="state" class="add-csr__label">State *</label> - <input class="add-csr__input" ng-model="newCSR.state" type="text" id="state" name="state" required></input> - <div ng-messages="add__csr__form.state.$error" class="form-error" ng-class="{'visible' : add__csr__form.state.$touched}"> - <p ng-message="required">Field is required</p> - </div> - </div> - - <div class="column medium-6"> - <label for="city" class="add-csr__label">City *</label> - <input class="add-csr__input" id="city" name="city" ng-model="newCSR.city" type="text" required></input> - <div ng-messages="add__csr__form.city.$error" class="form-error" ng-class="{'visible' : add__csr__form.city.$touched}"> - <p ng-message="required">Field is required</p> - </div> - </div> - - <div class="column medium-6"> - <label for="companyName" class="add-csr__label">Company Name *</label> - <input class="add-csr__input" type="text" ng-model="newCSR.organization" id="companyName" name="companyName" required></input> - <div ng-messages="add__csr__form.companyName.$error" class="form-error" ng-class="{'visible' : add__csr__form.companyName.$touched}"> - <p ng-message="required">Field is required</p> - </div> - </div> - - <div class="column medium-6"> - <label for="companyUnit" class="add-csr__label">Company Unit *</label> - <input class="add-csr__input" ng-model="newCSR.companyUnit" name="companyUnit" id="companyUnit" type="text" required></input> - <div ng-messages="add__csr__form.companyUnit.$error" class="form-error" ng-class="{'visible' : add__csr__form.companyUnit.$touched}"> - <p ng-message="required">Field is required</p> - </div> - </div> - - <div class="column medium-6"> - <label for="commonName" class="add-csr__label">Common Name *</label> - <input class="add-csr__input" ng-model="newCSR.commonName" name="commonName" type="text" id="commonName" required></input> - <div ng-messages="add__csr__form.commonName.$error" class="form-error" ng-class="{'visible' : add__csr__form.commonName.$touched}"> - <p ng-message="required">Field is required</p> - </div> - </div> - - <div class="column medium-6"> - <label for="challengePassword" class="add-csr__label">Challenge Password</label> - <input class="add-csr__input-no-validation" id="challengePassword" ng-model="newCSR.challengePassword" type="text"></input> - </div> - - <div class="column medium-6"> - <label for="contactPerson" class="add-csr__label">Contact Person</label> - <input class="add-csr__input-no-validation" id="contactPerson" ng-model="newCSR.contactPerson" type="text"></input> - </div> - - <div class="column medium-6"> - <label for="emailAddress" class="add-csr__label">Email Address</label> - <input class="add-csr__input-no-validation" id="emailAddress" ng-model="newCSR.emailAddress" type="text"></input> - </div> - - <div class="column medium-6"> - <div> - <label id="alternate-name-label" for="alternateName" class="add-csr__label">Alternate Name</label> - <input class="add-csr__input-no-validation" ng-model="newCSR.firstAlternativeName" id="alternateName" name="alternativeName" - type="text"></input> - </div> - <div class="add-csr__additional-alt-names" ng-repeat="name in names"> - <input id="alternate-name-input-{{$index}}" aria-describedby="alternate-name-label" class="add-csr__input-no-validation" - ng-model="name.Value" type="text"></input> - <button aria-label="Delete alternate name field" aria-controls="alternate-name-input-{{$index}}" class="btn btn-tertiary add-csr__alt-name-delete-btn" - ng-click="deleteOptionalRow($index)" ng-disabled="multiSelected"> - <icon aria-hidden="true" file="icon-trashcan.svg"> - </button> - </div> - </div> - - <div class="column medium-6"> - <button class="btn btn-tertiary add-csr__alt-name-add-btn" ng-click="addOptionalRow()"> - <icon file="icon-plus.svg"></icon> - Add another alternate name - </button> - </div> - </div> - </fieldset> - - <fieldset class="column medium-4 add-csr__section add-csr__section--border "> - <legend class="add-csr__section-title">Private key</legend> - <div class="add-csr__container-private-key"> - <div class="add-csr__content-private-key"> - <label for="keyPairAlgorithm" class="add-csr__label">Key Pair Algorithm *</label> - <select class="add-csr__select" ng-model="newCSR.keyPairAlgorithm" id="keyPairAlgorithm" name="keyPairAlgorithm" required> - <option class="courier-bold" ng-value="" ng-model="selectOption">Select an option</option> - <option class="courier-bold" ng-value="data" ng-repeat="data in keyPairAlgorithm">{{data}}</option> - </select> - <div ng-messages="add__csr__form.keyPairAlgorithm.$error" class="form-error" ng-class="{'visible' : add__csr__form.keyPairAlgorithm.$touched}"> - <p ng-message="required">Field is required</p> - </div> - - <div ng-if="newCSR.keyPairAlgorithm == 'EC'"> - <label for="keyCurveId" class="add-csr__label">Key Curve ID</label> - <select class="add-csr__select" ng-model="newCSR.keyCurveId" id="keyCurveId" name="keyCurveId" required> - <option class="courier-bold" ng-value="">None</option> - <option class="courier-bold" ng-value="data" ng-repeat="data in keyCurveId">{{data}}</option> - </select> - <div ng-messages="add__csr__form.keyCurveId.$error" class="form-error" ng-class="{'visible' : add__csr__form.keyCurveId.$touched}"> - <p ng-message="required">Field is required</p> - </div> - </div> - - - <div ng-if="newCSR.keyPairAlgorithm =='RSA'"> - <label for="keyBitLength" class="add-csr__label">Key Bit Length *</label> - <select class="add-csr__select" ng-model="newCSR.keyBitLength" id="keyBitLength" name="keyBitLength" required> - <option class="courier-bold" ng-value="">Select an option</option> - <option class="courier-bold" ng-value="data" ng-repeat="data in keyBitLength">{{data}}</option> - </select> - <div ng-messages="add__csr__form.keyBitLength.$error" class="form-error" ng-class="{'visible' : add__csr__form.keyBitLength.$touched}"> - <p ng-message="required">Field is required</p> - </div> - </div> - - </div> - </fieldset> - </div> - </div> - <div class="modal__button-wrapper"> - <button class="btn btn-secondary" ng-click="resetCSRModal();add__csr__form.$setUntouched();">Cancel</button> - <button class="btn btn-primary" ng-click="csrSubmitted = true; getCSRCode();add__csr__form.$setUntouched();" ng-disabled="add__csr__form.$invalid">Generate CSR</button> - </div> - </div> - </form> </section> - <div class="modal-overlay" tabindex="-1" ng-class="{'active': addCertificateModal || addCSRModal}"></div>
\ No newline at end of file +</div> diff --git a/app/access-control/controllers/certificate-controller.js b/app/access-control/controllers/certificate-controller.js index 8c2992a..ad9a060 100644 --- a/app/access-control/controllers/certificate-controller.js +++ b/app/access-control/controllers/certificate-controller.js @@ -9,27 +9,22 @@ window.angular && (function(angular) { 'use strict'; - angular.module('app.accessControl').controller('certificateController', [ - '$scope', 'APIUtils', '$q', 'Constants', 'toastService', - function($scope, APIUtils, $q, Constants, toastService) { + angular.module('app.configuration').controller('certificateController', [ + '$scope', 'APIUtils', '$q', 'Constants', 'toastService', '$timeout', + '$uibModal', + function( + $scope, APIUtils, $q, Constants, toastService, $timeout, $uibModal) { $scope.loading = false; $scope.certificates = []; $scope.availableCertificateTypes = []; $scope.allCertificateTypes = Constants.CERTIFICATE_TYPES; - $scope.addCertificateModal = false; - $scope.addCSRModal = false; $scope.newCertificate = {}; $scope.newCSR = {}; - $scope.submitted = false; - $scope.csrSubmitted = false; - $scope.csrCode = ''; - $scope.displayCSRCode = false; $scope.keyBitLength = Constants.CERTIFICATE.KEY_BIT_LENGTH; $scope.keyPairAlgorithm = Constants.CERTIFICATE.KEY_PAIR_ALGORITHM; $scope.keyCurveId = Constants.CERTIFICATE.KEY_CURVE_ID; $scope.countryList = Constants.COUNTRIES; - $scope.$on('$viewContentLoaded', () => { getBmcTime(); }) @@ -70,7 +65,6 @@ window.angular && (function(angular) { toastService.error('Certificate must be a .pem file.'); return; } - $scope.addCertificateModal = false; APIUtils .addNewCertificate( $scope.newCertificate.file, $scope.newCertificate.selectedType) @@ -131,7 +125,6 @@ window.angular && (function(angular) { } }; - // create a CSR object to send to the backend $scope.getCSRCode = function() { var addCSR = {}; @@ -143,7 +136,6 @@ window.angular && (function(angular) { alternativeNames.push($scope.newCSR.firstAlternativeName) : $scope.newCSR.firstAlternativeName = ''; - addCSR.CertificateCollection = { '@odata.id': $scope.newCSR.certificateCollection.location }; @@ -163,21 +155,72 @@ window.angular && (function(angular) { APIUtils.createCSRCertificate(addCSR).then( function(data) { - $scope.displayCSRCode = true; $scope.csrCode = data; + openDownloadCsrModal(); }, function(error) { - $scope.addCSRModal = false; toastService.error('Unable to generate CSR. Try again.'); console.log(JSON.stringify(error)); }) }; + function openDownloadCsrModal() { + const modalTemplateCsrDownload = + require('./certificate-modal-csr-download.html'); + $uibModal + .open({ + template: modalTemplateCsrDownload, + windowTopClass: 'uib-modal', + scope: $scope, + ariaLabelledBy: 'modal_label', + size: 'lg', + }) + .result.catch(function() { + resetCSRModal(); + }); + }; + + $scope.addCertModal = function() { + openAddCertModal(); + }; + + function openAddCertModal() { + const modalTemplateAddCert = + require('./certificate-modal-add-cert.html'); + $uibModal + .open({ + template: modalTemplateAddCert, + windowTopClass: 'uib-modal', + scope: $scope, + ariaLabelledBy: 'modal_label', + }) + .result.catch(function() { + // do nothing + }); + }; + + $scope.addCsrModal = function() { + openCsrModal(); + }; + + function openCsrModal() { + const modalTemplateCsrGen = require('./certificate-modal-csr-gen.html'); + $uibModal + .open({ + template: modalTemplateCsrGen, + windowTopClass: 'uib-modal', + scope: $scope, + ariaLabelledBy: 'modal_label', + size: 'lg', + }) + .result.catch(function() { + resetCSRModal(); + }); + }; + // resetting the modal when user clicks cancel/closes the // modal - $scope.resetCSRModal = function() { - $scope.addCSRModal = false; - $scope.displayCSRCode = false; + const resetCSRModal = function() { $scope.newCSR.certificateCollection = $scope.selectOption; $scope.newCSR.commonName = ''; $scope.newCSR.contactPerson = ''; diff --git a/app/access-control/controllers/certificate-modal-add-cert.html b/app/access-control/controllers/certificate-modal-add-cert.html new file mode 100644 index 0000000..208bf1b --- /dev/null +++ b/app/access-control/controllers/certificate-modal-add-cert.html @@ -0,0 +1,86 @@ +<div class="uib-modal__content"> + <div class="modal-header"> + <h2 class="modal-title" id="modal_label"> + Add new certificate + </h2> + <button + type="button" + class="btn btn--close" + ng-click="$dismiss()" + aria-label="close"> + <icon file="icon-close.svg" aria-hidden="true"></icon> + </button> + </div> + <form + name="addCertForm" + id="addCertForm" + ng-class="{'submitted': submitted}"> + <div class="modal-body"> + <div class="form__field"> + <label class="content-label" for="certType">Certificate type</label> + <select + class="cert-dropdown" + id="certType" + name="certType" + ng-model="newCertificate.selectedType" + required> + <option class="courier-bold" ng-value="">Select an option</option> + <option + class="courier-bold" + ng-value="type" + ng-repeat="type in availableCertificateTypes"> + {{ type.name }} + </option> + </select> + <div + ng-messages="addCertForm.certType.$error" + class="form-error" + ng-class="{'visible' : addCertForm.certType.$touched || submitted }"> + <p ng-message="required">Field is required</p> + </div> + </div> + <div class="content-label form__field">Certificate file</div> + <div class="file-upload"> + <label + for="upload_cert_new" + class="file-upload-btn btn btn-secondary" + tabindex="0"> + Choose file + </label> + <input + name="uploadCertNew" + id="upload_cert_new" + type="file" + file="newCertificate.file" + class="file-upload-input"/> + <div class="form__field file-upload-container"> + <span ng-hide="newCertificate.file">No file selected</span> + <span>{{ newCertificate.file.name }}</span> + <button + type="reset" + class="btn file-upload-reset" + ng-if="newCertificate.file.name" + ng-click="newCertificate.file = '';" + aria-label="remove selected file"> + <icon file="icon-close.svg" aria-hidden="true"></icon> + </button> + </div> + </div> + </div> + <div class="modal-footer"> + <button + type="button" + class="btn btn-secondary" + ng-click="$dismiss(); addCertForm.$setUntouched();"> + Cancel + </button> + <button + type="submit" + class="btn btn-primary" + ng-click="submitted = true; uploadCertificate(); $dismiss()" + ng-disabled="addCertForm.$invalid || !newCertificate.file"> + Save + </button> + </div> + </form> +</div> diff --git a/app/access-control/controllers/certificate-modal-csr-download.html b/app/access-control/controllers/certificate-modal-csr-download.html new file mode 100644 index 0000000..6e31426 --- /dev/null +++ b/app/access-control/controllers/certificate-modal-csr-download.html @@ -0,0 +1,41 @@ +<div class="uib-modal__content"> + <div class="modal-header"> + <h2 class="modal-title" id="modal_label"> + Certificate Signing Request (CSR) + </h2> + <button + type="button" + class="btn btn--close" + ng-click="$dismiss()" + aria-label="close"> + <icon file="icon-close.svg" aria-hidden="true"></icon> + </button> + </div> + <div class="modal-body"> + <span id="csrCode" class="add-csr__container-csr-code">{{ csrCode }}</span> + </div> + <div class="modal-footer"> + <button + type="button" + class="btn btn-secondary" + clipboard + text="csrCode" + on-copied="copySuccess(event)" + on-error="copyfailed(err)"> + <span ng-if="!copied">Copy</span> + <span ng-if="copied"> + <icon aria-hidden="true" file="icon-check.svg"></icon> + <span>Copied</span> + </span> + </button> + <button + type="button" + class="btn btn-primary"> + <a ng-href="data:text/json;charset=utf-8,{{ csrCode }}" + download="csrCode.txt" + class="add-csr__text-download"> + Download + </a> + </button> + </div> + </div>
\ No newline at end of file diff --git a/app/access-control/controllers/certificate-modal-csr-gen.html b/app/access-control/controllers/certificate-modal-csr-gen.html new file mode 100644 index 0000000..bde00cf --- /dev/null +++ b/app/access-control/controllers/certificate-modal-csr-gen.html @@ -0,0 +1,351 @@ +<div class="uib-modal__content"> + <div class="modal-header"> + <h2 class="modal-title" id="modal_label"> + Generate a Certificate Signing Request (CSR) + </h2> + <button + type="button" + class="btn btn--close" + ng-click="$dismiss()" + aria-label="close"> + <icon file="icon-close.svg" aria-hidden="true"></icon> + </button> + </div> + <form name="addCsrForm" id="addCsrForm" novalidate> + <div class="modal-body"> + <div class="row"> + <fieldset class="column large-8 add-csr__section"> + <legend class="content-label">General</legend> + <div class="row"> + <div class="column large-6"> + <label for="certType" class="content-label"> + Certificate Type * + </label> + <select + class="add-csr__select" + id="certType" + name="certType" + ng-model="newCSR.certificateCollection" + required> + <option + class="courier-bold" + ng-value="default" + ng-model="selectOption"> + Select an option + </option> + <!-- Do not show CA certificate as an option. + Only a certificate authority can generate a CA certificate + (known as TrustStore Certificate in Redfish) --> + <option + class="courier-bold" + ng-value="type" + ng-repeat="type in allCertificateTypes" + ng-if="type.Description !== 'TrustStore Certificate'"> + {{ type.name }} + </option> + </select> + <div + ng-messages="addCsrForm.certType.$error" + class="form-error" + ng-class="{'visible' : addCsrForm.certType.$touched}"> + <p ng-message="required">Field is required</p> + </div> + </div> + <div class="column large-6"> + <label for="countryCode" class="content-label">Country *</label> + <select + class="add-csr__select" + id="countryCode" + name="countryCode" + ng-model="newCSR.countryCode" + required> + <option + class="courier-bold" + ng-value="" + ng-model="selectOption"> + Select an option + </option> + <option + class="courier-bold" + ng-value="country" + ng-repeat="country in countryList"> + {{ country.Name }} + </option> + </select> + <div + ng-messages="addCsrForm.countryCode.$error" + class="form-error" + ng-class="{'visible' : addCsrForm.countryCode.$touched}"> + <p ng-message="required">Field is required</p> + </div> + </div> + <div class="column large-6"> + <label for="state" class="content-label">State *</label> + <input + class="add-csr__input" + ng-model="newCSR.state" + type="text" + id="state" + name="state" + required/> + <div + ng-messages="addCsrForm.state.$error" + class="form-error" + ng-class="{'visible' : addCsrForm.state.$touched}"> + <p ng-message="required">Field is required</p> + </div> + </div> + <div class="column large-6"> + <label for="city" class="content-label">City *</label> + <input + class="add-csr__input" + id="city" + name="city" + ng-model="newCSR.city" + type="text" + required/> + <div + ng-messages="addCsrForm.city.$error" + class="form-error" + ng-class="{'visible' : addCsrForm.city.$touched}"> + <p ng-message="required">Field is required</p> + </div> + </div> + <div class="column large-6"> + <label for="companyName" class="content-label"> + Company Name * + </label> + <input + class="add-csr__input" + type="text" + ng-model="newCSR.organization" + id="companyName" + name="companyName" + required/> + <div + ng-messages="addCsrForm.companyName.$error" + class="form-error" + ng-class="{'visible' : addCsrForm.companyName.$touched}"> + <p ng-message="required">Field is required</p> + </div> + </div> + <div class="column large-6"> + <label for="companyUnit" class="content-label"> + Company Unit * + </label> + <input + class="add-csr__input" + ng-model="newCSR.companyUnit" + name="companyUnit" + id="companyUnit" + type="text" + required/> + <div + ng-messages="addCsrForm.companyUnit.$error" + class="form-error" + ng-class="{'visible' : addCsrForm.companyUnit.$touched}"> + <p ng-message="required">Field is required</p> + </div> + </div> + <div class="column large-6"> + <label for="commonName" class="content-label"> + Common Name * + </label> + <input + class="add-csr__input" + ng-model="newCSR.commonName" + name="commonName" + type="text" + id="commonName" + required/> + <div + ng-messages="addCsrForm.commonName.$error" + class="form-error" + ng-class="{'visible' : addCsrForm.commonName.$touched}"> + <p ng-message="required">Field is required</p> + </div> + </div> + <div class="column large-6"> + <label for="challengePassword" class="content-label"> + Challenge Password + </label> + <input + class="add-csr__input-no-validation" + id="challengePassword" + ng-model="newCSR.challengePassword" + type="text"/> + </div> + <div class="column large-6"> + <label for="contactPerson" class="content-label"> + Contact Person + </label> + <input + class="add-csr__input-no-validation" + id="contactPerson" + ng-model="newCSR.contactPerson" + type="text"/> + </div> + <div class="column large-6"> + <label for="emailAddress" class="content-label"> + Email Address + </label> + <input + class="add-csr__input-no-validation" + id="emailAddress" + ng-model="newCSR.emailAddress" + type="text"/> + </div> + <div class="column large-6"> + <label + id="alternate-name-label" + for="alternateName" + class="content-label"> + Alternate Name + </label> + <input + class="add-csr__input-no-validation" + ng-model="newCSR.firstAlternativeName" + id="alternateName" + name="alternativeName" + type="text"/> + <div + class="add-csr__additional-alt-names" + ng-repeat="name in names"> + <input + id="alternate-name-input-{{ $index }}" + aria-describedby="alternate-name-label" + class="add-csr__input-no-validation" + ng-model="name.Value" + type="text"/> + <button + aria-label="Delete alternate name field" + aria-controls="alternate-name-input-{{ $index }}" + class="btn btn-tertiary add-csr__alt-name-delete-btn" + ng-click="deleteOptionalRow($index)" + ng-disabled="multiSelected"> + <icon aria-hidden="true" file="icon-trashcan.svg"></icon> + </button> + </div> + </div> + <div class="column large-6"> + <button + class="btn btn-tertiary add-csr__alt-name-add-btn" + ng-click="addOptionalRow()"> + <icon file="icon-plus.svg" aria-hidden="true"></icon> + Add another alternate name + </button> + </div> + </div> + </fieldset> + <fieldset + class="column medium-12 large-4 add-csr__section add-csr__section--border"> + <legend class="content-label">Private key</legend> + <div class="add-csr__container-private-key"> + <div class="add-csr__container-private-key"> + <label for="keyPairAlgorithm" class="content-label"> + Key Pair Algorithm * + </label> + <select + class="add-csr__select" + ng-model="newCSR.keyPairAlgorithm" + id="keyPairAlgorithm" + name="keyPairAlgorithm" + required> + <option + class="courier-bold" + ng-value="" + ng-model="selectOption"> + Select an option + </option> + <option + class="courier-bold" + ng-value="data" + ng-repeat="data in keyPairAlgorithm"> + {{ data }} + </option> + </select> + <div + ng-messages="addCsrForm.keyPairAlgorithm.$error" + class="form-error" + ng-class="{'visible' : addCsrForm.keyPairAlgorithm.$touched}"> + <p ng-message="required">Field is required</p> + </div> + </div> + <div ng-if="newCSR.keyPairAlgorithm == 'EC'"> + <div class="large-12"> + <label for="keyCurveId" class="content-label"> + Key Curve ID + </label> + <select + class="add-csr__select" + ng-model="newCSR.keyCurveId" + id="keyCurveId" + name="keyCurveId" + required> + <option class="courier-bold" ng-value="">None</option> + <option + class="courier-bold" + ng-value="data" + ng-repeat="data in keyCurveId"> + {{ data }} + </option> + </select> + <div + ng-messages="addCsrForm.keyCurveId.$error" + class="form-error" + ng-class="{'visible' : addCsrForm.keyCurveId.$touched}"> + <p ng-message="required">Field is required</p> + </div> + </div> + </div> + <div ng-if="newCSR.keyPairAlgorithm =='RSA'"> + <div class="large-12"> + <label for="keyBitLength" class="content-label"> + Key Bit Length * + </label> + <select + class="add-csr__select" + ng-model="newCSR.keyBitLength" + id="keyBitLength" + name="keyBitLength" + required> + <option class="courier-bold" ng-value=""> + Select an option + </option> + <option + class="courier-bold" + ng-value="data" + ng-repeat="data in keyBitLength"> + {{ data }} + </option> + </select> + <div + ng-messages="addCsrForm.keyBitLength.$error" + class="form-error" + ng-class="{'visible' : addCsrForm.keyBitLength.$touched}"> + <p ng-message="required">Field is required</p> + </div> + </div> + </div> + </div> + </fieldset> + </div> + </div> + <div class="modal-footer"> + <button + type="reset" + class="btn btn-secondary" + ng-click="$dismiss()"> + Cancel + </button> + <button + type="submit" + class="btn btn-primary" + ng-click="getCSRCode();addCsrForm.$setUntouched();$dismiss()" + ng-disabled="addCsrForm.$invalid"> + Generate CSR + </button> + </div> + </form> + </div> +</div> diff --git a/app/access-control/styles/certificate.scss b/app/access-control/styles/certificate.scss index a7c57f2..2d882f4 100644 --- a/app/access-control/styles/certificate.scss +++ b/app/access-control/styles/certificate.scss @@ -1,7 +1,7 @@ +// TODO: replace cert table with table component .certificate__table { border-left: 1px solid $border-color-01; border-right: 1px solid $border-color-01; - margin-top: 0.5em; .table__row-header { width: 100%; border-bottom: 1px solid $border-color-01; @@ -104,60 +104,22 @@ padding: 0.8em 0.8em 0.8em 1.5em; } .upload__certificate { - border-top: 1px solid $border-color-01; width: 100%; background: $background-02; - padding: 0.8em; - } -} -.certificate__upload-chooser { - background: $background-02; -} - - -.certificate__close-modal { - float: right; - position: relative; - bottom: 1rem; - left: 2rem; -} -.certificate__table__icon { - margin-left: 1.5em; - margin-bottom: .4em; -} - -.add__certificate__modal { - select { - margin-bottom: 0; - } - .file__upload { - margin-bottom: 2em; - } - .select__new-label { - margin-bottom: 1em; - } - .select__new-button { - font-size: 1.2em; + padding: 0.8em 1.8em 0.8em 0.8em; + height: 4rem; + display: flex; } - .file__name { - background-color: $background-02; - padding: 0.5em; + .replace-btn { + margin-left: auto; } } -.add-certificate__section { - padding-left: 0; -} - -// Combinator needed to match specificity -// of default modal settings -.modal.add-csr__modal { - width: 100%; - max-height: 100vh; - overflow-y: auto; - z-index: 1001; +.cert-dropdown{ + margin-bottom: 0; } +// TODO: Update CSR modal with global form-field styles .add-csr__section:first-of-type { padding-left: 0; } @@ -171,11 +133,6 @@ } } -.add-csr__section-title { - margin-bottom: 1rem; - font-weight: 700; -} - .add-csr__section--border { @media (min-width: 640px) { padding-left: 2rem; @@ -183,11 +140,6 @@ } } -.add-csr__label { - white-space: nowrap; - display: inline-block; -} - .add-csr__text-helper { color: $base-02--02; font-weight: 400; @@ -224,10 +176,8 @@ input.add-csr__input-no-validation { } .add-csr__alt-name-delete-btn { - width: 20px; - height: 30px; - padding: 0; - + height: 34px; + padding: 0 0 0 1rem; icon { margin-right: 0; } diff --git a/app/common/directives/certificate.html b/app/common/directives/certificate.html index 1a7d091..cf7b46d 100644 --- a/app/common/directives/certificate.html +++ b/app/common/directives/certificate.html @@ -1,4 +1,5 @@ -<div class="table__row-value row column"> +<!-- TODO: Replace table with resusable table component --> +<div class="table__row-value row"> <div class="certificate__type-cell bold"> {{getCertificateName(cert.Description)}} </div> @@ -39,35 +40,55 @@ {{cert.ValidNotAfter | date:medium}} </div> <div class="certificate__buttons-cell"> - <button type="button" ng-click="cert.upload = true" aria-label="Replace certificate" class="btn btn-tertiary certificate__button"> + <button + type="button" + ng-click="cert.upload = true" + aria-label="Replace certificate" + class="btn btn-tertiary certificate__button"> <icon file="icon-replace.svg" aria-hidden="true"></icon> </button> </div> <div ng-show="cert.upload === true" class="upload__certificate"> - <div class="certificate__upload-chooser row"> - <div class="small-1 column"> - <button type="button" ng-click="cert.upload=false" aria-label="close replace certificate upload form"> - <icon file="icon-close.svg" aria-hidden="true"></icon> - </button> - </div> - <div class="small-2 column"> - <label> - <input id="upload_{{cert.Description + cert.Id}}" type="file" file="cert.file" class="input-file" /> - <span class="btn btn-secondary">Choose file</span> - </label> - </div> - <div class="small-6 column"> - <span ng-if="!cert.file">No file selected</span> - <span>{{cert.file.name}}</span> - <button type="reset" ng-if="cert.file.name" ng-click="cert.file = '';" aria-label="remove selected file"> - <icon file="icon-close.svg" aria-hidden="true"></icon> - </button> - </div> - <div class="small-3 column"> - <button type="submit" ng-class="{disabled:!cert.file}" class="btn btn-primary" ng-click="replaceCertificate(cert)"> - Replace - </button> - </div> + <div class="close-btn"> + <button + type="button" + ng-click="cert.upload=false" + aria-label="close replace certificate upload form"> + <icon file="icon-close.svg" aria-hidden="true"></icon> + </button> + </div> + <div class="file-upload"> + <label + for="upload_{{ cert.Description + cert.Id }}" + class="file-upload-btn btn btn-secondary" + tabindex="0">Choose file</label> + <input + name="upload_{{ cert.Description + cert.Id }}" + id="upload_{{ cert.Description + cert.Id }}" + type="file" + file="cert.file" + class="file-upload-input"/> + </div> + <div class="file-upload-container file-upload-field"> + <span ng-if="!cert.file">No file selected</span> + <span>{{ cert.file.name }}</span> + <button + type="reset" + class="btn file-upload-reset" + ng-if="cert.file.name" + ng-click="cert.file = '';" + aria-label="remove selected file"> + <icon file="icon-close.svg" aria-hidden="true"></icon> + </button> + </div> + <div class="replace-btn"> + <button + type="submit" + ng-disabled="!cert.file" + class="btn btn-primary" + ng-click="replaceCertificate(cert)"> + Replace + </button> </div> </div> -</div>
\ No newline at end of file +</div> diff --git a/app/common/styles/elements/file-upload.scss b/app/common/styles/elements/file-upload.scss new file mode 100644 index 0000000..4704d6d --- /dev/null +++ b/app/common/styles/elements/file-upload.scss @@ -0,0 +1,56 @@ +/** + * Used for file upload + * Markup + <div class="file-upload"> + <label for="upload_cert_new" class="file-upload-btn btn btn-secondary" tabindex="0">Choose file</label> + <input + name="upload_cert_new" + id="upload_cert_new" + type="file" + file="newCertificate.file" + class="file-upload-input"/> + <div class="form__field file-upload-container"> + <span ng-hide="newCertificate.file">No file selected</span> + <span>{{ newCertificate.file.name }}</span> + <button + type="reset" + class="btn file-upload-reset" + ng-if="newCertificate.file.name" + ng-click="newCertificate.file = '';" + aria-label="remove selected file"> + <icon file="icon-close.svg" aria-hidden="true"></icon> + </button> + </div> + </div> +*/ + +// Choose/upload button +.file-upload-input { + width: 1px; + height: 1px; + opacity: 0; + overflow: hidden; + position: absolute; + z-index: -1; +} + +.file-upload-input:focus { + outline: 0.2rem solid $base-02--04; +} + +// File name of uploaded file field +.file-upload-container { + background: $background-02; + padding: 0.5rem; + span { + padding-left: .5rem; + } + .file-upload-reset { + float: right; + margin-top: -.5rem; + icon { + margin-right: -1.7em; + margin-left: -1.5em; + } + } +} diff --git a/app/common/styles/elements/index.scss b/app/common/styles/elements/index.scss index b1df113..9fee615 100644 --- a/app/common/styles/elements/index.scss +++ b/app/common/styles/elements/index.scss @@ -2,7 +2,7 @@ @import "toggle-filter"; @import "alerts"; @import "inline-confirm"; -@import "input-file"; +@import "file-upload"; @import "accordion"; @import "loader"; @import "content-search"; diff --git a/app/common/styles/elements/input-file.scss b/app/common/styles/elements/input-file.scss deleted file mode 100644 index 3f32b2b..0000000 --- a/app/common/styles/elements/input-file.scss +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Used for file upload "choose file" button - * Markup (example for certificate upload) - <label> - <input - id="upload_{{cert.Description + cert.Id}}" - type="file" - file="cert.file" - class="input-file" /> - <span class="btn btn-secondary">Choose file</span> - </label> -*/ - -.input-file { - width: 0.1px; - height: 0.1px; - opacity: 0; - overflow: hidden; - position: absolute; - z-index: -1; -} - -.input-file:focus + span { - outline: 0.2rem solid $box-shadow-color; -}
\ No newline at end of file diff --git a/app/common/styles/elements/modals.scss b/app/common/styles/elements/modals.scss index 263d285..3132934 100644 --- a/app/common/styles/elements/modals.scss +++ b/app/common/styles/elements/modals.scss @@ -83,9 +83,45 @@ } } +/** + * Markup for bootstrap modal + <div class="uib-modal__content"> + <div class="modal-header"> + <h2 class="modal-title" id="modal_label"> + Title here + </h2> + <button + type="button" + class="btn btn--close" + ng-click="$dismiss()" + aria-label="close"> + <icon file="icon-close.svg" aria-hidden="true"></icon> + </button> + </div> + <div class="modal-body"> + Body content + </div> + <div class="modal-footer"> + <button + type="button" + class="btn btn-secondary" + ng-click="$dismiss()"> + Cancel + </button> + <button + type="submit" + class="btn btn-primary" + ng-click="function()"> + Save + </button> + </div> + </div> +*/ + .uib-modal.fade.in { opacity: 1; } + .uib-modal.in .modal-dialog { transform: translate(0, 10vh); margin-top: 50px; @@ -99,14 +135,10 @@ } } -.uib-modal .modal-dialog { - // override bootstrap max-width set at 500px - max-width: 550px; -} - .modal-backdrop.in { opacity: 0.5; } + .uib-modal__content { padding: 1em; .modal-header { |