Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
D
dex
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
go
dex
Commits
42d07280
Commit
42d07280
authored
Feb 01, 2017
by
rithu leena john
Committed by
GitHub
Feb 01, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #785 from holgerkoser/master
Improve SAML Signature and Response Validation
parents
27224cdc
27a1e9f1
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
615 additions
and
30 deletions
+615
-30
saml.go
connector/saml/saml.go
+160
-21
saml_test.go
connector/saml/saml_test.go
+234
-5
idp-cert.pem
connector/saml/testdata/idp-cert.pem
+26
-0
idp-resp-signed-assertion.xml
connector/saml/testdata/idp-resp-signed-assertion.xml
+29
-0
idp-resp-signed-message-and-assertion.xml
...r/saml/testdata/idp-resp-signed-message-and-assertion.xml
+34
-0
idp-resp-signed-message.xml
connector/saml/testdata/idp-resp-signed-message.xml
+30
-0
idp-resp.xml
connector/saml/testdata/idp-resp.xml
+34
-0
types.go
connector/saml/types.go
+61
-0
glide.lock
glide.lock
+3
-3
glide.yaml
glide.yaml
+1
-1
canonicalize.go
vendor/github.com/russellhaering/goxmldsig/canonicalize.go
+3
-0
No files found.
connector/saml/saml.go
View file @
42d07280
...
@@ -2,8 +2,6 @@
...
@@ -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 (
...
@@ -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
...
@@ -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
...
@@ -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
...
@@ -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
...
@@ -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
...
@@ -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
()
}
}
...
...
connector/saml/saml_test.go
View file @
42d07280
...
@@ -2,12 +2,24 @@ package saml
...
@@ -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) {
...
@@ -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
:=
sd
ig
.
NewDefaultValidationContext
(
s
)
validator
:=
ds
ig
.
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
)
}
}
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
)
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"
)
}
}
}
connector/saml/testdata/idp-cert.pem
0 → 100644
View file @
42d07280
-----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-----
connector/saml/testdata/idp-resp-signed-assertion.xml
0 → 100644
View file @
42d07280
<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>
connector/saml/testdata/idp-resp-signed-message-and-assertion.xml
0 → 100644
View file @
42d07280
<?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>
connector/saml/testdata/idp-resp-signed-message.xml
0 → 100644
View file @
42d07280
<?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>
connector/saml/testdata/idp-resp.xml
0 → 100644
View file @
42d07280
<?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>
connector/saml/types.go
View file @
42d07280
...
@@ -57,6 +57,8 @@ type authnRequest struct {
...
@@ -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 {
...
@@ -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 {
...
@@ -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 {
...
@@ -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 {
...
@@ -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"`
}
}
...
...
glide.lock
View file @
42d07280
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:
...
@@ -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
...
...
glide.yaml
View file @
42d07280
...
@@ -134,7 +134,7 @@ import:
...
@@ -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
...
...
vendor/github.com/russellhaering/goxmldsig/canonicalize.go
View file @
42d07280
...
@@ -84,6 +84,9 @@ func (a attrsByKey) Less(i, j int) bool {
...
@@ -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"
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment