Hi,

Please review the attached patch. Thanks!

https://fedorahosted.org/reviewboard/r/94/

The service certificate management UI has been generalized and moved
into certificate.js. The host details page is now using the same code
to manage certificates. The host.py has been modified to return host
certificate info.

The Get/Revoke/View buttons behavior has been modified such that they
are visible only if there is a valid certificate. The Get dialog box
has been fixed to show the correct certificate header and footer.

New unit tests for certificate has been added. The test data has been
modified to include sample host certificate.

--
Endi S. Dewata
From 1859976ed57366f9217a36dc69f9ec74f374f931 Mon Sep 17 00:00:00 2001
From: Endi S. Dewata <edew...@redhat.com>
Date: Fri, 15 Oct 2010 23:40:38 -0500
Subject: [PATCH] Host certificate management

The service certificate management UI has been generalized and moved
into certificate.js. The host details page is now using the same code
to manage certificates. The host.py has been modified to return host
certificate info.

The Get/Revoke/View buttons behavior has been modified such that they
are visible only if there is a valid certificate. The Get dialog box
has been fixed to show the correct certificate header and footer.

New unit tests for certificate has been added. The test data has been
modified to include sample host certificate.
---
 install/static/certificate.js              |  345 +++++++++++++++++++++++++++-
 install/static/host.js                     |   41 +++-
 install/static/service.js                  |  290 ++----------------------
 install/static/test/all_tests.html         |    4 +-
 install/static/test/association_tests.html |    2 +-
 install/static/test/certificate_tests.html |   24 ++
 install/static/test/certificate_tests.js   |   79 +++++++
 install/static/test/data/cert_show.json    |    1 -
 install/static/test/data/host_show.json    |   14 +-
 install/static/test/details_tests.html     |    2 +-
 install/static/test/entity_tests.html      |    2 +-
 install/static/test/index.html             |    1 +
 install/static/test/ipa_tests.html         |    2 +-
 install/static/test/navigation_tests.html  |    2 +-
 ipalib/plugins/host.py                     |   35 +++
 15 files changed, 558 insertions(+), 286 deletions(-)
 create mode 100755 install/static/test/certificate_tests.html
 create mode 100755 install/static/test/certificate_tests.js

diff --git a/install/static/certificate.js b/install/static/certificate.js
index a688fe8..ec30f81 100755
--- a/install/static/certificate.js
+++ b/install/static/certificate.js
@@ -18,6 +18,9 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+var BEGIN_CERTIFICATE = '-----BEGIN CERTIFICATE-----';
+var END_CERTIFICATE   = '-----END CERTIFICATE-----';
+
 var BEGIN_CERTIFICATE_REQUEST = '-----BEGIN CERTIFICATE REQUEST-----';
 var END_CERTIFICATE_REQUEST   = '-----END CERTIFICATE REQUEST-----';
 
@@ -35,19 +38,33 @@ var CRL_REASON = [
     'AA Compromise'
 ];
 
