Created endpoint to get the device pairing code

pull/47/head
Matheus Lima 2019-02-13 14:10:57 -03:00
parent 75fe9eed96
commit 64e513a7df
5 changed files with 82 additions and 2 deletions

View File

@ -0,0 +1,53 @@
import json
import hashlib
import random
import uuid
from selene.api import SeleneEndpoint
from selene.util.selene_cache import SeleneCache
class DeviceCodeEndpoint(SeleneEndpoint):
"""Endpoint to get a pairing code"""
# We need to do that to avoid ambiguous characters, like 0 and O, that is even harder to distinguish
# in the display of the mark 1
ALLOWED_CHARACTERS = "ACEFHJKLMNPRTUVWXY3479"
def __init__(self):
super(DeviceCodeEndpoint, self).__init__()
self.device_pairing_time = 86400
self.cache: SeleneCache = self.config.get('SELENE_CACHE')
self.sha512 = hashlib.sha512()
def get(self):
# The pairing process happens in two steps, the step where we get the pairing code and
# the step to activate the device. The state parameter is used to make sure that the device that is
# trying to be activate is the device which started the previous step
state = self.request.args['state']
return self._create(state)
def _create(self, state):
self.sha512.update(bytes(str(uuid.uuid4()), 'utf-8'))
token = self.sha512.hexdigest()
code = self._pairing_code()
pairing = json.dumps({'code': code,
'state': state,
'token': token,
'expiration': self.device_pairing_time})
# This is to deal with the case where we generate a pairing code that already exists in the
# cache, meaning another device is trying to pairing using the same code. In this case, we should
# call the method again to get another random pairing code
if self.cache.set_if_not_exists_with_expiration(self._code_key(code),
value=pairing,
expiration=self.device_pairing_time):
return code
else:
return self._create(state)
@staticmethod
def _code_key(code):
return 'pairing.code:{}'.format(code)
def _pairing_code(self):
return ''.join(random.choice(self.ALLOWED_CHARACTERS) for _ in range(6))

View File

@ -10,6 +10,7 @@ psycopg2-binary = "*"
passlib = "*"
pyhamcrest = "*"
schematics = "*"
redis = "*"
[dev-packages]

10
shared/Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "63164ec5172150b56a6a3930e1eb1bfa195837d6dbc019864a1b5475cdfdf590"
"sha256": "c0c1a9ec56002f00c9a50fc67aba1b0463d05323813944a244860fd69f49b75c"
},
"pipfile-spec": 6,
"requires": {
@ -111,6 +111,14 @@
"index": "pypi",
"version": "==1.7.1"
},
"redis": {
"hashes": [
"sha256:724932360d48e5407e8f82e405ab3650a36ed02c7e460d1e6fddf0f038422b54",
"sha256:9b19425a38fd074eb5795ff2b0d9a55b46a44f91f5347995f27e3ad257a7d775"
],
"index": "pypi",
"version": "==3.2.0"
},
"requests": {
"hashes": [
"sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e",

View File

@ -0,0 +1,16 @@
import os
from redis import Redis
class SeleneCache(object):
def __init__(self):
# should the variables host and port be in the config class?
redis_host = os.environ['REDIS_HOST']
redis_port = int(os.environ['REDIS_PORT'])
self.redis = Redis(host=redis_host, port=redis_port)
def set_if_not_exists_with_expiration(self, key, value, expiration):
"""Sets a key only if it doesn't exist and using a given expiration time"""
return self.redis.set(name=key, value=value, ex=expiration, nx=True)

View File

@ -16,6 +16,8 @@ setup(
'pyhamcrest',
'pyjwt',
'psycopg2-binary',
'schematics'
'schematics',
'validator-collection',
'redis'
]
)