Exemple #1
0
def mangle_challenge_response(request, response):
    """
    This policy decorator is used in the AUTH scope to
    decorate the /validate/check endpoint.
    It can modify the contents of the response "detail"->"message"
    to allow a better readability for a challenge response text.

    :param request:
    :param response:
    :return:
    """
    if not response.is_json:
        # This can happen with the validate/radiuscheck endpoint
        return response
    content = response.json
    user_obj = request.User

    header_pol = Match.user(g,
                            scope=SCOPE.AUTH,
                            action=ACTION.CHALLENGETEXT_HEADER,
                            user_object=user_obj).action_values(
                                unique=True, allow_white_space_in_action=True)
    footer_pol = Match.user(g,
                            scope=SCOPE.AUTH,
                            action=ACTION.CHALLENGETEXT_FOOTER,
                            user_object=user_obj).action_values(
                                unique=True, allow_white_space_in_action=True)
    if header_pol:
        multi_challenge = content.get("detail", {}).get("multi_challenge")
        if multi_challenge:
            message = list(header_pol)[0]
            footer = ""
            if footer_pol:
                footer = list(footer_pol)[0]
            # We actually have challenge response
            messages = content.get("detail", {}).get("messages") or []
            messages = sorted(set(messages))
            if message[-4:].lower() in ["<ol>", "<ul>"]:
                for m in messages:
                    message += u"<li>{0!s}</li>\n".format(m)
            else:
                message += "\n"
                message += ", ".join(messages)
                message += "\n"
            # Add the footer
            message += footer

            content["detail"]["message"] = message
            response.set_data(json.dumps(content))

    return response
Exemple #2
0
def auth_user_has_no_token(wrapped_function, user_object, passw,
                           options=None):
    """
    This decorator checks if the user has a token at all.
    If the user has a token, the wrapped function is called.

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

    :param wrapped_function:
    :param user_object:
    :param passw:
    :param options: Dict containing values for "g" and "clientip"
    :return: Tuple of True/False and reply-dictionary
    """
    from privacyidea.lib.token import get_tokens
    options = options or {}
    g = options.get("g")
    if g:
        pass_no_token = Match.user(g, scope=SCOPE.AUTH, action=ACTION.PASSNOTOKEN,
                                   user_object=user_object).policies(write_to_audit_log=False)
        if pass_no_token:
            # Now we need to check, if the user really has no token.
            tokencount = get_tokens(user=user_object, count=True)
            if tokencount == 0:
                g.audit_object.add_policy([p.get("name") for p in pass_no_token])
                return True, {"message": u"user has no token, accepted due to '{!s}'".format(
                    pass_no_token[0].get("name"))}

    # If nothing else returned, we return the wrapped function
    return wrapped_function(user_object, passw, options)
Exemple #3
0
def auth_user_does_not_exist(wrapped_function, user_object, passw,
                               options=None):
    """
    This decorator checks, if the user does exist at all.
    If the user does exist, the wrapped function is called.

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

    :param wrapped_function:
    :param user_object:
    :param passw:
    :param options: Dict containing values for "g" and "clientip"
    :return: Tuple of True/False and reply-dictionary
    """
    options = options or {}
    g = options.get("g")
    if g:
        pass_no_user = Match.user(g, scope=SCOPE.AUTH, action=ACTION.PASSNOUSER,
                                  user_object=user_object).policies(write_to_audit_log=False)
        if pass_no_user:
            # Check if user object exists
            if not user_object.exist():
                g.audit_object.add_policy([p.get("name") for p in pass_no_user])
                return True, {"message": u"user does not exist, accepted due to '{!s}'".format(
                    pass_no_user[0].get("name"))}

    # If nothing else returned, we return the wrapped function
    return wrapped_function(user_object, passw, options)
Exemple #4
0
    def _get_sms_text(options):
        """
        This returns the SMSTEXT from the policy "smstext"
        
        options contains data like clientip, g, user and also the Request 
        parameters like "challenge" or "pass".

        :param options: contains user and g object.
        :type options: dict
        :return: Message template
        :rtype: basestring
        """
        message = "<otp>"
        g = options.get("g")
        user_object = options.get("user")
        if g:
            messages = Match.user(g,
                                  scope=SCOPE.AUTH,
                                  action=SMSACTION.SMSTEXT,
                                  user_object=user_object if user_object else
                                  None).action_values(unique=True)
            if len(messages) == 1:
                message = list(messages)[0]

        # Replace the {challenge}:
        message = message.format(challenge=options.get("challenge"))
        return message
Exemple #5
0
def login_mode(wrapped_function, *args, **kwds):
    """
    Decorator to decorate the lib.auth.check_webui_user function.
    Depending on ACTION.LOGINMODE it sets the check_otp parameter, to signal
    that the authentication should be performed against privacyIDEA.

    :param wrapped_function: Usually the function check_webui_user
    :param `*args`: arguments user_obj and password
    :param `**kwds`: keyword arguments like options and !check_otp!
        kwds["options"] contains the flask g
    :return: calls the original function with the modified "check_otp" argument
    """
    # if tokenclass.check_pin is called in any other way, options may be None
    #  or it might have no element "g".
    options = kwds.get("options") or {}
    g = options.get("g")
    if g:
        # We need the user but we do not need the password
        user_object = args[0]
        # get the policy
        login_mode_dict = Match.user(g, scope=SCOPE.WEBUI, action=ACTION.LOGINMODE,
                                     user_object=user_object).action_values(unique=True)
        if login_mode_dict:
            # There is a login mode policy
            if list(login_mode_dict)[0] == LOGINMODE.PRIVACYIDEA:
                # The original function should check against privacyidea!
                kwds["check_otp"] = True

            if list(login_mode_dict)[0] == LOGINMODE.DISABLE:
                # The login to the webui is disabled
                raise PolicyError("The login for this user is disabled.")

    return wrapped_function(*args, **kwds)
Exemple #6
0
    def challenge_response_wrapper(*args, **kwds):
        options = kwds.get("options", {})
        g = options.get("g")
        token = args[0]
        user_object = kwds.get("user") or User()
        if g:
            allowed_tokentypes_dict = Match.user(g, scope=SCOPE.AUTH,
                                                 action=ACTION.CHALLENGERESPONSE, user_object=user_object)\
                .action_values(unique=False, write_to_audit_log=False)
            log.debug("Found these allowed tokentypes: {0!s}".format(
                list(allowed_tokentypes_dict)))

            # allowed_tokentypes_dict.keys() is a list of actions from several policies. I
            # could look like this:
            # ["tiqr hotp totp", "tiqr motp"]
            chal_resp_found = False
            for toks in allowed_tokentypes_dict:
                if token.get_tokentype().upper() in [
                        x.upper() for x in toks.split(" ")
                ]:
                    # This token is allowed to to chal resp
                    chal_resp_found = True
                    g.audit_object.add_policy(
                        allowed_tokentypes_dict.get(toks))

            if not chal_resp_found:
                # No policy to allow this token to do challenge response
                return False

        f_result = func(*args, **kwds)
        return f_result
