saml-kit 0.2.11 → 0.2.12

Sign up to get free protection for your applications and to get access to all the features.
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.