Skip to content

Commit

Permalink
Merge branch 'token_redis_improvements' of https://github.com/javicac…
Browse files Browse the repository at this point in the history
  • Loading branch information
CendioOssman committed Jan 20, 2023
2 parents 7f53e9c + 3d2e93a commit a134655
Show file tree
Hide file tree
Showing 4 changed files with 254 additions and 23 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
install_requires=[
'numpy', 'requests',
'jwcrypto',
'redis', 'simplejson',
'redis',
],
zip_safe=False,
entry_points={
Expand Down
1 change: 1 addition & 0 deletions test-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mock
nose2
six
redis
wrapt<=1.12.1;python_version<="3.4"
150 changes: 150 additions & 0 deletions tests/test_token_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,153 @@ def test_simple(self, mock_redis):
self.assertIsNotNone(result)
self.assertEqual(result[0], 'remote_host')
self.assertEqual(result[1], 'remote_port')

@patch('redis.Redis')
def test_json_token_with_spaces(self, mock_redis):
plugin = TokenRedis('127.0.0.1:1234')

instance = mock_redis.return_value
instance.get.return_value = b' {"host": "remote_host:remote_port"} '

result = plugin.lookup('testhost')

instance.get.assert_called_once_with('testhost')
self.assertIsNotNone(result)
self.assertEqual(result[0], 'remote_host')
self.assertEqual(result[1], 'remote_port')

@patch('redis.Redis')
def test_text_token(self, mock_redis):
plugin = TokenRedis('127.0.0.1:1234')

instance = mock_redis.return_value
instance.get.return_value = b'remote_host:remote_port'

result = plugin.lookup('testhost')

instance.get.assert_called_once_with('testhost')
self.assertIsNotNone(result)
self.assertEqual(result[0], 'remote_host')
self.assertEqual(result[1], 'remote_port')

@patch('redis.Redis')
def test_text_token_with_spaces(self, mock_redis):
plugin = TokenRedis('127.0.0.1:1234')

instance = mock_redis.return_value
instance.get.return_value = b' remote_host:remote_port '

result = plugin.lookup('testhost')

instance.get.assert_called_once_with('testhost')
self.assertIsNotNone(result)
self.assertEqual(result[0], 'remote_host')
self.assertEqual(result[1], 'remote_port')

@patch('redis.Redis')
def test_invalid_token(self, mock_redis):
plugin = TokenRedis('127.0.0.1:1234')

instance = mock_redis.return_value
instance.get.return_value = b'{"host": "remote_host:remote_port" '

result = plugin.lookup('testhost')

instance.get.assert_called_once_with('testhost')
self.assertIsNone(result)

def test_src_only_host(self):
plugin = TokenRedis('127.0.0.1')

self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 6379)
self.assertEqual(plugin._db, 0)
self.assertEqual(plugin._password, None)

def test_src_with_host_port(self):
plugin = TokenRedis('127.0.0.1:1234')

self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 1234)
self.assertEqual(plugin._db, 0)
self.assertEqual(plugin._password, None)

def test_src_with_host_port_db(self):
plugin = TokenRedis('127.0.0.1:1234:2')

self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 1234)
self.assertEqual(plugin._db, 2)
self.assertEqual(plugin._password, None)

def test_src_with_host_port_db_pass(self):
plugin = TokenRedis('127.0.0.1:1234:2:verysecret')

self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 1234)
self.assertEqual(plugin._db, 2)
self.assertEqual(plugin._password, 'verysecret')

def test_src_with_host_empty_port_empty_db_pass(self):
plugin = TokenRedis('127.0.0.1:::verysecret')

self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 6379)
self.assertEqual(plugin._db, 0)
self.assertEqual(plugin._password, 'verysecret')

def test_src_with_host_empty_port_empty_db_empty_pass(self):
plugin = TokenRedis('127.0.0.1:::')

self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 6379)
self.assertEqual(plugin._db, 0)
self.assertEqual(plugin._password, None)

def test_src_with_host_empty_port_empty_db_no_pass(self):
plugin = TokenRedis('127.0.0.1::')

self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 6379)
self.assertEqual(plugin._db, 0)
self.assertEqual(plugin._password, None)

def test_src_with_host_empty_port_no_db_no_pass(self):
plugin = TokenRedis('127.0.0.1:')

self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 6379)
self.assertEqual(plugin._db, 0)
self.assertEqual(plugin._password, None)

def test_src_with_host_empty_port_db_no_pass(self):
plugin = TokenRedis('127.0.0.1::2')

self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 6379)
self.assertEqual(plugin._db, 2)
self.assertEqual(plugin._password, None)

def test_src_with_host_port_empty_db_pass(self):
plugin = TokenRedis('127.0.0.1:1234::verysecret')

self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 1234)
self.assertEqual(plugin._db, 0)
self.assertEqual(plugin._password, 'verysecret')

