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

     1  package localconfig
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/user"
     7  	"path"
     8  
     9  	"github.com/dgrijalva/jwt-go/v4"
    10  
    11  	configUtil "github.com/argoproj/argo-cd/util/config"
    12  )
    13  
    14  // LocalConfig is a local Argo CD config file
    15  type LocalConfig struct {
    16  	CurrentContext string       `json:"current-context"`
    17  	Contexts       []ContextRef `json:"contexts"`
    18  	Servers        []Server     `json:"servers"`
    19  	Users          []User       `json:"users"`
    20  }
    21  
    22  // ContextRef is a reference to a Server and User for an API client
    23  type ContextRef struct {
    24  	Name   string `json:"name"`
    25  	Server string `json:"server"`
    26  	User   string `json:"user"`
    27  }
    28  
    29  // Context is the resolved Server and User objects resolved
    30  type Context struct {
    31  	Name   string
    32  	Server Server
    33  	User   User
    34  }
    35  
    36  // Server contains Argo CD server information
    37  type Server struct {
    38  	// Server is the Argo CD server address
    39  	Server string `json:"server"`
    40  	// Insecure indicates to connect to the server over TLS insecurely
    41  	Insecure bool `json:"insecure,omitempty"`
    42  	// GRPCWeb indicates to connect to the server using gRPC Web protocol
    43  	GRPCWeb bool `json:"grpc-web,omitempty"`
    44  	// GRPCWebRootPath indicates to connect to the server using gRPC Web protocol with this root path
    45  	GRPCWebRootPath string `json:"grpc-web-root-path"`
    46  	// CACertificateAuthorityData is the base64 string of a PEM encoded certificate
    47  	// TODO: not yet implemented
    48  	CACertificateAuthorityData string `json:"certificate-authority-data,omitempty"`
    49  	// ClientCertificateData is the base64 string of a PEM encoded certificate used to authenticate the client
    50  	ClientCertificateData string `json:"client-certificate-data,omitempty"`
    51  	// ClientCertificateKeyData is the base64 string of a PEM encoded private key of the client certificate
    52  	ClientCertificateKeyData string `json:"client-certificate-key-data,omitempty"`
    53  	// PlainText indicates to connect with TLS disabled
    54  	PlainText bool `json:"plain-text,omitempty"`
    55  }
    56  
    57  // User contains user authentication information
    58  type User struct {
    59  	Name         string `json:"name"`
    60  	AuthToken    string `json:"auth-token,omitempty"`
    61  	RefreshToken string `json:"refresh-token,omitempty"`
    62  }
    63  
    64  // Claims returns the standard claims from the JWT claims
    65  func (u *User) Claims() (*jwt.StandardClaims, error) {
    66  	parser := &jwt.Parser{
    67  		ValidationHelper: jwt.NewValidationHelper(jwt.WithoutClaimsValidation(), jwt.WithoutAudienceValidation()),
    68  	}
    69  	claims := jwt.StandardClaims{}
    70  	_, _, err := parser.ParseUnverified(u.AuthToken, &claims)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	return &claims, nil
    75  }
    76  
    77  // ReadLocalConfig loads up the local configuration file. Returns nil if config does not exist
    78  func ReadLocalConfig(path string) (*LocalConfig, error) {
    79  	var err error
    80  	var config LocalConfig
    81  	err = configUtil.UnmarshalLocalFile(path, &config)
    82  	if os.IsNotExist(err) {
    83  		return nil, nil
    84  	}
    85  	err = ValidateLocalConfig(config)
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  	return &config, nil
    90  }
    91  
    92  func ValidateLocalConfig(config LocalConfig) error {
    93  	if config.CurrentContext == "" {
    94  		return nil
    95  	}
    96  	if _, err := config.ResolveContext(config.CurrentContext); err != nil {
    97  		return fmt.Errorf("Local config invalid: %s", err)
    98  	}
    99  	return nil
   100  }
   101  
   102  // WriteLocalConfig writes a new local configuration file.
   103  func WriteLocalConfig(config LocalConfig, configPath string) error {
   104  	err := os.MkdirAll(path.Dir(configPath), os.ModePerm)
   105  	if err != nil {
   106  		return err
   107  	}
   108  	return configUtil.MarshalLocalYAMLFile(configPath, config)
   109  }
   110  
   111  func DeleteLocalConfig(configPath string) error {
   112  	_, err := os.Stat(configPath)
   113  	if os.IsNotExist(err) {
   114  		return err
   115  	}
   116  	return os.Remove(configPath)
   117  }
   118  
   119  // ResolveContext resolves the specified context. If unspecified, resolves the current context
   120  func (l *LocalConfig) ResolveContext(name string) (*Context, error) {
   121  	if name == "" {
   122  		if l.CurrentContext == "" {
   123  			return nil, fmt.Errorf("Local config: current-context unset")
   124  		}
   125  		name = l.CurrentContext
   126  	}
   127  	for _, ctx := range l.Contexts {
   128  		if ctx.Name == name {
   129  			server, err := l.GetServer(ctx.Server)
   130  			if err != nil {
   131  				return nil, err
   132  			}
   133  			user, err := l.GetUser(ctx.User)
   134  			if err != nil {
   135  				return nil, err
   136  			}
   137  			return &Context{
   138  				Name:   ctx.Name,
   139  				Server: *server,
   140  				User:   *user,
   141  			}, nil
   142  		}
   143  	}
   144  	return nil, fmt.Errorf("Context '%s' undefined", name)
   145  }
   146  
   147  func (l *LocalConfig) GetServer(name string) (*Server, error) {
   148  	for _, s := range l.Servers {
   149  		if s.Server == name {
   150  			return &s, nil
   151  		}
   152  	}
   153  	return nil, fmt.Errorf("Server '%s' undefined", name)
   154  }
   155  
   156  func (l *LocalConfig) UpsertServer(server Server) {
   157  	for i, s := range l.Servers {
   158  		if s.Server == server.Server {
   159  			l.Servers[i] = server
   160  			return
   161  		}
   162  	}
   163  	l.Servers = append(l.Servers, server)
   164  }
   165  
   166  // Returns true if server was removed successfully
   167  func (l *LocalConfig) RemoveServer(serverName string) bool {
   168  	for i, s := range l.Servers {
   169  		if s.Server == serverName {
   170  			l.Servers = append(l.Servers[:i], l.Servers[i+1:]...)
   171  			return true
   172  		}
   173  	}
   174  	return false
   175  }
   176  
   177  func (l *LocalConfig) GetUser(name string) (*User, error) {
   178  	for _, u := range l.Users {
   179  		if u.Name == name {
   180  			return &u, nil
   181  		}
   182  	}
   183  	return nil, fmt.Errorf("User '%s' undefined", name)
   184  }
   185  
   186  func (l *LocalConfig) UpsertUser(user User) {
   187  	for i, u := range l.Users {
   188  		if u.Name == user.Name {
   189  			l.Users[i] = user
   190  			return
   191  		}
   192  	}
   193  	l.Users = append(l.Users, user)
   194  }
   195  
   196  // Returns true if user was removed successfully
   197  func (l *LocalConfig) RemoveUser(serverName string) bool {
   198  	for i, u := range l.Users {
   199  		if u.Name == serverName {
   200  			l.Users = append(l.Users[:i], l.Users[i+1:]...)
   201  			return true
   202  		}
   203  	}
   204  	return false
   205  }
   206  
   207  // Returns true if user was removed successfully
   208  func (l *LocalConfig) RemoveToken(serverName string) bool {
   209  	for i, u := range l.Users {
   210  		if u.Name == serverName {
   211  			l.Users[i].RefreshToken = ""
   212  			l.Users[i].AuthToken = ""
   213  			return true
   214  		}
   215  	}
   216  	return false
   217  }
   218  
   219  func (l *LocalConfig) UpsertContext(context ContextRef) {
   220  	for i, c := range l.Contexts {
   221  		if c.Name == context.Name {
   222  			l.Contexts[i] = context
   223  			return
   224  		}
   225  	}
   226  	l.Contexts = append(l.Contexts, context)
   227  }
   228  
   229  // Returns true if context was removed successfully
   230  func (l *LocalConfig) RemoveContext(serverName string) (string, bool) {
   231  	for i, c := range l.Contexts {
   232  		if c.Name == serverName {
   233  			l.Contexts = append(l.Contexts[:i], l.Contexts[i+1:]...)
   234  			return c.Server, true
   235  		}
   236  	}
   237  	return "", false
   238  }
   239  
   240  func (l *LocalConfig) IsEmpty() bool {
   241  	return len(l.Servers) == 0
   242  }
   243  
   244  // DefaultConfigDir returns the local configuration path for settings such as cached authentication tokens.
   245  func DefaultConfigDir() (string, error) {
   246  	homeDir := os.Getenv("HOME")
   247  	if homeDir == "" {
   248  		usr, err := user.Current()
   249  		if err != nil {
   250  			return "", err
   251  		}
   252  		homeDir = usr.HomeDir
   253  	}
   254  	return path.Join(homeDir, ".argocd"), nil
   255  }
   256  
   257  // DefaultLocalConfigPath returns the local configuration path for settings such as cached authentication tokens.
   258  func DefaultLocalConfigPath() (string, error) {
   259  	dir, err := DefaultConfigDir()
   260  	if err != nil {
   261  		return "", err
   262  	}
   263  	return path.Join(dir, "config"), nil
   264  }