ソースを参照

HUE-8530 [organization] Consolidate all the logic into the organization module

Romain 5 年 前
コミット
39caa9600b

+ 1 - 4
apps/useradmin/src/useradmin/models.py

@@ -59,17 +59,14 @@ from desktop.lib.idbroker.conf import is_idbroker_enabled
 from desktop.monkey_patches import monkey_patch_username_validator
 
 from useradmin.conf import DEFAULT_USER_GROUP
-from useradmin.models2 import Organization
 from useradmin.permissions import HuePermission, GroupPermission, LdapGroup
 
 if ENABLE_ORGANIZATIONS.get():
-  from useradmin.models2 import OrganizationUser as User, OrganizationGroup as Group, Organization, default_organization, get_organization, \
-      _fitered_queryset
+  from useradmin.organization import OrganizationUser as User, OrganizationGroup as Group, get_organization, Organization
 else:
   from django.contrib.auth.models import User, Group
   def get_organization(): pass
   class Organization(): pass
-  def _fitered_queryset(queryset): return queryset
 
   monkey_patch_username_validator()
 

+ 214 - 0
apps/useradmin/src/useradmin/organization.py

@@ -15,11 +15,21 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import logging
+import uuid
+
 from crequest.middleware import CrequestMiddleware
 
+from django.contrib.auth.models import models, AbstractUser, BaseUserManager
+from django.utils.functional import SimpleLazyObject
+from django.utils.translation import ugettext_lazy as _t
+
 from desktop.conf import ENABLE_ORGANIZATIONS
 
 
+LOG = logging.getLogger(__name__)
+
+
 def default_organization():
   from useradmin.models import Organization
   default_organization, created = Organization.objects.get_or_create(name='default', domain='default')
@@ -46,3 +56,207 @@ def _fitered_queryset(queryset, by_owner=False):
     queryset = queryset.filter(**filters)
 
   return queryset
+
+
+"""
+Organizations handle contained sets of setups (user, group, connectors...).
+"""
+
+def get_organization(email, is_multi_user=False):
+  domain = email.split('@')[1] if is_multi_user else email
+
+  if domain:
+    organization, created = Organization.objects.get_or_create(name=domain, domain=domain, is_multi_user=is_multi_user)
+    LOG.info("Materializing organization %s in the database, is_multi_user=%s" % (domain, is_multi_user))
+  else:
+    LOG.warn('No organization domain found for email %s' % email)  # For Backends without emails or when organization enabled by default
+    organization = default_organization()
+
+  return organization
+
+
+def uuid_default():
+  return str(uuid.uuid4())
+
+
+
+class OrganizationManager(models.Manager):
+  use_in_migrations = True
+
+  def get_by_natural_key(self, name):
+    return self.get(name=name)
+
+
+if ENABLE_ORGANIZATIONS.get():
+  class Organization(models.Model):
+    name = models.CharField(max_length=200, help_text=_t("The name of the organization"), unique=True)
+    uuid = models.CharField(default=uuid_default, max_length=36, unique=True)
+    domain = models.CharField(max_length=200, help_text=_t("The domain name of the organization, e.g. gethue.com"), unique=True)
+    customer_id = models.CharField(_t('Customer id'), max_length=128, default=None, null=True)
+    is_active = models.BooleanField(default=True)
+    is_multi_user = models.BooleanField(default=True)
+
+    objects = OrganizationManager()
+
+    def __str__(self):
+      return self.name or self.domain
+
+
+class OrganizationGroupManager(models.Manager):
+
+  def get_queryset(self):
+    """Make sure to restrict to only organization's groups"""
+    queryset = super(OrganizationGroupManager, self).get_queryset()
+    return _fitered_queryset(queryset)
+
+  def natural_key(self):
+    return (self.organization, self.name,)
+
+
+if ENABLE_ORGANIZATIONS.get():
+  class OrganizationGroup(models.Model):
+    name = models.CharField(_t('name'), max_length=80, unique=False)
+    organization = models.ForeignKey(Organization)
+
+    permissions = models.ManyToManyField(
+        'HuePermission',
+        verbose_name=_t('permissions'),
+        blank=True,
+    )
+    # Could also have a set of Roles at some point.
+
+    objects = OrganizationGroupManager()
+
+    def __init__(self, *args, **kwargs):
+      if not kwargs.get('organization'):
+        kwargs['organization'] = get_user_request_organization()
+
+      super(OrganizationGroup, self).__init__(*args, **kwargs)
+
+    class Meta:
+      verbose_name = _t('organization group')
+      verbose_name_plural = _t('organization groups')
+      unique_together = ('name', 'organization',)
+
+    def __str__(self):
+      return '%s @ %s' % (self.name, self.organization)
+
+
+class UserManager(BaseUserManager):
+  """Define a model manager for User model with no username field."""
+
+  use_in_migrations = True
+
+  def get_queryset(self):
+    """Make sure to restrict to only organization's user"""
+    queryset = super(UserManager, self).get_queryset()
+    return _fitered_queryset(queryset)
+
+  def get(self, *args, **kwargs):
+    if kwargs.get('username'):
+      kwargs['email'] = kwargs.pop('username')
+
+    return super(UserManager, self).get(*args, **kwargs)
+
+  def order_by(self, *args, **kwargs):
+    if 'username' in args:
+      args = list(args)
+      args.remove('username')
+      args.append('email')
+
+    return super(UserManager, self).order_by(*args, **kwargs)
+
+  def filter(self, *args, **kwargs):
+    f = super(UserManager, self).filter(*args, **kwargs)
+    # f.values_list = self.values_list  # Patch so that chaining after a filter is backward compatible
+    return f
+
+  def values_list(self, *args, **kwargs):
+    print(args)
+    print(kwargs)
+    if 'username' in args:
+      args = list(args)
+      args.remove('username')
+      args.append('email')
+    print(args)
+
+    return super(UserManager, self).values_list(*args, **kwargs)
+
+  def _create_user(self, email, password, **extra_fields):
+    """Create and save a User with the given email and password."""
+    if not email:
+      raise ValueError('The given email must be set')
+    email = self.normalize_email(email)
+    user = self.model(email=email, **extra_fields)
+    user.set_password(password)
+    user.save(using=self._db)
+    return user
+
+  def create_user(self, email=None, password=None, **extra_fields):
+    """Create and save a regular User with the given email and password."""
+    if extra_fields.get('username'):
+      email = extra_fields.pop('username')
+    if not extra_fields.get('organization'):
+      extra_fields['organization'] = get_user_request_organization()
+
+    extra_fields.setdefault('is_staff', False)
+    extra_fields.setdefault('is_superuser', False)
+    extra_fields.setdefault('is_admin', False)
+    return self._create_user(email, password, **extra_fields)
+
+  def create_superuser(self, email, password, **extra_fields):
+    """Create and save a SuperUser with the given email and password."""
+    extra_fields.setdefault('is_staff', False)
+    extra_fields.setdefault('is_superuser', False)
+    extra_fields.setdefault('is_admin', True)
+
+    if extra_fields.get('is_staff') is not False:
+      raise ValueError('Organization superuser must have is_staff=False.')
+    if extra_fields.get('is_superuser') is not False:
+      raise ValueError('Organization superuser must have is_superuser=False.')
+    if extra_fields.get('is_admin') is not True:
+      raise ValueError('Organization superuser must have is_admin=True.')
+
+    return self._create_user(email, password, **extra_fields)
+
+
+if ENABLE_ORGANIZATIONS.get():
+  class OrganizationUser(AbstractUser):
+    """User model in a multi tenant setup."""
+
+    username = None
+    email = models.EmailField(_t('Email address'), unique=True)
+    token = models.CharField(_t('Token'), max_length=128, default=None, null=True)
+    organization = models.ForeignKey(Organization, on_delete=models.CASCADE)
+    is_admin = models.BooleanField(default=False)
+
+    groups = models.ManyToManyField(
+        OrganizationGroup,
+        verbose_name=_t('groups'),
+        blank=True,
+        help_text=_t(
+            'The groups this user belongs to. A user will get all permissions granted to each of their groups.'
+        ),
+        related_name="user_set",
+        related_query_name="user",
+    )
+
+    USERNAME_FIELD = 'email'
+    REQUIRED_FIELDS = []
+
+    objects = UserManager()
+
+    def __str__(self):
+      return '%s @ %s' % (self.email, self.organization)
+
+    @property
+    def username(self):
+      return self.email
+
+    @property
+    def username_short(self):
+      return self.email.split('@')[0]
+
+    @username.setter
+    def username(self, value):
+      pass

+ 0 - 0
apps/useradmin/src/useradmin/models2_tests.py → apps/useradmin/src/useradmin/organization_tests.py


+ 1 - 1
apps/useradmin/src/useradmin/permissions.py

@@ -27,7 +27,7 @@ from useradmin.organization import _fitered_queryset, get_user_request_organizat
 
 
 if ENABLE_ORGANIZATIONS.get():
-  from useradmin.models2 import OrganizationGroup as Group, Organization
+  from useradmin.organization import OrganizationGroup as Group, Organization
 else:
   from django.contrib.auth.models import Group
 

+ 1 - 2
apps/useradmin/src/useradmin/views.py

@@ -51,8 +51,7 @@ from useradmin.forms import SyncLdapUsersGroupsForm, AddLdapGroupsForm, AddLdapU
   PermissionsEditForm, GroupEditForm, SuperUserChangeForm, validate_username, validate_first_name, \
   validate_last_name, PasswordChangeForm
 from useradmin.ldap_access import LdapBindException, LdapSearchException
-from useradmin.models import HuePermission, UserProfile, LdapGroup, get_profile, get_default_user_group, User, Group
-from useradmin.models2 import Organization
+from useradmin.models import HuePermission, UserProfile, LdapGroup, get_profile, get_default_user_group, User, Group, Organization
 
 if sys.version_info[0] > 2:
   unicode = str