maestrano 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +34 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +43 -0
  5. data/LICENSE +21 -0
  6. data/README.md +4 -0
  7. data/Rakefile +32 -0
  8. data/bin/maestrano-console +9 -0
  9. data/lib/maestrano.rb +114 -0
  10. data/lib/maestrano/account/bill.rb +14 -0
  11. data/lib/maestrano/api/error/authentication_error.rb +8 -0
  12. data/lib/maestrano/api/error/base_error.rb +24 -0
  13. data/lib/maestrano/api/error/connection_error.rb +8 -0
  14. data/lib/maestrano/api/error/invalid_request_error.rb +14 -0
  15. data/lib/maestrano/api/list_object.rb +37 -0
  16. data/lib/maestrano/api/object.rb +187 -0
  17. data/lib/maestrano/api/operation/base.rb +216 -0
  18. data/lib/maestrano/api/operation/create.rb +18 -0
  19. data/lib/maestrano/api/operation/delete.rb +13 -0
  20. data/lib/maestrano/api/operation/list.rb +18 -0
  21. data/lib/maestrano/api/operation/update.rb +59 -0
  22. data/lib/maestrano/api/resource.rb +39 -0
  23. data/lib/maestrano/api/util.rb +121 -0
  24. data/lib/maestrano/saml/attribute_value.rb +15 -0
  25. data/lib/maestrano/saml/metadata.rb +64 -0
  26. data/lib/maestrano/saml/request.rb +93 -0
  27. data/lib/maestrano/saml/response.rb +201 -0
  28. data/lib/maestrano/saml/schemas/saml20assertion_schema.xsd +283 -0
  29. data/lib/maestrano/saml/schemas/saml20protocol_schema.xsd +302 -0
  30. data/lib/maestrano/saml/schemas/xenc_schema.xsd +146 -0
  31. data/lib/maestrano/saml/schemas/xmldsig_schema.xsd +318 -0
  32. data/lib/maestrano/saml/settings.rb +37 -0
  33. data/lib/maestrano/saml/validation_error.rb +7 -0
  34. data/lib/maestrano/sso.rb +81 -0
  35. data/lib/maestrano/sso/base_group.rb +31 -0
  36. data/lib/maestrano/sso/base_user.rb +75 -0
  37. data/lib/maestrano/sso/group.rb +24 -0
  38. data/lib/maestrano/sso/session.rb +63 -0
  39. data/lib/maestrano/sso/user.rb +34 -0
  40. data/lib/maestrano/version.rb +3 -0
  41. data/lib/maestrano/xml_security/signed_document.rb +170 -0
  42. data/maestrano.gemspec +32 -0
  43. data/test/helpers/api_helpers.rb +82 -0
  44. data/test/helpers/saml_helpers.rb +62 -0
  45. data/test/maestrano/account/bill_test.rb +48 -0
  46. data/test/maestrano/api/list_object_test.rb +20 -0
  47. data/test/maestrano/api/object_test.rb +28 -0
  48. data/test/maestrano/api/resource_test.rb +343 -0
  49. data/test/maestrano/api/util_test.rb +31 -0
  50. data/test/maestrano/maestrano_test.rb +49 -0
  51. data/test/maestrano/saml/request_test.rb +168 -0
  52. data/test/maestrano/saml/response_test.rb +290 -0
  53. data/test/maestrano/saml/settings_test.rb +51 -0
  54. data/test/maestrano/sso/base_group_test.rb +54 -0
  55. data/test/maestrano/sso/base_user_test.rb +114 -0
  56. data/test/maestrano/sso/group_test.rb +47 -0
  57. data/test/maestrano/sso/session_test.rb +108 -0
  58. data/test/maestrano/sso/user_test.rb +65 -0
  59. data/test/maestrano/sso_test.rb +81 -0
  60. data/test/maestrano/xml_security/signed_document.rb +163 -0
  61. data/test/support/saml/certificates/certificate1 +12 -0
  62. data/test/support/saml/certificates/r1_certificate2_base64 +1 -0
  63. data/test/support/saml/responses/adfs_response_sha1.xml +46 -0
  64. data/test/support/saml/responses/adfs_response_sha256.xml +46 -0
  65. data/test/support/saml/responses/adfs_response_sha384.xml +46 -0
  66. data/test/support/saml/responses/adfs_response_sha512.xml +46 -0
  67. data/test/support/saml/responses/no_signature_ns.xml +48 -0
  68. data/test/support/saml/responses/open_saml_response.xml +56 -0
  69. data/test/support/saml/responses/r1_response6.xml.base64 +1 -0
  70. data/test/support/saml/responses/response1.xml.base64 +1 -0
  71. data/test/support/saml/responses/response2.xml.base64 +79 -0
  72. data/test/support/saml/responses/response3.xml.base64 +66 -0
  73. data/test/support/saml/responses/response4.xml.base64 +93 -0
  74. data/test/support/saml/responses/response5.xml.base64 +102 -0
  75. data/test/support/saml/responses/response_with_ampersands.xml +139 -0
  76. data/test/support/saml/responses/response_with_ampersands.xml.base64 +93 -0
  77. data/test/support/saml/responses/response_with_multiple_attribute_values.xml +57 -0
  78. data/test/support/saml/responses/simple_saml_php.xml +71 -0
  79. data/test/support/saml/responses/starfield_response.xml.base64 +1 -0
  80. data/test/support/saml/responses/wrapped_response_2.xml.base64 +150 -0
  81. data/test/test_helper.rb +46 -0
  82. metadata +305 -0
