• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SwissDataScienceCenter / renku-data-services / 11288123511

11 Oct 2024 07:23AM UTC coverage: 90.667% (+0.2%) from 90.477%
11288123511

Pull #407

github

web-flow
Merge 20c6c8af6 into 5b095d795
Pull Request #407: feat!: add data connectors

1226 of 1325 new or added lines in 28 files covered. (92.53%)

3 existing lines in 3 files now uncovered.

10589 of 11679 relevant lines covered (90.67%)

1.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.03
/components/renku_data_services/authz/schemas.py
1
"""Specification of SpiceDB schemas.
2✔
2

3
These are applied through alembic migrations in the common migrations folder.
4
"""
5

6
from collections.abc import Iterable
2✔
7
from dataclasses import dataclass
2✔
8
from typing import cast
2✔
9

10
from authzed.api.v1 import SyncClient
2✔
11
from authzed.api.v1.core_pb2 import Relationship, RelationshipUpdate, SubjectReference
2✔
12
from authzed.api.v1.permission_service_pb2 import (
2✔
13
    DeleteRelationshipsRequest,
14
    DeleteRelationshipsResponse,
15
    RelationshipFilter,
16
    SubjectFilter,
17
    WriteRelationshipsRequest,
18
)
19
from authzed.api.v1.schema_service_pb2 import WriteSchemaRequest, WriteSchemaResponse
2✔
20
from ulid import ULID
2✔
21

22
from renku_data_services.authz.authz import ResourceType, _AuthzConverter, _Relation
2✔
23
from renku_data_services.errors import errors
2✔
24

25

26
@dataclass
2✔
27
class AuthzSchemaMigration:
2✔
28
    """A representation of an Authzed DB schema used for migrations."""
2✔
29

30
    up: list[WriteRelationshipsRequest | DeleteRelationshipsRequest | WriteSchemaRequest]
2✔
31
    down: list[WriteRelationshipsRequest | DeleteRelationshipsRequest | WriteSchemaRequest]
2✔
32

33
    @staticmethod
2✔
34
    def _write_to_db(
2✔
35
        requests: list[WriteRelationshipsRequest | DeleteRelationshipsRequest | WriteSchemaRequest], client: SyncClient
36
    ) -> list[WriteSchemaResponse | DeleteRelationshipsResponse | WriteSchemaResponse]:
37
        output: list[WriteSchemaResponse | DeleteRelationshipsResponse | WriteSchemaResponse] = []
2✔
38
        for request in requests:
2✔
39
            match request:
2✔
40
                case WriteRelationshipsRequest():
2✔
41
                    res = client.WriteRelationships(request)
×
42
                    output.append(res)
×
43
                case DeleteRelationshipsRequest():
2✔
44
                    res = client.DeleteRelationships(request)
2✔
45
                    output.append(res)
2✔
46
                case WriteSchemaRequest():
2✔
47
                    res = client.WriteSchema(request)
2✔
48
                    output.append(res)
2✔
49
                case _:
×
50
                    raise errors.ProgrammingError(
×
51
                        message=f"Found an uknown authorization migration type {type(request)}"
52
                    )
53
        return output
2✔
54

55
    def upgrade(
2✔
56
        self, client: SyncClient
57
    ) -> list[WriteSchemaResponse | DeleteRelationshipsResponse | WriteSchemaResponse]:
58
        """Perform the required changes to upgrade the authorization database schema."""
59
        return self._write_to_db(self.up, client)
2✔
60

61
    def downgrade(
2✔
62
        self, client: SyncClient
63
    ) -> list[WriteSchemaResponse | DeleteRelationshipsResponse | WriteSchemaResponse]:
64
        """Perform the required changes to downgrade the authorization database schema."""
65
        return self._write_to_db(self.down, client)
1✔
66

67

68
_v1: str = """\
2✔
69
definition user {}
70

71
definition anonymous_user {}
72

73
definition platform {
74
    relation admin: user
75
    permission is_admin = admin
76
}
77

78
definition project {
79
    relation project_platform: platform
80
    relation owner: user
81
    relation editor: user
82
    relation viewer: user | user:* | anonymous_user:*
83
    permission read = viewer + write
84
    permission write = editor + delete
85
    permission change_membership = delete
86
    permission delete = owner + project_platform->is_admin
87
}"""
88

