ruby-saml 0.0.8 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of ruby-saml might be problematic. Click here for more details.

@@ -1,7 +1,84 @@
1
- = ruby-saml
1
+ = Ruby SAML
2
2
 
3
- * To build the gem run rake build
4
- * To install the gem run sudo gem install pkg/ruby-saml-x.x.x.gem
3
+ The Ruby SAML library is for implementing the client side of a SAML authorization, i.e. it provides a means for managing authorization initialization and confirmation requests from identity providers.
4
+
5
+ SAML authorization is a two step process and you are expected to implement support for both.
6
+
7
+ == The initialization phase
8
+
9
+ This is the first request you will get from the identity provider. It will hit your application at a specific URL (that you've announced as being your SAML initialization point). The response to this initialization, is a redirect back to the identity provider, which can look something like this (ignore the saml_settings method call for now):
10
+
11
+ def initialize
12
+ request = Onelogin::Saml::Authrequest.new
13
+ redirect_to(request.create(saml_settings))
14
+ end
15
+
16
+ Once you've redirected back to the identity provider, it will ensure that the user has been authorized and redirect back to your application for final consumption, this is can look something like this (the authorize_success and authorize_failure methods are specific to your application):
17
+
18
+ def consume
19
+ response = Onelogin::Saml::Response.new(params[:SAMLResponse])
20
+ response.settings = saml_settings
21
+
22
+ if response.is_valid? && user = current_account.users.find_by_email(response.name_id)
23
+ authorize_success(user)
24
+ else
25
+ authorize_failure(user)
26
+ end
27
+ end
28
+
29
+ In the above there are a few assumptions in place, one being that the response.name_id is an email address. This is all handled with how you specify the settings that are in play via the saml_settings method. That could be implemented along the lines of this:
30
+
31
+ def saml_settings
32
+ settings = Onelogin::Saml::Settings.new
33
+
34
+ settings.assertion_consumer_service_url = "http://#{request.host}/saml/finalize"
35
+ settings.issuer = request.host
36
+ settings.idp_sso_target_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}"
37
+ settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
38
+ settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
39
+
40
+ settings
41
+ end
42
+
43
+ What's left at this point, is to wrap it all up in a controller and point the initialization and consumption URLs in OneLogin at that. A full controller example could look like this:
44
+
45
+ # This controller expects you to use the URLs /saml/initialize and /saml/consume in your OneLogin application.
46
+ class SamlController < ApplicationController
47
+ def initialize
48
+ request = Onelogin::Saml::Authrequest.new
49
+ redirect_to(request.create(saml_settings))
50
+ end
51
+
52
+ def consume
53
+ response = Onelogin::Saml::Response.new(params[:SAMLResponse])
54
+ response.settings = saml_settings
55
+
56
+ if response.is_valid? && user = current_account.users.find_by_email(response.name_id)
57
+ authorize_success(user)
58
+ else
59
+ authorize_failure(user)
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def saml_settings
66
+ settings = Onelogin::Saml::Settings.new
67
+
68
+ settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
69
+ settings.issuer = request.host
70
+ settings.idp_sso_target_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}"
71
+ settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
72
+ settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
73
+
74
+ settings
75
+ end
76
+ end
77
+
78
+
79
+ = Full Example
80
+
81
+ Please check https://github.com/onelogin/ruby-saml-example for a very basic sample Rails application using this gem.
5
82
 
6
83
  == Note on Patches/Pull Requests
7
84
 
data/Rakefile CHANGED
@@ -10,10 +10,13 @@ begin
10
10
  gem.email = "support@onelogin.com"
11
11
  gem.homepage = "http://github.com/onelogin/ruby-saml"
12
12
  gem.authors = ["OneLogin LLC"]
13
- gem.add_dependency("XMLCanonicalizer",">= 1.0.1")
14
- #gem.add_development_dependency "thoughtbot-shoulda"
13
+ gem.add_dependency("xmlcanonicalizer","= 0.1.0")
14
+ gem.add_dependency("uuid","= 2.3.1")
15
+ gem.add_development_dependency "shoulda"
16
+ gem.add_development_dependency "mocha"
15
17
  #gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
18
  end
19
+ Jeweler::GemcutterTasks.new
17
20
  rescue LoadError
