omniauth-mpassid 0.1.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 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: []