89
v1 = AuthzSchemaMigration(
2✔
90
    up=[WriteSchemaRequest(schema=_v1)],
91
    down=[
92
        DeleteRelationshipsRequest(
93
            relationship_filter=RelationshipFilter(
94
                resource_type=ResourceType.project.value, optional_relation=_Relation.project_platform.value
95
            )
96
        ),
97
        DeleteRelationshipsRequest(
98
            relationship_filter=RelationshipFilter(
99
                resource_type=ResourceType.platform.value, optional_relation=_Relation.admin.value
100
            )
101
        ),
102
        DeleteRelationshipsRequest(
103
            relationship_filter=RelationshipFilter(
104
                resource_type=ResourceType.project.value, optional_relation=_Relation.owner.value
105
            )
106
        ),
107
        DeleteRelationshipsRequest(
108
            relationship_filter=RelationshipFilter(
109
                resource_type=ResourceType.project.value, optional_relation=_Relation.editor.value
110
            )
111
        ),
112
        DeleteRelationshipsRequest(
113
            relationship_filter=RelationshipFilter(
114
                resource_type=ResourceType.project.value, optional_relation=_Relation.viewer.value
115
            )
116
        ),
117
    ],
118
)
119

120
_v2: str = """\
2✔
121
definition user {}
122

123
definition group {
124
    relation group_platform: platform
125
    relation owner: user
126
    relation editor: user
127
    relation viewer: user | user:* | anonymous_user:*
128
    permission read = viewer + write
129
    permission write = editor + delete
130
    permission change_membership = delete
131
    permission delete = owner + group_platform->is_admin
132
}
133

134
definition user_namespace {
135
    relation user_namespace_platform: platform
136
    relation owner: user
137
    permission read = delete
138
    permission write = delete
139
    permission delete = owner + user_namespace_platform->is_admin
140
}
141

142
definition anonymous_user {}
143

144
definition platform {
145
    relation admin: user
146
    permission is_admin = admin
147
}
148

149
definition project {
150
    relation project_platform: platform
151
    relation project_namespace: user_namespace | group
152
    relation owner: user
153
    relation editor: user
154
    relation viewer: user | user:* | anonymous_user:*
155
    permission read = viewer + write + project_namespace->read
156
    permission write = editor + delete + project_namespace->write
157
    permission change_membership = delete
158
    permission delete = owner + project_platform->is_admin + project_namespace->delete
159
}"""
160

161
v2 = AuthzSchemaMigration(
2✔
162
    up=[WriteSchemaRequest(schema=_v2)],
163
    down=[
164
        DeleteRelationshipsRequest(
165
            relationship_filter=RelationshipFilter(
166
                resource_type=ResourceType.project.value, optional_relation=_Relation.project_namespace.value
167
            )
168
        ),
169
        DeleteRelationshipsRequest(
170
            relationship_filter=RelationshipFilter(
171
                resource_type=ResourceType.user_namespace.value,
172
                optional_relation=_Relation.user_namespace_platform.value,
173
            )
174
        ),
175
        DeleteRelationshipsRequest(
176
            relationship_filter=RelationshipFilter(
177
                resource_type=ResourceType.user_namespace.value, optional_relation=_Relation.owner.value
178
            )
179
        ),
180
        DeleteRelationshipsRequest(
181
            relationship_filter=RelationshipFilter(
182
                resource_type=ResourceType.group.value, optional_relation=_Relation.group_platform.value
183
            )
184
        ),
185
        DeleteRelationshipsRequest(
186
            relationship_filter=RelationshipFilter(
187
                resource_type=ResourceType.group.value, optional_relation=_Relation.owner.value
188
            )
189
        ),
190
        DeleteRelationshipsRequest(
191
            relationship_filter=RelationshipFilter(
192
                resource_type=ResourceType.group.value, optional_relation=_Relation.editor.value
193
            )
194
        ),
195
        DeleteRelationshipsRequest(
196
            relationship_filter=RelationshipFilter(
197
                resource_type=ResourceType.group.value, optional_relation=_Relation.viewer.value
198
            )
199
        ),
200
        WriteSchemaRequest(schema=_v1),
201
    ],
202
)
203

