saml_idp 0.12.0 → 0.14.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.
- checksums.yaml +4 -4
- data/README.md +18 -50
- data/lib/saml_idp.rb +1 -1
- data/lib/saml_idp/assertion_builder.rb +28 -3
- data/lib/saml_idp/controller.rb +6 -6
- data/lib/saml_idp/encryptor.rb +0 -1
- data/lib/saml_idp/saml_response.rb +24 -15
- data/lib/saml_idp/version.rb +1 -1
- data/saml_idp.gemspec +7 -6
- data/spec/lib/saml_idp/assertion_builder_spec.rb +73 -0
- data/spec/rails_app/app/controllers/saml_controller.rb +1 -5
- data/spec/rails_app/app/controllers/saml_idp_controller.rb +47 -8
- data/{app → spec/rails_app/app}/views/saml_idp/idp/new.html.erb +1 -5
- data/{app → spec/rails_app/app}/views/saml_idp/idp/saml_post.html.erb +1 -1
- data/spec/rails_app/config/environments/development.rb +2 -0
- data/spec/spec_helper.rb +1 -1
- metadata +42 -27
- data/app/controllers/saml_idp/idp_controller.rb +0 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 847e2cafec28e67417685e6ba1173aa88ac489172e0e31fa51f2bbab37ef5d10
|
4
|
+
data.tar.gz: 8d9ace44b46770b3ca7481461bd16fce9dfc0e2b9925a4a2b5460a780cfddce3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6aa29d58babddddc81037fe220276241a355d81a15acb3639a9cc6df935f531bf1fe53e3c5599d49640080776c357ebf6348559ac034313ad64f82f80a7824a0
|
7
|
+
data.tar.gz: 9f5b83fd6459476d81c2dc2d3f0e79c401ba01474856f201c5a32615da8e22e4c416d220ef82985205f69f1d87a560a80c3bbae552a39ec4bdb49cc0a1baafa0
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Ruby SAML Identity Provider (IdP)
|
2
2
|
|
3
|
-
Forked from https://github.com/lawrencepit/ruby-saml-idp
|
3
|
+
Forked from <https://github.com/lawrencepit/ruby-saml-idp>
|
4
4
|
|
5
5
|
[](https://travis-ci.org/saml-idp/saml_idp)
|
6
6
|
[](http://badge.fury.io/rb/saml_idp)
|
@@ -13,13 +13,15 @@ protocol. It provides a means for managing authentication requests and confirmat
|
|
13
13
|
This was originally setup by @lawrencepit to test SAML Clients. I took it closer to a real
|
14
14
|
SAML IDP implementation.
|
15
15
|
|
16
|
-
|
16
|
+
## Installation and Usage
|
17
17
|
|
18
18
|
Add this to your Gemfile:
|
19
19
|
|
20
|
+
```ruby
|
20
21
|
gem 'saml_idp'
|
22
|
+
```
|
21
23
|
|
22
|
-
|
24
|
+
### Not using rails?
|
23
25
|
|
24
26
|
Include `SamlIdp::Controller` and see the examples that use rails. It should be straightforward for you.
|
25
27
|
|
@@ -27,50 +29,16 @@ Basically you call `decode_request(params[:SAMLRequest])` on an incoming request
|
|
27
29
|
`saml_acs_url` to determine the source for which you need to authenticate a user. How you authenticate
|
28
30
|
a user is entirely up to you.
|
29
31
|
|
30
|
-
Once a user has successfully authenticated on your system send the Service Provider a
|
32
|
+
Once a user has successfully authenticated on your system send the Service Provider a SAMLResponse by
|
31
33
|
posting to `saml_acs_url` the parameter `SAMLResponse` with the return value from a call to
|
32
34
|
`encode_response(user_email)`.
|
33
35
|
|
34
|
-
|
36
|
+
### Using rails?
|
35
37
|
|
36
|
-
|
37
|
-
|
38
|
-
```ruby
|
39
|
-
get '/saml/auth' => 'saml_idp#new'
|
40
|
-
get '/saml/metadata' => 'saml_idp#show'
|
41
|
-
post '/saml/auth' => 'saml_idp#create'
|
42
|
-
match '/saml/logout' => 'saml_idp#logout', via: [:get, :post, :delete]
|
43
|
-
```
|
38
|
+
Check out our Wiki page for Rails integration
|
39
|
+
[Rails Integration guide](https://github.com/saml-idp/saml_idp/wiki/Rails_Integration)
|
44
40
|
|
45
|
-
|
46
|
-
|
47
|
-
```ruby
|
48
|
-
class SamlIdpController < SamlIdp::IdpController
|
49
|
-
def idp_authenticate(email, password) # not using params intentionally
|
50
|
-
user = User.by_email(email).first
|
51
|
-
user && user.valid_password?(password) ? user : nil
|
52
|
-
end
|
53
|
-
private :idp_authenticate
|
54
|
-
|
55
|
-
def idp_make_saml_response(found_user) # not using params intentionally
|
56
|
-
# NOTE encryption is optional
|
57
|
-
encode_response found_user, encryption: {
|
58
|
-
cert: saml_request.service_provider.cert,
|
59
|
-
block_encryption: 'aes256-cbc',
|
60
|
-
key_transport: 'rsa-oaep-mgf1p'
|
61
|
-
}
|
62
|
-
end
|
63
|
-
private :idp_make_saml_response
|
64
|
-
|
65
|
-
def idp_logout
|
66
|
-
user = User.by_email(saml_request.name_id)
|
67
|
-
user.logout
|
68
|
-
end
|
69
|
-
private :idp_logout
|
70
|
-
end
|
71
|
-
```
|
72
|
-
|
73
|
-
## Configuration
|
41
|
+
### Configuration
|
74
42
|
|
75
43
|
#### Signed assertions and Signed Response
|
76
44
|
|
@@ -230,7 +198,7 @@ CERT
|
|
230
198
|
end
|
231
199
|
```
|
232
200
|
|
233
|
-
|
201
|
+
## Keys and Secrets
|
234
202
|
|
235
203
|
To generate the SAML Response it uses a default X.509 certificate and secret key... which isn't so secret.
|
236
204
|
You can find them in `SamlIdp::Default`. The X.509 certificate is valid until year 2032.
|
@@ -241,31 +209,31 @@ and `SamlIdp.config.secret_key` properties.
|
|
241
209
|
|
242
210
|
The fingerprint to use, if you use the default X.509 certificate of this gem, is:
|
243
211
|
|
244
|
-
```
|
245
|
-
9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D
|
212
|
+
```bash
|
213
|
+
9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D
|
246
214
|
```
|
247
215
|
|
248
|
-
|
216
|
+
## Fingerprint
|
249
217
|
|
250
218
|
The gem provides an helper to generate a fingerprint for a X.509 certificate.
|
251
219
|
The second parameter is optional and default to your configuration `SamlIdp.config.algorithm`
|
252
220
|
|
253
221
|
```ruby
|
254
|
-
Fingerprint.certificate_digest(x509_cert, :sha512)
|
222
|
+
Fingerprint.certificate_digest(x509_cert, :sha512)
|
255
223
|
```
|
256
224
|
|
257
|
-
|
225
|
+
## Service Providers
|
258
226
|
|
259
227
|
To act as a Service Provider which generates SAML Requests and can react to SAML Responses use the
|
260
228
|
excellent [ruby-saml](https://github.com/onelogin/ruby-saml) gem.
|
261
229
|
|
262
|
-
|
230
|
+
## Author
|
263
231
|
|
264
232
|
Jon Phenow, jon@jphenow.com, jphenow.com, @jphenow
|
265
233
|
|
266
234
|
Lawrence Pit, lawrence.pit@gmail.com, lawrencepit.com, @lawrencepit
|
267
235
|
|
268
|
-
|
236
|
+
## Copyright
|
269
237
|
|
270
238
|
Copyright (c) 2012 Sport Ngin.
|
271
239
|
Portions Copyright (c) 2010 OneLogin, LLC
|
data/lib/saml_idp.rb
CHANGED
@@ -9,7 +9,7 @@ module SamlIdp
|
|
9
9
|
require 'saml_idp/metadata_builder'
|
10
10
|
require 'saml_idp/version'
|
11
11
|
require 'saml_idp/fingerprint'
|
12
|
-
require 'saml_idp/engine' if defined?(::Rails)
|
12
|
+
require 'saml_idp/engine' if defined?(::Rails)
|
13
13
|
|
14
14
|
def self.config
|
15
15
|
@config ||= SamlIdp::Configurator.new
|
@@ -16,10 +16,26 @@ module SamlIdp
|
|
16
16
|
attr_accessor :expiry
|
17
17
|
attr_accessor :encryption_opts
|
18
18
|
attr_accessor :session_expiry
|
19
|
+
attr_accessor :name_id_formats_opts
|
20
|
+
attr_accessor :asserted_attributes_opts
|
19
21
|
|
20
22
|
delegate :config, to: :SamlIdp
|
21
23
|
|
22
|
-
def initialize(
|
24
|
+
def initialize(
|
25
|
+
reference_id,
|
26
|
+
issuer_uri,
|
27
|
+
principal,
|
28
|
+
audience_uri,
|
29
|
+
saml_request_id,
|
30
|
+
saml_acs_url,
|
31
|
+
raw_algorithm,
|
32
|
+
authn_context_classref,
|
33
|
+
expiry=60*60,
|
34
|
+
encryption_opts=nil,
|
35
|
+
session_expiry=nil,
|
36
|
+
name_id_formats_opts = nil,
|
37
|
+
asserted_attributes_opts = nil
|
38
|
+
)
|
23
39
|
self.reference_id = reference_id
|
24
40
|
self.issuer_uri = issuer_uri
|
25
41
|
self.principal = principal
|
@@ -31,6 +47,8 @@ module SamlIdp
|
|
31
47
|
self.expiry = expiry
|
32
48
|
self.encryption_opts = encryption_opts
|
33
49
|
self.session_expiry = session_expiry.nil? ? config.session_expiry : session_expiry
|
50
|
+
self.name_id_formats_opts = name_id_formats_opts
|
51
|
+
self.asserted_attributes_opts = asserted_attributes_opts
|
34
52
|
end
|
35
53
|
|
36
54
|
def fresh
|
@@ -98,7 +116,9 @@ module SamlIdp
|
|
98
116
|
end
|
99
117
|
|
100
118
|
def asserted_attributes
|
101
|
-
if
|
119
|
+
if asserted_attributes_opts.present? && !asserted_attributes_opts.empty?
|
120
|
+
asserted_attributes_opts
|
121
|
+
elsif principal.respond_to?(:asserted_attributes)
|
102
122
|
principal.send(:asserted_attributes)
|
103
123
|
elsif !config.attributes.nil? && !config.attributes.empty?
|
104
124
|
config.attributes
|
@@ -139,10 +159,15 @@ module SamlIdp
|
|
139
159
|
private :name_id_getter
|
140
160
|
|
141
161
|
def name_id_format
|
142
|
-
@name_id_format ||= NameIdFormatter.new(
|
162
|
+
@name_id_format ||= NameIdFormatter.new(name_id_formats).chosen
|
143
163
|
end
|
144
164
|
private :name_id_format
|
145
165
|
|
166
|
+
def name_id_formats
|
167
|
+
@name_id_formats ||= (name_id_formats_opts || config.name_id.formats)
|
168
|
+
end
|
169
|
+
private :name_id_formats
|
170
|
+
|
146
171
|
def reference_string
|
147
172
|
"_#{reference_id}"
|
148
173
|
end
|
data/lib/saml_idp/controller.rb
CHANGED
@@ -37,11 +37,7 @@ module SamlIdp
|
|
37
37
|
decode_request(raw_saml_request)
|
38
38
|
return true if valid_saml_request?
|
39
39
|
if defined?(::Rails)
|
40
|
-
|
41
|
-
head :forbidden
|
42
|
-
else
|
43
|
-
render nothing: true, status: :forbidden
|
44
|
-
end
|
40
|
+
head :forbidden
|
45
41
|
end
|
46
42
|
false
|
47
43
|
end
|
@@ -65,6 +61,8 @@ module SamlIdp
|
|
65
61
|
session_expiry = opts[:session_expiry]
|
66
62
|
encryption_opts = opts[:encryption] || nil
|
67
63
|
signed_message_opts = opts[:signed_message] || false
|
64
|
+
name_id_formats_opts = opts[:name_id_formats] || nil
|
65
|
+
asserted_attributes_opts = opts[:attributes] || nil
|
68
66
|
|
69
67
|
SamlResponse.new(
|
70
68
|
reference_id,
|
@@ -79,7 +77,9 @@ module SamlIdp
|
|
79
77
|
expiry,
|
80
78
|
encryption_opts,
|
81
79
|
session_expiry,
|
82
|
-
signed_message_opts
|
80
|
+
signed_message_opts,
|
81
|
+
name_id_formats_opts,
|
82
|
+
asserted_attributes_opts
|
83
83
|
).build
|
84
84
|
end
|
85
85
|
|
data/lib/saml_idp/encryptor.rb
CHANGED
@@ -61,7 +61,6 @@ module SamlIdp
|
|
61
61
|
key_info.EncryptedKey Id: 'EK', xmlns: 'http://www.w3.org/2001/04/xmlenc#' do |enc_key|
|
62
62
|
enc_key.EncryptionMethod Algorithm: key_transport_ns
|
63
63
|
enc_key.tag! 'ds:KeyInfo', 'xmlns:ds' => 'http://www.w3.org/2000/09/xmldsig#' do |key_info2|
|
64
|
-
key_info2.tag! 'ds:KeyName'
|
65
64
|
key_info2.tag! 'ds:X509Data' do |x509_data|
|
66
65
|
x509_data.tag! 'ds:X509Certificate' do |x509_cert|
|
67
66
|
x509_cert << cert.to_s.gsub(/-+(BEGIN|END) CERTIFICATE-+/, '')
|
@@ -18,21 +18,26 @@ module SamlIdp
|
|
18
18
|
attr_accessor :encryption_opts
|
19
19
|
attr_accessor :session_expiry
|
20
20
|
attr_accessor :signed_message_opts
|
21
|
+
attr_accessor :name_id_formats_opts
|
22
|
+
attr_accessor :asserted_attributes_opts
|
21
23
|
|
22
|
-
def initialize(
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
24
|
+
def initialize(
|
25
|
+
reference_id,
|
26
|
+
response_id,
|
27
|
+
issuer_uri,
|
28
|
+
principal,
|
29
|
+
audience_uri,
|
30
|
+
saml_request_id,
|
31
|
+
saml_acs_url,
|
32
|
+
algorithm,
|
33
|
+
authn_context_classref,
|
34
|
+
expiry=60*60,
|
35
|
+
encryption_opts=nil,
|
36
|
+
session_expiry=0,
|
37
|
+
signed_message_opts=false,
|
38
|
+
name_id_formats_opts = nil,
|
39
|
+
asserted_attributes_opts = nil
|
40
|
+
)
|
36
41
|
self.reference_id = reference_id
|
37
42
|
self.response_id = response_id
|
38
43
|
self.issuer_uri = issuer_uri
|
@@ -48,6 +53,8 @@ module SamlIdp
|
|
48
53
|
self.encryption_opts = encryption_opts
|
49
54
|
self.session_expiry = session_expiry
|
50
55
|
self.signed_message_opts = signed_message_opts
|
56
|
+
self.name_id_formats_opts = name_id_formats_opts
|
57
|
+
self.asserted_attributes_opts = asserted_attributes_opts
|
51
58
|
end
|
52
59
|
|
53
60
|
def build
|
@@ -88,7 +95,9 @@ module SamlIdp
|
|
88
95
|
authn_context_classref,
|
89
96
|
expiry,
|
90
97
|
encryption_opts,
|
91
|
-
session_expiry
|
98
|
+
session_expiry,
|
99
|
+
name_id_formats_opts,
|
100
|
+
asserted_attributes_opts
|
92
101
|
end
|
93
102
|
private :assertion_builder
|
94
103
|
end
|
data/lib/saml_idp/version.rb
CHANGED
data/saml_idp.gemspec
CHANGED
@@ -12,8 +12,8 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.summary = 'SAML Indentity Provider for Ruby'
|
13
13
|
s.description = 'SAML IdP (Identity Provider) Library for Ruby'
|
14
14
|
s.date = Time.now.utc.strftime("%Y-%m-%d")
|
15
|
-
s.files = Dir['
|
16
|
-
s.required_ruby_version = '>= 2.
|
15
|
+
s.files = Dir['lib/**/*', 'LICENSE', 'README.md', 'Gemfile', 'saml_idp.gemspec']
|
16
|
+
s.required_ruby_version = '>= 2.5'
|
17
17
|
s.license = 'MIT'
|
18
18
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
19
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
@@ -43,19 +43,20 @@ Encrypted Assertions require the xmlenc gem. See the example in the Controller
|
|
43
43
|
section of the README.
|
44
44
|
INST
|
45
45
|
|
46
|
-
s.add_dependency('activesupport', '>=
|
46
|
+
s.add_dependency('activesupport', '>= 5.2')
|
47
47
|
s.add_dependency('builder', '>= 3.0')
|
48
48
|
s.add_dependency('nokogiri', '>= 1.6.2')
|
49
|
+
s.add_dependency('xmlenc', '>= 0.7.1')
|
50
|
+
s.add_dependency('rexml')
|
49
51
|
|
50
52
|
s.add_development_dependency('rake')
|
51
53
|
s.add_development_dependency('simplecov')
|
52
54
|
s.add_development_dependency('rspec', '>= 3.7.0')
|
53
55
|
s.add_development_dependency('ruby-saml', '>= 1.7.2')
|
54
|
-
s.add_development_dependency('rails', '>=
|
55
|
-
s.add_development_dependency('activeresource', '>=
|
56
|
+
s.add_development_dependency('rails', '>= 5.2')
|
57
|
+
s.add_development_dependency('activeresource', '>= 5.1')
|
56
58
|
s.add_development_dependency('capybara', '>= 2.16')
|
57
59
|
s.add_development_dependency('timecop', '>= 0.8')
|
58
|
-
s.add_development_dependency('xmlenc', '>= 0.6.4')
|
59
60
|
s.add_development_dependency('appraisal')
|
60
61
|
s.add_development_dependency('byebug')
|
61
62
|
end
|
@@ -19,6 +19,9 @@ module SamlIdp
|
|
19
19
|
key_transport: 'rsa-oaep-mgf1p',
|
20
20
|
}
|
21
21
|
end
|
22
|
+
let(:session_expiry) { nil }
|
23
|
+
let(:name_id_formats_opt) { nil }
|
24
|
+
let(:asserted_attributes_opt) { nil }
|
22
25
|
subject { described_class.new(
|
23
26
|
reference_id,
|
24
27
|
issuer_uri,
|
@@ -103,6 +106,76 @@ module SamlIdp
|
|
103
106
|
expect(encrypted_xml).to_not match(audience_uri)
|
104
107
|
end
|
105
108
|
|
109
|
+
describe "with name_id_formats_opt" do
|
110
|
+
let(:name_id_formats_opt) {
|
111
|
+
{
|
112
|
+
persistent: -> (principal) {
|
113
|
+
principal.unique_identifier
|
114
|
+
}
|
115
|
+
}
|
116
|
+
}
|
117
|
+
it "delegates name_id_formats to opts" do
|
118
|
+
UserWithUniqueId = Struct.new(:unique_identifier, :email, :asserted_attributes)
|
119
|
+
principal = UserWithUniqueId.new('unique_identifier_123456', 'foo@example.com', { emailAddress: { getter: :email } })
|
120
|
+
builder = described_class.new(
|
121
|
+
reference_id,
|
122
|
+
issuer_uri,
|
123
|
+
principal,
|
124
|
+
audience_uri,
|
125
|
+
saml_request_id,
|
126
|
+
saml_acs_url,
|
127
|
+
algorithm,
|
128
|
+
authn_context_classref,
|
129
|
+
expiry,
|
130
|
+
encryption_opts,
|
131
|
+
session_expiry,
|
132
|
+
name_id_formats_opt,
|
133
|
+
asserted_attributes_opt
|
134
|
+
)
|
135
|
+
Timecop.travel(Time.zone.local(2010, 6, 1, 13, 0, 0)) do
|
136
|
+
expect(builder.raw).to eq("<Assertion xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"_abc\" IssueInstant=\"2010-06-01T13:00:00Z\" Version=\"2.0\"><Issuer>http://sportngin.com</Issuer><Subject><NameID Format=\"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent\">unique_identifier_123456</NameID><SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><SubjectConfirmationData InResponseTo=\"123\" NotOnOrAfter=\"2010-06-01T13:03:00Z\" Recipient=\"http://saml.acs.url\"></SubjectConfirmationData></SubjectConfirmation></Subject><Conditions NotBefore=\"2010-06-01T12:59:55Z\" NotOnOrAfter=\"2010-06-01T16:00:00Z\"><AudienceRestriction><Audience>http://example.com</Audience></AudienceRestriction></Conditions><AuthnStatement AuthnInstant=\"2010-06-01T13:00:00Z\" SessionIndex=\"_abc\"><AuthnContext><AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef></AuthnContext></AuthnStatement><AttributeStatement><Attribute Name=\"emailAddress\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:uri\" FriendlyName=\"emailAddress\"><AttributeValue>foo@example.com</AttributeValue></Attribute></AttributeStatement></Assertion>")
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "with asserted_attributes_opt" do
|
142
|
+
let(:asserted_attributes_opt) {
|
143
|
+
{
|
144
|
+
'GivenName' => {
|
145
|
+
getter: :first_name
|
146
|
+
},
|
147
|
+
'SurName' => {
|
148
|
+
getter: -> (principal) {
|
149
|
+
principal.last_name
|
150
|
+
}
|
151
|
+
}
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
it "delegates asserted_attributes to opts" do
|
156
|
+
UserWithName = Struct.new(:email, :first_name, :last_name)
|
157
|
+
principal = UserWithName.new('foo@example.com', 'George', 'Washington')
|
158
|
+
builder = described_class.new(
|
159
|
+
reference_id,
|
160
|
+
issuer_uri,
|
161
|
+
principal,
|
162
|
+
audience_uri,
|
163
|
+
saml_request_id,
|
164
|
+
saml_acs_url,
|
165
|
+
algorithm,
|
166
|
+
authn_context_classref,
|
167
|
+
expiry,
|
168
|
+
encryption_opts,
|
169
|
+
session_expiry,
|
170
|
+
name_id_formats_opt,
|
171
|
+
asserted_attributes_opt
|
172
|
+
)
|
173
|
+
Timecop.travel(Time.zone.local(2010, 6, 1, 13, 0, 0)) do
|
174
|
+
expect(builder.raw).to eq("<Assertion xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"_abc\" IssueInstant=\"2010-06-01T13:00:00Z\" Version=\"2.0\"><Issuer>http://sportngin.com</Issuer><Subject><NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress\">foo@example.com</NameID><SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><SubjectConfirmationData InResponseTo=\"123\" NotOnOrAfter=\"2010-06-01T13:03:00Z\" Recipient=\"http://saml.acs.url\"></SubjectConfirmationData></SubjectConfirmation></Subject><Conditions NotBefore=\"2010-06-01T12:59:55Z\" NotOnOrAfter=\"2010-06-01T16:00:00Z\"><AudienceRestriction><Audience>http://example.com</Audience></AudienceRestriction></Conditions><AuthnStatement AuthnInstant=\"2010-06-01T13:00:00Z\" SessionIndex=\"_abc\"><AuthnContext><AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef></AuthnContext></AuthnStatement><AttributeStatement><Attribute Name=\"GivenName\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:uri\" FriendlyName=\"GivenName\"><AttributeValue>George</AttributeValue></Attribute><Attribute Name=\"SurName\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:uri\" FriendlyName=\"SurName\"><AttributeValue>Washington</AttributeValue></Attribute></AttributeStatement></Assertion>")
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
106
179
|
describe "with custom session_expiry configuration" do
|
107
180
|
let(:config) { SamlIdp::Configurator.new }
|
108
181
|
before do
|
@@ -2,11 +2,7 @@ class SamlController < ApplicationController
|
|
2
2
|
|
3
3
|
def consume
|
4
4
|
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
|
5
|
-
|
6
|
-
render :text => response.name_id
|
7
|
-
else
|
8
|
-
render :plain => response.name_id
|
9
|
-
end
|
5
|
+
render :plain => response.name_id
|
10
6
|
end
|
11
7
|
|
12
8
|
end
|
@@ -1,9 +1,48 @@
|
|
1
|
-
class SamlIdpController <
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
class SamlIdpController < ApplicationController
|
2
|
+
include SamlIdp::Controller
|
3
|
+
|
4
|
+
before_action :validate_saml_request, only: [:new, :create, :logout]
|
5
|
+
|
6
|
+
def new
|
7
|
+
render template: "saml_idp/idp/new"
|
8
|
+
end
|
9
|
+
|
10
|
+
def show
|
11
|
+
render xml: SamlIdp.metadata.signed
|
12
|
+
end
|
13
|
+
|
14
|
+
def create
|
15
|
+
unless params[:email].blank? && params[:password].blank?
|
16
|
+
person = idp_authenticate(params[:email], params[:password])
|
17
|
+
if person.nil?
|
18
|
+
@saml_idp_fail_msg = "Incorrect email or password."
|
19
|
+
else
|
20
|
+
@saml_response = idp_make_saml_response(person)
|
21
|
+
render :template => "saml_idp/idp/saml_post", :layout => false
|
22
|
+
return
|
23
|
+
end
|
24
|
+
end
|
25
|
+
render :template => "saml_idp/idp/new"
|
26
|
+
end
|
27
|
+
|
28
|
+
def logout
|
29
|
+
idp_logout
|
30
|
+
@saml_response = idp_make_saml_response(nil)
|
31
|
+
render :template => "saml_idp/idp/saml_post", :layout => false
|
32
|
+
end
|
33
|
+
|
34
|
+
def idp_logout
|
35
|
+
raise NotImplementedError
|
36
|
+
end
|
37
|
+
private :idp_logout
|
38
|
+
|
39
|
+
def idp_authenticate(email, password)
|
40
|
+
{ :email => email }
|
41
|
+
end
|
42
|
+
protected :idp_authenticate
|
43
|
+
|
44
|
+
def idp_make_saml_response(person)
|
45
|
+
encode_response(person[:email])
|
46
|
+
end
|
47
|
+
protected :idp_make_saml_response
|
9
48
|
end
|
@@ -1,22 +1,18 @@
|
|
1
1
|
<% if @saml_idp_fail_msg %>
|
2
2
|
<div id="saml_idp_fail_msg" class="flash error"><%= @saml_idp_fail_msg %></div>
|
3
3
|
<% end %>
|
4
|
-
|
5
4
|
<%= form_tag do %>
|
6
5
|
<%= hidden_field_tag("SAMLRequest", params[:SAMLRequest]) %>
|
7
6
|
<%= hidden_field_tag("RelayState", params[:RelayState]) %>
|
8
|
-
|
9
7
|
<p>
|
10
8
|
<%= label_tag :email %>
|
11
9
|
<%= email_field_tag :email, params[:email], :autocapitalize => "off", :autocorrect => "off", :autofocus => "autofocus", :spellcheck => "false", :size => 30, :class => "email_pwd txt" %>
|
12
10
|
</p>
|
13
|
-
|
14
11
|
<p>
|
15
12
|
<%= label_tag :password %>
|
16
13
|
<%= password_field_tag :password, params[:password], :autocapitalize => "off", :autocorrect => "off", :spellcheck => "false", :size => 30, :class => "email_pwd txt" %>
|
17
14
|
</p>
|
18
|
-
|
19
15
|
<p>
|
20
16
|
<%= submit_tag "Sign in", :class => "button big blueish" %>
|
21
17
|
</p>
|
22
|
-
<% end %>
|
18
|
+
<% end %>
|
@@ -29,4 +29,6 @@ RailsApp::Application.configure do
|
|
29
29
|
# Log the query plan for queries taking more than this (works
|
30
30
|
# with SQLite, MySQL, and PostgreSQL)
|
31
31
|
#config.active_record.auto_explain_threshold_in_seconds = 0.5
|
32
|
+
|
33
|
+
config.hosts << "foo.example.com" if config.respond_to?(:hosts)
|
32
34
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -67,4 +67,4 @@ end
|
|
67
67
|
SamlIdp::Default::SERVICE_PROVIDER[:metadata_url] = 'https://example.com/meta'
|
68
68
|
SamlIdp::Default::SERVICE_PROVIDER[:response_hosts] = ['foo.example.com']
|
69
69
|
SamlIdp::Default::SERVICE_PROVIDER[:assertion_consumer_logout_service_url] = 'https://foo.example.com/saml/logout'
|
70
|
-
Capybara.default_host = "https://
|
70
|
+
Capybara.default_host = "https://foo.example.com"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: saml_idp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Phenow
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-07-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '5.2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '5.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: builder
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +52,34 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 1.6.2
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: xmlenc
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.7.1
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.7.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rexml
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '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'
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
84
|
name: rake
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,28 +142,28 @@ dependencies:
|
|
114
142
|
requirements:
|
115
143
|
- - ">="
|
116
144
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
145
|
+
version: '5.2'
|
118
146
|
type: :development
|
119
147
|
prerelease: false
|
120
148
|
version_requirements: !ruby/object:Gem::Requirement
|
121
149
|
requirements:
|
122
150
|
- - ">="
|
123
151
|
- !ruby/object:Gem::Version
|
124
|
-
version: '
|
152
|
+
version: '5.2'
|
125
153
|
- !ruby/object:Gem::Dependency
|
126
154
|
name: activeresource
|
127
155
|
requirement: !ruby/object:Gem::Requirement
|
128
156
|
requirements:
|
129
157
|
- - ">="
|
130
158
|
- !ruby/object:Gem::Version
|
131
|
-
version: '
|
159
|
+
version: '5.1'
|
132
160
|
type: :development
|
133
161
|
prerelease: false
|
134
162
|
version_requirements: !ruby/object:Gem::Requirement
|
135
163
|
requirements:
|
136
164
|
- - ">="
|
137
165
|
- !ruby/object:Gem::Version
|
138
|
-
version: '
|
166
|
+
version: '5.1'
|
139
167
|
- !ruby/object:Gem::Dependency
|
140
168
|
name: capybara
|
141
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,20 +192,6 @@ dependencies:
|
|
164
192
|
- - ">="
|
165
193
|
- !ruby/object:Gem::Version
|
166
194
|
version: '0.8'
|
167
|
-
- !ruby/object:Gem::Dependency
|
168
|
-
name: xmlenc
|
169
|
-
requirement: !ruby/object:Gem::Requirement
|
170
|
-
requirements:
|
171
|
-
- - ">="
|
172
|
-
- !ruby/object:Gem::Version
|
173
|
-
version: 0.6.4
|
174
|
-
type: :development
|
175
|
-
prerelease: false
|
176
|
-
version_requirements: !ruby/object:Gem::Requirement
|
177
|
-
requirements:
|
178
|
-
- - ">="
|
179
|
-
- !ruby/object:Gem::Version
|
180
|
-
version: 0.6.4
|
181
195
|
- !ruby/object:Gem::Dependency
|
182
196
|
name: appraisal
|
183
197
|
requirement: !ruby/object:Gem::Requirement
|
@@ -215,9 +229,6 @@ files:
|
|
215
229
|
- Gemfile
|
216
230
|
- LICENSE
|
217
231
|
- README.md
|
218
|
-
- app/controllers/saml_idp/idp_controller.rb
|
219
|
-
- app/views/saml_idp/idp/new.html.erb
|
220
|
-
- app/views/saml_idp/idp/saml_post.html.erb
|
221
232
|
- lib/saml_idp.rb
|
222
233
|
- lib/saml_idp/algorithmable.rb
|
223
234
|
- lib/saml_idp/assertion_builder.rb
|
@@ -281,6 +292,8 @@ files:
|
|
281
292
|
- spec/rails_app/app/mailers/.gitkeep
|
282
293
|
- spec/rails_app/app/models/.gitkeep
|
283
294
|
- spec/rails_app/app/views/layouts/application.html.erb
|
295
|
+
- spec/rails_app/app/views/saml_idp/idp/new.html.erb
|
296
|
+
- spec/rails_app/app/views/saml_idp/idp/saml_post.html.erb
|
284
297
|
- spec/rails_app/config.ru
|
285
298
|
- spec/rails_app/config/application.rb
|
286
299
|
- spec/rails_app/config/boot.rb
|
@@ -352,7 +365,7 @@ metadata:
|
|
352
365
|
homepage_uri: https://github.com/saml-idp/saml_idp
|
353
366
|
source_code_uri: https://github.com/saml-idp/saml_idp
|
354
367
|
bug_tracker_uri: https://github.com/saml-idp/saml_idp/issues
|
355
|
-
documentation_uri: http://rdoc.info/gems/saml_idp/0.
|
368
|
+
documentation_uri: http://rdoc.info/gems/saml_idp/0.14.0
|
356
369
|
post_install_message: |
|
357
370
|
If you're just recently updating saml_idp - please be aware we've changed the default
|
358
371
|
certificate. See the PR and a description of why we've done this here:
|
@@ -376,7 +389,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
376
389
|
requirements:
|
377
390
|
- - ">="
|
378
391
|
- !ruby/object:Gem::Version
|
379
|
-
version: '2.
|
392
|
+
version: '2.5'
|
380
393
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
381
394
|
requirements:
|
382
395
|
- - ">="
|
@@ -422,6 +435,8 @@ test_files:
|
|
422
435
|
- spec/rails_app/app/mailers/.gitkeep
|
423
436
|
- spec/rails_app/app/models/.gitkeep
|
424
437
|
- spec/rails_app/app/views/layouts/application.html.erb
|
438
|
+
- spec/rails_app/app/views/saml_idp/idp/new.html.erb
|
439
|
+
- spec/rails_app/app/views/saml_idp/idp/saml_post.html.erb
|
425
440
|
- spec/rails_app/config.ru
|
426
441
|
- spec/rails_app/config/application.rb
|
427
442
|
- spec/rails_app/config/boot.rb
|
@@ -1,59 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module SamlIdp
|
4
|
-
class IdpController < ActionController::Base
|
5
|
-
include SamlIdp::Controller
|
6
|
-
|
7
|
-
unloadable unless Rails::VERSION::MAJOR >= 4
|
8
|
-
protect_from_forgery
|
9
|
-
|
10
|
-
if Rails::VERSION::MAJOR >= 4
|
11
|
-
before_action :validate_saml_request, only: [:new, :create]
|
12
|
-
else
|
13
|
-
before_filter :validate_saml_request, only: [:new, :create]
|
14
|
-
end
|
15
|
-
|
16
|
-
def new
|
17
|
-
render template: "saml_idp/idp/new"
|
18
|
-
end
|
19
|
-
|
20
|
-
def show
|
21
|
-
render xml: SamlIdp.metadata.signed
|
22
|
-
end
|
23
|
-
|
24
|
-
def create
|
25
|
-
unless params[:email].blank? && params[:password].blank?
|
26
|
-
person = idp_authenticate(params[:email], params[:password])
|
27
|
-
if person.nil?
|
28
|
-
@saml_idp_fail_msg = "Incorrect email or password."
|
29
|
-
else
|
30
|
-
@saml_response = idp_make_saml_response(person)
|
31
|
-
render :template => "saml_idp/idp/saml_post", :layout => false
|
32
|
-
return
|
33
|
-
end
|
34
|
-
end
|
35
|
-
render :template => "saml_idp/idp/new"
|
36
|
-
end
|
37
|
-
|
38
|
-
def logout
|
39
|
-
idp_logout
|
40
|
-
@saml_response = idp_make_saml_response(nil)
|
41
|
-
render :template => "saml_idp/idp/saml_post", :layout => false
|
42
|
-
end
|
43
|
-
|
44
|
-
def idp_logout
|
45
|
-
raise NotImplementedError
|
46
|
-
end
|
47
|
-
private :idp_logout
|
48
|
-
|
49
|
-
def idp_authenticate(email, password)
|
50
|
-
raise NotImplementedError
|
51
|
-
end
|
52
|
-
protected :idp_authenticate
|
53
|
-
|
54
|
-
def idp_make_saml_response(person)
|
55
|
-
raise NotImplementedError
|
56
|
-
end
|
57
|
-
protected :idp_make_saml_response
|
58
|
-
end
|
59
|
-
end
|