omniauth-mpassid 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0ee0bd0d81ad7593a430bdb8ee94ab733d7b396231ac284b7b99b1914d28ff84
4
+ data.tar.gz: 6e1ef387f6e6f96e8557ced45f60817f8bb6536f7b2e53d5a1da471b82041829
5
+ SHA512:
6
+ metadata.gz: e4c92ca00791f451b3a86cd8415abd1b45876e3c1515e0d4b6c038a4b293da544ffebef5b43e2c068af8991d3d22384454453b1e3b2f596f2cf129fc41148634
7
+ data.tar.gz: cf982a1632ee51213463d30ac7d4c1d8f6c06d6e7a499004840b84f85ae731eac46add9d13280953009f1a7294af7659a2b136e8a7bb0975c19d04d6f1f9388b
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2019 Mainio Tech Ltd.
2
+
3
+ MIT License
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,268 @@
1
+ # OmniAuth MPASSid (SAML 2.0)
2
+
3
+ [![Build Status](https://travis-ci.com/mainio/omniauth-mpassid.svg?branch=master)](https://travis-ci.com/mainio/omniauth-mpassid)
4
+ [![codecov](https://codecov.io/gh/mainio/omniauth-mpassid/branch/master/graph/badge.svg)](https://codecov.io/gh/mainio/omniauth-mpassid)
5
+
6
+ This is an unofficial OmniAuth strategy for authenticating with the MPASSid
7
+ identification service used by comprehensive schools and secondary education
8
+ facilities in Finland. This gem is mostly a configuration wrapper around
9
+ [`omniauth-saml`](https://github.com/omniauth/omniauth-saml) which uses
10
+ [`ruby-saml`](https://github.com/onelogin/ruby-saml) for SAML 2.0 based
11
+ authentication implementation with identity providers, such as MPASSid.
12
+
13
+ The gem can be used to hook Ruby/Rails applications to the MPASSid
14
+ identification service. It does not provide any strong authorization features
15
+ out of the box, as it does not know anything about the application users, but
16
+ those can be implemented using this gem and the data provided by the MPASSid
17
+ identification responses.
18
+
19
+ The gem has been developed by [Mainio Tech](https://www.mainiotech.fi/).
20
+
21
+ The development has been sponsored by the
22
+ [City of Helsinki](https://www.hel.fi/).
23
+
24
+ The MPASSid service is owned by the Ministry of the Education and Culture in
25
+ Finland and operated by CSC - Tieteen tietotekniikan keskus Oy. Neither of these
26
+ parties are related to this gem in any way, nor do they provide technical
27
+ support for it. Please contact the gem maintainers in case you find any issues
28
+ with it.
29
+
30
+ ## Preparation
31
+
32
+ In order to start using the MPASSid authentication endpoints, you will need to
33
+ send a join request to the MPASSid operator for them to configure the identity
34
+ provider server for your service. Please refer to the MPASSid documentation on
35
+ how to join the service:
36
+
37
+ https://wiki.eduuni.fi/display/CSCMPASSID/Testipalveluun+liittyminen
38
+
39
+ Follow the instructions for the SAML2 protocol.
40
+
41
+ The details that you need to send to MPASSid are similar to the following
42
+ information (apply to your service's domain) for non-Rails+Devise applications:
43
+
44
+ - SAML2 entity ID: https://www.service.fi/auth/mpassid/metadata
45
+ - Callback URL (ACS): https://www.service.fi/auth/mpassid/callback
46
+
47
+ ### Rails and Devise
48
+
49
+ When applying this gem to Rails and Devise, the URLs can also include a path
50
+ prefix to separate the scope of the authentication requests. For example, if
51
+ you are using a `:user` scope with Devise, the URLs would look like following:
52
+
53
+ - SAML2 entity ID: https://www.service.fi/users/auth/mpassid/metadata
54
+ - Callback URL (ACS): https://www.service.fi/users/auth/mpassid/callback
55
+
56
+ ## Installation and Configuration
57
+
58
+ This gem has been only tested and used with Rails applications using Devise, so
59
+ this installation guide only covers that part. In case you are interested to
60
+ learn how you can use this with other frameworks, please refer to the
61
+ [`omniauth-saml`](https://github.com/omniauth/omniauth-saml) documentation and
62
+ apply it to your needs (changing the strategy name to `:mpassid` and strategy
63
+ class to `OmniAuth::Strategies::MPASSid`).
64
+
65
+ To install this gem, add the following to your Gemfile:
66
+
67
+ ```ruby
68
+ gem 'omniauth-mpassid'
69
+ ```
70
+
71
+ For configuring the strategy for Devise, add the following in your
72
+ `config/initializers/devise.rb` file:
73
+
74
+ ```ruby
75
+ Devise.setup do |config|
76
+ config.omniauth :mpassid,
77
+ # The mode needs to be either :production or :test depending on which
78
+ # MPASSid enviroment you want to hook into. Please note that you will need
79
+ # to complete the preparation phases even for the test environment.
80
+ mode: :test, # :production (default, can be omitted) or :test
81
+ # The service provider entity ID that needs to match the ID you have sent to
82
+ # the MPASSid operator with your joining request.
83
+ sp_entity_id: 'https://www.service.fi/auth/mpassid/metadata'
84
+ end
85
+ ```
86
+
87
+ ## Identification Responses
88
+
89
+ The user's data is transmitted from MPASSid in the SAML authentication
90
+ response. This data will be available in the OmniAuth
91
+ [extra hash](https://github.com/omniauth/omniauth/wiki/Auth-Hash-Schema#schema-10-and-later).
92
+
93
+ In order to access the response data, you can fetch the OmniAuth extra has and
94
+ the corresponding user data in the OmniAuth callback handler, e.g. in Rails
95
+ Devise controllers as follows:
96
+
97
+ ```ruby
98
+ def saml_attributes
99
+ raw_hash = request.env["omniauth.auth"]
100
+ extra_hash = raw_hash[:extra]
101
+
102
+ extra_hash[:saml_attributes]
103
+ end
104
+ ```
105
+
106
+ ### Personal Information Transmitted From MPASSid
107
+
108
+ The user's personal information transmitted from MPASSid can be found under
109
+ the `:saml_attributes` key in the OmniAuth extra hash described above.
110
+
111
+ This attributes hash will contain the keys described in this following
112
+ sub-sections. The keys marked as `(undocumented)` are not described in the
113
+ MPASSid's own documentation but are available at least in some SAML responses.
114
+
115
+ See also the MPASSid data models documentation for more information:
116
+
117
+ https://wiki.eduuni.fi/display/CSCMPASSID/Data+models
118
+
119
+ The attributes can be either single or multi type defining whether they can
120
+ have a single or multiple values. The single type values are strings and multi
121
+ type values are arrays of string when the value exists in the SAML response.
122
+ When the value was not returned from the identity provider's endpoint, the value
123
+ is `nil` for both types.
124
+
125
+ #### `:given_name`
126
+
127
+ - SAML URI: urn:oid:2.5.4.42
128
+ - SAML FriendlyName: givenName
129
+ - Type: Single (`String`)
130
+
131
+ The first/given name of the user.
132
+
133
+ #### `:first_names`
134
+
135
+ - SAML URI: http://eidas.europa.eu/attributes/naturalperson/CurrentGivenName
136
+ - SAML FriendlyName: firstName
137
+ - Type: Single (`String`)
138
+
139
+ All the first/given names of the user.
140
+
141
+ #### `:last_name`
142
+
143
+ - SAML URI: urn:oid:2.5.4.4
144
+ - SAML FriendlyName: sn
145
+ - Type: Single (`String`)
146
+
147
+ The last/family name of the user.
148
+
149
+ #### `:municipality_code`
150
+
151
+ - SAML URI: urn:mpass.id:municipalityCode
152
+ - SAML FriendlyName: municipalityCode
153
+ - Type: Multi (`Array`)
154
+
155
+ The municipality codes of the authenticated user.
156
+
157
+ See:
158
+
159
+ http://tilastokeskus.fi/meta/luokitukset/kunta/001-2017/index.html
160
+
161
+ #### `:municipality_name`
162
+
163
+ - SAML URI: one of the following (first found attribute)
164
+ * urn:mpass.id:municipality
165
+ * urn:educloudalliance.org:municipality
166
+ - SAML FriendlyName: one of the following (first found attribute)
167
+ * N/A
168
+ * ecaMunicipality
169
+ - Type: Multi (`Array`)
170
+
171
+ The human-readable names of the municipalities of the authenticated user.
172
+
173
+ #### `:school_code`
174
+
175
+ - SAML URI: urn:mpass.id:municipalityCode
176
+ - SAML FriendlyName: N/A
177
+ - Type: Multi (`Array`)
178
+
179
+ The school codes of the authenticated user.
180
+
181
+ See (JSON format):
182
+
183
+ For the list of codes, see:
184
+
185
+ - JSON format: https://virkailija.opintopolku.fi/koodisto-service/rest/json/oppilaitosnumero/koodi
186
+ - XML format: https://virkailija.opintopolku.fi/koodisto-service/rest/oppilaitosnumero/koodi
187
+
188
+ An example for a single school code (04647), JSON format:
189
+
190
+ https://virkailija.opintopolku.fi/koodisto-service/rest/codeelement/oppilaitosnumero_04647
191
+
192
+ #### `:school_name`
193
+
194
+ - SAML URI: urn:mpass.id:school
195
+ - SAML FriendlyName: school
196
+ - Type: Multi (`Array`)
197
+
198
+ The human-readable names of the schools of the authenticated user.
199
+
200
+ #### `:class`
201
+
202
+ - SAML URI: one of the following (first found attribute)
203
+ * urn:mpass.id:class
204
+ * urn:educloudalliance.org:group
205
+ - SAML FriendlyName: one of the following (first found attribute)
206
+ * N/A
207
+ * ecaGroup
208
+ - Type: Multi (`Array`)
209
+
210
+ The class/group-information of the authenticated user.
211
+
212
+ For instance: 8A or 3B.
213
+
214
+ #### `:class_level`
215
+
216
+ - SAML URI: urn:mpass.id:classLevel
217
+ - SAML FriendlyName: N/A
218
+ - Type: Multi (`Array`)
219
+
220
+ The class/level-information of the authenticated user.
221
+
222
+ For instance 8 or 3.
223
+
224
+ #### `:role`
225
+
226
+ - SAML URI: one of the following (first found attribute)
227
+ * urn:mpass.id:role
228
+ * urn:educloudalliance.org:structuredRole
229
+ - SAML FriendlyName: one of the following (first found attribute)
230
+ * N/A
231
+ * ecaStructuredRole
232
+ - Type: Multi (`Array`)
233
+
234
+ The roles of the user in four parts, divided with a semicolon (;) character.
235
+ First municipality, followed by school code, group and role in the group.
236
+
237
+ For instance Helsinki;32132;9A;Oppilas.
238
+
239
+ #### `:role_name` (undocumented)
240
+
241
+ - SAML URI: urn:educloudalliance.org:role
242
+ - SAML FriendlyName: ecaRole
243
+ - Type: Multi (`Array`)
244
+
245
+ NOTE: This attribute is undocumented by MPASSid.
246
+
247
+ The human readable names of the role (in Finnish).
248
+
249
+ For instance Oppilas.
250
+
251
+ #### `:funet_person_learner_id` (undocumented)
252
+
253
+ - SAML URI: urn:oid:1.3.6.1.4.1.16161.1.1.27
254
+ - SAML FriendlyName: N/A
255
+ - Type: Single (`String`)
256
+
257
+ NOTE: This attribute is undocumented by MPASSid.
258
+
259
+ 11-digit identifier, which may be used to identify a person while storing,
260
+ managing or transferring personal data.
261
+
262
+ See:
263
+
264
+ https://wiki.eduuni.fi/display/CSCHAKA/funetEduPersonSchema2dot2#funetEduPersonSchema2dot2-funetEduPersonLearnerId
265
+
266
+ ## License
267
+
268
+ MIT, see [LICENSE](LICENSE).
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ # Run all tests, with coverage report
6
+ RSpec::Core::RakeTask.new(:coverage) do |t|
7
+ ENV['CODECOV'] = '1'
8
+ t.verbose = false
9
+ end
10
+
11
+ # Run all tests, include all
12
+ RSpec::Core::RakeTask.new(:spec) do |t|
13
+ t.verbose = false
14
+ end
15
+
16
+ # Default
17
+ task default: :coverage
@@ -0,0 +1,309 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'omniauth-saml'
4
+
5
+ module OmniAuth
6
+ module Strategies
7
+ class MPASSid < SAML
8
+ # Mode:
9
+ # :production - MPASSid production environment
10
+ # :test - MPASSid test environment
11
+ option :mode, :production
12
+
13
+ # The request attributes for MPASSid
14
+ option :request_attributes, [
15
+ # The unique identifier of the authenticated user. Currently recommended
16
+ # identifier for identifying the user. NOTE: will change if the user
17
+ # moves to another user registry.
18
+ # (single value)
19
+ {
20
+ name: 'urn:mpass.id:uid',
21
+ name_format: 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri'
22
+ },
23
+ # Funet EDU person learner ID
24
+ # (single value)
25
+ {
26
+ name: 'urn:oid:1.3.6.1.4.1.16161.1.1.27',
27
+ name_format: 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri'
28
+ },
29
+ # The first/given name of the user.
30
+ # (single value)
31
+ {
32
+ name: 'urn:oid:2.5.4.42',
33
+ name_format: 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
34
+ friendly_name: 'givenName'
35
+ },
36
+ # All the first/given names of the user.
37
+ # (single value)
38
+ {
39
+ name: 'http://eidas.europa.eu/attributes/naturalperson/CurrentGivenName',
40
+ name_format: 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
41
+ friendly_name: 'firstName'
42
+ },
43
+ # The last/family name of the user.
44
+ # (single value)
45
+ {
46
+ name: 'urn:oid:2.5.4.4',
47
+ name_format: 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
48
+ friendly_name: 'sn'
49
+ },
50
+ # The municipality code of the authenticated user. See
51
+ # http://tilastokeskus.fi/meta/luokitukset/kunta/001-2017/index.html
52
+ # for mappings in Finland.
53
+ # (multi value)
54
+ {
55
+ name: 'urn:mpass.id:municipalityCode',
56
+ name_format: 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
57
+ friendly_name: 'municipalityCode'
58
+ },
59
+ # The human-readable name of the municipality of the authenticated user.
60
+ # (multi value)
61
+ {
62
+ name: 'urn:mpass.id:municipality',
63
+ name_format: 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri'
64
+ },
65
+ {
66
+ name: 'urn:educloudalliance.org:municipality',
67
+ name_format: 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
68
+ friendly_name: 'ecaMunicipality'
69
+ },
70
+ # The school code of the authenticated user. See
71
+ # https://virkailija.opintopolku.fi/koodisto-service/rest/json/oppilaitosnumero/koodi
72
+ # (JSON format)
73
+ # https://virkailija.opintopolku.fi/koodisto-service/rest/oppilaitosnumero/koodi
74
+ # (XML format)
75
+ # for the mappings in Finland. For example,
76
+ # https://virkailija.opintopolku.fi/koodisto-service/rest/codeelement/oppilaitosnumero_04647
77
+ # for school code 04647.
78
+ # (multi value)
79
+ {
80
+ name: 'urn:mpass.id:schoolCode',
81
+ name_format: 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri'
82
+ },
83
+ # The human-readable name of the school of the authenticated user.
84
+ # (multi value)
85
+ {
86
+ name: 'urn:mpass.id:school',
87
+ name_format: 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
88
+ friendly_name: 'school'
89
+ },
90
+ # The class/group-information of the authenticated user.
91
+ # For instance: 8A or 3B.
92
+ # (multi value)
93
+ {
94
+ name: 'urn:mpass.id:class',
95
+ name_format: 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri'
96
+ },
97
+ {
98
+ name: 'urn:educloudalliance.org:group',
99
+ name_format: 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
100
+ friendly_name: 'ecaGroup'
101
+ },
102
+ # The class/level-information of the authenticated user.
103
+ # For instance 8 or 3.
104
+ # (multi value)
105
+ {
106
+ name: 'urn:mpass.id:classLevel',
107
+ name_format: 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri'
108
+ },
109
+ # The role name of the user.
110
+ # For instance Oppilas.
111
+ # (multi value)
112
+ {
113
+ name: 'urn:educloudalliance.org:role',
114
+ name_format: 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
115
+ friendly_name: 'ecaRole'
116
+ },
117
+ # The role of the user in four parts, divided with a semicolon (;)
118
+ # character. First municipality, followed by school code, group and role
119
+ # in the group.
120
+ # For instance Helsinki;32132;9A;Oppilas.
121
+ # (multi value)
122
+ {
123
+ name: 'urn:mpass.id:role',
124
+ name_format: 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri'
125
+ },
126
+ {
127
+ name: 'urn:educloudalliance.org:structuredRole',
128
+ name_format: 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
129
+ friendly_name: 'ecaStructuredRole'
130
+ }
131
+ ]
132
+
133
+ # Maps the SAML attributes to OmniAuth info schema:
134
+ # https://github.com/omniauth/omniauth/wiki/Auth-Hash-Schema#schema-10-and-later
135
+ option(
136
+ :attribute_statements,
137
+ # Given name or all first names (in case given name is not found)
138
+ first_name: ['urn:oid:2.5.4.42', 'http://eidas.europa.eu/attributes/naturalperson/CurrentGivenName'],
139
+ last_name: ['urn:oid:2.5.4.4'],
140
+ # The municipality of the person (literal format in Finnish)
141
+ location: ['urn:mpass.id:municipality', 'urn:educloudalliance.org:municipality']
142
+ )
143
+
144
+ info do
145
+ # Generate the full name to the info hash
146
+ first_name = find_attribute_by(
147
+ [
148
+ 'urn:oid:2.5.4.42',
149
+ 'http://eidas.europa.eu/attributes/naturalperson/CurrentGivenName'
150
+ ]
151
+ )
152
+ last_name = find_attribute_by(['urn:oid:2.5.4.4'])
153
+ display_name = "#{first_name} #{last_name}".strip
154
+ display_name = nil if display_name.length.zero?
155
+
156
+ found_attributes = [[:name, display_name]]
157
+
158
+ # Default functionality from omniauth-saml
159
+ found_attributes += options.attribute_statements.map do |key, values|
160
+ attribute = find_attribute_by(values)
161
+ [key, attribute]
162
+ end
163
+
164
+ Hash[found_attributes]
165
+ end
166
+
167
+ option(
168
+ :security_settings,
169
+ digest_method: XMLSecurity::Document::SHA256,
170
+ signature_method: XMLSecurity::Document::RSA_SHA256
171
+ )
172
+
173
+ # The attribute key maps to the SAML URIs so that we have more descriptive
174
+ # attribute keys available for use. These will be mapped to the OmniAuth
175
+ # `extra` information hash under the `:saml_attributes` key.
176
+ option(
177
+ :saml_attributes_map,
178
+ given_name: ['urn:oid:2.5.4.42'],
179
+ first_names: ['http://eidas.europa.eu/attributes/naturalperson/CurrentGivenName'],
180
+ last_name: ['urn:oid:2.5.4.4'],
181
+ municipality_code: {
182
+ name: ['urn:mpass.id:municipalityCode'],
183
+ type: :multi
184
+ },
185
+ municipality_name: {
186
+ name: ['urn:mpass.id:municipality', 'urn:educloudalliance.org:municipality'],
187
+ type: :multi
188
+ },
189
+ school_code: {
190
+ name: ['urn:mpass.id:schoolCode'],
191
+ type: :multi
192
+ },
193
+ school_name: {
194
+ name: ['urn:mpass.id:school'],
195
+ type: :multi
196
+ },
197
+ class: {
198
+ name: ['urn:mpass.id:class', 'urn:educloudalliance.org:group'],
199
+ type: :multi
200
+ },
201
+ class_level: {
202
+ name: ['urn:mpass.id:classLevel'],
203
+ type: :multi
204
+ },
205
+ role: {
206
+ name: ['urn:mpass.id:role', 'urn:educloudalliance.org:structuredRole'],
207
+ type: :multi
208
+ },
209
+ role_name: {
210
+ name: ['urn:educloudalliance.org:role'],
211
+ type: :multi
212
+ },
213
+ # Extra (undocumented)
214
+ funet_person_learner_id: ['urn:oid:1.3.6.1.4.1.16161.1.1.27']
215
+ )
216
+
217
+ # Defines the SAML attribute from which to determine the OmniAuth `uid`.
218
+ # NOTE:
219
+ # In case the user moves to another user registry, this will change.
220
+ # However, there is no other unique identifier passed along the SAML
221
+ # attributes that we could rely on, so this is the best bet.
222
+ option :uid_attribute, 'urn:mpass.id:uid'
223
+
224
+ # Add the SAML attributes to the extra hash for easier access.
225
+ extra { {saml_attributes: saml_attributes} }
226
+
227
+ def initialize(app, *args, &block)
228
+ super
229
+
230
+ # Add the MPASSid options to the local options, most of which are
231
+ # fetched from the metadata. The options array is the one that gets
232
+ # priority in case it overrides some of the metadata or locally defined
233
+ # option values.
234
+ @options = OmniAuth::Strategy::Options.new(
235
+ mpassid_options.merge(options)
236
+ )
237
+ end
238
+
239
+ # This method can be used externally to fetch information about the
240
+ # response, e.g. in case of failures.
241
+ def response_object
242
+ return nil unless request.params['SAMLResponse']
243
+
244
+ with_settings do |settings|
245
+ response = OneLogin::RubySaml::Response.new(
246
+ request.params['SAMLResponse'],
247
+ options_for_response_object.merge(settings: settings)
248
+ )
249
+ response.attributes['fingerprint'] = settings.idp_cert_fingerprint
250
+ response
251
+ end
252
+ end
253
+
254
+ private
255
+
256
+ def idp_metadata_url
257
+ case options.mode
258
+ when :test
259
+ 'https://mpass-proxy-test.csc.fi/idp/shibboleth'
260
+ else
261
+ 'https://mpass-proxy.csc.fi/idp/shibboleth'
262
+ end
263
+ end
264
+
265
+ def mpassid_options
266
+ idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
267
+
268
+ # Returns OneLogin::RubySaml::Settings prepopulated with idp metadata
269
+ # We are using the redirect binding for the SSO and SLO URLs as these
270
+ # are the ones expected by omniauth-saml. Otherwise the default would be
271
+ # the first one defined in the IdP metadata, which would be the
272
+ # HTTP-POST binding.
273
+ settings = idp_metadata_parser.parse_remote_to_hash(
274
+ idp_metadata_url,
275
+ true,
276
+ sso_binding: ['urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect']
277
+ )
278
+
279
+ # Define the security settings as there are some defaults that need to be
280
+ # modified
281
+ security_defaults = OneLogin::RubySaml::Settings::DEFAULTS[:security]
282
+ settings[:security] = security_defaults.merge(options.security_settings)
283
+
284
+ settings
285
+ end
286
+
287
+ def saml_attributes
288
+ {}.tap do |attrs|
289
+ options.saml_attributes_map.each do |target, definition|
290
+ unless definition.is_a?(Hash)
291
+ definition = {
292
+ name: definition,
293
+ type: :single
294
+ }
295
+ end
296
+
297
+ value = definition[:name].map do |key|
298
+ @attributes.public_send(definition[:type], key)
299
+ end.reject(&:nil?).first
300
+
301
+ attrs[target] = value
302
+ end
303
+ end
304
+ end
305
+ end
306
+ end
307
+ end
308
+
309
+ OmniAuth.config.add_camelization 'mpassid', 'MPASSid'
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAuth
4
+ module MPASSid
5
+ module Test
6
+ class CertificateGenerator
7
+ def private_key
8
+ @private_key ||= OpenSSL::PKey::RSA.new(2048)
9
+ end
10
+
11
+ def certificate
12
+ @certificate ||= begin
13
+ public_key = private_key.public_key
14
+
15
+ subject = '/C=FI/O=Test/OU=Test/CN=Test'
16
+
17
+ cert = OpenSSL::X509::Certificate.new
18
+ cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
19
+ cert.not_before = Time.now
20
+ cert.not_after = Time.now + 365 * 24 * 60 * 60
21
+ cert.public_key = public_key
22
+ cert.serial = 0x0
23
+ cert.version = 2
24
+
25
+ inject_certificate_extensions(cert)
26
+
27
+ cert.sign(private_key, OpenSSL::Digest::SHA1.new)
28
+
29
+ cert
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def inject_certificate_extensions(cert)
36
+ ef = OpenSSL::X509::ExtensionFactory.new
37
+ ef.subject_certificate = cert
38
+ ef.issuer_certificate = cert
39
+ cert.extensions = [
40
+ ef.create_extension('basicConstraints', 'CA:TRUE', true),
41
+ ef.create_extension('subjectKeyIdentifier', 'hash')
42
+ ]
43
+ cert.add_extension ef.create_extension(
44
+ 'authorityKeyIdentifier',
45
+ 'keyid:always,issuer:always'
46
+ )
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAuth
4
+ module MPASSid
5
+ module Test
6
+ class Utility
7
+ def self.inflate_xml(encoded_deflated_xml)
8
+ deflated_xml = Base64.decode64(encoded_deflated_xml)
9
+ Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(deflated_xml)
10
+ end
11
+
12
+ def self.signed_xml(raw_xml_file, opts)
13
+ raw_xml = IO.read(raw_xml_file)
14
+ signed_xml_from_string(raw_xml, opts)
15
+ end
16
+
17
+ def self.signed_xml_from_string(raw_xml, opts)
18
+ sign_xml_element(
19
+ raw_xml,
20
+ opts[:sign_certificate],
21
+ opts[:sign_private_key]
22
+ )
23
+ end
24
+
25
+ def self.sign_xml_element(element, sign_certificate, sign_key)
26
+ doc = XMLSecurity::Document.new(element)
27
+ doc.sign_document(
28
+ sign_key,
29
+ sign_certificate,
30
+ XMLSecurity::Document::RSA_SHA256,
31
+ XMLSecurity::Document::SHA256
32
+ )
33
+ # Move the signature to the correct position, otherwise schema
34
+ # validation does not work because the internal logic of ruby-saml
35
+ # cannot handle custom element names (saml2:Issuer instead of
36
+ # saml:Issuer).
37
+ signature = doc.delete_element('//ds:Signature')
38
+ issuer = doc.elements['//saml2:Issuer']
39
+ doc.root.insert_after(issuer, signature)
40
+
41
+ doc.to_s
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'test/certificate_generator'
4
+ require_relative 'test/utility'
5
+
6
+ module OmniAuth
7
+ module MPASSid
8
+ module Test
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAuth
4
+ module MPASSid
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'omniauth-mpassid/version'
4
+ require 'omniauth/strategies/mpassid'
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omniauth-mpassid
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Antti Hukkanen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-08-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: omniauth-saml
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.10.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.10.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '12.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '12.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.8'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rack-test
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.1.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.1.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: webmock
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.6'
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: 3.6.2
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - "~>"
84
+ - !ruby/object:Gem::Version
85
+ version: '3.6'
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: 3.6.2
89
+ - !ruby/object:Gem::Dependency
90
+ name: xmlenc
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: 0.7.1
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: 0.7.1
103
+ - !ruby/object:Gem::Dependency
104
+ name: simplecov
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: 0.16.0
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: 0.16.0
117
+ description: MPASSid identification service integration for OmniAuth.
118
+ email:
119
+ - antti.hukkanen@mainiotech.fi
120
+ executables: []
121
+ extensions: []
122
+ extra_rdoc_files: []
123
+ files:
124
+ - LICENSE
125
+ - README.md
126
+ - Rakefile
127
+ - lib/omniauth-mpassid.rb
128
+ - lib/omniauth-mpassid/test.rb
129
+ - lib/omniauth-mpassid/test/certificate_generator.rb
130
+ - lib/omniauth-mpassid/test/utility.rb
131
+ - lib/omniauth-mpassid/version.rb
132
+ - lib/omniauth/strategies/mpassid.rb
133
+ homepage: https://github.com/mainio/omniauth-mpassid
134
+ licenses:
135
+ - MIT
136
+ metadata: {}
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ requirements: []
152
+ rubygems_version: 3.0.3
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: Provides an MPASSid strategy for OmniAuth.
156
+ test_files: []