204
_v3: str = """\
2✔
205
definition user {}
206

207
definition group {
208
    relation group_platform: platform
209
    relation owner: user
210
    relation editor: user
211
    relation viewer: user
212
    relation public_viewer: user:* | anonymous_user:*
213
    permission read = public_viewer + read_children
214
    permission read_children = viewer + write
215
    permission write = editor + delete
216
    permission change_membership = delete
217
    permission delete = owner + group_platform->is_admin
218
}
219

220
definition user_namespace {
221
    relation user_namespace_platform: platform
222
    relation owner: user
223
    relation public_viewer: user:* | anonymous_user:*
224
    permission read = public_viewer + read_children
225
    permission read_children = delete
226
    permission write = delete
227
    permission delete = owner + user_namespace_platform->is_admin
228
}
229

230
definition anonymous_user {}
231

232
definition platform {
233
    relation admin: user
234
    permission is_admin = admin
235
}
236

237
definition project {
238
    relation project_platform: platform
239
    relation project_namespace: user_namespace | group
240
    relation owner: user
241
    relation editor: user
242
    relation viewer: user | user:* | anonymous_user:*
243
    permission read = viewer + write + project_namespace->read_children
244
    permission write = editor + delete + project_namespace->write
245
    permission change_membership = delete
246
    permission delete = owner + project_platform->is_admin + project_namespace->delete
247
}"""
248

249
v3 = AuthzSchemaMigration(
2✔
250
    up=[
251
        DeleteRelationshipsRequest(
252
            relationship_filter=RelationshipFilter(
253
                resource_type=ResourceType.group.value,
254
                optional_relation=_Relation.viewer.value,
255
                optional_subject_filter=SubjectFilter(
256
                    subject_type=ResourceType.user.value,
257
                    optional_subject_id=SubjectReference(object=_AuthzConverter.all_users()).object.object_id,
258
                ),
259
            )
260
        ),
261
        DeleteRelationshipsRequest(
262
            relationship_filter=RelationshipFilter(
263
                resource_type=ResourceType.group.value,
264
                optional_relation=_Relation.viewer.value,
265
                optional_subject_filter=SubjectFilter(
266
                    subject_type=ResourceType.anonymous_user.value,
267
                    optional_subject_id=SubjectReference(object=_AuthzConverter.anonymous_users()).object.object_id,
268
                ),
269
            )
270
        ),
271
        WriteSchemaRequest(schema=_v3),
272
    ],
273
    down=[
274
        DeleteRelationshipsRequest(
275
            relationship_filter=RelationshipFilter(
276
                resource_type=ResourceType.group.value, optional_relation=_Relation.public_viewer.value
277
            )
278
        ),
279
        DeleteRelationshipsRequest(
280
            relationship_filter=RelationshipFilter(
281
                resource_type=ResourceType.user_namespace.value, optional_relation=_Relation.public_viewer.value
282
            )
283
        ),
284
        WriteSchemaRequest(schema=_v2),
285
    ],
286
)
287

288
_v4: str = """\
2✔
289
definition user {}
290

291
definition group {
292
    relation group_platform: platform
293
    relation owner: user
294
    relation editor: user
295
    relation viewer: user
296
    relation public_viewer: user:* | anonymous_user:*
297
    permission read = public_viewer + read_children
298
    permission read_children = viewer + write
299
    permission write = editor + delete
300
    permission change_membership = delete
301
    permission delete = owner + group_platform->is_admin
302
}
303

304
definition user_namespace {
305
    relation user_namespace_platform: platform
306
    relation owner: user
307
    relation public_viewer: user:* | anonymous_user:*
308
    permission read = public_viewer + read_children
309
    permission read_children = delete
310
    permission write = delete
311
    permission delete = owner + user_namespace_platform->is_admin
312
}
313

314
definition anonymous_user {}
315

316
definition platform {
317
    relation admin: user
318
    permission is_admin = admin
319
}
320

321
definition project {
322
    relation project_platform: platform
323
    relation project_namespace: user_namespace | group
324
    relation owner: user
325
    relation editor: user
326
    relation viewer: user
327
    relation public_viewer: user:* | anonymous_user:*
328
    permission read = public_viewer + viewer + write + project_namespace->read_children
329
    permission read_linked_resources = viewer + editor + owner + project_platform->is_admin
330
    permission write = editor + delete + project_namespace->write
331
    permission change_membership = delete
332
    permission delete = owner + project_platform->is_admin + project_namespace->delete
333
}
334

335
definition data_connector {
336
    relation data_connector_platform: platform
337
    relation data_connector_namespace: user_namespace | group
338
    relation linked_to: project
339
    relation owner: user
340
    relation editor: user
341
    relation viewer: user
342
    relation public_viewer: user:* | anonymous_user:*
343
    permission read = public_viewer + viewer + write + \
344
        data_connector_namespace->read_children + read_from_linked_resource
345
    permission read_from_linked_resource = linked_to->read_linked_resources
346
    permission write = editor + delete + data_connector_namespace->write
347
    permission change_membership = delete
348
    permission delete = owner + data_connector_platform->is_admin + data_connector_namespace->delete
349
    permission add_link = write + public_viewer
350
}"""
351

