Skip to content

Commit

Permalink
Merge pull request #158 from GeoTob/tobi/callback_import
Browse files Browse the repository at this point in the history
Making WS4REDIS_ALLOWED_CHANNELS accept strings
  • Loading branch information
jrief committed Jan 23, 2016
2 parents 5cf3834 + 822192e commit d13876f
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 12 deletions.
4 changes: 4 additions & 0 deletions docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ Ensure that your template context contains at least these processors:
...
)
**Websocket for Redis** allows each client to subscribe and to publish on every possible
channel. To restrict and control access, the ``WS4REDIS_ALLOWED_CHANNELS`` options should
be set to a callback function anywhere inside your project. See the example and warnings in
:ref:`SafetyConsiderations`.

Check your Installation
-----------------------
Expand Down
16 changes: 9 additions & 7 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ and access the Websocket code:
receive_message: receiveMessage,
heartbeat_msg: {{ WS4REDIS_HEARTBEAT }}
});
// attach this function to an event handler on your site
function sendMessage() {
ws4redis.send_message('A message');
}
// receive a message though the websocket from the server
function receiveMessage(msg) {
alert('Message from Websocket: ' + msg);
Expand Down Expand Up @@ -114,7 +114,7 @@ message to all clients listening on the named facility, referred here as ``fooba
from ws4redis.publisher import RedisPublisher
from ws4redis.redis_store import RedisMessage
redis_publisher = RedisPublisher(facility='foobar', broadcast=True)
message = RedisMessage('Hello World')
# and somewhere else
Expand Down Expand Up @@ -157,7 +157,7 @@ group where this user is member of.
.. code-block:: python
redis_publisher = RedisPublisher(facility='foobar', groups=['chatters'])
# and somewhere else
redis_publisher.publish_message('Hello World')
Expand Down Expand Up @@ -191,7 +191,7 @@ In this context the the magic item ``SELF`` refers to all clients owning the sam

Publish for Broadcast, User, Group and Session
----------------------------------------------
A Websocket initialized with the URL ``ws://www.example.com/ws/foobar?publish-broadcast``,
A Websocket initialized with the URL ``ws://www.example.com/ws/foobar?publish-broadcast``,
``ws://www.example.com/ws/foobar?publish-user`` or ``ws://www.example.com/ws/foobar?publish-session``
will publish a message sent through the Websocket on the named Redis channel ``broadcast:foobar``,
``user:john:foobar`` and ``session:wnqd0gbw5obpnj50zwh6yaq2yz4o8g9x:foobar`` respectively.
Expand All @@ -207,7 +207,7 @@ whenever it requires them.
# if the publisher is required only for fetching messages, use an
# empty constructor, otherwise reuse an existing redis_publisher
redis_publisher = RedisPublisher()
# and somewhere else
facility = 'foobar'
audience = 'any'
Expand Down Expand Up @@ -241,6 +241,8 @@ the client, immediately after he connects to the server.
.. note:: By using client code, which automatically reconnects after the Websocket closes, one can
create a setup which is immune against server and client reboots.

.. _SafetyConsiderations:

Safety considerations
---------------------
The default setting of **Websocket for Redis** is to allow each client to subscribe and to publish
Expand All @@ -264,7 +266,7 @@ Disallow non authenticated users to subscribe or to publish on the Websocket:
.. code-block:: python
from django.core.exceptions import PermissionDenied
def get_allowed_channels(request, channels):
if not request.user.is_authenticated():
raise PermissionDenied('Not allowed to subscribe nor to publish on the Websocket!')
Expand Down
22 changes: 17 additions & 5 deletions examples/chatserver/tests/test_chatclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@

from django.core.servers.basehttp import WSGIServer
from websocket import create_connection, WebSocketException
from ws4redis import settings as private_settings
from ws4redis.django_runserver import application
from ws4redis.publisher import RedisPublisher
from ws4redis.redis_store import RedisMessage, SELF

from .denied_channels import denied_channels

if six.PY3:
unichr = chr
Expand Down Expand Up @@ -230,12 +232,22 @@ def test_defining_multiple_publishers(self):
self.assertEqual(pub2._publishers, set([self.prefix + ':user:john:' + self.facility]))

def test_forbidden_channel(self):
private_settings.WS4REDIS_ALLOWED_CHANNELS = None
websocket_url = self.websocket_base_url + u'?subscribe-broadcast&publish-broadcast'
try:
create_connection(websocket_url, header=['Deny-Channels: YES'])
self.fail('Did not reject channels')
except WebSocketException:
self.assertTrue(True)
ws = create_connection(websocket_url, header=['Deny-Channels: YES'])
self.assertTrue(True) # Passes because all channels allowed.
ws.close()

callbacks = [denied_channels, 'chatserver.tests.denied_channels.denied_channels']
for callback in callbacks:
private_settings.WS4REDIS_ALLOWED_CHANNELS = callback
try:
ws = create_connection(websocket_url, header=['Deny-Channels: YES'])
self.fail('Did not reject channels')
except WebSocketException:
self.assertTrue(True)
finally:
ws.close()

def test_close_connection(self):

Expand Down
8 changes: 8 additions & 0 deletions ws4redis/wsgi_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ def __call__(self, environ, start_response):
channels, echo_message = self.process_subscriptions(request)
if callable(private_settings.WS4REDIS_ALLOWED_CHANNELS):
channels = list(private_settings.WS4REDIS_ALLOWED_CHANNELS(request, channels))
elif private_settings.WS4REDIS_ALLOWED_CHANNELS is not None:
try:
mod, callback = private_settings.WS4REDIS_ALLOWED_CHANNELS.rsplit('.', 1)
callback = getattr(import_module(mod), callback, None)
if callable(callback):
channels = list(callback(request, channels))
except AttributeError:
pass
websocket = self.upgrade_websocket(environ, start_response)
logger.debug('Subscribed to channels: {0}'.format(', '.join(channels)))
subscriber.set_pubsub_channels(request, channels)
Expand Down

0 comments on commit d13876f

Please sign in to comment.