github.com/argoproj/argo-cd@v1.8.7/util/dex/config.go (about)

     1  package dex
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/ghodss/yaml"
     7  
     8  	"github.com/argoproj/argo-cd/common"
     9  	"github.com/argoproj/argo-cd/util/settings"
    10  )
    11  
    12  func GenerateDexConfigYAML(settings *settings.ArgoCDSettings) ([]byte, error) {
    13  	if !settings.IsDexConfigured() {
    14  		return nil, nil
    15  	}
    16  	redirectURL, err := settings.RedirectURL()
    17  	if err != nil {
    18  		return nil, fmt.Errorf("failed to infer redirect url from config: %v", err)
    19  	}
    20  	var dexCfg map[string]interface{}
    21  	err = yaml.Unmarshal([]byte(settings.DexConfig), &dexCfg)
    22  	if err != nil {
    23  		return nil, fmt.Errorf("failed to unmarshal dex.config from configmap: %v", err)
    24  	}
    25  	dexCfg["issuer"] = settings.IssuerURL()
    26  	dexCfg["storage"] = map[string]interface{}{
    27  		"type": "memory",
    28  	}
    29  	dexCfg["web"] = map[string]interface{}{
    30  		"http": "0.0.0.0:5556",
    31  	}
    32  	dexCfg["grpc"] = map[string]interface{}{
    33  		"addr": "0.0.0.0:5557",
    34  	}
    35  	dexCfg["telemetry"] = map[string]interface{}{
    36  		"http": "0.0.0.0:5558",
    37  	}
    38  	dexCfg["oauth2"] = map[string]interface{}{
    39  		"skipApprovalScreen": true,
    40  	}
    41  
    42  	argoCDStaticClient := map[string]interface{}{
    43  		"id":     common.ArgoCDClientAppID,
    44  		"name":   common.ArgoCDClientAppName,
    45  		"secret": settings.DexOAuth2ClientSecret(),
    46  		"redirectURIs": []string{
    47  			redirectURL,
    48  		},
    49  	}
    50  	argoCDCLIStaticClient := map[string]interface{}{
    51  		"id":     common.ArgoCDCLIClientAppID,
    52  		"name":   common.ArgoCDCLIClientAppName,
    53  		"public": true,
    54  		"redirectURIs": []string{
    55  			"http://localhost",
    56  			"http://localhost:8085/auth/callback",
    57  		},
    58  	}
    59  
    60  	staticClients, ok := dexCfg["staticClients"].([]interface{})
    61  	if ok {
    62  		dexCfg["staticClients"] = append([]interface{}{argoCDStaticClient, argoCDCLIStaticClient}, staticClients...)
    63  	} else {
    64  		dexCfg["staticClients"] = []interface{}{argoCDStaticClient, argoCDCLIStaticClient}
    65  	}
    66  
    67  	dexRedirectURL, err := settings.DexRedirectURL()
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	connectors, ok := dexCfg["connectors"].([]interface{})
    72  	if !ok {
    73  		return nil, fmt.Errorf("malformed Dex configuration found")
    74  	}
    75  	for i, connectorIf := range connectors {
    76  		connector, ok := connectorIf.(map[string]interface{})
    77  		if !ok {
    78  			return nil, fmt.Errorf("malformed Dex configuration found")
    79  		}
    80  		connectorType := connector["type"].(string)
    81  		if !needsRedirectURI(connectorType) {
    82  			continue
    83  		}
    84  		connectorCfg, ok := connector["config"].(map[string]interface{})
    85  		if !ok {
    86  			return nil, fmt.Errorf("malformed Dex configuration found")
    87  		}
    88  		connectorCfg["redirectURI"] = dexRedirectURL
    89  		connector["config"] = connectorCfg
    90  		connectors[i] = connector
    91  	}
    92  	dexCfg["connectors"] = connectors
    93  	dexCfg = replaceMapSecrets(dexCfg, settings.Secrets)
    94  	return yaml.Marshal(dexCfg)
    95  }
    96  
    97  // replaceMapSecrets takes a json object and recursively looks for any secret key references in the
    98  // object and replaces the value with the secret value
    99  func replaceMapSecrets(obj map[string]interface{}, secretValues map[string]string) map[string]interface{} {
   100  	newObj := make(map[string]interface{})
   101  	for k, v := range obj {
   102  		switch val := v.(type) {
   103  		case map[string]interface{}:
   104  			newObj[k] = replaceMapSecrets(val, secretValues)
   105  		case []interface{}:
   106  			newObj[k] = replaceListSecrets(val, secretValues)
   107  		case string:
   108  			newObj[k] = settings.ReplaceStringSecret(val, secretValues)
   109  		default:
   110  			newObj[k] = val
   111  		}
   112  	}
   113  	return newObj
   114  }
   115  
   116  func replaceListSecrets(obj []interface{}, secretValues map[string]string) []interface{} {
   117  	newObj := make([]interface{}, len(obj))
   118  	for i, v := range obj {
   119  		switch val := v.(type) {
   120  		case map[string]interface{}:
   121  			newObj[i] = replaceMapSecrets(val, secretValues)
   122  		case []interface{}:
   123  			newObj[i] = replaceListSecrets(val, secretValues)
   124  		case string:
   125  			newObj[i] = settings.ReplaceStringSecret(val, secretValues)
   126  		default:
   127  			newObj[i] = val
   128  		}
   129  	}
   130  	return newObj
   131  }
   132  
   133  // needsRedirectURI returns whether or not the given connector type needs a redirectURI
   134  // Update this list as necessary, as new connectors are added
   135  // https://github.com/dexidp/dex/tree/master/Documentation/connectors
   136  func needsRedirectURI(connectorType string) bool {
   137  	switch connectorType {
   138  	case "oidc", "saml", "microsoft", "linkedin", "gitlab", "github", "bitbucket-cloud", "openshift":
   139  		return true
   140  	}
   141  	return false
   142  }