Merge pull request #785 from holgerkoser/master
Improve SAML Signature and Response Validation
This commit is contained in:
		| @@ -2,8 +2,6 @@ | |||||||
| package saml | package saml | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"bytes" |  | ||||||
| 	"compress/flate" |  | ||||||
| 	"crypto/rand" | 	"crypto/rand" | ||||||
| 	"crypto/x509" | 	"crypto/x509" | ||||||
| 	"encoding/base64" | 	"encoding/base64" | ||||||
| @@ -36,6 +34,15 @@ const ( | |||||||
| 	nameIDFormatKerberos     = "urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos" | 	nameIDFormatKerberos     = "urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos" | ||||||
| 	nameIDFormatPersistent   = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" | 	nameIDFormatPersistent   = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" | ||||||
| 	nameIDformatTransient    = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" | 	nameIDformatTransient    = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" | ||||||
|  |  | ||||||
|  | 	// top level status codes | ||||||
|  | 	statusCodeSuccess = "urn:oasis:names:tc:SAML:2.0:status:Success" | ||||||
|  |  | ||||||
|  | 	// subject confirmation methods | ||||||
|  | 	subjectConfirmationMethodBearer = "urn:oasis:names:tc:SAML:2.0:cm:bearer" | ||||||
|  |  | ||||||
|  | 	// allowed clock drift for timestamp validation | ||||||
|  | 	allowedClockDrift = time.Duration(30) * time.Second | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| @@ -253,6 +260,7 @@ func (p *provider) POSTData(s connector.Scopes) (action, value string, err error | |||||||
| 			AllowCreate: true, | 			AllowCreate: true, | ||||||
| 			Format:      p.nameIDPolicyFormat, | 			Format:      p.nameIDPolicyFormat, | ||||||
| 		}, | 		}, | ||||||
|  | 		AssertionConsumerServiceURL: p.redirectURI, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	data, err := xml.MarshalIndent(r, "", "  ") | 	data, err := xml.MarshalIndent(r, "", "  ") | ||||||
| @@ -260,19 +268,7 @@ func (p *provider) POSTData(s connector.Scopes) (action, value string, err error | |||||||
| 		return "", "", fmt.Errorf("marshal authn request: %v", err) | 		return "", "", fmt.Errorf("marshal authn request: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	buff := new(bytes.Buffer) | 	return p.ssoURL, base64.StdEncoding.EncodeToString(data), nil | ||||||
| 	fw, err := flate.NewWriter(buff, flate.DefaultCompression) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return "", "", fmt.Errorf("new flate writer: %v", err) |  | ||||||
| 	} |  | ||||||
| 	if _, err := fw.Write(data); err != nil { |  | ||||||
| 		return "", "", fmt.Errorf("compress message: %v", err) |  | ||||||
| 	} |  | ||||||
| 	if err := fw.Close(); err != nil { |  | ||||||
| 		return "", "", fmt.Errorf("flush message: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return p.ssoURL, base64.StdEncoding.EncodeToString(buff.Bytes()), nil |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (p *provider) HandlePOST(s connector.Scopes, samlResponse string) (ident connector.Identity, err error) { | func (p *provider) HandlePOST(s connector.Scopes, samlResponse string) (ident connector.Identity, err error) { | ||||||
| @@ -296,6 +292,10 @@ func (p *provider) HandlePOST(s connector.Scopes, samlResponse string) (ident co | |||||||
|  |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if err = p.validateStatus(&resp); err != nil { | ||||||
|  | 		return ident, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	assertion := resp.Assertion | 	assertion := resp.Assertion | ||||||
| 	if assertion == nil { | 	if assertion == nil { | ||||||
| 		return ident, fmt.Errorf("response did not contain an assertion") | 		return ident, fmt.Errorf("response did not contain an assertion") | ||||||
| @@ -305,6 +305,13 @@ func (p *provider) HandlePOST(s connector.Scopes, samlResponse string) (ident co | |||||||
| 		return ident, fmt.Errorf("response did not contain a subject") | 		return ident, fmt.Errorf("response did not contain a subject") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if err = p.validateConditions(assertion); err != nil { | ||||||
|  | 		return ident, err | ||||||
|  | 	} | ||||||
|  | 	if err = p.validateSubjectConfirmation(subject); err != nil { | ||||||
|  | 		return ident, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	switch { | 	switch { | ||||||
| 	case subject.NameID != nil: | 	case subject.NameID != nil: | ||||||
| 		if ident.UserID = subject.NameID.Value; ident.UserID == "" { | 		if ident.UserID = subject.NameID.Value; ident.UserID == "" { | ||||||
| @@ -348,19 +355,151 @@ func (p *provider) HandlePOST(s connector.Scopes, samlResponse string) (ident co | |||||||
| 	return ident, nil | 	return ident, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Validate that the StatusCode of the Response is success. | ||||||
|  | // Otherwise return a human readable message to the end user | ||||||
|  | func (p *provider) validateStatus(resp *response) error { | ||||||
|  | 	// Status is mandatory in the Response type | ||||||
|  | 	status := resp.Status | ||||||
|  | 	if status == nil { | ||||||
|  | 		return fmt.Errorf("response did not contain a Status") | ||||||
|  | 	} | ||||||
|  | 	// StatusCode is mandatory in the Status type | ||||||
|  | 	statusCode := status.StatusCode | ||||||
|  | 	if statusCode == nil { | ||||||
|  | 		return fmt.Errorf("response did not contain a StatusCode") | ||||||
|  | 	} | ||||||
|  | 	if statusCode.Value != statusCodeSuccess { | ||||||
|  | 		parts := strings.Split(statusCode.Value, ":") | ||||||
|  | 		lastPart := parts[len(parts)-1] | ||||||
|  | 		errorMessage := fmt.Sprintf("status code of the Response was not Success, was %q", lastPart) | ||||||
|  | 		statusMessage := status.StatusMessage | ||||||
|  | 		if statusMessage != nil && statusMessage.Value != "" { | ||||||
|  | 			errorMessage += " -> " + statusMessage.Value | ||||||
|  | 		} | ||||||
|  | 		return fmt.Errorf(errorMessage) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Multiple subject SubjectConfirmation can be in the assertion | ||||||
|  | // and at least one SubjectConfirmation must be valid. | ||||||
|  | // This is described in the spec "Profiles for the OASIS Security | ||||||
|  | // Assertion Markup Language" in section 3.3 Bearer. | ||||||
|  | // see https://www.oasis-open.org/committees/download.php/35389/sstc-saml-profiles-errata-2.0-wd-06-diff.pdf | ||||||
|  | func (p *provider) validateSubjectConfirmation(subject *subject) error { | ||||||
|  | 	validSubjectConfirmation := false | ||||||
|  | 	subjectConfirmations := subject.SubjectConfirmations | ||||||
|  | 	if subjectConfirmations != nil && len(subjectConfirmations) > 0 { | ||||||
|  | 		for _, subjectConfirmation := range subjectConfirmations { | ||||||
|  | 			// skip if method is wrong | ||||||
|  | 			method := subjectConfirmation.Method | ||||||
|  | 			if method != "" && method != subjectConfirmationMethodBearer { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			subjectConfirmationData := subjectConfirmation.SubjectConfirmationData | ||||||
|  | 			if subjectConfirmationData == nil { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			inResponseTo := subjectConfirmationData.InResponseTo | ||||||
|  | 			if inResponseTo != "" { | ||||||
|  | 				// TODO also validate InResponseTo if present | ||||||
|  | 			} | ||||||
|  | 			// only validate that subjectConfirmationData is not expired | ||||||
|  | 			now := p.now() | ||||||
|  | 			notOnOrAfter := time.Time(subjectConfirmationData.NotOnOrAfter) | ||||||
|  | 			if !notOnOrAfter.IsZero() { | ||||||
|  | 				if now.After(notOnOrAfter) { | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			// validate recipient if present | ||||||
|  | 			recipient := subjectConfirmationData.Recipient | ||||||
|  | 			if recipient != "" && recipient != p.redirectURI { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			validSubjectConfirmation = true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if !validSubjectConfirmation { | ||||||
|  | 		return fmt.Errorf("no valid SubjectConfirmation was found on this Response") | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Validates the Conditions element and all of it's content | ||||||
|  | func (p *provider) validateConditions(assertion *assertion) error { | ||||||
|  | 	// Checks if a Conditions element exists | ||||||
|  | 	conditions := assertion.Conditions | ||||||
|  | 	if conditions == nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	// Validates Assertion timestamps | ||||||
|  | 	now := p.now() | ||||||
|  | 	notBefore := time.Time(conditions.NotBefore) | ||||||
|  | 	if !notBefore.IsZero() { | ||||||
|  | 		if now.Add(allowedClockDrift).Before(notBefore) { | ||||||
|  | 			return fmt.Errorf("at %s got response that cannot be processed before %s", now, notBefore) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	notOnOrAfter := time.Time(conditions.NotOnOrAfter) | ||||||
|  | 	if !notOnOrAfter.IsZero() { | ||||||
|  | 		if now.After(notOnOrAfter.Add(allowedClockDrift)) { | ||||||
|  | 			return fmt.Errorf("at %s got response that cannot be processed because it expired at %s", now, notOnOrAfter) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// Validates audience | ||||||
|  | 	audienceRestriction := conditions.AudienceRestriction | ||||||
|  | 	if audienceRestriction != nil { | ||||||
|  | 		audiences := audienceRestriction.Audiences | ||||||
|  | 		if audiences != nil && len(audiences) > 0 { | ||||||
|  | 			issuerInAudiences := false | ||||||
|  | 			for _, audience := range audiences { | ||||||
|  | 				if audience.Value == p.issuer { | ||||||
|  | 					issuerInAudiences = true | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if !issuerInAudiences { | ||||||
|  | 				return fmt.Errorf("required audience %s was not in Response audiences %s", p.issuer, audiences) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| // verify checks the signature info of a XML document and returns | // verify checks the signature info of a XML document and returns | ||||||
| // the signed elements. | // the signed elements. | ||||||
|  | // The Validate function of the goxmldsig library only looks for | ||||||
|  | // signatures on the root element level. But a saml Response is valid | ||||||
|  | // if the complete message is signed, or only the Assertion is signed, | ||||||
|  | // or but elements are signed. Therefore we first check a possible | ||||||
|  | // signature of the Response than of the Assertion. If one of these | ||||||
|  | // is successful the Response is considered as valid. | ||||||
| func verify(validator *dsig.ValidationContext, data []byte) (signed []byte, err error) { | func verify(validator *dsig.ValidationContext, data []byte) (signed []byte, err error) { | ||||||
| 	doc := etree.NewDocument() | 	doc := etree.NewDocument() | ||||||
| 	if err := doc.ReadFromBytes(data); err != nil { | 	if err = doc.ReadFromBytes(data); err != nil { | ||||||
| 		return nil, fmt.Errorf("parse document: %v", err) | 		return nil, fmt.Errorf("parse document: %v", err) | ||||||
| 	} | 	} | ||||||
|  | 	verified := false | ||||||
| 	result, err := validator.Validate(doc.Root()) | 	response := doc.Root() | ||||||
| 	if err != nil { | 	transformedResponse, err := validator.Validate(response) | ||||||
| 		return nil, err | 	if err == nil { | ||||||
|  | 		verified = true | ||||||
|  | 		doc.SetRoot(transformedResponse) | ||||||
|  | 	} | ||||||
|  | 	assertion := response.SelectElement("Assertion") | ||||||
|  | 	if assertion == nil { | ||||||
|  | 		return nil, fmt.Errorf("response does not contain an Assertion element") | ||||||
|  | 	} | ||||||
|  | 	transformedAssertion, err := validator.Validate(assertion) | ||||||
|  | 	if err == nil { | ||||||
|  | 		verified = true | ||||||
|  | 		response.RemoveChild(assertion) | ||||||
|  | 		response.AddChild(transformedAssertion) | ||||||
|  | 	} | ||||||
|  | 	if verified != true { | ||||||
|  | 		return nil, fmt.Errorf("response does not contain a valid Signature element") | ||||||
| 	} | 	} | ||||||
| 	doc.SetRoot(result) |  | ||||||
| 	return doc.WriteToBytes() | 	return doc.WriteToBytes() | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,12 +2,24 @@ package saml | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"crypto/x509" | 	"crypto/x509" | ||||||
|  | 	"encoding/base64" | ||||||
| 	"encoding/pem" | 	"encoding/pem" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
|  | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
| 	sdig "github.com/russellhaering/goxmldsig" | 	"github.com/Sirupsen/logrus" | ||||||
|  |  | ||||||
|  | 	dsig "github.com/russellhaering/goxmldsig" | ||||||
|  |  | ||||||
|  | 	"github.com/coreos/dex/connector" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	defaultIssuer      = "http://localhost:5556/dex/callback" | ||||||
|  | 	defaultRedirectURI = "http://localhost:5556/dex/callback" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func loadCert(ca string) (*x509.Certificate, error) { | func loadCert(ca string) (*x509.Certificate, error) { | ||||||
| @@ -22,21 +34,238 @@ func loadCert(ca string) (*x509.Certificate, error) { | |||||||
| 	return x509.ParseCertificate(block.Bytes) | 	return x509.ParseCertificate(block.Bytes) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestVerify(t *testing.T) { | func runVerify(t *testing.T, ca string, resp string, shouldSucceed bool) { | ||||||
| 	cert, err := loadCert("testdata/okta-ca.pem") | 	cert, err := loadCert(ca) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
| 	s := certStore{[]*x509.Certificate{cert}} | 	s := certStore{[]*x509.Certificate{cert}} | ||||||
|  |  | ||||||
| 	validator := sdig.NewDefaultValidationContext(s) | 	validator := dsig.NewDefaultValidationContext(s) | ||||||
|  |  | ||||||
| 	data, err := ioutil.ReadFile("testdata/okta-resp.xml") | 	data, err := ioutil.ReadFile(resp) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if _, err := verify(validator, data); err != nil { | 	if _, err := verify(validator, data); err != nil { | ||||||
|  | 		if shouldSucceed { | ||||||
| 			t.Fatal(err) | 			t.Fatal(err) | ||||||
| 		} | 		} | ||||||
|  | 	} else { | ||||||
|  | 		if !shouldSucceed { | ||||||
|  | 			t.Fatalf("expected an invalid signatrue but verification has been successful") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func newProvider(issuer string, redirectURI string) *provider { | ||||||
|  | 	if issuer == "" { | ||||||
|  | 		issuer = defaultIssuer | ||||||
|  | 	} | ||||||
|  | 	if redirectURI == "" { | ||||||
|  | 		redirectURI = defaultRedirectURI | ||||||
|  | 	} | ||||||
|  | 	now, _ := time.Parse(time.RFC3339, "2017-01-24T20:48:41Z") | ||||||
|  | 	timeFunc := func() time.Time { return now } | ||||||
|  | 	return &provider{ | ||||||
|  | 		issuer:       issuer, | ||||||
|  | 		ssoURL:       "http://idp.org/saml/sso", | ||||||
|  | 		now:          timeFunc, | ||||||
|  | 		usernameAttr: "user", | ||||||
|  | 		emailAttr:    "email", | ||||||
|  | 		redirectURI:  redirectURI, | ||||||
|  | 		logger:       logrus.New(), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestVerify(t *testing.T) { | ||||||
|  | 	runVerify(t, "testdata/okta-ca.pem", "testdata/okta-resp.xml", true) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestVerifySignedMessageAndUnsignedAssertion(t *testing.T) { | ||||||
|  | 	runVerify(t, "testdata/idp-cert.pem", "testdata/idp-resp-signed-message.xml", true) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestVerifyUnsignedMessageAndSignedAssertion(t *testing.T) { | ||||||
|  | 	runVerify(t, "testdata/idp-cert.pem", "testdata/idp-resp-signed-assertion.xml", true) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestVerifySignedMessageAndSignedAssertion(t *testing.T) { | ||||||
|  | 	runVerify(t, "testdata/idp-cert.pem", "testdata/idp-resp-signed-message-and-assertion.xml", true) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestVerifyUnsignedMessageAndUnsignedAssertion(t *testing.T) { | ||||||
|  | 	runVerify(t, "testdata/idp-cert.pem", "testdata/idp-resp.xml", false) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestHandlePOST(t *testing.T) { | ||||||
|  | 	p := newProvider("", "") | ||||||
|  | 	scopes := connector.Scopes{ | ||||||
|  | 		OfflineAccess: false, | ||||||
|  | 		Groups:        true, | ||||||
|  | 	} | ||||||
|  | 	data, err := ioutil.ReadFile("testdata/idp-resp.xml") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	ident, err := p.HandlePOST(scopes, base64.StdEncoding.EncodeToString(data)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	if ident.UserID != "eric.chiang+okta@coreos.com" { | ||||||
|  | 		t.Fatalf("unexpected UserID %q", ident.UserID) | ||||||
|  | 	} | ||||||
|  | 	if ident.Username != "admin" { | ||||||
|  | 		t.Fatalf("unexpected Username: %q", ident.UserID) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestValidateStatus(t *testing.T) { | ||||||
|  | 	p := newProvider("", "") | ||||||
|  | 	var err error | ||||||
|  | 	resp := response{} | ||||||
|  | 	// Test missing Status element | ||||||
|  | 	err = p.validateStatus(&resp) | ||||||
|  | 	if err == nil || !strings.HasSuffix(err.Error(), `Status`) { | ||||||
|  | 		t.Fatalf("validation should fail with missing Status") | ||||||
|  | 	} | ||||||
|  | 	// Test missing StatusCode element | ||||||
|  | 	resp.Status = &status{} | ||||||
|  | 	err = p.validateStatus(&resp) | ||||||
|  | 	if err == nil || !strings.HasSuffix(err.Error(), `StatusCode`) { | ||||||
|  | 		t.Fatalf("validation should fail with missing StatusCode") | ||||||
|  | 	} | ||||||
|  | 	// Test failed request without StatusMessage | ||||||
|  | 	resp.Status.StatusCode = &statusCode{ | ||||||
|  | 		Value: ":Requester", | ||||||
|  | 	} | ||||||
|  | 	err = p.validateStatus(&resp) | ||||||
|  | 	if err == nil || !strings.HasSuffix(err.Error(), `"Requester"`) { | ||||||
|  | 		t.Fatalf("validation should fail with code %q", "Requester") | ||||||
|  | 	} | ||||||
|  | 	// Test failed request with StatusMessage | ||||||
|  | 	resp.Status.StatusMessage = &statusMessage{ | ||||||
|  | 		Value: "Failed", | ||||||
|  | 	} | ||||||
|  | 	err = p.validateStatus(&resp) | ||||||
|  | 	if err == nil || !strings.HasSuffix(err.Error(), `"Requester" -> Failed`) { | ||||||
|  | 		t.Fatalf("validation should fail with code %q and message %q", "Requester", "Failed") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestValidateSubjectConfirmation(t *testing.T) { | ||||||
|  | 	p := newProvider("", "") | ||||||
|  | 	var err error | ||||||
|  | 	var notAfter time.Time | ||||||
|  | 	subj := &subject{} | ||||||
|  | 	// Subject without any SubjectConfirmation | ||||||
|  | 	err = p.validateSubjectConfirmation(subj) | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Fatalf("validation of %q should fail", "Subject without any SubjectConfirmation") | ||||||
|  | 	} | ||||||
|  | 	// SubjectConfirmation without Method and SubjectConfirmationData | ||||||
|  | 	subj.SubjectConfirmations = []subjectConfirmation{subjectConfirmation{}} | ||||||
|  | 	err = p.validateSubjectConfirmation(subj) | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Fatalf("validation of %q should fail", "SubjectConfirmation without Method and SubjectConfirmationData") | ||||||
|  | 	} | ||||||
|  | 	// SubjectConfirmation with invalid Method and no SubjectConfirmationData | ||||||
|  | 	subj.SubjectConfirmations = []subjectConfirmation{subjectConfirmation{ | ||||||
|  | 		Method: "invalid", | ||||||
|  | 	}} | ||||||
|  | 	err = p.validateSubjectConfirmation(subj) | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Fatalf("validation of %q should fail", "SubjectConfirmation with invalid Method and no SubjectConfirmationData") | ||||||
|  | 	} | ||||||
|  | 	// SubjectConfirmation with valid Method and empty SubjectConfirmationData | ||||||
|  | 	subjConfirmationData := subjectConfirmationData{} | ||||||
|  | 	subj.SubjectConfirmations = []subjectConfirmation{subjectConfirmation{ | ||||||
|  | 		Method:                  "urn:oasis:names:tc:SAML:2.0:cm:bearer", | ||||||
|  | 		SubjectConfirmationData: &subjConfirmationData, | ||||||
|  | 	}} | ||||||
|  | 	err = p.validateSubjectConfirmation(subj) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("validation of %q should succeed", "SubjectConfirmation with valid Method and empty SubjectConfirmationData") | ||||||
|  | 	} | ||||||
|  | 	// SubjectConfirmationData with invalid Recipient | ||||||
|  | 	subjConfirmationData.Recipient = "invalid" | ||||||
|  | 	err = p.validateSubjectConfirmation(subj) | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Fatalf("validation of %q should fail", "SubjectConfirmationData with invalid Recipient") | ||||||
|  | 	} | ||||||
|  | 	// expired SubjectConfirmationData | ||||||
|  | 	notAfter = p.now().Add(-time.Duration(60) * time.Second) | ||||||
|  | 	subjConfirmationData.NotOnOrAfter = xmlTime(notAfter) | ||||||
|  | 	subjConfirmationData.Recipient = defaultRedirectURI | ||||||
|  | 	err = p.validateSubjectConfirmation(subj) | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Fatalf("validation of %q should fail", " expired SubjectConfirmationData") | ||||||
|  | 	} | ||||||
|  | 	// valid SubjectConfirmationData | ||||||
|  | 	notAfter = p.now().Add(+time.Duration(60) * time.Second) | ||||||
|  | 	subjConfirmationData.NotOnOrAfter = xmlTime(notAfter) | ||||||
|  | 	subjConfirmationData.Recipient = defaultRedirectURI | ||||||
|  | 	err = p.validateSubjectConfirmation(subj) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("validation of %q should succed", "valid SubjectConfirmationData") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestValidateConditions(t *testing.T) { | ||||||
|  | 	p := newProvider("", "") | ||||||
|  | 	var err error | ||||||
|  | 	var notAfter, notBefore time.Time | ||||||
|  | 	cond := conditions{ | ||||||
|  | 		AudienceRestriction: &audienceRestriction{}, | ||||||
|  | 	} | ||||||
|  | 	assert := &assertion{} | ||||||
|  | 	// Assertion without Conditions | ||||||
|  | 	err = p.validateConditions(assert) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("validation of %q should succeed", "Assertion without Conditions") | ||||||
|  | 	} | ||||||
|  | 	// Assertion with empty Conditions | ||||||
|  | 	assert.Conditions = &cond | ||||||
|  | 	err = p.validateConditions(assert) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("validation of %q should succeed", "Assertion with empty Conditions") | ||||||
|  | 	} | ||||||
|  | 	// Conditions with valid timestamps | ||||||
|  | 	notBefore = p.now().Add(-time.Duration(60) * time.Second) | ||||||
|  | 	notAfter = p.now().Add(+time.Duration(60) * time.Second) | ||||||
|  | 	cond.NotBefore = xmlTime(notBefore) | ||||||
|  | 	cond.NotOnOrAfter = xmlTime(notAfter) | ||||||
|  | 	err = p.validateConditions(assert) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("validation of %q should succeed", "Conditions with valid timestamps") | ||||||
|  | 	} | ||||||
|  | 	// Conditions where notBefore is 45 seconds after now | ||||||
|  | 	notBefore = p.now().Add(+time.Duration(45) * time.Second) | ||||||
|  | 	cond.NotBefore = xmlTime(notBefore) | ||||||
|  | 	err = p.validateConditions(assert) | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Fatalf("validation of %q should fail", "Conditions where notBefore is 45 seconds after now") | ||||||
|  | 	} | ||||||
|  | 	// Conditions where notBefore is 15 seconds after now | ||||||
|  | 	notBefore = p.now().Add(+time.Duration(15) * time.Second) | ||||||
|  | 	cond.NotBefore = xmlTime(notBefore) | ||||||
|  | 	err = p.validateConditions(assert) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("validation of %q should succeed", "Conditions where notBefore is 15 seconds after now") | ||||||
|  | 	} | ||||||
|  | 	// Audiences contains the issuer | ||||||
|  | 	validAudience := audience{Value: p.issuer} | ||||||
|  | 	cond.AudienceRestriction.Audiences = []audience{validAudience} | ||||||
|  | 	err = p.validateConditions(assert) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("validation of %q should succeed", "Audiences contains the issuer") | ||||||
|  | 	} | ||||||
|  | 	// Audiences is not empty and not contains the issuer | ||||||
|  | 	invalidAudience := audience{Value: "invalid"} | ||||||
|  | 	cond.AudienceRestriction.Audiences = []audience{invalidAudience} | ||||||
|  | 	err = p.validateConditions(assert) | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Fatalf("validation of %q should succeed", "Audiences is not empty and not contains the issuer") | ||||||
|  | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										26
									
								
								connector/saml/testdata/idp-cert.pem
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								connector/saml/testdata/idp-cert.pem
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | -----BEGIN CERTIFICATE----- | ||||||
|  | MIIEUTCCAzmgAwIBAgIJAJdmunb39nFKMA0GCSqGSIb3DQEBCwUAMHgxCzAJBgNV | ||||||
|  | BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMQwwCgYDVQQKEwNJRFAxFDASBgNV | ||||||
|  | BAsTC1NTT1Byb3ZpZGVyMRMwEQYDVQQDEwpkZXYtOTY5MjQ0MRswGQYJKoZIhvcN | ||||||
|  | AQkBFgxpbmZvQGlkcC5vcmcwHhcNMTcwMTI0MTczMTI3WhcNMjcwMTIyMTczMTI3 | ||||||
|  | WjB4MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEMMAoGA1UEChMD | ||||||
|  | SURQMRQwEgYDVQQLEwtTU09Qcm92aWRlcjETMBEGA1UEAxMKZGV2LTk2OTI0NDEb | ||||||
|  | MBkGCSqGSIb3DQEJARYMaW5mb0BpZHAub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOC | ||||||
|  | AQ8AMIIBCgKCAQEA0X/AE1tmDmhGRROAWaJ82XSORivRfgNt9Fb4rLrf6nIJsQN3 | ||||||
|  | vNb1Nk4DSUEDdQuvHNaEemSVkSPgfq5qnhh37bJaghr0728J8dOyYzV5eArPvsby | ||||||
|  | CRcnhXQzpCK2zvHwjgxNJMsNJLbnYpG/U+dCdCtcOOn9JEhKO8wKn06y2tcrvC1u | ||||||
|  | uVs7bodukPUNq82KJTyvCQP8jh1hEZXeR2siJFDeJj1n2FNTMeCKIqOb42J/i+sB | ||||||
|  | TlyK3mV5Ni++hI/ssIYVbPwrMIBd6sKLVAgInshBHOj/7XcXW/rMf468YtBKs4Xn | ||||||
|  | XsE3hLoU02aWCRDlVHa4hm3jfIAqEADOUumklQIDAQABo4HdMIHaMB0GA1UdDgQW | ||||||
|  | BBRjN/dQSvhZxIsHTXmDKQJkPrjp0TCBqgYDVR0jBIGiMIGfgBRjN/dQSvhZxIsH | ||||||
|  | TXmDKQJkPrjp0aF8pHoweDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3Ju | ||||||
|  | aWExDDAKBgNVBAoTA0lEUDEUMBIGA1UECxMLU1NPUHJvdmlkZXIxEzARBgNVBAMT | ||||||
|  | CmRldi05NjkyNDQxGzAZBgkqhkiG9w0BCQEWDGluZm9AaWRwLm9yZ4IJAJdmunb3 | ||||||
|  | 9nFKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAIqHUglIUAA+BKMW | ||||||
|  | 6B0Q+cqIgDr9fWlsvDwIVK7/cvUeGIH3icSsje9AVZ4nQOJpxmC/E06HfuDXmbT1 | ||||||
|  | wG16jNo01mPW9qaOGRJuQqlZdegCSF385o/OHcbaEKBRwyYuvLfu80EREj8wcMUK | ||||||
|  | FpExoaxK7K8DS7hh3w7exLB80jyhIaDEYc1hdyAl+206XpOXSYBetsg7I622R2+a | ||||||
|  | jSL7ygUxQjmKQ5DyInPdXzCFCL6Ew/BN0dwzfnBEEK223ruOWBLpj13zMC077dor | ||||||
|  | /NgYyHZU6iqiDS2eYO5jhVMve/mP9734+6N34seQRmekfmsf2dJcEQhPVYr/j0De | ||||||
|  | Jc3men4= | ||||||
|  | -----END CERTIFICATE----- | ||||||
							
								
								
									
										29
									
								
								connector/saml/testdata/idp-resp-signed-assertion.xml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								connector/saml/testdata/idp-resp-signed-assertion.xml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | <Response xmlns="urn:oasis:names:tc:SAML:2.0:protocol" Destination="http://localhost:5556/dex/callback" ID="id108965453120986171998428970" InResponseTo="_fd1b3ef9-ec09-44a7-a66b-0d39c250f6a0" IssueInstant="2016-12-20T22:18:23.771Z" Version="2.0"> | ||||||
|  |   <Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://www.okta.com/exk91cb99lKkKSYoy0h7</Issuer> | ||||||
|  |   <Status> | ||||||
|  |     <StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/> | ||||||
|  |   </Status> | ||||||
|  |   <Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="pfxe4534a5f-0f40-2f3a-599d-4dfd123f7d0a" IssueInstant="2016-12-20T22:18:23.771Z" Version="2.0"> | ||||||
|  |     <Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://www.okta.com/exk91cb99lKkKSYoy0h7</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/2000/09/xmldsig#rsa-sha1"/> | ||||||
|  |   <ds:Reference URI="#pfxe4534a5f-0f40-2f3a-599d-4dfd123f7d0a"><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#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>HFNooGfpAONF7T96W3bFsXkH51k=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>dI0QBihhNT5rtRYE9iB0lEKXkE7Yr4+QueOItRH2RcKwAXJ6DA/m3D/S7qwXk00Hn8ZpHu48ZO+HJpyweEEh2UuUWJCCTwwggagKybbSoRx3UTnSuNAFTdoDWTGt89z8j4+gRMC0sepYwppF3u87vJKRVBh8HjFfrHmWsZKwNtfoeXOOFCeatwxcI1sKCoBs2fTn78683ThoAJe3pygipSHY5WPt4dfT/yAY5Ars+OPY/N02M80OfIygZXdJwND0tVPJIF3M9DaehSkvCBHs7QA7DARsRXcuXdsYY7R8wHzqDVJZ4OvcsprONamm5AgUIpql1CjT94rFwWOFyxF2tg==</ds:SignatureValue> | ||||||
|  | <ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIEUTCCAzmgAwIBAgIJAJdmunb39nFKMA0GCSqGSIb3DQEBCwUAMHgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMQwwCgYDVQQKEwNJRFAxFDASBgNVBAsTC1NTT1Byb3ZpZGVyMRMwEQYDVQQDEwpkZXYtOTY5MjQ0MRswGQYJKoZIhvcNAQkBFgxpbmZvQGlkcC5vcmcwHhcNMTcwMTI0MTczMTI3WhcNMjcwMTIyMTczMTI3WjB4MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEMMAoGA1UEChMDSURQMRQwEgYDVQQLEwtTU09Qcm92aWRlcjETMBEGA1UEAxMKZGV2LTk2OTI0NDEbMBkGCSqGSIb3DQEJARYMaW5mb0BpZHAub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0X/AE1tmDmhGRROAWaJ82XSORivRfgNt9Fb4rLrf6nIJsQN3vNb1Nk4DSUEDdQuvHNaEemSVkSPgfq5qnhh37bJaghr0728J8dOyYzV5eArPvsbyCRcnhXQzpCK2zvHwjgxNJMsNJLbnYpG/U+dCdCtcOOn9JEhKO8wKn06y2tcrvC1uuVs7bodukPUNq82KJTyvCQP8jh1hEZXeR2siJFDeJj1n2FNTMeCKIqOb42J/i+sBTlyK3mV5Ni++hI/ssIYVbPwrMIBd6sKLVAgInshBHOj/7XcXW/rMf468YtBKs4XnXsE3hLoU02aWCRDlVHa4hm3jfIAqEADOUumklQIDAQABo4HdMIHaMB0GA1UdDgQWBBRjN/dQSvhZxIsHTXmDKQJkPrjp0TCBqgYDVR0jBIGiMIGfgBRjN/dQSvhZxIsHTXmDKQJkPrjp0aF8pHoweDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDDAKBgNVBAoTA0lEUDEUMBIGA1UECxMLU1NPUHJvdmlkZXIxEzARBgNVBAMTCmRldi05NjkyNDQxGzAZBgkqhkiG9w0BCQEWDGluZm9AaWRwLm9yZ4IJAJdmunb39nFKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAIqHUglIUAA+BKMW6B0Q+cqIgDr9fWlsvDwIVK7/cvUeGIH3icSsje9AVZ4nQOJpxmC/E06HfuDXmbT1wG16jNo01mPW9qaOGRJuQqlZdegCSF385o/OHcbaEKBRwyYuvLfu80EREj8wcMUKFpExoaxK7K8DS7hh3w7exLB80jyhIaDEYc1hdyAl+206XpOXSYBetsg7I622R2+ajSL7ygUxQjmKQ5DyInPdXzCFCL6Ew/BN0dwzfnBEEK223ruOWBLpj13zMC077dor/NgYyHZU6iqiDS2eYO5jhVMve/mP9734+6N34seQRmekfmsf2dJcEQhPVYr/j0DeJc3men4=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature> | ||||||
|  |     <Subject> | ||||||
|  |       <NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">eric.chiang+okta@coreos.com</NameID> | ||||||
|  |       <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"> | ||||||
|  |         <SubjectConfirmationData InResponseTo="_fd1b3ef9-ec09-44a7-a66b-0d39c250f6a0" NotOnOrAfter="2116-12-20T22:23:23.772Z" Recipient="http://localhost:5556/dex/callback"/> | ||||||
|  |       </SubjectConfirmation> | ||||||
|  |     </Subject> | ||||||
|  |     <Conditions NotBefore="2016-12-20T22:13:23.772Z" NotOnOrAfter="2116-12-20T22:23:23.772Z"> | ||||||
|  |       <AudienceRestriction> | ||||||
|  |         <Audience>http://localhost:5556/dex/callback</Audience> | ||||||
|  |       </AudienceRestriction> | ||||||
|  |     </Conditions> | ||||||
|  |     <AuthnStatement AuthnInstant="2016-12-20T22:18:23.771Z" SessionIndex="_fd1b3ef9-ec09-44a7-a66b-0d39c250f6a0"> | ||||||
|  |       <AuthnContext> | ||||||
|  |         <AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</AuthnContextClassRef> | ||||||
|  |       </AuthnContext> | ||||||
|  |     </AuthnStatement> | ||||||
|  |   </Assertion> | ||||||
|  | </Response> | ||||||
							
								
								
									
										34
									
								
								connector/saml/testdata/idp-resp-signed-message-and-assertion.xml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								connector/saml/testdata/idp-resp-signed-message-and-assertion.xml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <Response xmlns="urn:oasis:names:tc:SAML:2.0:protocol" Destination="http://localhost:5556/dex/callback" ID="pfxb2d65771-7c81-6391-4e1f-79211ae3a3fd" InResponseTo="_fd1b3ef9-ec09-44a7-a66b-0d39c250f6a0" IssueInstant="2016-12-20T22:18:23.771Z" Version="2.0"><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/2000/09/xmldsig#rsa-sha1"/> | ||||||
|  |   <ds:Reference URI="#pfxb2d65771-7c81-6391-4e1f-79211ae3a3fd"><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#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>P2k0nQ19ZcowlcaOz6do6Tyu8WI=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>ytdy1qRPdMeIGnlkkaeLdblzTPtIFc0EJNm8WktsNU1Mn6G/6AmNaXEUik2BkTpk8zKabHdSf6+le8hwRiyfNWPTF84lzVdMjQ/+I8pnX/srpG534zoSAsP6ZFQvHp46AHPx31KP75H/ymqx2DNppqxh8JjUeMKQkPUEqduWUZ4kFjcsrz9H3MNVsHfxntnswibiknU/wAthtBuY2I6yOIF55RprUgYb5j2TqDd3IArF6LkxWRvHvhaw66MdhY1iiit7AFOcuHJVyPe8Attra94jwM+O1Ch+HQgoI43nX91d/jkP0vyWzWD8Xkcwb+KuRPsQflxjV22UU0+JbwrBYA==</ds:SignatureValue> | ||||||
|  | <ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIEUTCCAzmgAwIBAgIJAJdmunb39nFKMA0GCSqGSIb3DQEBCwUAMHgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMQwwCgYDVQQKEwNJRFAxFDASBgNVBAsTC1NTT1Byb3ZpZGVyMRMwEQYDVQQDEwpkZXYtOTY5MjQ0MRswGQYJKoZIhvcNAQkBFgxpbmZvQGlkcC5vcmcwHhcNMTcwMTI0MTczMTI3WhcNMjcwMTIyMTczMTI3WjB4MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEMMAoGA1UEChMDSURQMRQwEgYDVQQLEwtTU09Qcm92aWRlcjETMBEGA1UEAxMKZGV2LTk2OTI0NDEbMBkGCSqGSIb3DQEJARYMaW5mb0BpZHAub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0X/AE1tmDmhGRROAWaJ82XSORivRfgNt9Fb4rLrf6nIJsQN3vNb1Nk4DSUEDdQuvHNaEemSVkSPgfq5qnhh37bJaghr0728J8dOyYzV5eArPvsbyCRcnhXQzpCK2zvHwjgxNJMsNJLbnYpG/U+dCdCtcOOn9JEhKO8wKn06y2tcrvC1uuVs7bodukPUNq82KJTyvCQP8jh1hEZXeR2siJFDeJj1n2FNTMeCKIqOb42J/i+sBTlyK3mV5Ni++hI/ssIYVbPwrMIBd6sKLVAgInshBHOj/7XcXW/rMf468YtBKs4XnXsE3hLoU02aWCRDlVHa4hm3jfIAqEADOUumklQIDAQABo4HdMIHaMB0GA1UdDgQWBBRjN/dQSvhZxIsHTXmDKQJkPrjp0TCBqgYDVR0jBIGiMIGfgBRjN/dQSvhZxIsHTXmDKQJkPrjp0aF8pHoweDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDDAKBgNVBAoTA0lEUDEUMBIGA1UECxMLU1NPUHJvdmlkZXIxEzARBgNVBAMTCmRldi05NjkyNDQxGzAZBgkqhkiG9w0BCQEWDGluZm9AaWRwLm9yZ4IJAJdmunb39nFKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAIqHUglIUAA+BKMW6B0Q+cqIgDr9fWlsvDwIVK7/cvUeGIH3icSsje9AVZ4nQOJpxmC/E06HfuDXmbT1wG16jNo01mPW9qaOGRJuQqlZdegCSF385o/OHcbaEKBRwyYuvLfu80EREj8wcMUKFpExoaxK7K8DS7hh3w7exLB80jyhIaDEYc1hdyAl+206XpOXSYBetsg7I622R2+ajSL7ygUxQjmKQ5DyInPdXzCFCL6Ew/BN0dwzfnBEEK223ruOWBLpj13zMC077dor/NgYyHZU6iqiDS2eYO5jhVMve/mP9734+6N34seQRmekfmsf2dJcEQhPVYr/j0DeJc3men4=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature> | ||||||
|  |   <Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://www.okta.com/exk91cb99lKkKSYoy0h7</Issuer> | ||||||
|  |   <Status> | ||||||
|  |     <StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/> | ||||||
|  |   </Status> | ||||||
|  |   <Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="pfxa75683e6-25ff-5639-6806-08d2a132ad4e" IssueInstant="2016-12-20T22:18:23.771Z" Version="2.0"> | ||||||
|  |     <Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://www.okta.com/exk91cb99lKkKSYoy0h7</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/2000/09/xmldsig#rsa-sha1"/> | ||||||
|  |   <ds:Reference URI="#pfxa75683e6-25ff-5639-6806-08d2a132ad4e"><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#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>4jNCSI3tTnbpozZ8qT4FZe+EWV8=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>xtTnl92ArZyxskD3b34cIjo5LIpeE+3RjW+jgtXMXhUIZp3uGJ2RC6n1CbJ6IWuo4KmezpnVUnSWNz/fgOTZCN/1VlqsfLDpoTf790GrP+q6rKyw8CW7nd0uVS5FRYe05HTO6C5RqnaE9PmZ/YYbiWtLIDx0+kqvu/jFr+D144G/mukaVG4ydnDQ/tl21N6hWIOpi1tWaNPv50OEEgY//9VPql9Us3YuhfrxNggVugauArwY9RL4nVFVjALP1wpkZn1JzpgNMFgvXfY3MxnI1OnWg6ypJESugIKroKqj5RyqMIaLICsUOBwIKk8R4zAATrB+D+kuFV9Ec837duW/Eg==</ds:SignatureValue> | ||||||
|  | <ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIEUTCCAzmgAwIBAgIJAJdmunb39nFKMA0GCSqGSIb3DQEBCwUAMHgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMQwwCgYDVQQKEwNJRFAxFDASBgNVBAsTC1NTT1Byb3ZpZGVyMRMwEQYDVQQDEwpkZXYtOTY5MjQ0MRswGQYJKoZIhvcNAQkBFgxpbmZvQGlkcC5vcmcwHhcNMTcwMTI0MTczMTI3WhcNMjcwMTIyMTczMTI3WjB4MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEMMAoGA1UEChMDSURQMRQwEgYDVQQLEwtTU09Qcm92aWRlcjETMBEGA1UEAxMKZGV2LTk2OTI0NDEbMBkGCSqGSIb3DQEJARYMaW5mb0BpZHAub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0X/AE1tmDmhGRROAWaJ82XSORivRfgNt9Fb4rLrf6nIJsQN3vNb1Nk4DSUEDdQuvHNaEemSVkSPgfq5qnhh37bJaghr0728J8dOyYzV5eArPvsbyCRcnhXQzpCK2zvHwjgxNJMsNJLbnYpG/U+dCdCtcOOn9JEhKO8wKn06y2tcrvC1uuVs7bodukPUNq82KJTyvCQP8jh1hEZXeR2siJFDeJj1n2FNTMeCKIqOb42J/i+sBTlyK3mV5Ni++hI/ssIYVbPwrMIBd6sKLVAgInshBHOj/7XcXW/rMf468YtBKs4XnXsE3hLoU02aWCRDlVHa4hm3jfIAqEADOUumklQIDAQABo4HdMIHaMB0GA1UdDgQWBBRjN/dQSvhZxIsHTXmDKQJkPrjp0TCBqgYDVR0jBIGiMIGfgBRjN/dQSvhZxIsHTXmDKQJkPrjp0aF8pHoweDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDDAKBgNVBAoTA0lEUDEUMBIGA1UECxMLU1NPUHJvdmlkZXIxEzARBgNVBAMTCmRldi05NjkyNDQxGzAZBgkqhkiG9w0BCQEWDGluZm9AaWRwLm9yZ4IJAJdmunb39nFKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAIqHUglIUAA+BKMW6B0Q+cqIgDr9fWlsvDwIVK7/cvUeGIH3icSsje9AVZ4nQOJpxmC/E06HfuDXmbT1wG16jNo01mPW9qaOGRJuQqlZdegCSF385o/OHcbaEKBRwyYuvLfu80EREj8wcMUKFpExoaxK7K8DS7hh3w7exLB80jyhIaDEYc1hdyAl+206XpOXSYBetsg7I622R2+ajSL7ygUxQjmKQ5DyInPdXzCFCL6Ew/BN0dwzfnBEEK223ruOWBLpj13zMC077dor/NgYyHZU6iqiDS2eYO5jhVMve/mP9734+6N34seQRmekfmsf2dJcEQhPVYr/j0DeJc3men4=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature> | ||||||
|  |     <Subject> | ||||||
|  |       <NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">eric.chiang+okta@coreos.com</NameID> | ||||||
|  |       <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"> | ||||||
|  |         <SubjectConfirmationData InResponseTo="_fd1b3ef9-ec09-44a7-a66b-0d39c250f6a0" NotOnOrAfter="2116-12-20T22:23:23.772Z" Recipient="http://localhost:5556/dex/callback"/> | ||||||
|  |       </SubjectConfirmation> | ||||||
|  |     </Subject> | ||||||
|  |     <Conditions NotBefore="2016-12-20T22:13:23.772Z" NotOnOrAfter="2116-12-20T22:23:23.772Z"> | ||||||
|  |       <AudienceRestriction> | ||||||
|  |         <Audience>http://localhost:5556/dex/callback</Audience> | ||||||
|  |       </AudienceRestriction> | ||||||
|  |     </Conditions> | ||||||
|  |     <AuthnStatement AuthnInstant="2016-12-20T22:18:23.771Z" SessionIndex="_fd1b3ef9-ec09-44a7-a66b-0d39c250f6a0"> | ||||||
|  |       <AuthnContext> | ||||||
|  |         <AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</AuthnContextClassRef> | ||||||
|  |       </AuthnContext> | ||||||
|  |     </AuthnStatement> | ||||||
|  |   </Assertion> | ||||||
|  | </Response> | ||||||
							
								
								
									
										30
									
								
								connector/saml/testdata/idp-resp-signed-message.xml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								connector/saml/testdata/idp-resp-signed-message.xml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <Response xmlns="urn:oasis:names:tc:SAML:2.0:protocol" Destination="http://localhost:5556/dex/callback" ID="pfx2801a166-e451-31a9-87a4-4cd777c16182" InResponseTo="_fd1b3ef9-ec09-44a7-a66b-0d39c250f6a0" IssueInstant="2016-12-20T22:18:23.771Z" Version="2.0"><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/2000/09/xmldsig#rsa-sha1"/> | ||||||
|  |   <ds:Reference URI="#pfx2801a166-e451-31a9-87a4-4cd777c16182"><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#"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>Oy9CTB/hzFWyr6QF99EZ4ymIEfY=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>COKw1DUpDzNVulVNFlcrPwaalCwr8BfTye92GN5snTmx3IDKLudr1PT+WPt5i2N4xaoFcq/X1p/yEVmWtC1O+YYXNNIouFwps9Gyw/iDEMs1TtVKfikbloKWkDdYgfqgcon+mOq/lHagLVAcgz5QRBHVTIrFWcFYbnemsj1hy8q7ToIeoyHX9f5TAZBfZEEbdZcsD581xKPafNGowgfWxkgEwLBzFsJVYg/QfeoNnORTsKlsQBuswiXrsWatZNOOjWpdF9qqYn/3f9axqx2CJPD2HB38Vl0g4dTFnpmMAe45ndJq0IpXr9YJNCDJjUIvR7srdV1AW7qe2Mp6LxBBNw==</ds:SignatureValue> | ||||||
|  | <ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIIEUTCCAzmgAwIBAgIJAJdmunb39nFKMA0GCSqGSIb3DQEBCwUAMHgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMQwwCgYDVQQKEwNJRFAxFDASBgNVBAsTC1NTT1Byb3ZpZGVyMRMwEQYDVQQDEwpkZXYtOTY5MjQ0MRswGQYJKoZIhvcNAQkBFgxpbmZvQGlkcC5vcmcwHhcNMTcwMTI0MTczMTI3WhcNMjcwMTIyMTczMTI3WjB4MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEMMAoGA1UEChMDSURQMRQwEgYDVQQLEwtTU09Qcm92aWRlcjETMBEGA1UEAxMKZGV2LTk2OTI0NDEbMBkGCSqGSIb3DQEJARYMaW5mb0BpZHAub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0X/AE1tmDmhGRROAWaJ82XSORivRfgNt9Fb4rLrf6nIJsQN3vNb1Nk4DSUEDdQuvHNaEemSVkSPgfq5qnhh37bJaghr0728J8dOyYzV5eArPvsbyCRcnhXQzpCK2zvHwjgxNJMsNJLbnYpG/U+dCdCtcOOn9JEhKO8wKn06y2tcrvC1uuVs7bodukPUNq82KJTyvCQP8jh1hEZXeR2siJFDeJj1n2FNTMeCKIqOb42J/i+sBTlyK3mV5Ni++hI/ssIYVbPwrMIBd6sKLVAgInshBHOj/7XcXW/rMf468YtBKs4XnXsE3hLoU02aWCRDlVHa4hm3jfIAqEADOUumklQIDAQABo4HdMIHaMB0GA1UdDgQWBBRjN/dQSvhZxIsHTXmDKQJkPrjp0TCBqgYDVR0jBIGiMIGfgBRjN/dQSvhZxIsHTXmDKQJkPrjp0aF8pHoweDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDDAKBgNVBAoTA0lEUDEUMBIGA1UECxMLU1NPUHJvdmlkZXIxEzARBgNVBAMTCmRldi05NjkyNDQxGzAZBgkqhkiG9w0BCQEWDGluZm9AaWRwLm9yZ4IJAJdmunb39nFKMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAIqHUglIUAA+BKMW6B0Q+cqIgDr9fWlsvDwIVK7/cvUeGIH3icSsje9AVZ4nQOJpxmC/E06HfuDXmbT1wG16jNo01mPW9qaOGRJuQqlZdegCSF385o/OHcbaEKBRwyYuvLfu80EREj8wcMUKFpExoaxK7K8DS7hh3w7exLB80jyhIaDEYc1hdyAl+206XpOXSYBetsg7I622R2+ajSL7ygUxQjmKQ5DyInPdXzCFCL6Ew/BN0dwzfnBEEK223ruOWBLpj13zMC077dor/NgYyHZU6iqiDS2eYO5jhVMve/mP9734+6N34seQRmekfmsf2dJcEQhPVYr/j0DeJc3men4=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature> | ||||||
|  |   <Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://www.okta.com/exk91cb99lKkKSYoy0h7</Issuer> | ||||||
|  |   <Status> | ||||||
|  |     <StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/> | ||||||
|  |   </Status> | ||||||
|  |   <Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="id10896545312129779529177535" IssueInstant="2016-12-20T22:18:23.771Z" Version="2.0"> | ||||||
|  |     <Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://www.okta.com/exk91cb99lKkKSYoy0h7</Issuer> | ||||||
|  |     <Subject> | ||||||
|  |       <NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">eric.chiang+okta@coreos.com</NameID> | ||||||
|  |       <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"> | ||||||
|  |         <SubjectConfirmationData InResponseTo="_fd1b3ef9-ec09-44a7-a66b-0d39c250f6a0" NotOnOrAfter="2116-12-20T22:23:23.772Z" Recipient="http://localhost:5556/dex/callback"/> | ||||||
|  |       </SubjectConfirmation> | ||||||
|  |     </Subject> | ||||||
|  |     <Conditions NotBefore="2016-12-20T22:13:23.772Z" NotOnOrAfter="2116-12-20T22:23:23.772Z"> | ||||||
|  |       <AudienceRestriction> | ||||||
|  |         <Audience>http://localhost:5556/dex/callback</Audience> | ||||||
|  |       </AudienceRestriction> | ||||||
|  |     </Conditions> | ||||||
|  |     <AuthnStatement AuthnInstant="2016-12-20T22:18:23.771Z" SessionIndex="_fd1b3ef9-ec09-44a7-a66b-0d39c250f6a0"> | ||||||
|  |       <AuthnContext> | ||||||
|  |         <AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</AuthnContextClassRef> | ||||||
|  |       </AuthnContext> | ||||||
|  |     </AuthnStatement> | ||||||
|  |   </Assertion> | ||||||
|  | </Response> | ||||||
							
								
								
									
										34
									
								
								connector/saml/testdata/idp-resp.xml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								connector/saml/testdata/idp-resp.xml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <Response xmlns="urn:oasis:names:tc:SAML:2.0:protocol" Destination="http://localhost:5556/dex/callback" ID="id108965453120986171998428970" InResponseTo="_fd1b3ef9-ec09-44a7-a66b-0d39c250f6a0" IssueInstant="2016-12-20T22:18:23.771Z" Version="2.0"> | ||||||
|  |   <Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://www.okta.com/exk91cb99lKkKSYoy0h7</Issuer> | ||||||
|  |   <Status> | ||||||
|  |     <StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/> | ||||||
|  |   </Status> | ||||||
|  |   <Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="id10896545312129779529177535" IssueInstant="2016-12-20T22:18:23.771Z" Version="2.0"> | ||||||
|  |     <Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://www.okta.com/exk91cb99lKkKSYoy0h7</Issuer> | ||||||
|  |     <Subject> | ||||||
|  |       <NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">eric.chiang+okta@coreos.com</NameID> | ||||||
|  |       <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"> | ||||||
|  |         <SubjectConfirmationData InResponseTo="_fd1b3ef9-ec09-44a7-a66b-0d39c250f6a0" NotOnOrAfter="2116-12-20T22:23:23.772Z" Recipient="http://localhost:5556/dex/callback"/> | ||||||
|  |       </SubjectConfirmation> | ||||||
|  |     </Subject> | ||||||
|  |     <Conditions NotBefore="2016-12-20T22:13:23.772Z" NotOnOrAfter="2116-12-20T22:23:23.772Z"> | ||||||
|  |       <AudienceRestriction> | ||||||
|  |         <Audience>http://localhost:5556/dex/callback</Audience> | ||||||
|  |       </AudienceRestriction> | ||||||
|  |     </Conditions> | ||||||
|  |     <AuthnStatement AuthnInstant="2016-12-20T22:18:23.771Z" SessionIndex="_fd1b3ef9-ec09-44a7-a66b-0d39c250f6a0"> | ||||||
|  |       <AuthnContext> | ||||||
|  |         <AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</AuthnContextClassRef> | ||||||
|  |       </AuthnContext> | ||||||
|  |     </AuthnStatement> | ||||||
|  |     <AttributeStatement> | ||||||
|  |       <Attribute Name="user" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"> | ||||||
|  |         <AttributeValue xsi:type="xs:string">admin</AttributeValue> | ||||||
|  |       </Attribute> | ||||||
|  |       <Attribute Name="email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"> | ||||||
|  |         <AttributeValue xsi:type="xs:string">eric.chiang+okta@coreos.com</AttributeValue> | ||||||
|  |       </Attribute> | ||||||
|  |     </AttributeStatement> | ||||||
|  |   </Assertion> | ||||||
|  | </Response> | ||||||
| @@ -57,6 +57,8 @@ type authnRequest struct { | |||||||
| 	IsPassive       bool   `xml:"IsPassive,attr,omitempty"` | 	IsPassive       bool   `xml:"IsPassive,attr,omitempty"` | ||||||
| 	ProtocolBinding string `xml:"ProtocolBinding,attr,omitempty"` | 	ProtocolBinding string `xml:"ProtocolBinding,attr,omitempty"` | ||||||
|  |  | ||||||
|  | 	AssertionConsumerServiceURL string `xml:"AssertionConsumerServiceURL,attr,omitempty"` | ||||||
|  |  | ||||||
| 	Subject      *subject      `xml:"Subject,omitempty"` | 	Subject      *subject      `xml:"Subject,omitempty"` | ||||||
| 	Issuer       *issuer       `xml:"Issuer,omitempty"` | 	Issuer       *issuer       `xml:"Issuer,omitempty"` | ||||||
| 	NameIDPolicy *nameIDPolicy `xml:"NameIDPolicy,omitempty"` | 	NameIDPolicy *nameIDPolicy `xml:"NameIDPolicy,omitempty"` | ||||||
| @@ -69,6 +71,7 @@ type subject struct { | |||||||
| 	XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion Subject"` | 	XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion Subject"` | ||||||
|  |  | ||||||
| 	NameID               *nameID               `xml:"NameID,omitempty"` | 	NameID               *nameID               `xml:"NameID,omitempty"` | ||||||
|  | 	SubjectConfirmations []subjectConfirmation `xml:"SubjectConfirmation"` | ||||||
|  |  | ||||||
| 	// TODO(ericchiang): Do we need to deal with baseID? | 	// TODO(ericchiang): Do we need to deal with baseID? | ||||||
| } | } | ||||||
| @@ -80,6 +83,60 @@ type nameID struct { | |||||||
| 	Value  string `xml:",chardata"` | 	Value  string `xml:",chardata"` | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type subjectConfirmationData struct { | ||||||
|  | 	XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion SubjectConfirmationData"` | ||||||
|  |  | ||||||
|  | 	NotOnOrAfter xmlTime `xml:"NotOnOrAfter,attr,omitempty"` | ||||||
|  | 	Recipient    string  `xml:"Recipient,attr,omitempty"` | ||||||
|  | 	InResponseTo string  `xml:"InResponseTo,attr,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type subjectConfirmation struct { | ||||||
|  | 	XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion SubjectConfirmation"` | ||||||
|  |  | ||||||
|  | 	Method                  string                   `xml:"Method,attr,omitempty"` | ||||||
|  | 	SubjectConfirmationData *subjectConfirmationData `xml:"SubjectConfirmationData,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type audience struct { | ||||||
|  | 	XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion Audience"` | ||||||
|  | 	Value   string   `xml:",chardata"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type audienceRestriction struct { | ||||||
|  | 	XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion AudienceRestriction"` | ||||||
|  |  | ||||||
|  | 	Audiences []audience `xml:"Audience"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type conditions struct { | ||||||
|  | 	XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion Conditions"` | ||||||
|  |  | ||||||
|  | 	NotBefore    xmlTime `xml:"NotBefore,attr,omitempty"` | ||||||
|  | 	NotOnOrAfter xmlTime `xml:"NotOnOrAfter,attr,omitempty"` | ||||||
|  |  | ||||||
|  | 	AudienceRestriction *audienceRestriction `xml:"AudienceRestriction,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type statusCode struct { | ||||||
|  | 	XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol StatusCode"` | ||||||
|  |  | ||||||
|  | 	Value string `xml:"Value,attr,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type statusMessage struct { | ||||||
|  | 	XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol StatusMessage"` | ||||||
|  |  | ||||||
|  | 	Value string `xml:",chardata"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type status struct { | ||||||
|  | 	XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:protocol Status"` | ||||||
|  |  | ||||||
|  | 	StatusCode    *statusCode    `xml:"StatusCode"` | ||||||
|  | 	StatusMessage *statusMessage `xml:"StatusMessage,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
| type issuer struct { | type issuer struct { | ||||||
| 	XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion Issuer"` | 	XMLName xml.Name `xml:"urn:oasis:names:tc:SAML:2.0:assertion Issuer"` | ||||||
| 	Issuer  string   `xml:",chardata"` | 	Issuer  string   `xml:",chardata"` | ||||||
| @@ -112,6 +169,8 @@ type response struct { | |||||||
|  |  | ||||||
| 	Issuer *issuer `xml:"Issuer,omitempty"` | 	Issuer *issuer `xml:"Issuer,omitempty"` | ||||||
|  |  | ||||||
|  | 	Status *status `xml:"Status"` | ||||||
|  |  | ||||||
| 	// TODO(ericchiang): How do deal with multiple assertions? | 	// TODO(ericchiang): How do deal with multiple assertions? | ||||||
| 	Assertion *assertion `xml:"Assertion,omitempty"` | 	Assertion *assertion `xml:"Assertion,omitempty"` | ||||||
| } | } | ||||||
| @@ -127,6 +186,8 @@ type assertion struct { | |||||||
|  |  | ||||||
| 	Subject *subject `xml:"Subject,omitempty"` | 	Subject *subject `xml:"Subject,omitempty"` | ||||||
|  |  | ||||||
|  | 	Conditions *conditions `xml:"Conditions"` | ||||||
|  |  | ||||||
| 	AttributeStatement *attributeStatement `xml:"AttributeStatement,omitempty"` | 	AttributeStatement *attributeStatement `xml:"AttributeStatement,omitempty"` | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								glide.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								glide.lock
									
									
									
										generated
									
									
									
								
							| @@ -1,5 +1,5 @@ | |||||||
| hash: 2f68b742168a81ebbe604be42801d37e9da71dff5aeb6b8f8e91ed81ff0edec0 | hash: 1207c251a7dab3b824746d66219beabc40f0b1d3c08ed90ac50f5bbdb8872631 | ||||||
| updated: 2017-01-09T14:51:09.514065012-08:00 | updated: 2017-01-25T20:32:43.599648533+01:00 | ||||||
| imports: | imports: | ||||||
| - name: github.com/beevik/etree | - name: github.com/beevik/etree | ||||||
|   version: 4cd0dd976db869f817248477718071a28e978df0 |   version: 4cd0dd976db869f817248477718071a28e978df0 | ||||||
| @@ -46,7 +46,7 @@ imports: | |||||||
|   subpackages: |   subpackages: | ||||||
|   - cacheobject |   - cacheobject | ||||||
| - name: github.com/russellhaering/goxmldsig | - name: github.com/russellhaering/goxmldsig | ||||||
|   version: d9f653eb27ee8b145f7d5a45172e81a93def0860 |   version: e2990269f42f6ddfea940870a0800a14acdb8c21 | ||||||
| - name: github.com/Sirupsen/logrus | - name: github.com/Sirupsen/logrus | ||||||
|   version: d26492970760ca5d33129d2d799e34be5c4782eb |   version: d26492970760ca5d33129d2d799e34be5c4782eb | ||||||
| - name: github.com/spf13/cobra | - name: github.com/spf13/cobra | ||||||
|   | |||||||
| @@ -134,7 +134,7 @@ import: | |||||||
|  |  | ||||||
| # XML signature validation for SAML connector | # XML signature validation for SAML connector | ||||||
| - package: github.com/russellhaering/goxmldsig | - package: github.com/russellhaering/goxmldsig | ||||||
|   version: d9f653eb27ee8b145f7d5a45172e81a93def0860 |   version: e2990269f42f6ddfea940870a0800a14acdb8c21 | ||||||
| - package: github.com/beevik/etree | - package: github.com/beevik/etree | ||||||
|   version: 4cd0dd976db869f817248477718071a28e978df0 |   version: 4cd0dd976db869f817248477718071a28e978df0 | ||||||
| - package: github.com/jonboulle/clockwork | - package: github.com/jonboulle/clockwork | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								vendor/github.com/russellhaering/goxmldsig/canonicalize.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/russellhaering/goxmldsig/canonicalize.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -84,6 +84,9 @@ func (a attrsByKey) Less(i, j int) bool { | |||||||
| 	if a[i].Space == "" && a[i].Key == "xmlns" { | 	if a[i].Space == "" && a[i].Key == "xmlns" { | ||||||
| 		return true | 		return true | ||||||
| 	} | 	} | ||||||
|  | 	if a[j].Space == "" && a[j].Key == "xmlns" { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if a[i].Space == "xmlns" { | 	if a[i].Space == "xmlns" { | ||||||
| 		if a[j].Space == "xmlns" { | 		if a[j].Space == "xmlns" { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user