Example #1
0
    def test_13_parse_time_offset_from_now(self):
        td = parse_timedelta("+5s")
        self.assertEqual(td, timedelta(seconds=5))
        td = parse_timedelta("-12m")
        self.assertEqual(td, timedelta(minutes=-12))
        td = parse_timedelta("+123h")
        self.assertEqual(td, timedelta(hours=123))
        td = parse_timedelta("+2d")
        self.assertEqual(td, timedelta(days=2))

        # It is allowed to start without a +/- which would mean a +
        td = parse_timedelta("12d")
        self.assertEqual(td, timedelta(days=12))

        # Does not contains numbers
        self.assertRaises(Exception, parse_timedelta, "+twod")

        s, td = parse_time_offset_from_now("Hello {now}+5d with 5 days.")
        self.assertEqual(s, "Hello {now} with 5 days.")
        self.assertEqual(td, timedelta(days=5))

        s, td = parse_time_offset_from_now("Hello {current_time}+5m!")
        self.assertEqual(s, "Hello {current_time}!")
        self.assertEqual(td, timedelta(minutes=5))

        s, td = parse_time_offset_from_now("Hello {current_time}-3habc")
        self.assertEqual(s, "Hello {current_time}abc")
        self.assertEqual(td, timedelta(hours=-3))
    def test_13_parse_time_offset_from_now(self):
        td = parse_timedelta("+5s")
        self.assertEqual(td, timedelta(seconds=5))
        td = parse_timedelta("-12m")
        self.assertEqual(td, timedelta(minutes=-12))
        td = parse_timedelta("+123h")
        self.assertEqual(td, timedelta(hours=123))
        td = parse_timedelta("+2d")
        self.assertEqual(td, timedelta(days=2))

        # It is allowed to start without a +/- which would mean a +
        td = parse_timedelta("12d")
        self.assertEqual(td, timedelta(days=12))

        # Does not contains numbers
        self.assertRaises(Exception, parse_timedelta, "+twod")

        s, td = parse_time_offset_from_now("Hello {now}+5d with 5 days.")
        self.assertEqual(s, "Hello {now} with 5 days.")
        self.assertEqual(td, timedelta(days=5))

        s, td = parse_time_offset_from_now("Hello {current_time}+5m!")
        self.assertEqual(s, "Hello {current_time}!")
        self.assertEqual(td, timedelta(minutes=5))

        s, td = parse_time_offset_from_now("Hello {current_time}-3habc")
        self.assertEqual(s, "Hello {current_time}abc")
        self.assertEqual(td, timedelta(hours=-3))
Example #3
0
def auth_cache(wrapped_function, user_object, passw, options=None):
    """
    Decorate lib.token:check_user_pass. Verify, if the authentication can 
    be found in the auth_cache.
    
    :param wrapped_function: usually "check_user_pass"
    :param user_object: User who tries to authenticate
    :param passw: The PIN and OTP
    :param options: Dict containing values for "g" and "clientip".
    :return: Tuple of True/False and reply-dictionary
    """
    options = options or {}
    g = options.get("g")
    auth_cache_dict = None

    if g:
        auth_cache_dict = Match.user(g,
                                     scope=SCOPE.AUTH,
                                     action=ACTION.AUTH_CACHE,
                                     user_object=user_object).action_values(
                                         unique=True, write_to_audit_log=False)
        if auth_cache_dict:
            auth_times = list(auth_cache_dict)[0].split("/")

            # determine first_auth from policy!
            first_offset = parse_timedelta(auth_times[0])
            first_auth = datetime.datetime.utcnow() - first_offset
            last_auth = first_auth  # Default if no last auth exists
            max_auths = 0  # Default value, 0 has no effect on verification

            # Use auth cache when number of allowed authentications is defined
            if len(auth_times) == 2:
                if re.match(r"^\d+$", auth_times[1]):
                    max_auths = int(auth_times[1])
                else:
                    # Determine last_auth delta from policy
                    last_offset = parse_timedelta(auth_times[1])
                    last_auth = datetime.datetime.utcnow() - last_offset

            result = verify_in_cache(user_object.login,
                                     user_object.realm,
                                     user_object.resolver,
                                     passw,
                                     first_auth=first_auth,
                                     last_auth=last_auth,
                                     max_auths=max_auths)

            if result:
                g.audit_object.add_policy(next(iter(auth_cache_dict.values())))
                return True, {"message": "Authenticated by AuthCache."}

    # If nothing else returned, call the wrapped function
    res, reply_dict = wrapped_function(user_object, passw, options)
    if auth_cache_dict and res:
        # If authentication is successful, we store the password in auth_cache
        add_to_cache(user_object.login, user_object.realm,
                     user_object.resolver, passw)
    return res, reply_dict
