fix tenant view temporary password email, display message if user tries to register...
[plstackapi.git] / planetstack / core / models / user.py
index 9b54da9..c8df836 100644 (file)
@@ -1,10 +1,12 @@
 import os
 import datetime
 import os
 import datetime
+import sys
+import hashlib
 from collections import defaultdict
 from collections import defaultdict
+from django.forms.models import model_to_dict
 from django.db import models
 from django.db.models import F, Q
 from core.models import PlCoreBase,Site, DashboardView, DiffModelMixIn
 from django.db import models
 from django.db.models import F, Q
 from core.models import PlCoreBase,Site, DashboardView, DiffModelMixIn
-from core.models.site import Deployment
 from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
 from timezones.fields import TimeZoneField
 from operator import itemgetter, attrgetter
 from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
 from timezones.fields import TimeZoneField
 from operator import itemgetter, attrgetter
@@ -13,6 +15,20 @@ from core.middleware import get_request
 import model_policy
 from django.core.exceptions import PermissionDenied
 
 import model_policy
 from django.core.exceptions import PermissionDenied
 
+# ------ from plcorebase.py ------
+try:
+    # This is a no-op if observer_disabled is set to 1 in the config file
+    from observer import *
+except:
+    print >> sys.stderr, "import of observer failed! printing traceback and disabling observer:"
+    import traceback
+    traceback.print_exc()
+
+    # guard against something failing
+    def notify_observer(*args, **kwargs):
+        pass
+# ------ ------
+
 # Create your models here.
 class UserManager(BaseUserManager):
     def create_user(self, email, firstname, lastname, password=None):
 # Create your models here.
 class UserManager(BaseUserManager):
     def create_user(self, email, firstname, lastname, password=None):
@@ -48,6 +64,17 @@ class UserManager(BaseUserManager):
         user.save(using=self._db)
         return user
 
         user.save(using=self._db)
         return user
 
+    def get_queryset(self):
+        parent=super(UserManager, self)
+        if hasattr(parent, "get_queryset"):
+            return parent.get_queryset().filter(deleted=False)
+        else:
+            return parent.get_query_set().filter(deleted=False)
+
+    # deprecated in django 1.7 in favor of get_queryset().
+    def get_query_set(self):
+        return self.get_queryset()
+
 class DeletedUserManager(UserManager):
     def get_queryset(self):
         return super(UserManager, self).get_query_set().filter(deleted=True)
 class DeletedUserManager(UserManager):
     def get_queryset(self):
         return super(UserManager, self).get_query_set().filter(deleted=True)
@@ -56,7 +83,56 @@ class DeletedUserManager(UserManager):
     def get_query_set(self):
         return self.get_queryset()
 
     def get_query_set(self):
         return self.get_queryset()
 
-class User(AbstractBaseUser, DiffModelMixIn):
+class User(AbstractBaseUser): #, DiffModelMixIn):
+
+    # ---- copy stuff from DiffModelMixin ----
+
+    @property
+    def _dict(self):
+        return model_to_dict(self, fields=[field.name for field in
+                             self._meta.fields])
+
+    @property
+    def diff(self):
+        d1 = self._initial
+        d2 = self._dict
+        diffs = [(k, (v, d2[k])) for k, v in d1.items() if v != d2[k]]
+        return dict(diffs)
+
+    @property
+    def has_changed(self):
+        return bool(self.diff)
+
+    @property
+    def changed_fields(self):
+        return self.diff.keys()
+
+    def has_field_changed(self, field_name):
+        return field_name in self.diff.keys()
+
+    def get_field_diff(self, field_name):
+        return self.diff.get(field_name, None)
+
+    #classmethod
+    def getValidators(cls):
+        """ primarily for REST API, return a dictionary of field names mapped
+            to lists of the type of validations that need to be applied to
+            those fields.
+        """
+        validators = {}
+        for field in cls._meta.fields:
+            l = []
+            if field.blank==False:
+                l.append("notBlank")
+            if field.__class__.__name__=="URLField":
+                l.append("url")
+            validators[field.name] = l
+        return validators
+    # ---- end copy stuff from DiffModelMixin ----
+
+    @property
+    def remote_password(self):
+        return hashlib.md5(self.password).hexdigest()[:12]
 
     class Meta:
         app_label = "core"
 
     class Meta:
         app_label = "core"
