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