URL: https://github.com/freeipa/freeipa/pull/549 Author: pvomacka Title: #549: WebUI: certmap match Action: synchronized
To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/549/head:pr549 git checkout pr549
From 8bb768e9acfd4442deb579c43f0f90cf16dafb37 Mon Sep 17 00:00:00 2001 From: Pavel Vomacka <pvoma...@redhat.com> Date: Mon, 16 Jan 2017 13:59:16 +0100 Subject: [PATCH 1/8] WebUI: Add possibility to set field always writable If field will have set attribute 'always_writable' to true, then 'no_update' flag will be ingored. Used in command user-{add,remove}-certmap which needs to be writable in WebUI and also needs to be omitted from user-mod command. Part of: https://fedorahosted.org/freeipa/ticket/6601 --- install/ui/src/freeipa/field.js | 43 +++++++++++++++++++++++++++++++++++++++- install/ui/src/freeipa/widget.js | 35 ++++++++++---------------------- 2 files changed, 52 insertions(+), 26 deletions(-) diff --git a/install/ui/src/freeipa/field.js b/install/ui/src/freeipa/field.js index d70a778..9f287dd 100644 --- a/install/ui/src/freeipa/field.js +++ b/install/ui/src/freeipa/field.js @@ -484,7 +484,16 @@ field.field = IPA.field = function(spec) { writable = false; } - if (that.metadata.flags && array.indexOf(that.metadata.flags, 'no_update') > -1) { + // In case that field has set always_writable attribute, then + // 'no_update' flag is ignored in WebUI. It is done because of + // commands like user-{add,remove}-certmap. They operate with user's + // attribute, which cannot be changed using user-mod, but only + // using command user-{add,remove}-certmap. Therefore it has set + // 'no_update' flag, but we need to show 'Add', 'Remove' buttons in + // WebUI. + if (that.metadata.flags && + array.indexOf(that.metadata.flags, 'no_update') > -1 && + !that.always_writable) { writable = false; } } @@ -1259,6 +1268,37 @@ field.certs_field = IPA.certs_field = function(spec) { return that; }; + +/** + * Used along with custom_command_multivalued widget + * + * - by default has `w_if_no_aci` to workaround missing object class + * - by default has always_writable=true to workaround aci rights + * + * @class + * @alternateClassName IPA.custom_command_multivalued_field + * @extends IPA.field + */ +field.certmap_command_multivalued_field = function(spec) { + + spec = spec || {}; + spec.flags = spec.flags || ['w_if_no_aci']; + + var that = IPA.field(spec); + + /** + * Set field always writable in case that it is set to true + * @param Boolean always_writable + */ + that.always_writable = spec.always_writable === undefined ? true : + spec.always_writable; + + return that; +}; + + +IPA.custom_command_multivalued_field = field.custom_command_multivalued_field; + /** * SSH Keys Adapter * @class @@ -1652,6 +1692,7 @@ field.register = function() { f.register('checkbox', field.checkbox_field); f.register('checkboxes', field.field); f.register('combobox', field.field); + f.register('certmap_multivalued', field.certmap_command_multivalued_field); f.register('datetime', field.datetime_field); f.register('enable', field.enable_field); f.register('entity_select', field.field); diff --git a/install/ui/src/freeipa/widget.js b/install/ui/src/freeipa/widget.js index 15f0126..b7028a9 100644 --- a/install/ui/src/freeipa/widget.js +++ b/install/ui/src/freeipa/widget.js @@ -1534,12 +1534,8 @@ IPA.custom_command_multivalued_widget = function(spec) { * Called on error of add command. Override point. */ that.on_error_add = function(xhr, text_status, error_thrown) { - that.adder_dialog.focus_first_element(); - - if (error_thrown.message) { - var msg = error_thrown.message; - IPA.notify(msg, 'error'); - } + that.adder_dialog.show(); + exp.focus_invalid(that.adder_dialog); }; /** @@ -1599,27 +1595,16 @@ IPA.custom_command_multivalued_widget = function(spec) { name: 'custom-add-dialog' }; - that.adder_dialog = IPA.dialog(spec); - that.adder_dialog.create_button({ - name: 'add', - label: '@i18n:buttons.add', - click: function() { - if (!that.adder_dialog.validate()) { - exp.focus_invalid(that.adder_dialog); - } - else { - that.add(that.adder_dialog); - } + spec.on_ok = function() { + if (!that.adder_dialog.validate()) { + exp.focus_invalid(that.adder_dialog); } - }); - - that.adder_dialog.create_button({ - name: 'cancel', - label: '@i18n:buttons.cancel', - click: function() { - that.adder_dialog.close(); + else { + that.add(that.adder_dialog); } - }); + }; + + that.adder_dialog = IPA.custom_command_multivalued_dialog(spec); }; /* on button 'Add' on adder dialog click */ From 2c6e870f9e58e9eb2ca5aabff0be3e2cbefdc548 Mon Sep 17 00:00:00 2001 From: Pavel Vomacka <pvoma...@redhat.com> Date: Mon, 16 Jan 2017 14:13:42 +0100 Subject: [PATCH 2/8] WebUI: Create non editable row widget for mutlivalued widget Old krb-principal widget is changed to general one. And used also for ipacertmapdata in user. This widget make every line non-editable. Part of: https://fedorahosted.org/freeipa/ticket/6601 --- install/ui/src/freeipa/host.js | 3 ++- install/ui/src/freeipa/service.js | 3 ++- install/ui/src/freeipa/user.js | 3 ++- install/ui/src/freeipa/widget.js | 29 +++++++++++++++++++---------- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/install/ui/src/freeipa/host.js b/install/ui/src/freeipa/host.js index 1dfe05e..a09535c 100644 --- a/install/ui/src/freeipa/host.js +++ b/install/ui/src/freeipa/host.js @@ -93,7 +93,8 @@ return { name: 'krbprincipalname', item_name: 'principal', child_spec: { - $type: 'krb_principal' + $type: 'non_editable_row', + data_name: 'krb-principal' } }, { diff --git a/install/ui/src/freeipa/service.js b/install/ui/src/freeipa/service.js index 2533ad0..10f86ce 100644 --- a/install/ui/src/freeipa/service.js +++ b/install/ui/src/freeipa/service.js @@ -81,7 +81,8 @@ return { name: 'krbprincipalname', item_name: 'principal', child_spec: { - $type: 'krb_principal' + $type: 'non_editable_row', + data_name: 'krb-principal' } }, { diff --git a/install/ui/src/freeipa/user.js b/install/ui/src/freeipa/user.js index 628cf8e..8f78f2b 100644 --- a/install/ui/src/freeipa/user.js +++ b/install/ui/src/freeipa/user.js @@ -192,7 +192,8 @@ return { name: 'krbprincipalname', item_name: 'principal', child_spec: { - $type: 'krb_principal' + $type: 'non_editable_row', + data_name: 'krb-principal' } }, { diff --git a/install/ui/src/freeipa/widget.js b/install/ui/src/freeipa/widget.js index b7028a9..17b1376 100644 --- a/install/ui/src/freeipa/widget.js +++ b/install/ui/src/freeipa/widget.js @@ -1792,6 +1792,8 @@ IPA.custom_command_multivalued_widget = function(spec) { IPA.krb_principal_multivalued_widget = function (spec) { spec = spec || {}; + spec.child_spec = spec.child_spec || {}; + spec.child_spec.data_name = spec.child_spec.data_name || 'krb-principal'; spec.adder_dialog_spec = spec.adder_dialog_spec || { title: '@i18n:krbaliases.adder_title', @@ -1812,7 +1814,7 @@ IPA.krb_principal_multivalued_widget = function (spec) { that.create_remove_dialog_message = function(row) { var message = text.get('@i18n:krbaliases.remove_message'); - message = message.replace('${alias}', row.widget.principal_name); + message = message.replace('${alias}', row.widget.new_value); return message; }; @@ -1820,7 +1822,7 @@ IPA.krb_principal_multivalued_widget = function (spec) { that.create_remove_args = function(row) { var pkey = that.facet.get_pkey(); - var krbprincipalname = row.widget.principal_name; + var krbprincipalname = row.widget.new_value; krbprincipalname = [ krbprincipalname ]; var args = [ @@ -1847,22 +1849,27 @@ IPA.krb_principal_multivalued_widget = function (spec) { }; /** - * Widget which is used as row in kerberos aliases multivalued widget. - * It contains only string where is the principal alias name and delete button. + * Widget which is used as row in multivalued widget. Each row is just + * non-editable text field. * * @class * @extends IPA.input_widget */ -IPA.krb_principal_widget = function(spec) { +IPA.non_editable_row_widget = function(spec) { spec = spec || {}; var that = IPA.input_widget(); + /** + * Prefix of CSS class of each row. + */ + that.data_name = spec.data_name || 'non-editable'; + that.create = function(container) { that.widget_create(container); - that.principal_text = $('<span />', { - 'class': 'krb-principal-name', + that.data_text = $('<span />', { + 'class': that.data_name + '-data', text: '' }).appendTo(container); @@ -1875,19 +1882,20 @@ IPA.krb_principal_widget = function(spec) { that.update = function(value) { - var principal_name = value[0] || ''; + var single_value = value[0] || ''; - that.principal_name = principal_name; + that.new_value = single_value; that.update_text(); }; that.update_text = function() { - that.principal_text.text(that.principal_name); + that.data_text.text(that.new_value); }; return that; }; + /** * Option widget base * @@ -7161,6 +7169,7 @@ exp.register = function() { w.register('html', IPA.html_widget); w.register('link', IPA.link_widget); w.register('multivalued', IPA.multivalued_widget); + w.register('non_editable_row', IPA.non_editable_row_widget); w.register('custom_command_multivalued', IPA.custom_command_multivalued_widget); w.register('krb_principal_multivalued', From 40d5ffd0c597a1b9e2bfe4b352a5e6746d876eef Mon Sep 17 00:00:00 2001 From: Pavel Vomacka <pvoma...@redhat.com> Date: Mon, 27 Feb 2017 18:12:29 +0100 Subject: [PATCH 3/8] WebUI: Add Custom command multivalued adder dialog Adder dialog which is used along with custom_command_multivalued_widget. It behaivor of confirm dialog and adds fields which are necessary. Part of: https://fedorahosted.org/freeipa/ticket/6601 --- install/ui/src/freeipa/dialog.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/install/ui/src/freeipa/dialog.js b/install/ui/src/freeipa/dialog.js index 8552e76..5150527 100644 --- a/install/ui/src/freeipa/dialog.js +++ b/install/ui/src/freeipa/dialog.js @@ -1451,6 +1451,40 @@ IPA.confirm_dialog = function(spec) { return that; }; +/** + * Custom command multivalued dialog + * + * Combines confirmation dialog which could be reopen after unsucessful command + * call. It also allows to define fields and widgets in the dialog. + * + * Acceptation is done by clicking on 'Add' button or hitting 'ENTER' key, + * refusal by clicking on 'Cancel' button or hitting 'ESCAPE' key. + * + * @class + * @extends IPA.form_dialog + */ +IPA.custom_command_multivalued_dialog = function(spec) { + spec = spec || {}; + + /** + * Name of confirmation button, by default set to 'Add'. + * @param {String} ok_label + */ + spec.ok_label = spec.ok_label || '@i18n:buttons.add'; + + var that = IPA.form_dialog(spec); + + that.close = function() { + that.dialog_close(); + }; + + that.on_confirm = function() { + if (that.on_ok) that.on_ok(); + }; + + return that; +}; + /** * From 8ee38cfdcf4f3f6f003547fa1925fe61e99e424e Mon Sep 17 00:00:00 2001 From: Pavel Vomacka <pvoma...@redhat.com> Date: Mon, 16 Jan 2017 14:16:47 +0100 Subject: [PATCH 4/8] WebUI: Add certmap module Add facets for certmaprule and certmapconfigure entities. https://fedorahosted.org/freeipa/ticket/6601 --- install/ui/src/freeipa/app.js | 1 + install/ui/src/freeipa/navigation/menu_spec.js | 16 +- install/ui/src/freeipa/plugins/certmap.js | 381 +++++++++++++++++++++++++ install/ui/src/freeipa/stageuser.js | 12 + install/ui/src/freeipa/user.js | 12 + ipaserver/plugins/internal.py | 12 + 6 files changed, 433 insertions(+), 1 deletion(-) create mode 100644 install/ui/src/freeipa/plugins/certmap.js diff --git a/install/ui/src/freeipa/app.js b/install/ui/src/freeipa/app.js index 4eb045d..d262a64 100644 --- a/install/ui/src/freeipa/app.js +++ b/install/ui/src/freeipa/app.js @@ -32,6 +32,7 @@ define([ './plugins/ca', './plugins/caacl', './plugins/certprofile', + './plugins/certmap', './dns', './group', './hbac', diff --git a/install/ui/src/freeipa/navigation/menu_spec.js b/install/ui/src/freeipa/navigation/menu_spec.js index 0e717db..5f1d388 100644 --- a/install/ui/src/freeipa/navigation/menu_spec.js +++ b/install/ui/src/freeipa/navigation/menu_spec.js @@ -166,7 +166,21 @@ var nav = {}; ] }, { entity: 'otptoken' }, - { entity: 'radiusproxy' } + { entity: 'radiusproxy' }, + { + entity: 'certmaprule', + facet: 'search', + children: [ + { + entity: 'certmaprule', + facet: 'search' + }, + { + entity: 'certmapconfig', + facet: 'details' + } + ] + } ] }, { diff --git a/install/ui/src/freeipa/plugins/certmap.js b/install/ui/src/freeipa/plugins/certmap.js new file mode 100644 index 0000000..ddbc5a7 --- /dev/null +++ b/install/ui/src/freeipa/plugins/certmap.js @@ -0,0 +1,381 @@ +// +// Copyright (C) 2017 FreeIPA Contributors see COPYING for license +// + + +define([ + 'dojo/_base/lang', + 'dojo/_base/declare', + 'dojo/Evented', + 'dojo/on', + '../navigation', + '../field', + '../ipa', + '../phases', + '../reg', + '../widget', + '../text', + '../util', + // plain imports + '../search', + '../entity'], + function(lang, declare, Evented, on, navigation, mod_field, IPA, + phases, reg, widget_mod, text, util) { +/** + * Certificate map module + * @class + */ +var certmap = IPA.certmap = { + + search_facet_group: { + facets: { + certmaprule_search: 'certmaprule_search', + certmapconfig: 'certmapconfig_details' + } + } +}; + +var make_certmaprule_spec = function() { +return { + name: 'certmaprule', + facets: [ + { + $type: 'search', + always_request_members: true, + details_facet: 'details', + facet_groups: [certmap.search_facet_group], + facet_group: 'search', + row_enabled_attribute: 'ipaenabledflag', + columns: [ + 'cn', + { + name: 'ipaenabledflag', + label: '@i18n:status.label', + formatter: 'boolean_status' + }, + 'description' + ], + actions: [ + 'batch_disable', + 'batch_enable' + ], + control_buttons: [ + { + name: 'disable', + label: '@i18n:buttons.disable', + icon: 'fa-minus' + }, + { + name: 'enable', + label: '@i18n:buttons.enable', + icon: 'fa-check' + } + ] + }, + { + $type: 'details', + disable_facet_tabs: true, + facet_groups: [certmap.search_facet_group], + facet_group: 'search', + actions: [ + 'enable', + 'disable', + 'delete' + ], + header_actions: ['enable', 'disable', 'delete'], + state: { + evaluators: [ + { + $factory: IPA.enable_state_evaluator, + field: 'ipaenabledflag' + } + ] + }, + sections: [ + { + name: 'details', + fields: [ + 'cn', + { + $type: 'textarea', + name: 'description' + }, + { + name: 'ipacertmapmaprule', + tooltip: { + title: '@mc-opt:certmaprule_add:ipacertmapmaprule:doc' + } + }, + { + name: 'ipacertmapmatchrule', + tooltip: { + title: '@mc-opt:certmaprule_add:ipacertmapmatchrule:doc' + } + }, + { + $type: 'multivalued', + name: 'associateddomain', + tooltip: { + title: '@mc-opt:certmaprule_add:associateddomain:doc' + } + }, + { + name: 'ipacertmappriority', + tooltip: { + title: '@mc-opt:certmaprule_add:ipacertmappriority:doc' + } + } + ] + } + ] + } + ], + adder_dialog: { + fields: [ + 'cn', + { + name: 'ipacertmapmaprule', + tooltip: { + title: '@mc-opt:certmaprule_add:ipacertmapmaprule:doc' + } + }, + { + name: 'ipacertmapmatchrule', + tooltip: { + title: '@mc-opt:certmaprule_add:ipacertmapmatchrule:doc' + } + }, + { + $type: 'multivalued', + name: 'associateddomain', + tooltip: { + title: '@mc-opt:certmaprule_add:associateddomain:doc' + } + }, + { + name: 'ipacertmappriority', + tooltip: { + title: '@mc-opt:certmaprule_add:ipacertmappriority:doc' + } + }, + { + $type: 'textarea', + name: 'description' + } + ] + } +};}; + + +var make_certmapconfig_spec = function() { +return { + name: 'certmapconfig', + defines_key: false, + facets: [ + { + $type: 'details', + facet_groups: [certmap.search_facet_group], + facet_group: 'search', + sections: [ + { + name: 'details', + fields: [ + { + $type: 'checkbox', + name: 'ipacertmappromptusername' + } + ] + } + ] + } + ] +};}; + + +/** + * Multivalued widget which is used for working with user's certmap. + * + * @class + * @extends IPA.custom_command_multivalued_widget + */ +certmap.certmap_multivalued_widget = function (spec) { + + spec = spec || {}; + spec.adder_dialog_spec = spec.adder_dialog_spec || { + name: 'custom-add-dialog', + title: '@i18n:objects.certmap.adder_title', + policies: [ + { + $factory: IPA.multiple_choice_section_policy, + widget: 'type' + } + ], + fields: [ + { + $type: 'multivalued', + name: 'ipacertmapdata', + label: '@i18n:objects.certmap.data_label', + widget: 'type.ipacertmapdata' + }, + { + $type: 'multivalued', + name: 'certificate', + label: '@i18n:objects.certmap.certificate', + widget: 'type.certificate', + child_spec: { + $type: 'textarea' + } + }, + { + name: 'issuer', + label: '@i18n:objects.certmap.issuer', + widget: 'type.issuer' + }, + { + name: 'subject', + label: '@i18n:objects.certmap.subject', + widget: 'type.subject' + } + ], + widgets: [ + { + $type: 'multiple_choice_section', + name: 'type', + choices: [ + { + name: 'data', + label: '@i18n:objects.certmap.data_label', + fields: ['ipacertmapdata', 'certificate'], + required: [], + enabled: true + }, + { + name: 'issuer_subj', + label: '@i18n:objects.certmap.issuer_subject', + fields: ['issuer', 'subject'], + required: ['issuer', 'subject'] + } + ], + widgets: [ + { + $type: 'multivalued', + name: 'ipacertmapdata' + }, + { + $type: 'multivalued', + name: 'certificate', + child_spec: { + $type: 'textarea' + }, + tooltip: { + title: '@mc-opt:user_add_certmapdata:certificate:doc' + } + }, + { + name: 'issuer', + tooltip: { + title: '@mc-opt:user_add_certmapdata:issuer:doc' + } + }, + { + name: 'subject', + tooltip: { + title: '@mc-opt:user_add_certmapdata:subject:doc' + } + } + ] + } + ] + }; + + var that = IPA.custom_command_multivalued_widget(spec); + + that.create_remove_dialog_title = function(row) { + return text.get('@i18n:objects.certmap.deleter_title'); + }; + + that.create_remove_dialog_message = function(row) { + var message = text.get('@i18n:objects.certmap.deleter_content'); + message = message.replace('${data}', row.widget.new_value); + + return message; + }; + + /** + * Compose options for add command. + * @return {Object} options + */ + that.create_add_options = function() { + var options = {}; + var widgets = that.adder_dialog.widgets.get_widgets(); + var widget = widgets[0]; + var inner_widgets = widget.widgets.get_widgets(); + + for (var i = 0, l = inner_widgets.length; i<l; i++) { + var w = inner_widgets[i]; + + if (w.enabled) { + var field = that.adder_dialog.fields.get_field(w.name); + var value = field.save(); + + if (field.name === 'issuer' || field.name === 'subject') { + value = value[0]; + } + + if (!util.is_empty(value)) options[field.name] = value; + } + } + + return options; + }; + + + /** + * Compose options for remove command. + * + * @param {Object} row + * @return {Object} options + */ + that.create_remove_options = function(row) { + var options = {}; + var data = row.widget.new_value; + + options['ipacertmapdata'] = data; + + return options; + }; + + return that; +}; + +/** + * Certificat Mapping Rules entity specification object + * @member certmap + */ +certmap.certmaprule_spec = make_certmaprule_spec(); + +/** + * Certificate Mapping Configuration entity specification object + * @member certmap + */ +certmap.certmapconfig_spec = make_certmapconfig_spec(); + + +/** + * Register entity + * @member cermap + */ +certmap.register = function() { + var e = reg.entity; + var w = reg.widget; + + e.register({type: 'certmaprule', spec: certmap.certmaprule_spec}); + e.register({type: 'certmapconfig', spec: certmap.certmapconfig_spec}); + w.register('certmap_multivalued', + certmap.certmap_multivalued_widget); +}; + +phases.on('registration', certmap.register); + +return certmap; +}); diff --git a/install/ui/src/freeipa/stageuser.js b/install/ui/src/freeipa/stageuser.js index bf24491..f456189 100644 --- a/install/ui/src/freeipa/stageuser.js +++ b/install/ui/src/freeipa/stageuser.js @@ -147,6 +147,18 @@ return { label: '@i18n:objects.sshkeystore.keys' }, { + $type: 'certmap_multivalued', + name: 'ipacertmapdata', + item_name: 'certmapdata', + child_spec: { + $type: 'non_editable_row', + data_name: 'certmap' + }, + tooltip: { + title: '@mc:stageuser_add_certmapdata.doc' + } + }, + { $type: 'checkboxes', name: 'ipauserauthtype', flags: ['w_if_no_aci'], diff --git a/install/ui/src/freeipa/user.js b/install/ui/src/freeipa/user.js index 8f78f2b..4bb0448 100644 --- a/install/ui/src/freeipa/user.js +++ b/install/ui/src/freeipa/user.js @@ -218,6 +218,18 @@ return { label: '@i18n:objects.cert.certificates' }, { + $type: 'certmap_multivalued', + name: 'ipacertmapdata', + item_name: 'certmapdata', + child_spec: { + $type: 'non_editable_row', + data_name: 'certmap' + }, + tooltip: { + title: '@mc:user_add_certmapdata.doc' + } + }, + { $type: 'checkboxes', name: 'ipauserauthtype', flags: ['w_if_no_aci'], diff --git a/ipaserver/plugins/internal.py b/ipaserver/plugins/internal.py index 7084d54..acd417b 100644 --- a/ipaserver/plugins/internal.py +++ b/ipaserver/plugins/internal.py @@ -464,6 +464,18 @@ class i18n_messages(Command): "view_certificate": _("Certificate for ${entity} ${primary_key}"), "view_certificate_btn": _("View Certificate"), }, + "certmap": { + "adder_title": _("Add Certificate Mapping Data"), + "data_label": _("Certificate mapping data"), + "certificate": _("Certificate"), + "conf_str": _("Configuration string"), + "deleter_content": _("Do you want to remove certificate mapping data ${data}?"), + "deleter_title": _("Remove Certificate Mapping Data"), + "issuer": _("Issuer"), + "issuer_subject": _("Issuer and subject"), + "subject": _("Subject"), + "version": _("Version"), + }, "config": { "group": _("Group Options"), "search": _("Search Options"), From 552eeaf272ad5dea857e08c990903c02e6fad9b6 Mon Sep 17 00:00:00 2001 From: Pavel Vomacka <pvoma...@redhat.com> Date: Tue, 7 Mar 2017 21:28:32 +0100 Subject: [PATCH 5/8] WebUI: Add possibility to turn of autoload when details.load is called When field on details facet has set 'autoload_value' to false, then it won't be loaded using that.load method of details facet. That means that field might stay unchanged even that loading of data was performed. Part of: https://pagure.io/freeipa/issue/6601 --- install/ui/src/freeipa/details.js | 3 ++- install/ui/src/freeipa/field.js | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/install/ui/src/freeipa/details.js b/install/ui/src/freeipa/details.js index 9f0e632..87b355a 100644 --- a/install/ui/src/freeipa/details.js +++ b/install/ui/src/freeipa/details.js @@ -743,7 +743,8 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { var fields = that.fields.get_fields(); for (var i=0; i<fields.length; i++) { var field = fields[i]; - field.load(data); + + if (field.autoload_value) field.load(data); } that.policies.post_load(data); that.post_load.notify([data], that); diff --git a/install/ui/src/freeipa/field.js b/install/ui/src/freeipa/field.js index 9f287dd..4a63242 100644 --- a/install/ui/src/freeipa/field.js +++ b/install/ui/src/freeipa/field.js @@ -196,6 +196,14 @@ field.field = IPA.field = function(spec) { that.required = spec.required; /** + * Turns off loading value from command output on details pages. + * Used in certmap_match. + * @property {boolean} + */ + that.autoload_value = spec.autoload_value === undefined ? true : + spec.autoload_value; + + /** * read_only is set when widget is created * @readonly * @property {boolean} From d20cc327739533f02c94375711817fb56354bfb5 Mon Sep 17 00:00:00 2001 From: Pavel Vomacka <pvoma...@redhat.com> Date: Tue, 7 Mar 2017 21:30:00 +0100 Subject: [PATCH 6/8] WebUI: Possibility to choose object when API call returns list of objects In case that API call returns array of objects which contains data, using 'object_index' attribute in adapter specification we can set which object should be used. It is possible to choose only one object specified by its index in array. Part of: https://pagure.io/freeipa/issue/6601 --- install/ui/src/freeipa/field.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/install/ui/src/freeipa/field.js b/install/ui/src/freeipa/field.js index 4a63242..3b6b97b 100644 --- a/install/ui/src/freeipa/field.js +++ b/install/ui/src/freeipa/field.js @@ -819,6 +819,15 @@ field.Adapter = declare(null, { result_index: 0, /** + * When result of API call is an array of object this object index + * allows to specify exact object in array according to its position. + * Default value is null which means do not use object_index. + * + * @type {Number|null} + */ + object_index: null, + + /** * Name of the record which we want to extract from the result. * Used in dnslocations. * @type {String} @@ -849,6 +858,10 @@ field.Adapter = declare(null, { else if (dr.results) { var result = dr.results[this.result_index]; if (result) record = result[this.result_name]; + var res_type = typeof record; + var obj_in_type = typeof this.object_index; + if (res_type === 'object' && obj_in_type === 'number') + record = record[this.object_index]; } } return record; From f5cdd29d7e949ff123b0badb32e18d458f3a02f2 Mon Sep 17 00:00:00 2001 From: Pavel Vomacka <pvoma...@redhat.com> Date: Tue, 7 Mar 2017 21:30:45 +0100 Subject: [PATCH 7/8] WebUI: Add Adapter for certmap_match result table Result of certmap_match command is in the following format: [{domain: 'domain1', uid:[uid11,uid12,uid13]}, {domain: 'domain2', uid:[uid21, uid22, uid23},...] For correct displaying in table we need to reformat it to the following: [{domain: 'domain1', uid: 'uid11'}, {domain: 'domain1', uid: 'uid12'},... This can be done using this Adapter. Part of: https://pagure.io/freeipa/issue/6601 --- install/ui/src/freeipa/field.js | 79 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/install/ui/src/freeipa/field.js b/install/ui/src/freeipa/field.js index 3b6b97b..dde2837 100644 --- a/install/ui/src/freeipa/field.js +++ b/install/ui/src/freeipa/field.js @@ -1462,6 +1462,84 @@ field.AlternateAttrFieldAdapter = declare([field.Adapter], { /** + * Custom adapter specifically implemented for certmap_match where it + * transform items in format {domain: "xxx", uid: [arrayof_uids]} to + * {[{domain: "xxx", uid: "uid1"}, {domain: "xxx", uid: 'uid2'}, ...]}. + * This is necessary for possibility to correctly display table. + * + * @class + * @extends field.Adapter + */ +field.CertMatchTransformAdapter = declare([field.Adapter], { + + /** + * @param {Array} record + */ + transform_one_record: function(record) { + var domain = record.domain; + var uids = record.uid; + var results = []; + + for (var i=0, l=uids.length; i<l; i++) { + results.push({ + domain: domain, + uid: uids[i] + }); + } + + return results; + }, + + /** + * Transform record to array of arrays with objects in the following format: + * {domain: 'xxx', uid: 'uid1'} + * + * @param {Array|Object} record + */ + transform_record: function(record) { + if (lang.isArray(record)) { + for (var i=0, l=record.length; i<l; i++) { + record[i] = this.transform_one_record(record[i]); + } + } else { + record = this.transform_one_record(record); + } + }, + + /** + * Merge array of arrays of object into array of objects. + * + * @param {Array} records + */ + merge_object_into_array: function(records) { + if (!lang.isArray(records)) return records; + + var merged = []; + for (var i=0, l=records.length; i<l; i++) { + merged = merged.concat(records[i]); + } + + return merged; + }, + + /** + * + * @param {Object} data Object which contains the record or the record + * @returns {Array} attribute values + */ + load: function(data) { + var record = this.get_record(data); + + this.transform_record(record); + + var values = this.merge_object_into_array(record); + + return values; + } +}); + + +/** * Field for enabling/disabling entity * * - expects radio widget @@ -1735,6 +1813,7 @@ field.register = function() { l.register('adapter', field.Adapter); l.register('object_adapter', field.ObjectAdapter); l.register('alternate_attr_field_adapter', field.AlternateAttrFieldAdapter); + l.register('certmatch_transform', field.CertMatchTransformAdapter); }; phases.on('registration', field.register); From 1b4f8dce6846ab65412a56c80368452684c5f7b5 Mon Sep 17 00:00:00 2001 From: Pavel Vomacka <pvoma...@redhat.com> Date: Tue, 7 Mar 2017 21:31:22 +0100 Subject: [PATCH 8/8] WebUI: Add cermapmatch module Add module which can show users which are mapped to the provided certificate. Additionaly, the certificate is parsed and parsed information are also displayed. https://pagure.io/freeipa/issue/6601 --- install/ui/src/freeipa/app.js | 1 + install/ui/src/freeipa/navigation/menu_spec.js | 4 + install/ui/src/freeipa/plugins/certmap.js | 3 +- install/ui/src/freeipa/plugins/certmapmatch.js | 387 +++++++++++++++++++++++++ ipaserver/plugins/internal.py | 12 + 5 files changed, 406 insertions(+), 1 deletion(-) create mode 100644 install/ui/src/freeipa/plugins/certmapmatch.js diff --git a/install/ui/src/freeipa/app.js b/install/ui/src/freeipa/app.js index d262a64..5e70dba 100644 --- a/install/ui/src/freeipa/app.js +++ b/install/ui/src/freeipa/app.js @@ -33,6 +33,7 @@ define([ './plugins/caacl', './plugins/certprofile', './plugins/certmap', + './plugins/certmapmatch', './dns', './group', './hbac', diff --git a/install/ui/src/freeipa/navigation/menu_spec.js b/install/ui/src/freeipa/navigation/menu_spec.js index 5f1d388..8d13da1 100644 --- a/install/ui/src/freeipa/navigation/menu_spec.js +++ b/install/ui/src/freeipa/navigation/menu_spec.js @@ -178,6 +178,10 @@ var nav = {}; { entity: 'certmapconfig', facet: 'details' + }, + { + label: '@i18n:objects.certmap_match.facet_label', + entity: 'certmap_match' } ] } diff --git a/install/ui/src/freeipa/plugins/certmap.js b/install/ui/src/freeipa/plugins/certmap.js index ddbc5a7..ecbe095 100644 --- a/install/ui/src/freeipa/plugins/certmap.js +++ b/install/ui/src/freeipa/plugins/certmap.js @@ -30,7 +30,8 @@ var certmap = IPA.certmap = { search_facet_group: { facets: { certmaprule_search: 'certmaprule_search', - certmapconfig: 'certmapconfig_details' + certmapconfig: 'certmapconfig_details', + certmapmatch: 'certmapmatch_details' } } }; diff --git a/install/ui/src/freeipa/plugins/certmapmatch.js b/install/ui/src/freeipa/plugins/certmapmatch.js new file mode 100644 index 0000000..0791c53 --- /dev/null +++ b/install/ui/src/freeipa/plugins/certmapmatch.js @@ -0,0 +1,387 @@ +// +// Copyright (C) 2017 FreeIPA Contributors see COPYING for license +// + +define([ + 'dojo/_base/lang', + 'dojo/_base/declare', + 'dojo/Evented', + 'dojo/on', + '../metadata', + '../ipa', + '../phases', + '../reg', + '../rpc', + '../widget', + '../util', + // plain imports + '../search', + '../entity'], + function(lang, declare, Evented, on, metadata_provider, IPA, phases, + reg, rpc, widget_mod, util ) { + +var certmapmatch = IPA.certmapmatch = {}; + +var make_certmap_spec = function() { +return { + name: 'certmap_match', + facets: [ + { + + $factory: certmapmatch.details_certmapmatch_facet, + disable_breadcrumb: true, + no_update: true, + name: 'cert', + label: "@i18n:objects.certmap_match.facet_label", + actions: [ + 'match', 'clear' + ], + control_buttons: [ + { + name: 'match', + title: '@i18n:buttons.match_title', + label: '@i18n:buttons.match', + icon: 'fa-gear' + }, + { + name: 'clear', + title: '@i18n:buttons.clear_title', + label: '@i18n:buttons.clear', + icon: 'fa-refresh' + } + ], + sections: [ + { + name: 'cert_input', + label: '@i18n:objects.certmap_match.cert_for_match', + fields: [ + { + $type: 'cert_textarea', + name: 'cert_textarea', + label: '@i18n:objects.cert.certificate', + autoload_value: false, + undo: false, + rows: 20, + cols: 70 + } + ] + }, + { + name: 'parsed_cert', + label: '@i18n:objects.certmap_match.cert_data', + fields: [ + { + name: 'issuer', + label: '@i18n:objects.cert.issued_by', + adapter: { + object_index: 0, + result_index: 1 + }, + read_only: true + }, + { + name: 'subject', + label: '@i18n:objects.cert.issued_to', + adapter: { + object_index: 0, + result_index: 1 + }, + read_only: true + }, + { + name: 'serial_number', + label: '@i18n:objects.cert.serial_number', + adapter: { + object_index: 0, + result_index: 1 + }, + read_only: true + }, + { + name: 'serial_number_hex', + label: '@i18n:objects.cert.serial_number_hex', + adapter: { + object_index: 0, + result_index: 1 + }, + read_only: true + }, + { + name: 'valid_not_before', + label: '@i18n:objects.cert.valid_from', + adapter: { + object_index: 0, + result_index: 1 + }, + read_only: true + }, + { + name: 'valid_not_after', + label: '@i18n:objects.cert.valid_to', + adapter: { + object_index: 0, + result_index: 1 + }, + read_only: true + }, + { + name: 'sha1_fingerprint', + label: '@i18n:objects.cert.sha1_fingerprint', + adapter: { + object_index: 0, + result_index: 1 + }, + read_only: true + }, + { + name: 'sha256_fingerprint', + label: '@i18n:objects.cert.sha256_fingerprint', + adapter: { + object_index: 0, + result_index: 1 + }, + read_only: true + } + ] + }, + { + $factory: IPA.section, + name: 'divider', + layout_css_class: 'col-sm-12 col-sm-12', + fields: [] + }, + { + name: 'user_result_table', + label: '@i18n:objects.certmap_match.matched_users', + layout: { + $factory: widget_mod.fluid_layout, + widget_cls: "col-sm-12 col-sm-12", + label_cls: "hide" + }, + layout_css_class: 'col-md-12 col-sm-12', + fields: [ + { + $type: 'association_table', + name: 'result_table', + read_only: true, + selectable: false, + other_entity: 'user', + adapter: { + $type: 'certmatch_transform' + }, + columns: [ + { + name: 'uid', + label: '@i18n:objects.certmap_match.userlogin' + }, + { + name: 'domain', + label: '@i18n:objects.certmap_match.domain' + } + ] + } + ] + } + ] + } + ] +};}; + + +/** + * Artificial entity created from command which does not have its own entity + * + * @class certmapmatch.certmapmatch_entity + * @extends IPA.entity + */ +certmapmatch.certmapmatch_entity = function(spec) { + var that = IPA.entity(spec); + + that.get_default_metadata = function() { + return metadata_provider.get('@mc:'+that.name); + }; + + return that; +}; + +/** + * Custom facet which is used for showing certmap match information + * + * @class certmapmatch.details_certmapmatch_facet + * @extends IPA.details_facet + */ +certmapmatch.details_certmapmatch_facet = function(spec) { + + spec = spec || {}; + + var that = IPA.details_facet(spec); + + that.refresh = function() {}; + + // always not dirty + that.is_dirty = function() { + return false; + }; + + that.get_result_table_widget = function() { + return that.widgets.get_widget("user_result_table.result_table"); + }; + + that.update_result_table_summary = function(summary) { + var result_w = that.get_result_table_widget(); + result_w.summary.text(summary); + }; + + that.clean_result = function() { + var result_w = that.get_result_table_widget(); + result_w.empty(); + that.update_result_table_summary(''); + }; + + that.clean_cert_info = function() { + var widgets = that.widgets.get_widget('parsed_cert').widgets.get_widgets(); + + for (var i=0, l=widgets.length; i<l; i++) { + var widget = widgets[i]; + + widget.update(); + } + }; + + that.obtain_cert = function() { + var cert_w = that.widgets.get_widget('cert_input.cert_textarea'); + + return cert_w.save(); + }; + + that.on_cert_match = function(data) { + that.clean_result(); + that.clean_cert_info(); + var cert = that.obtain_cert(); + + if (util.is_empty(cert)) return; + + var batch_command = rpc.batch_command({ + name: 'certmap-match-batch', + show_error: false + }); + + var command = rpc.command({ + method: 'certmap_match', + args: cert + }); + + batch_command.add_command(command); + + command = rpc.command({ + entity: 'cert', + method: 'find', + options: { + certificate: cert[0], + all: true + } + }); + + batch_command.add_command(command); + + batch_command.on_success = function(data, text_status, xhr) { + // Error handling needs to be here because cert-find never fails, + // therefore batch_command always calls on_success method. + var certmatch_r = data.result.results[0]; + if (certmatch_r.error === null) { + //no error + that.load(data); + that.update_result_table_summary(certmatch_r.summary); + IPA.notify_success(certmatch_r.summary); + } else { + that.update_result_table_summary(certmatch_r.error); + IPA.notify(certmatch_r.error, 'error'); + } + }; + + batch_command.execute(); + }; + + that.on_clear_facet = function() { + that.reset(); + that.clean_result(); + that.clean_cert_info(); + }; + + that.init = function() { + on(that, 'cert-match', that.on_cert_match); + on(that, 'clear-facet', that.on_clear_facet); + }; + + return that; +}; + +/** + * Action which run certmap match. + * + * @class certmapmatch.match_action + * @extends IPA.object_action + */ +certmapmatch.match_action = function(spec) { + spec = spec || {}; + spec.name = spec.name || 'match'; + + var that = IPA.object_action(spec); + + that.execute_action = function(facet) { + facet.emit('cert-match'); + }; + + return that; +}; + + +/** + * Action which allows to clean whole facet. + * + * @class certmapmatch.clean_action + * @extends IPA.object_action + */ +certmapmatch.clear_action = function(spec) { + spec = spec || {}; + spec.name = spec.name || 'clear'; + + var that = IPA.object_action(spec); + + that.execute_action = function(facet) { + facet.emit('clear-facet'); + }; + + return that; +}; + +/** + * Certificate Mapping Configuration entity specification object + * @member certmap + */ +certmapmatch.certmap_spec = make_certmap_spec(); + + +/** + * Register entity + * @member cermap + */ +certmapmatch.register = function() { + var e = reg.entity; + var f = reg.field; + var a = reg.action; + + a.register('match', certmapmatch.match_action); + a.register('clear', certmapmatch.clear_action); + f.register('cert_textarea', certmapmatch.cert_textarea_field); + e.register({ + type: 'certmap_match', + spec: certmapmatch.certmap_spec, + factory: certmapmatch.certmapmatch_entity + }); +}; + +phases.on('registration', certmapmatch.register); + +return certmapmatch; +}); diff --git a/ipaserver/plugins/internal.py b/ipaserver/plugins/internal.py index acd417b..bf0726f 100644 --- a/ipaserver/plugins/internal.py +++ b/ipaserver/plugins/internal.py @@ -219,6 +219,8 @@ class i18n_messages(Command): "apply": _("Apply"), "back": _("Back"), "cancel": _("Cancel"), + "clear": _("Clear"), + "clear_title": _("Clear result fields on the page."), "close": _("Close"), "disable": _("Disable"), "download": _("Download"), @@ -230,6 +232,8 @@ class i18n_messages(Command): "get": _("Get"), "hide": _("Hide"), "issue": _("Issue"), + "match": _("Match"), + "match_title": _("Match users according to certificate."), "ok": _("OK"), "refresh": _("Refresh"), "refresh_title": _("Reload current settings from the server."), @@ -464,6 +468,14 @@ class i18n_messages(Command): "view_certificate": _("Certificate for ${entity} ${primary_key}"), "view_certificate_btn": _("View Certificate"), }, + "certmap_match": { + "cert_data": _("Certificate Data"), + "cert_for_match": _("Certificate For Match"), + "facet_label": _("Certificate Mapping Match"), + "domain": _("Domain"), + "matched_users": _("Matched Users"), + "userlogin": _("User Login"), + }, "certmap": { "adder_title": _("Add Certificate Mapping Data"), "data_label": _("Certificate mapping data"),
-- Manage your subscription for the Freeipa-devel mailing list: https://www.redhat.com/mailman/listinfo/freeipa-devel Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code