diff --git a/README.md b/README.md index ad97627..2bbd21d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +Note: This is now archived because we no longer need to maintain this fork. + go-saml ====== diff --git a/authnrequest.go b/authnrequest.go index 6d31e99..2fc0c0e 100644 --- a/authnrequest.go +++ b/authnrequest.go @@ -130,7 +130,7 @@ func NewAuthnRequest() *AuthnRequest { Url: "", // caller must populate ar.AppSettings.Issuer SAML: "urn:oasis:names:tc:SAML:2.0:assertion", }, - IssueInstant: time.Now().UTC().Format(time.RFC3339Nano), + IssueInstant: time.Now().UTC().Format(time.RFC3339), NameIDPolicy: NameIDPolicy{ XMLName: xml.Name{ Local: "samlp:NameIDPolicy", diff --git a/authnresponse.go b/authnresponse.go index 2dbeaa5..a7b09ba 100644 --- a/authnresponse.go +++ b/authnresponse.go @@ -103,7 +103,7 @@ func NewSignedResponse() *Response { SAMLSIG: "http://www.w3.org/2000/09/xmldsig#", ID: util.ID(), Version: "2.0", - IssueInstant: time.Now().UTC().Format(time.RFC3339Nano), + IssueInstant: time.Now().UTC().Format(time.RFC3339), Issuer: Issuer{ XMLName: xml.Name{ Local: "saml:Issuer", @@ -203,7 +203,7 @@ func NewSignedResponse() *Response { SAML: "urn:oasis:names:tc:SAML:2.0:assertion", Version: "2.0", ID: util.ID(), - IssueInstant: time.Now().UTC().Format(time.RFC3339Nano), + IssueInstant: time.Now().UTC().Format(time.RFC3339), Issuer: Issuer{ XMLName: xml.Name{ Local: "saml:Issuer", @@ -232,7 +232,7 @@ func NewSignedResponse() *Response { Local: "saml:SubjectConfirmationData", }, InResponseTo: "", - NotOnOrAfter: time.Now().Add(time.Minute * 5).UTC().Format(time.RFC3339Nano), + NotOnOrAfter: time.Now().Add(time.Minute * 5).UTC().Format(time.RFC3339), Recipient: "", }, }, @@ -241,8 +241,8 @@ func NewSignedResponse() *Response { XMLName: xml.Name{ Local: "saml:Conditions", }, - NotBefore: time.Now().Add(time.Minute * -5).UTC().Format(time.RFC3339Nano), - NotOnOrAfter: time.Now().Add(time.Minute * 5).UTC().Format(time.RFC3339Nano), + NotBefore: time.Now().Add(time.Minute * -5).UTC().Format(time.RFC3339), + NotOnOrAfter: time.Now().Add(time.Minute * 5).UTC().Format(time.RFC3339), AudienceRestrictions: []AudienceRestriction{}, }, AttributeStatement: AttributeStatement{ @@ -348,6 +348,15 @@ func (r *Response) CompressedEncodedSignedString(privateKeyPath string) (string, return b64XML, nil } +// GetStatusCode will check for nested status code values +func (r *Response) GetStatusCode() string { + statusCode := r.Status.StatusCode + if statusCode.StatusCode != nil { + return statusCode.StatusCode.Value + } + return statusCode.Value +} + // GetAttribute by Name or by FriendlyName. Return blank string if not found func (r *Response) GetAttribute(name string) string { for _, attr := range r.Assertion.AttributeStatement.Attributes { diff --git a/authnresponse_test.go b/authnresponse_test.go new file mode 100644 index 0000000..1fbf19b --- /dev/null +++ b/authnresponse_test.go @@ -0,0 +1,54 @@ +package saml + +import ( + "encoding/xml" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStatus_Success(t *testing.T) { + response := NewSignedResponse() + assert.NotNil(t, response) + status := response.GetStatusCode() + assert.Equal(t, "urn:oasis:names:tc:SAML:2.0:status:Success", status) +} + +func TestStatus_EmptyStatusCode(t *testing.T) { + response := NewSignedResponse() + response.Status = Status{} + assert.NotNil(t, response) + status := response.GetStatusCode() + assert.Equal(t, "", status) +} + +func TestStatus_Failure(t *testing.T) { + response := NewSignedResponse() + response.Status = Status{ + XMLName: xml.Name{ + Local: "samlp:Status", + }, + StatusCode: StatusCode{ + XMLName: xml.Name{ + Local: "samlp:StatusCode", + }, + Value: "urn:oasis:names:tc:SAML:2.0:status:Requester", + StatusCode: &StatusCode{ + XMLName: xml.Name{ + Local: "samlp:StatusCode", + }, + Value: "urn:oasis:names:tc:SAML:2.0:status:RequestDenied", + }, + }, + StatusMessage: StatusMessage{ + XMLName: xml.Name{ + Local: "samlp:StatusMessage", + }, + Value: "Invalid request, ACS Url in request https://example.com/callback doesn't match configured ACS Url https://test.com/callback.", + }, + } + assert.NotNil(t, response) + status := response.GetStatusCode() + assert.Equal(t, "urn:oasis:names:tc:SAML:2.0:status:RequestDenied", status) + assert.Equal(t, "Invalid request, ACS Url in request https://example.com/callback doesn't match configured ACS Url https://test.com/callback.", response.Status.StatusMessage.Value) +} diff --git a/types.go b/types.go index 27a98e3..d2ac30b 100644 --- a/types.go +++ b/types.go @@ -13,7 +13,6 @@ type AuthnRequest struct { AssertionConsumerServiceURL string `xml:"AssertionConsumerServiceURL,attr"` Destination string `xml:"Destination,attr"` IssueInstant string `xml:"IssueInstant,attr"` - AssertionConsumerServiceIndex int `xml:"AssertionConsumerServiceIndex,attr"` AttributeConsumingServiceIndex int `xml:"AttributeConsumingServiceIndex,attr"` Issuer Issuer `xml:"Issuer"` NameIDPolicy NameIDPolicy `xml:"NameIDPolicy"` @@ -240,8 +239,9 @@ type SubjectConfirmation struct { } type Status struct { - XMLName xml.Name - StatusCode StatusCode `xml:"StatusCode"` + XMLName xml.Name + StatusCode StatusCode `xml:"StatusCode"` + StatusMessage StatusMessage `xml:"StatusMessage"` } type SubjectConfirmationData struct { @@ -259,8 +259,14 @@ type NameID struct { } type StatusCode struct { + XMLName xml.Name + StatusCode *StatusCode `xml:"StatusCode"` + Value string `xml:",attr"` +} + +type StatusMessage struct { XMLName xml.Name - Value string `xml:",attr"` + Value string `xml:",chardata"` } type AttributeValue struct {