| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- /*
- Copyright © The ESO Authors
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- https://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package doppler
- import (
- "context"
- "encoding/json"
- "fmt"
- "os"
- "time"
- typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
- esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
- "github.com/external-secrets/external-secrets/runtime/oidc"
- )
- const dopplerOIDCPath = "/v3/auth/oidc"
- // OIDCTokenManager manages OIDC token exchange with Doppler.
- // It embeds the shared BaseTokenManager and implements the TokenExchanger interface.
- type OIDCTokenManager struct {
- *oidc.BaseTokenManager
- identity string
- }
- // NewOIDCTokenManager creates a new OIDCTokenManager for handling Doppler OIDC authentication.
- func NewOIDCTokenManager(
- corev1 typedcorev1.CoreV1Interface,
- store *esv1.DopplerProvider,
- namespace string,
- storeKind string,
- storeName string,
- ) *OIDCTokenManager {
- if store == nil || store.Auth == nil || store.Auth.OIDCConfig == nil {
- return nil
- }
- oidcAuth := store.Auth.OIDCConfig
- baseURL := "https://api.doppler.com"
- if customURL := os.Getenv(customBaseURLEnvVar); customURL != "" {
- baseURL = customURL
- }
- // Resource-specific audience binds the SA token to a specific
- // SecretStore/ClusterSecretStore, preventing token reuse across stores.
- var resourceAudience string
- if storeKind == esv1.ClusterSecretStoreKind {
- resourceAudience = fmt.Sprintf("clusterSecretStore:%s", storeName)
- } else {
- resourceAudience = fmt.Sprintf("secretStore:%s:%s", namespace, storeName)
- }
- btm := oidc.NewBaseTokenManager(corev1, namespace, storeKind, baseURL, oidcAuth.ServiceAccountRef)
- btm.ExtraAudiences = []string{resourceAudience}
- btm.ExpirationSeconds = oidcAuth.ExpirationSeconds
- manager := &OIDCTokenManager{
- identity: oidcAuth.Identity,
- BaseTokenManager: btm,
- }
- manager.Exchanger = manager
- return manager
- }
- // ExchangeToken exchanges a ServiceAccount token for a Doppler API token.
- func (m *OIDCTokenManager) ExchangeToken(ctx context.Context, saToken string) (string, time.Time, error) {
- url := m.BaseURL + dopplerOIDCPath
- requestBody := map[string]string{
- "identity": m.identity,
- "token": saToken,
- }
- body, err := oidc.PostJSONRequest(ctx, url, requestBody, "Doppler")
- if err != nil {
- return "", time.Time{}, err
- }
- var response struct {
- Success bool `json:"success"`
- Token string `json:"token"`
- ExpiresAt string `json:"expires_at"`
- }
- if err := json.Unmarshal(body, &response); err != nil {
- return "", time.Time{}, fmt.Errorf("failed to parse response: %w", err)
- }
- if !response.Success {
- return "", time.Time{}, fmt.Errorf("Doppler OIDC auth failed: %s", string(body))
- }
- expiresAt, err := time.Parse(time.RFC3339, response.ExpiresAt)
- if err != nil {
- return "", time.Time{}, fmt.Errorf("failed to parse expiration time: %w", err)
- }
- return response.Token, expiresAt, nil
- }
|