blob: f1d73d2af5a2bbbfb6fe45cc827f2b4878cf1d21 [file] [log] [blame]
Siobhan Tully53437282013-04-26 19:30:27 -04001import datetime
Tony Mack2f5be422015-01-03 14:26:15 -05002import hashlib
Jeremy Mowery11ed8992016-04-17 20:56:54 -07003import os
4import sys
Tony Mackc14de8f2013-05-09 21:44:17 -04005from collections import defaultdict
Jeremy Mowery11ed8992016-04-17 20:56:54 -07006from operator import attrgetter, itemgetter
7
8import synchronizers.model_policy
9from core.middleware import get_request
10from core.models import DashboardView, PlCoreBase, PlModelMixIn, Site
11from core.models.plcorebase import StrippedCharField
12from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
13from django.core.exceptions import PermissionDenied
14from django.core.mail import EmailMultiAlternatives
Siobhan Tully53437282013-04-26 19:30:27 -040015from django.db import models
Tony Mack5b061472014-02-04 07:57:10 -050016from django.db.models import F, Q
Jeremy Mowery11ed8992016-04-17 20:56:54 -070017from django.forms.models import model_to_dict
Scott Bakerdaca8162015-02-02 14:28:35 -080018from django.utils import timezone
Scott Baker9266e6b2013-05-19 15:54:48 -070019from timezones.fields import TimeZoneField
Siobhan Tully53437282013-04-26 19:30:27 -040020
Scott Bakerfd8c7c42014-10-09 16:16:02 -070021# ------ from plcorebase.py ------
22try:
23 # This is a no-op if observer_disabled is set to 1 in the config file
Sapan Bhatia003e84c2016-01-15 11:05:52 -050024 from synchronizers.base import *
Scott Bakerfd8c7c42014-10-09 16:16:02 -070025except:
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 Tully53437282013-04-26 19:30:27 -040035# Create your models here.
Jeremy Mowery11ed8992016-04-17 20:56:54 -070036
37
Siobhan Tully30fd4292013-05-10 08:59:56 -040038class UserManager(BaseUserManager):
Jeremy Mowery11ed8992016-04-17 20:56:54 -070039
Siobhan Tully53437282013-04-26 19:30:27 -040040 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 Tully30fd4292013-05-10 08:59:56 -040049 email=UserManager.normalize_email(email),
Siobhan Tully53437282013-04-26 19:30:27 -040050 firstname=firstname,
Siobhan Tully30fd4292013-05-10 08:59:56 -040051 lastname=lastname,
52 password=password
Siobhan Tully53437282013-04-26 19:30:27 -040053 )
Jeremy Mowery11ed8992016-04-17 20:56:54 -070054 # user.set_password(password)
Siobhan Tully53437282013-04-26 19:30:27 -040055 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 Mowery11ed8992016-04-17 20:56:54 -070065 password=password,
66 firstname=firstname,
67 lastname=lastname
68 )
Siobhan Tully53437282013-04-26 19:30:27 -040069 user.is_admin = True
70 user.save(using=self._db)
71 return user
72
Scott Baker6c684842014-10-17 18:45:00 -070073 def get_queryset(self):
Jeremy Mowery11ed8992016-04-17 20:56:54 -070074 parent = super(UserManager, self)
Scott Baker6c684842014-10-17 18:45:00 -070075 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 Mowery11ed8992016-04-17 20:56:54 -070084
Sapan Bhatia5d605ff2014-07-21 20:08:04 -040085class DeletedUserManager(UserManager):
Jeremy Mowery11ed8992016-04-17 20:56:54 -070086
Scott Bakerb08d6562014-09-12 12:57:27 -070087 def get_queryset(self):
Sapan Bhatia5d605ff2014-07-21 20:08:04 -040088 return super(UserManager, self).get_query_set().filter(deleted=True)
Siobhan Tully53437282013-04-26 19:30:27 -040089
Scott Bakerb08d6562014-09-12 12:57:27 -070090 # deprecated in django 1.7 in favor of get_queryset()
91 def get_query_set(self):
92 return self.get_queryset()
93
Jeremy Mowery11ed8992016-04-17 20:56:54 -070094
Scott Baker12113342015-02-10 15:44:30 -080095class User(AbstractBaseUser, PlModelMixIn):
Jeremy Mowery11ed8992016-04-17 20:56:54 -070096
Tony Mack2f5be422015-01-03 14:26:15 -050097 @property
98 def remote_password(self):
99 return hashlib.md5(self.password).hexdigest()[:12]
100
Siobhan Tully53437282013-04-26 19:30:27 -0400101 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 Tullyfece0d52013-09-06 12:57:05 -0400110
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700111 username = StrippedCharField(max_length=255, default="Something")
Siobhan Tullyfece0d52013-09-06 12:57:05 -0400112
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700113 firstname = StrippedCharField(
114 help_text="person's given name", max_length=200)
Tony Mackd84b1ff2015-03-09 13:03:56 -0400115 lastname = StrippedCharField(help_text="person's surname", max_length=200)
Siobhan Tully53437282013-04-26 19:30:27 -0400116
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700117 phone = StrippedCharField(null=True, blank=True,
118 help_text="phone number contact", max_length=100)
Siobhan Tully53437282013-04-26 19:30:27 -0400119 user_url = models.URLField(null=True, blank=True)
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700120 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 Tully53437282013-04-26 19:30:27 -0400124
125 is_active = models.BooleanField(default=True)
Tony Mack4bfcdc82015-01-28 12:03:39 -0500126 is_admin = models.BooleanField(default=False)
Siobhan Tully53437282013-04-26 19:30:27 -0400127 is_staff = models.BooleanField(default=True)
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500128 is_readonly = models.BooleanField(default=False)
Scott Baker28e2e3a2015-01-28 16:03:40 -0800129 is_registering = models.BooleanField(default=False)
Scott Baker74ebc7a2015-05-15 09:19:36 -0700130 is_appuser = models.BooleanField(default=False)
Siobhan Tully53437282013-04-26 19:30:27 -0400131
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700132 login_page = StrippedCharField(
133 help_text="send this user to a specific page on login", max_length=200, null=True, blank=True)
Scott Bakere61e3a02015-06-10 16:14:58 -0700134
Tony Mack0553f282013-06-10 22:54:50 -0400135 created = models.DateTimeField(auto_now_add=True)
136 updated = models.DateTimeField(auto_now=True)
137 enacted = models.DateTimeField(null=True, default=None)
Sapan Bhatia103465d2015-01-23 16:02:09 +0000138 policed = models.DateTimeField(null=True, default=None)
Tony Mackd84b1ff2015-03-09 13:03:56 -0400139 backend_status = StrippedCharField(max_length=1024,
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700140 default="Provisioning in progress")
Sapan Bhatiabcc18992014-04-29 10:32:14 -0400141 deleted = models.BooleanField(default=False)
Scott Baker690447e2015-03-10 12:14:25 -0700142 write_protect = models.BooleanField(default=False)
Scott Bakerced9e4f2016-04-14 23:41:07 -0700143 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 Mack0553f282013-06-10 22:54:50 -0400146
Scott Baker9266e6b2013-05-19 15:54:48 -0700147 timezone = TimeZoneField()
148
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700149 dashboards = models.ManyToManyField(
150 'DashboardView', through='UserDashboardView', blank=True)
Scott Baker2c3cb642014-05-19 17:55:56 -0700151
Siobhan Tully30fd4292013-05-10 08:59:56 -0400152 objects = UserManager()
Sapan Bhatia5d605ff2014-07-21 20:08:04 -0400153 deleted_objects = DeletedUserManager()
Siobhan Tully53437282013-04-26 19:30:27 -0400154
155 USERNAME_FIELD = 'email'
156 REQUIRED_FIELDS = ['firstname', 'lastname']
157
Scott Baker0119c152014-10-06 22:58:48 -0700158 PI_FORBIDDEN_FIELDS = ["is_admin", "site", "is_staff"]
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700159 USER_FORBIDDEN_FIELDS = ["is_admin", "is_active",
160 "site", "is_staff", "is_readonly"]
Scott Baker0119c152014-10-06 22:58:48 -0700161
Scott Bakercbfb6002014-10-03 00:32:37 -0700162 def __init__(self, *args, **kwargs):
163 super(User, self).__init__(*args, **kwargs)
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700164 self._initial = self._dict # for PlModelMixIn
Scott Bakercbfb6002014-10-03 00:32:37 -0700165
Siobhan Tullycf04fb62014-01-11 11:25:57 -0500166 def isReadOnlyUser(self):
167 return self.is_readonly
168
Siobhan Tully53437282013-04-26 19:30:27 -0400169 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 Bhatia5d605ff2014-07-21 20:08:04 -0400177 def delete(self, *args, **kwds):
178 # so we have something to give the observer
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700179 purge = kwds.get('purge', False)
Scott Bakerc5b50602014-10-09 16:22:00 -0700180 if purge:
181 del kwds['purge']
Sapan Bhatia5d605ff2014-07-21 20:08:04 -0400182 try:
183 purge = purge or observer_disabled
184 except NameError:
185 pass
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700186
Sapan Bhatia5d605ff2014-07-21 20:08:04 -0400187 if (purge):
188 super(User, self).delete(*args, **kwds)
189 else:
Scott Baker690447e2015-03-10 12:14:25 -0700190 if (not self.write_protect):
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700191 self.deleted = True
192 self.enacted = None
193 self.save(update_fields=['enacted', 'deleted'])
Sapan Bhatia5d605ff2014-07-21 20:08:04 -0400194
Tony Mackb0d97422013-06-10 09:57:45 -0400195 @property
196 def keyname(self):
197 return self.email[:self.email.find('@')]
198
Siobhan Tully53437282013-04-26 19:30:27 -0400199 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 Tullybfd11dc2013-09-03 12:59:24 -0400212 def is_superuser(self):
213 return False
Siobhan Tully53437282013-04-26 19:30:27 -0400214
Scott Baker2c3cb642014-05-19 17:55:56 -0700215 def get_dashboards(self):
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700216 DEFAULT_DASHBOARDS = ["Tenant"]
Scott Baker2c3cb642014-05-19 17:55:56 -0700217
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700218 dashboards = sorted(
219 list(self.userdashboardviews.all()), key=attrgetter('order'))
Scott Baker2c3cb642014-05-19 17:55:56 -0700220 dashboards = [x.dashboardView for x in dashboards]
221
Scott Baker811d4472015-05-19 16:39:48 -0700222 if (not dashboards) and (not self.is_appuser):
Scott Baker2c3cb642014-05-19 17:55:56 -0700223 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 Tullybfd11dc2013-09-03 12:59:24 -0400230# 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 Mowery11ed8992016-04-17 20:56:54 -0700241# return roles
Siobhan Tully53437282013-04-26 19:30:27 -0400242
Tony Mack53106f32013-04-27 16:43:01 -0400243 def save(self, *args, **kwds):
Siobhan Tully30fd4292013-05-10 08:59:56 -0400244 if not self.id:
Scott Bakera36d77e2014-08-29 11:43:23 -0700245 self.set_password(self.password)
Scott Baker28e2e3a2015-01-28 16:03:40 -0800246 if self.is_active and self.is_registering:
S.Çağlar Onur0e591832015-02-24 17:28:09 -0500247 self.send_temporary_password()
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700248 self.is_registering = False
S.Çağlar Onur0e591832015-02-24 17:28:09 -0500249
Siobhan Tullyfece0d52013-09-06 12:57:05 -0400250 self.username = self.email
Scott Bakera36d77e2014-08-29 11:43:23 -0700251 super(User, self).save(*args, **kwds)
252
Scott Bakercbfb6002014-10-03 00:32:37 -0700253 self._initial = self._dict
254
Scott Bakera36d77e2014-08-29 11:43:23 -0700255 def send_temporary_password(self):
256 password = User.objects.make_random_password()
S.Çağlar Onur0e591832015-02-24 17:28:09 -0500257 self.set_password(password)
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700258 subject, from_email, to = 'OpenCloud Account Credentials', 'support@opencloud.us', str(
259 self.email)
S.Çağlar Onur0e591832015-02-24 17:28:09 -0500260 text_content = 'This is an important message.'
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700261 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 Onur0e591832015-02-24 17:28:09 -0500266 msg.attach_alternative(html_content, "text/html")
Scott Bakera36d77e2014-08-29 11:43:23 -0700267 msg.send()
Tony Mack5b061472014-02-04 07:57:10 -0500268
Scott Bakercbfb6002014-10-03 00:32:37 -0700269 def can_update(self, user):
270 from core.models import SitePrivilege
Scott Baker0119c152014-10-06 22:58:48 -0700271 _cant_update_fieldName = None
Tony Mack2a56ce52015-02-09 12:16:03 -0500272 if user.can_update_root():
Scott Bakercbfb6002014-10-03 00:32:37 -0700273 return True
Tony Mack2a56ce52015-02-09 12:16:03 -0500274
Scott Bakercbfb6002014-10-03 00:32:37 -0700275 # site pis can update
276 site_privs = SitePrivilege.objects.filter(user=user, site=self.site)
277 for site_priv in site_privs:
Tony Mack2a56ce52015-02-09 12:16:03 -0500278 if site_priv.role.role == 'admin':
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700279 return True
Scott Bakercbfb6002014-10-03 00:32:37 -0700280 if site_priv.role.role == 'pi':
Scott Baker0119c152014-10-06 22:58:48 -0700281 for fieldName in self.diff.keys():
282 if fieldName in self.PI_FORBIDDEN_FIELDS:
283 _cant_update_fieldName = fieldName
284 return False
Scott Bakercbfb6002014-10-03 00:32:37 -0700285 return True
Scott Baker0119c152014-10-06 22:58:48 -0700286 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 Bakercbfb6002014-10-03 00:32:37 -0700292
293 return False
294
Tony Mack5ff90fc2015-02-08 21:38:41 -0500295 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 Mowery11ed8992016-04-17 20:56:54 -0700304 return False
Tony Mack5ff90fc2015-02-08 21:38:41 -0500305
306 def can_update_deployment(self, deployment):
307 from core.models.site import DeploymentPrivilege
308 if self.can_update_root():
Tony Mack5ff90fc2015-02-08 21:38:41 -0500309 return True
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700310
311 if DeploymentPrivilege.objects.filter(
312 deployment=deployment,
313 user=self,
314 role__role__in=['admin', 'Admin']):
315 return True
316 return False
Tony Mack5ff90fc2015-02-08 21:38:41 -0500317
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 Mowery11ed8992016-04-17 20:56:54 -0700323 site=site, user=self, role__role__in=['admin', 'Admin'] + allow):
Tony Mack5ff90fc2015-02-08 21:38:41 -0500324 return True
325 return False
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700326
Tony Mack5ff90fc2015-02-08 21:38:41 -0500327 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 Mowery11ed8992016-04-17 20:56:54 -0700335
Tony Mack5ff90fc2015-02-08 21:38:41 -0500336 if SlicePrivilege.objects.filter(
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700337 slice=slice, user=self, role__role__in=['admin', 'Admin']):
Tony Mack5ff90fc2015-02-08 21:38:41 -0500338 return True
339 return False
340
Tony Mack9d2ea092015-04-29 12:23:10 -0400341 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 Mowery11ed8992016-04-17 20:56:54 -0700346 service=service, user=self, role__role__in=['admin', 'Admin'] + allow):
Tony Mack9d2ea092015-04-29 12:23:10 -0400347 return True
Scott Baker4587b822015-07-01 18:29:08 -0700348 return False
349
Scott Baker8100d2a2015-07-07 12:13:17 -0700350 def can_update_tenant_root(self, tenant_root, allow=[]):
Scott Baker74639512015-07-24 15:34:25 -0700351 from core.models.service import TenantRoot, TenantRootPrivilege
Scott Baker4587b822015-07-01 18:29:08 -0700352 if self.can_update_root():
353 return True
354 if TenantRootPrivilege.objects.filter(
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700355 tenant_root=tenant_root, user=self, role__role__in=['admin', 'Admin'] + allow):
Scott Baker4587b822015-07-01 18:29:08 -0700356 return True
357 return False
Tony Mack9d2ea092015-04-29 12:23:10 -0400358
Scott Baker335882a2015-07-24 10:15:31 -0700359 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 Mackaf3aa182015-08-23 13:16:22 +0000362 def get_readable_objects(self, filter_by=None):
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700363 """ 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 Mack8f4ec842015-07-29 14:45:04 -0400375
Tony Mackaf3aa182015-08-23 13:16:22 +0000376 def get_permissions(self, filter_by=None):
Tony Mackef8a9e52015-08-04 16:41:25 -0400377 """ 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 Mowery11ed8992016-04-17 20:56:54 -0700380
Tony Mackef8a9e52015-08-04 16:41:25 -0400381 e.g.
382 [{'object': django_object_instance, 'permissions': 'rw'}, ...]
383
384 Returns:
385 list of dicts
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700386
Tony Mackef8a9e52015-08-04 16:41:25 -0400387 """
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700388 from core.models import Deployment, Flavor, Image, Network, NetworkTemplate, Node, PlModelMixIn, Site, Slice, SliceTag, Instance, Tag, User, DeploymentPrivilege, SitePrivilege, SlicePrivilege
Tony Mackef8a9e52015-08-04 16:41:25 -0400389 READ = 'r'
390 READWRITE = 'rw'
Tony Mackaf3aa182015-08-23 13:16:22 +0000391 models = []
392 if filter_by and isinstance(filter_by, list):
Tony Mack6abba5e2015-08-23 14:43:29 +0000393 models = [m for m in filter_by if issubclass(m, PlModelMixIn)]
Tony Mackef8a9e52015-08-04 16:41:25 -0400394
395 deployment_priv_objs = [Image, NetworkTemplate, Flavor]
396 site_priv_objs = [Node, Slice, User]
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700397 slice_priv_objs = [Instance, Network]
398
Tony Mackef8a9e52015-08-04 16:41:25 -0400399 # maps the set of objects a paticular role has write access
400 write_map = {
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700401 DeploymentPrivilege: {
Tony Mackef8a9e52015-08-04 16:41:25 -0400402 'admin': deployment_priv_objects,
403 },
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700404 SitePrivilege: {
405 'admin': site_priv_objs,
406 'pi': [Slice, User],
Tony Mackef8a9e52015-08-04 16:41:25 -0400407 'tech': [Node],
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700408 },
409 SlicePrivilege: {
410 'admin': slice_priv_objs,
411 },
Tony Mackef8a9e52015-08-04 16:41:25 -0400412 }
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700413
Tony Mackef8a9e52015-08-04 16:41:25 -0400414 privilege_map = {
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700415 DeploymentPrivilege: (Deployment, deployment_priv_objs),
416 SitePrivilege: (Site, site_priv_objs),
417 SlicePrivilege: (Slice, slice_priv_objs)
Tony Mackef8a9e52015-08-04 16:41:25 -0400418 }
419 permissions = []
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700420 permission_dict = lambda x, y: {'object': x, 'permission': y}
Tony Mackef8a9e52015-08-04 16:41:25 -0400421 for privilege_model, (model, affected_models) in privileg_map.items():
Tony Mackaf3aa182015-08-23 13:16:22 +0000422 if models and model not in models:
423 continue
424
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700425 # get the objects affected by this privilege model
Tony Mackef8a9e52015-08-04 16:41:25 -0400426 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 Mowery11ed8992016-04-17 20:56:54 -0700433 permissions.append(permission_dict(
434 affected_object, READWRITE))
Tony Mackef8a9e52015-08-04 16:41:25 -0400435 else:
436 # create a dict of the user's per object privileges
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700437 # ex: {princeton_tmack : ['admin']
Tony Mackef8a9e52015-08-04 16:41:25 -0400438 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 Mowery11ed8992016-04-17 20:56:54 -0700448
Tony Mackef8a9e52015-08-04 16:41:25 -0400449 # 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 Mowery11ed8992016-04-17 20:56:54 -0700453 permissions.append(
454 permission_dict(affected_object, READ))
Tony Mackef8a9e52015-08-04 16:41:25 -0400455 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 Mowery11ed8992016-04-17 20:56:54 -0700459 write_role in object_roles[affected_object]:
460 has_write_permission = True
461 break
Tony Mackef8a9e52015-08-04 16:41:25 -0400462 if has_write_permission:
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700463 permissions.append(
464 permission_dict(affected_object, WRITE))
Tony Mackef8a9e52015-08-04 16:41:25 -0400465 else:
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700466 permissions.append(
467 permission_dict(affected_object, READ))
468
469 return permissions
Tony Mackaf3aa182015-08-23 13:16:22 +0000470
471 def get_tenant_permissions(self):
472 from core.models import Site, Slice
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700473 return self.get_object_permissions(filter_by=[Site, Slice])
Tony Mackaf3aa182015-08-23 13:16:22 +0000474
Tony Mack5b061472014-02-04 07:57:10 -0500475 @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 Mowery11ed8992016-04-17 20:56:54 -0700483 sites = [sp.site for sp in site_privs if sp.role.role in [
484 'Admin', 'admin', 'pi']]
Tony Mack5b061472014-02-04 07:57:10 -0500485 # get site privs of users at these sites
486 site_privs = SitePrivilege.objects.filter(site__in=sites)
Scott Bakera36d77e2014-08-29 11:43:23 -0700487 user_ids = [sp.user.id for sp in site_privs] + [user.id]
Tony Mack5b061472014-02-04 07:57:10 -0500488 qs = User.objects.filter(Q(site__in=sites) | Q(id__in=user_ids))
Scott Bakera36d77e2014-08-29 11:43:23 -0700489 return qs
Tony Mack5b061472014-02-04 07:57:10 -0500490
Scott Bakercbfb6002014-10-03 00:32:37 -0700491 def save_by_user(self, user, *args, **kwds):
492 if not self.can_update(user):
Scott Baker0119c152014-10-06 22:58:48 -0700493 if getattr(self, "_cant_update_fieldName", None) is not None:
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700494 raise PermissionDenied("You do not have permission to update field %s on object %s" % (
495 self._cant_update_fieldName, self.__class__.__name__))
Scott Baker0119c152014-10-06 22:58:48 -0700496 else:
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700497 raise PermissionDenied(
498 "You do not have permission to update %s objects" % self.__class__.__name__)
Scott Bakercbfb6002014-10-03 00:32:37 -0700499
500 self.save(*args, **kwds)
501
502 def delete_by_user(self, user, *args, **kwds):
503 if not self.can_update(user):
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700504 raise PermissionDenied(
505 "You do not have permission to delete %s objects" % self.__class__.__name__)
Scott Bakercbfb6002014-10-03 00:32:37 -0700506 self.delete(*args, **kwds)
507
Scott Baker811d4472015-05-19 16:39:48 -0700508 def apply_profile(self, profile):
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700509 if profile == "regular":
Scott Baker811d4472015-05-19 16:39:48 -0700510 self.is_appuser = False
511 self.is_admin = False
512
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700513 elif profile == "cp":
Scott Baker811d4472015-05-19 16:39:48 -0700514 self.is_appuser = True
515 self.is_admin = False
516 for db in self.userdashboardviews.all():
517 db.delete()
518
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700519
Scott Baker2c3cb642014-05-19 17:55:56 -0700520class UserDashboardView(PlCoreBase):
Jeremy Mowery11ed8992016-04-17 20:56:54 -0700521 user = models.ForeignKey(User, related_name='userdashboardviews')
522 dashboardView = models.ForeignKey(
523 DashboardView, related_name='userdashboardviews')
524 order = models.IntegerField(default=0)