Example #1
0
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)
Example #2
0
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
Example #3
0
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
Example #4
0
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
Example #5
0
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)
Example #6
0
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
Example #7
0
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
Example #8
0
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
Example #9
0
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