Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remote idp metadata #136

Merged
merged 8 commits into from
Oct 30, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def saml_settings

settings.assertion_consumer_service_url = "http://#{request.host}/saml/finalize"
settings.issuer = request.host
settings.idp_sso_target_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}"
settings.idp_sso_target_url = "https://app.onelogin.com/trust/saml2/http-post/sso/#{OneLoginAppId}"
settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
# Optional for most SAML IdPs
Expand Down Expand Up @@ -91,6 +91,33 @@ class SamlController < ApplicationController
end
end
```
## Metadata Based Configuration

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
key certificates which add to the trusted relationship. The IdP administrator can also configure custom settings for an SP based on the metadata.

Using ```idp_metadata_parser.parse_remote``` IdP metadata will be added to the settings withouth further ado.

```ruby
def saml_settings

idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
# Returns OneLogin::RubySaml::Settings prepopulated with idp metadata
settings = idp_metadata_parser.parse_remote("https://example.com/auth/saml2/idp/metadata")

settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
settings.issuer = request.host
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
# Optional for most SAML IdPs
settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"

settings
end
```
The following attributes are set:
* id_sso_target_url
* idp_slo_target_url
* id_cert_fingerpint

If are using saml:AttributeStatement to transfare metadata, like the user name, you can access all the attributes through response.attributes. It
contains all the saml:AttributeStatement with its 'Name' as a indifferent key and the one saml:AttributeValue as value.
Expand Down
28 changes: 28 additions & 0 deletions lib/onelogin/ruby-saml/idp_metadata_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ class IdpMetadataParser

attr_reader :document

def parse_remote(url, validate_cert = true)
idp_metadata = get_idp_metadata(url, validate_cert)
parse(idp_metadata)
end

def parse(idp_metadata)
@document = REXML::Document.new(idp_metadata)

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

private

# Retrieve the remote IdP metadata from the URL or a cached copy
# # returns a REXML document of the metadata
def get_idp_metadata(url, validate_cert)
uri = URI.parse(url)
if uri.scheme == "http"
response = Net::HTTP.get_response(uri)
meta_text = response.body
elsif uri.scheme == "https"
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
# Most IdPs will probably use self signed certs
if validate_cert
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
else
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
get = Net::HTTP::Get.new(uri.request_uri)
response = http.request(get)
meta_text = response.body
end
meta_text
end

def single_signon_service_url
node = REXML::XPath.first(document, "/md:EntityDescriptor/md:IDPSSODescriptor/md:SingleSignOnService/@Location", { "md" => METADATA })
node.value if node
Expand Down
37 changes: 37 additions & 0 deletions test/idp_metadata_parser_test.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
require 'net/http'
require 'net/https'

class IdpMetadataParserTest < Test::Unit::TestCase

class MockResponse
attr_accessor :body
end

context "parsing an IdP descriptor file" do
should "extract settings details from xml" do
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
Expand All @@ -14,4 +20,35 @@ class IdpMetadataParserTest < Test::Unit::TestCase
end
end

context "download and parse IdP descriptor file" do
setup do
mock_response = MockResponse.new
mock_response.body = idp_metadata
@url = "https://example.com"
uri = URI(@url)

@http = Net::HTTP.new(uri.host, uri.port)
Net::HTTP.expects(:new).returns(@http)
@http.expects(:request).returns(mock_response)
end


should "extract settings from remote xml" do
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
settings = idp_metadata_parser.parse_remote(@url)

assert_equal "https://example.hello.com/access/saml/login", settings.idp_sso_target_url
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
assert_equal "https://example.hello.com/access/saml/logout", settings.idp_slo_target_url
assert_equal OpenSSL::SSL::VERIFY_PEER, @http.verify_mode
end

should "accept self signed certificate if insturcted" do
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
settings = idp_metadata_parser.parse_remote(@url, false)

assert_equal OpenSSL::SSL::VERIFY_NONE, @http.verify_mode
end
end

end