Sunday, February 24, 2019

Re: Django Channels and multi-tenant will this be a world of hurt?

@N Muchai, 

I don't know if you've found a workaround to this, but here's mine if people are interested in the future. 

I just made a custom ASGI application that adds the tenant schema name (provided your URLs are in the form *.domain.com, where * represents the schema name) and whether the project is multitenant or not. I haven't thought much about whether the boolean for multitenancy identifier helps, so feel free to remove it in your implementation. I just stacked this application in my project's `routing.py` file. Here's the code I have at the moment: 

My Middleware:
class MTSchemaMiddleware:
    def __init__(self, inner):
        self.inner = inner

    def __call__(self, scope):
        if "headers" not in scope:
            raise ValueError(
                "MTSchemaMiddleware was passed a scope that did not have a headers key "
                + "(make sure it is only passed HTTP or WebSocket connections)"
            )

        for key, value in scope.get('headers', []):
            if key == b'host':
                schema_name = value.decode('ascii').split('.')[0]
                break
        else:
            raise ValueError(
                "The headers key in the scope is invalid. "
                + "(make sure it is passed valid HTTP or WebSocket connections)"
            )
        return self.inner(
            dict(scope, schema_name=schema_name, multitenant=True)
        )

My `routing.py`:

import MTSchemaMiddleware # from wherever your Middleware class resides
application = ProtocolTypeRouter({
'websocket': <your stack>(
            MTSchemaMiddleware(
URLRouter(
chat.routing.websocket_urlpatterns
)
)
            )
})

After you do this, you will be able to access the `schema_name` and `multitenant` variables inside your consumer's `scope` like this: `schema_name = scope['schema_name']` or `multitenant = scope[`multitenant`]`. 

Hope this helps someone! 

Best,
Ahmed


On Thursday, January 10, 2019 at 7:22:40 AM UTC-5, N Muchai wrote:
@Filbert,

Am in the same exact spot using Django Channels with django-tenants. Did you find a way to identify the tenant?

Thanks.

On Tuesday, November 28, 2017 at 4:22:10 AM UTC+3, Filbert wrote:
Sorry, yeah dig before I post :-|  Can create a class-based consumer and use the message->headers->host. I'll get chest deep before ask another question.

On Monday, November 27, 2017 at 6:04:48 PM UTC-5, Andrew Godwin wrote:
That would be correct (though please be aware Origin can be faked like host headers, so don't use it as the sole point of security).

Is it not available via the headers list in the connect message?

Andrew

On Mon, Nov 27, 2017 at 2:58 PM, Filbert <tim...@gmail.com> wrote:
Thanks again. One last question, in order to "multi-tenant-ify" Channels I think the challenge will be trickle the websocket "orgin" into the consumer from Daphne somehow as it seems there is no way to know in a consumer which tenant (unique domain) the message originated from. Without the domain you can't establish the Postgres schema, without the schema you have no tenant :-)

Please confirm this would be the approach or whether I am wrong-headed here.
Thanks.

On Sunday, November 26, 2017 at 9:58:58 AM UTC-5, Andrew Godwin wrote:
I've never used attach-daemon so I can't help you there I'm afraid. As long as it does sensible process-management things it's probably alright?

Andrew

On Sat, Nov 25, 2017 at 5:17 PM, Filbert <tim...@gmail.com> wrote:
Andrew,
Thanks for the response. Seeing that I am keeping uWSGI for the non-websocket paths, any heartache with me starting daphne and the consumers using uWSGI's attach-daemon? I do this with celery and it is a convenient way to have everything for the App managed as a unit via a single /etc/init (upstart) conf file.
Thanks. 

On Friday, November 24, 2017 at 7:10:29 PM UTC-5, Andrew Godwin wrote:
Your assumptions all seem correct. Also consider that the security model of WebSockets is... interesting, so securing a multi-tenant setup is going to require a bit more work than you would merely doing the same for HTTP requests.

There are other non-Channels options around if you want to look into them, but I suspect they'll all have similar architectural challenges.

Andrew

On Fri, Nov 24, 2017 at 3:09 PM, Filbert <tim...@gmail.com> wrote:
Running multi-tenant site using a fork of Django tenant schemas with tens of web servers and thousands of tenants....

Piloting a project to implement Channels for real-time notifications, etc.

I want to confirm these assumptions:

1. Channels really has no support for multi-tenant, I will have to roll my own.
2. Since uWSGI is serving us well and (at this point) I wouldn't trust Daphne to serve HTTP, I've got split paths in NGinx for uWSGI and ASGI.
3. We are running RabbitMQ, so we have to cluster it and implement channels using asgi_rabbitmq (Redis would just add yet another moving part)
4. Plan on significant additional resource requirements on the web server and serious scaling challenges.

Are their any other non-Channels options, or is it the really the only game in town?

Thanks.



--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/aae2725b-d873-40fd-ae09-d1668ab9e727%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/d4906dc8-040b-4ee0-b11d-a7cc918b9e5d%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/4d7c94f1-81ad-4949-a987-35f45b188c2f%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/52e898ef-e71c-49d0-8476-7bbd7c6f20ce%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment