go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/auth/authtest/state.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 authtest 16 17 import ( 18 "net" 19 20 "golang.org/x/oauth2" 21 22 "go.chromium.org/luci/auth/identity" 23 24 "go.chromium.org/luci/server/auth" 25 "go.chromium.org/luci/server/auth/authdb" 26 "go.chromium.org/luci/server/auth/realms" 27 ) 28 29 // FakeState implements auth.State by returning predefined values. 30 // 31 // Inject it into the context when testing handlers that expect an auth state: 32 // 33 // ctx = auth.WithState(ctx, &authtest.FakeState{ 34 // Identity: "user:user@example.com", 35 // IdentityGroups: []string{"admins"}, 36 // IdentityPermissions: []authtest.RealmPermission{ 37 // {"proj:realm1", perm1}, 38 // {"proj:realm1", perm2}, 39 // } 40 // }) 41 // auth.IsMember(ctx, "admins") -> returns true. 42 // auth.HasPermission(ctx, perm1, "proj:realm1", nil) -> returns true. 43 // 44 // Note that IdentityGroups, IdentityPermissions, PeerIPAllowlist and Error 45 // are effective only when FakeDB is nil. They are used as a shortcut to 46 // construct the corresponding FakeDB on the fly. If you need to prepare a more 47 // complex fake state, pass NewFakeDB(...) as FakeDB instead: 48 // 49 // ctx = auth.WithState(ctx, &authtest.FakeState{ 50 // Identity: "user:user@example.com", 51 // FakeDB: NewFakeDB( 52 // authtest.MockMembership("user:user@example.com", "group"), 53 // authtest.MockMembership("user:another@example.com", "group"), 54 // authtest.MockPermission("user:user@example.com", "proj:realm1", perm1), 55 // ... 56 // ), 57 // }) 58 type FakeState struct { 59 // Identity is main identity associated with the request. 60 // 61 // identity.AnonymousIdentity if not set. 62 Identity identity.Identity 63 64 // IdentityGroups is list of groups the calling identity belongs to. 65 IdentityGroups []string 66 67 // IdentityPermissions is a list of (realm, permission) tuples that define 68 // caller's permissions. 69 IdentityPermissions []RealmPermission 70 71 // PeerIPAllowlist is a list of IP allowlists the caller IP belongs to. 72 PeerIPAllowlist []string 73 74 // Error, if not nil, is returned by auth DB checks. 75 Error error 76 77 // FakeDB is an authdb.DB implementation to use. 78 // 79 // If not nil, takes precedence over IdentityGroups, IdentityPermissions, 80 // PeerIPAllowlist and Error. 81 FakeDB authdb.DB 82 83 // SessionOverride may be set for Session() to return custom value. 84 // 85 // By default Session() returns nil. 86 SessionOverride auth.Session 87 88 // PeerIdentityOverride may be set for PeerIdentity() to return custom value. 89 // 90 // By default PeerIdentity() returns Identity (i.e. no delegation is 91 // happening). 92 PeerIdentityOverride identity.Identity 93 94 // PeerIPOverride may be set for PeerIP() to return custom value. 95 // 96 // By default PeerIP() returns "127.0.0.1". 97 PeerIPOverride net.IP 98 99 // UserCredentialsOverride may be set to override UserCredentials(). 100 // 101 // By default UserCredentials() returns ErrNoForwardableCreds error. 102 UserCredentialsOverride *oauth2.Token 103 104 // UserExtra is returned as Extra field of User() return value. 105 UserExtra any 106 } 107 108 // RealmPermission is used to populate IdentityPermissions in FakeState. 109 type RealmPermission struct { 110 Realm string 111 Permission realms.Permission 112 } 113 114 var _ auth.State = (*FakeState)(nil) 115 116 // Authenticator is part of State interface. 117 func (s *FakeState) Authenticator() *auth.Authenticator { 118 return &auth.Authenticator{ 119 Methods: []auth.Method{ 120 &FakeAuth{User: s.User()}, 121 }, 122 } 123 } 124 125 // DB is part of State interface. 126 func (s *FakeState) DB() authdb.DB { 127 if s.FakeDB != nil { 128 return s.FakeDB 129 } 130 131 ident := s.User().Identity 132 peerIP := s.PeerIP().String() 133 134 // We construct it on the fly each time to allow FakeState users to modify 135 // Identity, IdentityGroups, IdentityPermissions, etc. dynamically. 136 mocks := []MockedDatum{} 137 for _, group := range s.IdentityGroups { 138 mocks = append(mocks, MockMembership(ident, group)) 139 } 140 for _, perm := range s.IdentityPermissions { 141 mocks = append(mocks, MockPermission(ident, perm.Realm, perm.Permission)) 142 } 143 for _, wl := range s.PeerIPAllowlist { 144 mocks = append(mocks, MockIPAllowlist(peerIP, wl)) 145 } 146 if s.Error != nil { 147 mocks = append(mocks, MockError(s.Error)) 148 } 149 return NewFakeDB(mocks...) 150 } 151 152 // Method is part of State interface. 153 func (s *FakeState) Method() auth.Method { 154 return s.Authenticator().Methods[0] 155 } 156 157 // User is part of State interface. 158 func (s *FakeState) User() *auth.User { 159 ident := identity.AnonymousIdentity 160 if s.Identity != "" { 161 ident = s.Identity 162 } 163 return &auth.User{ 164 Identity: ident, 165 Email: ident.Email(), 166 Extra: s.UserExtra, 167 } 168 } 169 170 // Session is part of State interface. 171 func (s *FakeState) Session() auth.Session { 172 return s.SessionOverride 173 } 174 175 // PeerIdentity is part of State interface. 176 func (s *FakeState) PeerIdentity() identity.Identity { 177 if s.PeerIdentityOverride == "" { 178 return s.User().Identity 179 } 180 return s.PeerIdentityOverride 181 } 182 183 // PeerIP is part of State interface. 184 func (s *FakeState) PeerIP() net.IP { 185 if s.PeerIPOverride == nil { 186 return net.ParseIP("127.0.0.1") 187 } 188 return s.PeerIPOverride 189 } 190 191 // UserCredentials is part of State interface. 192 func (s *FakeState) UserCredentials() (*oauth2.Token, map[string]string, error) { 193 if s.UserCredentialsOverride != nil { 194 return s.UserCredentialsOverride, nil, nil 195 } 196 return nil, nil, auth.ErrNoForwardableCreds 197 }