Example #4
0
def auth_cache(wrapped_function, user_object, passw, options=None):
    """
    Decorate lib.token:check_user_pass. Verify, if the authentication can 
    be found in the auth_cache.
    
    :param wrapped_function: usually "check_user_pass"
    :param user_object: User who tries to authenticate
    :param passw: The PIN and OTP
    :param options: Dict containing values for "g" and "clientip".
    :return: Tuple of True/False and reply-dictionary
    """
    options = options or {}
    g = options.get("g")
    auth_cache_dict = None
    if g:
        clientip = options.get("clientip")
        policy_object = g.policy_object
        auth_cache_dict = policy_object.get_action_values(
            action=ACTION.AUTH_CACHE,
            scope=SCOPE.AUTH,
            realm=user_object.realm,
            resolver=user_object.resolver,
            user=user_object.login,
            client=clientip,
            unique=True)
        if auth_cache_dict:
            # verify in cache and return an early success
            auth_times = list(auth_cache_dict)[0].split("/")
            # determine first_auth from policy!
            first_offset = parse_timedelta(auth_times[0])

            if len(auth_times) == 2:
                # Determine last_auth from policy
                last_offset = parse_timedelta(auth_times[1])
            else:
                # If there is no last_auth, it is equal to first_auth
                last_offset = first_offset

            first_auth = datetime.datetime.utcnow() - first_offset
            last_auth = datetime.datetime.utcnow() - last_offset
            result = verify_in_cache(user_object.login,
                                     user_object.realm,
                                     user_object.resolver,
                                     passw,
                                     first_auth=first_auth,
                                     last_auth=last_auth)
            if result:
                g.audit_object.add_policy(next(iter(auth_cache_dict.values())))
                return True, {"message": "Authenticated by AuthCache."}

    # If nothing else returned, call the wrapped function
    res, reply_dict = wrapped_function(user_object, passw, options)
    if auth_cache_dict and res:
        # If authentication is successful, we store the password in auth_cache
        add_to_cache(user_object.login, user_object.realm,
                     user_object.resolver, passw)
    return res, reply_dict
def auth_cache(wrapped_function, user_object, passw, options=None):
    """
    Decorate lib.token:check_user_pass. Verify, if the authentication can 
    be found in the auth_cache.
    
    :param wrapped_function: usually "check_user_pass"
    :param user_object: User who tries to authenticate
    :param passw: The PIN and OTP
    :param options: Dict containing values for "g" and "clientip".
    :return: Tuple of True/False and reply-dictionary
    """
    options = options or {}
    g = options.get("g")
    auth_cache_dict = None
    if g:
        clientip = options.get("clientip")
        policy_object = g.policy_object
        auth_cache_dict = policy_object.get_action_values(
            action=ACTION.AUTH_CACHE,
            scope=SCOPE.AUTH,
            realm=user_object.realm,
            resolver=user_object.resolver,
            user=user_object.login,
            client=clientip,
            unique=True)
        if auth_cache_dict:
            # verify in cache and return an early success
            auth_times = list(auth_cache_dict)[0].split("/")
            # determine first_auth from policy!
            first_offset = parse_timedelta(auth_times[0])

            if len(auth_times) == 2:
                # Determine last_auth from policy
                last_offset = parse_timedelta(auth_times[1])
            else:
                # If there is no last_auth, it is equal to first_auth
                last_offset = first_offset

            first_auth = datetime.datetime.utcnow() - first_offset
            last_auth = datetime.datetime.utcnow() - last_offset
            result = verify_in_cache(user_object.login, user_object.realm,
                                     user_object.resolver, passw,
                                     first_auth=first_auth,
                                     last_auth=last_auth)
            if result:
                g.audit_object.add_policy(next(iter(auth_cache_dict.values())))
                return True, {"message": "Authenticated by AuthCache."}

    # If nothing else returned, call the wrapped function
    res, reply_dict = wrapped_function(user_object, passw, options)
    if auth_cache_dict and res:
        # If authentication is successful, we store the password in auth_cache
        add_to_cache(user_object.login, user_object.realm, user_object.resolver, passw)
    return res, reply_dict
Example #6
0
def auditlog_age(request=None, action=None):
    """
    This pre condition checks for the policy auditlog_age and set the
    "timelimit" paramter of the audit search API.

    Check ACTION.AUDIT_AGE

    The decorator can wrap GET /audit/

    :param request: The request that is intercepted during the API call
    :type request: Request Object
    :param action: An optional Action
    :type action: basestring
    :returns: Always true. Modified the parameter request
    """
    user_object = request.User
    policy_object = g.policy_object
    role = g.logged_in_user.get("role")
    if role == ROLE.ADMIN:
        scope = SCOPE.ADMIN
        adminrealm = g.logged_in_user.get("realm")
        user = g.logged_in_user.get("username")
        realm = user_object.realm
    else:
        scope = SCOPE.USER
        adminrealm = None
        user = user_object.login
        realm = user_object.realm

    audit_age = policy_object.get_action_values(ACTION.AUDIT_AGE,
                                                scope=scope,
                                                adminrealm=adminrealm,
                                                realm=realm,
                                                user=user,
                                                client=g.client_ip,
                                                unique=True)
    timelimit = None
    timelimit_s = None
    for aa in audit_age:
        if not timelimit:
            timelimit_s = aa
            timelimit = parse_timedelta(timelimit_s)
        else:
            # We will use the longest allowed timelimit
            if parse_timedelta(aa) > timelimit:
                timelimit_s = aa
                timelimit = parse_timedelta(timelimit_s)

        log.debug("auditlog_age: {0!s}".format(timelimit_s))
        request.all_data["timelimit"] = timelimit_s

    return True
Example #7
0
def auditlog_age(request=None, action=None):
    """
    This pre condition checks for the policy auditlog_age and set the
    "timelimit" parameter of the audit search API.

    Check ACTION.AUDIT_AGE

    The decorator can wrap GET /audit/

    :param request: The request that is intercepted during the API call
    :type request: Request Object
    :param action: An optional Action
    :type action: basestring
    :returns: Always true. Modified the parameter request
    """
    user_object = request.User
    policy_object = g.policy_object
    role = g.logged_in_user.get("role")
    if role == ROLE.ADMIN:
        scope = SCOPE.ADMIN
        adminrealm = g.logged_in_user.get("realm")
        user = g.logged_in_user.get("username")
        realm = user_object.realm
    else:
        scope = SCOPE.USER
        adminrealm = None
        user = user_object.login
        realm = user_object.realm

    audit_age = policy_object.get_action_values(ACTION.AUDIT_AGE,
                                                scope=scope,
                                                adminrealm=adminrealm,
                                                realm=realm,
                                                user=user,
                                                client=g.client_ip,
                                                unique=True)
    timelimit = None
    timelimit_s = None
    for aa in audit_age:
        if not timelimit:
            timelimit_s = aa
            timelimit = parse_timedelta(timelimit_s)
        else:
            # We will use the longest allowed timelimit
            if parse_timedelta(aa) > timelimit:
                timelimit_s = aa
                timelimit = parse_timedelta(timelimit_s)

        log.debug("auditlog_age: {0!s}".format(timelimit_s))
        request.all_data["timelimit"] = timelimit_s

    return True
Example #8
0
    def test_02_timedelta(self):
        tdelta = parse_timedelta("123d")
        self.assertEqual(tdelta, timedelta(days=123))

        tdelta = parse_timedelta("31h")
        self.assertEqual(tdelta, timedelta(hours=31))

        tdelta = parse_timedelta(" 2y")
        self.assertEqual(tdelta, timedelta(days=2*365))

        # A missing time specifier raises an Exception
        self.assertRaises(Exception, parse_timedelta, "7")

        # A non number raises an Exception
        self.assertRaises(Exception, parse_timedelta, "sevenm")
Example #9
0
def search(config, param=None, user=None):
    """
    Returns a list of audit entries, supports pagination

    :param config: The config entries from the file config
    :return: Audit dictionary with information about the previous and next
    pages.
    """
    audit = getAudit(config)
    sortorder = "desc"
    page_size = 15
    page = 1
    timelimit = None
    hidden_columns = None
    # The filtering dictionary
    param = param or {}
    # special treatment for:
    # sortorder, page, pagesize
    if "sortorder" in param:
        sortorder = param["sortorder"]
        del param["sortorder"]
    if "page" in param:
        page = param["page"]
        del param["page"]
    if "page_size" in param:
        page_size = param["page_size"]
        del param["page_size"]
    if "timelimit" in param:
        timelimit = parse_timedelta(param["timelimit"])
        del param["timelimit"]
    if "hidden_columns" in param:
        hidden_columns = param["hidden_columns"]
        del param["hidden_columns"]

    pagination = audit.search(param,
                              sortorder=sortorder,
                              page=page,
                              page_size=page_size,
                              timelimit=timelimit)

    # delete hidden columns from response
    if hidden_columns:
        for i in range(len(pagination.auditdata)):
            pagination.auditdata[i] = OrderedDict({
                audit_col: value
                for audit_col, value in pagination.auditdata[i].items()
                if audit_col not in hidden_columns
            })

    ret = {
        "auditdata": pagination.auditdata,
        "prev": pagination.prev,
        "next": pagination.next,
        "current": pagination.page,
        "count": pagination.total
    }

    return ret
Example #10
0
def download_csv(csvfile=None):
    """
    Download the audit entry as CSV file.

    Params can be passed as key-value-pairs.

    **Example request**:

    .. sourcecode:: http

       GET /audit/audit.csv?realm=realm1 HTTP/1.1
       Host: example.com
       Accept: text/csv

    **Example response**:

    .. sourcecode:: http

       HTTP/1.1 200 OK
       Content-Type: text/csv

        {
          "id": 1,
          "jsonrpc": "2.0",
          "result": {
            "status": true,
            "value": [
              {
                 "serial": "....",
                 "missing_line": "..."
              }
            ]
          },
          "version": "privacyIDEA unknown"
        }
    """
    audit = getAudit(current_app.config)
    g.audit_object.log({'success': True})
    param = request.all_data
    if "timelimit" in param:
        timelimit = parse_timedelta(param["timelimit"])
        del param["timelimit"]
    else:
        timelimit = None
    return current_app.response_class(stream_with_context(
        audit.csv_generator(param=param, timelimit=timelimit)),
                                      mimetype='text/csv',
                                      headers={
                                          "Content-Disposition":
                                          ("attachment; "
                                           "filename=%s" % csvfile)
                                      })
Example #11
0
def download_csv(csvfile=None):
    """
    Download the audit entry as CSV file.

    Params can be passed as key-value-pairs.

    **Example request**:

    .. sourcecode:: http

       GET /audit/audit.csv?realm=realm1 HTTP/1.1
       Host: example.com
       Accept: text/csv

    **Example response**:

    .. sourcecode:: http

       HTTP/1.1 200 OK
       Content-Type: text/csv

        {
          "id": 1,
          "jsonrpc": "2.0",
          "result": {
            "status": true,
            "value": [
              {
                 "serial": "....",
                 "missing_line": "..."
              }
            ]
          },
          "version": "privacyIDEA unknown"
        }
    """
    audit = getAudit(current_app.config)
    g.audit_object.log({'success': True})
    param = request.all_data
    if "timelimit" in param:
        timelimit = parse_timedelta(param["timelimit"])
        del param["timelimit"]
    else:
        timelimit = None
    return Response(stream_with_context(audit.csv_generator(param=param,
                                                            timelimit=timelimit)),
                    mimetype='text/csv',
                    headers={"Content-Disposition": ("attachment; "
                                                     "filename=%s" % csvfile)})
Example #12
0
def search(config, param=None, user=None):
    """
    Returns a list of audit entries, supports pagination

    :param config: The config entries from the file config
    :return: Audit dictionary with information about the previous and next
    pages.
    """
    audit = getAudit(config)
    sortorder = "desc"
    page_size = 15
    page = 1
    timelimit = None
    # The filtering dictionary
    param = param or {}
    # special treatment for:
    # sortorder, page, pagesize
    if "sortorder" in param:
        sortorder = param["sortorder"]
        del param["sortorder"]
    if "page" in param:
        page = param["page"]
        del param["page"]
    if "page_size" in param:
        page_size = param["page_size"]
        del param["page_size"]
    if "timelimit" in param:
        timelimit = parse_timedelta(param["timelimit"])
        del param["timelimit"]

    pagination = audit.search(param,
                              sortorder=sortorder,
                              page=page,
                              page_size=page_size,
                              timelimit=timelimit)

    ret = {
        "auditdata": pagination.auditdata,
        "prev": pagination.prev,
        "next": pagination.next,
        "current": pagination.page,
        "count": pagination.total
    }

    return ret
Example #13
0
def search(config, param=None, user=None):
    """
    Returns a list of audit entries, supports pagination

    :param config: The config entries from the file config
    :return: Audit dictionary with information about the previous and next
    pages.
    """
    audit = getAudit(config)
    sortorder = "desc"
    page_size = 15
    page = 1
    timelimit = None
    # The filtering dictionary
    param = param or {}
    # special treatment for:
    # sortorder, page, pagesize
    if "sortorder" in param:
        sortorder = param["sortorder"]
        del param["sortorder"]
    if "page" in param:
        page = param["page"]
        del param["page"]
    if "page_size" in param:
        page_size = param["page_size"]
        del param["page_size"]
    if "timelimit" in param:
        timelimit = parse_timedelta(param["timelimit"])
        del param["timelimit"]

    pagination = audit.search(param, sortorder=sortorder, page=page,
                              page_size=page_size, timelimit=timelimit)

    ret = {"auditdata": pagination.auditdata,
           "prev": pagination.prev,
           "next": pagination.next,
           "current": pagination.page,
           "count": pagination.total}

    return ret
Example #14
0
def auth_lastauth(wrapped_function, user_or_serial, passw, options=None):
    """
    This decorator checks the policy settings of ACTION.LASTAUTH
    If the last authentication stored in tokeninfo last_auth_success of a
    token is exceeded, the authentication is denied.

    The wrapped function is usually token.check_user_pass, which takes the
    arguments (user, passw, options={}) OR
    token.check_serial_pass with the arguments (user, passw, options={})

    :param wrapped_function: either check_user_pass or check_serial_pass
    :param user_or_serial: either the User user_or_serial or a serial
    :param passw:
    :param options: Dict containing values for "g" and "clientip"
    :return: Tuple of True/False and reply-dictionary
    """
    # First we call the wrapped function
    res, reply_dict = wrapped_function(user_or_serial, passw, options)

    options = options or {}
    g = options.get("g")
    if g and res:
        clientip = options.get("clientip")
        policy_object = g.policy_object

        # in case of a serial:
        realm = None
        login = None
        serial = user_or_serial
        try:
            # Assume we have a user
            realm = user_or_serial.realm
            login = user_or_serial.login
            serial = reply_dict.get("serial")
        except Exception:
            # in case of a serial:
            realm = None
            login = None
            serial = user_or_serial

        # In case of a passthru policy we have no serial in the response
        # So we may only continue, if we have a serial.
        if serial:
            from privacyidea.lib.token import get_tokens
            try:
                token = get_tokens(serial=serial)[0]
            except IndexError:
                # In the special case of a registration token, the token does not
                # exist anymore. So we immediately return
                return res, reply_dict

            last_auth = policy_object.get_action_values(
                action=ACTION.LASTAUTH,
                scope=SCOPE.AUTHZ,
                realm=realm,
                user=login,
                client=clientip, unique=True)

            if len(last_auth) == 1:
                # The tdelta in the policy
                tdelta = parse_timedelta(last_auth[0])

                # The last successful authentication of the token
                last_success_auth = token.get_tokeninfo(ACTION.LASTAUTH)
                if last_success_auth:
                    log.debug("Compare the last successful authentication of "
                              "token %s with policy "
                              "tdelat %s: %s" % (serial, tdelta,
                                                 last_success_auth))
                    # convert string of last_success_auth
                    last_success_auth = datetime.datetime.strptime(
                        last_success_auth, "%Y-%m-%d %H:%M:%S.%f")
                    # The last auth is to far in the past
                    if last_success_auth + tdelta < datetime.datetime.now():
                        res = False
                        log.debug("The last successful authentication is too old.")
                        reply_dict["message"] = "The last successful " \
                                                "authentication was %s. It is to " \
                                                "long ago." % last_success_auth

            # set the last successful authentication, if res still true
            if res:
                token.add_tokeninfo(ACTION.LASTAUTH, datetime.datetime.utcnow())

    return res, reply_dict
