This ticket was divided into 4 small almost independent patches.

1) 62-Added-support-of-custom-field-validators
2) 63-Added-validation-logic-to-multivalued-text-field - depends on 1
3) 64-Added-client-side-validation-of-A-and-AAAA-DNS-recor - depends on 1
4) 65-Fixed-IPv6-validation-special-case-single-colon - minor fix


Patch descriptions:

1) Added support of custom field validators

Current validation logic supports only validation based on metadata. It can be extended only by overriding field's validation method. This approach requires creating subclasses of field for each different format of desired value. It's inconvenient for cases like adding the same validation logic to two different subclasses of field.

This patch is adding support for creating custom validators.

Validator is an object which contains validation logic. Validation is executed in a validate(value, context) method. This method checks if the value is valid and returns a validation result. Validation result is a simple object which contains valid property and an error message if valid is false.

Field is extended by validators property. It can be set in spec object or later. It should contain instances of validators for the field. Validators are run in field's validation method.


2) Added validation logic to multivalued text field
3) Added client-side validation of A and AAAA DNS records
4) Fixed IPv6 validation special case: single colon

IPv6 parsing was incorrectly evaluating ':' as a valid IPv6 address.

All: https://fedorahosted.org/freeipa/ticket/1466
--
Petr Vobornik
From 986cd0ce439ae1eca8a0e623546ca44107fa78a1 Mon Sep 17 00:00:00 2001
From: Petr Vobornik <pvobo...@redhat.com>
Date: Mon, 2 Jan 2012 10:33:09 +0100
Subject: [PATCH 62/65] Added support of custom field validators

Current validation logic supports only validation based on metadata. It can be extended only by overriding field's validation method. This approach requires creating subclasses of field for each different format of desired value. It's inconvenient for cases like adding the same validation logic to two different subclasses of field.

This patch is adding support for creating custom validators.

Validator is an object which contains validation logic. Validation is executed in a validate(value, context) method. This method checks if the value is valid and returns a validation result. Validation result is a simple object which contains valid property and an error message if valid is false.

Field is extended by validators property. It can be set in spec object or later. It should contain instances of validators for the field. Validators are run in field's validation method.

This patch is a prerequisite for:
https://fedorahosted.org/freeipa/ticket/1466
---
 install/ui/field.js |  140 ++++++++++++++++++++++++++++++++-------------------
 1 files changed, 88 insertions(+), 52 deletions(-)

diff --git a/install/ui/field.js b/install/ui/field.js
index 18a52c9b6344ace274601c95ed25f12774cf778f..fc6b75ddaebc940bb6b7aed8ec4e32693b335364 100644
--- a/install/ui/field.js
+++ b/install/ui/field.js
@@ -53,6 +53,7 @@ IPA.field = function(spec) {
     that.join = spec.join;
 
     that.metadata = spec.metadata;
+    that.validators = spec.validators || [];
 
     that.priority = spec.priority;
 
@@ -74,6 +75,8 @@ IPA.field = function(spec) {
                 that.tooltip = that.metadata.doc;
             }
         }
