From fef26fe3d8355c5c3f22348bb7390af0a1e5cef7 Mon Sep 17 00:00:00 2001 From: Petr Vobornik Date: Thu, 12 Sep 2013 15:29:01 +0200 Subject: [PATCH] UI for OTP tokens https://fedorahosted.org/freeipa/ticket/3369 Reviewed-By: Adam Misnyovszki --- install/ui/doc/categories.json | 1 + install/ui/src/freeipa/app.js | 1 + .../ui/src/freeipa/navigation/menu_spec.js | 10 +- install/ui/src/freeipa/otptoken.js | 328 ++++++++++++++++++ install/ui/src/freeipa/search.js | 18 +- install/ui/test/data/ipa_init.json | 5 + ipalib/plugins/internal.py | 9 +- 7 files changed, 361 insertions(+), 11 deletions(-) create mode 100644 install/ui/src/freeipa/otptoken.js diff --git a/install/ui/doc/categories.json b/install/ui/doc/categories.json index 9f04c4b28..02487e542 100644 --- a/install/ui/doc/categories.json +++ b/install/ui/doc/categories.json @@ -218,6 +218,7 @@ "name": "Plugins", "classes": [ "aci", + "otptoken", "user" ] } diff --git a/install/ui/src/freeipa/app.js b/install/ui/src/freeipa/app.js index 2fbe0775c..05bef374f 100644 --- a/install/ui/src/freeipa/app.js +++ b/install/ui/src/freeipa/app.js @@ -41,6 +41,7 @@ define([ './host', './idrange', './netgroup', + './otptoken', './policy', './realmdomains', './rule', diff --git a/install/ui/src/freeipa/navigation/menu_spec.js b/install/ui/src/freeipa/navigation/menu_spec.js index 9d4c5eff9..e531a9f6d 100644 --- a/install/ui/src/freeipa/navigation/menu_spec.js +++ b/install/ui/src/freeipa/navigation/menu_spec.js @@ -56,7 +56,8 @@ var nav = {}; ] }, { entity: 'cert', label: '@i18n:tabs.cert' }, - { entity: 'realmdomains' } + { entity: 'realmdomains' }, + { entity: 'otptoken' } ] }, {name: 'policy', label: '@i18n:tabs.policy', children: [ @@ -148,10 +149,13 @@ nav.self_service = { { name: 'identity', label: '@i18n:tabs.identity', - children: [{entity: 'user'}] + children: [ + { entity: 'user' }, + { entity: 'otptoken' } + ] } ] }; return nav; -}); \ No newline at end of file +}); diff --git a/install/ui/src/freeipa/otptoken.js b/install/ui/src/freeipa/otptoken.js new file mode 100644 index 000000000..e300e76e9 --- /dev/null +++ b/install/ui/src/freeipa/otptoken.js @@ -0,0 +1,328 @@ +/* Authors: + * Petr Vobornik + * + * Copyright (C) 2013 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 . + */ + +define([ + './ipa', + './jquery', + './menu', + './phases', + './reg', + './details', + './facet', + './search', + './entity'], + function(IPA, $, menu, phases, reg, mod_details, mod_facet) { + +/** + * OTP tokens module + * @class + * @singleton + */ +var otptoken = IPA.otptoken = {}; + +var make_spec = function() { +return { + name: 'otptoken', + enable_test: function() { + return true; + }, + facets: [ + { + $type: 'search', + $pre_ops: [ + // redefining 'add' and 'remove' actions to be shown in + // self service + { + $replace: [ [ 'actions', [ + [ + 'add', + { + $type:'add', + name: 'add', + hide_cond: [] + } + ], + [ + 'batch_remove', + { + $type: 'batch_remove', + name: 'remove', + hide_cond: [] + } + ] + ] ] ] + } + ], + actions: [ + { + $type: 'batch_items', + name: 'enable', + method: 'mod', + options: { ipatokendisabled: false }, + needs_confirm: true, + enable_cond: ['item-selected'], + success_msg: '@i18n:search.enabled', + confirm_msg: '@i18n:search.enable_confirm' + }, + { + $type: 'batch_items', + name: 'disable', + method: 'mod', + options: { ipatokendisabled: true }, + needs_confirm: true, + enable_cond: ['item-selected'], + success_msg: '@i18n:search.disabled', + confirm_msg: '@i18n:search.disable_confirm' + }, + 'delete' + ], + control_buttons: [ + { + name: 'disable', + label: '@i18n:buttons.disable', + icon: 'fa-minus' + }, + { + name: 'enable', + label: '@i18n:buttons.enable', + icon: 'fa-check' + } + ], + columns: [ + 'ipatokenuniqueid', + 'ipatokenowner', + { + name: 'ipatokendisabled', + label: '@i18n:status.label', + formatter: { + $type: 'boolean_status', + invert_value: true, + empty_value: false + } + }, + 'description' + ] + }, + { + $type: 'details', + actions: [ + 'select', + { + $type: 'object', + name: 'otp_enable', + label: '@i18n:objects.otptoken.enable', + method: 'mod', + options: { ipatokendisabled: false }, + enable_cond: ['disabled'], + hide_cond: ['self-service'] + }, + { + $type: 'object', + name: 'otp_disable', + label: '@i18n:objects.otptoken.disable', + method: 'mod', + options: { ipatokendisabled: true }, + enable_cond: ['enabled'], + hide_cond: ['self-service'] + }, + 'delete' + ], + header_actions: ['select_action', 'otp_enable', 'otp_disable', 'delete'], + state: { + evaluators: [ + { + $factory: mod_details.enable_state_evaluator, + field: 'ipatokendisabled', + parser: { + $factory: IPA.boolean_formatter, + invert_value: true, + empty_value: false + } + }, + mod_facet.self_service_state_evaluator + ], + summary_conditions: [ + mod_details.enabled_summary_cond, + mod_details.disabled_summary_cond + ] + }, + sections: [ + { + name: 'details', + label: '@i18n:objects.otptoken.details', + fields: [ + 'ipatokenuniqueid', + { + $type: 'textarea', + name: 'description' + }, + { + $type: 'entity_select', + name: 'ipatokenowner', + other_entity: 'user', + other_field: 'uid' + }, + 'ipatokennotbefore', + 'ipatokennotafter', + 'ipatokenvendor', + 'ipatokenmodel', + 'ipatokenserial', + 'ipatokenotpalgorithm', + 'ipatokenotpdigits', + 'ipatokentotpclockoffset', + 'ipatokentotptimestep', + 'ipatokenhotpcounter' + ] + } + ] + } + ], + + adder_dialog: { + $factory: otptoken.adder_dialog, + $pre_ops: [ + otptoken.adder_dialog_preop + ], + fields: [ + { + $type: 'radio', + name: 'type', + default_value: 'totp', + options: [ + { label: 'TOTP', value: 'totp' }, + { label: 'HOTP', value: 'hotp' } + ] + }, + { + name: 'ipatokenuniqueid', + required: false + }, + 'description', + {// only when not self-service + $type: 'entity_select', + name: 'ipatokenowner', + other_entity: 'user', + other_field: 'uid' + }, + 'ipatokennotbefore', + 'ipatokennotafter', + 'ipatokenvendor', + 'ipatokenmodel', + 'ipatokenserial', + 'ipatokenotpkey', + { + $type: 'radio', + name: 'ipatokenotpalgorithm', + default_value: 'sha1', + options: [ + 'sha1', 'sha256', 'sha384', 'sha512' + ] + }, + { + $type: 'radio', + name: 'ipatokenotpdigits', + default_value: '6', + options: ['6', '8'] + }, + 'ipatokentotptimestep' + ], + selfservice_fields: [ + { + $type: 'radio', + name: 'type', + default_value: 'totp', + options: [ + { label: 'TOTP', value: 'totp' }, + { label: 'HOTP', value: 'hotp' } + ] + }, + { + name: 'ipatokenuniqueid', + required: false + }, + 'description' + ] + } +};}; + +/** + * OTP adder dialog pre-op. + * + * Switches fields to different set when in self-service. + */ +otptoken.adder_dialog_preop = function(spec) { + + spec.self_service = IPA.is_selfservice; + + if (IPA.is_selfservice) { + spec.fields = spec.selfservice_fields; + } + + return spec; +}; + +/** + * OTP adder dialog + * + * - otp-add requires 'type' to be set. At the moment IPA supports only 'totp' + * @class + * @extends IPA.entity_adder_dialog + */ +otptoken.adder_dialog = function(spec) { + + var that = IPA.entity_adder_dialog(spec); + + /** + * Dialog sends different command options when in self-service mode. + */ + that.self_service = !!spec.self_service; + + /** @inheritDoc */ + that.create_add_command = function(record) { + + var command = that.entity_adder_dialog_create_add_command(record); + if (that.self_service) { + command.set_option('ipatokenowner', IPA.whoami.uid[0]); + } + return command; + }; + + return that; +}; + +/** + * Entity specification object + * @member otptoken + */ +otptoken.spec = make_spec(); + +/** + * Register entity + * @member otptoken + */ +otptoken.register = function() { + var e = reg.entity; + e.register({type: 'otptoken', spec: otptoken.spec}); +}; + +phases.on('registration', otptoken.register); + +return otptoken; +}); diff --git a/install/ui/src/freeipa/search.js b/install/ui/src/freeipa/search.js index 3c2fca715..394de3e8c 100644 --- a/install/ui/src/freeipa/search.js +++ b/install/ui/src/freeipa/search.js @@ -466,6 +466,7 @@ IPA.batch_items_action = function(spec) { that.method = spec.method || 'disable'; that.success_msg = text.get(spec.success_msg); + that.options = spec.options || {}; that.execute_action = function(facet, on_success, on_error) { @@ -483,18 +484,23 @@ IPA.batch_items_action = function(spec) { var item_keys = pkeys.splice(0); item_keys.push(selected_keys[i]); - var command = IPA.command({ - entity: entity.name, - method: that.method, - args: item_keys - }); - + var command = that.create_action_command(facet, item_keys); that.batch.add_command(command); } that.batch.execute(); }; + that.create_action_command = function(facet, keys) { + var command = IPA.command({ + entity: facet.managed_entity.name, + method: that.method, + args: keys, + options: that.options + }); + return command; + }; + that.on_success = function(facet, data, text_status, xhr) { facet.on_update.notify(); facet.refresh(); diff --git a/install/ui/test/data/ipa_init.json b/install/ui/test/data/ipa_init.json index cf13591a5..828af9fc3 100644 --- a/install/ui/test/data/ipa_init.json +++ b/install/ui/test/data/ipa_init.json @@ -365,6 +365,11 @@ "usergroups": "User Groups", "users": "Users" }, + "otptoken": { + "details": "OTP Token Settings", + "disable": "Disable token", + "enable": "Enable token" + }, "permission": { "identity": "Identity", "invalid_target": "Permission with invalid target specification", diff --git a/ipalib/plugins/internal.py b/ipalib/plugins/internal.py index e282dfedf..1e2cec342 100644 --- a/ipalib/plugins/internal.py +++ b/ipalib/plugins/internal.py @@ -485,7 +485,7 @@ class i18n_messages(Command): }, "krbtpolicy": { "identity": _("Kerberos Ticket Policy"), - }, + }, "netgroup": { "any_host": _("Any Host"), "anyone": _("Anyone"), @@ -499,7 +499,12 @@ class i18n_messages(Command): "user": _("User"), "usergroups": _("User Groups"), "users": _("Users"), - }, + }, + "otptoken": { + "details": "OTP Token Settings", + "disable": "Disable token", + "enable": "Enable token", + }, "permission": { "identity": _("Identity"), "invalid_target": _("Permission with invalid target specification"),