88# * Make sure the view "login" from this module is used for login
99# * Map an url somwehere (typically /auth_receive/) to the auth_receive
1010# view.
11+ # * To get notified when a user is created from upstream, connect to the signal
12+ # auth_user_created_from_upstream.
1113# * To receive live updates (not just during login), map an url somewhere
1214# (typically /auth_api/) to the auth_api view.
1315# * To receive live updates, also connect to the signal auth_user_data_received.
4446import time
4547
4648
49+ # This signal fires when a user is created based on data from upstream.
50+ auth_user_created_from_upstream = Signal (providing_args = ['user' , ])
51+
4752# This signal fires whenever new user data has been received. Note that this
4853# happens *after* first_name, last_name and email has been updated on the user
4954# record, so those are not included in the userdata struct.
@@ -104,10 +109,15 @@ def auth_receive(request):
104109 return HttpResponse ("Missing data in url!" , status = 400 )
105110
106111 # Set up an AES object and decrypt the data we received
107- decryptor = AES .new (base64 .b64decode (settings .PGAUTH_KEY ),
108- AES .MODE_CBC ,
109- base64 .b64decode (str (request .GET ['i' ]), "-_" ))
110- s = decryptor .decrypt (base64 .b64decode (str (request .GET ['d' ]), "-_" )).rstrip (b' ' ).decode ('utf8' )
112+ try :
113+ decryptor = AES .new (base64 .b64decode (settings .PGAUTH_KEY ),
114+ AES .MODE_CBC ,
115+ base64 .b64decode (str (request .GET ['i' ]), "-_" ))
116+ s = decryptor .decrypt (base64 .b64decode (str (request .GET ['d' ]), "-_" )).rstrip (b' ' ).decode ('utf8' )
117+ except UnicodeDecodeError :
118+ return HttpResponse ("Badly encoded data found" , 400 )
119+ except Exception :
120+ return HttpResponse ("Could not decrypt data" , status = 400 )
111121
112122 # Now un-urlencode it
113123 try :
@@ -174,6 +184,8 @@ def auth_receive(request):
174184 )
175185 user .save ()
176186
187+ auth_user_created_from_upstream .send (sender = auth_receive , user = user )
188+
177189 # Ok, we have a proper user record. Now tell django that
178190 # we're authenticated so it persists it in the session. Before
179191 # we do that, we have to annotate it with the backend information.
@@ -281,7 +293,7 @@ def _conditionally_update_record(rectype, recordkey, structkey, fieldmap, struct
281293# Unlike the authentication, searching does not involve the browser - we just make
282294# a direct http call.
283295def user_search (searchterm = None , userid = None ):
284- # If upsteam isn't responding quickly, it's not going to respond at all, and
296+ # If upstream isn't responding quickly, it's not going to respond at all, and
285297 # 10 seconds is already quite long.
286298 socket .setdefaulttimeout (10 )
287299 if userid :
@@ -308,6 +320,30 @@ def user_search(searchterm=None, userid=None):
308320 return j
309321
310322
323+ # Subscribe to any changes about this user on the community auth upstream
324+ def subscribe_to_user_changes (userid ):
325+ socket .setdefaulttimeout (10 )
326+
327+ body = json .dumps ({
328+ 'u' : userid ,
329+ })
330+
331+ h = hmac .digest (
332+ base64 .b64decode (settings .PGAUTH_KEY ),
333+ msg = bytes (body , 'utf-8' ),
334+ digest = 'sha512' ,
335+ )
336+
337+ # Ignore the result code, just post it
338+ requests .post (
339+ '{0}subscribe/' .format (settings .PGAUTH_REDIRECT ),
340+ data = body ,
341+ headers = {
342+ 'X-pgauth-sig' : base64 .b64encode (h ),
343+ },
344+ )
345+
346+
311347# Import a user into the local authentication system. Will initially
312348# make a search for it, and if anything other than one entry is returned
313349# the import will fail.
@@ -335,4 +371,6 @@ def user_import(uid):
335371 )
336372 u .save ()
337373
374+ auth_user_created_from_upstream .send (sender = user_import , user = u )
375+
338376 return u
0 commit comments