Skip to content

Commit 258bf3e

Browse files
committed
Add Calnet schema attribute name validation
1 parent 27c9926 commit 258bf3e

3 files changed

Lines changed: 78 additions & 11 deletions

File tree

app/lib/error/calnet_error.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module Error
2+
# Raised calnet error when it returns an unexpected response,
3+
# e.g. missing email value because of the schema attribute name changed unexpected by Calnet from 'berkeleyEduAlternateId' to 'berkeleyEduAlternateID' .
4+
class CalnetError < ApplicationError
5+
end
6+
end

app/models/user.rb

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,21 @@ class User
77
FRAMEWORK_ADMIN_GROUP = 'cn=edu:berkeley:org:libr:framework:LIBR-framework-admins,ou=campus groups,dc=berkeley,dc=edu'.freeze
88
ALMA_ADMIN_GROUP = 'cn=edu:berkeley:org:libr:framework:alma-admins,ou=campus groups,dc=berkeley,dc=edu'.freeze
99

10+
CALNET_ATTRS = {
11+
affiliations: 'berkeleyEduAffiliations',
12+
cs_id: 'berkeleyEduCSID',
13+
groups: 'berkeleyEduIsMemberOf',
14+
# student_id: 'berkeleyEduStuID',
15+
ucpath_id: 'berkeleyEduUCPathID',
16+
email: 'berkeleyEduAlternateID',
17+
department_number: 'departmentNumber',
18+
display_name: 'displayName',
19+
employee_id: 'employeeNumber',
20+
given_name: 'givenName',
21+
surname: 'surname',
22+
uid: 'uid'
23+
}.freeze
24+
1025
class << self
1126
# Returns a new user object from the given "omniauth.auth" hash. That's a
1227
# hash of all data returned by the auth provider (in our case, calnet).
@@ -26,26 +41,50 @@ def from_omniauth(auth)
2641
# rubocop:disable Metrics/MethodLength
2742
def auth_params_from(auth)
2843
auth_extra = auth['extra']
44+
verify_calnet_attributes!(auth_extra)
2945
cal_groups = auth_extra['berkeleyEduIsMemberOf'] || []
3046

3147
# NOTE: berkeleyEduCSID should be same as berkeleyEduStuID for students
3248
{
33-
affiliations: auth_extra['berkeleyEduAffiliations'],
34-
cs_id: auth_extra['berkeleyEduCSID'],
35-
department_number: auth_extra['departmentNumber'],
36-
display_name: auth_extra['displayName'],
37-
email: auth_extra['berkeleyEduAlternateID'],
38-
employee_id: auth_extra['employeeNumber'],
39-
given_name: auth_extra['givenName'],
40-
student_id: auth_extra['berkeleyEduStuID'],
41-
surname: auth_extra['surname'],
42-
ucpath_id: auth_extra['berkeleyEduUCPathID'],
43-
uid: auth_extra['uid'] || auth['uid'],
49+
affiliations: auth_extra[CALNET_ATTRS[:affiliations]],
50+
cs_id: auth_extra[CALNET_ATTRS[:cs_id]],
51+
department_number: auth_extra[CALNET_ATTRS[:department_number]],
52+
display_name: auth_extra[CALNET_ATTRS[:display_name]],
53+
email: auth_extra[CALNET_ATTRS[:email]],
54+
employee_id: auth_extra[CALNET_ATTRS[:employee_id]],
55+
given_name: auth_extra[CALNET_ATTRS[:given_name]],
56+
student_id: auth_extra[CALNET_ATTRS[:cs_id]],
57+
surname: auth_extra[CALNET_ATTRS[:surname]],
58+
ucpath_id: auth_extra[CALNET_ATTRS[:ucpath_id]],
59+
uid: auth_extra[CALNET_ATTRS[:uid]] || auth['uid'],
4460
framework_admin: cal_groups.include?(FRAMEWORK_ADMIN_GROUP),
4561
alma_admin: cal_groups.include?(ALMA_ADMIN_GROUP)
4662
}
4763
end
4864
# rubocop:enable Metrics/MethodLength
65+
66+
# Verifies that auth_extra contains all required CalNet attributes with exact case-sensitive names
67+
# Raise [Error::CalnetError] if any required attributes are missing
68+
def verify_calnet_attributes!(auth_extra)
69+
required_attributes = CALNET_ATTRS.values
70+
71+
missing = required_attributes.reject { |attr| auth_extra.key?(attr) }
72+
73+
return if missing.empty?
74+
75+
current_calnet_keys = list_auth_extra_keys(auth_extra)
76+
msg = "Cannot find CalNet schema attribute(s) (case-sensitive): #{missing.join(', ')}. The current CalNet schema attributes: #{current_calnet_keys.join(', ')}."
77+
Rails.logger.error(msg)
78+
raise Error::CalnetError, msg
79+
end
80+
81+
# list all keys except duo keys
82+
def list_auth_extra_keys(auth_extra)
83+
keys = auth_extra.keys.reject { |k| k.start_with?('duo') }.sort
84+
Rails.logger.info("CalNet auth_extra keys: #{keys.join(', ')}")
85+
keys
86+
end
87+
4988
end
5089

5190
# Affiliations per CalNet (attribute `berkeleyEduAffiliations` e.g.

spec/models/user_spec.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,28 @@
2121
expect { User.from_omniauth(auth) }.to raise_error(Error::InvalidAuthProviderError)
2222
end
2323

24+
it 'rejects calnet when a required schema attribute is missing or renamed' do
25+
auth = {
26+
'provider' => 'calnet',
27+
'extra' => {
28+
'berkeleyEduAffiliations' => 'expected affiliation',
29+
'berkeleyEduCSID' => 'expected cs id',
30+
'berkeleyEduIsMemberOf' => [],
31+
'berkeleyEduStuID' => 'expected student id',
32+
'berkeleyEduUCPathID' => 'expected UC Path ID',
33+
'berkeleyEduAlternatid' => 'expected email', # intentionally wrong case to simulate renamed attribute: berkeleyEduAlternatid instead of berkeleyEduAlternateId
34+
'departmentNumber' => 'expected dept. number',
35+
'displayName' => 'expected display name',
36+
'employeeNumber' => 'expected employee ID',
37+
'givenName' => 'expected given name',
38+
'surname' => 'expected surname',
39+
'uid' => 'expected UID'
40+
}
41+
}
42+
43+
expect { User.from_omniauth(auth) }.to raise_error(Error::CalnetError, /Missing required CalNet attributes/)
44+
end
45+
2446
it 'populates a User object' do
2547
framework_admin_ldap = 'cn=edu:berkeley:org:libr:framework:LIBR-framework-admins,ou=campus groups,dc=berkeley,dc=edu'
2648
auth = {

0 commit comments

Comments
 (0)