Exemple #7
0
def reset_all_user_tokens(wrapped_function, *args, **kwds):
    """
    Resets all tokens if the corresponding policy is set.

    :param token: The successful token, the tokenowner is used to find policies.
    :param tokenobject_list: The list of all the tokens of the user
    :param options: options dictionary containing g.
    :return: None
    """
    tokenobject_list = args[0]
    options = kwds.get("options") or {}
    g = options.get("g")
    allow_reset = kwds.get("allow_reset_all_tokens")

    r = wrapped_function(*args, **kwds)

    # A successful authentication was done
    if r[0] and g and allow_reset:
        token_owner = tokenobject_list[0].user
        reset_all = Match.user(
            g,
            scope=SCOPE.AUTH,
            action=ACTION.RESETALLTOKENS,
            user_object=token_owner if token_owner else None).policies()
        if reset_all:
            log.debug(
                "Reset failcounter of all tokens of {0!s}".format(token_owner))
            for tok_obj_reset in tokenobject_list:
                try:
                    tok_obj_reset.reset()
                except Exception:
                    log.debug("registration token does not exist anymore and "
                              "cannot be reset.")

    return r
Exemple #8
0
def check_tokentype(request, response):
    """
    This policy function is to be used in a decorator of an API function.
    It checks, if the token, that was used in the API call is of a type that
    is allowed to be used.

    If not, a PolicyException is raised.

    :param response: The response of the decorated function
    :type response: Response object
    :return: A new (maybe modified) response
    """
    tokentype = response.json.get("detail", {}).get("type")
    user_object = request.User
    allowed_tokentypes = Match.user(
        g, scope=SCOPE.AUTHZ, action=ACTION.TOKENTYPE,
        user_object=user_object).action_values(unique=False)
    if tokentype and allowed_tokentypes and tokentype not in allowed_tokentypes:
        # If we have tokentype policies, but
        # the tokentype is not allowed, we raise an exception
        g.audit_object.log({
            "success":
            False,
            'action_detail':
            "Tokentype {0!r} not allowed for "
            "authentication".format(tokentype)
        })
        raise PolicyError("Tokentype not allowed for authentication!")
    return response
Exemple #9
0
def is_authorized(request, response):
    """
    This policy decorator is used in the AUTHZ scope to
    decorate the /validate/check and /validate/triggerchallenge endpoint.
    I will cause authentication to fail, if the policy
    authorized=deny_access is set.

    :param request:
    :param response:
    :return:
    """
    if not response.is_json:
        # This can happen with the validate/radiuscheck endpoint
        return response

    authorized_pol = Match.user(g,
                                scope=SCOPE.AUTHZ,
                                action=ACTION.AUTHORIZED,
                                user_object=request.User).action_values(
                                    unique=True,
                                    allow_white_space_in_action=True)

    if authorized_pol:
        if list(authorized_pol)[0] == AUTHORIZED.DENY:
            raise ValidateError(
                "User is not authorized to authenticate under these conditions."
            )

    return response
Exemple #10
0
def config_lost_token(wrapped_function, *args, **kwds):
    """
    Decorator to decorate the lib.token.lost_token function.
    Depending on ACTION.LOSTTOKENVALID, ACTION.LOSTTOKENPWCONTENTS,
    ACTION.LOSTTOKENPWLEN it sets the check_otp parameter, to signal
    how the lostToken should be generated.

    :param wrapped_function: Usually the function lost_token()
    :param args: argument "serial" as the old serial number
    :param kwds: keyword arguments like "validity", "contents", "pw_len"
    kwds["options"] contains the flask g

    :return: calls the original function with the modified "validity",
    "contents" and "pw_len" argument
    """
    # if called in any other way, options may be None
    #  or it might have no element "g".
    from privacyidea.lib.token import get_tokens
    options = kwds.get("options") or {}
    g = options.get("g")
    if g:
        # We need the old serial number, to determine the user - if it exist.
        serial = args[0]
        toks = get_tokens(serial=serial)
        if len(toks) == 1:
            user_object = toks[0].user
            # get the policy
            contents_dict = Match.user(g, scope=SCOPE.ENROLL, action=ACTION.LOSTTOKENPWCONTENTS,
                                       user_object=user_object if user_object else None)\
                .action_values(unique=True)
            validity_dict = Match.user(g, scope=SCOPE.ENROLL, action=ACTION.LOSTTOKENVALID,
                                       user_object=user_object if user_object else None)\
                .action_values(unique=True)
            pw_len_dict = Match.user(g, scope=SCOPE.ENROLL, action=ACTION.LOSTTOKENPWLEN,
                                     user_object=user_object if user_object else None)\
                .action_values(unique=True)

            if contents_dict:
                kwds["contents"] = list(contents_dict)[0]

            if validity_dict:
                kwds["validity"] = int(list(validity_dict)[0])

            if pw_len_dict:
                kwds["pw_len"] = int(list(pw_len_dict)[0])

    return wrapped_function(*args, **kwds)
Exemple #11
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:
        # in case of a serial:
        if isinstance(user_or_serial, User):
            user_object = user_or_serial
            serial = reply_dict.get("serial")
        else:
            # in case of a serial:
            user_object = 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_dict = Match.user(g, scope=SCOPE.AUTHZ, action=ACTION.LASTAUTH,
                                        user_object=user_object).action_values(unique=True, write_to_audit_log=False)
            if len(last_auth_dict) == 1:
                res = token.check_last_auth_newer(list(last_auth_dict)[0])
                if not res:
                    reply_dict["message"] = "The last successful " \
                                            "authentication was %s. " \
                                            "It is to long ago." % \
                                            token.get_tokeninfo(ACTION.LASTAUTH)
                    g.audit_object.add_policy(next(iter(last_auth_dict.values())))

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

    return res, reply_dict
Exemple #12
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
Exemple #13
0
def auth_otppin(wrapped_function, *args, **kwds):
    """
    Decorator to decorate the tokenclass.check_pin function.

    Depending on the ACTION.OTPPIN it
     * either simply accepts an empty pin
     * checks the pin against the userstore
     * or passes the request to the wrapped_function

    :param wrapped_function: In this case the wrapped function should be
        :py:func:`privacyidea.lib.tokenclass.TokenClass.check_pin`
    :param `*args`: args[1] is the pin
    :param `**kwds`: kwds["options"] contains the flask g
    :return: True or False
    """
    # if tokenclass.check_pin is called in any other way, options may be None
    #  or it might have no element "g".
    options = kwds.get("options") or {}
    g = options.get("g")
    if g:
        token = args[0]
        pin = args[1]
        clientip = options.get("clientip")
        user_object = kwds.get("user")
        if not user_object:
            # No user in the parameters, so we need to determine the owner of
            #  the token
            user_object = token.user
            realms = token.get_realms()
            if not user_object and len(realms):
                # if the token has not owner, we take a realm.
                user_object = User("", realm=realms[0])
        if not user_object:
            # If we still have no user and no tokenrealm, we create an empty
            # user object.
            user_object = User("", realm="")
        # get the policy
        otppin_dict = Match.user(
            g, scope=SCOPE.AUTH, action=ACTION.OTPPIN,
            user_object=user_object).action_values(unique=True)
        if otppin_dict:
            if list(otppin_dict)[0] == ACTIONVALUE.NONE:
                if pin == "":
                    # No PIN checking, we expect an empty PIN!
                    return True
                else:
                    return False

            if list(otppin_dict)[0] == ACTIONVALUE.USERSTORE:
                rv = user_object.check_password(pin)
                return rv is not None

    # call and return the original check_pin function
    return wrapped_function(*args, **kwds)
Exemple #14
0
def check_verify_enrollment(request, response):
    """
    This policy decorator is used in the ENROLL scope to
    decorate the /token/init
    If will check for action=verify_enrollment and ask the user
    in a 2nd step to provide information to verify, that the token was successfully enrolled.

    :param request:
    :param response:
    :return:
    """
    serial = response.json.get("detail").get("serial")
    verify = request.all_data.get("verify")
    if verify:
        # In case we are in a 2nd step verification, we must early exit
        return response
    tokenobj_list = get_tokens(serial=serial)
    if len(tokenobj_list) == 1:
        tokenobj = tokenobj_list[0]
        # check if this token type can do verify enrollment
        if tokenobj.can_verify_enrollment:
            # Get policies
            verify_pol_dict = Match.user(
                g,
                scope=SCOPE.ENROLL,
                action=ACTION.VERIFY_ENROLLMENT,
                user_object=request.User).action_values(
                    unique=False,
                    allow_white_space_in_action=True,
                    write_to_audit_log=False)
            # verify_pol_dict.keys() is a list of actions from several policies. It
            # could look like this:
            # ["hotp totp", "hotp email"]
            do_verify_enrollment = False
            for toks in verify_pol_dict:
                if tokenobj.get_tokentype().upper() in [
                        x.upper() for x in toks.split(" ")
                ]:
                    # This token is supposed to do verify enrollment
                    do_verify_enrollment = True
                    g.audit_object.add_policy(verify_pol_dict.get(toks))
            if do_verify_enrollment:
                content = response.json
                content["detail"][
                    "verify"] = tokenobj.prepare_verify_enrollment()
                content["detail"]["rollout_state"] = ROLLOUTSTATE.VERIFYPENDING
                tokenobj.token.rollout_state = ROLLOUTSTATE.VERIFYPENDING
                tokenobj.token.save()
                response.set_data(json.dumps(content))
    else:
        log.warning("No distinct token object found in enrollment response!")

    return response
Exemple #15
0
def add_user_detail_to_response(request, response):
    """
    This policy decorated is used in the AUTHZ scope.
    If the boolean value add_user_in_response is set,
    the details will contain a dictionary "user" with all user details.

    :param request:
    :param response:
    :return:
    """
    content = response.json

    # Check for ADD USER IN RESPONSE
    detail_pol = Match.user(g, scope=SCOPE.AUTHZ, action=ACTION.ADDUSERINRESPONSE, user_object=request.User)\
        .policies(write_to_audit_log=False)
    if detail_pol and content.get("result", {}).get("value") and request.User:
        # The policy was set, we need to add the user
        #  details
        ui = request.User.info.copy()
        ui["password"] = ""
        for key, value in ui.items():
            if type(value) == datetime.datetime:
                ui[key] = str(value)
        content.setdefault("detail", {})["user"] = ui
        g.audit_object.add_policy([p.get("name") for p in detail_pol])

    # Check for ADD RESOLVER IN RESPONSE
    detail_pol = Match.user(g, scope=SCOPE.AUTHZ, action=ACTION.ADDRESOLVERINRESPONSE, user_object=request.User)\
        .policies(write_to_audit_log=False)
    if detail_pol and content.get("result", {}).get("value") and request.User:
        # The policy was set, we need to add the resolver and the realm
        content.setdefault("detail",
                           {})["user-resolver"] = request.User.resolver
        content["detail"]["user-realm"] = request.User.realm
        g.audit_object.add_policy([p.get("name") for p in detail_pol])

    response.set_data(json.dumps(content))
    return response
    def _get_auto_email(options):
        """
        This returns the AUTOEMAIL setting.

        :param options: contains user and g object.
        :optins type: dict
        :return: True if an SMS should be sent automatically
        :rtype: bool
        """
        autosms = False
        g = options.get("g")
        user_object = options.get("user")
        if g:
            autoemailpol = Match.user(g, scope=SCOPE.AUTH, action=EMAILACTION.EMAILAUTO, user_object=user_object).policies()
            autosms = len(autoemailpol) >= 1

        return autosms
Exemple #17
0
    def _get_email_text_or_subject(options,
                                   action=EMAILACTION.EMAILTEXT,
                                   default="<otp>"):
        """
        This returns the EMAILTEXT or EMAILSUBJECT from the policy
        "emailtext" or "emailsubject

        :param options: contains user and g object.
        :type options: dict
        :param action: The action - either emailtext or emailsubject
        :param default: If no policy can be found, this is the default text
        :return: Message template, MIME type (one of "plain", "html")
        :rtype: (basestring, basestring)
        """
        message = default
        mimetype = "plain"
        g = options.get("g")
        user_object = options.get("user")
        if g:
            messages = Match.user(g, scope=SCOPE.AUTH, action=action, user_object=user_object if user_object else None)\
                .action_values(unique=True, allow_white_space_in_action=True)
            if len(messages) == 1:
                message = list(messages)[0]

        message = message.format(challenge=options.get("challenge"))
        if message.startswith("file:"):
            # We read the template from the file.
            try:
                with open(message[5:], "r") as f:
                    message = f.read()
                    mimetype = "html"
            except Exception as e:  # pragma: no cover
                message = default
                log.warning(u"Failed to read email template: {0!r}".format(e))
                log.debug(u"{0!s}".format(traceback.format_exc()))

        return message, mimetype
