saml_idp 0.12.0 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://travis-ci.org/saml-idp/saml_idp.svg)](https://travis-ci.org/saml-idp/saml_idp)
|
6
6
|
[![Gem Version](https://badge.fury.io/rb/saml_idp.svg)](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
|