-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #46 from hashicorp/f-sp-auto-configure
Auto-configure Org/Project ID for SPs
- Loading branch information
Showing
6 changed files
with
273 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
```release-note:improvement | ||
auth: If authenticating as a service principal, automatically populate the profile with the organization and project ID. This allows using the CLI without instantiating the profile. | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,16 +7,29 @@ import ( | |
"testing" | ||
|
||
"github.com/hashicorp/hcp/internal/commands/auth/mocks" | ||
mock_iam_service "github.com/hashicorp/hcp/internal/pkg/api/mocks/github.com/hashicorp/hcp-sdk-go/clients/cloud-iam/stable/2019-12-10/client/iam_service" | ||
hcpAuth "github.com/hashicorp/hcp/internal/pkg/auth" | ||
"github.com/hashicorp/hcp/internal/pkg/iostreams" | ||
"github.com/hashicorp/hcp/internal/pkg/profile" | ||
"github.com/stretchr/testify/mock" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/hashicorp/hcp-sdk-go/auth" | ||
"github.com/hashicorp/hcp-sdk-go/auth/workload" | ||
"github.com/hashicorp/hcp-sdk-go/clients/cloud-iam/stable/2019-12-10/client/iam_service" | ||
"github.com/hashicorp/hcp-sdk-go/clients/cloud-iam/stable/2019-12-10/models" | ||
hcpconf "github.com/hashicorp/hcp-sdk-go/config" | ||
) | ||
|
||
// getIAMClientFunc returns a getIAMClientFunc and a mock IAM client and returns | ||
// the mock IAM client for use in tests. | ||
func getIAMClientFunc(t *testing.T) (GetIAMClientFunc, *mock_iam_service.MockClientService) { | ||
client := mock_iam_service.NewMockClientService(t) | ||
return func(_ hcpconf.HCPConfig) (iam_service.ClientService, error) { | ||
return client, nil | ||
}, client | ||
} | ||
|
||
func TestLoginOpts_Validate(t *testing.T) { | ||
t.Parallel() | ||
r := require.New(t) | ||
|
@@ -56,17 +69,35 @@ func TestLogin_Browser(t *testing.T) { | |
return m, nil | ||
} | ||
|
||
getter, iamClient := getIAMClientFunc(t) | ||
io := iostreams.Test() | ||
o := &LoginOpts{ | ||
IO: io, | ||
Profile: profile.TestProfile(t), | ||
GetIAM: getter, | ||
ConfigFn: newHCP, | ||
CredentialDir: t.TempDir(), | ||
} | ||
|
||
// Expect the mock to be called | ||
m.EXPECT().Token().Return(nil, nil).Twice() | ||
|
||
// Expect the IAM client to be called | ||
iamClient.EXPECT().IamServiceGetCallerIdentity(mock.Anything, mock.Anything). | ||
Return(&iam_service.IamServiceGetCallerIdentityOK{ | ||
Payload: &models.HashicorpCloudIamGetCallerIdentityResponse{ | ||
Principal: &models.HashicorpCloudIamPrincipal{ | ||
ID: "123", | ||
Type: models.HashicorpCloudIamPrincipalTypePRINCIPALTYPEUSER.Pointer(), | ||
User: &models.HashicorpCloudIamUserPrincipal{ | ||
Email: "[email protected]", | ||
FullName: "Foo", | ||
ID: "123", | ||
}, | ||
}, | ||
}, | ||
}, nil) | ||
|
||
// Run the command | ||
r.NoError(loginRun(o)) | ||
r.Contains(io.Error.String(), "Successfully logged in!") | ||
|
@@ -80,17 +111,38 @@ func TestLogin_Browser(t *testing.T) { | |
|
||
func TestLogin_SP(t *testing.T) { | ||
t.Parallel() | ||
t.Run("profile has org/project", func(t *testing.T) { | ||
t.Parallel() | ||
testLoginSP(t, true) | ||
}) | ||
|
||
t.Run("profile does not have org/project", func(t *testing.T) { | ||
t.Parallel() | ||
testLoginSP(t, false) | ||
}) | ||
|
||
} | ||
|
||
func testLoginSP(t *testing.T, profilePreconfigured bool) { | ||
r := require.New(t) | ||
|
||
m := mocks.NewMockHCPConfig(t) | ||
newHCP := func(opts ...hcpconf.HCPConfigOption) (hcpconf.HCPConfig, error) { | ||
return m, nil | ||
} | ||
|
||
p := profile.TestProfile(t) | ||
if profilePreconfigured { | ||
p.OrganizationID = "preconfigured-org" | ||
p.ProjectID = "preconfigured-project" | ||
} | ||
|
||
getter, iamClient := getIAMClientFunc(t) | ||
io := iostreams.Test() | ||
o := &LoginOpts{ | ||
IO: io, | ||
Profile: profile.TestProfile(t), | ||
Profile: p, | ||
GetIAM: getter, | ||
ConfigFn: newHCP, | ||
CredentialDir: t.TempDir(), | ||
|
||
|
@@ -101,6 +153,25 @@ func TestLogin_SP(t *testing.T) { | |
// Expect the mock to be called | ||
m.EXPECT().Token().Return(nil, nil) | ||
|
||
// Expect the IAM client to be called | ||
orgID, projectID := "org-123", "project-456" | ||
if !profilePreconfigured { | ||
iamClient.EXPECT().IamServiceGetCallerIdentity(mock.Anything, mock.Anything). | ||
Return(&iam_service.IamServiceGetCallerIdentityOK{ | ||
Payload: &models.HashicorpCloudIamGetCallerIdentityResponse{ | ||
Principal: &models.HashicorpCloudIamPrincipal{ | ||
ID: "123", | ||
Type: models.HashicorpCloudIamPrincipalTypePRINCIPALTYPESERVICE.Pointer(), | ||
Service: &models.HashicorpCloudIamServicePrincipal{ | ||
ID: "123", | ||
OrganizationID: orgID, | ||
ProjectID: projectID, | ||
}, | ||
}, | ||
}, | ||
}, nil) | ||
} | ||
|
||
// Run the command | ||
r.NoError(loginRun(o)) | ||
r.Contains(io.Error.String(), "Successfully logged in!") | ||
|
@@ -114,6 +185,15 @@ func TestLogin_SP(t *testing.T) { | |
r.Equal(auth.CredentialFileSchemeServicePrincipal, credFile.Scheme) | ||
r.Equal(o.ClientID, credFile.Oauth.ClientID) | ||
r.Equal(o.ClientSecret, credFile.Oauth.ClientSecret) | ||
|
||
// Check the profile | ||
if profilePreconfigured { | ||
r.NotEqual(orgID, o.Profile.OrganizationID) | ||
r.NotEqual(projectID, o.Profile.ProjectID) | ||
} else { | ||
r.Equal(orgID, o.Profile.OrganizationID) | ||
r.Equal(projectID, o.Profile.ProjectID) | ||
} | ||
} | ||
|
||
func TestLogin_CredFile(t *testing.T) { | ||
|
@@ -145,10 +225,12 @@ func TestLogin_CredFile(t *testing.T) { | |
} | ||
r.NoError(json.NewEncoder(f).Encode(&credFile)) | ||
|
||
getter, iamClient := getIAMClientFunc(t) | ||
io := iostreams.Test() | ||
o := &LoginOpts{ | ||
IO: io, | ||
Profile: profile.TestProfile(t), | ||
GetIAM: getter, | ||
ConfigFn: newHCP, | ||
CredentialDir: t.TempDir(), | ||
CredentialFile: f.Name(), | ||
|
@@ -157,6 +239,23 @@ func TestLogin_CredFile(t *testing.T) { | |
// Expect the mock to be called | ||
m.EXPECT().Token().Return(nil, nil) | ||
|
||
// Expect the IAM client to be called | ||
orgID, projectID := "org-123", "project-456" | ||
iamClient.EXPECT().IamServiceGetCallerIdentity(mock.Anything, mock.Anything). | ||
Return(&iam_service.IamServiceGetCallerIdentityOK{ | ||
Payload: &models.HashicorpCloudIamGetCallerIdentityResponse{ | ||
Principal: &models.HashicorpCloudIamPrincipal{ | ||
ID: "123", | ||
Type: models.HashicorpCloudIamPrincipalTypePRINCIPALTYPESERVICE.Pointer(), | ||
Service: &models.HashicorpCloudIamServicePrincipal{ | ||
ID: "123", | ||
OrganizationID: orgID, | ||
ProjectID: projectID, | ||
}, | ||
}, | ||
}, | ||
}, nil) | ||
|
||
// Run the command | ||
r.NoError(loginRun(o)) | ||
r.Contains(io.Error.String(), "Successfully logged in!") | ||
|
@@ -171,6 +270,11 @@ func TestLogin_CredFile(t *testing.T) { | |
var copied auth.CredentialFile | ||
r.NoError(json.NewDecoder(copiedCF).Decode(&copied)) | ||
r.EqualValues(credFile, copied) | ||
|
||
// Check the profile | ||
r.Equal(orgID, o.Profile.OrganizationID) | ||
r.Equal(projectID, o.Profile.ProjectID) | ||
|
||
} | ||
|
||
func Test_getHCPConfig(t *testing.T) { | ||
|
Oops, something went wrong.