mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-30 18:17:55 +00:00 
			
		
		
		
	UI: PKI show missing info on generated certificate (#21635)
This commit is contained in:
		
							
								
								
									
										3
									
								
								changelog/21635.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								changelog/21635.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | ```release-note:bug  | ||||||
|  | ui: Adds missing values to details view after generating PKI certificate | ||||||
|  | ``` | ||||||
| @@ -87,8 +87,8 @@ export default class PkiCertificateBaseModel extends Model { | |||||||
|   @attr('string', { masked: true }) certificate; |   @attr('string', { masked: true }) certificate; | ||||||
|   @attr('number') expiration; |   @attr('number') expiration; | ||||||
|   @attr('string', { label: 'Issuing CA', masked: true }) issuingCa; |   @attr('string', { label: 'Issuing CA', masked: true }) issuingCa; | ||||||
|   @attr('string') privateKey; // only returned for type=exported |   @attr('string', { masked: true }) privateKey; // only returned for type=exported and /issue | ||||||
|   @attr('string') privateKeyType; // only returned for type=exported |   @attr('string') privateKeyType; // only returned for type=exported and /issue | ||||||
|   @attr('number', { formatDate: true }) revocationTime; |   @attr('number', { formatDate: true }) revocationTime; | ||||||
|   @attr('string') serialNumber; |   @attr('string') serialNumber; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -21,7 +21,18 @@ const generateFromRole = [ | |||||||
|     ], |     ], | ||||||
|   }, |   }, | ||||||
| ]; | ]; | ||||||
| @withFormFields(null, generateFromRole) | // Extra fields returned on the /issue endpoint | ||||||
|  | const certDisplayFields = [ | ||||||
|  |   'certificate', | ||||||
|  |   'commonName', | ||||||
|  |   'revocationTime', | ||||||
|  |   'serialNumber', | ||||||
|  |   'caChain', | ||||||
|  |   'issuingCa', | ||||||
|  |   'privateKey', | ||||||
|  |   'privateKeyType', | ||||||
|  | ]; | ||||||
|  | @withFormFields(certDisplayFields, generateFromRole) | ||||||
| export default class PkiCertificateGenerateModel extends PkiCertificateBaseModel { | export default class PkiCertificateGenerateModel extends PkiCertificateBaseModel { | ||||||
|   getHelpUrl(backend) { |   getHelpUrl(backend) { | ||||||
|     return `/v1/${backend}/issue/example?help=1`; |     return `/v1/${backend}/issue/example?help=1`; | ||||||
|   | |||||||
| @@ -18,10 +18,23 @@ | |||||||
|   </ToolbarActions> |   </ToolbarActions> | ||||||
| </Toolbar> | </Toolbar> | ||||||
|  |  | ||||||
|  | {{#if @model.privateKey}} | ||||||
|  |   <div class="has-top-margin-m"> | ||||||
|  |     <Hds::Alert data-test-cert-detail-next-steps @type="inline" @color="highlight" class="has-bottom-margin-s" as |A|> | ||||||
|  |       <A.Title>Next steps</A.Title> | ||||||
|  |       <A.Description> | ||||||
|  |         The | ||||||
|  |         <code>private_key</code> | ||||||
|  |         is only available once. Make sure you copy and save it now. | ||||||
|  |       </A.Description> | ||||||
|  |     </Hds::Alert> | ||||||
|  |   </div> | ||||||
|  | {{/if}} | ||||||
|  |  | ||||||
| {{#each @model.formFields as |field|}} | {{#each @model.formFields as |field|}} | ||||||
|   {{#if (eq field.name "certificate")}} |   {{#if field.options.masked}} | ||||||
|     <InfoTableRow @label="Certificate"> |     <InfoTableRow @label={{or field.options.label (capitalize (humanize (dasherize field.name)))}}> | ||||||
|       <MaskedInput @value={{@model.certificate}} @displayOnly={{true}} @allowCopy={{true}} /> |       <MaskedInput @value={{or (get @model field.name) null}} @displayOnly={{true}} @allowCopy={{true}} /> | ||||||
|     </InfoTableRow> |     </InfoTableRow> | ||||||
|   {{else if (eq field.name "serialNumber")}} |   {{else if (eq field.name "serialNumber")}} | ||||||
|     <InfoTableRow @label="Serial number"> |     <InfoTableRow @label="Serial number"> | ||||||
|   | |||||||
| @@ -40,7 +40,25 @@ module('Integration | Component | pki | Page::PkiCertificateDetails', function ( | |||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
|     }); |     }); | ||||||
|  |     store.pushPayload('pki/certificate/generate', { | ||||||
|  |       modelName: 'pki/certificate/generate', | ||||||
|  |       data: { | ||||||
|  |         certificate: '-----BEGIN CERTIFICATE-----', | ||||||
|  |         ca_chain: '-----BEGIN CERTIFICATE-----', | ||||||
|  |         issuer_ca: '-----BEGIN CERTIFICATE-----', | ||||||
|  |         private_key: '-----BEGIN PRIVATE KEY-----', | ||||||
|  |         private_key_type: 'rsa', | ||||||
|  |         common_name: 'example.com Intermediate Authority', | ||||||
|  |         issue_date: 1673540867000, | ||||||
|  |         serial_number: id, | ||||||
|  |         parsed_certificate: { | ||||||
|  |           not_valid_after: 1831220897000, | ||||||
|  |           not_valid_before: 1673540867000, | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|     this.model = store.peekRecord('pki/certificate/base', id); |     this.model = store.peekRecord('pki/certificate/base', id); | ||||||
|  |     this.generatedModel = store.peekRecord('pki/certificate/generate', id); | ||||||
|  |  | ||||||
|     this.server.post('/sys/capabilities-self', () => ({ |     this.server.post('/sys/capabilities-self', () => ({ | ||||||
|       data: { |       data: { | ||||||
| @@ -50,7 +68,7 @@ module('Integration | Component | pki | Page::PkiCertificateDetails', function ( | |||||||
|     })); |     })); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test('it should render actions and fields', async function (assert) { |   test('it should render actions and fields for base cert', async function (assert) { | ||||||
|     assert.expect(6); |     assert.expect(6); | ||||||
|  |  | ||||||
|     this.server.post('/pki/revoke', (schema, req) => { |     this.server.post('/pki/revoke', (schema, req) => { | ||||||
| @@ -90,6 +108,56 @@ module('Integration | Component | pki | Page::PkiCertificateDetails', function ( | |||||||
|     assert.dom('[data-test-value-div="Revocation time"]').exists('Revocation time is displayed'); |     assert.dom('[data-test-value-div="Revocation time"]').exists('Revocation time is displayed'); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|  |   test('it should render actions and fields for generated cert', async function (assert) { | ||||||
|  |     assert.expect(10); | ||||||
|  |  | ||||||
|  |     this.server.post('/pki/revoke', (schema, req) => { | ||||||
|  |       const data = JSON.parse(req.requestBody); | ||||||
|  |       assert.strictEqual( | ||||||
|  |         data.serial_number, | ||||||
|  |         this.model.serialNumber, | ||||||
|  |         'Revoke request made with serial number' | ||||||
|  |       ); | ||||||
|  |       return { | ||||||
|  |         data: { | ||||||
|  |           revocation_time: 1673972804, | ||||||
|  |           revocation_time_rfc3339: '2023-01-17T16:26:44.960933411Z', | ||||||
|  |         }, | ||||||
|  |       }; | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     await render(hbs`<Page::PkiCertificateDetails @model={{this.generatedModel}} />`, { owner: this.engine }); | ||||||
|  |     assert.dom('[data-test-cert-detail-next-steps]').exists('Private key next steps warning shows'); | ||||||
|  |     assert | ||||||
|  |       .dom('[data-test-component="info-table-row"]') | ||||||
|  |       .exists({ count: 9 }, 'Correct number of fields render when certificate has not been revoked'); | ||||||
|  |     assert | ||||||
|  |       .dom('[data-test-value-div="Certificate"] [data-test-masked-input]') | ||||||
|  |       .exists('Masked input renders for certificate'); | ||||||
|  |     assert.dom('[data-test-value-div="Serial number"] code').exists('Serial number renders as monospace'); | ||||||
|  |     assert | ||||||
|  |       .dom('[data-test-value-div="CA Chain"] [data-test-masked-input]') | ||||||
|  |       .exists('CA Chain shows with masked value'); | ||||||
|  |     assert | ||||||
|  |       .dom('[data-test-value-div="Issuing CA"] [data-test-masked-input]') | ||||||
|  |       .exists('Issuing CA shows with masked value'); | ||||||
|  |     assert | ||||||
|  |       .dom('[data-test-value-div="Private key"] [data-test-masked-input]') | ||||||
|  |       .exists('Private key shows with masked value'); | ||||||
|  |  | ||||||
|  |     await click('[data-test-pki-cert-download-button]'); | ||||||
|  |     const { serialNumber, certificate } = this.model; | ||||||
|  |     assert.ok( | ||||||
|  |       this.downloadSpy.calledWith(serialNumber.replace(/(\s|:)+/g, '-'), certificate), | ||||||
|  |       'Download pem method called with correct args' | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     await click('[data-test-confirm-action-trigger]'); | ||||||
|  |     await click('[data-test-confirm-button]'); | ||||||
|  |  | ||||||
|  |     assert.dom('[data-test-value-div="Revocation time"]').exists('Revocation time is displayed'); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|   test('it should render back button', async function (assert) { |   test('it should render back button', async function (assert) { | ||||||
|     assert.expect(1); |     assert.expect(1); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Chelsea Shaw
					Chelsea Shaw