Skip to content

Commit

Permalink
Support custom idp entity id
Browse files Browse the repository at this point in the history
  • Loading branch information
sword-jin committed Nov 21, 2023
1 parent a32b643 commit a97ac98
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 8 deletions.
21 changes: 19 additions & 2 deletions identity_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,11 @@ type IdentityProvider struct {
AssertionMaker AssertionMaker
SignatureMethod string
ValidDuration *time.Duration
EntityIDConstructor EntityIDConstructor
}

type EntityIDConstructor func() string

// Metadata returns the metadata structure for this identity provider.
func (idp *IdentityProvider) Metadata() *EntityDescriptor {
certStr := base64.StdEncoding.EncodeToString(idp.Certificate.Raw)
Expand All @@ -121,7 +124,7 @@ func (idp *IdentityProvider) Metadata() *EntityDescriptor {
}

ed := &EntityDescriptor{
EntityID: idp.MetadataURL.String(),
EntityID: idp.getEntityID(),
ValidUntil: TimeNow().Add(validDuration),
CacheDuration: validDuration,
IDPSSODescriptors: []IDPSSODescriptor{
Expand Down Expand Up @@ -334,6 +337,20 @@ func (idp *IdentityProvider) ServeIDPInitiated(w http.ResponseWriter, r *http.Re
}
}

// createDefaultEntityIDConstructor creates a function to return entityID from metadataURL.
func createDefaultEntityIDConstructor(metadataURL url.URL) func() string {
return func() string {
return metadataURL.String()
}
}

func (idp *IdentityProvider) getEntityID() string {
if idp.EntityIDConstructor == nil {
return createDefaultEntityIDConstructor(idp.MetadataURL)()
}
return idp.EntityIDConstructor()
}

// IdpAuthnRequest is used by IdentityProvider to handle a single authentication request.
type IdpAuthnRequest struct {
IDP *IdentityProvider
Expand Down Expand Up @@ -1019,7 +1036,7 @@ func (req *IdpAuthnRequest) MakeResponse() error {
Version: "2.0",
Issuer: &Issuer{
Format: "urn:oasis:names:tc:SAML:2.0:nameid-format:entity",
Value: req.IDP.MetadataURL.String(),
Value: req.IDP.getEntityID(),
},
Status: Status{
StatusCode: StatusCode{
Expand Down
47 changes: 41 additions & 6 deletions identity_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ type IdentityProviderTest struct {
SPCertificate *x509.Certificate
SP ServiceProvider

Key crypto.PrivateKey
Signer crypto.Signer
Certificate *x509.Certificate
SessionProvider SessionProvider
IDP IdentityProvider
Key crypto.PrivateKey
Signer crypto.Signer
Certificate *x509.Certificate
SessionProvider SessionProvider
IDP IdentityProvider
ExpectedFilename string
}

func mustParseURL(s string) url.URL {
Expand Down Expand Up @@ -98,6 +99,24 @@ var applySigner = idpTestOpts{
},
}

// applyEntityIDConstructor will set the entity ID constructor for the identity provider.
func applyEntityIDConstructor(c EntityIDConstructor) idpTestOpts {
return idpTestOpts{
apply: func(_ *testing.T, test *IdentityProviderTest) {
test.IDP.EntityIDConstructor = c
},
}
}

// applyExpectedFilename will set the expected filename for the identity provider.
func applyExpectedFilename(filename string) idpTestOpts {
return idpTestOpts{
apply: func(_ *testing.T, test *IdentityProviderTest) {
test.ExpectedFilename = filename
},
}
}

func NewIdentityProviderTest(t *testing.T, opts ...idpTestOpts) *IdentityProviderTest {
test := IdentityProviderTest{}
TimeNow = func() time.Time {
Expand Down Expand Up @@ -139,6 +158,7 @@ func NewIdentityProviderTest(t *testing.T, opts ...idpTestOpts) *IdentityProvide
},
},
}
test.ExpectedFilename = "TestIDPMakeResponse_response.xml"

// apply the test options
for _, opt := range opts {
Expand Down Expand Up @@ -772,7 +792,7 @@ func testMakeResponse(t *testing.T, test *IdentityProviderTest) {
doc.Indent(2)
responseStr, err := doc.WriteToString()
assert.Check(t, err)
golden.Assert(t, responseStr, "TestIDPMakeResponse_response.xml")
golden.Assert(t, responseStr, test.ExpectedFilename)
}

func TestIDPWriteResponse(t *testing.T) {
Expand Down Expand Up @@ -1130,3 +1150,18 @@ func TestIDPHTTPCanHandleSSORequest(t *testing.T) {
assert.Check(t, is.Equal(http.StatusBadRequest, w.Code))
}
}

func TestIdentityProviderCustomEntityID(t *testing.T) {
customEntityID := "https://idp.example.com/entity-id"
test := NewIdentityProviderTest(
t,
applyKey,
applyEntityIDConstructor(func() string {
return customEntityID
}),
applyExpectedFilename("TestIDPMakeResponse_response_with_custom_entity_id.xml"),
)

assert.Equal(t, customEntityID, test.IDP.Metadata().EntityID)
testMakeResponse(t, test)
}
27 changes: 27 additions & 0 deletions testdata/TestIDPMakeResponse_response_with_custom_entity_id.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<samlp:Response xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="id-282a2c2e30323436383a3c3e40424446484a4c4e" InResponseTo="id-00020406080a0c0e10121416181a1c1e" Version="2.0" IssueInstant="2015-12-01T01:57:09Z" Destination="https://sp.example.com/saml2/acs">
<saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://idp.example.com/entity-id</saml: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="#id-282a2c2e30323436383a3c3e40424446484a4c4e">
<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>5bBiRThV9gjcTNlKa+y00Gnzkh8=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>A9fzgSO00HntRcx32qCEVHoTR8YiisGk6tkeAbhRKzXoIOw3UE4nhoBIYPTYj5G+mMjnB/eEw84kuUSZ9mLV+EIAMQuR6ctJyO6xdxy65l+iC0IBSk65wqCb6C4IRB5OaxN/QC0yTJ8Ps2+s1WRJSLLcmQU6Xatpe25vzk+hQ+4=</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>MIIB7zCCAVgCCQDFzbKIp7b3MTANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCR0ExDDAKBgNVBAoMA2ZvbzESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTEzMTAwMjAwMDg1MVoXDTE0MTAwMjAwMDg1MVowPDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkdBMQwwCgYDVQQKDANmb28xEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1PMHYmhZj308kWLhZVT4vOulqx/9ibm5B86fPWwUKKQ2i12MYtz07tzukPymisTDhQaqyJ8Kqb/6JjhmeMnEOdTvSPmHO8m1ZVveJU6NoKRn/mP/BD7FW52WhbrUXLSeHVSKfWkNk6S4hk9MV9TswTvyRIKvRsw0X/gfnqkroJcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQCMMlIO+GNcGekevKgkakpMdAqJfs24maGb90DvTLbRZRD7Xvn1MnVBBS9hzlXiFLYOInXACMW5gcoRFfeTQLSouMM8o57h0uKjfTmuoWHLQLi6hnF+cvCsEFiJZ4AbF+DgmO6TarJ8O05t8zvnOwJlNCASPZRH/JmF8tX0hoHuAQ==</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<this-is-an-encrypted-assertion/>
</samlp:Response>

0 comments on commit a97ac98

Please sign in to comment.