Skip to content

Commit

Permalink
sts: support issuing HoK token using HoK token
Browse files Browse the repository at this point in the history
  • Loading branch information
dougm committed Feb 2, 2022
1 parent 2d7cd13 commit 6b4e239
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 13 deletions.
7 changes: 7 additions & 0 deletions govc/session/login.go
Expand Up @@ -215,6 +215,13 @@ func (cmd *login) loginByToken(ctx context.Context, c *vim25.Client) error {
},
}

// something behind the LoginByToken scene requires a version from /sdk/vimServiceVersions.xml
// in the SOAPAction header. For example, if vim25.Version is "7.0" but the service version is "6.3",
// LoginByToken fails with: 'VersionMismatchFaultCode: Unsupported version URI "urn:vim25/7.0"'
if c.Version == vim25.Version {
_ = c.UseServiceVersion()
}

return session.NewManager(c).LoginByToken(c.WithHeader(ctx, header))
}

Expand Down
3 changes: 3 additions & 0 deletions sts/client.go
Expand Up @@ -71,6 +71,9 @@ func (c *Client) RoundTrip(ctx context.Context, req, res soap.HasFault) error {

// TokenRequest parameters for issuing a SAML token.
// At least one of Userinfo or Certificate must be specified.
// When `TokenRequest.Certificate` is set, the `tls.Certificate.PrivateKey` field must be set as it is required to sign the request.
// When the `tls.Certificate.Certificate` field is not set, the request Assertion header is set to that of the TokenRequest.Token.
// Otherwise `tls.Certificate.Certificate` is used as the BinarySecurityToken in the request.
type TokenRequest struct {
Userinfo *url.Userinfo // Userinfo when set issues a Bearer token
Certificate *tls.Certificate // Certificate when set issues a HoK token
Expand Down
72 changes: 72 additions & 0 deletions sts/client_test.go
Expand Up @@ -122,6 +122,7 @@ func TestIssueHOK(t *testing.T) {
if err != nil {
log.Fatal(err)
}
_ = c.UseServiceVersion()

if !c.IsVC() {
t.SkipNow()
Expand Down Expand Up @@ -161,6 +162,75 @@ func TestIssueHOK(t *testing.T) {
log.Printf("current time=%s", now)
}

func TestIssueTokenByToken(t *testing.T) {
ctx := context.Background()
url := os.Getenv("GOVC_TEST_URL")
if url == "" {
t.SkipNow()
}

u, err := soap.ParseURL(url)
if err != nil {
t.Fatal(err)
}

vc1, err := vim25.NewClient(ctx, soap.NewClient(u, true))
if err != nil {
log.Fatal(err)
}
_ = vc1.UseServiceVersion()

if !vc1.IsVC() {
t.SkipNow()
}

vc2, err := vim25.NewClient(ctx, soap.NewClient(u, true))
if err != nil {
log.Fatal(err)
}
_ = vc2.UseServiceVersion()

sts1, err := NewClient(ctx, vc1)
if err != nil {
t.Fatal(err)
}

if err = solutionUserCreate(ctx, u.User, sts1, vc1); err != nil {
t.Fatal(err)
}

sts2, err := NewClient(ctx, vc2)
if err != nil {
t.Fatal(err)
}

req1 := TokenRequest{
Certificate: solutionUserCert(),
Delegatable: true,
}

signer1, err := sts1.Issue(ctx, req1)
if err != nil {
t.Fatal(err)
}

req2 := signer1.NewRequest()
// use Assertion header instead of BinarySecurityToken
req2.Certificate = &tls.Certificate{PrivateKey: req1.Certificate.PrivateKey}

signer2, err := sts2.Issue(ctx, req2)
if err != nil {
t.Fatal(err)
}

header := soap.Header{Security: signer2}

err = session.NewManager(vc2).LoginByToken(vc2.WithHeader(ctx, header))
if err != nil {
t.Fatal(err)
}
}

func TestIssueBearer(t *testing.T) {
ctx := context.Background()
url := os.Getenv("GOVC_TEST_URL")
Expand All @@ -177,6 +247,7 @@ func TestIssueBearer(t *testing.T) {
if err != nil {
log.Fatal(err)
}
_ = c.UseServiceVersion()

if !c.IsVC() {
t.SkipNow()
Expand Down Expand Up @@ -233,6 +304,7 @@ func TestIssueActAs(t *testing.T) {
if err != nil {
log.Fatal(err)
}
_ = c.UseServiceVersion()

if !c.IsVC() {
t.SkipNow()
Expand Down
34 changes: 21 additions & 13 deletions sts/signer.go
Expand Up @@ -128,20 +128,28 @@ func (s *Signer) Sign(env soap.Envelope) ([]byte, error) {
req := x.RequestSecurityToken()
c14n = req.C14N()
body = req.String()
id := newID()

info.SecurityTokenReference = &internal.SecurityTokenReference{
Reference: &internal.SecurityReference{
URI: "#" + id,
ValueType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3",
},
}

header.BinarySecurityToken = &internal.BinarySecurityToken{
EncodingType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary",
ValueType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3",
ID: id,
Value: base64.StdEncoding.EncodeToString(s.Certificate.Certificate[0]),
if len(s.Certificate.Certificate) == 0 {
header.Assertion = s.Token
if err := s.setTokenReference(&info); err != nil {
return nil, err
}
} else {
id := newID()

header.BinarySecurityToken = &internal.BinarySecurityToken{
EncodingType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary",
ValueType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3",
ID: id,
Value: base64.StdEncoding.EncodeToString(s.Certificate.Certificate[0]),
}

info.SecurityTokenReference = &internal.SecurityTokenReference{
Reference: &internal.SecurityReference{
URI: "#" + id,
ValueType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3",
},
}
}
}
// When requesting HoK token for interactive user, request will have both priv. key and username/password.
Expand Down

0 comments on commit 6b4e239

Please sign in to comment.