Skip to content

Commit 8fa4c1d

Browse files
committed
Merge pull request #136 from bonyiii/remote_idp_metadata
Remote idp metadata
2 parents a6fa4b5 + e250684 commit 8fa4c1d

3 files changed

Lines changed: 93 additions & 1 deletion

File tree

README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def saml_settings
7373

7474
settings.assertion_consumer_service_url = "http://#{request.host}/saml/finalize"
7575
settings.issuer = request.host
76-
settings.idp_sso_target_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}"
76+
settings.idp_sso_target_url = "https://app.onelogin.com/trust/saml2/http-post/sso/#{OneLoginAppId}"
7777
settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
7878
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
7979

@@ -126,6 +126,33 @@ class SamlController < ApplicationController
126126
end
127127
end
128128
```
129+
## Metadata Based Configuration
130+
131+
The method above requires a little extra work to manually specify attributes about the IdP. (And your SP application) There's an easier method -- use a metadata exchange. Metadata is just an XML file that defines the capabilities of both the IdP and the SP application. It also contains the X.509 public
132+
key certificates which add to the trusted relationship. The IdP administrator can also configure custom settings for an SP based on the metadata.
133+
134+
Using ```idp_metadata_parser.parse_remote``` IdP metadata will be added to the settings withouth further ado.
135+
136+
```ruby
137+
def saml_settings
138+
139+
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
140+
# Returns OneLogin::RubySaml::Settings prepopulated with idp metadata
141+
settings = idp_metadata_parser.parse_remote("https://example.com/auth/saml2/idp/metadata")
142+
143+
settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
144+
settings.issuer = request.host
145+
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
146+
# Optional for most SAML IdPs
147+
settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
148+
149+
settings
150+
end
151+
```
152+
The following attributes are set:
153+
* id_sso_target_url
154+
* idp_slo_target_url
155+
* id_cert_fingerpint
129156

130157
If are using saml:AttributeStatement to transfer metadata, like the user name, you can access all the attributes through response.attributes. It
131158
contains all the saml:AttributeStatement with its 'Name' as a indifferent key and the one saml:AttributeValue as value.

lib/onelogin/ruby-saml/idp_metadata_parser.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ class IdpMetadataParser
1616

1717
attr_reader :document
1818

19+
def parse_remote(url, validate_cert = true)
20+
idp_metadata = get_idp_metadata(url, validate_cert)
21+
parse(idp_metadata)
22+
end
23+
1924
def parse(idp_metadata)
2025
@document = REXML::Document.new(idp_metadata)
2126

@@ -29,6 +34,29 @@ def parse(idp_metadata)
2934

3035
private
3136

37+
# Retrieve the remote IdP metadata from the URL or a cached copy
38+
# # returns a REXML document of the metadata
39+
def get_idp_metadata(url, validate_cert)
40+
uri = URI.parse(url)
41+
if uri.scheme == "http"
42+
response = Net::HTTP.get_response(uri)
43+
meta_text = response.body
44+
elsif uri.scheme == "https"
45+
http = Net::HTTP.new(uri.host, uri.port)
46+
http.use_ssl = true
47+
# Most IdPs will probably use self signed certs
48+
if validate_cert
49+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
50+
else
51+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
52+
end
53+
get = Net::HTTP::Get.new(uri.request_uri)
54+
response = http.request(get)
55+
meta_text = response.body
56+
end
57+
meta_text
58+
end
59+
3260
def single_signon_service_url
3361
node = REXML::XPath.first(document, "/md:EntityDescriptor/md:IDPSSODescriptor/md:SingleSignOnService/@Location", { "md" => METADATA })
3462
node.value if node

test/idp_metadata_parser_test.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2+
require 'net/http'
3+
require 'net/https'
24

35
class IdpMetadataParserTest < Test::Unit::TestCase
46

7+
class MockResponse
8+
attr_accessor :body
9+
end
10+
511
context "parsing an IdP descriptor file" do
612
should "extract settings details from xml" do
713
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
@@ -14,4 +20,35 @@ class IdpMetadataParserTest < Test::Unit::TestCase
1420
end
1521
end
1622

23+
context "download and parse IdP descriptor file" do
24+
setup do
25+
mock_response = MockResponse.new
26+
mock_response.body = idp_metadata
27+
@url = "https://example.com"
28+
uri = URI(@url)
29+
30+
@http = Net::HTTP.new(uri.host, uri.port)
31+
Net::HTTP.expects(:new).returns(@http)
32+
@http.expects(:request).returns(mock_response)
33+
end
34+
35+
36+
should "extract settings from remote xml" do
37+
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
38+
settings = idp_metadata_parser.parse_remote(@url)
39+
40+
assert_equal "https://example.hello.com/access/saml/login", settings.idp_sso_target_url
41+
assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", settings.idp_cert_fingerprint
42+
assert_equal "https://example.hello.com/access/saml/logout", settings.idp_slo_target_url
43+
assert_equal OpenSSL::SSL::VERIFY_PEER, @http.verify_mode
44+
end
45+
46+
should "accept self signed certificate if insturcted" do
47+
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
48+
settings = idp_metadata_parser.parse_remote(@url, false)
49+
50+
assert_equal OpenSSL::SSL::VERIFY_NONE, @http.verify_mode
51+
end
52+
end
53+
1754
end

0 commit comments

Comments
 (0)