+var CERTIFICATE_STATUS_MISSING = 0;
+var CERTIFICATE_STATUS_VALID   = 1;
+var CERTIFICATE_STATUS_REVOKED = 2;
+
 function certificate_parse_dn(dn) {
 
     var result = {};
+    if (!dn) return result;
 
     // TODO: Use proper LDAP DN parser
     var rdns = dn.split(',');
     for (var i=0; i<rdns.length; i++) {
         var rdn = rdns[i];
-        var parts = rdn.split('=');
-        var name = parts[0].toLowerCase();
-        var value = parts[1];
+        if (!rdn) continue;
 
-        result[name] = value;
+        var parts = rdn.split('=');
+        var name = $.trim(parts[0].toLowerCase());
+        var value = $.trim(parts[1]);
+
+        var old_value = result[name];
+        if (!old_value) {
+            result[name] = value;
+        } else if (typeof old_value == "string") {
+            result[name] = [old_value, value];
+        } else {
+            result[name].push(value);
+        }
     }
 
     return result;
@@ -70,9 +87,9 @@ function certificate_get_dialog(spec) {
     }).appendTo(dialog);
 
     textarea.val(
-        BEGIN_CERTIFICATE_REQUEST+'\n'+
+        BEGIN_CERTIFICATE+'\n'+
         that.usercertificate+'\n'+
-        END_CERTIFICATE_REQUEST
+        END_CERTIFICATE
     );
 
     that.open = function() {
@@ -371,3 +388,319 @@ function certificate_request_dialog(spec) {
 
     return that;
 }
+
+function certificate_status_panel(spec) {
+    var that = $('<div/>');
+    spec = spec || {};
+
+    that.entity_type = spec.entity_type;
+    if (!that.entity_type) {
+        alert('Missing entity_type.');
+        return;
+    }
+
+    that.entity_label = spec.entity_label || that.entity_type;
+
+    that.result = spec.result;
+    if (!that.result) {
+        alert('Missing result.');
+        return;
+    }
+
+    that.get_entity_pkey = spec.get_entity_pkey;
+    if (!that.get_entity_pkey) {
+        alert('Missing get_entity_pkey().');
+        return;
+    }
+
+    that.get_entity_name = spec.get_entity_name;
+    if (!that.get_entity_name) {
+        alert('Missing get_entity_name().');
+        return;
+    }
+
+    that.get_entity_principal = spec.get_entity_principal;
+    if (!that.get_entity_principal) {
+        alert('Missing get_entity_principal().');
+        return;
+    }
+
+    that.get_entity_certificate = spec.get_entity_certificate;
+    if (!that.get_entity_certificate) {
+        alert('Missing get_entity_certificate().');
+        return;
+    }
+
+    var li1, li2, li3;
+
+    function init() {
+        var pkey = that.get_entity_pkey(that.result);
+
+        var table = $('<table/>').appendTo(that);
+
+        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/>', {
+            'id': 'get_button',
+            'type': 'button',
+            'value': 'Get',
+            'click': function() {
+                ipa_cmd(that.entity_type+'_show', [pkey], {},
+                    function(data, text_status, xhr) {
+                        get_certificate(data.result.result);
+                    }
+                );
+            }
+        }).appendTo(td);
+
+        $('<input/>', {
+            'id': 'revoke_button',
+            'type': 'button',
+            'value': 'Revoke',
+            'click': function() {
+                ipa_cmd(that.entity_type+'_show', [pkey], {},
+                    function(data, text_status, xhr) {
+                        revoke_certificate(data.result.result);
+                    }
+                );
+            }
+        }).appendTo(td);
+
+        $('<input/>', {
+            'id': 'view_button',
+            'type': 'button',
+            'value': 'View',
+            'click': function() {
+                ipa_cmd(that.entity_type+'_show', [pkey], {},
+                    function(data, text_status, xhr) {
+                        view_certificate(data.result.result);
+                    }
+                );
+            }
+        }).appendTo(td);
+
+        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/>', {
+            'id': 'restore_button',
+            'type': 'button',
+            'value': 'Restore',
+            'click': function() {
+                ipa_cmd(that.entity_type+'_show', [pkey], {},
+                    function(data, text_status, xhr) {
+                        restore_certificate(data.result.result);
+                    }
+                );
+            }
+        }).appendTo(td);
+
+        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() {
+                request_certificate(that.result);
+            }
+        }).appendTo(td);
+
+        var entity_certificate = that.get_entity_certificate(that.result);
+        if (entity_certificate) {
+            check_status(that.result.serial_number);
+        } else {
+            set_status(CERTIFICATE_STATUS_MISSING);
+        }
+    }
+
+    function set_status(status, revocation_reason) {
+        li1.css('list-style-type', status == CERTIFICATE_STATUS_VALID ? 'disc' : 'circle');
+        li2.css('list-style-type', status == CERTIFICATE_STATUS_REVOKED ? 'disc' : 'circle');
+        li3.css('list-style-type', status == CERTIFICATE_STATUS_MISSING ? 'disc' : 'circle');
+
+        $('#get_button', that).css('visibility', status == CERTIFICATE_STATUS_VALID ? 'visible' : 'hidden');
+        $('#revoke_button', that).css('visibility', status == CERTIFICATE_STATUS_VALID ? 'visible' : 'hidden');
+        $('#view_button', that).css('visibility', status == CERTIFICATE_STATUS_VALID ? 'visible' : 'hidden');
+        $('#revocation_reason', that).html(revocation_reason == undefined ? '' : CRL_REASON[revocation_reason]);
+        $('#restore_button', that).css('visibility', revocation_reason == 6 ? 'visible' : 'hidden');
+    }
+
+    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 == undefined) {
+                    set_status(CERTIFICATE_STATUS_VALID);
+                } else {
+                    set_status(CERTIFICATE_STATUS_REVOKED, revocation_reason);
+                }
+            }
+        );
+    }
+
+    function view_certificate(result) {
+
+        var entity_certificate = that.get_entity_certificate(result);
+        if (!entity_certificate) {
+            set_status(CERTIFICATE_STATUS_MISSING);
+            return;
+        }
+
+        var entity_name = that.get_entity_name(result);
+
+        var dialog = certificate_view_dialog({
+            'title': 'Certificate for '+that.entity_label+' '+entity_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) {
+
+        var entity_certificate = that.get_entity_certificate(result);
+        if (!entity_certificate) {
+            set_status(CERTIFICATE_STATUS_MISSING);
+            return;
+        }
+
+        var entity_name = that.get_entity_name(result);
+
+        var dialog = certificate_get_dialog({
+            'title': 'Certificate for '+that.entity_label+' '+entity_name,
+            'usercertificate': entity_certificate
+        });
+
+        dialog.open();
+    }
+
+    function request_certificate(result) {
+
+        var entity_name = that.get_entity_name(result);
+        var entity_principal = that.get_entity_principal(result);
+
+        var dialog = certificate_request_dialog({
+            'title': 'Issue New Certificate for '+that.entity_label+' '+entity_name,
+            'request': function(values) {
+                var request = values['request'];
+
+                ipa_cmd(
+                    'cert_request',
+                    [request],
+                    {
+                        'principal': entity_principal
+                    },
+                    function(data, text_status, xhr) {
+                        check_status(data.result.result.serial_number);
+                    }
+                );
+            }
+        });
+
+        dialog.open();
+    }
+
+    function revoke_certificate(result) {
+
+        var entity_certificate = that.get_entity_certificate(result);
+        if (!entity_certificate) {
+            set_status(CERTIFICATE_STATUS_MISSING);
+            return;
+        }
+
+        var entity_name = that.get_entity_name(result);
+        var serial_number = result['serial_number'];
+
+        var dialog = certificate_revoke_dialog({
+            'title': 'Revoke Certificate for '+that.entity_label+' '+entity_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 restore_certificate(result) {
+
+        var entity_certificate = that.get_entity_certificate(result);
+        if (!entity_certificate) {
+            set_status(CERTIFICATE_STATUS_MISSING);
+            return;
+        }
+
+        var entity_name = that.get_entity_name(result);
+        var serial_number = result['serial_number'];
+
+        var dialog = certificate_restore_dialog({
+            'title': 'Restore Certificate for '+that.entity_label+' '+entity_name,
+            'restore': function(values) {
+                ipa_cmd(
+                    'cert_remove_hold',
+                    [serial_number],
+                    { },
+                    function(data, text_status, xhr) {
+                        check_status(serial_number);
+                    }
+                );
+            }
+        });
+
+        dialog.open();
+    }
+
+    init();
+
+    return that;
+}
diff --git a/install/static/host.js b/install/static/host.js
index d2cdd7d..c4d110a 100644
--- a/install/static/host.js
+++ b/install/static/host.js
@@ -35,13 +35,50 @@ ipa_entity_set_add_definition('host', [
 ]);
 
 ipa_entity_set_details_definition('host', [
-    ipa_stanza({name:'host', label:'Host Details'}).
+    ipa_stanza({name:'details', label:'Host Details'}).
         input({name:'fqdn', label:'Fully Qualified Domain Name'}).
         input({name:'krbprincipalname', label:'Kerberos Principal'}).
-        input({name:'serverhostname', label:'Server Host Name'})
+        input({name:'serverhostname', label:'Server Host Name'}),
+    ipa_stanza({name:'enrollment', label:'Enrollment'}).
+        input({name:'enrollment_status', label:'Status',
+               load:host_enrollment_status_load}),
+    ipa_stanza({name:'certificate', label:'Host Certificate'}).
+        input({name:'certificate_status', label:'Status',
+               load:host_usercertificate_load})
 ]);
 
 ipa_entity_set_association_definition('host', {
     'hostgroup': { associator: SerialAssociator },
     'rolegroup': { associator: SerialAssociator }
 });
+
+function host_enrollment_status_load(container, dt, result) {
+    // skip enrollment_status
+}
+
+function host_usercertificate_load(container, dt, result) {
+
+    var panel = certificate_status_panel({
+        'entity_type': 'host',
+        'entity_label': 'Host',
+        'result': result,
+        'get_entity_pkey': function(result) {
+            var values = result['fqdn'];
+            return values ? values[0] : null;
+        },
+        'get_entity_name': function(result) {
+            return this.get_entity_pkey(result);
+        },
+        'get_entity_principal': function(result) {
+            var values = result['krbprincipalname'];
+            return values ? values[0] : null;
+        },
+        'get_entity_certificate': function(result) {
+            var values = result['usercertificate'];
+            return values ? values[0].__base64__ : null;
+        }
+    });
+
+    var dd = ipa_create_first_dd(this.name, panel);
+    dt.after(dd);
+}
diff --git a/install/static/service.js b/install/static/service.js
index 5e37f6a..620c1ba 100644
--- a/install/static/service.js
+++ b/install/static/service.js
@@ -20,10 +20,6 @@
 
 /* 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]
@@ -94,273 +90,27 @@ function service_provisioning_status_load(container, dt, result) {
 
 function service_usercertificate_load(container, dt, result) {
 
-    var li1, li2, li3;
-
-    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');
-
-        $('#revocation_reason').html(revocation_reason ? CRL_REASON[revocation_reason] : '');
-        $('#restore_button').css('visibility', revocation_reason == 6 ? 'visible' : 'hidden')
-    }
-
-    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);
-                }
-            }
-        );
-    }
-
-    function get_certificate(result) {
-
-        var usercertificate = result['usercertificate'];
-        if (!usercertificate) {
-            set_status(SERVICE_CERTIFICATE_MISSING);
-            return;
-        }
-
-        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__
-        });
-
-        dialog.open();
-    }
-
-    function view_certificate(result) {
-
-        var usercertificate = result['usercertificate'];
-        if (!usercertificate) {
-            set_status(SERVICE_CERTIFICATE_MISSING);
-            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();
-    }
-
-    function revoke_certificate(result) {
-
-        var usercertificate = result['usercertificate'];
-        if (!usercertificate) {
-            set_status(SERVICE_CERTIFICATE_MISSING);
-            return;
+    var panel = certificate_status_panel({
+        'entity_type': 'service',
+        'entity_label': 'Service',
+        'result': result,
+        'get_entity_pkey': function(result) {
+            var values = result['krbprincipalname'];
+            return values ? values[0] : null;
+        },
+        'get_entity_name': function(result) {
+            var value = this.get_entity_pkey(result);
+            return value ? value.replace(/@.*$/, '') : null;
+        },
+        'get_entity_principal': function(result) {
+            return this.get_entity_pkey(result);
+        },
+        'get_entity_certificate': function(result) {
+            var values = result['usercertificate'];
+            return values ? values[0].__base64__ : null;
         }
+    });
 
-        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 restore_certificate(result) {
-
-        var usercertificate = result['usercertificate'];
-        if (!usercertificate) {
-            set_status(SERVICE_CERTIFICATE_MISSING);
-            return;
-        }
-
-        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 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];
-
-    var table = $('<table/>');
-
-    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() {
-            ipa_cmd('service_show', [krbprincipalname], {},
-                function(data, text_status, xhr) {
-                    get_certificate(data.result.result);
-                }
-            );
-        }
-    }).appendTo(td);
-
-    $('<input/>', {
-        'type': 'button',
-        'value': 'Revoke',
-        'click': function() {
-            ipa_cmd('service_show', [krbprincipalname], {},
-                function(data, text_status, xhr) {
-                    revoke_certificate(data.result.result);
-                }
-            );
-        }
-    }).appendTo(td);
-
-    $('<input/>', {
-        'type': 'button',
-        'value': 'View',
-        'click': function() {
-            ipa_cmd('service_show', [krbprincipalname], {},
-                function(data, text_status, xhr) {
-                    view_certificate(data.result.result);
-                }
-            );
-        }
-    }).appendTo(td);
-
-    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/>', {
-        'id': 'restore_button',
-        'type': 'button',
-        'value': 'Restore',
-        'click': function() {
-            ipa_cmd('service_show', [krbprincipalname], {},
-                function(data, text_status, xhr) {
-                    restore_certificate(data.result.result);
-                }
-            );
-        }
-    }).appendTo(td);
-
-    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() {
-            request_certificate(result);
-        }
-    }).appendTo(td);
-
-    var dd = ipa_create_first_dd(this.name, table);
+    var dd = ipa_create_first_dd(this.name, panel);
     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/all_tests.html b/install/static/test/all_tests.html
index aede08e..50c5155 100644
--- a/install/static/test/all_tests.html
+++ b/install/static/test/all_tests.html
@@ -14,11 +14,13 @@
     <script type="text/javascript" src="../entity.js"></script>
     <script type="text/javascript" src="../associate.js"></script>
     <script type="text/javascript" src="../navigation.js"></script>
+    <script type="text/javascript" src="../certificate.js"></script>
     <script type="text/javascript" src="ipa_tests.js"></script>
     <script type="text/javascript" src="details_tests.js"></script>
     <script type="text/javascript" src="entity_tests.js"></script>
     <script type="text/javascript" src="association_tests.js"></script>
     <script type="text/javascript" src="navigation_tests.js"></script>
+    <script type="text/javascript" src="certificate_tests.js"></script>
 </head>
 <body>
     <h1 id="qunit-header">Complete Test Suite</h1>
@@ -26,6 +28,6 @@
     <div id="qunit-testrunner-toolbar"></div>
     <h2 id="qunit-userAgent"></h2>
     <ol id="qunit-tests"></ol>
-    <div id="qunit-fixture">test markup</div>
+    <div id="qunit-fixture"></div>
 </body>
 </html>
diff --git a/install/static/test/association_tests.html b/install/static/test/association_tests.html
index 5b5fb71..40b3c20 100644
--- a/install/static/test/association_tests.html
+++ b/install/static/test/association_tests.html
@@ -18,6 +18,6 @@
     <div id="qunit-testrunner-toolbar"></div>
     <h2 id="qunit-userAgent"></h2>
     <ol id="qunit-tests"></ol>
-    <div id="qunit-fixture">test markup</div>
+    <div id="qunit-fixture"></div>
 </body>
 </html>
diff --git a/install/static/test/certificate_tests.html b/install/static/test/certificate_tests.html
new file mode 100755
index 0000000..90f09b2
--- /dev/null
+++ b/install/static/test/certificate_tests.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Certificate Test Suite</title>
+    <link rel="stylesheet" href="qunit.css" type="text/css" media="screen">
+    <script type="text/javascript" src="qunit.js"></script>
+    <script type="text/javascript" src="../jquery.js"></script>
+    <script type="text/javascript" src="../ipa.js"></script>
+    <script type="text/javascript" src="../details.js"></script>
+    <script type="text/javascript" src="../search.js"></script>
+    <script type="text/javascript" src="../add.js"></script>
+    <script type="text/javascript" src="../navigation.js"></script>
+    <script type="text/javascript" src="../certificate.js"></script>
+    <script type="text/javascript" src="certificate_tests.js"></script>
+</head>
+<body>
+    <h1 id="qunit-header">Certificate Test Suite</h1>
+    <h2 id="qunit-banner"></h2>
+    <div id="qunit-testrunner-toolbar"></div>
+    <h2 id="qunit-userAgent"></h2>
+    <ol id="qunit-tests"></ol>
+    <div id="qunit-fixture"></div>
+</body>
+</html>
diff --git a/install/static/test/certificate_tests.js b/install/static/test/certificate_tests.js
new file mode 100755
index 0000000..6ada6e4
--- /dev/null
+++ b/install/static/test/certificate_tests.js
@@ -0,0 +1,79 @@
+/*  Authors:
+ *    Endi Sukma Dewata <edew...@redhat.com>
+ *
+ * Copyright (C) 2010 Red Hat
+ * see file 'COPYING' for use and warranty information
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 only
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+test("Testing certificate_parse_dn().", function() {
+
+    same(
+        certificate_parse_dn(), {},
+        "Checking certificate_parse_dn()"
+    );
+
+    same(
+        certificate_parse_dn(''), {},
+        "Checking certificate_parse_dn('')"
+    );
+
+    same(
+        certificate_parse_dn('c=US'), {'c': 'US'},
+        "Checking certificate_parse_dn('c=US')"
+    );
+
+    same(
+        certificate_parse_dn('st=TX,c=US'), {'st': 'TX','c': 'US'},
+        "Checking certificate_parse_dn('st=TX,c=US')"
+    );
+
+    same(
+        certificate_parse_dn('c=US,st=TX'), {'st': 'TX','c': 'US'},
+        "Checking certificate_parse_dn('c=US,st=TX')"
+    );
+
+    same(
+        certificate_parse_dn(' st = New Mexico , c = US '), {'st': 'New Mexico','c': 'US'},
+        "Checking certificate_parse_dn(' st = New Mexico , c = US ')"
+    );
+
+    same(
+        certificate_parse_dn('ST=TX,C=US'), {'st': 'TX','c': 'US'},
+        "Checking certificate_parse_dn('ST=TX,C=US')"
+    );
+
+    same(
+        certificate_parse_dn('cn=dev.example.com,ou=Engineering,o=Example,l=Austin,ST=TX,C=US'),
+        {   'cn': 'dev.example.com',
+            'ou': 'Engineering',
+            'o': 'Example',
+            'l': 'Austin',
+            'st': 'TX',
+            'c': 'US'
+        },
+        "Checking certificate_parse_dn('cn=dev.example.com,ou=Engineering,o=Example,l=Austin,ST=TX,C=US')"
+    );
+
+    same(
+        certificate_parse_dn('cn=John Smith,ou=Developers,ou=Users,dc=example,dc=com'),
+        {
+            'cn': 'John Smith',
+            'ou': ['Developers','Users'],
+            'dc': ['example', 'com']
+        },
+        "Checking certificate_parse_dn('cn=John Smith,ou=Developers,ou=Users,dc=example,dc=com')"
+    );
+});
diff --git a/install/static/test/data/cert_show.json b/install/static/test/data/cert_show.json
index f899671..71d895f 100644
--- a/install/static/test/data/cert_show.json
+++ b/install/static/test/data/cert_show.json
@@ -6,7 +6,6 @@
             "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",
diff --git a/install/static/test/data/host_show.json b/install/static/test/data/host_show.json
index 64a7fb3..b091621 100644
--- a/install/static/test/data/host_show.json
+++ b/install/static/test/data/host_show.json
@@ -14,6 +14,7 @@
             "ipauniqueid": [
                 "b54b73a8-8ba8-11df-80bc-00163e26b89e"
             ],
+            "issuer": "CN=IPA Test Certificate Authority",
             "krbextradata": [
                 {
                     "__base64__": "AAKOoTdMYWRtaW4vYWRtaW5ASURNLkxBQi5CT1MuUkVESEFULkNPTQA="
@@ -34,6 +35,7 @@
             "managedby": [
                 "fqdn=vm-121.idm.lab.bos.redhat.com,cn=computers,cn=accounts,dc=idm,dc=lab,dc=bos,dc=redhat,dc=com"
             ],
+            "md5_fingerprint": "08:86:a9:f9:87:af:0d:d7:42:01:e0:5f:12:9b:32:7f",
             "memberof": [],
             "objectclass": [
                 "top",
@@ -46,9 +48,19 @@
                 "krbprincipal",
                 "krbticketpolicyaux"
             ],
+            "serial_number": "1",
             "serverhostname": [
                 "vm-121"
-            ]
+            ],
+            "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": [
+                {
+                    "__base64__": "MIICAjCCAWugAwIBAgICBAswDQYJKoZIhvcNAQEFBQAwKTEnMCUGA1UEAxMeSVBBIFRlc3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTEwMTAwNzIzMzk0NFoXDTE1MTAwNzIzMzk0NFowKDEMMAoGA1UECgwDSVBBMRgwFgYDVQQDDA9kZXYuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOTXyj8grVB7Rj95RFawgdwn9OYZ03LWHZ+HMYggu2/xCCrUrdThP14YBlVqZumjVJSclj6T4ACjjdPJq9JTTmx7gMizDTReus7IPlS6fCxb5v5whQJZsEksXL04OxUMl25euPRFkYcTK1rdW47+AkG10j1qeNW+B6CpdQGR6eM/AgMBAAGjOjA4MBEGCWCGSAGG+EIBAQQEAwIGQDATBgNVHSUEDDAKBggrBgEFBQcDATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADgYEASIhq723VL5xP0q51MYXFlGU1boD7pPD1pIQspD/MjCIEupcbH2kAo4wf+EiKsXR0rs+WZkaSgvFqaM4OQ2kWSFTiqmFXFDBEi6EFr68yLg7IpQpNTzVBXERd8B4GwNL9wrRw60jPXlUK29DPBsdGq8fDgX18l39wKkWXv7p1to4="
+                }
+            ],
+            "valid_not_after": "Tue Oct 13 01:59:32 2015 UTC",
+            "valid_not_before": "Wed Oct 13 01:59:32 2010 UTC"
         },
         "summary": null,
         "value": "vm-121.idm.lab.bos.redhat.com"
diff --git a/install/static/test/details_tests.html b/install/static/test/details_tests.html
index 7c32361..e96fa4b 100644
--- a/install/static/test/details_tests.html
+++ b/install/static/test/details_tests.html
@@ -19,6 +19,6 @@
     <div id="qunit-testrunner-toolbar"></div>
     <h2 id="qunit-userAgent"></h2>
     <ol id="qunit-tests"></ol>
-    <div id="qunit-fixture">test markup</div>
+    <div id="qunit-fixture"></div>
 </body>
 </html>
diff --git a/install/static/test/entity_tests.html b/install/static/test/entity_tests.html
index a6d3f72..b8da0f4 100644
--- a/install/static/test/entity_tests.html
+++ b/install/static/test/entity_tests.html
@@ -19,6 +19,6 @@
     <div id="qunit-testrunner-toolbar"></div>
     <h2 id="qunit-userAgent"></h2>
     <ol id="qunit-tests"></ol>
-    <div id="qunit-fixture">test markup</div>
+    <div id="qunit-fixture"></div>
 </body>
 </html>
diff --git a/install/static/test/index.html b/install/static/test/index.html
index a71ee60..5467a68 100644
--- a/install/static/test/index.html
+++ b/install/static/test/index.html
@@ -29,6 +29,7 @@
         <li><a href="details_tests.html">Details Test Suite</a>
         <li><a href="association_tests.html">Association Test Suite</a>
         <li><a href="navigation_tests.html">Navigation Test Suite</a>
+        <li><a href="certificate_tests.html">Certificate Test Suite</a>
 
         </ul>
     </div>
diff --git a/install/static/test/ipa_tests.html b/install/static/test/ipa_tests.html
index dfe2720..3f3c168 100644
--- a/install/static/test/ipa_tests.html
+++ b/install/static/test/ipa_tests.html
@@ -17,6 +17,6 @@
     <div id="qunit-testrunner-toolbar"></div>
     <h2 id="qunit-userAgent"></h2>
     <ol id="qunit-tests"></ol>
-    <div id="qunit-fixture">test markup</div>
+    <div id="qunit-fixture"></div>
 </body>
 </html>
diff --git a/install/static/test/navigation_tests.html b/install/static/test/navigation_tests.html
index dbb562f..fa911d6 100644
--- a/install/static/test/navigation_tests.html
+++ b/install/static/test/navigation_tests.html
@@ -16,6 +16,6 @@
     <div id="qunit-testrunner-toolbar"></div>
     <h2 id="qunit-userAgent"></h2>
     <ol id="qunit-tests"></ol>
-    <div id="qunit-fixture">test markup</div>
+    <div id="qunit-fixture"></div>
 </body>
 </html>
diff --git a/ipalib/plugins/host.py b/ipalib/plugins/host.py
index d207f52..3a63d21 100644
--- a/ipalib/plugins/host.py
+++ b/ipalib/plugins/host.py
@@ -76,6 +76,7 @@ from ipalib.plugins.service import validate_certificate
 from ipalib import _, ngettext
 from ipalib import x509
 import base64
+import nss.nss as nss
 
 
 def validate_host(ugettext, fqdn):
@@ -335,6 +336,30 @@ class host_show(LDAPRetrieve):
     has_output_params = (
         Flag('has_keytab',
             label=_('Keytab'),
+        ),
+        Str('subject',
+            label=_('Subject'),
+        ),
+        Str('serial_number',
+            label=_('Serial Number'),
+        ),
+        Str('issuer',
+            label=_('Issuer'),
+        ),
+        Str('valid_not_before',
+            label=_('Not Before'),
+        ),
+        Str('valid_not_after',
+            label=_('Not After'),
+        ),
+        Str('md5_fingerprint',
+            label=_('Fingerprint (MD5)'),
+        ),
+        Str('sha1_fingerprint',
+            label=_('Fingerprint (SHA1)'),
+        ),
+        Str('revocation_reason?',
+            label=_('Revocation reason'),
         )
     )
 
@@ -346,6 +371,16 @@ class host_show(LDAPRetrieve):
         else:
             entry_attrs['has_keytab'] = False
 
+        if 'usercertificate' in entry_attrs:
+            cert = x509.load_certificate(entry_attrs['usercertificate'][0], datatype=x509.DER)
+            entry_attrs['subject'] = unicode(cert.subject)
+            entry_attrs['serial_number'] = unicode(cert.serial_number)
+            entry_attrs['issuer'] = unicode(cert.issuer)
+            entry_attrs['valid_not_before'] = unicode(cert.valid_not_before_str)
+            entry_attrs['valid_not_after'] = unicode(cert.valid_not_after_str)
+            entry_attrs['md5_fingerprint'] = unicode(nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0])
+            entry_attrs['sha1_fingerprint'] = unicode(nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0])
+
         return dn
 
 api.register(host_show)
-- 
1.6.6.1

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to