vSphere Cloud Provider: update vmware/gomvomi godeps

This commit is contained in:
Doug MacEachern
2018-05-14 14:09:20 -07:00
parent 83768d286c
commit c340f6f9a4
55 changed files with 8733 additions and 596 deletions

193
vendor/github.com/vmware/govmomi/sts/client.go generated vendored Normal file
View File

@@ -0,0 +1,193 @@
/*
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package sts
import (
"context"
"crypto/tls"
"errors"
"net/url"
"time"
"github.com/vmware/govmomi/lookup"
"github.com/vmware/govmomi/lookup/types"
"github.com/vmware/govmomi/sts/internal"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/soap"
)
const (
Namespace = "oasis:names:tc:SAML:2.0:assertion"
Path = "/sts/STSService"
)
// Client is a soap.Client targeting the STS (Secure Token Service) API endpoint.
type Client struct {
*soap.Client
}
// NewClient returns a client targeting the STS API endpoint.
// The Client.URL will be set to that of the Lookup Service's endpoint registration,
// as the SSO endpoint can be external to vCenter. If the Lookup Service is not available,
// URL defaults to Path on the vim25.Client.URL.Host.
func NewClient(ctx context.Context, c *vim25.Client) (*Client, error) {
filter := &types.LookupServiceRegistrationFilter{
ServiceType: &types.LookupServiceRegistrationServiceType{
Product: "com.vmware.cis",
Type: "sso:sts",
},
EndpointType: &types.LookupServiceRegistrationEndpointType{
Protocol: "wsTrust",
Type: "com.vmware.cis.cs.identity.sso",
},
}
url := lookup.EndpointURL(ctx, c, Path, filter)
sc := c.Client.NewServiceClient(url, Namespace)
return &Client{sc}, nil
}
// TokenRequest parameters for issuing a SAML token.
// At least one of Userinfo or Certificate must be specified.
type TokenRequest struct {
Userinfo *url.Userinfo // Userinfo when set issues a Bearer token
Certificate *tls.Certificate // Certificate when set issues a HoK token
Lifetime time.Duration // Lifetime is the token's lifetime, defaults to 10m
Renewable bool // Renewable allows the issued token to be renewed
Delegatable bool // Delegatable allows the issued token to be delegated (e.g. for use with ActAs)
Token string // Token for Renew request or Issue request ActAs identity
}
func (c *Client) newRequest(req TokenRequest, kind string, s *Signer) (internal.RequestSecurityToken, error) {
if req.Lifetime == 0 {
req.Lifetime = 5 * time.Minute
}
created := time.Now().UTC()
rst := internal.RequestSecurityToken{
TokenType: c.Namespace,
RequestType: "http://docs.oasis-open.org/ws-sx/ws-trust/200512/" + kind,
SignatureAlgorithm: internal.SHA256,
Lifetime: &internal.Lifetime{
Created: created.Format(internal.Time),
Expires: created.Add(req.Lifetime).Format(internal.Time),
},
Renewing: &internal.Renewing{
Allow: req.Renewable,
// /wst:RequestSecurityToken/wst:Renewing/@OK
// "It NOT RECOMMENDED to use this as it can leave you open to certain types of security attacks.
// Issuers MAY restrict the period after expiration during which time the token can be renewed.
// This window is governed by the issuer's policy."
OK: false,
},
Delegatable: req.Delegatable,
}
if req.Certificate == nil {
if req.Userinfo == nil {
return rst, errors.New("one of TokenRequest Certificate or Userinfo is required")
}
rst.KeyType = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer"
} else {
rst.KeyType = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey"
rst.UseKey = &internal.UseKey{Sig: newID()}
s.keyID = rst.UseKey.Sig
}
return rst, nil
}
func (s *Signer) setLifetime(lifetime *internal.Lifetime) error {
var err error
if lifetime != nil {
s.Lifetime.Created, err = time.Parse(internal.Time, lifetime.Created)
if err == nil {
s.Lifetime.Expires, err = time.Parse(internal.Time, lifetime.Expires)
}
}
return err
}
// Issue is used to request a security token.
// The returned Signer can be used to sign SOAP requests, such as the SessionManager LoginByToken method and the RequestSecurityToken method itself.
// One of TokenRequest Certificate or Userinfo is required, with Certificate taking precedence.
// When Certificate is set, a Holder-of-Key token will be requested. Otherwise, a Bearer token is requested with the Userinfo credentials.
// See: http://docs.oasis-open.org/ws-sx/ws-trust/v1.4/errata01/os/ws-trust-1.4-errata01-os-complete.html#_Toc325658937
func (c *Client) Issue(ctx context.Context, req TokenRequest) (*Signer, error) {
s := &Signer{
Certificate: req.Certificate,
user: req.Userinfo,
}
rst, err := c.newRequest(req, "Issue", s)
if err != nil {
return nil, err
}
if req.Token != "" {
rst.ActAs = &internal.Target{
Token: req.Token,
}
}
header := soap.Header{
Security: s,
Action: rst.Action(),
}
res, err := internal.Issue(c.WithHeader(ctx, header), c, &rst)
if err != nil {
return nil, err
}
s.Token = res.RequestSecurityTokenResponse.RequestedSecurityToken.Assertion
return s, s.setLifetime(res.RequestSecurityTokenResponse.Lifetime)
}
// Renew is used to request a security token renewal.
func (c *Client) Renew(ctx context.Context, req TokenRequest) (*Signer, error) {
s := &Signer{
Certificate: req.Certificate,
}
rst, err := c.newRequest(req, "Renew", s)
if err != nil {
return nil, err
}
if req.Token == "" {
return nil, errors.New("TokenRequest Token is required")
}
rst.RenewTarget = &internal.Target{Token: req.Token}
header := soap.Header{
Security: s,
Action: rst.Action(),
}
res, err := internal.Renew(c.WithHeader(ctx, header), c, &rst)
if err != nil {
return nil, err
}
s.Token = res.RequestedSecurityToken.Assertion
return s, s.setLifetime(res.Lifetime)
}

694
vendor/github.com/vmware/govmomi/sts/internal/types.go generated vendored Normal file
View File

@@ -0,0 +1,694 @@
/*
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package internal
// The sts/internal package provides the types for invoking the sts.Issue method.
// The sts.Issue and SessionManager LoginByToken methods require an XML signature.
// Unlike the JRE and .NET runtimes, the Go stdlib does not support XML signing.
// We should considering contributing to the goxmldsig package and gosaml2 to meet
// the needs of sts.Issue rather than maintaining this package long term.
// The tricky part of xmldig is the XML canonicalization (C14N), which is responsible
// for most of the make-your-eyes bleed XML formatting in this package.
// C14N is also why some structures use xml.Name without a field tag and methods modify the xml.Name directly,
// though also working around Go's handling of XML namespace prefixes.
// Most of the types in this package were originally generated from the wsdl and hacked up gen/ scripts,
// but have since been modified by hand.
import (
"bytes"
"context"
"crypto/sha256"
"encoding/base64"
"fmt"
"log"
"path"
"reflect"
"strings"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
"github.com/vmware/govmomi/vim25/xml"
)
const (
XSI = "http://www.w3.org/2001/XMLSchema-instance"
WSU = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
DSIG = "http://www.w3.org/2000/09/xmldsig#"
SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
Time = "2006-01-02T15:04:05.000Z"
)
// Security is used as soap.Envelope.Header.Security when signing requests.
type Security struct {
XMLName xml.Name `xml:"wsse:Security"`
WSSE string `xml:"xmlns:wsse,attr"`
WSU string `xml:"xmlns:wsu,attr"`
Timestamp Timestamp
BinarySecurityToken *BinarySecurityToken `xml:",omitempty"`
UsernameToken *UsernameToken `xml:",omitempty"`
Assertion string `xml:",innerxml"`
Signature *Signature `xml:",omitempty"`
}
type Timestamp struct {
XMLName xml.Name `xml:"wsu:Timestamp"`
NS string `xml:"xmlns:wsu,attr"`
ID string `xml:"wsu:Id,attr"`
Created string `xml:"wsu:Created"`
Expires string `xml:"wsu:Expires"`
}
func (t *Timestamp) C14N() string {
return Marshal(t)
}
type BinarySecurityToken struct {
XMLName xml.Name `xml:"wsse:BinarySecurityToken"`
EncodingType string `xml:"EncodingType,attr"`
ValueType string `xml:"ValueType,attr"`
ID string `xml:"wsu:Id,attr"`
Value string `xml:",chardata"`
}
type UsernameToken struct {
XMLName xml.Name `xml:"wsse:UsernameToken"`
Username string `xml:"wsse:Username"`
Password string `xml:"wsse:Password"`
}
type Signature struct {
XMLName xml.Name
NS string `xml:"xmlns:ds,attr"`
ID string `xml:"Id,attr"`
SignedInfo SignedInfo
SignatureValue Value
KeyInfo KeyInfo
}
func (s *Signature) C14N() string {
return fmt.Sprintf(`<ds:Signature xmlns:ds="%s">%s%s%s</ds:Signature>`,
DSIG, s.SignedInfo.C14N(), s.SignatureValue.C14N(), s.KeyInfo.C14N())
}
type SignedInfo struct {
XMLName xml.Name
NS string `xml:"xmlns:ds,attr,omitempty"`
CanonicalizationMethod Method
SignatureMethod Method
Reference []Reference
}
func (s SignedInfo) C14N() string {
ns := "" // empty in ActAs c14n form for example
if s.NS != "" {
ns = fmt.Sprintf(` xmlns:ds="%s"`, s.NS)
}
c14n := []string{fmt.Sprintf("<ds:SignedInfo%s>", ns)}
c14n = append(c14n, s.CanonicalizationMethod.C14N(), s.SignatureMethod.C14N())
for i := range s.Reference {
c14n = append(c14n, s.Reference[i].C14N())
}
c14n = append(c14n, "</ds:SignedInfo>")
return strings.Join(c14n, "")
}
type Method struct {
XMLName xml.Name
Algorithm string `xml:",attr"`
}
func (m *Method) C14N() string {
return mkns("ds", m, &m.XMLName)
}
type Value struct {
XMLName xml.Name
Value string `xml:",innerxml"`
}
func (v *Value) C14N() string {
return mkns("ds", v, &v.XMLName)
}
type Reference struct {
XMLName xml.Name
URI string `xml:",attr"`
Transforms Transforms
DigestMethod Method
DigestValue Value
}
func (r Reference) C14N() string {
for i := range r.Transforms.Transform {
t := &r.Transforms.Transform[i]
t.XMLName.Local = "ds:Transform"
t.XMLName.Space = ""
if t.InclusiveNamespaces != nil {
name := &t.InclusiveNamespaces.XMLName
if !strings.HasPrefix(name.Local, "ec:") {
name.Local = "ec:" + name.Local
name.Space = ""
}
t.InclusiveNamespaces.NS = t.Algorithm
}
}
c14n := []string{
fmt.Sprintf(`<ds:Reference URI="%s">`, r.URI),
r.Transforms.C14N(),
r.DigestMethod.C14N(),
r.DigestValue.C14N(),
"</ds:Reference>",
}
return strings.Join(c14n, "")
}
func NewReference(id string, val string) Reference {
sum := sha256.Sum256([]byte(val))
return Reference{
XMLName: xml.Name{Local: "ds:Reference"},
URI: "#" + id,
Transforms: Transforms{
XMLName: xml.Name{Local: "ds:Transforms"},
Transform: []Transform{
Transform{
XMLName: xml.Name{Local: "ds:Transform"},
Algorithm: "http://www.w3.org/2001/10/xml-exc-c14n#",
},
},
},
DigestMethod: Method{
XMLName: xml.Name{Local: "ds:DigestMethod"},
Algorithm: "http://www.w3.org/2001/04/xmlenc#sha256",
},
DigestValue: Value{
XMLName: xml.Name{Local: "ds:DigestValue"},
Value: base64.StdEncoding.EncodeToString(sum[:]),
},
}
}
type Transforms struct {
XMLName xml.Name
Transform []Transform
}
func (t *Transforms) C14N() string {
return mkns("ds", t, &t.XMLName)
}
type Transform struct {
XMLName xml.Name
Algorithm string `xml:",attr"`
InclusiveNamespaces *InclusiveNamespaces `xml:",omitempty"`
}
type InclusiveNamespaces struct {
XMLName xml.Name
NS string `xml:"xmlns:ec,attr,omitempty"`
PrefixList string `xml:",attr"`
}
type X509Data struct {
XMLName xml.Name
X509Certificate string `xml:",innerxml"`
}
type KeyInfo struct {
XMLName xml.Name
NS string `xml:"xmlns:ds,attr,omitempty"`
SecurityTokenReference *SecurityTokenReference `xml:",omitempty"`
X509Data *X509Data `xml:",omitempty"`
}
func (o *KeyInfo) C14N() string {
names := []*xml.Name{
&o.XMLName,
}
if o.SecurityTokenReference != nil {
names = append(names, &o.SecurityTokenReference.XMLName)
}
if o.X509Data != nil {
names = append(names, &o.X509Data.XMLName)
}
return mkns("ds", o, names...)
}
type SecurityTokenReference struct {
XMLName xml.Name `xml:"wsse:SecurityTokenReference"`
WSSE11 string `xml:"xmlns:wsse11,attr,omitempty"`
TokenType string `xml:"wsse11:TokenType,attr,omitempty"`
Reference *SecurityReference `xml:",omitempty"`
KeyIdentifier *KeyIdentifier `xml:",omitempty"`
}
type SecurityReference struct {
XMLName xml.Name `xml:"wsse:Reference"`
URI string `xml:",attr"`
ValueType string `xml:",attr"`
}
type KeyIdentifier struct {
XMLName xml.Name `xml:"wsse:KeyIdentifier"`
ID string `xml:",innerxml"`
ValueType string `xml:",attr"`
}
type Issuer struct {
XMLName xml.Name
Format string `xml:",attr"`
Value string `xml:",innerxml"`
}
func (i *Issuer) C14N() string {
return mkns("saml2", i, &i.XMLName)
}
type Assertion struct {
XMLName xml.Name
ID string `xml:",attr"`
IssueInstant string `xml:",attr"`
Version string `xml:",attr"`
Issuer Issuer
Signature Signature
Subject Subject
Conditions Conditions
AuthnStatement AuthnStatement
AttributeStatement AttributeStatement
}
func (a *Assertion) C14N() string {
start := `<saml2:Assertion xmlns:saml2="%s" ID="%s" IssueInstant="%s" Version="%s">`
c14n := []string{
fmt.Sprintf(start, a.XMLName.Space, a.ID, a.IssueInstant, a.Version),
a.Issuer.C14N(),
a.Signature.C14N(),
a.Subject.C14N(),
a.Conditions.C14N(),
a.AuthnStatement.C14N(),
a.AttributeStatement.C14N(),
`</saml2:Assertion>`,
}
return strings.Join(c14n, "")
}
type NameID struct {
XMLName xml.Name
Format string `xml:",attr"`
ID string `xml:",innerxml"`
}
type Subject struct {
XMLName xml.Name
NameID NameID
SubjectConfirmation SubjectConfirmation
}
func (s *Subject) C14N() string {
data := &s.SubjectConfirmation.SubjectConfirmationData
names := []*xml.Name{
&s.XMLName,
&s.NameID.XMLName,
&s.SubjectConfirmation.XMLName,
&data.XMLName,
}
if s.SubjectConfirmation.NameID != nil {
names = append(names, &s.SubjectConfirmation.NameID.XMLName)
}
if data.KeyInfo != nil {
data.NS = XSI
data.Type = "saml2:KeyInfoConfirmationDataType"
data.KeyInfo.XMLName = xml.Name{Local: "ds:KeyInfo"}
data.KeyInfo.X509Data.XMLName = xml.Name{Local: "ds:X509Data"}
data.KeyInfo.NS = DSIG
}
return mkns("saml2", s, names...)
}
type SubjectConfirmationData struct {
XMLName xml.Name
NS string `xml:"xmlns:xsi,attr,omitempty"`
Type string `xml:"xsi:type,attr,omitempty"`
NotOnOrAfter string `xml:",attr,omitempty"`
KeyInfo *KeyInfo
}
type SubjectConfirmation struct {
XMLName xml.Name
Method string `xml:",attr"`
NameID *NameID
SubjectConfirmationData SubjectConfirmationData
}
type Condition struct {
Type string `xml:"xsi:type,attr,omitempty"`
}
func (c *Condition) GetCondition() *Condition {
return c
}
type BaseCondition interface {
GetCondition() *Condition
}
func init() {
types.Add("BaseCondition", reflect.TypeOf((*Condition)(nil)).Elem())
types.Add("del:DelegationRestrictionType", reflect.TypeOf((*DelegateRestriction)(nil)).Elem())
types.Add("rsa:RenewRestrictionType", reflect.TypeOf((*RenewRestriction)(nil)).Elem())
}
type Conditions struct {
XMLName xml.Name
NotBefore string `xml:",attr"`
NotOnOrAfter string `xml:",attr"`
ProxyRestriction *ProxyRestriction `xml:",omitempty"`
Condition []BaseCondition `xml:",omitempty"`
}
func (c *Conditions) C14N() string {
names := []*xml.Name{
&c.XMLName,
}
if c.ProxyRestriction != nil {
names = append(names, &c.ProxyRestriction.XMLName)
}
for i := range c.Condition {
switch r := c.Condition[i].(type) {
case *DelegateRestriction:
names = append(names, &r.XMLName, &r.Delegate.NameID.XMLName)
r.NS = XSI
r.Type = "del:DelegationRestrictionType"
r.Delegate.NS = r.Delegate.XMLName.Space
r.Delegate.XMLName = xml.Name{Local: "del:Delegate"}
case *RenewRestriction:
names = append(names, &r.XMLName)
r.NS = XSI
r.Type = "rsa:RenewRestrictionType"
}
}
return mkns("saml2", c, names...)
}
type ProxyRestriction struct {
XMLName xml.Name
Count int32 `xml:",attr"`
}
type RenewRestriction struct {
XMLName xml.Name
NS string `xml:"xmlns:xsi,attr,omitempty"`
Count int32 `xml:",attr"`
Condition
}
type Delegate struct {
XMLName xml.Name
NS string `xml:"xmlns:del,attr,omitempty"`
DelegationInstant string `xml:",attr"`
NameID NameID
}
type DelegateRestriction struct {
XMLName xml.Name
NS string `xml:"xmlns:xsi,attr,omitempty"`
Condition
Delegate Delegate
}
type AuthnStatement struct {
XMLName xml.Name
AuthnInstant string `xml:",attr"`
AuthnContext struct {
XMLName xml.Name
AuthnContextClassRef struct {
XMLName xml.Name
Value string `xml:",innerxml"`
}
}
}
func (a *AuthnStatement) C14N() string {
return mkns("saml2", a, &a.XMLName, &a.AuthnContext.XMLName, &a.AuthnContext.AuthnContextClassRef.XMLName)
}
type AttributeStatement struct {
XMLName xml.Name
Attribute []Attribute
}
func (a *AttributeStatement) C14N() string {
c14n := []string{"<saml2:AttributeStatement>"}
for i := range a.Attribute {
c14n = append(c14n, a.Attribute[i].C14N())
}
c14n = append(c14n, "</saml2:AttributeStatement>")
return strings.Join(c14n, "")
}
type AttributeValue struct {
XMLName xml.Name
Type string `xml:"type,attr"`
Value string `xml:",innerxml"`
}
func (a *AttributeValue) C14N() string {
return fmt.Sprintf(`<saml2:AttributeValue xmlns:xsi="%s" xsi:type="xs:string">%s</saml2:AttributeValue>`, XSI, a.Value)
}
type Attribute struct {
XMLName xml.Name
FriendlyName string `xml:",attr"`
Name string `xml:",attr"`
NameFormat string `xml:",attr"`
AttributeValue []AttributeValue
}
func (a *Attribute) C14N() string {
c14n := []string{
fmt.Sprintf(`<saml2:Attribute FriendlyName="%s" Name="%s" NameFormat="%s">`, a.FriendlyName, a.Name, a.NameFormat),
}
for i := range a.AttributeValue {
c14n = append(c14n, a.AttributeValue[i].C14N())
}
c14n = append(c14n, `</saml2:Attribute>`)
return strings.Join(c14n, "")
}
type Lifetime struct {
Created string `xml:"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd Created"`
Expires string `xml:"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd Expires"`
}
func (t *Lifetime) C14N() string {
return fmt.Sprintf(`<Lifetime><wsu:Created>%s</wsu:Created><wsu:Expires>%s</wsu:Expires></Lifetime>`, t.Created, t.Expires)
}
type Renewing struct {
Allow bool `xml:",attr"`
OK bool `xml:",attr"`
}
type UseKey struct {
Sig string `xml:",attr"`
}
type Target struct {
Token string `xml:",innerxml"`
}
type RequestSecurityToken struct {
TokenType string `xml:",omitempty"`
RequestType string `xml:",omitempty"`
Lifetime *Lifetime `xml:",omitempty"`
Renewing *Renewing `xml:",omitempty"`
Delegatable bool `xml:",omitempty"`
KeyType string `xml:",omitempty"`
SignatureAlgorithm string `xml:",omitempty"`
UseKey *UseKey `xml:",omitempty"`
ActAs *Target `xml:",omitempty"`
ValidateTarget *Target `xml:",omitempty"`
RenewTarget *Target `xml:",omitempty"`
}
func Unmarshal(data []byte, v interface{}) error {
dec := xml.NewDecoder(bytes.NewReader(data))
dec.TypeFunc = types.TypeFunc()
return dec.Decode(v)
}
// toString returns an XML encoded RequestSecurityToken.
// When c14n is true, returns the canonicalized ActAs.Assertion which is required to sign the Issue request.
// When c14n is false, returns the original content of the ActAs.Assertion.
// The original content must be used within the request Body, as it has its own signature.
func (r *RequestSecurityToken) toString(c14n bool) string {
actas := ""
if r.ActAs != nil {
token := r.ActAs.Token
if c14n {
var a Assertion
err := Unmarshal([]byte(r.ActAs.Token), &a)
if err != nil {
log.Printf("decode ActAs: %s", err)
}
token = a.C14N()
}
actas = fmt.Sprintf(`<wst:ActAs xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200802">%s</wst:ActAs>`, token)
}
body := []string{
fmt.Sprintf(`<RequestSecurityToken xmlns="http://docs.oasis-open.org/ws-sx/ws-trust/200512">`),
fmt.Sprintf(`<TokenType>%s</TokenType>`, r.TokenType),
fmt.Sprintf(`<RequestType>%s</RequestType>`, r.RequestType),
r.Lifetime.C14N(),
}
if r.RenewTarget == nil {
body = append(body,
fmt.Sprintf(`<Renewing Allow="%t" OK="%t"></Renewing>`, r.Renewing.Allow, r.Renewing.OK),
fmt.Sprintf(`<Delegatable>%t</Delegatable>`, r.Delegatable),
actas,
fmt.Sprintf(`<KeyType>%s</KeyType>`, r.KeyType),
fmt.Sprintf(`<SignatureAlgorithm>%s</SignatureAlgorithm>`, r.SignatureAlgorithm),
fmt.Sprintf(`<UseKey Sig="%s"></UseKey>`, r.UseKey.Sig))
} else {
token := r.RenewTarget.Token
if c14n {
var a Assertion
err := Unmarshal([]byte(r.RenewTarget.Token), &a)
if err != nil {
log.Printf("decode Renew: %s", err)
}
token = a.C14N()
}
body = append(body,
fmt.Sprintf(`<UseKey Sig="%s"></UseKey>`, r.UseKey.Sig),
fmt.Sprintf(`<RenewTarget>%s</RenewTarget>`, token))
}
return strings.Join(append(body, `</RequestSecurityToken>`), "")
}
func (r *RequestSecurityToken) C14N() string {
return r.toString(true)
}
func (r *RequestSecurityToken) String() string {
return r.toString(false)
}
type RequestSecurityTokenResponseCollection struct {
RequestSecurityTokenResponse RequestSecurityTokenResponse
}
type RequestSecurityTokenResponse struct {
RequestedSecurityToken RequestedSecurityToken
Lifetime *Lifetime `xml:"http://docs.oasis-open.org/ws-sx/ws-trust/200512 Lifetime"`
}
type RequestedSecurityToken struct {
Assertion string `xml:",innerxml"`
}
type RequestSecurityTokenBody struct {
Req *RequestSecurityToken `xml:"http://docs.oasis-open.org/ws-sx/ws-trust/200512 RequestSecurityToken,omitempty"`
Res *RequestSecurityTokenResponseCollection `xml:"http://docs.oasis-open.org/ws-sx/ws-trust/200512 RequestSecurityTokenResponseCollection,omitempty"`
Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
}
func (b *RequestSecurityTokenBody) Fault() *soap.Fault { return b.Fault_ }
func (b *RequestSecurityTokenBody) RequestSecurityToken() *RequestSecurityToken { return b.Req }
func (r *RequestSecurityToken) Action() string {
kind := path.Base(r.RequestType)
return "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/" + kind
}
func Issue(ctx context.Context, r soap.RoundTripper, req *RequestSecurityToken) (*RequestSecurityTokenResponseCollection, error) {
var reqBody, resBody RequestSecurityTokenBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
type RenewSecurityTokenBody struct {
Req *RequestSecurityToken `xml:"http://docs.oasis-open.org/ws-sx/ws-trust/200512 RequestSecurityToken,omitempty"`
Res *RequestSecurityTokenResponse `xml:"http://docs.oasis-open.org/ws-sx/ws-trust/200512 RequestSecurityTokenResponse,omitempty"`
Fault_ *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
}
func (b *RenewSecurityTokenBody) Fault() *soap.Fault { return b.Fault_ }
func (b *RenewSecurityTokenBody) RequestSecurityToken() *RequestSecurityToken { return b.Req }
func Renew(ctx context.Context, r soap.RoundTripper, req *RequestSecurityToken) (*RequestSecurityTokenResponse, error) {
var reqBody, resBody RenewSecurityTokenBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
// Marshal panics if xml.Marshal returns an error
func Marshal(val interface{}) string {
b, err := xml.Marshal(val)
if err != nil {
panic(err)
}
return string(b)
}
// mkns prepends the given namespace to xml.Name.Local and returns obj encoded as xml.
// Note that the namespace is required when encoding, but the namespace prefix must not be
// present when decoding as Go's decoding does not handle namespace prefix.
func mkns(ns string, obj interface{}, name ...*xml.Name) string {
ns = ns + ":"
for i := range name {
name[i].Space = ""
if !strings.HasPrefix(name[i].Local, ns) {
name[i].Local = ns + name[i].Local
}
}
return Marshal(obj)
}

220
vendor/github.com/vmware/govmomi/sts/signer.go generated vendored Normal file
View File

@@ -0,0 +1,220 @@
/*
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package sts
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/tls"
"encoding/base64"
"errors"
"fmt"
"net/url"
"time"
"github.com/google/uuid"
"github.com/vmware/govmomi/sts/internal"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/xml"
)
// Signer implements the soap.Signer interface.
type Signer struct {
Token string // Token is a SAML token
Certificate *tls.Certificate // Certificate is used to sign requests
Lifetime struct {
Created time.Time
Expires time.Time
}
user *url.Userinfo // user contains the credentials for bearer token request
keyID string // keyID is the Signature UseKey ID, which is referenced in both the soap body and header
}
// signedEnvelope is similar to soap.Envelope, but with namespace and Body as innerxml
type signedEnvelope struct {
XMLName xml.Name `xml:"soap:Envelope"`
NS string `xml:"xmlns:soap,attr"`
Header soap.Header `xml:"soap:Header"`
Body string `xml:",innerxml"`
}
// newID returns a unique Reference ID, with a leading underscore as required by STS.
func newID() string {
return "_" + uuid.New().String()
}
func (s *Signer) setTokenReference(info *internal.KeyInfo) error {
var token struct {
ID string `xml:",attr"` // parse saml2:Assertion ID attribute
InnerXML string `xml:",innerxml"` // no need to parse the entire token
}
if err := xml.Unmarshal([]byte(s.Token), &token); err != nil {
return err
}
info.SecurityTokenReference = &internal.SecurityTokenReference{
WSSE11: "http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd",
TokenType: "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0",
KeyIdentifier: &internal.KeyIdentifier{
ID: token.ID,
ValueType: "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLID",
},
}
return nil
}
// Sign is a soap.Signer implementation which can be used to sign RequestSecurityToken and LoginByTokenBody requests.
func (s *Signer) Sign(env soap.Envelope) ([]byte, error) {
var key *rsa.PrivateKey
hasKey := false
if s.Certificate != nil {
key, hasKey = s.Certificate.PrivateKey.(*rsa.PrivateKey)
if !hasKey {
return nil, errors.New("sts: rsa.PrivateKey is required")
}
}
created := time.Now().UTC()
header := &internal.Security{
WSU: internal.WSU,
WSSE: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
Timestamp: internal.Timestamp{
NS: internal.WSU,
ID: newID(),
Created: created.Format(internal.Time),
Expires: created.Add(time.Minute).Format(internal.Time), // If STS receives this request after this, it is assumed to have expired.
},
}
env.Header.Security = header
info := internal.KeyInfo{XMLName: xml.Name{Local: "ds:KeyInfo"}}
var c14n, body string
type requestToken interface {
RequestSecurityToken() *internal.RequestSecurityToken
}
switch x := env.Body.(type) {
case requestToken:
if hasKey {
// We need c14n for all requests, as its digest is included in the signature and must match on the server side.
// We need the body in original form when using an ActAs or RenewTarget token, where the token and its signature are embedded in the body.
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]),
}
} else {
header.UsernameToken = &internal.UsernameToken{
Username: s.user.Username(),
}
header.UsernameToken.Password, _ = s.user.Password()
}
case *methods.LoginByTokenBody:
header.Assertion = s.Token
if hasKey {
if err := s.setTokenReference(&info); err != nil {
return nil, err
}
c14n = internal.Marshal(x.Req)
}
default:
// We can end up here via ssoadmin.SessionManager.Login().
// No other known cases where a signed request is needed.
header.Assertion = s.Token
if hasKey {
if err := s.setTokenReference(&info); err != nil {
return nil, err
}
type Req interface {
C14N() string
}
c14n = env.Body.(Req).C14N()
}
}
if !hasKey {
return xml.Marshal(env) // Bearer token without key to sign
}
id := newID()
tmpl := `<soap:Body xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsu="%s" wsu:Id="%s">%s</soap:Body>`
c14n = fmt.Sprintf(tmpl, internal.WSU, id, c14n)
if body == "" {
body = c14n
} else {
body = fmt.Sprintf(tmpl, internal.WSU, id, body)
}
header.Signature = &internal.Signature{
XMLName: xml.Name{Local: "ds:Signature"},
NS: internal.DSIG,
ID: s.keyID,
KeyInfo: info,
SignedInfo: internal.SignedInfo{
XMLName: xml.Name{Local: "ds:SignedInfo"},
NS: internal.DSIG,
CanonicalizationMethod: internal.Method{
XMLName: xml.Name{Local: "ds:CanonicalizationMethod"},
Algorithm: "http://www.w3.org/2001/10/xml-exc-c14n#",
},
SignatureMethod: internal.Method{
XMLName: xml.Name{Local: "ds:SignatureMethod"},
Algorithm: internal.SHA256,
},
Reference: []internal.Reference{
internal.NewReference(header.Timestamp.ID, header.Timestamp.C14N()),
internal.NewReference(id, c14n),
},
},
}
sum := sha256.Sum256([]byte(header.Signature.SignedInfo.C14N()))
sig, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, sum[:])
if err != nil {
return nil, err
}
header.Signature.SignatureValue = internal.Value{
XMLName: xml.Name{Local: "ds:SignatureValue"},
Value: base64.StdEncoding.EncodeToString(sig),
}
return xml.Marshal(signedEnvelope{
NS: "http://schemas.xmlsoap.org/soap/envelope/",
Header: *env.Header,
Body: body,
})
}

View File

@@ -0,0 +1,134 @@
/*
Copyright (c) 2018 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package simulator
import (
"fmt"
"log"
"net/http"
"net/url"
"path"
"time"
"github.com/vmware/govmomi/sts/internal"
"github.com/vmware/govmomi/vim25/soap"
vim "github.com/vmware/govmomi/vim25/types"
)
// New creates an STS simulator and configures the simulator endpoint in the given settings.
// The path returned is that of the settings "config.vpxd.sso.sts.uri" property.
func New(u *url.URL, settings []vim.BaseOptionValue) (string, http.Handler) {
for i := range settings {
setting := settings[i].GetOptionValue()
if setting.Key == "config.vpxd.sso.sts.uri" {
endpoint, _ := url.Parse(setting.Value.(string))
endpoint.Host = u.Host
setting.Value = endpoint.String()
settings[i] = setting
return endpoint.Path, new(handler)
}
}
return "", nil
}
type handler struct{}
// ServeHTTP handles STS requests.
func (s *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
action := r.Header.Get("SOAPAction")
env := soap.Envelope{}
now := time.Now()
lifetime := &internal.Lifetime{
Created: now.Format(internal.Time),
Expires: now.Add(5 * time.Minute).Format(internal.Time),
}
switch path.Base(action) {
case "Issue":
body := internal.RequestSecurityTokenBody{
Res: &internal.RequestSecurityTokenResponseCollection{
RequestSecurityTokenResponse: internal.RequestSecurityTokenResponse{
RequestedSecurityToken: internal.RequestedSecurityToken{
Assertion: token,
},
Lifetime: lifetime,
},
},
}
env.Body = body
case "Renew":
body := internal.RenewSecurityTokenBody{
Res: &internal.RequestSecurityTokenResponse{
RequestedSecurityToken: internal.RequestedSecurityToken{
Assertion: token,
},
Lifetime: lifetime,
},
}
env.Body = body
default:
log.Printf("sts: unsupported action=%s", action)
w.WriteHeader(http.StatusNotFound)
return
}
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, internal.Marshal(env))
}
// Currently simulator.SessionManager.LoginByToken() only checks for a non-empty Assertion.Subject.NameID field,
// so the token below is returned by Issue and Renew requests for now.
var token = `<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="_1881a9ba-4a76-4baa-839b-36e2cba10743" IssueInstant="2018-03-04T00:27:56.409Z" Version="2.0"><saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://office1-sfo2-dhcp221.eng.vmware.com/websso/SAML2/Metadata/vsphere.local</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#_1881a9ba-4a76-4baa-839b-36e2cba10743"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs xsi"/></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue>l/0AzCGiPB69oTstUdrCkihBIDtwb83A93zAe10tG3k=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>EKHf14V0CHctwqXRlhYSYNyID5lNJLimbw57eUBm/QlAMLY7GJ1wth44oeQPSj3eMpJaXKHEYYtn
fqMngciTrq4ZP2SS7KizxuBjcHChWGmcp+t0zn7+fTbp5sL8HfF3AfOwcyZxwj8n2S7E6Eee7zeC
cjZpKKZ1QIEwASwpuMCs7vU9IuXsUguHAaN55Jpx3N5u7PlSo/NZE0TJZ+zNWP8m9H5shPDY272D
Vnp3MGfoD+Dj6T4H8OVF6bMp6czbHsEHTthwPh+pBTzR8ppkyxPKWLkC7OWiOtZBKqLSMTchQyqn
GNJdl72FBXHS8WXGtJjbwL+MKf+WujhqwdRbXw==</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIDxTCCAq2gAwIBAgIJAMYXe1r3pfByMA0GCSqGSIb3DQEBCwUAMIGqMQswCQYDVQQDDAJDQTEX
MBUGCgmSJomT8ixkARkWB3ZzcGhlcmUxFTATBgoJkiaJk/IsZAEZFgVsb2NhbDELMAkGA1UEBhMC
VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExLDAqBgNVBAoMI29mZmljZTEtc2ZvMi1kaGNwMjIxLmVu
Zy52bXdhcmUuY29tMRswGQYDVQQLDBJWTXdhcmUgRW5naW5lZXJpbmcwHhcNMTgwMTExMjE1MjQ3
WhcNMjgwMTA2MjIwMjMxWjAYMRYwFAYDVQQDDA1zc29zZXJ2ZXJTaWduMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAohfKdXEpiCB+EewJJKk98he/KeAK/1bZ2MjnLspwt3Nvv2uh2xoa
1asP/TMAhxcztPxhqEZmi0W+nihF/yffY/AhQrGx9XynaOMUNarCNGVI2qBovi8gohT2pXlbKxgZ
b8VZkVl41WYkDBfQrzoP0XU/sFeOoNIHcFQX/82NFAYtN/4aBZ9gDqhyPihv2RSNG4MnvxxgxtZI
FPb3eyDt8poKOMjt8zG2JkJRQYiEOCLo/sKJEKXLZeWiqYsbk391/vIk2vaX3L3pgu8yYx/dLfxv
X/mRYIOcVzpXWQCEPdCejQBwrmVeRaepW5cMhOVlMAAw+mEXYVVTaIi1pfN53wIDAQABo38wfTAL
BgNVHQ8EBAMCBeAwLgYDVR0RBCcwJYIjb2ZmaWNlMS1zZm8yLWRoY3AyMjEuZW5nLnZtd2FyZS5j
b20wHQYDVR0OBBYEFAtGcFg9jVO3aBjgd2K0iBFTAPNSMB8GA1UdIwQYMBaAFLpyqy2v1I7a3URK
ohtSLAtqve5qMA0GCSqGSIb3DQEBCwUAA4IBAQB91dZHRFunBs+YvuOYFRlwJTZOPXzlSYurxC7h
VeYv6LUGZnuTkp0KfVMsfHyaeDslM8+5F9Iug1jxmEmpeyoaY12zQmxQB6P8lN4jj1Aazj8qmDH6
ClaSY4Pp0lOSp9ROVlnLi6sRsRphOg+4MS4UeXGgSFlMN1BWJmXcwCazbii8l/EzGx2QhlVjWMAz
lPFQlWQ4FvV5vUCf8iE+UTin+6oJSXmFzip1NOBOGiIbClmpergZUchNiqTYTrpqblD/Qex5Bv9e
+xAwuw8e0Lm0XICOcFmKvpotLKKiqMMsRqPoeTqnoSyKqvCGRo2hUs4Y4O6SqEd80+E5lbXImrSt</ds:X509Certificate><ds:X509Certificate>MIIEPzCCAyegAwIBAgIJANS+QleTVJNbMA0GCSqGSIb3DQEBCwUAMIGqMQswCQYDVQQDDAJDQTEX
MBUGCgmSJomT8ixkARkWB3ZzcGhlcmUxFTATBgoJkiaJk/IsZAEZFgVsb2NhbDELMAkGA1UEBhMC
VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExLDAqBgNVBAoMI29mZmljZTEtc2ZvMi1kaGNwMjIxLmVu
Zy52bXdhcmUuY29tMRswGQYDVQQLDBJWTXdhcmUgRW5naW5lZXJpbmcwHhcNMTgwMTA4MjIwMjMx
WhcNMjgwMTA2MjIwMjMxWjCBqjELMAkGA1UEAwwCQ0ExFzAVBgoJkiaJk/IsZAEZFgd2c3BoZXJl
MRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlh
MSwwKgYDVQQKDCNvZmZpY2UxLXNmbzItZGhjcDIyMS5lbmcudm13YXJlLmNvbTEbMBkGA1UECwwS
Vk13YXJlIEVuZ2luZWVyaW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxcN7rsoK
CIapsEOYejPK38Qk7CUSPFcOmT7iF15UFlZDogHe1G/ZkYvcP0IvLvpemRiYuRpVGVuUZ9XOgeW6
J5xpSuNRXMHSMDTUwLM9t/4NMAQxgWVlJjFmPVBIZiWaQgdCzEbCDcv/XaZeb6uJYlbmLKvopmwy
oDfncGXRUuQIZFsVIUhUgOtbbp9UmvXyjo9ukWdVcTkKlKK7NZGaVa4JYy7q4cc6g5eRmD9qp16o
vx8DageNAasTP6arnb5CyoGI4KPqJjaI7V4Z1KiOUs+Zj+VtC3XdpVthNtiJ+vgXccO8e7zYfP0y
d1PCQ/GEZAlRabus5Iplu4/xC23NywIDAQABo2YwZDAdBgNVHQ4EFgQUunKrLa/UjtrdREqiG1Is
C2q97mowHwYDVR0RBBgwFoEOZW1haWxAYWNtZS5jb22HBH8AAAEwDgYDVR0PAQH/BAQDAgEGMBIG
A1UdEwEB/wQIMAYBAf8CAQAwDQYJKoZIhvcNAQELBQADggEBAC8bMIhFtlXnCF2fUixTXJ5HZFNY
vbxa1eFjLFYuBsGBqhPEHkHkdKwgpfo1sd4t0L7JaGS9wsH6zyRUQs97subV5YUI6rvAPOBGDQTm
RmCeqz3ODZq6JwZEnTTqZjvUVckmt/L/QaRUHAW27MU+SuN8rP0Nghf/gkOabsaWfyT2ADquko4e
b7seYIlR5mJs+pxVBBsBB2nzxuaV5EjkgestxBqpGkxMnKEDhG6+VjqVxsZoEiNzdBNU7eM67Jc2
2KU85jHKAao9LfMbwbHOA//1RStXXElyzPQvecq17ATvpw8AxCRu2KeKRwp3Pm2RiquDQFx8aiCe
2Re4gkrEemA=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml2:Subject><saml2:NameID Format="http://schemas.xmlsoap.org/claims/UPN">Administrator@VSPHERE.LOCAL</saml2:NameID><saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml2:SubjectConfirmationData NotOnOrAfter="2018-03-04T00:27:01.401Z"/></saml2:SubjectConfirmation></saml2:Subject><saml2:Conditions NotBefore="2018-03-04T00:22:01.401Z" NotOnOrAfter="2018-03-04T00:27:01.401Z"><saml2:ProxyRestriction Count="10"/></saml2:Conditions><saml2:AuthnStatement AuthnInstant="2018-03-04T00:27:56.402Z"><saml2:AuthnContext><saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef></saml2:AuthnContext></saml2:AuthnStatement><saml2:AttributeStatement><saml2:Attribute FriendlyName="Groups" Name="http://rsa.com/schemas/attr-names/2009/01/GroupIdentity" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xsi:type="xs:string">vsphere.local\Users</saml2:AttributeValue><saml2:AttributeValue xsi:type="xs:string">vsphere.local\Administrators</saml2:AttributeValue><saml2:AttributeValue xsi:type="xs:string">vsphere.local\CAAdmins</saml2:AttributeValue><saml2:AttributeValue xsi:type="xs:string">vsphere.local\ComponentManager.Administrators</saml2:AttributeValue><saml2:AttributeValue xsi:type="xs:string">vsphere.local\SystemConfiguration.BashShellAdministrators</saml2:AttributeValue><saml2:AttributeValue xsi:type="xs:string">vsphere.local\SystemConfiguration.Administrators</saml2:AttributeValue><saml2:AttributeValue xsi:type="xs:string">vsphere.local\LicenseService.Administrators</saml2:AttributeValue><saml2:AttributeValue xsi:type="xs:string">vsphere.local\ActAsUsers</saml2:AttributeValue><saml2:AttributeValue xsi:type="xs:string">vsphere.local\Everyone</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="givenName" Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xsi:type="xs:string">Administrator</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="surname" Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xsi:type="xs:string">vsphere.local</saml2:AttributeValue></saml2:Attribute><saml2:Attribute FriendlyName="Subject Type" Name="http://vmware.com/schemas/attr-names/2011/07/isSolution" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri"><saml2:AttributeValue xsi:type="xs:string">false</saml2:AttributeValue></saml2:Attribute></saml2:AttributeStatement></saml2:Assertion>`