Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 1 | import datetime |
Tony Mack | 2f5be42 | 2015-01-03 14:26:15 -0500 | [diff] [blame] | 2 | import hashlib |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 3 | import os |
| 4 | import sys |
Tony Mack | c14de8f | 2013-05-09 21:44:17 -0400 | [diff] [blame] | 5 | from collections import defaultdict |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 6 | from operator import attrgetter, itemgetter |
| 7 | |
| 8 | import synchronizers.model_policy |
| 9 | from core.middleware import get_request |
| 10 | from core.models import DashboardView, PlCoreBase, PlModelMixIn, Site |
| 11 | from core.models.plcorebase import StrippedCharField |
| 12 | from django.contrib.auth.models import AbstractBaseUser, BaseUserManager |
| 13 | from django.core.exceptions import PermissionDenied |
| 14 | from django.core.mail import EmailMultiAlternatives |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 15 | from django.db import models |
Tony Mack | 5b06147 | 2014-02-04 07:57:10 -0500 | [diff] [blame] | 16 | from django.db.models import F, Q |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 17 | from django.forms.models import model_to_dict |
Scott Baker | daca816 | 2015-02-02 14:28:35 -0800 | [diff] [blame] | 18 | from django.utils import timezone |
Scott Baker | 9266e6b | 2013-05-19 15:54:48 -0700 | [diff] [blame] | 19 | from timezones.fields import TimeZoneField |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 20 | |
Scott Baker | fd8c7c4 | 2014-10-09 16:16:02 -0700 | [diff] [blame] | 21 | # ------ from plcorebase.py ------ |
| 22 | try: |
| 23 | # This is a no-op if observer_disabled is set to 1 in the config file |
Sapan Bhatia | 003e84c | 2016-01-15 11:05:52 -0500 | [diff] [blame] | 24 | from synchronizers.base import * |
Scott Baker | fd8c7c4 | 2014-10-09 16:16:02 -0700 | [diff] [blame] | 25 | except: |
| 26 | print >> sys.stderr, "import of observer failed! printing traceback and disabling observer:" |
| 27 | import traceback |
| 28 | traceback.print_exc() |
| 29 | |
| 30 | # guard against something failing |
| 31 | def notify_observer(*args, **kwargs): |
| 32 | pass |
| 33 | # ------ ------ |
| 34 | |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 35 | # Create your models here. |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 36 | |
| 37 | |
Siobhan Tully | 30fd429 | 2013-05-10 08:59:56 -0400 | [diff] [blame] | 38 | class UserManager(BaseUserManager): |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 39 | |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 40 | def create_user(self, email, firstname, lastname, password=None): |
| 41 | """ |
| 42 | Creates and saves a User with the given email, date of |
| 43 | birth and password. |
| 44 | """ |
| 45 | if not email: |
| 46 | raise ValueError('Users must have an email address') |
| 47 | |
| 48 | user = self.model( |
Siobhan Tully | 30fd429 | 2013-05-10 08:59:56 -0400 | [diff] [blame] | 49 | email=UserManager.normalize_email(email), |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 50 | firstname=firstname, |
Siobhan Tully | 30fd429 | 2013-05-10 08:59:56 -0400 | [diff] [blame] | 51 | lastname=lastname, |
| 52 | password=password |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 53 | ) |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 54 | # user.set_password(password) |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 55 | user.is_admin = True |
| 56 | user.save(using=self._db) |
| 57 | return user |
| 58 | |
| 59 | def create_superuser(self, email, firstname, lastname, password): |
| 60 | """ |
| 61 | Creates and saves a superuser with the given email, date of |
| 62 | birth and password. |
| 63 | """ |
| 64 | user = self.create_user(email, |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 65 | password=password, |
| 66 | firstname=firstname, |
| 67 | lastname=lastname |
| 68 | ) |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 69 | user.is_admin = True |
| 70 | user.save(using=self._db) |
| 71 | return user |
| 72 | |
Scott Baker | 6c68484 | 2014-10-17 18:45:00 -0700 | [diff] [blame] | 73 | def get_queryset(self): |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 74 | parent = super(UserManager, self) |
Scott Baker | 6c68484 | 2014-10-17 18:45:00 -0700 | [diff] [blame] | 75 | if hasattr(parent, "get_queryset"): |
| 76 | return parent.get_queryset().filter(deleted=False) |
| 77 | else: |
| 78 | return parent.get_query_set().filter(deleted=False) |
| 79 | |
| 80 | # deprecated in django 1.7 in favor of get_queryset(). |
| 81 | def get_query_set(self): |
| 82 | return self.get_queryset() |
| 83 | |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 84 | |
Sapan Bhatia | 5d605ff | 2014-07-21 20:08:04 -0400 | [diff] [blame] | 85 | class DeletedUserManager(UserManager): |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 86 | |
Scott Baker | b08d656 | 2014-09-12 12:57:27 -0700 | [diff] [blame] | 87 | def get_queryset(self): |
Sapan Bhatia | 5d605ff | 2014-07-21 20:08:04 -0400 | [diff] [blame] | 88 | return super(UserManager, self).get_query_set().filter(deleted=True) |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 89 | |
Scott Baker | b08d656 | 2014-09-12 12:57:27 -0700 | [diff] [blame] | 90 | # deprecated in django 1.7 in favor of get_queryset() |
| 91 | def get_query_set(self): |
| 92 | return self.get_queryset() |
| 93 | |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 94 | |
Scott Baker | 1211334 | 2015-02-10 15:44:30 -0800 | [diff] [blame] | 95 | class User(AbstractBaseUser, PlModelMixIn): |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 96 | |
Tony Mack | 2f5be42 | 2015-01-03 14:26:15 -0500 | [diff] [blame] | 97 | @property |
| 98 | def remote_password(self): |
| 99 | return hashlib.md5(self.password).hexdigest()[:12] |
| 100 | |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 101 | class Meta: |
| 102 | app_label = "core" |
| 103 | |
| 104 | email = models.EmailField( |
| 105 | verbose_name='email address', |
| 106 | max_length=255, |
| 107 | unique=True, |
| 108 | db_index=True, |
| 109 | ) |
Siobhan Tully | fece0d5 | 2013-09-06 12:57:05 -0400 | [diff] [blame] | 110 | |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 111 | username = StrippedCharField(max_length=255, default="Something") |
Siobhan Tully | fece0d5 | 2013-09-06 12:57:05 -0400 | [diff] [blame] | 112 | |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 113 | firstname = StrippedCharField( |
| 114 | help_text="person's given name", max_length=200) |
Tony Mack | d84b1ff | 2015-03-09 13:03:56 -0400 | [diff] [blame] | 115 | lastname = StrippedCharField(help_text="person's surname", max_length=200) |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 116 | |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 117 | phone = StrippedCharField(null=True, blank=True, |
| 118 | help_text="phone number contact", max_length=100) |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 119 | user_url = models.URLField(null=True, blank=True) |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 120 | site = models.ForeignKey(Site, related_name='users', |
| 121 | help_text="Site this user will be homed too") |
| 122 | public_key = models.TextField( |
| 123 | null=True, blank=True, max_length=1024, help_text="Public key string") |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 124 | |
| 125 | is_active = models.BooleanField(default=True) |
Tony Mack | 4bfcdc8 | 2015-01-28 12:03:39 -0500 | [diff] [blame] | 126 | is_admin = models.BooleanField(default=False) |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 127 | is_staff = models.BooleanField(default=True) |
Siobhan Tully | cf04fb6 | 2014-01-11 11:25:57 -0500 | [diff] [blame] | 128 | is_readonly = models.BooleanField(default=False) |
Scott Baker | 28e2e3a | 2015-01-28 16:03:40 -0800 | [diff] [blame] | 129 | is_registering = models.BooleanField(default=False) |
Scott Baker | 74ebc7a | 2015-05-15 09:19:36 -0700 | [diff] [blame] | 130 | is_appuser = models.BooleanField(default=False) |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 131 | |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 132 | login_page = StrippedCharField( |
| 133 | help_text="send this user to a specific page on login", max_length=200, null=True, blank=True) |
Scott Baker | e61e3a0 | 2015-06-10 16:14:58 -0700 | [diff] [blame] | 134 | |
Tony Mack | 0553f28 | 2013-06-10 22:54:50 -0400 | [diff] [blame] | 135 | created = models.DateTimeField(auto_now_add=True) |
| 136 | updated = models.DateTimeField(auto_now=True) |
| 137 | enacted = models.DateTimeField(null=True, default=None) |
Sapan Bhatia | 103465d | 2015-01-23 16:02:09 +0000 | [diff] [blame] | 138 | policed = models.DateTimeField(null=True, default=None) |
Tony Mack | d84b1ff | 2015-03-09 13:03:56 -0400 | [diff] [blame] | 139 | backend_status = StrippedCharField(max_length=1024, |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 140 | default="Provisioning in progress") |
Sapan Bhatia | bcc1899 | 2014-04-29 10:32:14 -0400 | [diff] [blame] | 141 | deleted = models.BooleanField(default=False) |
Scott Baker | 690447e | 2015-03-10 12:14:25 -0700 | [diff] [blame] | 142 | write_protect = models.BooleanField(default=False) |
Scott Baker | ced9e4f | 2016-04-14 23:41:07 -0700 | [diff] [blame] | 143 | lazy_blocked = models.BooleanField(default=False) |
| 144 | no_sync = models.BooleanField(default=False) # prevent object sync |
| 145 | no_policy = models.BooleanField(default=False) # prevent model_policy run |
Tony Mack | 0553f28 | 2013-06-10 22:54:50 -0400 | [diff] [blame] | 146 | |
Scott Baker | 9266e6b | 2013-05-19 15:54:48 -0700 | [diff] [blame] | 147 | timezone = TimeZoneField() |
| 148 | |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 149 | dashboards = models.ManyToManyField( |
| 150 | 'DashboardView', through='UserDashboardView', blank=True) |
Scott Baker | 2c3cb64 | 2014-05-19 17:55:56 -0700 | [diff] [blame] | 151 | |
Siobhan Tully | 30fd429 | 2013-05-10 08:59:56 -0400 | [diff] [blame] | 152 | objects = UserManager() |
Sapan Bhatia | 5d605ff | 2014-07-21 20:08:04 -0400 | [diff] [blame] | 153 | deleted_objects = DeletedUserManager() |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 154 | |
| 155 | USERNAME_FIELD = 'email' |
| 156 | REQUIRED_FIELDS = ['firstname', 'lastname'] |
| 157 | |
Scott Baker | 0119c15 | 2014-10-06 22:58:48 -0700 | [diff] [blame] | 158 | PI_FORBIDDEN_FIELDS = ["is_admin", "site", "is_staff"] |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 159 | USER_FORBIDDEN_FIELDS = ["is_admin", "is_active", |
| 160 | "site", "is_staff", "is_readonly"] |
Scott Baker | 0119c15 | 2014-10-06 22:58:48 -0700 | [diff] [blame] | 161 | |
Scott Baker | cbfb600 | 2014-10-03 00:32:37 -0700 | [diff] [blame] | 162 | def __init__(self, *args, **kwargs): |
| 163 | super(User, self).__init__(*args, **kwargs) |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 164 | self._initial = self._dict # for PlModelMixIn |
Scott Baker | cbfb600 | 2014-10-03 00:32:37 -0700 | [diff] [blame] | 165 | |
Siobhan Tully | cf04fb6 | 2014-01-11 11:25:57 -0500 | [diff] [blame] | 166 | def isReadOnlyUser(self): |
| 167 | return self.is_readonly |
| 168 | |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 169 | def get_full_name(self): |
| 170 | # The user is identified by their email address |
| 171 | return self.email |
| 172 | |
| 173 | def get_short_name(self): |
| 174 | # The user is identified by their email address |
| 175 | return self.email |
| 176 | |
Sapan Bhatia | 5d605ff | 2014-07-21 20:08:04 -0400 | [diff] [blame] | 177 | def delete(self, *args, **kwds): |
| 178 | # so we have something to give the observer |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 179 | purge = kwds.get('purge', False) |
Scott Baker | c5b5060 | 2014-10-09 16:22:00 -0700 | [diff] [blame] | 180 | if purge: |
| 181 | del kwds['purge'] |
Sapan Bhatia | 5d605ff | 2014-07-21 20:08:04 -0400 | [diff] [blame] | 182 | try: |
| 183 | purge = purge or observer_disabled |
| 184 | except NameError: |
| 185 | pass |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 186 | |
Sapan Bhatia | 5d605ff | 2014-07-21 20:08:04 -0400 | [diff] [blame] | 187 | if (purge): |
| 188 | super(User, self).delete(*args, **kwds) |
| 189 | else: |
Scott Baker | 690447e | 2015-03-10 12:14:25 -0700 | [diff] [blame] | 190 | if (not self.write_protect): |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 191 | self.deleted = True |
| 192 | self.enacted = None |
| 193 | self.save(update_fields=['enacted', 'deleted']) |
Sapan Bhatia | 5d605ff | 2014-07-21 20:08:04 -0400 | [diff] [blame] | 194 | |
Tony Mack | b0d9742 | 2013-06-10 09:57:45 -0400 | [diff] [blame] | 195 | @property |
| 196 | def keyname(self): |
| 197 | return self.email[:self.email.find('@')] |
| 198 | |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 199 | def __unicode__(self): |
| 200 | return self.email |
| 201 | |
| 202 | def has_perm(self, perm, obj=None): |
| 203 | "Does the user have a specific permission?" |
| 204 | # Simplest possible answer: Yes, always |
| 205 | return True |
| 206 | |
| 207 | def has_module_perms(self, app_label): |
| 208 | "Does the user have permissions to view the app `app_label`?" |
| 209 | # Simplest possible answer: Yes, always |
| 210 | return True |
| 211 | |
Siobhan Tully | bfd11dc | 2013-09-03 12:59:24 -0400 | [diff] [blame] | 212 | def is_superuser(self): |
| 213 | return False |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 214 | |
Scott Baker | 2c3cb64 | 2014-05-19 17:55:56 -0700 | [diff] [blame] | 215 | def get_dashboards(self): |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 216 | DEFAULT_DASHBOARDS = ["Tenant"] |
Scott Baker | 2c3cb64 | 2014-05-19 17:55:56 -0700 | [diff] [blame] | 217 | |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 218 | dashboards = sorted( |
| 219 | list(self.userdashboardviews.all()), key=attrgetter('order')) |
Scott Baker | 2c3cb64 | 2014-05-19 17:55:56 -0700 | [diff] [blame] | 220 | dashboards = [x.dashboardView for x in dashboards] |
| 221 | |
Scott Baker | 811d447 | 2015-05-19 16:39:48 -0700 | [diff] [blame] | 222 | if (not dashboards) and (not self.is_appuser): |
Scott Baker | 2c3cb64 | 2014-05-19 17:55:56 -0700 | [diff] [blame] | 223 | for dashboardName in DEFAULT_DASHBOARDS: |
| 224 | dbv = DashboardView.objects.filter(name=dashboardName) |
| 225 | if dbv: |
| 226 | dashboards.append(dbv[0]) |
| 227 | |
| 228 | return dashboards |
| 229 | |
Siobhan Tully | bfd11dc | 2013-09-03 12:59:24 -0400 | [diff] [blame] | 230 | # def get_roles(self): |
| 231 | # from core.models.site import SitePrivilege |
| 232 | # from core.models.slice import SliceMembership |
| 233 | # |
| 234 | # site_privileges = SitePrivilege.objects.filter(user=self) |
| 235 | # slice_memberships = SliceMembership.objects.filter(user=self) |
| 236 | # roles = defaultdict(list) |
| 237 | # for site_privilege in site_privileges: |
| 238 | # roles[site_privilege.role.role_type].append(site_privilege.site.login_base) |
| 239 | # for slice_membership in slice_memberships: |
| 240 | # roles[slice_membership.role.role_type].append(slice_membership.slice.name) |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 241 | # return roles |
Siobhan Tully | 5343728 | 2013-04-26 19:30:27 -0400 | [diff] [blame] | 242 | |
Tony Mack | 53106f3 | 2013-04-27 16:43:01 -0400 | [diff] [blame] | 243 | def save(self, *args, **kwds): |
Siobhan Tully | 30fd429 | 2013-05-10 08:59:56 -0400 | [diff] [blame] | 244 | if not self.id: |
Scott Baker | a36d77e | 2014-08-29 11:43:23 -0700 | [diff] [blame] | 245 | self.set_password(self.password) |
Scott Baker | 28e2e3a | 2015-01-28 16:03:40 -0800 | [diff] [blame] | 246 | if self.is_active and self.is_registering: |
S.Çağlar Onur | 0e59183 | 2015-02-24 17:28:09 -0500 | [diff] [blame] | 247 | self.send_temporary_password() |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 248 | self.is_registering = False |
S.Çağlar Onur | 0e59183 | 2015-02-24 17:28:09 -0500 | [diff] [blame] | 249 | |
Siobhan Tully | fece0d5 | 2013-09-06 12:57:05 -0400 | [diff] [blame] | 250 | self.username = self.email |
Scott Baker | a36d77e | 2014-08-29 11:43:23 -0700 | [diff] [blame] | 251 | super(User, self).save(*args, **kwds) |
| 252 | |
Scott Baker | cbfb600 | 2014-10-03 00:32:37 -0700 | [diff] [blame] | 253 | self._initial = self._dict |
| 254 | |
Scott Baker | a36d77e | 2014-08-29 11:43:23 -0700 | [diff] [blame] | 255 | def send_temporary_password(self): |
| 256 | password = User.objects.make_random_password() |
S.Çağlar Onur | 0e59183 | 2015-02-24 17:28:09 -0500 | [diff] [blame] | 257 | self.set_password(password) |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 258 | subject, from_email, to = 'OpenCloud Account Credentials', 'support@opencloud.us', str( |
| 259 | self.email) |
S.Çağlar Onur | 0e59183 | 2015-02-24 17:28:09 -0500 | [diff] [blame] | 260 | text_content = 'This is an important message.' |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 261 | userUrl = "http://%s/" % get_request().get_host() |
| 262 | html_content = """<p>Your account has been created on OpenCloud. Please log in <a href=""" + userUrl + """>here</a> to activate your account<br><br>Username: """ + \ |
| 263 | self.email + """<br>Temporary Password: """ + password + \ |
| 264 | """<br>Please change your password once you successully login into the site.</p>""" |
| 265 | msg = EmailMultiAlternatives(subject, text_content, from_email, [to]) |
S.Çağlar Onur | 0e59183 | 2015-02-24 17:28:09 -0500 | [diff] [blame] | 266 | msg.attach_alternative(html_content, "text/html") |
Scott Baker | a36d77e | 2014-08-29 11:43:23 -0700 | [diff] [blame] | 267 | msg.send() |
Tony Mack | 5b06147 | 2014-02-04 07:57:10 -0500 | [diff] [blame] | 268 | |
Scott Baker | cbfb600 | 2014-10-03 00:32:37 -0700 | [diff] [blame] | 269 | def can_update(self, user): |
| 270 | from core.models import SitePrivilege |
Scott Baker | 0119c15 | 2014-10-06 22:58:48 -0700 | [diff] [blame] | 271 | _cant_update_fieldName = None |
Tony Mack | 2a56ce5 | 2015-02-09 12:16:03 -0500 | [diff] [blame] | 272 | if user.can_update_root(): |
Scott Baker | cbfb600 | 2014-10-03 00:32:37 -0700 | [diff] [blame] | 273 | return True |
Tony Mack | 2a56ce5 | 2015-02-09 12:16:03 -0500 | [diff] [blame] | 274 | |
Scott Baker | cbfb600 | 2014-10-03 00:32:37 -0700 | [diff] [blame] | 275 | # site pis can update |
| 276 | site_privs = SitePrivilege.objects.filter(user=user, site=self.site) |
| 277 | for site_priv in site_privs: |
Tony Mack | 2a56ce5 | 2015-02-09 12:16:03 -0500 | [diff] [blame] | 278 | if site_priv.role.role == 'admin': |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 279 | return True |
Scott Baker | cbfb600 | 2014-10-03 00:32:37 -0700 | [diff] [blame] | 280 | if site_priv.role.role == 'pi': |
Scott Baker | 0119c15 | 2014-10-06 22:58:48 -0700 | [diff] [blame] | 281 | for fieldName in self.diff.keys(): |
| 282 | if fieldName in self.PI_FORBIDDEN_FIELDS: |
| 283 | _cant_update_fieldName = fieldName |
| 284 | return False |
Scott Baker | cbfb600 | 2014-10-03 00:32:37 -0700 | [diff] [blame] | 285 | return True |
Scott Baker | 0119c15 | 2014-10-06 22:58:48 -0700 | [diff] [blame] | 286 | if (user.id == self.id): |
| 287 | for fieldName in self.diff.keys(): |
| 288 | if fieldName in self.USER_FORBIDDEN_FIELDS: |
| 289 | _cant_update_fieldName = fieldName |
| 290 | return False |
| 291 | return True |
Scott Baker | cbfb600 | 2014-10-03 00:32:37 -0700 | [diff] [blame] | 292 | |
| 293 | return False |
| 294 | |
Tony Mack | 5ff90fc | 2015-02-08 21:38:41 -0500 | [diff] [blame] | 295 | def can_update_root(self): |
| 296 | """ |
| 297 | Return True if user has root (global) write access. |
| 298 | """ |
| 299 | if self.is_readonly: |
| 300 | return False |
| 301 | if self.is_admin: |
| 302 | return True |
| 303 | |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 304 | return False |
Tony Mack | 5ff90fc | 2015-02-08 21:38:41 -0500 | [diff] [blame] | 305 | |
| 306 | def can_update_deployment(self, deployment): |
| 307 | from core.models.site import DeploymentPrivilege |
| 308 | if self.can_update_root(): |
Tony Mack | 5ff90fc | 2015-02-08 21:38:41 -0500 | [diff] [blame] | 309 | return True |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 310 | |
| 311 | if DeploymentPrivilege.objects.filter( |
| 312 | deployment=deployment, |
| 313 | user=self, |
| 314 | role__role__in=['admin', 'Admin']): |
| 315 | return True |
| 316 | return False |
Tony Mack | 5ff90fc | 2015-02-08 21:38:41 -0500 | [diff] [blame] | 317 | |
| 318 | def can_update_site(self, site, allow=[]): |
| 319 | from core.models.site import SitePrivilege |
| 320 | if self.can_update_root(): |
| 321 | return True |
| 322 | if SitePrivilege.objects.filter( |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 323 | site=site, user=self, role__role__in=['admin', 'Admin'] + allow): |
Tony Mack | 5ff90fc | 2015-02-08 21:38:41 -0500 | [diff] [blame] | 324 | return True |
| 325 | return False |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 326 | |
Tony Mack | 5ff90fc | 2015-02-08 21:38:41 -0500 | [diff] [blame] | 327 | def can_update_slice(self, slice): |
| 328 | from core.models.slice import SlicePrivilege |
| 329 | if self.can_update_root(): |
| 330 | return True |
| 331 | if self == slice.creator: |
| 332 | return True |
| 333 | if self.can_update_site(slice.site, allow=['pi']): |
| 334 | return True |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 335 | |
Tony Mack | 5ff90fc | 2015-02-08 21:38:41 -0500 | [diff] [blame] | 336 | if SlicePrivilege.objects.filter( |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 337 | slice=slice, user=self, role__role__in=['admin', 'Admin']): |
Tony Mack | 5ff90fc | 2015-02-08 21:38:41 -0500 | [diff] [blame] | 338 | return True |
| 339 | return False |
| 340 | |
Tony Mack | 9d2ea09 | 2015-04-29 12:23:10 -0400 | [diff] [blame] | 341 | def can_update_service(self, service, allow=[]): |
| 342 | from core.models.service import ServicePrivilege |
| 343 | if self.can_update_root(): |
| 344 | return True |
| 345 | if ServicePrivilege.objects.filter( |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 346 | service=service, user=self, role__role__in=['admin', 'Admin'] + allow): |
Tony Mack | 9d2ea09 | 2015-04-29 12:23:10 -0400 | [diff] [blame] | 347 | return True |
Scott Baker | 4587b82 | 2015-07-01 18:29:08 -0700 | [diff] [blame] | 348 | return False |
| 349 | |
Scott Baker | 8100d2a | 2015-07-07 12:13:17 -0700 | [diff] [blame] | 350 | def can_update_tenant_root(self, tenant_root, allow=[]): |
Scott Baker | 7463951 | 2015-07-24 15:34:25 -0700 | [diff] [blame] | 351 | from core.models.service import TenantRoot, TenantRootPrivilege |
Scott Baker | 4587b82 | 2015-07-01 18:29:08 -0700 | [diff] [blame] | 352 | if self.can_update_root(): |
| 353 | return True |
| 354 | if TenantRootPrivilege.objects.filter( |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 355 | tenant_root=tenant_root, user=self, role__role__in=['admin', 'Admin'] + allow): |
Scott Baker | 4587b82 | 2015-07-01 18:29:08 -0700 | [diff] [blame] | 356 | return True |
| 357 | return False |
Tony Mack | 9d2ea09 | 2015-04-29 12:23:10 -0400 | [diff] [blame] | 358 | |
Scott Baker | 335882a | 2015-07-24 10:15:31 -0700 | [diff] [blame] | 359 | def can_update_tenant_root_privilege(self, tenant_root_privilege, allow=[]): |
| 360 | return self.can_update_tenant_root(tenant_root_privilege.tenant_root, allow) |
| 361 | |
Tony Mack | af3aa18 | 2015-08-23 13:16:22 +0000 | [diff] [blame] | 362 | def get_readable_objects(self, filter_by=None): |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 363 | """ Returns a list of objects that the user is allowed to read. """ |
| 364 | from core.models import Deployment, Flavor, Image, Network, NetworkTemplate, Node, PlModelMixIn, Site, Slice, SliceTag, Instance, Tag, User, DeploymentPrivilege, SitePrivilege, SlicePrivilege |
| 365 | models = [] |
| 366 | if filter_by and isinstance(filter_by, list): |
| 367 | models = [m for m in filter_by if issubclass(m, PlModelMixIn)] |
| 368 | if not models: |
| 369 | models = [Deployment, Network, Site, |
| 370 | Slice, SliceTag, Instance, Tag, User] |
| 371 | readable_objects = [] |
| 372 | for model in models: |
| 373 | readable_objects.extend(model.select_by_user(self)) |
| 374 | return readable_objects |
Tony Mack | 8f4ec84 | 2015-07-29 14:45:04 -0400 | [diff] [blame] | 375 | |
Tony Mack | af3aa18 | 2015-08-23 13:16:22 +0000 | [diff] [blame] | 376 | def get_permissions(self, filter_by=None): |
Tony Mack | ef8a9e5 | 2015-08-04 16:41:25 -0400 | [diff] [blame] | 377 | """ Return a list of objects for which the user has read or read/write |
| 378 | access. The object will be an instance of a django model object. |
| 379 | Permissions will be either 'r' or 'rw'. |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 380 | |
Tony Mack | ef8a9e5 | 2015-08-04 16:41:25 -0400 | [diff] [blame] | 381 | e.g. |
| 382 | [{'object': django_object_instance, 'permissions': 'rw'}, ...] |
| 383 | |
| 384 | Returns: |
| 385 | list of dicts |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 386 | |
Tony Mack | ef8a9e5 | 2015-08-04 16:41:25 -0400 | [diff] [blame] | 387 | """ |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 388 | from core.models import Deployment, Flavor, Image, Network, NetworkTemplate, Node, PlModelMixIn, Site, Slice, SliceTag, Instance, Tag, User, DeploymentPrivilege, SitePrivilege, SlicePrivilege |
Tony Mack | ef8a9e5 | 2015-08-04 16:41:25 -0400 | [diff] [blame] | 389 | READ = 'r' |
| 390 | READWRITE = 'rw' |
Tony Mack | af3aa18 | 2015-08-23 13:16:22 +0000 | [diff] [blame] | 391 | models = [] |
| 392 | if filter_by and isinstance(filter_by, list): |
Tony Mack | 6abba5e | 2015-08-23 14:43:29 +0000 | [diff] [blame] | 393 | models = [m for m in filter_by if issubclass(m, PlModelMixIn)] |
Tony Mack | ef8a9e5 | 2015-08-04 16:41:25 -0400 | [diff] [blame] | 394 | |
| 395 | deployment_priv_objs = [Image, NetworkTemplate, Flavor] |
| 396 | site_priv_objs = [Node, Slice, User] |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 397 | slice_priv_objs = [Instance, Network] |
| 398 | |
Tony Mack | ef8a9e5 | 2015-08-04 16:41:25 -0400 | [diff] [blame] | 399 | # maps the set of objects a paticular role has write access |
| 400 | write_map = { |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 401 | DeploymentPrivilege: { |
Tony Mack | ef8a9e5 | 2015-08-04 16:41:25 -0400 | [diff] [blame] | 402 | 'admin': deployment_priv_objects, |
| 403 | }, |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 404 | SitePrivilege: { |
| 405 | 'admin': site_priv_objs, |
| 406 | 'pi': [Slice, User], |
Tony Mack | ef8a9e5 | 2015-08-04 16:41:25 -0400 | [diff] [blame] | 407 | 'tech': [Node], |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 408 | }, |
| 409 | SlicePrivilege: { |
| 410 | 'admin': slice_priv_objs, |
| 411 | }, |
Tony Mack | ef8a9e5 | 2015-08-04 16:41:25 -0400 | [diff] [blame] | 412 | } |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 413 | |
Tony Mack | ef8a9e5 | 2015-08-04 16:41:25 -0400 | [diff] [blame] | 414 | privilege_map = { |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 415 | DeploymentPrivilege: (Deployment, deployment_priv_objs), |
| 416 | SitePrivilege: (Site, site_priv_objs), |
| 417 | SlicePrivilege: (Slice, slice_priv_objs) |
Tony Mack | ef8a9e5 | 2015-08-04 16:41:25 -0400 | [diff] [blame] | 418 | } |
| 419 | permissions = [] |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 420 | permission_dict = lambda x, y: {'object': x, 'permission': y} |
Tony Mack | ef8a9e5 | 2015-08-04 16:41:25 -0400 | [diff] [blame] | 421 | for privilege_model, (model, affected_models) in privileg_map.items(): |
Tony Mack | af3aa18 | 2015-08-23 13:16:22 +0000 | [diff] [blame] | 422 | if models and model not in models: |
| 423 | continue |
| 424 | |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 425 | # get the objects affected by this privilege model |
Tony Mack | ef8a9e5 | 2015-08-04 16:41:25 -0400 | [diff] [blame] | 426 | affected_objects = [] |
| 427 | for affected_model in affected_models: |
| 428 | affected_objects.extend(affected_model.select_by_user(self)) |
| 429 | |
| 430 | if self.is_admin: |
| 431 | # assume admin users have read/write access to all objects |
| 432 | for affected_object in affected_objects: |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 433 | permissions.append(permission_dict( |
| 434 | affected_object, READWRITE)) |
Tony Mack | ef8a9e5 | 2015-08-04 16:41:25 -0400 | [diff] [blame] | 435 | else: |
| 436 | # create a dict of the user's per object privileges |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 437 | # ex: {princeton_tmack : ['admin'] |
Tony Mack | ef8a9e5 | 2015-08-04 16:41:25 -0400 | [diff] [blame] | 438 | privileges = privilege_model.objects.filter(user=self) |
| 439 | for privilege in privileges: |
| 440 | object_roles = defaultdict(list) |
| 441 | obj = None |
| 442 | roles = [] |
| 443 | for field in dir(privilege): |
| 444 | if field == model.__name__.lower(): |
| 445 | obj = getattr(privilege, field) |
| 446 | if obj: |
| 447 | object_roles[obj].append(privilege.role.role) |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 448 | |
Tony Mack | ef8a9e5 | 2015-08-04 16:41:25 -0400 | [diff] [blame] | 449 | # loop through all objects the user has access to and determine |
| 450 | # if they also have write access |
| 451 | for affected_object in affected_objects: |
| 452 | if affected_object not in objects_roles: |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 453 | permissions.append( |
| 454 | permission_dict(affected_object, READ)) |
Tony Mack | ef8a9e5 | 2015-08-04 16:41:25 -0400 | [diff] [blame] | 455 | else: |
| 456 | has_write_permission = False |
| 457 | for write_role, models in write_dict.items(): |
| 458 | if affected_object._meta.model in models and \ |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 459 | write_role in object_roles[affected_object]: |
| 460 | has_write_permission = True |
| 461 | break |
Tony Mack | ef8a9e5 | 2015-08-04 16:41:25 -0400 | [diff] [blame] | 462 | if has_write_permission: |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 463 | permissions.append( |
| 464 | permission_dict(affected_object, WRITE)) |
Tony Mack | ef8a9e5 | 2015-08-04 16:41:25 -0400 | [diff] [blame] | 465 | else: |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 466 | permissions.append( |
| 467 | permission_dict(affected_object, READ)) |
| 468 | |
| 469 | return permissions |
Tony Mack | af3aa18 | 2015-08-23 13:16:22 +0000 | [diff] [blame] | 470 | |
| 471 | def get_tenant_permissions(self): |
| 472 | from core.models import Site, Slice |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 473 | return self.get_object_permissions(filter_by=[Site, Slice]) |
Tony Mack | af3aa18 | 2015-08-23 13:16:22 +0000 | [diff] [blame] | 474 | |
Tony Mack | 5b06147 | 2014-02-04 07:57:10 -0500 | [diff] [blame] | 475 | @staticmethod |
| 476 | def select_by_user(user): |
| 477 | if user.is_admin: |
| 478 | qs = User.objects.all() |
| 479 | else: |
| 480 | # can see all users at any site where this user has pi role |
| 481 | from core.models.site import SitePrivilege |
| 482 | site_privs = SitePrivilege.objects.filter(user=user) |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 483 | sites = [sp.site for sp in site_privs if sp.role.role in [ |
| 484 | 'Admin', 'admin', 'pi']] |
Tony Mack | 5b06147 | 2014-02-04 07:57:10 -0500 | [diff] [blame] | 485 | # get site privs of users at these sites |
| 486 | site_privs = SitePrivilege.objects.filter(site__in=sites) |
Scott Baker | a36d77e | 2014-08-29 11:43:23 -0700 | [diff] [blame] | 487 | user_ids = [sp.user.id for sp in site_privs] + [user.id] |
Tony Mack | 5b06147 | 2014-02-04 07:57:10 -0500 | [diff] [blame] | 488 | qs = User.objects.filter(Q(site__in=sites) | Q(id__in=user_ids)) |
Scott Baker | a36d77e | 2014-08-29 11:43:23 -0700 | [diff] [blame] | 489 | return qs |
Tony Mack | 5b06147 | 2014-02-04 07:57:10 -0500 | [diff] [blame] | 490 | |
Scott Baker | cbfb600 | 2014-10-03 00:32:37 -0700 | [diff] [blame] | 491 | def save_by_user(self, user, *args, **kwds): |
| 492 | if not self.can_update(user): |
Scott Baker | 0119c15 | 2014-10-06 22:58:48 -0700 | [diff] [blame] | 493 | if getattr(self, "_cant_update_fieldName", None) is not None: |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 494 | raise PermissionDenied("You do not have permission to update field %s on object %s" % ( |
| 495 | self._cant_update_fieldName, self.__class__.__name__)) |
Scott Baker | 0119c15 | 2014-10-06 22:58:48 -0700 | [diff] [blame] | 496 | else: |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 497 | raise PermissionDenied( |
| 498 | "You do not have permission to update %s objects" % self.__class__.__name__) |
Scott Baker | cbfb600 | 2014-10-03 00:32:37 -0700 | [diff] [blame] | 499 | |
| 500 | self.save(*args, **kwds) |
| 501 | |
| 502 | def delete_by_user(self, user, *args, **kwds): |
| 503 | if not self.can_update(user): |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 504 | raise PermissionDenied( |
| 505 | "You do not have permission to delete %s objects" % self.__class__.__name__) |
Scott Baker | cbfb600 | 2014-10-03 00:32:37 -0700 | [diff] [blame] | 506 | self.delete(*args, **kwds) |
| 507 | |
Scott Baker | 811d447 | 2015-05-19 16:39:48 -0700 | [diff] [blame] | 508 | def apply_profile(self, profile): |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 509 | if profile == "regular": |
Scott Baker | 811d447 | 2015-05-19 16:39:48 -0700 | [diff] [blame] | 510 | self.is_appuser = False |
| 511 | self.is_admin = False |
| 512 | |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 513 | elif profile == "cp": |
Scott Baker | 811d447 | 2015-05-19 16:39:48 -0700 | [diff] [blame] | 514 | self.is_appuser = True |
| 515 | self.is_admin = False |
| 516 | for db in self.userdashboardviews.all(): |
| 517 | db.delete() |
| 518 | |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 519 | |
Scott Baker | 2c3cb64 | 2014-05-19 17:55:56 -0700 | [diff] [blame] | 520 | class UserDashboardView(PlCoreBase): |
Jeremy Mowery | 11ed899 | 2016-04-17 20:56:54 -0700 | [diff] [blame] | 521 | user = models.ForeignKey(User, related_name='userdashboardviews') |
| 522 | dashboardView = models.ForeignKey( |
| 523 | DashboardView, related_name='userdashboardviews') |
| 524 | order = models.IntegerField(default=0) |