libsaml 2.0.6 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +8 -8
  2. data/README.rdoc +4 -3
  3. data/lib/saml.rb +73 -11
  4. data/lib/saml/artifact.rb +1 -1
  5. data/lib/saml/artifact_resolve.rb +7 -0
  6. data/lib/saml/assertion.rb +2 -1
  7. data/lib/saml/authn_request.rb +1 -0
  8. data/lib/saml/basic_provider.rb +12 -0
  9. data/lib/saml/complex_types/attribute_type.rb +24 -0
  10. data/lib/saml/complex_types/localized_name_type.rb +14 -0
  11. data/lib/saml/complex_types/request_abstract_type.rb +5 -9
  12. data/lib/saml/complex_types/sso_descriptor_type.rb +16 -0
  13. data/lib/saml/complex_types/statement_abstract_type.rb +34 -0
  14. data/lib/saml/config.rb +13 -31
  15. data/lib/saml/elements/attribute.rb +1 -13
  16. data/lib/saml/elements/attribute_consuming_service.rb +20 -0
  17. data/lib/saml/elements/attribute_statement.rb +1 -6
  18. data/lib/saml/elements/authenticating_authority.rb +14 -0
  19. data/lib/saml/elements/authn_context.rb +2 -0
  20. data/lib/saml/elements/contact_person.rb +1 -1
  21. data/lib/saml/elements/encrypted_attribute.rb +18 -0
  22. data/lib/saml/elements/entity_attributes.rb +15 -0
  23. data/lib/saml/elements/entity_descriptor.rb +4 -3
  24. data/lib/saml/elements/idp_sso_descriptor.rb +2 -0
  25. data/lib/saml/elements/md_extensions.rb +13 -0
  26. data/lib/saml/elements/name_id.rb +2 -0
  27. data/lib/saml/elements/organization.rb +3 -3
  28. data/lib/saml/elements/organization_display_name.rb +13 -0
  29. data/lib/saml/elements/organization_name.rb +13 -0
  30. data/lib/saml/elements/organization_url.rb +13 -0
  31. data/lib/saml/elements/requested_attribute.rb +14 -0
  32. data/lib/saml/elements/samlp_extensions.rb +13 -0
  33. data/lib/saml/elements/service_description.rb +13 -0
  34. data/lib/saml/elements/service_name.rb +13 -0
  35. data/lib/saml/elements/signature.rb +4 -1
  36. data/lib/saml/elements/sp_sso_descriptor.rb +8 -0
  37. data/lib/saml/null_provider.rb +9 -0
  38. data/lib/saml/provider.rb +24 -8
  39. data/lib/saml/provider_stores/file.rb +2 -13
  40. data/lib/saml/rails/controller_helper.rb +30 -0
  41. data/lib/saml/response.rb +8 -1
  42. data/lib/saml/util.rb +11 -5
  43. data/lib/saml/version.rb +1 -1
  44. data/lib/saml/xml_helpers.rb +6 -0
  45. metadata +34 -2
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- OWIyNTBiNGEzODgxNmY2NjkxNzIwMmU1MzlhY2Y2Yzg2Y2U4ZmZmZg==
4
+ OGYxNjQ4ZDFiYTBiNWNkODE0NWZkOTIxZWUwZjU2ODk0YmQxNGE2OA==
5
5
  data.tar.gz: !binary |-
6
- MjNkYzcwNTc0YjBjYmY4OTE3OGMxNTZhNjcwZTUyNDk1ZGVkMDQ4MA==
6
+ ZTcxMjVjMDJkNzA2NjRhMDcyNDQ3ZmVkNjBmZjFjYWEzYTFhOTE4NQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- Y2EwZDZmZTJhODU3NTU1N2IwY2FkYzJjNGZlYTZkZDQ1MWNjNGY4NTE0NThm
10
- ZTcxMTk5NDUyZjZjZjI1YjgzYWU3MTk5YTc4MzQ4OTRjNDQyY2Q4YzdhMmEz
11
- N2YyOTNhY2ViOTFiODZjMDlhYmJkYjEyYjE4Yzc5YWE1ZGI5ZWQ=
9
+ ZGFjMzNhNmM2ODViOTAyMjAwYjQ1ZWE4YjYzOTU3MjQwNmUwNmY0YjNiNzU1
10
+ NjcxNmRhNGMyNGI2NzlkMzc4ZGM2MTlhNGFjNWIwZDNiYWU2M2FiZTIzY2Vh
11
+ Nzg0MmIxZDc4NTFiZTY0MGIwMzc4ZTgxZTY0YTRkMWI4ZWZjMjc=
12
12
  data.tar.gz: !binary |-
13
- NzI2ZmJhNTk0MTZjZjBkNmY4OGIwYTFhOTBiYTU3MTJhMTA4NGJjMTU1OGZm
14
- ZGZiNWJiYTc5NTk5ZjhiNjA4MzRjZmUxZWE4NmYwNDI1OWJlNGIxNWFiOTZm
15
- MzQ5NDlhMmJhNWZjMTc5MGVkN2FlYTE2MGZhYTJmOWRjM2NhZDU=
13
+ NGQ5YWMwM2I3OWExODk4ZmUzMGMyMTM0NzdjNzM2MGIwY2FiM2U2MTg5ZGMz
14
+ ZDJhNDIyZWEwMzM4MzIzZDA3Y2MyN2RjNGU0MzExYjEyYjhiNDI4OGY5NWE4
15
+ ZjJiOWM1NjUwZDgyMDJmYmY2ZWY5MzEzMWNmMmU2NzhlZTZjOWQ=
data/README.rdoc CHANGED
@@ -49,9 +49,7 @@ Add the Service Provider configuration file to config/metadata/service_provider.
49
49
 
50
50
  Set up an intializer in config/initializers/saml_config.rb:
51
51
  Saml.setup do |config|
52
- config.entity_id = "my:very:original:entityid"
53
- config.provider_store = Saml::ProviderStore::File.new("config/metadata", "config/ssl/key.pem")
54
- # config.provider_store = SamlProvider
52
+ config.register_store :file, Saml::ProviderStore::File.new("config/metadata", "config/ssl/key.pem"), default: true
55
53
  end
56
54
 
57
55
  By default this will use a SamlProvider model that uses the filestore, if you want a database driven model comment out the #provider_store function in the initializer and make a model that defines #find_by_entity_id:
@@ -66,6 +64,9 @@ By default this will use a SamlProvider model that uses the filestore, if you wa
66
64
 
67
65
  Now you can make a SAML controller in app/controllers/saml_controller.rb:
68
66
  class SamlController < ApplicationController
67
+ extend Saml::Rails::ControllerHelper
68
+ current_provider "entity_id"
69
+
69
70
  def request_authentication
70
71
  provider = Saml.provider("my:very:original:entityid")
71
72
  destination = provider.single_sign_on_service_url(Saml::ProtocolBindings::HTTP_POST)
data/lib/saml.rb CHANGED
@@ -4,25 +4,39 @@ require 'saml/base'
4
4
  require 'saml/xml_helpers'
5
5
  require 'saml/encoding'
6
6
  require 'saml/util'
7
+ require 'xmlenc'
7
8
  require 'xmldsig'
8
9
  require 'httpi'
9
10
 
10
11
  module Saml
11
12
  MD_NAMESPACE = 'urn:oasis:names:tc:SAML:2.0:metadata'
13
+ MD_ATTR_NAMESPACE = 'urn:oasis:names:tc:SAML:metadata:attribute'
12
14
  SAML_NAMESPACE = 'urn:oasis:names:tc:SAML:2.0:assertion'
13
15
  SAMLP_NAMESPACE = 'urn:oasis:names:tc:SAML:2.0:protocol'
14
16
  XML_DSIG_NAMESPACE = 'http://www.w3.org/2000/09/xmldsig#'
15
17
  SAML_VERSION = '2.0'
16
18
 
17
19
  module Errors
18
- class SamlError < StandardError;
20
+ class SamlError < StandardError
19
21
  end
20
-
21
- class SignatureInvalid < SamlError;
22
+ class SignatureInvalid < SamlError
23
+ end
24
+ class InvalidProvider < SamlError
22
25
  end
23
- class InvalidProvider < SamlError;
26
+ class UnparseableMessage < SamlError
24
27
  end
25
- class UnparseableMessage < SamlError;
28
+ class InvalidStore < SamlError
29
+ def initialize(store = '')
30
+ @store = store
31
+ end
32
+
33
+ def message
34
+ if @store.nil? || @store == ''
35
+ 'Store cannot be blank'
36
+ else
37
+ "Store #{@store} not registered"
38
+ end
39
+ end
26
40
  end
27
41
  end
28
42
 
@@ -52,11 +66,15 @@ module Saml
52
66
  end
53
67
 
54
68
  module ClassRefs
