Skip to content

Commit

Permalink
Merge pull request #79 from Versent/refactor_hostname_to_url
Browse files Browse the repository at this point in the history
refactor(Hostname) Changed hostname to URL to better reflect current values.
  • Loading branch information
wolfeidau authored Nov 1, 2017
2 parents 9fc6306 + 2402cbe commit 7c6d8be
Show file tree
Hide file tree
Showing 18 changed files with 91 additions and 58 deletions.
7 changes: 4 additions & 3 deletions aws_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"github.com/pkg/errors"
)

const awsURL = "https://signin.aws.amazon.com/saml"

// AWSAccount holds the AWS account name and roles
type AWSAccount struct {
Name string
Expand All @@ -20,16 +22,15 @@ type AWSAccount struct {

// ParseAWSAccounts extract the aws accounts from the saml assertion
func ParseAWSAccounts(samlAssertion string) ([]*AWSAccount, error) {
awsURL := "https://signin.aws.amazon.com/saml"

res, err := http.PostForm(awsURL, url.Values{"SAMLResponse": {samlAssertion}})
if err != nil {
return nil, errors.Wrap(err, "error retieving AWS login form")
return nil, errors.Wrap(err, "error retrieving AWS login form")
}

data, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, errors.Wrap(err, "error retieving AWS login body")
return nil, errors.Wrap(err, "error retrieving AWS login body")
}

return ExtractAWSAccounts(data)
Expand Down
15 changes: 8 additions & 7 deletions cmd/saml2aws/commands/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ const MaxDurationSeconds = 3600
type LoginFlags struct {
IdpAccount string
//Provider string
Profile string
Hostname string
Profile string
//Hostname string
URL string
Username string
Password string
RoleArn string
Expand Down Expand Up @@ -76,7 +77,7 @@ func Login(loginFlags *LoginFlags) error {
os.Exit(1)
}

err = credentials.SaveCredentials(loginDetails.Hostname, loginDetails.Username, loginDetails.Password)
err = credentials.SaveCredentials(loginDetails.URL, loginDetails.Username, loginDetails.Password)
if err != nil {
return errors.Wrap(err, "error storing password in keychain")
}
Expand Down Expand Up @@ -147,9 +148,9 @@ func resolveLoginDetails(account *cfg.IDPAccount, loginFlags *LoginFlags) (*cred

// fmt.Printf("loginFlags %+v\n", loginFlags)

loginDetails := &creds.LoginDetails{Hostname: account.Hostname, Username: account.Username}
loginDetails := &creds.LoginDetails{URL: account.URL, Username: account.Username}

fmt.Printf("Using IDP Account %s to access %s https://%s\n", loginFlags.IdpAccount, account.Provider, account.Hostname)
fmt.Printf("Using IDP Account %s to access %s %s\n", loginFlags.IdpAccount, account.Provider, account.URL)

err := credentials.LookupCredentials(loginDetails)
if err != nil {
Expand Down Expand Up @@ -261,8 +262,8 @@ func loginToStsUsingRole(role *saml2aws.AWSRole, samlAssertion string, profile s
}

func applyFlagOverrides(loginFlags *LoginFlags, account *cfg.IDPAccount) {
if loginFlags.Hostname != "" {
account.Hostname = loginFlags.Hostname
if loginFlags.URL != "" {
account.URL = loginFlags.URL
}

if loginFlags.Username != "" {
Expand Down
6 changes: 3 additions & 3 deletions cmd/saml2aws/commands/login_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@ import (

func TestResolveLoginDetailsWithFlags(t *testing.T) {

loginFlags := &LoginFlags{Hostname: "id.example.com", Username: "wolfeidau", Password: "testtestlol", SkipPrompt: true}
loginFlags := &LoginFlags{URL: "https://id.example.com", Username: "wolfeidau", Password: "testtestlol", SkipPrompt: true}

idpa := &cfg.IDPAccount{
Hostname: "id.example.com",
URL: "https://id.example.com",
MFA: "none",
Provider: "Ping",
Username: "wolfeidau",
}
loginDetails, err := resolveLoginDetails(idpa, loginFlags)

assert.Empty(t, err)
assert.Equal(t, &creds.LoginDetails{Username: "wolfeidau", Password: "testtestlol", Hostname: "id.example.com"}, loginDetails)
assert.Equal(t, &creds.LoginDetails{Username: "wolfeidau", Password: "testtestlol", URL: "https://id.example.com"}, loginDetails)
}

func TestResolveRoleSingleEntry(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion cmd/saml2aws/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func configureLoginFlags(app *kingpin.Application) *commands.LoginFlags {
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)
// app.Flag("provider", "The type of SAML IDP provider.").Short('i').Default("ADFS").EnumVar(&c.Provider, "ADFS", "ADFS2", "Ping", "JumpCloud", "Okta", "KeyCloak")
app.Flag("hostname", "The hostname of the SAML IDP server used to login.").StringVar(&c.Hostname)
app.Flag("URL", "The URL of the SAML IDP server used to login.").StringVar(&c.URL)
app.Flag("username", "The username used to login.").StringVar(&c.Username)
app.Flag("password", "The password used to login.").Envar("SAML2AWS_PASSWORD").StringVar(&c.Password)
app.Flag("role", "The ARN of the role to assume.").StringVar(&c.RoleArn)
Expand Down
6 changes: 3 additions & 3 deletions helper/credentials/saml.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
// LookupCredentials lookup an existing set of credentials and validate it.
func LookupCredentials(loginDetails *creds.LoginDetails) error {

username, password, err := CurrentHelper.Get(fmt.Sprintf("https://%s", loginDetails.Hostname))
username, password, err := CurrentHelper.Get(fmt.Sprintf("%s", loginDetails.URL))
if err != nil {
return err
}
Expand All @@ -21,10 +21,10 @@ func LookupCredentials(loginDetails *creds.LoginDetails) error {
}

// SaveCredentials save the user credentials.
func SaveCredentials(hostname, username, password string) error {
func SaveCredentials(url, username, password string) error {

creds := &Credentials{
ServerURL: fmt.Sprintf("https://%s", hostname),
ServerURL: fmt.Sprintf("%s", url),
Username: username,
Secret: password,
}
Expand Down
25 changes: 24 additions & 1 deletion input.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package saml2aws
import (
"bufio"
"fmt"
"net/url"
"os"
"strconv"
"strings"
Expand Down Expand Up @@ -34,7 +35,7 @@ func PromptForConfigurationDetails(idpAccount *cfg.IDPAccount) error {

fmt.Println("")

idpAccount.Hostname = promptFor("Hostname [%s]", idpAccount.Hostname)
idpAccount.URL = promptForURL("URL [%s]", idpAccount.URL)
idpAccount.Username = promptFor("Username [%s]", idpAccount.Username)

fmt.Println("")
Expand Down Expand Up @@ -141,3 +142,25 @@ func promptFor(promptString, defaultValue string) string {

return val
}

func promptForURL(promptString, defaultValue string) string {
var rawURL string

// do while
for {
rawURL = prompt.String(promptString, defaultValue)

if rawURL == "" {
rawURL = defaultValue
}

_, err := url.ParseRequestURI(rawURL)
if err != nil {
fmt.Println("please enter a valid url eg https://id.example.com")
} else {
break
}
}

return rawURL
}
10 changes: 5 additions & 5 deletions input_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ func TestLoginDetails_Validate(t *testing.T) {
type fields struct {
Username string
Password string
Hostname string
URL string
}
tests := []struct {
name string
Expand All @@ -19,16 +19,16 @@ func TestLoginDetails_Validate(t *testing.T) {
}{
// TODO: Add test cases.
{name: "hostname missing error", fields: fields{}, wantErr: true},
{name: "username missing error", fields: fields{Hostname: "id.example.com"}, wantErr: true},
{name: "password missing error", fields: fields{Hostname: "id.example.com", Username: "test"}, wantErr: true},
{name: "ok", fields: fields{Hostname: "id.example.com", Username: "test", Password: "test"}, wantErr: false},
{name: "username missing error", fields: fields{URL: "id.example.com"}, wantErr: true},
{name: "password missing error", fields: fields{URL: "id.example.com", Username: "test"}, wantErr: true},
{name: "ok", fields: fields{URL: "id.example.com", Username: "test", Password: "test"}, wantErr: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ld := &creds.LoginDetails{
Username: tt.fields.Username,
Password: tt.fields.Password,
Hostname: tt.fields.Hostname,
URL: tt.fields.URL,
}
if err := ld.Validate(); (err != nil) != tt.wantErr {
t.Errorf("LoginDetails.Validate() error = %v, wantErr %v", err, tt.wantErr)
Expand Down
15 changes: 11 additions & 4 deletions pkg/cfg/cfg.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cfg

import (
"net/url"

"github.com/fatih/structs"
"github.com/mitchellh/go-homedir"
"github.com/pkg/errors"
Expand All @@ -15,18 +17,23 @@ var DefaultConfigPath = "~/.saml2aws"

// IDPAccount saml IDP account
type IDPAccount struct {
Hostname string `ini:"hostname"`
URL string `ini:"url"`
Username string `ini:"username"`
Provider string `ini:"provider"`
MFA string `ini:"mfa"`
SkipVerify bool `ini:"skip_verify"`
Timeout int `ini:"timeout"`
}

// Validate validate the required / exptected fields are set
// Validate validate the required / expected fields are set
func (ia *IDPAccount) Validate() error {
if ia.Hostname == "" {
return errors.New("Hostname empty in idp account")
if ia.URL == "" {
return errors.New("URL empty in idp account")
}

_, err := url.Parse(ia.URL)
if err != nil {
return errors.New("URL parse failed")
}

if ia.Provider == "" {
Expand Down
15 changes: 9 additions & 6 deletions pkg/cfg/cfg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func TestNewConfigManagerLoad(t *testing.T) {

idpAccount, err := cfgm.LoadIDPAccount("test123")
require.Nil(t, err)
require.Equal(t, &IDPAccount{Hostname: "id.whatever.com", Username: "abc@whatever.com", Provider: "keycloak", MFA: "sms"}, idpAccount)
require.Equal(t, &IDPAccount{URL: "https://id.whatever.com", Username: "abc@whatever.com", Provider: "keycloak", MFA: "sms"}, idpAccount)

idpAccount, err = cfgm.LoadIDPAccount("test1234")
require.Nil(t, err)
Expand All @@ -40,9 +40,9 @@ func TestNewConfigManagerLoadVerify(t *testing.T) {

require.NotNil(t, cfgm)

idpAccount, err := cfgm.LoadIDPAccount("test123")
idpAccount, err := cfgm.LoadVerifyIDPAccount("test123")
require.Nil(t, err)
require.Equal(t, &IDPAccount{Hostname: "id.whatever.com", Username: "abc@whatever.com", Provider: "keycloak", MFA: "sms"}, idpAccount)
require.Equal(t, &IDPAccount{URL: "https://id.whatever.com", Username: "abc@whatever.com", Provider: "keycloak", MFA: "sms"}, idpAccount)

idpAccount, err = cfgm.LoadVerifyIDPAccount("test1234")
require.Equal(t, err, ErrIdpAccountNotFound)
Expand All @@ -55,12 +55,15 @@ func TestNewConfigManagerSave(t *testing.T) {
require.Nil(t, err)

err = cfgm.SaveIDPAccount("testing2", &IDPAccount{
Hostname: "test123",
URL: "https://id.whatever.com",
MFA: "none",
Provider: "Ping",
Username: "testetst",
Provider: "keycloak",
Username: "abc@whatever.com",
})
require.Nil(t, err)
idpAccount, err := cfgm.LoadVerifyIDPAccount("testing2")
require.Nil(t, err)
require.Equal(t, &IDPAccount{URL: "https://id.whatever.com", Username: "abc@whatever.com", Provider: "keycloak", MFA: "none"}, idpAccount)

os.Remove(throwAwayConfig)

Expand Down
12 changes: 7 additions & 5 deletions pkg/cfg/example/saml2aws.ini
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
[wolfeidau]
hostname = id.wolfe.id.au
username = mark@wolfe.id.au
provider = keycloak
mfa = totp

[test123]
hostname = id.whatever.com
username = abc@whatever.com
provider = keycloak
mfa = sms
username = abc@whatever.com
provider = keycloak
mfa = sms
url = https://id.whatever.com
skip_verify = false
timeout = 0

6 changes: 3 additions & 3 deletions pkg/creds/creds.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import "errors"
type LoginDetails struct {
Username string
Password string
Hostname string
URL string
}

// Validate validate the login details
func (ld *LoginDetails) Validate() error {
if ld.Hostname == "" {
return errors.New("Empty hostname")
if ld.URL == "" {
return errors.New("Empty URL")
}
if ld.Username == "" {
return errors.New("Empty username")
Expand Down
8 changes: 4 additions & 4 deletions pkg/creds/creds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func TestValidateEmptyLoginDetails(t *testing.T) {

require.Error(t, err)
}
func TestValidateEmptyHostnameLoginDetails(t *testing.T) {
func TestValidateEmptyURLLoginDetails(t *testing.T) {

ld := &LoginDetails{Username: "test", Password: "test"}

Expand All @@ -26,7 +26,7 @@ func TestValidateEmptyHostnameLoginDetails(t *testing.T) {

func TestValidateEmptyUsernameLoginDetails(t *testing.T) {

ld := &LoginDetails{Hostname: "test", Password: "test"}
ld := &LoginDetails{URL: "https://test.com", Password: "test"}

err := ld.Validate()

Expand All @@ -35,7 +35,7 @@ func TestValidateEmptyUsernameLoginDetails(t *testing.T) {
}
func TestValidateEmptyPasswordLoginDetails(t *testing.T) {

ld := &LoginDetails{Hostname: "test", Username: "test"}
ld := &LoginDetails{URL: "https://test.com", Username: "test"}

err := ld.Validate()

Expand All @@ -44,7 +44,7 @@ func TestValidateEmptyPasswordLoginDetails(t *testing.T) {

func TestValidateLoginDetails(t *testing.T) {

ld := &LoginDetails{Hostname: "test", Username: "test", Password: "test"}
ld := &LoginDetails{URL: "https://test.com", Username: "test", Password: "test"}

err := ld.Validate()

Expand Down
3 changes: 2 additions & 1 deletion pkg/provider/adfs/adfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ func New(idpAccount *cfg.IDPAccount) (*Client, error) {

// Authenticate authenticate to ADFS and return the data from the body of the SAML assertion.
func (ac *Client) Authenticate(loginDetails *creds.LoginDetails) (string, error) {

var authSubmitURL string
var samlAssertion string

adfsURL := fmt.Sprintf("https://%s/adfs/ls/IdpInitiatedSignOn.aspx?loginToRp=urn:amazon:webservices", loginDetails.Hostname)
adfsURL := fmt.Sprintf("%s/adfs/ls/IdpInitiatedSignOn.aspx?loginToRp=urn:amazon:webservices", loginDetails.URL)

res, err := ac.client.Get(adfsURL)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/provider/adfs2/adfs2.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (ac *Client) Authenticate(loginDetails *creds.LoginDetails) (string, error)
},
}

url := fmt.Sprintf("https://%s/adfs/ls/IdpInitiatedSignOn.aspx?loginToRp=urn:amazon:webservices", loginDetails.Hostname)
url := fmt.Sprintf("%s/adfs/ls/IdpInitiatedSignOn.aspx?loginToRp=urn:amazon:webservices", loginDetails.URL)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return samlAssertion, err
Expand Down
2 changes: 1 addition & 1 deletion pkg/provider/jumpcloud/jumpcloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (jc *Client) Authenticate(loginDetails *creds.LoginDetails) (string, error)
mfaRequired := false

authForm := url.Values{}
jumpCloudURL := fmt.Sprintf("https://%s", loginDetails.Hostname)
jumpCloudURL := loginDetails.URL

res, err := jc.client.Get(jumpCloudURL)
if err != nil {
Expand Down
4 changes: 1 addition & 3 deletions pkg/provider/keycloak/keycloak.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,7 @@ func (kc *Client) Authenticate(loginDetails *creds.LoginDetails) (string, error)
var samlAssertion string
authForm := url.Values{}

samlAssertion = ""

keyCloakURL := fmt.Sprintf("https://%s", loginDetails.Hostname)
keyCloakURL := loginDetails.URL

// fmt.Printf("KeyCloak URL: %s\n\n", keyCloakURL)

Expand Down
Loading

0 comments on commit 7c6d8be

Please sign in to comment.