2
0

gcp.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. package integrations
  2. import (
  3. "context"
  4. "encoding/json"
  5. "github.com/porter-dev/porter/api/types"
  6. "golang.org/x/oauth2"
  7. "golang.org/x/oauth2/google"
  8. "gorm.io/gorm"
  9. )
  10. // GCPIntegration is an auth mechanism that uses a GCP service account to
  11. // authenticate
  12. type GCPIntegration struct {
  13. gorm.Model
  14. // The id of the user that linked this auth mechanism
  15. UserID uint `json:"user_id"`
  16. // The project that this integration belongs to
  17. ProjectID uint `json:"project_id"`
  18. // The GCP project id where the service account for this auth mechanism persists
  19. GCPProjectID string `json:"gcp_project_id"`
  20. // The GCP service account email for this credential
  21. GCPSAEmail string `json:"gcp_sa_email"`
  22. // The GCP user email that linked this service account
  23. GCPUserEmail string `json:"gcp-user-email"`
  24. // The GCP region, which may or may not be used by the integration
  25. GCPRegion string `json:"gcp_region"`
  26. // ------------------------------------------------------------------
  27. // All fields encrypted before storage.
  28. // ------------------------------------------------------------------
  29. // KeyData for a service account for GCP connectors
  30. GCPKeyData []byte `json:"gcp_key_data"`
  31. }
  32. func (g *GCPIntegration) ToGCPIntegrationType() *types.GCPIntegration {
  33. return &types.GCPIntegration{
  34. CreatedAt: g.CreatedAt,
  35. ID: g.ID,
  36. UserID: g.UserID,
  37. ProjectID: g.ProjectID,
  38. GCPProjectID: g.GCPProjectID,
  39. GCPSAEmail: g.GCPSAEmail,
  40. }
  41. }
  42. // GetBearerToken retrieves a bearer token for a GCP account
  43. func (g *GCPIntegration) GetBearerToken(
  44. getTokenCache GetTokenCacheFunc,
  45. setTokenCache SetTokenCacheFunc,
  46. scopes ...string,
  47. ) (*oauth2.Token, error) {
  48. cache, err := getTokenCache()
  49. // check the token cache for a non-expired token
  50. if cache != nil {
  51. if tok := cache.Token; err == nil && !cache.IsExpired() && len(tok) > 0 {
  52. return &oauth2.Token{
  53. AccessToken: string(cache.Token),
  54. Expiry: cache.Expiry,
  55. }, nil
  56. }
  57. }
  58. creds, err := google.CredentialsFromJSON(
  59. context.Background(),
  60. g.GCPKeyData,
  61. scopes...,
  62. )
  63. if err != nil {
  64. return nil, err
  65. }
  66. tok, err := creds.TokenSource.Token()
  67. if err != nil {
  68. return nil, err
  69. }
  70. // update the token cache
  71. err = setTokenCache(tok.AccessToken, tok.Expiry)
  72. return tok, err
  73. }
  74. // credentialsFile is the unmarshalled representation of a GCP credentials file.
  75. // Source; golang.org/x/oauth2/google
  76. type credentialsFile struct {
  77. Type string `json:"type"` // serviceAccountKey or userCredentialsKey
  78. // Service Account fields
  79. ClientEmail string `json:"client_email"`
  80. PrivateKeyID string `json:"private_key_id"`
  81. PrivateKey string `json:"private_key"`
  82. TokenURL string `json:"token_uri"`
  83. ProjectID string `json:"project_id"`
  84. // User Credential fields
  85. // (These typically come from gcloud auth.)
  86. ClientSecret string `json:"client_secret"`
  87. ClientID string `json:"client_id"`
  88. RefreshToken string `json:"refresh_token"`
  89. // External Account fields
  90. Audience string `json:"audience"`
  91. SubjectTokenType string `json:"subject_token_type"`
  92. TokenURLExternal string `json:"token_url"`
  93. TokenInfoURL string `json:"token_info_url"`
  94. ServiceAccountImpersonationURL string `json:"service_account_impersonation_url"`
  95. // CredentialSource externalaccount.CredentialSource `json:"credential_source"`
  96. QuotaProjectID string `json:"quota_project_id"`
  97. }
  98. func GCPProjectIDFromJSON(jsonData []byte) (string, error) {
  99. var f credentialsFile
  100. if err := json.Unmarshal(jsonData, &f); err != nil {
  101. return "", err
  102. }
  103. return f.ProjectID, nil
  104. }
  105. // PopulateGCPMetadata uses the credentials file to get the GCP SA name and
  106. // project ID, and attaches it to the GCP credential
  107. func (g *GCPIntegration) PopulateGCPMetadata() {
  108. var f credentialsFile
  109. if err := json.Unmarshal(g.GCPKeyData, &f); err != nil {
  110. return
  111. }
  112. g.GCPProjectID = f.ProjectID
  113. g.GCPSAEmail = f.ClientEmail
  114. }