Exemple #18
0
def autoassign(request, response):
    """
    This decorator decorates the function /validate/check.
    Depending on ACTION.AUTOASSIGN it checks if the user has no token and if
    the given OTP-value matches a token in the users realm, that is not yet
    assigned to any user.

    If a token can be found, it assigns the token to the user also taking
    into account ACTION.MAXTOKENUSER and ACTION.MAXTOKENREALM.
    :return:
    """
    content = response.json
    # check, if the authentication was successful, then we need to do nothing
    if content.get("result").get("value") is False:
        user_obj = request.User
        #user_obj = get_user_from_param(request.all_data)
        password = request.all_data.get("pass", "")
        if user_obj.login and user_obj.realm:
            # If there is no user in the request (because it is a serial
            # authentication request) we immediately bail out
            # check if the policy is defined
            autoassign_values = Match.user(g, scope=SCOPE.ENROLL, action=ACTION.AUTOASSIGN,
                                           user_object=user_obj).action_values(unique=True, write_to_audit_log=False)
            # check if the user has no token
            if autoassign_values and get_tokens(user=user_obj, count=True) == 0:
                # Check if the token would match
                # get all unassigned tokens in the realm and look for
                # a matching OTP:
                realm_tokens = get_tokens(realm=user_obj.realm,
                                          assigned=False)

                for token_obj in realm_tokens:
                    (res, pin, otp) = token_obj.split_pin_pass(password)
                    if res:
                        pin_check = True
                        if list(autoassign_values)[0] == \
                                AUTOASSIGNVALUE.USERSTORE:
                            # If the autoassign policy is set to userstore,
                            # we need to check against the userstore.
                            pin_check = user_obj.check_password(pin)
                        if pin_check:
                            otp_check = token_obj.check_otp(otp)
                            if otp_check >= 0:
                                # we found a matching token
                                #    check MAXTOKENUSER and MAXTOKENREALM
                                check_max_token_user(request=request)
                                check_max_token_realm(request=request)
                                #    Assign token
                                assign_token(serial=token_obj.token.serial,
                                             user=user_obj, pin=pin)
                                # Set the response to true
                                content.get("result")["value"] = True
                                # Set the serial number
                                detail = content.setdefault("detail", {})
                                detail["serial"] = token_obj.token.serial
                                detail["otplen"] = token_obj.token.otplen
                                detail["type"] = token_obj.type
                                detail["message"] = "Token assigned to user via Autoassignment"
                                response.set_data(json.dumps(content))

                                g.audit_object.log(
                                    {"success": True,
                                     "info":
                                         "Token assigned via auto assignment",
                                     "serial": token_obj.token.serial})
                                # The token was assigned by autoassign. We save the first policy name
                                g.audit_object.add_policy(next(iter(autoassign_values.values())))
                                break

    return response
Exemple #19
0
def get_webui_settings(request, response):
    """
    This decorator is used in the /auth API to add configuration information
    like the logout_time or the policy_template_url to the response.
    :param request: flask request object
    :param response: flask response object
    :return: the response
    """
    content = response.json
    # check, if the authentication was successful, then we need to do nothing
    if content.get("result").get("status") is True:
        role = content.get("result").get("value").get("role")
        loginname = content.get("result").get("value").get("username")
        realm = content.get("result").get("value").get("realm") or get_default_realm()

        # At this point the logged in user is not necessarily a user object. It can
        # also be a local admin.
        logout_time_pol = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.LOGOUTTIME,
                                        user=loginname, realm=realm).action_values(unique=True)
        timeout_action_pol = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.TIMEOUT_ACTION,
                                           user=loginname, realm=realm).action_values(unique=True)
        token_page_size_pol = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.TOKENPAGESIZE,
                                            user=loginname, realm=realm).action_values(unique=True)
        user_page_size_pol = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.USERPAGESIZE,
                                           user=loginname, realm=realm).action_values(unique=True)
        token_wizard_2nd = bool(role == ROLE.USER
                                and Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.TOKENWIZARD2ND,
                                                  user=loginname, realm=realm).policies())
        admin_dashboard = (role == ROLE.ADMIN
                           and Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.ADMIN_DASHBOARD,
                                         user=loginname, realm=realm).any())
        token_wizard = False
        dialog_no_token = False
        if role == ROLE.USER:
            user_obj = User(loginname, realm)
            user_token_num = get_tokens(user=user_obj, count=True)
            token_wizard_pol = Match.user(g, scope=SCOPE.WEBUI, action=ACTION.TOKENWIZARD, user_object=user_obj).any()
            # We also need to check, if the user has not tokens assigned.
            # If the user has no tokens, we run the wizard. If the user
            # already has tokens, we do not run the wizard.
            token_wizard = token_wizard_pol and (user_token_num == 0)

            dialog_no_token_pol = Match.user(g, scope=SCOPE.WEBUI, action=ACTION.DIALOG_NO_TOKEN,
                                             user_object=user_obj).any()
            dialog_no_token = dialog_no_token_pol and (user_token_num == 0)
        user_details_pol = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.USERDETAILS,
                                         user=loginname, realm=realm).policies()
        search_on_enter = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.SEARCH_ON_ENTER,
                                        user=loginname, realm=realm).policies()
        hide_welcome = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.HIDE_WELCOME,
                                     user=loginname, realm=realm).any()
        hide_buttons = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.HIDE_BUTTONS,
                                     user=loginname, realm=realm).any()
        default_tokentype_pol = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.DEFAULT_TOKENTYPE,
                                              user=loginname, realm=realm).action_values(unique=True)
        show_seed = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.SHOW_SEED,
                                  user=loginname, realm=realm).any()
        show_node = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.SHOW_NODE, realm=realm).any()
        qr_ios_authenticator = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.SHOW_IOS_AUTHENTICATOR,
                                             user=loginname, realm=realm).any()
        qr_android_authenticator = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.SHOW_ANDROID_AUTHENTICATOR,
                                                 user=loginname, realm=realm).any()
        qr_custom_authenticator_url = Match.generic(g, scope=SCOPE.WEBUI, action=ACTION.SHOW_CUSTOM_AUTHENTICATOR,
                                                    user=loginname, realm=realm).action_values(unique=True)

        qr_image_android = create_img(DEFAULT_ANDROID_APP_URL) if qr_android_authenticator else None
        qr_image_ios = create_img(DEFAULT_IOS_APP_URL) if qr_ios_authenticator else None
        qr_image_custom = create_img(list(qr_custom_authenticator_url)[0]) if qr_custom_authenticator_url else None
        token_page_size = DEFAULT_PAGE_SIZE
        user_page_size = DEFAULT_PAGE_SIZE
        default_tokentype = DEFAULT_TOKENTYPE
        if len(token_page_size_pol) == 1:
            token_page_size = int(list(token_page_size_pol)[0])
        if len(user_page_size_pol) == 1:
            user_page_size = int(list(user_page_size_pol)[0])
        if len(default_tokentype_pol) == 1:
            default_tokentype = list(default_tokentype_pol)[0]

        logout_time = DEFAULT_LOGOUT_TIME
        if len(logout_time_pol) == 1:
            logout_time = int(list(logout_time_pol)[0])

        timeout_action = DEFAULT_TIMEOUT_ACTION
        if len(timeout_action_pol) == 1:
            timeout_action = list(timeout_action_pol)[0]

        policy_template_url_pol = Match.action_only(g, scope=SCOPE.WEBUI,
                                                    action=ACTION.POLICYTEMPLATEURL).action_values(unique=True)
        policy_template_url = DEFAULT_POLICY_TEMPLATE_URL
        if len(policy_template_url_pol) == 1:
            policy_template_url = list(policy_template_url_pol)[0]

        indexed_preset_attribute = Match.realm(g, scope=SCOPE.WEBUI, action="indexedsecret_preset_attribute",
                                               realm=realm).action_values(unique=True)
        if len(indexed_preset_attribute) == 1:
            content["result"]["value"]["indexedsecret_preset_attribute"] = list(indexed_preset_attribute)[0]

        # This only works for users, because the value of the policy does not change while logged in.
        if role == ROLE.USER and \
                Match.user(g, SCOPE.USER, "indexedsecret_force_attribute", user_obj).action_values(unique=False):
            content["result"]["value"]["indexedsecret_force_attribute"] = 1

        content["result"]["value"]["logout_time"] = logout_time
        content["result"]["value"]["token_page_size"] = token_page_size
        content["result"]["value"]["user_page_size"] = user_page_size
        content["result"]["value"]["policy_template_url"] = policy_template_url
        content["result"]["value"]["default_tokentype"] = default_tokentype
        content["result"]["value"]["user_details"] = len(user_details_pol) > 0
        content["result"]["value"]["token_wizard"] = token_wizard
        content["result"]["value"]["token_wizard_2nd"] = token_wizard_2nd
        content["result"]["value"]["admin_dashboard"] = admin_dashboard
        content["result"]["value"]["dialog_no_token"] = dialog_no_token
        content["result"]["value"]["search_on_enter"] = len(search_on_enter) > 0
        content["result"]["value"]["timeout_action"] = timeout_action
        content["result"]["value"]["hide_welcome"] = hide_welcome
        content["result"]["value"]["hide_buttons"] = hide_buttons
        content["result"]["value"]["show_seed"] = show_seed
        content["result"]["value"]["show_node"] = get_privacyidea_node() if show_node else ""
        content["result"]["value"]["subscription_status"] = subscription_status()
        content["result"]["value"]["qr_image_android"] = qr_image_android
        content["result"]["value"]["qr_image_ios"] = qr_image_ios
        content["result"]["value"]["qr_image_custom"] = qr_image_custom
        response.set_data(json.dumps(content))
    return response
Exemple #20
0
def get_webui_settings(request, response):
    """
    This decorator is used in the /auth API to add configuration information
    like the logout_time or the policy_template_url to the response.
    :param request: flask request object
    :param response: flask response object
    :return: the response
    """
    content = response.json
    # check, if the authentication was successful, then we need to do nothing
    if content.get("result").get("status") is True:
        role = content.get("result").get("value").get("role")
        loginname = content.get("result").get("value").get("username")
        realm = content.get("result").get("value").get("realm")
        realm = realm or get_default_realm()

        logout_time_pol = Match.realm(g,
                                      scope=SCOPE.WEBUI,
                                      action=ACTION.LOGOUTTIME,
                                      realm=realm).action_values(unique=True)
        timeout_action_pol = Match.realm(
            g, scope=SCOPE.WEBUI, action=ACTION.TIMEOUT_ACTION,
            realm=realm).action_values(unique=True)
        token_page_size_pol = Match.realm(
            g, scope=SCOPE.WEBUI, action=ACTION.TOKENPAGESIZE,
            realm=realm).action_values(unique=True)
        user_page_size_pol = Match.realm(
            g, scope=SCOPE.WEBUI, action=ACTION.USERPAGESIZE,
            realm=realm).action_values(unique=True)
        token_wizard_2nd = (role == ROLE.USER and Match.realm(
            g, scope=SCOPE.WEBUI, action=ACTION.TOKENWIZARD2ND,
            realm=realm).policies())
        token_wizard = False
        dialog_no_token = False
        if role == ROLE.USER:
            user_obj = User(loginname, realm)
            user_token_num = get_tokens(user=user_obj, count=True)
            token_wizard_pol = Match.user(g,
                                          scope=SCOPE.WEBUI,
                                          action=ACTION.TOKENWIZARD,
                                          user_object=user_obj).any()
            # We also need to check, if the user has not tokens assigned.
            # If the user has no tokens, we run the wizard. If the user
            # already has tokens, we do not run the wizard.
            token_wizard = token_wizard_pol and (user_token_num == 0)

            dialog_no_token_pol = Match.user(g,
                                             scope=SCOPE.WEBUI,
                                             action=ACTION.DIALOG_NO_TOKEN,
                                             user_object=user_obj).any()
            dialog_no_token = dialog_no_token_pol and (user_token_num == 0)
        user_details_pol = Match.realm(g,
                                       scope=SCOPE.WEBUI,
                                       action=ACTION.USERDETAILS,
                                       realm=realm).policies()
        search_on_enter = Match.realm(g,
                                      scope=SCOPE.WEBUI,
                                      action=ACTION.SEARCH_ON_ENTER,
                                      realm=realm).policies()
        hide_welcome = Match.realm(g,
                                   scope=SCOPE.WEBUI,
                                   action=ACTION.HIDE_WELCOME,
                                   realm=realm).any()
        hide_buttons = Match.realm(g,
                                   scope=SCOPE.WEBUI,
                                   action=ACTION.HIDE_BUTTONS,
                                   realm=realm).any()
        default_tokentype_pol = Match.realm(
            g, scope=SCOPE.WEBUI, action=ACTION.DEFAULT_TOKENTYPE,
            realm=realm).action_values(unique=True)
        show_seed = Match.realm(g,
                                scope=SCOPE.WEBUI,
                                action=ACTION.SHOW_SEED,
                                realm=realm).any()
        token_page_size = DEFAULT_PAGE_SIZE
        user_page_size = DEFAULT_PAGE_SIZE
        default_tokentype = DEFAULT_TOKENTYPE
        if len(token_page_size_pol) == 1:
            token_page_size = int(list(token_page_size_pol)[0])
        if len(user_page_size_pol) == 1:
            user_page_size = int(list(user_page_size_pol)[0])
        if len(default_tokentype_pol) == 1:
            default_tokentype = list(default_tokentype_pol)[0]

        logout_time = DEFAULT_LOGOUT_TIME
        if len(logout_time_pol) == 1:
            logout_time = int(list(logout_time_pol)[0])

        timeout_action = DEFAULT_TIMEOUT_ACTION
        if len(timeout_action_pol) == 1:
            timeout_action = list(timeout_action_pol)[0]

        policy_template_url_pol = Match.action_only(
            g, scope=SCOPE.WEBUI,
            action=ACTION.POLICYTEMPLATEURL).action_values(unique=True)
        policy_template_url = DEFAULT_POLICY_TEMPLATE_URL
        if len(policy_template_url_pol) == 1:
            policy_template_url = list(policy_template_url_pol)[0]

        content["result"]["value"]["logout_time"] = logout_time
        content["result"]["value"]["token_page_size"] = token_page_size
        content["result"]["value"]["user_page_size"] = user_page_size
        content["result"]["value"]["policy_template_url"] = policy_template_url
        content["result"]["value"]["default_tokentype"] = default_tokentype
        content["result"]["value"]["user_details"] = len(user_details_pol) > 0
        content["result"]["value"]["token_wizard"] = token_wizard
        content["result"]["value"]["token_wizard_2nd"] = token_wizard_2nd
        content["result"]["value"]["dialog_no_token"] = dialog_no_token
        content["result"]["value"]["search_on_enter"] = len(
            search_on_enter) > 0
        content["result"]["value"]["timeout_action"] = timeout_action
        content["result"]["value"]["hide_welcome"] = hide_welcome
        content["result"]["value"]["hide_buttons"] = hide_buttons
        content["result"]["value"]["show_seed"] = show_seed
        content["result"]["value"][
            "subscription_status"] = subscription_status()
        response.set_data(json.dumps(content))
    return response
