anoubis_sso_client 1.0.1
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +41 -0
- data/Rakefile +15 -0
- data/app/controllers/anoubis_sso_client/application_common.rb +516 -0
- data/app/controllers/anoubis_sso_client/application_controller.rb +5 -0
- data/app/controllers/anoubis_sso_client/data_controller.rb +5 -0
- data/app/controllers/anoubis_sso_client/index_controller.rb +30 -0
- data/app/models/anoubis_sso_client/application_record.rb +125 -0
- data/app/models/anoubis_sso_client/group.rb +101 -0
- data/app/models/anoubis_sso_client/group_menu.rb +107 -0
- data/app/models/anoubis_sso_client/group_user.rb +25 -0
- data/app/models/anoubis_sso_client/menu.rb +220 -0
- data/app/models/anoubis_sso_client/user.rb +91 -0
- data/config/locales/en.yml +28 -0
- data/config/locales/ru.yml +28 -0
- data/config/routes.rb +23 -0
- data/db/migrate/20220302061651_create_anoubis_sso_client_users.rb +15 -0
- data/db/migrate/20220315061539_create_anoubis_sso_client_groups.rb +11 -0
- data/db/migrate/20220413110404_create_anoubis_sso_client_menus.rb +21 -0
- data/db/migrate/20220415131249_create_anoubis_sso_client_group_menus.rb +12 -0
- data/db/migrate/20220420054028_create_anoubis_sso_client_group_users.rb +10 -0
- data/db/seeds.rb +15 -0
- data/lib/anoubis_sso_client/engine.rb +13 -0
- data/lib/anoubis_sso_client/version.rb +4 -0
- data/lib/anoubis_sso_client.rb +7 -0
- metadata +240 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d6d6fc2d74ea7bf5a7702de2638af217e68f7095567ea9a179de4b139a01b5cd
|
4
|
+
data.tar.gz: c5978605ce5ec97d9e8bc5e1f632a15f36cb8a6ce7573b6ce8694adb67b79123
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 658c4bf239c8dcab773f3551a93d6ca1649ea2b0ab5a95ecdadce60ec5ee14bbc59586b56aec6cb92b4e3c96983a405f3d0b23d592c2f538896aceae3dbf56fe
|
7
|
+
data.tar.gz: a01020e0880338c9a014466c3ead470b499505c3216fc45a67f62a56b224e40e1f9d0833f01168436a522b25126e77aea0f1756dde56c881cdd54676b811b32b
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2022 Andrey Ryabov
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# AnoubisSsoClient
|
2
|
+
Short description and motivation.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
How to use my plugin.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem "anoubis_sso_client"
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
```bash
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
```bash
|
21
|
+
$ gem install anoubis_sso_client
|
22
|
+
```
|
23
|
+
|
24
|
+
## Configuration parameters
|
25
|
+
|
26
|
+
This configuration parameters can be placed at files config/application.rb for global configuration or config/environments/<environment>.rb for custom environment configuration.
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
config.anoubis_sso_server = 'https://sso.example.com/' # Full URL of SSO server (*required)
|
30
|
+
config.anoubis_sso_user_model = 'AnoubisSsoClient::User'# Used User model. (By default used AnoubisSsoServer::User model) (*optional)
|
31
|
+
config.anoubis_sso_menu_model = 'AnoubisSsoClient::Menu'# Used Menu model. (By default used AnoubisSsoServer::Menu model) (*optional)
|
32
|
+
config.anoubis_sso_group_model = 'AnoubisSsoClient::Group'# Used Group model. (By default used AnoubisSsoServer::Group model) (*optional)
|
33
|
+
config.anoubis_sso_group_menu_model = 'AnoubisSsoClient::GroupMenu'# Used GroupMenu model. (By default used AnoubisSsoServer::GroupMenu model) (*optional)
|
34
|
+
config.anoubis_sso_group_user_model = 'AnoubisSsoClient::GroupUser'# Used GroupMenu model. (By default used AnoubisSsoServer::GroupUser model) (*optional)
|
35
|
+
```
|
36
|
+
|
37
|
+
## Contributing
|
38
|
+
Contribution directions go here.
|
39
|
+
|
40
|
+
## License
|
41
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rspec/core/rake_task"
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
7
|
+
|
8
|
+
require "rubocop/rake_task"
|
9
|
+
|
10
|
+
RuboCop::RakeTask.new
|
11
|
+
|
12
|
+
task default: %i[spec rubocop]
|
13
|
+
|
14
|
+
APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
|
15
|
+
load 'rails/tasks/engine.rake'
|
@@ -0,0 +1,516 @@
|
|
1
|
+
##
|
2
|
+
# Common functions for all controllers
|
3
|
+
module AnoubisSsoClient::ApplicationCommon
|
4
|
+
## Returns [Anoubis::Etc::Base] global system parameters
|
5
|
+
attr_accessor :etc
|
6
|
+
|
7
|
+
## Returns main SSO server URL.
|
8
|
+
attr_accessor :sso_server
|
9
|
+
|
10
|
+
## Returns SSO JWK data URL
|
11
|
+
attr_accessor :sso_jwk_data_url
|
12
|
+
|
13
|
+
## Returns SSO userinfo URL
|
14
|
+
attr_accessor :sso_userinfo_url
|
15
|
+
|
16
|
+
## Returns Hash of current user
|
17
|
+
attr_accessor :current_user
|
18
|
+
|
19
|
+
## Returns Hash of menu for current user
|
20
|
+
attr_accessor :current_menu
|
21
|
+
|
22
|
+
##
|
23
|
+
# Returns main SSO server URL. Link should be defined in Rails.configuration.anoubis.sso_server configuration parameter
|
24
|
+
# @return [String] link to SSO server
|
25
|
+
def sso_server
|
26
|
+
@sso_server ||= get_sso_server
|
27
|
+
end
|
28
|
+
|
29
|
+
private def get_sso_server
|
30
|
+
begin
|
31
|
+
value = Rails.configuration.anoubis_sso_server
|
32
|
+
rescue StandardError
|
33
|
+
value = ''
|
34
|
+
render json: { error: 'Please setup Rails.configuration.anoubis_sso_server configuration variable' }
|
35
|
+
end
|
36
|
+
|
37
|
+
value
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Returns SSO Menu model.
|
42
|
+
# Can be redefined in Rails.application configuration_anoubis_sso_menu_model configuration parameter.
|
43
|
+
# By default returns {AnoubisSsoClient::Menu} model class
|
44
|
+
# @return [Class] Menu model class
|
45
|
+
def menu_model
|
46
|
+
begin
|
47
|
+
value = Object.const_get Rails.configuration.anoubis_sso_menu_model
|
48
|
+
rescue StandardError
|
49
|
+
value = AnoubisSsoClient::Menu
|
50
|
+
end
|
51
|
+
|
52
|
+
value
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Returns SSO Group model.
|
57
|
+
# Can be redefined in Rails.application configuration_anoubis_sso_group_model configuration parameter.
|
58
|
+
# By default returns {AnoubisSsoClient::Group} model class
|
59
|
+
# @return [Class] Group model class
|
60
|
+
def group_model
|
61
|
+
begin
|
62
|
+
value = Object.const_get Rails.configuration.anoubis_sso_group_model
|
63
|
+
rescue StandardError
|
64
|
+
value = AnoubisSsoClient::Group
|
65
|
+
end
|
66
|
+
|
67
|
+
value
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Returns SSO GroupMenu model.
|
72
|
+
# Can be redefined in Rails.application configuration_anoubis_sso_group_menu_model configuration parameter.
|
73
|
+
# By default returns {AnoubisSsoClient::GroupMenu} model class
|
74
|
+
# @return [Class] GroupMenu model class
|
75
|
+
def group_menu_model
|
76
|
+
begin
|
77
|
+
value = Object.const_get Rails.configuration.anoubis_sso_group_menu_model
|
78
|
+
rescue StandardError
|
79
|
+
value = AnoubisSsoClient::GroupMenu
|
80
|
+
end
|
81
|
+
|
82
|
+
value
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# Returns SSO GroupUser model.
|
87
|
+
# Can be redefined in Rails.application configuration_anoubis_sso_group_user_model configuration parameter.
|
88
|
+
# By default returns {AnoubisSsoClient::GroupUser} model class
|
89
|
+
# @return [Class] GroupUser model class
|
90
|
+
def group_user_model
|
91
|
+
begin
|
92
|
+
value = Object.const_get Rails.configuration.anoubis_sso_group_user_model
|
93
|
+
rescue StandardError
|
94
|
+
value = AnoubisSsoClient::GroupUser
|
95
|
+
end
|
96
|
+
|
97
|
+
value
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# Returns SSO User model.
|
102
|
+
# Can be redefined in Rails.application configuration_anoubis_sso_user_model configuration parameter.
|
103
|
+
# By default returns {AnoubisSsoClient::User} model class
|
104
|
+
# @return [Class] User model class
|
105
|
+
def user_model
|
106
|
+
begin
|
107
|
+
value = Object.const_get Rails.configuration.anoubis_sso_user_model
|
108
|
+
rescue
|
109
|
+
value = AnoubisSsoClient::User
|
110
|
+
end
|
111
|
+
|
112
|
+
value
|
113
|
+
end
|
114
|
+
|
115
|
+
##
|
116
|
+
# Action fires before any other actions
|
117
|
+
def after_anoubis_initialization
|
118
|
+
self.current_user = nil
|
119
|
+
self.current_menu = nil
|
120
|
+
self.sso_jwk_data_url = nil
|
121
|
+
self.sso_userinfo_url = nil
|
122
|
+
if defined? params
|
123
|
+
self.etc = Anoubis::Etc::Base.new({ params: params })
|
124
|
+
else
|
125
|
+
self.etc = Anoubis::Etc::Base.new
|
126
|
+
end
|
127
|
+
|
128
|
+
if access_allowed?
|
129
|
+
options request.method.to_s.upcase
|
130
|
+
else
|
131
|
+
render_error_exit({ error: I18n.t('anoubis.errors.access_not_allowed') })
|
132
|
+
return
|
133
|
+
end
|
134
|
+
|
135
|
+
if authenticate?
|
136
|
+
if authentication
|
137
|
+
if check_menu_access?
|
138
|
+
allow = false
|
139
|
+
if current_menu.key? params[:controller].to_sym
|
140
|
+
etc.menu = Anoubis::Etc::Menu.new current_menu[params[:controller].to_sym]
|
141
|
+
allow = true unless current_menu[params[:controller].to_sym][:access] == 'disable'
|
142
|
+
end
|
143
|
+
unless allow
|
144
|
+
render_error_exit({ error: I18n.t('anoubis.errors.access_not_allowed') })
|
145
|
+
return
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
after_sso_client_initialization
|
152
|
+
end
|
153
|
+
|
154
|
+
##
|
155
|
+
# Procedure fires after initializes all parameters of {AnoubisSsoClient::ApplicationController}
|
156
|
+
def after_sso_client_initialization
|
157
|
+
puts etc.inspect
|
158
|
+
end
|
159
|
+
|
160
|
+
##
|
161
|
+
# Check for site access. By default return true.
|
162
|
+
def access_allowed?
|
163
|
+
true
|
164
|
+
end
|
165
|
+
|
166
|
+
##
|
167
|
+
# Gracefully terminate script execution with code 422 (Unprocessable entity). And JSON data
|
168
|
+
# @param data [Hash] Resulting data
|
169
|
+
# @option data [Integer] :code resulting error code
|
170
|
+
# @option data [String] :error resulting error message
|
171
|
+
def render_error_exit(data = {})
|
172
|
+
result = {
|
173
|
+
result: -1,
|
174
|
+
message: I18n.t('anoubis.error')
|
175
|
+
}
|
176
|
+
|
177
|
+
result[:result] = data[:code] if data.has_key? :code
|
178
|
+
result[:message] = data[:error] if data.has_key? :error
|
179
|
+
|
180
|
+
|
181
|
+
render json: result, status: :unprocessable_entity
|
182
|
+
|
183
|
+
begin
|
184
|
+
exit
|
185
|
+
rescue SystemExit => e
|
186
|
+
puts result[:message]
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
##
|
191
|
+
# Checks if needed user authentication.
|
192
|
+
# @return [Boolean] if true, then user must be authenticated. By default application do not need authorization.
|
193
|
+
def authenticate?
|
194
|
+
true
|
195
|
+
end
|
196
|
+
|
197
|
+
##
|
198
|
+
# Procedure authenticates user in the system
|
199
|
+
def authentication
|
200
|
+
session = get_oauth_session
|
201
|
+
|
202
|
+
unless session
|
203
|
+
render_error_exit code: -2, error: I18n.t('anoubis.errors.session_expired')
|
204
|
+
return
|
205
|
+
end
|
206
|
+
|
207
|
+
self.current_user = session[:user]
|
208
|
+
self.current_menu = session[:menu]
|
209
|
+
end
|
210
|
+
|
211
|
+
##
|
212
|
+
# Check if menu access required
|
213
|
+
# @return [Boolean] menu access requirements
|
214
|
+
def check_menu_access?
|
215
|
+
if controller_name == 'index'
|
216
|
+
if action_name == 'login' || action_name == 'menu' || action_name == 'logout'
|
217
|
+
return false
|
218
|
+
end
|
219
|
+
end
|
220
|
+
return true
|
221
|
+
end
|
222
|
+
|
223
|
+
##
|
224
|
+
# Return OAUTH session for current request. Session name gets from cookies. If session present but it's timeout was expired, then session regenerated.
|
225
|
+
def get_oauth_session
|
226
|
+
begin
|
227
|
+
session = JSON.parse(redis.get("#{redis_prefix}session:#{token}"),{ symbolize_names: true })
|
228
|
+
rescue
|
229
|
+
session = nil
|
230
|
+
end
|
231
|
+
|
232
|
+
return session if session
|
233
|
+
|
234
|
+
puts 'get_oauth_session'
|
235
|
+
|
236
|
+
jwt = check_sso_token
|
237
|
+
|
238
|
+
puts "JWT #{jwt}"
|
239
|
+
|
240
|
+
return nil unless jwt
|
241
|
+
|
242
|
+
ttl = jwt['exp'] - Time.now.utc.to_i
|
243
|
+
|
244
|
+
puts "Time: #{Time.now.to_i} -> #{ttl}"
|
245
|
+
|
246
|
+
return nil if ttl <= 0
|
247
|
+
|
248
|
+
session = {
|
249
|
+
user: {},
|
250
|
+
menu: {}
|
251
|
+
}
|
252
|
+
|
253
|
+
user_data = load_user_from_sso_server
|
254
|
+
|
255
|
+
puts "User data: #{user_data}"
|
256
|
+
|
257
|
+
return nil unless user_data
|
258
|
+
return nil if user_data.key? :error
|
259
|
+
|
260
|
+
c_user = user_model.where(sso_uuid: user_data[:public]).first
|
261
|
+
c_user = user_model.create(sso_uuid: user_data[:public]) unless c_user
|
262
|
+
c_user.update_user_data(user_data)
|
263
|
+
c_user.save if c_user.changed?
|
264
|
+
session[:user] = c_user.session_data
|
265
|
+
session[:menu] = load_full_menu(c_user.id)
|
266
|
+
|
267
|
+
puts session.inspect
|
268
|
+
|
269
|
+
redis.set("#{redis_prefix}session:#{token}", session.to_json, ex: ttl)
|
270
|
+
|
271
|
+
session
|
272
|
+
end
|
273
|
+
|
274
|
+
##
|
275
|
+
# Load full menu from database
|
276
|
+
# @param [Integer] user_id - User ID
|
277
|
+
def load_full_menu(user_id)
|
278
|
+
query = <<-SQL
|
279
|
+
SELECT `t`.*
|
280
|
+
FROM
|
281
|
+
(
|
282
|
+
SELECT `t2`.`id`, `t2`.`mode`, `t2`.`action`, `t2`.`title_locale`, `t2`.`page_title_locale`, `t2`.`short_title_locale`,
|
283
|
+
`t2`.`position`, `t2`.`tab`, `t2`.`menu_id`, `t2`.`state`, MAX(`t2`.`access`) AS `access`,
|
284
|
+
`t2`.`user_id`, `t2`.`parent_mode`
|
285
|
+
FROM (
|
286
|
+
SELECT `menus`.`id`, `menus`.`id` AS `menu_id`, `menus`.`mode`, `menus`.`action`, `menus`.`title_locale`, `menus`.`page_title_locale`,
|
287
|
+
`menus`.`short_title_locale`, `menus`.`position`, `menus`.`tab`, `menus`.`menu_id` AS `parent_menu_id`, `menus`.`state`,
|
288
|
+
`group_menus`.`access`, `group_users`.`user_id`, `parent_menu`.`mode` AS `parent_mode`
|
289
|
+
FROM (`menus`, `group_menus`, `groups`, `group_users`)
|
290
|
+
LEFT JOIN `menus` AS `parent_menu` ON `menus`.`menu_id` = `parent_menu`.`id`
|
291
|
+
WHERE `menus`.`id` = `group_menus`.`menu_id` AND `menus`.`status` = 0 AND `group_menus`.`group_id` = `groups`.`id` AND
|
292
|
+
`groups`.`id` = `group_users`.`group_id` AND `group_users`.`user_id` = #{user_id}
|
293
|
+
) AS `t2`
|
294
|
+
GROUP BY `t2`.`id`, `t2`.`mode`, `t2`.`action`, `t2`.`title_locale`, `t2`.`page_title_locale`, `t2`.`short_title_locale`,
|
295
|
+
`t2`.`position`, `t2`.`tab`, `t2`.`menu_id`, `t2`.`state`, `t2`.`user_id`, `t2`.`parent_mode`
|
296
|
+
) AS `t`
|
297
|
+
ORDER BY `t`.`tab`, `t`.`position`
|
298
|
+
SQL
|
299
|
+
|
300
|
+
result = {}
|
301
|
+
group_menu_model.find_by_sql(query).each do |data|
|
302
|
+
result[data.mode.to_sym] = {
|
303
|
+
mode: data.mode,
|
304
|
+
title: data.title,
|
305
|
+
page_title: data.page_title,
|
306
|
+
short_title: data.short_title,
|
307
|
+
position: data.position,
|
308
|
+
tab: data.tab,
|
309
|
+
action: data.action,
|
310
|
+
access: data.access,
|
311
|
+
state: menu_model.states.invert[data.state],
|
312
|
+
parent: data.parent_mode
|
313
|
+
}
|
314
|
+
#self.output[:data].push menu_id[data.id.to_s.to_sym]
|
315
|
+
end
|
316
|
+
|
317
|
+
result
|
318
|
+
end
|
319
|
+
|
320
|
+
##
|
321
|
+
# Get current token based on HTTP Authorization
|
322
|
+
# @return [String] current token
|
323
|
+
def token
|
324
|
+
return params[:oauth_token] if params.key? :oauth_token
|
325
|
+
request.env.fetch('HTTP_AUTHORIZATION', '').scan(/Bearer (.*)$/).flatten.last
|
326
|
+
end
|
327
|
+
|
328
|
+
##
|
329
|
+
# Validate SSO token
|
330
|
+
def check_sso_token
|
331
|
+
puts 'check_sso_token'
|
332
|
+
jwt = jwt_decode token
|
333
|
+
|
334
|
+
puts "JWT #{jwt}"
|
335
|
+
|
336
|
+
return nil unless jwt
|
337
|
+
return nil unless jwt.key? :payload
|
338
|
+
return nil unless jwt[:payload].key? 'iss'
|
339
|
+
|
340
|
+
puts "ISS #{jwt[:payload]['iss']} -> #{sso_server}"
|
341
|
+
puts jwt[:payload]['iss'].index(sso_server)
|
342
|
+
|
343
|
+
return nil if jwt[:payload]['iss'].index(sso_server) == nil
|
344
|
+
return nil if jwt[:payload]['iss'].index(sso_server) != 0
|
345
|
+
|
346
|
+
begin
|
347
|
+
iss = JSON.parse(redis.get("#{redis_prefix}iss:#{jwt[:payload]['iss']}"),{ symbolize_names: true })
|
348
|
+
rescue StandardError
|
349
|
+
iss = nil
|
350
|
+
end
|
351
|
+
|
352
|
+
puts "ISS1 #{iss}"
|
353
|
+
|
354
|
+
unless iss
|
355
|
+
begin
|
356
|
+
response = RestClient.get "#{jwt[:payload]['iss']}.well-known/openid-configuration", { accept: :json }
|
357
|
+
rescue StandardError
|
358
|
+
return nil
|
359
|
+
end
|
360
|
+
|
361
|
+
begin
|
362
|
+
iss = JSON.parse(response.body, { symbolize_names: true })
|
363
|
+
rescue StandardError
|
364
|
+
return nil
|
365
|
+
end
|
366
|
+
|
367
|
+
redis.set("#{redis_prefix}iss:#{jwt[:payload]['iss']}", iss.to_json, ex: 86400)
|
368
|
+
end
|
369
|
+
|
370
|
+
puts "ISS2 #{iss}"
|
371
|
+
return nil unless iss.key? :jwks_uri
|
372
|
+
self.sso_jwk_data_url = iss[:jwks_uri]
|
373
|
+
self.sso_userinfo_url = iss[:userinfo_endpoint]
|
374
|
+
|
375
|
+
jwk = jwk_key(jwt[:header]['kid'])
|
376
|
+
|
377
|
+
puts "JWK #{jwk}"
|
378
|
+
|
379
|
+
return nil unless jwk
|
380
|
+
|
381
|
+
begin
|
382
|
+
public_key = JWT::JWK::RSA.import(jwk).public_key
|
383
|
+
rescue StandardError
|
384
|
+
return nil
|
385
|
+
end
|
386
|
+
|
387
|
+
begin
|
388
|
+
jwt_v = JWT.decode token, public_key, true, { algorithm: jwk[:alg] }
|
389
|
+
rescue StandardError => e
|
390
|
+
puts e
|
391
|
+
return nil
|
392
|
+
end
|
393
|
+
|
394
|
+
jwt_v[0]
|
395
|
+
end
|
396
|
+
|
397
|
+
##
|
398
|
+
# Decode JWT token
|
399
|
+
# @param token [String] selected token
|
400
|
+
# @return [Hash] Encoded JWT token
|
401
|
+
def jwt_decode(token)
|
402
|
+
begin
|
403
|
+
jwt = JWT.decode token, nil, false
|
404
|
+
rescue StandardError => e
|
405
|
+
puts e
|
406
|
+
return nil
|
407
|
+
end
|
408
|
+
|
409
|
+
#puts jwt
|
410
|
+
|
411
|
+
return nil if jwt.count != 2
|
412
|
+
|
413
|
+
payload = nil
|
414
|
+
payload = jwt[0] if jwt[0].key? 'aud'
|
415
|
+
payload = jwt[1] if jwt[1].key? 'aud'
|
416
|
+
|
417
|
+
header = nil
|
418
|
+
header = jwt[0] if jwt[0].key? 'alg'
|
419
|
+
header = jwt[1] if jwt[1].key? 'alg'
|
420
|
+
|
421
|
+
return nil unless payload || header
|
422
|
+
|
423
|
+
return nil if Time.now.utc.to_i > payload['exp']
|
424
|
+
|
425
|
+
{
|
426
|
+
header: header,
|
427
|
+
payload: payload
|
428
|
+
}
|
429
|
+
end
|
430
|
+
|
431
|
+
##
|
432
|
+
# Receives JWK key
|
433
|
+
# @param [String] key - public key identifier
|
434
|
+
# @return [Hash] - JWK selected key
|
435
|
+
def jwk_key(key)
|
436
|
+
puts "jwk_key #{key}"
|
437
|
+
jwk = jwk_data
|
438
|
+
|
439
|
+
return nil unless jwk
|
440
|
+
|
441
|
+
jwk[:keys].each do |item|
|
442
|
+
return item if item[:kid] == key
|
443
|
+
end
|
444
|
+
|
445
|
+
nil
|
446
|
+
end
|
447
|
+
|
448
|
+
##
|
449
|
+
# Load JWK keys from cache or server.
|
450
|
+
# @return [Hash] JWK loaded from cache or server
|
451
|
+
def jwk_data
|
452
|
+
puts "jwk_data"
|
453
|
+
puts "#{redis_prefix}jwk"
|
454
|
+
jwk = redis.get("#{redis_prefix}jwk")
|
455
|
+
|
456
|
+
if jwk
|
457
|
+
begin
|
458
|
+
return JSON.parse(jwk,{ symbolize_names: true })
|
459
|
+
rescue StandardError
|
460
|
+
return nil
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
jwk = load_jwk_data_from_sso_server
|
465
|
+
|
466
|
+
return nil unless jwk
|
467
|
+
|
468
|
+
redis.set("#{redis_prefix}jwk", jwk.to_json, ex: 3600)
|
469
|
+
|
470
|
+
jwk
|
471
|
+
end
|
472
|
+
|
473
|
+
##
|
474
|
+
# Load JWK keys from server according by OAUTH specification.
|
475
|
+
# @return [Object] returns JWK loaded from server
|
476
|
+
def load_jwk_data_from_sso_server
|
477
|
+
puts 'load_jwk_data_from_sso_server'
|
478
|
+
puts sso_jwk_data_url
|
479
|
+
begin
|
480
|
+
response = RestClient.get sso_jwk_data_url
|
481
|
+
rescue StandardError
|
482
|
+
return nil
|
483
|
+
end
|
484
|
+
|
485
|
+
begin
|
486
|
+
data = JSON.parse(response.body, { symbolize_names: true })
|
487
|
+
rescue StandardError
|
488
|
+
return nil
|
489
|
+
end
|
490
|
+
|
491
|
+
data[:update] = Time.now
|
492
|
+
|
493
|
+
data
|
494
|
+
end
|
495
|
+
|
496
|
+
##
|
497
|
+
# Loads user data from SSO server and returns it.
|
498
|
+
# @return [Hash] User data
|
499
|
+
def load_user_from_sso_server
|
500
|
+
puts 'load_user_from_sso_server'
|
501
|
+
puts sso_userinfo_url
|
502
|
+
begin
|
503
|
+
response = RestClient.get sso_userinfo_url, { authorization: "Bearer #{token}" }
|
504
|
+
rescue StandardError
|
505
|
+
return nil
|
506
|
+
end
|
507
|
+
|
508
|
+
begin
|
509
|
+
result = JSON.parse(response.body, { symbolize_names: true })
|
510
|
+
rescue StandardError
|
511
|
+
return nil
|
512
|
+
end
|
513
|
+
|
514
|
+
result
|
515
|
+
end
|
516
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
##
|
2
|
+
# Main application class inherited from {https://www.rubydoc.info/gems/anoubis/Anoubis/ApplicationController Anoubis::ApplicationController}
|
3
|
+
class AnoubisSsoClient::ApplicationController < Anoubis::ApplicationController
|
4
|
+
include AnoubisSsoClient::ApplicationCommon
|
5
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
##
|
2
|
+
# Index controller class. Output system actions
|
3
|
+
class AnoubisSsoClient::IndexController < AnoubisSsoClient::ApplicationController
|
4
|
+
|
5
|
+
##
|
6
|
+
# Output allowed menu items
|
7
|
+
def menu
|
8
|
+
result = {
|
9
|
+
result: 0,
|
10
|
+
message: I18n.t('anoubis.success'),
|
11
|
+
menu: []
|
12
|
+
}
|
13
|
+
|
14
|
+
if current_menu
|
15
|
+
current_menu.each_value do |dat|
|
16
|
+
result[:menu].push dat
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
before_menu_output result
|
21
|
+
|
22
|
+
render json: result
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# Callback for change menu output
|
27
|
+
def before_menu_output(result)
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|