jcrvalidator 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/jcr +26 -0
- data/lib/jcr/check_groups.rb +195 -0
- data/lib/jcr/evaluate_array_rules.rb +270 -0
- data/lib/jcr/evaluate_group_rules.rb +46 -0
- data/lib/jcr/evaluate_member_rules.rb +61 -0
- data/lib/jcr/evaluate_object_rules.rb +115 -0
- data/lib/jcr/evaluate_rules.rb +211 -0
- data/lib/jcr/evaluate_value_rules.rb +279 -0
- data/lib/jcr/find_roots.rb +106 -0
- data/lib/jcr/jcr.rb +228 -0
- data/lib/jcr/map_rule_names.rb +82 -0
- data/lib/jcr/parser.rb +398 -0
- data/lib/jcr/process_directives.rb +83 -0
- data/lib/jcr.rb +1 -0
- metadata +60 -0
@@ -0,0 +1,115 @@
|
|
1
|
+
# Copyright (c) 2015 American Registry for Internet Numbers
|
2
|
+
#
|
3
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
4
|
+
# purpose with or without fee is hereby granted, provided that the above
|
5
|
+
# copyright notice and this permission notice appear in all copies.
|
6
|
+
#
|
7
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
8
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
9
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
10
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
11
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
12
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
13
|
+
# IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
14
|
+
|
15
|
+
require 'jcr/parser'
|
16
|
+
require 'jcr/map_rule_names'
|
17
|
+
require 'jcr/check_groups'
|
18
|
+
require 'jcr/evaluate_rules'
|
19
|
+
|
20
|
+
module JCR
|
21
|
+
|
22
|
+
class ObjectBehavior
|
23
|
+
attr_accessor :checked_hash
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@checked_hash = {}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.evaluate_object_rule jcr, rule_atom, data, econs, behavior = nil
|
31
|
+
|
32
|
+
rules, annotations = get_rules_and_annotations( jcr )
|
33
|
+
|
34
|
+
# if the data is not an object (Hash)
|
35
|
+
return evaluate_reject( annotations,
|
36
|
+
Evaluation.new( false, "#{data} is not an object at #{jcr} from #{rule_atom}") ) unless data.is_a? Hash
|
37
|
+
|
38
|
+
# if the object has no members and there are zero sub-rules (it is suppose to be empty)
|
39
|
+
return evaluate_reject( annotations,
|
40
|
+
Evaluation.new( true, nil ) ) if rules.empty? && data.length == 0
|
41
|
+
|
42
|
+
# if the object has members and there are zero sub-rules (it is suppose to be empty)
|
43
|
+
return evaluate_reject( annotations,
|
44
|
+
Evaluation.new( false, "Non-empty object at #{jcr} from #{rule_atom}" ) ) if rules.empty? && data.length != 0
|
45
|
+
|
46
|
+
retval = nil
|
47
|
+
behavior = ObjectBehavior.new unless behavior
|
48
|
+
|
49
|
+
rules.each do |rule|
|
50
|
+
|
51
|
+
# short circuit logic
|
52
|
+
if rule[:choice_combiner] && retval && retval.success
|
53
|
+
next
|
54
|
+
elsif rule[:sequence_combiner] && retval && !retval.success
|
55
|
+
return evaluate_reject( annotations, retval ) # short circuit
|
56
|
+
end
|
57
|
+
|
58
|
+
repeat_min, repeat_max = get_repetitions( rule )
|
59
|
+
|
60
|
+
# Pay attention here:
|
61
|
+
# Group rules need to be treated differently than other rules
|
62
|
+
# Groups must be evaluated as if they are rules evaluated in
|
63
|
+
# isolation until they evaluate as true.
|
64
|
+
# Also, groups must be handed the entire object, not key/values
|
65
|
+
# as member rules use.
|
66
|
+
|
67
|
+
if (grule = get_group(rule, econs))
|
68
|
+
|
69
|
+
successes = 0
|
70
|
+
for i in 0..repeat_max
|
71
|
+
group_behavior = ObjectBehavior.new
|
72
|
+
e = evaluate_rule( grule, rule_atom, data, econs, group_behavior )
|
73
|
+
if e.success
|
74
|
+
behavior.checked_hash.merge!( group_behavior.checked_hash )
|
75
|
+
successes = successes + 1
|
76
|
+
else
|
77
|
+
break;
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
if successes == 0 && repeat_min > 0
|
82
|
+
retval = Evaluation.new( false, "object does not contain group #{rule} for #{jcr} from #{rule_atom}")
|
83
|
+
elsif successes < repeat_min
|
84
|
+
retval = Evaluation.new( false, "object does not have contain necessary number of group #{rule} for #{jcr} from #{rule_atom}")
|
85
|
+
else
|
86
|
+
retval = Evaluation.new( true, nil )
|
87
|
+
end
|
88
|
+
|
89
|
+
else # if not grule
|
90
|
+
|
91
|
+
repeat_results = data.select do |k,v|
|
92
|
+
unless behavior.checked_hash[k]
|
93
|
+
e = evaluate_rule(rule, rule_atom, [k, v], econs, nil)
|
94
|
+
behavior.checked_hash[k] = e.success
|
95
|
+
e.success
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
if repeat_results.length == 0 && repeat_min > 0
|
100
|
+
retval = Evaluation.new( false, "object does not contain #{rule} for #{jcr} from #{rule_atom}")
|
101
|
+
elsif repeat_results.length < repeat_min
|
102
|
+
retval = Evaluation.new( false, "object does not have enough #{rule} for #{jcr} from #{rule_atom}")
|
103
|
+
elsif repeat_results.length > repeat_max
|
104
|
+
retval = Evaluation.new( false, "object has too many #{rule} for #{jcr} from #{rule_atom}")
|
105
|
+
else
|
106
|
+
retval = Evaluation.new( true, nil)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end # end if grule else
|
111
|
+
|
112
|
+
return evaluate_reject( annotations, retval )
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
@@ -0,0 +1,211 @@
|
|
1
|
+
# Copyright (c) 2015 American Registry for Internet Numbers
|
2
|
+
#
|
3
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
4
|
+
# purpose with or without fee is hereby granted, provided that the above
|
5
|
+
# copyright notice and this permission notice appear in all copies.
|
6
|
+
#
|
7
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
8
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
9
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
10
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
11
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
12
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
13
|
+
# IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
14
|
+
|
15
|
+
require 'ipaddr'
|
16
|
+
require 'time'
|
17
|
+
require 'addressable/uri'
|
18
|
+
require 'addressable/template'
|
19
|
+
require 'email_address_validator'
|
20
|
+
require 'big-phoney'
|
21
|
+
|
22
|
+
require 'jcr/parser'
|
23
|
+
require 'jcr/map_rule_names'
|
24
|
+
require 'jcr/check_groups'
|
25
|
+
require 'jcr/evaluate_array_rules'
|
26
|
+
require 'jcr/evaluate_object_rules'
|
27
|
+
require 'jcr/evaluate_group_rules'
|
28
|
+
require 'jcr/evaluate_member_rules'
|
29
|
+
require 'jcr/evaluate_value_rules'
|
30
|
+
|
31
|
+
|
32
|
+
# Adapted from Matt Sears
|
33
|
+
class Proc
|
34
|
+
def jcr_callback(callable, *args)
|
35
|
+
self === Class.new do
|
36
|
+
method_name = callable.to_sym
|
37
|
+
define_method(method_name) { |&block| block.nil? ? true : block.call(*args) }
|
38
|
+
define_method("#{method_name}?") { true }
|
39
|
+
def method_missing(method_name, *args, &block) false; end
|
40
|
+
end.new
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module JCR
|
45
|
+
|
46
|
+
class Evaluation
|
47
|
+
attr_accessor :success, :reason, :child_evaluation
|
48
|
+
def initialize success, reason
|
49
|
+
@success = success
|
50
|
+
@reason = reason
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class EvalConditions
|
55
|
+
attr_accessor :mapping, :callbacks
|
56
|
+
def initialize mapping, callbacks
|
57
|
+
@mapping = mapping
|
58
|
+
if callbacks
|
59
|
+
@callbacks = callbacks
|
60
|
+
else
|
61
|
+
@callbacks = {}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.evaluate_rule jcr, rule_atom, data, econs, behavior = nil
|
67
|
+
retval = Evaluation.new( false, "failed to evaluate rule properly" )
|
68
|
+
case
|
69
|
+
when behavior.is_a?( ArrayBehavior )
|
70
|
+
retval = evaluate_array_rule( jcr, rule_atom, data, econs, behavior)
|
71
|
+
when behavior.is_a?( ObjectBehavior )
|
72
|
+
retval = evaluate_object_rule( jcr, rule_atom, data, econs, behavior)
|
73
|
+
when jcr[:rule]
|
74
|
+
retval = evaluate_rule( jcr[:rule], rule_atom, data, econs, behavior)
|
75
|
+
when jcr[:target_rule_name]
|
76
|
+
target = econs.mapping[ jcr[:target_rule_name][:rule_name].to_s ]
|
77
|
+
raise "Target rule not in mapping. This should have been checked earlier." unless target
|
78
|
+
retval = evaluate_rule( target, target, data, econs, behavior )
|
79
|
+
when jcr[:primitive_rule]
|
80
|
+
retval = evaluate_value_rule( jcr[:primitive_rule], rule_atom, data, econs)
|
81
|
+
when jcr[:group_rule]
|
82
|
+
retval = evaluate_group_rule( jcr[:group_rule], rule_atom, data, econs, behavior)
|
83
|
+
when jcr[:array_rule]
|
84
|
+
retval = evaluate_array_rule( jcr[:array_rule], rule_atom, data, econs, behavior)
|
85
|
+
when jcr[:object_rule]
|
86
|
+
retval = evaluate_object_rule( jcr[:object_rule], rule_atom, data, econs, behavior)
|
87
|
+
when jcr[:member_rule]
|
88
|
+
retval = evaluate_member_rule( jcr[:member_rule], rule_atom, data, econs)
|
89
|
+
else
|
90
|
+
retval = Evaluation.new( true, nil )
|
91
|
+
end
|
92
|
+
if jcr.is_a?( Hash ) && jcr[:rule_name]
|
93
|
+
rn = jcr[:rule_name].to_s
|
94
|
+
if econs.callbacks[ rn ]
|
95
|
+
retval = evaluate_callback( jcr, data, econs, rn, retval )
|
96
|
+
end
|
97
|
+
end
|
98
|
+
return retval
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.evaluate_callback jcr, data, econs, callback, e
|
102
|
+
retval = e
|
103
|
+
c = econs.callbacks[ callback ]
|
104
|
+
if e.success
|
105
|
+
retval = c.jcr_callback :rule_eval_true, jcr, data
|
106
|
+
else
|
107
|
+
retval = c.jcr_callback :rule_eval_false, jcr, data, e
|
108
|
+
end
|
109
|
+
if retval.is_a? TrueClass
|
110
|
+
retval = Evaluation.new( true, nil )
|
111
|
+
elsif retval.is_a? FalseClass
|
112
|
+
retval = Evaluation.new( false, nil )
|
113
|
+
elsif retval.is_a? String
|
114
|
+
retval = Evaluation.new( false, retval )
|
115
|
+
end
|
116
|
+
return retval
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.get_repetitions rule
|
120
|
+
|
121
|
+
repeat_min = 1
|
122
|
+
repeat_max = 1
|
123
|
+
if rule[:optional]
|
124
|
+
repeat_min = 0
|
125
|
+
repeat_max = 1
|
126
|
+
elsif rule[:one_or_more]
|
127
|
+
repeat_min = 1
|
128
|
+
repeat_max = Float::INFINITY
|
129
|
+
elsif rule[:specific_repetition] && rule[:specific_repetition].is_a?( Parslet::Slice )
|
130
|
+
repeat_min = repeat_max = rule[:specific_repetition].to_s.to_i
|
131
|
+
else
|
132
|
+
o = rule[:repetition_interval]
|
133
|
+
if o
|
134
|
+
repeat_min = 0
|
135
|
+
repeat_max = Float::INFINITY
|
136
|
+
end
|
137
|
+
o = rule[:repetition_min]
|
138
|
+
if o
|
139
|
+
if o.is_a?( Parslet::Slice )
|
140
|
+
repeat_min = o.to_s.to_i
|
141
|
+
end
|
142
|
+
end
|
143
|
+
o = rule[:repetition_max]
|
144
|
+
if o
|
145
|
+
if o.is_a?( Parslet::Slice )
|
146
|
+
repeat_max = o.to_s.to_i
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
return repeat_min, repeat_max
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.get_rules_and_annotations jcr
|
155
|
+
rules = []
|
156
|
+
annotations = []
|
157
|
+
|
158
|
+
if jcr.is_a?( Hash )
|
159
|
+
jcr = [ jcr ]
|
160
|
+
end
|
161
|
+
|
162
|
+
if jcr.is_a? Array
|
163
|
+
i = 0
|
164
|
+
jcr.each do |sub|
|
165
|
+
case
|
166
|
+
when sub[:unordered_annotation]
|
167
|
+
annotations << sub
|
168
|
+
i = i + 1
|
169
|
+
when sub[:reject_annotation]
|
170
|
+
annotations << sub
|
171
|
+
i = i + 1
|
172
|
+
when sub[:root_annotation]
|
173
|
+
annotations << sub
|
174
|
+
i = i + 1
|
175
|
+
when sub[:primitive_rule],sub[:object_rule],sub[:group_rule],sub[:array_rule],sub[:target_rule_name]
|
176
|
+
break
|
177
|
+
end
|
178
|
+
end
|
179
|
+
rules = jcr[i,jcr.length]
|
180
|
+
end
|
181
|
+
|
182
|
+
return rules, annotations
|
183
|
+
end
|
184
|
+
|
185
|
+
def self.evaluate_reject annotations, evaluation
|
186
|
+
reject = false
|
187
|
+
annotations.each do |a|
|
188
|
+
if a[:reject_annotation]
|
189
|
+
reject = true
|
190
|
+
break
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
if reject
|
195
|
+
evaluation.success = !evaluation.success
|
196
|
+
end
|
197
|
+
return evaluation
|
198
|
+
end
|
199
|
+
|
200
|
+
def self.get_group rule, econs
|
201
|
+
return rule[:group_rule] if rule[:group_rule]
|
202
|
+
#else
|
203
|
+
if rule[:target_rule_name]
|
204
|
+
target = econs.mapping[ rule[:target_rule_name][:rule_name].to_s ]
|
205
|
+
raise "Target rule not in mapping. This should have been checked earlier." unless target
|
206
|
+
return get_group( target, econs )
|
207
|
+
end
|
208
|
+
#else
|
209
|
+
return false
|
210
|
+
end
|
211
|
+
end
|
@@ -0,0 +1,279 @@
|
|
1
|
+
# Copyright (c) 2015 American Registry for Internet Numbers
|
2
|
+
#
|
3
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
4
|
+
# purpose with or without fee is hereby granted, provided that the above
|
5
|
+
# copyright notice and this permission notice appear in all copies.
|
6
|
+
#
|
7
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
8
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
9
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
10
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
11
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
12
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
13
|
+
# IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
14
|
+
|
15
|
+
require 'ipaddr'
|
16
|
+
require 'time'
|
17
|
+
require 'addressable/uri'
|
18
|
+
require 'addressable/template'
|
19
|
+
require 'email_address_validator'
|
20
|
+
require 'big-phoney'
|
21
|
+
|
22
|
+
require 'jcr/parser'
|
23
|
+
require 'jcr/map_rule_names'
|
24
|
+
require 'jcr/check_groups'
|
25
|
+
|
26
|
+
module JCR
|
27
|
+
|
28
|
+
def self.evaluate_value_rule jcr, rule_atom, data, mapping
|
29
|
+
rules, annotations = get_rules_and_annotations( jcr )
|
30
|
+
|
31
|
+
return evaluate_reject( annotations, evaluate_values( rules[0], rule_atom, data, mapping ) )
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.evaluate_values jcr, rule_atom, data, mapping
|
35
|
+
case
|
36
|
+
|
37
|
+
#
|
38
|
+
# any
|
39
|
+
#
|
40
|
+
|
41
|
+
when jcr[:any]
|
42
|
+
return Evaluation.new( true, nil )
|
43
|
+
|
44
|
+
#
|
45
|
+
# integers
|
46
|
+
#
|
47
|
+
|
48
|
+
when jcr[:integer_v]
|
49
|
+
si = jcr[:integer_v].to_s
|
50
|
+
if si == "integer"
|
51
|
+
return bad_value( jcr, rule_atom, "integer", data ) unless data.is_a?( Fixnum )
|
52
|
+
end
|
53
|
+
when jcr[:integer]
|
54
|
+
i = jcr[:integer].to_s.to_i
|
55
|
+
return bad_value( jcr, rule_atom, i, data ) unless data == i
|
56
|
+
when jcr[:integer_min],jcr[:integer_max]
|
57
|
+
return bad_value( jcr, rule_atom, "integer", data ) unless data.is_a?( Fixnum )
|
58
|
+
min = jcr[:integer_min].to_s.to_i
|
59
|
+
return bad_value( jcr, rule_atom, min, data ) unless data >= min
|
60
|
+
max = jcr[:integer_max].to_s.to_i
|
61
|
+
return bad_value( jcr, rule_atom, max, data ) unless data <= max
|
62
|
+
|
63
|
+
#
|
64
|
+
# floats
|
65
|
+
#
|
66
|
+
|
67
|
+
when jcr[:float_v]
|
68
|
+
sf = jcr[:float_v].to_s
|
69
|
+
if sf == "float"
|
70
|
+
return bad_value( jcr, rule_atom, "float", data ) unless data.is_a?( Float )
|
71
|
+
end
|
72
|
+
when jcr[:float]
|
73
|
+
f = jcr[:float].to_s.to_f
|
74
|
+
return bad_value( jcr, rule_atom, f, data ) unless data == f
|
75
|
+
when jcr[:float_min],jcr[:float_max]
|
76
|
+
return bad_value( jcr, rule_atom, "float", data ) unless data.is_a?( Float )
|
77
|
+
min = jcr[:float_min].to_s.to_f
|
78
|
+
return bad_value( jcr, rule_atom, min, data ) unless data >= min
|
79
|
+
max = jcr[:float_max].to_s.to_f
|
80
|
+
return bad_value( jcr, rule_atom, max, data ) unless data <= max
|
81
|
+
|
82
|
+
#
|
83
|
+
# boolean
|
84
|
+
#
|
85
|
+
|
86
|
+
when jcr[:true_v]
|
87
|
+
return bad_value( jcr, rule_atom, "true", data ) unless data
|
88
|
+
when jcr[:false_v]
|
89
|
+
return bad_value( jcr, rule_atom, "false", data ) if data
|
90
|
+
when jcr[:boolean_v]
|
91
|
+
return bad_value( jcr, rule_atom, "boolean", data ) unless ( data.is_a?( TrueClass ) || data.is_a?( FalseClass ) )
|
92
|
+
|
93
|
+
#
|
94
|
+
# strings
|
95
|
+
#
|
96
|
+
|
97
|
+
when jcr[:string]
|
98
|
+
return bad_value( jcr, rule_atom, "string", data ) unless data.is_a? String
|
99
|
+
when jcr[:q_string]
|
100
|
+
s = jcr[:q_string].to_s
|
101
|
+
return bad_value( jcr, rule_atom, s, data ) unless data == s
|
102
|
+
|
103
|
+
#
|
104
|
+
# regex
|
105
|
+
#
|
106
|
+
|
107
|
+
when jcr[:regex]
|
108
|
+
regex = Regexp.new( jcr[:regex].to_s )
|
109
|
+
return bad_value( jcr, rule_atom, regex, data ) unless data.is_a? String
|
110
|
+
return bad_value( jcr, rule_atom, regex, data ) unless data =~ regex
|
111
|
+
|
112
|
+
#
|
113
|
+
# ip addresses
|
114
|
+
#
|
115
|
+
|
116
|
+
when jcr[:ip4]
|
117
|
+
return bad_value( jcr, rule_atom, "IPv4 Address", data ) unless data.is_a? String
|
118
|
+
begin
|
119
|
+
ip = IPAddr.new( data )
|
120
|
+
rescue IPAddr::InvalidAddressError
|
121
|
+
return bad_value( jcr, rule_atom, "IPv4 Address", data )
|
122
|
+
end
|
123
|
+
return bad_value( jcr, rule_atom, "IPv4 Address", data ) unless ip.ipv4?
|
124
|
+
when jcr[:ip6]
|
125
|
+
return bad_value( jcr, rule_atom, "IPv6 Address", data ) unless data.is_a? String
|
126
|
+
begin
|
127
|
+
ip = IPAddr.new( data )
|
128
|
+
rescue IPAddr::InvalidAddressError
|
129
|
+
return bad_value( jcr, rule_atom, "IPv6 Address", data )
|
130
|
+
end
|
131
|
+
return bad_value( jcr, rule_atom, "IPv6 Address", data ) unless ip.ipv6?
|
132
|
+
|
133
|
+
#
|
134
|
+
# domain names
|
135
|
+
#
|
136
|
+
|
137
|
+
when jcr[:fqdn]
|
138
|
+
return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) unless data.is_a? String
|
139
|
+
return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) if data.empty?
|
140
|
+
a = data.split( '.' )
|
141
|
+
a.each do |label|
|
142
|
+
return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) if label.start_with?( '-' )
|
143
|
+
return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data ) if label.end_with?( '-' )
|
144
|
+
label.each_char do |char|
|
145
|
+
unless (char >= 'a' && char <= 'z') \
|
146
|
+
|| (char >= 'A' && char <= 'Z') \
|
147
|
+
|| (char >= '0' && char <='9') \
|
148
|
+
|| char == '-'
|
149
|
+
return bad_value( jcr, rule_atom, "Fully Qualified Domain Name", data )
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
when jcr[:idn]
|
154
|
+
return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) unless data.is_a? String
|
155
|
+
return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) if data.empty?
|
156
|
+
a = data.split( '.' )
|
157
|
+
a.each do |label|
|
158
|
+
return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) if label.start_with?( '-' )
|
159
|
+
return bad_value( jcr, rule_atom, "Internationalized Domain Name", data ) if label.end_with?( '-' )
|
160
|
+
label.each_char do |char|
|
161
|
+
unless (char >= 'a' && char <= 'z') \
|
162
|
+
|| (char >= 'A' && char <= 'Z') \
|
163
|
+
|| (char >= '0' && char <='9') \
|
164
|
+
|| char == '-' \
|
165
|
+
|| char.ord > 127
|
166
|
+
return bad_value( jcr, rule_atom, "Internationalized Domain Name", data )
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
#
|
172
|
+
# uri and uri templates
|
173
|
+
#
|
174
|
+
|
175
|
+
when jcr[:uri]
|
176
|
+
return bad_value( jcr, rule_atom, "URI", data ) unless data.is_a?( String )
|
177
|
+
uri = Addressable::URI.parse( data )
|
178
|
+
return bad_value( jcr, rule_atom, "URI", data ) unless uri.is_a?( Addressable::URI )
|
179
|
+
when jcr[:uri_template]
|
180
|
+
t = jcr[:uri_template].to_s
|
181
|
+
return bad_value( jcr, rule_atom, t, data ) unless data.is_a? String
|
182
|
+
template = Addressable::Template.new( t )
|
183
|
+
e = template.extract( data )
|
184
|
+
if e == nil
|
185
|
+
return bad_value( jcr, rule_atom, t, data )
|
186
|
+
else
|
187
|
+
e.each do |k,v|
|
188
|
+
return bad_value( jcr, rule_atom, t, data ) unless v
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
#
|
193
|
+
# phone and email value rules
|
194
|
+
#
|
195
|
+
|
196
|
+
when jcr[:email]
|
197
|
+
return bad_value( jcr, rule_atom, "Email Address", data ) unless data.is_a? String
|
198
|
+
return bad_value( jcr, rule_atom, "Email Address", data ) unless EmailAddressValidator.validate( data, true )
|
199
|
+
|
200
|
+
when jcr[:phone]
|
201
|
+
return bad_value( jcr, rule_atom, "Phone Number", data ) unless data.is_a? String
|
202
|
+
p = BigPhoney::PhoneNumber.new( data )
|
203
|
+
return bad_value( jcr, rule_atom, "Phone Number", data ) unless p.valid?
|
204
|
+
|
205
|
+
#
|
206
|
+
# base64 values
|
207
|
+
#
|
208
|
+
|
209
|
+
when jcr[:base64]
|
210
|
+
return bad_value( jcr, rule_atom, "Base 64 Data", data ) unless data.is_a? String
|
211
|
+
return bad_value( jcr, rule_atom, "Base 64 Data", data ) if data.empty?
|
212
|
+
pad_start = false
|
213
|
+
data.each_char do |char|
|
214
|
+
if pad_start && char != '='
|
215
|
+
return bad_value( jcr, rule_atom, "Base 64 Data", data )
|
216
|
+
elsif char == '='
|
217
|
+
pad_start = true
|
218
|
+
end
|
219
|
+
unless (char >= 'a' && char <= 'z') \
|
220
|
+
|| (char >= 'A' && char <= 'Z') \
|
221
|
+
|| (char >= '0' && char <='9') \
|
222
|
+
|| char == '=' || char == '+' || char == '/'
|
223
|
+
return bad_value( jcr, rule_atom, "Base 64 Data", data )
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
#
|
228
|
+
# time and date values
|
229
|
+
#
|
230
|
+
|
231
|
+
when jcr[:date_time]
|
232
|
+
return bad_value( jcr, rule_atom, "Time and Date", data ) unless data.is_a? String
|
233
|
+
begin
|
234
|
+
Time.iso8601( data )
|
235
|
+
rescue ArgumentError
|
236
|
+
return bad_value( jcr, rule_atom, "Time and Date", data )
|
237
|
+
end
|
238
|
+
when jcr[:full_date]
|
239
|
+
return bad_value( jcr, rule_atom, "Date", data ) unless data.is_a? String
|
240
|
+
begin
|
241
|
+
d = data + "T23:20:50.52Z"
|
242
|
+
Time.iso8601( d )
|
243
|
+
rescue ArgumentError
|
244
|
+
return bad_value( jcr, rule_atom, "Date", data )
|
245
|
+
end
|
246
|
+
when jcr[:full_time]
|
247
|
+
return bad_value( jcr, rule_atom, "Time", data ) unless data.is_a? String
|
248
|
+
begin
|
249
|
+
t = "1985-04-12T" + data + "Z"
|
250
|
+
Time.iso8601( t )
|
251
|
+
rescue ArgumentError
|
252
|
+
return bad_value( jcr, rule_atom, "Time", data )
|
253
|
+
end
|
254
|
+
|
255
|
+
#
|
256
|
+
# null
|
257
|
+
#
|
258
|
+
|
259
|
+
when jcr[:null]
|
260
|
+
return bad_value( jcr, rule_atom, nil, data ) unless data == nil
|
261
|
+
|
262
|
+
#
|
263
|
+
# groups
|
264
|
+
#
|
265
|
+
|
266
|
+
when jcr[:group_rule]
|
267
|
+
return evaluate_group_rule jcr[:group_rule], rule_atom, data, mapping
|
268
|
+
|
269
|
+
else
|
270
|
+
raise "unknown value rule evaluation. this shouldn't happen"
|
271
|
+
end
|
272
|
+
return Evaluation.new( true, nil )
|
273
|
+
end
|
274
|
+
|
275
|
+
def self.bad_value jcr, rule_atom, expected, actual
|
276
|
+
Evaluation.new( false, "expected #{expected} but got #{actual} at #{jcr} from #{rule_atom}" )
|
277
|
+
end
|
278
|
+
|
279
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# Copyright (c) 2015 American Registry for Internet Numbers
|
2
|
+
#
|
3
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
4
|
+
# purpose with or without fee is hereby granted, provided that the above
|
5
|
+
# copyright notice and this permission notice appear in all copies.
|
6
|
+
#
|
7
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
8
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
9
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
10
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
11
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
12
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
13
|
+
# IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
14
|
+
|
15
|
+
require 'jcr/parser'
|
16
|
+
require 'jcr/evaluate_rules'
|
17
|
+
|
18
|
+
module JCR
|
19
|
+
|
20
|
+
class Root
|
21
|
+
attr_accessor :nameless, :name, :rule, :default
|
22
|
+
|
23
|
+
def initialize rule, name = nil, nameless = true, default = false
|
24
|
+
@rule = rule
|
25
|
+
@name = name
|
26
|
+
@nameless = nameless
|
27
|
+
if name
|
28
|
+
@nameless = false
|
29
|
+
end
|
30
|
+
@default = default
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.find_roots( tree )
|
35
|
+
roots = Array.new
|
36
|
+
if tree.is_a? Hash
|
37
|
+
tree = [ tree ]
|
38
|
+
end
|
39
|
+
tree.each do |node|
|
40
|
+
if node[:rule]
|
41
|
+
roots.concat( find_roots_in_named( node ) )
|
42
|
+
elsif (top_rule = get_rule_by_type( node ))
|
43
|
+
roots << Root.new( node, nil, true, true )
|
44
|
+
roots.concat( find_roots_in_unnamed( top_rule ) )
|
45
|
+
end
|
46
|
+
end
|
47
|
+
return roots
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.find_roots_in_named( node )
|
51
|
+
roots = Array.new
|
52
|
+
rn = node[:rule][:rule_name].to_str
|
53
|
+
rule = node[:rule]
|
54
|
+
ruledef = get_rule_by_type( rule )
|
55
|
+
if ruledef
|
56
|
+
if ruledef.is_a? Array
|
57
|
+
ruledef.each do |rdi|
|
58
|
+
if rdi[:root_annotation]
|
59
|
+
roots << Root.new( node, rn )
|
60
|
+
elsif (subrule = get_rule_by_type( rdi ))
|
61
|
+
roots.concat( find_roots_in_unnamed( subrule ) )
|
62
|
+
end
|
63
|
+
end
|
64
|
+
elsif ruledef.is_a? Hash
|
65
|
+
subrule = get_rule_by_type( ruledef )
|
66
|
+
roots.concat( find_roots_in_unnamed( subrule ) ) if subrule
|
67
|
+
end
|
68
|
+
end
|
69
|
+
return roots
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.find_roots_in_unnamed( node )
|
73
|
+
roots = Array.new
|
74
|
+
if node.is_a? Array
|
75
|
+
node.each do |n|
|
76
|
+
if n[:root_annotation]
|
77
|
+
roots << Root.new( node )
|
78
|
+
elsif (subrule = get_rule_by_type( n ) )
|
79
|
+
roots.concat( find_roots_in_unnamed( subrule ) ) if subrule
|
80
|
+
end
|
81
|
+
end
|
82
|
+
else
|
83
|
+
subrule = get_rule_by_type( node )
|
84
|
+
roots.concat( find_roots_in_unnamed( subrule ) ) if subrule
|
85
|
+
end
|
86
|
+
return roots
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.get_rule_by_type rule
|
90
|
+
retval = nil
|
91
|
+
case
|
92
|
+
when rule[:array_rule]
|
93
|
+
retval = rule[:array_rule]
|
94
|
+
when rule[:object_rule]
|
95
|
+
retval = rule[:object_rule]
|
96
|
+
when rule[:member_rule]
|
97
|
+
retval = rule[:member_rule]
|
98
|
+
when rule[:primitive_rule]
|
99
|
+
retval = rule[:primitive_rule]
|
100
|
+
when rule[:group_rule]
|
101
|
+
retval = rule[:group_rule]
|
102
|
+
end
|
103
|
+
return retval
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|