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 }