Skip to content

Commit

Permalink
Merge pull request #31 from bpedro/feature/validate-idp-certificate-o…
Browse files Browse the repository at this point in the history
…n-callback

Validate an idp fingerprint using a configurable lambda
  • Loading branch information
bpedro committed Jan 7, 2015
2 parents ce20514 + 0fff08d commit f6c14e2
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 5 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ A generic SAML strategy for OmniAuth.

https://github.com/PracticallyGreen/omniauth-saml

## 1.3.0 (2014-14-10)

* add `idp_cert_fingerprint_validator` option

## 1.2.0 (2014-03-19)

* provide SP metadata at `/auth/saml/metadata`
Expand Down
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use OmniAuth::Strategies::SAML,
:idp_sso_target_url_runtime_params => {:original_request_param => :mapped_idp_param},
:idp_cert => "-----BEGIN CERTIFICATE-----\n...-----END CERTIFICATE-----",
:idp_cert_fingerprint => "E7:91:B2:E1:...",
:idp_cert_fingerprint_validator => lambda { |fingerprint| fingerprint },
:name_identifier_format => "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
```

Expand All @@ -44,6 +45,7 @@ Rails.application.config.middleware.use OmniAuth::Builder do
:idp_sso_target_url_runtime_params => {:original_request_param => :mapped_idp_param},
:idp_cert => "-----BEGIN CERTIFICATE-----\n...-----END CERTIFICATE-----",
:idp_cert_fingerprint => "E7:91:B2:E1:...",
:idp_cert_fingerprint_validator => lambda { |fingerprint| fingerprint },
:name_identifier_format => "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
end
```
Expand Down Expand Up @@ -73,12 +75,16 @@ The service provider metadata used to ease configuration of the SAML SP in the I
`original_param_value`. Optional.

* `:idp_cert` - The identity provider's certificate in PEM format. Takes precedence
over the fingerprint option below. This option or `:idp_cert_fingerprint` must
over the fingerprint option below. This option or `:idp_cert_fingerprint` or `:idp_cert_fingerprint_validator` must
be present.

* `:idp_cert_fingerprint` - The SHA1 fingerprint of the certificate, e.g.
"90:CC:16:F0:8D:...". This is provided from the identity provider when setting up
the relationship. This option or `:idp_cert` must be present.
the relationship. This option or `:idp_cert` or `:idp_cert_fingerprint_validator` MUST be present.

* `:idp_cert_fingerprint_validator` - A lambda that MUST accept one parameter
(the fingerprint), verify if it is valid and return it if successful. This option
or `:idp_cert` or `:idp_cert_fingerprint` MUST be present.

* `:name_identifier_format` - Used during SP-initiated SSO. Describes the format of
the username required by this application. If you need the email address, use
Expand All @@ -92,7 +98,7 @@ The service provider metadata used to ease configuration of the SAML SP in the I

## Authors

Authored by [Rajiv Aaron Manglani](http://www.rajivmanglani.com/), Raecoo Cao, Todd W Saxton, Ryan Wilcox, Steven Anderson, Nikos Dimitrakopoulos, and Rudolf Vriend.
Authored by [Rajiv Aaron Manglani](http://www.rajivmanglani.com/), Raecoo Cao, Todd W Saxton, Ryan Wilcox, Steven Anderson, Nikos Dimitrakopoulos, Rudolf Vriend and [Bruno Pedro](http://brunopedro.com/).

## License

Expand Down
2 changes: 1 addition & 1 deletion lib/omniauth-saml/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module OmniAuth
module SAML
VERSION = '1.2.0'
VERSION = '1.3.0'
end
end
22 changes: 22 additions & 0 deletions lib/omniauth/strategies/saml.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ def callback_phase
raise OmniAuth::Strategies::SAML::ValidationError.new("SAML response missing")
end

# Call a fingerprint validation method if there's one
if options.idp_cert_fingerprint_validator
fingerprint_exists = options.idp_cert_fingerprint_validator[response_fingerprint]
unless fingerprint_exists
raise OmniAuth::Strategies::SAML::ValidationError.new("Non-existent fingerprint")
end
# id_cert_fingerprint becomes the given fingerprint if it exists
options.idp_cert_fingerprint = fingerprint_exists
end

response = Onelogin::Saml::Response.new(request.params['SAMLResponse'], options)
response.settings = Onelogin::Saml::Settings.new(options)

Expand All @@ -48,6 +58,18 @@ def callback_phase
fail!(:invalid_ticket, $!)
end

# Obtain an idp certificate fingerprint from the response.
def response_fingerprint
response = request.params['SAMLResponse']
response = (response =~ /^</) ? response : Base64.decode64(response)
document = XMLSecurity::SignedDocument::new(response)
cert_element = REXML::XPath.first(document, "//ds:X509Certificate", { "ds"=> 'http://www.w3.org/2000/09/xmldsig#' })
base64_cert = cert_element.text
cert_text = Base64.decode64(base64_cert)
cert = OpenSSL::X509::Certificate.new(cert_text)
Digest::SHA1.hexdigest(cert.to_der).upcase.scan(/../).join(':')
end

def other_phase
if on_path?("#{request_path}/metadata")
# omniauth does not set the strategy on the other_phase
Expand Down
2 changes: 1 addition & 1 deletion omniauth-saml.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Gem::Specification.new do |gem|
gem.email = '[email protected]'
gem.homepage = 'https://github.com/PracticallyGreen/omniauth-saml'

gem.add_runtime_dependency 'omniauth', '~> 1.2'
gem.add_runtime_dependency 'omniauth', '~> 1.1'
gem.add_runtime_dependency 'ruby-saml', '~> 0.7.3'

gem.add_development_dependency 'rspec', '~> 2.8'
Expand Down
21 changes: 21 additions & 0 deletions spec/omniauth/strategies/saml_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,27 @@ def post_xml(xml=:example_response)
end
end

context "when fingerprint is empty and there's a fingerprint validator" do
before :each do
saml_options.delete(:idp_cert_fingerprint)
saml_options[:idp_cert_fingerprint_validator] = lambda { |fingerprint| "C1:59:74:2B:E8:0C:6C:A9:41:0F:6E:83:F6:D1:52:25:45:58:89:FB" }
post_xml
end

it "should set the uid to the nameID in the SAML response" do
auth_hash['uid'].should == '_1f6fcf6be5e13b08b1e3610e7ff59f205fbd814f23'
end

it "should set the raw info to all attributes" do
auth_hash['extra']['raw_info'].to_hash.should == {
'first_name' => 'Rajiv',
'last_name' => 'Manglani',
'email' => '[email protected]',
'company_name' => 'Example Company'
}
end
end

context "when there is no SAMLResponse parameter" do
before :each do
post '/auth/saml/callback'
Expand Down

0 comments on commit f6c14e2

Please sign in to comment.