Merge ~pappacena/launchpad:ocirecipe-subscribe-removal-job into launchpad:master
- Git
- lp:~pappacena/launchpad
- ocirecipe-subscribe-removal-job
- Merge into master
Proposed by
Thiago F. Pappacena
Status: | Merged |
---|---|
Approved by: | Thiago F. Pappacena |
Approved revision: | 8e5bd69ecdd6bccdc1e76c20ecc8bb6cce70b852 |
Merged at revision: | 65142a38e43e863baf2068006265af09ceac7aaa |
Proposed branch: | ~pappacena/launchpad:ocirecipe-subscribe-removal-job |
Merge into: | launchpad:master |
Prerequisite: | ~pappacena/launchpad:ocirecipe-filter-private |
Diff against target: |
384 lines (+128/-25) 4 files modified
database/schema/security.cfg (+3/-0) lib/lp/registry/model/sharingjob.py (+52/-3) lib/lp/registry/services/sharingservice.py (+1/-10) lib/lp/registry/tests/test_sharingjob.py (+72/-12) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Colin Watson (community) | Approve | ||
Review via email: mp+399889@code.launchpad.net |
Commit message
Consider OCIRecipeSubscr
Description of the change
To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/database/schema/security.cfg b/database/schema/security.cfg |
2 | index 5e5e000..e149a3c 100644 |
3 | --- a/database/schema/security.cfg |
4 | +++ b/database/schema/security.cfg |
5 | @@ -2110,6 +2110,9 @@ public.gitrepository = SELECT |
6 | public.gitsubscription = SELECT, UPDATE, DELETE |
7 | public.emailaddress = SELECT |
8 | public.job = SELECT, INSERT, UPDATE |
9 | +public.ociproject = SELECT |
10 | +public.ocirecipe = SELECT |
11 | +public.ocirecipesubscription = SELECT, UPDATE, DELETE |
12 | public.person = SELECT |
13 | public.product = SELECT |
14 | public.sharingjob = SELECT, INSERT, UPDATE |
15 | diff --git a/lib/lp/registry/model/sharingjob.py b/lib/lp/registry/model/sharingjob.py |
16 | index 81e6a9e..b123547 100644 |
17 | --- a/lib/lp/registry/model/sharingjob.py |
18 | +++ b/lib/lp/registry/model/sharingjob.py |
19 | @@ -69,6 +69,12 @@ from lp.code.model.gitrepository import ( |
20 | GitRepository, |
21 | ) |
22 | from lp.code.model.gitsubscription import GitSubscription |
23 | +from lp.oci.interfaces.ocirecipe import IOCIRecipe |
24 | +from lp.oci.model.ocirecipe import ( |
25 | + get_ocirecipe_privacy_filter, |
26 | + OCIRecipe, |
27 | + ) |
28 | +from lp.oci.model.ocirecipesubscription import OCIRecipeSubscription |
29 | from lp.registry.interfaces.person import IPersonSet |
30 | from lp.registry.interfaces.product import IProduct |
31 | from lp.registry.interfaces.sharingjob import ( |
32 | @@ -78,6 +84,7 @@ from lp.registry.interfaces.sharingjob import ( |
33 | ISharingJobSource, |
34 | ) |
35 | from lp.registry.model.distribution import Distribution |
36 | +from lp.registry.model.ociproject import OCIProject |
37 | from lp.registry.model.person import Person |
38 | from lp.registry.model.product import Product |
39 | from lp.registry.model.teammembership import TeamParticipation |
40 | @@ -271,6 +278,7 @@ class RemoveArtifactSubscriptionsJob(SharingJobDerived): |
41 | gitrepository_ids = [] |
42 | snap_ids = [] |
43 | specification_ids = [] |
44 | + ocirecipe_ids = [] |
45 | if artifacts: |
46 | for artifact in artifacts: |
47 | if IBug.providedBy(artifact): |
48 | @@ -283,6 +291,8 @@ class RemoveArtifactSubscriptionsJob(SharingJobDerived): |
49 | snap_ids.append(artifact.id) |
50 | elif ISpecification.providedBy(artifact): |
51 | specification_ids.append(artifact.id) |
52 | + elif IOCIRecipe.providedBy(artifact): |
53 | + ocirecipe_ids.append(artifact.id) |
54 | else: |
55 | raise ValueError( |
56 | 'Unsupported artifact: %r' % artifact) |
57 | @@ -295,6 +305,7 @@ class RemoveArtifactSubscriptionsJob(SharingJobDerived): |
58 | 'gitrepository_ids': gitrepository_ids, |
59 | 'snap_ids': snap_ids, |
60 | 'specification_ids': specification_ids, |
61 | + 'ocirecipe_ids': ocirecipe_ids, |
62 | 'information_types': information_types, |
63 | 'requestor.id': requestor.id |
64 | } |
65 | @@ -338,6 +349,10 @@ class RemoveArtifactSubscriptionsJob(SharingJobDerived): |
66 | return self.metadata.get('specification_ids', []) |
67 | |
68 | @property |
69 | + def ocirecipe_ids(self): |
70 | + return self.metadata.get('ocirecipe_ids', []) |
71 | + |
72 | + @property |
73 | def information_types(self): |
74 | if not 'information_types' in self.metadata: |
75 | return [] |
76 | @@ -365,6 +380,7 @@ class RemoveArtifactSubscriptionsJob(SharingJobDerived): |
77 | 'gitrepository_ids': self.gitrepository_ids, |
78 | 'snap_ids': self.snap_ids, |
79 | 'specification_ids': self.specification_ids, |
80 | + 'ocirecipe_ids': self.ocirecipe_ids, |
81 | 'pillar': getattr(self.pillar, 'name', None), |
82 | 'grantee': getattr(self.grantee, 'name', None) |
83 | } |
84 | @@ -382,6 +398,7 @@ class RemoveArtifactSubscriptionsJob(SharingJobDerived): |
85 | gitrepository_filters = [] |
86 | snap_filters = [] |
87 | specification_filters = [] |
88 | + ocirecipe_filters = [] |
89 | |
90 | if self.branch_ids: |
91 | branch_filters.append(Branch.id.is_in(self.branch_ids)) |
92 | @@ -393,6 +410,8 @@ class RemoveArtifactSubscriptionsJob(SharingJobDerived): |
93 | if self.specification_ids: |
94 | specification_filters.append(Specification.id.is_in( |
95 | self.specification_ids)) |
96 | + if self.ocirecipe_ids: |
97 | + ocirecipe_filters.append(OCIRecipe.id.is_in(self.ocirecipe_ids)) |
98 | if self.bug_ids: |
99 | bug_filters.append(BugTaskFlat.bug_id.is_in(self.bug_ids)) |
100 | else: |
101 | @@ -410,6 +429,8 @@ class RemoveArtifactSubscriptionsJob(SharingJobDerived): |
102 | specification_filters.append( |
103 | Specification.information_type.is_in( |
104 | self.information_types)) |
105 | + ocirecipe_filters.append(OCIRecipe._information_type.is_in( |
106 | + self.information_types)) |
107 | if self.product: |
108 | bug_filters.append( |
109 | BugTaskFlat.product == self.product) |
110 | @@ -418,6 +439,11 @@ class RemoveArtifactSubscriptionsJob(SharingJobDerived): |
111 | GitRepository.project == self.product) |
112 | specification_filters.append( |
113 | Specification.product == self.product) |
114 | + ocirecipe_filters.append( |
115 | + In(OCIRecipe.id, |
116 | + Select(OCIRecipe.id, |
117 | + And(OCIRecipe.oci_project_id == OCIProject.id, |
118 | + OCIProject.project == self.product)))) |
119 | if self.distro: |
120 | bug_filters.append( |
121 | BugTaskFlat.distribution == self.distro) |
122 | @@ -426,6 +452,11 @@ class RemoveArtifactSubscriptionsJob(SharingJobDerived): |
123 | GitRepository.distribution == self.distro) |
124 | specification_filters.append( |
125 | Specification.distribution == self.distro) |
126 | + ocirecipe_filters.append( |
127 | + In(OCIRecipe.id, |
128 | + Select(OCIRecipe.id, |
129 | + And(OCIRecipe.oci_project_id == OCIProject.id, |
130 | + OCIProject.distribution == self.distro)))) |
131 | |
132 | if self.grantee: |
133 | bug_filters.append( |
134 | @@ -450,9 +481,14 @@ class RemoveArtifactSubscriptionsJob(SharingJobDerived): |
135 | where=TeamParticipation.team == self.grantee))) |
136 | specification_filters.append( |
137 | In(SpecificationSubscription.person_id, |
138 | - Select( |
139 | - TeamParticipation.personID, |
140 | - where=TeamParticipation.team == self.grantee))) |
141 | + Select( |
142 | + TeamParticipation.personID, |
143 | + where=TeamParticipation.team == self.grantee))) |
144 | + ocirecipe_filters.append( |
145 | + In(OCIRecipeSubscription.person_id, |
146 | + Select( |
147 | + TeamParticipation.personID, |
148 | + where=TeamParticipation.team == self.grantee))) |
149 | |
150 | if bug_filters: |
151 | bug_filters.append(Not( |
152 | @@ -517,3 +553,16 @@ class RemoveArtifactSubscriptionsJob(SharingJobDerived): |
153 | for sub in specifications_subscriptions: |
154 | sub.specification.unsubscribe( |
155 | sub.person, self.requestor, ignore_permissions=True) |
156 | + if ocirecipe_filters: |
157 | + ocirecipe_filters.append( |
158 | + Not(get_ocirecipe_privacy_filter( |
159 | + OCIRecipeSubscription.person_id))) |
160 | + ocirecipe_subscriptions = IStore(OCIRecipeSubscription).using( |
161 | + OCIRecipeSubscription, |
162 | + Join(OCIRecipe, |
163 | + OCIRecipe.id == OCIRecipeSubscription.recipe_id) |
164 | + ).find(OCIRecipeSubscription, *ocirecipe_filters).config( |
165 | + distinct=True) |
166 | + for sub in ocirecipe_subscriptions: |
167 | + sub.recipe.unsubscribe( |
168 | + sub.person, self.requestor, ignore_permissions=True) |
169 | diff --git a/lib/lp/registry/services/sharingservice.py b/lib/lp/registry/services/sharingservice.py |
170 | index a0e755b..a311aff 100644 |
171 | --- a/lib/lp/registry/services/sharingservice.py |
172 | +++ b/lib/lp/registry/services/sharingservice.py |
173 | @@ -39,10 +39,7 @@ from lp.bugs.interfaces.bugtask import IBugTaskSet |
174 | from lp.bugs.interfaces.bugtasksearch import BugTaskSearchParams |
175 | from lp.code.interfaces.branchcollection import IAllBranches |
176 | from lp.code.interfaces.gitcollection import IAllGitRepositories |
177 | -from lp.oci.interfaces.ocirecipe import ( |
178 | - IOCIRecipe, |
179 | - IOCIRecipeSet, |
180 | - ) |
181 | +from lp.oci.interfaces.ocirecipe import IOCIRecipeSet |
182 | from lp.oci.model.ocirecipe import OCIRecipe |
183 | from lp.registry.enums import ( |
184 | BranchSharingPolicy, |
185 | @@ -857,12 +854,6 @@ class SharingService: |
186 | getUtility(IAccessArtifactGrantSource).revokeByArtifact( |
187 | artifacts_to_delete, [grantee]) |
188 | |
189 | - # XXX: Pappacena 2021-03-09: OCI recipes should not trigger this job, |
190 | - # since we do not have a "OCIRecipeSubscription" yet. |
191 | - artifacts = [i for i in artifacts if not IOCIRecipe.providedBy(i)] |
192 | - if not artifacts: |
193 | - return |
194 | - |
195 | # Create a job to remove subscriptions for artifacts the grantee can no |
196 | # longer see. |
197 | return getUtility(IRemoveArtifactSubscriptionsJobSource).create( |
198 | diff --git a/lib/lp/registry/tests/test_sharingjob.py b/lib/lp/registry/tests/test_sharingjob.py |
199 | index 73ad71f..65f382b 100644 |
200 | --- a/lib/lp/registry/tests/test_sharingjob.py |
201 | +++ b/lib/lp/registry/tests/test_sharingjob.py |
202 | @@ -16,6 +16,10 @@ from lp.code.enums import ( |
203 | BranchSubscriptionNotificationLevel, |
204 | CodeReviewNotificationLevel, |
205 | ) |
206 | +from lp.oci.interfaces.ocirecipe import ( |
207 | + OCI_RECIPE_ALLOW_CREATE, |
208 | + OCI_RECIPE_PRIVATE_FEATURE_FLAG, |
209 | + ) |
210 | from lp.registry.enums import SpecificationSharingPolicy |
211 | from lp.registry.interfaces.accesspolicy import ( |
212 | IAccessArtifactGrantSource, |
213 | @@ -148,6 +152,21 @@ class SharingJobDerivedTestCase(TestCaseWithFactory): |
214 | 'for requestor=%s, specification_ids=[%d]>' |
215 | % (requestor.name, specification.id), repr(job)) |
216 | |
217 | + def test_repr_ocirecipe(self): |
218 | + features = { |
219 | + OCI_RECIPE_ALLOW_CREATE: 'on', |
220 | + OCI_RECIPE_PRIVATE_FEATURE_FLAG: 'on' |
221 | + } |
222 | + self.useFixture(FeatureFixture(features)) |
223 | + requestor = self.factory.makePerson() |
224 | + recipe = self.factory.makeOCIRecipe() |
225 | + job = getUtility(IRemoveArtifactSubscriptionsJobSource).create( |
226 | + requestor, artifacts=[recipe]) |
227 | + self.assertEqual( |
228 | + '<REMOVE_ARTIFACT_SUBSCRIPTIONS job reconciling subscriptions ' |
229 | + 'for ocirecipe_ids=[%d], requestor=%s>' |
230 | + % (recipe.id, requestor.name), repr(job)) |
231 | + |
232 | def test_create_success(self): |
233 | # Create an instance of SharingJobDerived that delegates to SharingJob. |
234 | self.assertIs(True, ISharingJobSource.providedBy(SharingJobDerived)) |
235 | @@ -254,6 +273,8 @@ class RemoveArtifactSubscriptionsJobTestCase(TestCaseWithFactory): |
236 | def setUp(self): |
237 | features = { |
238 | 'jobs.celery.enabled_classes': 'RemoveArtifactSubscriptionsJob', |
239 | + OCI_RECIPE_ALLOW_CREATE: 'on', |
240 | + OCI_RECIPE_PRIVATE_FEATURE_FLAG: 'on' |
241 | } |
242 | features.update(SNAP_TESTING_FLAGS) |
243 | self.useFixture(FeatureFixture(features)) |
244 | @@ -334,6 +355,10 @@ class RemoveArtifactSubscriptionsJobTestCase(TestCaseWithFactory): |
245 | specification = self.factory.makeSpecification( |
246 | owner=owner, product=product, |
247 | information_type=InformationType.PROPRIETARY) |
248 | + ocirecipe = self.factory.makeOCIRecipe( |
249 | + owner=owner, registrant=owner, |
250 | + oci_project=self.factory.makeOCIProject(pillar=product), |
251 | + information_type=InformationType.USERDATA) |
252 | |
253 | # The artifact grantees will not lose access when the job is run. |
254 | artifact_indirect_grantee = self.factory.makePerson() |
255 | @@ -349,6 +374,7 @@ class RemoveArtifactSubscriptionsJobTestCase(TestCaseWithFactory): |
256 | BranchSubscriptionNotificationLevel.NOEMAIL, None, |
257 | CodeReviewNotificationLevel.NOEMAIL, owner) |
258 | snap.subscribe(artifact_indirect_grantee, owner) |
259 | + ocirecipe.subscribe(artifact_indirect_grantee, owner) |
260 | # Subscribing somebody to a specification does not automatically |
261 | # create an artifact grant. |
262 | spec_artifact = self.factory.makeAccessArtifact(specification) |
263 | @@ -358,9 +384,9 @@ class RemoveArtifactSubscriptionsJobTestCase(TestCaseWithFactory): |
264 | specification.subscribe(artifact_indirect_grantee, owner) |
265 | |
266 | # pick one of the concrete artifacts (bug, branch, Git repository, |
267 | - # snap, or spec) and subscribe the teams and persons. |
268 | + # snap, spec or ocirecipe) and subscribe the teams and persons. |
269 | concrete_artifact, get_pillars, get_subscribers = configure_test( |
270 | - bug, branch, gitrepository, snap, specification, |
271 | + bug, branch, gitrepository, snap, specification, ocirecipe, |
272 | policy_team_grantee, policy_indirect_grantee, |
273 | artifact_team_grantee, owner) |
274 | |
275 | @@ -396,6 +422,7 @@ class RemoveArtifactSubscriptionsJobTestCase(TestCaseWithFactory): |
276 | self.assertIn(artifact_indirect_grantee, branch.subscribers) |
277 | self.assertIn(artifact_indirect_grantee, gitrepository.subscribers) |
278 | self.assertIn(artifact_indirect_grantee, snap.subscribers) |
279 | + self.assertIn(artifact_indirect_grantee, ocirecipe.subscribers) |
280 | self.assertIn(artifact_indirect_grantee, |
281 | removeSecurityProxy(specification).subscribers) |
282 | |
283 | @@ -409,8 +436,9 @@ class RemoveArtifactSubscriptionsJobTestCase(TestCaseWithFactory): |
284 | concrete_artifact).getDirectSubscribers() |
285 | |
286 | def configure_test(bug, branch, gitrepository, snap, specification, |
287 | - policy_team_grantee, policy_indirect_grantee, |
288 | - artifact_team_grantee, owner): |
289 | + ocirecipe, policy_team_grantee, |
290 | + policy_indirect_grantee, artifact_team_grantee, |
291 | + owner): |
292 | concrete_artifact = bug |
293 | bug.subscribe(policy_team_grantee, owner) |
294 | bug.subscribe(policy_indirect_grantee, owner) |
295 | @@ -429,8 +457,9 @@ class RemoveArtifactSubscriptionsJobTestCase(TestCaseWithFactory): |
296 | return concrete_artifact.subscribers |
297 | |
298 | def configure_test(bug, branch, gitrepository, snap, specification, |
299 | - policy_team_grantee, policy_indirect_grantee, |
300 | - artifact_team_grantee, owner): |
301 | + ocirecipe, policy_team_grantee, |
302 | + policy_indirect_grantee, artifact_team_grantee, |
303 | + owner): |
304 | concrete_artifact = branch |
305 | branch.subscribe( |
306 | policy_team_grantee, |
307 | @@ -458,8 +487,9 @@ class RemoveArtifactSubscriptionsJobTestCase(TestCaseWithFactory): |
308 | return concrete_artifact.subscribers |
309 | |
310 | def configure_test(bug, branch, gitrepository, snap, specification, |
311 | - policy_team_grantee, policy_indirect_grantee, |
312 | - artifact_team_grantee, owner): |
313 | + ocirecipe, policy_team_grantee, |
314 | + policy_indirect_grantee, artifact_team_grantee, |
315 | + owner): |
316 | concrete_artifact = gitrepository |
317 | gitrepository.subscribe( |
318 | policy_team_grantee, |
319 | @@ -487,8 +517,9 @@ class RemoveArtifactSubscriptionsJobTestCase(TestCaseWithFactory): |
320 | return concrete_artifact.subscribers |
321 | |
322 | def configure_test(bug, branch, gitrepository, snap, specification, |
323 | - policy_team_grantee, policy_indirect_grantee, |
324 | - artifact_team_grantee, owner): |
325 | + ocirecipe, policy_team_grantee, |
326 | + policy_indirect_grantee, artifact_team_grantee, |
327 | + owner): |
328 | concrete_artifact = snap |
329 | snap.subscribe(policy_team_grantee, owner) |
330 | snap.subscribe(policy_indirect_grantee, owner) |
331 | @@ -507,8 +538,9 @@ class RemoveArtifactSubscriptionsJobTestCase(TestCaseWithFactory): |
332 | return concrete_artifact.subscribers |
333 | |
334 | def configure_test(bug, branch, gitrepository, snap, specification, |
335 | - policy_team_grantee, policy_indirect_grantee, |
336 | - artifact_team_grantee, owner): |
337 | + ocirecipe, policy_team_grantee, |
338 | + policy_indirect_grantee, artifact_team_grantee, |
339 | + owner): |
340 | naked_spec = removeSecurityProxy(specification) |
341 | naked_spec.subscribe(policy_team_grantee, owner) |
342 | naked_spec.subscribe(policy_indirect_grantee, owner) |
343 | @@ -521,6 +553,27 @@ class RemoveArtifactSubscriptionsJobTestCase(TestCaseWithFactory): |
344 | self._assert_artifact_change_unsubscribes( |
345 | change_callback, configure_test) |
346 | |
347 | + def _assert_ocirecipe_change_unsubscribes(self, change_callback): |
348 | + |
349 | + def get_pillars(concrete_artifact): |
350 | + return [concrete_artifact.oci_project.project] |
351 | + |
352 | + def get_subscribers(concrete_artifact): |
353 | + return concrete_artifact.subscribers |
354 | + |
355 | + def configure_test(bug, branch, gitrepository, snap, specification, |
356 | + ocirecipe, policy_team_grantee, |
357 | + policy_indirect_grantee, artifact_team_grantee, |
358 | + owner): |
359 | + concrete_artifact = ocirecipe |
360 | + ocirecipe.subscribe(policy_team_grantee, owner) |
361 | + ocirecipe.subscribe(policy_indirect_grantee, owner) |
362 | + ocirecipe.subscribe(artifact_team_grantee, owner) |
363 | + return concrete_artifact, get_pillars, get_subscribers |
364 | + |
365 | + self._assert_artifact_change_unsubscribes( |
366 | + change_callback, configure_test) |
367 | + |
368 | def test_change_information_type_branch(self): |
369 | def change_information_type(branch): |
370 | removeSecurityProxy(branch).information_type = ( |
371 | @@ -549,6 +602,13 @@ class RemoveArtifactSubscriptionsJobTestCase(TestCaseWithFactory): |
372 | |
373 | self._assert_specification_change_unsubscribes(change_information_type) |
374 | |
375 | + def test_change_information_type_ocirecipe(self): |
376 | + def change_information_type(ocirecipe): |
377 | + removeSecurityProxy(ocirecipe).information_type = ( |
378 | + InformationType.PRIVATESECURITY) |
379 | + |
380 | + self._assert_ocirecipe_change_unsubscribes(change_information_type) |
381 | + |
382 | def test_change_information_type(self): |
383 | # Changing the information type of a bug unsubscribes users who can no |
384 | # longer see the bug. |