File Coverage

blib/lib/Config/Apple/Profile/Payload/Types/Validation.pm
Criterion Covered Total %
statement 77 116 66.3
branch 35 68 51.4
condition 4 9 44.4
subroutine 19 24 79.1
pod 10 10 100.0
total 145 227 63.8


line stmt bran cond sub pod time code
1             package Config::Apple::Profile::Payload::Types::Validation;
2              
3 7     7   99 use 5.14.4;
  7         25  
  7         326  
4 7     7   40 use strict;
  7         14  
  7         246  
5 7     7   45 use warnings FATAL => 'all';
  7         12  
  7         442  
6              
7             our $VERSION = '0.55';
8              
9 7     7   12150 use DateTime;
  7         1282780  
  7         308  
10 7     7   8027 use DateTime::Format::Flexible;
  7         642952  
  7         112  
11 7     7   913 use Encode qw(encode);
  7         17  
  7         1276  
12             use Exporter::Easy (
13 7         103 OK => [qw(
14             validate
15             validate_string validate_number validate_real validate_date
16             validate_boolean validate_data validate_identifier validate_uuid
17             validate_class
18             )],
19             TAGS => [
20             'all' => [qw(
21             validate
22             validate_string validate_number validate_real validate_date
23             validate_boolean validate_data validate_identifier validate_uuid
24             validate_class
25             )],
26             'types' => [qw(
27             validate_string validate_number validate_real validate_date
28             validate_boolean validate_data validate_identifier validate_uuid
29             validate_class
30             )],
31             ],
32 7     7   48 );
  7         15  
33 7     7   22629 use Regexp::Common;
  7         19667  
  7         39  
34 7     7   566420 use Scalar::Util qw(openhandle blessed);
  7         23  
  7         544  
35 7     7   48 use Try::Tiny;
  7         17  
  7         498  
36 7     7   47 use Config::Apple::Profile::Payload::Types qw(:all);
  7         17  
  7         10782  