@@ -79,13 +155,15 @@ class User(AbstractBaseUser, DiffModelMixIn):
     public_key = models.TextField(null=True, blank=True, max_length=1024, help_text="Public key string")
 
     is_active = models.BooleanField(default=True)
     public_key = models.TextField(null=True, blank=True, max_length=1024, help_text="Public key string")
 
     is_active = models.BooleanField(default=True)
-    is_admin = models.BooleanField(default=True)
+    is_admin = models.BooleanField(default=False)
     is_staff = models.BooleanField(default=True)
     is_readonly = models.BooleanField(default=False)
     is_staff = models.BooleanField(default=True)
     is_readonly = models.BooleanField(default=False)
+    is_registering = models.BooleanField(default=False)
 
     created = models.DateTimeField(auto_now_add=True)
     updated = models.DateTimeField(auto_now=True)
     enacted = models.DateTimeField(null=True, default=None)
 
     created = models.DateTimeField(auto_now_add=True)
     updated = models.DateTimeField(auto_now=True)
     enacted = models.DateTimeField(null=True, default=None)
+    policed = models.DateTimeField(null=True, default=None)
     backend_status = models.CharField(max_length=140,
                                       default="Provisioning in progress")
     deleted = models.BooleanField(default=False)
     backend_status = models.CharField(max_length=140,
                                       default="Provisioning in progress")
     deleted = models.BooleanField(default=False)
@@ -100,6 +178,9 @@ class User(AbstractBaseUser, DiffModelMixIn):
     USERNAME_FIELD = 'email'
     REQUIRED_FIELDS = ['firstname', 'lastname']
 
     USERNAME_FIELD = 'email'
     REQUIRED_FIELDS = ['firstname', 'lastname']
 
+    PI_FORBIDDEN_FIELDS = ["is_admin", "site", "is_staff"]
+    USER_FORBIDDEN_FIELDS = ["is_admin", "is_active", "site", "is_staff", "is_readonly"]
+
     def __init__(self, *args, **kwargs):
         super(User, self).__init__(*args, **kwargs)
         self._initial = self._dict # for DiffModelMixIn
     def __init__(self, *args, **kwargs):
         super(User, self).__init__(*args, **kwargs)
         self._initial = self._dict # for DiffModelMixIn
@@ -118,6 +199,8 @@ class User(AbstractBaseUser, DiffModelMixIn):
     def delete(self, *args, **kwds):
         # so we have something to give the observer
         purge = kwds.get('purge',False)
     def delete(self, *args, **kwds):
         # so we have something to give the observer
         purge = kwds.get('purge',False)
+        if purge:
+            del kwds['purge']
         try:
             purge = purge or observer_disabled
         except NameError:
         try:
             purge = purge or observer_disabled
         except NameError:
@@ -153,7 +236,7 @@ class User(AbstractBaseUser, DiffModelMixIn):
     def get_dashboards(self):
         DEFAULT_DASHBOARDS=["Tenant"]
 
     def get_dashboards(self):
         DEFAULT_DASHBOARDS=["Tenant"]
 
-        dashboards = sorted(list(self.dashboardViews.all()), key=attrgetter('order'))
+        dashboards = sorted(list(self.userdashboardviews.all()), key=attrgetter('order'))
         dashboards = [x.dashboardView for x in dashboards]
 
         if not dashboards:
         dashboards = [x.dashboardView for x in dashboards]
 
         if not dashboards:
@@ -180,9 +263,10 @@ class User(AbstractBaseUser, DiffModelMixIn):
     def save(self, *args, **kwds):
         if not self.id:
             self.set_password(self.password)
     def save(self, *args, **kwds):
         if not self.id:
             self.set_password(self.password)
