Repository: getredash/redash

Issue:
record_event task fails for view events generated by an user who accesses a dashboard using a public sharing link
### Issue Summary

If I share a dashboard and access it through the generated public URL, the worker prints an error to the log when it tries to write the view event into the events log table from the record_event task.

### Steps to Reproduce

I was able to reproduce this issue by using the docker-compose.yml file provided in the repository (i.e. in a dev setup):

1. Setup a fresh instance of Redash
2. Create a new dashboard
3. Click the share button and enable public sharing
4. Copy the public sharing link (e.g. `http://localhost:5000/public/dashboards/CH6m72tekgbplwzelZuxWzdtWu3LlbsewVYVxm8J?org_slug=default`) and access it with a clean browser profile (or private window that has no redash login cookies around)

### What happens?
The following error can be seen in the worker logs:

```
[2017-09-07 08:03:40,006: ERROR/MainProcess] Task redash.tasks.record_event[41674041-c67b-4659-a151-99f9e73e3301] raised unexpected: DataError('(psycopg2.DataError) invalid input syntax for integer: "CH6m72tekgbplwzelZuxWzdtWu3LlbsewVYVxm8J"\nLINE 1: ...id, additional_properties, created_at) VALUES (1, \'CH6m72tek...\n                                                             ^\n',)
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/celery/app/trace.py", line 240, in trace_task
    R = retval = fun(*args, **kwargs)
  File "/app/redash/worker.py", line 69, in __call__
    return TaskBase.__call__(self, *args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/celery/app/trace.py", line 438, in __protected_call__
    return self.run(*args, **kwargs)
  File "/app/redash/tasks/general.py", line 15, in record_event
    models.db.session.commit()
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/scoping.py", line 157, in do
    return getattr(self.registry(), name)(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 874, in commit
    self.transaction.commit()
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 461, in commit
    self._prepare_impl()
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 441, in _prepare_impl
    self.session.flush()
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 2137, in flush
    self._flush(objects)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 2257, in _flush
    transaction.rollback(_capture_exception=True)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/util/langhelpers.py", line 60, in __exit__
    compat.reraise(exc_type, exc_value, exc_tb)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 2221, in _flush
    flush_context.execute()
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/unitofwork.py", line 389, in execute
    rec.execute(self)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/unitofwork.py", line 548, in execute
    uow
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/persistence.py", line 181, in save_obj
    mapper, table, insert)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/persistence.py", line 835, in _emit_insert_statements
    execute(statement, params)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 945, in execute
    return meth(self, multiparams, params)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/sql/elements.py", line 263, in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1053, in _execute_clauseelement
    compiled_sql, distilled_params
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1189, in _execute_context
    context)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1393, in _handle_dbapi_exception
    exc_info
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/util/compat.py", line 202, in raise_from_cause
    reraise(type(exception), exception, tb=exc_tb, cause=cause)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1182, in _execute_context
    context)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/default.py", line 469, in do_execute
    cursor.execute(statement, parameters)
DataError: (psycopg2.DataError) invalid input syntax for integer: "CH6m72tekgbplwzelZuxWzdtWu3LlbsewVYVxm8J"
LINE 1: ...id, additional_properties, created_at) VALUES (1, 'CH6m72tek...
                                                             ^
 [SQL: 'INSERT INTO events (org_id, user_id, action, object_type, object_id, additional_properties, created_at) VALUES (%(org_id)s, %(user_id)s, %(action)s, %(object_type)s, %(object_id)s, %(additional_properties)s, %(created_at)s) RETURNING events.id'] [parameters: {'user_id': u'CH6m72tekgbplwzelZuxWzdtWu3LlbsewVYVxm8J', 'created_at': datetime.datetime(2017, 9, 7, 8, 3, 39), 'object_type': 'dashboard', 'org_id': 1, 'object_id': 1, 'additional_properties': '{"ip": "172.20.0.1", "public": true, "referer": null, "user_agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:55.0) Gecko/20100101 Firefox/55.0", "headless": false}', 'action': 'view'}]
```

Coincidentally, the value that fails to be set to the user_id field (`CH6m72tekgbplwzelZuxWzdtWu3LlbsewVYVxm8J`) is the token seen in the public sharing URL (`http://localhost:5000/public/dashboards/CH6m72tekgbplwzelZuxWzdtWu3LlbsewVYVxm8J?org_slug=default`)

### What should happen?
The view event is properly saved to the events table and the task does not crash.

### Technical details:
* Redash Version: Both stable release 2.0.0 and latest master with commit ID 2a22b98c77757f378d3a4ed1ec8eceb99f0a8b35
* How did you install Redash: Stable v2.0.0 by upgrading a previous version with the bin/upgrade script and the latest commit in master using the provided docker-compose configuration and following the developer environment setup.

I believe this issue was discussed in #1238 but that lacked a proper STR to actually fix the root cause of this issue. It did had some good ideas about what could cause the bug and it indeed seems that an anonymous user causes an incorrect user_id to be included in the view event.


Discussion:

Resolving patch:
diff --git a/redash/handlers/base.py b/redash/handlers/base.py
--- a/redash/handlers/base.py
+++ b/redash/handlers/base.py
@@ -44,7 +44,7 @@ def update_model(self, model, updates):
 
 
 def record_event(org, user, options):
-    if isinstance(user, ApiUser):
+    if user.is_api_user():
         options.update({
             'api_key': user.name,
             'org_id': org.id
@@ -52,6 +52,7 @@ def record_event(org, user, options):
     else:
         options.update({
             'user_id': user.id,
+            'user_name': user.name,
             'org_id': org.id
         })
 
@@ -88,7 +89,7 @@ def paginate(query_set, page, page_size, serializer):
     if page < 1:
         abort(400, message='Page must be positive integer.')
 
-    if (page-1)*page_size+1 > count > 0:
+    if (page - 1) * page_size + 1 > count > 0:
         abort(400, message='Page is out of range.')
 
     if page_size > 250 or page_size < 1:


Test patch: