Skip to content

Commit 1918984

Browse files
committed
api: add override mechanism for SOAP Header.Cookie
Sessions are consumed by API endpoints via one of: - HTTP Cookie (vim25 for example) - HTTP Header (vmodl2 /rest and /api) - SOAP Header (pbm, vslm, sms for example) The soap.Client had always set the SOAP Header cookie regardless if consumed by an API endpoint. Clients must now opt-in to this behavior if they need it, such as pbm and vslm. This also allows clients to change the name of soap.Header.Cookie (default is still "vcSessionCookie") vcsim: add override mechanism for session mapping On the simulator side, API endpoints can specify where a session is sourced from. Default is still the "vmware_soap_session" HTTP Cookie. The pbm simulator now specifies the "vcSessionCookie" SOAP Header, behaving as real pbm/VC does. Signed-off-by: Doug MacEachern <[email protected]>
1 parent ab30b51 commit 1918984

File tree

13 files changed

+102
-35
lines changed

13 files changed

+102
-35
lines changed

Diff for: pbm/client.go

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ type Client struct {
4949

5050
func NewClient(ctx context.Context, c *vim25.Client) (*Client, error) {
5151
sc := c.Client.NewServiceClient(Path, Namespace)
52+
sc.Cookie = sc.SessionCookie // vcSessionCookie soap.Header
5253

5354
req := types.PbmRetrieveServiceContent{
5455
This: ServiceInstance,

Diff for: pbm/client_test.go

+25
Original file line numberDiff line numberDiff line change
@@ -346,3 +346,28 @@ func TestSupportsEncryption(t *testing.T) {
346346
})
347347
})
348348
}
349+
350+
func TestClientCookie(t *testing.T) {
351+
simulator.Test(func(ctx context.Context, c *vim25.Client) {
352+
pbmc, err := pbm.NewClient(ctx, c)
353+
assert.NoError(t, err)
354+
assert.NotNil(t, pbmc)
355+
356+
// Using default / expected Header.Cookie.XMLName = vcSessionCookie
357+
ok, err := pbmc.SupportsEncryption(ctx, pbmsim.DefaultEncryptionProfileID)
358+
assert.NoError(t, err)
359+
assert.True(t, ok)
360+
361+
// Using invalid Header.Cookie.XMLName = myCookie
362+
myCookie := c.SessionCookie()
363+
myCookie.XMLName.Local = "myCookie"
364+
365+
pbmc.Client.Cookie = func() *soap.HeaderElement {
366+
return myCookie
367+
}
368+
369+
ok, err = pbmc.SupportsEncryption(ctx, pbmsim.DefaultEncryptionProfileID)
370+
assert.EqualError(t, err, "ServerFaultCode: NotAuthenticated")
371+
assert.False(t, ok)
372+
})
373+
}

