From b025165064f97b83a453e910d697b82045017521 Mon Sep 17 00:00:00 2001 From: dickeyxxx Date: Tue, 24 Nov 2015 13:21:31 -0800 Subject: [PATCH] sso --- Godeps/Godeps.json | 5 + .../toqueteos/webbrowser/.travis.yml | 11 ++ .../toqueteos/webbrowser/CONTRIBUTING.md | 11 ++ .../toqueteos/webbrowser/LICENSE.md | 19 +++ .../github.com/toqueteos/webbrowser/README.md | 42 +++++++ .../github.com/toqueteos/webbrowser/doc.go | 3 + .../toqueteos/webbrowser/examples/simple.go | 8 ++ .../toqueteos/webbrowser/webbrowser.go | 111 ++++++++++++++++++ cli.go | 2 +- login.go | 66 +++++++++-- two_factor.go | 4 +- whoami.go | 12 +- 12 files changed, 274 insertions(+), 20 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/toqueteos/webbrowser/.travis.yml create mode 100644 Godeps/_workspace/src/github.com/toqueteos/webbrowser/CONTRIBUTING.md create mode 100644 Godeps/_workspace/src/github.com/toqueteos/webbrowser/LICENSE.md create mode 100644 Godeps/_workspace/src/github.com/toqueteos/webbrowser/README.md create mode 100644 Godeps/_workspace/src/github.com/toqueteos/webbrowser/doc.go create mode 100644 Godeps/_workspace/src/github.com/toqueteos/webbrowser/examples/simple.go create mode 100644 Godeps/_workspace/src/github.com/toqueteos/webbrowser/webbrowser.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index ebe0e40a7e..f2452a476e 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -38,6 +38,11 @@ "Comment": "v0.3.1-4-g3f10961", "Rev": "3f10961d0bdb493ad6a6ecec9fb16d97c33d593e" }, + { + "ImportPath": "github.com/toqueteos/webbrowser", + "Comment": "v1.0", + "Rev": "21fc9f95c83442fd164094666f7cb4f9fdd56cd6" + }, { "ImportPath": "golang.org/x/crypto/ssh/terminal", "Rev": "59435533c88bd0b1254c738244da1fe96b59d05d" diff --git a/Godeps/_workspace/src/github.com/toqueteos/webbrowser/.travis.yml b/Godeps/_workspace/src/github.com/toqueteos/webbrowser/.travis.yml new file mode 100644 index 0000000000..c54dc7843b --- /dev/null +++ b/Godeps/_workspace/src/github.com/toqueteos/webbrowser/.travis.yml @@ -0,0 +1,11 @@ +language: go + +go: + - 1.2 + - 1.3 + - 1.4 + - tip + +script: + - go get -t ./... + - go test ./... diff --git a/Godeps/_workspace/src/github.com/toqueteos/webbrowser/CONTRIBUTING.md b/Godeps/_workspace/src/github.com/toqueteos/webbrowser/CONTRIBUTING.md new file mode 100644 index 0000000000..b9f7bf82a2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/toqueteos/webbrowser/CONTRIBUTING.md @@ -0,0 +1,11 @@ +# webbrowser contributing guide + +Any changes are welcomed! + +1. Be nice. +2. Don't be afraid to ask, but please try search first. + +## Looking for contact info? + +- Twitter: [@toqueteos](https://twitter.com/toqueteos) +- Mail: `toqueteos AT gmail DOT com` diff --git a/Godeps/_workspace/src/github.com/toqueteos/webbrowser/LICENSE.md b/Godeps/_workspace/src/github.com/toqueteos/webbrowser/LICENSE.md new file mode 100644 index 0000000000..2f43b08522 --- /dev/null +++ b/Godeps/_workspace/src/github.com/toqueteos/webbrowser/LICENSE.md @@ -0,0 +1,19 @@ +The MIT License (MIT) +Copyright (c) 2013-15 by Carlos Cobo and contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/toqueteos/webbrowser/README.md b/Godeps/_workspace/src/github.com/toqueteos/webbrowser/README.md new file mode 100644 index 0000000000..74e1aefc16 --- /dev/null +++ b/Godeps/_workspace/src/github.com/toqueteos/webbrowser/README.md @@ -0,0 +1,42 @@ +# webbrowser [![Build Status](https://travis-ci.org/toqueteos/webbrowser.png?branch=master)](https://travis-ci.org/toqueteos/webbrowser) [![GoDoc](http://godoc.org/github.com/toqueteos/webbrowser?status.png)](http://godoc.org/github.com/toqueteos/webbrowser) [![GitHub release](https://img.shields.io/github/release/toqueteos/webbrowser.svg)](https://github.com/toqueteos/webbrowser/releases) + +webbrowser provides a simple API for opening web pages on your default browser. It's inspired on [Python's webbrowser](http://docs.python.org/3.3/library/webbrowser.html) package but lacks some of its features (open new window). + +It just opens a webpage, most browsers will open it on a new tab. + +## Installation + +Copy & Paste fans: `go get github.com/toqueteos/webbrowser` + +## Usage + +Import the package (once you have installed it): + + import "github.com/toqueteos/webbrowser" + +Then use the `Open` function. + + webbrowser.Open("http://golang.org") + +That's it! + +Need a full blown example? Checkout [examples/simple.go]. + +## Already disliking it? + +No problem! There's alternative libraries that may be better to your needs: + +- https://github.com/pkg/browser, it does what webbrowser does and more! +- https://github.com/skratchdot/open-golang, it even provides a `xdg-open` implementation in case you don't have it! + +## Crossplatform support + +The package is guaranteed to work on `windows`, `linux` and `darwin`. It also has default support for `freebsd`, `openbsd` and `netbsd` but these three have not been tested yet (that I'm aware of). + +## License + +It is licensed under the MIT open source license, please see the [LICENSE.txt] file for more information. + +## Thanks... + +Miki Tebeka wrote a nicer version that wasn't on godoc.org when I did this, [check it out!](https://bitbucket.org/tebeka/go-wise/src/d8db9bf5c4d1/desktop.go?at=default). diff --git a/Godeps/_workspace/src/github.com/toqueteos/webbrowser/doc.go b/Godeps/_workspace/src/github.com/toqueteos/webbrowser/doc.go new file mode 100644 index 0000000000..3b86c7461c --- /dev/null +++ b/Godeps/_workspace/src/github.com/toqueteos/webbrowser/doc.go @@ -0,0 +1,3 @@ +// Package webbrowser provides a simple API for opening web pages on your +// default browser. +package webbrowser diff --git a/Godeps/_workspace/src/github.com/toqueteos/webbrowser/examples/simple.go b/Godeps/_workspace/src/github.com/toqueteos/webbrowser/examples/simple.go new file mode 100644 index 0000000000..3171ccb4b0 --- /dev/null +++ b/Godeps/_workspace/src/github.com/toqueteos/webbrowser/examples/simple.go @@ -0,0 +1,8 @@ +package main + +import "github.com/toqueteos/webbrowser" + +func main() { + webbrowser.Open("http://golang.org") + webbrowser.Open("http://reddit.com") +} diff --git a/Godeps/_workspace/src/github.com/toqueteos/webbrowser/webbrowser.go b/Godeps/_workspace/src/github.com/toqueteos/webbrowser/webbrowser.go new file mode 100644 index 0000000000..bc509a7936 --- /dev/null +++ b/Godeps/_workspace/src/github.com/toqueteos/webbrowser/webbrowser.go @@ -0,0 +1,111 @@ +package webbrowser + +import ( + "errors" + "fmt" + "net/url" + "os" + "os/exec" + "runtime" + "strings" +) + +func init() { + // Register a generic browser, if any, for current OS. + if os, ok := osCommand[runtime.GOOS]; ok { + Candidates = append(Candidates, GenericBrowser{os.cmd, os.args}) + } +} + +type args struct { + cmd string + args []string +} + +var ( + osCommand = map[string]*args{ + "darwin": &args{"open", nil}, + "freebsd": &args{"xdg-open", nil}, + "linux": &args{"xdg-open", nil}, + "netbsd": &args{"xdg-open", nil}, + "openbsd": &args{"xdg-open", nil}, // It may be open instead + "windows": &args{"cmd", []string{"/c", "start"}}, + } + ErrCantOpen = errors.New("webbrowser.Open: can't open webpage") + ErrNoCandidates = errors.New("webbrowser.Open: no browser candidate found for your OS.") +) + +// List of registered `Browser`s that will be tried with Open. +var Candidates []Browser + +type Browser interface { + Open(string) error +} + +// GenericBrowser just holds a command name and its arguments; the url will be +// appended as last arg. If you need to use string replacement for url define +// your own implementation. +type GenericBrowser struct { + Cmd string + Args []string +} + +func (gb GenericBrowser) Open(s string) error { + u, err := url.Parse(s) + if err != nil { + return err + } + + // Enforce a scheme (windows requires scheme to be set to work properly). + if u.Scheme != "https" { + u.Scheme = "http" + } + s = u.String() + + // Escape characters not allowed by cmd/bash + switch runtime.GOOS { + case "windows": + s = strings.Replace(s, "&", `^&`, -1) + } + + var cmd *exec.Cmd + if gb.Args != nil { + cmd = exec.Command(gb.Cmd, append(gb.Args, s)...) + } else { + cmd = exec.Command(gb.Cmd, s) + } + return cmd.Run() +} + +// Open opens an URL on the first available candidate found. +func Open(s string) error { + if len(Candidates) == 0 { + return ErrNoCandidates + } + + for _, b := range Candidates { + err := b.Open(s) + if err == nil { + return nil + } + } + + // Try to determine if there's a display available (only linux) and we + // aren't on a terminal (all but windows). + switch runtime.GOOS { + case "linux": + // No display, no need to open a browser. Lynx users **MAY** have + // something to say about this. + if os.Getenv("DISPLAY") == "" { + return fmt.Errorf("Tried to open %q on default webbrowser, no screen found.\n", s) + } + fallthrough + case "darwin": + // Check SSH env vars. + if os.Getenv("SSH_CLIENT") != "" || os.Getenv("SSH_TTY") != "" { + return fmt.Errorf("Tried to open %q on default webbrowser, but you are running a shell session.\n", s) + } + } + + return ErrCantOpen +} diff --git a/cli.go b/cli.go index 05d7309666..66bd70a10d 100644 --- a/cli.go +++ b/cli.go @@ -194,7 +194,7 @@ func getNetrc() *netrc.Netrc { func auth() (password string) { token := apiToken() if token == "" { - login() + interactiveLogin() return auth() } return token diff --git a/login.go b/login.go index a3b4bd589f..157c75053f 100644 --- a/login.go +++ b/login.go @@ -9,6 +9,7 @@ import ( "time" "github.com/dickeyxxx/speakeasy" + "github.com/toqueteos/webbrowser" ) var loginTopic = &Topic{ @@ -19,24 +20,73 @@ var loginTopic = &Topic{ var loginCmd = &Command{ Topic: "login", Description: "Login with your Heroku credentials.", - Run: func(ctx *Context) { - login() + Flags: []Flag{ + {Name: "sso", Description: "login for enterprise users under SSO"}, }, + Run: login, } var authLoginCmd = &Command{ Topic: "auth", Command: "login", Description: "Login with your Heroku credentials.", - Run: func(ctx *Context) { - login() + Flags: []Flag{ + {Name: "sso", Description: "login for enterprise users under SSO"}, }, + Run: login, +} + +func login(ctx *Context) { + if ctx.Flags["sso"] == true { + ssoLogin() + } else { + interactiveLogin() + } +} + +func ssoLogin() { + url := os.Getenv("SSO_URL") + if url == "" { + org := os.Getenv("HEROKU_ORGANIZATION") + for org == "" { + org = getString("Enter your organization name: ") + } + url = "https://sso.heroku.com/saml/" + org + "/init?cli=true" + } + Err("Opening browser for login...") + err := webbrowser.Open(url) + if err != nil { + Errln(" " + err.Error() + ".\nNavigate to " + cyan(url)) + } else { + Errln(" done") + } + token := getPassword("Enter your access token (typing will be hidden): ") + user := getUserFromToken(token) + if user == "" { + ExitIfError(errors.New("Access token invalid.")) + } + saveOauthToken(user, token) + Println("Logged in as " + cyan(user)) +} + +func getUserFromToken(token string) string { + req := apiRequest(token) + req.Method = "GET" + req.Uri = req.Uri + "/account" + res, err := req.Do() + ExitIfError(err) + if res.StatusCode != 200 { + return "" + } + var doc map[string]interface{} + res.Body.FromJsonTo(&doc) + return doc["email"].(string) } -func login() { +func interactiveLogin() { Println("Enter your Heroku credentials.") email := getString("Email: ") - password := getPassword() + password := getPassword("Password (typing will be hidden): ") token, err := v2login(email, password, "") // TODO: use createOauthToken (v3 API) @@ -71,8 +121,8 @@ func getString(prompt string) string { return s } -func getPassword() string { - password, err := speakeasy.Ask("Password (typing will be hidden): ") +func getPassword(prompt string) string { + password, err := speakeasy.Ask(prompt) if err != nil { if err.Error() == "The handle is invalid." { Errln(`Login is currently incompatible with git bash/cygwin diff --git a/two_factor.go b/two_factor.go index 17cdfd8040..d740bbcb9d 100644 --- a/two_factor.go +++ b/two_factor.go @@ -57,7 +57,7 @@ func twoFactorGenerateRun(ctx *Context) { req := apiRequest(ctx.APIToken) req.Method = "POST" req.Uri = req.Uri + "/account/recovery-codes" - req.AddHeader("Heroku-Password", getPassword()) + req.AddHeader("Heroku-Password", getPassword("Password (typing will be hidden): ")) req.AddHeader("Heroku-Two-Factor-Code", getString("Two-factor code: ")) res, err := req.Do() ExitIfError(err) @@ -91,7 +91,7 @@ func twoFactorDisableRun(ctx *Context) { req.Uri = req.Uri + "/account/" req.Body = map[string]interface{}{ "two_factor_authentication": "false", - "password": getPassword(), + "password": getPassword("Password (typing will be hidden):"), } res, err := req.Do() ExitIfError(err) diff --git a/whoami.go b/whoami.go index 7860a4ea0c..86aa01e84b 100644 --- a/whoami.go +++ b/whoami.go @@ -24,17 +24,11 @@ var whoamiCmd = &Command{ Exit(100) } - req := apiRequest(ctx.APIToken) - req.Method = "GET" - req.Uri = req.Uri + "/account" - res, err := req.Do() - ExitIfError(err) - if res.StatusCode != 200 { + user := getUserFromToken(ctx.APIToken) + if user == "" { Println("not logged in") Exit(100) } - var doc map[string]interface{} - res.Body.FromJsonTo(&doc) - Println(doc["email"]) + Println(user) }, }