37              
38              
39             =encoding utf8
40              
41             =head1 NAME
42              
43             Config::Apple::Profile::Payload::Types::Validation - Validation of common
44             payload types.
45              
46             =head1 DESCRIPTION
47              
48             Apple Configuration Profiles contain one or more I. Each payload
49             contains a dictionary, which can be thought of like a Perl hash. Within a
50             payload's dictionary, each key's value is restricted to a specific type.
51             One key might require a number; a different key might require a string, or some
52             binary data.
53              
54             Provided in this module are a number of Readonly scalars that will be used
55             (instead of strings) to identify the data types for configuration profile keys.
56             The scalars are all OK for import into your local namespace, or you can simply
57             import C<:all> to get all of them at once.
58              
59              
60             =head1 FUNCTIONS
61              
62             =head2 validate
63              
64             my $validated_value = validate($type, $value);
65              
66             Validates C<$value> as a valid C<$type>. If valid, returns the de-tainted
67             C<$value>. If invalid, returns C.
68              
69             C<$type> is one of the values from L.
70             C<$value> is the value to be validated.
71              
72             IF C<$type> is not valid, or C<$value> is C, then C is returned.
73              
74             =cut
75              
76             sub validate {
77 235     235 1 468 my ($type, $value) = @_;
78            
79             # We recognize String types
80 235 100 33     682 if ($type == $ProfileString) {
    100          
    100          
    50          
    50          
    50          
    100          
    50          
    0          
81 16         103 return validate_string($value);
82             }
83            
84             # We recognize Number types
85             elsif ($type == $ProfileNumber) {
86 21         203 return validate_number($value);
87             }
88            
89             # We recognize Real (floating-point number) types
90             elsif ($type == $ProfileReal) {
91 25         403 return validate_real($value);
92             }
93            
94             # We recognize Boolean types
95             elsif ($type == $ProfileBool) {
96 0         0 return validate_boolean($value);
97             }
98            
99             # We recognize Data types
100             elsif ( ($type == $ProfileData)
101             || ($type == $ProfileNSDataBlob)
102             ) {
103 0         0 return validate_data($value);
104             }
105            
106             # We recognize Date types
107             elsif ($type == $ProfileDate) {
108 0         0 return validate_date ($value);
109             }
110            
111             # We recognize Identifier types
112             elsif ($type == $ProfileIdentifier) {
113 16         568 return validate_identifier($value);
114             }
115            
116             # We recognize UUID types
117             elsif ($type == $ProfileUUID) {
118 157         7893 return validate_uuid($value);
119             }
120            
121             # We recognize classes
122             elsif ($type == $ProfileClass) {
123 0         0 return validate_class($value);
124             }
125             }
126              
127              
128             =head2 validate_string
129              
130             my $valid_string = validate_string($value);
131              
132             Returns a de-tained C<$value> if it is a defined, non-empty scalar, and can be
133             encoded as UTF-8 by L.
134              
135             =cut
136              
137             sub validate_string {
138 28     28 1 46 my ($value) = @_;
139            
140             # References aren't allowed here
141             ## no critic (ProhibitExplicitReturnUndef)
142 28 100       82 return undef if ref($value);
143             ##use critic
144            
145             # Empty strings aren't allowed, either.
146 22 100       123 if ($value =~ m/^(.+)$/s) {
147 19         51 $value = $1;
148            
149             }
150             else {
151             ## no critic (ProhibitExplicitReturnUndef)
152 2         7 return undef;
153             ##use critic
154             }
155            
156             # Try to encode as UTF-8, to make sure it's safe
157             try {
158 19     19   746 encode('UTF-8', $value, Encode::FB_CROAK | Encode::LEAVE_SRC);
159             }
160             catch {
161 2     2   120 $value = undef;
162 19         161 };
163            
164             # If we're here, then we are valid!
165 19         1158 return $value;
166             }
167              
168              
169             =head2 validate_number
170              
171             my $number = validate_number($value)
172              
173             Returns a de-tained C<$value> if it is an integer. A leading + is OK.
174             Any other input returns C.
175              
176             =cut
177              
178             sub validate_number {
179 81     81 1 120 my ($value) = @_;
180            
181             # References aren't allowed here
182             ## no critic (ProhibitExplicitReturnUndef)
183 81 100       196 return undef if ref($value);
184             ##use critic
185            
186             # Numbers must be integers, positive or negative (or zero).
187 75 100       488 if ($value =~ /^$RE{num}{int}{-keep}$/) {
188 62         11691 return $1;
189             }
190            
191             # If we're here, the matching failed, so return undef
192             ## no critic (ProhibitExplicitReturnUndef)
193 12         2631 return undef;
194             ##use critic
195             }
196              
197              
198             =head2 validate_real
199              
200             my $number = validate_real($value)
201              
202             Returns a de-tainted C<$value> if it is an integer or a floating-point number.
203             A loading C<+> is OK. An exponent, positive or negative, is also OK.
204             Any other input returns C.
205              
206             =cut
207              
208             sub validate_real {
209 49     49 1 95 my ($value) = @_;
210            
211             # References aren't allowed here
212             ## no critic (ProhibitExplicitReturnUndef)
213 49 100       131 return undef if ref($value);
214             ##use critic
215            
216             # Numbers must be floating-point, positive or negative (or zero).
217 43 100       444 if ($value =~ /^$RE{num}{real}{-keep}$/i) {
218 36         10860 return $1;
219             }
220            
221             # If we're here, the matching failed, so return undef
222             ## no critic (ProhibitExplicitReturnUndef)
223 6         1548 return undef;
224             ##use critic
225             }
226              
227              
228             =head2 validate_boolean
229              
230             my $boolean = validate_boolean($value);
231              
232             If C<$value> can be evaluated as true or false, returns a C<1> or a C<0>,
233             respectively. Will return C if a reference is passed.
234              
235             =cut
236              
237             sub validate_boolean {
238 0     0 1 0 my ($value) = @_;
239            
240             # References aren't allowed here
241             ## no critic (ProhibitExplicitReturnUndef)
242 0 0       0 return undef if ref($value);
243             ##use critic
244            
245             # A simple evaluation!
246 0 0       0 if ($value) {
247 0         0 return 1;
248             }
249 0 0       0 if (!$value) {
250 0         0 return 0;
251             }
252             }
253              
254              
255             =head2 validate_date
256              
257             my $date_object = validate_date($value);
258              
259             If C<$value> is already a finite C object, it is returned immediately.
260             If C<$value> is a string, and can be parsed by L,
261             the resulting C object will be returned.
262              
263             Unparseable strings, infinite C objects, and any other input will
264             return C.
265              
266             =cut
267              
268             sub validate_date {
269 0     0 1 0 my ($value) = @_;
270            
271             # If we have a blessed ref, which is a DateTime object, and it represents
272             # a finite time, then we're good!
273 0 0       0 if (ref $value) {
274             ## no critic (ProhibitExplicitReturnUndef)
275 0 0       0 return undef unless blessed($value);
276 0 0       0 return undef unless $value->isa('DateTime');
277 0 0       0 return undef unless $value->is_finite;
278             ##use critic
279            
280 0         0 return $value
281             }
282            
283             # At this point, we have a scalar, so let's see if it can be parsed
284             try {
285 0     0   0 $value = DateTime::Format::Flexible->parse_datetime($value);
286 0         0 return $value;
287             }
288            
289             # I give up!
290             ## no critic (ProhibitExplicitReturnUndef)
291 0         0 return undef;
292             ##use critic
293             }
294              
295              
296             =head2 validate_data
297              
298             # With a file handle
299             my $handle = validate_data($handle);
300              
301             # With binary data
302             my $handle = validate_data($bytes);
303              
304             If passed an already-open file handle, or any object that represents a file
305             (such as an C object), will return what was passed.
306              
307             If passed a scalar, it will be checked to make sure it is not empty, and that
308             is not a utf8 string. The contents of the string will be placed into an
309             in-memory file, and an IO::File object will be returned.
310              
311             =cut
312              
313             sub validate_data {
314 0     0 1 0 my ($value) = @_;
315            
316             # How we act here depends if we have a string or a filehandle
317             # Let's first check if we were given an open filehandle
318 0 0       0 if (openhandle($value)) {
319             # We've got an open file, so we're good!
320 0         0 return $value;
321             }
322            
323             # If we don't have an open handle, then make sure it's not an object
324 0 0       0 unless (ref($value)) {
325             # We have a string. Let's make sure it's binary, and non-empty.
326 0 0 0     0 if ( (!utf8::is_utf8($value))
327             && (length($value) > 0)
328             ) {
329             # Pull the string into an in-memory file, and return that.
330 0         0 my ($memory, $file);
331 0         0 $file = IO::File::new(\$memory, 'w+');
332 0         0 binmode($file);
333 0         0 $file->print($value);
334 0         0 $file->seek(0, 0);
335 0         0 return $file;
336             }
337             }
338            
339             # If we're here, then we are dealing with something unknown to us.
340             ## no critic (ProhibitExplicitReturnUndef)
341 0 0       0 return undef if ref($value);
342             ##use critic
343             }
344              
345              
346             =head2 validate_identifier
347              
348             my $valid_identifier = validate_identifier($value);
349              
350             Returns a de-tained C<$value> if it is a single-line string that matches the
351             format of a domain name (without spaces). Otherwise, returns C.
352              
353             =cut
354              
355             sub validate_identifier {
356 28     28 1 40 my ($value) = @_;
357            
358             # References aren't allowed here
359             ## no critic (ProhibitExplicitReturnUndef)
360 28 100       76 return undef if ref($value);
361             ##use critic
362            
363             # Empty strings aren't allowed, either.
364 22 100       117 if ($value =~ m/^(.+)$/s) {
365 19         52 my $matched_string = $1;
366             # Identifiers are one-line strings
367 19 100 100     168 if ( ($matched_string !~ m/\n/s)
368             && ($matched_string =~ m/^$RE{net}{domain}{-nospace}$/)
369             ) {
370 14         3312 return $matched_string;
371             }
372             }
373            
374             # If we're here, the matching failed, so return undef
375             ## no critic (ProhibitExplicitReturnUndef)
376 7         886 return undef;
377             ##use critic
378             }
379              
380             =head2 validate_uuid
381              
382             my $guid = validate_uuid($value);
383              
384             If C<$value> is a C object, it is returned immediately.
385             If C<$value> is a C object, an equivalent C object is
386             returned. Objects of other types return C.
387              
388             If C<$value> is a string that can be parsed as a GUID, an equivalent
389             C object is returned. Otherwise, C is returned.
390              
391             =cut
392              
393             sub validate_uuid {
394 319     319 1 497 my ($value) = @_;
395            
396 319         592 my $class = ref($value);
397 319         400 my $uuid;
398            
399             # We accept Data::UUID objects
400 319 50       978 if ($class eq 'Data::UUID') {
401 0         0 $uuid = Data::GUID::from_data_uuid($value);
402 0         0 return $uuid;
403             }
404            
405             # We accept Data::GUID objects
406 319 100       752 if ($class eq 'Data::GUID') {
407 100         458 return $value;
408             }
409            
410             # We don't accept other kinds of objects
411 219 100       543 if ($class ne '') {
412             ## no critic (ProhibitExplicitReturnUndef)
413 3         11 return undef;
414             ## use critic
415             }
416            
417             # Have Data::GUID try to parse the input
418             # If from_any_string doesn't die, then it wored OK
419 216         318 eval {
420 216         838 $uuid = Data::GUID->from_any_string($value);
421             };
422              
423             # If parsing went OK, then we have our object! Otherwise, we have undef.
424 216         310750 return $uuid;
425             }
426              
427              
428             =head2 validate_class
429              
430             my $object = validate_class($value)
431              
432             If C<$value> is an object, and is also an instance of
433             C (or something that is a subclass),
434             then C<$value> is returned. Otherwise, C is returned.
435              
436             =cut
437              
438             sub validate_class {
439 0     0 1   my ($object) = @_;
440            
441             # We don't accept non-references
442 0 0         if (!blessed($object)) {
443             ## no critic (ProhibitExplicitReturnUndef)
444 0           return undef;
445             ## use critic
446             }
447            
448 0 0         if ($object->isa('Config::Apple::Profile::Payload::Common')) {
449 0           return $object;
450             }
451            
452             # If we're here, then we have an object of the wrong class
453             ## no critic (ProhibitExplicitReturnUndef)
454 0           return undef;
455             ## use critic
456             }
457              
458             =head1 ACKNOWLEDGEMENTS
459              
460             Refer to L for acknowledgements.
461              
462             =head1 AUTHOR
463              
464             A. Karl Kornel, C<< >>
465              
466             =head1 COPYRIGHT AND LICENSE
467              
468             Copyright © 2014 A. Karl Kornel.
469              
470             This program is free software; you can redistribute it and/or modify it
471             under the terms of either: the GNU General Public License as published
472             by the Free Software Foundation; or the Artistic License.
473              
474             See L for more information.
475              
476             =cut
477              
478             1;