mirror of https://github.com/zulip/zulip.git
auth: Make supported authentication backends a bitfield on realm.
This makes it possible to configure only certain authentication methods to be enabled on a per-realm basis. Note that the authentication_methods_dict function (which checks what backends are supported on the realm) requires an in function import due to a circular dependency.
This commit is contained in:
parent
b41c15fa05
commit
21c024fc29
|
@ -4,8 +4,10 @@ from typing import Dict, Any
|
|||
from django.http import HttpRequest
|
||||
from django.conf import settings
|
||||
import ujson
|
||||
from zerver.models import get_realm_by_string_id
|
||||
from zproject.backends import (password_auth_enabled, dev_auth_enabled,
|
||||
google_auth_enabled, github_auth_enabled)
|
||||
from zerver.lib.utils import get_subdomain
|
||||
|
||||
def add_settings(request):
|
||||
# type: (HttpRequest) -> Dict[str, Any]
|
||||
|
@ -13,7 +15,11 @@ def add_settings(request):
|
|||
realm = request.user.realm
|
||||
realm_uri = realm.uri
|
||||
else:
|
||||
realm = None
|
||||
if settings.REALMS_HAVE_SUBDOMAINS:
|
||||
subdomain = get_subdomain(request)
|
||||
realm = get_realm_by_string_id(subdomain)
|
||||
else:
|
||||
realm = None
|
||||
# TODO: Figure out how to add an assertion that this is not used
|
||||
realm_uri = settings.SERVER_URI
|
||||
|
||||
|
@ -38,9 +44,9 @@ def add_settings(request):
|
|||
'email_gateway_example': settings.EMAIL_GATEWAY_EXAMPLE,
|
||||
'open_realm_creation': settings.OPEN_REALM_CREATION,
|
||||
'password_auth_enabled': password_auth_enabled(realm),
|
||||
'dev_auth_enabled': dev_auth_enabled(),
|
||||
'google_auth_enabled': google_auth_enabled(),
|
||||
'github_auth_enabled': github_auth_enabled(),
|
||||
'dev_auth_enabled': dev_auth_enabled(realm),
|
||||
'google_auth_enabled': google_auth_enabled(realm),
|
||||
'github_auth_enabled': github_auth_enabled(realm),
|
||||
'development_environment': settings.DEVELOPMENT,
|
||||
'support_email': settings.ZULIP_ADMINISTRATOR,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import bitfield.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('zerver', '0039_realmalias_drop_uniqueness'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='realm',
|
||||
name='authentication_methods',
|
||||
field=bitfield.models.BitField(['Google', 'Email', 'GitHub', 'LDAP', 'Dev', 'RemoteUser'], default=2147483647),
|
||||
),
|
||||
]
|
|
@ -10,6 +10,7 @@ from django.db.models import Manager
|
|||
from django.conf import settings
|
||||
from django.contrib.auth.models import AbstractBaseUser, UserManager, \
|
||||
PermissionsMixin
|
||||
import django.contrib.auth
|
||||
from django.dispatch import receiver
|
||||
from zerver.lib.cache import cache_with_key, flush_user_profile, flush_realm, \
|
||||
user_profile_by_id_cache_key, user_profile_by_email_cache_key, \
|
||||
|
@ -163,9 +164,29 @@ class Realm(ModelReprMixin, models.Model):
|
|||
notifications_stream = models.ForeignKey('Stream', related_name='+', null=True, blank=True) # type: Optional[Stream]
|
||||
deactivated = models.BooleanField(default=False) # type: bool
|
||||
default_language = models.CharField(default=u'en', max_length=MAX_LANGUAGE_ID_LENGTH) # type: text_type
|
||||
authentication_methods = BitField(flags=AUTHENTICATION_FLAGS,
|
||||
default=2**31 - 1) # type: BitHandler
|
||||
|
||||
DEFAULT_NOTIFICATION_STREAM_NAME = u'announce'
|
||||
|
||||
def authentication_methods_dict(self):
|
||||
# type: () -> Dict[text_type, bool]
|
||||
"""Returns the a mapping from authentication flags to their status,
|
||||
showing only those authentication flags that are supported on
|
||||
the current server (i.e. if EmailAuthBackend is not configured
|
||||
on the server, this will not return an entry for "Email")."""
|
||||
# This mapping needs to be imported from here due to the cyclic
|
||||
# dependency.
|
||||
from zproject.backends import AUTH_BACKEND_NAME_MAP
|
||||
|
||||
ret = {} # type: Dict[text_type, bool]
|
||||
supported_backends = {backend.__class__ for backend in django.contrib.auth.get_backends()}
|
||||
for k, v in self.authentication_methods.iteritems():
|
||||
backend = AUTH_BACKEND_NAME_MAP[k]
|
||||
if backend in supported_backends:
|
||||
ret[k] = v
|
||||
return ret
|
||||
|
||||
def __unicode__(self):
|
||||
# type: () -> text_type
|
||||
return u"<Realm: %s %s>" % (self.domain, self.id)
|
||||
|
|
|
@ -29,7 +29,7 @@ from confirmation.models import Confirmation
|
|||
from zproject.backends import ZulipDummyBackend, EmailAuthBackend, \
|
||||
GoogleMobileOauth2Backend, ZulipRemoteUserBackend, ZulipLDAPAuthBackend, \
|
||||
ZulipLDAPUserPopulator, DevAuthBackend, GitHubAuthBackend, ZulipAuthMixin, \
|
||||
password_auth_enabled, github_auth_enabled
|
||||
password_auth_enabled, github_auth_enabled, AUTH_BACKEND_NAME_MAP
|
||||
|
||||
from zerver.views.auth import maybe_send_to_registration
|
||||
|
||||
|
@ -96,6 +96,18 @@ class AuthBackendTest(TestCase):
|
|||
with self.settings(AUTHENTICATION_BACKENDS=('zproject.backends.ZulipDummyBackend',)):
|
||||
self.assertIsNone(backend.authenticate(username, *good_args, **good_kwargs))
|
||||
|
||||
# Verify auth fails if the auth backend is disabled for the realm
|
||||
for backend_name in AUTH_BACKEND_NAME_MAP.keys():
|
||||
if isinstance(backend, AUTH_BACKEND_NAME_MAP[backend_name]):
|
||||
break
|
||||
|
||||
index = getattr(user_profile.realm.authentication_methods, backend_name).number
|
||||
user_profile.realm.authentication_methods.set_bit(index, False)
|
||||
user_profile.realm.save()
|
||||
self.assertIsNone(backend.authenticate(username, *good_args, **good_kwargs))
|
||||
user_profile.realm.authentication_methods.set_bit(index, True)
|
||||
user_profile.realm.save()
|
||||
|
||||
def test_dummy_backend(self):
|
||||
# type: () -> None
|
||||
self.verify_backend(ZulipDummyBackend(),
|
||||
|
|
|
@ -36,8 +36,12 @@ def pad_method_dict(method_dict):
|
|||
|
||||
def auth_enabled_helper(backends_to_check, realm):
|
||||
# type: (List[text_type], Optional[Realm]) -> bool
|
||||
enabled_method_dict = dict((method, True) for method in Realm.AUTHENTICATION_FLAGS)
|
||||
pad_method_dict(enabled_method_dict)
|
||||
if realm is not None:
|
||||
enabled_method_dict = realm.authentication_methods_dict()
|
||||
pad_method_dict(enabled_method_dict)
|
||||
else:
|
||||
enabled_method_dict = dict((method, True) for method in Realm.AUTHENTICATION_FLAGS)
|
||||
pad_method_dict(enabled_method_dict)
|
||||
for supported_backend in django.contrib.auth.get_backends():
|
||||
for backend_name in backends_to_check:
|
||||
backend = AUTH_BACKEND_NAME_MAP[backend_name]
|
||||
|
|
Loading…
Reference in New Issue