+
+        that.validators.push(IPA.metadata_validator());
     };
 
     that.is_required = function() {
@@ -98,71 +101,37 @@ IPA.field = function(spec) {
 
     that.validate_required = function() {
         var values = that.save();
-        if (!values || !values.length || values[0] === '') {
-            if (that.is_required()) {
-                that.valid = false;
-                that.show_error(IPA.messages.widget.validation.required);
-                return false;
-            }
+        if (that.is_empty(values) && that.is_required()) {
+            that.valid = false;
+            that.show_error(IPA.messages.widget.validation.required);
+            return false;
         }
         return true;
     };
 
-    /*returns true and clears the error message if the field value  passes
-     *   the validation pattern.  If the field value does not pass validation,
-     *   displays the error message and returns false. */
+    /**
+     *   Returns true and clears the error message if the field value passes
+     *   the validation pattern. If the field value does not pass validation,
+     *   displays the error message and returns false.
+     */
     that.validate = function() {
         that.hide_error();
         that.valid = true;
 
         var values = that.save();
-        if (!values) {
-            return that.valid;
-        }
-        if (values.length === 0) {
+
+        if (that.is_empty(values)) {
             return that.valid;
         }
+
         var value = values[0];
-        if (!value) {
-            return that.valid;
-        }
 
-        if (!that.metadata) {
-            return that.valid;
-        }
-
-        var message;
-
-        if (that.metadata.type == 'int') {
-            if (!value.match(/^-?\d+$/)) {
-                that.valid = false;
-                that.show_error(IPA.messages.widget.validation.integer);
-                return that.valid;
-            }
-
-            if (that.metadata.minvalue !== undefined && value < that.metadata.minvalue) {
-                that.valid = false;
-                message = IPA.messages.widget.validation.min_value;
-                message = message.replace('${value}', that.metadata.minvalue);
-                that.show_error(message);
-                return that.valid;
-            }
-
-            if (that.metadata.maxvalue !== undefined && value > that.metadata.maxvalue) {
-                that.valid = false;
-                message = IPA.messages.widget.validation.max_value;
-                message = message.replace('${value}', that.metadata.maxvalue);
-                that.show_error(message);
-                return that.valid;
-            }
-        }
-
-        if (that.metadata.pattern) {
-            var regex = new RegExp(that.metadata.pattern);
-            if (!value.match(regex)) {
-                that.valid = false;
-                that.show_error(that.metadata.pattern_errmsg);
-                return that.valid;
+        for (var i=0; i<that.validators.length; i++) {
+            var validation_result = that.validators[i].validate(value, that);
+            that.valid = validation_result.valid;
+            if (!that.valid) {
+                that.show_error(validation_result.message);
+                break;
             }
         }
 
@@ -403,6 +372,73 @@ IPA.field = function(spec) {
     return that;
 };
 
+IPA.validator = function(spec) {
+
+    spec = spec || {};
+
+    var that = {};
+
+    that.validate = function() {
+        return { valid: true };
+    };
+
+    return that;
+};
+
+IPA.metadata_validator = function(spec) {
+
+    var that = IPA.validator(spec);
+
+    that.validate = function(value, context) {
+
+        var message;
+        var metadata = context.metadata;
+
+        if (!metadata) return { valid: true };
+
+        if (metadata.type == 'int') {
+            if (!value.match(/^-?\d+$/)) {
+                return {
+                    valid: false,
+                    message: IPA.messages.widget.validation.integer
+                };
+            }
+
+            if (metadata.minvalue !== undefined && value < metadata.minvalue) {
+                message = IPA.messages.widget.validation.min_value;
+                message = message.replace('${value}', metadata.minvalue);
+                return {
+                    valid: false,
+                    message: message
+                };
+            }
+
+            if (metadata.maxvalue !== undefined && value > metadata.maxvalue) {
+                message = IPA.messages.widget.validation.max_value;
+                message = message.replace('${value}', metadata.maxvalue);
+                return {
+                    valid: false,
+                    message: message
+                };
+            }
+        }
+
+        if (metadata.pattern) {
+            var regex = new RegExp(metadata.pattern);
+            if (!value.match(regex)) {
+                return {
+                    valid: false,
+                    message: metadata.pattern_errmsg
+                };
+            }
+        }
+
+        return { valid: true };
+    };
+
+    return that;
+};
+
 IPA.checkbox_field = function(spec) {
 
     spec = spec || {};
-- 
1.7.6.4

From 2debf6974a56f4d8666b3d0d05f45f9202ff0bad Mon Sep 17 00:00:00 2001
From: Petr Vobornik <pvobo...@redhat.com>
Date: Mon, 2 Jan 2012 16:41:04 +0100
Subject: [PATCH 63/65] Added validation logic to multivalued text field

https://fedorahosted.org/freeipa/ticket/1466
---
 install/ui/field.js  |   30 +++++++++++++++++++++++++++++-
 install/ui/widget.js |   27 ++++++++++++++++++++++++++-
 2 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/install/ui/field.js b/install/ui/field.js
index fc6b75ddaebc940bb6b7aed8ec4e32693b335364..8db0c87cb0ff28ad6a8f8dc6bc0532538591c143 100644
--- a/install/ui/field.js
+++ b/install/ui/field.js
@@ -522,7 +522,35 @@ IPA.multivalued_field = function(spec) {
 
     that.widget_value_changed = function() {
         that.set_dirty(that.test_dirty());
-        //that.validate(); disabling validation for now
+        that.validate();
+    };
+
+    that.validate = function() {
+
+        that.hide_error();
+        that.valid = true;
+
+        var values = that.save();
+
+        if (that.is_empty(values)) {
+            return that.valid;
+        }
+
+        for (var i=0; i<values.length; i++) {
+
+            for (var j=0; j<that.validators.length; j++) {
+
+                var validation_result = that.validators[j].validate(values[i], that);
+                if (!validation_result.valid) {
+                    that.valid = false;
+                    var row_index = that.widget.get_saved_value_row_index(i);
+                    that.widget.show_child_error(row_index, validation_result.message);
+                    break;
+                }
+            }
+        }
+
+        return that.valid;
     };
 
     return that;
diff --git a/install/ui/widget.js b/install/ui/widget.js
index 43910d6e603ae90f7d5e2717c1d08493b3fc6c8e..38646c99836060eafe1c72d69efd476b2f465a9b 100644
--- a/install/ui/widget.js
+++ b/install/ui/widget.js
@@ -351,7 +351,7 @@ IPA.multivalued_text_widget = function(spec) {
         for(var i=0; i<that.rows.length; i++) {
             var row = that.rows[i];
             row.widget.hide_undo();
-            if(row.is_new) row.remove_link.show();
+            row.remove_link.show();
         }
     };
 
@@ -365,6 +365,31 @@ IPA.multivalued_text_widget = function(spec) {
         that.show_undo();
     };
 
+    that.hide_error = function() {
+
+        that.widget_hide_error();
+
+        for (var i=0; i<that.rows.length; i++) {
+            that.rows[i].widget.hide_error();
+        }
+    };
+
+    that.show_child_error = function(index, error) {
+
+        that.rows[index].widget.show_error(error);
+    };
+
+    that.get_saved_value_row_index = function(index) {
+
+        for (var i=0; i<that.rows.length;i++) {
+
+            if(that.rows[i].deleted) index++;
+            if(i === index) return i;
+        }
+
+        return -1; //error state
+    };
+
     that.save = function() {
 
         var values = [];
-- 
1.7.6.4

From 0042e8c09535adfaefce69f963afc585e2b313e9 Mon Sep 17 00:00:00 2001
From: Petr Vobornik <pvobo...@redhat.com>
Date: Mon, 2 Jan 2012 16:55:51 +0100
Subject: [PATCH 64/65] Added client-side validation of A and AAAA DNS records

https://fedorahosted.org/freeipa/ticket/1466
---
 install/ui/dns.js                  |   60 ++++++++++++++++++++++++++++++++++--
 install/ui/index.html              |    1 +
 install/ui/test/data/ipa_init.json |    3 ++
 ipalib/plugins/internal.py         |    3 ++
 4 files changed, 64 insertions(+), 3 deletions(-)

diff --git a/install/ui/dns.js b/install/ui/dns.js
index 4853a94453b22599f576dca096fb29348f4a196a..f4f93389ba7cb3850c14e687e8bd90d13b96e841 100644
--- a/install/ui/dns.js
+++ b/install/ui/dns.js
@@ -1,5 +1,6 @@
 /*jsl:import ipa.js */
 /*jsl:import search.js */
+/*jsl:import net.js */
 
 /*  Authors:
  *    Adam Young <ayo...@redhat.com>
@@ -21,7 +22,8 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js, widget.js */
+/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js,
+ *           net.js, widget.js */
 
 IPA.dns = {};
 
@@ -526,13 +528,15 @@ IPA.dns.record_entity = function(spec) {
                             type: 'multivalued',
                             name: 'arecord',
                             metadata: { primary_key: false },
-                            label: 'A'
+                            label: 'A',
+                            validators: [ IPA.ip_v4_address_validator() ]
                         },
                         {
                             type: 'multivalued',
                             name: 'aaaarecord',
                             metadata: { primary_key: false },
-                            label: 'AAAA'
+                            label: 'AAAA',
+                            validators: [ IPA.ip_v6_address_validator() ]
                         },
                         {
                             type: 'multivalued',
@@ -824,5 +828,55 @@ IPA.dnsrecord_get_delete_values = function() {
     return value_array;
 };
 
+IPA.ip_address_validator = function(spec) {
+
+    var that = IPA.validator(spec);
+
+    that.address_type = spec.address_type;
+    that.message = spec.message || IPA.messages.widget.validation.ip_address;
+
+    that.validate = function(value) {
+
+        var address = NET.ip_address(value);
+
+        if (!address.valid || !that.is_type_match(address.type)) {
+            return {
+                valid: false,
+                message: that.message
+            };
+        }
+
+        return { valid: true };
+    };
+
+    that.is_type_match = function(net_type) {
+
+        return (!that.address_type ||
+
+                (that.address_type === 'IPv4' &&
+                    (net_type === 'v4-quads' || net_type === 'v4-int')) ||
+
+                (that.address_type === 'IPv6' && net_type === 'v6'));
+    };
+
+    return that;
+};
+
+IPA.ip_v4_address_validator = function(spec) {
+
+    spec = spec || {};
+    spec.address_type = 'IPv4';
+    spec.message = IPA.messages.widget.validation.ip_v4_address;
+    return IPA.ip_address_validator(spec);
+};
+
+IPA.ip_v6_address_validator = function(spec) {
+
+    spec = spec || {};
+    spec.address_type = 'IPv6';
+    spec.message = IPA.messages.widget.validation.ip_v6_address;
+    return IPA.ip_address_validator(spec);
+};
+
 IPA.register('dnszone', IPA.dns.zone_entity);
 IPA.register('dnsrecord', IPA.dns.record_entity);
diff --git a/install/ui/index.html b/install/ui/index.html
index 5c0ebdbb9fc7bf40c18535c2fa878e814e47e608..6239debab2afe4f9e5cdd514f8686727fbac036c 100644
--- a/install/ui/index.html
+++ b/install/ui/index.html
@@ -10,6 +10,7 @@
     <script type="text/javascript" src="jquery.ba-bbq.js"></script>
     <script type="text/javascript" src="jquery.ordered-map.js"></script>
     <script type="text/javascript" src="browser.js"></script>
+    <script type="text/javascript" src="net.js"></script>
 
     <script type="text/javascript" src="ipa.js"></script>
     <script type="text/javascript" src="widget.js"></script>
diff --git a/install/ui/test/data/ipa_init.json b/install/ui/test/data/ipa_init.json
index a5ef756ebb4cb302e3c9a3de9d24a043cbdac7a7..77af2f8f2e001f191488dfa384d5f055151afc76 100644
--- a/install/ui/test/data/ipa_init.json
+++ b/install/ui/test/data/ipa_init.json
@@ -376,6 +376,9 @@
                         "validation": {
                             "error": "Text does not match field pattern",
                             "integer": "Must be an integer",
+                            "ip_address": "Not a valid IP address",
+                            "ip_v4_address": "Not a valid IPv4 address",
+                            "ip_v6_address": "Not a valid IPv6 address",
                             "max_value": "Maximum value is ${value}",
                             "min_value": "Minimum value is ${value}",
                             "required": "Required field"
diff --git a/ipalib/plugins/internal.py b/ipalib/plugins/internal.py
index e4308140b3dae10104c1aa996535edd8101c1ff5..3eed9ec56929a03758986b1872640409048285c6 100644
--- a/ipalib/plugins/internal.py
+++ b/ipalib/plugins/internal.py
@@ -515,6 +515,9 @@ class i18n_messages(Command):
             "validation": {
                 "error": _("Text does not match field pattern"),
                 "integer": _("Must be an integer"),
+                "ip_address": _('Not a valid IP address'),
+                "ip_v4_address": _('Not a valid IPv4 address'),
+                "ip_v6_address": _('Not a valid IPv6 address'),
                 "max_value": _("Maximum value is ${value}"),
                 "min_value": _("Minimum value is ${value}"),
                 "required": _("Required field"),
-- 
1.7.6.4

From e62042316caa18caf5e74e1d48202b6a84bf92a8 Mon Sep 17 00:00:00 2001
From: Petr Vobornik <pvobo...@redhat.com>
Date: Tue, 3 Jan 2012 09:37:17 +0100
Subject: [PATCH 65/65] Fixed IPv6 validation special case: single colon

IPv6 parsing was incorrectly evaluating ':' as a valid IPv6 address.

https://fedorahosted.org/freeipa/ticket/1466
---
 install/ui/net.js           |    5 +++++
 install/ui/test/ip_tests.js |    3 +++
 2 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/install/ui/net.js b/install/ui/net.js
index 6b4b20cfa40a5a5ec22b34ff95b4e1a9f65df82b..9eba6dc7a77bad13230eee788807c92e58e085b3 100644
--- a/install/ui/net.js
+++ b/install/ui/net.js
@@ -125,6 +125,11 @@ NET.ip_address = function(spec) {
 
         var i;
 
+        //usecases like ':'
+        if (that.parts.length <= 2) {
+            return that.set_error('invalid format');
+        }
+
         for (i=0; i<that.parts.length; i++) {
             var part = that.parts[i];
 
diff --git a/install/ui/test/ip_tests.js b/install/ui/test/ip_tests.js
index 8bac3a6ad384c747c48d862a90683a9a4478e7f8..74fe7c2be890fa776c2362cc79dd0f567a0ad348 100644
--- a/install/ui/test/ip_tests.js
+++ b/install/ui/test/ip_tests.js
@@ -156,6 +156,9 @@ test('Testing incorrect IPv6 addresses', function() {
     address = NET.ip_address('2001:db8:85a3:0:0:8a2e:370');
     ok(!address.valid, 'Missing part - 2001:db8:85a3:0:0:8a2e:370');
 
+    address = NET.ip_address(':');
+    ok(!address.valid, 'Address - :');
+
     address = NET.ip_address('::1::');
     ok(!address.valid, 'Address - ::1::');
 
-- 
1.7.6.4

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to