@@ -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
208332end
0 commit comments