MM-19872: Update dependencies for enterprise implementation of new SAML2 golib. (#13298)

* Update dependencies for enterprise implementation of new SAML2 golib.

* add indirect
Этот коммит содержится в:
Scott Bishel
2019-12-19 08:27:34 -07:00
коммит произвёл GitHub
родитель 155ada09fb
Коммит 3bccc03df3
53 изменённых файлов: 7563 добавлений и 0 удалений

1
vendor/github.com/mattermost/gosaml2/.gitignore сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1 @@
*.test

15
vendor/github.com/mattermost/gosaml2/.travis.yml сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,15 @@
language: go
go:
- 1.5.x
- 1.6.x
- 1.7.x
- 1.8.x
- 1.9.x
- 1.10.x
- 1.11.x
- tip
matrix:
allow_failures:
- go: tip

175
vendor/github.com/mattermost/gosaml2/LICENSE сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,175 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.

34
vendor/github.com/mattermost/gosaml2/README.md сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,34 @@
# gosaml2
[![Build Status](https://travis-ci.org/russellhaering/gosaml2.svg?branch=master)](https://travis-ci.org/russellhaering/gosaml2)
[![GoDoc](https://godoc.org/github.com/russellhaering/gosaml2?status.svg)](https://godoc.org/github.com/russellhaering/gosaml2)
SAML 2.0 implemementation for Service Providers based on [etree](https://github.com/beevik/etree)
and [goxmldsig](https://github.com/russellhaering/goxmldsig), a pure Go
implementation of XML digital signatures.
## Installation
Install `gosaml2` into your `$GOPATH` using `go get`:
```
go get github.com/russellhaering/gosaml2
```
## Example
See [demo.go](s2example/demo.go).
## Supported Identity Providers
This library is meant to be a generic SAML implementation. If you find a
standards compliant identity provider that it doesn't work with please
submit a bug or pull request.
The following identity providers have been tested:
* Okta
* Auth0
* Shibboleth
* Ipsilon
* OneLogin

19
vendor/github.com/mattermost/gosaml2/attribute.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,19 @@
package saml2
import "github.com/mattermost/gosaml2/types"
// Values is a convenience wrapper for a map of strings to Attributes, which
// can be used for easy access to the string values of Attribute lists.
type Values map[string]types.Attribute
// Get is a safe method (nil maps will not panic) for returning the first value
// for an attribute at a key, or the empty string if none exists.
func (vals Values) Get(k string) string {
if vals == nil {
return ""
}
if v, ok := vals[k]; ok && len(v.Values) > 0 {
return string(v.Values[0].Value)
}
return ""
}

16
vendor/github.com/mattermost/gosaml2/authn_request.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,16 @@
package saml2
import "time"
// AuthNRequest is the go struct representation of an authentication request
type AuthNRequest struct {
ID string `xml:",attr"`
Version string `xml:",attr"`
ProtocolBinding string `xml:",attr"`
AssertionConsumerServiceURL string `xml:",attr"`
IssueInstant time.Time `xml:",attr"`
Destination string `xml:",attr"`
Issuer string
}

226
vendor/github.com/mattermost/gosaml2/build_request.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,226 @@
package saml2
import (
"bytes"
"compress/flate"
"encoding/base64"
"fmt"
"net/http"
"net/url"
"github.com/beevik/etree"
"github.com/mattermost/gosaml2/uuid"
)
const issueInstantFormat = "2006-01-02T15:04:05Z"
func (sp *SAMLServiceProvider) buildAuthnRequest(includeSig bool) (*etree.Document, error) {
authnRequest := &etree.Element{
Space: "samlp",
Tag: "AuthnRequest",
}
authnRequest.CreateAttr("xmlns:samlp", "urn:oasis:names:tc:SAML:2.0:protocol")
authnRequest.CreateAttr("xmlns:saml", "urn:oasis:names:tc:SAML:2.0:assertion")
arId := uuid.NewV4()
authnRequest.CreateAttr("ID", "_"+arId.String())
authnRequest.CreateAttr("Version", "2.0")
authnRequest.CreateAttr("ProtocolBinding", "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST")
authnRequest.CreateAttr("AssertionConsumerServiceURL", sp.AssertionConsumerServiceURL)
authnRequest.CreateAttr("IssueInstant", sp.Clock.Now().UTC().Format(issueInstantFormat))
authnRequest.CreateAttr("Destination", sp.IdentityProviderSSOURL)
// NOTE(russell_h): In earlier versions we mistakenly sent the IdentityProviderIssuer
// in the AuthnRequest. For backwards compatibility we will fall back to that
// behavior when ServiceProviderIssuer isn't set.
if sp.ServiceProviderIssuer != "" {
authnRequest.CreateElement("saml:Issuer").SetText(sp.ServiceProviderIssuer)
} else {
authnRequest.CreateElement("saml:Issuer").SetText(sp.IdentityProviderIssuer)
}
nameIdPolicy := authnRequest.CreateElement("samlp:NameIDPolicy")
nameIdPolicy.CreateAttr("AllowCreate", "true")
nameIdPolicy.CreateAttr("Format", sp.NameIdFormat)
if sp.RequestedAuthnContext != nil {
requestedAuthnContext := authnRequest.CreateElement("samlp:RequestedAuthnContext")
requestedAuthnContext.CreateAttr("Comparison", sp.RequestedAuthnContext.Comparison)
for _, context := range sp.RequestedAuthnContext.Contexts {
authnContextClassRef := requestedAuthnContext.CreateElement("saml:AuthnContextClassRef")
authnContextClassRef.SetText(context)
}
}
if sp.ScopingIDPProviderId != "" && sp.ScopingIDPProviderName != "" {
scoping := authnRequest.CreateElement("samlp:Scoping")
idpList := scoping.CreateElement("samlp:IDPList")
idpEntry := idpList.CreateElement("samlp:IDPEntry")
idpEntry.CreateAttr("ProviderID", sp.ScopingIDPProviderId)
idpEntry.CreateAttr("Name", sp.ScopingIDPProviderName)
}
doc := etree.NewDocument()
// Only POST binding includes <Signature> in <AuthnRequest> (includeSig)
if sp.SignAuthnRequests && includeSig {
signed, err := sp.SignAuthnRequest(authnRequest)
if err != nil {
return nil, err
}
doc.SetRoot(signed)
} else {
doc.SetRoot(authnRequest)
}
return doc, nil
}
func (sp *SAMLServiceProvider) BuildAuthRequestDocument() (*etree.Document, error) {
return sp.buildAuthnRequest(true)
}
func (sp *SAMLServiceProvider) BuildAuthRequestDocumentNoSig() (*etree.Document, error) {
return sp.buildAuthnRequest(false)
}
// SignAuthnRequest takes a document, builds a signature, creates another document
// and inserts the signature in it. According to the schema, the position of the
// signature is right after the Issuer [1] then all other children.
//
// [1] https://docs.oasis-open.org/security/saml/v2.0/saml-schema-protocol-2.0.xsd
func (sp *SAMLServiceProvider) SignAuthnRequest(el *etree.Element) (*etree.Element, error) {
ctx := sp.SigningContext()
sig, err := ctx.ConstructSignature(el, true)
if err != nil {
return nil, err
}
ret := el.Copy()
var children []etree.Token
children = append(children, ret.Child[0]) // issuer is always first
children = append(children, sig) // next is the signature
children = append(children, ret.Child[1:]...) // then all other children
ret.Child = children
return ret, nil
}
// BuildAuthRequest builds <AuthnRequest> for identity provider
func (sp *SAMLServiceProvider) BuildAuthRequest() (string, error) {
doc, err := sp.BuildAuthRequestDocument()
if err != nil {
return "", err
}
return doc.WriteToString()
}
func (sp *SAMLServiceProvider) buildAuthURLFromDocument(relayState, binding string, doc *etree.Document) (string, error) {
parsedUrl, err := url.Parse(sp.IdentityProviderSSOURL)
if err != nil {
return "", err
}
authnRequest, err := doc.WriteToString()
if err != nil {
return "", err
}
buf := &bytes.Buffer{}
fw, err := flate.NewWriter(buf, flate.DefaultCompression)
if err != nil {
return "", fmt.Errorf("flate NewWriter error: %v", err)
}
_, err = fw.Write([]byte(authnRequest))
if err != nil {
return "", fmt.Errorf("flate.Writer Write error: %v", err)
}
err = fw.Close()
if err != nil {
return "", fmt.Errorf("flate.Writer Close error: %v", err)
}
qs := parsedUrl.Query()
qs.Add("SAMLRequest", base64.StdEncoding.EncodeToString(buf.Bytes()))
if relayState != "" {
qs.Add("RelayState", relayState)
}
if sp.SignAuthnRequests && binding == BindingHttpRedirect {
// Sign URL encoded query (see Section 3.4.4.1 DEFLATE Encoding of saml-bindings-2.0-os.pdf)
ctx := sp.SigningContext()
qs.Add("SigAlg", ctx.GetSignatureMethodIdentifier())
var rawSignature []byte
if rawSignature, err = ctx.SignString(signatureInputString(qs.Get("SAMLRequest"), qs.Get("RelayState"), qs.Get("SigAlg"))); err != nil {
return "", fmt.Errorf("unable to sign query string of redirect URL: %v", err)
}
// Now add base64 encoded Signature
qs.Add("Signature", base64.StdEncoding.EncodeToString(rawSignature))
}
parsedUrl.RawQuery = qs.Encode()
return parsedUrl.String(), nil
}
func (sp *SAMLServiceProvider) BuildAuthURLFromDocument(relayState string, doc *etree.Document) (string, error) {
return sp.buildAuthURLFromDocument(relayState, BindingHttpPost, doc)
}
func (sp *SAMLServiceProvider) BuildAuthURLRedirect(relayState string, doc *etree.Document) (string, error) {
return sp.buildAuthURLFromDocument(relayState, BindingHttpRedirect, doc)
}
// BuildAuthURL builds redirect URL to be sent to principal
func (sp *SAMLServiceProvider) BuildAuthURL(relayState string) (string, error) {
doc, err := sp.BuildAuthRequestDocument()
if err != nil {
return "", err
}
return sp.BuildAuthURLFromDocument(relayState, doc)
}
// AuthRedirect takes a ResponseWriter and Request from an http interaction and
// redirects to the SAMLServiceProvider's configured IdP, including the
// relayState provided, if any.
func (sp *SAMLServiceProvider) AuthRedirect(w http.ResponseWriter, r *http.Request, relayState string) (err error) {
url, err := sp.BuildAuthURL(relayState)
if err != nil {
return err
}
http.Redirect(w, r, url, http.StatusFound)
return nil
}
// signatureInputString constructs the string to be fed into the signature algorithm, as described
// in section 3.4.4.1 of
// https://www.oasis-open.org/committees/download.php/56779/sstc-saml-bindings-errata-2.0-wd-06.pdf
func signatureInputString(samlRequest, relayState, sigAlg string) string {
var params [][2]string
if relayState == "" {
params = [][2]string{{"SAMLRequest", samlRequest}, {"SigAlg", sigAlg}}
} else {
params = [][2]string{{"SAMLRequest", samlRequest}, {"RelayState", relayState}, {"SigAlg", sigAlg}}
}
var buf bytes.Buffer
for _, kv := range params {
k, v := kv[0], kv[1]
if buf.Len() > 0 {
buf.WriteByte('&')
}
buf.WriteString(url.QueryEscape(k) + "=" + url.QueryEscape(v))
}
return buf.String()
}

337
vendor/github.com/mattermost/gosaml2/decode_response.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,337 @@
package saml2
import (
"bytes"
"compress/flate"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"fmt"
"io/ioutil"
"encoding/xml"
"github.com/beevik/etree"
"github.com/mattermost/gosaml2/types"
dsig "github.com/russellhaering/goxmldsig"
"github.com/russellhaering/goxmldsig/etreeutils"
)
func (sp *SAMLServiceProvider) validationContext() *dsig.ValidationContext {
ctx := dsig.NewDefaultValidationContext(sp.IDPCertificateStore)
ctx.Clock = sp.Clock
return ctx
}
// validateResponseAttributes validates a SAML Response's tag and attributes. It does
// not inspect child elements of the Response at all.
func (sp *SAMLServiceProvider) validateResponseAttributes(response *types.Response) error {
if response.Destination != "" && response.Destination != sp.AssertionConsumerServiceURL {
return ErrInvalidValue{
Key: DestinationAttr,
Expected: sp.AssertionConsumerServiceURL,
Actual: response.Destination,
}
}
if response.Version != "2.0" {
return ErrInvalidValue{
Reason: ReasonUnsupported,
Key: "SAML version",
Expected: "2.0",
Actual: response.Version,
}
}
return nil
}
func xmlUnmarshalElement(el *etree.Element, obj interface{}) error {
doc := etree.NewDocument()
doc.SetRoot(el)
data, err := doc.WriteToBytes()
if err != nil {
return err
}
err = xml.Unmarshal(data, obj)
if err != nil {
return err
}
return nil
}
func (sp *SAMLServiceProvider) getDecryptCert() (*tls.Certificate, error) {
if sp.SPKeyStore == nil {
return nil, fmt.Errorf("no decryption certs available")
}
//This is the tls.Certificate we'll use to decrypt any encrypted assertions
var decryptCert tls.Certificate
switch crt := sp.SPKeyStore.(type) {
case dsig.TLSCertKeyStore:
// Get the tls.Certificate directly if possible
decryptCert = tls.Certificate(crt)
default:
//Otherwise, construct one from the results of GetKeyPair
pk, cert, err := sp.SPKeyStore.GetKeyPair()
if err != nil {
return nil, fmt.Errorf("error getting keypair: %v", err)
}
decryptCert = tls.Certificate{
Certificate: [][]byte{cert},
PrivateKey: pk,
}
}
if sp.ValidateEncryptionCert {
// Check Validity period of certificate
if len(decryptCert.Certificate) < 1 || len(decryptCert.Certificate[0]) < 1 {
return nil, fmt.Errorf("empty decryption cert")
} else if cert, err := x509.ParseCertificate(decryptCert.Certificate[0]); err != nil {
return nil, fmt.Errorf("invalid x509 decryption cert: %v", err)
} else {
now := sp.Clock.Now()
if now.Before(cert.NotBefore) || now.After(cert.NotAfter) {
return nil, fmt.Errorf("decryption cert is not valid at this time")
}
}
}
return &decryptCert, nil
}
func (sp *SAMLServiceProvider) decryptAssertions(el *etree.Element) error {
var decryptCert *tls.Certificate
decryptAssertion := func(ctx etreeutils.NSContext, encryptedElement *etree.Element) error {
if encryptedElement.Parent() != el {
return fmt.Errorf("found encrypted assertion with unexpected parent element: %s", encryptedElement.Parent().Tag)
}
detached, err := etreeutils.NSDetatch(ctx, encryptedElement) // make a detached copy
if err != nil {
return fmt.Errorf("unable to detach encrypted assertion: %v", err)
}
encryptedAssertion := &types.EncryptedAssertion{}
err = xmlUnmarshalElement(detached, encryptedAssertion)
if err != nil {
return fmt.Errorf("unable to unmarshal encrypted assertion: %v", err)
}
if decryptCert == nil {
decryptCert, err = sp.getDecryptCert()
if err != nil {
return fmt.Errorf("unable to get decryption certificate: %v", err)
}
}
raw, derr := encryptedAssertion.DecryptBytes(decryptCert)
if derr != nil {
return fmt.Errorf("unable to decrypt encrypted assertion: %v", derr)
}
doc, _, err := parseResponse(raw)
if err != nil {
return fmt.Errorf("unable to create element from decrypted assertion bytes: %v", derr)
}
// Replace the original encrypted assertion with the decrypted one.
if el.RemoveChild(encryptedElement) == nil {
// Out of an abundance of caution, make sure removed worked
panic("unable to remove encrypted assertion")
}
el.AddChild(doc.Root())
return nil
}
if err := etreeutils.NSFindIterate(el, SAMLAssertionNamespace, EncryptedAssertionTag, decryptAssertion); err != nil {
return err
} else {
return nil
}
}
func (sp *SAMLServiceProvider) validateElementSignature(el *etree.Element) (*etree.Element, error) {
return sp.validationContext().Validate(el)
}
func (sp *SAMLServiceProvider) validateAssertionSignatures(el *etree.Element) error {
signedAssertions := 0
unsignedAssertions := 0
validateAssertion := func(ctx etreeutils.NSContext, unverifiedAssertion *etree.Element) error {
if unverifiedAssertion.Parent() != el {
return fmt.Errorf("found assertion with unexpected parent element: %s", unverifiedAssertion.Parent().Tag)
}
detached, err := etreeutils.NSDetatch(ctx, unverifiedAssertion) // make a detached copy
if err != nil {
return fmt.Errorf("unable to detach unverified assertion: %v", err)
}
assertion, err := sp.validationContext().Validate(detached)
if err == dsig.ErrMissingSignature {
unsignedAssertions++
return nil
} else if err != nil {
return err
}
// Replace the original unverified Assertion with the verified one. Note that
// if the Response is not signed, only signed Assertions (and not the parent Response) can be trusted.
if el.RemoveChild(unverifiedAssertion) == nil {
// Out of an abundance of caution, check to make sure an Assertion was actually
// removed. If it wasn't a programming error has occurred.
panic("unable to remove assertion")
}
el.AddChild(assertion)
signedAssertions++
return nil
}
if err := etreeutils.NSFindIterate(el, SAMLAssertionNamespace, AssertionTag, validateAssertion); err != nil {
return err
} else if signedAssertions > 0 && unsignedAssertions > 0 {
return fmt.Errorf("invalid to have both signed and unsigned assertions")
} else if signedAssertions < 1 {
return dsig.ErrMissingSignature
} else {
return nil
}
}
//ValidateEncodedResponse both decodes and validates, based on SP
//configuration, an encoded, signed response. It will also appropriately
//decrypt a response if the assertion was encrypted
func (sp *SAMLServiceProvider) ValidateEncodedResponse(encodedResponse string) (*types.Response, error) {
raw, err := base64.StdEncoding.DecodeString(encodedResponse)
if err != nil {
return nil, err
}
// Parse the raw response
doc, el, err := parseResponse(raw)
if err != nil {
return nil, err
}
var responseSignatureValidated bool
if !sp.SkipSignatureValidation {
el, err = sp.validateElementSignature(el)
if err == dsig.ErrMissingSignature {
// Unfortunately we just blew away our Response
el = doc.Root()
} else if err != nil {
return nil, err
} else if el == nil {
return nil, fmt.Errorf("missing transformed response")
} else {
responseSignatureValidated = true
}
}
err = sp.decryptAssertions(el)
if err != nil {
return nil, err
}
var assertionSignaturesValidated bool
if !sp.SkipSignatureValidation {
err = sp.validateAssertionSignatures(el)
if err == dsig.ErrMissingSignature {
if !responseSignatureValidated {
return nil, fmt.Errorf("response and/or assertions must be signed")
}
} else if err != nil {
return nil, err
} else {
assertionSignaturesValidated = true
}
}
decodedResponse := &types.Response{}
err = xmlUnmarshalElement(el, decodedResponse)
if err != nil {
return nil, fmt.Errorf("unable to unmarshal response: %v", err)
}
decodedResponse.SignatureValidated = responseSignatureValidated
if assertionSignaturesValidated {
for idx := 0; idx < len(decodedResponse.Assertions); idx++ {
decodedResponse.Assertions[idx].SignatureValidated = true
}
}
err = sp.Validate(decodedResponse)
if err != nil {
return nil, err
}
return decodedResponse, nil
}
// DecodeUnverifiedBaseResponse decodes several attributes from a SAML response for the purpose
// of determining how to validate the response. This is useful for Service Providers which
// expose a single Assertion Consumer Service URL but consume Responses from many IdPs.
func DecodeUnverifiedBaseResponse(encodedResponse string) (*types.UnverifiedBaseResponse, error) {
raw, err := base64.StdEncoding.DecodeString(encodedResponse)
if err != nil {
return nil, err
}
var response *types.UnverifiedBaseResponse
err = maybeDeflate(raw, func(maybeXML []byte) error {
response = &types.UnverifiedBaseResponse{}
return xml.Unmarshal(maybeXML, response)
})
if err != nil {
return nil, err
}
return response, nil
}
// maybeDeflate invokes the passed decoder over the passed data. If an error is
// returned, it then attempts to deflate the passed data before re-invoking
// the decoder over the deflated data.
func maybeDeflate(data []byte, decoder func([]byte) error) error {
err := decoder(data)
if err == nil {
return nil
}
deflated, err := ioutil.ReadAll(flate.NewReader(bytes.NewReader(data)))
if err != nil {
return err
}
return decoder(deflated)
}
// parseResponse is a helper function that was refactored out so that the XML parsing behavior can be isolated and unit tested
func parseResponse(xml []byte) (*etree.Document, *etree.Element, error) {
var doc *etree.Document
err := maybeDeflate(xml, func(xml []byte) error {
doc = etree.NewDocument()
return doc.ReadFromBytes(xml)
})
if err != nil {
return nil, nil, err
}
el := doc.Root()
if el == nil {
return nil, nil, fmt.Errorf("unable to parse response")
}
return doc, el, nil
}

95
vendor/github.com/mattermost/gosaml2/retrieve_assertion.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,95 @@
package saml2
import "fmt"
//ErrMissingElement is the error type that indicates an element and/or attribute is
//missing. It provides a structured error that can be more appropriately acted
//upon.
type ErrMissingElement struct {
Tag, Attribute string
}
type ErrVerification struct {
Cause error
}
func (e ErrVerification) Error() string {
return fmt.Sprintf("error validating response: %s", e.Cause.Error())
}
//ErrMissingAssertion indicates that an appropriate assertion element could not
//be found in the SAML Response
var (
ErrMissingAssertion = ErrMissingElement{Tag: AssertionTag}
)
func (e ErrMissingElement) Error() string {
if e.Attribute != "" {
return fmt.Sprintf("missing %s attribute on %s element", e.Attribute, e.Tag)
}
return fmt.Sprintf("missing %s element", e.Tag)
}
//RetrieveAssertionInfo takes an encoded response and returns the AssertionInfo
//contained, or an error message if an error has been encountered.
func (sp *SAMLServiceProvider) RetrieveAssertionInfo(encodedResponse string) (*AssertionInfo, error) {
assertionInfo := &AssertionInfo{
Values: make(Values),
}
response, err := sp.ValidateEncodedResponse(encodedResponse)
if err != nil {
return nil, ErrVerification{Cause: err}
}
// TODO: Support multiple assertions
if len(response.Assertions) == 0 {
return nil, ErrMissingAssertion
}
assertion := response.Assertions[0]
assertionInfo.Assertions = response.Assertions
assertionInfo.ResponseSignatureValidated = response.SignatureValidated
warningInfo, err := sp.VerifyAssertionConditions(&assertion)
if err != nil {
return nil, err
}
//Get the NameID
subject := assertion.Subject
if subject == nil {
return nil, ErrMissingElement{Tag: SubjectTag}
}
nameID := subject.NameID
if nameID == nil {
return nil, ErrMissingElement{Tag: NameIdTag}
}
assertionInfo.NameID = nameID.Value
//Get the actual assertion attributes
attributeStatement := assertion.AttributeStatement
if attributeStatement == nil && !sp.AllowMissingAttributes {
return nil, ErrMissingElement{Tag: AttributeStatementTag}
}
if attributeStatement != nil {
for _, attribute := range attributeStatement.Attributes {
assertionInfo.Values[attribute.Name] = attribute
}
}
if assertion.AuthnStatement != nil {
if assertion.AuthnStatement.AuthnInstant != nil {
assertionInfo.AuthnInstant = assertion.AuthnStatement.AuthnInstant
}
if assertion.AuthnStatement.SessionNotOnOrAfter != nil {
assertionInfo.SessionNotOnOrAfter = assertion.AuthnStatement.SessionNotOnOrAfter
}
}
assertionInfo.WarningInfo = warningInfo
return assertionInfo, nil
}

12
vendor/github.com/mattermost/gosaml2/run_test.sh сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,12 @@
#!/bin/bash
cd `dirname $0`
DIRS=`git grep -l 'func Test' | xargs dirname | sort -u`
for DIR in $DIRS
do
echo
echo "dir: $DIR"
echo "======================================"
pushd $DIR >/dev/null
go test -v || exit 1
popd >/dev/null
done

196
vendor/github.com/mattermost/gosaml2/saml.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,196 @@
package saml2
import (
"encoding/base64"
"sync"
"time"
"github.com/mattermost/gosaml2/types"
dsig "github.com/russellhaering/goxmldsig"
dsigtypes "github.com/russellhaering/goxmldsig/types"
)
type ErrSaml struct {
Message string
System error
}
func (serr ErrSaml) Error() string {
if serr.Message != "" {
return serr.Message
}
return "SAML error"
}
type SAMLServiceProvider struct {
IdentityProviderSSOURL string
IdentityProviderIssuer string
AssertionConsumerServiceURL string
ServiceProviderIssuer string
SignAuthnRequests bool
SignAuthnRequestsAlgorithm string
SignAuthnRequestsCanonicalizer dsig.Canonicalizer
// RequestedAuthnContext allows service providers to require that the identity
// provider use specific authentication mechanisms. Leaving this unset will
// permit the identity provider to choose the auth method. To maximize compatibility
// with identity providers it is recommended to leave this unset.
RequestedAuthnContext *RequestedAuthnContext
AudienceURI string
IDPCertificateStore dsig.X509CertificateStore
SPKeyStore dsig.X509KeyStore // Required encryption key, default signing key
SPSigningKeyStore dsig.X509KeyStore // Optional signing key
NameIdFormat string
ValidateEncryptionCert bool
SkipSignatureValidation bool
AllowMissingAttributes bool
ScopingIDPProviderId string
ScopingIDPProviderName string
Clock *dsig.Clock
signingContextMu sync.RWMutex
signingContext *dsig.SigningContext
}
// RequestedAuthnContext controls which authentication mechanisms are requested of
// the identity provider. It is generally sufficient to omit this and let the
// identity provider select an authentication mechansim.
type RequestedAuthnContext struct {
// The RequestedAuthnContext comparison policy to use. See the section 3.3.2.2.1
// of the SAML 2.0 specification for details. Constants named AuthnPolicyMatch*
// contain standardized values.
Comparison string
// Contexts will be passed as AuthnContextClassRefs. For example, to force password
// authentication on some identity providers, Contexts should have a value of
// []string{AuthnContextPasswordProtectedTransport}, and Comparison should have a
// value of AuthnPolicyMatchExact.
Contexts []string
}
func (sp *SAMLServiceProvider) Metadata() (*types.EntityDescriptor, error) {
signingCertBytes, err := sp.GetSigningCertBytes()
if err != nil {
return nil, err
}
encryptionCertBytes, err := sp.GetEncryptionCertBytes()
if err != nil {
return nil, err
}
return &types.EntityDescriptor{
ValidUntil: time.Now().UTC().Add(time.Hour * 24 * 7), // 7 days
EntityID: sp.ServiceProviderIssuer,
SPSSODescriptor: &types.SPSSODescriptor{
AuthnRequestsSigned: sp.SignAuthnRequests,
WantAssertionsSigned: !sp.SkipSignatureValidation,
ProtocolSupportEnumeration: SAMLProtocolNamespace,
KeyDescriptors: []types.KeyDescriptor{
{
Use: "signing",
KeyInfo: dsigtypes.KeyInfo{
X509Data: dsigtypes.X509Data{
X509Certificates: []dsigtypes.X509Certificate{dsigtypes.X509Certificate{
Data: base64.StdEncoding.EncodeToString(signingCertBytes),
}},
},
},
},
{
Use: "encryption",
KeyInfo: dsigtypes.KeyInfo{
X509Data: dsigtypes.X509Data{
X509Certificates: []dsigtypes.X509Certificate{dsigtypes.X509Certificate{
Data: base64.StdEncoding.EncodeToString(encryptionCertBytes),
}},
},
},
EncryptionMethods: []types.EncryptionMethod{
{Algorithm: types.MethodAES128GCM},
{Algorithm: types.MethodAES128CBC},
{Algorithm: types.MethodAES256CBC},
},
},
},
AssertionConsumerServices: []types.IndexedEndpoint{{
Binding: BindingHttpPost,
Location: sp.AssertionConsumerServiceURL,
Index: 1,
}},
},
}, nil
}
func (sp *SAMLServiceProvider) GetEncryptionKey() dsig.X509KeyStore {
return sp.SPKeyStore
}
func (sp *SAMLServiceProvider) GetSigningKey() dsig.X509KeyStore {
if sp.SPSigningKeyStore == nil {
return sp.GetEncryptionKey() // Default is signing key is same as encryption key
}
return sp.SPSigningKeyStore
}
func (sp *SAMLServiceProvider) GetEncryptionCertBytes() ([]byte, error) {
if _, encryptionCert, err := sp.GetEncryptionKey().GetKeyPair(); err != nil {
return nil, ErrSaml{Message: "no SP encryption certificate", System: err}
} else if len(encryptionCert) < 1 {
return nil, ErrSaml{Message: "empty SP encryption certificate"}
} else {
return encryptionCert, nil
}
}
func (sp *SAMLServiceProvider) GetSigningCertBytes() ([]byte, error) {
if _, signingCert, err := sp.GetSigningKey().GetKeyPair(); err != nil {
return nil, ErrSaml{Message: "no SP signing certificate", System: err}
} else if len(signingCert) < 1 {
return nil, ErrSaml{Message: "empty SP signing certificate"}
} else {
return signingCert, nil
}
}
func (sp *SAMLServiceProvider) SigningContext() *dsig.SigningContext {
sp.signingContextMu.RLock()
signingContext := sp.signingContext
sp.signingContextMu.RUnlock()
if signingContext != nil {
return signingContext
}
sp.signingContextMu.Lock()
defer sp.signingContextMu.Unlock()
sp.signingContext = dsig.NewDefaultSigningContext(sp.GetSigningKey())
sp.signingContext.SetSignatureMethod(sp.SignAuthnRequestsAlgorithm)
if sp.SignAuthnRequestsCanonicalizer != nil {
sp.signingContext.Canonicalizer = sp.SignAuthnRequestsCanonicalizer
}
return sp.signingContext
}
type ProxyRestriction struct {
Count int
Audience []string
}
type WarningInfo struct {
OneTimeUse bool
ProxyRestriction *ProxyRestriction
NotInAudience bool
InvalidTime bool
}
type AssertionInfo struct {
NameID string
Values Values
WarningInfo *WarningInfo
AuthnInstant *time.Time
SessionNotOnOrAfter *time.Time
Assertions []types.Assertion
ResponseSignatureValidated bool
}

395
vendor/github.com/mattermost/gosaml2/test_constants.go сгенерированный поставляемый Обычный файл

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

83
vendor/github.com/mattermost/gosaml2/types/encrypted_assertion.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,83 @@
package types
import (
"bytes"
"crypto/cipher"
"crypto/tls"
"encoding/base64"
"encoding/xml"
"fmt"
)
type EncryptedAssertion struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion EncryptedAssertion"`
EncryptionMethod EncryptionMethod `xml:"EncryptedData>EncryptionMethod"`
EncryptedKey EncryptedKey `xml:"EncryptedData>KeyInfo>EncryptedKey"`
DetEncryptedKey EncryptedKey `xml:"EncryptedKey"` // detached EncryptedKey element
CipherValue string `xml:"EncryptedData>CipherData>CipherValue"`
}
func (ea *EncryptedAssertion) DecryptBytes(cert *tls.Certificate) ([]byte, error) {
data, err := base64.StdEncoding.DecodeString(ea.CipherValue)
if err != nil {
return nil, err
}
// EncryptedKey must include CipherValue. EncryptedKey may be part of EncryptedData.
ek := &ea.EncryptedKey
if ek.CipherValue == "" {
// Use detached EncryptedKey element (sibling of EncryptedData). See:
// https://www.w3.org/TR/2002/REC-xmlenc-core-20021210/Overview.html#sec-Extensions-to-KeyInfo
ek = &ea.DetEncryptedKey
}
k, err := ek.DecryptSymmetricKey(cert)
if err != nil {
return nil, fmt.Errorf("cannot decrypt, error retrieving private key: %s", err)
}
switch ea.EncryptionMethod.Algorithm {
case MethodAES128GCM:
c, err := cipher.NewGCM(k)
if err != nil {
return nil, fmt.Errorf("cannot create AES-GCM: %s", err)
}
nonce, data := data[:c.NonceSize()], data[c.NonceSize():]
plainText, err := c.Open(nil, nonce, data, nil)
if err != nil {
return nil, fmt.Errorf("cannot open AES-GCM: %s", err)
}
return plainText, nil
case MethodAES128CBC, MethodAES256CBC:
nonce, data := data[:k.BlockSize()], data[k.BlockSize():]
c := cipher.NewCBCDecrypter(k, nonce)
c.CryptBlocks(data, data)
// Remove zero bytes
data = bytes.TrimRight(data, "\x00")
// Calculate index to remove based on padding
padLength := data[len(data)-1]
lastGoodIndex := len(data) - int(padLength)
return data[:lastGoodIndex], nil
default:
return nil, fmt.Errorf("unknown symmetric encryption method %#v", ea.EncryptionMethod.Algorithm)
}
}
// Decrypt decrypts and unmarshals the EncryptedAssertion.
func (ea *EncryptedAssertion) Decrypt(cert *tls.Certificate) (*Assertion, error) {
plaintext, err := ea.DecryptBytes(cert)
if err != nil {
return nil, fmt.Errorf("Error decrypting assertion: %v", err)
}
assertion := &Assertion{}
err = xml.Unmarshal(plaintext, assertion)
if err != nil {
return nil, fmt.Errorf("Error unmarshaling assertion: %v", err)
}
return assertion, nil
}

140
vendor/github.com/mattermost/gosaml2/types/encrypted_key.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,140 @@
package types
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"crypto/tls"
"encoding/base64"
"encoding/hex"
"fmt"
"hash"
"strings"
)
//EncryptedKey contains the decryption key data from the saml2 core and xmlenc
//standards.
type EncryptedKey struct {
// EncryptionMethod string `xml:"EncryptionMethod>Algorithm"`
X509Data string `xml:"KeyInfo>X509Data>X509Certificate"`
CipherValue string `xml:"CipherData>CipherValue"`
EncryptionMethod EncryptionMethod
}
//EncryptionMethod specifies the type of encryption that was used.
type EncryptionMethod struct {
Algorithm string `xml:",attr,omitempty"`
DigestMethod DigestMethod `xml:",omitempty"`
}
//DigestMethod is a digest type specification
type DigestMethod struct {
Algorithm string `xml:",attr,omitempty"`
}
//Well-known public-key encryption methods
const (
MethodRSAOAEP = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"
MethodRSAOAEP2 = "http://www.w3.org/2009/xmlenc11#rsa-oaep"
)
//Well-known private key encryption methods
const (
MethodAES128GCM = "http://www.w3.org/2009/xmlenc11#aes128-gcm"
MethodAES128CBC = "http://www.w3.org/2001/04/xmlenc#aes128-cbc"
MethodAES256CBC = "http://www.w3.org/2001/04/xmlenc#aes256-cbc"
)
//Well-known hash methods
const (
MethodSHA1 = "http://www.w3.org/2000/09/xmldsig#sha1"
MethodSHA256 = "http://www.w3.org/2000/09/xmldsig#sha256"
MethodSHA512 = "http://www.w3.org/2000/09/xmldsig#sha512"
)
//SHA-1 is commonly used for certificate fingerprints (openssl -fingerprint and ADFS thumbprint).
//SHA-1 is sufficient for our purposes here (error message).
func debugKeyFp(keyBytes []byte) string {
if len(keyBytes) < 1 {
return ""
}
hashFunc := sha1.New()
hashFunc.Write(keyBytes)
sum := strings.ToLower(hex.EncodeToString(hashFunc.Sum(nil)))
var ret string
for idx := 0; idx+1 < len(sum); idx += 2 {
if idx == 0 {
ret += sum[idx : idx+2]
} else {
ret += ":" + sum[idx:idx+2]
}
}
return ret
}
//DecryptSymmetricKey returns the private key contained in the EncryptedKey document
func (ek *EncryptedKey) DecryptSymmetricKey(cert *tls.Certificate) (cipher.Block, error) {
if len(cert.Certificate) < 1 {
return nil, fmt.Errorf("decryption tls.Certificate has no public certs attached")
}
// The EncryptedKey may or may not include X509Data (certificate).
// If included, the EncryptedKey certificate:
// - is FYI only (fail if it does not match the SP certificate)
// - is NOT used to decrypt CipherData
if ek.X509Data != "" {
if encCert, err := base64.StdEncoding.DecodeString(ek.X509Data); err != nil {
return nil, fmt.Errorf("error decoding EncryptedKey certificate: %v", err)
} else if !bytes.Equal(cert.Certificate[0], encCert) {
return nil, fmt.Errorf("key decryption attempted with mismatched cert, SP cert(%.11s), assertion cert(%.11s)",
debugKeyFp(cert.Certificate[0]), debugKeyFp(encCert))
}
}
cipherText, err := base64.StdEncoding.DecodeString(ek.CipherValue)
if err != nil {
return nil, err
}
switch pk := cert.PrivateKey.(type) {
case *rsa.PrivateKey:
var h hash.Hash
switch ek.EncryptionMethod.DigestMethod.Algorithm {
case "", MethodSHA1:
h = sha1.New() // default
case MethodSHA256:
h = sha256.New()
case MethodSHA512:
h = sha512.New()
default:
return nil, fmt.Errorf("unsupported digest algorithm: %v",
ek.EncryptionMethod.DigestMethod.Algorithm)
}
switch ek.EncryptionMethod.Algorithm {
case "":
return nil, fmt.Errorf("missing encryption algorithm")
case MethodRSAOAEP, MethodRSAOAEP2:
pt, err := rsa.DecryptOAEP(h, rand.Reader, pk, cipherText, nil)
if err != nil {
return nil, fmt.Errorf("rsa internal error: %v", err)
}
b, err := aes.NewCipher(pt)
if err != nil {
return nil, err
}
return b, nil
default:
return nil, fmt.Errorf("unsupported encryption algorithm: %s", ek.EncryptionMethod.Algorithm)
}
}
return nil, fmt.Errorf("no cipher for decoding symmetric key")
}

67
vendor/github.com/mattermost/gosaml2/types/metadata.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,67 @@
package types
import (
"encoding/xml"
"time"
dsigtypes "github.com/russellhaering/goxmldsig/types"
)
type EntityDescriptor struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata EntityDescriptor"`
ValidUntil time.Time `xml:"validUntil,attr"`
// SAML 2.0 8.3.6 Entity Identifier could be used to represent issuer
EntityID string `xml:"entityID,attr"`
SPSSODescriptor *SPSSODescriptor `xml:"SPSSODescriptor,omitempty"`
IDPSSODescriptor *IDPSSODescriptor `xml:"IDPSSODescriptor,omitempty"`
}
type Endpoint struct {
Binding string `xml:"Binding,attr"`
Location string `xml:"Location,attr"`
ResponseLocation string `xml:"ResponseLocation,attr,omitempty"`
}
type IndexedEndpoint struct {
Binding string `xml:"Binding,attr"`
Location string `xml:"Location,attr"`
Index int `xml:"index,attr"`
}
type SPSSODescriptor struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata SPSSODescriptor"`
AuthnRequestsSigned bool `xml:"AuthnRequestsSigned,attr"`
WantAssertionsSigned bool `xml:"WantAssertionsSigned,attr"`
ProtocolSupportEnumeration string `xml:"protocolSupportEnumeration,attr"`
KeyDescriptors []KeyDescriptor `xml:"KeyDescriptor"`
SingleLogoutServices []Endpoint `xml:"SingleLogoutService"`
NameIDFormats []string `xml:"NameIDFormat"`
AssertionConsumerServices []IndexedEndpoint `xml:"AssertionConsumerService"`
}
type IDPSSODescriptor struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata IDPSSODescriptor"`
WantAuthnRequestsSigned bool `xml:"WantAuthnRequestsSigned,attr"`
KeyDescriptors []KeyDescriptor `xml:"KeyDescriptor"`
NameIDFormats []NameIDFormat `xml:"NameIDFormat"`
SingleSignOnServices []SingleSignOnService `xml:"SingleSignOnService"`
Attributes []Attribute `xml:"Attribute"`
}
type KeyDescriptor struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata KeyDescriptor"`
Use string `xml:"use,attr"`
KeyInfo dsigtypes.KeyInfo `xml:"KeyInfo"`
EncryptionMethods []EncryptionMethod `xml:"EncryptionMethod"`
}
type NameIDFormat struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata NameIDFormat"`
Value string `xml:",chardata"`
}
type SingleSignOnService struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:metadata SingleSignOnService"`
Binding string `xml:"Binding,attr"`
Location string `xml:"Location,attr"`
}

156
vendor/github.com/mattermost/gosaml2/types/response.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,156 @@
package types
import (
"encoding/xml"
"time"
)
// UnverifiedBaseResponse extracts several basic attributes of a SAML Response
// which may be useful in deciding how to validate the Response. An UnverifiedBaseResponse
// is parsed by this library prior to any validation of the Response, so the
// values it contains may have been supplied by an attacker and should not be
// trusted as authoritative from the IdP.
type UnverifiedBaseResponse struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol Response"`
ID string `xml:"ID,attr"`
InResponseTo string `xml:"InResponseTo,attr"`
Destination string `xml:"Destination,attr"`
Version string `xml:"Version,attr"`
Issuer *Issuer `xml:"Issuer"`
}
type Response struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol Response"`
ID string `xml:"ID,attr"`
InResponseTo string `xml:"InResponseTo,attr"`
Destination string `xml:"Destination,attr"`
Version string `xml:"Version,attr"`
IssueInstant time.Time `xml:"IssueInstant,attr"`
Status *Status `xml:"Status"`
Issuer *Issuer `xml:"Issuer"`
Assertions []Assertion `xml:"Assertion"`
EncryptedAssertions []EncryptedAssertion `xml:"EncryptedAssertion"`
SignatureValidated bool `xml:"-"` // not read, not dumped
}
type Status struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol Status"`
StatusCode *StatusCode `xml:"StatusCode"`
}
type StatusCode struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol StatusCode"`
Value string `xml:"Value,attr"`
}
type Issuer struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion Issuer"`
Value string `xml:",chardata"`
}
type Signature struct {
SignatureDocument []byte `xml:",innerxml"`
}
type Assertion struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion Assertion"`
Version string `xml:"Version,attr"`
ID string `xml:"ID,attr"`
IssueInstant time.Time `xml:"IssueInstant,attr"`
Issuer *Issuer `xml:"Issuer"`
Signature *Signature `xml:"Signature"`
Subject *Subject `xml:"Subject"`
Conditions *Conditions `xml:"Conditions"`
AttributeStatement *AttributeStatement `xml:"AttributeStatement"`
AuthnStatement *AuthnStatement `xml:"AuthnStatement"`
SignatureValidated bool `xml:"-"` // not read, not dumped
}
type Subject struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion Subject"`
NameID *NameID `xml:"NameID"`
SubjectConfirmation *SubjectConfirmation `xml:"SubjectConfirmation"`
}
type AuthnContext struct {
XMLName xml.Name `xml:urn:oasis:names:tc:SAML:2.0:assertion AuthnContext"`
AuthnContextClassRef *AuthnContextClassRef `xml:"AuthnContextClassRef"`
}
type AuthnContextClassRef struct {
XMLName xml.Name `xml:urn:oasis:names:tc:SAML:2.0:assertion AuthnContextClassRef"`
Value string `xml:",chardata"`
}
type NameID struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion NameID"`
Value string `xml:",chardata"`
}
type SubjectConfirmation struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion SubjectConfirmation"`
Method string `xml:"Method,attr"`
SubjectConfirmationData *SubjectConfirmationData `xml:"SubjectConfirmationData"`
}
type SubjectConfirmationData struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion SubjectConfirmationData"`
NotOnOrAfter string `xml:"NotOnOrAfter,attr"`
Recipient string `xml:"Recipient,attr"`
InResponseTo string `xml:"InResponseTo,attr"`
}
type Conditions struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion Conditions"`
NotBefore string `xml:"NotBefore,attr"`
NotOnOrAfter string `xml:"NotOnOrAfter,attr"`
AudienceRestrictions []AudienceRestriction `xml:"AudienceRestriction"`
OneTimeUse *OneTimeUse `xml:"OneTimeUse"`
ProxyRestriction *ProxyRestriction `xml:"ProxyRestriction"`
}
type AudienceRestriction struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion AudienceRestriction"`
Audiences []Audience `xml:"Audience"`
}
type Audience struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion Audience"`
Value string `xml:",chardata"`
}
type OneTimeUse struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion OneTimeUse"`
}
type ProxyRestriction struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion ProxyRestriction"`
Count int `xml:"Count,attr"`
Audience []Audience `xml:"Audience"`
}
type AttributeStatement struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion AttributeStatement"`
Attributes []Attribute `xml:"Attribute"`
}
type Attribute struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion Attribute"`
FriendlyName string `xml:"FriendlyName,attr"`
Name string `xml:"Name,attr"`
NameFormat string `xml:"NameFormat,attr"`
Values []AttributeValue `xml:"AttributeValue"`
}
type AttributeValue struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion AttributeValue"`
Type string `xml:"xsi:type,attr"`
Value string `xml:",chardata"`
}
type AuthnStatement struct {
XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion AuthnStatement"`
AuthnInstant *time.Time `xml:"AuthnInstant,attr,omitempty"`
SessionNotOnOrAfter *time.Time `xml:"SessionNotOnOrAfter,attr,omitempty"`
AuthnContext *AuthnContext `xml:"AuthnContext"`
}

27
vendor/github.com/mattermost/gosaml2/uuid/uuid.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,27 @@
package uuid
// relevant bits from https://github.com/abneptis/GoUUID/blob/master/uuid.go
import (
"crypto/rand"
"fmt"
)
type UUID [16]byte
// NewV4 returns random generated UUID.
func NewV4() *UUID {
u := &UUID{}
_, err := rand.Read(u[:16])
if err != nil {
panic(err)
}
u[8] = (u[8] | 0x80) & 0xBf
u[6] = (u[6] | 0x40) & 0x4f
return u
}
func (u *UUID) String() string {
return fmt.Sprintf("%x-%x-%x-%x-%x", u[:4], u[4:6], u[6:8], u[8:10], u[10:])
}

231
vendor/github.com/mattermost/gosaml2/validate.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,231 @@
package saml2
import (
"fmt"
"time"
"github.com/mattermost/gosaml2/types"
)
//ErrParsing indicates that the value present in an assertion could not be
//parsed. It can be inspected for the specific tag name, the contents, and the
//intended type.
type ErrParsing struct {
Tag, Value, Type string
}
func (ep ErrParsing) Error() string {
return fmt.Sprintf("Error parsing %s tag value as type %s", ep.Tag, ep.Value)
}
//Oft-used messages
const (
ReasonUnsupported = "Unsupported"
ReasonExpired = "Expired"
)
//ErrInvalidValue indicates that the expected value did not match the received
//value.
type ErrInvalidValue struct {
Key, Expected, Actual string
Reason string
}
func (e ErrInvalidValue) Error() string {
if e.Reason == "" {
e.Reason = "Unrecognized"
}
return fmt.Sprintf("%s %s value, Expected: %s, Actual: %s", e.Reason, e.Key, e.Expected, e.Actual)
}
//Well-known methods of subject confirmation
const (
SubjMethodBearer = "urn:oasis:names:tc:SAML:2.0:cm:bearer"
)
//VerifyAssertionConditions inspects an assertion element and makes sure that
//all SAML2 contracts are upheld.
func (sp *SAMLServiceProvider) VerifyAssertionConditions(assertion *types.Assertion) (*WarningInfo, error) {
warningInfo := &WarningInfo{}
now := sp.Clock.Now()
conditions := assertion.Conditions
if conditions == nil {
return nil, ErrMissingElement{Tag: ConditionsTag}
}
if conditions.NotBefore == "" {
return nil, ErrMissingElement{Tag: ConditionsTag, Attribute: NotBeforeAttr}
}
notBefore, err := time.Parse(time.RFC3339, conditions.NotBefore)
if err != nil {
return nil, ErrParsing{Tag: NotBeforeAttr, Value: conditions.NotBefore, Type: "time.RFC3339"}
}
if now.Before(notBefore) {
warningInfo.InvalidTime = true
}
if conditions.NotOnOrAfter == "" {
return nil, ErrMissingElement{Tag: ConditionsTag, Attribute: NotOnOrAfterAttr}
}
notOnOrAfter, err := time.Parse(time.RFC3339, conditions.NotOnOrAfter)
if err != nil {
return nil, ErrParsing{Tag: NotOnOrAfterAttr, Value: conditions.NotOnOrAfter, Type: "time.RFC3339"}
}
if now.After(notOnOrAfter) {
warningInfo.InvalidTime = true
}
for _, audienceRestriction := range conditions.AudienceRestrictions {
matched := false
for _, audience := range audienceRestriction.Audiences {
if audience.Value == sp.AudienceURI {
matched = true
break
}
}
if !matched {
warningInfo.NotInAudience = true
break
}
}
if conditions.OneTimeUse != nil {
warningInfo.OneTimeUse = true
}
proxyRestriction := conditions.ProxyRestriction
if proxyRestriction != nil {
proxyRestrictionInfo := &ProxyRestriction{
Count: proxyRestriction.Count,
Audience: []string{},
}
for _, audience := range proxyRestriction.Audience {
proxyRestrictionInfo.Audience = append(proxyRestrictionInfo.Audience, audience.Value)
}
warningInfo.ProxyRestriction = proxyRestrictionInfo
}
return warningInfo, nil
}
//Validate ensures that the assertion passed is valid for the current Service
//Provider.
func (sp *SAMLServiceProvider) Validate(response *types.Response) error {
err := sp.validateResponseAttributes(response)
if err != nil {
return err
}
if len(response.Assertions) == 0 {
return ErrMissingAssertion
}
issuer := response.Issuer
if issuer == nil {
// FIXME?: SAML Core 2.0 Section 3.2.2 has Response.Issuer as [Optional]
return ErrMissingElement{Tag: IssuerTag}
}
if sp.IdentityProviderIssuer != "" && response.Issuer.Value != sp.IdentityProviderIssuer {
return ErrInvalidValue{
Key: IssuerTag,
Expected: sp.IdentityProviderIssuer,
Actual: response.Issuer.Value,
}
}
status := response.Status
if status == nil {
return ErrMissingElement{Tag: StatusTag}
}
statusCode := status.StatusCode
if statusCode == nil {
return ErrMissingElement{Tag: StatusCodeTag}
}
if statusCode.Value != StatusCodeSuccess {
return ErrInvalidValue{
Key: StatusCodeTag,
Expected: StatusCodeSuccess,
Actual: statusCode.Value,
}
}
for _, assertion := range response.Assertions {
issuer = assertion.Issuer
if issuer == nil {
return ErrMissingElement{Tag: IssuerTag}
}
if sp.IdentityProviderIssuer != "" && assertion.Issuer.Value != sp.IdentityProviderIssuer {
return ErrInvalidValue{
Key: IssuerTag,
Expected: sp.IdentityProviderIssuer,
Actual: issuer.Value,
}
}
subject := assertion.Subject
if subject == nil {
return ErrMissingElement{Tag: SubjectTag}
}
subjectConfirmation := subject.SubjectConfirmation
if subjectConfirmation == nil {
return ErrMissingElement{Tag: SubjectConfirmationTag}
}
if subjectConfirmation.Method != SubjMethodBearer {
return ErrInvalidValue{
Reason: ReasonUnsupported,
Key: SubjectConfirmationTag,
Expected: SubjMethodBearer,
Actual: subjectConfirmation.Method,
}
}
subjectConfirmationData := subjectConfirmation.SubjectConfirmationData
if subjectConfirmationData == nil {
return ErrMissingElement{Tag: SubjectConfirmationDataTag}
}
if subjectConfirmationData.Recipient != sp.AssertionConsumerServiceURL {
return ErrInvalidValue{
Key: RecipientAttr,
Expected: sp.AssertionConsumerServiceURL,
Actual: subjectConfirmationData.Recipient,
}
}
if subjectConfirmationData.NotOnOrAfter == "" {
return ErrMissingElement{Tag: SubjectConfirmationDataTag, Attribute: NotOnOrAfterAttr}
}
notOnOrAfter, err := time.Parse(time.RFC3339, subjectConfirmationData.NotOnOrAfter)
if err != nil {
return ErrParsing{Tag: NotOnOrAfterAttr, Value: subjectConfirmationData.NotOnOrAfter, Type: "time.RFC3339"}
}
now := sp.Clock.Now()
if now.After(notOnOrAfter) {
return ErrInvalidValue{
Reason: ReasonExpired,
Key: NotOnOrAfterAttr,
Expected: now.Format(time.RFC3339),
Actual: subjectConfirmationData.NotOnOrAfter,
}
}
}
return nil
}

58
vendor/github.com/mattermost/gosaml2/xml_constants.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,58 @@
package saml2
const (
ResponseTag = "Response"
AssertionTag = "Assertion"
EncryptedAssertionTag = "EncryptedAssertion"
SubjectTag = "Subject"
NameIdTag = "NameID"
SubjectConfirmationTag = "SubjectConfirmation"
SubjectConfirmationDataTag = "SubjectConfirmationData"
AttributeStatementTag = "AttributeStatement"
AttributeValueTag = "AttributeValue"
ConditionsTag = "Conditions"
AudienceRestrictionTag = "AudienceRestriction"
AudienceTag = "Audience"
OneTimeUseTag = "OneTimeUse"
ProxyRestrictionTag = "ProxyRestriction"
IssuerTag = "Issuer"
StatusTag = "Status"
StatusCodeTag = "StatusCode"
)
const (
DestinationAttr = "Destination"
VersionAttr = "Version"
IdAttr = "ID"
MethodAttr = "Method"
RecipientAttr = "Recipient"
NameAttr = "Name"
NotBeforeAttr = "NotBefore"
NotOnOrAfterAttr = "NotOnOrAfter"
CountAttr = "Count"
)
const (
NameIdFormatPersistent = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
NameIdFormatTransient = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
NameIdFormatEmailAddress = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
NameIdFormatUnspecified = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
NameIdFormatX509SubjectName = "urn:oasis:names:tc:SAML:1.1:nameid-format:x509SubjectName"
AuthnContextPasswordProtectedTransport = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
AuthnPolicyMatchExact = "exact"
AuthnPolicyMatchMinimum = "minimum"
AuthnPolicyMatchMaximum = "maximum"
AuthnPolicyMatchBetter = "better"
StatusCodeSuccess = "urn:oasis:names:tc:SAML:2.0:status:Success"
BindingHttpPost = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
BindingHttpRedirect = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
)
const (
SAMLAssertionNamespace = "urn:oasis:names:tc:SAML:2.0:assertion"
SAMLProtocolNamespace = "urn:oasis:names:tc:SAML:2.0:protocol"
)