|
| 1 | +/* |
| 2 | +Copyright 2023 The Flux authors |
| 3 | +
|
| 4 | +Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | +you may not use this file except in compliance with the License. |
| 6 | +You may obtain a copy of the License at |
| 7 | +
|
| 8 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | +
|
| 10 | +Unless required by applicable law or agreed to in writing, software |
| 11 | +distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | +See the License for the specific language governing permissions and |
| 14 | +limitations under the License. |
| 15 | +*/ |
| 16 | + |
| 17 | +package git |
| 18 | + |
| 19 | +import ( |
| 20 | + "context" |
| 21 | + "time" |
| 22 | + |
| 23 | + "github.com/fluxcd/pkg/auth" |
| 24 | + "github.com/fluxcd/pkg/auth/azure" |
| 25 | + "github.com/fluxcd/pkg/auth/gcp" |
| 26 | + "github.com/fluxcd/pkg/auth/github" |
| 27 | +) |
| 28 | + |
| 29 | +const GitHubAccessTokenUsername = "x-access-token" |
| 30 | + |
| 31 | +// Credentials contains the various authentication data needed |
| 32 | +// in order to access a Git repository. |
| 33 | +type Credentials struct { |
| 34 | + Username string `json:"username,omitempty"` |
| 35 | + Password string `json:"password,omitempty"` |
| 36 | + BearerToken string `json:"bearerToken,omitempty"` |
| 37 | +} |
| 38 | + |
| 39 | +// ToSecretData returns the Credentials object in the format |
| 40 | +// of the data found in Kubernetes Generic Secret. |
| 41 | +func (c *Credentials) ToSecretData() map[string][]byte { |
| 42 | + var data map[string][]byte |
| 43 | + |
| 44 | + if c.BearerToken != "" { |
| 45 | + data["bearerToken"] = []byte(c.BearerToken) |
| 46 | + } |
| 47 | + if c.Password != "" { |
| 48 | + data["password"] = []byte(c.Password) |
| 49 | + } |
| 50 | + if c.Username != "" { |
| 51 | + data["username"] = []byte(c.Username) |
| 52 | + } |
| 53 | + return data |
| 54 | +} |
| 55 | + |
| 56 | +// GetCredentials returns authentication credentials for accessing the provided |
| 57 | +// Git repository. |
| 58 | +// If caching is enabled and cacheKey is not blank, the credentials are cached |
| 59 | +// according to the ttl advertised by the Git provider. |
| 60 | +func GetCredentials(ctx context.Context, provider string, authOpts *auth.AuthOptions) (*Credentials, error) { |
| 61 | + var creds Credentials |
| 62 | + |
| 63 | + cache := auth.GetCache() |
| 64 | + if cache != nil && authOpts != nil && authOpts.CacheKey != "" { |
| 65 | + val, found := cache.Get(authOpts.CacheKey) |
| 66 | + if found { |
| 67 | + creds = val.(Credentials) |
| 68 | + return &creds, nil |
| 69 | + } |
| 70 | + } |
| 71 | + |
| 72 | + var expiresIn time.Duration |
| 73 | + switch provider { |
| 74 | + case auth.ProviderAzure: |
| 75 | + var opts []azure.ProviderOptFunc |
| 76 | + if authOpts != nil { |
| 77 | + opts = authOpts.ProviderOptions.AzureOpts |
| 78 | + } |
| 79 | + azureProvider := azure.NewProvider(opts...) |
| 80 | + |
| 81 | + armToken, err := azureProvider.GetResourceManagerToken(ctx) |
| 82 | + if err != nil { |
| 83 | + return nil, err |
| 84 | + } |
| 85 | + creds = Credentials{ |
| 86 | + BearerToken: armToken.Token, |
| 87 | + } |
| 88 | + expiresIn = armToken.ExpiresOn.UTC().Sub(time.Now().UTC()) |
| 89 | + case auth.ProviderGCP: |
| 90 | + var opts []gcp.ProviderOptFunc |
| 91 | + if authOpts != nil { |
| 92 | + opts = authOpts.ProviderOptions.GcpOpts |
| 93 | + } |
| 94 | + gcpProvider := gcp.NewProvider(opts...) |
| 95 | + |
| 96 | + saToken, err := gcpProvider.GetServiceAccountToken(ctx) |
| 97 | + if err != nil { |
| 98 | + return nil, err |
| 99 | + } |
| 100 | + email, err := gcpProvider.GetServiceAccountEmail(ctx) |
| 101 | + if err != nil { |
| 102 | + return nil, err |
| 103 | + } |
| 104 | + |
| 105 | + creds = Credentials{ |
| 106 | + Username: email, |
| 107 | + Password: saToken.AccessToken, |
| 108 | + } |
| 109 | + expiresIn = time.Duration(saToken.ExpiresIn) |
| 110 | + case auth.ProviderGitHub: |
| 111 | + var opts []github.ProviderOptFunc |
| 112 | + if authOpts != nil { |
| 113 | + if authOpts.Secret != nil { |
| 114 | + opts = append(opts, github.WithSecret(*authOpts.Secret)) |
| 115 | + } |
| 116 | + opts = append(opts, authOpts.ProviderOptions.GitHubOpts...) |
| 117 | + } |
| 118 | + |
| 119 | + ghProvider, err := github.NewProvider(opts...) |
| 120 | + if err != nil { |
| 121 | + return nil, err |
| 122 | + } |
| 123 | + |
| 124 | + appToken, err := ghProvider.GetAppToken(ctx) |
| 125 | + if err != nil { |
| 126 | + return nil, err |
| 127 | + } |
| 128 | + creds = Credentials{ |
| 129 | + Username: GitHubAccessTokenUsername, |
| 130 | + Password: appToken.Token, |
| 131 | + } |
| 132 | + expiresIn = appToken.ExpiresIn |
| 133 | + default: |
| 134 | + return nil, nil |
| 135 | + } |
| 136 | + |
| 137 | + if cache != nil && authOpts != nil && authOpts.CacheKey != "" { |
| 138 | + if err := cache.Set(authOpts.CacheKey, creds, expiresIn); err != nil { |
| 139 | + return nil, err |
| 140 | + } |
| 141 | + } |
| 142 | + return &creds, nil |
| 143 | +} |
0 commit comments