Skip to content

Commit

Permalink
Issue #14: ip validation
Browse files Browse the repository at this point in the history
use regex to validate ipv6 subnet and ipv4 subnet. change
implementation for ipv4 directive to handele subnets and ipv6 addresses,
too.
  • Loading branch information
geirkairam committed Oct 24, 2015
1 parent cb8a8b6 commit 8e9a8bd
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 33 deletions.
37 changes: 26 additions & 11 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -459,14 +459,24 @@ <h2>

<div class="row" data-collapse="state.ip.register">
<div class="col-md-offset-3 col-md-9">
<div class="form-group">
<div class="form-group hasFeedback" ng-class="formFeedback('ipv6Prefix')">
<label for="ipv6Prefix" class="col-md-3 control-label" translate>
.v6Prefix.label
</label>
<div class="col-md-9">
<input type="text" class="form-control" id="ipv6Prefix"
<input type="text" class="form-control" name="ipv6Prefix" id="ipv6Prefix"
placeholder="{{'ip.v6Prefix.placeholder' | translate}}"
data-ng-model="wizard.ip.v6Prefix">
data-ng-model="wizard.ip.v6Prefix"
data-ng-required="!state.ip.register"
data-ip-address data-ip-version="6" data-ip-type="subnet">
<div class="help-block" ng-show="hasError('ipv6Prefix')">
<div ng-show="wizardForm['ipv6Prefix'].$error.required" translate>
.error.required
</div>
<div ng-show="wizardForm['ipv6Prefix'].$error.ipv6Subnet" translate>
.v6Prefix.error.subnet
</div>
</div>
</div>
</div>

Expand All @@ -481,13 +491,14 @@ <h2>
<input type="text" class="form-control" id="ipv4Wifi_{{$index}}" name="ipv4Wifi_{{$index}}"
translate translate-attr-placeholder=".v4.placeholder"
translate-values="{index: $index + 1}"
data-ng-model="wizard.ip.v4[name]" data-ipv4-address
data-ng-required="!state.ip.register">
data-ng-model="wizard.ip.v4[name]"
data-ng-required="!state.ip.register"
data-ip-address data-ip-version="4">
<span class="fa form-control-feedback"
ng-class="formControlFeedback('ipv4Wifi_' + $index)"></span>
<div class="help-block" ng-show="hasError('ipv4Wifi_' + $index)">
<div ng-show="wizardForm['ipv4Wifi_' + $index].$error.required" translate>
.v4.error.required
.error.required
</div>
<div ng-show="wizardForm['ipv4Wifi_' + $index].$error.ipv4Address" translate>
.v4.error.ipv4Address
Expand Down Expand Up @@ -523,13 +534,15 @@ <h2>
name="ipv4Lan"
translate translate-attr-placeholder=".v4.placeholder"
translate-values="{index: (state.wifi.devices | objectLength) + 1}"
data-ng-model="wizard.ip.v4.lan" data-ipv4-address
data-ng-required="wizard.ip.meshLan && !state.ip.generate">
data-ng-model="wizard.ip.v4.lan"
data-ng-required="wizard.ip.meshLan && !state.ip.generate"
data-ip-address
data-ip-version="4">
<span class="fa form-control-feedback"
ng-class="formControlFeedback('ipv4Lan')"></span>
<div class="help-block" ng-show="hasError('ipv4Lan')">
<div ng-show="wizardForm.ipv4Lan.$error.required" translate>
.v4.error.required
.error.required
</div>
<div ng-show="wizardForm.ipv4Lan.$error.ipv4Address" translate>
.v4.error.ipv4Address
Expand Down Expand Up @@ -568,15 +581,17 @@ <h2>
placeholder="{{'ip.distribute.subnet.placeholder' | translate}}"
data-ng-model="wizard.ip.v4ClientSubnet"
data-ng-required="wizard.ip.distribute && !state.ip.register"
data-ng-pattern="ipv4Pattern+'\/(25|26|27|28)'">
data-ip-address
data-ip-version="4"
data-ip-type="subnet">
<p class="text-muted" data-ng-hide="hasError('ipv4ClientSubnet')" translate>
.distribute.subnet.help
</p>
<div class="help-block" data-ng-show="hasError('ipv4ClientSubnet')">
<div data-ng-show="wizardForm.ipv4ClientSubnet.$error.required" translate>
.error.required
</div>
<div data-ng-show="wizardForm.ipv4ClientSubnet.$error.pattern" translate>
<div data-ng-show="wizardForm.ipv4ClientSubnet.$error.ipv4Subnet" translate>
.distribute.subnet.error.pattern
</div>
</div>
Expand Down
16 changes: 0 additions & 16 deletions src/js/controllers/wizard.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,22 +263,6 @@ module.exports = function(app) {
});
};

// helpers for validation
$scope.ipv4Pattern = '(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]?)';

//I really hate v6 regex patterns >:(

//http://home.deds.nl/~aeron/regex/
//$scope.ipv6Pattern = '((?=.*::)(?!.*::.+::)(::)?([\\dA-F]{1,4}:(:|ß\b)|){5}|([\\dA-F]{1,4}:){6})(((\[\dA-F]{1,4}((?!\\3)::|:\\b|$))|(?! \\2\\3)){2})';

