go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/auth/identity/identity.go (about) 1 // Copyright 2015 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package identity 16 17 import ( 18 "fmt" 19 "regexp" 20 "strings" 21 ) 22 23 // Kind is enumeration of known identity kinds. See Identity. 24 type Kind string 25 26 const ( 27 // Anonymous kind means no identity information is provided. Identity value 28 // is always 'anonymous'. 29 Anonymous Kind = "anonymous" 30 31 // Bot is used for bots authenticated via IP allowlist. Used primarily by 32 // Swarming. Identity value encodes bot ID. 33 Bot Kind = "bot" 34 35 // Project is used to convey that the request from one LUCI project to another 36 // is being made on behalf of some LUCI project. Identity value is project ID. 37 Project Kind = "project" 38 39 // Service is used for GAE apps using X-Appengine-Inbound-Appid header for 40 // authentication. Identity value is GAE app id. 41 Service Kind = "service" 42 43 // User is used for regular users. Identity value is email address. 44 User Kind = "user" 45 ) 46 47 // KnownKinds is mapping between Kind and regexp for identity values. 48 // 49 // See also appengine/components/components/auth/model.py in luci-py. 50 var KnownKinds = map[Kind]*regexp.Regexp{ 51 Anonymous: regexp.MustCompile(`^anonymous$`), 52 Bot: regexp.MustCompile(`^[0-9a-zA-Z_\-\.@]+$`), 53 Project: regexp.MustCompile(`^[a-z0-9\-_]+$`), 54 Service: regexp.MustCompile(`^[0-9a-zA-Z_\-\:\.]+$`), 55 User: regexp.MustCompile(`^[0-9a-zA-Z_\-\.\+\%]+@[0-9a-zA-Z_\-\.]+$`), 56 } 57 58 // Identity represents a caller that makes requests. A string of the form 59 // "kind:value" where 'kind' is one of Kind constant and meaning of 'value' 60 // depends on a kind (see comments for Kind values). 61 type Identity string 62 63 // AnonymousIdentity represents anonymous user. 64 const AnonymousIdentity Identity = "anonymous:anonymous" 65 66 // MakeIdentity ensures 'identity' string looks like a valid identity and 67 // returns it as Identity value. 68 func MakeIdentity(identity string) (Identity, error) { 69 id := Identity(identity) 70 if err := id.Validate(); err != nil { 71 return "", err 72 } 73 return id, nil 74 } 75 76 // Validate checks that the identity string is well-formed. 77 func (id Identity) Validate() error { 78 chunks := strings.SplitN(string(id), ":", 2) 79 if len(chunks) != 2 { 80 return fmt.Errorf("auth: bad identity string %q", id) 81 } 82 re := KnownKinds[Kind(chunks[0])] 83 if re == nil { 84 return fmt.Errorf("auth: bad identity kind %q", chunks[0]) 85 } 86 if !re.MatchString(chunks[1]) { 87 return fmt.Errorf("auth: bad value %q for identity kind %q", chunks[1], chunks[0]) 88 } 89 return nil 90 } 91 92 // Kind returns identity kind. If identity string is invalid returns Anonymous. 93 func (id Identity) Kind() Kind { 94 chunks := strings.SplitN(string(id), ":", 2) 95 if len(chunks) != 2 { 96 return Anonymous 97 } 98 return Kind(chunks[0]) 99 } 100 101 // Value returns a valued encoded in the identity, e.g. for User identity kind 102 // it is user's email address. If identity string is invalid returns "anonymous". 103 func (id Identity) Value() string { 104 chunks := strings.SplitN(string(id), ":", 2) 105 if len(chunks) != 2 { 106 return "anonymous" 107 } 108 return chunks[1] 109 } 110 111 // Email returns user's email for identity with kind User or empty string for 112 // all other identity kinds. If identity string is undefined returns "". 113 func (id Identity) Email() string { 114 chunks := strings.SplitN(string(id), ":", 2) 115 if len(chunks) != 2 { 116 return "" 117 } 118 if Kind(chunks[0]) == User { 119 return chunks[1] 120 } 121 return "" 122 }