Repository: privacyidea/privacyidea

Issue:
Error retrieving users from a SQL (create_internal) resolver
# What did you try to do?

I tried to enroll a token for a user added to a SQL resolver named internalusers created with `pi-manage resolver create_internal internalusers`

# What outcome did you expect?

To see the details of the user without any error being displayed and being able to enroll a token for the user.

# What outcome did you experience?

The user details are correctly displayed but the following error message gets displayed and it is impossible to enroll a token:

`(ProgrammingError) operator does not exist: character varying = integer LINE 3: ...oken.resolver = 'internalusers' AND token.user_id = 1 ORDER ... ^ HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts. 'SELECT DISTINCT token.id AS token_id, token.description AS token_description, token.serial AS token_serial, token.tokentype AS token_tokentype, token.user_pin AS token_user_pin, token.user_pin_iv AS token_user_pin_iv, token.so_pin AS token_so_pin, token.so_pin_iv AS token_so_pin_iv, token.resolver AS token_resolver, token.resolver_type AS token_resolver_type, token.user_id AS token_user_id, token.pin_seed AS token_pin_seed, token.otplen AS token_otplen, token.pin_hash AS token_pin_hash, token.key_enc AS token_key_enc, token.key_iv AS token_key_iv, token.maxfail AS token_maxfail, token.active AS token_active, token.revoked AS token_revoked, token.locked AS token_locked, token.failcount AS token_failcount, token.count AS token_count, token.count_window AS token_count_window, token.sync_window AS token_sync_window, token.rollout_state AS token_rollout_state \nFROM token, realm, tokenrealm \nWHERE lower(realm.name) = %(lower_1)s AND tokenrealm.realm_id = realm.id AND tokenrealm.token_id = token.id AND token.resolver = %(resolver_1)s AND token.user_id = %(user_id_1)s ORDER BY token.serial ASC \n LIMIT %(param_1)s OFFSET %(param_2)s' {'lower_1': u'internalusers', 'param_1': 5, 'user_id_1': 1, 'resolver_1': u'internalusers', 'param_2': 0}`

# Configuration

* privacyIDEA Version: 2.22-1trusty

* Installation method:
`apt-get install python-privacyidea` retrieved from http://ppa.launchpad.net/privacyidea/privacyidea/ubuntu/ trusty/main


## more details: 

* OS: Debian GNU/Linux 8.11 (jessie)

* Webserver: 2.4.10-10+deb8u12

* Tokendatabase: PostgreSQL 9.4.1


# Log file

Relevant extract:
```
[2018-10-09 15:24:26,989][11384][140340754294528][INFO][privacyidea.lib.user:230] user u'testuserpaul' found in resolver u'internalusers'
[2018-10-09 15:24:26,989][11384][140340754294528][INFO][privacyidea.lib.user:231] userid resolved to 1 
[2018-10-09 15:24:27,020][11384][140340754294528][INFO][privacyidea.lib.user:230] user u'testuserpaul' found in resolver u'internalusers'
[2018-10-09 15:24:27,021][11384][140340754294528][INFO][privacyidea.lib.user:231] userid resolved to 1 
[2018-10-09 15:24:27,043][11384][140340754294528][INFO][privacyidea.lib.user:230] user u'testuserpaul' found in resolver u'internalusers'
[2018-10-09 15:24:27,043][11384][140340754294528][INFO][privacyidea.lib.user:231] userid resolved to 1 
[2018-10-09 15:24:27,051][11384][140340754294528][ERROR][privacyidea.app:1423] Exception on /token/ [GET]
Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/lib/python2.7/dist-packages/flask/app.py", line 1477, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/lib/python2.7/dist-packages/flask/app.py", line 1381, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/lib/python2.7/dist-packages/flask/app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/lib/python2.7/dist-packages/flask/app.py", line 1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/usr/lib/python2.7/dist-packages/privacyidea/lib/event.py", line 60, in event_wrapper
    f_result = func(*args, **kwds)
  File "/usr/lib/python2.7/dist-packages/privacyidea/lib/log.py", line 154, in log_wrapper
    return func(*args, **kwds)
  File "/usr/lib/python2.7/dist-packages/privacyidea/api/token.py", line 389, in list_api
    userid=userid)
  File "/usr/lib/python2.7/dist-packages/privacyidea/lib/log.py", line 154, in log_wrapper
    return func(*args, **kwds)
  File "/usr/lib/python2.7/dist-packages/privacyidea/lib/token.py", line 406, in get_tokens_paginate
    error_out=False)
  File "/usr/lib/python2.7/dist-packages/flask_sqlalchemy/__init__.py", line 431, in paginate
    items = self.limit(per_page).offset((page - 1) * per_page).all()
  File "/usr/lib/python2.7/dist-packages/sqlalchemy/orm/query.py", line 2320, in all
    return list(self)
  File "/usr/lib/python2.7/dist-packages/sqlalchemy/orm/query.py", line 2438, in __iter__
    return self._execute_and_instances(context)
  File "/usr/lib/python2.7/dist-packages/sqlalchemy/orm/query.py", line 2453, in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
  File "/usr/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 729, in execute
    return meth(self, multiparams, params)
  File "/usr/lib/python2.7/dist-packages/sqlalchemy/sql/elements.py", line 322, in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
  File "/usr/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 826, in _execute_clauseelement
    compiled_sql, distilled_params
  File "/usr/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 958, in _execute_context
    context)
  File "/usr/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1159, in _handle_dbapi_exception
    exc_info
  File "/usr/lib/python2.7/dist-packages/sqlalchemy/util/compat.py", line 199, in raise_from_cause
    reraise(type(exception), exception, tb=exc_tb)
  File "/usr/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 951, in _execute_context
    context)
  File "/usr/lib/python2.7/dist-packages/sqlalchemy/engine/default.py", line 436, in do_execute
    cursor.execute(statement, parameters)
ProgrammingError: (ProgrammingError) operator does not exist: character varying = integer
LINE 3: ...oken.resolver = 'internalusers' AND token.user_id = 1 ORDER ...
                                                             ^
HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
 'SELECT DISTINCT token.id AS token_id, token.description AS token_description, token.serial AS token_serial, token.tokentype AS token_tokentype, token.user_pin AS token_user_pin, token.user_pin_iv AS token_user_pin_iv, token.so_pin AS token_so_pin, token.so_pin_iv AS token_so_pin_iv, token.resolver AS token_resolver, token.resolver_type AS token_resolver_type, token.user_id AS token_user_id, token.pin_seed AS token_pin_seed, token.otplen AS token_otplen, token.pin_hash AS token_pin_hash, token.key_enc AS token_key_enc, token.key_iv AS token_key_iv, token.maxfail AS token_maxfail, token.active AS token_active, token.revoked AS token_revoked, token.locked AS token_locked, token.failcount AS token_failcount, token.count AS token_count, token.count_window AS token_count_window, token.sync_window AS token_sync_window, token.rollout_state AS token_rollout_state \nFROM token, realm, tokenrealm \nWHERE lower(realm.name) = %(lower_1)s AND tokenrealm.realm_id = realm.id AND tokenrealm.token_id = token.id AND token.resolver = %(resolver_1)s AND token.user_id = %(user_id_1)s ORDER BY token.serial ASC \n LIMIT %(param_1)s OFFSET %(param_2)s' {'lower_1': u'internalusers', 'param_1': 5, 'user_id_1': 1, 'resolver_1': u'internalusers', 'param_2': 0}
```



Discussion:
Hi,
thanks for the bug-report.
I could reproduce the bug, it seems that postgres does not automatically converts INT to VARCHAR (or varying). I guess we need to cast to string in these cases...
Hi,
Great that you could reproduce it!
Although I did not report it, I experienced the same issue trying to use a sqlite db generated with privacyidea-create-userdb
Best regards,
Paul
Basically Postgres does not implicitly casts data, in this case from int to string.
The [UserIdResolver](https://github.com/privacyidea/privacyidea/blob/7a18294290e25aca98acf59c6e5064fa61c9d85f/privacyidea/lib/resolvers/UserIdResolver.py#L132) on the other hand, allows to return a string **or** int.
We will fix this by casting to string before assembling the db-query. But if You prefer You can enable [implicit casting](https://dba.stackexchange.com/questions/82511/how-to-enable-implicit-casts-in-postgresql-9-2#172370) in Postgres as well :-)

Regards
Paul ;-)

Resolving patch:
diff --git a/privacyidea/lib/token.py b/privacyidea/lib/token.py
--- a/privacyidea/lib/token.py
+++ b/privacyidea/lib/token.py
@@ -221,6 +221,8 @@ def _create_token_query(tokentype=None, realm=None, assigned=None, user=None,
             sql_query = sql_query.filter(Token.resolver == user.resolver)
         (uid, _rtype, _resolver) = user.get_user_identifiers()
         if uid:
+            if type(uid) == int:
+                uid = str(uid)
             sql_query = sql_query.filter(Token.user_id == uid)
 
     if active is not None:


Test patch: