def login_attempts(request): """ Track number of login attempts made by a specific IP within a specified amount of time """ ip, username = check_lockout(request) attempts_key = safe_key("{}{}-{}".format(LOGIN_ATTEMPTS, ip, username)) attempts = cache.get(attempts_key) if attempts: cache.incr(attempts_key) attempts = cache.get(attempts_key) if attempts >= getattr(settings, "MAX_LOGIN_ATTEMPTS", 10): lockout_key = safe_key("{}{}-{}".format(LOCKOUT_IP, ip, username)) lockout = cache.get(lockout_key) if not lockout: send_lockout_email(username, ip) cache.set( lockout_key, datetime.now().strftime("%Y-%m-%dT%H:%M:%S"), getattr(settings, "LOCKOUT_TIME", 1800), ) check_lockout(request) return attempts return attempts cache.set(attempts_key, 1) return cache.get(attempts_key)
def check_lockout(request) -> Tuple[Optional[str], Optional[str]]: """Check request user is not locked out on authentication. Returns the username if not locked out, None if request path is in LOCKOUT_EXCLUDED_PATHS. Raises AuthenticationFailed on lockout. """ uri_path = request.get_full_path() if not any(part in LOCKOUT_EXCLUDED_PATHS for part in uri_path.split("/")): ip, username = retrieve_user_identification(request) if ip and username: lockout = cache.get( safe_key("{}{}-{}".format(LOCKOUT_IP, ip, username))) if lockout: time_locked_out = datetime.now() - datetime.strptime( lockout, "%Y-%m-%dT%H:%M:%S") remaining_time = round( (getattr(settings, "LOCKOUT_TIME", 1800) - time_locked_out.seconds) / 60) raise AuthenticationFailed( _("Locked out. Too many wrong username" "/password attempts. " "Try again in {} minutes.".format(remaining_time))) return ip, username return None, None
def xformsManifest(request, username, id_string): # pylint: disable=C0103 """ XFormManifest view, part of OpenRosa Form Discovery API 1.0. """ xform_kwargs = { 'id_string__iexact': id_string, 'user__username__iexact': username } xform = get_form(xform_kwargs) formlist_user = xform.user profile = cache.get(f'{USER_PROFILE_PREFIX}{formlist_user.username}') if not profile: profile, __ = UserProfile.objects.get_or_create( user__username=formlist_user.username) if profile.require_auth: authenticator = HttpDigestAuthenticator() if not authenticator.authenticate(request): return authenticator.build_challenge_response() response = render( request, "xformsManifest.xml", { 'host': request.build_absolute_uri().replace(request.get_full_path(), ''), 'media_files': MetaData.media_upload(xform, download=True) }, content_type="text/xml; charset=utf-8") response['X-OpenRosa-Version'] = '1.0' response['Date'] = datetime.now(pytz.timezone(settings.TIME_ZONE))\ .strftime('%a, %d %b %Y %H:%M:%S %Z') return response
def check_lockout(request): """Check request user is not locked out on authentication. Returns the username if not locked out, None if request path is in LOCKOUT_EXCLUDED_PATHS. Raises AuthenticationFailed on lockout. """ uri_path = request.get_full_path() if any(part in LOCKOUT_EXCLUDED_PATHS for part in uri_path.split("/")): return None try: if isinstance(request.META["HTTP_AUTHORIZATION"], bytes): username = (request.META["HTTP_AUTHORIZATION"].decode( "utf-8").split('"')[1]) else: username = request.META["HTTP_AUTHORIZATION"].split('"')[1] except (TypeError, AttributeError, IndexError): pass else: lockout = cache.get(safe_key("{}{}".format(LOCKOUT_USER, username))) if lockout: time_locked_out = datetime.now() - datetime.strptime( lockout, "%Y-%m-%dT%H:%M:%S") remaining_time = round((getattr(settings, "LOCKOUT_TIME", 1800) - time_locked_out.seconds) / 60) raise AuthenticationFailed( _("Locked out. Too many wrong username/password attempts. " "Try again in {} minutes.".format(remaining_time))) return username return None
def user_profile_w_token_response(request, status): """ Returns authenticated user profile""" if request and not request.user.is_anonymous: session = getattr(request, "session") if not session.session_key: # login user to create session token # TODO cannot call this without calling authenticate first or # setting the backend, commented for now. # login(request, request.user) session.set_expiry(DEFAULT_SESSION_EXPIRY_TIME) try: user_profile = request.user.profile except UserProfile.DoesNotExist: user_profile = cache.get( f'{USER_PROFILE_PREFIX}{request.user.username}') if not user_profile: with use_master: user_profile, _ = UserProfile.objects.get_or_create( user=request.user) cache.set(f'{USER_PROFILE_PREFIX}{request.user.username}', user_profile) serializer = UserProfileWithTokenSerializer(instance=user_profile, context={"request": request}) return Response(serializer.data, status=status)
def change_password_attempts(request): """Track number of login attempts made by user within a specified amount of time""" username = request.user.username password_attempts = '{}{}'.format(CHANGE_PASSWORD_ATTEMPTS, username) attempts = cache.get(password_attempts) if attempts: cache.incr(password_attempts) attempts = cache.get(password_attempts) if attempts >= MAX_CHANGE_PASSWORD_ATTEMPTS: cache.set('{}{}'.format(LOCKOUT_CHANGE_PASSWORD_USER, username), datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S'), LOCKOUT_TIME) if check_user_lockout(request): return check_user_lockout(request) return attempts cache.set(password_attempts, 1) return 1
def check_user_lockout(request): username = request.user.username lockout = cache.get('{}{}'.format(LOCKOUT_CHANGE_PASSWORD_USER, username)) response_obj = { 'error': 'Too many password reset attempts, Try again in {} minutes' } if lockout: time_locked_out = \ datetime.datetime.now() - datetime.datetime.strptime( lockout, '%Y-%m-%dT%H:%M:%S') remaining_time = round((LOCKOUT_TIME - time_locked_out.seconds) / 60) response = response_obj['error'].format(remaining_time) return response
def formList(request, username): # pylint: disable=C0103 """ formList view, /formList OpenRosa Form Discovery API 1.0. """ formlist_user = get_object_or_404(User, username__iexact=username) profile = cache.get(f'{USER_PROFILE_PREFIX}{formlist_user.username}') if not profile: profile, __ = UserProfile.objects.get_or_create( user__username=formlist_user.username) if profile.require_auth: authenticator = HttpDigestAuthenticator() if not authenticator.authenticate(request): return authenticator.build_challenge_response() # unauthorized if user in auth request does not match user in path # unauthorized if user not active if not request.user.is_active: return HttpResponseNotAuthorized() # filter private forms (where require_auth=False) # for users who are non-owner if request.user.username == profile.user.username: xforms = XForm.objects.filter(downloadable=True, deleted_at__isnull=True, user__username__iexact=username) else: xforms = XForm.objects.filter(downloadable=True, deleted_at__isnull=True, user__username__iexact=username, require_auth=False) audit = {} audit_log(Actions.USER_FORMLIST_REQUESTED, request.user, formlist_user, _("Requested forms list."), audit, request) data = { 'host': request.build_absolute_uri().replace(request.get_full_path(), ''), 'xforms': xforms } response = render(request, "xformsList.xml", data, content_type="text/xml; charset=utf-8") response['X-OpenRosa-Version'] = '1.0' response['Date'] = datetime.now(pytz.timezone(settings.TIME_ZONE))\ .strftime('%a, %d %b %Y %H:%M:%S %Z') return response
def check_lockout(request): try: if isinstance(request.META["HTTP_AUTHORIZATION"], bytes): username = (request.META["HTTP_AUTHORIZATION"].decode( "utf-8").split('"')[1]) else: username = request.META["HTTP_AUTHORIZATION"].split('"')[1] except (TypeError, AttributeError, IndexError): return else: lockout = cache.get(safe_key("{}{}".format(LOCKOUT_USER, username))) if lockout: time_locked_out = datetime.now() - datetime.strptime( lockout, "%Y-%m-%dT%H:%M:%S") remaining_time = round((getattr(settings, "LOCKOUT_TIME", 1800) - time_locked_out.seconds) / 60) raise AuthenticationFailed( _("Locked out. Too many wrong username/password attempts. " "Try again in {} minutes.".format(remaining_time))) return username