Files
freeipa/install/ui/certificate.js
Endi S. Dewata 52981883ab Fixed inconsistent image names.
The images have been renamed to be more consistent and moved into
the "images" directory to mimic the original jQuery UI structure.

Ticket #1613
2011-10-27 14:05:12 +00:00

916 lines
27 KiB
JavaScript
Executable File

/*jsl:import ipa.js */
/* Authors:
* Endi Sukma Dewata <edewata@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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
IPA.cert = {};
IPA.cert.BEGIN_CERTIFICATE = '-----BEGIN CERTIFICATE-----';
IPA.cert.END_CERTIFICATE = '-----END CERTIFICATE-----';
IPA.cert.BEGIN_CERTIFICATE_REQUEST = '-----BEGIN CERTIFICATE REQUEST-----';
IPA.cert.END_CERTIFICATE_REQUEST = '-----END CERTIFICATE REQUEST-----';
/*
* Pre-compiled regular expression to match a PEM cert.
*
* regexp group 1: entire canonical cert (delimiters plus base64)
* regexp group 2: base64 data inside PEM delimiters
*/
IPA.cert.PEM_CERT_REGEXP = RegExp('(-----BEGIN CERTIFICATE-----([^-]*)-----END CERTIFICATE-----)');
/*
* Pre-compiled regular expression to match a CSR (Certificate Signing Request).
* The delimiter "CERTIFICATE REQUEST" is the cononical standard, however some legacy
* software will produce a delimiter with "NEW" in it, i.e. "NEW CERTIFICATE REQUEST"
* This regexp will work with either form.
*
* regexp group 1: entire canonical CSR (delimiters plus base64)
* regexp group 2: base64 data inside canonical CSR delimiters
* regexp group 3: entire legacy CSR (delimiters plus base64)
* regexp group 4: base64 data inside legacy CSR delimiters
*/
IPA.cert.PEM_CSR_REGEXP = RegExp('(-----BEGIN CERTIFICATE REQUEST-----([^-]*)-----END CERTIFICATE REQUEST-----)|(-----BEGIN NEW CERTIFICATE REQUEST-----([^-]*)-----END NEW CERTIFICATE REQUEST-----)');
IPA.cert.CERTIFICATE_STATUS_MISSING = 0;
IPA.cert.CERTIFICATE_STATUS_VALID = 1;
IPA.cert.CERTIFICATE_STATUS_REVOKED = 2;
IPA.cert.CRL_REASON = [
'unspecified',
'key_compromise',
'ca_compromise',
'affiliation_changed',
'superseded',
'cessation_of_operation',
'certificate_hold',
null,
'remove_from_crl',
'privilege_withdrawn',
'aa_compromise'
];
IPA.cert.parse_dn = function(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];
if (!rdn) continue;
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;
};
IPA.cert.pem_format_base64 = function(text) {
/*
* Input is assumed to be base64 possibly with embedded whitespace.
* Format the base64 text such that it conforms to PEM, which is a
* sequence of 64 character lines, except for the last line which
* may be less than 64 characters. The last line does NOT have a
* new line appended to it.
*/
var formatted = "";
/* Strip out any whitespace including line endings */
text = text.replace(/\s*/g,"");
/*
* Break up into lines with 64 chars each.
* Do not add a newline to final line.
*/
for (var i = 0; i < text.length; i+=64) {
formatted += text.substring(i, i+64);
if (i+64 < text.length) {
formatted += "\n";
}
}
return (formatted);
};
IPA.cert.pem_cert_format = function(text) {
/*
* Input is assumed to be either PEM formated data or the
* base64 encoding of DER binary certificate data. Return data
* in PEM format. The function checks if the input text is PEM
* formatted, if so it just returns the input text. Otherwise
* the input is treated as base64 which is formatted to be PEM>
*/
/*
* Does the text already have the PEM delimiters?
* If so just return the text unmodified.
*/
if (text.match(IPA.cert.PEM_CERT_REGEXP)) {
return text;
}
/* No PEM delimiters so format the base64 & add the delimiters. */
return IPA.cert.BEGIN_CERTIFICATE + "\n" +
IPA.cert.pem_format_base64(text) + "\n" +
IPA.cert.END_CERTIFICATE;
};
IPA.cert.pem_csr_format = function(text) {
/*
* Input is assumed to be either PEM formated data or the base64
* encoding of DER binary certificate request (csr) data. Return
* data in PEM format. The function checks if the input text is
* PEM formatted, if so it just returns the input text. Otherwise
* the input is treated as base64 which is formatted to be PEM>
*/
/*
* Does the text already have the PEM delimiters?
* If so just return the text unmodified.
*/
if (text.match(IPA.cert.PEM_CSR_REGEXP)) {
return text;
}
/* No PEM delimiters so format the base64 & add the delimiters. */
return IPA.cert.BEGIN_CERTIFICATE_REQUEST + "\n" +
IPA.cert.pem_format_base64(text) + "\n" +
IPA.cert.END_CERTIFICATE_REQUEST;
};
IPA.cert.download_dialog = function(spec) {
spec = spec || {};
var that = IPA.dialog(spec);
that.width = spec.width || 500;
that.height = spec.height || 400;
that.add_pem_delimiters = typeof spec.add_pem_delimiters == 'undefined' ? true : spec.add_pem_delimiters;
that.certificate = spec.certificate || '';
that.create_button({
name: 'close',
label: IPA.messages.buttons.close,
click: function() {
that.close();
}
});
that.create = function() {
var textarea = $('<textarea/>', {
readonly: 'yes',
style: 'width: 100%; height: 275px;'
}).appendTo(that.container);
var certificate = that.certificate;
if (that.add_pem_delimiters) {
certificate = IPA.cert.pem_cert_format(that.certificate);
}
textarea.val(certificate);
};
return that;
};
IPA.cert.revoke_dialog = function(spec) {
spec = spec || {};
var that = IPA.dialog(spec);
that.width = spec.width || 500;
that.height = spec.height || 300;
that.revoke = spec.revoke;
that.create_button({
name: 'revoke',
label: IPA.messages.buttons.revoke,
click: function() {
var values = {};
values['reason'] = that.select.val();
if (that.revoke) {
that.revoke(values);
}
that.close();
}
});
that.create_button({
name: 'cancel',
label: IPA.messages.buttons.cancel,
click: function() {
that.close();
}
});
that.create = function() {
var table = $('<table/>').appendTo(that.container);
var tr = $('<tr/>').appendTo(table);
var td = $('<td/>').appendTo(tr);
td.append(IPA.messages.objects.cert.note+':');
td = $('<td/>').appendTo(tr);
td.append(IPA.messages.objects.cert.revoke_confirmation);
tr = $('<tr/>').appendTo(table);
td = $('<td/>').appendTo(tr);
td.append(IPA.messages.objects.cert.reason+':');
td = $('<td/>').appendTo(tr);
that.select = $('<select/>').appendTo(td);
for (var i=0; i<IPA.cert.CRL_REASON.length; i++) {
var reason = IPA.cert.CRL_REASON[i];
if (!reason) continue;
$('<option/>', {
'value': i,
'html': IPA.messages.objects.cert[reason]
}).appendTo(that.select);
}
};
return that;
};
IPA.cert.restore_dialog = function(spec) {
spec = spec || {};
var that = IPA.dialog(spec);
that.width = spec.width || 400;
that.height = spec.height || 200;
that.restore = spec.restore;
that.create_button({
name: 'restore',
label: IPA.messages.buttons.restore,
click: function() {
var values = {};
if (that.restore) {
that.restore(values);
}
that.close();
}
});
that.create_button({
name: 'cancel',
label: IPA.messages.buttons.cancel,
click: function() {
that.close();
}
});
that.create = function() {
that.container.append(
IPA.messages.objects.cert.restore_confirmation);
};
return that;
};
IPA.cert.view_dialog = function(spec) {
spec = spec || {};
var that = IPA.dialog(spec);
that.width = spec.width || 600;
that.height = spec.height || 500;
that.subject = IPA.cert.parse_dn(spec.subject);
that.serial_number = spec.serial_number || '';
that.issuer = IPA.cert.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 || '';
that.create_button({
name: 'close',
label: IPA.messages.buttons.close,
click: function() {
that.close();
}
});
that.create = function() {
var table = $('<table/>').appendTo(that.container);
var tr = $('<tr/>').appendTo(table);
$('<td/>', {
'colspan': 2,
'html': '<h3>'+IPA.messages.objects.cert.issued_to+'</h3>'
}).appendTo(tr);
tr = $('<tr/>').appendTo(table);
$('<td>'+IPA.messages.objects.cert.common_name+':</td>').appendTo(tr);
$('<td/>', {
text: that.subject.cn
}).appendTo(tr);
tr = $('<tr/>').appendTo(table);
$('<td>'+IPA.messages.objects.cert.organization+':</td>').appendTo(tr);
$('<td/>', {
text: that.subject.o
}).appendTo(tr);
tr = $('<tr/>').appendTo(table);
$('<td>'+IPA.messages.objects.cert.organizational_unit+':</td>').appendTo(tr);
$('<td/>', {
text: that.subject.ou
}).appendTo(tr);
tr = $('<tr/>').appendTo(table);
$('<td>'+IPA.messages.objects.cert.serial_number+':</td>').appendTo(tr);
$('<td/>', {
text: that.serial_number
}).appendTo(tr);
tr = $('<tr/>').appendTo(table);
$('<td/>', {
'colspan': 2,
'html': '<h3>'+IPA.messages.objects.cert.issued_by+'</h3>'
}).appendTo(tr);
tr = $('<tr/>').appendTo(table);
$('<td>'+IPA.messages.objects.cert.common_name+':</td>').appendTo(tr);
$('<td/>', {
text: that.issuer.cn
}).appendTo(tr);
tr = $('<tr/>').appendTo(table);
$('<td>'+IPA.messages.objects.cert.organization+':</td>').appendTo(tr);
$('<td/>', {
text: that.issuer.o
}).appendTo(tr);
tr = $('<tr/>').appendTo(table);
$('<td>'+IPA.messages.objects.cert.organizational_unit+':</td>').appendTo(tr);
$('<td/>', {
text: that.issuer.ou
}).appendTo(tr);
tr = $('<tr/>').appendTo(table);
$('<td/>', {
'colspan': 2,
'html': '<h3>'+IPA.messages.objects.cert.validity+'</h3>'
}).appendTo(tr);
tr = $('<tr/>').appendTo(table);
$('<td>'+IPA.messages.objects.cert.issued_on+':</td>').appendTo(tr);
$('<td/>', {
text: that.issued_on
}).appendTo(tr);
tr = $('<tr/>').appendTo(table);
$('<td>'+IPA.messages.objects.cert.expires_on+':</td>').appendTo(tr);
$('<td/>', {
text: that.expires_on
}).appendTo(tr);
tr = $('<tr/>').appendTo(table);
$('<td/>', {
'colspan': 2,
'html': '<h3>'+IPA.messages.objects.cert.fingerprints+'</h3>'
}).appendTo(tr);
tr = $('<tr/>').appendTo(table);
$('<td>'+IPA.messages.objects.cert.sha1_fingerprint+':</td>').appendTo(tr);
$('<td/>', {
text: that.sha1_fingerprint
}).appendTo(tr);
tr = $('<tr/>').appendTo(table);
$('<td>'+IPA.messages.objects.cert.md5_fingerprint+':</td>').appendTo(tr);
$('<td/>', {
text: that.md5_fingerprint
}).appendTo(tr);
};
return that;
};
IPA.cert.request_dialog = function(spec) {
spec = spec || {};
var that = IPA.dialog(spec);
that.width = spec.width || 500;
that.height = spec.height || 400;
that.request = spec.request;
that.create_button({
name: 'issue',
label: IPA.messages.buttons.issue,
click: function() {
var values = {};
var request = $.trim(that.textarea.val());
request = IPA.cert.pem_csr_format(request);
values['request'] = request;
if (that.request) {
that.request(values);
}
that.close();
}
});
that.create_button({
name: 'cancel',
label: IPA.messages.buttons.cancel,
click: function() {
that.close();
}
});
that.create = function() {
that.container.append(IPA.messages.objects.cert.enter_csr+':');
that.container.append('<br/>');
that.container.append('<br/>');
that.textarea = $('<textarea/>', {
style: 'width: 100%; height: 225px;'
}).appendTo(that.container);
};
return that;
};
IPA.cert.status_widget = function(spec) {
spec = spec || {};
var that = IPA.widget(spec);
that.entity_label = spec.entity_label || that.entity.metadata.label_singular;
that.result = spec.result;
that.get_entity_pkey = spec.get_entity_pkey;
that.get_entity_name = spec.get_entity_name;
that.get_entity_principal = spec.get_entity_principal;
that.get_entity_certificate = spec.get_entity_certificate;
that.is_selfsign = function() {
return IPA.env.ra_plugin == 'selfsign';
};
that.create = function(container) {
that.widget_create(container);
var div = $('<div/>', {
name: 'certificate-valid',
style: 'display: none;'
}).appendTo(container);
$('<img/>', {
src: 'images/check-icon.png',
style: 'float: left;',
'class': 'status-icon'
}).appendTo(div);
var content_div = $('<div/>', {
style: 'float: left;'
}).appendTo(div);
content_div.append('<b>'+IPA.messages.objects.cert.valid+':</b>');
content_div.append(' ');
$('<input/>', {
'type': 'button',
'name': 'get',
'value': IPA.messages.buttons.get
}).appendTo(content_div);
content_div.append(' ');
if (!that.is_selfsign()) {
$('<input/>', {
'type': 'button',
'name': 'revoke',
'value': IPA.messages.buttons.revoke
}).appendTo(content_div);
content_div.append(' ');
}
$('<input/>', {
'type': 'button',
'name': 'view',
'value': IPA.messages.buttons.view
}).appendTo(content_div);
content_div.append(' ');
$('<input/>', {
'type': 'button',
'name': 'create',
'value': IPA.messages.objects.cert.new_certificate
}).appendTo(content_div);
if (!that.is_selfsign()) {
div = $('<div/>', {
name: 'certificate-revoked',
style: 'display: none;'
}).appendTo(container);
$('<img/>', {
src: 'images/caution-icon.png',
style: 'float: left;',
'class': 'status-icon'
}).appendTo(div);
content_div = $('<div/>', {
style: 'float: left;'
}).appendTo(div);
content_div.append('<b>'+IPA.messages.objects.cert.revoked+':</b>');
content_div.append(' ');
content_div.append($('<span/>', {
'name': 'revocation_reason'
}));
content_div.append(' ');
$('<input/>', {
'type': 'button',
'name': 'restore',
'value': IPA.messages.buttons.restore
}).appendTo(content_div);
content_div.append(' ');
$('<input/>', {
'type': 'button',
'name': 'create',
'value': IPA.messages.objects.cert.new_certificate
}).appendTo(content_div);
}
div = $('<div/>', {
name: 'certificate-missing',
style: 'display: none;'
}).appendTo(container);
$('<img/>', {
src: 'images/caution-icon.png',
style: 'float: left;',
'class': 'status-icon'
}).appendTo(div);
content_div = $('<div/>', {
style: 'float: left;'
}).appendTo(div);
content_div.append('<b>'+IPA.messages.objects.cert.missing+':</b>');
content_div.append(' ');
$('<input/>', {
'type': 'button',
'name': 'create',
'value': IPA.messages.objects.cert.new_certificate
}).appendTo(content_div);
that.status_valid = $('div[name=certificate-valid]', that.container);
that.status_revoked = $('div[name=certificate-revoked]', that.container);
that.status_missing = $('div[name=certificate-missing]', that.container);
var button = $('input[name=get]', that.container);
that.get_button = IPA.button({
name: 'get',
label: IPA.messages.buttons.get,
click: function() {
IPA.command({
entity: that.entity.name,
method: 'show',
args: [that.pkey],
on_success: function(data, text_status, xhr) {
get_certificate(data.result.result);
}
}).execute();
return false;
}
});
button.replaceWith(that.get_button);
button = $('input[name=revoke]', that.container);
that.revoke_button = IPA.button({
name: 'revoke',
label: IPA.messages.buttons.revoke,
click: function() {
IPA.command({
entity: that.entity.name,
method: 'show',
args: [that.pkey],
on_success: function(data, text_status, xhr) {
revoke_certificate(data.result.result);
}
}).execute();
return false;
}
});
button.replaceWith(that.revoke_button);
button = $('input[name=view]', that.container);
that.view_button = IPA.button({
name: 'view',
label: IPA.messages.buttons.view,
click: function() {
IPA.command({
entity: that.entity.name,
method: 'show',
args: [that.pkey],
on_success: function(data, text_status, xhr) {
view_certificate(data.result.result);
}
}).execute();
return false;
}
});
button.replaceWith(that.view_button);
that.revocation_reason = $('span[name=revocation_reason]', that.container);
button = $('input[name=restore]', that.container);
that.restore_button = IPA.button({
name: 'restore',
label: IPA.messages.buttons.restore,
click: function() {
IPA.command({
entity: that.entity.name,
method: 'show',
args: [that.pkey],
on_success: function(data, text_status, xhr) {
restore_certificate(data.result.result);
}
}).execute();
return false;
}
});
button.replaceWith(that.restore_button);
$('input[name=create]', that.container).each(function(index) {
button = $(this);
that.create_button = IPA.button({
name: 'create',
label: IPA.messages.objects.cert.new_certificate,
click: function() {
request_certificate(that.result);
return false;
}
});
button.replaceWith(that.create_button);
});
};
that.load = function(result) {
that.result = result;
that.pkey = that.get_entity_pkey(that.result);
var entity_certificate = that.get_entity_certificate(that.result);
if (entity_certificate) {
check_status(that.result.serial_number);
} else {
set_status(IPA.cert.CERTIFICATE_STATUS_MISSING);
}
};
function set_status(status, revocation_reason) {
that.status_valid.css('display', status == IPA.cert.CERTIFICATE_STATUS_VALID ? 'inline' : 'none');
that.status_missing.css('display', status == IPA.cert.CERTIFICATE_STATUS_MISSING ? 'inline' : 'none');
if (!that.is_selfsign()) {
that.status_revoked.css('display', status == IPA.cert.CERTIFICATE_STATUS_REVOKED ? 'inline' : 'none');
that.revoke_button.css('display', status == IPA.cert.CERTIFICATE_STATUS_VALID ? 'inline' : 'none');
var reason = IPA.cert.CRL_REASON[revocation_reason];
that.revocation_reason.html(revocation_reason === undefined || reason === null ? '' : IPA.messages.objects.cert[reason]);
that.restore_button.css('display', reason == 'certificate_hold' ? 'inline' : 'none');
}
}
function check_status(serial_number) {
if (that.is_selfsign()) {
set_status(IPA.cert.CERTIFICATE_STATUS_VALID);
return;
}
IPA.command({
entity: 'cert',
method: 'show',
args: [serial_number],
on_success: function(data, text_status, xhr) {
var revocation_reason = data.result.result.revocation_reason;
if (revocation_reason == undefined) {
set_status(IPA.cert.CERTIFICATE_STATUS_VALID);
} else {
set_status(IPA.cert.CERTIFICATE_STATUS_REVOKED, revocation_reason);
}
}
}).execute();
}
function view_certificate(result) {
var entity_certificate = that.get_entity_certificate(result);
if (!entity_certificate) {
set_status(IPA.cert.CERTIFICATE_STATUS_MISSING);
return;
}
var entity_name = that.get_entity_name(result);
var title = IPA.messages.objects.cert.view_certificate;
title = title.replace('${entity}', that.entity_label);
title = title.replace('${primary_key}', entity_name);
var dialog = IPA.cert.view_dialog({
'title': title,
'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(IPA.cert.CERTIFICATE_STATUS_MISSING);
return;
}
var entity_name = that.get_entity_name(result);
var title = IPA.messages.objects.cert.view_certificate;
title = title.replace('${entity}', that.entity_label);
title = title.replace('${primary_key}', entity_name);
var dialog = IPA.cert.download_dialog({
title: title,
certificate: 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 title = IPA.messages.objects.cert.issue_certificate;
title = title.replace('${entity}', that.entity_label);
title = title.replace('${primary_key}', entity_name);
var dialog = IPA.cert.request_dialog({
'title': title,
'request': function(values) {
var request = values['request'];
IPA.command({
entity: 'cert',
method: 'request',
args: [request],
options: {
'principal': entity_principal
},
on_success: function(data, text_status, xhr) {
check_status(data.result.result.serial_number);
}
}).execute();
}
});
dialog.open();
}
function revoke_certificate(result) {
var entity_certificate = that.get_entity_certificate(result);
if (!entity_certificate) {
set_status(IPA.cert.CERTIFICATE_STATUS_MISSING);
return;
}
var entity_name = that.get_entity_name(result);
var serial_number = result['serial_number'];
var title = IPA.messages.objects.cert.revoke_certificate;
title = title.replace('${entity}', that.entity_label);
title = title.replace('${primary_key}', entity_name);
var dialog = IPA.cert.revoke_dialog({
'title': title,
'revoke': function(values) {
var reason = values['reason'];
IPA.command({
entity: 'cert',
method: 'revoke',
args: [serial_number],
options: {
'revocation_reason': reason
},
on_success: function(data, text_status, xhr) {
check_status(serial_number);
}
}).execute();
}
});
dialog.open();
}
function restore_certificate(result) {
var entity_certificate = that.get_entity_certificate(result);
if (!entity_certificate) {
set_status(IPA.cert.CERTIFICATE_STATUS_MISSING);
return;
}
var entity_name = that.get_entity_name(result);
var serial_number = result['serial_number'];
var title = IPA.messages.objects.cert.restore_certificate;
title = title.replace('${entity}', that.entity_label);
title = title.replace('${primary_key}', entity_name);
var dialog = IPA.cert.restore_dialog({
'title': title,
'restore': function(values) {
IPA.command({
entity: 'cert',
method: 'remove_hold',
args: [serial_number],
on_success: function(data, text_status, xhr) {
check_status(serial_number);
}
}).execute();
}
});
dialog.open();
}
return that;
};