Skip to content

Commit 803b636

Browse files
committed
sts: support issuing HoK token using HoK token
1 parent fd21c74 commit 803b636

File tree

4 files changed

+103
-13
lines changed

4 files changed

+103
-13
lines changed

govc/session/login.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,13 @@ func (cmd *login) loginByToken(ctx context.Context, c *vim25.Client) error {
215215
},
216216
}
217217

218+
// something behind the LoginByToken scene requires a version from /sdk/vimServiceVersions.xml
219+
// in the SOAPAction header. For example, if vim25.Version is "7.0" but the service version is "6.3",
220+
// LoginByToken fails with: 'VersionMismatchFaultCode: Unsupported version URI "urn:vim25/7.0"'
221+
if c.Version == vim25.Version {
222+
_ = c.UseServiceVersion()
223+
}
224+
218225
return session.NewManager(c).LoginByToken(c.WithHeader(ctx, header))
219226
}
220227

sts/client.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ func (c *Client) RoundTrip(ctx context.Context, req, res soap.HasFault) error {
7171

7272
// TokenRequest parameters for issuing a SAML token.
7373
// At least one of Userinfo or Certificate must be specified.
74+
// When `TokenRequest.Certificate` is set, the `tls.Certificate.PrivateKey` field must be set as it is required to sign the request.
75+
// When the `tls.Certificate.Certificate` field is not set, the request Assertion header is set to that of the TokenRequest.Token.
76+
// Otherwise `tls.Certificate.Certificate` is used as the BinarySecurityToken in the request.
7477
type TokenRequest struct {
7578
Userinfo *url.Userinfo // Userinfo when set issues a Bearer token
7679
Certificate *tls.Certificate // Certificate when set issues a HoK token

sts/client_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ func TestIssueHOK(t *testing.T) {
122122
if err != nil {
123123
log.Fatal(err)
124124
}
125+
_ = c.UseServiceVersion()
125126

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

165+
func TestIssueTokenByToken(t *testing.T) {
166+
ctx := context.Background()
167+
url := os.Getenv("GOVC_TEST_URL")
168+
if url == "" {
169+
t.SkipNow()
170+
}
171+
172+
u, err := soap.ParseURL(url)
173+
if err != nil {
174+
t.Fatal(err)
175+
}
176+
177+
vc1, err := vim25.NewClient(ctx, soap.NewClient(u, true))
178+
if err != nil {
179+
log.Fatal(err)
180+
}
181+
_ = vc1.UseServiceVersion()
182+
183+
if !vc1.IsVC() {
184+
t.SkipNow()
185+
}
186+
187+
vc2, err := vim25.NewClient(ctx, soap.NewClient(u, true))
188+
if err != nil {
189+
log.Fatal(err)
190+
}
191+
_ = vc2.UseServiceVersion()
192+
193+
sts1, err := NewClient(ctx, vc1)
194+
if err != nil {
195+
t.Fatal(err)
196+
}
197+
198+
if err = solutionUserCreate(ctx, u.User, sts1, vc1); err != nil {
199+
t.Fatal(err)
200+
}
201+
202+
sts2, err := NewClient(ctx, vc2)
203+
if err != nil {
204+
t.Fatal(err)
205+
}
206+
207+
req1 := TokenRequest{
208+
Certificate: solutionUserCert(),
209+
Delegatable: true,
210+
}
211+
212+
signer1, err := sts1.Issue(ctx, req1)
213+
if err != nil {
214+
t.Fatal(err)
215+
}
216+
217+
req2 := signer1.NewRequest()
218+
// use Assertion header instead of BinarySecurityToken
219+
req2.Certificate = &tls.Certificate{PrivateKey: req1.Certificate.PrivateKey}
220+
221+
signer2, err := sts2.Issue(ctx, req2)
222+
if err != nil {
223+
t.Fatal(err)
224+
}
225+
226+
header := soap.Header{Security: signer2}
227+
228+
err = session.NewManager(vc2).LoginByToken(vc2.WithHeader(ctx, header))
229+
if err != nil {
230+
t.Fatal(err)
231+
}
232+
}
233+
164234
func TestIssueBearer(t *testing.T) {
165235
ctx := context.Background()
166236
url := os.Getenv("GOVC_TEST_URL")
@@ -177,6 +247,7 @@ func TestIssueBearer(t *testing.T) {
177247
if err != nil {
178248
log.Fatal(err)
179249
}
250+
_ = c.UseServiceVersion()
180251

181252
if !c.IsVC() {
182253
t.SkipNow()
@@ -233,6 +304,7 @@ func TestIssueActAs(t *testing.T) {
233304
if err != nil {
234305
log.Fatal(err)
235306
}
307+
_ = c.UseServiceVersion()
236308

237309
if !c.IsVC() {
238310
t.SkipNow()

sts/signer.go

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -128,20 +128,28 @@ func (s *Signer) Sign(env soap.Envelope) ([]byte, error) {
128128
req := x.RequestSecurityToken()
129129
c14n = req.C14N()
130130
body = req.String()
131-
id := newID()
132131

133-
info.SecurityTokenReference = &internal.SecurityTokenReference{
134-
Reference: &internal.SecurityReference{
135-
URI: "#" + id,
136-
ValueType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3",
137-
},
138-
}
139-
140-
header.BinarySecurityToken = &internal.BinarySecurityToken{
141-
EncodingType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary",
142-
ValueType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3",
143-
ID: id,
144-
Value: base64.StdEncoding.EncodeToString(s.Certificate.Certificate[0]),
132+
if len(s.Certificate.Certificate) == 0 {
133+
header.Assertion = s.Token
134+
if err := s.setTokenReference(&info); err != nil {
135+
return nil, err
136+
}
137+
} else {
138+
id := newID()
139+
140+
header.BinarySecurityToken = &internal.BinarySecurityToken{
141+
EncodingType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary",
142+
ValueType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3",
143+
ID: id,
144+
Value: base64.StdEncoding.EncodeToString(s.Certificate.Certificate[0]),
145+
}
146+
147+
info.SecurityTokenReference = &internal.SecurityTokenReference{
148+
Reference: &internal.SecurityReference{
149+
URI: "#" + id,
150+
ValueType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3",
151+
},
152+
}
145153
}
146154
}
147155
// When requesting HoK token for interactive user, request will have both priv. key and username/password.

0 commit comments

Comments
 (0)