55from endorsement .dao .access import (
66 get_accessee_model , store_access_record , set_delegate )
77from endorsement .dao .office import get_office_accessor
8- from endorsement .exceptions import UnrecognizedUWNetid , UnrecognizedGroupID
9- from uw_msca .delegate import get_all_delegates
8+ from endorsement .exceptions import (
9+ UnrecognizedUWNetid , UnrecognizedGroupID , NoAccessRecordException ,
10+ NullDelegateException , AccessRecordException , DeletedAccessRecordException ,
11+ TooManyRightsException , EmptyDelegateRightsException ,
12+ DelegateRightMismatchException )
13+ from uw_msca .delegate import get_delegates , get_all_delegates
1014import json
1115import csv
1216import logging
1317
1418
1519logger = logging .getLogger (__name__ )
1620logger .setLevel (logging .INFO )
21+ MISSING_DELEGATES_THRESHOLD = 1024
1722
1823
1924def reconcile_access (commit_changes = False ):
2025 delegates = get_all_delegates ()[1 :]
2126 delegate_count = len (delegates )
2227 # make sure an empty response (likely an MSCA error condition) doesn't
2328 # cause all delegations to be deleted
24- if delegate_count < 1024 :
29+ if delegate_count < MISSING_DELEGATES_THRESHOLD :
2530 logger .error (
2631 "Possible malformed delegates response: {}" .format (delegates ))
2732 return
2833
29- logger .info ("Reconcile: {} delegates reported" .format (delegate_count ))
30-
31- accessee_mailboxes = list (AccessRecord .objects .filter (
32- is_deleted__isnull = True ).values (
33- 'accessee__netid' ).distinct ().values_list (
34- 'accessee__netid' , flat = True ))
34+ # all active record ids
35+ record_ids = list (AccessRecord .objects .filter (
36+ is_deleted__isnull = True ).values_list ('id' , flat = True ))
3537
3638 for row in csv .reader (delegates , delimiter = "," ):
3739 if len (row ) != 2 :
38- logger .error ("Reconcile: malformed row: {}" . format ( row ) )
40+ logger .error (f "Reconcile: malformed row: { row } " )
3941 continue
4042
4143 netid = strip_domain (row [0 ])
4244 accessee = get_accessee_model (netid )
43- records = list (
44- AccessRecord .objects .get_access_for_accessee (accessee ))
45-
46- try :
47- accessee_mailboxes .remove (netid )
48- except ValueError :
49- pass
50-
51- for delegate , rights in get_delegates (row [1 ]).items ():
52- # loop thru office mailbox delegates
53- # 1) catch dupes and record them
54- # 2) add records for delegations without one
55- # 3) delete records without a matching delegation
56- if not delegate or delegate .lower () == 'null' :
45+
46+ for delegate , rights in get_delegations (row [1 ]).items ():
47+ try :
48+ record = reconcile_delegation (accessee , delegate , rights )
49+ clear_record_id (record_ids , record .id )
50+ except NullDelegateException :
5751 logger .info (
58- f"mailbox { netid } with null delegate has rights: { rights } " )
59- continue
52+ f"NULL DELEGATE: mailbox { netid } delegate null "
53+ f"with rights: { rights } " )
54+ except NoAccessRecordException :
55+ logger .info (f"NO ACCESS RECORD FOR: mailbox { netid } "
56+ f"delegate { delegate } rights: { rights } " )
57+ if commit_changes :
58+ new_access_record (accessee , delegate , right_record )
59+ except DeletedAccessRecordException as ex :
60+ record = None
61+ # try "live" query since report may have outdated data
62+ for d in get_delegates (netid ):
63+ if d .delegate == delegate :
64+ record = ex .record
65+ break
6066
61- record , i = get_accessor_record (records , delegate )
62- if len (rights ) > 1 :
6367 if record :
64- if commit_changes :
65- # stash existing access record
66- revoke_record (record )
67-
68- records .remove (record )
68+ logger .info (f"DELETED ACCESS RECORD: "
69+ f"mailbox { netid } delegate { delegate } "
70+ f"right: { record .access_right .name } on "
71+ f"{ record .datetime_expired } " )
6972
73+ if commit_changes :
74+ right = next (iter (rights ))
75+ right_record = get_access_right (right )
76+ assign_access_right (record , right_record )
77+ except EmptyDelegateRightsException as ex :
78+ record = ex .record
79+ logger .info (f"NO RIGHTS FOR DELEGATION: "
80+ f"mailbox { netid } delegate { delegate } BUT "
81+ f"record has right: { record .access_right .name } " )
82+ clear_record_id (record_ids , record .id )
83+ except TooManyRightsException as ex :
84+ logger .info (
85+ f"CONFLICT: mailbox { netid } delegate { delegate } "
86+ f"rights: { rights } " )
87+ record = ex .record
7088 if commit_changes :
89+ revoke_record (record )
7190 save_conflict_record (accessee , record , delegate , rights )
72- else :
73- logger .info (
74- f"CONFLICT: mailbox { netid } delegate { delegate } "
75- f"rights: { rights } " )
7691
77- elif len (rights ) == 1 :
92+ clear_record_id (record_ids , record .id )
93+ except DelegateRightMismatchException as ex :
94+ record = ex .record
7895 right = next (iter (rights ))
79- right_record = get_access_right (right )
80- if record :
81- if record .access_right .name != right :
82- if commit_changes :
83- assign_access_right (record , right_record )
84- else :
85- logger .info (
86- f"CHANGED mailbox { netid } delegate { delegate } "
87- f" ({ record .access_right .name } ) to { right } " )
88- # else delegate right and record match
89-
90- records .remove (record )
91- else :
92- # create record for unrecognized delegate right
93- if commit_changes :
94- new_access_record (accessee , delegate , right_record )
95- else :
96- logger .info (
97- "MISSING ACCESS RECORD: "
98- f"mailbox { accessee .netid } "
99- f"delegate { delegate } ({ right_record .name } )" )
100- else :
101- logger .info (f"EMPTY RIGHTS: mailbox { netid } "
102- f"empty rights for { delegate } " )
103-
104- for record in records :
105- # delegations that were reported, but for which we have no
106- # access record
107- if commit_changes :
108- assign_delegation (accessee , record )
109- else :
110- logger .info ("MISSING DELEGATION: "
111- f"mailbox { record .accessee .netid } "
112- f"delegation { record .accessor .name } "
113- f"({ record .access_right .name } ) on "
114- f"{ record .datetime_granted } " )
115-
116- for mailbox in accessee_mailboxes :
117- # access records for which no delegation was reported
118- accessee = get_accessee_model (mailbox )
119- for record in AccessRecord .objects .get_access_for_accessee (accessee ):
120- if commit_changes :
121- assign_delegation (accessee , record )
122- else :
123- logger .info (f"MISSING DELEGATION: mailbox { accessee .netid } "
124- f"delegation { record .accessor .name } "
125- f"({ record .access_right .name } )"
126- f" on { record .datetime_granted } not "
127- "assigned in Outlook" )
96+
97+ logger .info (
98+ f"DELEGATION CHANGE: mailbox { netid } delegate { delegate } "
99+ f" ({ record .access_right .name } ) to { right } " )
100+
101+ if commit_changes :
102+ right_record = get_access_right (right )
103+ assign_access_right (record , right_record )
104+
105+ clear_record_id (record_ids , record .id )
106+
107+ # access records for which no delegation was reported
108+ for record in AccessRecord .objects .filter (id__in = record_ids ):
109+ if commit_changes :
110+ assign_delegation (accessee , record )
111+ else :
112+ logger .info (f"MISSING DELEGATION: mailbox { accessee .netid } "
113+ f"delegate { record .accessor .name } "
114+ f"({ record .access_right .name } )"
115+ f" on { record .datetime_granted } not "
116+ "assigned in Outlook" )
117+
118+
119+ def clear_record_id (record_ids , record_id ):
120+ try :
121+ record_ids .remove (record_id )
122+ except ValueError :
123+ pass
124+
125+
126+ def reconcile_delegation (accessee , delegate , rights ):
127+ if not delegate or delegate .lower () == 'null' :
128+ raise NullDelegateException ()
129+
130+ try :
131+ record = AccessRecord .objects .get (
132+ accessee = accessee , accessor__name = delegate )
133+ except AccessRecord .DoesNotExist :
134+ raise NoAccessRecordException ()
135+
136+ if len (rights ) > 1 :
137+ raise TooManyRightsException (record = record )
138+
139+ if record .is_deleted :
140+ raise DeletedAccessRecordException (record = record )
141+
142+ if len (rights ) < 1 :
143+ raise EmptyDelegateRightsException (record = record )
144+
145+ right = next (iter (rights ))
146+ if record .access_right .name != right :
147+ raise DelegateRightMismatchException (record = record )
148+
149+ return record
128150
129151
130152def get_access_right (right ):
@@ -134,7 +156,7 @@ def get_access_right(right):
134156
135157def new_access_record (accessee , delegate , right ):
136158 logger .info (
137- f"CREATE mailbox { accessee .netid } "
159+ f"CREATE RECORD: mailbox { accessee .netid } "
138160 f"delegate { delegate } ({ right .name } )" )
139161
140162 logger .info ("FAILSAFE HIT" )
@@ -153,20 +175,21 @@ def new_access_record(accessee, delegate, right):
153175
154176
155177def assign_delegation (accessee , record ):
156-
157178 logger .info ('commit set delegate ' )
179+
180+ logger .info ("FAILSAFE HIT" )
158181 return
159182
160183 try :
161184 set_delegate (accessee .netid , record .accessor .name ,
162185 record .access_right .name )
163- logger .info (f"mailbox { accessee .netid } delegation "
186+ logger .info (f"SET DELGATION: mailbox { accessee .netid } delegation "
164187 f"{ record .accessor .name } "
165- f"({ record .access_right .name } ) assigned " )
188+ f"({ record .access_right .name } )" )
166189 except Exception as ex :
167- logger .error ("set delegate {record.accessor.name} "
190+ logger .error ("SET DELEGATE FAILED: {record.accessor.name} "
168191 f"({ record .access_right .name } ) on "
169- f"{ accessee .netid } failed : { ex } " )
192+ f"{ accessee .netid } reason : { ex } " )
170193
171194
172195def revoke_record (record ):
@@ -209,22 +232,16 @@ def save_conflict_record(accessee, record, delegate, rights):
209232 conflict .save ()
210233
211234
212- def get_accessor_record (records , delegate ):
213- for i , record in enumerate (records ):
214- if record .accessor .name == delegate :
215- return record , i
216-
217- return None , - 1
218-
219-
220- def get_delegates (raw ):
235+ def get_delegations (raw ):
221236 delegates = {}
222237 cooked = json .loads (raw )
223238 for right in [cooked ] if isinstance (cooked , dict ) else cooked :
224- try :
225- delegates [right ["User" ]].append (right ['AccessRights' ])
226- except KeyError :
227- delegates [right ["User" ]] = [right ['AccessRights' ]]
239+ user = right ["User" ]
240+ if user and user .lower () != 'null' :
241+ try :
242+ delegates [user ].append (right ['AccessRights' ])
243+ except KeyError :
244+ delegates [user ] = [right ['AccessRights' ]]
228245
229246 return delegates
230247
0 commit comments