Skip to content
Snippets Groups Projects
Commit 7b9d1f2c authored by Nathaniel Kofalt's avatar Nathaniel Kofalt
Browse files

Change API key format and allow for colon-separated key preambles

parent f458081c
No related branches found
No related tags found
No related merge requests found
......@@ -75,7 +75,6 @@ class AuthProvider(object):
}
dbutil.fault_tolerant_replace_one('refreshtokens', query, refresh_doc, upsert=True)
class JWTAuthProvider(AuthProvider):
def __init__(self):
......@@ -103,8 +102,6 @@ class JWTAuthProvider(AuthProvider):
return uid
class GoogleOAuthProvider(AuthProvider):
def __init__(self):
......@@ -185,7 +182,6 @@ class GoogleOAuthProvider(AuthProvider):
# If the user has no avatar set, mark their provider_avatar as their chosen avatar.
config.db.users.update_one({'_id': uid, 'avatar': {'$exists': False}}, {'$set':{'avatar': provider_avatar, 'modified': timestamp}})
class WechatOAuthProvider(AuthProvider):
def __init__(self):
......@@ -273,7 +269,6 @@ class WechatOAuthProvider(AuthProvider):
def set_user_avatar(self, uid, identity):
pass
class APIKeyAuthProvider(AuthProvider):
"""
Uses an API key for authentication.
......@@ -293,7 +288,19 @@ class APIKeyAuthProvider(AuthProvider):
"""
super(APIKeyAuthProvider,self).__init__('api-key', set_config=False)
@staticmethod
def _preprocess_key(key):
"""
Convention for API keys is that they can have arbitrary information, separated by a :,
before the actual key. Generally, this will have a connection string in it.
Strip this preamble, if any, before processing the key.
"""
return key.split(":")[-1] # Get the last segment of the string after any : separators
def validate_code(self, code, **kwargs):
code = APIKeyAuthProvider._preprocess_key(code)
uid = self.validate_user_api_key(code)
return {
'access_token': code,
......@@ -309,6 +316,8 @@ class APIKeyAuthProvider(AuthProvider):
401s via APIAuthProviderException on failure.
"""
key = APIKeyAuthProvider._preprocess_key(key)
timestamp = datetime.datetime.utcnow()
user = config.db.users.find_one_and_update({'api_key.key': key}, {'$set': {'api_key.last_used': timestamp}}, ['_id'])
if user:
......
......@@ -164,7 +164,7 @@ class UserHandler(base.RequestHandler):
def generate_api_key(self):
if not self.uid:
self.abort(400, 'no user is logged in')
generated_key = base64.urlsafe_b64encode(os.urandom(42))
generated_key = util.create_nonce()
now = datetime.datetime.utcnow()
payload = {'api_key': {'key': generated_key, 'created': now, 'last_used': None}}
result = self.storage.exec_op('PUT', _id=self.uid, payload=payload)
......
import datetime
import enum as baseEnum
import errno
import hashlib
import json
import mimetypes
import os
import uuid
import random
import requests
import hashlib
import string
import uuid
MIMETYPES = [
('.bvec', 'text', 'bvec'),
......@@ -204,3 +206,15 @@ def mkdir_p(path):
pass
else:
raise
NONCE_CHARS = string.ascii_letters + string.digits
NONCE_LENGTH = 18
def create_nonce():
x = len(NONCE_CHARS)
# Class that uses the os.urandom() function for generating random numbers.
# https://docs.python.org/2/library/random.html#random.SystemRandom
randrange = random.SystemRandom().randrange
return ''.join([NONCE_CHARS[randrange(x)] for _ in range(NONCE_LENGTH)])
import pytest
from api.auth.authproviders import APIKeyAuthProvider
def test_api_key_preprocess():
assert APIKeyAuthProvider._preprocess_key("key") == "key"
assert APIKeyAuthProvider._preprocess_key("preamble:key") == "key"
assert APIKeyAuthProvider._preprocess_key("preamble:37:key") == "key"
assert APIKeyAuthProvider._preprocess_key("preamble::key") == "key"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment