adss 0.1__py3-none-any.whl → 1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,433 @@
1
+ """
2
+ Administrative functionality for the Astronomy TAP Client.
3
+ """
4
+ import requests
5
+ from typing import Dict, List, Optional, Any
6
+
7
+ from ..exceptions import (
8
+ AuthenticationError, ResourceNotFoundError, PermissionDeniedError,
9
+ ValidationError
10
+ )
11
+ from ..utils import handle_response_errors, format_permission
12
+ from ..models.user import User, Role, SchemaPermission, TablePermission, RolePermissions
13
+
14
+
15
+ class AdminEndpoint:
16
+ """
17
+ Handles administrative operations (superuser/staff only).
18
+ """
19
+
20
+ def __init__(self, base_url: str, auth_manager):
21
+ """
22
+ Initialize the Admin endpoint.
23
+
24
+ Args:
25
+ base_url: The base URL of the API server
26
+ auth_manager: Authentication manager providing auth headers
27
+ """
28
+ self.base_url = base_url.rstrip('/')
29
+ self.auth_manager = auth_manager
30
+
31
+ def create_role(self, name: str, description: Optional[str] = None, **kwargs) -> Role:
32
+ """
33
+ Create a new role (superuser only).
34
+
35
+ Args:
36
+ name: Role name
37
+ description: Optional role description
38
+ **kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
39
+
40
+ Returns:
41
+ The newly created Role object
42
+
43
+ Raises:
44
+ AuthenticationError: If not authenticated
45
+ PermissionDeniedError: If not a superuser
46
+ ValidationError: If input validation fails
47
+ """
48
+ data = {
49
+ "name": name
50
+ }
51
+
52
+ if description:
53
+ data["description"] = description
54
+
55
+ try:
56
+ response = self.auth_manager.request(
57
+ method="POST",
58
+ url="/adss/v1/users/roles",
59
+ json=data,
60
+ auth_required=True,
61
+ **kwargs
62
+ )
63
+ handle_response_errors(response)
64
+
65
+ role_data = response.json()
66
+ return Role.from_dict(role_data)
67
+
68
+ except Exception as e:
69
+ if hasattr(e, 'response'):
70
+ if e.response.status_code == 401:
71
+ raise AuthenticationError("Authentication required")
72
+ elif e.response.status_code == 403:
73
+ raise PermissionDeniedError("Superuser access required")
74
+ elif e.response.status_code == 400:
75
+ try:
76
+ error_data = e.response.json()
77
+ raise ValidationError(
78
+ f"Role creation failed: {error_data.get('detail', str(e))}",
79
+ error_data.get('errors')
80
+ )
81
+ except (ValueError, AttributeError):
82
+ pass
83
+ raise
84
+
85
+ def get_roles(self, skip: int = 0, limit: int = 100, **kwargs) -> List[Role]:
86
+ """
87
+ Get a list of roles (staff only).
88
+
89
+ Args:
90
+ skip: Number of roles to skip (for pagination)
91
+ limit: Maximum number of roles to return
92
+ **kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
93
+
94
+ Returns:
95
+ List of Role objects
96
+
97
+ Raises:
98
+ AuthenticationError: If not authenticated
99
+ PermissionDeniedError: If not a staff user
100
+ """
101
+ params = {"skip": skip, "limit": limit}
102
+
103
+ try:
104
+ response = self.auth_manager.request(
105
+ method="GET",
106
+ url="/adss/v1/users/roles",
107
+ params=params,
108
+ auth_required=True,
109
+ **kwargs
110
+ )
111
+ handle_response_errors(response)
112
+
113
+ roles_data = response.json()
114
+ return [Role.from_dict(role_data) for role_data in roles_data]
115
+
116
+ except Exception as e:
117
+ if hasattr(e, 'response'):
118
+ if e.response.status_code == 401:
119
+ raise AuthenticationError("Authentication required")
120
+ elif e.response.status_code == 403:
121
+ raise PermissionDeniedError("Staff access required")
122
+ raise
123
+
124
+ def add_user_to_role(self, user_id: str, role_id: int, **kwargs) -> bool:
125
+ """
126
+ Add a user to a role (superuser only).
127
+
128
+ Args:
129
+ user_id: ID of the user
130
+ role_id: ID of the role
131
+ **kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
132
+
133
+ Returns:
134
+ True if the user was successfully added to the role
135
+
136
+ Raises:
137
+ AuthenticationError: If not authenticated
138
+ PermissionDeniedError: If not a superuser
139
+ ResourceNotFoundError: If the user or role doesn't exist
140
+ """
141
+ try:
142
+ response = self.auth_manager.request(
143
+ method="POST",
144
+ url=f"/adss/v1/users/{user_id}/roles/{role_id}",
145
+ auth_required=True,
146
+ **kwargs
147
+ )
148
+ handle_response_errors(response)
149
+
150
+ return True
151
+
152
+ except Exception as e:
153
+ if hasattr(e, 'response'):
154
+ if e.response.status_code == 401:
155
+ raise AuthenticationError("Authentication required")
156
+ elif e.response.status_code == 403:
157
+ raise PermissionDeniedError("Superuser access required")
158
+ elif e.response.status_code == 404:
159
+ raise ResourceNotFoundError("User or role not found")
160
+ raise
161
+
162
+ def remove_user_from_role(self, user_id: str, role_id: int, **kwargs) -> bool:
163
+ """
164
+ Remove a user from a role (superuser only).
165
+
166
+ Args:
167
+ user_id: ID of the user
168
+ role_id: ID of the role
169
+ **kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
170
+
171
+ Returns:
172
+ True if the user was successfully removed from the role
173
+
174
+ Raises:
175
+ AuthenticationError: If not authenticated
176
+ PermissionDeniedError: If not a superuser
177
+ ResourceNotFoundError: If the user or role doesn't exist
178
+ """
179
+ try:
180
+ response = self.auth_manager.request(
181
+ method="DELETE",
182
+ url=f"/adss/v1/users/{user_id}/roles/{role_id}",
183
+ auth_required=True,
184
+ **kwargs
185
+ )
186
+ handle_response_errors(response)
187
+
188
+ return True
189
+
190
+ except Exception as e:
191
+ if hasattr(e, 'response'):
192
+ if e.response.status_code == 401:
193
+ raise AuthenticationError("Authentication required")
194
+ elif e.response.status_code == 403:
195
+ raise PermissionDeniedError("Superuser access required")
196
+ elif e.response.status_code == 404:
197
+ raise ResourceNotFoundError("User or role not found")
198
+ raise
199
+
200
+ def add_schema_permission(self, role_id: int, schema_name: str, permission: str, **kwargs) -> bool:
201
+ """
202
+ Add a schema permission to a role (superuser only).
203
+
204
+ Args:
205
+ role_id: ID of the role
206
+ schema_name: Name of the schema
207
+ permission: Permission type ('read', 'write', or 'all')
208
+ **kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
209
+
210
+ Returns:
211
+ True if the permission was successfully added
212
+
213
+ Raises:
214
+ AuthenticationError: If not authenticated
215
+ PermissionDeniedError: If not a superuser
216
+ ResourceNotFoundError: If the role doesn't exist
217
+ ValidationError: If the permission type is invalid
218
+ """
219
+ # Validate and format permission
220
+ permission = format_permission(permission)
221
+
222
+ data = {
223
+ "schema_name": schema_name,
224
+ "permission": permission
225
+ }
226
+
227
+ try:
228
+ response = self.auth_manager.request(
229
+ method="POST",
230
+ url=f"/adss/v1/users/roles/{role_id}/schema-permissions",
231
+ json=data,
232
+ auth_required=True,
233
+ **kwargs
234
+ )
235
+ handle_response_errors(response)
236
+
237
+ return True
238
+
239
+ except Exception as e:
240
+ if hasattr(e, 'response'):
241
+ if e.response.status_code == 401:
242
+ raise AuthenticationError("Authentication required")
243
+ elif e.response.status_code == 403:
244
+ raise PermissionDeniedError("Superuser access required")
245
+ elif e.response.status_code == 404:
246
+ raise ResourceNotFoundError("Role not found")
247
+ elif e.response.status_code == 400:
248
+ try:
249
+ error_data = e.response.json()
250
+ raise ValidationError(
251
+ f"Adding schema permission failed: {error_data.get('detail', str(e))}",
252
+ error_data.get('errors')
253
+ )
254
+ except (ValueError, AttributeError):
255
+ pass
256
+ raise
257
+
258
+ def add_table_permission(self, role_id: int, schema_name: str, table_name: str, permission: str, **kwargs) -> bool:
259
+ """
260
+ Add a table permission to a role (superuser only).
261
+
262
+ Args:
263
+ role_id: ID of the role
264
+ schema_name: Name of the schema
265
+ table_name: Name of the table
266
+ permission: Permission type ('read', 'write', or 'all')
267
+ **kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
268
+
269
+ Returns:
270
+ True if the permission was successfully added
271
+
272
+ Raises:
273
+ AuthenticationError: If not authenticated
274
+ PermissionDeniedError: If not a superuser
275
+ ResourceNotFoundError: If the role doesn't exist
276
+ ValidationError: If the permission type is invalid
277
+ """
278
+ # Validate and format permission
279
+ permission = format_permission(permission)
280
+
281
+ data = {
282
+ "schema_name": schema_name,
283
+ "table_name": table_name,
284
+ "permission": permission
285
+ }
286
+
287
+ try:
288
+ response = self.auth_manager.request(
289
+ method="POST",
290
+ url=f"/adss/v1/users/roles/{role_id}/table-permissions",
291
+ json=data,
292
+ auth_required=True,
293
+ **kwargs
294
+ )
295
+ handle_response_errors(response)
296
+
297
+ return True
298
+
299
+ except Exception as e:
300
+ if hasattr(e, 'response'):
301
+ if e.response.status_code == 401:
302
+ raise AuthenticationError("Authentication required")
303
+ elif e.response.status_code == 403:
304
+ raise PermissionDeniedError("Superuser access required")
305
+ elif e.response.status_code == 404:
306
+ raise ResourceNotFoundError("Role not found")
307
+ elif e.response.status_code == 400:
308
+ try:
309
+ error_data = e.response.json()
310
+ raise ValidationError(
311
+ f"Adding table permission failed: {error_data.get('detail', str(e))}",
312
+ error_data.get('errors')
313
+ )
314
+ except (ValueError, AttributeError):
315
+ pass
316
+ raise
317
+
318
+ def remove_schema_permission(self, role_id: int, schema_name: str, **kwargs) -> bool:
319
+ """
320
+ Remove a schema permission from a role (superuser only).
321
+
322
+ Args:
323
+ role_id: ID of the role
324
+ schema_name: Name of the schema
325
+ **kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
326
+
327
+ Returns:
328
+ True if the permission was successfully removed
329
+
330
+ Raises:
331
+ AuthenticationError: If not authenticated
332
+ PermissionDeniedError: If not a superuser
333
+ ResourceNotFoundError: If the role doesn't exist
334
+ """
335
+ try:
336
+ response = self.auth_manager.request(
337
+ method="DELETE",
338
+ url=f"/adss/v1/users/roles/{role_id}/schema-permissions/{schema_name}",
339
+ auth_required=True,
340
+ **kwargs
341
+ )
342
+ handle_response_errors(response)
343
+
344
+ return True
345
+
346
+ except Exception as e:
347
+ if hasattr(e, 'response'):
348
+ if e.response.status_code == 401:
349
+ raise AuthenticationError("Authentication required")
350
+ elif e.response.status_code == 403:
351
+ raise PermissionDeniedError("Superuser access required")
352
+ elif e.response.status_code == 404:
353
+ raise ResourceNotFoundError("Role or schema permission not found")
354
+ raise
355
+
356
+ def remove_table_permission(self, role_id: int, schema_name: str, table_name: str, **kwargs) -> bool:
357
+ """
358
+ Remove a table permission from a role (superuser only).
359
+
360
+ Args:
361
+ role_id: ID of the role
362
+ schema_name: Name of the schema
363
+ table_name: Name of the table
364
+ **kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
365
+
366
+ Returns:
367
+ True if the permission was successfully removed
368
+
369
+ Raises:
370
+ AuthenticationError: If not authenticated
371
+ PermissionDeniedError: If not a superuser
372
+ ResourceNotFoundError: If the role doesn't exist
373
+ """
374
+ try:
375
+ response = self.auth_manager.request(
376
+ method="DELETE",
377
+ url=f"/adss/v1/users/roles/{role_id}/table-permissions/{schema_name}/{table_name}",
378
+ auth_required=True,
379
+ **kwargs
380
+ )
381
+ handle_response_errors(response)
382
+
383
+ return True
384
+
385
+ except Exception as e:
386
+ if hasattr(e, 'response'):
387
+ if e.response.status_code == 401:
388
+ raise AuthenticationError("Authentication required")
389
+ elif e.response.status_code == 403:
390
+ raise PermissionDeniedError("Superuser access required")
391
+ elif e.response.status_code == 404:
392
+ raise ResourceNotFoundError("Role or table permission not found")
393
+ raise
394
+
395
+ def get_role_permissions(self, role_id: int, **kwargs) -> RolePermissions:
396
+ """
397
+ Get permissions for a role (staff only).
398
+
399
+ Args:
400
+ role_id: ID of the role
401
+ **kwargs: Additional keyword arguments to pass to the request (e.g., verify=False)
402
+
403
+ Returns:
404
+ RolePermissions object containing schema and table permissions
405
+
406
+ Raises:
407
+ AuthenticationError: If not authenticated
408
+ PermissionDeniedError: If not a staff user
409
+ ResourceNotFoundError: If the role doesn't exist
410
+ """
411
+ try:
412
+ response = self.auth_manager.request(
413
+ method="GET",
414
+ url=f"/adss/v1/users/roles/{role_id}/permissions",
415
+ auth_required=True,
416
+ **kwargs
417
+ )
418
+ handle_response_errors(response)
419
+
420
+ role_data = response.json()
421
+ permissions_data = role_data.get("permissions", {})
422
+
423
+ return RolePermissions.from_dict(permissions_data)
424
+
425
+ except Exception as e:
426
+ if hasattr(e, 'response'):
427
+ if e.response.status_code == 401:
428
+ raise AuthenticationError("Authentication required")
429
+ elif e.response.status_code == 403:
430
+ raise PermissionDeniedError("Staff access required")
431
+ elif e.response.status_code == 404:
432
+ raise ResourceNotFoundError("Role not found")
433
+ raise