Merge branch 'master' of git://git.planet-lab.org/plstackapi
[plstackapi.git] / planetstack / core / models / user.py
index d8355a2..7063f4f 100644 (file)
@@ -3,7 +3,7 @@ import datetime
 from collections import defaultdict
 from django.db import models
 from django.db.models import F, Q
 from collections import defaultdict
 from django.db import models
 from django.db.models import F, Q
-from core.models import PlCoreBase,Site, DashboardView
+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 core.models.site import Deployment
 from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
 from timezones.fields import TimeZoneField
@@ -11,6 +11,21 @@ from operator import itemgetter, attrgetter
 from django.core.mail import EmailMultiAlternatives
 from core.middleware import get_request
 import model_policy
 from django.core.mail import EmailMultiAlternatives
 from core.middleware import get_request
 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):
 
 # Create your models here.
 class UserManager(BaseUserManager):
@@ -47,11 +62,26 @@ class UserManager(BaseUserManager):
         user.save(using=self._db)
         return user
 
         user.save(using=self._db)
         return user
 
-class DeletedUserManager(UserManager):
+    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):
     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)
 
         return super(UserManager, self).get_query_set().filter(deleted=True)
 
-class User(AbstractBaseUser):
+    # deprecated in django 1.7 in favor of get_queryset()
+    def get_query_set(self):
+        return self.get_queryset()
+
+class User(AbstractBaseUser, DiffModelMixIn):
 
     class Meta:
         app_label = "core"
 
     class Meta:
         app_label = "core"
@@ -95,6 +125,13 @@ class User(AbstractBaseUser):
     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 isReadOnlyUser(self):
         return self.is_readonly
 
     def isReadOnlyUser(self):
         return self.is_readonly
 
@@ -109,6 +146,8 @@ class User(AbstractBaseUser):
     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:
@@ -144,7 +183,7 @@ class User(AbstractBaseUser):
     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:
@@ -178,6 +217,8 @@ class User(AbstractBaseUser):
         self.username = self.email
         super(User, self).save(*args, **kwds)
 
         self.username = self.email
         super(User, self).save(*args, **kwds)
 
+        self._initial = self._dict
+
     def send_temporary_password(self):
         password = User.objects.make_random_password()
         self.set_password(password)\r
     def send_temporary_password(self):
         password = User.objects.make_random_password()
         self.set_password(password)\r
@@ -189,6 +230,31 @@ class User(AbstractBaseUser):
         msg.attach_alternative(html_content, "text/html")\r
         msg.send()
 
         msg.attach_alternative(html_content, "text/html")\r
         msg.send()
 
+    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
+        # 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
+        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
+
     @staticmethod
     def select_by_user(user):
         if user.is_admin:
     @staticmethod
     def select_by_user(user):
         if user.is_admin:
@@ -204,7 +270,21 @@ class User(AbstractBaseUser):
             qs = User.objects.filter(Q(site__in=sites) | Q(id__in=user_ids))
         return qs
 
             qs = User.objects.filter(Q(site__in=sites) | Q(id__in=user_ids))
         return qs
 
+    def save_by_user(self, user, *args, **kwds):
+        if not self.can_update(user):
+            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)
+
+    def delete_by_user(self, user, *args, **kwds):
+        if not self.can_update(user):
+            raise PermissionDenied("You do not have permission to delete %s objects" % self.__class__.__name__)
+        self.delete(*args, **kwds)
+
 class UserDashboardView(PlCoreBase):
 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)