hmac.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. // Copyright 2019 Google LLC
  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. package storage
  15. import (
  16. "context"
  17. "errors"
  18. "fmt"
  19. "time"
  20. "cloud.google.com/go/storage/internal/apiv2/storagepb"
  21. "google.golang.org/api/iterator"
  22. raw "google.golang.org/api/storage/v1"
  23. )
  24. // HMACState is the state of the HMAC key.
  25. type HMACState string
  26. const (
  27. // Active is the status for an active key that can be used to sign
  28. // requests.
  29. Active HMACState = "ACTIVE"
  30. // Inactive is the status for an inactive key thus requests signed by
  31. // this key will be denied.
  32. Inactive HMACState = "INACTIVE"
  33. // Deleted is the status for a key that is deleted.
  34. // Once in this state the key cannot key cannot be recovered
  35. // and does not count towards key limits. Deleted keys will be cleaned
  36. // up later.
  37. Deleted HMACState = "DELETED"
  38. )
  39. // HMACKey is the representation of a Google Cloud Storage HMAC key.
  40. //
  41. // HMAC keys are used to authenticate signed access to objects. To enable HMAC key
  42. // authentication, please visit https://cloud.google.com/storage/docs/migrating.
  43. type HMACKey struct {
  44. // The HMAC's secret key.
  45. Secret string
  46. // AccessID is the ID of the HMAC key.
  47. AccessID string
  48. // Etag is the HTTP/1.1 Entity tag.
  49. Etag string
  50. // ID is the ID of the HMAC key, including the ProjectID and AccessID.
  51. ID string
  52. // ProjectID is the ID of the project that owns the
  53. // service account to which the key authenticates.
  54. ProjectID string
  55. // ServiceAccountEmail is the email address
  56. // of the key's associated service account.
  57. ServiceAccountEmail string
  58. // CreatedTime is the creation time of the HMAC key.
  59. CreatedTime time.Time
  60. // UpdatedTime is the last modification time of the HMAC key metadata.
  61. UpdatedTime time.Time
  62. // State is the state of the HMAC key.
  63. // It can be one of StateActive, StateInactive or StateDeleted.
  64. State HMACState
  65. }
  66. // HMACKeyHandle helps provide access and management for HMAC keys.
  67. type HMACKeyHandle struct {
  68. projectID string
  69. accessID string
  70. retry *retryConfig
  71. tc storageClient
  72. }
  73. // HMACKeyHandle creates a handle that will be used for HMACKey operations.
  74. func (c *Client) HMACKeyHandle(projectID, accessID string) *HMACKeyHandle {
  75. return &HMACKeyHandle{
  76. projectID: projectID,
  77. accessID: accessID,
  78. retry: c.retry,
  79. tc: c.tc,
  80. }
  81. }
  82. // Get invokes an RPC to retrieve the HMAC key referenced by the
  83. // HMACKeyHandle's accessID.
  84. //
  85. // Options such as UserProjectForHMACKeys can be used to set the
  86. // userProject to be billed against for operations.
  87. func (hkh *HMACKeyHandle) Get(ctx context.Context, opts ...HMACKeyOption) (*HMACKey, error) {
  88. desc := new(hmacKeyDesc)
  89. for _, opt := range opts {
  90. opt.withHMACKeyDesc(desc)
  91. }
  92. o := makeStorageOpts(true, hkh.retry, desc.userProjectID)
  93. hk, err := hkh.tc.GetHMACKey(ctx, hkh.projectID, hkh.accessID, o...)
  94. return hk, err
  95. }
  96. // Delete invokes an RPC to delete the key referenced by accessID, on Google Cloud Storage.
  97. // Only inactive HMAC keys can be deleted.
  98. // After deletion, a key cannot be used to authenticate requests.
  99. func (hkh *HMACKeyHandle) Delete(ctx context.Context, opts ...HMACKeyOption) error {
  100. desc := new(hmacKeyDesc)
  101. for _, opt := range opts {
  102. opt.withHMACKeyDesc(desc)
  103. }
  104. o := makeStorageOpts(true, hkh.retry, desc.userProjectID)
  105. return hkh.tc.DeleteHMACKey(ctx, hkh.projectID, hkh.accessID, o...)
  106. }
  107. func toHMACKeyFromRaw(hk *raw.HmacKey, updatedTimeCanBeNil bool) (*HMACKey, error) {
  108. hkmd := hk.Metadata
  109. if hkmd == nil {
  110. return nil, errors.New("field Metadata cannot be nil")
  111. }
  112. createdTime, err := time.Parse(time.RFC3339, hkmd.TimeCreated)
  113. if err != nil {
  114. return nil, fmt.Errorf("field CreatedTime: %w", err)
  115. }
  116. updatedTime, err := time.Parse(time.RFC3339, hkmd.Updated)
  117. if err != nil && !updatedTimeCanBeNil {
  118. return nil, fmt.Errorf("field UpdatedTime: %w", err)
  119. }
  120. hmKey := &HMACKey{
  121. AccessID: hkmd.AccessId,
  122. Secret: hk.Secret,
  123. Etag: hkmd.Etag,
  124. ID: hkmd.Id,
  125. State: HMACState(hkmd.State),
  126. ProjectID: hkmd.ProjectId,
  127. CreatedTime: createdTime,
  128. UpdatedTime: updatedTime,
  129. ServiceAccountEmail: hkmd.ServiceAccountEmail,
  130. }
  131. return hmKey, nil
  132. }
  133. func toHMACKeyFromProto(pbmd *storagepb.HmacKeyMetadata) *HMACKey {
  134. if pbmd == nil {
  135. return nil
  136. }
  137. return &HMACKey{
  138. AccessID: pbmd.GetAccessId(),
  139. ID: pbmd.GetId(),
  140. State: HMACState(pbmd.GetState()),
  141. ProjectID: pbmd.GetProject(),
  142. CreatedTime: convertProtoTime(pbmd.GetCreateTime()),
  143. UpdatedTime: convertProtoTime(pbmd.GetUpdateTime()),
  144. ServiceAccountEmail: pbmd.GetServiceAccountEmail(),
  145. }
  146. }
  147. // CreateHMACKey invokes an RPC for Google Cloud Storage to create a new HMACKey.
  148. func (c *Client) CreateHMACKey(ctx context.Context, projectID, serviceAccountEmail string, opts ...HMACKeyOption) (*HMACKey, error) {
  149. if projectID == "" {
  150. return nil, errors.New("storage: expecting a non-blank projectID")
  151. }
  152. if serviceAccountEmail == "" {
  153. return nil, errors.New("storage: expecting a non-blank service account email")
  154. }
  155. desc := new(hmacKeyDesc)
  156. for _, opt := range opts {
  157. opt.withHMACKeyDesc(desc)
  158. }
  159. o := makeStorageOpts(false, c.retry, desc.userProjectID)
  160. hk, err := c.tc.CreateHMACKey(ctx, projectID, serviceAccountEmail, o...)
  161. return hk, err
  162. }
  163. // HMACKeyAttrsToUpdate defines the attributes of an HMACKey that will be updated.
  164. type HMACKeyAttrsToUpdate struct {
  165. // State is required and must be either StateActive or StateInactive.
  166. State HMACState
  167. // Etag is an optional field and it is the HTTP/1.1 Entity tag.
  168. Etag string
  169. }
  170. // Update mutates the HMACKey referred to by accessID.
  171. func (h *HMACKeyHandle) Update(ctx context.Context, au HMACKeyAttrsToUpdate, opts ...HMACKeyOption) (*HMACKey, error) {
  172. if au.State != Active && au.State != Inactive {
  173. return nil, fmt.Errorf("storage: invalid state %q for update, must be either %q or %q", au.State, Active, Inactive)
  174. }
  175. desc := new(hmacKeyDesc)
  176. for _, opt := range opts {
  177. opt.withHMACKeyDesc(desc)
  178. }
  179. isIdempotent := len(au.Etag) > 0
  180. o := makeStorageOpts(isIdempotent, h.retry, desc.userProjectID)
  181. hk, err := h.tc.UpdateHMACKey(ctx, h.projectID, desc.forServiceAccountEmail, h.accessID, &au, o...)
  182. return hk, err
  183. }
  184. // An HMACKeysIterator is an iterator over HMACKeys.
  185. //
  186. // Note: This iterator is not safe for concurrent operations without explicit synchronization.
  187. type HMACKeysIterator struct {
  188. ctx context.Context
  189. raw *raw.ProjectsHmacKeysService
  190. projectID string
  191. hmacKeys []*HMACKey
  192. pageInfo *iterator.PageInfo
  193. nextFunc func() error
  194. index int
  195. desc hmacKeyDesc
  196. retry *retryConfig
  197. }
  198. // ListHMACKeys returns an iterator for listing HMACKeys.
  199. //
  200. // Note: This iterator is not safe for concurrent operations without explicit synchronization.
  201. func (c *Client) ListHMACKeys(ctx context.Context, projectID string, opts ...HMACKeyOption) *HMACKeysIterator {
  202. desc := new(hmacKeyDesc)
  203. for _, opt := range opts {
  204. opt.withHMACKeyDesc(desc)
  205. }
  206. o := makeStorageOpts(true, c.retry, desc.userProjectID)
  207. return c.tc.ListHMACKeys(ctx, projectID, desc.forServiceAccountEmail, desc.showDeletedKeys, o...)
  208. }
  209. // Next returns the next result. Its second return value is iterator.Done if
  210. // there are no more results. Once Next returns iterator.Done, all subsequent
  211. // calls will return iterator.Done.
  212. //
  213. // Note: This iterator is not safe for concurrent operations without explicit synchronization.
  214. func (it *HMACKeysIterator) Next() (*HMACKey, error) {
  215. if err := it.nextFunc(); err != nil {
  216. return nil, err
  217. }
  218. key := it.hmacKeys[it.index]
  219. it.index++
  220. return key, nil
  221. }
  222. // PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
  223. //
  224. // Note: This iterator is not safe for concurrent operations without explicit synchronization.
  225. func (it *HMACKeysIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }
  226. func (it *HMACKeysIterator) fetch(pageSize int, pageToken string) (token string, err error) {
  227. // TODO: Remove fetch method upon integration. This method is internalized into
  228. // httpStorageClient.ListHMACKeys() as it is the only caller.
  229. call := it.raw.List(it.projectID)
  230. setClientHeader(call.Header())
  231. if pageToken != "" {
  232. call = call.PageToken(pageToken)
  233. }
  234. if it.desc.showDeletedKeys {
  235. call = call.ShowDeletedKeys(true)
  236. }
  237. if it.desc.userProjectID != "" {
  238. call = call.UserProject(it.desc.userProjectID)
  239. }
  240. if it.desc.forServiceAccountEmail != "" {
  241. call = call.ServiceAccountEmail(it.desc.forServiceAccountEmail)
  242. }
  243. if pageSize > 0 {
  244. call = call.MaxResults(int64(pageSize))
  245. }
  246. var resp *raw.HmacKeysMetadata
  247. err = run(it.ctx, func(ctx context.Context) error {
  248. resp, err = call.Context(ctx).Do()
  249. return err
  250. }, it.retry, true)
  251. if err != nil {
  252. return "", err
  253. }
  254. for _, metadata := range resp.Items {
  255. hk := &raw.HmacKey{
  256. Metadata: metadata,
  257. }
  258. hkey, err := toHMACKeyFromRaw(hk, true)
  259. if err != nil {
  260. return "", err
  261. }
  262. it.hmacKeys = append(it.hmacKeys, hkey)
  263. }
  264. return resp.NextPageToken, nil
  265. }
  266. type hmacKeyDesc struct {
  267. forServiceAccountEmail string
  268. showDeletedKeys bool
  269. userProjectID string
  270. }
  271. // HMACKeyOption configures the behavior of HMACKey related methods and actions.
  272. type HMACKeyOption interface {
  273. withHMACKeyDesc(*hmacKeyDesc)
  274. }
  275. type hmacKeyDescFunc func(*hmacKeyDesc)
  276. func (hkdf hmacKeyDescFunc) withHMACKeyDesc(hkd *hmacKeyDesc) {
  277. hkdf(hkd)
  278. }
  279. // ForHMACKeyServiceAccountEmail returns HMAC Keys that are
  280. // associated with the email address of a service account in the project.
  281. //
  282. // Only one service account email can be used as a filter, so if multiple
  283. // of these options are applied, the last email to be set will be used.
  284. func ForHMACKeyServiceAccountEmail(serviceAccountEmail string) HMACKeyOption {
  285. return hmacKeyDescFunc(func(hkd *hmacKeyDesc) {
  286. hkd.forServiceAccountEmail = serviceAccountEmail
  287. })
  288. }
  289. // ShowDeletedHMACKeys will also list keys whose state is "DELETED".
  290. func ShowDeletedHMACKeys() HMACKeyOption {
  291. return hmacKeyDescFunc(func(hkd *hmacKeyDesc) {
  292. hkd.showDeletedKeys = true
  293. })
  294. }
  295. // UserProjectForHMACKeys will bill the request against userProjectID
  296. // if userProjectID is non-empty.
  297. //
  298. // Note: This is a noop right now and only provided for API compatibility.
  299. func UserProjectForHMACKeys(userProjectID string) HMACKeyOption {
  300. return hmacKeyDescFunc(func(hkd *hmacKeyDesc) {
  301. hkd.userProjectID = userProjectID
  302. })
  303. }