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

     1  package dexserver
     2  
     3  import (
     4  	"context"
     5  	"crypto/rsa"
     6  	"strings"
     7  	"time"
     8  
     9  	"code.cloudfoundry.org/lager"
    10  	"github.com/pf-qiu/concourse/v6/skymarshal/logger"
    11  	"github.com/pf-qiu/concourse/v6/skymarshal/skycmd"
    12  	s "github.com/pf-qiu/concourse/v6/skymarshal/storage"
    13  	"github.com/concourse/dex/server"
    14  	"github.com/concourse/dex/storage"
    15  	"github.com/markbates/pkger"
    16  	"golang.org/x/crypto/bcrypt"
    17  )
    18  
    19  type DexConfig struct {
    20  	Logger      lager.Logger
    21  	IssuerURL   string
    22  	WebHostURL  string
    23  	SigningKey  *rsa.PrivateKey
    24  	Expiration  time.Duration
    25  	Clients     map[string]string
    26  	Users       map[string]string
    27  	RedirectURL string
    28  	Storage     s.Storage
    29  }
    30  
    31  func NewDexServer(config *DexConfig) (*server.Server, error) {
    32  
    33  	newDexServerConfig, err := NewDexServerConfig(config)
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  
    38  	return server.NewServerWithKey(context.Background(), newDexServerConfig, config.SigningKey)
    39  }
    40  
    41  func NewDexServerConfig(config *DexConfig) (server.Config, error) {
    42  
    43  	var clients []storage.Client
    44  	var connectors []storage.Connector
    45  	var passwords []storage.Password
    46  
    47  	for username, password := range newLocalUsers(config) {
    48  		passwords = append(passwords, storage.Password{
    49  			UserID:   username,
    50  			Username: username,
    51  			Email:    username,
    52  			Hash:     password,
    53  		})
    54  	}
    55  
    56  	if len(passwords) > 0 {
    57  		connectors = append(connectors, storage.Connector{
    58  			ID:   "local",
    59  			Type: "local",
    60  			Name: "Username/Password",
    61  		})
    62  	}
    63  
    64  	redirectURI := strings.TrimRight(config.IssuerURL, "/") + "/callback"
    65  
    66  	for _, connector := range skycmd.GetConnectors() {
    67  		if c, err := connector.Serialize(redirectURI); err == nil {
    68  			connectors = append(connectors, storage.Connector{
    69  				ID:     connector.ID(),
    70  				Type:   connector.ID(),
    71  				Name:   connector.Name(),
    72  				Config: c,
    73  			})
    74  		}
    75  	}
    76  
    77  	for clientId, clientSecret := range newLocalClients(config) {
    78  		clients = append(clients, storage.Client{
    79  			ID:           clientId,
    80  			Secret:       string(clientSecret),
    81  			RedirectURIs: []string{config.RedirectURL},
    82  		})
    83  	}
    84  
    85  	if err := replacePasswords(config.Storage, passwords); err != nil {
    86  		return server.Config{}, err
    87  	}
    88  
    89  	if err := replaceClients(config.Storage, clients); err != nil {
    90  		return server.Config{}, err
    91  	}
    92  
    93  	if err := replaceConnectors(config.Storage, connectors); err != nil {
    94  		return server.Config{}, err
    95  	}
    96  
    97  	webConfig := server.WebConfig{
    98  		LogoURL: strings.TrimRight(config.WebHostURL, "/") + "/themes/concourse/logo.svg",
    99  		HostURL: config.WebHostURL,
   100  		Theme:   "concourse",
   101  		Issuer:  "Concourse",
   102  		Dir:     pkger.Include("/skymarshal/web"),
   103  	}
   104  
   105  	return server.Config{
   106  		PasswordConnector:      "local",
   107  		SupportedResponseTypes: []string{"code", "token", "id_token"},
   108  		SkipApprovalScreen:     true,
   109  		IDTokensValidFor:       config.Expiration,
   110  		Issuer:                 config.IssuerURL,
   111  		Storage:                config.Storage,
   112  		Web:                    webConfig,
   113  		Logger:                 logger.New(config.Logger),
   114  	}, nil
   115  }
   116  
   117  func replacePasswords(store s.Storage, passwords []storage.Password) error {
   118  	existing, err := store.ListPasswords()
   119  	if err != nil {
   120  		return err
   121  	}
   122  
   123  	for _, oldPass := range existing {
   124  		err = store.DeletePassword(oldPass.Email)
   125  		if err != nil {
   126  			return err
   127  		}
   128  	}
   129  
   130  	for _, newPass := range passwords {
   131  		err = store.CreatePassword(newPass)
   132  		//if this already exists, some other ATC process has created it already
   133  		//we can assume that both ATCs have the same desired config.
   134  		if err != nil && err != storage.ErrAlreadyExists {
   135  			return err
   136  		}
   137  	}
   138  
   139  	return nil
   140  }
   141  
   142  func replaceClients(store s.Storage, clients []storage.Client) error {
   143  	existing, err := store.ListClients()
   144  	if err != nil {
   145  		return err
   146  	}
   147  
   148  	for _, oldClient := range existing {
   149  		err = store.DeleteClient(oldClient.ID)
   150  		if err != nil {
   151  			return err
   152  		}
   153  	}
   154  
   155  	for _, newClient := range clients {
   156  		err = store.CreateClient(newClient)
   157  		//if this already exists, some other ATC process has created it already
   158  		//we can assume that both ATCs have the same desired config.
   159  		if err != nil && err != storage.ErrAlreadyExists {
   160  			return err
   161  		}
   162  	}
   163  
   164  	return nil
   165  }
   166  
   167  func replaceConnectors(store s.Storage, connectors []storage.Connector) error {
   168  	existing, err := store.ListConnectors()
   169  	if err != nil {
   170  		return err
   171  	}
   172  
   173  	for _, oldConn := range existing {
   174  		err = store.DeleteConnector(oldConn.ID)
   175  		if err != nil {
   176  			return err
   177  		}
   178  	}
   179  
   180  	for _, newConn := range connectors {
   181  		err = store.CreateConnector(newConn)
   182  		//if this already exists, some other ATC process has created it already
   183  		//we can assume that both ATCs have the same desired config.
   184  		if err != nil && err != storage.ErrAlreadyExists {
   185  			return err
   186  		}
   187  	}
   188  
   189  	return nil
   190  }
   191  
   192  func newLocalUsers(config *DexConfig) map[string][]byte {
   193  	users := map[string][]byte{}
   194  
   195  	for username, password := range config.Users {
   196  		if username != "" && password != "" {
   197  
   198  			var hashed []byte
   199  
   200  			if _, err := bcrypt.Cost([]byte(password)); err != nil {
   201  				if hashed, err = bcrypt.GenerateFromPassword([]byte(password), 0); err != nil {
   202  
   203  					config.Logger.Error("bcrypt-local-user", err, lager.Data{
   204  						"username": username,
   205  					})
   206  
   207  					continue
   208  				}
   209  			} else {
   210  				hashed = []byte(password)
   211  			}
   212  
   213  			users[username] = hashed
   214  		}
   215  	}
   216  
   217  	return users
   218  
   219  }
   220  
   221  func newLocalClients(config *DexConfig) map[string][]byte {
   222  	clients := map[string][]byte{}
   223  
   224  	for clientID, clientSecret := range config.Clients {
   225  		if clientID != "" && clientSecret != "" {
   226  
   227  			var hashed []byte
   228  
   229  			if _, err := bcrypt.Cost([]byte(clientSecret)); err != nil {
   230  				if hashed, err = bcrypt.GenerateFromPassword([]byte(clientSecret), 0); err != nil {
   231  
   232  					config.Logger.Error("bcrypt-client-secret", err, lager.Data{
   233  						"client_id": clientID,
   234  					})
   235  
   236  					continue
   237  				}
   238  			} else {
   239  				hashed = []byte(clientSecret)
   240  			}
   241  
   242  			clients[clientID] = hashed
   243  		}
   244  	}
   245  
   246  	return clients
   247  
   248  }