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  }