omniauth-mpassid 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +268 -0
- data/Rakefile +17 -0
- data/lib/omniauth/strategies/mpassid.rb +309 -0
- data/lib/omniauth-mpassid/test/certificate_generator.rb +51 -0
- data/lib/omniauth-mpassid/test/utility.rb +46 -0
- data/lib/omniauth-mpassid/test.rb +11 -0
- data/lib/omniauth-mpassid/version.rb +7 -0
- data/lib/omniauth-mpassid.rb +4 -0
- metadata +156 -0
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
|
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: []
|