def testRepeatedFailedLogin(self): from auditcare.decorators import login login.FAILURE_LIMIT = 3 login.LOCK_OUT_AT_FAILURE=True login.COOLOFF_TIME = timedelta(seconds=4) start_count = AccessAudit.view('auditcare/login_events', key=['user', '*****@*****.**']).count() response = self.client.post(reverse('auth_login'), {'username': '******', 'password': '******'}) firstlogin_count = AccessAudit.view('auditcare/login_events', key=['user', '*****@*****.**']).count() self.assertEquals(start_count+1, firstlogin_count) first_audit = get_latest_access(['user', '*****@*****.**']) self.assertEquals(first_audit.access_type, models.ACCESS_FAILED) self.assertEquals(first_audit.failures_since_start, 1) start_failures = first_audit.failures_since_start for n in range(1, 3): #we are logging in within the cooloff period, so let's check to see if it doesn't increment. response = self.client.post(reverse('auth_login'), {'username': '******', 'password': '******'}) next_count = AccessAudit.view('auditcare/login_events', key=['user', '*****@*****.**']).count() self.assertEquals(firstlogin_count, next_count) next_audit = get_latest_access(['user', '*****@*****.**']) self.assertEquals(next_audit.access_type, models.ACCESS_FAILED) self.assertEquals(next_audit.failures_since_start, n+start_failures) time.sleep(1) time.sleep(3) response = self.client.post(reverse('auth_login'), {'username': '******', 'password': '******'}) cooled_audit = get_latest_access(['user', '*****@*****.**']) self.assertEquals(cooled_audit.failures_since_start, 1)
def testRepeatedFailedLogin(self): from auditcare.decorators import login login.FAILURE_LIMIT = 3 login.LOCK_OUT_AT_FAILURE=True login.COOLOFF_TIME = timedelta(seconds=4) start_count = AccessAudit.view('auditcare/login_events', key=['user', '*****@*****.**']).count() response = self.client.post(reverse('auth_login'), {'username': '******', 'password': '******'}) firstlogin_count = AccessAudit.view('auditcare/login_events', key=['user', '*****@*****.**']).count() self.assertEquals(start_count+1, firstlogin_count) first_audit = get_latest_access(['user', '*****@*****.**']) self.assertEquals(first_audit.access_type, models.ACCESS_FAILED) self.assertEquals(first_audit.failures_since_start, 1) start_failures = first_audit.failures_since_start for n in range(1,3): #we are logging in within the cooloff period, so let's check to see if it doesn't increment. response = self.client.post(reverse('auth_login'), {'username': '******', 'password': '******'}) next_count = AccessAudit.view('auditcare/login_events', key=['user', '*****@*****.**']).count() self.assertEquals(firstlogin_count, next_count) next_audit = get_latest_access(['user', '*****@*****.**']) self.assertEquals(next_audit.access_type, models.ACCESS_FAILED) self.assertEquals(next_audit.failures_since_start, n+start_failures) time.sleep(1) time.sleep(3) response = self.client.post(reverse('auth_login'), {'username': '******', 'password': '******'}) cooled_audit = get_latest_access(['user', '*****@*****.**']) self.assertEquals(cooled_audit.failures_since_start,1)
def testLogin(self): #login start_count = AccessAudit.view('auditcare/login_events', key=['user', '*****@*****.**']).count() response = self.client.post(reverse('auth_login'), {'username': '******', 'password': '******'}) login_count = AccessAudit.view('auditcare/login_events', key=['user', '*****@*****.**']).count() self.assertEqual(start_count+1, login_count) latest_audit = get_latest_access(['user', '*****@*****.**']) self.assertEquals(latest_audit.access_type, models.ACCESS_LOGIN)
def testSingleFailedLogin(self): start_count = AccessAudit.view('auditcare/login_events', key=['user', '*****@*****.**']).count() response = self.client.post(reverse('auth_login'), {'username': '******', 'password': '******'}) login_count = AccessAudit.view('auditcare/login_events', key=['user', '*****@*****.**']).count() self.assertEquals(start_count+1, login_count) #got the basic count, now let's inspect this value to see what kind of result it is. latest_audit = get_latest_access(['user', '*****@*****.**']) self.assertEquals(latest_audit.access_type, models.ACCESS_FAILED) self.assertEquals(latest_audit.failures_since_start, 1)
def get_user_attempt(request): """ Returns access attempt record if it exists. Otherwise return None. """ ip = request.META.get('REMOTE_ADDR', '') if USE_USER_AGENT: ua = request.META.get('HTTP_USER_AGENT', '<unknown>') attempts = AccessAudit.view('auditcare/login_events', key=['ip_ua', ip, ua], include_docs=True, limit=25).all() #attempts = AccessAttempt.objects.filter( user_agent=ua, ip_address=ip ) else: attempts = AccessAudit.view('auditcare/login_events', key=['ip', ip], include_docs=True, limit=25).all() #attempts = AccessAttempt.objects.filter( ip_address=ip ) attempts = sorted(attempts, key=lambda x: x.event_date, reverse=True) if not attempts: log.info("No attempts for given access, creating new attempt") return None #walk the attempts attempt = None for at in attempts: if at.access_type == models.ACCESS_FAILED: attempt = at break elif at.access_type == models.ACCESS_LOGIN: attempt = None break elif at.access_type == models.ACCESS_LOGOUT: attempt = None break if COOLOFF_TIME and attempt and datetime.utcnow( ) - attempt.event_date < COOLOFF_TIME: log.info( "Last login failure is still within the cooloff time, incrementing last access attempt." ) else: log.info( "Last login failure is outside the cooloff time, creating new access attempt." ) return None return attempt
def auditor(request, template="ewsghana/auditor.html"): auditEvents = AccessAudit.view("auditcare/by_date_access_events", descending=True, include_docs=True).all() realEvents = [] for a in auditEvents: designation = organization = facility = location = first_name = last_name = '' try: user = User.objects.get(username=a.user) except User.DoesNotExist: # OK - anonymous user pass else: first_name = user.first_name last_name = user.last_name try: profile = user.get_profile() except LogisticsProfile.DoesNotExist: profile = None else: designation = profile.designation if profile.designation else '' organization = profile.organization if profile.organization else '' facility = profile.supply_point if profile.supply_point else '' location = profile.location if profile.location else '' realEvents.append({'user': a.user, 'date': a.event_date, 'class': a.doc_type, 'access_type': a.access_type, 'first_name': first_name, 'last_name': last_name, 'designation': designation, 'organization': organization, 'facility': facility, 'location': location }) return render_to_response(template, {"audit_table": AuditLogTable(realEvents, request=request)}, context_instance=RequestContext(request))
def audited_views(request, *args, **kwargs): db = AccessAudit.get_db() views = db.view('auditcare/urlpath_by_user_date', reduce=False).all() template = "auditcare/audit_views.html" return render_to_response(template, {"audit_views": views}, context_instance=RequestContext(request))
def get_latest_access(key): access_events = AccessAudit.view('auditcare/login_events', key=key, include_docs=True).all() access_events = sorted(access_events, key=lambda x: x.event_date, reverse=True) return access_events[0]
def single_model_history(request, model_name, *args, **kwargs): # it's for a particular model context=RequestContext(request) db = AccessAudit.get_db() vals = db.view('auditcare/model_actions_by_id', group=True, startkey=[model_name,u''], endkey=[model_name,u'z']).all() model_dict= dict((x['key'][1], x['value']) for x in vals) context['instances_dict']=model_dict context['model'] = model_name return render_to_response('auditcare/single_model_changes.html', context)
def auditAll(request, template="auditcare/index.html"): auditEvents = AccessAudit.view("auditcare/by_date_access_events", descending=True, include_docs=True).all() realEvents = [{'user': a.user, 'date': a.event_date, 'class': a.doc_type, 'access_type': a.access_type } for a in auditEvents] return render_to_response(template, {"audit_table": AuditLogTable(realEvents, request=request)}, context_instance=RequestContext(request))
def export_all(request): auditEvents = AccessAudit.view("auditcare/by_date_access_events", descending=True, include_docs=True).all() response = HttpResponse() response['Content-Disposition'] = 'attachment; filename="AuditAll.xls"' writer = csv.UnicodeWriter(response) writer.writerow(['User', 'Access Type', 'Date']) for a in auditEvents: writer.writerow([a.user, a.access_type, a.event_date]) return response
def model_histories(request, *args, **kwargs): """ Looks at all the audit model histories and shows for a given model """ db = AccessAudit.get_db() vals = db.view('auditcare/model_actions_by_id', group=True, group_level=1).all() # do a dict comprehension here because we know all the keys in this reduce are unique model_dict = dict((x['value'][0], x['value']) for x in vals) context = {'model_dict': model_dict} return render(request, 'auditcare/model_changes.html', context)
def get_user_attempt(request): """ Returns access attempt record if it exists. Otherwise return None. """ ip = request.META.get('REMOTE_ADDR', '') if USE_USER_AGENT: ua = request.META.get('HTTP_USER_AGENT', '<unknown>') attempts = AccessAudit.view('auditcare/login_events', key=['ip_ua',ip, ua], include_docs=True, limit=25).all() #attempts = AccessAttempt.objects.filter( user_agent=ua, ip_address=ip ) else: attempts = AccessAudit.view('auditcare/login_events',key=['ip', ip], include_docs=True, limit=25).all() #attempts = AccessAttempt.objects.filter( ip_address=ip ) attempts = sorted(attempts, key=lambda x: x.event_date, reverse=True) if not attempts: log.info("No attempts for given access, creating new attempt") return None #walk the attempts attempt = None for at in attempts: if at.access_type == models.ACCESS_FAILED: attempt = at break elif at.access_type == models.ACCESS_LOGIN: attempt = None break elif at.access_type == models.ACCESS_LOGOUT: attempt = None break if COOLOFF_TIME and attempt and datetime.utcnow() - attempt.event_date < COOLOFF_TIME: log.info("Last login failure is still within the cooloff time, incrementing last access attempt.") else: log.info("Last login failure is outside the cooloff time, creating new access attempt.") return None return attempt
def model_instance_history(request, model_name, model_uuid, *args, **kwargs): # it's for a particular model context=RequestContext(request) db = AccessAudit.get_db() if ContentType.objects.filter(name=model_name).count() == 0: # it's couchdbkit obj = db.get(model_uuid) else: obj = ContentType.objects.filter(name=model_name)[0].model_class().objects.get(id=model_uuid) context['change_history'] = history_for_doc(obj) context['model'] = model_name context['model_uuid'] = model_uuid return render_to_response('auditcare/model_instance_history.html', context)
def model_instance_history(request, model_name, model_uuid, *args, **kwargs): #it's for a particular model context=RequestContext(request) db = AccessAudit.get_db() changes=db.view('auditcare/model_actions_by_id', reduce=False, key=[model_name, model_uuid], include_docs=True).all() #context['changes']= sorted([(x['doc']['_id'], x['doc']) for x in changes], key=lambda y: y[1]['event_date'], reverse=True) if ContentType.objects.filter(name=model_name).count() == 0: #it's couchdbkit obj = db.get(model_uuid) else: obj = ContentType.objects.filter(name=model_name)[0].model_class().objects.get(id=model_uuid) context['change_history'] = history_for_doc(obj) context['model'] = model_name context['model_uuid'] = model_uuid return render_to_response('auditcare/model_instance_history.html', context)
def auditor(request, template="ewsghana/auditor.html"): auditEvents = AccessAudit.view("auditcare/by_date_access_events", descending=True, include_docs=True).all() realEvents = [] for a in auditEvents: designation = organization = facility = location = first_name = last_name = '' try: user = User.objects.get(username=a.user) except User.DoesNotExist: # OK - anonymous user pass else: first_name = user.first_name last_name = user.last_name try: profile = user.get_profile() except LogisticsProfile.DoesNotExist: profile = None else: designation = profile.designation if profile.designation else '' organization = profile.organization if profile.organization else '' facility = profile.supply_point if profile.supply_point else '' location = profile.location if profile.location else '' realEvents.append({ 'user': a.user, 'date': a.event_date, 'class': a.doc_type, 'access_type': a.access_type, 'first_name': first_name, 'last_name': last_name, 'designation': designation, 'organization': organization, 'facility': facility, 'location': location }) return render_to_response( template, {"audit_table": AuditLogTable(realEvents, request=request)}, context_instance=RequestContext(request))
def audited_logout(request, *args, **kwargs): # share some useful information func = auth_views.logout logging.info("Function: %s" % (func.__name__)) logging.info("Logged logout for user %s" % (request.user.username)) user = request.user # it's a successful login. ip = request.META.get('REMOTE_ADDR', '') ua = request.META.get('HTTP_USER_AGENT', '<unknown>') attempt = AccessAudit() attempt.doc_type = AccessAudit.__name__ attempt.access_type = models.ACCESS_LOGOUT attempt.user_agent = ua attempt.user = user.username attempt.session_key = request.session.session_key attempt.ip_address = ip attempt.get_data = [] attempt.post_data = [] attempt.http_accept = request.META.get('HTTP_ACCEPT', '<unknown>') attempt.path_info = request.META.get('PATH_INFO', '<unknown>') attempt.failures_since_start = 0 attempt.save() # call the logout function response = func(request, *args, **kwargs) return response
def log_request(request, login_unsuccessful): failures = 0 attempt = get_user_attempt(request) if attempt: failures = attempt.failures_since_start # no matter what, we want to lock them out # if they're past the number of attempts allowed if failures > FAILURE_LIMIT and LOCK_OUT_AT_FAILURE: # We log them out in case they actually managed to enter # the correct password. logout(request) log.warning('AXES: locked out %s after repeated login attempts.', attempt.ip_address) return False if login_unsuccessful: #interpret the auth form to get the user in question if request.method == "POST": form = AuthenticationForm(data=request.POST) if form.is_valid(): attempted_username = form.get_user().username else: attempted_username = form.data.get('username') attempted_password = form.data.get('password') # add a failed attempt for this user failures += 1 # Create an AccessAttempt record if the login wasn't successful # has already attempted, update the info if attempt: #attempt.get_data.append(query2str(request.GET.items())) #attempt.post_data.append(query2str(request.POST.items())) attempt.access_type = models.ACCESS_FAILED attempt.user = attempted_username attempt.http_accept = request.META.get('HTTP_ACCEPT', '<unknown>') attempt.path_info = request.META.get('PATH_INFO', '<unknown>') attempt.failures_since_start = failures attempt.event_date = datetime.utcnow() #why do we do this? attempt.save() log.info( 'AXES: Repeated login failure by %s. Updating access ' 'record. Count = %s', attempt.ip_address, failures) else: ip = request.META.get('REMOTE_ADDR', '') ua = request.META.get('HTTP_USER_AGENT', '<unknown>') attempt = AccessAudit() attempt.event_date = datetime.utcnow() attempt.doc_type = AccessAudit.__name__ attempt.access_type = models.ACCESS_FAILED attempt.user_agent = ua attempt.user = attempted_username attempt.ip_address = ip #attempt.get_data = [query2str(request.GET.items())] #attempt.post_data= [query2str(request.POST.items())] attempt.http_accept = request.META.get('HTTP_ACCEPT', '<unknown>') attempt.path_info = request.META.get('PATH_INFO', '<unknown>') attempt.failures_since_start = failures attempt.save() log.info('AXES: New login failure by %s. Creating access record.', ip) return True
def audited_views(request, *args, **kwargs): db = AccessAudit.get_db() views = db.view('auditcare/urlpath_by_user_date', reduce=False).all() template = "auditcare/audit_views.html" context = {"audit_views": views} return render(request, template, context)
def handle(self, **options): recompute = options['recompute'] print recompute db = AccessAudit.get_db() vals = db.view('auditcare/model_actions_by_id', group=True, group_level=1).all() #get all model types #python 2.7 dict comprehension #model_dict= {x['key'][0]: x['value'] for x in vals} model_dict = dict((x['key'][0], x['value']) for x in vals) for model, count in model_dict.items(): #for each model type, query ALL audit instances. print "### %s" % (model) model_counts = db.view('auditcare/model_actions_by_id', group=True, startkey=[model, u''], endkey=[model, u'z']).all() #within a given model, query ALL instances #sort the models by id, then by rev descending #{u'value': <num>, u'key': [u'model', u'uuid']} for mc in model_counts: num = mc['value'] model_uuid = mc['key'][1] #now for each model uuid, do a query again to get all the rev numbers item_revs = db.view('auditcare/model_actions_by_id', reduce=False, startkey=[model, model_uuid], endkey=[model, model_uuid]).all() revs = sorted([(x['id'], x['value']) for x in item_revs], key=lambda y: y[1], reverse=True) #tuples of (audit_id, rev_id) #print "%s:%s -> %s" % (model, model_uuid, revs) #ok, for each arr of revs, if it's length greater than 1, then do it #we're going backwards, so...yeah if len(revs) > 1: for i, t in enumerate(revs): audit_id = t[0] current = ModelActionAudit.get(audit_id) if i + 1 == len(revs): current.prev_id = None current.save() break prev_audit_id = revs[i + 1][0] prev_rev = ModelActionAudit.get(prev_audit_id) if i == 0: current.next_id = None if current.prev_id != prev_rev._id: current.prev_id = prev_rev._id #current saves later if prev_rev.next_id != current._id: prev_rev.next_id = current._id prev_rev.save() # #sanity check # if prev_rev.revision_checksum == current.revision_checksum: # continue # # if (current.archived_data.get('doc_type', None) =='XFormInstance' and prev_rev.archived_data.get('doc_type', None) == 'XFormInstance'): # #it's an xforminstance # removed, added, changed = utils.dict_diff(current.archived_data['form'], prev_rev.archived_data['form']) # else: # removed, added, changed = utils.dict_diff(current.archived_data, prev_rev.archived_data) # current.removed = removed # current.added = added # current.changed = changed # current.save() current.compute_changes(save=True)
def handle(self, **options): recompute = options['recompute'] print recompute db = AccessAudit.get_db() vals = db.view('auditcare/model_actions_by_id', group=True, group_level=1).all() #get all model types #python 2.7 dict comprehension #model_dict= {x['key'][0]: x['value'] for x in vals} model_dict= dict((x['key'][0], x['value']) for x in vals) for model, count in model_dict.items(): #for each model type, query ALL audit instances. print "### %s" % (model) model_counts = db.view('auditcare/model_actions_by_id', group=True, startkey=[model,u''], endkey=[model,u'z']).all() #within a given model, query ALL instances #sort the models by id, then by rev descending #{u'value': <num>, u'key': [u'model', u'uuid']} for mc in model_counts: num = mc['value'] model_uuid = mc['key'][1] #now for each model uuid, do a query again to get all the rev numbers item_revs = db.view('auditcare/model_actions_by_id', reduce=False, startkey=[model,model_uuid], endkey=[model,model_uuid]).all() revs = sorted([(x['id'], x['value']) for x in item_revs], key=lambda y: y[1], reverse=True) #tuples of (audit_id, rev_id) #print "%s:%s -> %s" % (model, model_uuid, revs) #ok, for each arr of revs, if it's length greater than 1, then do it #we're going backwards, so...yeah if len(revs) > 1: for i, t in enumerate(revs): audit_id = t[0] current = ModelActionAudit.get(audit_id) if i+1 == len(revs): current.prev_id = None current.save() break prev_audit_id = revs[i+1][0] prev_rev = ModelActionAudit.get(prev_audit_id) if i == 0: current.next_id = None if current.prev_id != prev_rev._id: current.prev_id = prev_rev._id #current saves later if prev_rev.next_id != current._id: prev_rev.next_id = current._id prev_rev.save() # #sanity check # if prev_rev.revision_checksum == current.revision_checksum: # continue # # if (current.archived_data.get('doc_type', None) =='XFormInstance' and prev_rev.archived_data.get('doc_type', None) == 'XFormInstance'): # #it's an xforminstance # removed, added, changed = utils.dict_diff(current.archived_data['form'], prev_rev.archived_data['form']) # else: # removed, added, changed = utils.dict_diff(current.archived_data, prev_rev.archived_data) # current.removed = removed # current.added = added # current.changed = changed # current.save() current.compute_changes(save=True)
def decorated_logout (request, *args, **kwargs): # share some useful information if func.__name__ != 'decorated_logout' and VERBOSE: log.info('AXES: Calling decorated logout function: %s', func.__name__) if args: log.info('args: %s', args) if kwargs: log.info('kwargs: %s', kwargs) log.info("Function: %s", func.__name__) log.info("Logged logout for user %s", request.user.username) user = request.user #it's a successful login. ip = request.META.get('REMOTE_ADDR', '') ua = request.META.get('HTTP_USER_AGENT', '<unknown>') attempt = AccessAudit() attempt.doc_type=AccessAudit.__name__ attempt.access_type = models.ACCESS_LOGOUT attempt.user_agent=ua attempt.user = user.username attempt.session_key = request.session.session_key attempt.ip_address=ip attempt.get_data=[] #[query2str(request.GET.items())] attempt.post_data=[] attempt.http_accept=request.META.get('HTTP_ACCEPT', '<unknown>') attempt.path_info=request.META.get('PATH_INFO', '<unknown>') attempt.failures_since_start=0 attempt.save() # call the logout function response = func(request, *args, **kwargs) if func.__name__ == 'decorated_logout': # if we're dealing with this function itself, don't bother checking # for invalid login attempts. I suppose there's a bunch of # recursion going on here that used to cause one failed login # attempt to generate 10+ failed access attempt records (with 3 # failed attempts each supposedly) return response return response
def log_request(request, login_unsuccessful): failures = 0 attempt = get_user_attempt(request) if attempt: failures = attempt.failures_since_start # no matter what, we want to lock them out # if they're past the number of attempts allowed if failures > FAILURE_LIMIT and LOCK_OUT_AT_FAILURE: # We log them out in case they actually managed to enter # the correct password. logout(request) log.warning('AXES: locked out %s after repeated login attempts.', attempt.ip_address) return False if login_unsuccessful: #interpret the auth form to get the user in question if request.method == "POST": form = AuthenticationForm(data=request.POST) if form.is_valid(): attempted_username = form.get_user().username else: attempted_username = form.data.get('username') attempted_password = form.data.get('password') # add a failed attempt for this user failures += 1 # Create an AccessAttempt record if the login wasn't successful # has already attempted, update the info if attempt: #attempt.get_data.append(query2str(request.GET.items())) #attempt.post_data.append(query2str(request.POST.items())) attempt.access_type = models.ACCESS_FAILED attempt.user = attempted_username attempt.http_accept = request.META.get('HTTP_ACCEPT', '<unknown>') attempt.path_info = request.META.get('PATH_INFO', '<unknown>') attempt.failures_since_start = failures attempt.event_date = datetime.utcnow() #why do we do this? attempt.save() log.info('AXES: Repeated login failure by %s. Updating access ' 'record. Count = %s', attempt.ip_address, failures) else: ip = request.META.get('REMOTE_ADDR', '') ua = request.META.get('HTTP_USER_AGENT', '<unknown>') attempt = AccessAudit() attempt.event_date = datetime.utcnow() attempt.doc_type=AccessAudit.__name__ attempt.access_type = models.ACCESS_FAILED attempt.user_agent=ua attempt.user = attempted_username attempt.ip_address=ip #attempt.get_data = [query2str(request.GET.items())] #attempt.post_data= [query2str(request.POST.items())] attempt.http_accept=request.META.get('HTTP_ACCEPT', '<unknown>') attempt.path_info=request.META.get('PATH_INFO', '<unknown>') attempt.failures_since_start=failures attempt.save() log.info('AXES: New login failure by %s. Creating access record.', ip) else: #it's a successful login. #if we're django 1.3, this will have already been logged. if django.get_version() < '1.3': AccessAudit.audit_login(request, request.user) return True
def audited_logout (request, *args, **kwargs): # share some useful information func = auth_views.logout logging.info("Function: %s" %(func.__name__)) logging.info("Logged logout for user %s" % (request.user.username)) user = request.user # it's a successful login. ip = request.META.get('REMOTE_ADDR', '') ua = request.META.get('HTTP_USER_AGENT', '<unknown>') attempt = AccessAudit() attempt.doc_type=AccessAudit.__name__ attempt.access_type = models.ACCESS_LOGOUT attempt.user_agent=ua attempt.user = user.username attempt.session_key = request.session.session_key attempt.ip_address=ip attempt.get_data=[] attempt.post_data=[] attempt.http_accept=request.META.get('HTTP_ACCEPT', '<unknown>') attempt.path_info=request.META.get('PATH_INFO', '<unknown>') attempt.failures_since_start=0 attempt.save() # call the logout function response = func(request, *args, **kwargs) return response
def decorated_logout(request, *args, **kwargs): # share some useful information if func.__name__ != 'decorated_logout' and VERBOSE: log.info('AXES: Calling decorated logout function: %s', func.__name__) if args: log.info('args: %s', args) if kwargs: log.info('kwargs: %s', kwargs) log.info("Function: %s", func.__name__) log.info("Logged logout for user %s", request.user.username) user = request.user #it's a successful login. ip = request.META.get('REMOTE_ADDR', '') ua = request.META.get('HTTP_USER_AGENT', '<unknown>') attempt = AccessAudit() attempt.doc_type = AccessAudit.__name__ attempt.access_type = models.ACCESS_LOGOUT attempt.user_agent = ua attempt.user = user.username attempt.session_key = request.session.session_key attempt.ip_address = ip attempt.get_data = [] #[query2str(request.GET.items())] attempt.post_data = [] attempt.http_accept = request.META.get('HTTP_ACCEPT', '<unknown>') attempt.path_info = request.META.get('PATH_INFO', '<unknown>') attempt.failures_since_start = 0 attempt.save() # call the logout function response = func(request, *args, **kwargs) if func.__name__ == 'decorated_logout': # if we're dealing with this function itself, don't bother checking # for invalid login attempts. I suppose there's a bunch of # recursion going on here that used to cause one failed login # attempt to generate 10+ failed access attempt records (with 3 # failed attempts each supposedly) return response return response