gcp.go 4.3 KB

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