github.com/rakutentech/cli@v6.12.5-0.20151006231303-24468b65536e+incompatible/cf/commands/login.go (about) 1 package commands 2 3 import ( 4 "strconv" 5 6 "github.com/cloudfoundry/cli/cf/command_registry" 7 . "github.com/cloudfoundry/cli/cf/i18n" 8 "github.com/cloudfoundry/cli/flags" 9 "github.com/cloudfoundry/cli/flags/flag" 10 11 "github.com/cloudfoundry/cli/cf/api" 12 "github.com/cloudfoundry/cli/cf/api/authentication" 13 "github.com/cloudfoundry/cli/cf/api/organizations" 14 "github.com/cloudfoundry/cli/cf/api/spaces" 15 "github.com/cloudfoundry/cli/cf/configuration/core_config" 16 "github.com/cloudfoundry/cli/cf/models" 17 "github.com/cloudfoundry/cli/cf/requirements" 18 "github.com/cloudfoundry/cli/cf/terminal" 19 ) 20 21 const maxLoginTries = 3 22 const maxChoices = 50 23 24 type Login struct { 25 ui terminal.UI 26 config core_config.ReadWriter 27 authenticator authentication.AuthenticationRepository 28 endpointRepo api.EndpointRepository 29 orgRepo organizations.OrganizationRepository 30 spaceRepo spaces.SpaceRepository 31 } 32 33 func init() { 34 command_registry.Register(&Login{}) 35 } 36 37 func (cmd *Login) MetaData() command_registry.CommandMetadata { 38 fs := make(map[string]flags.FlagSet) 39 fs["a"] = &cliFlags.StringFlag{Name: "a", Usage: T("API endpoint (e.g. https://api.example.com)")} 40 fs["u"] = &cliFlags.StringFlag{Name: "u", Usage: T("Username")} 41 fs["p"] = &cliFlags.StringFlag{Name: "p", Usage: T("Password")} 42 fs["o"] = &cliFlags.StringFlag{Name: "o", Usage: T("Org")} 43 fs["s"] = &cliFlags.StringFlag{Name: "s", Usage: T("Space")} 44 fs["sso"] = &cliFlags.BoolFlag{Name: "sso", Usage: T("Use a one-time password to login")} 45 fs["skip-ssl-validation"] = &cliFlags.BoolFlag{Name: "skip-ssl-validation", Usage: T("Please don't")} 46 47 return command_registry.CommandMetadata{ 48 Name: "login", 49 ShortName: "l", 50 Description: T("Log user in"), 51 Usage: T("CF_NAME login [-a API_URL] [-u USERNAME] [-p PASSWORD] [-o ORG] [-s SPACE]\n\n") + 52 terminal.WarningColor(T("WARNING:\n Providing your password as a command line option is highly discouraged\n Your password may be visible to others and may be recorded in your shell history\n\n")) + T("EXAMPLE:\n") + T(" CF_NAME login (omit username and password to login interactively -- CF_NAME will prompt for both)\n") + T(" CF_NAME login -u name@example.com -p pa55woRD (specify username and password as arguments)\n") + T(" CF_NAME login -u name@example.com -p \"my password\" (use quotes for passwords with a space)\n") + T(" CF_NAME login -u name@example.com -p \"\\\"password\\\"\" (escape quotes if used in password)\n") + T(" CF_NAME login --sso (CF_NAME will provide a url to obtain a one-time password to login)"), 53 Flags: fs, 54 } 55 } 56 57 func (cmd *Login) Requirements(requirementsFactory requirements.Factory, fc flags.FlagContext) (reqs []requirements.Requirement, err error) { 58 return 59 } 60 61 func (cmd *Login) SetDependency(deps command_registry.Dependency, pluginCall bool) command_registry.Command { 62 cmd.ui = deps.Ui 63 cmd.config = deps.Config 64 cmd.authenticator = deps.RepoLocator.GetAuthenticationRepository() 65 cmd.endpointRepo = deps.RepoLocator.GetEndpointRepository() 66 cmd.orgRepo = deps.RepoLocator.GetOrganizationRepository() 67 cmd.spaceRepo = deps.RepoLocator.GetSpaceRepository() 68 return cmd 69 } 70 71 func (cmd *Login) Execute(c flags.FlagContext) { 72 cmd.config.ClearSession() 73 74 endpoint, skipSSL := cmd.decideEndpoint(c) 75 76 Api{ 77 ui: cmd.ui, 78 config: cmd.config, 79 endpointRepo: cmd.endpointRepo, 80 }.setApiEndpoint(endpoint, skipSSL, cmd.MetaData().Name) 81 82 defer func() { 83 cmd.ui.Say("") 84 cmd.ui.ShowConfiguration(cmd.config) 85 }() 86 87 // We thought we would never need to explicitly branch in this code 88 // for anything as simple as authentication, but it turns out that our 89 // assumptions did not match reality. 90 91 // When SAML is enabled (but not configured) then the UAA/Login server 92 // will always returns password prompts that includes the Passcode field. 93 // Users can authenticate with: 94 // EITHER username and password 95 // OR a one-time passcode 96 97 if c.Bool("sso") { 98 cmd.authenticateSSO(c) 99 } else { 100 cmd.authenticate(c) 101 } 102 103 orgIsSet := cmd.setOrganization(c) 104 105 if orgIsSet { 106 cmd.setSpace(c) 107 } 108 cmd.ui.NotifyUpdateIfNeeded(cmd.config) 109 } 110 111 func (cmd Login) decideEndpoint(c flags.FlagContext) (string, bool) { 112 endpoint := c.String("a") 113 skipSSL := c.Bool("skip-ssl-validation") 114 if endpoint == "" { 115 endpoint = cmd.config.ApiEndpoint() 116 skipSSL = cmd.config.IsSSLDisabled() || skipSSL 117 } 118 119 if endpoint == "" { 120 endpoint = cmd.ui.Ask(T("API endpoint")) 121 } else { 122 cmd.ui.Say(T("API endpoint: {{.Endpoint}}", map[string]interface{}{"Endpoint": terminal.EntityNameColor(endpoint)})) 123 } 124 125 return endpoint, skipSSL 126 } 127 128 func (cmd Login) authenticateSSO(c flags.FlagContext) { 129 prompts, err := cmd.authenticator.GetLoginPromptsAndSaveUAAServerURL() 130 if err != nil { 131 cmd.ui.Failed(err.Error()) 132 } 133 134 credentials := make(map[string]string) 135 passcode := prompts["passcode"] 136 137 for i := 0; i < maxLoginTries; i++ { 138 credentials["passcode"] = cmd.ui.AskForPassword("%s", passcode.DisplayName) 139 140 cmd.ui.Say(T("Authenticating...")) 141 err = cmd.authenticator.Authenticate(credentials) 142 143 if err == nil { 144 cmd.ui.Ok() 145 cmd.ui.Say("") 146 break 147 } 148 149 cmd.ui.Say(err.Error()) 150 } 151 152 if err != nil { 153 cmd.ui.Failed(T("Unable to authenticate.")) 154 } 155 } 156 157 func (cmd Login) authenticate(c flags.FlagContext) { 158 usernameFlagValue := c.String("u") 159 passwordFlagValue := c.String("p") 160 161 prompts, err := cmd.authenticator.GetLoginPromptsAndSaveUAAServerURL() 162 if err != nil { 163 cmd.ui.Failed(err.Error()) 164 } 165 passwordKeys := []string{} 166 credentials := make(map[string]string) 167 168 if value, ok := prompts["username"]; ok { 169 if prompts["username"].Type == core_config.AuthPromptTypeText && usernameFlagValue != "" { 170 credentials["username"] = usernameFlagValue 171 } else { 172 credentials["username"] = cmd.ui.Ask("%s", value.DisplayName) 173 } 174 } 175 176 for key, prompt := range prompts { 177 if prompt.Type == core_config.AuthPromptTypePassword { 178 if key == "passcode" { 179 continue 180 } 181 182 passwordKeys = append(passwordKeys, key) 183 } else if key == "username" { 184 continue 185 } else { 186 credentials[key] = cmd.ui.Ask("%s", prompt.DisplayName) 187 } 188 } 189 190 for i := 0; i < maxLoginTries; i++ { 191 for _, key := range passwordKeys { 192 if key == "password" && passwordFlagValue != "" { 193 credentials[key] = passwordFlagValue 194 passwordFlagValue = "" 195 } else { 196 credentials[key] = cmd.ui.AskForPassword("%s", prompts[key].DisplayName) 197 } 198 } 199 200 cmd.ui.Say(T("Authenticating...")) 201 err = cmd.authenticator.Authenticate(credentials) 202 203 if err == nil { 204 cmd.ui.Ok() 205 cmd.ui.Say("") 206 break 207 } 208 209 cmd.ui.Say(err.Error()) 210 } 211 212 if err != nil { 213 cmd.ui.Failed(T("Unable to authenticate.")) 214 } 215 } 216 217 func (cmd Login) setOrganization(c flags.FlagContext) (isOrgSet bool) { 218 orgName := c.String("o") 219 220 if orgName == "" { 221 availableOrgs := []models.Organization{} 222 orgs, apiErr := cmd.orgRepo.ListOrgs() 223 if apiErr != nil { 224 cmd.ui.Failed(T("Error finding available orgs\n{{.ApiErr}}", 225 map[string]interface{}{"ApiErr": apiErr.Error()})) 226 } 227 for _, org := range orgs { 228 if len(availableOrgs) < maxChoices { 229 availableOrgs = append(availableOrgs, org) 230 } 231 } 232 233 if len(availableOrgs) == 0 { 234 return false 235 } else if len(availableOrgs) == 1 { 236 cmd.targetOrganization(availableOrgs[0]) 237 return true 238 } else { 239 orgName = cmd.promptForOrgName(availableOrgs) 240 if orgName == "" { 241 cmd.ui.Say("") 242 return false 243 } 244 } 245 } 246 247 org, err := cmd.orgRepo.FindByName(orgName) 248 if err != nil { 249 cmd.ui.Failed(T("Error finding org {{.OrgName}}\n{{.Err}}", 250 map[string]interface{}{"OrgName": terminal.EntityNameColor(orgName), "Err": err.Error()})) 251 } 252 253 cmd.targetOrganization(org) 254 return true 255 } 256 257 func (cmd Login) promptForOrgName(orgs []models.Organization) string { 258 orgNames := []string{} 259 for _, org := range orgs { 260 orgNames = append(orgNames, org.Name) 261 } 262 263 return cmd.promptForName(orgNames, T("Select an org (or press enter to skip):"), "Org") 264 } 265 266 func (cmd Login) targetOrganization(org models.Organization) { 267 cmd.config.SetOrganizationFields(org.OrganizationFields) 268 cmd.ui.Say(T("Targeted org {{.OrgName}}\n", 269 map[string]interface{}{"OrgName": terminal.EntityNameColor(org.Name)})) 270 } 271 272 func (cmd Login) setSpace(c flags.FlagContext) { 273 spaceName := c.String("s") 274 275 if spaceName == "" { 276 var availableSpaces []models.Space 277 err := cmd.spaceRepo.ListSpaces(func(space models.Space) bool { 278 availableSpaces = append(availableSpaces, space) 279 return (len(availableSpaces) < maxChoices) 280 }) 281 if err != nil { 282 cmd.ui.Failed(T("Error finding available spaces\n{{.Err}}", 283 map[string]interface{}{"Err": err.Error()})) 284 } 285 286 if len(availableSpaces) == 0 { 287 return 288 } else if len(availableSpaces) == 1 { 289 cmd.targetSpace(availableSpaces[0]) 290 return 291 } else { 292 spaceName = cmd.promptForSpaceName(availableSpaces) 293 if spaceName == "" { 294 cmd.ui.Say("") 295 return 296 } 297 } 298 } 299 300 space, err := cmd.spaceRepo.FindByName(spaceName) 301 if err != nil { 302 cmd.ui.Failed(T("Error finding space {{.SpaceName}}\n{{.Err}}", 303 map[string]interface{}{"SpaceName": terminal.EntityNameColor(spaceName), "Err": err.Error()})) 304 } 305 306 cmd.targetSpace(space) 307 } 308 309 func (cmd Login) promptForSpaceName(spaces []models.Space) string { 310 spaceNames := []string{} 311 for _, space := range spaces { 312 spaceNames = append(spaceNames, space.Name) 313 } 314 315 return cmd.promptForName(spaceNames, T("Select a space (or press enter to skip):"), "Space") 316 } 317 318 func (cmd Login) targetSpace(space models.Space) { 319 cmd.config.SetSpaceFields(space.SpaceFields) 320 cmd.ui.Say(T("Targeted space {{.SpaceName}}\n", 321 map[string]interface{}{"SpaceName": terminal.EntityNameColor(space.Name)})) 322 } 323 324 func (cmd Login) promptForName(names []string, listPrompt, itemPrompt string) string { 325 nameIndex := 0 326 var nameString string 327 for nameIndex < 1 || nameIndex > len(names) { 328 var err error 329 330 // list header 331 cmd.ui.Say(listPrompt) 332 333 // only display list if it is shorter than maxChoices 334 if len(names) < maxChoices { 335 for i, name := range names { 336 cmd.ui.Say("%d. %s", i+1, name) 337 } 338 } else { 339 cmd.ui.Say(T("There are too many options to display, please type in the name.")) 340 } 341 342 nameString = cmd.ui.Ask("%s", itemPrompt) 343 if nameString == "" { 344 return "" 345 } 346 347 nameIndex, err = strconv.Atoi(nameString) 348 349 if err != nil { 350 nameIndex = 1 351 return nameString 352 } 353 } 354 355 return names[nameIndex-1] 356 }