github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/skymarshal/skycmd/flags.go (about)

     1  package skycmd
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"reflect"
     8  	"strings"
     9  	"time"
    10  
    11  	flags "github.com/jessevdk/go-flags"
    12  	"github.com/mitchellh/mapstructure"
    13  	"sigs.k8s.io/yaml"
    14  
    15  	"github.com/pf-qiu/concourse/v6/atc"
    16  	"github.com/concourse/flag"
    17  )
    18  
    19  var connectors []*Connector
    20  
    21  func RegisterConnector(connector *Connector) {
    22  	connectors = append(connectors, connector)
    23  }
    24  
    25  func GetConnectors() []*Connector {
    26  	return connectors
    27  }
    28  
    29  func WireConnectors(group *flags.Group) {
    30  	for _, c := range connectors {
    31  		description := fmt.Sprintf("%s (%s)", group.ShortDescription, c.Name())
    32  		connGroup, _ := group.AddGroup(description, description, c.config)
    33  		connGroup.Namespace = c.ID()
    34  	}
    35  }
    36  
    37  func WireTeamConnectors(group *flags.Group) {
    38  	for _, c := range connectors {
    39  		description := fmt.Sprintf("%s (%s)", group.ShortDescription, c.Name())
    40  		connTeamGroup, _ := group.AddGroup(description, description, c.teamConfig)
    41  		connTeamGroup.Namespace = c.ID()
    42  	}
    43  }
    44  
    45  type AuthFlags struct {
    46  	SecureCookies bool              `long:"cookie-secure" description:"Force sending secure flag on http cookies"`
    47  	Expiration    time.Duration     `long:"auth-duration" default:"24h" description:"Length of time for which tokens are valid. Afterwards, users will have to log back in."`
    48  	SigningKey    *flag.PrivateKey  `long:"session-signing-key" required:"true" description:"File containing an RSA private key, used to sign auth tokens."`
    49  	LocalUsers    map[string]string `long:"add-local-user" description:"List of username:password combinations for all your local users. The password can be bcrypted - if so, it must have a minimum cost of 10." value-name:"USERNAME:PASSWORD"`
    50  	Clients       map[string]string `long:"add-client" description:"List of client_id:client_secret combinations" value-name:"CLIENT_ID:CLIENT_SECRET"`
    51  }
    52  
    53  type AuthTeamFlags struct {
    54  	LocalUsers []string  `long:"local-user" description:"A whitelisted local concourse user. These are the users you've added at web startup with the --add-local-user flag." value-name:"USERNAME"`
    55  	Config     flag.File `short:"c" long:"config" description:"Configuration file for specifying team params"`
    56  }
    57  
    58  func (flag *AuthTeamFlags) Format() (atc.TeamAuth, error) {
    59  
    60  	if path := flag.Config.Path(); path != "" {
    61  		return flag.formatFromFile()
    62  
    63  	}
    64  	return flag.formatFromFlags()
    65  
    66  }
    67  
    68  // When formatting from a configuration file we iterate over each connector
    69  // type and create a new instance of the TeamConfig object for each connector.
    70  // These connectors all have their own unique configuration so we need to use
    71  // mapstructure to decode the generic result into a known struct.
    72  
    73  // e.g.
    74  // The github connector has configuration for: users, teams, orgs
    75  // The cf conncetor has configuration for: users, orgs, spaces
    76  
    77  func (flag *AuthTeamFlags) formatFromFile() (atc.TeamAuth, error) {
    78  
    79  	content, err := ioutil.ReadFile(flag.Config.Path())
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	var data struct {
    85  		Roles []map[string]interface{} `json:"roles"`
    86  	}
    87  	if err = yaml.Unmarshal(content, &data); err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	auth := atc.TeamAuth{}
    92  
    93  	for _, role := range data.Roles {
    94  		roleName := role["name"].(string)
    95  
    96  		users := []string{}
    97  		groups := []string{}
    98  
    99  		for _, connector := range connectors {
   100  			config, ok := role[connector.ID()]
   101  			if !ok {
   102  				continue
   103  			}
   104  
   105  			teamConfig, err := connector.newTeamConfig()
   106  			if err != nil {
   107  				return nil, err
   108  			}
   109  
   110  			err = mapstructure.Decode(config, &teamConfig)
   111  			if err != nil {
   112  				return nil, err
   113  			}
   114  
   115  			for _, user := range teamConfig.GetUsers() {
   116  				if user != "" {
   117  					users = append(users, connector.ID()+":"+strings.ToLower(user))
   118  				}
   119  			}
   120  
   121  			for _, group := range teamConfig.GetGroups() {
   122  				if group != "" {
   123  					groups = append(groups, connector.ID()+":"+strings.ToLower(group))
   124  				}
   125  			}
   126  		}
   127  
   128  		if conf, ok := role["local"].(map[string]interface{}); ok {
   129  			for _, user := range conf["users"].([]interface{}) {
   130  				if user != "" {
   131  					users = append(users, "local:"+strings.ToLower(user.(string)))
   132  				}
   133  			}
   134  		}
   135  
   136  		if len(users) == 0 && len(groups) == 0 {
   137  			continue
   138  		}
   139  
   140  		auth[roleName] = map[string][]string{
   141  			"users":  users,
   142  			"groups": groups,
   143  		}
   144  	}
   145  
   146  	if err := auth.Validate(); err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	return auth, nil
   151  }
   152  
   153  // When formatting team config from the command line flags, the connector's
   154  // TeamConfig has already been populated by the flags library. All we need to
   155  // do is grab the teamConfig object and extract the users and groups.
   156  
   157  func (flag *AuthTeamFlags) formatFromFlags() (atc.TeamAuth, error) {
   158  
   159  	users := []string{}
   160  	groups := []string{}
   161  
   162  	for _, connector := range connectors {
   163  
   164  		teamConfig := connector.teamConfig
   165  
   166  		for _, user := range teamConfig.GetUsers() {
   167  			if user != "" {
   168  				users = append(users, connector.ID()+":"+strings.ToLower(user))
   169  			}
   170  		}
   171  
   172  		for _, group := range teamConfig.GetGroups() {
   173  			if group != "" {
   174  				groups = append(groups, connector.ID()+":"+strings.ToLower(group))
   175  			}
   176  		}
   177  	}
   178  
   179  	for _, user := range flag.LocalUsers {
   180  		if user != "" {
   181  			users = append(users, "local:"+strings.ToLower(user))
   182  		}
   183  	}
   184  
   185  	if len(users) == 0 && len(groups) == 0 {
   186  		return nil, atc.ErrAuthConfigInvalid
   187  	}
   188  
   189  	return atc.TeamAuth{
   190  		"owner": map[string][]string{
   191  			"users":  users,
   192  			"groups": groups,
   193  		},
   194  	}, nil
   195  }
   196  
   197  type Config interface {
   198  	Name() string
   199  	Serialize(redirectURI string) ([]byte, error)
   200  }
   201  
   202  type TeamConfig interface {
   203  	GetUsers() []string
   204  	GetGroups() []string
   205  }
   206  
   207  type Connector struct {
   208  	id         string
   209  	config     Config
   210  	teamConfig TeamConfig
   211  }
   212  
   213  func (con *Connector) ID() string {
   214  	return con.id
   215  }
   216  
   217  func (con *Connector) Name() string {
   218  	return con.config.Name()
   219  }
   220  
   221  func (con *Connector) Serialize(redirectURI string) ([]byte, error) {
   222  	return con.config.Serialize(redirectURI)
   223  }
   224  
   225  func (con *Connector) newTeamConfig() (TeamConfig, error) {
   226  
   227  	typeof := reflect.TypeOf(con.teamConfig)
   228  	if typeof.Kind() == reflect.Ptr {
   229  		typeof = typeof.Elem()
   230  	}
   231  
   232  	valueof := reflect.ValueOf(con.teamConfig)
   233  	if valueof.Kind() == reflect.Ptr {
   234  		valueof = valueof.Elem()
   235  	}
   236  
   237  	instance := reflect.New(typeof).Interface()
   238  	res, ok := instance.(TeamConfig)
   239  	if !ok {
   240  		return nil, errors.New("Invalid TeamConfig")
   241  	}
   242  
   243  	return res, nil
   244  }