18
21
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
22
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.4
1
+ 0.2.1
@@ -1,4 +1,4 @@
1
- require 'onelogin/saml'
2
- module Onelogin::Saml
3
-
4
- end
1
+ require 'onelogin/saml/authrequest'
2
+ require 'onelogin/saml/response'
3
+ require 'onelogin/saml/settings'
4
+
@@ -1,56 +1,28 @@
1
1
  require "base64"
2
2
  require "uuid"
3
+ require "zlib"
4
+ require "cgi"
3
5
 
4
6
  module Onelogin::Saml
5
-
6
- class Authrequest
7
- def create(settings, options = {})
8
- @id = Onelogin::Saml::Authrequest.generateUniqueID(42)
9
- @issue_instant = Onelogin::Saml::Authrequest.getTimestamp
10
- debugger
11
- request = generate_saml_request(settings, options)
12
-
13
-
14
- deflated_request = Zlib::Deflate.deflate(request, 9)[2..-5]
15
- base64_request = Base64.encode64(deflated_request)
16
- encoded_request = CGI.escape(base64_request)
17
-
18
- settings.idp_sso_target_url + "?SAMLRequest=" + encoded_request
19
- end
20
-
21
- private
22
-
23
- def self.generateUniqueID(length)
24
- UUID.new.generate
25
- end
26
-
27
- def generate_saml_request(settings, options = {})
28
- options[:style] ||= :default
29
- case options[:style]
30
- when :default
31
- standard_saml_request(settings)
32
- when :google
33
- google_saml_request(settings)
34
- end
35
- end
36
-
37
- def standard_saml_request(settings)
38
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
39
- <samlp:AuthnRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"#{@id}\" Version=\"2.0\" IssueInstant=\"#{@issue_instant}\" ProtocolBinding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" AssertionConsumerServiceURL=\"#{settings.assertion_consumer_service_url}\">" +
40
- "<saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">#{settings.issuer}</saml:Issuer>\n" +
41
- "<samlp:NameIDPolicy xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" Format=\"#{settings.name_identifier_format}\" AllowCreate=\"true\"></samlp:NameIDPolicy>\n" +
42
- "<samlp:RequestedAuthnContext xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" Comparison=\"exact\">" +
43
- "<saml:AuthnContextClassRef xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext>\n" +
44
- "</samlp:AuthnRequest>"
45
- end
46
-
47
- def google_saml_request(settings)
48
- %Q(<?xml version="1.0" encoding="UTF-8"?><samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="#{@id}" Version="2.0" IssueInstant="#{@issue_instant}" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" ProviderName="#{settings.issuer}" AssertionConsumerServiceURL="#{settings.assertion_consumer_service_url}"><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">#{settings.issuer}</saml:Issuer><samlp:NameIDPolicy AllowCreate="true" Format="#{settings.name_identifier_format}" /></samlp:AuthnRequest>)
49
- end
50
-
51
- def self.getTimestamp
52
- Time.new().strftime("%Y-%m-%dT%H:%M:%SZ")
53
- end
54
- end
55
-
7
+ class Authrequest
8
+ def create(settings)
9
+ uuid = UUID.new.generate
10
+ time = Time.now.strftime("%Y-%m-%dT%H:%M:%SZ")
11
+
12
+ request =
13
+ "<samlp:AuthnRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"#{uuid}\" Version=\"2.0\" IssueInstant=\"#{time}\" ProtocolBinding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" AssertionConsumerServiceURL=\"#{settings.assertion_consumer_service_url}\">" +
14
+ "<saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">#{settings.issuer}</saml:Issuer>\n" +
15
+ "<samlp:NameIDPolicy xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" Format=\"#{settings.name_identifier_format}\" AllowCreate=\"true\"></samlp:NameIDPolicy>\n" +
16
+ "<samlp:RequestedAuthnContext xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" Comparison=\"exact\">" +
17
+ "<saml:AuthnContextClassRef xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></samlp:RequestedAuthnContext>\n" +
18
+ "</samlp:AuthnRequest>"
19
+
20
+ deflated_request = Zlib::Deflate.deflate(request, 9)[2..-5]
21
+ base64_request = Base64.encode64(deflated_request)
22
+ encoded_request = CGI.escape(base64_request)
23
+
24
+ settings.idp_sso_target_url + "?SAMLRequest=" + encoded_request
25
+ end
26
+
27
+ end
56
28
  end
@@ -1,33 +1,33 @@
1
1
  require "rexml/document"
2
2
  require "xml_sec"
3
+ require "time"
3
4
 
4
5
  module Onelogin::Saml
5
6
  class Response
7
+ attr_accessor :response, :document, :logger, :settings
8
+
6
9
  def initialize(response)
7
-
8
- @response = response
9
- @document = XMLSecurity::SignedDocument.new(Base64.decode64(@response))
10
- @document = XMLSecurity::SignedDocument.new(@response) if @document.root.blank?
11
- @document = REXML::Document.new(@response) if @document.root.blank?
12
- end
13
-
14
- def logger=(val)
15
- @logger = val
10
+ raise ArgumentError.new("Response cannot be nil") if response.nil?
11
+ self.response = response
12
+ self.document = XMLSecurity::SignedDocument.new(Base64.decode64(response))
16
13
  end
17
-
18
- def settings=(_settings)
19
- @settings = _settings
20
- end
21
-
14
+
22
15
  def is_valid?
23
- unless @response.blank?
24
- @document.validate(@settings.idp_cert_fingerprint, @logger) unless !@settings.idp_cert_fingerprint
25
- end
16
+ return false if response.empty?
17
+ return false if settings.nil?
18
+ return false if settings.idp_cert_fingerprint.nil?
19
+
20
+ document.validate(settings.idp_cert_fingerprint, logger)
26
21
  end
27
22
 
23
+ # The value of the user identifier as designated by the initialization request response
28
24
  def name_id
29
- node = @document.elements["/samlp:Response/saml:Assertion/saml:Subject/saml:NameID"] || @document.elements["/samlp:Response/Assertion/Subject/NameID"]
30
- node.text.strip
25
+ @name_id ||= document.elements["/samlp:Response/saml:Assertion/saml:Subject/saml:NameID"].text
26
+ end
27
+
28
+ # When this user session should expire at latest
29
+ def session_expires_at
30
+ @expires_at ||= Time.parse(document.elements["/samlp:Response/saml:Assertion/saml:AuthnStatement"].attributes["SessionNotOnOrAfter"])
31
31
  end
32
32
  end
33
33
  end
@@ -1,45 +1,6 @@
1
1
  module Onelogin::Saml
2
2
  class Settings
3
- def assertion_consumer_service_url
4
- @assertion_consumer_service_url
5
- end
6
- def assertion_consumer_service_url=(val)
7
- @assertion_consumer_service_url = val
8
- end
9
-
10
- def issuer
11
- @issuer
12
- end
13
- def issuer=(val)
14
- @issuer = val
15
- end
16
-
17
- def sp_name_qualifier
18
- @sp_name_qualifier
19
- end
20
- def sp_name_qualifier=(val)
21
- @sp_name_qualifier = val
22
- end
23
-
24
- def idp_sso_target_url
25
- @idp_sso_target_url
26
- end
27
- def idp_sso_target_url=(val)
28
- @idp_sso_target_url = val
29
- end
30
-
31
- def idp_cert_fingerprint
32
- @idp_cert_fingerprint
33
- end
34
- def idp_cert_fingerprint=(val)
35
- @idp_cert_fingerprint = val
36
- end
37
-
38
- def name_identifier_format
39
- @name_identifier_format
40
- end
41
- def name_identifier_format=(val)
42
- @name_identifier_format = val
43
- end
3
+ attr_accessor :assertion_consumer_service_url, :issuer, :sp_name_qualifier
4
+ attr_accessor :idp_sso_target_url, :idp_cert_fingerprint, :name_identifier_format
44
5
  end
45
6
  end
@@ -35,7 +35,6 @@ module XMLSecurity
35
35
 
36
36
  def validate (idp_cert_fingerprint, logger = nil)
37
37
  # get cert from response
38
- return true if self.elements["//ds:X509Certificate"].blank?
39
38
  base64_cert = self.elements["//ds:X509Certificate"].text
40
39
  cert_text = Base64.decode64(base64_cert)
41
40
  cert = OpenSSL::X509::Certificate.new(cert_text)
@@ -1,60 +1,67 @@
1
1
  # Generated by jeweler
2
- # DO NOT EDIT THIS FILE
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{ruby-saml}
8
- s.version = "0.0.8"
8
+ s.version = "0.2.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["OneLogin LLC"]
12
- s.date = %q{2010-08-10}
12
+ s.date = %q{2010-12-01}
13
13
  s.description = %q{SAML toolkit for Ruby on Rails}
14
14
  s.email = %q{support@onelogin.com}
15
15
  s.extra_rdoc_files = [
16
16
  "LICENSE",
17
- "README.rdoc"
17
+ "README.rdoc"
18
18
  ]
19
19
  s.files = [
20
20
  ".document",
21
- ".gitignore",
22
- "LICENSE",
23
- "README.rdoc",
24
- "Rakefile",
25
- "VERSION",
26
- "lib/onelogin/saml.rb",
27
- "lib/onelogin/saml/authrequest.rb",
28
- "lib/onelogin/saml/response.rb",
29
- "lib/onelogin/saml/settings.rb",
30
- "lib/ruby-saml.rb",
31
- "lib/xml_sec.rb",
32
- "ruby-saml.gemspec",
33
- "test/ruby-saml_test.rb",
34
- "test/test_helper.rb"
21
+ "LICENSE",
22
+ "README.rdoc",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "lib/onelogin/saml.rb",
26
+ "lib/onelogin/saml/authrequest.rb",
27
+ "lib/onelogin/saml/response.rb",
28
+ "lib/onelogin/saml/settings.rb",
29
+ "lib/ruby-saml.rb",
30
+ "lib/xml_sec.rb",
31
+ "ruby-saml.gemspec",
32
+ "test/response.txt",
33
+ "test/ruby-saml_test.rb",
34
+ "test/test_helper.rb"
35
35
  ]
36
36
  s.homepage = %q{http://github.com/onelogin/ruby-saml}
37
- s.rdoc_options = ["--charset=UTF-8"]
38
37
  s.require_paths = ["lib"]
39
- s.rubygems_version = %q{1.3.6}
38
+ s.rubygems_version = %q{1.3.7}
40
39
  s.summary = %q{SAML Ruby Tookit}
41
40
  s.test_files = [
42
41
  "test/ruby-saml_test.rb",
43
- "test/test_helper.rb"
42
+ "test/test_helper.rb"
44
43
  ]
45
44
 
46
45
  if s.respond_to? :specification_version then
47
46
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
48
47
  s.specification_version = 3
49
48
 
50
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
51
- s.add_runtime_dependency(%q<XMLCanonicalizer>, [">= 1.0.1"])
52
- s.add_runtime_dependency(%q<uuid>, [">= 2.0.0"])
49
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
50
+ s.add_runtime_dependency(%q<xmlcanonicalizer>, ["= 0.1.0"])
51
+ s.add_runtime_dependency(%q<uuid>, ["= 2.3.1"])
52
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
53
+ s.add_development_dependency(%q<mocha>, [">= 0"])
53
54
  else
54
- s.add_dependency(%q<XMLCanonicalizer>, [">= 1.0.1"])
55
- s.add_dependency(%q<uuid>, [">= 2.0.0"])
55
+ s.add_dependency(%q<xmlcanonicalizer>, ["= 0.1.0"])
56
+ s.add_dependency(%q<uuid>, ["= 2.3.1"])
57
+ s.add_dependency(%q<shoulda>, [">= 0"])
58
+ s.add_dependency(%q<mocha>, [">= 0"])
56
59
  end
57
60
  else
58
- s.add_dependency(%q<uuid>, [">= 2.0.0"])
61
+ s.add_dependency(%q<xmlcanonicalizer>, ["= 0.1.0"])
62
+ s.add_dependency(%q<uuid>, ["= 2.3.1"])
63
+ s.add_dependency(%q<shoulda>, [">= 0"])
64
+ s.add_dependency(%q<mocha>, [">= 0"])
59
65
  end
60
66
  end
67
+
@@ -0,0 +1,68 @@
1
+ PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0
2
+ YzpTQU1MOjIuMDphc3NlcnRpb24iIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6
3
+ bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIElEPSJHT1NBTUxSMTI5MDEx
4
+ NzQ1NzE3OTQiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDEwLTEx
5
+ LTE4VDIxOjU3OjM3WiIgRGVzdGluYXRpb249IntyZWNpcGllbnR9Ij48c2Ft
6
+ bHA6U3RhdHVzPjxzYW1scDpTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6
7
+ bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L3NhbWxwOlN0
8
+ YXR1cz48c2FtbDpBc3NlcnRpb24geG1sbnM6eHM9Imh0dHA6Ly93d3cudzMu
9
+ b3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMu
10
+ b3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiBWZXJzaW9uPSIyLjAiIElE
11
+ PSJwZnhhNDY1NzRkZi1iM2IwLWEwNmEtMjNjOC02MzY0MTMxOTg3NzIiIElz
12
+ c3VlSW5zdGFudD0iMjAxMC0xMS0xOFQyMTo1NzozN1oiPjxzYW1sOklzc3Vl
13
+ cj5odHRwczovL2FwcC5vbmVsb2dpbi5jb20vc2FtbC9tZXRhZGF0YS8xMzU5
14
+ MDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDov
15
+ L3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgPGRzOlNpZ25lZElu
16
+ Zm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRw
17
+ Oi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxk
18
+ czpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9y
19
+ Zy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4KICA8ZHM6UmVmZXJlbmNl
20
+ IFVSST0iI3BmeGE0NjU3NGRmLWIzYjAtYTA2YS0yM2M4LTYzNjQxMzE5ODc3
21
+ MiI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0
22
+ dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2ln
23
+ bmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cu
24
+ dzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jt
25
+ cz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5v
26
+ cmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiLz48ZHM6RGlnZXN0VmFsdWU+cEpR
27
+ N01TL2VrNEtSUldHbXYvSDQzUmVIWU1zPTwvZHM6RGlnZXN0VmFsdWU+PC9k
28
+ czpSZWZlcmVuY2U+PC9kczpTaWduZWRJbmZvPjxkczpTaWduYXR1cmVWYWx1
29
+ ZT55aXZlS2NQZERwdUROajZzaHJRM0FCd3IvY0EzQ3J5RDJwaEcveExac3pL
30
+ V3hVNS9tbGFLdDhld2JaT2RLS3Z0T3MycEhCeTVEdWEzazk0QUYrenhHeWVs
31
+ NWdPb3dtb3lYSnIrQU9yK2tQTzB2bGkxVjhvM2hQUFVad1JnU1g2UTlwUzFD
32
+ cVFnaEtpRWFzUnl5bHFxSlVhUFl6bU96T0U4L1hsTWt3aVdtTzA9PC9kczpT
33
+ aWduYXR1cmVWYWx1ZT4KPGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpY
34
+ NTA5Q2VydGlmaWNhdGU+TUlJQnJUQ0NBYUdnQXdJQkFnSUJBVEFEQmdFQU1H
35
+ Y3hDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwRFlXeHBabTl5Ym1s
36
+ aE1SVXdFd1lEVlFRSERBeFRZVzUwWVNCTmIyNXBZMkV4RVRBUEJnTlZCQW9N
37
+ Q0U5dVpVeHZaMmx1TVJrd0Z3WURWUVFEREJCaGNIQXViMjVsYkc5bmFXNHVZ
38
+ Mjl0TUI0WERURXdNRE13T1RBNU5UZzBOVm9YRFRFMU1ETXdPVEE1TlRnME5W
39
+ b3daekVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNK
40
+ dWFXRXhGVEFUQmdOVkJBY01ERk5oYm5SaElFMXZibWxqWVRFUk1BOEdBMVVF
41
+ Q2d3SVQyNWxURzluYVc0eEdUQVhCZ05WQkFNTUVHRndjQzV2Ym1Wc2IyZHBi
42
+ aTVqYjIwd2daOHdEUVlKS29aSWh2Y05BUUVCQlFBRGdZMEFNSUdKQW9HQkFP
43
+ alN1MWZqUHk4ZDV3NFF5TDEremQ0aEl3MU1ra2ZmNFdZL1RMRzhPWmtVNVlU
44
+ U1dtbUhQRDVrdllINXVvWFMvNnFRODFxWHBSMndWOENUb3daSlVMZzA5ZGRS
45
+ ZFJuOFFzcWoxRnlPQzVzbEUzeTJiWjJvRnVhNzJvZi80OWZwdWpuRlQ2S25R
46
+ NjFDQk1xbERvVFFxT1Q2MnZHSjhuUDZNWld2QTZzeHF1ZDVBZ01CQUFFd0F3
47
+ WUJBQU1CQUE9PTwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+
48
+ PC9kczpLZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxzYW1sOlN1YmplY3Q+PHNh
49
+ bWw6TmFtZUlEIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4x
50
+ Om5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIj5zdXBwb3J0QG9uZWxvZ2lu
51
+ LmNvbTwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBN
52
+ ZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIi
53
+ PjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0i
54
+ MjAxMC0xMS0xOFQyMjowMjozN1oiIFJlY2lwaWVudD0ie3JlY2lwaWVudH0i
55
+ Lz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWw6U3ViamVjdD48
56
+ c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxMC0xMS0xOFQyMTo1Mjoz
57
+ N1oiIE5vdE9uT3JBZnRlcj0iMjAxMC0xMS0xOFQyMjowMjozN1oiPjxzYW1s
58
+ OkF1ZGllbmNlUmVzdHJpY3Rpb24+PHNhbWw6QXVkaWVuY2U+e2F1ZGllbmNl
59
+ fTwvc2FtbDpBdWRpZW5jZT48L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48
60
+ L3NhbWw6Q29uZGl0aW9ucz48c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobklu
61
+ c3RhbnQ9IjIwMTAtMTEtMThUMjE6NTc6MzdaIiBTZXNzaW9uTm90T25PckFm
62
+ dGVyPSIyMDEwLTExLTE5VDIxOjU3OjM3WiIgU2Vzc2lvbkluZGV4PSJfNTMx
63
+ YzMyZDI4M2JkZmY3ZTA0ZTQ4N2JjZGJjNGRkOGQiPjxzYW1sOkF1dGhuQ29u
64
+ dGV4dD48c2FtbDpBdXRobkNvbnRleHRDbGFzc1JlZj51cm46b2FzaXM6bmFt
65
+ ZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZDwvc2FtbDpBdXRo
66
+ bkNvbnRleHRDbGFzc1JlZj48L3NhbWw6QXV0aG5Db250ZXh0Pjwvc2FtbDpB
67
+ dXRoblN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9u
68
+ c2U+Cg==
@@ -1,7 +1,82 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class RubySamlTest < Test::Unit::TestCase
4
- should "probably rename this file and start testing for real" do
5
- flunk "hey buddy, you should probably rename this file and start testing for real"
4
+
5
+ context "Settings" do
6
+ setup do
7
+ @settings = Onelogin::Saml::Settings.new
8
+ end
9
+ should "should provide getters and settings" do
10
+ accessors = [
11
+ :assertion_consumer_service_url, :issuer, :sp_name_qualifier, :sp_name_qualifier,
12
+ :idp_sso_target_url, :idp_cert_fingerprint, :name_identifier_format
13
+ ]
14
+
15
+ accessors.each do |accessor|
16
+ value = Kernel.rand
17
+ @settings.send("#{accessor}=".to_sym, value)
18
+ assert_equal value, @settings.send(accessor)
19
+ end
20
+ end
6
21
  end
22
+
23
+ context "Response" do
24
+ should "provide setter for a logger" do
25
+ response = Onelogin::Saml::Response.new('')
26
+ assert response.logger = 'hello'
27
+ end
28
+
29
+ should "raise an exception when response is initialized with nil" do
30
+ assert_raises(ArgumentError) { Onelogin::Saml::Response.new(nil) }
31
+ end
32
+
33
+ context "#is_valid?" do
34
+ should "return false when response is initialized with blank data" do
35
+ response = Onelogin::Saml::Response.new('')
36
+ assert !response.is_valid?
37
+ end
38
+
39
+ should "return false if settings have not been set" do
40
+ response = Onelogin::Saml::Response.new(response_document)
41
+ assert !response.is_valid?
42
+ end
43
+
44
+ should "return true when the response is initialized with valid data" do
45
+ response = Onelogin::Saml::Response.new(response_document)
46
+ settings = Onelogin::Saml::Settings.new
47
+ settings.idp_cert_fingerprint = 'hello'
48
+ response.settings = settings
49
+ assert !response.is_valid?
50
+ document = stub()
51
+ document.stubs(:validate).returns(true)
52
+ response.document = document
53
+ assert response.is_valid?
54
+ end
55
+ end
56
+
57
+ context "#name_id" do
58
+ should "extract the value of the name id element" do
59
+ response = Onelogin::Saml::Response.new(response_document)
60
+ assert_equal "support@onelogin.com", response.name_id
61
+ end
62
+ end
63
+
64
+ context "#session_expires_at" do
65
+ should "extract the value of the SessionNotOnOrAfter attribute" do
66
+ response = Onelogin::Saml::Response.new(response_document)
67
+ assert response.session_expires_at.is_a?(Time)
68
+ end
69
+ end
70
+ end
71
+
72
+ context "Authrequest" do
73
+ should "create the SAMLRequest URL parameter" do
74
+ settings = Onelogin::Saml::Settings.new
75
+ settings.idp_sso_target_url = "http://stuff.com"
76
+ auth_url = Onelogin::Saml::Authrequest.new.create(settings)
77
+ assert auth_url =~ /^http:\/\/stuff\.com\?SAMLRequest=/
78
+ payload = CGI.unescape(auth_url.split("=").last)
79
+ end
80
+ end
81
+
7
82
  end
@@ -1,10 +1,14 @@
1
1
  require 'rubygems'
2
2
  require 'test/unit'
3
3
  require 'shoulda'
4
+ require 'mocha'
4
5
 
5
6
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
7
  $LOAD_PATH.unshift(File.dirname(__FILE__))
7
8
  require 'ruby-saml'
8
9
 
9
10
  class Test::Unit::TestCase
11
+ def response_document
12
+ @response_document ||= File.read(File.join(File.dirname(__FILE__), 'response.txt'))
13
+ end
10
14
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-saml
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 0
9
- - 8
10
- version: 0.0.8
8
+ - 2
9
+ - 1
10
+ version: 0.2.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - OneLogin LLC
@@ -15,23 +15,23 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-08-10 00:00:00 -05:00
18
+ date: 2010-12-01 00:00:00 -06:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- name: XMLCanonicalizer
22
+ name: xmlcanonicalizer
23
23
  prerelease: false
24
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
25
  none: false
26
26
  requirements:
27
- - - ">="
27
+ - - "="
28
28
  - !ruby/object:Gem::Version
29
- hash: 21
29
+ hash: 27
30
30
  segments:
31
- - 1
32
31
  - 0
33
32
  - 1
34
- version: 1.0.1
33
+ - 0
34
+ version: 0.1.0
35
35
  type: :runtime
36
36
  version_requirements: *id001
37
37
  - !ruby/object:Gem::Dependency
@@ -40,16 +40,44 @@ dependencies:
40
40
  requirement: &id002 !ruby/object:Gem::Requirement
41
41
  none: false
42
42
  requirements:
43
- - - ">="
43
+ - - "="
44
44
  - !ruby/object:Gem::Version
45
- hash: 15
45
+ hash: 1
46
46
  segments:
47
47
  - 2
48
- - 0
49
- - 0
50
- version: 2.0.0
48
+ - 3
49
+ - 1
50
+ version: 2.3.1
51
51
  type: :runtime
52
52
  version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: shoulda
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 0
64
+ version: "0"
65
+ type: :development
66
+ version_requirements: *id003
67
+ - !ruby/object:Gem::Dependency
68
+ name: mocha
69
+ prerelease: false
70
+ requirement: &id004 !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ hash: 3
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ type: :development
80
+ version_requirements: *id004
53
81
  description: SAML toolkit for Ruby on Rails
54
82
  email: support@onelogin.com
55
83
  executables: []
@@ -61,7 +89,6 @@ extra_rdoc_files:
61
89
  - README.rdoc
62
90
  files:
63
91
  - .document
64
- - .gitignore
65
92
  - LICENSE
66
93
  - README.rdoc
67
94
  - Rakefile
@@ -73,6 +100,7 @@ files:
73
100
  - lib/ruby-saml.rb
74
101
  - lib/xml_sec.rb
75
102
  - ruby-saml.gemspec
103
+ - test/response.txt
76
104
  - test/ruby-saml_test.rb
77
105
  - test/test_helper.rb
78
106
  has_rdoc: true
@@ -80,8 +108,8 @@ homepage: http://github.com/onelogin/ruby-saml
80
108
  licenses: []
81
109
 
82
110
  post_install_message:
83
- rdoc_options:
84
- - --charset=UTF-8
111
+ rdoc_options: []
112
+
85
113
  require_paths:
86
114
  - lib
87
115
  required_ruby_version: !ruby/object:Gem::Requirement
data/.gitignore DELETED
@@ -1,5 +0,0 @@
1
- *.sw?
2
- .DS_Store
3
- coverage
4
- rdoc
5
- pkg