Diff for: pbm/simulator/simulator.go

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ func New() *simulator.Registry {
5656
r := simulator.NewRegistry()
5757
r.Namespace = pbm.Namespace
5858
r.Path = pbm.Path
59+
r.Cookie = simulator.SOAPCookie
5960

6061
r.Put(&ServiceInstance{
6162
ManagedObjectReference: pbm.ServiceInstance,

Diff for: simulator/registry.go

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ type Registry struct {
7878
Namespace string
7979
Path string
8080
Handler func(*Context, *Method) (mo.Reference, types.BaseMethodFault)
81+
Cookie func(*Context) string
8182

8283
tagManager tagManager
8384
}

Diff for: simulator/session_manager.go

+21-4
Original file line numberDiff line numberDiff line change
@@ -329,12 +329,29 @@ type Context struct {
329329
Map *Registry
330330
}
331331

332+
func SOAPCookie(ctx *Context) string {
333+
if cookie := ctx.Header.Cookie; cookie != nil {
334+
return cookie.Value
335+
}
336+
return ""
337+
}
338+
339+
func HTTPCookie(ctx *Context) string {
340+
if cookie, err := ctx.req.Cookie(soap.SessionCookieName); err == nil {
341+
return cookie.Value
342+
}
343+
return ""
344+
}
345+
332346
// mapSession maps an HTTP cookie to a Session.
333347
func (c *Context) mapSession() {
334-
if cookie, err := c.req.Cookie(soap.SessionCookieName); err == nil {
335-
if val, ok := c.svc.sm.getSession(cookie.Value); ok {
336-
c.SetSession(val, false)
337-
}
348+
cookie := c.Map.Cookie
349+
if cookie == nil {
350+
cookie = HTTPCookie
351+
}
352+
353+
if val, ok := c.svc.sm.getSession(cookie(c)); ok {
354+
c.SetSession(val, false)
338355
}
339356
}
340357

Diff for: simulator/simulator.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2017-2023 VMware, Inc. All Rights Reserved.
2+
Copyright (c) 2017-2024 VMware, Inc. All Rights Reserved.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -497,7 +497,6 @@ func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
497497
Map: s.sdk[r.URL.Path],
498498
Context: context.Background(),
499499
}
500-
ctx.Map.WithLock(ctx, s.sm, ctx.mapSession)
501500

502501
var res soap.HasFault
503502
var soapBody interface{}
@@ -511,6 +510,7 @@ func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
511510
// Redirect any Fetch method calls to the PropertyCollector singleton
512511
method.This = ctx.Map.content().PropertyCollector
513512
}
513+
ctx.Map.WithLock(ctx, s.sm, ctx.mapSession)
514514
res = s.call(ctx, method)
515515
}
516516

Diff for: ssoadmin/client.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/*
2-
Copyright (c) 2018-2023 VMware, Inc. All Rights Reserved.
2+
Copyright (c) 2018-2024 VMware, Inc. All Rights Reserved.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
66
You may obtain a copy of the License at
77
8-
http://www.apache.org/licenses/LICENSE-2.0
8+
http://www.apache.org/licenses/LICENSE-2.0
99
1010
Unless required by applicable law or agreed to in writing, software
1111
distributed under the License is distributed on an "AS IS" BASIS,

Diff for: sts/client.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/*
2-
Copyright (c) 2018-2023 VMware, Inc. All Rights Reserved.
2+
Copyright (c) 2018-2024 VMware, Inc. All Rights Reserved.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
66
You may obtain a copy of the License at
77
8-
http://www.apache.org/licenses/LICENSE-2.0
8+
http://www.apache.org/licenses/LICENSE-2.0
99
1010
Unless required by applicable law or agreed to in writing, software
1111
distributed under the License is distributed on an "AS IS" BASIS,

Diff for: vim25/soap/client.go

+20-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2014-2023 VMware, Inc. All Rights Reserved.
2+
Copyright (c) 2014-2024 VMware, Inc. All Rights Reserved.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -86,7 +86,11 @@ type Client struct {
8686
Types types.Func `json:"types"`
8787
UserAgent string `json:"userAgent"`
8888

89-
cookie string
89+
// Cookie returns a value for the SOAP Header.Cookie.
90+
// This SOAP request header is used for authentication by
91+
// API endpoints such as pbm, vslm and sms.
92+
// When nil, no SOAP Header.Cookie is set.
93+
Cookie func() *HeaderElement
9094
insecureCookies bool
9195

9296
useJSON bool
@@ -210,6 +214,16 @@ func (c *Client) NewServiceClient(path string, namespace string) *Client {
210214
return c.newServiceClientWithTransport(path, namespace, c.t)
211215
}
212216

217+
// SessionCookie returns a SessionCookie with value of the vmware_soap_session http.Cookie.
218+
func (c *Client) SessionCookie() *HeaderElement {
219+
for _, cookie := range c.Jar.Cookies(c.URL()) {
220+
if cookie.Name == SessionCookieName {
221+
return &HeaderElement{Value: cookie.Value}
222+
}
223+
}
224+
return nil
225+
}
226+
213227
func (c *Client) newServiceClientWithTransport(path string, namespace string, t *http.Transport) *Client {
214228
vc := c.URL()
215229
u, err := url.Parse(path)
@@ -234,14 +248,6 @@ func (c *Client) newServiceClientWithTransport(path string, namespace string, t
234248
// Copy the cookies
235249
client.Client.Jar.SetCookies(u, c.Client.Jar.Cookies(u))
236250

237-
// Set SOAP Header cookie
238-
for _, cookie := range client.Jar.Cookies(u) {
239-
if cookie.Name == SessionCookieName {
240-
client.cookie = cookie.Value
241-
break
242-
}
243-
}
244-
245251
// Copy any query params (e.g. GOVMOMI_TUNNEL_PROXY_PORT used in testing)
246252
client.u.RawQuery = vc.RawQuery
247253

@@ -718,8 +724,10 @@ func (c *Client) soapRoundTrip(ctx context.Context, reqBody, resBody HasFault) e
718724
h.ID = id
719725
}
720726

721-
h.Cookie = c.cookie
722-
if h.Cookie != "" || h.ID != "" || h.Security != nil {
727+
if c.Cookie != nil {
728+
h.Cookie = c.Cookie()
729+
}
730+
if h.Cookie != nil || h.ID != "" || h.Security != nil {
723731
reqEnv.Header = &h // XML marshal header only if a field is set
724732
}
725733

Diff for: vim25/soap/json_client.go

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2023-2023 VMware, Inc. All Rights Reserved.
2+
Copyright (c) 2023-2024 VMware, Inc. All Rights Reserved.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -71,8 +71,10 @@ func (c *Client) invoke(ctx context.Context, this types.ManagedObjectReference,
7171
return err
7272
}
7373

74-
if len(c.cookie) != 0 {
75-
req.Header.Add(sessionHeader, c.cookie)
74+
if c.Cookie != nil {
75+
if cookie := c.Cookie(); cookie != nil {
76+
req.Header.Add(sessionHeader, cookie.Value)
77+
}
7678
}
7779

7880
result, err := getSOAPResultPtr(res)
@@ -156,8 +158,10 @@ func isError(statusCode int) bool {
156158
// session header.
157159
func (c *Client) checkForSessionHeader(resp *http.Response) {
158160
sessionKey := resp.Header.Get(sessionHeader)
159-
if len(sessionKey) > 0 {
160-
c.cookie = sessionKey
161+
if sessionKey != "" {
162+
c.Cookie = func() *HeaderElement {
163+
return &HeaderElement{Value: sessionKey}
164+
}
161165
}
162166
}
163167

Diff for: vim25/soap/json_client_test.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2023-2023 VMware, Inc. All Rights Reserved.
2+
Copyright (c) 2023-2024 VMware, Inc. All Rights Reserved.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -206,7 +206,9 @@ func TestFullRequestCycle(t *testing.T) {
206206
c := NewClient(addr, true)
207207
c.Namespace = "urn:vim25"
208208
c.Version = "8.0.0.1"
209-
c.cookie = "(original)"
209+
c.Cookie = func() *HeaderElement {
210+
return &HeaderElement{Value: "(original)"}
211+
}
210212
c.UseJSON(true)
211213

212214
c.Transport = &mockHTTP{

Diff for: vim25/soap/soap.go

+12-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/*
2-
Copyright (c) 2014-2018 VMware, Inc. All Rights Reserved.
2+
Copyright (c) 2014-2024 VMware, Inc. All Rights Reserved.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
66
You may obtain a copy of the License at
77
8-
http://www.apache.org/licenses/LICENSE-2.0
8+
http://www.apache.org/licenses/LICENSE-2.0
99
1010
Unless required by applicable law or agreed to in writing, software
1111
distributed under the License is distributed on an "AS IS" BASIS,
@@ -21,12 +21,18 @@ import (
2121
"github.com/vmware/govmomi/vim25/xml"
2222
)
2323

24+
// HeaderElement allows changing the default XMLName (e.g. Cookie's default of vcSessionCookie)
25+
type HeaderElement struct {
26+
XMLName xml.Name
27+
Value string `xml:",chardata"`
28+
}
29+
2430
// Header includes optional soap Header fields.
2531
type Header struct {
26-
Action string `xml:"-"` // Action is the 'SOAPAction' HTTP header value. Defaults to "Client.Namespace/Client.Version".
27-
Cookie string `xml:"vcSessionCookie,omitempty"` // Cookie is a vCenter session cookie that can be used with other SDK endpoints (e.g. pbm).
28-
ID string `xml:"operationID,omitempty"` // ID is the operationID used by ESX/vCenter logging for correlation.
29-
Security interface{} `xml:",omitempty"` // Security is used for SAML token authentication and request signing.
32+
Action string `xml:"-"` // Action is the 'SOAPAction' HTTP header value. Defaults to "Client.Namespace/Client.Version".
33+
Cookie *HeaderElement `xml:"vcSessionCookie,omitempty"` // Cookie is a vCenter session cookie that can be used with other SDK endpoints (e.g. pbm, vslm).
34+
ID string `xml:"operationID,omitempty"` // ID is the operationID used by ESX/vCenter logging for correlation.
35+
Security interface{} `xml:",omitempty"` // Security is used for SAML token authentication and request signing.
3036
}
3137

3238
type Envelope struct {

Diff for: vslm/client.go

+2
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ type Client struct {
4646

4747
func NewClient(ctx context.Context, c *vim25.Client) (*Client, error) {
4848
sc := c.Client.NewServiceClient(Path, Namespace)
49+
sc.Cookie = sc.SessionCookie // vcSessionCookie soap.Header
50+
4951
req := types.RetrieveContent{
5052
This: ServiceInstance,
5153
}

0 commit comments

Comments
 (0)