@@ -0,0 +1,37 @@
1
+ module Maestrano
2
+ module Saml
3
+ class Settings
4
+ NAMEID_EMAIL_ADDRESS = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'
5
+ NAMEID_X509_SUBJECT_NAME = 'urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName'
6
+ NAMEID_WINDOWS_DOMAIN_QUALIFIED_NAME = 'urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName'
7
+ NAMEID_KERBEROS = 'urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos'
8
+ NAMEID_ENTITY = 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity'
9
+ NAMEID_TRANSIENT = 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'
10
+ NAMEID_PERSISTENT = 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent'
11
+ PROTOCOL_BINDING_POST = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
12
+
13
+ def initialize(overrides = {})
14
+ config = DEFAULTS.merge(overrides)
15
+ config.each do |k,v|
16
+ acc = "#{k.to_s}=".to_sym
17
+ self.send(acc, v) if self.respond_to? acc
18
+ end
19
+ end
20
+ attr_accessor :assertion_consumer_service_url, :issuer, :sp_name_qualifier
21
+ attr_accessor :idp_sso_target_url, :idp_cert_fingerprint, :idp_cert, :name_identifier_format
22
+ attr_accessor :authn_context
23
+ attr_accessor :idp_slo_target_url
24
+ attr_accessor :name_identifier_value
25
+ attr_accessor :sessionindex
26
+ attr_accessor :assertion_consumer_logout_service_url
27
+ attr_accessor :compress_request
28
+ attr_accessor :double_quote_xml_attribute_values
29
+ attr_accessor :passive
30
+ attr_accessor :protocol_binding
31
+
32
+ private
33
+
34
+ DEFAULTS = {:compress_request => true, :double_quote_xml_attribute_values => false}
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,7 @@
1
+ module Maestrano
2
+ module Saml
3
+ class ValidationError < StandardError
4
+ end
5
+ end
6
+ end
7
+
@@ -0,0 +1,81 @@
1
+ module Maestrano
2
+ module SSO
3
+ # Return the saml_settings based on
4
+ # Maestrano configuration
5
+ def self.saml_settings
6
+ settings = Maestrano::Saml::Settings.new
7
+ settings.assertion_consumer_service_url = self.consume_url
8
+ settings.issuer = Maestrano.param('app_host')
9
+ settings.idp_sso_target_url = self.idp_url
10
+ settings.idp_cert_fingerprint = Maestrano.param('sso_x509_fingerprint')
11
+ settings.name_identifier_format = Maestrano.param('sso_name_id_format')
12
+ settings
13
+ end
14
+
15
+ # Build a new SAML Request
16
+ def self.build_request(get_params = {})
17
+ Maestrano::Saml::Request.new(get_params)
18
+ end
19
+
20
+ # Build a new SAML response
21
+ def self.build_response(saml_post_param)
22
+ Maestrano::Saml::Response.new(saml_post_param)
23
+ end
24
+
25
+ def self.enabled?
26
+ !!Maestrano.param('sso_enabled')
27
+ end
28
+
29
+ def self.init_url
30
+ host = Maestrano.param('app_host')
31
+ path = Maestrano.param('sso_app_init_path')
32
+ return "#{host}#{path}"
33
+ end
34
+
35
+ def self.consume_url
36
+ host = Maestrano.param('app_host')
37
+ path = Maestrano.param('sso_app_consume_path')
38
+ return "#{host}#{path}"
39
+ end
40
+
41
+ def self.logout_url
42
+ host = Maestrano.param('api_host')
43
+ path = '/app_logout'
44
+ return "#{host}#{path}"
45
+ end
46
+
47
+ def self.unauthorized_url
48
+ host = Maestrano.param('api_host')
49
+ path = '/app_access_unauthorized'
50
+ return "#{host}#{path}";
51
+ end
52
+
53
+ def self.idp_url
54
+ host = Maestrano.param('api_host')
55
+ api_base = Maestrano.param('api_base')
56
+ endpoint = 'auth/saml'
57
+ return "#{host}#{api_base}#{endpoint}"
58
+ end
59
+
60
+ def self.session_check_url(user_uid,sso_session)
61
+ host = Maestrano.param('api_host')
62
+ api_base = Maestrano.param('api_base')
63
+ endpoint = 'auth/saml'
64
+ return URI.escape("#{host}#{api_base}#{endpoint}/#{user_uid}?session=#{sso_session}")
65
+ end
66
+
67
+ # Set maestrano attributes in session
68
+ # Takes the BaseUser hash representation and current session
69
+ # in arguments
70
+ def self.set_session(session, auth)
71
+ if auth && (extra = (auth[:extra] || auth['extra'])) && (sso_session = (extra[:session] || extra['session']))
72
+ session[:mno_uid] = (sso_session[:uid] || sso_session['uid'])
73
+ session[:mno_session] = (sso_session[:token] || sso_session['token'])
74
+ if recheck = (sso_session[:recheck] || sso_session['recheck'])
75
+ session[:mno_session_recheck] = recheck.utc.iso8601
76
+ end
77
+ session[:mno_group_uid] = (sso_session[:group_uid] || sso_session['group_uid'])
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,31 @@
1
+ module Maestrano
2
+ module SSO
3
+ class BaseGroup
4
+ attr_accessor :local_id
5
+ attr_reader :uid,:country, :company_name, :free_trial_end_at
6
+
7
+ # Initializer
8
+ # @param Maestrano::SAML::Response
9
+ def initialize(saml_response)
10
+ att = saml_response.attributes
11
+ @uid = att['group_uid']
12
+ @country = att['country']
13
+ @free_trial_end_at = Time.iso8601(att['group_end_free_trial'])
14
+ @company_name = att['company_name']
15
+ end
16
+
17
+ def to_hash
18
+ {
19
+ provider: 'maestrano',
20
+ uid: self.uid,
21
+ info: {
22
+ free_trial_end_at: self.free_trial_end_at,
23
+ company_name: self.company_name,
24
+ country: self.country,
25
+ },
26
+ extra: {}
27
+ }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,75 @@
1
+ module Maestrano
2
+ module SSO
3
+ class BaseUser
4
+ attr_accessor :local_id
5
+ attr_reader :sso_session,:sso_session_recheck,
6
+ :group_uid,:group_role,:uid,:virtual_uid,:email,
7
+ :virtual_email,:first_name, :last_name,:country, :company_name
8
+
9
+ # Initializer
10
+ # @param Maestrano::SAML::Response
11
+ def initialize(saml_response)
12
+ att = saml_response.attributes
13
+ @sso_session = att['mno_session']
14
+ @sso_session_recheck = Time.iso8601(att['mno_session_recheck'])
15
+ @group_uid = att['group_uid']
16
+ @group_role = att['group_role']
17
+ @uid = att['uid']
18
+ @virtual_uid = att['virtual_uid']
19
+ @email = att['email']
20
+ @virtual_email = att['virtual_email']
21
+ @first_name = att['name']
22
+ @last_name = att['surname']
23
+ @country = att['country']
24
+ @company_name = att['company_name']
25
+ end
26
+
27
+ def to_uid
28
+ if Maestrano.param('user_creation_mode') == 'real'
29
+ return self.uid
30
+ else
31
+ return self.virtual_uid
32
+ end
33
+ end
34
+
35
+ def to_email
36
+ if Maestrano.param('user_creation_mode') == 'real'
37
+ return self.email
38
+ else
39
+ return self.virtual_email
40
+ end
41
+ end
42
+
43
+ # Hash representation of the resource
44
+ def to_hash
45
+ {
46
+ provider: 'maestrano',
47
+ uid: self.to_uid,
48
+ info: {
49
+ email: self.to_email,
50
+ first_name: self.first_name,
51
+ last_name: self.last_name,
52
+ country: self.country,
53
+ company_name: self.company_name,
54
+ },
55
+ extra: {
56
+ uid: self.uid,
57
+ virtual_uid: self.virtual_uid,
58
+ real_email: self.email,
59
+ virtual_email: self.virtual_email,
60
+ group: {
61
+ uid: self.group_uid,
62
+ role: self.group_role,
63
+ },
64
+ session: {
65
+ uid: self.uid,
66
+ token: self.sso_session,
67
+ recheck: self.sso_session_recheck,
68
+ group_uid: self.group_uid
69
+ },
70
+ }
71
+ }
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,24 @@
1
+ module Maestrano
2
+ module SSO
3
+ module Group
4
+ def find_for_maestrano_auth(auth)
5
+ # E.g with Rails
6
+ # where(auth.slice(:provider, :uid)).first_or_create do |group|
7
+ # group.provider = auth[:provider]
8
+ # group.uid = auth[:uid]
9
+ # group.name = (auth[:info][:company_name] || 'Your Group')
10
+ # group.country = auth[:info][:country]
11
+ # end
12
+ raise NoMethodError, "You need to override find_for_maestrano_auth in your #{self.class.name} model"
13
+ end
14
+
15
+ def maestrano?
16
+ if self.respond_to?(:provider)
17
+ return self.provider.to_s == 'maestrano'
18
+ else
19
+ raise NoMethodError, "You need to override maestrano? in your #{self.class.name} model"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,63 @@
1
+ module Maestrano
2
+ module SSO
3
+ class Session
4
+ attr_accessor :session, :uid, :session_token, :recheck
5
+
6
+ def initialize(session)
7
+ self.session = session
8
+ self.uid = (self.session['mno_uid'] || self.session[:mno_uid])
9
+ self.session_token = (self.session['mno_session'] || self.session[:mno_session])
10
+ if recheck = (self.session['mno_session_recheck'] || self.session[:mno_session_recheck])
11
+ self.recheck = Time.iso8601(recheck)
12
+ end
13
+
14
+ if self.uid.nil? || self.session_token.nil? || self.recheck.nil?
15
+ $stderr.puts "WARNING: Maestrano session information missing. User will have to relogin"
16
+ end
17
+ end
18
+
19
+ def remote_check_required?
20
+ if self.uid && self.session_token && self.recheck
21
+ return (self.recheck <= Time.now)
22
+ end
23
+ return true
24
+ end
25
+
26
+ # Check remote maestrano session and update the
27
+ # recheck attribute if the session is still valid
28
+ # Return true if the session is still valid and
29
+ # false otherwise
30
+ def perform_remote_check
31
+ # Get remote session info
32
+ url = Maestrano::SSO.session_check_url(self.uid, self.session_token)
33
+ begin
34
+ response = RestClient.get(url)
35
+ response = JSON.parse(response)
36
+ rescue Exception => e
37
+ response = {}
38
+ end
39
+
40
+ # Process response
41
+ if response['valid'] && response['recheck']
42
+ self.recheck = Time.iso8601(response['recheck'])
43
+ return true
44
+ end
45
+
46
+ return false
47
+ end
48
+
49
+ def valid?
50
+ if self.remote_check_required?
51
+ if perform_remote_check
52
+ self.session[:mno_session_recheck] = self.recheck.utc.iso8601
53
+ return true
54
+ else
55
+ return false
56
+ end
57
+ end
58
+ return true
59
+ end
60
+
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,34 @@
1
+ module Maestrano
2
+ module SSO
3
+ module User
4
+ def find_for_maestrano_auth(auth)
5
+ # E.g with Rails
6
+ # where(auth.slice(:provider, :uid)).first_or_create do |user|
7
+ # user.provider = auth[:provider]
8
+ # user.uid = auth[:uid]
9
+ # user.email = auth[:info][:email]
10
+ # user.name = auth[:info][:first_name]
11
+ # user.surname = auth[:info][:last_name]
12
+ # user.country = auth[:info][:country]
13
+ # user.company = auth[:info][:company_name]
14
+ # end
15
+ raise NoMethodError, "You need to override find_for_maestrano_auth in your #{self.class.name} model"
16
+ end
17
+
18
+ # Check whether the user is a maestrano one
19
+ def maestrano?
20
+ if self.respond_to?(:provider)
21
+ return self.provider.to_s == 'maestrano'
22
+ else
23
+ raise NoMethodError, "You need to override maestrano? in your #{self.class.name} model"
24
+ end
25
+ end
26
+
27
+ # Check whether the SSO session is still valid
28
+ # or not
29
+ def maestrano_session_valid?(session)
30
+ Maestrano::SSO::Session.new(session).valid?
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ module Maestrano
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,170 @@
1
+ # The contents of this file are subject to the terms
2
+ # of the Common Development and Distribution License
3
+ # (the License). You may not use this file except in
4
+ # compliance with the License.
5
+ #
6
+ # You can obtain a copy of the License at
7
+ # https://opensso.dev.java.net/public/CDDLv1.0.html or
8
+ # opensso/legal/CDDLv1.0.txt
9
+ # See the License for the specific language governing
10
+ # permission and limitations under the License.
11
+ #
12
+ # When distributing Covered Code, include this CDDL
13
+ # Header Notice in each file and include the License file
14
+ # at opensso/legal/CDDLv1.0.txt.
15
+ # If applicable, add the following below the CDDL Header,
16
+ # with the fields enclosed by brackets [] replaced by
17
+ # your own identifying information:
18
+ # "Portions Copyrighted [year] [name of copyright owner]"
19
+ #
20
+ # $Id: xml_sec.rb,v 1.6 2007/10/24 00:28:41 todddd Exp $
21
+ #
22
+ # Copyright 2007 Sun Microsystems Inc. All Rights Reserved
23
+ # Portions Copyrighted 2007 Todd W Saxton.
24
+
25
+ require 'rubygems'
26
+ require "rexml/document"
27
+ require "rexml/xpath"
28
+ require "openssl"
29
+ require 'nokogiri'
30
+ require "digest/sha1"
31
+ require "digest/sha2"
32
+ require "maestrano/saml/validation_error"
33
+
34
+ module Maestrano
35
+ module XMLSecurity
36
+ class SignedDocument < REXML::Document
37
+ C14N = "http://www.w3.org/2001/10/xml-exc-c14n#"
38
+ DSIG = "http://www.w3.org/2000/09/xmldsig#"
39
+
40
+ attr_accessor :signed_element_id
41
+
42
+ def initialize(response)
43
+ super(response)
44
+ extract_signed_element_id
45
+ end
46
+
47
+ def validate_document(idp_cert_fingerprint, soft = true)
48
+ # get cert from response
49
+ cert_element = REXML::XPath.first(self, "//ds:X509Certificate", { "ds"=>DSIG })
50
+ raise Maestrano::Saml::ValidationError.new("Certificate element missing in response (ds:X509Certificate)") unless cert_element
51
+ base64_cert = cert_element.text
52
+ cert_text = Base64.decode64(base64_cert)
53
+ cert = OpenSSL::X509::Certificate.new(cert_text)
54
+
55
+ # check cert matches registered idp cert
56
+ fingerprint = Digest::SHA1.hexdigest(cert.to_der)
57
+
58
+ if fingerprint != idp_cert_fingerprint.gsub(/[^a-zA-Z0-9]/,"").downcase
59
+ return soft ? false : (raise Maestrano::Saml::ValidationError.new("Fingerprint mismatch"))
60
+ end
61
+
62
+ validate_signature(base64_cert, soft)
63
+ end
64
+
65
+ def validate_signature(base64_cert, soft = true)
66
+ # validate references
67
+
68
+ # check for inclusive namespaces
69
+ inclusive_namespaces = extract_inclusive_namespaces
70
+
71
+ document = Nokogiri.parse(self.to_s)
72
+
73
+ # create a working copy so we don't modify the original
74
+ @working_copy ||= REXML::Document.new(self.to_s).root
75
+
76
+ # store and remove signature node
77
+ @sig_element ||= begin
78
+ element = REXML::XPath.first(@working_copy, "//ds:Signature", {"ds"=>DSIG})
79
+ element.remove
80
+ end
81
+
82
+
83
+ # verify signature
84
+ signed_info_element = REXML::XPath.first(@sig_element, "//ds:SignedInfo", {"ds"=>DSIG})
85
+ noko_sig_element = document.at_xpath('//ds:Signature', 'ds' => DSIG)
86
+ noko_signed_info_element = noko_sig_element.at_xpath('./ds:SignedInfo', 'ds' => DSIG)
87
+ canon_algorithm = canon_algorithm REXML::XPath.first(@sig_element, '//ds:CanonicalizationMethod', 'ds' => DSIG)
88
+ canon_string = noko_signed_info_element.canonicalize(canon_algorithm)
89
+ noko_sig_element.remove
90
+
91
+ # check digests
92
+ REXML::XPath.each(@sig_element, "//ds:Reference", {"ds"=>DSIG}) do |ref|
93
+ uri = ref.attributes.get_attribute("URI").value
94
+
95
+ hashed_element = document.at_xpath("//*[@ID='#{uri[1..-1]}']")
96
+ canon_algorithm = canon_algorithm REXML::XPath.first(ref, '//ds:CanonicalizationMethod', 'ds' => DSIG)
97
+ canon_hashed_element = hashed_element.canonicalize(canon_algorithm, inclusive_namespaces)
98
+
99
+ digest_algorithm = algorithm(REXML::XPath.first(ref, "//ds:DigestMethod"))
100
+
101
+ hash = digest_algorithm.digest(canon_hashed_element)
102
+ digest_value = Base64.decode64(REXML::XPath.first(ref, "//ds:DigestValue", {"ds"=>DSIG}).text)
103
+
104
+ unless digests_match?(hash, digest_value)
105
+ return soft ? false : (raise Maestrano::Saml::ValidationError.new("Digest mismatch"))
106
+ end
107
+ end
108
+
109
+ base64_signature = REXML::XPath.first(@sig_element, "//ds:SignatureValue", {"ds"=>DSIG}).text
110
+ signature = Base64.decode64(base64_signature)
111
+
112
+ # get certificate object
113
+ cert_text = Base64.decode64(base64_cert)
114
+ cert = OpenSSL::X509::Certificate.new(cert_text)
115
+
116
+ # signature method
117
+ signature_algorithm = algorithm(REXML::XPath.first(signed_info_element, "//ds:SignatureMethod", {"ds"=>DSIG}))
118
+
119
+ unless cert.public_key.verify(signature_algorithm.new, signature, canon_string)
120
+ return soft ? false : (raise Maestrano::Saml::ValidationError.new("Key validation error"))
121
+ end
122
+
123
+ return true
124
+ end
125
+
126
+ private
127
+
128
+ def digests_match?(hash, digest_value)
129
+ hash == digest_value
130
+ end
131
+
132
+ def extract_signed_element_id
133
+ reference_element = REXML::XPath.first(self, "//ds:Signature/ds:SignedInfo/ds:Reference", {"ds"=>DSIG})
134
+ self.signed_element_id = reference_element.attribute("URI").value[1..-1] unless reference_element.nil?
135
+ end
136
+
137
+ def canon_algorithm(element)
138
+ algorithm = element.attribute('Algorithm').value if element
139
+ case algorithm
140
+ when "http://www.w3.org/2001/10/xml-exc-c14n#" then Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
141
+ when "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" then Nokogiri::XML::XML_C14N_1_0
142
+ when "http://www.w3.org/2006/12/xml-c14n11" then Nokogiri::XML::XML_C14N_1_1
143
+ else Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
144
+ end
145
+ end
146
+
147
+ def algorithm(element)
148
+ algorithm = element.attribute("Algorithm").value if element
149
+ algorithm = algorithm && algorithm =~ /sha(.*?)$/i && $1.to_i
150
+ case algorithm
151
+ when 256 then OpenSSL::Digest::SHA256
152
+ when 384 then OpenSSL::Digest::SHA384
153
+ when 512 then OpenSSL::Digest::SHA512
154
+ else
155
+ OpenSSL::Digest::SHA1
156
+ end
157
+ end
158
+
159
+ def extract_inclusive_namespaces
160
+ if element = REXML::XPath.first(self, "//ec:InclusiveNamespaces", { "ec" => C14N })
161
+ prefix_list = element.attributes.get_attribute("PrefixList").value
162
+ prefix_list.split(" ")
163
+ else
164
+ []
165
+ end
166
+ end
167
+
168
+ end
169
+ end
170
+ end