Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(Input) Started moving user input out so it can be mocked #72

Merged
merged 1 commit into from
Oct 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ endif

deps: glide
# go get github.com/buildkite/github-release
go get -u github.com/alecthomas/gometalinter
go get github.com/mitchellh/gox
./glide install
gometalinter --install

compile: deps
@rm -rf build/
Expand All @@ -36,6 +38,10 @@ compile: deps
-output "build/{{.Dir}}_$(VERSION)_{{.OS}}_{{.Arch}}/$(NAME)" \
$(shell ./glide novendor)

# Run all the linters
lint:
gometalinter --vendor ./...

install:
go install ./cmd/saml2aws

Expand All @@ -58,4 +64,4 @@ clean:
rm ./glide
rm -fr ./build

.PHONY: default deps compile dist release test clean
.PHONY: default deps compile lint dist release test clean
2 changes: 1 addition & 1 deletion cmd/saml2aws/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func buildCmdList(s kingpin.Settings) (target *[]string) {
func configureLoginFlags(app *kingpin.Application) *commands.LoginFlags {
c := &commands.LoginFlags{}

app.Flag("idp-account", "The name of the configured IDP account").Short('i').Default("default").StringVar(&c.IdpAccount)
app.Flag("idp-account", "The name of the configured IDP account").Short('a').Default("default").StringVar(&c.IdpAccount)
app.Flag("profile", "The AWS profile to save the temporary credentials").Short('p').Default("saml").StringVar(&c.Profile)
app.Flag("skip-verify", "Skip verification of server certificate.").Short('s').BoolVar(&c.SkipVerify)
// app.Flag("timeout", "Override the default HTTP client timeout in seconds.").Short('t').IntVar(&c.Timeout)
Expand Down
35 changes: 35 additions & 0 deletions pkg/prompter/prompter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package prompter

import prompt "github.com/segmentio/go-prompt"

// Prompter handles prompting user for input
type Prompter interface {
RequestSecurityCode(pattern string) string
Choice(prompt string, options []string) string
StringRequired(pr string) string
}

// CliPrompter used to prompt for cli input
type CliPrompter struct {
}

// NewCli builds a new cli prompter
func NewCli() Prompter {
return &CliPrompter{}
}

// RequestSecurityCode request a security code to be entered by the user
func (cli *CliPrompter) RequestSecurityCode(pattern string) string {
return prompt.StringRequired("\nSecurity Token [%s]\n", pattern)
}

// Choice given the choice return the option selected
func (cli *CliPrompter) Choice(pr string, options []string) string {
selected := prompt.Choose(pr, options)
return options[selected]
}

// StringRequired prompt for string which is required
func (cli *CliPrompter) StringRequired(pr string) string {
return prompt.StringRequired(pr)
}
48 changes: 32 additions & 16 deletions pkg/provider/okta/okta.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import (
"strings"
"time"

"github.com/versent/saml2aws/pkg/prompter"

"github.com/PuerkitoBio/goquery"
"github.com/pkg/errors"
prompt "github.com/segmentio/go-prompt"
"github.com/tidwall/gjson"
"github.com/versent/saml2aws/pkg/cfg"
"github.com/versent/saml2aws/pkg/creds"
Expand All @@ -22,9 +23,15 @@ import (
"encoding/json"
)

var duoMFAOptions = []string{
"Passcode",
"Duo Push",
}

// Client is a wrapper representing a Okta SAML client
type Client struct {
client *provider.HTTPClient
client *provider.HTTPClient
prompter prompter.Prompter
}

// AuthRequest represents an mfa okta request
Expand All @@ -50,7 +57,8 @@ func New(idpAccount *cfg.IDPAccount) (*Client, error) {
}

return &Client{
client: client,
client: client,
prompter: prompter.NewCli(),
}, nil
}

Expand All @@ -60,12 +68,19 @@ func (oc *Client) Authenticate(loginDetails *creds.LoginDetails) (string, error)

oktaEntryURL := fmt.Sprintf("https://%s", loginDetails.Hostname)
oktaURL, err := url.Parse(oktaEntryURL)
if err != nil {
return samlAssertion, errors.Wrap(err, "error building oktaURL")
}

oktaOrgHost := oktaURL.Host

//authenticate via okta api
authReq := AuthRequest{Username: loginDetails.Username, Password: loginDetails.Password}
authBody := new(bytes.Buffer)
json.NewEncoder(authBody).Encode(authReq)
err = json.NewEncoder(authBody).Encode(authReq)
if err != nil {
return samlAssertion, errors.Wrap(err, "error encoding authreq")
}

authSubmitURL := fmt.Sprintf("https://%s/api/v1/authn", oktaOrgHost)

Expand Down Expand Up @@ -102,7 +117,10 @@ func (oc *Client) Authenticate(loginDetails *creds.LoginDetails) (string, error)
// get duo host, signature & callback
verifyReq := VerifyRequest{StateToken: stateToken}
verifyBody := new(bytes.Buffer)
json.NewEncoder(verifyBody).Encode(verifyReq)
err := json.NewEncoder(verifyBody).Encode(verifyReq)
if err != nil {
return samlAssertion, errors.Wrap(err, "error encoding verifyReq")
}

req, err := http.NewRequest("POST", oktaVerify, verifyBody)
if err != nil {
Expand Down Expand Up @@ -170,16 +188,11 @@ func (oc *Client) Authenticate(loginDetails *creds.LoginDetails) (string, error)
//only supporting push or passcode for now
var token string

var mfaOptions = []string{
"Passcode",
"Duo Push",
}
optionSelected := oc.prompter.Choice("Select a DUO MFA Option", duoMFAOptions)

mfaOption := prompt.Choose("Select a DUO MFA Option", mfaOptions)

if mfaOptions[mfaOption] == "Passcode" {
if optionSelected == "Passcode" {
//get users DUO MFA Token
token = prompt.StringRequired("Enter passcode")
token = oc.prompter.StringRequired("Enter passcode")
}

// send mfa auth request
Expand All @@ -188,9 +201,9 @@ func (oc *Client) Authenticate(loginDetails *creds.LoginDetails) (string, error)
duoForm = url.Values{}
duoForm.Add("sid", duoSID)
duoForm.Add("device", "phone1")
duoForm.Add("factor", mfaOptions[mfaOption])
duoForm.Add("factor", optionSelected)
duoForm.Add("out_of_date", "false")
if mfaOptions[mfaOption] == "Passcode" {
if optionSelected == "Passcode" {
duoForm.Add("passcode", token)
}

Expand Down Expand Up @@ -299,7 +312,10 @@ func (oc *Client) Authenticate(loginDetails *creds.LoginDetails) (string, error)

verifyReq = VerifyRequest{StateToken: stateToken}
verifyBody = new(bytes.Buffer)
json.NewEncoder(verifyBody).Encode(verifyReq)
err = json.NewEncoder(verifyBody).Encode(verifyReq)
if err != nil {
return samlAssertion, errors.Wrap(err, "error encoding verifyReq")
}

req, err = http.NewRequest("POST", oktaVerify, verifyBody)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type HTTPClient struct {
}

// NewHTTPClient configure the default http client used by the providers
func NewHTTPClient(tr *http.Transport) (*HTTPClient, error) {
func NewHTTPClient(tr http.RoundTripper) (*HTTPClient, error) {

options := &cookiejar.Options{
PublicSuffixList: publicsuffix.List,
Expand Down