config.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. package oauth
  2. import (
  3. "context"
  4. "crypto/rand"
  5. "encoding/base64"
  6. "time"
  7. "github.com/porter-dev/porter/internal/models/integrations"
  8. "github.com/porter-dev/porter/internal/repository"
  9. "golang.org/x/oauth2"
  10. )
  11. type Config struct {
  12. ClientID string
  13. ClientSecret string
  14. Scopes []string
  15. BaseURL string
  16. }
  17. // GithubAppConf is standard oauth2 config but it need to keeps track of the app name and webhook secret
  18. type GithubAppConf struct {
  19. AppName string
  20. WebhookSecret string
  21. SecretPath string
  22. AppID int64
  23. oauth2.Config
  24. }
  25. func NewGithubClient(cfg *Config) *oauth2.Config {
  26. return &oauth2.Config{
  27. ClientID: cfg.ClientID,
  28. ClientSecret: cfg.ClientSecret,
  29. Endpoint: oauth2.Endpoint{
  30. AuthURL: "https://github.com/login/oauth/authorize",
  31. TokenURL: "https://github.com/login/oauth/access_token",
  32. },
  33. RedirectURL: cfg.BaseURL + "/api/oauth/github/callback",
  34. Scopes: cfg.Scopes,
  35. }
  36. }
  37. func NewGithubAppClient(cfg *Config, name string, secret string, secretPath string, appID int64) *GithubAppConf {
  38. return &GithubAppConf{
  39. AppName: name,
  40. WebhookSecret: secret,
  41. SecretPath: secretPath,
  42. AppID: appID,
  43. Config: oauth2.Config{
  44. ClientID: cfg.ClientID,
  45. ClientSecret: cfg.ClientSecret,
  46. Endpoint: oauth2.Endpoint{
  47. AuthURL: "https://github.com/login/oauth/authorize",
  48. TokenURL: "https://github.com/login/oauth/access_token",
  49. },
  50. RedirectURL: cfg.BaseURL + "/api/oauth/github-app/callback",
  51. Scopes: cfg.Scopes,
  52. },
  53. }
  54. }
  55. func NewDigitalOceanClient(cfg *Config) *oauth2.Config {
  56. return &oauth2.Config{
  57. ClientID: cfg.ClientID,
  58. ClientSecret: cfg.ClientSecret,
  59. Endpoint: oauth2.Endpoint{
  60. AuthURL: "https://cloud.digitalocean.com/v1/oauth/authorize",
  61. TokenURL: "https://cloud.digitalocean.com/v1/oauth/token",
  62. },
  63. RedirectURL: cfg.BaseURL + "/api/oauth/digitalocean/callback",
  64. Scopes: cfg.Scopes,
  65. }
  66. }
  67. func NewGoogleClient(cfg *Config) *oauth2.Config {
  68. return &oauth2.Config{
  69. ClientID: cfg.ClientID,
  70. ClientSecret: cfg.ClientSecret,
  71. Endpoint: oauth2.Endpoint{
  72. AuthURL: "https://accounts.google.com/o/oauth2/v2/auth",
  73. TokenURL: "https://oauth2.googleapis.com/token",
  74. },
  75. RedirectURL: cfg.BaseURL + "/api/oauth/google/callback",
  76. Scopes: cfg.Scopes,
  77. }
  78. }
  79. func NewSlackClient(cfg *Config) *oauth2.Config {
  80. return &oauth2.Config{
  81. ClientID: cfg.ClientID,
  82. ClientSecret: cfg.ClientSecret,
  83. Endpoint: oauth2.Endpoint{
  84. AuthURL: "https://slack.com/oauth/v2/authorize",
  85. TokenURL: "https://slack.com/api/oauth.v2.access",
  86. },
  87. RedirectURL: cfg.BaseURL + "/api/oauth/slack/callback",
  88. Scopes: cfg.Scopes,
  89. }
  90. }
  91. func CreateRandomState() string {
  92. b := make([]byte, 16)
  93. rand.Read(b)
  94. state := base64.URLEncoding.EncodeToString(b)
  95. return state
  96. }
  97. // MakeUpdateOAuthIntegrationTokenFunction creates a function to be passed to GetAccessToken that updates the OauthIntegration
  98. // if it needs to be updated
  99. func MakeUpdateOAuthIntegrationTokenFunction(
  100. o *integrations.OAuthIntegration,
  101. repo repository.Repository) func(accessToken []byte, refreshToken []byte, expiry time.Time) error {
  102. return func(accessToken []byte, refreshToken []byte, expiry time.Time) error {
  103. o.AccessToken = accessToken
  104. o.RefreshToken = refreshToken
  105. o.Expiry = expiry
  106. _, err := repo.OAuthIntegration.UpdateOAuthIntegration(o)
  107. return err
  108. }
  109. }
  110. // MakeUpdateGithubAppOauthIntegrationFunction creates a function to be passed to GetAccessToken that updates the GithubAppOauthIntegration
  111. // if it needs to be updated
  112. func MakeUpdateGithubAppOauthIntegrationFunction(
  113. o *integrations.GithubAppOAuthIntegration,
  114. repo repository.Repository) func(accessToken []byte, refreshToken []byte, expiry time.Time) error {
  115. return func(accessToken []byte, refreshToken []byte, expiry time.Time) error {
  116. o.AccessToken = accessToken
  117. o.RefreshToken = refreshToken
  118. o.Expiry = expiry
  119. _, err := repo.GithubAppOAuthIntegration.UpdateGithubAppOauthIntegration(o)
  120. return err
  121. }
  122. }
  123. // GetAccessToken retrieves an access token for a given client. It updates the
  124. // access token in the DB if necessary
  125. func GetAccessToken(
  126. prevToken integrations.SharedOAuthModel,
  127. conf *oauth2.Config,
  128. updateToken func(accessToken []byte, refreshToken []byte, expiry time.Time) error,
  129. ) (string, *time.Time, error) {
  130. tokSource := conf.TokenSource(context.TODO(), &oauth2.Token{
  131. AccessToken: string(prevToken.AccessToken),
  132. RefreshToken: string(prevToken.RefreshToken),
  133. TokenType: "Bearer",
  134. Expiry: prevToken.Expiry,
  135. })
  136. token, err := tokSource.Token()
  137. if err != nil {
  138. return "", nil, err
  139. }
  140. if token.AccessToken != string(prevToken.AccessToken) {
  141. err := updateToken([]byte(token.AccessToken), []byte(token.RefreshToken), token.Expiry)
  142. if err != nil {
  143. return "", nil, err
  144. }
  145. }
  146. return token.AccessToken, &token.Expiry, nil
  147. }