//TODO more test and prefix addition
$scope.ipv6PrefixPattern = '((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4}))*::((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4}))*|((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4})){7}';

//http://regexlib.com/Search.aspx?k=ipv6&AspxAutoDetectCookieSupport=1

$scope.hasError = function(field) {
var form = $scope.wizardForm;
return (form.$submitted || form[field].$dirty) && form[field].$invalid;
Expand Down
114 changes: 109 additions & 5 deletions src/js/directives/ipv4Address.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,32 @@ var ip = require('ip');

module.exports = function(app) {
// check if the provided value is a valid ipv4 address
app.directive('ipv4Address', function() {
app.directive('ipAddress', function() {
return {
require: 'ngModel',
link: function(scope, element, attributes, ngModel) {
ngModel.$validators.ipv4Address = function(modelValue) {

var ipv4Pattern = '(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}' +
'(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)';
var ipv4SubnetPattern = ipv4Pattern + '\/(25|26|27|28)';

// there are a lot of regex for ipv6
// http://regexlib.com/Search.aspx?k=ipv6
// this one matches the expanded ipv6 pattern
var ipv6Pattern = '([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}';
var ipv6SubnetPattern = ipv6Pattern + '\/(64|65)';

var ipv4AddressValidator = function(modelValue) {
//validation for required should be done via ng-required directive
//or the validation even fails if the field is not required
if (typeof modelValue == 'undefined' || modelValue === '') {
if (isEmpty(modelValue)) {
return true;
}

// we run an additional regular expression against the model
// because the 'ip' module does not catch all cases
var ipRegex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
if (!ipRegex.exec(modelValue)) {
if (!regexMatches(ipv4Pattern, modelValue)) {
console.log('ipv4 pattern does not match');
return false;
}

Expand All @@ -32,6 +43,99 @@ module.exports = function(app) {
return true;
};

var ipv4SubnetValidator = function(modelValue) {
//validation for required should be done via ng-required directive
//or the validation even fails if the field is not required
if (isEmpty(modelValue)) {
return true;
}
return regexMatches(ipv4SubnetPattern, modelValue);
};

var ipv6AddressValidator = function(modelValue) {
if (isEmpty(modelValue)) {
return true;
}
modelValue = expandV6Address(modelValue);
return regexMatches(ipv6Pattern, modelValue);
};

var ipv6SubnetValidator = function(modelValue) {
if (isEmpty(modelValue)) {
return true;
}

var splitted = modelValue.split('/');
modelValue = expandV6Address(splitted[0]) + '/' + splitted[1];
var valid = regexMatches(ipv6SubnetPattern, modelValue);
return valid;
};

//expands an ipv6 address
var expandV6Address = function(modelValue) {
var collapsedV6 = modelValue.split(':');
modelValue = '';
for (var i = 0; i < collapsedV6.length; i++) {
if (isEmpty(collapsedV6[i]) && isEmpty(collapsedV6[i + 1])) {
//if there are two empty array items in a row
//it means the pattern :: has to be replaced with :0
for (var j = 8; j > collapsedV6.length - 2; j--) {
modelValue = modelValue + ':0';
}
//skip next entry
i++;
} else {
modelValue = modelValue + ':' + collapsedV6[i];
}
}
return modelValue.substr(1);
};

var regexMatches = function(pattern, modelValue) {
var ipRegex = new RegExp('^' + pattern + '$');
if (ipRegex.exec(modelValue)) {
return true;
}
return false;
};

var isEmpty = function(modelValue) {
if (typeof modelValue == 'undefined' || modelValue === '') {
return true;
}
return false;
};

//add validators
if (typeof attributes.ipVersion == 'undefined' ||
attributes.ipVersion == '4') {
if (typeof attributes.ipType == 'undefined') {
ngModel.$validators.ipv4Address = ipv4AddressValidator;
} else if (attributes.ipType == 'subnet') {
ngModel.$validators.ipv4Subnet = ipv4SubnetValidator;
} else {
if (console) {
console.error('ip type >' + attributes.ipType +
'< is not supported');
}
}
} else if (attributes.ipVersion == '6') {
if (typeof attributes.ipType == 'undefined') {
ngModel.$validators.ipv6Address = ipv6AddressValidator;
} else if (attributes.ipType == 'subnet') {
ngModel.$validators.ipv6Subnet = ipv6SubnetValidator;
} else {
if (console) {
console.error('ip type >' + attributes.ipType +
'< is not supported');
}
}
} else {
if (console) {
console.error('ip version >' + attributes.ipVersion +
'< is not supported');
}
}
}
};
});
Expand Down
2 changes: 1 addition & 1 deletion src/nls/locale-en.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,11 @@
},
"v6Prefix": {
"label": "IPv6 prefix",
"error.subnet": "This is not a valid IPv6 prefix.",
"placeholder": "looks like 2001:bf7:c4ff:3300::/56"
},
"v4": {
"placeholder": "looks like 10.31.133.{{40+index}}",
"error.required": "This field is required.",
"error.ipv4Address": "Not a valid IPv4 address.",
"wifi": {
"label": "Wifi ({{name}}) Mesh-IPv4"
Expand Down

0 comments on commit 8e9a8bd

Please sign in to comment.