ucb_rails_user 4.0.7 → 4.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +14 -0
- data/app/assets/javascripts/ucb_rails_user/ucb_rails_user.js +1 -1
- data/app/models/ucb_rails_user/impersonation.rb +2 -2
- data/app/models/ucb_rails_user/user_session_manager/in_uc_path_add_to_users_table.rb +39 -0
- data/app/models/ucb_rails_user/user_uc_path_service.rb +121 -0
- data/app/views/ucb_rails_user/users/index.html.haml +1 -1
- data/lib/ucb_rails_user/version.rb +1 -1
- metadata +57 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7a1de8958c36cbeeb3541649d812e7288468672f9352ec84749810fccaf81005
|
4
|
+
data.tar.gz: e2262f788179205abf8dc7d4558a434f3826130f6d713448ef90a783e2341fe1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 27314e556fa6987b12779ea570dacbd3f5089a7cfdef91a316020f13754ceb147cd74de4e3e468a298109cc1336beef659708507bde98605743242e9cd4f6c91
|
7
|
+
data.tar.gz: dbb93ff5a2cde7347e2b5441155edafae6f18557cbaf584ac6f89fdb34adda7b67dc36b8f43a5c571e2fb0b6de8eefd71dacb6f3a3e6cd04458a56acd1d417fe
|
data/README.md
CHANGED
@@ -105,6 +105,20 @@ For example, if the admin screens for your app are under the `/backend` path rat
|
|
105
105
|
resources :users, controller: "ucb_rails_user/users", path: "backend/users", as: :backend_users
|
106
106
|
```
|
107
107
|
|
108
|
+
## Session Managers
|
109
|
+
|
110
|
+
Authentication during login is handled by UCB's central auth system and this gem uses the [omniauth-cas](https://github.com/dlindahl/omniauth-cas) to manage the handshake. Once that is completed, we're handed the LDAP uid of the authenticated user, and, by default, this gem will attempt to pull the user's employee data and create or update a `User` record for them in the local database.
|
111
|
+
|
112
|
+
This behavior can be customized by writing your own user session manager. There are two steps to do this:
|
113
|
+
|
114
|
+
1. Create a subclass of [`UcbRailsUser::UserSessionManager::Base`](https://github.com/ucb-ist-eas/ucb_rails_user/blob/main/app/models/ucb_rails_user/user_session_manager/base.rb). At a minimum you need to implement a `login` method that accepts the user's LDAP uid and returns a `User` instance (or raises an error if the process fails), and a `current_user` method that returns the `User` instance for the currently-logged-in user. [Several implementations](https://github.com/ucb-ist-eas/ucb_rails_user/tree/main/app/models/ucb_rails_user/user_session_manager) are included so you can look to these to base yours off of.
|
115
|
+
|
116
|
+
1. Open `config/initializers/ucb_rails_user.rb` in your host app and change the `user_session_manager` config setting to the class your custom implementation:
|
117
|
+
|
118
|
+
```
|
119
|
+
config.user_session_manager = "MyApp::MyCustomUserSessionManager"
|
120
|
+
```
|
121
|
+
|
108
122
|
## User Impersonation
|
109
123
|
|
110
124
|
The impersonation feature allows admins to be logged in as a different user in the system. This is useful when trying to diagnose data-specific problems, as the admin can see exactly what the user sees.
|
@@ -32,7 +32,7 @@ var addDatatablesToUsersTable = function () {
|
|
32
32
|
}],
|
33
33
|
})
|
34
34
|
var addNewHtml = ' <a href="/admin/users/new" class="btn btn-primary">Add New</a>'
|
35
|
-
$('#DataTables_Table_0_filter').append(addNewHtml)
|
35
|
+
$('.ucb-rails-users-table-wrapper #DataTables_Table_0_filter').append(addNewHtml)
|
36
36
|
}
|
37
37
|
|
38
38
|
var resetImpersonateButton = function() {
|
@@ -1,8 +1,8 @@
|
|
1
1
|
class UcbRailsUser::Impersonation < ApplicationRecord
|
2
2
|
include UcbRailsUser::Concerns::ImpersonationConcerns
|
3
3
|
|
4
|
-
# Don't add anything more here - any logic for the
|
5
|
-
#
|
4
|
+
# Don't add anything more here - any logic for the Impersonation class should go into
|
5
|
+
# ImpersonationConcerns. This will make it much easier for host apps to customize
|
6
6
|
# behavior if they need to
|
7
7
|
# http://guides.rubyonrails.org/engines.html#implementing-decorator-pattern-using-activesupport-concern
|
8
8
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Session manager that attempts to pull the user record from UCPath, and
|
2
|
+
# falls back to LDAP if needed
|
3
|
+
|
4
|
+
module UcbRailsUser
|
5
|
+
module UserSessionManager
|
6
|
+
|
7
|
+
class InUcPathAddToUsersTable < ActiveInUserTable
|
8
|
+
def login(uid)
|
9
|
+
self.uid = uid
|
10
|
+
|
11
|
+
# try UCPath first
|
12
|
+
user = safely_load_user_from_api do
|
13
|
+
UcbRailsUser::UserUcPathService.create_or_update_user_from_ldap_uid(self.uid)
|
14
|
+
end
|
15
|
+
|
16
|
+
# if that doesn't work, try LDAP
|
17
|
+
user ||= safely_load_user_from_api do
|
18
|
+
UcbRailsUser::UserLdapService.create_or_update_user_from_entry(people_ou_entry)
|
19
|
+
end
|
20
|
+
|
21
|
+
user&.tap do |u|
|
22
|
+
u&.touch(:last_login_at)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def safely_load_user_from_api(&block)
|
29
|
+
begin
|
30
|
+
user = block.call
|
31
|
+
rescue StandardError
|
32
|
+
user = nil
|
33
|
+
end
|
34
|
+
user
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require "faraday"
|
2
|
+
|
3
|
+
class UcbRailsUser::UserUcPathService
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def create_or_update_user_from_employee_id(employee_id)
|
8
|
+
ucpath_entry = ucpath_client.fetch_employee_data_with_employee_id(employee_id)
|
9
|
+
return nil unless ucpath_entry.present?
|
10
|
+
user = User.find_or_initialize_by(employee_id: employee_id)
|
11
|
+
update_user_record_from_ucpath_entry!(user, ucpath_entry)
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_or_update_user_from_ldap_uid(ldap_uid)
|
15
|
+
ucpath_entry = ucpath_client.fetch_employee_data_with_ldap_uid(ldap_uid)
|
16
|
+
return nil unless ucpath_entry.present?
|
17
|
+
user = User.find_or_initialize_by(ldap_uid: ldap_uid)
|
18
|
+
update_user_record_from_ucpath_entry!(user, ucpath_entry)
|
19
|
+
end
|
20
|
+
|
21
|
+
def ucpath_client
|
22
|
+
UcPathClient.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_user_record_from_ucpath_entry!(user, ucpath_entry)
|
26
|
+
user.tap do |u|
|
27
|
+
name_entry = parse_name(ucpath_entry)
|
28
|
+
u.first_name = name_entry["givenName"]
|
29
|
+
u.last_name = name_entry["familyName"]
|
30
|
+
u.employee_id ||= ucpath_entry["identifiers"]&.detect do |id|
|
31
|
+
id["type"] == "hr-employee-id"
|
32
|
+
end&.fetch("id")
|
33
|
+
u.ldap_uid ||= ucpath_entry["identifiers"]&.detect do |id|
|
34
|
+
id["type"] == "campus-uid"
|
35
|
+
end&.fetch("id")
|
36
|
+
u.email = parse_email(ucpath_entry)
|
37
|
+
u.inactive_flag = false # any way to pull this from the API?
|
38
|
+
u.save!
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse_name(entry)
|
43
|
+
return nil unless entry.present?
|
44
|
+
find_name_by_type(entry["names"], "PRF") ||
|
45
|
+
find_name_by_type(entry["names"], "PRI")
|
46
|
+
end
|
47
|
+
|
48
|
+
def find_name_by_type(names, type)
|
49
|
+
names&.detect do |n|
|
50
|
+
n.dig("type", "code") == type
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse_email(entry)
|
55
|
+
email_entry =
|
56
|
+
entry["emails"]&.detect do |email|
|
57
|
+
email["primary"] == true
|
58
|
+
end
|
59
|
+
email_entry ||= entry["emails"]&.first # if there's no primary email, grab whatever we can
|
60
|
+
email_entry&.fetch("emailAddress")
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
class UcPathClient
|
66
|
+
attr_reader :app_id, :app_key, :endpoint
|
67
|
+
|
68
|
+
def initialize
|
69
|
+
base_credentials =
|
70
|
+
Rails.application.credentials.ucpath&.with_indifferent_access ||
|
71
|
+
Rails.application.credentials.hcm&.with_indifferent_access ||
|
72
|
+
Rails.application.credentials.fetch(:"ucb-hcm", {})&.with_indifferent_access
|
73
|
+
env_credentials = base_credentials&.fetch(Rails.env, {})
|
74
|
+
@app_id = env_credentials&.fetch(:app_id, nil) || base_credentials&.fetch(:app_id, nil)
|
75
|
+
@app_key = env_credentials&.fetch(:app_key, nil) || base_credentials&.fetch(:app_key, nil)
|
76
|
+
@endpoint = env_credentials&.fetch(:endpoint, nil) || base_credentials&.fetch(:endpoint, nil)
|
77
|
+
end
|
78
|
+
|
79
|
+
def fetch_employee_data_with_ldap_uid(ldap_uid)
|
80
|
+
fetch_employee_data(ldap_uid, "campus-uid")
|
81
|
+
end
|
82
|
+
|
83
|
+
def fetch_employee_data_with_employee_id(employee_id)
|
84
|
+
fetch_employee_data(employee_id, "hr-employee-id")
|
85
|
+
end
|
86
|
+
|
87
|
+
def fetch_employee_data(id, id_type)
|
88
|
+
if [app_id, app_key, endpoint].any?(&:blank?)
|
89
|
+
Rails.logger.warn missing_api_values_message
|
90
|
+
return nil
|
91
|
+
end
|
92
|
+
response =
|
93
|
+
Faraday.get("#{endpoint}/employees/#{id}") do |req|
|
94
|
+
req.params["id-type"] = id_type
|
95
|
+
req.headers["Accept"] = "application/json"
|
96
|
+
req.headers["app_id"] = app_id
|
97
|
+
req.headers["app_key"] = app_key
|
98
|
+
end
|
99
|
+
parse_response(response)&.first
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def parse_response(response)
|
105
|
+
return nil if !response.success? || response.body.empty?
|
106
|
+
JSON.parse(response.body)&.fetch("response")
|
107
|
+
end
|
108
|
+
|
109
|
+
def missing_api_values_message
|
110
|
+
<<~END_MSG.strip
|
111
|
+
It looks like you're trying to use the preferred user name feature of
|
112
|
+
ucb_rails_user, but the host app has not provided all of the expected
|
113
|
+
credentials to access the UCPath API.
|
114
|
+
To resolve this, add an "hcm" section to the Rails credentials file of
|
115
|
+
the host app, and provide values for app_id, app_key, and endpoint.
|
116
|
+
END_MSG
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ucb_rails_user
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steve Downey
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date:
|
14
|
+
date: 2022-09-14 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rails
|
@@ -19,14 +19,34 @@ dependencies:
|
|
19
19
|
requirements:
|
20
20
|
- - ">"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '5.
|
22
|
+
version: '5.2'
|
23
|
+
- - "<"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '8.0'
|
23
26
|
type: :runtime
|
24
27
|
prerelease: false
|
25
28
|
version_requirements: !ruby/object:Gem::Requirement
|
26
29
|
requirements:
|
27
30
|
- - ">"
|
28
31
|
- !ruby/object:Gem::Version
|
29
|
-
version: '5.
|
32
|
+
version: '5.2'
|
33
|
+
- - "<"
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '8.0'
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: sprockets-rails
|
38
|
+
requirement: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
30
50
|
- !ruby/object:Gem::Dependency
|
31
51
|
name: haml
|
32
52
|
requirement: !ruby/object:Gem::Requirement
|
@@ -87,16 +107,22 @@ dependencies:
|
|
87
107
|
name: omniauth
|
88
108
|
requirement: !ruby/object:Gem::Requirement
|
89
109
|
requirements:
|
90
|
-
- - "
|
110
|
+
- - ">="
|
91
111
|
- !ruby/object:Gem::Version
|
92
112
|
version: '1.8'
|
113
|
+
- - "<"
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '3.0'
|
93
116
|
type: :runtime
|
94
117
|
prerelease: false
|
95
118
|
version_requirements: !ruby/object:Gem::Requirement
|
96
119
|
requirements:
|
97
|
-
- - "
|
120
|
+
- - ">="
|
98
121
|
- !ruby/object:Gem::Version
|
99
122
|
version: '1.8'
|
123
|
+
- - "<"
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '3.0'
|
100
126
|
- !ruby/object:Gem::Dependency
|
101
127
|
name: omniauth-cas
|
102
128
|
requirement: !ruby/object:Gem::Requirement
|
@@ -126,53 +152,61 @@ dependencies:
|
|
126
152
|
- !ruby/object:Gem::Version
|
127
153
|
version: '3.0'
|
128
154
|
- !ruby/object:Gem::Dependency
|
129
|
-
name:
|
155
|
+
name: faraday
|
130
156
|
requirement: !ruby/object:Gem::Requirement
|
131
157
|
requirements:
|
132
158
|
- - "~>"
|
133
159
|
- !ruby/object:Gem::Version
|
134
|
-
version: '
|
135
|
-
type: :
|
160
|
+
version: '1.0'
|
161
|
+
type: :runtime
|
136
162
|
prerelease: false
|
137
163
|
version_requirements: !ruby/object:Gem::Requirement
|
138
164
|
requirements:
|
139
165
|
- - "~>"
|
140
166
|
- !ruby/object:Gem::Version
|
141
|
-
version: '
|
167
|
+
version: '1.0'
|
142
168
|
- !ruby/object:Gem::Dependency
|
143
|
-
name:
|
169
|
+
name: puma
|
144
170
|
requirement: !ruby/object:Gem::Requirement
|
145
171
|
requirements:
|
146
172
|
- - "~>"
|
147
173
|
- !ruby/object:Gem::Version
|
148
|
-
version: '
|
149
|
-
- - "<"
|
150
|
-
- !ruby/object:Gem::Version
|
151
|
-
version: '1.4'
|
174
|
+
version: '5.6'
|
152
175
|
type: :development
|
153
176
|
prerelease: false
|
154
177
|
version_requirements: !ruby/object:Gem::Requirement
|
155
178
|
requirements:
|
156
179
|
- - "~>"
|
157
180
|
- !ruby/object:Gem::Version
|
158
|
-
version: '
|
159
|
-
|
181
|
+
version: '5.6'
|
182
|
+
- !ruby/object:Gem::Dependency
|
183
|
+
name: sqlite3
|
184
|
+
requirement: !ruby/object:Gem::Requirement
|
185
|
+
requirements:
|
186
|
+
- - ">="
|
160
187
|
- !ruby/object:Gem::Version
|
161
|
-
version: '
|
188
|
+
version: '0'
|
189
|
+
type: :development
|
190
|
+
prerelease: false
|
191
|
+
version_requirements: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - ">="
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: '0'
|
162
196
|
- !ruby/object:Gem::Dependency
|
163
197
|
name: rspec-rails
|
164
198
|
requirement: !ruby/object:Gem::Requirement
|
165
199
|
requirements:
|
166
200
|
- - "~>"
|
167
201
|
- !ruby/object:Gem::Version
|
168
|
-
version: '
|
202
|
+
version: '5.0'
|
169
203
|
type: :development
|
170
204
|
prerelease: false
|
171
205
|
version_requirements: !ruby/object:Gem::Requirement
|
172
206
|
requirements:
|
173
207
|
- - "~>"
|
174
208
|
- !ruby/object:Gem::Version
|
175
|
-
version: '
|
209
|
+
version: '5.0'
|
176
210
|
- !ruby/object:Gem::Dependency
|
177
211
|
name: factory_bot_rails
|
178
212
|
requirement: !ruby/object:Gem::Requirement
|
@@ -308,8 +342,10 @@ files:
|
|
308
342
|
- app/models/ucb_rails_user/user_session_manager/base.rb
|
309
343
|
- app/models/ucb_rails_user/user_session_manager/in_people_ou.rb
|
310
344
|
- app/models/ucb_rails_user/user_session_manager/in_people_ou_add_to_users_table.rb
|
345
|
+
- app/models/ucb_rails_user/user_session_manager/in_uc_path_add_to_users_table.rb
|
311
346
|
- app/models/ucb_rails_user/user_session_manager/ldap_person_user_wrapper.rb
|
312
347
|
- app/models/ucb_rails_user/user_session_manager/test_session_manager.rb
|
348
|
+
- app/models/ucb_rails_user/user_uc_path_service.rb
|
313
349
|
- app/models/user.rb
|
314
350
|
- app/views/ucb_rails_user/home/logged_in.html.haml
|
315
351
|
- app/views/ucb_rails_user/home/not_logged_in.html.haml
|
@@ -356,7 +392,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
356
392
|
- !ruby/object:Gem::Version
|
357
393
|
version: '0'
|
358
394
|
requirements: []
|
359
|
-
rubygems_version: 3.
|
395
|
+
rubygems_version: 3.3.7
|
360
396
|
signing_key:
|
361
397
|
specification_version: 4
|
362
398
|
summary: Rails engine for UCB user accounts
|