352

353
def generate_v4(public_project_ids: Iterable[str]) -> AuthzSchemaMigration:
2✔
354
    """Creates the v4 schema migration."""
355
    up: list[WriteRelationshipsRequest | DeleteRelationshipsRequest | WriteSchemaRequest] = [
2✔
356
        DeleteRelationshipsRequest(
357
            relationship_filter=RelationshipFilter(
358
                resource_type=ResourceType.project.value,
359
                optional_relation=_Relation.viewer.value,
360
                optional_subject_filter=SubjectFilter(
361
                    subject_type=ResourceType.user.value,
362
                    optional_subject_id=SubjectReference(object=_AuthzConverter.all_users()).object.object_id,
363
                ),
364
            )
365
        ),
366
        DeleteRelationshipsRequest(
367
            relationship_filter=RelationshipFilter(
368
                resource_type=ResourceType.project.value,
369
                optional_relation=_Relation.viewer.value,
370
                optional_subject_filter=SubjectFilter(
371
                    subject_type=ResourceType.anonymous_user.value,
372
                    optional_subject_id=SubjectReference(object=_AuthzConverter.anonymous_users()).object.object_id,
373
                ),
374
            )
375
        ),
376
        WriteSchemaRequest(schema=_v4),
377
    ]
378
    down: list[WriteRelationshipsRequest | DeleteRelationshipsRequest | WriteSchemaRequest] = [
2✔
379
        DeleteRelationshipsRequest(
380
            relationship_filter=RelationshipFilter(
381
                resource_type=ResourceType.project.value, optional_relation=_Relation.public_viewer.value
382
            )
383
        ),
384
        DeleteRelationshipsRequest(
385
            relationship_filter=RelationshipFilter(resource_type=ResourceType.data_connector.value)
386
        ),
387
        WriteSchemaRequest(schema=_v3),
388
    ]
389

390
    all_users_sub = SubjectReference(object=_AuthzConverter.all_users())
2✔
391
    anon_users_sub = SubjectReference(object=_AuthzConverter.anonymous_users())
2✔
392
    for project_id in public_project_ids:
2✔
NEW
393
        project_res = _AuthzConverter.project(cast(ULID, ULID.from_str(project_id)))
×
NEW
394
        all_users_are_viewers = Relationship(
×
395
            resource=project_res,
396
            relation=_Relation.public_viewer.value,
397
            subject=all_users_sub,
398
        )
NEW
399
        anon_users_are_viewers = Relationship(
×
400
            resource=project_res,
401
            relation=_Relation.public_viewer.value,
402
            subject=anon_users_sub,
403
        )
NEW
404
        down_all_users_are_viewers = Relationship(
×
405
            resource=project_res,
406
            relation=_Relation.viewer.value,
407
            subject=all_users_sub,
408
        )
NEW
409
        down_anon_users_are_viewers = Relationship(
×
410
            resource=project_res,
411
            relation=_Relation.viewer.value,
412
            subject=anon_users_sub,
413
        )
NEW
414
        up.append(
×
415
            WriteRelationshipsRequest(
416
                updates=[
417
                    RelationshipUpdate(
418
                        operation=RelationshipUpdate.OPERATION_TOUCH, relationship=all_users_are_viewers
419
                    ),
420
                    RelationshipUpdate(
421
                        operation=RelationshipUpdate.OPERATION_TOUCH, relationship=anon_users_are_viewers
422
                    ),
423
                ],
424
            )
425
        )
NEW
426
        down.append(
×
427
            WriteRelationshipsRequest(
428
                updates=[
429
                    RelationshipUpdate(
430
                        operation=RelationshipUpdate.OPERATION_TOUCH, relationship=down_all_users_are_viewers
431
                    ),
432
                    RelationshipUpdate(
433
                        operation=RelationshipUpdate.OPERATION_TOUCH, relationship=down_anon_users_are_viewers
434
                    ),
435
                ],
436
            )
437
        )
438

439
    return AuthzSchemaMigration(up=up, down=down)
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc