Django挂钩拒绝基于站点的用户访问管理员

时间:2015-01-14 02:29:33

标签: python django django-admin

我们有一个包含多个站点的Django项目(使用Sites框架的派生),其中每个站点都是我们域的子域。我需要能够拒绝某些员工用户(具有站点外键的自定义用户模型的实例)访问管理站点,具体取决于用户是否拥有该站点的外键。

自定义用户模型看起来基本上是这样的:

class SiteTenantUser(AbstractUser):
    site = models.ForeignKey(Site, null=True)

您的第一个倾向可能是建议我们使用自定义authenticate身份验证后端的SiteAuthBackend方法阻止身份验证,以便只允许用户登录自己的网站。这有助于防止身份验证,但诀窍是我们拥有跨站点访问权限的超级用户,因此我们希望使用wilcard会话cookie(.ourdomain.com)让超级用户从一个站点到另一个站点而不必登录在每一次。但是,通过这样做,我们现在需要除authenticate之外的其他内容,以防止没有该权限的用户进行跨站点访问。否则,他们可以对自己的网站进行身份验证,然后切换到另一个网站的管理网址,并进行合法会话并进入大门。

总而言之,虽然我们的自定义身份验证后端可以防止非超级用户员工(仅限于一个站点)登录到其他站点的管理员,但这并不会阻止他们登录到自己的管理员,然后切换到另一个网站的管理员。是否有一个简单的钩子来阻止这种情况,类似于Django如何拒绝任何管理页面中没有is_staff的用户,无论他们是否经过身份验证?

PS我们有一个全局get_current_site方法,它使用来自中间件的缓存值,可以在任何地方使用,不需要请求。

2 个答案:

答案 0 :(得分:2)

您可以创建自定义中间件以拒绝用户访问。在MIDDLEWARE_CLASSES之后将其添加到AuthenticationMiddleware

from django.http import HttpResponseForbidden 

class CheckUserSiteMiddleware(object):

    def process_request(self, request):
        if (request.path.startswith('/admin/') and
                request.user.is_authenticated() and
                user.site != get_current_site()):
            return HttpResponseForbidden()

答案 1 :(得分:0)

我最终创建了一个自定义AdminSite,如下所示:

from django.contrib.admin import AdminSite

class TenantSitesAdminSite(AdminSite):
    def has_permission(self, request):
        user = request.user
        return (
            user.is_active and
            user.is_staff and
            (user.is_superuser or user.site == get_current_site()))

site = TenantSitesAdminSite()

要使我的自定义site生效,我必须切换contrib.admin.site的所有用法以改为使用自定义site,以及取消注册任何第三方或贡献管理员contrib.admin.site并使用自定义site或(evilly)monkey-patch contrib.admin.site重新注册它们以指向自定义site。不会承认我做了什么。 :)

通过使用CustomAdminSite并重新定义has_permission逻辑,没有特定网站权限的员工用户获得与非员工用户尝试点击管理员时相同的体验 - 这将是进入管理员登录页面。 @ catvaran的建议有效,但是用户只会得到一个403错误页面,甚至无法返回到他们授权和退出的网站的管理员登录页面,我对于非工作人员用户来说,这种体验是相同的(这是对未经授权的管理页面的任何请求将他们带到管理员登录页面)。