go.chromium.org/luci@v0.0.0-20250314024836-d9a61d0730e6/tokenserver/appengine/impl/utils/projectidentity/storage.go (about)

     1  // Copyright 2019 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 projectidentity
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  
    21  	"go.chromium.org/luci/common/logging"
    22  	"go.chromium.org/luci/common/retry/transient"
    23  	ds "go.chromium.org/luci/gae/service/datastore"
    24  )
    25  
    26  var (
    27  	// ErrNotFound indicates that the entity which was queried does not exist in the storage.
    28  	ErrNotFound = errors.New("not found")
    29  )
    30  
    31  // projectIdentities is the default storage for all scoped identities.
    32  var projectIdentities = &persistentStorage{}
    33  
    34  // ProjectIdentities returns the global scoped identity storage.
    35  func ProjectIdentities(_ context.Context) Storage {
    36  	return projectIdentities
    37  }
    38  
    39  // Storage interface declares methods for the scoped identity storage.
    40  type Storage interface {
    41  
    42  	// Create an identity or update if it already exists.
    43  	Create(c context.Context, identity *ProjectIdentity) (*ProjectIdentity, error)
    44  
    45  	// Update an identity in the storage.
    46  	Update(c context.Context, identity *ProjectIdentity) (*ProjectIdentity, error)
    47  
    48  	// Delete an identity from the storage.
    49  	Delete(c context.Context, identity *ProjectIdentity) error
    50  
    51  	// LookupByProject performs a lookup by project name.
    52  	LookupByProject(c context.Context, project string) (*ProjectIdentity, error)
    53  }
    54  
    55  // ProjectIdentity defines a scoped identity in the storage.
    56  type ProjectIdentity struct {
    57  	_kind   string `gae:"$kind,ScopedIdentity"`
    58  	Project string `gae:"$id"`
    59  	Email   string
    60  }
    61  
    62  // persistentStorage implements ScopedIdentityManager.
    63  type persistentStorage struct {
    64  }
    65  
    66  // lookup reads an identity from the storage based on what fields are set in the identity struct.
    67  func (s *persistentStorage) lookup(c context.Context, identity *ProjectIdentity) (*ProjectIdentity, error) {
    68  	logging.Debugf(c, "lookup project scoped identity %v", identity)
    69  	tmp := *identity
    70  	if err := ds.Get(c, &tmp); err != nil {
    71  		switch {
    72  		case err == ds.ErrNoSuchEntity:
    73  			return nil, ErrNotFound
    74  		case err != nil:
    75  			return nil, transient.Tag.Apply(err)
    76  		}
    77  	}
    78  	return &tmp, nil
    79  }
    80  
    81  // LookupByProject returns the project identity stored for a given project.
    82  func (s *persistentStorage) LookupByProject(c context.Context, project string) (*ProjectIdentity, error) {
    83  	return s.lookup(c, &ProjectIdentity{Project: project})
    84  }
    85  
    86  // Delete removes an identity from the storage.
    87  func (s *persistentStorage) Delete(c context.Context, identity *ProjectIdentity) error {
    88  	logging.Debugf(c, "delete project scoped identity %v", identity)
    89  	return ds.Delete(c, identity)
    90  }
    91  
    92  // Create stores a new entry for a project identity.
    93  func (s *persistentStorage) Create(c context.Context, identity *ProjectIdentity) (*ProjectIdentity, error) {
    94  	logging.Debugf(c, "create project scoped identity %v", identity)
    95  	return s.Update(c, identity)
    96  }
    97  
    98  // Update allows an identity to be updated, e.g. when the service account email changes.
    99  func (s *persistentStorage) Update(c context.Context, identity *ProjectIdentity) (*ProjectIdentity, error) {
   100  	logging.Debugf(c, "update project scoped identity %v", identity)
   101  	tmp, err := s.lookup(c, identity)
   102  	switch {
   103  	case err == nil && *tmp == *identity: // Doesn't need update
   104  		return identity, nil
   105  	case err != nil && err != ErrNotFound: // Lookup error to propagate
   106  		return nil, err
   107  	}
   108  
   109  	if err := ds.Put(c, identity); err != nil {
   110  		return nil, err
   111  	}
   112  	return identity, nil
   113  }