def test_src_with_host_empty_port_db_pass(self):
plugin = TokenRedis('127.0.0.1::2:verysecret')

self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 6379)
self.assertEqual(plugin._db, 2)
self.assertEqual(plugin._password, 'verysecret')

def test_src_with_host_empty_port_db_empty_pass(self):
plugin = TokenRedis('127.0.0.1::2:')

self.assertEqual(plugin._server, '127.0.0.1')
self.assertEqual(plugin._port, 6379)
self.assertEqual(plugin._db, 2)
self.assertEqual(plugin._password, None)
124 changes: 102 additions & 22 deletions websockify/token_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import sys
import time
import re
import json

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -154,57 +155,136 @@ def lookup(self, token):
logger.error("package jwcrypto not found, are you sure you've installed it correctly?")
return None

class TokenRedis():
"""
The TokenRedis plugin expects the format of the data in a form of json.

class TokenRedis(BasePlugin):
"""Token plugin based on the Redis in-memory data store.
The token source is in the format:
host[:port[:db[:password]]]
where port, db and password are optional. If port or db are left empty
they will take its default value, ie. 6379 and 0 respectively.
If your redis server is using the default port (6379) then you can use:
my-redis-host
In case you need to authenticate with the redis server and you are using
the default database and port you can use:
my-redis-host:::verysecretpass
In the more general case you will use:
my-redis-host:6380:1:verysecretpass
The TokenRedis plugin expects the format of the target in one of these two
formats:
- JSON
{"host": "target-host:target-port"}
- Plain text
target-host:target-port
Prepare data with:
redis-cli set hello '{"host":"127.0.0.1:5000"}'
redis-cli set my-token '{"host": "127.0.0.1:5000"}'
Verify with:
redis-cli --raw get hello
redis-cli --raw get my-token
Spawn a test "server" using netcat
nc -l 5000 -v
Note: you have to install also the 'redis' and 'simplejson' modules
pip install redis simplejson
Note: This Token Plugin depends on the 'redis' module, so you have
to install it before using this plugin:
pip install redis
"""
def __init__(self, src):
try:
# import those ahead of time so we provide error earlier
import redis
import simplejson
self._server, self._port = src.split(":")
except ImportError:
logger.error("Unable to load redis module")
sys.exit()
# Default values
self._port = 6379
self._db = 0
self._password = None
try:
fields = src.split(":")
if len(fields) == 1:
self._server = fields[0]
elif len(fields) == 2:
self._server, self._port = fields
if not self._port:
self._port = 6379
elif len(fields) == 3:
self._server, self._port, self._db = fields
if not self._port:
self._port = 6379
if not self._db:
self._db = 0
elif len(fields) == 4:
self._server, self._port, self._db, self._password = fields
if not self._port:
self._port = 6379
if not self._db:
self._db = 0
if not self._password:
self._password = None
else:
raise ValueError
self._port = int(self._port)
self._db = int(self._db)
logger.info("TokenRedis backend initilized (%s:%s)" %
(self._server, self._port))
except ValueError:
logger.error("The provided --token-source='%s' is not in an expected format <host>:<port>" %
src)
sys.exit()
except ImportError:
logger.error("package redis or simplejson not found, are you sure you've installed them correctly?")
logger.error("The provided --token-source='%s' is not in the "
"expected format <host>[:<port>[:<db>[:<password>]]]" %
src)
sys.exit()

def lookup(self, token):
try:
import redis
import simplejson
except ImportError:
logger.error("package redis or simplejson not found, are you sure you've installed them correctly?")
logger.error("package redis not found, are you sure you've installed them correctly?")
sys.exit()

logger.info("resolving token '%s'" % token)
client = redis.Redis(host=self._server, port=self._port)
client = redis.Redis(host=self._server, port=self._port,
db=self._db, password=self._password)
stuff = client.get(token)
if stuff is None:
return None
else:
responseStr = stuff.decode("utf-8")
responseStr = stuff.decode("utf-8").strip()
logger.debug("response from redis : %s" % responseStr)
combo = simplejson.loads(responseStr)
(host, port) = combo["host"].split(':')
logger.debug("host: %s, port: %s" % (host,port))
if responseStr.startswith("{"):
try:
combo = json.loads(responseStr)
host, port = combo["host"].split(":")
except ValueError:
logger.error("Unable to decode JSON token: %s" %
responseStr)
return None
except KeyError:
logger.error("Unable to find 'host' key in JSON token: %s" %
responseStr)
return None
elif re.match(r'\S+:\S+', responseStr):
host, port = responseStr.split(":")
else:
logger.error("Unable to parse token: %s" % responseStr)
return None
logger.debug("host: %s, port: %s" % (host, port))
return [host, port]


Expand Down

0 comments on commit a134655

Please sign in to comment.