Exemple #21
0
def auth_user_timelimit(wrapped_function, user_object, passw, options=None):
    """
    This decorator checks the policy settings of
    ACTION.AUTHMAXSUCCESS,
    ACTION.AUTHMAXFAIL
    If the authentication was successful, it checks, if the number of allowed
    successful authentications is exceeded (AUTHMAXSUCCESS).

    If the AUTHMAXFAIL is exceed it denies even a successful authentication.

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

    :param wrapped_function:
    :param user_object:
    :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_object, passw, options)

    options = options or {}
    g = options.get("g")
    if g:
        max_success_dict = Match.user(g,
                                      scope=SCOPE.AUTHZ,
                                      action=ACTION.AUTHMAXSUCCESS,
                                      user_object=user_object).action_values(
                                          unique=True,
                                          write_to_audit_log=False)
        max_fail_dict = Match.user(g,
                                   scope=SCOPE.AUTHZ,
                                   action=ACTION.AUTHMAXFAIL,
                                   user_object=user_object).action_values(
                                       unique=True, write_to_audit_log=False)
        # Check for maximum failed authentications
        # Always - also in case of unsuccessful authentication
        if len(max_fail_dict) == 1:
            policy_count, tdelta = parse_timelimit(list(max_fail_dict)[0])
            fail_c = g.audit_object.get_count(
                {
                    "user": user_object.login,
                    "realm": user_object.realm,
                    "action": "%/validate/check"
                },
                success=False,
                timedelta=tdelta)
            log.debug("Checking users timelimit %s: %s "
                      "failed authentications with /validate/check" %
                      (list(max_fail_dict)[0], fail_c))
            fail_auth_c = g.audit_object.get_count(
                {
                    "user": user_object.login,
                    "realm": user_object.realm,
                    "info": "%loginmode=privacyIDEA%",
                    "action": "%/auth"
                },
                success=False,
                timedelta=tdelta)
            log.debug("Checking users timelimit %s: %s "
                      "failed authentications with /auth" %
                      (list(max_fail_dict)[0], fail_auth_c))
            if fail_c + fail_auth_c >= policy_count:
                res = False
                reply_dict["message"] = ("Only %s failed authentications "
                                         "per %s" % (policy_count, tdelta))
                g.audit_object.add_policy(next(iter(max_fail_dict.values())))

        if res:
            # Check for maximum successful authentications
            # Only in case of a successful authentication
            if len(max_success_dict) == 1:
                policy_count, tdelta = parse_timelimit(
                    list(max_success_dict)[0])
                # check the successful authentications for this user
                succ_c = g.audit_object.get_count(
                    {
                        "user": user_object.login,
                        "realm": user_object.realm,
                        "action": "%/validate/check"
                    },
                    success=True,
                    timedelta=tdelta)
                log.debug("Checking users timelimit %s: %s "
                          "successful authentications with /validate/check" %
                          (list(max_success_dict)[0], succ_c))
                succ_auth_c = g.audit_object.get_count(
                    {
                        "user": user_object.login,
                        "realm": user_object.realm,
                        "info": "%loginmode=privacyIDEA%",
                        "action": "%/auth"
                    },
                    success=True,
                    timedelta=tdelta)
                log.debug("Checking users timelimit %s: %s "
                          "successful authentications with /auth" %
                          (list(max_success_dict)[0], succ_auth_c))
                if succ_c + succ_auth_c >= policy_count:
                    res = False
                    reply_dict["message"] = ("Only %s successful "
                                             "authentications per %s" %
                                             (policy_count, tdelta))

    return res, reply_dict
Exemple #22
0
def auth_user_passthru(wrapped_function, user_object, passw, options=None):
    """
    This decorator checks the policy settings of ACTION.PASSTHRU.
    If the authentication against the userstore is not successful,
    the wrapped function is called.

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

    :param wrapped_function:
    :param user_object:
    :param passw:
    :param options: Dict containing values for "g" and "clientip"
    :return: Tuple of True/False and reply-dictionary
    """
    from privacyidea.lib.token import get_tokens
    from privacyidea.lib.token import assign_token
    options = options or {}
    g = options.get("g")
    if g:
        policy_object = g.policy_object
        pass_thru = Match.user(
            g,
            scope=SCOPE.AUTH,
            action=ACTION.PASSTHRU,
            user_object=user_object).policies(write_to_audit_log=False)
        # We only go to passthru, if the user has no tokens!
        if pass_thru and get_tokens(user=user_object, count=True) == 0:
            # Ensure that there are no conflicting action values within the same priority
            policy_object.check_for_conflicts(pass_thru, "passthru")
            pass_thru_action = pass_thru[0].get("action").get("passthru")
            policy_name = pass_thru[0].get("name")
            if pass_thru_action in ["userstore", True]:
                # Now we need to check the userstore password
                if user_object.check_password(passw):
                    g.audit_object.add_policy(
                        [p.get("name") for p in pass_thru])
                    return True, {
                        "message":
                        u"against userstore due to '{!s}'".format(policy_name)
                    }
            else:
                # We are doing RADIUS passthru
                log.info("Forwarding the authentication request to the radius "
                         "server %s" % pass_thru_action)
                radius = get_radius(pass_thru_action)
                r = radius.request(radius.config, user_object.login, passw)
                if r:
                    g.audit_object.add_policy(
                        [p.get("name") for p in pass_thru])
                    # TODO: here we can check, if the token should be assigned.
                    passthru_assign = Match.user(
                        g,
                        scope=SCOPE.AUTH,
                        action=ACTION.PASSTHRU_ASSIGN,
                        user_object=user_object).action_values(unique=True)
                    messages = []
                    if passthru_assign:
                        components = list(passthru_assign)[0].split(":")
                        if len(components) >= 2:
                            prepend_pin = components[0] == "pin"
                            otp_length = int(components[int(prepend_pin)])
                            pin, otp = split_pin_pass(passw, otp_length,
                                                      prepend_pin)
                            realm_tokens = get_tokens(realm=user_object.realm,
                                                      assigned=False)
                            window = 100
                            if len(components) == 3:
                                window = int(components[2])
                            for token_obj in realm_tokens:
                                otp_check = token_obj.check_otp(otp,
                                                                window=window)
                                if otp_check >= 0:
                                    # We do not check any max tokens per realm or user,
                                    # since this very user currently has no token
                                    # and the unassigned token already was contained in the user's realm
                                    assign_token(serial=token_obj.token.serial,
                                                 user=user_object,
                                                 pin=pin)
                                    messages.append(
                                        u"autoassigned {0!s}".format(
                                            token_obj.token.serial))
                                    break

                        else:
                            log.warning(
                                "Wrong value in passthru_assign policy: {0!s}".
                                format(passthru_assign))
                    messages.append(
                        u"against RADIUS server {!s} due to '{!s}'".format(
                            pass_thru_action, policy_name))
                    return True, {'message': ",".join(messages)}

    # If nothing else returned, we return the wrapped function
    return wrapped_function(user_object, passw, options)
Exemple #23
0
    def check_otp(self, otpval, counter=None, window=None, options=None):
        """
        This checks the response of a previous challenge.

        :param otpval: N/A
        :param counter: The authentication counter
        :param window: N/A
        :param options: contains "clientdata", "signaturedata" and
            "transaction_id"
        :return: A value > 0 in case of success
        """
        ret = -1
        clientdata = options.get("clientdata")
        signaturedata = options.get("signaturedata")
        transaction_id = options.get("transaction_id")
        # The challenge in the challenge DB object is saved in hex
        challenge = binascii.unhexlify(options.get("challenge", ""))
        if clientdata and signaturedata and transaction_id and challenge:
            # This is a valid response for a U2F token
            challenge_url = url_encode(challenge)
            clientdata = url_decode(clientdata)
            clientdata_dict = json.loads(to_unicode(clientdata))
            client_challenge = clientdata_dict.get("challenge")
            if challenge_url != client_challenge:
                return ret
            if clientdata_dict.get("typ") != "navigator.id.getAssertion":
                raise ValidateError("Incorrect navigator.id")
            #client_origin = clientdata_dict.get("origin")
            signaturedata = url_decode(signaturedata)
            signaturedata_hex = hexlify_and_unicode(signaturedata)
            user_presence, counter, signature = parse_response_data(
                signaturedata_hex)

            user_pub_key = self.get_tokeninfo("pubKey")
            app_id = self.get_tokeninfo("appId")
            if check_response(user_pub_key, app_id, clientdata,
                              hexlify_and_unicode(signature), counter,
                              user_presence):
                # Signature verified.
                # check, if the counter increased!
                if counter > self.get_otp_count():
                    self.set_otp_count(counter)
                    ret = counter
                    # At this point we can check, if the attestation
                    # certificate is authorized.
                    # If not, we can raise a policy exception
                    if not attestation_certificate_allowed(
                        {
                            "attestation_issuer":
                            self.get_tokeninfo("attestation_issuer"),
                            "attestation_serial":
                            self.get_tokeninfo("attestation_serial"),
                            "attestation_subject":
                            self.get_tokeninfo("attestation_subject")
                        },
                            Match.user(options.get("g"),
                                       scope=SCOPE.AUTHZ,
                                       action=U2FACTION.REQ,
                                       user_object=self.user if self.user else
                                       None).action_values(unique=False)):
                        log.warning(
                            "The U2F device {0!s} is not allowed to authenticate due to policy restriction"
                            .format(self.token.serial))
                        raise PolicyError("The U2F device is not allowed "
                                          "to authenticate due to policy "
                                          "restriction.")

                else:
                    log.warning("The signature of %s was valid, but contained "
                                "an old counter." % self.token.serial)
            else:
                log.warning("Checking response for token {0!s} failed.".format(
                    self.token.serial))

        return ret
Exemple #24
0
def check():
    """
    check the authentication for a user or a serial number.
    Either a ``serial`` or a ``user`` is required to authenticate.
    The PIN and OTP value is sent in the parameter ``pass``.
    In case of successful authentication it returns ``result->value: true``.

    In case of a challenge response authentication a parameter ``exception=1``
    can be passed. This would result in a HTTP 500 Server Error response if
    an error occurred during sending of SMS or Email.

    In case ``/validate/radiuscheck`` is requested, the responses are
    modified as follows: A successful authentication returns an empty HTTP
    204 response. An unsuccessful authentication returns an empty HTTP
    400 response. Error responses are the same responses as for the
    ``/validate/check`` endpoint.

    :param serial: The serial number of the token, that tries to authenticate.
    :param user: The loginname/username of the user, who tries to authenticate.
    :param realm: The realm of the user, who tries to authenticate. If the
        realm is omitted, the user is looked up in the default realm.
    :param pass: The password, that consists of the OTP PIN and the OTP value.
    :param otponly: If set to 1, only the OTP value is verified. This is used
        in the management UI. Only used with the parameter serial.
    :param transaction_id: The transaction ID for a response to a challenge
        request
    :param state: The state ID for a response to a challenge request

    :return: a json result with a boolean "result": true

    **Example Validation Request**:

        .. sourcecode:: http

           POST /validate/check HTTP/1.1
           Host: example.com
           Accept: application/json

           user=user
           realm=realm1
           pass=s3cret123456

    **Example response** for a successful authentication:

       .. sourcecode:: http

           HTTP/1.1 200 OK
           Content-Type: application/json

            {
              "detail": {
                "message": "matching 1 tokens",
                "serial": "PISP0000AB00",
                "type": "spass"
              },
              "id": 1,
              "jsonrpc": "2.0",
              "result": {
                "status": true,
                "value": true
              },
              "version": "privacyIDEA unknown"
            }

    **Example response** for this first part of a challenge response
    authentication:

       .. sourcecode:: http

           HTTP/1.1 200 OK
           Content-Type: application/json

            {
              "detail": {
                "serial": "PIEM0000AB00",
                "type": "email",
                "transaction_id": "12345678901234567890",
                "multi_challenge: [ {"serial": "PIEM0000AB00",
                                     "transaction_id":  "12345678901234567890",
                                     "message": "Please enter otp from your
                                     email"},
                                    {"serial": "PISM12345678",
                                     "transaction_id": "12345678901234567890",
                                     "message": "Please enter otp from your
                                     SMS"}
                ]
              },
              "id": 1,
              "jsonrpc": "2.0",
              "result": {
                "status": true,
                "value": false
              },
              "version": "privacyIDEA unknown"
            }

    In this example two challenges are triggered, one with an email and one
    with an SMS. The application and thus the user has to decide, which one
    to use. They can use either.

    .. note:: All challenge response tokens have the same transaction_id in
       this case.
    """
    user = request.User
    serial = getParam(request.all_data, "serial")
    password = getParam(request.all_data, "pass", required)
    otp_only = getParam(request.all_data, "otponly")
    options = {"g": g, "clientip": g.client_ip}
    # Add all params to the options
    for key, value in request.all_data.items():
        if value and key not in ["g", "clientip"]:
            options[key] = value

    allowed_tokentypes = Match.user(
        g, scope=SCOPE.AUTHZ, action=ACTION.TOKENTYPE,
        user_object=user).action_values(unique=False)

    if allowed_tokentypes:
        options["type"] = allowed_tokentypes

    g.audit_object.log({
        "user": user.login,
        "resolver": user.resolver,
        "realm": user.realm
    })

    if serial:
        if user:
            # check if the given token belongs to the user
            if not get_tokens(user=user, serial=serial, count=True):
                raise ParameterError(
                    'Given serial does not belong to given user!')
        if not otp_only:
            result, details = check_serial_pass(serial,
                                                password,
                                                options=options)
        else:
            result, details = check_otp(serial, password)

    else:
        result, details = check_user_pass(user, password, options=options)

    g.audit_object.log({
        "info": log_used_user(user, details.get("message")),
        "success": result,
        "serial": serial or details.get("serial"),
        "token_type": details.get("type")
    })
    return send_result(result, details=details)
Exemple #25
0
def trigger_challenge():
    """
    An administrator can call this endpoint if he has the right of
    ``triggerchallenge`` (scope: admin).
    He can pass a ``user`` name and or a ``serial`` number.
    privacyIDEA will trigger challenges for all native challenges response
    tokens, possessed by this user or only for the given serial number.

    The request needs to contain a valid PI-Authorization header.

    :param user: The loginname/username of the user, who tries to authenticate.
    :param realm: The realm of the user, who tries to authenticate. If the
        realm is omitted, the user is looked up in the default realm.
    :param serial: The serial number of the token.

    :return: a json result with a "result" of the number of matching
        challenge response tokens

    **Example response** for a successful triggering of challenge:

       .. sourcecode:: http

           {"jsonrpc": "2.0",
            "signature": "1939...146964",
            "detail": {"transaction_ids": ["03921966357577766962"],
                       "messages": ["Enter the OTP from the SMS:"],
                       "threadid": 140422378276608},
            "versionnumber": "unknown",
            "version": "privacyIDEA unknown",
            "result": {"status": true,
                       "value": 1},
            "time": 1482223663.517212,
            "id": 1}

    **Example response** for response, if the user has no challenge token:

       .. sourcecode:: http

           {"detail": {"messages": [],
                       "threadid": 140031212377856,
                       "transaction_ids": []},
            "id": 1,
            "jsonrpc": "2.0",
            "result": {"status": true,
                       "value": 0},
            "signature": "205530282...54508",
            "time": 1484303812.346576,
            "version": "privacyIDEA 2.17",
            "versionnumber": "2.17"}

    **Example response** for a failed triggering of a challenge. In this case
        the ``status`` will be ``false``.

       .. sourcecode:: http

           {"detail": null,
            "id": 1,
            "jsonrpc": "2.0",
            "result": {"error": {"code": 905,
                                 "message": "ERR905: The user can not be
                                 found in any resolver in this realm!"},
                       "status": false},
            "signature": "14468...081555",
            "time": 1484303933.72481,
            "version": "privacyIDEA 2.17"}

    """
    user = request.User
    serial = getParam(request.all_data, "serial")
    details = {"messages": [], "transaction_ids": []}
    options = {"g": g, "clientip": g.client_ip, "user": user}

    allowed_tokentypes = Match.user(
        g, scope=SCOPE.AUTHZ, action=ACTION.TOKENTYPE,
        user_object=user).action_values(unique=False)

    token_objs = get_tokens(tokentype=allowed_tokentypes,
                            serial=serial,
                            user=user,
                            active=True,
                            revoked=False,
                            locked=False)

    # Only use the tokens, that are allowed to do challenge response
    chal_resp_tokens = [
        token_obj for token_obj in token_objs if "challenge" in token_obj.mode
    ]
    create_challenges_from_tokens(chal_resp_tokens, details, options)
    result_obj = len(details.get("multi_challenge"))

    challenge_serials = [
        challenge_info["serial"]
        for challenge_info in details["multi_challenge"]
    ]
    g.audit_object.log({
        "user":
        user.login,
        "resolver":
        user.resolver,
        "realm":
        user.realm,
        "success":
        result_obj > 0,
        "info":
        log_used_user(user, "triggered {0!s} challenges".format(result_obj)),
        "serial":
        ",".join(challenge_serials),
    })

    return send_result(result_obj, details=details)