github.com/hashicorp/vault/sdk@v0.13.0/database/helper/credsutil/usernames.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package credsutil 5 6 import ( 7 "fmt" 8 "strings" 9 "time" 10 ) 11 12 //go:generate enumer -type=CaseOp -transform=snake 13 type CaseOp int 14 15 const ( 16 KeepCase CaseOp = iota 17 Uppercase 18 Lowercase 19 ) 20 21 type usernameBuilder struct { 22 displayName string 23 roleName string 24 separator string 25 26 maxLen int 27 caseOperation CaseOp 28 } 29 30 func (ub usernameBuilder) makeUsername() (string, error) { 31 userUUID, err := RandomAlphaNumeric(20, false) 32 if err != nil { 33 return "", err 34 } 35 36 now := fmt.Sprint(time.Now().Unix()) 37 38 username := joinNonEmpty(ub.separator, 39 "v", 40 ub.displayName, 41 ub.roleName, 42 userUUID, 43 now) 44 username = trunc(username, ub.maxLen) 45 switch ub.caseOperation { 46 case Lowercase: 47 username = strings.ToLower(username) 48 case Uppercase: 49 username = strings.ToUpper(username) 50 } 51 52 return username, nil 53 } 54 55 type UsernameOpt func(*usernameBuilder) 56 57 func DisplayName(dispName string, maxLength int) UsernameOpt { 58 return func(b *usernameBuilder) { 59 b.displayName = trunc(dispName, maxLength) 60 } 61 } 62 63 func RoleName(roleName string, maxLength int) UsernameOpt { 64 return func(b *usernameBuilder) { 65 b.roleName = trunc(roleName, maxLength) 66 } 67 } 68 69 func Separator(sep string) UsernameOpt { 70 return func(b *usernameBuilder) { 71 b.separator = sep 72 } 73 } 74 75 func MaxLength(maxLen int) UsernameOpt { 76 return func(b *usernameBuilder) { 77 b.maxLen = maxLen 78 } 79 } 80 81 func Case(c CaseOp) UsernameOpt { 82 return func(b *usernameBuilder) { 83 b.caseOperation = c 84 } 85 } 86 87 func ToLower() UsernameOpt { 88 return Case(Lowercase) 89 } 90 91 func ToUpper() UsernameOpt { 92 return Case(Uppercase) 93 } 94 95 func GenerateUsername(opts ...UsernameOpt) (string, error) { 96 b := usernameBuilder{ 97 separator: "_", 98 maxLen: 100, 99 caseOperation: KeepCase, 100 } 101 102 for _, opt := range opts { 103 opt(&b) 104 } 105 106 return b.makeUsername() 107 } 108 109 func trunc(str string, l int) string { 110 switch { 111 case l > 0: 112 if l > len(str) { 113 return str 114 } 115 return str[:l] 116 case l == 0: 117 return str 118 default: 119 return "" 120 } 121 } 122 123 func joinNonEmpty(sep string, vals ...string) string { 124 if sep == "" { 125 return strings.Join(vals, sep) 126 } 127 switch len(vals) { 128 case 0: 129 return "" 130 case 1: 131 return vals[0] 132 } 133 builder := &strings.Builder{} 134 for _, val := range vals { 135 if val == "" { 136 continue 137 } 138 if builder.Len() > 0 { 139 builder.WriteString(sep) 140 } 141 builder.WriteString(val) 142 } 143 return builder.String() 144 }