Example #15
0
def auth_lastauth(wrapped_function, user_or_serial, passw, options=None):
    """
    This decorator checks the policy settings of ACTION.LASTAUTH
    If the last authentication stored in tokeninfo last_auth_success of a
    token is exceeded, the authentication is denied.

    The wrapped function is usually token.check_user_pass, which takes the
    arguments (user, passw, options={}) OR
    token.check_serial_pass with the arguments (user, passw, options={})

    :param wrapped_function: either check_user_pass or check_serial_pass
    :param user_or_serial: either the User user_or_serial or a serial
    :param passw:
    :param options: Dict containing values for "g" and "clientip"
    :return: Tuple of True/False and reply-dictionary
    """
    # First we call the wrapped function
    res, reply_dict = wrapped_function(user_or_serial, passw, options)

    options = options or {}
    g = options.get("g")
    if g and res:
        clientip = options.get("clientip")
        policy_object = g.policy_object

        # in case of a serial:
        realm = None
        login = None
        serial = user_or_serial
        try:
            # Assume we have a user
            realm = user_or_serial.realm
            login = user_or_serial.login
            serial = reply_dict.get("serial")
        except Exception:
            # in case of a serial:
            realm = None
            login = None
            serial = user_or_serial

        # In case of a passthru policy we have no serial in the response
        # So we may only continue, if we have a serial.
        if serial:
            from privacyidea.lib.token import get_tokens
            try:
                token = get_tokens(serial=serial)[0]
            except IndexError:
                # In the special case of a registration token, the token does not
                # exist anymore. So we immediately return
                return res, reply_dict

            last_auth = policy_object.get_action_values(action=ACTION.LASTAUTH,
                                                        scope=SCOPE.AUTHZ,
                                                        realm=realm,
                                                        user=login,
                                                        client=clientip,
                                                        unique=True)

            if len(last_auth) == 1:
                # The tdelta in the policy
                tdelta = parse_timedelta(last_auth[0])

                # The last successful authentication of the token
                last_success_auth = token.get_tokeninfo(ACTION.LASTAUTH)
                if last_success_auth:
                    log.debug("Compare the last successful authentication of "
                              "token %s with policy "
                              "tdelat %s: %s" %
                              (serial, tdelta, last_success_auth))
                    # convert string of last_success_auth
                    last_success_auth = datetime.datetime.strptime(
                        last_success_auth, "%Y-%m-%d %H:%M:%S.%f")
                    # The last auth is to far in the past
                    if last_success_auth + tdelta < datetime.datetime.now():
                        res = False
                        log.debug(
                            "The last successful authentication is too old.")
                        reply_dict["message"] = "The last successful " \
                                                "authentication was %s. It is to " \
                                                "long ago." % last_success_auth

            # set the last successful authentication, if res still true
            if res:
                token.add_tokeninfo(ACTION.LASTAUTH,
                                    datetime.datetime.utcnow())

    return res, reply_dict