github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/golang/text/secure/precis/profile.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package precis 6 7 import ( 8 "errors" 9 "unicode/utf8" 10 11 "github.com/insionng/yougam/libraries/x/text/runes" 12 "github.com/insionng/yougam/libraries/x/text/transform" 13 "github.com/insionng/yougam/libraries/x/text/width" 14 ) 15 16 var ( 17 disallowedRune = errors.New("disallowed rune encountered") 18 ) 19 20 var dpTrie = newDerivedPropertiesTrie(0) 21 22 // A Profile represents a set of rules for normalizing and validating strings in 23 // the PRECIS framework. 24 type Profile struct { 25 options 26 class *class 27 transform.NopResetter 28 } 29 30 // NewIdentifier creates a new PRECIS profile based on the Identifier string 31 // class. Profiles created from this class are suitable for use where safety is 32 // prioritized over expressiveness like network identifiers, user accounts, chat 33 // rooms, and file names. 34 func NewIdentifier(opts ...Option) Profile { 35 return Profile{ 36 options: getOpts(opts...), 37 class: identifier, 38 } 39 } 40 41 // NewFreeform creates a new PRECIS profile based on the Freeform string class. 42 // Profiles created from this class are suitable for use where expressiveness is 43 // prioritized over safety like passwords, and display-elements such as 44 // nicknames in a chat room. 45 func NewFreeform(opts ...Option) Profile { 46 return Profile{ 47 options: getOpts(opts...), 48 class: freeform, 49 } 50 } 51 52 // NewTransformer creates a new transform.Transformer that performs the PRECIS 53 // preparation and enforcement steps on the given UTF-8 encoded bytes. 54 func (p Profile) NewTransformer() *Transformer { 55 var ts []transform.Transformer 56 57 if p.options.allowwidechars { 58 ts = append(ts, width.Fold) 59 } 60 61 ts = append(ts, checker{p: p}) 62 63 if p.options.width != nil { 64 ts = append(ts, width.Fold) 65 } 66 67 for _, f := range p.options.additional { 68 ts = append(ts, f()) 69 } 70 71 if p.options.cases != nil { 72 ts = append(ts, p.options.cases) 73 } 74 75 ts = append(ts, p.options.norm) 76 77 // TODO: Apply directionality rule (blocking on the Bidi package) 78 // TODO: Add the disallow empty rule with a dummy transformer? 79 80 return &Transformer{transform.Chain(ts...)} 81 } 82 83 // Bytes returns a new byte slice with the result of applying the profile to b. 84 func (p Profile) Bytes(b []byte) ([]byte, error) { 85 b, _, err := transform.Bytes(p.NewTransformer(), b) 86 if err == nil && p.options.disallowEmpty && len(b) == 0 { 87 return b, errors.New("enforce resulted in empty string") 88 } 89 return b, err 90 } 91 92 // String returns a string with the result of applying the profile to s. 93 func (p Profile) String(s string) (string, error) { 94 s, _, err := transform.String(p.NewTransformer(), s) 95 if err == nil && p.options.disallowEmpty && len(s) == 0 { 96 return s, errors.New("enforce resulted in empty string") 97 } 98 return s, err 99 } 100 101 // Compare enforces both strings, and then compares them for bit-string identity 102 // (byte-for-byte equality). If either string cannot be enforced, the comparison 103 // is false. 104 func (p Profile) Compare(a, b string) bool { 105 a, err := p.String(a) 106 if err != nil { 107 return false 108 } 109 b, err = p.String(b) 110 if err != nil { 111 return false 112 } 113 114 // TODO: This is out of order. Need to extract the transformation logic and 115 // put this in where the normal case folding would go (but only for 116 // comparison). 117 if p.options.ignorecase { 118 a = width.Fold.String(a) 119 b = width.Fold.String(a) 120 } 121 122 return a == b 123 } 124 125 // Allowed returns a runes.Set containing every rune that is a member of the 126 // underlying profile's string class and not disallowed by any profile specific 127 // rules. 128 func (p Profile) Allowed() runes.Set { 129 return runes.Predicate(func(r rune) bool { 130 if p.options.disallow != nil { 131 return p.class.Contains(r) && !p.options.disallow.Contains(r) 132 } else { 133 return p.class.Contains(r) 134 } 135 }) 136 } 137 138 type checker struct { 139 p Profile 140 transform.NopResetter 141 } 142 143 func (c checker) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { 144 for nSrc < len(src) { 145 r, size := utf8.DecodeRune(src[nSrc:]) 146 if size == 0 { // Incomplete UTF-8 encoding 147 if !atEOF { 148 return nDst, nSrc, transform.ErrShortSrc 149 } 150 size = 1 151 } 152 if c.p.Allowed().Contains(r) { 153 if size != copy(dst[nDst:], src[nSrc:nSrc+size]) { 154 return nDst, nSrc, transform.ErrShortDst 155 } 156 nDst += size 157 } else { 158 return nDst, nSrc, disallowedRune 159 } 160 nSrc += size 161 } 162 return nDst, nSrc, nil 163 }