55
- PASSWORD_PROTECTED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
56
- MOBILE_TWO_FACTOR_CONTRACT = 'urn:oasis:names:tc:SAML:2.0:ac:classes:MobileTwoFactorContract'
57
- MOBILE_SMARTCARD_PKI = 'urn:oasis:names:tc:SAML:2.0:ac:classes:SmartcardPKI'
58
-
59
- ALL_CLASS_REFS = [PASSWORD_PROTECTED,
69
+ UNSPECIFIED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified'
70
+ PASSWORD_PROTECTED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
71
+ MOBILE_TWO_FACTOR_UNREGISTERED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:MobileTwoFactorUnregistered'
72
+ MOBILE_TWO_FACTOR_CONTRACT = 'urn:oasis:names:tc:SAML:2.0:ac:classes:MobileTwoFactorContract'
73
+ MOBILE_SMARTCARD_PKI = 'urn:oasis:names:tc:SAML:2.0:ac:classes:SmartcardPKI'
74
+
75
+ ALL_CLASS_REFS = [UNSPECIFIED,
76
+ PASSWORD_PROTECTED,
77
+ MOBILE_TWO_FACTOR_UNREGISTERED,
60
78
  MOBILE_TWO_FACTOR_CONTRACT,
61
79
  MOBILE_SMARTCARD_PKI]
62
80
  ORDERED_CLASS_REFS = ALL_CLASS_REFS
@@ -68,10 +86,14 @@ module Saml
68
86
  require 'saml/complex_types/endpoint_type'
69
87
  require 'saml/complex_types/indexed_endpoint_type'
70
88
  require 'saml/complex_types/sso_descriptor_type'
89
+ require 'saml/complex_types/attribute_type'
90
+ require 'saml/complex_types/localized_name_type'
91
+ require 'saml/complex_types/statement_abstract_type'
71
92
  end
72
93
 
73
94
  module Elements
74
95
  require 'saml/elements/signature'
96
+ require 'saml/elements/authenticating_authority'
75
97
  require 'saml/elements/subject_locality'
76
98
  require 'saml/elements/authn_context'
77
99
  require 'saml/elements/audience_restriction'
@@ -80,14 +102,25 @@ module Saml
80
102
  require 'saml/elements/status'
81
103
  require 'saml/elements/subject_confirmation_data'
82
104
  require 'saml/elements/subject_confirmation'
105
+ require 'saml/elements/encrypted_attribute'
83
106
  require 'saml/elements/attribute'
84
107
  require 'saml/elements/attribute_statement'
108
+ require 'saml/elements/entity_attributes'
109
+ require 'saml/elements/md_extensions'
110
+ require 'saml/elements/samlp_extensions'
111
+ require 'saml/elements/service_name'
112
+ require 'saml/elements/service_description'
113
+ require 'saml/elements/requested_attribute'
114
+ require 'saml/elements/attribute_consuming_service'
85
115
  require 'saml/elements/name_id'
86
116
  require 'saml/elements/subject'
87
117
  require 'saml/elements/conditions'
88
118
  require 'saml/elements/authn_statement'
89
119
  require 'saml/elements/requested_authn_context'
90
120
  require 'saml/elements/key_descriptor'
121
+ require 'saml/elements/organization_name'
122
+ require 'saml/elements/organization_display_name'
123
+ require 'saml/elements/organization_url'
91
124
  require 'saml/elements/organization'
92
125
  require 'saml/elements/contact_person'
93
126
  require 'saml/elements/idp_sso_descriptor'
@@ -96,6 +129,10 @@ module Saml
96
129
  require 'saml/elements/entities_descriptor'
97
130
  end
98
131
 
132
+ module Rails
133
+ require 'saml/rails/controller_helper'
134
+ end
135
+
99
136
  require 'saml/assertion'
100
137
  require 'saml/authn_request'
101
138
  require 'saml/artifact'
@@ -105,6 +142,8 @@ module Saml
105
142
  require 'saml/logout_request'
106
143
  require 'saml/logout_response'
107
144
  require 'saml/provider'
145
+ require 'saml/basic_provider'
146
+ require 'saml/null_provider'
108
147
 
109
148
  module ProviderStores
110
149
  require 'saml/provider_stores/file'
@@ -117,6 +156,25 @@ module Saml
117
156
  SOAP = 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP'
118
157
  end
119
158
 
159
+ def self.current_provider
160
+ Thread.current['saml_current_provider'] || NullProvider.new
161
+ end
162
+
163
+ def self.current_provider=(provider)
164
+ Thread.current['saml_current_provider'] = provider
165
+ end
166
+
167
+ def self.current_store
168
+ store_name = Thread.current['saml_current_store']
169
+ Saml::Config.registered_stores[store_name] ||
170
+ Saml::Config.registered_stores[Saml::Config.default_store] ||
171
+ raise(Errors::InvalidStore.new(store_name))
172
+ end
173
+
174
+ def self.current_store=(store_name)
175
+ Thread.current['saml_current_store'] = store_name
176
+ end
177
+
120
178
  def self.setup
121
179
  yield Saml::Config
122
180
  end
@@ -126,7 +184,11 @@ module Saml
126
184
  end
127
185
 
128
186
  def self.provider(entity_id)
129
- Saml::Config.provider_store.find_by_entity_id(entity_id) || raise(Saml::Errors::InvalidProvider.new)
187
+ if current_provider.entity_id == entity_id
188
+ current_provider
189
+ else
190
+ current_store.find_by_entity_id(entity_id) || raise(Saml::Errors::InvalidProvider.new)
191
+ end
130
192
  end
131
193
 
132
194
  def self.parse_message(message, type)
data/lib/saml/artifact.rb CHANGED
@@ -14,7 +14,7 @@ module Saml
14
14
  if artifact
15
15
  @artifact = artifact
16
16
  else
17
- source_id = ::Digest::SHA1.digest(Saml::Config.entity_id)
17
+ source_id = ::Digest::SHA1.digest(Saml.current_provider.entity_id)
18
18
  message_handle = ::SecureRandom.random_bytes(20)
19
19
  @type_code = TYPE_CODE
20
20
  @endpoint_index = END_POINT_INDEX
@@ -6,5 +6,12 @@ module Saml
6
6
  has_one :artifact, Saml::Artifact
7
7
 
8
8
  validates :artifact, :presence => true
9
+
10
+ def initialize(*args)
11
+ options = args.extract_options!
12
+ artifact = options.delete(:artifact)
13
+ @artifact = artifact.is_a?(Saml::Artifact) ? artifact : Saml::Artifact.new(artifact)
14
+ super(*(args << options))
15
+ end
9
16
  end
10
17
  end
@@ -18,6 +18,7 @@ module Saml
18
18
  has_one :signature, Saml::Elements::Signature
19
19
  has_one :subject, Saml::Elements::Subject
20
20
  has_one :conditions, Saml::Elements::Conditions
21
+ has_many :statements, Saml::ComplexTypes::StatementAbstractType
21
22
  has_many :authn_statement, Saml::Elements::AuthnStatement
22
23
  has_one :attribute_statement, Saml::Elements::AttributeStatement
23
24
 
@@ -40,7 +41,7 @@ module Saml
40
41
  super(*(args << options))
41
42
  @_id ||= Saml.generate_id
42
43
  @issue_instant ||= Time.now
43
- @issuer ||= Saml::Config.entity_id
44
+ @issuer ||= Saml.current_provider.entity_id
44
45
  @version ||= Saml::SAML_VERSION
45
46
  end
46
47
 
@@ -4,6 +4,7 @@ module Saml
4
4
 
5
5
  tag 'AuthnRequest'
6
6
  attribute :force_authn, Boolean, :tag => "ForceAuthn"
7
+ attribute :is_passive, Boolean, :tag => "IsPassive"
7
8
  attribute :assertion_consumer_service_index, Integer, :tag => "AssertionConsumerServiceIndex"
8
9
  attribute :assertion_consumer_service_url, String, :tag => "AssertionConsumerServiceURL"
9
10
  attribute :attribute_consuming_service_index, Integer, :tag => "AttributeConsumingServiceIndex"
@@ -0,0 +1,12 @@
1
+ module Saml
2
+ class BasicProvider
3
+ include Provider
4
+ attr_accessor :entity_descriptor, :private_key, :type
5
+
6
+ def initialize(entity_descriptor, private_key, type)
7
+ @entity_descriptor = entity_descriptor
8
+ @private_key = private_key
9
+ @type = type
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,24 @@
1
+ module Saml
2
+ module ComplexTypes
3
+ module AttributeType
4
+ extend ActiveSupport::Concern
5
+ include Saml::Base
6
+
7
+ included do
8
+ register_namespace "saml", Saml::SAML_NAMESPACE
9
+
10
+ attribute :name, String, :tag => 'Name'
11
+ attribute :format, String, tag: 'NameFormat'
12
+ attribute :friendly_name, String, tag: 'FriendlyName'
13
+ element :attribute_value, String, :namespace => 'saml', :tag => "AttributeValue"
14
+
15
+ validates :name, :presence => true
16
+ end
17
+
18
+ def initialize(*args)
19
+ options = args.extract_options!
20
+ super(*(args << options))
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,14 @@
1
+ module Saml
2
+ module ComplexTypes
3
+ module LocalizedNameType
4
+ extend ActiveSupport::Concern
5
+ include Saml::Base
6
+
7
+ included do
8
+ attribute :language, String, :tag => 'xml:lang'
9
+
10
+ validates :language, :presence => true
11
+ end
12
+ end
13
+ end
14
+ end
@@ -14,12 +14,14 @@ module Saml
14
14
 
15
15
  attribute :_id, String, :tag => 'ID'
16
16
  attribute :version, String, :tag => "Version"
17
- attribute :issue_instant, Time, :tag => "IssueInstant", :on_save => lambda { |val| val.utc.xmlschema }
17
+ attribute :issue_instant, Time, :tag => "IssueInstant", :on_save => lambda { |val| val.utc.xmlschema if val.present? }
18
+ attribute :consent, String, :tag => "Consent"
18
19
 
19
20
  attribute :destination, String, :tag => "Destination"
20
21
  element :issuer, String, :namespace => 'saml', :tag => "Issuer"
21
22
 
22
- has_one :signature, Saml::Elements::Signature, :tag => "Signature"
23
+ has_one :signature, Saml::Elements::Signature
24
+ has_one :extensions, Saml::Elements::SAMLPExtensions
23
25
 
24
26
  validates :_id, :version, :issue_instant, :presence => true
25
27
 
@@ -31,16 +33,10 @@ module Saml
31
33
  super(*args)
32
34
  @_id ||= Saml.generate_id
33
35
  @issue_instant ||= Time.now
34
- @issuer ||= Saml::Config.entity_id
36
+ @issuer ||= Saml.current_provider.entity_id
35
37
  @version ||= Saml::SAML_VERSION
36
38
  end
37
39
 
38
- def add_signature
39
- self.signature = Saml::Elements::Signature.new(uri: "##{self._id}")
40
- x509certificate = OpenSSL::X509::Certificate.new(provider.certificate) rescue nil
41
- self.signature.key_info = Saml::Elements::KeyDescriptor::KeyInfo.new(x509certificate.to_pem) if x509certificate
42
- end
43
-
44
40
  # @return [Saml::Provider]
45
41
  def provider
46
42
  Saml.provider(issuer)
@@ -43,6 +43,22 @@ module Saml
43
43
  @artifact_resolution_services ||= []
44
44
  @protocol_support_enumeration ||= PROTOCOL_SUPPORT_ENUMERATION
45
45
  end
46
+
47
+ def find_key_descriptor(key_name, use)
48
+ key_descriptors_by_use = find_key_descriptors_by_use(use)
49
+
50
+ if key_name.present?
51
+ key_descriptors_by_use.find { |key| key.key_info.key_name == key_name }
52
+ else
53
+ key_descriptors_by_use.first
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def find_key_descriptors_by_use(use)
60
+ key_descriptors.select { |key| key.use == use || key.use == "" }
61
+ end
46
62
  end
47
63
  end
48
64
  end
@@ -0,0 +1,34 @@
1
+ module Saml
2
+ module ComplexTypes
3
+ class StatementAbstractType
4
+ include HappyMapper
5
+
6
+ register_namespace 'xsi', 'http://www.w3.org/2001/XMLSchema-instance'
7
+
8
+ tag 'Statement'
9
+ namespace 'saml'
10
+
11
+ attribute :type, String, tag: 'xsi:type'
12
+
13
+ def self.register_type(type, klass)
14
+ types[type] = klass
15
+ end
16
+
17
+ def self.types
18
+ @types ||= {}
19
+ end
20
+
21
+ # TODO: handle multiple statements with different types
22
+ def self.parse(xml, options = {})
23
+ statements = Array(super)
24
+ statements.collect do |statement|
25
+ if (type = types[statement.type])
26
+ type.parse(xml, options.merge(single: true))
27
+ else
28
+ statement
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
data/lib/saml/config.rb CHANGED
@@ -3,40 +3,9 @@ module Saml
3
3
  mattr_accessor :provider_type
4
4
  @@provider_type = "service_provider"
5
5
 
6
- mattr_accessor :provider_store
7
- @@provider_store = Saml::ProviderStores::File.new
8
-
9
- mattr_accessor :entity_id
10
- @@entity_id = 'SamlEntity'
11
-
12
- mattr_accessor :authn_context_levels
13
- @@authn_context_levels = {}
14
-
15
- mattr_accessor :artifact_ttl
16
- @@artifact_ttl = 15
17
-
18
- mattr_accessor :private_key
19
- @@private_key = 'PRIVATE_KEY'
20
-
21
- mattr_accessor :private_key_file
22
- @@private_key_file = 'PRIVATE_KEY_FILE'
23
-
24
6
  mattr_accessor :max_issue_instant_offset
25
7
  @@max_issue_instant_offset = 2
26
8
 
27
- mattr_accessor :absolute_timeout
28
- @@absolute_timeout = 8*60
29
-
30
- mattr_accessor :graceperiod_timeout
31
- @@graceperiod_timeout = 15
32
-
33
- mattr_accessor :session_timeout
34
- @@session_timeout = 15
35
-
36
- # SSL
37
- mattr_accessor :ssl_private_key
38
- @@ssl_private_key = 'SSL_PRIVATE_KEY'
39
-
40
9
  mattr_accessor :ssl_private_key_file
41
10
  @@ssl_private_key_file = 'SSL_PRIVATE_KEY_FILE'
42
11
 
@@ -45,5 +14,18 @@ module Saml
45
14
 
46
15
  mattr_accessor :ssl_certificate_file
47
16
  @@ssl_certificate_file = 'SSL_CERTIFICATE_FILE'
17
+
18
+ mattr_accessor :registered_stores
19
+ @@registered_stores = {}
20
+
21
+ mattr_accessor :default_store
22
+
23
+ def register_store(name, store, options = {})
24
+ registered_stores[name] = store
25
+ self.default_store = name if options[:default]
26
+ end
27
+
28
+ module_function :register_store
29
+
48
30
  end
49
31
  end
@@ -1,24 +1,12 @@
1
1
  module Saml
2
2
  module Elements
3
3
  class Attribute
4
+ include Saml::ComplexTypes::AttributeType
4
5
  include Saml::Base
5
6
 
6
7
  tag "Attribute"
7
8
  register_namespace 'saml', Saml::SAML_NAMESPACE
8
9
  namespace 'saml'
9
-
10
- attribute :name, String, :tag => 'Name'
11
- attribute :format, String, tag: 'NameFormat'
12
- attribute :friendly_name, String, tag: 'FriendlyName'
13
- element :attribute_value, String, :namespace => 'saml', :tag => "AttributeValue"
14
-
15
- validates :name, :presence => true
16
-
17
- def initialize(*args)
18
- options = args.extract_options!
19
- super(*(args << options))
20
- end
21
-
22
10
  end
23
11
  end
24
12
  end
@@ -0,0 +1,20 @@
1
+ module Saml
2
+ module Elements
3
+ class AttributeConsumingService
4
+ include Saml::Base
5
+
6
+ tag "AttributeConsumingService"
7
+ register_namespace "md", Saml::MD_NAMESPACE
8
+ namespace "md"
9
+
10
+ attribute :index, Integer, :tag => "index"
11
+ attribute :is_default, HappyMapper::Boolean, :tag => "isDefault"
12
+
13
+ has_many :service_names, ServiceName
14
+ has_many :service_descriptions, ServiceDescription
15
+ has_many :requested_attributes, RequestedAttribute
16
+
17
+ validates :index, :service_names, :requested_attributes, :presence => true
18
+ end
19
+ end
20
+ end
@@ -8,11 +8,7 @@ module Saml
8
8
  namespace 'saml'
9
9
 
10
10
  has_many :attribute, Saml::Elements::Attribute
11
-
12
- def initialize(*args)
13
- options = args.extract_options!
14
- super(*(args << options))
15
- end
11
+ has_many :encrypted_attributes, Saml::Elements::EncryptedAttribute
16
12
 
17
13
  def fetch_attribute(key)
18
14
  attribute = self.attribute.find do |attr|
@@ -20,7 +16,6 @@ module Saml
20
16
  end
21
17
  attribute.attribute_value if attribute
22
18
  end
23
-
24
19
  end
25
20
  end
26
21
  end
@@ -0,0 +1,14 @@
1
+ module Saml
2
+ module Elements
3
+ class AuthenticatingAuthority
4
+ include Saml::Base
5
+
6
+ tag "AuthenticatingAuthority"
7
+
8
+ register_namespace "saml", ::Saml::SAML_NAMESPACE
9
+ namespace "saml"
10
+
11
+ content :value, String
12
+ end
13
+ end
14
+ end
@@ -7,6 +7,8 @@ module Saml
7
7
  namespace 'saml'
8
8
  element :authn_context_class_ref, String, :tag => "AuthnContextClassRef"
9
9
 
10
+ has_many :authenticating_authorities, ::Saml::Elements::AuthenticatingAuthority
11
+
10
12
  validates :authn_context_class_ref, :inclusion => ClassRefs::ALL_CLASS_REFS + [nil]
11
13
  end
12
14
  end
@@ -16,7 +16,7 @@ module Saml
16
16
  ALL = [TECHNICAL, SUPPORT, ADMINISTRATIVE, BILLING, OTHER]
17
17
  end
18
18
 
19
- attribute :contact_type, String, :tag => "ContactType"
19
+ attribute :contact_type, String, :tag => "contactType"
20
20
 
21
21
  element :company, String, :tag => "Company"
22
22
  element :given_name, String, :tag => "GivenName"
@@ -0,0 +1,18 @@
1
+ module Saml
2
+ module Elements
3
+ class EncryptedAttribute
4
+ include Saml::Base
5
+
6
+ tag "EncryptedAttribute"
7
+
8
+ register_namespace "saml", Saml::SAML_NAMESPACE
9
+ namespace "saml"
10
+
11
+ element :encrypted_data, Xmlenc::Builder::EncryptedData
12
+
13
+ has_many :encrypted_keys, Xmlenc::Builder::EncryptedKey, xpath: "./"
14
+
15
+ validates :encrypted_data, presence: true
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ module Saml
2
+ module Elements
3
+ class EntityAttributes
4
+ include Saml::Base
5
+ include Saml::XMLHelpers
6
+
7
+ register_namespace "mdattr", Saml::MD_ATTR_NAMESPACE
8
+
9
+ tag "EntityAttributes"
10
+ namespace "mdattr"
11
+
12
+ has_many :attributes, Saml::Elements::Attribute
13
+ end
14
+ end
15
+ end
@@ -4,7 +4,6 @@ module Saml
4
4
  include Saml::Base
5
5
  include Saml::XMLHelpers
6
6
 
7
-
8
7
  register_namespace 'md', Saml::MD_NAMESPACE
9
8
 
10
9
  tag 'EntityDescriptor'
@@ -18,12 +17,14 @@ module Saml
18
17
 
19
18
  has_one :signature, Saml::Elements::Signature
20
19
 
21
- has_one :organization, Saml::Elements::Organization
22
- has_many :contact_persons, Saml::Elements::ContactPerson
20
+ has_one :extensions, Saml::Elements::MDExtensions
23
21
 
24
22
  has_one :idp_sso_descriptor, Saml::Elements::IDPSSODescriptor
25
23
  has_one :sp_sso_descriptor, Saml::Elements::SPSSODescriptor
26
24
 
25
+ has_one :organization, Saml::Elements::Organization
26
+ has_many :contact_persons, Saml::Elements::ContactPerson
27
+
27
28
  validates :entity_id, :presence => true
28
29
 
29
30
  def initialize(*args)
@@ -10,6 +10,8 @@ module Saml
10
10
 
11
11
  tag 'IDPSSODescriptor'
12
12
 
13
+ attribute :want_authn_requests_signed, HappyMapper::Boolean, :tag => "WantAuthnRequestsSigned", :default => false
14
+
13
15
  has_many :single_sign_on_services, SingleSignOnService
14
16
 
15
17
  validates :single_sign_on_services, :presence => true
@@ -0,0 +1,13 @@
1
+ module Saml
2
+ module Elements
3
+ class MDExtensions
4
+ include Saml::Base
5
+ include Saml::XMLHelpers
6
+
7
+ tag "Extensions"
8
+ namespace "md"
9
+
10
+ has_one :entity_attributes, Saml::Elements::EntityAttributes
11
+ end
12
+ end
13
+ end
@@ -8,6 +8,8 @@ module Saml
8
8
  namespace 'saml'
9
9
 
10
10
  attribute :format, String, :tag => "Format"
11
+ attribute :name_qualifier, String, :tag => "NameQualifier"
12
+
11
13
  content :value, String
12
14
  end
13
15
  end
@@ -6,9 +6,9 @@ module Saml
6
6
  tag 'Organization'
7
7
  namespace 'md'
8
8
 
9
- has_many :organization_names, String, :tag => "OrganizationName"
10
- has_many :organization_display_names, String, :tag => "OrganizationDisplayName"
11
- has_many :organization_urls, String, :tag => "OrganizationURL"
9
+ has_many :organization_names, Saml::Elements::OrganizationName
10
+ has_many :organization_display_names, Saml::Elements::OrganizationDisplayName
11
+ has_many :organization_urls, Saml::Elements::OrganizationUrl
12
12
 
13
13
  validates :organization_names, :organization_display_names, :organization_urls, :presence => true
14
14
  end
@@ -0,0 +1,13 @@
1
+ module Saml
2
+ module Elements
3
+ class OrganizationDisplayName
4
+ include Saml::ComplexTypes::LocalizedNameType
5
+ include Saml::Base
6
+
7
+ tag 'OrganizationDisplayName'
8
+ namespace 'md'
9
+
10
+ content :value, String
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Saml
2
+ module Elements
3
+ class OrganizationName
4
+ include Saml::ComplexTypes::LocalizedNameType
5
+ include Saml::Base
6
+
7
+ tag 'OrganizationName'
8
+ namespace 'md'
9
+
10
+ content :value, String
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Saml
2
+ module Elements
3
+ class OrganizationUrl
4
+ include Saml::ComplexTypes::LocalizedNameType
5
+ include Saml::Base
6
+
7
+ tag 'OrganizationURL'
8
+ namespace 'md'
9
+
10
+ content :value, String
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module Saml
2
+ module Elements
3
+ class RequestedAttribute
4
+ include Saml::ComplexTypes::AttributeType
5
+ include Saml::Base
6
+
7
+ tag "RequestedAttribute"
8
+ register_namespace "md", Saml::MD_NAMESPACE
9
+ namespace "md"
10
+
11
+ attribute :is_required, HappyMapper::Boolean, :tag => "isRequired"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module Saml
2
+ module Elements
3
+ class SAMLPExtensions
4
+ include Saml::Base
5
+ include Saml::XMLHelpers
6
+
7
+ tag "Extensions"
8
+ namespace "samlp"
9
+
10
+ has_many :attributes, Saml::Elements::Attribute
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Saml
2
+ module Elements
3
+ class ServiceDescription
4
+ include Saml::Base
5
+
6
+ tag "ServiceDescription"
7
+ register_namespace "md", Saml::MD_NAMESPACE
8
+ namespace "md"
9
+
10
+ content :value, String
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Saml
2
+ module Elements
3
+ class ServiceName
4
+ include Saml::Base
5
+
6
+ tag "ServiceName"
7
+ register_namespace "md", Saml::MD_NAMESPACE
8
+ namespace "md"
9
+
10
+ content :value, String
11
+ end
12
+ end
13
+ end
@@ -21,13 +21,16 @@ module Saml
21
21
  element :signature_value, String, :tag => "SignatureValue", :state_when_nil => true
22
22
  has_one :key_info, KeyInfo
23
23
 
24
-
25
24
  def initialize(*args)
26
25
  super(*args)
27
26
  options = args.extract_options!
28
27
  @signed_info ||= SignedInfo.new(:uri => options.delete(:uri), :digest_value => options.delete(:digest_value))
29
28
  @key_info ||= KeyInfo.new
30
29
  end
30
+
31
+ def key_name
32
+ @key_info.try(:key_name)
33
+ end
31
34
  end
32
35
  end
33
36
  end
@@ -14,6 +14,7 @@ module Saml
14
14
  attribute :want_assertions_signed, Boolean, :tag => "WantAssertionsSigned", :default => false
15
15
 
16
16
  has_many :assertion_consumer_services, AssertionConsumerService
17
+ has_many :attribute_consuming_services, Saml::Elements::AttributeConsumingService
17
18
 
18
19
  validates :assertion_consumer_services, :presence => true
19
20
 
@@ -22,6 +23,13 @@ module Saml
22
23
  self.assertion_consumer_services ||= []
23
24
  end
24
25
 
26
+ def add_assertion_consumer_service(binding, location, index, default = false)
27
+ assertion_consumer_services << AssertionConsumerService.new(binding: binding,
28
+ location: location,
29
+ index: index,
30
+ is_default: default)
31
+ end
32
+
25
33
  end
26
34
  end
27
35
  end
@@ -0,0 +1,9 @@
1
+ module Saml
2
+ class NullProvider
3
+ include Provider
4
+
5
+ def entity_id
6
+ nil
7
+ end
8
+ end
9
+ end
data/lib/saml/provider.rb CHANGED
@@ -3,11 +3,23 @@ module Saml
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  def assertion_consumer_service_url(index = nil)
6
- find_indexed_service(descriptor.assertion_consumer_services, index)
6
+ find_indexed_service_url(descriptor.assertion_consumer_services, index)
7
7
  end
8
8
 
9
9
  def artifact_resolution_service_url(index = nil)
10
- find_indexed_service(descriptor.artifact_resolution_services, index)
10
+ find_indexed_service_url(descriptor.artifact_resolution_services, index)
11
+ end
12
+
13
+ def attribute_consuming_service(index = nil)
14
+ find_indexed_service(descriptor.attribute_consuming_services, index)
15
+ end
16
+
17
+ def assertion_consumer_service_indices
18
+ if descriptor.assertion_consumer_services.present?
19
+ descriptor.assertion_consumer_services.map(&:index)
20
+ else
21
+ []
22
+ end
11
23
  end
12
24
 
13
25
  def entity_descriptor
@@ -18,8 +30,8 @@ module Saml
18
30
  entity_descriptor.entity_id
19
31
  end
20
32
 
21
- def certificate(use = "signing")
22
- key_descriptor = descriptor.key_descriptors.find { |key| key.use == use || key.use == "" }
33
+ def certificate(key_name, use = "signing")
34
+ key_descriptor = descriptor.find_key_descriptor(key_name, use)
23
35
  key_descriptor.certificate if key_descriptor
24
36
  end
25
37
 
@@ -43,8 +55,8 @@ module Saml
43
55
  descriptor.is_a?(Saml::Elements::SPSSODescriptor) ? "service_provider" : "identity_provider"
44
56
  end
45
57
 
46
- def verify(signature_algorithm, signature, data)
47
- certificate.public_key.verify(digest_method(signature_algorithm).new, signature, data) rescue nil
58
+ def verify(signature_algorithm, signature, data, key_name = nil)
59
+ certificate(key_name).public_key.verify(digest_method(signature_algorithm).new, signature, data) rescue nil
48
60
  end
49
61
 
50
62
  def authn_requests_signed?
@@ -68,13 +80,17 @@ module Saml
68
80
  entity_descriptor.sp_sso_descriptor || entity_descriptor.idp_sso_descriptor
69
81
  end
70
82
 
83
+ def find_indexed_service_url(service_list, index)
84
+ service = find_indexed_service(service_list, index)
85
+ service.location if service
86
+ end
87
+
71
88
  def find_indexed_service(service_list, index)
72
- service = if index
89
+ if index
73
90
  service_list.find { |service| service.index == index }
74
91
  else
75
92
  service_list.find { |service| service.is_default }
76
93
  end
77
- service.location if service
78
94
  end
79
95
 
80
96
  def find_binding_service(service_list, binding)
@@ -1,17 +1,6 @@
1
1
  module Saml
2
2
  module ProviderStores
3
3
  class File
4
- class Provider
5
- include Saml::Provider
6
- attr_accessor :entity_descriptor, :private_key, :type
7
-
8
- def initialize(entity_descriptor, private_key, type)
9
- @entity_descriptor = entity_descriptor
10
- @private_key = private_key
11
- @type = type
12
- end
13
- end
14
-
15
4
  attr_accessor :providers
16
5
 
17
6
  def initialize(metadata_dir = "config/metadata", key_file = "config/ssl/key.pem")
@@ -21,7 +10,7 @@ module Saml
21
10
  private_key = OpenSSL::PKey::RSA.new(::File.read(key_file))
22
11
  type = entity_descriptor.sp_sso_descriptor.present? ? "service_provider" : "identity_provider"
23
12
 
24
- self.providers << Provider.new(entity_descriptor, private_key, type)
13
+ self.providers << BasicProvider.new(entity_descriptor, private_key, type)
25
14
  end
26
15
  end
27
16
 
@@ -30,4 +19,4 @@ module Saml
30
19
  end
31
20
  end
32
21
  end
33
- end
22
+ end
@@ -0,0 +1,30 @@
1
+ module Saml
2
+ module Rails
3
+ module ControllerHelper
4
+ def current_provider(entity_id_or_method = nil, &block)
5
+ if block_given?
6
+ before_action &block
7
+ else
8
+ case entity_id_or_method
9
+ when Symbol
10
+ before_action { Saml.current_provider = send(entity_id_or_method) }
11
+ else
12
+ before_action { Saml.current_provider = Saml.provider("#{entity_id_or_method}") }
13
+ end
14
+ end
15
+ end
16
+
17
+ def current_store(store_or_symbol = nil)
18
+ case store_or_symbol
19
+ when Symbol
20
+ before_action { Saml.current_store = store_or_symbol }
21
+ else
22
+ before_action do
23
+ Saml::Config.register_store klass.name.underscore, klass_or_symbol
24
+ Saml.current_store = klass.name.underscore
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
data/lib/saml/response.rb CHANGED
@@ -3,7 +3,6 @@ module Saml
3
3
  include Saml::ComplexTypes::StatusResponseType
4
4
 
5
5
  tag "Response"
6
- has_one :assertion, Saml::Assertion, :tag => "Assertion"
7
6
  has_many :assertions, Saml::Assertion, :tag => "Assertion"
8
7
 
9
8
  def authn_failed?
@@ -17,5 +16,13 @@ module Saml
17
16
  def no_authn_context?
18
17
  !success? && status.status_code.no_authn_context?
19
18
  end
19
+
20
+ def assertion
21
+ assertions.first
22
+ end
23
+
24
+ def assertion=(assertion)
25
+ (self.assertions ||= []) << assertion
26
+ end
20
27
  end
21
28
  end
data/lib/saml/util.rb CHANGED
@@ -26,12 +26,16 @@ module Saml
26
26
  HTTPI.post request
27
27
  end
28
28
 
29
- def sign_xml(message, format = :xml)
29
+ def sign_xml(message, format = :xml, &block)
30
30
  message.add_signature
31
31
 
32
32
  document = Xmldsig::SignedDocument.new(message.send("to_#{format}"))
33
- document.sign do |data, signature_algorithm|
34
- message.provider.sign(signature_algorithm, data)
33
+ if block_given?
34
+ document.sign(&block)
35
+ else
36
+ document.sign do |data, signature_algorithm|
37
+ message.provider.sign(signature_algorithm, data)
38
+ end
35
39
  end
36
40
  end
37
41
 
@@ -39,12 +43,14 @@ module Saml
39
43
  document = Xmldsig::SignedDocument.new(raw_body)
40
44
 
41
45
  signature_valid = document.validate do |signature, data, signature_algorithm|
42
- message.provider.verify(signature_algorithm, signature, data)
46
+ message.provider.verify(signature_algorithm, signature, data, message.signature.key_name)
43
47
  end
44
48
 
45
49
  raise Saml::Errors::SignatureInvalid.new unless signature_valid
46
50
 
47
- message.class.parse(document.signed_nodes.first.to_xml, single: true)
51
+ signed_node = document.signed_nodes.find { |node| node['ID'] == message._id }
52
+
53
+ message.class.parse(signed_node.to_xml, single: true)
48
54
  end
49
55
  end
50
56
  end
data/lib/saml/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Saml
2
- VERSION = "2.0.6"
2
+ VERSION = '2.1.0'
3
3
  end
@@ -2,6 +2,12 @@ module Saml
2
2
  module XMLHelpers
3
3
  extend ActiveSupport::Concern
4
4
 
5
+ def add_signature
6
+ self.signature = Saml::Elements::Signature.new(uri: "##{self._id}")
7
+ x509certificate = OpenSSL::X509::Certificate.new(provider.certificate) rescue nil
8
+ self.signature.key_info = Saml::Elements::KeyDescriptor::KeyInfo.new(x509certificate.to_pem) if x509certificate
9
+ end
10
+
5
11
  def to_xml(builder = nil, default_namespace = nil, instruct = true)
6
12
  write_xml = builder.nil? ? true : false
7
13
  builder ||= Nokogiri::XML::Builder.new
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: libsaml
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.6
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benoist Claassen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-14 00:00:00.000000000 Z
11
+ date: 2013-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ~>
67
67
  - !ruby/object:Gem::Version
68
68
  version: 0.2.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: xmlenc
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 0.1.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 0.1.0
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: curb
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -110,32 +124,48 @@ files:
110
124
  - lib/saml/assertion.rb
111
125
  - lib/saml/authn_request.rb
112
126
  - lib/saml/base.rb
127
+ - lib/saml/basic_provider.rb
113
128
  - lib/saml/bindings/http_artifact.rb
114
129
  - lib/saml/bindings/http_post.rb
115
130
  - lib/saml/bindings/http_redirect.rb
116
131
  - lib/saml/bindings/soap.rb
132
+ - lib/saml/complex_types/attribute_type.rb
117
133
  - lib/saml/complex_types/endpoint_type.rb
118
134
  - lib/saml/complex_types/indexed_endpoint_type.rb
135
+ - lib/saml/complex_types/localized_name_type.rb
119
136
  - lib/saml/complex_types/request_abstract_type.rb
120
137
  - lib/saml/complex_types/sso_descriptor_type.rb
138
+ - lib/saml/complex_types/statement_abstract_type.rb
121
139
  - lib/saml/complex_types/status_response_type.rb
122
140
  - lib/saml/config.rb
123
141
  - lib/saml/elements/attribute.rb
142
+ - lib/saml/elements/attribute_consuming_service.rb
124
143
  - lib/saml/elements/attribute_statement.rb
125
144
  - lib/saml/elements/audience_restriction.rb
145
+ - lib/saml/elements/authenticating_authority.rb
126
146
  - lib/saml/elements/authn_context.rb
127
147
  - lib/saml/elements/authn_statement.rb
128
148
  - lib/saml/elements/conditions.rb
129
149
  - lib/saml/elements/contact_person.rb
150
+ - lib/saml/elements/encrypted_attribute.rb
130
151
  - lib/saml/elements/entities_descriptor.rb
152
+ - lib/saml/elements/entity_attributes.rb
131
153
  - lib/saml/elements/entity_descriptor.rb
132
154
  - lib/saml/elements/idp_sso_descriptor.rb
133
155
  - lib/saml/elements/key_descriptor/key_info/x509_data.rb
134
156
  - lib/saml/elements/key_descriptor/key_info.rb
135
157
  - lib/saml/elements/key_descriptor.rb
158
+ - lib/saml/elements/md_extensions.rb
136
159
  - lib/saml/elements/name_id.rb
137
160
  - lib/saml/elements/organization.rb
161
+ - lib/saml/elements/organization_display_name.rb
162
+ - lib/saml/elements/organization_name.rb
163
+ - lib/saml/elements/organization_url.rb
164
+ - lib/saml/elements/requested_attribute.rb
138
165
  - lib/saml/elements/requested_authn_context.rb
166
+ - lib/saml/elements/samlp_extensions.rb
167
+ - lib/saml/elements/service_description.rb
168
+ - lib/saml/elements/service_name.rb
139
169
  - lib/saml/elements/signature/canonicalization_method.rb
140
170
  - lib/saml/elements/signature/digest_method.rb
141
171
  - lib/saml/elements/signature/inclusive_namespaces.rb
@@ -157,8 +187,10 @@ files:
157
187
  - lib/saml/encoding.rb
158
188
  - lib/saml/logout_request.rb
159
189
  - lib/saml/logout_response.rb
190
+ - lib/saml/null_provider.rb
160
191
  - lib/saml/provider.rb
161
192
  - lib/saml/provider_stores/file.rb
193
+ - lib/saml/rails/controller_helper.rb
162
194
  - lib/saml/response.rb
163
195
  - lib/saml/util.rb
164
196
  - lib/saml/version.rb