diff options
Diffstat (limited to 'install/static')
-rwxr-xr-x | install/static/certificate.js | 78 | ||||
-rw-r--r-- | install/static/service.js | 326 | ||||
-rw-r--r-- | install/static/test/data/cert_request.json | 12 | ||||
-rw-r--r-- | install/static/test/data/cert_show.json | 17 | ||||
-rw-r--r-- | install/static/test/data/service_show.json | 2 |
5 files changed, 249 insertions, 186 deletions
diff --git a/install/static/certificate.js b/install/static/certificate.js index 4302e2f8..a688fe81 100755 --- a/install/static/certificate.js +++ b/install/static/certificate.js @@ -53,38 +53,15 @@ function certificate_parse_dn(dn) { return result; } -function certificate_confirmation_dialog(spec) { - var that = {}; - spec = spec || {}; - - var dialog = $('<div/>', { - 'title': spec.title - }); - - dialog.append(spec.message); - - that.open = function() { - dialog.dialog({ - modal: true, - width: 300, - height: 150, - buttons: { - 'Close': function() { - dialog.dialog('destroy'); - } - } - }); - }; - - return that; -} - function certificate_get_dialog(spec) { var that = {}; spec = spec || {}; + that.title = spec.title || ''; + that.usercertificate = spec.usercertificate || ''; + var dialog = $('<div/>', { - 'title': spec.title + 'title': that.title }); var textarea = $('<textarea/>', { @@ -94,7 +71,7 @@ function certificate_get_dialog(spec) { textarea.val( BEGIN_CERTIFICATE_REQUEST+'\n'+ - spec.usercertificate+'\n'+ + that.usercertificate+'\n'+ END_CERTIFICATE_REQUEST ); @@ -118,8 +95,11 @@ function certificate_revoke_dialog(spec) { var that = {}; spec = spec || {}; + that.title = spec.title || ''; + that.revoke = spec.revoke; + var dialog = $('<div/>', { - 'title': spec.title + 'title': that.title }); var table = $('<table/>').appendTo(dialog); @@ -160,8 +140,8 @@ function certificate_revoke_dialog(spec) { 'Revoke': function() { var values = {}; values['reason'] = select.val(); - if (spec.revoke) { - spec.revoke(values); + if (that.revoke) { + that.revoke(values); } dialog.dialog('destroy'); }, @@ -179,8 +159,11 @@ function certificate_restore_dialog(spec) { var that = {}; spec = spec || {}; + that.title = spec.title || ''; + that.restore = spec.restore; + var dialog = $('<div/>', { - 'title': spec.title + 'title': that.title }); dialog.append( @@ -195,8 +178,8 @@ function certificate_restore_dialog(spec) { buttons: { 'Restore': function() { var values = {}; - if (spec.restore) { - spec.restore(values); + if (that.restore) { + that.restore(values); } dialog.dialog('destroy'); }, @@ -214,11 +197,17 @@ function certificate_view_dialog(spec) { var that = {}; spec = spec || {}; + that.title = spec.title || ''; that.subject = certificate_parse_dn(spec.subject); + that.serial_number = spec.serial_number || ''; that.issuer = certificate_parse_dn(spec.issuer); + that.issued_on = spec.issued_on || ''; + that.expires_on = spec.expires_on || ''; + that.md5_fingerprint = spec.md5_fingerprint || ''; + that.sha1_fingerprint = spec.sha1_fingerprint || ''; var dialog = $('<div/>', { - 'title': spec.title + 'title': that.title }); var table = $('<table/>').appendTo(dialog); @@ -250,7 +239,7 @@ function certificate_view_dialog(spec) { tr = $('<tr/>').appendTo(table); $('<td>Serial Number:</td>').appendTo(tr); $('<td/>', { - 'html': spec.serial_number + 'html': that.serial_number }).appendTo(tr); tr = $('<tr/>').appendTo(table); @@ -286,13 +275,13 @@ function certificate_view_dialog(spec) { tr = $('<tr/>').appendTo(table); $('<td>Issued On:</td>').appendTo(tr); $('<td/>', { - 'html': spec.issued_on + 'html': that.issued_on }).appendTo(tr); tr = $('<tr/>').appendTo(table); $('<td>Expires On:</td>').appendTo(tr); $('<td/>', { - 'html': spec.expires_on + 'html': that.expires_on }).appendTo(tr); tr = $('<tr/>').appendTo(table); @@ -304,13 +293,13 @@ function certificate_view_dialog(spec) { tr = $('<tr/>').appendTo(table); $('<td>SHA1 Fingerprint:</td>').appendTo(tr); $('<td/>', { - 'html': spec.sha1_fingerprint + 'html': that.sha1_fingerprint }).appendTo(tr); tr = $('<tr/>').appendTo(table); $('<td>MD5 Fingerprint:</td>').appendTo(tr); $('<td/>', { - 'html': spec.md5_fingerprint + 'html': that.md5_fingerprint }).appendTo(tr); that.open = function() { @@ -333,8 +322,11 @@ function certificate_request_dialog(spec) { var that = {}; spec = spec || {}; + that.title = spec.title || ''; + that.request = spec.request; + var dialog = $('<div/>', { - 'title': spec.title + 'title': that.title }); dialog.append('Copy and paste the Base64-encoded CSR below:'); @@ -365,8 +357,8 @@ function certificate_request_dialog(spec) { $.trim(request)+'\n'+ END_CERTIFICATE_REQUEST+'\n'; values['request'] = request; - if (spec.request) { - spec.request(values); + if (that.request) { + that.request(values); } dialog.dialog('destroy'); }, diff --git a/install/static/service.js b/install/static/service.js index d688b9ad..5e37f6a7 100644 --- a/install/static/service.js +++ b/install/static/service.js @@ -20,6 +20,10 @@ /* REQUIRES: ipa.js, details.js, search.js, add.js, entity.js */ +var SERVICE_CERTIFICATE_VALID = 1; +var SERVICE_CERTIFICATE_REVOKED = 2; +var SERVICE_CERTIFICATE_MISSING = 3; + ipa_entity_set_search_definition('service', [ ['krbprincipalname', 'Principal', null], ['quick_links', 'Quick Links', ipa_entity_quick_links] @@ -88,146 +92,168 @@ function service_provisioning_status_load(container, dt, result) { // skip provisioning_status } -function service_usercertificate_get(result) { - - var usercertificate = result['usercertificate']; - if (!usercertificate) { - alert('Service has no usercertificate.'); - return; - } +function service_usercertificate_load(container, dt, result) { - var krbprincipalname = result['krbprincipalname'][0]; - var service_name = krbprincipalname.replace(/@.*$/, ''); + var li1, li2, li3; - var dialog = certificate_get_dialog({ - 'title': 'Certificate for Service '+service_name, - 'usercertificate': usercertificate[0].__base64__ - }); + function set_status(status, revocation_reason) { + li1.css('list-style-type', status == SERVICE_CERTIFICATE_VALID ? 'disc' : 'circle'); + li2.css('list-style-type', status == SERVICE_CERTIFICATE_REVOKED ? 'disc' : 'circle'); + li3.css('list-style-type', status == SERVICE_CERTIFICATE_MISSING ? 'disc' : 'circle'); - dialog.open(); -} - -function service_usercertificate_view(result) { + $('#revocation_reason').html(revocation_reason ? CRL_REASON[revocation_reason] : ''); + $('#restore_button').css('visibility', revocation_reason == 6 ? 'visible' : 'hidden') + } - var usercertificate = result['usercertificate']; - if (!usercertificate) { - alert('Service has no usercertificate.'); - return; + function check_status(serial_number) { + ipa_cmd( + 'cert_show', + [serial_number], + { }, + function(data, text_status, xhr) { + var revocation_reason = data.result.result.revocation_reason; + if (revocation_reason) { + set_status(SERVICE_CERTIFICATE_REVOKED, revocation_reason); + } else { + set_status(SERVICE_CERTIFICATE_VALID); + } + } + ); } - var krbprincipalname = result['krbprincipalname'][0]; - var service_name = krbprincipalname.replace(/@.*$/, ''); - - var dialog = certificate_view_dialog({ - 'title': 'Certificate for Service '+service_name, - 'subject': result['subject'], - 'serial_number': result['serial_number'], - 'issuer': result['issuer'], - 'issued_on': result['valid_not_before'], - 'expires_on': result['valid_not_after'], - 'md5_fingerprint': result['md5_fingerprint'], - 'sha1_fingerprint': result['sha1_fingerprint'] - }); - - dialog.open(); -} + function get_certificate(result) { -function service_usercertificate_revoke(result) { + var usercertificate = result['usercertificate']; + if (!usercertificate) { + set_status(SERVICE_CERTIFICATE_MISSING); + return; + } - var usercertificate = result['usercertificate']; - if (!usercertificate) { - alert('Service has no usercertificate.'); - return; - } + var krbprincipalname = result['krbprincipalname'][0]; + var service_name = krbprincipalname.replace(/@.*$/, ''); - var krbprincipalname = result['krbprincipalname'][0]; - var service_name = krbprincipalname.replace(/@.*$/, ''); + var dialog = certificate_get_dialog({ + 'title': 'Certificate for Service '+service_name, + 'usercertificate': usercertificate[0].__base64__ + }); - var serial_number = result['serial_number']; + dialog.open(); + } - var dialog = certificate_revoke_dialog({ - 'title': 'Revoke Certificate for Service '+service_name, - 'revoke': function(values) { - var reason = values['reason']; + function view_certificate(result) { - ipa_cmd( - 'cert_revoke', - [serial_number], - { - 'revocation_reason': reason - }, - function(data, text_status, xhr) { - var dialog = certificate_confirmation_dialog({ - title: 'Success', - message: 'Certificate has been revoked successfully.' - }); - dialog.open(); - } - ); + var usercertificate = result['usercertificate']; + if (!usercertificate) { + set_status(SERVICE_CERTIFICATE_MISSING); + return; } - }); - - dialog.open(); -} -function service_usercertificate_restore(result) { - - var usercertificate = result['usercertificate']; - if (!usercertificate) { - alert('Service has no usercertificate.'); - return; + var krbprincipalname = result['krbprincipalname'][0]; + var service_name = krbprincipalname.replace(/@.*$/, ''); + + var dialog = certificate_view_dialog({ + 'title': 'Certificate for Service '+service_name, + 'subject': result['subject'], + 'serial_number': result['serial_number'], + 'issuer': result['issuer'], + 'issued_on': result['valid_not_before'], + 'expires_on': result['valid_not_after'], + 'md5_fingerprint': result['md5_fingerprint'], + 'sha1_fingerprint': result['sha1_fingerprint'] + }); + + dialog.open(); } - var krbprincipalname = result['krbprincipalname'][0]; - var service_name = krbprincipalname.replace(/@.*$/, ''); - - var serial_number = result['serial_number']; + function revoke_certificate(result) { - var dialog = certificate_restore_dialog({ - 'title': 'Restore Certificate for Service '+service_name, - 'restore': function(values) { - ipa_cmd( - 'cert_remove_hold', - [serial_number], - { }, - function(data, text_status, xhr) { - var dialog = certificate_confirmation_dialog({ - title: 'Success', - message: 'Certificate has been restored successfully.' - }); - dialog.open(); - } - ); + var usercertificate = result['usercertificate']; + if (!usercertificate) { + set_status(SERVICE_CERTIFICATE_MISSING); + return; } - }); - dialog.open(); -} + var krbprincipalname = result['krbprincipalname'][0]; + var service_name = krbprincipalname.replace(/@.*$/, ''); + + var serial_number = result['serial_number']; + + var dialog = certificate_revoke_dialog({ + 'title': 'Revoke Certificate for Service '+service_name, + 'revoke': function(values) { + var reason = values['reason']; + + ipa_cmd( + 'cert_revoke', + [serial_number], + { + 'revocation_reason': reason + }, + function(data, text_status, xhr) { + check_status(serial_number); + } + ); + } + }); + + dialog.open(); + } -function service_usercertificate_request(result) { + function restore_certificate(result) { - var krbprincipalname = result['krbprincipalname'][0]; - var service_name = krbprincipalname.replace(/@.*$/, ''); - - var dialog = certificate_request_dialog({ - 'title': 'Issue New Certificate for Service '+service_name, - 'request': function(values) { - var request = values['request']; - - ipa_cmd( - 'cert_request', - [request], - { - 'principal': krbprincipalname - } - ); + var usercertificate = result['usercertificate']; + if (!usercertificate) { + set_status(SERVICE_CERTIFICATE_MISSING); + return; } - }); - dialog.open(); -} + var krbprincipalname = result['krbprincipalname'][0]; + var service_name = krbprincipalname.replace(/@.*$/, ''); + + var serial_number = result['serial_number']; + + var dialog = certificate_restore_dialog({ + 'title': 'Restore Certificate for Service '+service_name, + 'restore': function(values) { + ipa_cmd( + 'cert_remove_hold', + [serial_number], + { }, + function(data, text_status, xhr) { + check_status(serial_number); + } + ); + } + }); + + dialog.open(); + } -function service_usercertificate_load(container, dt, result) { + function request_certificate(result) { + + var krbprincipalname = result['krbprincipalname'][0]; + var service_name = krbprincipalname.replace(/@.*$/, ''); + + var dialog = certificate_request_dialog({ + 'title': 'Issue New Certificate for Service '+service_name, + 'request': function(values) { + var request = values['request']; + + ipa_cmd( + 'cert_request', + [request], + { + 'principal': krbprincipalname + }, + function(data, text_status, xhr) { + check_status(data.result.result.serial_number); + } + ); + } + }); + + dialog.open(); + } var krbprincipalname = result['krbprincipalname'][0]; @@ -236,40 +262,45 @@ function service_usercertificate_load(container, dt, result) { var tr = $('<tr/>').appendTo(table); var td = $('<td/>').appendTo(tr); + li1 = $('<li/>', { + style: 'color: green;' + }).appendTo(td); + + td = $('<td/>').appendTo(tr); td.append('Valid Certificate Present:'); td = $('<td/>').appendTo(tr); $('<input/>', { - type: 'button', - value: 'Get', - click: function() { + 'type': 'button', + 'value': 'Get', + 'click': function() { ipa_cmd('service_show', [krbprincipalname], {}, function(data, text_status, xhr) { - service_usercertificate_get(data.result.result); + get_certificate(data.result.result); } ); } }).appendTo(td); $('<input/>', { - type: 'button', - value: 'Revoke', - click: function() { + 'type': 'button', + 'value': 'Revoke', + 'click': function() { ipa_cmd('service_show', [krbprincipalname], {}, function(data, text_status, xhr) { - service_usercertificate_revoke(data.result.result); + revoke_certificate(data.result.result); } ); } }).appendTo(td); $('<input/>', { - type: 'button', - value: 'View', - click: function() { + 'type': 'button', + 'value': 'View', + 'click': function() { ipa_cmd('service_show', [krbprincipalname], {}, function(data, text_status, xhr) { - service_usercertificate_view(data.result.result); + view_certificate(data.result.result); } ); } @@ -278,16 +309,27 @@ function service_usercertificate_load(container, dt, result) { tr = $('<tr/>').appendTo(table); td = $('<td/>').appendTo(tr); + li2 = $('<li/>', { + 'style': 'color: red;' + }).appendTo(td); + + td = $('<td/>').appendTo(tr); td.append('Certificate Revoked:'); td = $('<td/>').appendTo(tr); + td.append($('<span/>', { + 'id': 'revocation_reason' + })); + td.append(' '); + $('<input/>', { - type: 'button', - value: 'Restore', - click: function() { + 'id': 'restore_button', + 'type': 'button', + 'value': 'Restore', + 'click': function() { ipa_cmd('service_show', [krbprincipalname], {}, function(data, text_status, xhr) { - service_usercertificate_restore(data.result.result); + restore_certificate(data.result.result); } ); } @@ -296,17 +338,29 @@ function service_usercertificate_load(container, dt, result) { tr = $('<tr/>').appendTo(table); td = $('<td/>').appendTo(tr); + li3 = $('<li/>', { + 'style': 'color: goldenrod;' + }).appendTo(td); + + td = $('<td/>').appendTo(tr); td.append('No Valid Certificate:'); td = $('<td/>').appendTo(tr); $('<input/>', { - type: 'button', - value: 'New Certificate', - click: function() { - service_usercertificate_request(result); + 'type': 'button', + 'value': 'New Certificate', + 'click': function() { + request_certificate(result); } }).appendTo(td); var dd = ipa_create_first_dd(this.name, table); dt.after(dd); + + var usercertificate = result['usercertificate']; + if (usercertificate) { + check_status(result.serial_number); + } else { + set_status(SERVICE_CERTIFICATE_MISSING); + } } diff --git a/install/static/test/data/cert_request.json b/install/static/test/data/cert_request.json index 190a4443..c4410729 100644 --- a/install/static/test/data/cert_request.json +++ b/install/static/test/data/cert_request.json @@ -3,15 +3,15 @@ "id": 0, "result": { "result": { - "certificate": "MIIC2DCCAcCgAwIBAgIBFTANBgkqhkiG9w0BAQsFADAuMQwwCgYDVQQKEwNJUEExHjAcBgNVBAMTFUNlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xMDEwMTUwNTE3MzZaFw0xMTA0MTMwNTE3MzZaMCgxDDAKBgNVBAoTA0lQQTEYMBYGA1UEAxMPZGV2LmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDk18o/IK1Qe0Y/eURWsIHcJ/TmGdNy1h2fhzGIILtv8Qgq1K3U4T9eGAZVambpo1SUnJY+k+AAo43TyavSU05se4DIsw00XrrOyD5UunwsW+b+cIUCWbBJLFy9ODsVDJduXrj0RZGHEyta3VuO/gJBtdI9anjVvgegqXUBkenjPwIDAQABo4GKMIGHMB8GA1UdIwQYMBaAFCv9XyGV5ijtHriYMcECVmnNiMMAMD8GCCsGAQUFBwEBBDMwMTAvBggrBgEFBQcwAYYjaHR0cDovL2Rldi5leGFtcGxlLmNvbTo5MTgwL2NhL29jc3AwDgYDVR0PAQH/BAQDAgTwMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4IBAQB/OtoBZUYuGD0KZWpNdzIVdCxCTzZAetHA+o97cZCdufiBckZfZ9LEkQdL2MWvcnLlXOnRnQO/BnEAAtYVe4dpmizuzPn1+JTmK9+7q2HhQhXuU2NcsWutgYySNme7eNmfqi8uDoQ8FOPX4LvxLQDyKjkOogs6cEMZePMK8RE60ulHKdYfI23rRNtIExOl1zLJXkpExuXNq1flshyaLLu84B9zwE5FhSD/XAjPyAqYP97IPIkUfWpMyEs3N12JVCVNm0/753CDI946RPnyPpsULZufBV/8sdhDnULHfPIUMPgdOYemm9cYTP/V1uhwwCnFemhSAXSM/g6e9Xm4hH7s", + "certificate": "MIICAjCCAWugAwIBAgICBAswDQYJKoZIhvcNAQEFBQAwKTEnMCUGA1UEAxMeSVBBIFRlc3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTEwMTAwNzIzMzk0NFoXDTE1MTAwNzIzMzk0NFowKDEMMAoGA1UECgwDSVBBMRgwFgYDVQQDDA9kZXYuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOTXyj8grVB7Rj95RFawgdwn9OYZ03LWHZ+HMYggu2/xCCrUrdThP14YBlVqZumjVJSclj6T4ACjjdPJq9JTTmx7gMizDTReus7IPlS6fCxb5v5whQJZsEksXL04OxUMl25euPRFkYcTK1rdW47+AkG10j1qeNW+B6CpdQGR6eM/AgMBAAGjOjA4MBEGCWCGSAGG+EIBAQQEAwIGQDATBgNVHSUEDDAKBggrBgEFBQcDATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADgYEASIhq723VL5xP0q51MYXFlGU1boD7pPD1pIQspD/MjCIEupcbH2kAo4wf+EiKsXR0rs+WZkaSgvFqaM4OQ2kWSFTiqmFXFDBEi6EFr68yLg7IpQpNTzVBXERd8B4GwNL9wrRw60jPXlUK29DPBsdGq8fDgX18l39wKkWXv7p1to4=", "issuer": "CN=Certificate Authority,O=IPA", - "md5_fingerprint": "87:ca:33:52:e3:07:4c:82:76:24:8d:53:ba:da:b3:fe", - "request_id": "50", + "md5_fingerprint": "08:86:a9:f9:87:af:0d:d7:42:01:e0:5f:12:9b:32:7f", + "request_id": "1", "serial_number": "1", - "sha1_fingerprint": "9f:fc:d3:e2:3b:f0:c1:1d:fc:5c:09:fa:f4:10:de:7b:b2:25:ae:7c", + "sha1_fingerprint": "b8:4c:4b:79:4f:13:03:79:47:08:fa:6b:52:63:3d:f9:15:8e:7e:dc", "subject": "CN=dev.example.com,O=IPA", - "valid_not_after": "Wed Apr 13 05:17:36 2011 UTC", - "valid_not_before": "Fri Oct 15 05:17:36 2010 UTC" + "valid_not_after": "Tue Oct 13 01:59:32 2015 UTC", + "valid_not_before": "Wed Oct 13 01:59:32 2010 UTC" } } } diff --git a/install/static/test/data/cert_show.json b/install/static/test/data/cert_show.json new file mode 100644 index 00000000..f8996715 --- /dev/null +++ b/install/static/test/data/cert_show.json @@ -0,0 +1,17 @@ +{ + "error": null, + "id": 0, + "result": { + "result": { + "certificate": "MIICAjCCAWugAwIBAgICBAswDQYJKoZIhvcNAQEFBQAwKTEnMCUGA1UEAxMeSVBBIFRlc3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTEwMTAwNzIzMzk0NFoXDTE1MTAwNzIzMzk0NFowKDEMMAoGA1UECgwDSVBBMRgwFgYDVQQDDA9kZXYuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOTXyj8grVB7Rj95RFawgdwn9OYZ03LWHZ+HMYggu2/xCCrUrdThP14YBlVqZumjVJSclj6T4ACjjdPJq9JTTmx7gMizDTReus7IPlS6fCxb5v5whQJZsEksXL04OxUMl25euPRFkYcTK1rdW47+AkG10j1qeNW+B6CpdQGR6eM/AgMBAAGjOjA4MBEGCWCGSAGG+EIBAQQEAwIGQDATBgNVHSUEDDAKBggrBgEFBQcDATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADgYEASIhq723VL5xP0q51MYXFlGU1boD7pPD1pIQspD/MjCIEupcbH2kAo4wf+EiKsXR0rs+WZkaSgvFqaM4OQ2kWSFTiqmFXFDBEi6EFr68yLg7IpQpNTzVBXERd8B4GwNL9wrRw60jPXlUK29DPBsdGq8fDgX18l39wKkWXv7p1to4=", + "issuer": "CN=Certificate Authority,O=IPA", + "md5_fingerprint": "08:86:a9:f9:87:af:0d:d7:42:01:e0:5f:12:9b:32:7f", + "revocation_reason": 6, + "serial_number": "1", + "sha1_fingerprint": "b8:4c:4b:79:4f:13:03:79:47:08:fa:6b:52:63:3d:f9:15:8e:7e:dc", + "subject": "CN=dev.example.com,O=IPA", + "valid_not_after": "Tue Oct 13 01:59:32 2015 UTC", + "valid_not_before": "Wed Oct 13 01:59:32 2010 UTC" + } + } +} diff --git a/install/static/test/data/service_show.json b/install/static/test/data/service_show.json index d01f8585..e941d43d 100644 --- a/install/static/test/data/service_show.json +++ b/install/static/test/data/service_show.json @@ -13,7 +13,7 @@ "dev.example.com" ], "md5_fingerprint": "08:86:a9:f9:87:af:0d:d7:42:01:e0:5f:12:9b:32:7f", - "serial_number": "1052", + "serial_number": "1", "sha1_fingerprint": "b8:4c:4b:79:4f:13:03:79:47:08:fa:6b:52:63:3d:f9:15:8e:7e:dc", "subject": "CN=dev.example.com,O=IPA", "usercertificate": [ |