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