diff --git a/qiita_db/handlers/artifact.py b/qiita_db/handlers/artifact.py
index 82732863f..f33e0bc5d 100644
--- a/qiita_db/handlers/artifact.py
+++ b/qiita_db/handlers/artifact.py
@@ -11,7 +11,7 @@
from json import loads
import qiita_db as qdb
-from .oauth2 import OauthBaseHandler, authenticate_oauth
+from .oauth2 import OauthBaseHandler, authenticate_oauth2
def _get_artifact(a_id):
@@ -46,7 +46,7 @@ def _get_artifact(a_id):
class ArtifactHandler(OauthBaseHandler):
- @authenticate_oauth
+ @authenticate_oauth2(default_public=False, inject_user=False)
def get(self, artifact_id):
"""Retrieves the artifact information
@@ -109,7 +109,7 @@ def get(self, artifact_id):
self.write(response)
- @authenticate_oauth
+ @authenticate_oauth2(default_public=False, inject_user=False)
def patch(self, artifact_id):
"""Patches the artifact information
@@ -140,7 +140,7 @@ def patch(self, artifact_id):
class ArtifactAPItestHandler(OauthBaseHandler):
- @authenticate_oauth
+ @authenticate_oauth2(default_public=False, inject_user=False)
def post(self):
"""Creates a new artifact
@@ -180,7 +180,7 @@ def post(self):
class ArtifactTypeHandler(OauthBaseHandler):
- @authenticate_oauth
+ @authenticate_oauth2(default_public=False, inject_user=False)
def post(self):
"""Creates a new artifact type
diff --git a/qiita_db/handlers/core.py b/qiita_db/handlers/core.py
index 9d3e380f1..b0cf38345 100644
--- a/qiita_db/handlers/core.py
+++ b/qiita_db/handlers/core.py
@@ -6,11 +6,11 @@
# The full license is in the file LICENSE, distributed with this software.
# -----------------------------------------------------------------------------
-from .oauth2 import OauthBaseHandler, authenticate_oauth
+from .oauth2 import OauthBaseHandler, authenticate_oauth2
import qiita_db as qdb
class ResetAPItestHandler(OauthBaseHandler):
- @authenticate_oauth
+ @authenticate_oauth2(default_public=False, inject_user=False)
def post(self):
qdb.environment_manager.drop_and_rebuild_tst_database()
diff --git a/qiita_db/handlers/oauth2.py b/qiita_db/handlers/oauth2.py
index d156b5696..595108a0e 100644
--- a/qiita_db/handlers/oauth2.py
+++ b/qiita_db/handlers/oauth2.py
@@ -47,66 +47,138 @@ def _oauth_error(handler, error_msg, error):
handler.finish()
-def authenticate_oauth(f):
+def _check_oauth2_header(handler):
+ """Check if the oauth2 header is valid
+
+ Parameters
+ ----------
+ handler : tornado.web.RequestHandler instance
+ The handler instance being requested
+
+ Returns
+ -------
+ errtype
+ The type of error, None if no error was observed
+ errdesc
+ A description of the error, None if no error was observed.
+ client_id
+ The observed client ID. This field is None if any error was observed.
+ """
+ header = handler.request.headers.get('Authorization', None)
+
+ if header is None:
+ return ('invalid_request', 'Oauth2 error: invalid access token', None)
+
+ token_info = header.split()
+ # Based on RFC6750 if reply is not 2 elements in the format of:
+ # ['Bearer', token] we assume a wrong reply
+ if len(token_info) != 2 or token_info[0] != 'Bearer':
+ return ('invalid_grant', 'Oauth2 error: invalid access token', None)
+
+ token = token_info[1]
+ db_token = r_client.hgetall(token)
+ if not db_token:
+ # token has timed out or never existed
+ return ('invalid_grant', 'Oauth2 error: token has timed out', None)
+
+ # Check daily rate limit for key if password style key
+ if db_token['grant_type'] == 'password':
+ limit_key = '%s_%s_daily_limit' % (db_token['client_id'],
+ db_token['user'])
+ limiter = r_client.get(limit_key)
+ if limiter is None:
+ # Set limit to 5,000 requests per day
+ r_client.setex(limit_key, 5000, 86400)
+ else:
+ r_client.decr(limit_key)
+ if int(r_client.get(limit_key)) <= 0:
+ return ('invalid_grant',
+ 'Oauth2 error: daily request limit reached', None)
+
+ return (None, None, db_token['client_id'])
+
+
+class authenticate_oauth2:
"""Decorate methods to require valid Oauth2 Authorization header[1]
If a valid header is given, the handoff is done and the page is rendered.
If an invalid header is given, a 400 error code is returned and the json
error message is automatically sent.
- Returns
- -------
- Sends oauth2 formatted error JSON if authorizaton fails
-
- Notes
- -----
- Expects handler to be a tornado RequestHandler or subclass
+ Attributes
+ ----------
+ default_public : bool
+ If True, execute the handler if a) the oauth2 token is acceptable or
+ b) if the Authorization header is not present. If False, the handler
+ will only be executed if the oauth2 token is acceptable.
+ inject_user : bool
+ If True, monkey patch the handler's get_current_user method to return
+ the instance of the User associated with the token's client ID. If
+ False, get_current_user is not monkey patched. If default_public is
+ also True, the default User returned is "demo@microbio.me"
References
----------
[1] The OAuth 2.0 Authorization Framework.
http://tools.ietf.org/html/rfc6749
"""
- @functools.wraps(f)
- def wrapper(handler, *args, **kwargs):
- header = handler.request.headers.get('Authorization', None)
- if header is None:
- _oauth_error(handler, 'Oauth2 error: invalid access token',
- 'invalid_request')
- return
- token_info = header.split()
- # Based on RFC6750 if reply is not 2 elements in the format of:
- # ['Bearer', token] we assume a wrong reply
- if len(token_info) != 2 or token_info[0] != 'Bearer':
- _oauth_error(handler, 'Oauth2 error: invalid access token',
- 'invalid_grant')
- return
+ def __init__(self, default_public=False, inject_user=False):
+ self.default_public = default_public
+ self.inject_user = inject_user
+
+ def get_user_maker(self, cid):
+ """Produce a function which acts like get_current_user"""
+ def f():
+ if cid is None:
+ return qdb.user.User("demo@microbio.me")
+ else:
+ return qdb.user.User.from_client_id(cid)
+ return f
- token = token_info[1]
- db_token = r_client.hgetall(token)
- if not db_token:
- # token has timed out or never existed
- _oauth_error(handler, 'Oauth2 error: token has timed out',
- 'invalid_grant')
- return
- # Check daily rate limit for key if password style key
- if db_token['grant_type'] == 'password':
- limit_key = '%s_%s_daily_limit' % (db_token['client_id'],
- db_token['user'])
- limiter = r_client.get(limit_key)
- if limiter is None:
- # Set limit to 5,000 requests per day
- r_client.setex(limit_key, 5000, 86400)
+ def __call__(self, f):
+ """Handle oauth, and execute the handler's method if appropriate
+
+ Parameters
+ ----------
+ f : function
+ The function decorated is expected to be a member method of a
+ subclass of `Tornado.web.RequestHandler`
+
+ Notes
+ -----
+ If an error with oauth2 occurs, a status code of 400 is set, a message
+ about the error is sent out over `write` and the response is ended
+ with `finish`. This happens without control being passed to the
+ handler, and in this situation, the handler is not executed.
+ """
+ @functools.wraps(f)
+ def wrapper(handler, *args, **kwargs):
+ errtype, errdesc, cid = _check_oauth2_header(handler)
+
+ if self.default_public:
+ # no error, or no authorization header. We should error if
+ # oauth is actually attempted but there was an auth issue
+ # (e.g., rate limit hit)
+ if errtype not in (None, 'invalid_request'):
+ _oauth_error(handler, errdesc, errtype)
+ return
+
+ if self.inject_user:
+ handler.get_current_user = self.get_user_maker(cid)
else:
- r_client.decr(limit_key)
- if int(r_client.get(limit_key)) <= 0:
- _oauth_error(
- handler, 'Oauth2 error: daily request limit reached',
- 'invalid_grant')
+ if errtype is not None:
+ _oauth_error(handler, errdesc, errtype)
return
+ if self.inject_user:
+ if cid is None:
+ raise ValueError("cid is None, without an oauth "
+ "error. This should never happen.")
+ else:
+ handler.get_current_user = self.get_user_maker(cid)
+
+ return f(handler, *args, **kwargs)
- return f(handler, *args, **kwargs)
- return wrapper
+ return wrapper
class OauthBaseHandler(RequestHandler):
diff --git a/qiita_db/handlers/plugin.py b/qiita_db/handlers/plugin.py
index 5850df51c..9f36cadce 100644
--- a/qiita_db/handlers/plugin.py
+++ b/qiita_db/handlers/plugin.py
@@ -12,7 +12,7 @@
from tornado.web import HTTPError
-from .oauth2 import OauthBaseHandler, authenticate_oauth
+from .oauth2 import OauthBaseHandler, authenticate_oauth2
from qiita_core.qiita_settings import qiita_config
import qiita_db as qdb
@@ -50,7 +50,7 @@ def _get_plugin(name, version):
class PluginHandler(OauthBaseHandler):
- @authenticate_oauth
+ @authenticate_oauth2(default_public=False, inject_user=False)
def get(self, name, version):
"""Retrieve the plugin information
@@ -91,7 +91,7 @@ def get(self, name, version):
class CommandListHandler(OauthBaseHandler):
- @authenticate_oauth
+ @authenticate_oauth2(default_public=False, inject_user=False)
def post(self, name, version):
with qdb.sql_connection.TRN:
plugin = _get_plugin(name, version)
@@ -154,7 +154,7 @@ def _get_command(plugin_name, plugin_version, cmd_name):
class CommandHandler(OauthBaseHandler):
- @authenticate_oauth
+ @authenticate_oauth2(default_public=False, inject_user=False)
def get(self, plugin_name, plugin_version, cmd_name):
"""Retrieve the command information
@@ -193,7 +193,7 @@ def get(self, plugin_name, plugin_version, cmd_name):
class CommandActivateHandler(OauthBaseHandler):
- @authenticate_oauth
+ @authenticate_oauth2(default_public=False, inject_user=False)
def post(self, plugin_name, plugin_version, cmd_name):
"""Activates the command
@@ -214,7 +214,7 @@ def post(self, plugin_name, plugin_version, cmd_name):
class ReloadPluginAPItestHandler(OauthBaseHandler):
- @authenticate_oauth
+ @authenticate_oauth2(default_public=False, inject_user=False)
def post(self):
"""Reloads the plugins"""
conf_files = glob(join(qiita_config.plugin_dir, "*.conf"))
diff --git a/qiita_db/handlers/prep_template.py b/qiita_db/handlers/prep_template.py
index 0192ca773..cfbcd334b 100644
--- a/qiita_db/handlers/prep_template.py
+++ b/qiita_db/handlers/prep_template.py
@@ -13,7 +13,7 @@
import pandas as pd
import qiita_db as qdb
-from .oauth2 import OauthBaseHandler, authenticate_oauth
+from .oauth2 import OauthBaseHandler, authenticate_oauth2
def _get_prep_template(pid):
@@ -48,7 +48,7 @@ def _get_prep_template(pid):
class PrepTemplateDBHandler(OauthBaseHandler):
- @authenticate_oauth
+ @authenticate_oauth2(default_public=False, inject_user=False)
def get(self, prep_id):
"""Retrieves the prep template information
@@ -89,7 +89,7 @@ def get(self, prep_id):
class PrepTemplateDataHandler(OauthBaseHandler):
- @authenticate_oauth
+ @authenticate_oauth2(default_public=False, inject_user=False)
def get(self, prep_id):
"""Retrieves the prep contents
@@ -111,7 +111,7 @@ def get(self, prep_id):
class PrepTemplateAPItestHandler(OauthBaseHandler):
- @authenticate_oauth
+ @authenticate_oauth2(default_public=False, inject_user=False)
def post(self):
prep_info_dict = loads(self.get_argument('prep_info'))
study = self.get_argument('study')
diff --git a/qiita_db/handlers/processing_job.py b/qiita_db/handlers/processing_job.py
index efd5a94bd..4d9962fbc 100644
--- a/qiita_db/handlers/processing_job.py
+++ b/qiita_db/handlers/processing_job.py
@@ -13,7 +13,7 @@
from qiita_core.qiita_settings import qiita_config
import qiita_db as qdb
-from .oauth2 import OauthBaseHandler, authenticate_oauth
+from .oauth2 import OauthBaseHandler, authenticate_oauth2
def _get_job(job_id):
@@ -71,7 +71,7 @@ def _job_completer(job_id, payload):
class JobHandler(OauthBaseHandler):
- @authenticate_oauth
+ @authenticate_oauth2(default_public=False, inject_user=False)
def get(self, job_id):
"""Get the job information
@@ -103,7 +103,7 @@ def get(self, job_id):
class HeartbeatHandler(OauthBaseHandler):
- @authenticate_oauth
+ @authenticate_oauth2(default_public=False, inject_user=False)
def post(self, job_id):
"""Update the heartbeat timestamp of the job
@@ -124,7 +124,7 @@ def post(self, job_id):
class ActiveStepHandler(OauthBaseHandler):
- @authenticate_oauth
+ @authenticate_oauth2(default_public=False, inject_user=False)
def post(self, job_id):
"""Changes the current exectuion step of the given job
@@ -146,7 +146,7 @@ def post(self, job_id):
class CompleteHandler(OauthBaseHandler):
- @authenticate_oauth
+ @authenticate_oauth2(default_public=False, inject_user=False)
def post(self, job_id):
"""Updates the job to one of the completed statuses: 'success', 'error'
@@ -171,7 +171,7 @@ def post(self, job_id):
class ProcessingJobAPItestHandler(OauthBaseHandler):
- @authenticate_oauth
+ @authenticate_oauth2(default_public=False, inject_user=False)
def post(self):
user = self.get_argument('user', 'test@foo.bar')
s_name, s_version, cmd_name = loads(self.get_argument('command'))
diff --git a/qiita_db/handlers/reference.py b/qiita_db/handlers/reference.py
index ccc0638a5..04d6fc877 100644
--- a/qiita_db/handlers/reference.py
+++ b/qiita_db/handlers/reference.py
@@ -8,7 +8,7 @@
from tornado.web import HTTPError
-from .oauth2 import OauthBaseHandler, authenticate_oauth
+from .oauth2 import OauthBaseHandler, authenticate_oauth2
import qiita_db as qdb
@@ -42,7 +42,7 @@ def _get_reference(r_id):
class ReferenceHandler(OauthBaseHandler):
- @authenticate_oauth
+ @authenticate_oauth2(default_public=False, inject_user=False)
def get(self, reference_id):
"""Retrieves the filepath information of the given reference
diff --git a/qiita_db/handlers/tests/oauthbase.py b/qiita_db/handlers/tests/oauthbase.py
index 2fad62ec2..f4450bdbe 100644
--- a/qiita_db/handlers/tests/oauthbase.py
+++ b/qiita_db/handlers/tests/oauthbase.py
@@ -9,5 +9,6 @@ def setUp(self):
self.header = {'Authorization': 'Bearer ' + self.token}
r_client.hset(self.token, 'timestamp', '12/12/12 12:12:00')
r_client.hset(self.token, 'grant_type', 'client')
+ r_client.hset(self.token, 'client_id', 'foo')
r_client.expire(self.token, 20)
super(OauthTestingBase, self).setUp()
diff --git a/qiita_db/handlers/tests/test_oauth2.py b/qiita_db/handlers/tests/test_oauth2.py
index 2c4bd8f3d..0ec6b2157 100644
--- a/qiita_db/handlers/tests/test_oauth2.py
+++ b/qiita_db/handlers/tests/test_oauth2.py
@@ -10,9 +10,63 @@
from moi import r_client
+from qiita_db.user import User
+from qiita_db.handlers.oauth2 import _check_oauth2_header, authenticate_oauth2
from qiita_pet.test.tornado_test_base import TestHandlerBase
+def make_mock_handler():
+ class mock_object:
+ def __init__(self):
+ self.status = None
+ self.body = None
+
+ def set_status(self, thing):
+ self.status = thing
+
+ def write(self, thing):
+ self.body = thing
+
+ def finish(self):
+ pass
+
+ handler = mock_object()
+ handler.request = mock_object()
+ handler.request.headers = {}
+
+ return handler
+
+
+def make_mock_decorated_handler(default_public, inject_user):
+ class mock_object:
+ def __init__(self):
+ self.status = None
+ self.body = None
+
+ def set_status(self, thing):
+ self.status = thing
+
+ def write(self, thing):
+ self.body = thing
+
+ def finish(self):
+ pass
+
+ @authenticate_oauth2(default_public=default_public,
+ inject_user=inject_user)
+ def get(self, item, item2=None):
+ self.body = {'x': item, 'y': item2}
+
+ def get_current_user(self):
+ return "Default get_current_user method"
+
+ handler = mock_object()
+ handler.request = mock_object()
+ handler.request.headers = {}
+
+ return handler
+
+
class OAuth2BaseHandlerTests(TestHandlerBase):
def setUp(self):
# Create client test authentication token
@@ -76,6 +130,163 @@ def test_authenticate_header_bad_header_type(self):
'error_description': 'Oauth2 error: invalid access token'}
self.assertEqual(loads(obs.body), exp)
+ def test_check_oauth2_header_bad_token(self):
+ obj = make_mock_handler()
+ obj.request.headers['Authorization'] = 'Bearer BADTOKEN'
+ exp = ('invalid_grant', 'Oauth2 error: token has timed out', None)
+ self.assertEqual(_check_oauth2_header(obj), exp)
+
+ def test_check_oauth2_header_bad_header(self):
+ obj = make_mock_handler()
+ obj.request.headers['Authorofthestuff'] = 'foo'
+ exp = ('invalid_request', 'Oauth2 error: invalid access token', None)
+ self.assertEqual(_check_oauth2_header(obj), exp)
+
+ def test_check_oauth2_header_bad_header_format(self):
+ obj = make_mock_handler()
+ obj.request.headers['Authorization'] = 'Bear ' + self.user_token
+ exp = ('invalid_grant', 'Oauth2 error: invalid access token', None)
+ self.assertEqual(_check_oauth2_header(obj), exp)
+
+ def test_check_oauth2_header_bad_header_format_toomany_tokens(self):
+ obj = make_mock_handler()
+ obj.request.headers['Authorization'] = 'Bear er ' + self.user_token
+ exp = ('invalid_grant', 'Oauth2 error: invalid access token', None)
+ self.assertEqual(_check_oauth2_header(obj), exp)
+
+ def test_check_oauth2_header_valid(self):
+ obj = make_mock_handler()
+ obj.request.headers['Authorization'] = 'Bearer ' + self.client_token
+ self.assertEqual(_check_oauth2_header(obj), (None, None,
+ 'test123123123'))
+
+ def test_check_oauth2_rate_limiting(self):
+ # Check rate limiting works
+ obj = make_mock_handler()
+ obj.request.headers['Authorization'] = 'Bearer ' + self.user_token
+ self.assertEqual(_check_oauth2_header(obj), (None, None,
+ 'testuser'))
+
+ self.assertEqual(int(r_client.get(self.user_rate_key)), 1)
+ r_client.setex('testuser_test@foo.bar_daily_limit', 0, 2)
+ exp = ('invalid_grant', 'Oauth2 error: daily request limit reached',
+ None)
+ self.assertEqual(_check_oauth2_header(obj), exp)
+
+
+class AuthorizeOauth2DecoratorTests(TestHandlerBase):
+ def setUp(self):
+ # Create client test authentication token
+ self.client_token = 'SOMEAUTHTESTINGTOKENHERE2122'
+ r_client.hset(self.client_token, 'timestamp', '12/12/12 12:12:00')
+ r_client.hset(self.client_token, 'client_id',
+ '19ndkO3oMKsoChjVVWluF7QkxHRfYhTKSFbAVt8IhK7gZgDaO4')
+ r_client.hset(self.client_token, 'grant_type', 'client')
+ r_client.expire(self.client_token, 5)
+ # Create username test authentication token
+ self.user_token = 'SOMEAUTHTESTINGTOKENHEREUSERNAME'
+ r_client.hset(self.user_token, 'timestamp', '12/12/12 12:12:00')
+ r_client.hset(self.user_token, 'client_id',
+ 'yKDgajoKn5xlOA8tpo48Rq8mWJkH9z4LBCx2SvqWYLIryaan2u')
+ r_client.hset(self.user_token, 'grant_type', 'password')
+ r_client.hset(self.user_token, 'user', 'test@foo.bar')
+ r_client.expire(self.user_token, 5)
+ # Create test access limit token
+ self.user_rate_key = 'testuser_test@foo.bar_daily_limit'
+ r_client.setex(self.user_rate_key, 2, 5)
+ super(AuthorizeOauth2DecoratorTests, self).setUp()
+
+ def test_not_public_no_inject_user(self):
+ obj = make_mock_decorated_handler(False, False)
+ obj.request.headers['Authorization'] = 'Bearer ' + self.user_token
+ obj.get('item1', 'item2')
+ self.assertEqual(obj.body, {'x': 'item1', 'y': 'item2'})
+ self.assertEqual(obj.get_current_user(),
+ "Default get_current_user method")
+
+ def test_not_public_no_inject_user_bad_token(self):
+ obj = make_mock_decorated_handler(False, False)
+ token = 'Bearer ' + self.user_token + 'asdasd'
+ obj.request.headers['Authorization'] = token
+ obj.get('item1', 'item2')
+ exp = {'error': 'invalid_grant',
+ 'error_description': 'Oauth2 error: token has timed out'}
+ self.assertEqual(obj.status, 400)
+ self.assertEqual(obj.body, exp)
+ self.assertEqual(obj.get_current_user(),
+ "Default get_current_user method")
+
+ def test_not_public_inject_user(self):
+ cid = 'yKDgajoKn5xlOA8tpo48Rq8mWJkH9z4LBCx2SvqWYLIryaan2u'
+ u = User('admin@foo.bar')
+ u.oauth_client_id = cid
+
+ obj = make_mock_decorated_handler(False, True)
+ obj.request.headers['Authorization'] = 'Bearer ' + self.user_token
+ obj.get('item1', 'item2')
+ self.assertEqual(obj.body, {'x': 'item1', 'y': 'item2'})
+ self.assertEqual(obj.get_current_user(), u)
+
+ def test_not_public_inject_user_bad_auth(self):
+ obj = make_mock_decorated_handler(False, True)
+ token = 'Bearer ' + self.user_token + 'asdasd'
+ obj.request.headers['Authorization'] = token
+ obj.get('item1', 'item2')
+ exp = {'error': 'invalid_grant',
+ 'error_description': 'Oauth2 error: token has timed out'}
+ self.assertEqual(obj.status, 400)
+ self.assertEqual(obj.body, exp)
+
+ # if we request a user injection, but we have a authentication error
+ # then we should _not_ actually inject the user
+ self.assertEqual(obj.get_current_user(),
+ "Default get_current_user method")
+
+ def test_public_no_inject_user_notoken(self):
+ obj = make_mock_decorated_handler(True, False)
+ # no header info
+ obj.get('item1', 'item2')
+ self.assertEqual(obj.body, {'x': 'item1', 'y': 'item2'})
+ self.assertEqual(obj.get_current_user(),
+ "Default get_current_user method")
+
+ def test_public_no_inject_user_badtoken(self):
+ obj = make_mock_decorated_handler(True, False)
+ token = 'Bearer ' + self.user_token + 'asdasd'
+ obj.request.headers['Authorization'] = token
+ obj.get('item1', 'item2')
+ exp = {'error': 'invalid_grant',
+ 'error_description': 'Oauth2 error: token has timed out'}
+ self.assertEqual(obj.status, 400)
+ self.assertEqual(obj.body, exp)
+ self.assertEqual(obj.get_current_user(),
+ "Default get_current_user method")
+
+ def test_public_no_inject_user_token(self):
+ obj = make_mock_decorated_handler(True, False)
+ obj.request.headers['Authorization'] = 'Bearer ' + self.user_token
+ obj.get('item1', 'item2')
+ self.assertEqual(obj.body, {'x': 'item1', 'y': 'item2'})
+ self.assertEqual(obj.get_current_user(),
+ "Default get_current_user method")
+
+ def test_public_inject_user_token(self):
+ cid = 'yKDgajoKn5xlOA8tpo48Rq8mWJkH9z4LBCx2SvqWYLIryaan2u'
+ u = User('admin@foo.bar')
+ u.oauth_client_id = cid
+
+ obj = make_mock_decorated_handler(True, True)
+ obj.request.headers['Authorization'] = 'Bearer ' + self.user_token
+ obj.get('item1', 'item2')
+ self.assertEqual(obj.body, {'x': 'item1', 'y': 'item2'})
+ self.assertEqual(obj.get_current_user(), u)
+
+ def test_public_inject_user_notoken(self):
+ obj = make_mock_decorated_handler(True, True)
+ obj.get('item1', 'item2')
+ self.assertEqual(obj.body, {'x': 'item1', 'y': 'item2'})
+ self.assertEqual(obj.get_current_user(), User('demo@microbio.me'))
+
class OAuth2HandlerTests(TestHandlerBase):
def test_authenticate_client_header(self):
diff --git a/qiita_db/support_files/patches/54.sql b/qiita_db/support_files/patches/54.sql
new file mode 100644
index 000000000..4fc0ff798
--- /dev/null
+++ b/qiita_db/support_files/patches/54.sql
@@ -0,0 +1,7 @@
+-- Apr 3, 2017
+-- Linking qiita users to the oauth table
+ALTER TABLE qiita.qiita_user ADD client_id varchar ;
+
+ALTER TABLE qiita.qiita_user ADD CONSTRAINT uc_qiita_user_client_id UNIQUE ( client_id ) ;
+
+ALTER TABLE qiita.qiita_user ADD CONSTRAINT fk_qiita_user_client_id FOREIGN KEY ( client_id ) REFERENCES qiita.oauth_identifiers( client_id ) ;
diff --git a/qiita_db/support_files/qiita-db.dbs b/qiita_db/support_files/qiita-db.dbs
index 1f7ebe2f5..fc00742ac 100644
--- a/qiita_db/support_files/qiita-db.dbs
+++ b/qiita_db/support_files/qiita-db.dbs
@@ -1389,15 +1389,22 @@
+
+
+
+
+
+
+
@@ -1892,7 +1899,6 @@ Controlled Vocabulary]]>
-
@@ -1971,6 +1977,7 @@ Controlled Vocabulary]]>
+
analysis tables
diff --git a/qiita_db/support_files/qiita-db.html b/qiita_db/support_files/qiita-db.html
index 5c7f9311b..c92a99785 100644
--- a/qiita_db/support_files/qiita-db.html
+++ b/qiita_db/support_files/qiita-db.html
@@ -267,7 +267,7 @@
-
+
Group_users
@@ -366,7 +366,7 @@
analysis_users references analysis ( analysis_id )
analysis_id
+ analysis_users references analysis ( analysis_id )' style='fill:#a1a0a0;'>analysis_id
Foreign Key fk_analysis_users_user
analysis_users references qiita_user ( email )
@@ -411,12 +411,7 @@
column_ontology references mixs_field_description ( column_name )
column_name
- Foreign Key fk_user_user_level
- qiita_user references user_level ( user_level_id )
-
-user_level_id
+ column_ontology references mixs_field_description ( column_name )' style='fill:#a1a0a0;'>column_name
Foreign Key fk_filepath
filepath references filepath_type ( filepath_type_id )
@@ -456,7 +451,7 @@
collection_analysis references analysis ( analysis_id )
analysis_id
+ collection_analysis references analysis ( analysis_id )' style='fill:#a1a0a0;'>analysis_id
Foreign Key fk_collection
collection references qiita_user ( email )
@@ -471,7 +466,7 @@
collection_users references collection ( collection_id )
collection_id
+ collection_users references collection ( collection_id )' style='fill:#a1a0a0;'>collection_id
Foreign Key fk_collection_user_email
collection_users references qiita_user ( email )
@@ -506,7 +501,7 @@
message_user references message ( message_id )
message_id
+ message_user references message ( message_id )' style='fill:#a1a0a0;'>message_id
Foreign Key fk_message_user_0
message_user references qiita_user ( email )
@@ -586,7 +581,7 @@
study_users references qiita_user ( email )
email
+ study_users references qiita_user ( email )' style='fill:#a1a0a0;'>email
Foreign Key fk_study_user
study references qiita_user ( email )
@@ -826,7 +821,7 @@
parent_processing_job references processing_job ( child_id -> processing_job_id )
child_id
+ parent_processing_job references processing_job ( child_id -> processing_job_id )' style='fill:#a1a0a0;'>child_id
Foreign Key fk_processing_job_workflow
processing_job_workflow references qiita_user ( email )
@@ -896,7 +891,7 @@
job references reference ( input_file_reference_id -> reference_id )
input_file_reference_id
+ job references reference ( input_file_reference_id -> reference_id )' style='fill:#a1a0a0;'>input_file_reference_id
Foreign Key fk_processing_job_qiita_user
processing_job references qiita_user ( email )
@@ -976,12 +971,22 @@
study_experimental_factor references study ( study_id )
study_id
+ study_experimental_factor references study ( study_id )' style='fill:#a1a0a0;'>study_id
Foreign Key fk_study_tags_qiita_user
study_tags references qiita_user ( email )
email
+ study_tags references qiita_user ( email )' style='fill:#a1a0a0;'>email
+ Foreign Key fk_user_user_level
+ qiita_user references user_level ( user_level_id )
+
+user_level_id
+ Foreign Key fk_qiita_user_client_id
+ qiita_user references oauth_identifiers ( client_id )
+
+client_id
controlled_vocab_valuesTable qiita.controlled_vocab_values
@@ -1173,39 +1178,6 @@
definitiondefinition text
load_dateload_date date not null
-
-
-
-qiita_userTable qiita.qiita_user
-Holds all user information
- Primary Key ( email )
-emailemail varchar not null
-Referred by analysis ( email )
-Referred by analysis_users ( email )
-Referred by collection ( email )
-Referred by collection_users ( email )
-Referred by message_user ( email )
-Referred by processing_job ( email )
-Referred by processing_job_workflow ( email )
-Referred by study ( email )
-Referred by study_users ( email )
-Referred by study_tags ( email )
- Index ( user_level_id )
-user_level_iduser_level_id integer not null default 5
-user level
-References user_level ( user_level_id )
- passwordpassword varchar not null
- namename varchar
- affiliationaffiliation varchar
- addressaddress varchar
- phonephone varchar
- user_verify_codeuser_verify_code varchar
-Code for initial user email verification
- pass_reset_codepass_reset_code varchar
-Randomly generated code for password reset
- pass_reset_timestamppass_reset_timestamp timestamp
-Time the reset code was generated
-
@@ -1561,6 +1533,7 @@
study_idstudy_id bigserial not null
Unique name for study
Referred by investigation_study ( study_id )
+Referred by per_study_tags ( study_id )
Referred by sample_template_filepath ( study_id )
Referred by study_artifact ( study_id )
Referred by study_environmental_package ( study_id )
@@ -1569,8 +1542,7 @@
Referred by study_prep_template ( study_id )
Referred by study_publication ( study_id )
Referred by study_sample ( study_id )
-Referred by study_users ( study_id )
-Referred by per_study_tags ( study_id )
+Referred by study_users ( study_id )
Index ( email )
emailemail varchar not null
Email of study owner
@@ -1712,7 +1684,8 @@
oauth_identifiersTable qiita.oauth_identifiers
Primary Key ( client_id )
client_idclient_id varchar(50) not null
-Referred by oauth_software ( client_id )
+Referred by oauth_software ( client_id )
+Referred by qiita_user ( client_id )
client_secretclient_secret varchar(255)
@@ -2348,6 +2321,42 @@
emailemail varchar not null
References qiita_user ( email )
+
+
+
+qiita_userTable qiita.qiita_user
+Holds all user information
+ Primary Key ( email )
+emailemail varchar not null
+Referred by analysis ( email )
+Referred by analysis_users ( email )
+Referred by collection ( email )
+Referred by collection_users ( email )
+Referred by message_user ( email )
+Referred by processing_job ( email )
+Referred by processing_job_workflow ( email )
+Referred by study ( email )
+Referred by study_tags ( email )
+Referred by study_users ( email )
+ Index ( user_level_id )
+user_level_iduser_level_id integer not null default 5
+user level
+References user_level ( user_level_id )
+ passwordpassword varchar not null
+ namename varchar
+ affiliationaffiliation varchar
+ addressaddress varchar
+ phonephone varchar
+ user_verify_codeuser_verify_code varchar
+Code for initial user email verification
+ pass_reset_codepass_reset_code varchar
+Randomly generated code for password reset
+ pass_reset_timestamppass_reset_timestamp timestamp
+Time the reset code was generated
+ Unique Index ( client_id )
+client_idclient_id varchar
+References oauth_identifiers ( client_id )
+
@@ -3029,91 +3038,6 @@
-
-
-
-Table qiita_user |
-Holds all user information |
-
-
-
- * |
- email |
- varchar |
- |
-
-
- * |
- user_level_id |
- integer DEFO 5 |
- user level |
-
-
- * |
- password |
- varchar |
- |
-
-
- |
- name |
- varchar |
- |
-
-
- |
- affiliation |
- varchar |
- |
-
-
- |
- address |
- varchar |
- |
-
-
- |
- phone |
- varchar |
- |
-
-
- |
- user_verify_code |
- varchar |
- Code for initial user email verification |
-
-
- |
- pass_reset_code |
- varchar |
- Randomly generated code for password reset |
-
-
- |
- pass_reset_timestamp |
- timestamp |
- Time the reset code was generated |
-
-Indexes |
- Pk | pk_user |
- ON email |
- |
-
- | idx_user |
- ON user_level_id |
- |
-
-Foreign Keys |
-
- | fk_user_user_level |
- ( user_level_id ) ref user_level (user_level_id) |
- |
-
-
-
-
+
+
+
+Table qiita_user |
+Holds all user information |
+
+
+
+ * |
+ email |
+ varchar |
+ |
+
+
+ * |
+ user_level_id |
+ integer DEFO 5 |
+ user level |
+
+
+ * |
+ password |
+ varchar |
+ |
+
+
+ |
+ name |
+ varchar |
+ |
+
+
+ |
+ affiliation |
+ varchar |
+ |
+
+
+ |
+ address |
+ varchar |
+ |
+
+
+ |
+ phone |
+ varchar |
+ |
+
+
+ |
+ user_verify_code |
+ varchar |
+ Code for initial user email verification |
+
+
+ |
+ pass_reset_code |
+ varchar |
+ Randomly generated code for password reset |
+
+
+ |
+ pass_reset_timestamp |
+ timestamp |
+ Time the reset code was generated |
+
+
+ |
+ client_id |
+ varchar |
+ |
+
+Indexes |
+ Pk | pk_user |
+ ON email |
+ |
+
+ | idx_user |
+ ON user_level_id |
+ |
+
+ U | uc_qiita_user_client_id |
+ ON client_id |
+ |
+
+Foreign Keys |
+
+ | fk_user_user_level |
+ ( user_level_id ) ref user_level (user_level_id) |
+ |
+
+
+ | fk_qiita_user_client_id |
+ ( client_id ) ref oauth_identifiers (client_id) |
+ |
+
+
+
+