Random PIN generation - base pinhandler class not receiving full user data

Random PIN generation - base pinhandler class not receiving full user data

Hi!

I have an privacyIDEA installation running on Ubuntu Server 18.04 LTS, installed via Ubuntu Packages.
For our use-case it’s a mandatory feature to have a random PIN generated automatically upon token enrollment and send it to the user via SMS. I therefor want to use the default pinhandler class and modify it to our needs. The default function of the class works, so the PIN is logged. Unfortunately I’m not able to deliver the SMS via a request.get (or request.post) because the phone number of the user somehow is missing in the object delivered to the pinhandler (although in the sourcecode is mentioned to receive it from privacyIDEA).

  It receives the necessary data like
      * the PIN
      * the serial number of the token
      * the username
      * all other user data:

        *  given name, surname
        *  email address
        *  telephone
        *  mobile (if the module would deliver via SMS)
      * the administrator name (who enrolled the token)

I added under the log line a simple prints to verify the content of the variables:

        [...]
        # The most simple way of handling a random PIN! ;-)
        print (pin)
        print (serial)
        print (user)
        print (tokentype)
        print (logged_in_user)
        print (userdata)
        print (options)
        log.info("PIN {0!r} TOKENSN {1!r} forUSERNAME {2!r} ENROLLEDBY {3!r} USER {4!r} TOKENTYPE {5!r}".format(pin,serial,user, logged_in_user.get("username"), user, tokentype))
        [...]

However, the output in Apache Error Log from Flask remains “None” for userdata:

/var/log/apache2/error.log.2.gz:[Fri Aug 07 14:03:51.277341 2020] [wsgi:error] [pid 20746:tid 140614714185472] [remote 192.168.1.100:49239] laSE3U
/var/log/apache2/error.log.2.gz:[Fri Aug 07 14:03:51.277372 2020] [wsgi:error] [pid 20746:tid 140614714185472] [remote 192.168.1.100:49239] N/A
/var/log/apache2/error.log.2.gz:[Fri Aug 07 14:03:51.277388 2020] [wsgi:error] [pid 20746:tid 140614714185472] [remote 192.168.1.100:49239] <testuser@test>
/var/log/apache2/error.log.2.gz:[Fri Aug 07 14:03:51.277396 2020] [wsgi:error] [pid 20746:tid 140614714185472] [remote 192.168.1.100:49239] email
/var/log/apache2/error.log.2.gz:[Fri Aug 07 14:03:51.277405 2020] [wsgi:error] [pid 20746:tid 140614714185472] [remote 192.168.1.100:49239] {'username': 'admin', 'realm': '', 'role': 'admin'}
/var/log/apache2/error.log.2.gz:[Fri Aug 07 14:03:51.277405 2020] [wsgi:error] [pid 20929:tid 140614714185472] [remote 192.168.1.100:49865] None
/var/log/apache2/error.log.2.gz:[Fri Aug 07 14:03:51.277405 2020] [wsgi:error] [pid 20929:tid 140614714185472] [remote 192.168.1.100:49865] None

At first I had the impression it’s related to misconfigured sqlite mappings but the I’m encountering the same when trying to deploy a token to an LDAP user - the phone numbers are displayed correctly in the userlist though. I also tried removing the default values of the “send” function variables ("= None") which ended up generating python errors.
Is this an expected error or am I missing something here? Appreciate any help :slight_smile:

BR

Okay, I think I found out why I’m not getting attributes like phone or email.
The pinhandler is being called by prepolicy.py (function init_random_pin), which eventually gets the user details by calling the function get_user_from_param, located at user.py.
The “problem” is the get_user_from_param function, as it only returns the following attributes:

    user_object = User(login=username, realm=realm,
                       resolver=param.get("resolver"))

This is the exact same object I’m getting returned when I did a print of the variable inside the base-pinhandler.
However, the variable “params” in the function init_random_pin contains all the necessary informations to accomplish what is “promised” in the class description, see below:

[Mon Aug 10 13:51:22.779036 2020] [wsgi:error] [pid 31283:tid 140273895872256] [remote 192.168.1.100:54172] {'timeStep': 30, 'otplen': 6, 'genkey': True, 'type': 'email', 'hashlib': 'sha1', 'radius.system_settings': True, 'email': 'test@test.com', 'phone': '00123456789', 'dynamic_email': True, 'validity_period_start': '', 'validity_period_end': '', 'user': 'test', 'realm': 'test'}

As I now found out how I can “manipulate” the output of each function/variable, the question remains if the statement in the description of the base pinhandler (" It receives the necessary data like […] given name, surname, email address, telephone, mobile […]") isn’t a bit misleading. Don’t get me wrong, I appreciate this project very much as it does exactly what it claims it should. I know exactly that much of coding to accomplish my idea/plan - but I think a lot of users or administrators will have a hard nut to crack when they read “it receives all necessary informations” when in fact it doesn’t :slight_smile:

Coming back to topic: is there anything that speaks agains populating the required informations like phone and email in the prepolicy.py using the prepared variable “userdata” like such?:

        pin_handler.send(request.all_data["pin"],
                    request.all_data.get("serial", "N/A"),
                    user_object,
                    tokentype=request.all_data.get("type", "hotp"),
                    logged_in_user=g.logged_in_user,
                    userdata=params.get("phone"))

Cheers

I think the problem might actually arise from the fact that your user does not have a phone number defined in the user resolver.

The call of pin_handler.send should be generic. Sending the phone number is not generic. Especially you are sending the phone number from the request, not from the user object.

I would recommend the following anyways:

  1. The pin handler is ment to be inherited. Write your own module. Do not change the existing one! Then use this module in the according policy.
  2. If you are missing any data, you can still retrieve it in your handler. If you are missing any data (which you probably do not) you can use the user_object to query
    • data from the user store
    • data from other tokens of the user…

Also, there is another possibility to create and send a random PIN.
Using event handlers to fetch the PIN from the init request and call and call an arbitrary script (handler).

And look at this: the user object with all the userinfo actually exits (see test_api_lib_policy…):

image

Please note again! This is the phone number of the user in the userstore. Not any phone number from the HTTP request (which would make no sense)

And I do not think, that an average administrator should ever read any comments in the code :wink:

Hope this helps.
Regards,
Cornelius

1 Like

Geez, I feel so stupid … I actually spent 30 minutes preparing an answer different than this …
I thought I tried everything I could. But one thing I did not notice or try was to expand the user object returned by the function call :see_no_evil:
Believe it or not, I was going through your answer, quoted your answers step by step and as soon as I hit your screenshot: BOOM, that must be it!
When I do a print(user.info) I’m now getting a complete dictionary of the users dataset, which is exactly what I was looking for.

[Mon Aug 10 20:47:11.473194 2020] [wsgi:error] [pid 1987:tid 140671362176768] [remote 192.168.1.100:60460] {'id': 1, 'userid': 1, 'username': 'test', 'email': 'test@test.com', 'mobile': '00123456789', 'phone': '00123456789', 'surname': 'Test', 'givenname': 'Test', 'description': 'Test'}

How would we say in German?: Oft sieht man den Wald vor lauter Bäumen nicht :wink:
Thank you very much for your help and keep up the good work! Looking forward seeing more of you guys :+1:

1 Like