| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- // Copyright 2019 Google LLC
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package storage
- import (
- "context"
- "errors"
- "fmt"
- "time"
- "cloud.google.com/go/storage/internal/apiv2/storagepb"
- "google.golang.org/api/iterator"
- raw "google.golang.org/api/storage/v1"
- )
- // HMACState is the state of the HMAC key.
- type HMACState string
- const (
- // Active is the status for an active key that can be used to sign
- // requests.
- Active HMACState = "ACTIVE"
- // Inactive is the status for an inactive key thus requests signed by
- // this key will be denied.
- Inactive HMACState = "INACTIVE"
- // Deleted is the status for a key that is deleted.
- // Once in this state the key cannot key cannot be recovered
- // and does not count towards key limits. Deleted keys will be cleaned
- // up later.
- Deleted HMACState = "DELETED"
- )
- // HMACKey is the representation of a Google Cloud Storage HMAC key.
- //
- // HMAC keys are used to authenticate signed access to objects. To enable HMAC key
- // authentication, please visit https://cloud.google.com/storage/docs/migrating.
- type HMACKey struct {
- // The HMAC's secret key.
- Secret string
- // AccessID is the ID of the HMAC key.
- AccessID string
- // Etag is the HTTP/1.1 Entity tag.
- Etag string
- // ID is the ID of the HMAC key, including the ProjectID and AccessID.
- ID string
- // ProjectID is the ID of the project that owns the
- // service account to which the key authenticates.
- ProjectID string
- // ServiceAccountEmail is the email address
- // of the key's associated service account.
- ServiceAccountEmail string
- // CreatedTime is the creation time of the HMAC key.
- CreatedTime time.Time
- // UpdatedTime is the last modification time of the HMAC key metadata.
- UpdatedTime time.Time
- // State is the state of the HMAC key.
- // It can be one of StateActive, StateInactive or StateDeleted.
- State HMACState
- }
- // HMACKeyHandle helps provide access and management for HMAC keys.
- type HMACKeyHandle struct {
- projectID string
- accessID string
- retry *retryConfig
- tc storageClient
- }
- // HMACKeyHandle creates a handle that will be used for HMACKey operations.
- func (c *Client) HMACKeyHandle(projectID, accessID string) *HMACKeyHandle {
- return &HMACKeyHandle{
- projectID: projectID,
- accessID: accessID,
- retry: c.retry,
- tc: c.tc,
- }
- }
- // Get invokes an RPC to retrieve the HMAC key referenced by the
- // HMACKeyHandle's accessID.
- //
- // Options such as UserProjectForHMACKeys can be used to set the
- // userProject to be billed against for operations.
- func (hkh *HMACKeyHandle) Get(ctx context.Context, opts ...HMACKeyOption) (*HMACKey, error) {
- desc := new(hmacKeyDesc)
- for _, opt := range opts {
- opt.withHMACKeyDesc(desc)
- }
- o := makeStorageOpts(true, hkh.retry, desc.userProjectID)
- hk, err := hkh.tc.GetHMACKey(ctx, hkh.projectID, hkh.accessID, o...)
- return hk, err
- }
- // Delete invokes an RPC to delete the key referenced by accessID, on Google Cloud Storage.
- // Only inactive HMAC keys can be deleted.
- // After deletion, a key cannot be used to authenticate requests.
- func (hkh *HMACKeyHandle) Delete(ctx context.Context, opts ...HMACKeyOption) error {
- desc := new(hmacKeyDesc)
- for _, opt := range opts {
- opt.withHMACKeyDesc(desc)
- }
- o := makeStorageOpts(true, hkh.retry, desc.userProjectID)
- return hkh.tc.DeleteHMACKey(ctx, hkh.projectID, hkh.accessID, o...)
- }
- func toHMACKeyFromRaw(hk *raw.HmacKey, updatedTimeCanBeNil bool) (*HMACKey, error) {
- hkmd := hk.Metadata
- if hkmd == nil {
- return nil, errors.New("field Metadata cannot be nil")
- }
- createdTime, err := time.Parse(time.RFC3339, hkmd.TimeCreated)
- if err != nil {
- return nil, fmt.Errorf("field CreatedTime: %w", err)
- }
- updatedTime, err := time.Parse(time.RFC3339, hkmd.Updated)
- if err != nil && !updatedTimeCanBeNil {
- return nil, fmt.Errorf("field UpdatedTime: %w", err)
- }
- hmKey := &HMACKey{
- AccessID: hkmd.AccessId,
- Secret: hk.Secret,
- Etag: hkmd.Etag,
- ID: hkmd.Id,
- State: HMACState(hkmd.State),
- ProjectID: hkmd.ProjectId,
- CreatedTime: createdTime,
- UpdatedTime: updatedTime,
- ServiceAccountEmail: hkmd.ServiceAccountEmail,
- }
- return hmKey, nil
- }
- func toHMACKeyFromProto(pbmd *storagepb.HmacKeyMetadata) *HMACKey {
- if pbmd == nil {
- return nil
- }
- return &HMACKey{
- AccessID: pbmd.GetAccessId(),
- ID: pbmd.GetId(),
- State: HMACState(pbmd.GetState()),
- ProjectID: pbmd.GetProject(),
- CreatedTime: convertProtoTime(pbmd.GetCreateTime()),
- UpdatedTime: convertProtoTime(pbmd.GetUpdateTime()),
- ServiceAccountEmail: pbmd.GetServiceAccountEmail(),
- }
- }
- // CreateHMACKey invokes an RPC for Google Cloud Storage to create a new HMACKey.
- func (c *Client) CreateHMACKey(ctx context.Context, projectID, serviceAccountEmail string, opts ...HMACKeyOption) (*HMACKey, error) {
- if projectID == "" {
- return nil, errors.New("storage: expecting a non-blank projectID")
- }
- if serviceAccountEmail == "" {
- return nil, errors.New("storage: expecting a non-blank service account email")
- }
- desc := new(hmacKeyDesc)
- for _, opt := range opts {
- opt.withHMACKeyDesc(desc)
- }
- o := makeStorageOpts(false, c.retry, desc.userProjectID)
- hk, err := c.tc.CreateHMACKey(ctx, projectID, serviceAccountEmail, o...)
- return hk, err
- }
- // HMACKeyAttrsToUpdate defines the attributes of an HMACKey that will be updated.
- type HMACKeyAttrsToUpdate struct {
- // State is required and must be either StateActive or StateInactive.
- State HMACState
- // Etag is an optional field and it is the HTTP/1.1 Entity tag.
- Etag string
- }
- // Update mutates the HMACKey referred to by accessID.
- func (h *HMACKeyHandle) Update(ctx context.Context, au HMACKeyAttrsToUpdate, opts ...HMACKeyOption) (*HMACKey, error) {
- if au.State != Active && au.State != Inactive {
- return nil, fmt.Errorf("storage: invalid state %q for update, must be either %q or %q", au.State, Active, Inactive)
- }
- desc := new(hmacKeyDesc)
- for _, opt := range opts {
- opt.withHMACKeyDesc(desc)
- }
- isIdempotent := len(au.Etag) > 0
- o := makeStorageOpts(isIdempotent, h.retry, desc.userProjectID)
- hk, err := h.tc.UpdateHMACKey(ctx, h.projectID, desc.forServiceAccountEmail, h.accessID, &au, o...)
- return hk, err
- }
- // An HMACKeysIterator is an iterator over HMACKeys.
- //
- // Note: This iterator is not safe for concurrent operations without explicit synchronization.
- type HMACKeysIterator struct {
- ctx context.Context
- raw *raw.ProjectsHmacKeysService
- projectID string
- hmacKeys []*HMACKey
- pageInfo *iterator.PageInfo
- nextFunc func() error
- index int
- desc hmacKeyDesc
- retry *retryConfig
- }
- // ListHMACKeys returns an iterator for listing HMACKeys.
- //
- // Note: This iterator is not safe for concurrent operations without explicit synchronization.
- func (c *Client) ListHMACKeys(ctx context.Context, projectID string, opts ...HMACKeyOption) *HMACKeysIterator {
- desc := new(hmacKeyDesc)
- for _, opt := range opts {
- opt.withHMACKeyDesc(desc)
- }
- o := makeStorageOpts(true, c.retry, desc.userProjectID)
- return c.tc.ListHMACKeys(ctx, projectID, desc.forServiceAccountEmail, desc.showDeletedKeys, o...)
- }
- // Next returns the next result. Its second return value is iterator.Done if
- // there are no more results. Once Next returns iterator.Done, all subsequent
- // calls will return iterator.Done.
- //
- // Note: This iterator is not safe for concurrent operations without explicit synchronization.
- func (it *HMACKeysIterator) Next() (*HMACKey, error) {
- if err := it.nextFunc(); err != nil {
- return nil, err
- }
- key := it.hmacKeys[it.index]
- it.index++
- return key, nil
- }
- // PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
- //
- // Note: This iterator is not safe for concurrent operations without explicit synchronization.
- func (it *HMACKeysIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }
- func (it *HMACKeysIterator) fetch(pageSize int, pageToken string) (token string, err error) {
- // TODO: Remove fetch method upon integration. This method is internalized into
- // httpStorageClient.ListHMACKeys() as it is the only caller.
- call := it.raw.List(it.projectID)
- setClientHeader(call.Header())
- if pageToken != "" {
- call = call.PageToken(pageToken)
- }
- if it.desc.showDeletedKeys {
- call = call.ShowDeletedKeys(true)
- }
- if it.desc.userProjectID != "" {
- call = call.UserProject(it.desc.userProjectID)
- }
- if it.desc.forServiceAccountEmail != "" {
- call = call.ServiceAccountEmail(it.desc.forServiceAccountEmail)
- }
- if pageSize > 0 {
- call = call.MaxResults(int64(pageSize))
- }
- var resp *raw.HmacKeysMetadata
- err = run(it.ctx, func(ctx context.Context) error {
- resp, err = call.Context(ctx).Do()
- return err
- }, it.retry, true)
- if err != nil {
- return "", err
- }
- for _, metadata := range resp.Items {
- hk := &raw.HmacKey{
- Metadata: metadata,
- }
- hkey, err := toHMACKeyFromRaw(hk, true)
- if err != nil {
- return "", err
- }
- it.hmacKeys = append(it.hmacKeys, hkey)
- }
- return resp.NextPageToken, nil
- }
- type hmacKeyDesc struct {
- forServiceAccountEmail string
- showDeletedKeys bool
- userProjectID string
- }
- // HMACKeyOption configures the behavior of HMACKey related methods and actions.
- type HMACKeyOption interface {
- withHMACKeyDesc(*hmacKeyDesc)
- }
- type hmacKeyDescFunc func(*hmacKeyDesc)
- func (hkdf hmacKeyDescFunc) withHMACKeyDesc(hkd *hmacKeyDesc) {
- hkdf(hkd)
- }
- // ForHMACKeyServiceAccountEmail returns HMAC Keys that are
- // associated with the email address of a service account in the project.
- //
- // Only one service account email can be used as a filter, so if multiple
- // of these options are applied, the last email to be set will be used.
- func ForHMACKeyServiceAccountEmail(serviceAccountEmail string) HMACKeyOption {
- return hmacKeyDescFunc(func(hkd *hmacKeyDesc) {
- hkd.forServiceAccountEmail = serviceAccountEmail
- })
- }
- // ShowDeletedHMACKeys will also list keys whose state is "DELETED".
- func ShowDeletedHMACKeys() HMACKeyOption {
- return hmacKeyDescFunc(func(hkd *hmacKeyDesc) {
- hkd.showDeletedKeys = true
- })
- }
- // UserProjectForHMACKeys will bill the request against userProjectID
- // if userProjectID is non-empty.
- //
- // Note: This is a noop right now and only provided for API compatibility.
- func UserProjectForHMACKeys(userProjectID string) HMACKeyOption {
- return hmacKeyDescFunc(func(hkd *hmacKeyDesc) {
- hkd.userProjectID = userProjectID
- })
- }
|