· 4 years ago · May 31, 2021, 12:24 PM
1class PolicyManager(models.Manager):
2 def create_policy_for_profilev2(self,
3 policy_name: str,
4 policy_type: str,
5 policy_settings: list,
6 profilev2: ProfileV2,
7 company: Company,
8 profile: Profile = None):
9 def process_kiosk_type(policy_type, policy_settings):
10 if policy_type == PolicyType.kiosk.name:
11 if policy_settings['apps'] is None:
12 policy_settings['apps'] = []
13 springdel_managed_apps = Application.objects.filter(device_type=None,
14 app_category=AppCategory.springdel_managed_app.name,
15 current_version=True). \
16 order_by('app_id_name', '-app_version_code'). \
17 distinct('app_id_name')
18 for springdel_managed_app in springdel_managed_apps:
19 app_exists = False
20 for policy_app_info in policy_settings['apps']:
21 policy_app = Application.objects.filter(pk=policy_app_info['app_id']).first()
22 if policy_app is not None and policy_app.app_id_name == springdel_managed_app.app_id_name:
23 app_exists = True
24 break
25 if not app_exists:
26 policy_settings['apps'].append({
27 'app_id': str(springdel_managed_app.app_id),
28 'app_id_name': springdel_managed_app.app_id_name,
29 'app_name': springdel_managed_app.app_name,
30 'app_version': springdel_managed_app.app_version,
31 'app_version_code': springdel_managed_app.app_version_code
32 })
33 policy_settings['applications'] = policy_settings['apps']
34 policy_settings['apps'] = []
35 for policy_settings_app in policy_settings['applications']:
36 if policy_settings_app['app_id'] is not None:
37 policy_settings['apps'].append(policy_settings_app['app_id'])
38 return policy_settings
39 else:
40 return policy_settings
41 policy_settings = process_kiosk_type(policy_type=policy_type,
42 policy_settings=policy_settings)
43 self.create(policy_name=policy_name,
44 policy_type=policy_type,
45 policy_settings=policy_settings,
46 profilev2=profilev2,
47 company=company,
48 profile=profile)
49class Policy(models.Model):
50 """Policy model"""
51 policy_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
52 policy_name = models.TextField(null=True)
53 policy_type = models.TextField(choices=PolicyType.choices(), null=False)
54 policy_settings = JSONField(null=False)
55 policy_data = models.BinaryField(null=True)
56 last_updated = models.DateTimeField(null=False, auto_now=True)
57 company = models.ForeignKey('Company', on_delete=models.CASCADE)
58 profile = models.ForeignKey('Profile', on_delete=models.CASCADE, null=True)
59 profilev2 = models.ForeignKey('ProfileV2', on_delete=models.CASCADE, null=True, related_name='policies')
60 objects = PolicyManager()
61
62
63class FleetV2Manager(models.Manager):
64 def create_brand_new(self, name:str, company: Company, parent: FleetV2=None):
65 new_profilev2 = self.create(name=name, company=company, parent=parent)
66 return new_profilev2
67class FleetV2(models.Model):
68 id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
69 name = models.TextField(blank=False, null=False)
70 company = models.ForeignKey('Company', on_delete=models.CASCADE, related_name='fleets_v2')
71 parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True)
72 devices = models.ManyToManyField(Device)
73 created_at = models.DateTimeField(auto_now_add=True)
74 updated_at = models.DateTimeField(auto_now=True)
75 objects = FleetV2Manager()
76
77 def __str__(self):
78 return str(self.id)
79
80 def verify_fleet(self, company_id: str) -> None:
81 """
82 Security stuff.
83 It's for verifying that updating fleet has the same company which user has.
84 We can avoid the situation when users from other companies are capable to edit fleets from other companies.
85 """
86 if str(self.company.pk) == company_id:
87 return
88 else:
89 raise ValueError('Security issue')
90
91 class Meta:
92 permissions = (
93 ('view_specific_fleet_v2', 'View specific fleet v2'),
94 ('enable_rc_v2', 'Enable RC v2'),
95 )
96 db_table = 'fleet_v2'
97class ProfileV2Tree(models.Model):
98 """
99 Helper table for ProfileV2 linked linear list
100 It helps us get all profiles which are related to tree
101 """
102 id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
103 company = models.ForeignKey('Company', related_name='fleet_trees', on_delete=models.CASCADE)
104class ProfileV2Manager(models.Manager):
105
106 def create_policies(self,
107 policies: list,
108 profilev2: ProfileV2,
109 company: Company) -> None:
110 for policy in policies:
111 Policy.objects.create_policy_for_profilev2(policy_name=policy["policy_name"],
112 policy_type=policy["policy_type"],
113 policy_settings=policy["policy_settings"],
114 profilev2=profilev2,
115 company=company)
116
117 @transaction.atomic
118 def create_brand_new(self,
119 name: str,
120 company: Company,
121 policies: list = None,
122 tree: ProfileV2Tree=None) -> ProfileV2Tree:
123
124 tree = tree if tree else ProfileV2Tree.objects.create(company=company)
125 profile = self.create(name=name,
126 newest_version=True,
127 previous_version=None,
128 published=False,
129 draft=True,
130 profile_tree=tree,
131 company=company)
132 if policies:
133 self.create_policies(profilev2=profile, policies=policies, company=company)
134 return profile
135class ProfileV2(models.Model):
136 """
137 Profile should provide some restriction for policies. Fixed amount of policies
138 just 1 policy per 1 type.
139
140 endpoints:
141 1) create brand new - DONE
142 3) create new version - DONE
143 4) clone - DONE
144 5) publish - DONE
145 6) assign fleets - DONE
146
147 If status is Published - Creating new version.
148 If status is draft - Just updating the profile
149
150 BRAND NEW PROFILE - draft=True, published=False
151 NEW VERSION PROFILE - draft=True, published=False
152 CLONED PROFILE - draft=True, published=False
153 PUBLISH PROFILE - draft=False, published=True
154 """
155
156 CLONED_PREFIX: str = 'cloned_'
157 MAX_PROFILE_COUNT_PER_TREE: int = os.getenv("MAX_PROFILE_COUNT_PER_TREE", 20)
158
159 id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
160 name = models.TextField()
161 version = models.IntegerField(null=False, blank=False, default=1)
162 newest_version = models.BooleanField(null=False, blank=False, default=False)
163 draft = models.BooleanField(null=False, blank=False, default=True)
164 published = models.BooleanField(null=False, blank=False, default=False)
165 previous_version = models.ForeignKey('self', related_name="older_version", on_delete=models.CASCADE, null=True)
166 company = models.ForeignKey('Company', related_name='profiles_v2', on_delete=models.CASCADE)
167 profile_tree = models.ForeignKey('ProfileV2Tree', related_name='profiles', on_delete=models.PROTECT)
168 created_at = models.DateTimeField(auto_now_add=True)
169 updated_at = models.DateTimeField(auto_now=True)
170 fleets = models.ManyToManyField(FleetV2)
171
172
173 # TODO: CHANGE MIGRATION STUFF
174 # SOME STUFF THAT WILL BE IMPROVED. ITS REGARDING SCHEDULING PROFILES
175 # begin_date = models.DateField(null=True, blank=False)
176 # begin_time = models.TimeField(null=True, blank=False)
177 # end_date = models.DateField(null=True, blank=False)
178 # end_time = models.TimeField(null=True, blank=False)
179 # datetime_offset = JSONField(null=True, blank=False)
180
181 objects = ProfileV2Manager()
182
183 class Meta:
184 ordering = ['created_at']
185 db_table = 'profile_v2'
186
187 def __str__(self):
188 return str(self.id)
189
190 @atomic
191 def create_new_version(self, name: str, policies: list=None):
192 """
193 CLONED PROFILE - draft=True, published=False
194 """
195 def check_newest_version_existing():
196 if ProfileV2.objects.filter(previous_version=self).exists():
197 raise ValueError('Newest version for the profile already exists')
198 else:
199 return
200 check_newest_version_existing()
201 new_version_profile = ProfileV2.objects.create(name=name,
202 newest_version=True,
203 draft=True,
204 published=False,
205 profile_tree=self.profile_tree,
206 previous_version=self,
207 version=self.version + 1,
208 company=self.company)
209 if policies:
210 ProfileV2.objects.create_policies(policies=policies, profilev2=new_version_profile, company=self.company)
211 return new_version_profile
212
213 @atomic
214 def clone(self):
215 """
216 Name will be generated from CLONED_PREFIX and self.name
217 Neither of fleets will not be assigned to cloned profile
218 version=1
219 draft=True
220 published=False
221 """
222 tree = ProfileV2Tree.objects.create(company=self.company)
223 return ProfileV2.objects.create(name= f'{self.CLONED_PREFIX}{self.name}',
224 draft=True,
225 published=False,
226 newest_version=True,
227 profile_tree=tree,
228 version=1,
229 company=self.company)
230
231 @atomic
232 def publish(self):
233 """
234 Get published profile
235 Move all fleets from previous published profile to the certain one
236 Save self as published
237 Save previous published profile as not published
238 """
239 published_profile = self._get_published_profile_from_tree()
240 self._transpose_fleets_from_published_version(published_profile=published_profile)
241 self.draft = False
242 self.published = True
243 self.save()
244 if published_profile:
245 published_profile.published = False
246 published_profile.save()
247
248 def _transpose_fleets_from_published_version(self, published_profile=None):
249 """
250 If user wants to publish profile we need to assign fleets from published version to the new published
251 assign profiles to the new published profile and clear from previous published
252 """
253 if published_profile:
254 self.fleets.add(*published_profile.fleets.all())
255 published_profile.fleets.clear()
256
257 def _get_published_profile_from_tree(self):
258 published_profile = self.profile_tree.profiles.filter(published=True)
259 if published_profile.exists():
260 if published_profile.count() > 1:
261 raise ValueError(f'Profile tree has more than one published profile. It has {published_profile.count()}')
262 else:
263 return published_profile[0]
264 else:
265 # raise ValueError('Profile tree has no any published profile inside')
266 return None
267
268 def assign_fleets(self, *fleets):
269 """
270 Assign fleets if its published and not draft
271 """
272 if self.draft or not self.published:
273 raise AssigningError
274 else:
275 self.fleets.add(*fleets)
276 self.save()
277
278 def unassign_fleets(self, *fleets):
279 """
280 Unassign fleets
281 """
282 self.fleets.remove(*fleets)
283 self.save()