icn_saml_idp 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/LICENSE +22 -0
- data/README.md +238 -0
- data/app/controllers/saml_idp/idp_controller.rb +53 -0
- data/app/views/saml_idp/idp/new.html.erb +22 -0
- data/app/views/saml_idp/idp/saml_post.html.erb +14 -0
- data/lib/saml_idp.rb +92 -0
- data/lib/saml_idp/algorithmable.rb +19 -0
- data/lib/saml_idp/assertion_builder.rb +172 -0
- data/lib/saml_idp/attribute_decorator.rb +27 -0
- data/lib/saml_idp/attributeable.rb +24 -0
- data/lib/saml_idp/configurator.rb +48 -0
- data/lib/saml_idp/controller.rb +128 -0
- data/lib/saml_idp/default.rb +49 -0
- data/lib/saml_idp/encryptor.rb +86 -0
- data/lib/saml_idp/engine.rb +5 -0
- data/lib/saml_idp/hashable.rb +26 -0
- data/lib/saml_idp/incoming_metadata.rb +144 -0
- data/lib/saml_idp/logout_builder.rb +42 -0
- data/lib/saml_idp/logout_request_builder.rb +34 -0
- data/lib/saml_idp/logout_response_builder.rb +35 -0
- data/lib/saml_idp/metadata_builder.rb +160 -0
- data/lib/saml_idp/name_id_formatter.rb +68 -0
- data/lib/saml_idp/persisted_metadata.rb +10 -0
- data/lib/saml_idp/request.rb +180 -0
- data/lib/saml_idp/response_builder.rb +62 -0
- data/lib/saml_idp/saml_response.rb +79 -0
- data/lib/saml_idp/service_provider.rb +76 -0
- data/lib/saml_idp/signable.rb +131 -0
- data/lib/saml_idp/signature_builder.rb +42 -0
- data/lib/saml_idp/signed_info_builder.rb +88 -0
- data/lib/saml_idp/version.rb +4 -0
- data/lib/saml_idp/xml_security.rb +181 -0
- data/saml_idp.gemspec +65 -0
- data/spec/acceptance/acceptance_helper.rb +9 -0
- data/spec/acceptance/idp_controller_spec.rb +16 -0
- data/spec/lib/saml_idp/algorithmable_spec.rb +48 -0
- data/spec/lib/saml_idp/assertion_builder_spec.rb +106 -0
- data/spec/lib/saml_idp/attribute_decorator_spec.rb +53 -0
- data/spec/lib/saml_idp/configurator_spec.rb +43 -0
- data/spec/lib/saml_idp/controller_spec.rb +94 -0
- data/spec/lib/saml_idp/encryptor_spec.rb +27 -0
- data/spec/lib/saml_idp/logout_request_builder_spec.rb +41 -0
- data/spec/lib/saml_idp/logout_response_builder_spec.rb +41 -0
- data/spec/lib/saml_idp/metadata_builder_spec.rb +19 -0
- data/spec/lib/saml_idp/name_id_formatter_spec.rb +42 -0
- data/spec/lib/saml_idp/request_spec.rb +106 -0
- data/spec/lib/saml_idp/response_builder_spec.rb +42 -0
- data/spec/lib/saml_idp/saml_response_spec.rb +68 -0
- data/spec/lib/saml_idp/service_provider_spec.rb +27 -0
- data/spec/lib/saml_idp/signable_spec.rb +77 -0
- data/spec/lib/saml_idp/signature_builder_spec.rb +19 -0
- data/spec/lib/saml_idp/signed_info_builder_spec.rb +25 -0
- data/spec/rails_app/.gitignore +15 -0
- data/spec/rails_app/README.rdoc +261 -0
- data/spec/rails_app/Rakefile +7 -0
- data/spec/rails_app/app/assets/images/rails.png +0 -0
- data/spec/rails_app/app/assets/javascripts/application.js +15 -0
- data/spec/rails_app/app/assets/stylesheets/application.css +13 -0
- data/spec/rails_app/app/controllers/application_controller.rb +3 -0
- data/spec/rails_app/app/controllers/saml_controller.rb +8 -0
- data/spec/rails_app/app/controllers/saml_idp_controller.rb +11 -0
- data/spec/rails_app/app/helpers/application_helper.rb +2 -0
- data/spec/rails_app/app/mailers/.gitkeep +0 -0
- data/spec/rails_app/app/models/.gitkeep +0 -0
- data/spec/rails_app/app/views/layouts/application.html.erb +14 -0
- data/spec/rails_app/config.ru +4 -0
- data/spec/rails_app/config/application.rb +60 -0
- data/spec/rails_app/config/boot.rb +6 -0
- data/spec/rails_app/config/database.yml +25 -0
- data/spec/rails_app/config/environment.rb +5 -0
- data/spec/rails_app/config/environments/development.rb +37 -0
- data/spec/rails_app/config/environments/production.rb +67 -0
- data/spec/rails_app/config/environments/test.rb +37 -0
- data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_app/config/initializers/inflections.rb +15 -0
- data/spec/rails_app/config/initializers/mime_types.rb +5 -0
- data/spec/rails_app/config/initializers/secret_token.rb +7 -0
- data/spec/rails_app/config/initializers/session_store.rb +8 -0
- data/spec/rails_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/rails_app/config/locales/en.yml +5 -0
- data/spec/rails_app/config/routes.rb +6 -0
- data/spec/rails_app/db/seeds.rb +7 -0
- data/spec/rails_app/doc/README_FOR_APP +2 -0
- data/spec/rails_app/lib/assets/.gitkeep +0 -0
- data/spec/rails_app/lib/tasks/.gitkeep +0 -0
- data/spec/rails_app/log/.gitkeep +0 -0
- data/spec/rails_app/public/404.html +26 -0
- data/spec/rails_app/public/422.html +26 -0
- data/spec/rails_app/public/500.html +25 -0
- data/spec/rails_app/public/favicon.ico +0 -0
- data/spec/rails_app/public/index.html +241 -0
- data/spec/rails_app/public/robots.txt +5 -0
- data/spec/rails_app/script/rails +6 -0
- data/spec/rails_app/test/fixtures/.gitkeep +0 -0
- data/spec/rails_app/test/functional/.gitkeep +0 -0
- data/spec/rails_app/test/integration/.gitkeep +0 -0
- data/spec/rails_app/test/performance/browsing_test.rb +12 -0
- data/spec/rails_app/test/test_helper.rb +13 -0
- data/spec/rails_app/test/unit/.gitkeep +0 -0
- data/spec/rails_app/vendor/assets/javascripts/.gitkeep +0 -0
- data/spec/rails_app/vendor/assets/stylesheets/.gitkeep +0 -0
- data/spec/rails_app/vendor/plugins/.gitkeep +0 -0
- data/spec/spec_helper.rb +49 -0
- data/spec/support/certificates/certificate1 +12 -0
- data/spec/support/certificates/r1_certificate2_base64 +1 -0
- data/spec/support/responses/adfs_response_sha1.xml +46 -0
- data/spec/support/responses/adfs_response_sha256.xml +46 -0
- data/spec/support/responses/adfs_response_sha384.xml +46 -0
- data/spec/support/responses/adfs_response_sha512.xml +46 -0
- data/spec/support/responses/logoutresponse_fixtures.rb +67 -0
- data/spec/support/responses/no_signature_ns.xml +48 -0
- data/spec/support/responses/open_saml_response.xml +56 -0
- data/spec/support/responses/r1_response6.xml.base64 +1 -0
- data/spec/support/responses/response1.xml.base64 +1 -0
- data/spec/support/responses/response2.xml.base64 +79 -0
- data/spec/support/responses/response3.xml.base64 +66 -0
- data/spec/support/responses/response4.xml.base64 +93 -0
- data/spec/support/responses/response5.xml.base64 +102 -0
- data/spec/support/responses/response_with_ampersands.xml +139 -0
- data/spec/support/responses/response_with_ampersands.xml.base64 +93 -0
- data/spec/support/responses/simple_saml_php.xml +71 -0
- data/spec/support/responses/starfield_response.xml.base64 +1 -0
- data/spec/support/responses/wrapped_response_2.xml.base64 +150 -0
- data/spec/support/saml_request_macros.rb +38 -0
- data/spec/support/security_helpers.rb +61 -0
- data/spec/xml_security_spec.rb +137 -0
- metadata +465 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ea7921102fe43bfbba0086ee40cfd65f7783e1ad
|
4
|
+
data.tar.gz: 542bc9cc6358ccb222f47a164506354afa70e0ab
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0602eb253d25f4dac6106ff2dc1f2a8ebd2eb4cab128d4ec588ce1e528427374d7a8f44d44d888266548f3c035ecc44eb735cb6e16e83be3d3cccfdc9a2d30df
|
7
|
+
data.tar.gz: 10aa55f5cdc46f505910a8fcb1b86c4a843ec046be75d33ca29ea3baacb4cea1e3b1bb8518120fc22079a19f7365bfc86d47675bcd75927c673386c56905169a
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Sport Ngin (http://sportngin.com)
|
2
|
+
Portions Copyright (c) 2010 OneLogin, LLC
|
3
|
+
Portions Copyright (c) 2012 Lawrence Pit (http://lawrencepit.com)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,238 @@
|
|
1
|
+
# Ruby SAML Identity Provider (IdP)
|
2
|
+
Forked from https://github.com/lawrencepit/ruby-saml-idp
|
3
|
+
|
4
|
+
[![Build Status](https://travis-ci.org/sportngin/saml_idp.png)](https://travis-ci.org/sportngin/saml_idp)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/saml_idp.png)](http://badge.fury.io/rb/saml_idp)
|
6
|
+
|
7
|
+
The ruby SAML Identity Provider library is for implementing the server side of SAML authentication. It allows
|
8
|
+
your application to act as an IdP (Identity Provider) using the
|
9
|
+
[SAML v2.0](http://en.wikipedia.org/wiki/Security_Assertion_Markup_Language)
|
10
|
+
protocol. It provides a means for managing authentication requests and confirmation responses for SPs (Service Providers).
|
11
|
+
|
12
|
+
This was originally setup by @lawrencepit to test SAML Clients. I took it closer to a real
|
13
|
+
SAML IDP implementation.
|
14
|
+
|
15
|
+
# Installation and Usage
|
16
|
+
|
17
|
+
Add this to your Gemfile:
|
18
|
+
|
19
|
+
gem 'saml_idp'
|
20
|
+
|
21
|
+
## Not using rails?
|
22
|
+
Include `SamlIdp::Controller` and see the examples that use rails. It should be straightforward for you.
|
23
|
+
|
24
|
+
Basically you call `decode_request(params[:SAMLRequest])` on an incoming request and then use the value
|
25
|
+
`saml_acs_url` to determine the source for which you need to authenticate a user. How you authenticate
|
26
|
+
a user is entirely up to you.
|
27
|
+
|
28
|
+
Once a user has successfully authenticated on your system send the Service Provider a SAMLReponse by
|
29
|
+
posting to `saml_acs_url` the parameter `SAMLResponse` with the return value from a call to
|
30
|
+
`encode_response(user_email)`.
|
31
|
+
|
32
|
+
## Using rails?
|
33
|
+
Add to your `routes.rb` file, for example:
|
34
|
+
|
35
|
+
``` ruby
|
36
|
+
get '/saml/auth' => 'saml_idp#new'
|
37
|
+
get '/saml/metadata' => 'saml_idp#show'
|
38
|
+
post '/saml/auth' => 'saml_idp#create'
|
39
|
+
match '/saml/logout' => 'saml_idp#logout', via: [:get, :post, :delete]
|
40
|
+
```
|
41
|
+
|
42
|
+
Create a controller that looks like this, customize to your own situation:
|
43
|
+
|
44
|
+
``` ruby
|
45
|
+
class SamlIdpController
|
46
|
+
include SamlIdp::IdpController
|
47
|
+
|
48
|
+
def idp_authenticate(email, password) # not using params intentionally
|
49
|
+
user = User.by_email(email).first
|
50
|
+
user && user.valid_password?(password) ? user : nil
|
51
|
+
end
|
52
|
+
private :idp_authenticate
|
53
|
+
|
54
|
+
def idp_make_saml_response(found_user) # not using params intentionally
|
55
|
+
# NOTE encryption is optional
|
56
|
+
encode_response found_user, encryption: {
|
57
|
+
cert: saml_request.service_provider.cert,
|
58
|
+
block_encryption: 'aes256-cbc',
|
59
|
+
key_transport: 'rsa-oaep-mgf1p'
|
60
|
+
}
|
61
|
+
end
|
62
|
+
private :idp_make_saml_response
|
63
|
+
|
64
|
+
def idp_logout
|
65
|
+
user = User.by_email(saml_request.name_id)
|
66
|
+
user.logout
|
67
|
+
end
|
68
|
+
private :idp_logout
|
69
|
+
end
|
70
|
+
```
|
71
|
+
|
72
|
+
## Configuration
|
73
|
+
|
74
|
+
Be sure to load a file like this during your app initialization:
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
SamlIdp.configure do |config|
|
78
|
+
base = "http://example.com"
|
79
|
+
|
80
|
+
config.x509_certificate = <<-CERT
|
81
|
+
-----BEGIN CERTIFICATE-----
|
82
|
+
CERTIFICATE DATA
|
83
|
+
-----END CERTIFICATE-----
|
84
|
+
CERT
|
85
|
+
|
86
|
+
config.secret_key = <<-CERT
|
87
|
+
-----BEGIN RSA PRIVATE KEY-----
|
88
|
+
KEY DATA
|
89
|
+
-----END RSA PRIVATE KEY-----
|
90
|
+
CERT
|
91
|
+
|
92
|
+
# config.password = "secret_key_password"
|
93
|
+
# config.algorithm = :sha256
|
94
|
+
# config.organization_name = "Your Organization"
|
95
|
+
# config.organization_url = "http://example.com"
|
96
|
+
# config.base_saml_location = "#{base}/saml"
|
97
|
+
# config.reference_id_generator # Default: -> { UUID.generate }
|
98
|
+
# config.attribute_service_location = "#{base}/saml/attributes"
|
99
|
+
# config.single_service_post_location = "#{base}/saml/auth"
|
100
|
+
|
101
|
+
# Principal (e.g. User) is passed in when you `encode_response`
|
102
|
+
#
|
103
|
+
# config.name_id.formats # =>
|
104
|
+
# { # All 2.0
|
105
|
+
# email_address: -> (principal) { principal.email_address },
|
106
|
+
# transient: -> (principal) { principal.id },
|
107
|
+
# persistent: -> (p) { p.id },
|
108
|
+
# }
|
109
|
+
# OR
|
110
|
+
#
|
111
|
+
# {
|
112
|
+
# "1.1" => {
|
113
|
+
# email_address: -> (principal) { principal.email_address },
|
114
|
+
# },
|
115
|
+
# "2.0" => {
|
116
|
+
# transient: -> (principal) { principal.email_address },
|
117
|
+
# persistent: -> (p) { p.id },
|
118
|
+
# },
|
119
|
+
# }
|
120
|
+
|
121
|
+
# If Principal responds to a method called `asserted_attributes`
|
122
|
+
# the return value of that method will be used in lieu of the
|
123
|
+
# attributes defined here in the global space. This allows for
|
124
|
+
# per-user attribute definitions.
|
125
|
+
#
|
126
|
+
## EXAMPLE **
|
127
|
+
# class User
|
128
|
+
# def asserted_attributes
|
129
|
+
# {
|
130
|
+
# phone: { getter: :phone },
|
131
|
+
# email: {
|
132
|
+
# getter: :email,
|
133
|
+
# name_format: Saml::XML::Namespaces::Formats::NameId::EMAIL_ADDRESS,
|
134
|
+
# name_id_format: Saml::XML::Namespaces::Formats::NameId::EMAIL_ADDRESS
|
135
|
+
# }
|
136
|
+
# }
|
137
|
+
# end
|
138
|
+
# end
|
139
|
+
#
|
140
|
+
# If you have a method called `asserted_attributes` in your Principal class,
|
141
|
+
# there is no need to define it here in the config.
|
142
|
+
|
143
|
+
# config.attributes # =>
|
144
|
+
# {
|
145
|
+
# <friendly_name> => { # required (ex "eduPersonAffiliation")
|
146
|
+
# "name" => <attrname> # required (ex "urn:oid:1.3.6.1.4.1.5923.1.1.1.1")
|
147
|
+
# "name_format" => "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", # not required
|
148
|
+
# "getter" => ->(principal) { # not required
|
149
|
+
# principal.get_eduPersonAffiliation # If no "getter" defined, will try
|
150
|
+
# } # `principal.eduPersonAffiliation`, or no values will
|
151
|
+
# } # be output
|
152
|
+
#
|
153
|
+
## EXAMPLE ##
|
154
|
+
# config.attributes = {
|
155
|
+
# GivenName: {
|
156
|
+
# getter: :first_name,
|
157
|
+
# },
|
158
|
+
# SurName: {
|
159
|
+
# getter: :last_name,
|
160
|
+
# },
|
161
|
+
# }
|
162
|
+
## EXAMPLE ##
|
163
|
+
|
164
|
+
# config.technical_contact.company = "Example"
|
165
|
+
# config.technical_contact.given_name = "Jonny"
|
166
|
+
# config.technical_contact.sur_name = "Support"
|
167
|
+
# config.technical_contact.telephone = "55555555555"
|
168
|
+
# config.technical_contact.email_address = "example@example.com"
|
169
|
+
|
170
|
+
service_providers = {
|
171
|
+
"some-issuer-url.com/saml" => {
|
172
|
+
fingerprint: "9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D",
|
173
|
+
metadata_url: "http://some-issuer-url.com/saml/metadata"
|
174
|
+
},
|
175
|
+
}
|
176
|
+
|
177
|
+
# `identifier` is the entity_id or issuer of the Service Provider,
|
178
|
+
# settings is an IncomingMetadata object which has a to_h method that needs to be persisted
|
179
|
+
config.service_provider.metadata_persister = ->(identifier, settings) {
|
180
|
+
fname = identifier.to_s.gsub(/\/|:/,"_")
|
181
|
+
`mkdir -p #{Rails.root.join("cache/saml/metadata")}`
|
182
|
+
File.open Rails.root.join("cache/saml/metadata/#{fname}"), "r+b" do |f|
|
183
|
+
Marshal.dump settings.to_h, f
|
184
|
+
end
|
185
|
+
}
|
186
|
+
|
187
|
+
# `identifier` is the entity_id or issuer of the Service Provider,
|
188
|
+
# `service_provider` is a ServiceProvider object. Based on the `identifier` or the
|
189
|
+
# `service_provider` you should return the settings.to_h from above
|
190
|
+
config.service_provider.persisted_metadata_getter = ->(identifier, service_provider){
|
191
|
+
fname = identifier.to_s.gsub(/\/|:/,"_")
|
192
|
+
`mkdir -p #{Rails.root.join("cache/saml/metadata")}`
|
193
|
+
full_filename = Rails.root.join("cache/saml/metadata/#{fname}")
|
194
|
+
if File.file?(full_filename)
|
195
|
+
File.open full_filename, "rb" do |f|
|
196
|
+
Marshal.load f
|
197
|
+
end
|
198
|
+
end
|
199
|
+
}
|
200
|
+
|
201
|
+
# Find ServiceProvider metadata_url and fingerprint based on our settings
|
202
|
+
config.service_provider.finder = ->(issuer_or_entity_id) do
|
203
|
+
service_providers[issuer_or_entity_id]
|
204
|
+
end
|
205
|
+
end
|
206
|
+
```
|
207
|
+
|
208
|
+
# Keys and Secrets
|
209
|
+
To generate the SAML Response it uses a default X.509 certificate and secret key... which isn't so secret.
|
210
|
+
You can find them in `SamlIdp::Default`. The X.509 certificate is valid until year 2032.
|
211
|
+
Obviously you shouldn't use these if you intend to use this in production environments. In that case,
|
212
|
+
within the controller set the properties `x509_certificate` and `secret_key` using a `prepend_before_filter`
|
213
|
+
callback within the current request context or set them globally via the `SamlIdp.config.x509_certificate`
|
214
|
+
and `SamlIdp.config.secret_key` properties.
|
215
|
+
|
216
|
+
The fingerprint to use, if you use the default X.509 certificate of this gem, is:
|
217
|
+
|
218
|
+
```
|
219
|
+
9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D
|
220
|
+
```
|
221
|
+
|
222
|
+
|
223
|
+
# Service Providers
|
224
|
+
To act as a Service Provider which generates SAML Requests and can react to SAML Responses use the
|
225
|
+
excellent [ruby-saml](https://github.com/onelogin/ruby-saml) gem.
|
226
|
+
|
227
|
+
|
228
|
+
# Author
|
229
|
+
Jon Phenow, me@jphenow.com
|
230
|
+
|
231
|
+
Lawrence Pit, lawrence.pit@gmail.com, lawrencepit.com, @lawrencepit
|
232
|
+
|
233
|
+
# Copyright
|
234
|
+
Copyright (c) 2012 Sport Ngin.
|
235
|
+
Portions Copyright (c) 2010 OneLogin, LLC
|
236
|
+
Portions Copyright (c) 2012 Lawrence Pit (http://lawrencepit.com)
|
237
|
+
|
238
|
+
See LICENSE for details.
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module SamlIdp
|
3
|
+
class IdpController < ActionController::Base
|
4
|
+
include SamlIdp::Controller
|
5
|
+
|
6
|
+
unloadable unless Rails::VERSION::MAJOR >= 4
|
7
|
+
protect_from_forgery
|
8
|
+
before_filter :validate_saml_request, only: [:new, :create]
|
9
|
+
|
10
|
+
def new
|
11
|
+
render template: "saml_idp/idp/new"
|
12
|
+
end
|
13
|
+
|
14
|
+
def show
|
15
|
+
render xml: SamlIdp.metadata.signed
|
16
|
+
end
|
17
|
+
|
18
|
+
def create
|
19
|
+
unless params[:email].blank? && params[:password].blank?
|
20
|
+
person = idp_authenticate(params[:email], params[:password])
|
21
|
+
if person.nil?
|
22
|
+
@saml_idp_fail_msg = "Incorrect email or password."
|
23
|
+
else
|
24
|
+
@saml_response = idp_make_saml_response(person)
|
25
|
+
render :template => "saml_idp/idp/saml_post", :layout => false
|
26
|
+
return
|
27
|
+
end
|
28
|
+
end
|
29
|
+
render :template => "saml_idp/idp/new"
|
30
|
+
end
|
31
|
+
|
32
|
+
def logout
|
33
|
+
idp_logout
|
34
|
+
@saml_response = idp_make_saml_response(nil)
|
35
|
+
render :template => "saml_idp/idp/saml_post", :layout => false
|
36
|
+
end
|
37
|
+
|
38
|
+
def idp_logout
|
39
|
+
raise NotImplementedError
|
40
|
+
end
|
41
|
+
private :idp_logout
|
42
|
+
|
43
|
+
def idp_authenticate(email, password)
|
44
|
+
raise NotImplementedError
|
45
|
+
end
|
46
|
+
protected :idp_authenticate
|
47
|
+
|
48
|
+
def idp_make_saml_response(person)
|
49
|
+
raise NotImplementedError
|
50
|
+
end
|
51
|
+
protected :idp_make_saml_response
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<% if @saml_idp_fail_msg %>
|
2
|
+
<div id="saml_idp_fail_msg" class="flash error"><%= @saml_idp_fail_msg %></div>
|
3
|
+
<% end %>
|
4
|
+
|
5
|
+
<%= form_tag do %>
|
6
|
+
<%= hidden_field_tag("SAMLRequest", params[:SAMLRequest]) %>
|
7
|
+
<%= hidden_field_tag("RelayState", params[:RelayState]) %>
|
8
|
+
|
9
|
+
<p>
|
10
|
+
<%= label_tag :email %>
|
11
|
+
<%= email_field_tag :email, params[:email], :autocapitalize => "off", :autocorrect => "off", :autofocus => "autofocus", :spellcheck => "false", :size => 30, :class => "email_pwd txt" %>
|
12
|
+
</p>
|
13
|
+
|
14
|
+
<p>
|
15
|
+
<%= label_tag :password %>
|
16
|
+
<%= password_field_tag :password, params[:password], :autocapitalize => "off", :autocorrect => "off", :spellcheck => "false", :size => 30, :class => "email_pwd txt" %>
|
17
|
+
</p>
|
18
|
+
|
19
|
+
<p>
|
20
|
+
<%= submit_tag "Sign in", :class => "button big blueish" %>
|
21
|
+
</p>
|
22
|
+
<% end %>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
6
|
+
</head>
|
7
|
+
<body onload="document.forms[0].submit();" style="visibility:hidden;">
|
8
|
+
<%= form_tag(saml_acs_url) do %>
|
9
|
+
<%= hidden_field_tag("SAMLResponse", @saml_response) %>
|
10
|
+
<%= hidden_field_tag("RelayState", params[:RelayState]) %>
|
11
|
+
<%= submit_tag "Submit" %>
|
12
|
+
<% end %>
|
13
|
+
</body>
|
14
|
+
</html>
|
data/lib/saml_idp.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module SamlIdp
|
3
|
+
require 'active_support/all'
|
4
|
+
require 'saml_idp/saml_response'
|
5
|
+
require 'saml_idp/xml_security'
|
6
|
+
require 'saml_idp/configurator'
|
7
|
+
require 'saml_idp/controller'
|
8
|
+
require 'saml_idp/default'
|
9
|
+
require 'saml_idp/metadata_builder'
|
10
|
+
require 'saml_idp/version'
|
11
|
+
require 'saml_idp/engine' if defined?(::Rails) && Rails::VERSION::MAJOR > 2
|
12
|
+
|
13
|
+
def self.config
|
14
|
+
@config ||= SamlIdp::Configurator.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.configure
|
18
|
+
yield config
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.metadata
|
22
|
+
@metadata ||= MetadataBuilder.new(config)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# TODO Needs extraction out
|
27
|
+
module Saml
|
28
|
+
module XML
|
29
|
+
module Namespaces
|
30
|
+
METADATA = "urn:oasis:names:tc:SAML:2.0:metadata"
|
31
|
+
ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
|
32
|
+
SIGNATURE = "http://www.w3.org/2000/09/xmldsig#"
|
33
|
+
PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"
|
34
|
+
|
35
|
+
module Statuses
|
36
|
+
SUCCESS = "urn:oasis:names:tc:SAML:2.0:status:Success"
|
37
|
+
end
|
38
|
+
|
39
|
+
module Consents
|
40
|
+
UNSPECIFIED = "urn:oasis:names:tc:SAML:2.0:consent:unspecified"
|
41
|
+
end
|
42
|
+
|
43
|
+
module AuthnContext
|
44
|
+
module ClassRef
|
45
|
+
PASSWORD = "urn:oasis:names:tc:SAML:2.0:ac:classes:Password"
|
46
|
+
PASSWORD_PROTECTED = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module Methods
|
51
|
+
BEARER = "urn:oasis:names:tc:SAML:2.0:cm:bearer"
|
52
|
+
end
|
53
|
+
|
54
|
+
module Formats
|
55
|
+
module Attr
|
56
|
+
URI = "urn:oasis:names:tc:SAML:2.0:attrname-format:uri"
|
57
|
+
end
|
58
|
+
|
59
|
+
module NameId
|
60
|
+
EMAIL_ADDRESS = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
61
|
+
TRANSIENT = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
|
62
|
+
PERSISTENT = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Document < Nokogiri::XML::Document
|
68
|
+
def signed?
|
69
|
+
!!xpath("//ds:Signature", ds: signature_namespace).first
|
70
|
+
end
|
71
|
+
|
72
|
+
def valid_signature?(fingerprint)
|
73
|
+
signed? &&
|
74
|
+
signed_document.validate(fingerprint, :soft)
|
75
|
+
end
|
76
|
+
|
77
|
+
def signed_document
|
78
|
+
SamlIdp::XMLSecurity::SignedDocument.new(to_xml)
|
79
|
+
end
|
80
|
+
|
81
|
+
def signature_namespace
|
82
|
+
Namespaces::SIGNATURE
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_xml
|
86
|
+
super(
|
87
|
+
save_with: Nokogiri::XML::Node::SaveOptions::AS_XML | Nokogiri::XML::Node::SaveOptions::NO_DECLARATION
|
88
|
+
).strip
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|