blob: 715c67076f43dd46d03276e1b30280c7696b8b4f [file] [log] [blame]
Siobhan Tully53437282013-04-26 19:30:27 -04001import datetime
Tony Mack2f5be422015-01-03 14:26:15 -05002import hashlib
Jeremy Mowery36fb9e52016-04-05 23:32:10 -07003import os
4import sys
Tony Mackc14de8f2013-05-09 21:44:17 -04005from collections import defaultdict
Jeremy Mowery36fb9e52016-04-05 23:32:10 -07006from operator import attrgetter, itemgetter
7
Jeremy Moweryc2fcd002016-04-19 10:16:19 -07008import synchronizers.model_policy
9from core.middleware import get_request
10from core.models import DashboardView, PlCoreBase, PlModelMixIn, Site
11from core.models.plcorebase import StrippedCharField
Jeremy Mowery36fb9e52016-04-05 23:32:10 -070012from 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 Mowery36fb9e52016-04-05 23:32:10 -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 Mowery95635132016-04-15 17:39:49 -070036
37
Siobhan Tully30fd4292013-05-10 08:59:56 -040038class UserManager(BaseUserManager):
Jeremy Mowery95635132016-04-15 17:39:49 -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 Mowery95635132016-04-15 17:39:49 -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 Mowery95635132016-04-15 17:39:49 -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 Mowery95635132016-04-15 17:39:49 -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 Mowery95635132016-04-15 17:39:49 -070084
Sapan Bhatia5d605ff2014-07-21 20:08:04 -040085class DeletedUserManager(UserManager):
Jeremy Mowery95635132016-04-15 17:39:49 -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 Mowery95635132016-04-15 17:39:49 -070094
Scott Baker12113342015-02-10 15:44:30 -080095class User(AbstractBaseUser, PlModelMixIn):
Jeremy Mowery95635132016-04-15 17:39:49 -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 Mowery95635132016-04-15 17:39:49 -0700111 username = StrippedCharField(max_length=255, default="Something")
Siobhan Tullyfece0d52013-09-06 12:57:05 -0400112
Jeremy Mowery95635132016-04-15 17:39:49 -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 Mowery95635132016-04-15 17:39:49 -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 Mowery95635132016-04-15 17:39:49 -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 Mowery95635132016-04-15 17:39:49 -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 Mowery95635132016-04-15 17:39:49 -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 Mowery95635132016-04-15 17:39:49 -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 Mowery95635132016-04-15 17:39:49 -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 Mowery95635132016-04-15 17:39:49 -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 Mowery95635132016-04-15 17:39:49 -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 Moweryb31bd9e2016-03-14 23:59:11 -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 Mowery95635132016-04-15 17:39:49 -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 Mowery95635132016-04-15 17:39:49 -0700216 DEFAULT_DASHBOARDS = ["Tenant"]
Scott Baker2c3cb642014-05-19 17:55:56 -0700217
Jeremy Mowery95635132016-04-15 17:39:49 -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 Moweryb31bd9e2016-03-14 23:59:11 -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 Mowery95635132016-04-15 17:39:49 -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 Mowery95635132016-04-15 17:39:49 -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 Mowery95635132016-04-15 17:39:49 -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 Moweryb31bd9e2016-03-14 23:59:11 -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 """
Jeremy Mowerye714dd52016-04-19 10:24:49 -0700297 Return True if user has root (global) write access.
Tony Mack5ff90fc2015-02-08 21:38:41 -0500298 """
299 if self.is_readonly:
300 return False
301 if self.is_admin:
302 return True
303
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -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():
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -0700309 return True
310
Tony Mack5ff90fc2015-02-08 21:38:41 -0500311 if DeploymentPrivilege.objects.filter(
Jeremy Mowery95635132016-04-15 17:39:49 -0700312 deployment=deployment,
313 user=self,
314 role__role__in=['admin', 'Admin']):
Tony Mack5ff90fc2015-02-08 21:38:41 -0500315 return True
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -0700316 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 Mowery95635132016-04-15 17:39:49 -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 Moweryb31bd9e2016-03-14 23:59:11 -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 Moweryb31bd9e2016-03-14 23:59:11 -0700335
Tony Mack5ff90fc2015-02-08 21:38:41 -0500336 if SlicePrivilege.objects.filter(
Jeremy Mowery95635132016-04-15 17:39:49 -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 Mowery95635132016-04-15 17:39:49 -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 Mowery95635132016-04-15 17:39:49 -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
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -0700359 def can_update_tenant(self, tenant, allow=[]):
360 from core.models.service import Tenant, TenantPrivilege
361 if self.can_update_root():
362 return True
363 if TenantPrivilege.objects.filter(
Jeremy Mowery95635132016-04-15 17:39:49 -0700364 tenant=tenant, user=self, role__role__in=['admin', 'Admin'] + allow):
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -0700365 return True
366 return False
367
Scott Baker335882a2015-07-24 10:15:31 -0700368 def can_update_tenant_root_privilege(self, tenant_root_privilege, allow=[]):
369 return self.can_update_tenant_root(tenant_root_privilege.tenant_root, allow)
370
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -0700371 def can_update_tenant_privilege(self, tenant_privilege, allow=[]):
372 return self.can_update_tenant(tenant_privilege.tenant, allow)
373
Tony Mackaf3aa182015-08-23 13:16:22 +0000374 def get_readable_objects(self, filter_by=None):
Jeremy Mowery95635132016-04-15 17:39:49 -0700375 """ Returns a list of objects that the user is allowed to read. """
376 from core.models import Deployment, Flavor, Image, Network, NetworkTemplate, Node, PlModelMixIn, Site, Slice, SliceTag, Instance, Tag, User, DeploymentPrivilege, SitePrivilege, SlicePrivilege
377 models = []
378 if filter_by and isinstance(filter_by, list):
379 models = [m for m in filter_by if issubclass(m, PlModelMixIn)]
380 if not models:
381 models = [Deployment, Network, Site,
382 Slice, SliceTag, Instance, Tag, User]
383 readable_objects = []
384 for model in models:
385 readable_objects.extend(model.select_by_user(self))
386 return readable_objects
Tony Mack8f4ec842015-07-29 14:45:04 -0400387
Tony Mackaf3aa182015-08-23 13:16:22 +0000388 def get_permissions(self, filter_by=None):
Jeremy Mowerycf7493c2016-04-19 10:23:29 -0700389 """ Return a list of objects for which the user has read or read/write
Jeremy Mowerye714dd52016-04-19 10:24:49 -0700390 access. The object will be an instance of a django model object.
Tony Mackef8a9e52015-08-04 16:41:25 -0400391 Permissions will be either 'r' or 'rw'.
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -0700392
Tony Mackef8a9e52015-08-04 16:41:25 -0400393 e.g.
394 [{'object': django_object_instance, 'permissions': 'rw'}, ...]
395
396 Returns:
Jeremy Mowerycf7493c2016-04-19 10:23:29 -0700397 list of dicts
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -0700398
Tony Mackef8a9e52015-08-04 16:41:25 -0400399 """
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -0700400 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 -0400401 READ = 'r'
402 READWRITE = 'rw'
Tony Mackaf3aa182015-08-23 13:16:22 +0000403 models = []
404 if filter_by and isinstance(filter_by, list):
Tony Mack6abba5e2015-08-23 14:43:29 +0000405 models = [m for m in filter_by if issubclass(m, PlModelMixIn)]
Tony Mackef8a9e52015-08-04 16:41:25 -0400406
407 deployment_priv_objs = [Image, NetworkTemplate, Flavor]
408 site_priv_objs = [Node, Slice, User]
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -0700409 slice_priv_objs = [Instance, Network]
410
Tony Mackef8a9e52015-08-04 16:41:25 -0400411 # maps the set of objects a paticular role has write access
412 write_map = {
Jeremy Mowery95635132016-04-15 17:39:49 -0700413 DeploymentPrivilege: {
Tony Mackef8a9e52015-08-04 16:41:25 -0400414 'admin': deployment_priv_objects,
415 },
Jeremy Mowery95635132016-04-15 17:39:49 -0700416 SitePrivilege: {
417 'admin': site_priv_objs,
418 'pi': [Slice, User],
Tony Mackef8a9e52015-08-04 16:41:25 -0400419 'tech': [Node],
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -0700420 },
Jeremy Mowery95635132016-04-15 17:39:49 -0700421 SlicePrivilege: {
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -0700422 'admin': slice_priv_objs,
423 },
Tony Mackef8a9e52015-08-04 16:41:25 -0400424 }
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -0700425
Tony Mackef8a9e52015-08-04 16:41:25 -0400426 privilege_map = {
Jeremy Mowery95635132016-04-15 17:39:49 -0700427 DeploymentPrivilege: (Deployment, deployment_priv_objs),
428 SitePrivilege: (Site, site_priv_objs),
429 SlicePrivilege: (Slice, slice_priv_objs)
Tony Mackef8a9e52015-08-04 16:41:25 -0400430 }
431 permissions = []
Jeremy Mowery95635132016-04-15 17:39:49 -0700432 permission_dict = lambda x, y: {'object': x, 'permission': y}
Tony Mackef8a9e52015-08-04 16:41:25 -0400433 for privilege_model, (model, affected_models) in privileg_map.items():
Tony Mackaf3aa182015-08-23 13:16:22 +0000434 if models and model not in models:
435 continue
436
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -0700437 # get the objects affected by this privilege model
Tony Mackef8a9e52015-08-04 16:41:25 -0400438 affected_objects = []
439 for affected_model in affected_models:
440 affected_objects.extend(affected_model.select_by_user(self))
441
442 if self.is_admin:
443 # assume admin users have read/write access to all objects
444 for affected_object in affected_objects:
Jeremy Mowery95635132016-04-15 17:39:49 -0700445 permissions.append(permission_dict(
446 affected_object, READWRITE))
Tony Mackef8a9e52015-08-04 16:41:25 -0400447 else:
448 # create a dict of the user's per object privileges
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -0700449 # ex: {princeton_tmack : ['admin']
Tony Mackef8a9e52015-08-04 16:41:25 -0400450 privileges = privilege_model.objects.filter(user=self)
451 for privilege in privileges:
452 object_roles = defaultdict(list)
453 obj = None
454 roles = []
455 for field in dir(privilege):
456 if field == model.__name__.lower():
457 obj = getattr(privilege, field)
458 if obj:
459 object_roles[obj].append(privilege.role.role)
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -0700460
Tony Mackef8a9e52015-08-04 16:41:25 -0400461 # loop through all objects the user has access to and determine
462 # if they also have write access
463 for affected_object in affected_objects:
464 if affected_object not in objects_roles:
Jeremy Mowery95635132016-04-15 17:39:49 -0700465 permissions.append(
466 permission_dict(affected_object, READ))
Tony Mackef8a9e52015-08-04 16:41:25 -0400467 else:
468 has_write_permission = False
469 for write_role, models in write_dict.items():
470 if affected_object._meta.model in models and \
Jeremy Mowery95635132016-04-15 17:39:49 -0700471 write_role in object_roles[affected_object]:
472 has_write_permission = True
473 break
Tony Mackef8a9e52015-08-04 16:41:25 -0400474 if has_write_permission:
Jeremy Mowery95635132016-04-15 17:39:49 -0700475 permissions.append(
476 permission_dict(affected_object, WRITE))
Tony Mackef8a9e52015-08-04 16:41:25 -0400477 else:
Jeremy Mowery95635132016-04-15 17:39:49 -0700478 permissions.append(
479 permission_dict(affected_object, READ))
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -0700480
481 return permissions
482
Tony Mackaf3aa182015-08-23 13:16:22 +0000483 def get_tenant_permissions(self):
484 from core.models import Site, Slice
Jeremy Mowery95635132016-04-15 17:39:49 -0700485 return self.get_object_permissions(filter_by=[Site, Slice])
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -0700486
Tony Mack5b061472014-02-04 07:57:10 -0500487 @staticmethod
488 def select_by_user(user):
489 if user.is_admin:
490 qs = User.objects.all()
491 else:
492 # can see all users at any site where this user has pi role
493 from core.models.site import SitePrivilege
494 site_privs = SitePrivilege.objects.filter(user=user)
Jeremy Mowery95635132016-04-15 17:39:49 -0700495 sites = [sp.site for sp in site_privs if sp.role.role in [
496 'Admin', 'admin', 'pi']]
Tony Mack5b061472014-02-04 07:57:10 -0500497 # get site privs of users at these sites
498 site_privs = SitePrivilege.objects.filter(site__in=sites)
Scott Bakera36d77e2014-08-29 11:43:23 -0700499 user_ids = [sp.user.id for sp in site_privs] + [user.id]
Tony Mack5b061472014-02-04 07:57:10 -0500500 qs = User.objects.filter(Q(site__in=sites) | Q(id__in=user_ids))
Scott Bakera36d77e2014-08-29 11:43:23 -0700501 return qs
Tony Mack5b061472014-02-04 07:57:10 -0500502
Scott Bakercbfb6002014-10-03 00:32:37 -0700503 def save_by_user(self, user, *args, **kwds):
504 if not self.can_update(user):
Scott Baker0119c152014-10-06 22:58:48 -0700505 if getattr(self, "_cant_update_fieldName", None) is not None:
Jeremy Mowery95635132016-04-15 17:39:49 -0700506 raise PermissionDenied("You do not have permission to update field %s on object %s" % (
507 self._cant_update_fieldName, self.__class__.__name__))
Scott Baker0119c152014-10-06 22:58:48 -0700508 else:
Jeremy Mowery95635132016-04-15 17:39:49 -0700509 raise PermissionDenied(
510 "You do not have permission to update %s objects" % self.__class__.__name__)
Scott Bakercbfb6002014-10-03 00:32:37 -0700511
512 self.save(*args, **kwds)
513
514 def delete_by_user(self, user, *args, **kwds):
515 if not self.can_update(user):
Jeremy Mowery95635132016-04-15 17:39:49 -0700516 raise PermissionDenied(
517 "You do not have permission to delete %s objects" % self.__class__.__name__)
Scott Bakercbfb6002014-10-03 00:32:37 -0700518 self.delete(*args, **kwds)
519
Scott Baker811d4472015-05-19 16:39:48 -0700520 def apply_profile(self, profile):
Jeremy Mowery95635132016-04-15 17:39:49 -0700521 if profile == "regular":
Scott Baker811d4472015-05-19 16:39:48 -0700522 self.is_appuser = False
523 self.is_admin = False
524
Jeremy Mowery95635132016-04-15 17:39:49 -0700525 elif profile == "cp":
Scott Baker811d4472015-05-19 16:39:48 -0700526 self.is_appuser = True
527 self.is_admin = False
528 for db in self.userdashboardviews.all():
529 db.delete()
530
Jeremy Mowery95635132016-04-15 17:39:49 -0700531
Scott Baker2c3cb642014-05-19 17:55:56 -0700532class UserDashboardView(PlCoreBase):
Jeremy Mowery95635132016-04-15 17:39:49 -0700533 user = models.ForeignKey(User, related_name='userdashboardviews')
534 dashboardView = models.ForeignKey(
535 DashboardView, related_name='userdashboardviews')
536 order = models.IntegerField(default=0)