/* 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', './qrcode', './search', './entity'], function(IPA, $, menu, phases, reg, mod_details, mod_facet, QRCode) { /** * 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'], enabled: false, 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'], enabled: false, 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', needs_confirm: true, confirm_msg: '@i18n:actions.enable_confirm', options: { ipatokendisabled: false }, enable_cond: ['disabled'], hide_cond: ['self-service'] }, { $type: 'object', name: 'otp_disable', label: '@i18n:objects.otptoken.disable', method: 'mod', needs_confirm: true, confirm_msg: '@i18n:actions.disable_confirm', options: { ipatokendisabled: true }, enable_cond: ['enabled'], hide_cond: ['self-service'] }, 'delete' ], header_actions: ['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' }, { $type: 'datetime', name: 'ipatokennotbefore' }, { $type: 'datetime', name: 'ipatokennotafter' }, 'ipatokenvendor', 'ipatokenmodel', 'ipatokenserial', 'ipatokenotpalgorithm', 'ipatokenotpdigits', 'ipatokentotpclockoffset', 'ipatokentotptimestep', 'ipatokenhotpcounter' ] } ] } ], adder_dialog: { $factory: otptoken.adder_dialog, $pre_ops: [ otptoken.adder_dialog_preop ], $post_ops: [ otptoken.adder_dialog_qrcode_post_op ], 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' }, { $type: 'datetime', name: 'ipatokennotbefore' }, { $type: 'datetime', name: '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 * * @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; }; /** * Displays text as QR code * @class * @extends IPA.widget */ otptoken.qr_widget = function(spec) { var that = IPA.widget(spec); /** * Text to be displayed as QR Code * @property {string} * @readonly */ that.text = spec.text; /** * Show link with the text instead of QR code * @property {boolean} */ that.show_link = !!spec.show_link; /** @inheritDoc */ that.create = function(container) { that.widget_create(container); container.addClass('qrcode-widget'); that.div_link_control = $('', { name: that.name, href: '' }).appendTo(that.container); that.qr_control = $('
', { name: that.name }).appendTo(that.div_link_control); that.uri_control = $('
', { name: 'uri-control', 'class': 'otp-uri', style: 'display: none;' }).appendTo(container); that.link_container = $('
', { style: 'padding: 5px 0;' }).appendTo(container); that.show_uri_link = $('', { name: 'show-uri', href: '#', text: 'Show configuration uri', click: function(e) { e.preventDefault(); that.update_display_mode(!that.show_link); } }).appendTo(that.link_container); that.qrcode = new QRCode(that.qr_control[0], { text: "", width: 450, height: 450, colorDark : "#000000", colorLight : "#ffffff", correctLevel : QRCode.CorrectLevel.M }); that.update(that.text); that.update_display_mode(that.show_link); }; /** * Update displayed information with supplied values. * @param {String|Array|null} values */ that.update = function(values) { var val; if (typeof values === 'string') { val = values; } else if (values.length) { val = values[0]; } else { val = ''; } that.text = val; that.qrcode.makeCode(that.text); that.uri_control.text(that.text); that.div_link_control.prop('href', that.text); that.emit('value-change', { source: that, value: val }); }; /** * Switches between QR code and link * @protected * @param {boolean} show_link */ that.update_display_mode = function(show_link) { that.show_link = !!show_link; if (that.show_link) { that.show_uri_link.text('Show QR code'); that.qr_control.hide(); that.uri_control.show(); } else { that.show_uri_link.text('Show configuration uri'); that.qr_control.show(); that.uri_control.hide(); } }; /** * @inheritDoc */ that.clear = function() { that.qrcode.clear(); that.link_control.text(''); }; return that; }; /** * Displays text as QR code in a dialog * @class * @extends IPA.message_dialog */ otptoken.qr_dialog = function(spec) { var that = IPA.message_dialog(spec); /** * Uses IPA.dialog UI */ that.create_content = that.dialog_create_content; return that; }; /** * OTP adder dialog post-op which enables showing of QR code after token is * successfully added by displaying QR dialog. * @member otptoken */ otptoken.adder_dialog_qrcode_post_op = function(object) { object.added.attach(function(data) { var uri = data.result.result.uri; var qr_dialog = otptoken.qr_dialog({ name: 'qr_dialog', title: 'Configure your token', widgets: [ { $type: 'qrcode', name: 'qr', css_class: 'col-sm-12', text: uri } ] }); qr_dialog.open(); qr_dialog.show_message('Configure your token by scanning the QR code \ below. Click on the QR core if you see this on \ the device you want to configure.'); }); return object; }; /** * Entity specification object * @member otptoken */ otptoken.spec = make_spec(); /** * Register entity * @member otptoken */ otptoken.register = function() { var e = reg.entity; var w = reg.widget; e.register({type: 'otptoken', spec: otptoken.spec}); w.register('qrcode', otptoken.qr_widget); }; phases.on('registration', otptoken.register); return otptoken; });