diff options
author | Joas Schilling <coding@schilljs.com> | 2016-08-03 14:38:53 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-08-03 14:38:53 +0200 |
commit | 8ad79eb09765393b83982f1c4503d9659bb08325 (patch) | |
tree | c47b3543ebe75c3433f7500bfbc2431833ce5c26 /apps | |
parent | 223f3fa9d5b4f36ccd455a214d8b6cabcdb49d31 (diff) | |
parent | bcf022c964eb72d1c1ce3fd784f9d8531767a3c3 (diff) | |
download | nextcloud-server-8ad79eb09765393b83982f1c4503d9659bb08325.tar.gz nextcloud-server-8ad79eb09765393b83982f1c4503d9659bb08325.zip |
Merge pull request #611 from nextcloud/add-more-checks
Add more checks
Diffstat (limited to 'apps')
26 files changed, 2207 insertions, 33 deletions
diff --git a/apps/workflowengine/appinfo/routes.php b/apps/workflowengine/appinfo/routes.php index b8c9ae1c236..5ae74bcafc3 100644 --- a/apps/workflowengine/appinfo/routes.php +++ b/apps/workflowengine/appinfo/routes.php @@ -25,5 +25,6 @@ return [ ['name' => 'flowOperations#addOperation', 'url' => '/operations', 'verb' => 'POST'], ['name' => 'flowOperations#updateOperation', 'url' => '/operations/{id}', 'verb' => 'PUT'], ['name' => 'flowOperations#deleteOperation', 'url' => '/operations/{id}', 'verb' => 'DELETE'], + ['name' => 'requestTime#getTimezones', 'url' => '/timezones', 'verb' => 'GET'], ] ]; diff --git a/apps/workflowengine/css/admin.css b/apps/workflowengine/css/admin.css index 1d94fced003..70185615ad6 100644 --- a/apps/workflowengine/css/admin.css +++ b/apps/workflowengine/css/admin.css @@ -46,3 +46,7 @@ margin-right: 5px; } +.workflowengine .invalid-input { + border-color: #aa0000; +} + diff --git a/apps/workflowengine/js/admin.js b/apps/workflowengine/js/admin.js index 48d1592b457..2c6fa54d87d 100644 --- a/apps/workflowengine/js/admin.js +++ b/apps/workflowengine/js/admin.js @@ -75,12 +75,6 @@ }); /** - * @class OCA.WorkflowEngine.AvailableCheck - */ - OCA.WorkflowEngine.AvailableCheck = - OC.Backbone.Model.extend({}); - - /** * .d8888b. 888 888 888 d8b * d88P Y88b 888 888 888 Y8P * 888 888 888 888 888 @@ -143,6 +137,7 @@ 'change .check-operator': 'checkChanged', 'change .check-value': 'checkChanged', 'change .operation-name': 'operationChanged', + 'change .operation-operation': 'operationChanged', 'click .button-reset': 'reset', 'click .button-save': 'save', 'click .button-add': 'add', @@ -248,7 +243,7 @@ // model change will trigger render this.model.set({'checks': checks}); }, - deleteCheck: function() { + deleteCheck: function(event) { console.log(arguments); var id = $(event.target.parentElement).data('id'), checks = JSON.parse(JSON.stringify(this.model.get('checks'))); @@ -275,7 +270,7 @@ return; } - if (key !== 'name') { + if (key !== 'name' && key !== 'operation') { console.warn('key "' + key + '" is no valid attribute'); return; } @@ -302,7 +297,7 @@ _.each(OCA.WorkflowEngine.availablePlugins, function(plugin) { if (_.isFunction(plugin.render)) { - plugin.render(valueElement, check['class'], check['value']); + plugin.render(valueElement, check); } }); }, this); @@ -315,6 +310,7 @@ this.message = ''; } + return this.$el; } }); @@ -331,16 +327,15 @@ events: { 'click .button-add-operation': 'add' }, - initialize: function() { - this._initialize('OCA\\WorkflowEngine\\Operation'); - }, - _initialize: function(classname) { - OCA.WorkflowEngine.availablePlugins = OC.Plugins.getPlugins('OCA.WorkflowEngine.CheckPlugins'); - _.each(OCA.WorkflowEngine.availablePlugins, function(plugin) { - if (_.isFunction(plugin.getCheck)) { - OCA.WorkflowEngine.availableChecks.push(plugin.getCheck()); - } - }); + initialize: function(classname) { + if (!OCA.WorkflowEngine.availablePlugins.length) { + OCA.WorkflowEngine.availablePlugins = OC.Plugins.getPlugins('OCA.WorkflowEngine.CheckPlugins'); + _.each(OCA.WorkflowEngine.availablePlugins, function(plugin) { + if (_.isFunction(plugin.getCheck)) { + OCA.WorkflowEngine.availableChecks.push(plugin.getCheck(classname)); + } + }); + } this.collection.fetch({data: { 'class': classname @@ -351,12 +346,8 @@ var operation = this.collection.create(); this.renderOperation(operation); }, - renderOperation: function(operation){ - console.log(operation); - var subView = new OCA.WorkflowEngine.OperationView({ - model: operation - }), - operationsElement = this.$el.find('.operations'); + renderOperation: function(subView){ + var operationsElement = this.$el.find('.operations'); operationsElement.append(subView.$el); subView.render(); }, diff --git a/apps/workflowengine/js/filemimetypeplugin.js b/apps/workflowengine/js/filemimetypeplugin.js new file mode 100644 index 00000000000..33cbbd7fd7e --- /dev/null +++ b/apps/workflowengine/js/filemimetypeplugin.js @@ -0,0 +1,72 @@ +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +(function() { + + OCA.WorkflowEngine = OCA.WorkflowEngine || {}; + OCA.WorkflowEngine.Plugins = OCA.WorkflowEngine.Plugins || {}; + + OCA.WorkflowEngine.Plugins.FileMimeTypePlugin = { + getCheck: function() { + return { + 'class': 'OCA\\WorkflowEngine\\Check\\FileMimeType', + 'name': t('workflowengine', 'File mime type (upload)'), + 'operators': [ + {'operator': 'is', 'name': t('workflowengine', 'is')}, + {'operator': '!is', 'name': t('workflowengine', 'is not')}, + {'operator': 'matches', 'name': t('workflowengine', 'matches')}, + {'operator': '!matches', 'name': t('workflowengine', 'does not match')} + ] + }; + }, + render: function(element, check) { + if (check['class'] !== 'OCA\\WorkflowEngine\\Check\\FileMimeType') { + return; + } + + var placeholder = t('workflowengine', 'text/plain'); + if (check['operator'] === 'matches' || check['operator'] === '!matches') { + placeholder = t('workflowengine', '/^text\\/(plain|html)$/i'); + + if (this._validateRegex(check['value'])) { + $(element).removeClass('invalid-input'); + } else { + $(element).addClass('invalid-input'); + } + } + + $(element).css('width', '250px') + .attr('placeholder', placeholder) + .attr('title', t('workflowengine', 'Example: {placeholder}', {placeholder: placeholder})) + .addClass('has-tooltip') + .tooltip({ + placement: 'bottom' + }); + }, + + _validateRegex: function(string) { + var regexRegex = /^\/(.*)\/([gui]{0,3})$/, + result = regexRegex.exec(string); + return result !== null; + } + }; +})(); + +OC.Plugins.register('OCA.WorkflowEngine.CheckPlugins', OCA.WorkflowEngine.Plugins.FileMimeTypePlugin); diff --git a/apps/workflowengine/js/filesizeplugin.js b/apps/workflowengine/js/filesizeplugin.js new file mode 100644 index 00000000000..0efa9d00edf --- /dev/null +++ b/apps/workflowengine/js/filesizeplugin.js @@ -0,0 +1,56 @@ +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +(function() { + + OCA.WorkflowEngine = OCA.WorkflowEngine || {}; + OCA.WorkflowEngine.Plugins = OCA.WorkflowEngine.Plugins || {}; + + OCA.WorkflowEngine.Plugins.FileSizePlugin = { + getCheck: function() { + return { + 'class': 'OCA\\WorkflowEngine\\Check\\FileSize', + 'name': t('workflowengine', 'File size (upload)'), + 'operators': [ + {'operator': 'less', 'name': t('workflowengine', 'less')}, + {'operator': '!greater', 'name': t('workflowengine', 'less or equals')}, + {'operator': '!less', 'name': t('workflowengine', 'greater or equals')}, + {'operator': 'greater', 'name': t('workflowengine', 'greater')} + ] + }; + }, + render: function(element, check) { + if (check['class'] !== 'OCA\\WorkflowEngine\\Check\\FileSize') { + return; + } + + var placeholder = '12 MB'; // Do not translate!!! + $(element).css('width', '250px') + .attr('placeholder', placeholder) + .attr('title', t('workflowengine', 'Example: {placeholder}', {placeholder: placeholder})) + .addClass('has-tooltip') + .tooltip({ + placement: 'bottom' + }); + } + }; +})(); + +OC.Plugins.register('OCA.WorkflowEngine.CheckPlugins', OCA.WorkflowEngine.Plugins.FileSizePlugin); diff --git a/apps/workflowengine/js/filesystemtagsplugin.js b/apps/workflowengine/js/filesystemtagsplugin.js new file mode 100644 index 00000000000..dc6f608d85a --- /dev/null +++ b/apps/workflowengine/js/filesystemtagsplugin.js @@ -0,0 +1,76 @@ +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +(function() { + + OCA.WorkflowEngine = OCA.WorkflowEngine || {}; + OCA.WorkflowEngine.Plugins = OCA.WorkflowEngine.Plugins || {}; + + OCA.WorkflowEngine.Plugins.FileSystemTagsPlugin = { + getCheck: function() { + this.collection = OC.SystemTags.collection; + + return { + 'class': 'OCA\\WorkflowEngine\\Check\\FileSystemTags', + 'name': t('workflowengine', 'File system tag'), + 'operators': [ + {'operator': 'is', 'name': t('workflowengine', 'is tagged with')}, + {'operator': '!is', 'name': t('workflowengine', 'is not tagged with')} + ] + }; + }, + render: function(element, check) { + if (check['class'] !== 'OCA\\WorkflowEngine\\Check\\FileSystemTags') { + return; + } + + $(element).css('width', '400px'); + + $(element).select2({ + allowClear: false, + multiple: false, + placeholder: t('workflowengine', 'Select tag…'), + query: _.debounce(function(query) { + query.callback({ + results: OC.SystemTags.collection.filterByName(query.term) + }); + }, 100, true), + id: function(element) { + return element.get('id'); + }, + initSelection: function(element, callback) { + callback($(element).val()); + }, + formatResult: function (tag) { + return OC.SystemTags.getDescriptiveTag(tag); + }, + formatSelection: function (tagId) { + var tag = OC.SystemTags.collection.get(tagId); + return OC.SystemTags.getDescriptiveTag(tag); + }, + escapeMarkup: function(m) { + return m; + } + }); + } + }; +})(); + +OC.Plugins.register('OCA.WorkflowEngine.CheckPlugins', OCA.WorkflowEngine.Plugins.FileSystemTagsPlugin); diff --git a/apps/workflowengine/js/requestremoteaddressplugin.js b/apps/workflowengine/js/requestremoteaddressplugin.js new file mode 100644 index 00000000000..a66d6f51f0f --- /dev/null +++ b/apps/workflowengine/js/requestremoteaddressplugin.js @@ -0,0 +1,83 @@ +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +(function() { + + OCA.WorkflowEngine = OCA.WorkflowEngine || {}; + OCA.WorkflowEngine.Plugins = OCA.WorkflowEngine.Plugins || {}; + + OCA.WorkflowEngine.Plugins.RequestRemoteAddressPlugin = { + getCheck: function() { + return { + 'class': 'OCA\\WorkflowEngine\\Check\\RequestRemoteAddress', + 'name': t('workflowengine', 'Request remote address'), + 'operators': [ + {'operator': 'matchesIPv4', 'name': t('workflowengine', 'matches IPv4')}, + {'operator': '!matchesIPv4', 'name': t('workflowengine', 'does not match IPv4')}, + {'operator': 'matchesIPv6', 'name': t('workflowengine', 'matches IPv6')}, + {'operator': '!matchesIPv6', 'name': t('workflowengine', 'does not match IPv6')} + ] + }; + }, + render: function(element, check) { + if (check['class'] !== 'OCA\\WorkflowEngine\\Check\\RequestRemoteAddress') { + return; + } + + var placeholder = '127.0.0.1/32'; // Do not translate!!! + if (check['operator'] === 'matchesIPv6' || check['operator'] === '!matchesIPv6') { + placeholder = '::1/128'; // Do not translate!!! + if (this._validateIPv6(check['value'])) { + $(element).removeClass('invalid-input'); + } else { + $(element).addClass('invalid-input'); + } + } else { + if (this._validateIPv4(check['value'])) { + $(element).removeClass('invalid-input'); + } else { + $(element).addClass('invalid-input'); + } + } + + $(element).css('width', '300px') + .attr('placeholder', placeholder) + .attr('title', t('workflowengine', 'Example: {placeholder}', {placeholder: placeholder})) + .addClass('has-tooltip') + .tooltip({ + placement: 'bottom' + }); + }, + + _validateIPv4: function(string) { + var regexRegex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\/(3[0-2]|[1-2][0-9]|[1-9])$/, + result = regexRegex.exec(string); + return result !== null; + }, + + _validateIPv6: function(string) { + var regexRegex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/(1([01][0-9]|2[0-8])|[1-9][0-9]|[0-9])$/, + result = regexRegex.exec(string); + return result !== null; + } + }; +})(); + +OC.Plugins.register('OCA.WorkflowEngine.CheckPlugins', OCA.WorkflowEngine.Plugins.RequestRemoteAddressPlugin); diff --git a/apps/workflowengine/js/requesttimeplugin.js b/apps/workflowengine/js/requesttimeplugin.js new file mode 100644 index 00000000000..111b2bb7437 --- /dev/null +++ b/apps/workflowengine/js/requesttimeplugin.js @@ -0,0 +1,196 @@ +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +(function() { + + OCA.WorkflowEngine = OCA.WorkflowEngine || {}; + OCA.WorkflowEngine.Plugins = OCA.WorkflowEngine.Plugins || {}; + + OCA.WorkflowEngine.Plugins.RequestTimePlugin = { + timezones: [ + "Europe/Berlin", + "Europe/London" + ], + _$element: null, + getCheck: function() { + return { + 'class': 'OCA\\WorkflowEngine\\Check\\RequestTime', + 'name': t('workflowengine', 'Request time'), + 'operators': [ + {'operator': 'in', 'name': t('workflowengine', 'between')}, + {'operator': '!in', 'name': t('workflowengine', 'not between')} + ] + }; + }, + render: function(element, check) { + if (check['class'] !== 'OCA\\WorkflowEngine\\Check\\RequestTime') { + return; + } + + var startTime = '09:00', + endTime = '18:00', + timezone = jstz.determine().name(), + $element = $(element); + + if (_.isString(check['value']) && check['value'] !== '') { + var value = JSON.parse(check['value']), + splittedStart = value[0].split(' ', 2), + splittedEnd = value[1].split(' ', 2); + + startTime = splittedStart[0]; + endTime = splittedEnd[0]; + timezone = splittedStart[1]; + } + + var valueJSON = JSON.stringify([startTime + ' ' + timezone, endTime + ' ' + timezone]); + if (check['value'] !== valueJSON) { + check['value'] = valueJSON; + $element.val(valueJSON); + } + + $element.css('display', 'none'); + + $('<input>') + .attr('type', 'text') + .attr('placeholder', t('workflowengine', 'Start')) + .attr('title', t('workflowengine', 'Example: {placeholder}', {placeholder: '16:00'})) + .addClass('has-tooltip') + .tooltip({ + placement: 'bottom' + }) + .addClass('start') + .val(startTime) + .insertBefore($element); + $('<input>') + .attr('type', 'text') + .attr('placeholder', t('workflowengine', 'End')) + .attr('title', t('workflowengine', 'Example: {placeholder}', {placeholder: '16:00'})) + .addClass('has-tooltip') + .tooltip({ + placement: 'bottom' + }) + .addClass('end') + .val(endTime) + .insertBefore($element); + + var timezoneInput = $('<input>') + .attr('type', 'hidden') + .css('width', '250px') + .insertBefore($element) + .val(timezone); + + timezoneInput.select2({ + allowClear: false, + multiple: false, + placeholder: t('workflowengine', 'Select timezone…'), + ajax: { + url: OC.generateUrl('apps/workflowengine/timezones'), + dataType: 'json', + quietMillis: 100, + data: function (term) { + if (term === '') { + // Default search in the same continent... + term = jstz.determine().name().split('/'); + term = term[0]; + } + return { + search: term + }; + }, + results: function (response) { + var results = []; + $.each(response, function(timezone) { + results.push({ id: timezone }); + }); + + return { + results: results, + more: false + }; + } + }, + initSelection: function (element, callback) { + callback(element.val()); + }, + formatResult: function (element) { + return '<span>' + element.id + '</span>'; + }, + formatSelection: function (element) { + if (!_.isUndefined(element.id)) { + element = element.id; + } + return '<span>' + element + '</span>'; + } + }); + + // Has to be added after select2 for `event.target.classList` + timezoneInput.addClass('timezone'); + + $element.parent() + .on('change', '.start', _.bind(this.update, this)) + .on('change', '.end', _.bind(this.update, this)) + .on('change', '.timezone', _.bind(this.update, this)); + + this._$element = $element; + }, + update: function(event) { + var value = event.target.value, + key = null; + + for (var i = 0; i < event.target.classList.length; i++) { + key = event.target.classList[i]; + } + + if (key === null) { + console.warn('update triggered but element doesn\'t have any class'); + return; + } + + var data = JSON.parse(this._$element.val()), + startTime = moment(data[0].split(' ', 2)[0], 'H:m Z'), + endTime = moment(data[1].split(' ', 2)[0], 'H:m Z'), + timezone = data[0].split(' ', 2)[1]; + + if (key === 'start' || key === 'end') { + var parsedDate = moment(value, ['H:m', 'h:m a'], true).format('HH:mm'); + + if (parsedDate === 'Invalid date') { + return; + } + + var indexValue = 0; + if (key === 'end') { + indexValue = 1; + } + data[indexValue] = parsedDate + ' ' + timezone; + } + + if (key === 'timezone') { + data[0] = startTime.format('HH:mm') + ' ' + value; + data[1] = endTime.format('HH:mm') + ' ' + value; + } + + this._$element.val(JSON.stringify(data)); + this._$element.trigger('change'); + } + }; +})(); + +OC.Plugins.register('OCA.WorkflowEngine.CheckPlugins', OCA.WorkflowEngine.Plugins.RequestTimePlugin); diff --git a/apps/workflowengine/js/requesturlplugin.js b/apps/workflowengine/js/requesturlplugin.js new file mode 100644 index 00000000000..5f21d2a59fc --- /dev/null +++ b/apps/workflowengine/js/requesturlplugin.js @@ -0,0 +1,117 @@ +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +(function() { + + OCA.WorkflowEngine = OCA.WorkflowEngine || {}; + OCA.WorkflowEngine.Plugins = OCA.WorkflowEngine.Plugins || {}; + + OCA.WorkflowEngine.Plugins.RequestURLPlugin = { + predefinedValues: ['webdav'], + getCheck: function() { + return { + 'class': 'OCA\\WorkflowEngine\\Check\\RequestURL', + 'name': t('workflowengine', 'Request URL'), + 'operators': [ + {'operator': 'is', 'name': t('workflowengine', 'is')}, + {'operator': '!is', 'name': t('workflowengine', 'is not')}, + {'operator': 'matches', 'name': t('workflowengine', 'matches')}, + {'operator': '!matches', 'name': t('workflowengine', 'does not match')} + ] + }; + }, + render: function(element, check) { + if (check['class'] !== 'OCA\\WorkflowEngine\\Check\\RequestURL') { + return; + } + + var placeholder = t('workflowengine', 'https://localhost/index.php'); + + if (check['operator'] === 'matches' || check['operator'] === '!matches') { + placeholder = t('workflowengine', '/^https\\:\\/\\/localhost\\/index\\.php$/i'); + } + + $(element).css('width', '250px') + .attr('placeholder', placeholder) + .attr('title', t('workflowengine', 'Example: {placeholder}', {placeholder: placeholder})) + .addClass('has-tooltip') + .tooltip({ + placement: 'bottom' + }); + + if (check['operator'] === 'matches' || check['operator'] === '!matches') { + if (this._validateRegex(check['value'])) { + $(element).removeClass('invalid-input'); + } else { + $(element).addClass('invalid-input'); + } + } else { + var self = this, + data = [ + { + text: t('workflowengine', 'Predefined URLs'), + children: [ + {id: 'webdav', text: t('workflowengine', 'Files WebDAV')} + ] + } + ]; + if (this.predefinedValues.indexOf(check['value']) === -1) { + data.unshift({ + id: check['value'], + text: check['value'] + }) + } + + + $(element).select2({ + data: data, + createSearchChoice: function(term) { + if (self.predefinedValues.indexOf(check['value']) === -1) { + return { + id: term, + text: term + }; + } + }, + id: function(element) { + return element.id; + }, + formatResult: function (tag) { + return tag.text; + }, + formatSelection: function (tag) { + return tag.text; + }, + escapeMarkup: function(m) { + return m; + } + }) + } + }, + + _validateRegex: function(string) { + var regexRegex = /^\/(.*)\/([gui]{0,3})$/, + result = regexRegex.exec(string); + return result !== null; + } + }; +})(); + +OC.Plugins.register('OCA.WorkflowEngine.CheckPlugins', OCA.WorkflowEngine.Plugins.RequestURLPlugin); diff --git a/apps/workflowengine/js/requestuseragentplugin.js b/apps/workflowengine/js/requestuseragentplugin.js new file mode 100644 index 00000000000..8413d52ac43 --- /dev/null +++ b/apps/workflowengine/js/requestuseragentplugin.js @@ -0,0 +1,118 @@ +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +(function() { + + OCA.WorkflowEngine = OCA.WorkflowEngine || {}; + OCA.WorkflowEngine.Plugins = OCA.WorkflowEngine.Plugins || {}; + + OCA.WorkflowEngine.Plugins.RequestUserAgentPlugin = { + predefinedValues: ['android', 'ios', 'desktop'], + getCheck: function() { + return { + 'class': 'OCA\\WorkflowEngine\\Check\\RequestUserAgent', + 'name': t('workflowengine', 'Request user agent'), + 'operators': [ + {'operator': 'is', 'name': t('workflowengine', 'is')}, + {'operator': '!is', 'name': t('workflowengine', 'is not')}, + {'operator': 'matches', 'name': t('workflowengine', 'matches')}, + {'operator': '!matches', 'name': t('workflowengine', 'does not match')} + ] + }; + }, + render: function(element, check) { + if (check['class'] !== 'OCA\\WorkflowEngine\\Check\\RequestUserAgent') { + return; + } + + var placeholder = t('workflowengine', 'Mozilla/5.0 User Agent'); + + if (check['operator'] === 'matches' || check['operator'] === '!matches') { + placeholder = t('workflowengine', '/^Mozilla\\/5\\.0 (.?)$/i'); + } + + $(element).css('width', '250px') + .attr('placeholder', placeholder) + .attr('title', t('workflowengine', 'Example: {placeholder}', {placeholder: placeholder})) + .addClass('has-tooltip') + .tooltip({ + placement: 'bottom' + }); + + if (check['operator'] === 'matches' || check['operator'] === '!matches') { + if (this._validateRegex(check['value'])) { + $(element).removeClass('invalid-input'); + } else { + $(element).addClass('invalid-input'); + } + } else { + var self = this, + data = [ + { + text: t('workflowengine', 'Sync clients'), + children: [ + {id: 'android', text: t('workflowengine', 'Android client')}, + {id: 'ios', text: t('workflowengine', 'iOS client')}, + {id: 'desktop', text: t('workflowengine', 'Desktop client')} + ] + } + ]; + if (this.predefinedValues.indexOf(check['value']) === -1) { + data.unshift({ + id: check['value'], + text: check['value'] + }) + } + + $(element).select2({ + data: data, + createSearchChoice: function(term) { + if (self.predefinedValues.indexOf(check['value']) === -1) { + return { + id: term, + text: term + }; + } + }, + id: function(element) { + return element.id; + }, + formatResult: function (tag) { + return tag.text; + }, + formatSelection: function (tag) { + return tag.text; + }, + escapeMarkup: function(m) { + return m; + } + }) + } + }, + + _validateRegex: function(string) { + var regexRegex = /^\/(.*)\/([gui]{0,3})$/, + result = regexRegex.exec(string); + return result !== null; + } + }; +})(); + +OC.Plugins.register('OCA.WorkflowEngine.CheckPlugins', OCA.WorkflowEngine.Plugins.RequestUserAgentPlugin); diff --git a/apps/workflowengine/js/usergroupmembershipplugin.js b/apps/workflowengine/js/usergroupmembershipplugin.js index 528a7bd3e3d..1c09e7d5ccd 100644 --- a/apps/workflowengine/js/usergroupmembershipplugin.js +++ b/apps/workflowengine/js/usergroupmembershipplugin.js @@ -34,8 +34,8 @@ ] }; }, - render: function(element, classname, value) { - if (classname !== 'OCA\\WorkflowEngine\\Check\\UserGroupMembership') { + render: function(element, check) { + if (check['class'] !== 'OCA\\WorkflowEngine\\Check\\UserGroupMembership') { return; } diff --git a/apps/workflowengine/lib/AppInfo/Application.php b/apps/workflowengine/lib/AppInfo/Application.php index 84339503047..b5e769d01d7 100644 --- a/apps/workflowengine/lib/AppInfo/Application.php +++ b/apps/workflowengine/lib/AppInfo/Application.php @@ -30,6 +30,7 @@ class Application extends \OCP\AppFramework\App { parent::__construct('workflowengine'); $this->getContainer()->registerAlias('FlowOperationsController', 'OCA\WorkflowEngine\Controller\FlowOperations'); + $this->getContainer()->registerAlias('RequestTimeController', 'OCA\WorkflowEngine\Controller\RequestTime'); } /** @@ -40,9 +41,32 @@ class Application extends \OCP\AppFramework\App { $dispatcher->addListener( 'OCP\WorkflowEngine::loadAdditionalSettingScripts', function() { - Util::addStyle('workflowengine', 'admin'); - Util::addScript('workflowengine', 'admin'); - Util::addScript('workflowengine', 'usergroupmembershipplugin'); + style('workflowengine', [ + 'admin', + ]); + + script('core', [ + 'oc-backbone-webdav', + 'systemtags/systemtags', + 'systemtags/systemtagmodel', + 'systemtags/systemtagscollection', + ]); + + vendor_script('jsTimezoneDetect/jstz'); + + script('workflowengine', [ + 'admin', + + // Check plugins + 'filemimetypeplugin', + 'filesizeplugin', + 'filesystemtagsplugin', + 'requestremoteaddressplugin', + 'requesttimeplugin', + 'requesturlplugin', + 'requestuseragentplugin', + 'usergroupmembershipplugin', + ]); }, -100 ); diff --git a/apps/workflowengine/lib/Check/AbstractStringCheck.php b/apps/workflowengine/lib/Check/AbstractStringCheck.php new file mode 100644 index 00000000000..0fd728e3496 --- /dev/null +++ b/apps/workflowengine/lib/Check/AbstractStringCheck.php @@ -0,0 +1,121 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\WorkflowEngine\Check; + + +use OCP\Files\Storage\IStorage; +use OCP\IL10N; +use OCP\WorkflowEngine\ICheck; + +abstract class AbstractStringCheck implements ICheck { + + /** @var array[] Nested array: [Pattern => [ActualValue => Regex Result]] */ + protected $matches; + + /** @var IL10N */ + protected $l; + + /** + * @param IL10N $l + */ + public function __construct(IL10N $l) { + $this->l = $l; + } + + /** + * @param IStorage $storage + * @param string $path + */ + public function setFileInfo(IStorage $storage, $path) { + // Nothing changes here with a different path + } + + /** + * @return string + */ + abstract protected function getActualValue(); + + /** + * @param string $operator + * @param string $value + * @return bool + */ + public function executeCheck($operator, $value) { + $actualValue = $this->getActualValue(); + return $this->executeStringCheck($operator, $value, $actualValue); + } + + /** + * @param string $operator + * @param string $checkValue + * @param string $actualValue + * @return bool + */ + protected function executeStringCheck($operator, $checkValue, $actualValue) { + if ($operator === 'is') { + return $checkValue === $actualValue; + } else if ($operator === '!is') { + return $checkValue !== $actualValue; + } else { + $match = $this->match($checkValue, $actualValue); + if ($operator === 'matches') { + return $match === 1; + } else { + return $match === 0; + } + } + } + + /** + * @param string $operator + * @param string $value + * @throws \UnexpectedValueException + */ + public function validateCheck($operator, $value) { + if (!in_array($operator, ['is', '!is', 'matches', '!matches'])) { + throw new \UnexpectedValueException($this->l->t('The given operator is invalid'), 1); + } + + if (in_array($operator, ['matches', '!matches']) && + @preg_match($value, null) === false) { + throw new \UnexpectedValueException($this->l->t('The given regular expression is invalid'), 2); + } + } + + /** + * @param string $pattern + * @param string $subject + * @return int|bool + */ + protected function match($pattern, $subject) { + $patternHash = md5($pattern); + $subjectHash = md5($subject); + if (isset($this->matches[$patternHash][$subjectHash])) { + return $this->matches[$patternHash][$subjectHash]; + } + if (!isset($this->matches[$patternHash])) { + $this->matches[$patternHash] = []; + } + $this->matches[$patternHash][$subjectHash] = preg_match($pattern, $subject); + return $this->matches[$patternHash][$subjectHash]; + } +} diff --git a/apps/workflowengine/lib/Check/FileMimeType.php b/apps/workflowengine/lib/Check/FileMimeType.php new file mode 100644 index 00000000000..1de9a70a17d --- /dev/null +++ b/apps/workflowengine/lib/Check/FileMimeType.php @@ -0,0 +1,85 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\WorkflowEngine\Check; + + +use OCP\Files\IMimeTypeDetector; +use OCP\IL10N; +use OCP\IRequest; + +class FileMimeType extends AbstractStringCheck { + + /** @var string */ + protected $mimeType; + + /** @var IRequest */ + protected $request; + + /** @var IMimeTypeDetector */ + protected $mimeTypeDetector; + + /** + * @param IL10N $l + * @param IRequest $request + * @param IMimeTypeDetector $mimeTypeDetector + */ + public function __construct(IL10N $l, IRequest $request, IMimeTypeDetector $mimeTypeDetector) { + parent::__construct($l); + $this->request = $request; + $this->mimeTypeDetector = $mimeTypeDetector; + } + + /** + * @return string + */ + protected function getActualValue() { + if ($this->mimeType !== null) { + return $this->mimeType; + } + + $this->mimeType = ''; + if ($this->isWebDAVRequest()) { + if ($this->request->getMethod() === 'PUT') { + $path = $this->request->getPathInfo(); + $this->mimeType = $this->mimeTypeDetector->detectPath($path); + } + } else if (in_array($this->request->getMethod(), ['POST', 'PUT'])) { + $files = $this->request->getUploadedFile('files'); + if (isset($files['type'][0])) { + $this->mimeType = $files['type'][0]; + } + } + return $this->mimeType; + } + + /** + * @return bool + */ + protected function isWebDAVRequest() { + return substr($this->request->getScriptName(), 0 - strlen('/remote.php')) === '/remote.php' && ( + $this->request->getPathInfo() === '/webdav' || + strpos($this->request->getPathInfo(), '/webdav/') === 0 || + $this->request->getPathInfo() === '/dav/files' || + strpos($this->request->getPathInfo(), '/dav/files/') === 0 + ); + } +} diff --git a/apps/workflowengine/lib/Check/FileSize.php b/apps/workflowengine/lib/Check/FileSize.php new file mode 100644 index 00000000000..1744793dec7 --- /dev/null +++ b/apps/workflowengine/lib/Check/FileSize.php @@ -0,0 +1,119 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\WorkflowEngine\Check; + + +use OCP\Files\Storage\IStorage; +use OCP\IL10N; +use OCP\IRequest; +use OCP\Util; +use OCP\WorkflowEngine\ICheck; + +class FileSize implements ICheck { + + /** @var int */ + protected $size; + + /** @var IL10N */ + protected $l; + + /** @var IRequest */ + protected $request; + + /** + * @param IL10N $l + * @param IRequest $request + */ + public function __construct(IL10N $l, IRequest $request) { + $this->l = $l; + $this->request = $request; + } + + /** + * @param IStorage $storage + * @param string $path + */ + public function setFileInfo(IStorage $storage, $path) { + } + + /** + * @param string $operator + * @param string $value + * @return bool + */ + public function executeCheck($operator, $value) { + $size = $this->getFileSizeFromHeader(); + + $value = Util::computerFileSize($value); + if ($size !== false) { + switch ($operator) { + case 'less': + return $size < $value; + case '!less': + return $size >= $value; + case 'greater': + return $size > $value; + case '!greater': + return $size <= $value; + } + } + return false; + } + + /** + * @param string $operator + * @param string $value + * @throws \UnexpectedValueException + */ + public function validateCheck($operator, $value) { + if (!in_array($operator, ['less', '!less', 'greater', '!greater'])) { + throw new \UnexpectedValueException($this->l->t('The given operator is invalid'), 1); + } + + if (!preg_match('/^[0-9]+[ ]?[kmgt]?b$/i', $value)) { + throw new \UnexpectedValueException($this->l->t('The given file size is invalid'), 2); + } + } + + /** + * @return string + */ + protected function getFileSizeFromHeader() { + if ($this->size !== null) { + return $this->size; + } + + $size = $this->request->getHeader('OC-Total-Length'); + if ($size === null) { + if (in_array($this->request->getMethod(), ['POST', 'PUT'])) { + $size = $this->request->getHeader('Content-Length'); + } + } + + if ($size === null) { + $size = false; + } + + $this->size = $size; + return $this->size; + } +} diff --git a/apps/workflowengine/lib/Check/FileSystemTags.php b/apps/workflowengine/lib/Check/FileSystemTags.php new file mode 100644 index 00000000000..e9b5a945967 --- /dev/null +++ b/apps/workflowengine/lib/Check/FileSystemTags.php @@ -0,0 +1,161 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\WorkflowEngine\Check; + + +use OCP\Files\Cache\ICache; +use OCP\Files\Storage\IStorage; +use OCP\IL10N; +use OCP\SystemTag\ISystemTagManager; +use OCP\SystemTag\ISystemTagObjectMapper; +use OCP\SystemTag\TagNotFoundException; +use OCP\WorkflowEngine\ICheck; + +class FileSystemTags implements ICheck { + + /** @var array */ + protected $fileIds; + + /** @var array */ + protected $fileSystemTags; + + /** @var IL10N */ + protected $l; + + /** @var ISystemTagManager */ + protected $systemTagManager; + + /** @var ISystemTagObjectMapper */ + protected $systemTagObjectMapper; + + /** @var IStorage */ + protected $storage; + + /** @var string */ + protected $path; + + /** + * @param IL10N $l + * @param ISystemTagManager $systemTagManager + * @param ISystemTagObjectMapper $systemTagObjectMapper + */ + public function __construct(IL10N $l, ISystemTagManager $systemTagManager, ISystemTagObjectMapper $systemTagObjectMapper) { + $this->l = $l; + $this->systemTagManager = $systemTagManager; + $this->systemTagObjectMapper = $systemTagObjectMapper; + } + + /** + * @param IStorage $storage + * @param string $path + */ + public function setFileInfo(IStorage $storage, $path) { + $this->storage = $storage; + $this->path = $path; + } + + /** + * @param string $operator + * @param string $value + * @return bool + */ + public function executeCheck($operator, $value) { + $systemTags = $this->getSystemTags(); + return ($operator === 'is') === in_array($value, $systemTags); + } + + /** + * @param string $operator + * @param string $value + * @throws \UnexpectedValueException + */ + public function validateCheck($operator, $value) { + if (!in_array($operator, ['is', '!is'])) { + throw new \UnexpectedValueException($this->l->t('The given operator is invalid'), 1); + } + + try { + $this->systemTagManager->getTagsByIds($value); + } catch (TagNotFoundException $e) { + throw new \UnexpectedValueException($this->l->t('The given tag id is invalid'), 2); + } catch (\InvalidArgumentException $e) { + throw new \UnexpectedValueException($this->l->t('The given tag id is invalid'), 3); + } + } + + /** + * Get the ids of the assigned system tags + * @return string[] + */ + protected function getSystemTags() { + $cache = $this->storage->getCache(); + $fileIds = $this->getFileIds($cache, $this->path); + + $systemTags = []; + foreach ($fileIds as $i => $fileId) { + if (isset($this->fileSystemTags[$fileId])) { + $systemTags[] = $this->fileSystemTags[$fileId]; + unset($fileIds[$i]); + } + } + + if (!empty($fileIds)) { + $mappedSystemTags = $this->systemTagObjectMapper->getTagIdsForObjects($fileIds, 'files'); + foreach ($mappedSystemTags as $fileId => $fileSystemTags) { + $this->fileSystemTags[$fileId] = $fileSystemTags; + $systemTags[] = $fileSystemTags; + } + } + + $systemTags = call_user_func_array('array_merge', $systemTags); + $systemTags = array_unique($systemTags); + return $systemTags; + } + + /** + * Get the file ids of the given path and its parents + * @param ICache $cache + * @param string $path + * @return int[] + */ + protected function getFileIds(ICache $cache, $path) { + $cacheId = $cache->getNumericStorageId(); + if (isset($this->fileIds[$cacheId][$path])) { + return $this->fileIds[$cacheId][$path]; + } + + if ($path !== dirname($path)) { + $parentIds = $this->getFileIds($cache, dirname($path)); + } else { + return []; + } + + $fileId = $cache->getId($path); + if ($fileId !== -1) { + $parentIds[] = $cache->getId($path); + } + + $this->fileIds[$cacheId][$path] = $parentIds; + + return $parentIds; + } +} diff --git a/apps/workflowengine/lib/Check/RequestRemoteAddress.php b/apps/workflowengine/lib/Check/RequestRemoteAddress.php new file mode 100644 index 00000000000..de9738fb631 --- /dev/null +++ b/apps/workflowengine/lib/Check/RequestRemoteAddress.php @@ -0,0 +1,154 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\WorkflowEngine\Check; + + +use OCP\Files\Storage\IStorage; +use OCP\IL10N; +use OCP\IRequest; +use OCP\WorkflowEngine\ICheck; + +class RequestRemoteAddress implements ICheck { + + /** @var IL10N */ + protected $l; + + /** @var IRequest */ + protected $request; + + /** + * @param IL10N $l + * @param IRequest $request + */ + public function __construct(IL10N $l, IRequest $request) { + $this->l = $l; + $this->request = $request; + } + + /** + * @param IStorage $storage + * @param string $path + */ + public function setFileInfo(IStorage $storage, $path) { + // A different path doesn't change time, so nothing to do here. + } + + /** + * @param string $operator + * @param string $value + * @return bool + */ + public function executeCheck($operator, $value) { + $actualValue = $this->request->getRemoteAddress(); + $decodedValue = explode('/', $value); + + if ($operator === 'matchesIPv4') { + return $this->matchIPv4($actualValue, $decodedValue[0], $decodedValue[1]); + } else if ($operator === '!matchesIPv4') { + return !$this->matchIPv4($actualValue, $decodedValue[0], $decodedValue[1]); + } else if ($operator === 'matchesIPv6') { + return $this->matchIPv6($actualValue, $decodedValue[0], $decodedValue[1]); + } else { + return !$this->matchIPv6($actualValue, $decodedValue[0], $decodedValue[1]); + } + } + + /** + * @param string $operator + * @param string $value + * @throws \UnexpectedValueException + */ + public function validateCheck($operator, $value) { + if (!in_array($operator, ['matchesIPv4', '!matchesIPv4', 'matchesIPv6', '!matchesIPv6'])) { + throw new \UnexpectedValueException($this->l->t('The given operator is invalid'), 1); + } + + $decodedValue = explode('/', $value); + if (sizeof($decodedValue) !== 2) { + throw new \UnexpectedValueException($this->l->t('The given IP range is invalid'), 2); + } + + if (in_array($operator, ['matchesIPv4', '!matchesIPv4'])) { + if (!filter_var($decodedValue[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { + throw new \UnexpectedValueException($this->l->t('The given IP range is not valid for IPv4'), 3); + } + if ($decodedValue[1] > 32 || $decodedValue[1] <= 0) { + throw new \UnexpectedValueException($this->l->t('The given IP range is not valid for IPv4'), 4); + } + } else { + if (!filter_var($decodedValue[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + throw new \UnexpectedValueException($this->l->t('The given IP range is not valid for IPv6'), 3); + } + if ($decodedValue[1] > 128 || $decodedValue[1] <= 0) { + throw new \UnexpectedValueException($this->l->t('The given IP range is not valid for IPv6'), 4); + } + } + } + + /** + * Based on http://stackoverflow.com/a/594134 + * @param string $ip + * @param string $rangeIp + * @param int $bits + * @return bool + */ + protected function matchIPv4($ip, $rangeIp, $bits) { + $rangeDecimal = ip2long($rangeIp); + $ipDecimal = ip2long($ip); + $mask = -1 << (32 - $bits); + return ($ipDecimal & $mask) === ($rangeDecimal & $mask); + } + + /** + * Based on http://stackoverflow.com/a/7951507 + * @param string $ip + * @param string $rangeIp + * @param int $bits + * @return bool + */ + protected function matchIPv6($ip, $rangeIp, $bits) { + $ipNet = inet_pton($ip); + $binaryIp = $this->ipv6ToBits($ipNet); + $ipNetBits = substr($binaryIp, 0, $bits); + + $rangeNet = inet_pton($rangeIp); + $binaryRange = $this->ipv6ToBits($rangeNet); + $rangeNetBits = substr($binaryRange, 0, $bits); + + return $ipNetBits === $rangeNetBits; + } + + /** + * Based on http://stackoverflow.com/a/7951507 + * @param string $packedIp + * @return string + */ + protected function ipv6ToBits($packedIp) { + $unpackedIp = unpack('A16', $packedIp); + $unpackedIp = str_split($unpackedIp[1]); + $binaryIp = ''; + foreach ($unpackedIp as $char) { + $binaryIp .= str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT); + } + return str_pad($binaryIp, 128, '0', STR_PAD_RIGHT); + } +} diff --git a/apps/workflowengine/lib/Check/RequestTime.php b/apps/workflowengine/lib/Check/RequestTime.php new file mode 100644 index 00000000000..2aa79e77673 --- /dev/null +++ b/apps/workflowengine/lib/Check/RequestTime.php @@ -0,0 +1,129 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\WorkflowEngine\Check; + + +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Files\Storage\IStorage; +use OCP\IL10N; +use OCP\WorkflowEngine\ICheck; + +class RequestTime implements ICheck { + + const REGEX_TIME = '([0-1][0-9]|2[0-3]):([0-5][0-9])'; + const REGEX_TIMEZONE = '([a-zA-Z]+(?:\\/[a-zA-Z\-\_]+)+)'; + + /** @var bool[] */ + protected $cachedResults; + + /** @var IL10N */ + protected $l; + + /** @var ITimeFactory */ + protected $timeFactory; + + /** + * @param ITimeFactory $timeFactory + */ + public function __construct(IL10N $l, ITimeFactory $timeFactory) { + $this->l = $l; + $this->timeFactory = $timeFactory; + } + + /** + * @param IStorage $storage + * @param string $path + */ + public function setFileInfo(IStorage $storage, $path) { + // A different path doesn't change time, so nothing to do here. + } + + /** + * @param string $operator + * @param string $value + * @return bool + */ + public function executeCheck($operator, $value) { + $valueHash = md5($value); + + if (isset($this->cachedResults[$valueHash])) { + return $this->cachedResults[$valueHash]; + } + + $timestamp = $this->timeFactory->getTime(); + + $values = json_decode($value, true); + $timestamp1 = $this->getTimestamp($timestamp, $values[0]); + $timestamp2 = $this->getTimestamp($timestamp, $values[1]); + + if ($timestamp1 < $timestamp2) { + $in = $timestamp1 <= $timestamp && $timestamp <= $timestamp2; + } else { + $in = $timestamp1 <= $timestamp || $timestamp <= $timestamp2; + } + + return ($operator === 'in') ? $in : !$in; + } + + /** + * @param int $currentTimestamp + * @param string $value Format: "H:i e" + * @return int + */ + protected function getTimestamp($currentTimestamp, $value) { + list($time1, $timezone1) = explode(' ', $value); + list($hour1, $minute1) = explode(':', $time1); + $date1 = new \DateTime('now', new \DateTimeZone($timezone1)); + $date1->setTimestamp($currentTimestamp); + $date1->setTime($hour1, $minute1); + + return $date1->getTimestamp(); + } + + /** + * @param string $operator + * @param string $value + * @throws \UnexpectedValueException + */ + public function validateCheck($operator, $value) { + if (!in_array($operator, ['in', '!in'])) { + throw new \UnexpectedValueException($this->l->t('The given operator is invalid'), 1); + } + + $regexValue = '\"' . self::REGEX_TIME . ' ' . self::REGEX_TIMEZONE . '\"'; + $result = preg_match('/^\[' . $regexValue . ',' . $regexValue . '\]$/', $value, $matches); + if (!$result) { + throw new \UnexpectedValueException($this->l->t('The given time span is invalid'), 2); + } + + $values = json_decode($value, true); + $time1 = \DateTime::createFromFormat('H:i e', $values[0]); + if ($time1 === false) { + throw new \UnexpectedValueException($this->l->t('The given start time is invalid'), 3); + } + + $time2 = \DateTime::createFromFormat('H:i e', $values[1]); + if ($time2 === false) { + throw new \UnexpectedValueException($this->l->t('The given end time is invalid'), 4); + } + } +} diff --git a/apps/workflowengine/lib/Check/RequestURL.php b/apps/workflowengine/lib/Check/RequestURL.php new file mode 100644 index 00000000000..36d41c101f2 --- /dev/null +++ b/apps/workflowengine/lib/Check/RequestURL.php @@ -0,0 +1,92 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\WorkflowEngine\Check; + + +use OCP\IL10N; +use OCP\IRequest; + +class RequestURL extends AbstractStringCheck { + + /** @var string */ + protected $url; + + /** @var IRequest */ + protected $request; + + /** + * @param IL10N $l + * @param IRequest $request + */ + public function __construct(IL10N $l, IRequest $request) { + parent::__construct($l); + $this->request = $request; + } + + /** + * @param string $operator + * @param string $value + * @return bool + */ + public function executeCheck($operator, $value) { + $actualValue = $this->getActualValue(); + if (in_array($operator, ['is', '!is'])) { + switch ($value) { + case 'webdav': + if ($operator === 'is') { + return $this->isWebDAVRequest(); + } else { + return !$this->isWebDAVRequest(); + } + } + } + return $this->executeStringCheck($operator, $value, $actualValue); + } + + /** + * @return string + */ + protected function getActualValue() { + if ($this->url !== null) { + return $this->url; + } + + $this->url = $this->request->getServerProtocol() . '://';// E.g. http(s) + :// + $this->url .= $this->request->getServerHost();// E.g. localhost + $this->url .= $this->request->getScriptName();// E.g. /nextcloud/index.php + $this->url .= $this->request->getPathInfo();// E.g. /apps/files_texteditor/ajax/loadfile + + return $this->url; // E.g. https://localhost/nextcloud/index.php/apps/files_texteditor/ajax/loadfile + } + + /** + * @return bool + */ + protected function isWebDAVRequest() { + return substr($this->request->getScriptName(), 0 - strlen('/remote.php')) === '/remote.php' && ( + $this->request->getPathInfo() === '/webdav' || + strpos($this->request->getPathInfo(), '/webdav/') === 0 || + $this->request->getPathInfo() === '/dav/files' || + strpos($this->request->getPathInfo(), '/dav/files/') === 0 + ); + } +} diff --git a/apps/workflowengine/lib/Check/RequestUserAgent.php b/apps/workflowengine/lib/Check/RequestUserAgent.php new file mode 100644 index 00000000000..7a8d4a71acf --- /dev/null +++ b/apps/workflowengine/lib/Check/RequestUserAgent.php @@ -0,0 +1,74 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\WorkflowEngine\Check; + + +use OCP\IL10N; +use OCP\IRequest; + +class RequestUserAgent extends AbstractStringCheck { + + /** @var IRequest */ + protected $request; + + /** + * @param IL10N $l + * @param IRequest $request + */ + public function __construct(IL10N $l, IRequest $request) { + parent::__construct($l); + $this->request = $request; + } + + /** + * @param string $operator + * @param string $value + * @return bool + */ + public function executeCheck($operator, $value) { + $actualValue = $this->getActualValue(); + if (in_array($operator, ['is', '!is'])) { + switch ($value) { + case 'android': + $operator = $operator === 'is' ? 'matches' : '!matches'; + $value = IRequest::USER_AGENT_CLIENT_ANDROID; + break; + case 'ios': + $operator = $operator === 'is' ? 'matches' : '!matches'; + $value = IRequest::USER_AGENT_CLIENT_IOS; + break; + case 'desktop': + $operator = $operator === 'is' ? 'matches' : '!matches'; + $value = IRequest::USER_AGENT_CLIENT_DESKTOP; + break; + } + } + return $this->executeStringCheck($operator, $value, $actualValue); + } + + /** + * @return string + */ + protected function getActualValue() { + return (string) $this->request->getHeader('User-Agent'); + } +} diff --git a/apps/workflowengine/lib/Check/UserGroupMembership.php b/apps/workflowengine/lib/Check/UserGroupMembership.php index 6390c57fbea..fd6ba00d092 100644 --- a/apps/workflowengine/lib/Check/UserGroupMembership.php +++ b/apps/workflowengine/lib/Check/UserGroupMembership.php @@ -89,11 +89,11 @@ class UserGroupMembership implements ICheck { */ public function validateCheck($operator, $value) { if (!in_array($operator, ['is', '!is'])) { - throw new \UnexpectedValueException($this->l->t('Operator %s is invalid', $operator), 1); + throw new \UnexpectedValueException($this->l->t('The given operator is invalid'), 1); } if (!$this->groupManager->groupExists($value)) { - throw new \UnexpectedValueException($this->l->t('Group %s does not exist', $value), 2); + throw new \UnexpectedValueException($this->l->t('The given group does not exist'), 2); } } diff --git a/apps/workflowengine/lib/Controller/RequestTime.php b/apps/workflowengine/lib/Controller/RequestTime.php new file mode 100644 index 00000000000..dd0efa89b91 --- /dev/null +++ b/apps/workflowengine/lib/Controller/RequestTime.php @@ -0,0 +1,52 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\WorkflowEngine\Controller; + +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\JSONResponse; + +class RequestTime extends Controller { + + /** + * @NoAdminRequired + * + * @param string $search + * @return JSONResponse + */ + public function getTimezones($search = '') { + $timezones = \DateTimeZone::listIdentifiers(); + + if ($search !== '') { + $timezones = array_filter($timezones, function ($timezone) use ($search) { + return strpos(strtolower($timezone), strtolower($search)) !== false; + }); + } + + $timezones = array_slice($timezones, 0, 10); + + $response = []; + foreach ($timezones as $timezone) { + $response[$timezone] = $timezone; + } + return new JSONResponse($response); + } +} diff --git a/apps/workflowengine/templates/admin.php b/apps/workflowengine/templates/admin.php index 4eabf783bba..935e8c70f17 100644 --- a/apps/workflowengine/templates/admin.php +++ b/apps/workflowengine/templates/admin.php @@ -36,7 +36,7 @@ {{#if operation.id}} <span class="button-delete pull-right icon-delete"></span> {{/if}} - <span class="pull-right info">{{operation.class}} - ID: {{operation.id}} - operation: {{operation.operation}}</span> + <input type="text" class="pull-right operation-operation" value="{{operation.operation}}"> <div class="checks"> {{#each operation.checks}} diff --git a/apps/workflowengine/tests/Check/AbstractStringCheckTest.php b/apps/workflowengine/tests/Check/AbstractStringCheckTest.php new file mode 100644 index 00000000000..91da8931604 --- /dev/null +++ b/apps/workflowengine/tests/Check/AbstractStringCheckTest.php @@ -0,0 +1,149 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\WorkflowEngine\Tests\Check; + + +class AbstractStringCheckTest extends \Test\TestCase { + + protected function getCheckMock() { + $l = $this->getMockBuilder('OCP\IL10N') + ->disableOriginalConstructor() + ->getMock(); + $l->expects($this->any()) + ->method('t') + ->willReturnCallback(function($string, $args) { + return sprintf($string, $args); + }); + + $check = $this->getMockBuilder('OCA\WorkflowEngine\Check\AbstractStringCheck') + ->setConstructorArgs([ + $l, + ]) + ->setMethods([ + 'setPath', + 'executeCheck', + 'getActualValue', + ]) + ->getMock(); + + return $check; + } + + public function dataExecuteStringCheck() { + return [ + ['is', 'same', 'same', true], + ['is', 'different', 'not the same', false], + ['!is', 'same', 'same', false], + ['!is', 'different', 'not the same', true], + + ['matches', '/match/', 'match', true], + ['matches', '/different/', 'not the same', false], + ['!matches', '/match/', 'match', false], + ['!matches', '/different/', 'not the same', true], + ]; + } + + /** + * @dataProvider dataExecuteStringCheck + * @param string $operation + * @param string $checkValue + * @param string $actualValue + * @param bool $expected + */ + public function testExecuteStringCheck($operation, $checkValue, $actualValue, $expected) { + $check = $this->getCheckMock(); + + /** @var \OCA\WorkflowEngine\Check\AbstractStringCheck $check */ + $this->assertEquals($expected, $this->invokePrivate($check, 'executeStringCheck', [$operation, $checkValue, $actualValue])); + } + + public function dataValidateCheck() { + return [ + ['is', '/Invalid(Regex/'], + ['!is', '/Invalid(Regex/'], + ['matches', '/Valid(Regex)/'], + ['!matches', '/Valid(Regex)/'], + ]; + } + + /** + * @dataProvider dataValidateCheck + * @param string $operator + * @param string $value + */ + public function testValidateCheck($operator, $value) { + $check = $this->getCheckMock(); + + /** @var \OCA\WorkflowEngine\Check\AbstractStringCheck $check */ + $check->validateCheck($operator, $value); + } + + public function dataValidateCheckInvalid() { + return [ + ['!!is', '', 1, 'The given operator is invalid'], + ['less', '', 1, 'The given operator is invalid'], + ['matches', '/Invalid(Regex/', 2, 'The given regular expression is invalid'], + ['!matches', '/Invalid(Regex/', 2, 'The given regular expression is invalid'], + ]; + } + + /** + * @dataProvider dataValidateCheckInvalid + * @param $operator + * @param $value + * @param $exceptionCode + * @param $exceptionMessage + */ + public function testValidateCheckInvalid($operator, $value, $exceptionCode, $exceptionMessage) { + $check = $this->getCheckMock(); + + try { + /** @var \OCA\WorkflowEngine\Check\AbstractStringCheck $check */ + $check->validateCheck($operator, $value); + } catch (\UnexpectedValueException $e) { + $this->assertEquals($exceptionCode, $e->getCode()); + $this->assertEquals($exceptionMessage, $e->getMessage()); + } + } + + public function dataMatch() { + return [ + ['/valid/', 'valid', [], true], + ['/valid/', 'valid', [md5('/valid/') => [md5('valid') => false]], false], // Cache hit + ]; + } + + /** + * @dataProvider dataMatch + * @param string $pattern + * @param string $subject + * @param array[] $matches + * @param bool $expected + */ + public function testMatch($pattern, $subject, $matches, $expected) { + $check = $this->getCheckMock(); + + $this->invokePrivate($check, 'matches', [$matches]); + + $this->assertEquals($expected, $this->invokePrivate($check, 'match', [$pattern, $subject])); + } +} diff --git a/apps/workflowengine/tests/Check/RequestRemoteAddressTest.php b/apps/workflowengine/tests/Check/RequestRemoteAddressTest.php new file mode 100644 index 00000000000..efe8f6372dd --- /dev/null +++ b/apps/workflowengine/tests/Check/RequestRemoteAddressTest.php @@ -0,0 +1,138 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\WorkflowEngine\Tests\Check; + + +class RequestRemoteAddressTest extends \Test\TestCase { + + /** @var \OCP\IRequest|\PHPUnit_Framework_MockObject_MockObject */ + protected $request; + + /** + * @return \OCP\IL10N|\PHPUnit_Framework_MockObject_MockObject + */ + protected function getL10NMock() { + $l = $this->getMockBuilder('OCP\IL10N') + ->disableOriginalConstructor() + ->getMock(); + $l->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($string, $args) { + return sprintf($string, $args); + }); + return $l; + } + + protected function setUp() { + parent::setUp(); + + $this->request = $this->getMockBuilder('OCP\IRequest') + ->getMock(); + } + + public function dataExecuteCheckIPv4() { + return [ + ['127.0.0.1/32', '127.0.0.1', true], + ['127.0.0.1/32', '127.0.0.0', false], + ['127.0.0.1/31', '127.0.0.0', true], + ['127.0.0.1/32', '127.0.0.2', false], + ['127.0.0.1/31', '127.0.0.2', false], + ['127.0.0.1/30', '127.0.0.2', true], + ]; + } + + /** + * @dataProvider dataExecuteCheckIPv4 + * @param string $value + * @param string $ip + * @param bool $expected + */ + public function testExecuteCheckMatchesIPv4($value, $ip, $expected) { + $check = new \OCA\WorkflowEngine\Check\RequestRemoteAddress($this->getL10NMock(), $this->request); + + $this->request->expects($this->once()) + ->method('getRemoteAddress') + ->willReturn($ip); + + $this->assertEquals($expected, $check->executeCheck('matchesIPv4', $value)); + } + + /** + * @dataProvider dataExecuteCheckIPv4 + * @param string $value + * @param string $ip + * @param bool $expected + */ + public function testExecuteCheckNotMatchesIPv4($value, $ip, $expected) { + $check = new \OCA\WorkflowEngine\Check\RequestRemoteAddress($this->getL10NMock(), $this->request); + + $this->request->expects($this->once()) + ->method('getRemoteAddress') + ->willReturn($ip); + + $this->assertEquals(!$expected, $check->executeCheck('!matchesIPv4', $value)); + } + + public function dataExecuteCheckIPv6() { + return [ + ['::1/128', '::1', true], + ['::2/128', '::3', false], + ['::2/127', '::3', true], + ['::1/128', '::2', false], + ['::1/127', '::2', false], + ['::1/126', '::2', true], + ['1234::1/127', '1234::', true], + ]; + } + + /** + * @dataProvider dataExecuteCheckIPv6 + * @param string $value + * @param string $ip + * @param bool $expected + */ + public function testExecuteCheckMatchesIPv6($value, $ip, $expected) { + $check = new \OCA\WorkflowEngine\Check\RequestRemoteAddress($this->getL10NMock(), $this->request); + + $this->request->expects($this->once()) + ->method('getRemoteAddress') + ->willReturn($ip); + + $this->assertEquals($expected, $check->executeCheck('matchesIPv6', $value)); + } + + /** + * @dataProvider dataExecuteCheckIPv6 + * @param string $value + * @param string $ip + * @param bool $expected + */ + public function testExecuteCheckNotMatchesIPv6($value, $ip, $expected) { + $check = new \OCA\WorkflowEngine\Check\RequestRemoteAddress($this->getL10NMock(), $this->request); + + $this->request->expects($this->once()) + ->method('getRemoteAddress') + ->willReturn($ip); + + $this->assertEquals(!$expected, $check->executeCheck('!matchesIPv6', $value)); + } +} diff --git a/apps/workflowengine/tests/Check/RequestTimeTest.php b/apps/workflowengine/tests/Check/RequestTimeTest.php new file mode 100644 index 00000000000..c07b4bf8775 --- /dev/null +++ b/apps/workflowengine/tests/Check/RequestTimeTest.php @@ -0,0 +1,162 @@ +<?php +/** + * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\WorkflowEngine\Tests\Check; + + +class RequestTimeTest extends \Test\TestCase { + + /** @var \OCP\AppFramework\Utility\ITimeFactory|\PHPUnit_Framework_MockObject_MockObject */ + protected $timeFactory; + + /** + * @return \OCP\IL10N|\PHPUnit_Framework_MockObject_MockObject + */ + protected function getL10NMock() { + $l = $this->getMockBuilder('OCP\IL10N') + ->disableOriginalConstructor() + ->getMock(); + $l->expects($this->any()) + ->method('t') + ->willReturnCallback(function ($string, $args) { + return sprintf($string, $args); + }); + return $l; + } + + protected function setUp() { + parent::setUp(); + + $this->timeFactory = $this->getMockBuilder('OCP\AppFramework\Utility\ITimeFactory') + ->getMock(); + } + + public function dataExecuteCheck() { + return [ + [json_encode(['08:00 Europe/Berlin', '17:00 Europe/Berlin']), 1467870105, false], // 2016-07-07T07:41:45+02:00 + [json_encode(['08:00 Europe/Berlin', '17:00 Europe/Berlin']), 1467873705, true], // 2016-07-07T08:41:45+02:00 + [json_encode(['08:00 Europe/Berlin', '17:00 Europe/Berlin']), 1467902505, true], // 2016-07-07T16:41:45+02:00 + [json_encode(['08:00 Europe/Berlin', '17:00 Europe/Berlin']), 1467906105, false], // 2016-07-07T17:41:45+02:00 + [json_encode(['17:00 Europe/Berlin', '08:00 Europe/Berlin']), 1467870105, true], // 2016-07-07T07:41:45+02:00 + [json_encode(['17:00 Europe/Berlin', '08:00 Europe/Berlin']), 1467873705, false], // 2016-07-07T08:41:45+02:00 + [json_encode(['17:00 Europe/Berlin', '08:00 Europe/Berlin']), 1467902505, false], // 2016-07-07T16:41:45+02:00 + [json_encode(['17:00 Europe/Berlin', '08:00 Europe/Berlin']), 1467906105, true], // 2016-07-07T17:41:45+02:00 + + [json_encode(['08:00 Australia/Adelaide', '17:00 Australia/Adelaide']), 1467843105, false], // 2016-07-07T07:41:45+09:30 + [json_encode(['08:00 Australia/Adelaide', '17:00 Australia/Adelaide']), 1467846705, true], // 2016-07-07T08:41:45+09:30 + [json_encode(['08:00 Australia/Adelaide', '17:00 Australia/Adelaide']), 1467875505, true], // 2016-07-07T16:41:45+09:30 + [json_encode(['08:00 Australia/Adelaide', '17:00 Australia/Adelaide']), 1467879105, false], // 2016-07-07T17:41:45+09:30 + [json_encode(['17:00 Australia/Adelaide', '08:00 Australia/Adelaide']), 1467843105, true], // 2016-07-07T07:41:45+09:30 + [json_encode(['17:00 Australia/Adelaide', '08:00 Australia/Adelaide']), 1467846705, false], // 2016-07-07T08:41:45+09:30 + [json_encode(['17:00 Australia/Adelaide', '08:00 Australia/Adelaide']), 1467875505, false], // 2016-07-07T16:41:45+09:30 + [json_encode(['17:00 Australia/Adelaide', '08:00 Australia/Adelaide']), 1467879105, true], // 2016-07-07T17:41:45+09:30 + + [json_encode(['08:00 Pacific/Niue', '17:00 Pacific/Niue']), 1467916905, false], // 2016-07-07T07:41:45-11:00 + [json_encode(['08:00 Pacific/Niue', '17:00 Pacific/Niue']), 1467920505, true], // 2016-07-07T08:41:45-11:00 + [json_encode(['08:00 Pacific/Niue', '17:00 Pacific/Niue']), 1467949305, true], // 2016-07-07T16:41:45-11:00 + [json_encode(['08:00 Pacific/Niue', '17:00 Pacific/Niue']), 1467952905, false], // 2016-07-07T17:41:45-11:00 + [json_encode(['17:00 Pacific/Niue', '08:00 Pacific/Niue']), 1467916905, true], // 2016-07-07T07:41:45-11:00 + [json_encode(['17:00 Pacific/Niue', '08:00 Pacific/Niue']), 1467920505, false], // 2016-07-07T08:41:45-11:00 + [json_encode(['17:00 Pacific/Niue', '08:00 Pacific/Niue']), 1467949305, false], // 2016-07-07T16:41:45-11:00 + [json_encode(['17:00 Pacific/Niue', '08:00 Pacific/Niue']), 1467952905, true], // 2016-07-07T17:41:45-11:00 + ]; + } + + /** + * @dataProvider dataExecuteCheck + * @param string $value + * @param int $timestamp + * @param bool $expected + */ + public function testExecuteCheckIn($value, $timestamp, $expected) { + $check = new \OCA\WorkflowEngine\Check\RequestTime($this->getL10NMock(), $this->timeFactory); + + $this->timeFactory->expects($this->once()) + ->method('getTime') + ->willReturn($timestamp); + + $this->assertEquals($expected, $check->executeCheck('in', $value)); + } + + /** + * @dataProvider dataExecuteCheck + * @param string $value + * @param int $timestamp + * @param bool $expected + */ + public function testExecuteCheckNotIn($value, $timestamp, $expected) { + $check = new \OCA\WorkflowEngine\Check\RequestTime($this->getL10NMock(), $this->timeFactory); + + $this->timeFactory->expects($this->once()) + ->method('getTime') + ->willReturn($timestamp); + + $this->assertEquals(!$expected, $check->executeCheck('!in', $value)); + } + + public function dataValidateCheck() { + return [ + ['in', '["08:00 Europe/Berlin","17:00 Europe/Berlin"]'], + ['!in', '["08:00 Europe/Berlin","17:00 America/North_Dakota/Beulah"]'], + ['in', '["08:00 America/Port-au-Prince","17:00 America/Argentina/San_Luis"]'], + ]; + } + + /** + * @dataProvider dataValidateCheck + * @param string $operator + * @param string $value + */ + public function testValidateCheck($operator, $value) { + $check = new \OCA\WorkflowEngine\Check\RequestTime($this->getL10NMock(), $this->timeFactory); + $check->validateCheck($operator, $value); + } + + public function dataValidateCheckInvalid() { + return [ + ['!!in', '["08:00 Europe/Berlin","17:00 Europe/Berlin"]', 1, 'The given operator is invalid'], + ['in', '["28:00 Europe/Berlin","17:00 Europe/Berlin"]', 2, 'The given time span is invalid'], + ['in', '["08:00 Europe/Berlin","27:00 Europe/Berlin"]', 2, 'The given time span is invalid'], + ['in', '["08:00 Europa/Berlin","17:00 Europe/Berlin"]', 3, 'The given start time is invalid'], + ['in', '["08:00 Europe/Berlin","17:00 Europa/Berlin"]', 4, 'The given end time is invalid'], + ['in', '["08:00 Europe/Bearlin","17:00 Europe/Berlin"]', 3, 'The given start time is invalid'], + ['in', '["08:00 Europe/Berlin","17:00 Europe/Bearlin"]', 4, 'The given end time is invalid'], + ]; + } + + /** + * @dataProvider dataValidateCheckInvalid + * @param string $operator + * @param string $value + * @param int $exceptionCode + * @param string $exceptionMessage + */ + public function testValidateCheckInvalid($operator, $value, $exceptionCode, $exceptionMessage) { + $check = new \OCA\WorkflowEngine\Check\RequestTime($this->getL10NMock(), $this->timeFactory); + + try { + $check->validateCheck($operator, $value); + } catch (\UnexpectedValueException $e) { + $this->assertEquals($exceptionCode, $e->getCode()); + $this->assertEquals($exceptionMessage, $e->getMessage()); + } + } +} |