Enhance/fix k8s install for Ubuntu
[iec.git] / src / foundation / api / revel / validators.go
1 // Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
2 // Revel Framework source code and usage is governed by a MIT style
3 // license that can be found in the LICENSE file.
4
5 package revel
6
7 import (
8         "errors"
9         "fmt"
10         "html"
11         "net"
12         "net/url"
13         "reflect"
14         "regexp"
15         "strconv"
16         "strings"
17         "unicode/utf8"
18 )
19
20 type Validator interface {
21         IsSatisfied(interface{}) bool
22         DefaultMessage() string
23 }
24
25 type Required struct{}
26
27 func ValidRequired() Required {
28         return Required{}
29 }
30
31 func (r Required) IsSatisfied(obj interface{}) bool {
32         if obj == nil {
33                 return false
34         }
35         switch v := reflect.ValueOf(obj); v.Kind() {
36         case reflect.Array, reflect.Slice, reflect.Map, reflect.String, reflect.Chan:
37                 if v.Len() == 0 {
38                         return false
39                 }
40         case reflect.Ptr:
41                 return r.IsSatisfied(reflect.Indirect(v).Interface())
42         }
43         return !reflect.DeepEqual(obj, reflect.Zero(reflect.TypeOf(obj)).Interface())
44 }
45
46 func (r Required) DefaultMessage() string {
47         return fmt.Sprintln("Required")
48 }
49
50 type Min struct {
51         Min float64
52 }
53
54 func ValidMin(min int) Min {
55         return ValidMinFloat(float64(min))
56 }
57
58 func ValidMinFloat(min float64) Min {
59         return Min{min}
60 }
61
62 func (m Min) IsSatisfied(obj interface{}) bool {
63         var (
64                 num float64
65                 ok  bool
66         )
67         switch reflect.TypeOf(obj).Kind() {
68         case reflect.Float64:
69                 num, ok = obj.(float64)
70         case reflect.Float32:
71                 ok = true
72                 num = float64(obj.(float32))
73         case reflect.Int:
74                 ok = true
75                 num = float64(obj.(int))
76         }
77
78         if ok {
79                 return num >= m.Min
80         }
81         return false
82 }
83
84 func (m Min) DefaultMessage() string {
85         return fmt.Sprintln("Minimum is", m.Min)
86 }
87
88 type Max struct {
89         Max float64
90 }
91
92 func ValidMax(max int) Max {
93         return ValidMaxFloat(float64(max))
94 }
95
96 func ValidMaxFloat(max float64) Max {
97         return Max{max}
98 }
99
100 func (m Max) IsSatisfied(obj interface{}) bool {
101         var (
102                 num float64
103                 ok  bool
104         )
105         switch reflect.TypeOf(obj).Kind() {
106         case reflect.Float64:
107                 num, ok = obj.(float64)
108         case reflect.Float32:
109                 ok = true
110                 num = float64(obj.(float32))
111         case reflect.Int:
112                 ok = true
113                 num = float64(obj.(int))
114         }
115
116         if ok {
117                 return num <= m.Max
118         }
119         return false
120 }
121
122 func (m Max) DefaultMessage() string {
123         return fmt.Sprintln("Maximum is", m.Max)
124 }
125
126 // Range requires an integer to be within Min, Max inclusive.
127 type Range struct {
128         Min
129         Max
130 }
131
132 func ValidRange(min, max int) Range {
133         return ValidRangeFloat(float64(min), float64(max))
134 }
135
136 func ValidRangeFloat(min, max float64) Range {
137         return Range{Min{min}, Max{max}}
138 }
139
140 func (r Range) IsSatisfied(obj interface{}) bool {
141         return r.Min.IsSatisfied(obj) && r.Max.IsSatisfied(obj)
142 }
143
144 func (r Range) DefaultMessage() string {
145         return fmt.Sprintln("Range is", r.Min.Min, "to", r.Max.Max)
146 }
147
148 // MinSize requires an array or string to be at least a given length.
149 type MinSize struct {
150         Min int
151 }
152
153 func ValidMinSize(min int) MinSize {
154         return MinSize{min}
155 }
156
157 func (m MinSize) IsSatisfied(obj interface{}) bool {
158         if str, ok := obj.(string); ok {
159                 return utf8.RuneCountInString(str) >= m.Min
160         }
161         v := reflect.ValueOf(obj)
162         if v.Kind() == reflect.Slice {
163                 return v.Len() >= m.Min
164         }
165         return false
166 }
167
168 func (m MinSize) DefaultMessage() string {
169         return fmt.Sprintln("Minimum size is", m.Min)
170 }
171
172 // MaxSize requires an array or string to be at most a given length.
173 type MaxSize struct {
174         Max int
175 }
176
177 func ValidMaxSize(max int) MaxSize {
178         return MaxSize{max}
179 }
180
181 func (m MaxSize) IsSatisfied(obj interface{}) bool {
182         if str, ok := obj.(string); ok {
183                 return utf8.RuneCountInString(str) <= m.Max
184         }
185         v := reflect.ValueOf(obj)
186         if v.Kind() == reflect.Slice {
187                 return v.Len() <= m.Max
188         }
189         return false
190 }
191
192 func (m MaxSize) DefaultMessage() string {
193         return fmt.Sprintln("Maximum size is", m.Max)
194 }
195
196 // Length requires an array or string to be exactly a given length.
197 type Length struct {
198         N int
199 }
200
201 func ValidLength(n int) Length {
202         return Length{n}
203 }
204
205 func (s Length) IsSatisfied(obj interface{}) bool {
206         if str, ok := obj.(string); ok {
207                 return utf8.RuneCountInString(str) == s.N
208         }
209         v := reflect.ValueOf(obj)
210         if v.Kind() == reflect.Slice {
211                 return v.Len() == s.N
212         }
213         return false
214 }
215
216 func (s Length) DefaultMessage() string {
217         return fmt.Sprintln("Required length is", s.N)
218 }
219
220 // Match requires a string to match a given regex.
221 type Match struct {
222         Regexp *regexp.Regexp
223 }
224
225 func ValidMatch(regex *regexp.Regexp) Match {
226         return Match{regex}
227 }
228
229 func (m Match) IsSatisfied(obj interface{}) bool {
230         str := obj.(string)
231         return m.Regexp.MatchString(str)
232 }
233
234 func (m Match) DefaultMessage() string {
235         return fmt.Sprintln("Must match", m.Regexp)
236 }
237
238 var emailPattern = regexp.MustCompile("^[\\w!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[a-zA-Z0-9](?:[\\w-]*[\\w])?$")
239
240 type Email struct {
241         Match
242 }
243
244 func ValidEmail() Email {
245         return Email{Match{emailPattern}}
246 }
247
248 func (e Email) DefaultMessage() string {
249         return fmt.Sprintln("Must be a valid email address")
250 }
251
252 const (
253         None               = 0
254         IPAny              = 1
255         IPv4               = 32 // IPv4 (32 chars)
256         IPv6               = 39 // IPv6(39 chars)
257         IPv4MappedIPv6     = 45 // IP4-mapped IPv6 (45 chars) , Ex) ::FFFF:129.144.52.38
258         IPv4CIDR           = IPv4 + 3
259         IPv6CIDR           = IPv6 + 3
260         IPv4MappedIPv6CIDR = IPv4MappedIPv6 + 3
261 )
262
263 // Requires a string(IP Address) to be within IP Pattern type inclusive.
264 type IPAddr struct {
265         Vaildtypes []int
266 }
267
268 // Requires an IP Address string to be exactly a given  validation type (IPv4, IPv6, IPv4MappedIPv6, IPv4CIDR, IPv6CIDR, IPv4MappedIPv6CIDR OR IPAny)
269 func ValidIPAddr(cktypes ...int) IPAddr {
270
271         for _, cktype := range cktypes {
272
273                 if cktype != IPAny && cktype != IPv4 && cktype != IPv6 && cktype != IPv4MappedIPv6 && cktype != IPv4CIDR && cktype != IPv6CIDR && cktype != IPv4MappedIPv6CIDR {
274                         return IPAddr{Vaildtypes: []int{None}}
275                 }
276         }
277
278         return IPAddr{Vaildtypes: cktypes}
279 }
280
281 func isWithCIDR(str string, l int) bool {
282
283         if str[l-3] == '/' || str[l-2] == '/' {
284
285                 cidr_bit := strings.Split(str, "/")
286                 if 2 == len(cidr_bit) {
287                         bit, err := strconv.Atoi(cidr_bit[1])
288                         //IPv4 : 0~32, IPv6 : 0 ~ 128
289                         if err == nil && bit >= 0 && bit <= 128 {
290                                 return true
291                         }
292                 }
293         }
294
295         return false
296 }
297
298 func getIPType(str string, l int) int {
299
300         if l < 3 { //least 3 chars (::F)
301                 return None
302         }
303
304         has_dot := strings.Index(str[2:], ".")
305         has_colon := strings.Index(str[2:], ":")
306
307         switch {
308         case has_dot > -1 && has_colon == -1 && l >= 7 && l <= IPv4CIDR:
309                 if isWithCIDR(str, l) == true {
310                         return IPv4CIDR
311                 } else {
312                         return IPv4
313                 }
314         case has_dot == -1 && has_colon > -1 && l >= 6 && l <= IPv6CIDR:
315                 if isWithCIDR(str, l) == true {
316                         return IPv6CIDR
317                 } else {
318                         return IPv6
319                 }
320
321         case has_dot > -1 && has_colon > -1 && l >= 14 && l <= IPv4MappedIPv6:
322                 if isWithCIDR(str, l) == true {
323                         return IPv4MappedIPv6CIDR
324                 } else {
325                         return IPv4MappedIPv6
326                 }
327         }
328
329         return None
330 }
331
332 func (i IPAddr) IsSatisfied(obj interface{}) bool {
333
334         if str, ok := obj.(string); ok {
335
336                 l := len(str)
337                 ret := getIPType(str, l)
338
339                 for _, ck := range i.Vaildtypes {
340
341                         if ret != None && (ck == ret || ck == IPAny) {
342
343                                 switch ret {
344                                 case IPv4, IPv6, IPv4MappedIPv6:
345                                         ip := net.ParseIP(str)
346
347                                         if ip != nil {
348                                                 return true
349                                         }
350
351                                 case IPv4CIDR, IPv6CIDR, IPv4MappedIPv6CIDR:
352                                         _, _, err := net.ParseCIDR(str)
353                                         if err == nil {
354                                                 return true
355                                         }
356                                 }
357                         }
358                 }
359         }
360
361         return false
362 }
363
364 func (i IPAddr) DefaultMessage() string {
365         return fmt.Sprintln("Must be a vaild IP address")
366 }
367
368 // Requires a MAC Address string to be exactly
369 type MacAddr struct{}
370
371 func ValidMacAddr() MacAddr {
372
373         return MacAddr{}
374 }
375
376 func (m MacAddr) IsSatisfied(obj interface{}) bool {
377
378         if str, ok := obj.(string); ok {
379                 if _, err := net.ParseMAC(str); err == nil {
380                         return true
381                 }
382         }
383
384         return false
385 }
386
387 func (m MacAddr) DefaultMessage() string {
388         return fmt.Sprintln("Must be a vaild MAC address")
389 }
390
391 var domainPattern = regexp.MustCompile(`^(([a-zA-Z0-9-\p{L}]{1,63}\.)?(xn--)?[a-zA-Z0-9\p{L}]+(-[a-zA-Z0-9\p{L}]+)*\.)+[a-zA-Z\p{L}]{2,63}$`)
392
393 // Requires a Domain string to be exactly
394 type Domain struct {
395         Regexp *regexp.Regexp
396 }
397
398 func ValidDomain() Domain {
399         return Domain{domainPattern}
400 }
401
402 func (d Domain) IsSatisfied(obj interface{}) bool {
403
404         if str, ok := obj.(string); ok {
405
406                 l := len(str)
407                 //can't exceed 253 chars.
408                 if l > 253 {
409                         return false
410                 }
411
412                 //first and last char must be alphanumeric
413                 if str[l-1] == 46 || str[0] == 46 {
414                         return false
415                 }
416
417                 return domainPattern.MatchString(str)
418         }
419
420         return false
421 }
422
423 func (d Domain) DefaultMessage() string {
424         return fmt.Sprintln("Must be a vaild domain address")
425 }
426
427 var urlPattern = regexp.MustCompile(`^((((https?|ftps?|gopher|telnet|nntp)://)|(mailto:|news:))(%[0-9A-Fa-f]{2}|[-()_.!~*';/?:@#&=+$,A-Za-z0-9\p{L}])+)([).!';/?:,][[:blank:]])?$`)
428
429 type URL struct {
430         Domain
431 }
432
433 func ValidURL() URL {
434         return URL{Domain: ValidDomain()}
435 }
436
437 func (u URL) IsSatisfied(obj interface{}) bool {
438
439         if str, ok := obj.(string); ok {
440
441                 // TODO : Required lot of testing
442                 return urlPattern.MatchString(str)
443         }
444
445         return false
446 }
447
448 func (u URL) DefaultMessage() string {
449         return fmt.Sprintln("Must be a vaild URL address")
450 }
451
452 /*
453 NORMAL BenchmarkRegex-8         2000000000               0.24 ns/op
454 STRICT BenchmarkLoop-8          2000000000               0.01 ns/op
455 */
456 const (
457         NORMAL = 0
458         STRICT = 4
459 )
460
461 // Requires a string to be without invisible characters
462 type PureText struct {
463         Mode int
464 }
465
466 func ValidPureText(m int) PureText {
467         if m != NORMAL && m != STRICT { // Q:required fatal error
468                 m = STRICT
469         }
470         return PureText{m}
471 }
472
473 func isPureTextStrict(str string) (bool, error) {
474
475         l := len(str)
476
477         for i := 0; i < l; i++ {
478
479                 c := str[i]
480
481                 // deny : control char (00-31 without 9(TAB) and Single 10(LF),13(CR)
482                 if c >= 0 && c <= 31 && c != 9 && c != 10 && c != 13 {
483                         return false, errors.New("detect control character")
484                 }
485
486                 // deny : control char (DEL)
487                 if c == 127 {
488                         return false, errors.New("detect control character (DEL)")
489                 }
490
491                 //deny : html tag (< ~ >)
492                 if c == 60 {
493
494                         ds := 0
495                         for n := i; n < l; n++ {
496
497                                 // 60 (<) , 47(/) | 33(!) | 63(?)
498                                 if str[n] == 60 && n+1 <= l && (str[n+1] == 47 || str[n+1] == 33 || str[n+1] == 63) {
499                                         ds = 1
500                                         n += 3 //jump to next char
501                                 }
502
503                                 // 62 (>)
504                                 if ds == 1 && str[n] == 62 {
505                                         return false, errors.New("detect tag (<[!|?]~>)")
506                                 }
507                         }
508                 }
509
510                 //deby : html encoded tag (&xxx;)
511                 if c == 38 && i+1 <= l && str[i+1] != 35 {
512
513                         max := i + 64
514                         if max > l {
515                                 max = l
516                         }
517                         for n := i; n < max; n++ {
518                                 if str[n] == 59 {
519                                         return false, errors.New("detect html encoded ta (&XXX;)")
520                                 }
521                         }
522                 }
523         }
524
525         return true, nil
526 }
527
528 // Requires a string to match a given html tag elements regex pattern
529 // referrer : http://www.w3schools.com/Tags/
530 var elementPattern = regexp.MustCompile(`(?im)<(?P<tag>(/*\s*|\?*|\!*)(figcaption|expression|blockquote|plaintext|textarea|progress|optgroup|noscript|noframes|menuitem|frameset|fieldset|!DOCTYPE|datalist|colgroup|behavior|basefont|summary|section|isindex|details|caption|bgsound|article|address|acronym|strong|strike|source|select|script|output|option|object|legend|keygen|ilayer|iframe|header|footer|figure|dialog|center|canvas|button|applet|video|track|title|thead|tfoot|tbody|table|style|small|param|meter|layer|label|input|frame|embed|blink|audio|aside|alert|time|span|samp|ruby|meta|menu|mark|main|link|html|head|form|font|code|cite|body|base|area|abbr|xss|xml|wbr|var|svg|sup|sub|pre|nav|map|kbd|ins|img|div|dir|dfn|del|col|big|bdo|bdi|!--|ul|tt|tr|th|td|rt|rp|ol|li|hr|em|dt|dl|dd|br|u|s|q|p|i|b|a|(h[0-9]+)))([^><]*)([><]*)`)
531
532 // Requires a string to match a given urlencoded regex pattern
533 var urlencodedPattern = regexp.MustCompile(`(?im)(\%[0-9a-fA-F]{1,})`)
534
535 // Requires a string to match a given control characters regex pattern (ASCII : 00-08, 11, 12, 14, 15-31)
536 var controlcharPattern = regexp.MustCompile(`(?im)([\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+)`)
537
538 func isPureTextNormal(str string) (bool, error) {
539
540         decoded_str := html.UnescapeString(str)
541
542         matched_urlencoded := urlencodedPattern.MatchString(decoded_str)
543         if matched_urlencoded == true {
544                 temp_buf, err := url.QueryUnescape(decoded_str)
545                 if err == nil {
546                         decoded_str = temp_buf
547                 }
548         }
549
550         matched_element := elementPattern.MatchString(decoded_str)
551         if matched_element == true {
552                 return false, errors.New("detect html element")
553         }
554
555         matched_cc := controlcharPattern.MatchString(decoded_str)
556         if matched_cc == true {
557                 return false, errors.New("detect control character")
558         }
559
560         return true, nil
561 }
562
563 func (p PureText) IsSatisfied(obj interface{}) bool {
564
565         if str, ok := obj.(string); ok {
566
567                 var ret bool
568                 switch p.Mode {
569                 case STRICT:
570                         ret, _ = isPureTextStrict(str)
571                 case NORMAL:
572                         ret, _ = isPureTextStrict(str)
573                 }
574                 return ret
575         }
576
577         return false
578 }
579
580 func (p PureText) DefaultMessage() string {
581         return fmt.Sprintln("Must be a vaild Text")
582 }
583
584 const (
585         ONLY_FILENAME       = 0
586         ALLOW_RELATIVE_PATH = 1
587 )
588
589 const regexDenyFileNameCharList = `[\x00-\x1f|\x21-\x2c|\x3b-\x40|\x5b-\x5e|\x60|\x7b-\x7f]+`
590 const regexDenyFileName = `|\x2e\x2e\x2f+`
591
592 var checkAllowRelativePath = regexp.MustCompile(`(?m)(` + regexDenyFileNameCharList + `)`)
593 var checkDenyRelativePath = regexp.MustCompile(`(?m)(` + regexDenyFileNameCharList + regexDenyFileName + `)`)
594
595 // Requires an string to be sanitary file path
596 type FilePath struct {
597         Mode int
598 }
599
600 func ValidFilePath(m int) FilePath {
601
602         if m != ONLY_FILENAME && m != ALLOW_RELATIVE_PATH {
603                 m = ONLY_FILENAME
604         }
605         return FilePath{m}
606 }
607
608 func (f FilePath) IsSatisfied(obj interface{}) bool {
609
610         if str, ok := obj.(string); ok {
611
612                 var ret bool
613                 switch f.Mode {
614
615                 case ALLOW_RELATIVE_PATH:
616                         ret = checkAllowRelativePath.MatchString(str)
617                         if ret == false {
618                                 return true
619                         }
620                 default: //ONLY_FILENAME
621                         ret = checkDenyRelativePath.MatchString(str)
622                         if ret == false {
623                                 return true
624                         }
625                 }
626         }
627
628         return false
629 }
630
631 func (f FilePath) DefaultMessage() string {
632         return fmt.Sprintln("Must be a unsanitary string")
633 }