github.com/webonyx/up@v0.7.4-0.20180808230834-91b94e551323/internal/userconfig/userconfig.go (about)

     1  // Package userconfig provides user machine-level configuration.
     2  package userconfig
     3  
     4  import (
     5  	"encoding/base64"
     6  	"encoding/json"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  
    11  	"github.com/apex/up/internal/util"
    12  	"github.com/mitchellh/go-homedir"
    13  	"github.com/pkg/errors"
    14  )
    15  
    16  var (
    17  	// configDir is the dir name where up config is stored relative to HOME.
    18  	configDir = ".up"
    19  
    20  	// envName is the environment variable which can be used to store
    21  	// Up's configuration, primarily for continuous integration.
    22  	envName = "UP_CONFIG"
    23  )
    24  
    25  // Team is the user configuration for a given team.
    26  type Team struct {
    27  	// ID is the team identifier.
    28  	ID string `json:"team"`
    29  
    30  	// Email is the user's email.
    31  	Email string `json:"email"`
    32  
    33  	// Token is the access token.
    34  	Token string `json:"token"`
    35  }
    36  
    37  // IsPersonal returns true if it is a personal team.
    38  func (t *Team) IsPersonal() bool {
    39  	return t.Email == t.ID
    40  }
    41  
    42  // Config is the user configuration.
    43  type Config struct {
    44  	// Team is the active team.
    45  	Team string `json:"team"`
    46  
    47  	// Teams is the user's active teams.
    48  	Teams map[string]*Team `json:"teams"`
    49  }
    50  
    51  // initTeams inits the map.
    52  func (c *Config) initTeams() {
    53  	if c.Teams == nil {
    54  		c.Teams = make(map[string]*Team)
    55  	}
    56  }
    57  
    58  // AddTeam adds or replaces the given team.
    59  func (c *Config) AddTeam(t *Team) {
    60  	c.initTeams()
    61  	c.Teams[t.ID] = t
    62  }
    63  
    64  // GetTeams returns a list of teams.
    65  func (c *Config) GetTeams() (teams []*Team) {
    66  	for _, t := range c.Teams {
    67  		teams = append(teams, t)
    68  	}
    69  	return
    70  }
    71  
    72  // GetTeam returns a team by id or nil
    73  func (c *Config) GetTeam(id string) *Team {
    74  	return c.Teams[id]
    75  }
    76  
    77  // GetActiveTeam returns the active team.
    78  func (c *Config) GetActiveTeam() *Team {
    79  	return c.GetTeam(c.Team)
    80  }
    81  
    82  // Authenticated returns true if the user has an active team.
    83  func (c *Config) Authenticated() bool {
    84  	return c.GetActiveTeam() != nil
    85  }
    86  
    87  // Require requires authentication and returns the active team.
    88  func Require() (*Team, error) {
    89  	var c Config
    90  
    91  	if err := c.Load(); err != nil {
    92  		return nil, errors.Wrap(err, "loading config")
    93  	}
    94  
    95  	if !c.Authenticated() {
    96  		return nil, errors.New("user credentials missing, make sure to `up team login` first")
    97  	}
    98  
    99  	return c.GetActiveTeam(), nil
   100  }
   101  
   102  // Alter config, loading and saving after manipulation.
   103  func Alter(fn func(*Config)) error {
   104  	var config Config
   105  
   106  	if err := config.Load(); err != nil {
   107  		return errors.Wrap(err, "loading")
   108  	}
   109  
   110  	fn(&config)
   111  
   112  	if err := config.Save(); err != nil {
   113  		return errors.Wrap(err, "saving")
   114  	}
   115  
   116  	return nil
   117  }
   118  
   119  // Load the configuration.
   120  func (c *Config) Load() error {
   121  	path, err := c.path()
   122  	if err != nil {
   123  		return errors.Wrap(err, "getting path")
   124  	}
   125  
   126  	// env
   127  	if s := os.Getenv(envName); s != "" {
   128  		// if not JSON, check for base64 encoding
   129  		if !util.IsJSON(s) {
   130  			decoded, err := base64.StdEncoding.DecodeString(s)
   131  			if err != nil {
   132  				return errors.Wrap(err, "decoding base64")
   133  			}
   134  			s = string(decoded)
   135  		}
   136  
   137  		if err := json.Unmarshal([]byte(s), &c); err != nil {
   138  			return errors.Wrap(err, "unmarshaling")
   139  		}
   140  		return nil
   141  	}
   142  
   143  	// file
   144  	b, err := ioutil.ReadFile(path)
   145  
   146  	if os.IsNotExist(err) {
   147  		return nil
   148  	}
   149  
   150  	if err != nil {
   151  		return errors.Wrap(err, "reading")
   152  	}
   153  
   154  	if err := json.Unmarshal(b, c); err != nil {
   155  		return errors.Wrap(err, "unmarshaling")
   156  	}
   157  
   158  	return nil
   159  }
   160  
   161  // Save the configuration.
   162  func (c *Config) Save() error {
   163  	b, err := json.MarshalIndent(c, "", " ")
   164  	if err != nil {
   165  		return errors.Wrap(err, "marshaling")
   166  	}
   167  
   168  	path, err := c.path()
   169  	if err != nil {
   170  		return errors.Wrap(err, "getting path")
   171  	}
   172  
   173  	if err := ioutil.WriteFile(path, b, 0755); err != nil {
   174  		return errors.Wrap(err, "writing")
   175  	}
   176  
   177  	return nil
   178  }
   179  
   180  // path returns the path and sets up dir if necessary.
   181  func (c *Config) path() (string, error) {
   182  	home, err := homedir.Dir()
   183  	if err != nil {
   184  		return "", errors.Wrap(err, "homedir")
   185  	}
   186  
   187  	dir := filepath.Join(home, configDir)
   188  	if err := os.MkdirAll(dir, 0755); err != nil {
   189  		return "", errors.Wrap(err, "mkdir")
   190  	}
   191  
   192  	path := filepath.Join(dir, "config.json")
   193  	return path, nil
   194  }