Repository: privacyidea/privacyidea

Issue:
passOnNoUser stop to work after upgrade from "2.22.1" to "2.23"
I've two Realm:    **a**    and   **b**

**Policies (in this order)**: 

a_authentication | authentication | {   "passthru": "userstore",   "reset_all_user_tokens": true,   "otppin": "tokenpin" } | [   "a" ] | [] | [] | []

b_authentication | authentication | {   "reset_all_user_tokens": true,   "passOnNoUser": true,   "passOnNoToken": true,   "otppin": "none" } | [   "b" ] | [] | [] | []

**Note**: 

Other options as "passOnNoToken" work correctly

**Connection:**
https://192.168.x.x/validate/check?user=xxxx&pass=xxxx&realm=b

**Results**:

detail
message	"The user does not exist, but is accepted due to policy 'b_authentication'."
threadid	140712283485952
versionnumber	"2.22.1"
version	"privacyIDEA 2.22.1"
result	
status	true
value	true
time	1535623419.165142
id	1


detail	null
version	"privacyIDEA 2.23"
result	
status	false
error	
message	"ERR904: The user can not be found in any resolver in this realm!"
code	904
time	1535623524.065644
id	1


Regards





Discussion:
I created the following unittest, that should simulate your problem:

~~~~python

class AA_Policy_Validate(MyTestCase):
    """
    test the api.validate endpoints
    """

    def test_00_create_realms(self):
        self.setUp_user_realms()
        self.setUp_user_realm2()

    def test_01_passOnNoUser(self):
        set_policy("auth_1", scope=SCOPE.AUTH, realm=self.realm1,
                   action={ACTION.PASSTHRU: "userstore",
                           ACTION.RESETALLTOKENS: True,
                           ACTION.OTPPIN: "tokenpin"}
                   )
        set_policy("auth_2", scope=SCOPE.AUTH, realm=self.realm2,
                   action={ACTION.PASSNOUSER: True,
                           ACTION.PASSNOTOKEN: True,
                           ACTION.RESETALLTOKENS: True,
                           ACTION.OTPPIN: "none"}
                   )
        with self.app.test_request_context('/validate/check',
                                           method='POST',
                                           data={"user": "UNKNOWN_USER_WHICH_WE_WILL_NEVER_CREATE",
                                                 "pass": "PIN",
                                                 "realm": self.realm2}):
            res = self.app.full_dispatch_request()
            self.assertEqual(res.status_code, 200)
            resp = json.loads(res.data)
            self.assertTrue(resp.get("result").get("value"))
            self.assertEqual(resp.get("detail").get("message"),
                             "user does not exist, accepted due to 'auth_2'")

        delete_policy("auth_1")
        delete_policy("auth_2")
~~~~

But it runs as expected. I can not reproduce your problem with your data.
So there is something missing.

What is your default realm?
Can you provide the debug privacyidea.log? (Or take a look at it and provide the corresponding lines)

Can you provide some more information about the realms? Are all users exclusive?
I will send you the log by email.
The "default" realm is "a" (and not this "b" used in this case)
It looks like it is due to a event handler you specified!

The event handler conditions determine the tokenowner - otherwise in your case the notification email could not be sent.
It bails out here:

https://github.com/privacyidea/privacyidea/blob/master/privacyidea/lib/eventhandler/base.py#L302

This would at least solve this issue:

~~~~python
        # conditions can be corresponding to the property conditions
        conditions = e_handler_def.get("conditions")
        content = self._get_response_content(response)
        try:
            user = self._get_tokenowner(request)
        except UserError:
            # There is an error with the user - maybe the user does not exist and we came here
            # due to PassOnNoUser.
            user = User()
~~~~


Resolving patch:
diff --git a/privacyidea/lib/eventhandler/base.py b/privacyidea/lib/eventhandler/base.py
--- a/privacyidea/lib/eventhandler/base.py
+++ b/privacyidea/lib/eventhandler/base.py
@@ -268,7 +268,9 @@ def _get_tokenowner(request):
         try:
             ui = user.info
         except UserError as exx:
-            if exx.id == 905:
+            if exx.id in [904, 905]:
+                # 904: User can not be found - maybe we got here due to PassOnNoUser
+                # 905: This would be a parameter error - why is this here?
                 user = User()
             else:
                 raise exx


Test patch:
diff --git a/tests/test_api_validate.py b/tests/test_api_validate.py
--- a/tests/test_api_validate.py
+++ b/tests/test_api_validate.py
@@ -12,6 +12,8 @@
 from privacyidea.lib.token import (get_tokens, init_token, remove_token,
                                    reset_token, enable_token, revoke_token)
 from privacyidea.lib.policy import SCOPE, ACTION, set_policy, delete_policy
+from privacyidea.lib.event import set_event
+from privacyidea.lib.event import delete_event
 from privacyidea.lib.error import ERROR
 from privacyidea.lib.resolver import save_resolver, get_resolver_list
 from privacyidea.lib.realm import set_realm, set_default_realm
@@ -1530,6 +1532,27 @@ def test_23_pass_no_user_and_pass_no_token(self):
                              u"user does not exist, accepted "
                              u"due to 'pass_no'")
 
+        # Creating a notification event. The non-existing user must
+        # still be able to pass!
+        eid = set_event("notify", event=["validate_check"], action="sendmail",
+                  handlermodule="UserNotification", conditions={"token_locked": True})
+
+        with self.app.test_request_context('/validate/check',
+                                           method='POST',
+                                           data={"user": "doesNotExist",
+                                                 "realm": self.realm2,
+                                                 "pass": pin}):
+            res = self.app.full_dispatch_request()
+            self.assertTrue(res.status_code == 200, res)
+            result = json.loads(res.data).get("result")
+            self.assertEqual(result.get("value"), True)
+            detail = json.loads(res.data).get("detail")
+            self.assertEqual(detail.get("message"),
+                             u"user does not exist, accepted "
+                             u"due to 'pass_no'")
+
+        delete_event(eid)
+
         r = get_tokens(user=User(user, self.realm2), count=True)
         self.assertEqual(r, 1)
         # User with no token can authenticate