libsaml 2.0.6 → 2.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 (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