Skip to content

Commit d3fdfbb

Browse files
committed
feat(inner_range/integriti_user_sync): apply CSV sync field
1 parent 241a25f commit d3fdfbb

File tree

1 file changed

+134
-10
lines changed

1 file changed

+134
-10
lines changed

drivers/inner_range/integriti_user_sync.cr

Lines changed: 134 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,36 @@ class InnerRange::IntegritiUserSync < PlaceOS::Driver
1515
sync_cron: "0 21 * * *",
1616
integriti_security_group: "QG15",
1717

18+
_csv_sync_mappings: {
19+
parking: {
20+
default: "unisex with parking",
21+
female: "Female with parking",
22+
male: "Male with parking",
23+
},
24+
default: {
25+
default: "unisex without parking",
26+
female: "Female without parking",
27+
male: "Male without parking",
28+
},
29+
},
30+
1831
# use these for enabling push notifications
19-
# push_authority: "authority-GAdySsf05mL"
20-
# push_notification_url: "https://placeos-dev.aca.im/api/engine/v2/notifications/office365"
32+
_push_authority: "authority-GAdySsf05mL",
33+
_push_notification_url: "https://placeos-dev.aca.im/api/engine/v2/notifications/office365",
2134
})
2235

2336
accessor directory : Calendar_1
2437
accessor integriti : Integriti_1
38+
accessor staff_api : StaffAPI_1
2539

2640
@time_zone : Time::Location = Time::Location.load("GMT")
2741

2842
@syncing : Bool = false
2943
@sync_mutex : Mutex = Mutex.new
3044
@sync_requests : Int32 = 0
3145

46+
getter csv_sync_mappings : Hash(String, Hash(String, String))? = nil
47+
3248
def on_update
3349
@time_zone_string = setting?(String, :time_zone).presence || config.control_system.not_nil!.timezone.presence || "GMT"
3450
@time_zone = Time::Location.load(@time_zone_string)
@@ -37,6 +53,8 @@ class InnerRange::IntegritiUserSync < PlaceOS::Driver
3753
@user_group_id = setting(String, :user_group_id)
3854
@integriti_security_group = setting(String, :integriti_security_group)
3955

56+
@csv_sync_mappings = setting?(Hash(String, Hash(String, String)), :csv_sync_mappings)
57+
4058
@graph_group_id = nil
4159

4260
schedule.clear
@@ -86,6 +104,7 @@ class InnerRange::IntegritiUserSync < PlaceOS::Driver
86104
protected def sync_users
87105
# get the list of users in the integriti permissions group: (i.e. QG2)
88106
email_to_user_id = integriti.managed_users_in_group(integriti_security_group).get.as_h.transform_values(&.as_s)
107+
logger.debug { "Number of users in Integrity security group: #{email_to_user_id.size}" }
89108

90109
ad_emails = [] of String
91110
new_users = [] of DirUser
@@ -95,10 +114,20 @@ class InnerRange::IntegritiUserSync < PlaceOS::Driver
95114
loop do
96115
# keep track of users that need to be created
97116
users.each do |user|
98-
user_email = user.email.downcase
99117
unless user.suspended
100-
ad_emails << user_email
101-
new_users << user unless email_to_user_id[user_email]?
118+
user_email = user.email.strip.downcase
119+
user.email = user_email
120+
username = user.username.strip.downcase
121+
user.username = username
122+
# handle cases where email may not equal username (and already configured in the system)
123+
user_id = email_to_user_id[username]?
124+
if user_id.nil? && username != user_email
125+
if user_id = email_to_user_id[user_email]?
126+
email_to_user_id[username] = user_id
127+
end
128+
end
129+
ad_emails << username
130+
new_users << user unless user_id
102131
end
103132
end
104133

@@ -107,10 +136,13 @@ class InnerRange::IntegritiUserSync < PlaceOS::Driver
107136

108137
# ensure we don't blow any request limits
109138
logger.debug { "fetching next page..." }
110-
sleep 1
139+
sleep 500.milliseconds
111140
users = Array(DirUser).from_json directory.get_members(user_group_id, next_page).get.to_json
112141
end
113142

143+
logger.debug { "Number of users in Integrity security group: #{email_to_user_id.size}" }
144+
logger.debug { "Number of users in Directory security group: #{ad_emails.size}" }
145+
114146
# find all the users that need to be removed from the group
115147
removed = 0
116148
removed_errors = 0
@@ -132,17 +164,30 @@ class InnerRange::IntegritiUserSync < PlaceOS::Driver
132164
end
133165
end
134166

167+
logger.debug { "Removed #{removed} users from integrity security group" }
168+
135169
# add the users that need to be in the group
136170
added = 0
137171
added_errors = 0
138172

139173
new_users.each do |user|
140-
user_email = user.email.downcase
174+
username = user.username
175+
user_email = user.email
176+
141177
begin
142-
# check if the user exists (find by email)
143-
users = integriti.user_id_lookup(user_email).get.as_a.map(&.as_s)
178+
# check if the user exists (find by email and username)
179+
users = integriti.user_id_lookup(username).get.as_a.map(&.as_s)
144180
if users.empty?
145-
users << integriti.create_user(user.name, user_email, user.phone).get.as_s
181+
users = integriti.user_id_lookup(user_email).get.as_a.map(&.as_s) unless user_email == username
182+
if users.empty?
183+
new_user_id = integriti.create_user(user.name, username, user.phone).get.as_s
184+
users << new_user_id
185+
email_to_user_id[username] = new_user_id
186+
else
187+
# we want to update the users email address to be the username
188+
logger.debug { "updating user email #{user_email} to #{username}" }
189+
integriti.update_user_custom(users.first, username)
190+
end
146191
end
147192

148193
# add the user permission group
@@ -161,11 +206,17 @@ class InnerRange::IntegritiUserSync < PlaceOS::Driver
161206
end
162207
end
163208

209+
logger.debug { "Added #{added} users to integrity security group" }
210+
211+
# CSV array
212+
csv_changed = sync_csv_field(ad_emails, email_to_user_id)
213+
164214
result = {
165215
removed: removed,
166216
removed_errors: removed_errors,
167217
added: added,
168218
added_errors: added_errors,
219+
base_building: csv_changed,
169220
}
170221
logger.info { "integriti user sync results: #{result}" }
171222
result
@@ -205,4 +256,77 @@ class InnerRange::IntegritiUserSync < PlaceOS::Driver
205256
raise "google is not supported"
206257
end
207258
end
259+
260+
# ===================
261+
# CSV Mappings
262+
# ===================
263+
264+
DEFAULT_KEY = "default"
265+
266+
getter building_id : String { get_building_id.not_nil! }
267+
268+
def get_building_id
269+
building_setting = setting?(String, :building_zone_override)
270+
return building_setting if building_setting.presence
271+
zone_ids = staff_api.zones(tags: "building").get.as_a.map(&.[]("id").as_s)
272+
(zone_ids & system.zones).first
273+
rescue error
274+
logger.warn(exception: error) { "unable to determine building zone id" }
275+
nil
276+
end
277+
278+
protected def sync_csv_field(ad_emails : Array(String), email_to_user_id : Hash(String, String))
279+
mappings = csv_sync_mappings
280+
return "no CSV mappings" unless mappings && !mappings.empty?
281+
282+
check = mappings.keys
283+
check.delete(DEFAULT_KEY)
284+
285+
possible_csv_strings = mappings.values.flat_map do |hash|
286+
hash.values
287+
end
288+
289+
logger.debug { "checking base building access for #{ad_emails.size} users" }
290+
291+
now = Time.local(@time_zone).at_beginning_of_day
292+
end_of_day = 3.days.from_now.in(@time_zone).at_end_of_day
293+
building = building_id
294+
295+
ad_emails.each do |email|
296+
user_id = email_to_user_id[email]?
297+
if user_id.nil?
298+
logger.warn { "unable to apply CSV sync to #{email}. Possibly no matching integriti user" }
299+
next
300+
end
301+
302+
# TODO:: lookup gender
303+
gender = DEFAULT_KEY
304+
305+
# check if the user has any of the required bookings
306+
bookings = check.flat_map do |booking_type|
307+
staff_api.query_bookings(now.to_unix, end_of_day.to_unix, zones: {building}, type: booking_type, email: email).get.as_a
308+
end
309+
310+
key = if booking = bookings.first?
311+
booking["booking_type"].as_s
312+
else
313+
DEFAULT_KEY
314+
end
315+
316+
# ensure appropriate security group is selected
317+
csv_security_group = mappings[key][gender]
318+
user = integriti.user(user_id).get
319+
csv_string = user["cf_csv"].as_s?
320+
if csv_string != csv_security_group
321+
if !csv_string.presence || csv_string.in?(possible_csv_strings)
322+
# change the CSV string of this user
323+
integriti.update_user_custom(user_id, email: email, csv: csv_security_group)
324+
else
325+
logger.debug { "skipping csv update for #{email} as current mapping #{csv_string} may have been manually configured" }
326+
end
327+
end
328+
rescue error
329+
logger.warn(exception: error) { "failed to check csv field for #{email}" }
330+
end
331+
end
208332
end

0 commit comments

Comments
 (0)