saml-kit 0.2.11 → 0.2.12

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: 3b17d6e67c0342733456fdee9e58558179676a4080d30eb2ad7915de9803dc9c
4
- data.tar.gz: 972b8a7c9bde4fe28b82e67f5bcaa9752df484c6c09e9b4eaa1131db1c088691
2
+ SHA1:
3
+ metadata.gz: 6a933c25128ff1220caca50012fa928dc9512d9a
4
+ data.tar.gz: 9460788814e60d65169adb762ee084aa71743555
5
5
  SHA512:
6
- metadata.gz: b88ee1a08f313d7d48ef33ec4d09b9ce620939b4bb81bb40865c78b941c1756fd69a6e2ad6440faa13260d5c619b08bcb435629d1974c6f9d9655a9006e0d1cd
7
- data.tar.gz: fc65fdf9ad747aaf5473535b05d3a29c91331a6c6208b0daaf6e5e4a97b06b33d0e93026eccfb13f5364974c907052ffb58812f6893d43584ba0acb7c830d5e4
6
+ metadata.gz: c9eb5d4474d9e4e4dbcb76fa37afc3e819863c00b239df34b0a504b0f008cb5fce0ac0e64211484a00eaf2a868073f9f8be8f61817440ea1ca914c35e241b02f
7
+ data.tar.gz: 23a961b54e7ad38e9c4fabd85550f4a0e8e5d64a87e2254e91cd70edee38dba14e0392e8ca9addb156c2ce2f4c6f3ba533f6b6323a6ea203d561c955892e43cb
data/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # Saml::Kit
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/saml/kit`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
3
+ Saml::Kit is a library with the purpose of creating and consuming SAML
4
+ documents. It supports the HTTP Post and HTTP Redirect bindings. It can
5
+ create Service Provider Metadata, Identity Provider Metadata,
6
+ AuthnRequest, Response, LogoutRequest, LogoutResponse documents.
7
+ It also supports generating signed and encrypted assertions.
6
8
 
7
9
  ## Installation
8
10
 
@@ -22,7 +24,219 @@ Or install it yourself as:
22
24
 
23
25
  ## Usage
24
26
 
25
- TODO: Write usage instructions here
27
+ To specify a global configuration: (useful for a rails application)
28
+
29
+ ```ruby
30
+ Saml::Kit.configure do |configuration|
31
+ configuration.issuer = ENV['ISSUER']
32
+ configuration.generate_key_pair_for(use: :signing)
33
+ configuration.generate_key_pair_for(use: :signing)
34
+ end
35
+ ```
36
+
37
+ ### Metadata
38
+
39
+ To generate metadata for an Identity Provider.
40
+
41
+ ```ruby
42
+ Saml::Kit::Metadata.build_xml do |builder|
43
+ builder.contact_email = 'hi@example.com'
44
+ builder.organization_name = "Acme, Inc"
45
+ builder.organization_url = 'https://www.example.com'
46
+ builder.build_identity_provider do |x|
47
+ x.add_single_sign_on_service('https://www.example.com/login', binding: :http_post)
48
+ x.add_single_sign_on_service('https://www.example.com/login', binding: :http_redirect)
49
+ x.add_single_logout_service('https://www.example.com/logout', binding: :http_post)
50
+ x.name_id_formats = [ Saml::Kit::Namespaces::EMAIL_ADDRESS ]
51
+ x.attributes << :id
52
+ x.attributes << :email
53
+ end
54
+ end
55
+ ```
56
+
57
+ Will produce something like:
58
+
59
+ ```xml
60
+ <?xml version="1.0" encoding="UTF-8"?>
61
+ <EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_efe0c000-8d0d-4406-96b8-61f649e004f6" entityID="">
62
+ <IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
63
+ <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://www.example.com/logout"/>
64
+ <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>
65
+ <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://www.example.com/login"/>
66
+ <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://www.example.com/login"/>
67
+ <saml:Attribute Name="id"/>
68
+ <saml:Attribute Name="email"/>
69
+ </IDPSSODescriptor>
70
+ <Organization>
71
+ <OrganizationName xml:lang="en">Acme, Inc</OrganizationName>
72
+ <OrganizationDisplayName xml:lang="en">Acme, Inc</OrganizationDisplayName>
73
+ <OrganizationURL xml:lang="en">https://www.example.com</OrganizationURL>
74
+ </Organization>
75
+ <ContactPerson contactType="technical">
76
+ <Company>mailto:hi@example.com</Company>
77
+ </ContactPerson>
78
+ </EntityDescriptor>
79
+ ```
80
+
81
+ To generate service provider metadata:
82
+
83
+ ```xml
84
+ metadata = Saml::Kit::Metadata.build do |builder|
85
+ builder.contact_email = 'hi@example.com'
86
+ builder.organization_name = "Acme, Inc"
87
+ builder.organization_url = 'https://www.example.com'
88
+ builder.build_service_provider do |x|
89
+ x.add_assertion_consumer_service('https://www.example.com/consume', binding: :http_post)
90
+ x.add_single_logout_service('https://www.example.com/logout', binding: :http_post)
91
+ end
92
+ end
93
+ puts metadata.to_xml(pretty: true)
94
+ ```
95
+
96
+ Will produce something like:
97
+
98
+ ```xml
99
+ <?xml version="1.0" encoding="UTF-8"?>
100
+ <EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_3ff5e4b3-4fce-4cc9-b278-6cb3a0a8cb10" entityID="">
101
+ <SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
102
+ <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://www.example.com/logout"/>
103
+ <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
104
+ <AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://www.example.com/consume" index="0" isDefault="true"/>
105
+ </SPSSODescriptor>
106
+ <Organization>
107
+ <OrganizationName xml:lang="en">Acme, Inc</OrganizationName>
108
+ <OrganizationDisplayName xml:lang="en">Acme, Inc</OrganizationDisplayName>
109
+ <OrganizationURL xml:lang="en">https://www.example.com</OrganizationURL>
110
+ </Organization>
111
+ <ContactPerson contactType="technical">
112
+ <Company>mailto:hi@example.com</Company>
113
+ </ContactPerson>
114
+ </EntityDescriptor>
115
+ ```
116
+
117
+ To produce Metadata with an IDPSSODescriptor and SPSSODescriptor.
118
+
119
+ ```ruby
120
+ metadata = Saml::Kit::Metadata.build do |builder|
121
+ builder.contact_email = 'hi@example.com'
122
+ builder.organization_name = "Acme, Inc"
123
+ builder.organization_url = 'https://www.example.com'
124
+ builder.build_identity_provider do |x|
125
+ x.add_single_sign_on_service('https://www.example.com/login', binding: :http_post)
126
+ x.add_single_sign_on_service('https://www.example.com/login', binding: :http_redirect)
127
+ x.add_single_logout_service('https://www.example.com/logout', binding: :http_post)
128
+ x.name_id_formats = [ Saml::Kit::Namespaces::EMAIL_ADDRESS ]
129
+ x.attributes << :id
130
+ x.attributes << :email
131
+ end
132
+ builder.build_service_provider do |x|
133
+ x.add_assertion_consumer_service('https://www.example.com/consume', binding: :http_post)
134
+ x.add_single_logout_service('https://www.example.com/logout', binding: :http_post)
135
+ end
136
+ end
137
+ puts metadata.to_xml(pretty: true)
138
+ ```
139
+
140
+ ```xml
141
+ <?xml version="1.0" encoding="UTF-8"?>
142
+ <EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_a29a3a9d-ad16-4839-8f5d-a59daed6f3ce" entityID="">
143
+ <IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
144
+ <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://www.example.com/logout"/>
145
+ <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>
146
+ <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://www.example.com/login"/>
147
+ <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://www.example.com/login"/>
148
+ <saml:Attribute Name="id"/>
149
+ <saml:Attribute Name="email"/>
150
+ </IDPSSODescriptor>
151
+ <SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
152
+ <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://www.example.com/logout"/>
153
+ <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
154
+ <AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://www.example.com/consume" index="0" isDefault="true"/>
155
+ </SPSSODescriptor>
156
+ <Organization>
157
+ <OrganizationName xml:lang="en">Acme, Inc</OrganizationName>
158
+ <OrganizationDisplayName xml:lang="en">Acme, Inc</OrganizationDisplayName>
159
+ <OrganizationURL xml:lang="en">https://www.example.com</OrganizationURL>
160
+ </Organization>
161
+ <ContactPerson contactType="technical">
162
+ <Company>mailto:hi@example.com</Company>
163
+ </ContactPerson>
164
+ </EntityDescriptor>
165
+ ```
166
+
167
+ ### AuthnRequest
168
+
169
+ To generate an Authentication Request choose the desired binding from
170
+ the metadata and use it to serialize a request.
171
+
172
+ ```ruby
173
+ idp = Saml::Kit::IdentityProviderMetadata.new(raw_xml)
174
+ url, saml_params = idp.login_request_for(binding: :http_post)
175
+ puts [url, saml_params].inspect
176
+ # ["https://www.example.com/login", {"SAMLRequest"=>"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbHA6QXV0aG5SZXF1ZXN0IHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfN2Y0YjkxZGMtNTMyNi00NjgzLTgyOWItYWViNzlkNjM0ZWYzIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNy0xMi0xOVQwNDo0ODoxMloiIERlc3RpbmF0aW9uPSJodHRwczovL3d3dy5leGFtcGxlLmNvbS9sb2dpbiI+PHNhbWw6SXNzdWVyLz48c2FtbHA6TmFtZUlEUG9saWN5IEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6cGVyc2lzdGVudCIvPjwvc2FtbHA6QXV0aG5SZXF1ZXN0Pg=="}]
177
+ ```
178
+
179
+ ### Response
180
+
181
+ To generate a Response you will need a request object and the desired binding
182
+ to serialize a response. You will also need to specify a user
183
+ object to create a response for.
184
+
185
+ ```ruby
186
+ binding = idp.single_sign_on_service_for(binding: :http_post)
187
+ raw_params = Hash[uri.query.split("&amp;").map { |x| x.split("=", 2) }].symbolize_keys
188
+ saml_request = binding.deserialize(raw_params)
189
+
190
+ url, saml_params = saml_request.response_for(user, binding: :http_post)
191
+ puts [url, saml_params].inspect
192
+ # ["https://www.example.com/consume", {"SAMLResponse"=>"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48UmVzcG9uc2UgSUQ9Il9hZjFiNTg5Ni0wN2MzLTQ2Y2QtYTA5ZC0xOTRmZGNkNWZiZmYiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDE3LTEyLTE5VDA1OjI5OjU0WiIgRGVzdGluYXRpb249Imh0dHBzOi8vd3d3LmV4YW1wbGUuY29tL2NvbnN1bWUiIENvbnNlbnQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjb25zZW50OnVuc3BlY2lmaWVkIiBJblJlc3BvbnNlVG89Il9mYzg5MjllOC0zY2ZkLTQ5YmQtOTgzNi0xNTRhZGYzOTEzZjYiIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiPjxJc3N1ZXIgeG1sbnM9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iLz48U3RhdHVzPjxTdGF0dXNDb2RlIFZhbHVlPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6c3RhdHVzOlN1Y2Nlc3MiLz48L1N0YXR1cz48QXNzZXJ0aW9uIElEPSJfYjg4OWNmNzEtYTFmNS00ZWUxLWEzZTctMGM4ZTU5ZDY3ZTJkIiBJc3N1ZUluc3RhbnQ9IjIwMTctMTItMTlUMDU6Mjk6NTRaIiBWZXJzaW9uPSIyLjAiIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj48SXNzdWVyLz48U3ViamVjdD48TmFtZUlEIEZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6cGVyc2lzdGVudCI+Yjk2ODE1MDA
193
+ ```
194
+
195
+ ### LogoutRequest
196
+
197
+ To create a logout request you will need to choose the desired binding
198
+ from the metadata then generate a request for a specific user.
199
+
200
+ ```ruby
201
+ class User
202
+ attr_reader :id, :email
203
+
204
+ def initialize(id:, email:)
205
+ @id = id
206
+ @email = email
207
+ end
208
+
209
+ def name_id_for(name_id_format)
210
+ Saml::Kit::Namespaces::PERSISTENT == name_id_format ? id : email
211
+ end
212
+
213
+ def assertion_attributes_for(request)
214
+ request.trusted? ? { access_token: SecureRandom.uuid } : {}
215
+ end
216
+ end
217
+
218
+ user = User.new(id: SecureRandom.uuid, email: "hello@example.com")
219
+ sp = Saml::Kit::IdentityProviderMetadata.new(xml)
220
+ url, saml_params = sp.logout_request_for(user, binding: :http_post)
221
+ puts [url, saml_params].inspect
222
+ # ["https://www.example.com/logout", {"SAMLRequest"=>"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48TG9nb3V0UmVxdWVzdCBJRD0iXzg3NjZiNTYyLTc2MzQtNDU4Zi04MzJmLTE4ODkwMjRlZDQ0MyIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTctMTItMTlUMDQ6NTg6MThaIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly93d3cuZXhhbXBsZS5jb20vbG9nb3V0IiB4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIj48SXNzdWVyIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIi8+PE5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OnBlcnNpc3RlbnQiIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj5kODc3YWEzZS01YTUyLTRhODAtYTA3ZC1lM2U5YzBjNTA1Nzk8L05hbWVJRD48L0xvZ291dFJlcXVlc3Q+"}]
223
+ ```
224
+
225
+ ### LogoutResponse
226
+
227
+ To generate a logout response, deserialize the logout request then
228
+ generate a response from the request.
229
+
230
+ ```ruby
231
+ idp = Saml::Kit::IdentityProviderMetadata.new(xml)
232
+ raw_params = Hash[uri.query.split("&amp;").map { |x| x.split("=", 2) }].symbolize_keys
233
+
234
+ binding = idp.single_logout_service_for(binding: :http_post)
235
+ saml_request = binding.deserialize(raw_params)
236
+ url, saml_params = saml_request.response_for(binding: :http_post)
237
+ puts [url, saml_params].inspect
238
+ # ["https://www.example.com/logout", {"SAMLResponse"=>"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48TG9nb3V0UmVzcG9uc2UgeG1sbnM9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgSUQ9Il9kZDA2YmY5MC04ODI2LTQ5ZTMtYmYxNS1jYzAxMWJkNzU3NGEiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDE3LTEyLTE5VDA1OjQyOjQyWiIgRGVzdGluYXRpb249Imh0dHBzOi8vd3d3LmV4YW1wbGUuY29tL2xvZ291dCIgSW5SZXNwb25zZVRvPSJfYmVhZjJiN2ItMDlmNC00ZmFkLWJkYmYtOWQ0ZDc1N2I5ZDU0Ij48SXNzdWVyIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIi8+PFN0YXR1cz48U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9TdGF0dXM+PC9Mb2dvdXRSZXNwb25zZT4="}]
239
+ ```
26
240
 
27
241
  ## Development
28
242
 
@@ -1,20 +1,46 @@
1
1
  module Saml
2
2
  module Kit
3
+ # This class can be used to parse a SAML AuthnRequest or generate one.
4
+ #
5
+ # To generate an AuthnRequest use the builder API.
6
+ #
7
+ # request = AuthenticationRequest.build do |builder|
8
+ # builder.name_id_format = [Saml::Kit::Namespaces::EMAIL_ADDRESS]
9
+ # end
10
+ #
11
+ # <?xml version="1.0" encoding="UTF-8"?>
12
+ # <samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_ca3a0e72-9530-41f1-9518-c53716de88b2" Version="2.0" IssueInstant="2017-12-19T16:27:44Z" Destination="http://hartmann.info" AssertionConsumerServiceURL="https://carroll.com/acs">
13
+ # <saml:Issuer>Day of the Dangerous Cousins</saml:Issuer>
14
+ # <samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"/>
15
+ # </samlp:AuthnRequest>
3
16
  class AuthenticationRequest < Document
4
17
  include Requestable
5
18
 
19
+ # Create an instance of an AuthnRequest document.
20
+ #
21
+ # @param xml [String] the raw xml.
22
+ # @param configuration [Saml::Kit::Configuration] defaults to the global configuration.
6
23
  def initialize(xml, configuration: Saml::Kit.configuration)
7
24
  super(xml, name: "AuthnRequest", configuration: configuration)
8
25
  end
9
26
 
27
+ # Extract the AssertionConsumerServiceURL from the AuthnRequest
28
+ # <samlp:AuthnRequest AssertionConsumerServiceURL="https://carroll.com/acs"></samlp:AuthnRequest>
10
29
  def assertion_consumer_service_url
11
30
  to_h[name]['AssertionConsumerServiceURL']
12
31
  end
13
32
 
33
+ # Extract the NameIDPolicy from the AuthnRequest
34
+ # <samlp:AuthnRequest>
35
+ # <samlp:NameIDPolicy Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"/>
36
+ # </samlp:AuthnRequest>
14
37
  def name_id_format
15
38
  to_h[name]['NameIDPolicy']['Format']
16
39
  end
17
40
 
41
+ # Generate a Response for a specific user.
42
+ # @param user [Object] this is a custom user object that can be used for generating a nameid and assertion attributes.
43
+ # @param binding [Symbol] the SAML binding to use `:http_post` or `:http_redirect`.
18
44
  def response_for(user, binding:, relay_state: nil)
19
45
  response_binding = provider.assertion_consumer_service_for(binding: binding)
20
46
  builder = Saml::Kit::Response.builder(user, self) do |x|
@@ -24,6 +50,7 @@ module Saml
24
50
  response_binding.serialize(builder, relay_state: relay_state)
25
51
  end
26
52
 
53
+ # @deprecated Use {#Saml::Kit::Builders::AuthenticationRequest} instead of this.
27
54
  Builder = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Saml::Kit::AuthenticationRequest::Builder', 'Saml::Kit::Builders::AuthenticationRequest')
28
55
  end
29
56
  end
@@ -4,15 +4,15 @@ module Saml
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  class_methods do
7
- def build(*args, &block)
7
+ def build(*args, &block) # :yields builder
8
8
  builder(*args, &block).build
9
9
  end
10
10
 
11
- def build_xml(*args, &block)
11
+ def build_xml(*args, &block) # :yields builder
12
12
  builder(*args, &block).to_xml
13
13
  end
14
14
 
15
- def builder(*args)
15
+ def builder(*args) # :yields builder
16
16
  builder_class.new(*args).tap do |builder|
17
17
  yield builder if block_given?
18
18
  end
@@ -45,7 +45,7 @@ module Saml
45
45
  def conditions_options
46
46
  {
47
47
  NotBefore: now.utc.iso8601,
48
- NotOnOrAfter: configuration.session_timeout.from_now.utc.iso8601,
48
+ NotOnOrAfter: configuration.session_timeout.since(now).utc.iso8601,
49
49
  }
50
50
  end
51
51
 
@@ -53,7 +53,7 @@ module Saml
53
53
  {
54
54
  AuthnInstant: now.iso8601,
55
55
  SessionIndex: reference_id,
56
- SessionNotOnOrAfter: 3.hours.since(now).utc.iso8601,
56
+ SessionNotOnOrAfter: configuration.session_timeout.since(now).utc.iso8601,
57
57
  }
58
58
  end
59
59
  end
@@ -16,11 +16,13 @@ module Saml
16
16
  @version = "2.0"
17
17
  end
18
18
 
19
+ # @deprecated Use {#assertion_consumer_service_url} instead of this method.
19
20
  def acs_url
20
21
  Saml::Kit.deprecate("acs_url is deprecated. Use assertion_consumer_service_url instead")
21
22
  self.assertion_consumer_service_url
22
23
  end
23
24
 
25
+ # @deprecated Use {#assertion_consumer_service_url=} instead of this method.
24
26
  def acs_url=(value)
25
27
  Saml::Kit.deprecate("acs_url= is deprecated. Use assertion_consumer_service_url= instead")
26
28
  self.assertion_consumer_service_url = value
@@ -7,14 +7,13 @@ module Saml
7
7
  attr_reader :request
8
8
  attr_reader :configuration
9
9
 
10
- def initialize(user, request, configuration: Saml::Kit.configuration)
10
+ def initialize(request, configuration: Saml::Kit.configuration)
11
11
  @configuration = configuration
12
12
  @id = Id.generate
13
13
  @issuer = configuration.issuer
14
14
  @now = Time.now.utc
15
15
  @request = request
16
16
  @status_code = Namespaces::SUCCESS
17
- @user = user
18
17
  @version = "2.0"
19
18
  end
20
19
 
@@ -1,6 +1,6 @@
1
1
  module Saml
2
2
  module Kit
3
- class CompositeMetadata < Metadata
3
+ class CompositeMetadata < Metadata # :nodoc:
4
4
  include Enumerable
5
5
  attr_reader :service_provider, :identity_provider
6
6
 
@@ -1,12 +1,39 @@
1
1
  module Saml
2
2
  module Kit
3
+ # This class represents the main configuration that is use for generating SAML documents.
4
+ #
5
+ # Saml::Kit::Configuration.new do |config|
6
+ # config.issuer = "com:saml:kit"
7
+ # config.signature_method = :SHA256
8
+ # config.digest_method = :SHA256
9
+ # config.registry = Saml::Kit::DefaultRegistry.new
10
+ # config.session_timeout = 30.minutes
11
+ # config.logger = Rails.logger
12
+ # end
13
+ #
14
+ # To specify global configuration it is best to do this in an initialize
15
+ # that runs at the start of the program.
16
+ #
17
+ # Saml::Kit.configure do |configuration|
18
+ # configuration.issuer = "https://www.example.com/saml/metadata"
19
+ # configuration.generate_key_pair_for(use: :signing)
20
+ # configuration.add_key_pair(ENV["X509_CERTIFICATE"], ENV["PRIVATE_KEY"], password: ENV['PRIVATE_KEY_PASSWORD'], use: :encryption)
21
+ # end
3
22
  class Configuration
23
+ # The issuer or entity_id to use.
4
24
  attr_accessor :issuer
5
- attr_accessor :signature_method, :digest_method
6
- attr_accessor :registry, :session_timeout
25
+ # The signature method to use when generating signatures (See {SAML::Kit::Builders::XmlSignature::SIGNATURE_METHODS})
26
+ attr_accessor :signature_method
27
+ # The digest method to use when generating signatures (See {SAML::Kit::Builders::XmlSignature::DIGEST_METHODS})
28
+ attr_accessor :digest_method
29
+ # The metadata registry to use for searching for metadata associated with an issuer.
30
+ attr_accessor :registry
31
+ # The session timeout to use when generating an Assertion.
32
+ attr_accessor :session_timeout
33
+ # The logger to write log messages to.
7
34
  attr_accessor :logger
8
35
 
9
- def initialize
36
+ def initialize # :yields configuration
10
37
  @signature_method = :SHA256
11
38
  @digest_method = :SHA256
12
39
  @registry = DefaultRegistry.new
@@ -16,42 +43,65 @@ module Saml
16
43
  yield self if block_given?
17
44
  end
18
45
 
19
- def add_key_pair(certificate, private_key, password:, use: :signing)
46
+ # Add a key pair that can be used for either signing or encryption.
47
+ #
48
+ # @param certificate [String] the x509 certificate with public key.
49
+ # @param private_key [String] the plain text private key.
50
+ # @param password [String] the password to decrypt the private key.
51
+ # @param use [Symbol] the type of key pair, `:signing` or `:encryption`
52
+ def add_key_pair(certificate, private_key, password: '', use: :signing)
20
53
  @key_pairs.push(KeyPair.new(certificate, private_key, password, use.to_sym))
21
54
  end
22
55
 
56
+ # Generates a unique key pair that can be used for signing or encryption.
57
+ #
58
+ # @param use [Symbol] the type of key pair, `:signing` or `:encryption`
59
+ # @param password [String] the private key password to use.
23
60
  def generate_key_pair_for(use:, password: SecureRandom.uuid)
24
61
  certificate, private_key = SelfSignedCertificate.new(password).create
25
62
  add_key_pair(certificate, private_key, password: password, use: use)
26
63
  end
27
64
 
65
+ # Return each key pair for a specific use.
66
+ #
67
+ # @param use [Symbol] the type of key pair to return `nil`, `:signing` or `:encryption`
28
68
  def key_pairs(use: nil)
29
69
  use.present? ? @key_pairs.find_all { |x| x.for?(use) } : @key_pairs
30
70
  end
31
71
 
72
+ # Return each certificate for a specific use.
73
+ #
74
+ # @param use [Symbol] the type of key pair to return `nil`, `:signing` or `:encryption`
32
75
  def certificates(use: nil)
33
76
  key_pairs(use: use).flat_map(&:certificate)
34
77
  end
35
78
 
79
+ # Return each private for a specific use.
80
+ #
81
+ # @param use [Symbol] the type of key pair to return `nil`, `:signing` or `:encryption`
36
82
  def private_keys(use: :signing)
37
83
  key_pairs(use: use).flat_map(&:private_key)
38
84
  end
39
85
 
86
+ # @deprecated Use {#certificates} instead of this method.
40
87
  def encryption_certificate
41
88
  Saml::Kit.deprecate("encryption_certificate is deprecated. Use certificates(use: :encryption) instead")
42
89
  certificates(use: :encryption).last
43
90
  end
44
91
 
92
+ # @deprecated Use {#private_keys} instead of this method.
45
93
  def signing_private_key
46
94
  Saml::Kit.deprecate("signing_private_key is deprecated. Use private_keys(use: :signing) instead")
47
95
  private_keys(use: :signing).last
48
96
  end
49
97
 
98
+ # @deprecated Use {#private_keys} instead of this method.
50
99
  def encryption_private_key
51
100
  Saml::Kit.deprecate("encryption_private_key is deprecated. Use private_keys(use: :encryption) instead")
52
101
  private_keys(use: :encryption).last
53
102
  end
54
103
 
104
+ # Returns true if there is at least one signing certificate registered.
55
105
  def sign?
56
106
  certificates(use: :signing).any?
57
107
  end
@@ -1,27 +1,38 @@
1
1
  module Saml
2
2
  module Kit
3
+ # The default metadata registry is used to fetch the metadata associated with an issuer or entity id.
4
+ # The metadata associated with an issuer is used to verify trust for any SAML documents that are received.
3
5
  class DefaultRegistry
4
6
  def initialize(items = {})
5
7
  @items = items
6
8
  end
7
9
 
10
+ # Register a metadata document
11
+ #
12
+ # @param metadata [Saml::Kit::Metadata] the metadata to register.
8
13
  def register(metadata)
9
14
  Saml::Kit.logger.debug(metadata.to_xml(pretty: true))
10
15
  @items[metadata.entity_id] = metadata
11
16
  end
12
17
 
18
+ # Register metadata via a remote URL.
19
+ # This will attempt to connect to the remove URL to download the metadata and register it in the registry.
20
+ #
21
+ # @param url [String] the url to download the metadata from.
22
+ # @param verify_ssl [Boolean] enable/disable SSL peer verification.
13
23
  def register_url(url, verify_ssl: true)
14
24
  content = HttpApi.new(url, verify_ssl: verify_ssl).get
15
25
  register(Saml::Kit::Metadata.from(content))
16
26
  end
17
27
 
28
+ # Returns the metadata document associated with an issuer or entityID.
29
+ #
30
+ # @param entity_id [String] the unique entityID/Issuer associated with metadata.
18
31
  def metadata_for(entity_id)
19
32
  @items[entity_id]
20
33
  end
21
34
 
22
- class HttpApi
23
- attr_reader :uri, :verify_ssl
24
-
35
+ class HttpApi # :nodoc:
25
36
  def initialize(url, verify_ssl: true)
26
37
  @uri = URI.parse(url)
27
38
  @verify_ssl = verify_ssl
@@ -37,6 +48,8 @@ module Saml
37
48
 
38
49
  private
39
50
 
51
+ attr_reader :uri, :verify_ssl
52
+
40
53
  def http
41
54
  http = Net::HTTP.new(uri.host, uri.port)
42
55
  http.read_timeout = 30
@@ -13,8 +13,6 @@ module Saml
13
13
  validate :must_be_expected_type
14
14
  validate :must_be_valid_version
15
15
 
16
- attr_reader :content, :name, :configuration
17
-
18
16
  def initialize(xml, name:, configuration: Saml::Kit.configuration)
19
17
  @configuration = configuration
20
18
  @content = xml
@@ -22,39 +20,45 @@ module Saml
22
20
  @xml_hash = Hash.from_xml(xml) || {}
23
21
  end
24
22
 
23
+ # Returns the ID for the SAML document.
25
24
  def id
26
25
  to_h.fetch(name, {}).fetch('ID', nil)
27
26
  end
28
27
 
28
+ # Returns the Issuer for the SAML document.
29
29
  def issuer
30
30
  to_h.fetch(name, {}).fetch('Issuer', nil)
31
31
  end
32
32
 
33
+ # Returns the Version of the SAML document.
33
34
  def version
34
35
  to_h.fetch(name, {}).fetch('Version', {})
35
36
  end
36
37
 
38
+ # Returns the Destination of the SAML document.
37
39
  def destination
38
40
  to_h.fetch(name, {}).fetch('Destination', nil)
39
41
  end
40
42
 
43
+ # Returns the Destination of the SAML document.
41
44
  def issue_instant
42
- to_h[name]['IssueInstant']
43
- end
44
-
45
- def expected_type?
46
- return false if to_xml.blank?
47
- to_h[name].present?
45
+ Time.parse(to_h[name]['IssueInstant'])
48
46
  end
49
47
 
48
+ # Returns the SAML document returned as a Hash.
50
49
  def to_h
51
50
  @xml_hash
52
51
  end
53
52
 
53
+ # Returns the SAML document as an XML string.
54
+ #
55
+ # @param pretty [Boolean] formats the xml or returns the raw xml.
54
56
  def to_xml(pretty: false)
55
57
  pretty ? Nokogiri::XML(content).to_xml(indent: 2) : content
56
58
  end
57
59
 
60
+ # Returns the SAML document as an XHTML string.
61
+ # This is useful for rendering in a web page.
58
62
  def to_xhtml
59
63
  Nokogiri::XML(content, &:noblanks).to_xhtml
60
64
  end
@@ -64,6 +68,10 @@ module Saml
64
68
  end
65
69
 
66
70
  class << self
71
+ # Returns the raw xml as a Saml::Kit SAML document.
72
+ #
73
+ # @param xml [String] the raw xml string.
74
+ # @param configuration [Saml::Kit::Configuration] the configuration to use for unpacking the document.
67
75
  def to_saml_document(xml, configuration: Saml::Kit.configuration)
68
76
  hash = Hash.from_xml(xml)
69
77
  if hash['Response'].present?
@@ -80,7 +88,8 @@ module Saml
80
88
  InvalidDocument.new(xml)
81
89
  end
82
90
 
83
- def builder_class
91
+ # @!visibility private
92
+ def builder_class # :nodoc:
84
93
  case name
85
94
  when Saml::Kit::Response.to_s
86
95
  Saml::Kit::Builders::Response
@@ -98,6 +107,8 @@ module Saml
98
107
 
99
108
  private
100
109
 
110
+ attr_reader :content, :name, :configuration
111
+
101
112
  def must_match_xsd
102
113
  matches_xsd?(PROTOCOL_XSD)
103
114
  end
@@ -108,6 +119,11 @@ module Saml
108
119
  errors[:base] << error_message(:invalid) unless expected_type?
109
120
  end
110
121
 
122
+ def expected_type?
123
+ return false if to_xml.blank?
124
+ to_h[name].present?
125
+ end
126
+
111
127
  def must_be_valid_version
112
128
  return unless expected_type?
113
129
  return if "2.0" == version
@@ -1,12 +1,18 @@
1
1
  module Saml
2
2
  module Kit
3
+ # This generates a fingerprint for an X509 Certificate.
3
4
  class Fingerprint
5
+ # The OpenSSL::X509::Certificate
4
6
  attr_reader :x509
5
7
 
6
8
  def initialize(raw_certificate)
7
9
  @x509 = Certificate.to_x509(raw_certificate)
8
10
  end
9
11
 
12
+ # Generates a formatted fingerprint using the specified hash algorithm.
13
+ #
14
+ # @param algorithm [OpenSSL::Digest] the openssl algorithm to use `OpenSSL::Digest::SHA256`, `OpenSSL::Digest::SHA1`.
15
+ # @return [String] in the format of `"BF:ED:C5:F1:6C:AB:F5:B2:15:1F:BF:BD:7D:68:1A:F9:A5:4E:4C:19:30:BC:6D:25:B1:8E:98:D4:23:FD:B4:09"`
10
16
  def algorithm(algorithm)
11
17
  pretty_fingerprint(algorithm.new.hexdigest(x509.to_der))
12
18
  end
@@ -1,10 +1,23 @@
1
1
  module Saml
2
2
  module Kit
3
+ # This class is used to parse the IDPSSODescriptor from a SAML metadata document.
4
+ #
5
+ # <?xml version="1.0" encoding="UTF-8"?>
6
+ # <EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_cfa24e2f-0ec0-4ee3-abb8-b2fcfe394c1c" entityID="">
7
+ # <IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
8
+ # <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://www.example.com/logout"/>
9
+ # <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>
10
+ # <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://www.example.com/login"/>
11
+ # <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://www.example.com/login"/>
12
+ # <saml:Attribute Name="id"/>
13
+ # </IDPSSODescriptor>
14
+ # </EntityDescriptor>
3
15
  class IdentityProviderMetadata < Metadata
4
16
  def initialize(xml)
5
17
  super("IDPSSODescriptor", xml)
6
18
  end
7
19
 
20
+ # Returns the IDPSSODescriptor/@WantAuthnRequestsSigned attribute.
8
21
  def want_authn_requests_signed
9
22
  xpath = "/md:EntityDescriptor/md:#{name}"
10
23
  attribute = document.find_by(xpath).attribute("WantAuthnRequestsSigned")
@@ -12,14 +25,19 @@ module Saml
12
25
  attribute.text.downcase == "true"
13
26
  end
14
27
 
28
+ # Returns each of the SingleSignOnService elements.
15
29
  def single_sign_on_services
16
30
  services('SingleSignOnService')
17
31
  end
18
32
 
33
+ # Returns a SingleSignOnService elements with the specified binding.
34
+ #
35
+ # @param binding [Symbol] `:http_post` or `:http_redirect`.
19
36
  def single_sign_on_service_for(binding:)
20
37
  service_for(binding: binding, type: 'SingleSignOnService')
21
38
  end
22
39
 
40
+ # Returns each of the Attributes in the metadata.
23
41
  def attributes
24
42
  document.find_all("/md:EntityDescriptor/md:#{name}/saml:Attribute").map do |item|
25
43
  {
@@ -29,7 +47,13 @@ module Saml
29
47
  end
30
48
  end
31
49
 
32
- def login_request_for(binding:, relay_state: nil, configuration: Saml::Kit.configuration)
50
+ # Creates a AuthnRequest document for the specified binding.
51
+ #
52
+ # @param binding [Symbol] `:http_post` or `:http_redirect`.
53
+ # @param relay_state [Object] The RelayState to include the returned SAML params.
54
+ # @param configuration [Saml::Kit::Configuration] the configuration to use for generating the request.
55
+ # @return [Array] The url and saml params encoded using the rules for the specified binding.
56
+ def login_request_for(binding:, relay_state: nil, configuration: Saml::Kit.configuration) # :yields builder
33
57
  builder = Saml::Kit::AuthenticationRequest.builder(configuration: configuration) do |x|
34
58
  x.embed_signature = want_authn_requests_signed
35
59
  yield x if block_given?
@@ -38,10 +62,12 @@ module Saml
38
62
  request_binding.serialize(builder, relay_state: relay_state)
39
63
  end
40
64
 
65
+ # @!visibility private
41
66
  def self.builder_class
42
67
  Saml::Kit::Builders::IdentityProviderMetadata
43
68
  end
44
69
 
70
+ # @deprecated Use {#Saml::Kit::Builders::IdentityProviderMetadata} instead of this.
45
71
  Builder = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Saml::Kit::IdentityProviderMetadata::Builder', 'Saml::Kit::Builders::IdentityProviderMetadata')
46
72
  end
47
73
  end
@@ -1,6 +1,6 @@
1
1
  module Saml
2
2
  module Kit
3
- class KeyPair
3
+ class KeyPair # :nodoc:
4
4
  attr_reader :certificate, :private_key, :use
5
5
 
6
6
  def initialize(certificate, private_key, password, use)
@@ -1,5 +1,6 @@
1
1
  module Saml
2
2
  module Kit
3
+ # This class parses a LogoutRequest SAML document.
3
4
  class LogoutRequest < Document
4
5
  include Requestable
5
6
  validates_presence_of :single_logout_service, if: :expected_type?
@@ -8,25 +9,34 @@ module Saml
8
9
  super(xml, name: "LogoutRequest", configuration: configuration)
9
10
  end
10
11
 
12
+ # Returns the NameID value.
11
13
  def name_id
12
14
  to_h[name]['NameID']
13
15
  end
14
16
 
15
- def single_logout_service
16
- return if provider.nil?
17
- urls = provider.single_logout_services
18
- urls.first
19
- end
20
-
21
- def response_for(user, binding:, relay_state: nil)
22
- builder = Saml::Kit::LogoutResponse.builder(user, self) do |x|
17
+ # Generates a Serialized LogoutResponse using the encoding rules for the specified binding.
18
+ #
19
+ # @param binding [Symbol] The binding to use `:http_redirect` or `:http_post`.
20
+ # @param relay_state [Object] The RelayState to include in the RelayState param.
21
+ # @return [Array] Returns an array with a url and Hash of parameters to return to the requestor.
22
+ def response_for(binding:, relay_state: nil)
23
+ builder = Saml::Kit::LogoutResponse.builder(self) do |x|
23
24
  yield x if block_given?
24
25
  end
25
26
  response_binding = provider.single_logout_service_for(binding: binding)
26
27
  response_binding.serialize(builder, relay_state: relay_state)
27
28
  end
28
29
 
30
+ # @deprecated Use {#Saml::Kit::Builders::LogoutRequest} instead of this.
29
31
  Builder = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Saml::Kit::LogoutRequest::Builder', 'Saml::Kit::Builders::LogoutRequest')
32
+
33
+ private
34
+
35
+ def single_logout_service
36
+ return if provider.nil?
37
+ urls = provider.single_logout_services
38
+ urls.first
39
+ end
30
40
  end
31
41
  end
32
42
  end
@@ -1,5 +1,6 @@
1
1
  module Saml
2
2
  module Kit
3
+ # This class is used to parse a LogoutResponse SAML document.
3
4
  class LogoutResponse < Document
4
5
  include Respondable
5
6
 
@@ -8,6 +9,7 @@ module Saml
8
9
  super(xml, name: "LogoutResponse", configuration: configuration)
9
10
  end
10
11
 
12
+ # @deprecated Use {#Saml::Kit::Builders::LogoutResponse} instead of this.
11
13
  Builder = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Saml::Kit::LogoutResponse::Builder', 'Saml::Kit::Builders::LogoutResponse')
12
14
  end
13
15
  end
@@ -3,6 +3,7 @@ module Saml
3
3
  module Requestable
4
4
  extend ActiveSupport::Concern
5
5
 
6
+ # @!visibility private
6
7
  def query_string_parameter
7
8
  'SAMLRequest'
8
9
  end
@@ -9,18 +9,22 @@ module Saml
9
9
  validate :must_match_request_id
10
10
  end
11
11
 
12
+ # @!visibility private
12
13
  def query_string_parameter
13
14
  'SAMLResponse'
14
15
  end
15
16
 
17
+ # Returns the /Status/StatusCode@Value
16
18
  def status_code
17
19
  to_h.fetch(name, {}).fetch('Status', {}).fetch('StatusCode', {}).fetch('Value', nil)
18
20
  end
19
21
 
22
+ # Returns the /InResponseTo attribute.
20
23
  def in_response_to
21
24
  to_h.fetch(name, {}).fetch('InResponseTo', nil)
22
25
  end
23
26
 
27
+ # Returns true if the Status code is #{Saml::Kit::Namespaces::SUCCESS}
24
28
  def success?
25
29
  Namespaces::SUCCESS == status_code
26
30
  end
@@ -1,37 +1,45 @@
1
1
  module Saml
2
2
  module Kit
3
- class Signatures
3
+ class Signatures # :nodoc:
4
+ # @!visibility private
4
5
  attr_reader :configuration
5
6
 
7
+ # @!visibility private
6
8
  def initialize(configuration:)
7
9
  @configuration = configuration
8
10
  end
9
11
 
12
+ # @!visibility private
10
13
  def build(reference_id)
11
14
  return nil unless configuration.sign?
12
15
  Saml::Kit::Builders::XmlSignature.new(reference_id, configuration: configuration)
13
16
  end
14
17
 
18
+ # @!visibility private
15
19
  def complete(raw_xml)
16
20
  return raw_xml unless configuration.sign?
17
21
  private_key = configuration.private_keys(use: :signing).last
18
22
  Xmldsig::SignedDocument.new(raw_xml).sign(private_key)
19
23
  end
20
24
 
25
+ # @!visibility private
21
26
  def self.sign(xml: ::Builder::XmlMarkup.new, configuration: Saml::Kit.configuration)
22
27
  signatures = Saml::Kit::Signatures.new(configuration: configuration)
23
28
  yield xml, XmlSignatureTemplate.new(xml, signatures)
24
29
  signatures.complete(xml.target!)
25
30
  end
26
31
 
27
- class XmlSignatureTemplate
32
+ class XmlSignatureTemplate # :nodoc:
33
+ # @!visibility private
28
34
  attr_reader :signatures, :xml
29
35
 
36
+ # @!visibility private
30
37
  def initialize(xml, signatures)
31
38
  @signatures = signatures
32
39
  @xml = xml
33
40
  end
34
41
 
42
+ # @!visibility private
35
43
  def template(reference_id)
36
44
  Template.new(signatures.build(reference_id)).to_xml(xml: xml)
37
45
  end
@@ -1,6 +1,7 @@
1
1
  module Saml
2
2
  module Kit
3
3
  module Translatable
4
+ # @!visibility private
4
5
  def error_message(attribute, type: :invalid)
5
6
  I18n.translate(attribute, scope: "saml/kit.errors.#{name}")
6
7
  end
@@ -13,6 +13,7 @@ module Saml
13
13
  signature_manually_verified || signature.present?
14
14
  end
15
15
 
16
+ # @!visibility private
16
17
  def signature
17
18
  xml_hash = to_h.fetch(name, {}).fetch('Signature', nil)
18
19
  xml_hash ? Signature.new(xml_hash) : nil
@@ -24,10 +25,12 @@ module Saml
24
25
  signature.trusted?(provider)
25
26
  end
26
27
 
28
+ # @!visibility private
27
29
  def provider
28
30
  configuration.registry.metadata_for(issuer)
29
31
  end
30
32
 
33
+ # @!visibility private
31
34
  def signature_verified!
32
35
  @signature_manually_verified = true
33
36
  end
@@ -1,5 +1,5 @@
1
1
  module Saml
2
2
  module Kit
3
- VERSION = "0.2.11"
3
+ VERSION = "0.2.12"
4
4
  end
5
5
  end
data/lib/saml/kit/xml.rb CHANGED
@@ -1,13 +1,13 @@
1
1
  module Saml
2
2
  module Kit
3
- class Xml
3
+ class Xml # :nodoc:
4
4
  include ActiveModel::Validations
5
5
  NAMESPACES = {
6
6
  "NameFormat": Namespaces::ATTR_SPLAT,
7
7
  "ds": Namespaces::XMLDSIG,
8
8
  "md": Namespaces::METADATA,
9
9
  "saml": Namespaces::ASSERTION,
10
- "pro": Namespaces::PROTOCOL,
10
+ "samlp": Namespaces::PROTOCOL,
11
11
  }.freeze
12
12
 
13
13
  attr_reader :raw_xml, :document
@@ -1,6 +1,7 @@
1
1
  module Saml
2
2
  module Kit
3
3
  module XsdValidatable
4
+ # @!visibility private
4
5
  def matches_xsd?(xsd)
5
6
  Dir.chdir(File.dirname(xsd)) do
6
7
  xsd = Nokogiri::XML::Schema(IO.read(xsd))
data/saml-kit.gemspec CHANGED
@@ -18,6 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
19
  f.match(%r{^(test|spec|features)/})
20
20
  end
21
+ spec.metadata["yard.run"] = "yri"
21
22
  spec.bindir = "exe"
22
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
24
  spec.require_paths = ["lib"]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: saml-kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.11
4
+ version: 0.2.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - mo khan
@@ -278,7 +278,8 @@ files:
278
278
  homepage: http://www.mokhan.ca
279
279
  licenses:
280
280
  - MIT
281
- metadata: {}
281
+ metadata:
282
+ yard.run: yri
282
283
  post_install_message:
283
284
  rdoc_options: []
284
285
  require_paths:
@@ -295,7 +296,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
295
296
  version: '0'
296
297
  requirements: []
297
298
  rubyforge_project:
298
- rubygems_version: 2.7.3
299
+ rubygems_version: 2.6.14
299
300
  signing_key:
300
301
  specification_version: 4
301
302
  summary: A simple toolkit for working with SAML.