-        if self.is_active:
-            if self.password=="!":\r
-                self.send_temporary_password()\r
+        print "XXX", self, self.is_active, self.is_registering
+        if self.is_active and self.is_registering:
+            self.send_temporary_password()\r
+            self.is_registering=False\r
 \r
         self.username = self.email
         super(User, self).save(*args, **kwds)
 \r
         self.username = self.email
         super(User, self).save(*args, **kwds)
@@ -200,38 +284,28 @@ class User(AbstractBaseUser, DiffModelMixIn):
         msg.attach_alternative(html_content, "text/html")\r
         msg.send()
 
         msg.attach_alternative(html_content, "text/html")\r
         msg.send()
 
-    def can_update_field(self, user, fieldName):
-        from core.models import SitePrivilege
-        if (user.is_admin):
-            # admin can update anything
-            return True
-
-        # fields that a site PI can update
-        if fieldName in ["is_active", "is_readonly"]:
-            site_privs = SitePrivilege.objects.filter(user=user, site=self.site)
-            for site_priv in site_privs:
-                if site_priv.role.role == 'pi':
-                    return True
-
-        # fields that a user cannot update in his/her own record
-        if fieldName in ["is_admin", "is_active", "site", "is_staff", "is_readonly"]:
-            return False
-
-        return True
-
     def can_update(self, user):
         from core.models import SitePrivilege
     def can_update(self, user):
         from core.models import SitePrivilege
+        _cant_update_fieldName = None
         if user.is_readonly:
             return False
         if user.is_admin:
             return True
         if user.is_readonly:
             return False
         if user.is_admin:
             return True
-        if (user.id == self.id):
-            return True
         # site pis can update
         site_privs = SitePrivilege.objects.filter(user=user, site=self.site)
         for site_priv in site_privs:
             if site_priv.role.role == 'pi':
         # site pis can update
         site_privs = SitePrivilege.objects.filter(user=user, site=self.site)
         for site_priv in site_privs:
             if site_priv.role.role == 'pi':
+                for fieldName in self.diff.keys():
+                    if fieldName in self.PI_FORBIDDEN_FIELDS:
+                        _cant_update_fieldName = fieldName
+                        return False
                 return True
                 return True
+        if (user.id == self.id):
+            for fieldName in self.diff.keys():
+                if fieldName in self.USER_FORBIDDEN_FIELDS:
+                    _cant_update_fieldName = fieldName
+                    return False
+            return True
 
         return False
 
 
         return False
 
@@ -252,11 +326,10 @@ class User(AbstractBaseUser, DiffModelMixIn):
 
     def save_by_user(self, user, *args, **kwds):
         if not self.can_update(user):
 
     def save_by_user(self, user, *args, **kwds):
         if not self.can_update(user):
-            raise PermissionDenied("You do not have permission to update %s objects" % self.__class__.__name__)
-
-        for fieldName in self.changed_fields:
-            if not self.can_update_field(user, fieldName):
-                raise PermissionDenied("You do not have permission to update field %s in object %s" % (fieldName, self.__class__.__name__))
+            if getattr(self, "_cant_update_fieldName", None) is not None:
+                raise PermissionDenied("You do not have permission to update field %s on object %s" % (self._cant_update_fieldName, self.__class__.__name__))
+            else:
+                raise PermissionDenied("You do not have permission to update %s objects" % self.__class__.__name__)
 
         self.save(*args, **kwds)
 
 
         self.save(*args, **kwds)
 
@@ -266,6 +339,6 @@ class User(AbstractBaseUser, DiffModelMixIn):
         self.delete(*args, **kwds)
 
 class UserDashboardView(PlCoreBase):
         self.delete(*args, **kwds)
 
 class UserDashboardView(PlCoreBase):
-     user = models.ForeignKey(User, related_name="dashboardViews")
-     dashboardView = models.ForeignKey(DashboardView, related_name="dashboardViews")
+     user = models.ForeignKey(User, related_name='userdashboardviews')
+     dashboardView = models.ForeignKey(DashboardView, related_name='userdashboardviews')
      order = models.IntegerField(default=0)
      order = models.IntegerField(default=0)