Skip to content

Commit

Permalink
add field to IdpAuthnRequest so you can externally control the “curre…
Browse files Browse the repository at this point in the history
…nt” time (crewjam#136)

The default is obviously the current time, but for various reasons you may wish to evaluate the
response at a different reference time, for example processing a response that has been deferred.

We can’t use the global TimeNow() thunk, which is designed for testing, because it isn’t safe to modify concurrently.
  • Loading branch information
crewjam authored Jan 10, 2018
1 parent f5e68a0 commit 16d16c2
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 5 deletions.
12 changes: 7 additions & 5 deletions identity_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ type IdpAuthnRequest struct {
Assertion *Assertion
AssertionEl *etree.Element
ResponseEl *etree.Element
Now time.Time
}

// NewIdpAuthnRequest returns a new IdpAuthnRequest for the given HTTP request to the authorization
Expand All @@ -306,6 +307,7 @@ func NewIdpAuthnRequest(idp *IdentityProvider, r *http.Request) (*IdpAuthnReques
req := &IdpAuthnRequest{
IDP: idp,
HTTPRequest: r,
Now: TimeNow(),
}

switch r.Method {
Expand Down Expand Up @@ -375,7 +377,7 @@ func (req *IdpAuthnRequest) Validate() error {
}
}

if req.Request.IssueInstant.Add(MaxIssueDelay).Before(TimeNow()) {
if req.Request.IssueInstant.Add(MaxIssueDelay).Before(req.Now) {
return fmt.Errorf("request expired at %s",
req.Request.IssueInstant.Add(MaxIssueDelay))
}
Expand Down Expand Up @@ -591,8 +593,8 @@ func (DefaultAssertionMaker) MakeAssertion(req *IdpAuthnRequest, session *Sessio

// allow for some clock skew in the validity period using the
// issuer's apparent clock.
notBefore := TimeNow().Add(-1 * MaxClockSkew)
notOnOrAfterAfter := notBefore.Add(MaxClockSkew).Add(MaxIssueDelay)
notBefore := req.Now.Add(-1 * MaxClockSkew)
notOnOrAfterAfter := req.Now.Add(MaxIssueDelay)
if notBefore.Before(req.Request.IssueInstant) {
notBefore = req.Request.IssueInstant
notOnOrAfterAfter = notBefore.Add(MaxIssueDelay)
Expand All @@ -619,7 +621,7 @@ func (DefaultAssertionMaker) MakeAssertion(req *IdpAuthnRequest, session *Sessio
SubjectConfirmationData: &SubjectConfirmationData{
Address: req.HTTPRequest.RemoteAddr,
InResponseTo: req.Request.ID,
NotOnOrAfter: TimeNow().Add(MaxIssueDelay),
NotOnOrAfter: req.Now.Add(MaxIssueDelay),
Recipient: req.ACSEndpoint.Location,
},
},
Expand Down Expand Up @@ -842,7 +844,7 @@ func (req *IdpAuthnRequest) MakeResponse() error {
Destination: req.ACSEndpoint.Location,
ID: fmt.Sprintf("id-%x", randomBytes(20)),
InResponseTo: req.Request.ID,
IssueInstant: TimeNow(),
IssueInstant: req.Now,
Version: "2.0",
Issuer: &Issuer{
Format: "urn:oasis:names:tc:SAML:2.0:nameid-format:entity",
Expand Down
13 changes: 13 additions & 0 deletions identity_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ func (test *IdentityProviderTest) TestCanParse(c *C) {

func (test *IdentityProviderTest) TestCanValidate(c *C) {
req := IdpAuthnRequest{
Now: TimeNow(),
IDP: &test.IDP,
RequestBuffer: []byte("" +
"<AuthnRequest xmlns=\"urn:oasis:names:tc:SAML:2.0:protocol\" " +
Expand All @@ -383,12 +384,14 @@ func (test *IdentityProviderTest) TestCanValidate(c *C) {
c.Assert(req.ACSEndpoint, DeepEquals, &IndexedEndpoint{Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", Location: "https://sp.example.com/saml2/acs", Index: 1})

req = IdpAuthnRequest{
Now: TimeNow(),
IDP: &test.IDP,
RequestBuffer: []byte("<AuthnRequest"),
}
c.Assert(req.Validate(), ErrorMatches, "XML syntax error on line 1: unexpected EOF")

req = IdpAuthnRequest{
Now: TimeNow(),
IDP: &test.IDP,
RequestBuffer: []byte("" +
"<AuthnRequest xmlns=\"urn:oasis:names:tc:SAML:2.0:protocol\" " +
Expand All @@ -406,6 +409,7 @@ func (test *IdentityProviderTest) TestCanValidate(c *C) {
c.Assert(req.Validate(), ErrorMatches, "expected destination to be \"https://idp.example.com/saml/sso\", not \"https://idp.wrongDestination.com/saml/sso\"")

req = IdpAuthnRequest{
Now: TimeNow(),
IDP: &test.IDP,
RequestBuffer: []byte("" +
"<AuthnRequest xmlns=\"urn:oasis:names:tc:SAML:2.0:protocol\" " +
Expand All @@ -423,6 +427,7 @@ func (test *IdentityProviderTest) TestCanValidate(c *C) {
c.Assert(req.Validate(), ErrorMatches, "request expired at 2014\\-12\\-01 01:58:39 \\+0000 UTC")

req = IdpAuthnRequest{
Now: TimeNow(),
IDP: &test.IDP,
RequestBuffer: []byte("" +
"<AuthnRequest xmlns=\"urn:oasis:names:tc:SAML:2.0:protocol\" " +
Expand All @@ -440,6 +445,7 @@ func (test *IdentityProviderTest) TestCanValidate(c *C) {
c.Assert(req.Validate(), ErrorMatches, "expected SAML request version 2.0 got 4.2")

req = IdpAuthnRequest{
Now: TimeNow(),
IDP: &test.IDP,
RequestBuffer: []byte("" +
"<AuthnRequest xmlns=\"urn:oasis:names:tc:SAML:2.0:protocol\" " +
Expand All @@ -457,6 +463,7 @@ func (test *IdentityProviderTest) TestCanValidate(c *C) {
c.Assert(req.Validate(), ErrorMatches, "cannot handle request from unknown service provider https://unknownSP.example.com/saml2/metadata")

req = IdpAuthnRequest{
Now: TimeNow(),
IDP: &test.IDP,
RequestBuffer: []byte("" +
"<AuthnRequest xmlns=\"urn:oasis:names:tc:SAML:2.0:protocol\" " +
Expand All @@ -477,6 +484,7 @@ func (test *IdentityProviderTest) TestCanValidate(c *C) {

func (test *IdentityProviderTest) TestMakeAssertion(c *C) {
req := IdpAuthnRequest{
Now: TimeNow(),
IDP: &test.IDP,
RequestBuffer: []byte("" +
"<AuthnRequest xmlns=\"urn:oasis:names:tc:SAML:2.0:protocol\" " +
Expand Down Expand Up @@ -656,6 +664,7 @@ func (test *IdentityProviderTest) TestMakeAssertion(c *C) {

func (test *IdentityProviderTest) TestMarshalAssertion(c *C) {
req := IdpAuthnRequest{
Now: TimeNow(),
IDP: &test.IDP,
RequestBuffer: []byte("" +
"<AuthnRequest xmlns=\"urn:oasis:names:tc:SAML:2.0:protocol\" " +
Expand Down Expand Up @@ -703,6 +712,7 @@ func (test *IdentityProviderTest) TestMarshalAssertion(c *C) {

func (test *IdentityProviderTest) TestMakeResponse(c *C) {
req := IdpAuthnRequest{
Now: TimeNow(),
IDP: &test.IDP,
RequestBuffer: []byte("" +
"<AuthnRequest xmlns=\"urn:oasis:names:tc:SAML:2.0:protocol\" " +
Expand Down Expand Up @@ -772,6 +782,7 @@ func (test *IdentityProviderTest) TestMakeResponse(c *C) {

func (test *IdentityProviderTest) TestWriteResponse(c *C) {
req := IdpAuthnRequest{
Now: TimeNow(),
IDP: &test.IDP,
RelayState: "THIS_IS_THE_RELAY_STATE",
RequestBuffer: []byte("" +
Expand Down Expand Up @@ -868,6 +879,7 @@ func (test *IdentityProviderTest) TestCanHandleUnencryptedResponse(c *C) {
}

req := IdpAuthnRequest{
Now: TimeNow(),
IDP: &test.IDP,
RequestBuffer: []byte("" +
"<AuthnRequest xmlns=\"urn:oasis:names:tc:SAML:2.0:protocol\" " +
Expand Down Expand Up @@ -1132,6 +1144,7 @@ func (test *IdentityProviderTest) TestNoDestination(c *C) {
}

req := IdpAuthnRequest{
Now: TimeNow(),
IDP: &test.IDP,
RequestBuffer: []byte("" +
"<AuthnRequest xmlns=\"urn:oasis:names:tc:SAML:2.0:protocol\" " +
Expand Down

0 comments on commit 16d16c2

Please sign in to comment.