pangea-sdk 6.0.0__py3-none-any.whl → 6.1.0__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.
pangea/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "6.0.0"
1
+ __version__ = "6.1.0"
2
2
 
3
3
  from pangea.asyncio.request import PangeaRequestAsync
4
4
  from pangea.config import PangeaConfig
@@ -2,14 +2,17 @@
2
2
  # Author: Pangea Cyber Corporation
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Dict, List, Optional, Union
5
+ from typing import Dict, List, Literal, Optional, Union
6
6
 
7
7
  import pangea.services.authn.models as m
8
8
  from pangea.asyncio.services.base import ServiceBaseAsync
9
9
  from pangea.config import PangeaConfig
10
10
  from pangea.response import PangeaResponse, PangeaResponseResult
11
11
 
12
- SERVICE_NAME = "authn"
12
+ __all__ = ["AuthNAsync"]
13
+
14
+
15
+ _SERVICE_NAME = "authn"
13
16
 
14
17
 
15
18
  class AuthNAsync(ServiceBaseAsync):
@@ -35,7 +38,7 @@ class AuthNAsync(ServiceBaseAsync):
35
38
  authn = AuthNAsync(token=PANGEA_TOKEN, config=authn_config)
36
39
  """
37
40
 
38
- service_name = SERVICE_NAME
41
+ service_name = _SERVICE_NAME
39
42
 
40
43
  def __init__(
41
44
  self,
@@ -65,7 +68,7 @@ class AuthNAsync(ServiceBaseAsync):
65
68
  self.agreements = AuthNAsync.AgreementsAsync(token, config, logger_name=logger_name)
66
69
 
67
70
  class SessionAsync(ServiceBaseAsync):
68
- service_name = SERVICE_NAME
71
+ service_name = _SERVICE_NAME
69
72
 
70
73
  def __init__(
71
74
  self,
@@ -162,7 +165,7 @@ class AuthNAsync(ServiceBaseAsync):
162
165
  )
163
166
 
164
167
  class ClientAsync(ServiceBaseAsync):
165
- service_name = SERVICE_NAME
168
+ service_name = _SERVICE_NAME
166
169
 
167
170
  def __init__(
168
171
  self,
@@ -222,7 +225,7 @@ class AuthNAsync(ServiceBaseAsync):
222
225
  return await self.request.post("v2/client/jwks", m.ClientJWKSResult, {})
223
226
 
224
227
  class SessionAsync(ServiceBaseAsync):
225
- service_name = SERVICE_NAME
228
+ service_name = _SERVICE_NAME
226
229
 
227
230
  def __init__(
228
231
  self,
@@ -359,7 +362,7 @@ class AuthNAsync(ServiceBaseAsync):
359
362
  )
360
363
 
361
364
  class PasswordAsync(ServiceBaseAsync):
362
- service_name = SERVICE_NAME
365
+ service_name = _SERVICE_NAME
363
366
 
364
367
  def __init__(
365
368
  self,
@@ -419,7 +422,7 @@ class AuthNAsync(ServiceBaseAsync):
419
422
  return await self.request.post("v2/user/password/expire", PangeaResponseResult, {"id": user_id})
420
423
 
421
424
  class TokenAsync(ServiceBaseAsync):
422
- service_name = SERVICE_NAME
425
+ service_name = _SERVICE_NAME
423
426
 
424
427
  def __init__(
425
428
  self,
@@ -456,7 +459,7 @@ class AuthNAsync(ServiceBaseAsync):
456
459
  )
457
460
 
458
461
  class UserAsync(ServiceBaseAsync):
459
- service_name = SERVICE_NAME
462
+ service_name = _SERVICE_NAME
460
463
 
461
464
  def __init__(
462
465
  self,
@@ -667,7 +670,7 @@ class AuthNAsync(ServiceBaseAsync):
667
670
  return await self.request.post("v2/user/list", m.UserListResult, data=input.model_dump(exclude_none=True))
668
671
 
669
672
  class InvitesAsync(ServiceBaseAsync):
670
- service_name = SERVICE_NAME
673
+ service_name = _SERVICE_NAME
671
674
 
672
675
  def __init__(
673
676
  self,
@@ -739,7 +742,7 @@ class AuthNAsync(ServiceBaseAsync):
739
742
  )
740
743
 
741
744
  class AuthenticatorsAsync(ServiceBaseAsync):
742
- service_name = SERVICE_NAME
745
+ service_name = _SERVICE_NAME
743
746
 
744
747
  def __init__(
745
748
  self,
@@ -821,7 +824,7 @@ class AuthNAsync(ServiceBaseAsync):
821
824
  )
822
825
 
823
826
  class ProfileAsync(ServiceBaseAsync):
824
- service_name = SERVICE_NAME
827
+ service_name = _SERVICE_NAME
825
828
 
826
829
  def __init__(
827
830
  self,
@@ -905,8 +908,57 @@ class AuthNAsync(ServiceBaseAsync):
905
908
  "v2/user/profile/update", m.UserProfileUpdateResult, data=input.model_dump(exclude_none=True)
906
909
  )
907
910
 
911
+ class GroupAsync(ServiceBaseAsync):
912
+ service_name = _SERVICE_NAME
913
+
914
+ def __init__(
915
+ self,
916
+ token: str,
917
+ config: PangeaConfig | None = None,
918
+ logger_name: str = "pangea",
919
+ ) -> None:
920
+ super().__init__(token, config, logger_name=logger_name)
921
+
922
+ async def assign(self, user_id: str, group_ids: list[str]) -> PangeaResponse[PangeaResponseResult]:
923
+ """
924
+ Assign groups to a user
925
+
926
+ Add a list of groups to a specified user
927
+
928
+ OperationId: authn_post_v2_user_group_assign
929
+ """
930
+ return await self.request.post(
931
+ "v2/user/group/assign",
932
+ data={"id": user_id, "group_ids": group_ids},
933
+ result_class=m.PangeaResponseResult,
934
+ )
935
+
936
+ async def remove(self, user_id: str, group_id: str) -> PangeaResponse[PangeaResponseResult]:
937
+ """
938
+ Remove a group assigned to a user
939
+
940
+ Remove a group assigned to a user
941
+
942
+ OperationId: authn_post_v2_user_group_remove
943
+ """
944
+ return await self.request.post(
945
+ "v2/user/group/remove",
946
+ data={"id": user_id, "group_id": group_id},
947
+ result_class=m.PangeaResponseResult,
948
+ )
949
+
950
+ async def list(self, user_id: str) -> PangeaResponse[m.GroupList]:
951
+ """
952
+ List of groups assigned to a user
953
+
954
+ Return a list of ids for groups assigned to a user
955
+
956
+ OperationId: authn_post_v2_user_group_list
957
+ """
958
+ return await self.request.post("v2/user/group/list", data={"id": user_id}, result_class=m.GroupList)
959
+
908
960
  class FlowAsync(ServiceBaseAsync):
909
- service_name = SERVICE_NAME
961
+ service_name = _SERVICE_NAME
910
962
 
911
963
  def __init__(
912
964
  self,
@@ -1052,7 +1104,7 @@ class AuthNAsync(ServiceBaseAsync):
1052
1104
  )
1053
1105
 
1054
1106
  class AgreementsAsync(ServiceBaseAsync):
1055
- service_name = SERVICE_NAME
1107
+ service_name = _SERVICE_NAME
1056
1108
 
1057
1109
  def __init__(
1058
1110
  self,
@@ -1201,3 +1253,108 @@ class AuthNAsync(ServiceBaseAsync):
1201
1253
  return await self.request.post(
1202
1254
  "v2/agreements/update", m.AgreementUpdateResult, data=input.model_dump(exclude_none=True)
1203
1255
  )
1256
+
1257
+ class GroupAsync(ServiceBaseAsync):
1258
+ service_name = _SERVICE_NAME
1259
+
1260
+ def __init__(
1261
+ self,
1262
+ token: str,
1263
+ config: PangeaConfig | None = None,
1264
+ logger_name: str = "pangea",
1265
+ ) -> None:
1266
+ super().__init__(token, config, logger_name=logger_name)
1267
+
1268
+ async def create(
1269
+ self, name: str, type: str, *, description: str | None = None, attributes: dict[str, str] | None = None
1270
+ ) -> PangeaResponse[m.GroupInfo]:
1271
+ """
1272
+ Create a new group
1273
+
1274
+ Create a new group
1275
+
1276
+ OperationId: authn_post_v2_group_create
1277
+ """
1278
+ return await self.request.post(
1279
+ "v2/group/create",
1280
+ data={"name": name, "type": type, "description": description, "attributes": attributes},
1281
+ result_class=m.GroupInfo,
1282
+ )
1283
+
1284
+ async def delete(self, id: str) -> PangeaResponse[PangeaResponseResult]:
1285
+ """
1286
+ Delete a group
1287
+
1288
+ Delete a group
1289
+
1290
+ OperationId: authn_post_v2_group_delete
1291
+ """
1292
+ return await self.request.post("v2/group/delete", data={"id": id}, result_class=PangeaResponseResult)
1293
+
1294
+ async def get(self, id: str) -> PangeaResponse[m.GroupInfo]:
1295
+ """
1296
+ Get group information
1297
+
1298
+ Look up a group by ID and return its information.
1299
+
1300
+ OperationId: authn_post_v2_group_get
1301
+ """
1302
+ return await self.request.post("v2/group/get", data={"id": id}, result_class=m.GroupInfo)
1303
+
1304
+ async def list(
1305
+ self,
1306
+ *,
1307
+ filter: m.GroupsFilter | None = None,
1308
+ last: str | None = None,
1309
+ order: Literal["asc", "desc"] | None = None,
1310
+ order_by: Literal["id", "created_at", "updated_at", "name", "type"] | None = None,
1311
+ size: int | None = None,
1312
+ ) -> PangeaResponse[m.GroupList]:
1313
+ """
1314
+ List groups
1315
+
1316
+ Look up groups by name, type, or attributes.
1317
+ """
1318
+ return await self.request.post(
1319
+ "v2/group/list",
1320
+ data={"filter": filter, "last": last, "order": order, "order_by": order_by, "size": size},
1321
+ result_class=m.GroupList,
1322
+ )
1323
+
1324
+ async def list_users(
1325
+ self, id: str, *, last: str | None = None, size: int | None = None
1326
+ ) -> PangeaResponse[m.GroupUserList]:
1327
+ """
1328
+ List of users assigned to a group
1329
+
1330
+ Return a list of ids for users assigned to a group
1331
+
1332
+ OperationId: authn_post_v2_group_user_list
1333
+ """
1334
+ return await self.request.post(
1335
+ "v2/group/user/list",
1336
+ data={"id": id, "last": last, "size": size},
1337
+ result_class=m.GroupUserList,
1338
+ )
1339
+
1340
+ async def update(
1341
+ self,
1342
+ id: str,
1343
+ *,
1344
+ name: str | None = None,
1345
+ description: str | None = None,
1346
+ type: str | None = None,
1347
+ attributes: dict[str, str] | None = None,
1348
+ ) -> PangeaResponse[m.GroupInfo]:
1349
+ """
1350
+ Update group information
1351
+
1352
+ Update group information
1353
+
1354
+ OperationId: authn_post_v2_group_update
1355
+ """
1356
+ return await self.request.post(
1357
+ "v2/group/update",
1358
+ data={"id": id, "name": name, "description": description, "type": type, "attributes": attributes},
1359
+ result_class=m.GroupInfo,
1360
+ )
@@ -3,7 +3,7 @@
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
- from typing import Any, Dict, List, Optional
6
+ from typing import Any
7
7
 
8
8
  from pangea.asyncio.services.base import ServiceBaseAsync
9
9
  from pangea.config import PangeaConfig
@@ -73,14 +73,14 @@ class AuthZAsync(ServiceBaseAsync):
73
73
 
74
74
  super().__init__(token, config, logger_name, config_id=config_id)
75
75
 
76
- async def tuple_create(self, tuples: List[Tuple]) -> PangeaResponse[TupleCreateResult]:
76
+ async def tuple_create(self, tuples: list[Tuple]) -> PangeaResponse[TupleCreateResult]:
77
77
  """Create tuples.
78
78
 
79
79
  Create tuples in the AuthZ Service. The request will fail if there is no schema
80
80
  or the tuples do not validate against the schema.
81
81
 
82
82
  Args:
83
- tuples (List[Tuple]): List of tuples to be created.
83
+ tuples: Tuples to be created.
84
84
 
85
85
  Raises:
86
86
  PangeaAPIException: If an API Error happens.
@@ -110,10 +110,10 @@ class AuthZAsync(ServiceBaseAsync):
110
110
  async def tuple_list(
111
111
  self,
112
112
  filter: TupleListFilter,
113
- size: Optional[int] = None,
114
- last: Optional[str] = None,
115
- order: Optional[ItemOrder] = None,
116
- order_by: Optional[TupleOrderBy] = None,
113
+ size: int | None = None,
114
+ last: str | None = None,
115
+ order: ItemOrder | None = None,
116
+ order_by: TupleOrderBy | None = None,
117
117
  ) -> PangeaResponse[TupleListResult]:
118
118
  """List tuples.
119
119
 
@@ -122,11 +122,11 @@ class AuthZAsync(ServiceBaseAsync):
122
122
  is empty it will return all the tuples.
123
123
 
124
124
  Args:
125
- filter (TupleListFilter): The filter for listing tuples.
126
- size (Optional[int]): The size of the result set. Default is None.
127
- last (Optional[str]): The last token from a previous response. Default is None.
128
- order (Optional[ItemOrder]): Order results asc(ending) or desc(ending).
129
- order_by (Optional[TupleOrderBy]): Which field to order results by.
125
+ filter: The filter for listing tuples.
126
+ size: The size of the result set. Default is None.
127
+ last: The last token from a previous response. Default is None.
128
+ order: Order results asc(ending) or desc(ending).
129
+ order_by: Which field to order results by.
130
130
 
131
131
  Raises:
132
132
  PangeaAPIException: If an API Error happens.
@@ -144,13 +144,13 @@ class AuthZAsync(ServiceBaseAsync):
144
144
  )
145
145
  return await self.request.post("v1/tuple/list", TupleListResult, data=input_data.model_dump(exclude_none=True))
146
146
 
147
- async def tuple_delete(self, tuples: List[Tuple]) -> PangeaResponse[TupleDeleteResult]:
147
+ async def tuple_delete(self, tuples: list[Tuple]) -> PangeaResponse[TupleDeleteResult]:
148
148
  """Delete tuples.
149
149
 
150
150
  Delete tuples in the AuthZ Service.
151
151
 
152
152
  Args:
153
- tuples (List[Tuple]): List of tuples to be deleted.
153
+ tuples: Tuples to be deleted.
154
154
 
155
155
  Raises:
156
156
  PangeaAPIException: If an API Error happens.
@@ -182,8 +182,8 @@ class AuthZAsync(ServiceBaseAsync):
182
182
  resource: Resource,
183
183
  action: str,
184
184
  subject: Subject,
185
- debug: Optional[bool] = None,
186
- attributes: Optional[Dict[str, Any]] = None,
185
+ debug: bool | None = None,
186
+ attributes: dict[str, Any] | None = None,
187
187
  ) -> PangeaResponse[CheckResult]:
188
188
  """Perform a check request.
189
189
 
@@ -192,9 +192,9 @@ class AuthZAsync(ServiceBaseAsync):
192
192
  Args:
193
193
  resource (Resource): The resource to check.
194
194
  action (str): The action to check.
195
- subject (Subject): The subject to check.
196
- debug (Optional[bool]): Setting this value to True will provide a detailed analysis of the check.
197
- attributes (Optional[Dict[str, Any]]): Additional attributes for the check.
195
+ subject: The subject to check.
196
+ debug: Setting this value to True will provide a detailed analysis of the check.
197
+ attributes: Additional attributes for the check.
198
198
 
199
199
  Raises:
200
200
  PangeaAPIException: If an API Error happens.
@@ -217,7 +217,7 @@ class AuthZAsync(ServiceBaseAsync):
217
217
  return await self.request.post("v1/check", CheckResult, data=input_data.model_dump(exclude_none=True))
218
218
 
219
219
  async def list_resources(
220
- self, type: str, action: str, subject: Subject, attributes: Optional[Dict[str, Any]] = None
220
+ self, type: str, action: str, subject: Subject, attributes: dict[str, Any] | None = None
221
221
  ) -> PangeaResponse[ListResourcesResult]:
222
222
  """List resources.
223
223
 
@@ -225,10 +225,10 @@ class AuthZAsync(ServiceBaseAsync):
225
225
  type that the subject has access to the action with.
226
226
 
227
227
  Args:
228
- type (str): The type to filter resources.
229
- action (str): The action to filter resources.
230
- subject (Subject): The subject to filter resources.
231
- attributes (Optional[Dict[str, Any]]): A JSON object of attribute data.
228
+ type: The type to filter resources.
229
+ action: The action to filter resources.
230
+ subject: The subject to filter resources.
231
+ attributes: A JSON object of attribute data.
232
232
 
233
233
  Raises:
234
234
  PangeaAPIException: If an API Error happens.
@@ -252,7 +252,7 @@ class AuthZAsync(ServiceBaseAsync):
252
252
  )
253
253
 
254
254
  async def list_subjects(
255
- self, resource: Resource, action: str, attributes: Optional[Dict[str, Any]] = None
255
+ self, resource: Resource, action: str, attributes: dict[str, Any] | None = None
256
256
  ) -> PangeaResponse[ListSubjectsResult]:
257
257
  """List subjects.
258
258
 
@@ -260,9 +260,9 @@ class AuthZAsync(ServiceBaseAsync):
260
260
  access to the action for the given resource.
261
261
 
262
262
  Args:
263
- resource (Resource): The resource to filter subjects.
264
- action (str): The action to filter subjects.
265
- attributes (Optional[Dict[str, Any]]): A JSON object of attribute data.
263
+ resource: The resource to filter subjects.
264
+ action: The action to filter subjects.
265
+ attributes: A JSON object of attribute data.
266
266
 
267
267
  Raises:
268
268
  PangeaAPIException: If an API Error happens.
@@ -2,14 +2,17 @@
2
2
  # Author: Pangea Cyber Corporation
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Dict, List, Optional, Union
5
+ from typing import Dict, List, Literal, Optional, Union
6
6
 
7
7
  import pangea.services.authn.models as m
8
8
  from pangea.config import PangeaConfig
9
9
  from pangea.response import PangeaResponse, PangeaResponseResult
10
10
  from pangea.services.base import ServiceBase
11
11
 
12
- SERVICE_NAME = "authn"
12
+ __all__ = ["AuthN"]
13
+
14
+
15
+ _SERVICE_NAME = "authn"
13
16
 
14
17
 
15
18
  class AuthN(ServiceBase):
@@ -35,7 +38,7 @@ class AuthN(ServiceBase):
35
38
  authn = AuthN(token=PANGEA_TOKEN, config=authn_config)
36
39
  """
37
40
 
38
- service_name = SERVICE_NAME
41
+ service_name = _SERVICE_NAME
39
42
 
40
43
  def __init__(
41
44
  self,
@@ -63,9 +66,10 @@ class AuthN(ServiceBase):
63
66
  self.client = AuthN.Client(token, config, logger_name=logger_name)
64
67
  self.session = AuthN.Session(token, config, logger_name=logger_name)
65
68
  self.agreements = AuthN.Agreements(token, config, logger_name=logger_name)
69
+ self.group = AuthN.Group(token, config, logger_name=logger_name)
66
70
 
67
71
  class Session(ServiceBase):
68
- service_name = SERVICE_NAME
72
+ service_name = _SERVICE_NAME
69
73
 
70
74
  def __init__(
71
75
  self,
@@ -161,7 +165,7 @@ class AuthN(ServiceBase):
161
165
  )
162
166
 
163
167
  class Client(ServiceBase):
164
- service_name = SERVICE_NAME
168
+ service_name = _SERVICE_NAME
165
169
 
166
170
  def __init__(
167
171
  self,
@@ -221,7 +225,7 @@ class AuthN(ServiceBase):
221
225
  return self.request.post("v2/client/jwks", m.ClientJWKSResult, {})
222
226
 
223
227
  class Session(ServiceBase):
224
- service_name = SERVICE_NAME
228
+ service_name = _SERVICE_NAME
225
229
 
226
230
  def __init__(
227
231
  self,
@@ -359,7 +363,7 @@ class AuthN(ServiceBase):
359
363
  )
360
364
 
361
365
  class Password(ServiceBase):
362
- service_name = SERVICE_NAME
366
+ service_name = _SERVICE_NAME
363
367
 
364
368
  def __init__(
365
369
  self,
@@ -419,7 +423,7 @@ class AuthN(ServiceBase):
419
423
  return self.request.post("v2/user/password/expire", PangeaResponseResult, {"id": user_id})
420
424
 
421
425
  class Token(ServiceBase):
422
- service_name = SERVICE_NAME
426
+ service_name = _SERVICE_NAME
423
427
 
424
428
  def __init__(
425
429
  self,
@@ -456,18 +460,19 @@ class AuthN(ServiceBase):
456
460
  )
457
461
 
458
462
  class User(ServiceBase):
459
- service_name = SERVICE_NAME
463
+ service_name = _SERVICE_NAME
460
464
 
461
465
  def __init__(
462
466
  self,
463
- token,
464
- config=None,
465
- logger_name="pangea",
466
- ):
467
+ token: str,
468
+ config: PangeaConfig | None = None,
469
+ logger_name: str = "pangea",
470
+ ) -> None:
467
471
  super().__init__(token, config, logger_name=logger_name)
468
472
  self.profile = AuthN.User.Profile(token, config, logger_name=logger_name)
469
473
  self.authenticators = AuthN.User.Authenticators(token, config, logger_name=logger_name)
470
474
  self.invites = AuthN.User.Invites(token, config, logger_name=logger_name)
475
+ self.group = AuthN.User.Group(token, config, logger_name=logger_name)
471
476
 
472
477
  def create(
473
478
  self,
@@ -663,7 +668,7 @@ class AuthN(ServiceBase):
663
668
  return self.request.post("v2/user/list", m.UserListResult, data=input.model_dump(exclude_none=True))
664
669
 
665
670
  class Invites(ServiceBase):
666
- service_name = SERVICE_NAME
671
+ service_name = _SERVICE_NAME
667
672
 
668
673
  def __init__(
669
674
  self,
@@ -735,7 +740,7 @@ class AuthN(ServiceBase):
735
740
  )
736
741
 
737
742
  class Authenticators(ServiceBase):
738
- service_name = SERVICE_NAME
743
+ service_name = _SERVICE_NAME
739
744
 
740
745
  def __init__(
741
746
  self,
@@ -817,7 +822,7 @@ class AuthN(ServiceBase):
817
822
  )
818
823
 
819
824
  class Profile(ServiceBase):
820
- service_name = SERVICE_NAME
825
+ service_name = _SERVICE_NAME
821
826
 
822
827
  def __init__(
823
828
  self,
@@ -901,8 +906,57 @@ class AuthN(ServiceBase):
901
906
  "v2/user/profile/update", m.UserProfileUpdateResult, data=input.model_dump(exclude_none=True)
902
907
  )
903
908
 
909
+ class Group(ServiceBase):
910
+ service_name = _SERVICE_NAME
911
+
912
+ def __init__(
913
+ self,
914
+ token: str,
915
+ config: PangeaConfig | None = None,
916
+ logger_name: str = "pangea",
917
+ ) -> None:
918
+ super().__init__(token, config, logger_name=logger_name)
919
+
920
+ def assign(self, user_id: str, group_ids: list[str]) -> PangeaResponse[PangeaResponseResult]:
921
+ """
922
+ Assign groups to a user
923
+
924
+ Add a list of groups to a specified user
925
+
926
+ OperationId: authn_post_v2_user_group_assign
927
+ """
928
+ return self.request.post(
929
+ "v2/user/group/assign",
930
+ data={"id": user_id, "group_ids": group_ids},
931
+ result_class=m.PangeaResponseResult,
932
+ )
933
+
934
+ def remove(self, user_id: str, group_id: str) -> PangeaResponse[PangeaResponseResult]:
935
+ """
936
+ Remove a group assigned to a user
937
+
938
+ Remove a group assigned to a user
939
+
940
+ OperationId: authn_post_v2_user_group_remove
941
+ """
942
+ return self.request.post(
943
+ "v2/user/group/remove",
944
+ data={"id": user_id, "group_id": group_id},
945
+ result_class=m.PangeaResponseResult,
946
+ )
947
+
948
+ def list(self, user_id: str) -> PangeaResponse[m.GroupList]:
949
+ """
950
+ List of groups assigned to a user
951
+
952
+ Return a list of ids for groups assigned to a user
953
+
954
+ OperationId: authn_post_v2_user_group_list
955
+ """
956
+ return self.request.post("v2/user/group/list", data={"id": user_id}, result_class=m.GroupList)
957
+
904
958
  class Flow(ServiceBase):
905
- service_name = SERVICE_NAME
959
+ service_name = _SERVICE_NAME
906
960
 
907
961
  def __init__(
908
962
  self,
@@ -1042,7 +1096,7 @@ class AuthN(ServiceBase):
1042
1096
  return self.request.post("v2/flow/update", m.FlowUpdateResult, data=input.model_dump(exclude_none=True))
1043
1097
 
1044
1098
  class Agreements(ServiceBase):
1045
- service_name = SERVICE_NAME
1099
+ service_name = _SERVICE_NAME
1046
1100
 
1047
1101
  def __init__(
1048
1102
  self,
@@ -1192,3 +1246,108 @@ class AuthN(ServiceBase):
1192
1246
  return self.request.post(
1193
1247
  "v2/agreements/update", m.AgreementUpdateResult, data=input.model_dump(exclude_none=True)
1194
1248
  )
1249
+
1250
+ class Group(ServiceBase):
1251
+ service_name = _SERVICE_NAME
1252
+
1253
+ def __init__(
1254
+ self,
1255
+ token: str,
1256
+ config: PangeaConfig | None = None,
1257
+ logger_name: str = "pangea",
1258
+ ) -> None:
1259
+ super().__init__(token, config, logger_name=logger_name)
1260
+
1261
+ def create(
1262
+ self, name: str, type: str, *, description: str | None = None, attributes: dict[str, str] | None = None
1263
+ ) -> PangeaResponse[m.GroupInfo]:
1264
+ """
1265
+ Create a new group
1266
+
1267
+ Create a new group
1268
+
1269
+ OperationId: authn_post_v2_group_create
1270
+ """
1271
+ return self.request.post(
1272
+ "v2/group/create",
1273
+ data={"name": name, "type": type, "description": description, "attributes": attributes},
1274
+ result_class=m.GroupInfo,
1275
+ )
1276
+
1277
+ def delete(self, id: str) -> PangeaResponse[PangeaResponseResult]:
1278
+ """
1279
+ Delete a group
1280
+
1281
+ Delete a group
1282
+
1283
+ OperationId: authn_post_v2_group_delete
1284
+ """
1285
+ return self.request.post("v2/group/delete", data={"id": id}, result_class=PangeaResponseResult)
1286
+
1287
+ def get(self, id: str) -> PangeaResponse[m.GroupInfo]:
1288
+ """
1289
+ Get group information
1290
+
1291
+ Look up a group by ID and return its information.
1292
+
1293
+ OperationId: authn_post_v2_group_get
1294
+ """
1295
+ return self.request.post("v2/group/get", data={"id": id}, result_class=m.GroupInfo)
1296
+
1297
+ def list(
1298
+ self,
1299
+ *,
1300
+ filter: m.GroupsFilter | None = None,
1301
+ last: str | None = None,
1302
+ order: Literal["asc", "desc"] | None = None,
1303
+ order_by: Literal["id", "created_at", "updated_at", "name", "type"] | None = None,
1304
+ size: int | None = None,
1305
+ ) -> PangeaResponse[m.GroupList]:
1306
+ """
1307
+ List groups
1308
+
1309
+ Look up groups by name, type, or attributes.
1310
+ """
1311
+ return self.request.post(
1312
+ "v2/group/list",
1313
+ data={"filter": filter, "last": last, "order": order, "order_by": order_by, "size": size},
1314
+ result_class=m.GroupList,
1315
+ )
1316
+
1317
+ def list_users(
1318
+ self, id: str, *, last: str | None = None, size: int | None = None
1319
+ ) -> PangeaResponse[m.GroupUserList]:
1320
+ """
1321
+ List of users assigned to a group
1322
+
1323
+ Return a list of ids for users assigned to a group
1324
+
1325
+ OperationId: authn_post_v2_group_user_list
1326
+ """
1327
+ return self.request.post(
1328
+ "v2/group/user/list",
1329
+ data={"id": id, "last": last, "size": size},
1330
+ result_class=m.GroupUserList,
1331
+ )
1332
+
1333
+ def update(
1334
+ self,
1335
+ id: str,
1336
+ *,
1337
+ name: str | None = None,
1338
+ description: str | None = None,
1339
+ type: str | None = None,
1340
+ attributes: dict[str, str] | None = None,
1341
+ ) -> PangeaResponse[m.GroupInfo]:
1342
+ """
1343
+ Update group information
1344
+
1345
+ Update group information
1346
+
1347
+ OperationId: authn_post_v2_group_update
1348
+ """
1349
+ return self.request.post(
1350
+ "v2/group/update",
1351
+ data={"id": id, "name": name, "description": description, "type": type, "attributes": attributes},
1352
+ result_class=m.GroupInfo,
1353
+ )
@@ -939,3 +939,97 @@ class AgreementUpdateRequest(APIRequestModel):
939
939
 
940
940
  class AgreementUpdateResult(AgreementInfo):
941
941
  pass
942
+
943
+
944
+ class GroupInfo(PangeaResponseResult):
945
+ """A group and its information"""
946
+
947
+ id: str
948
+ name: str
949
+ type: str
950
+
951
+ description: Optional[str] = None
952
+ attributes: Optional[Dict[str, str]] = None
953
+ created_at: Optional[str] = None
954
+ updated_at: Optional[str] = None
955
+
956
+
957
+ class GroupsFilter(APIRequestModel):
958
+ """Search filter for groups"""
959
+
960
+ created_at: Optional[str] = None
961
+ """Only records where created_at equals this value."""
962
+
963
+ created_at__gt: Optional[str] = None
964
+ """Only records where created_at is greater than this value."""
965
+
966
+ created_at__gte: Optional[str] = None
967
+ """Only records where created_at is greater than or equal to this value."""
968
+
969
+ created_at__lt: Optional[str] = None
970
+ """Only records where created_at is less than this value."""
971
+
972
+ created_at__lte: Optional[str] = None
973
+ """Only records where created_at is less than or equal to this value."""
974
+
975
+ created_at__contains: Optional[str] = None
976
+ """Only records where created_at includes this value."""
977
+
978
+ id: Optional[str] = None
979
+ """Only records where id equals this value."""
980
+
981
+ id__contains: Optional[List[str]] = None
982
+ """Only records where id includes each substring."""
983
+
984
+ id__in: Optional[List[str]] = None
985
+ """Only records where id equals one of the provided substrings."""
986
+
987
+ name: Optional[str] = None
988
+ """Only records where name equals this value."""
989
+
990
+ name__contains: Optional[List[str]] = None
991
+ """Only records where name includes each substring."""
992
+
993
+ name__in: Optional[List[str]] = None
994
+ """Only records where name equals one of the provided substrings."""
995
+
996
+ type: Optional[str] = None
997
+ """Only records where type equals this value."""
998
+
999
+ type__contains: Optional[List[str]] = None
1000
+ """Only records where type includes each substring."""
1001
+
1002
+ type__in: Optional[List[str]] = None
1003
+ """Only records where type equals one of the provided substrings."""
1004
+
1005
+ updated_at: Optional[str] = None
1006
+ """Only records where updated_at equals this value."""
1007
+
1008
+ updated_at__gt: Optional[str] = None
1009
+ """Only records where updated_at is greater than this value."""
1010
+
1011
+ updated_at__gte: Optional[str] = None
1012
+ """Only records where updated_at is greater than or equal to this value."""
1013
+
1014
+ updated_at__lt: Optional[str] = None
1015
+ """Only records where updated_at is less than this value."""
1016
+
1017
+ updated_at__lte: Optional[str] = None
1018
+ """Only records where updated_at is less than or equal to this value."""
1019
+
1020
+ updated_at__contains: Optional[str] = None
1021
+ """Only records where updated_at includes this value."""
1022
+
1023
+
1024
+ class GroupList(PangeaResponseResult):
1025
+ groups: List[GroupInfo]
1026
+ """List of matching groups"""
1027
+
1028
+ count: int
1029
+ last: Optional[str] = None
1030
+
1031
+
1032
+ class GroupUserList(PangeaResponseResult):
1033
+ users: List[User]
1034
+ count: int
1035
+ last: Optional[str] = None
pangea/services/authz.py CHANGED
@@ -51,6 +51,8 @@ class Tuple(PangeaResponseResult):
51
51
  resource: Resource
52
52
  relation: str
53
53
  subject: Subject
54
+ expires_at: Optional[str] = None
55
+ """A time in ISO-8601 format"""
54
56
 
55
57
 
56
58
  class TupleCreateRequest(APIRequestModel):
@@ -63,23 +65,51 @@ class TupleCreateResult(PangeaResponseResult):
63
65
 
64
66
  class TupleListFilter(APIRequestModel):
65
67
  resource_type: Optional[str] = None
68
+ """Only records where resource type equals this value."""
66
69
  resource_type__contains: Optional[List[str]] = None
70
+ """Only records where resource type includes each substring."""
67
71
  resource_type__in: Optional[List[str]] = None
72
+ """Only records where resource type equals one of the provided substrings."""
68
73
  resource_id: Optional[str] = None
74
+ """Only records where resource id equals this value."""
69
75
  resource_id__contains: Optional[List[str]] = None
76
+ """Only records where resource id includes each substring."""
70
77
  resource_id__in: Optional[List[str]] = None
78
+ """Only records where resource id equals one of the provided substrings."""
71
79
  relation: Optional[str] = None
80
+ """Only records where relation equals this value."""
72
81
  relation__contains: Optional[List[str]] = None
82
+ """Only records where relation includes each substring."""
73
83
  relation__in: Optional[List[str]] = None
84
+ """Only records where relation equals one of the provided substrings."""
74
85
  subject_type: Optional[str] = None
86
+ """Only records where subject type equals this value."""
75
87
  subject_type__contains: Optional[List[str]] = None
88
+ """Only records where subject type includes each substring."""
76
89
  subject_type__in: Optional[List[str]] = None
90
+ """Only records where subject type equals one of the provided substrings."""
77
91
  subject_id: Optional[str] = None
92
+ """Only records where subject id equals this value."""
78
93
  subject_id__contains: Optional[List[str]] = None
94
+ """Only records where subject id includes each substring."""
79
95
  subject_id__in: Optional[List[str]] = None
96
+ """Only records where subject id equals one of the provided substrings."""
80
97
  subject_action: Optional[str] = None
98
+ """Only records where subject action equals this value."""
81
99
  subject_action__contains: Optional[List[str]] = None
100
+ """Only records where subject action includes each substring."""
82
101
  subject_action__in: Optional[List[str]] = None
102
+ """Only records where subject action equals one of the provided substrings."""
103
+ expires_at: Optional[str] = None
104
+ """Only records where expires_at equals this value."""
105
+ expires_at__gt: Optional[str] = None
106
+ """Only records where expires_at is greater than this value."""
107
+ expires_at__gte: Optional[str] = None
108
+ """Only records where expires_at is greater than or equal to this value."""
109
+ expires_at__lt: Optional[str] = None
110
+ """Only records where expires_at is less than this value."""
111
+ expires_at__lte: Optional[str] = None
112
+ """Only records where expires_at is less than or equal to this value."""
83
113
 
84
114
 
85
115
  class TupleListRequest(APIRequestModel):
@@ -109,7 +139,9 @@ class CheckRequest(APIRequestModel):
109
139
  action: str
110
140
  subject: Subject
111
141
  debug: Optional[bool] = None
142
+ """In the event of an allowed check, return a path that granted access."""
112
143
  attributes: Optional[Dict[str, Any]] = None
144
+ """A JSON object of attribute data."""
113
145
 
114
146
 
115
147
  class DebugPath(APIResponseModel):
@@ -145,6 +177,8 @@ class ListSubjectsRequest(APIRequestModel):
145
177
  resource: Resource
146
178
  action: str
147
179
  attributes: Optional[Dict[str, Any]] = None
180
+ debug: Optional[bool] = None
181
+ """Return a path for each found subject"""
148
182
 
149
183
 
150
184
  class ListSubjectsResult(PangeaResponseResult):
@@ -194,14 +228,14 @@ class AuthZ(ServiceBase):
194
228
 
195
229
  super().__init__(token, config, logger_name, config_id=config_id)
196
230
 
197
- def tuple_create(self, tuples: List[Tuple]) -> PangeaResponse[TupleCreateResult]:
231
+ def tuple_create(self, tuples: list[Tuple]) -> PangeaResponse[TupleCreateResult]:
198
232
  """Create tuples.
199
233
 
200
234
  Create tuples in the AuthZ Service. The request will fail if there is no schema
201
235
  or the tuples do not validate against the schema.
202
236
 
203
237
  Args:
204
- tuples (List[Tuple]): List of tuples to be created.
238
+ tuples: Tuples to be created.
205
239
 
206
240
  Raises:
207
241
  PangeaAPIException: If an API Error happens.
@@ -229,10 +263,10 @@ class AuthZ(ServiceBase):
229
263
  def tuple_list(
230
264
  self,
231
265
  filter: TupleListFilter,
232
- size: Optional[int] = None,
233
- last: Optional[str] = None,
234
- order: Optional[ItemOrder] = None,
235
- order_by: Optional[TupleOrderBy] = None,
266
+ size: int | None = None,
267
+ last: str | None = None,
268
+ order: ItemOrder | None = None,
269
+ order_by: TupleOrderBy | None = None,
236
270
  ) -> PangeaResponse[TupleListResult]:
237
271
  """List tuples.
238
272
 
@@ -241,11 +275,11 @@ class AuthZ(ServiceBase):
241
275
  is empty it will return all the tuples.
242
276
 
243
277
  Args:
244
- filter (TupleListFilter): The filter for listing tuples.
245
- size (Optional[int]): The size of the result set. Default is None.
246
- last (Optional[str]): The last token from a previous response. Default is None.
247
- order (Optional[ItemOrder]): Order results asc(ending) or desc(ending).
248
- order_by (Optional[TupleOrderBy]): Which field to order results by.
278
+ filter: The filter for listing tuples.
279
+ size: The size of the result set. Default is None.
280
+ last: The last token from a previous response. Default is None.
281
+ order: Order results asc(ending) or desc(ending).
282
+ order_by: Which field to order results by.
249
283
 
250
284
  Raises:
251
285
  PangeaAPIException: If an API Error happens.
@@ -263,13 +297,13 @@ class AuthZ(ServiceBase):
263
297
  )
264
298
  return self.request.post("v1/tuple/list", TupleListResult, data=input_data.model_dump(exclude_none=True))
265
299
 
266
- def tuple_delete(self, tuples: List[Tuple]) -> PangeaResponse[TupleDeleteResult]:
300
+ def tuple_delete(self, tuples: list[Tuple]) -> PangeaResponse[TupleDeleteResult]:
267
301
  """Delete tuples.
268
302
 
269
303
  Delete tuples in the AuthZ Service.
270
304
 
271
305
  Args:
272
- tuples (List[Tuple]): List of tuples to be deleted.
306
+ tuples: Tuples to be deleted.
273
307
 
274
308
  Raises:
275
309
  PangeaAPIException: If an API Error happens.
@@ -299,19 +333,19 @@ class AuthZ(ServiceBase):
299
333
  resource: Resource,
300
334
  action: str,
301
335
  subject: Subject,
302
- debug: Optional[bool] = None,
303
- attributes: Optional[Dict[str, Any]] = None,
336
+ debug: bool | None = None,
337
+ attributes: dict[str, Any] | None = None,
304
338
  ) -> PangeaResponse[CheckResult]:
305
339
  """Perform a check request.
306
340
 
307
341
  Check if a subject has permission to perform an action on the resource.
308
342
 
309
343
  Args:
310
- resource (Resource): The resource to check.
311
- action (str): The action to check.
312
- subject (Subject): The subject to check.
313
- debug (Optional[bool]): Setting this value to True will provide a detailed analysis of the check.
314
- attributes (Optional[Dict[str, Any]]): Additional attributes for the check.
344
+ resource: The resource to check.
345
+ action: The action to check.
346
+ subject: The subject to check.
347
+ debug: In the event of an allowed check, return a path that granted access.
348
+ attributes: Additional attributes for the check.
315
349
 
316
350
  Raises:
317
351
  PangeaAPIException: If an API Error happens.
@@ -334,7 +368,7 @@ class AuthZ(ServiceBase):
334
368
  return self.request.post("v1/check", CheckResult, data=input_data.model_dump(exclude_none=True))
335
369
 
336
370
  def list_resources(
337
- self, type: str, action: str, subject: Subject, attributes: Optional[Dict[str, Any]] = None
371
+ self, type: str, action: str, subject: Subject, attributes: dict[str, Any] | None = None
338
372
  ) -> PangeaResponse[ListResourcesResult]:
339
373
  """List resources.
340
374
 
@@ -342,10 +376,10 @@ class AuthZ(ServiceBase):
342
376
  type that the subject has access to the action with.
343
377
 
344
378
  Args:
345
- type (str): The type to filter resources.
346
- action (str): The action to filter resources.
347
- subject (Subject): The subject to filter resources.
348
- attributes (Optional[Dict[str, Any]]): A JSON object of attribute data.
379
+ type: The type to filter resources.
380
+ action: The action to filter resources.
381
+ subject: The subject to filter resources.
382
+ attributes: A JSON object of attribute data.
349
383
 
350
384
  Raises:
351
385
  PangeaAPIException: If an API Error happens.
@@ -369,7 +403,7 @@ class AuthZ(ServiceBase):
369
403
  )
370
404
 
371
405
  def list_subjects(
372
- self, resource: Resource, action: str, attributes: Optional[Dict[str, Any]] = None
406
+ self, resource: Resource, action: str, attributes: dict[str, Any] | None = None, *, debug: bool | None = None
373
407
  ) -> PangeaResponse[ListSubjectsResult]:
374
408
  """List subjects.
375
409
 
@@ -377,9 +411,10 @@ class AuthZ(ServiceBase):
377
411
  access to the action for the given resource.
378
412
 
379
413
  Args:
380
- resource (Resource): The resource to filter subjects.
381
- action (str): The action to filter subjects.
382
- attributes (Optional[Dict[str, Any]]): A JSON object of attribute data.
414
+ resource: The resource to filter subjects.
415
+ action: The action to filter subjects.
416
+ attributes: A JSON object of attribute data.
417
+ debug: Return a path for each found subject.
383
418
 
384
419
  Raises:
385
420
  PangeaAPIException: If an API Error happens.
@@ -396,5 +431,5 @@ class AuthZ(ServiceBase):
396
431
  )
397
432
  """
398
433
 
399
- input_data = ListSubjectsRequest(resource=resource, action=action, attributes=attributes)
434
+ input_data = ListSubjectsRequest(resource=resource, action=action, attributes=attributes, debug=debug)
400
435
  return self.request.post("v1/list-subjects", ListSubjectsResult, data=input_data.model_dump(exclude_none=True))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pangea-sdk
3
- Version: 6.0.0
3
+ Version: 6.1.0
4
4
  Summary: Pangea API SDK
5
5
  License: MIT
6
6
  Keywords: Pangea,SDK,Audit
@@ -1,12 +1,12 @@
1
- pangea/__init__.py,sha256=WlZa15dkqzwfQrtPRyiGS0sYP9wBJcaBvWFl_kcNUjs,246
1
+ pangea/__init__.py,sha256=SsSFodzFjA5wp7MeTcC2YDLJyt4DeR98QUsA7pVkhf0,246
2
2
  pangea/asyncio/__init__.py,sha256=kjEMkqMQ521LlMSu5jn3_WgweyArwVZ2C-s3x7mR6Pk,45
3
3
  pangea/asyncio/file_uploader.py,sha256=wI7epib7Rc5jtZw4eJ1L1SlmutDG6CPv59C8N2UPhtY,1436
4
4
  pangea/asyncio/request.py,sha256=lpLY-o405r3-VUfrAE5uxYxI8UjM4hjPqUzAUtOGE5o,18040
5
5
  pangea/asyncio/services/__init__.py,sha256=L6Tdhjfx_ZECHskhLMPaCcOefi-r-imw6q_zlU4j-FY,464
6
6
  pangea/asyncio/services/ai_guard.py,sha256=rFksT8LQkyioW3QOq4fLCEZbW5SXiYWpWjLbVovRouE,6294
7
7
  pangea/asyncio/services/audit.py,sha256=Ue3KDmTn-a2KsqlzxbakLQJAWiRyLaJrYVi1hO7a6sw,26030
8
- pangea/asyncio/services/authn.py,sha256=rPeLJweL8mYH_t4ebcQn4n_Wglr3kClKNnCXNCimZU4,46622
9
- pangea/asyncio/services/authz.py,sha256=B_0_nhDMJcjNpjpCx3Vi2LDRhlmfV9325GKbUZ8reos,10025
8
+ pangea/asyncio/services/authn.py,sha256=apa0kcxmSXH2ChJbTYVFvXTB4cn69cN9O7tW_jOXy_w,52100
9
+ pangea/asyncio/services/authz.py,sha256=ocImj2g-P9wAo6CqAvZs7YR4wOTUAx0ZWmenKh6HwLk,9691
10
10
  pangea/asyncio/services/base.py,sha256=vRFVcO_uEAGJte3OUUBLD43RoiiFB1vC7SPyN6yEMoA,3158
11
11
  pangea/asyncio/services/embargo.py,sha256=ctzj3kip6xos-Eu3JuOskrCGYC8T3JlsgAopZHiPSXM,3068
12
12
  pangea/asyncio/services/file_scan.py,sha256=PLG1O-PL4Yk9uY9D6NbMrZ5LHg70Z311s7bFe46UMZA,7108
@@ -34,9 +34,9 @@ pangea/services/audit/exceptions.py,sha256=bhVuYe4ammacOVxwg98CChxvwZf5FKgR2Dcgq
34
34
  pangea/services/audit/models.py,sha256=1h1B9eSYQMYG3f8WNi1UcDX2-impRrET_ErjJYUnj7M,14678
35
35
  pangea/services/audit/signing.py,sha256=5A4hvPtpfP2kMz8bsiiKUACriXbh5dv9gb_rbqiUtuI,5583
36
36
  pangea/services/audit/util.py,sha256=Zq1qvfeplYfhCP_ud5YMvntSB0UvnCdsuYbOzZkHbjg,7620
37
- pangea/services/authn/authn.py,sha256=cZKl2Ixc6HwHnkRecpSaAGTQUgaZUtxfLa0T3S03HMs,45478
38
- pangea/services/authn/models.py,sha256=HH5su6jx3O9AwVGzASXZ99-eIWjgXEP5LhIVdewM13s,22394
39
- pangea/services/authz.py,sha256=bB0ZEUuXLT7Xjs5kZef1hZK6Du6VUusHe5aekNaxamw,12746
37
+ pangea/services/authn/authn.py,sha256=AkGyjGz5eHpMkxMn0F-xP94DCV7Ai88yaI4pSqAQ_qc,51021
38
+ pangea/services/authn/models.py,sha256=EOoseAWVhr7Gy2HHqxqtvyUNimoi6OVc-x9OMxEeRX8,25253
39
+ pangea/services/authz.py,sha256=fOPucOMdoYee5B4FXYgM8d6HHUeGg8QwiyTfKcD1JCk,14581
40
40
  pangea/services/base.py,sha256=43pWQcR9CeT4sGzgctF3Sy4M_h7DaUzkuZD2Z7CcDUU,3845
41
41
  pangea/services/embargo.py,sha256=9Wfku4td5ORaIENKmnGmS5jxJJIRfWp6Q51L36Jsy0I,3897
42
42
  pangea/services/file_scan.py,sha256=QiO80uKqB_BnAOiYQKznXfxpa5j40qqETE3-zBRT_QE,7813
@@ -55,6 +55,6 @@ pangea/services/vault/vault.py,sha256=ow-Zm7PYzfWIfUcA4UNnpeL2DHfZM4C7inRDmNR3zQ
55
55
  pangea/tools.py,sha256=icHduOfZLi02UYdGb5Xl1fQqu-PBRB4tiDPT_nL2Q8E,6380
56
56
  pangea/utils.py,sha256=dZ6MwFVEWXUgXvvDg-k6JnvVfsgslvtaBd7ez7afrqk,4983
57
57
  pangea/verify_audit.py,sha256=nSP17OzoSPdvezRExwfcf45H8ZPZnxZu-CbEp3qFJO0,17354
58
- pangea_sdk-6.0.0.dist-info/METADATA,sha256=X9QYxYoEw53LK2rdWTOvEs9I24--d27P7Xeft4DTGzM,6886
59
- pangea_sdk-6.0.0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
60
- pangea_sdk-6.0.0.dist-info/RECORD,,
58
+ pangea_sdk-6.1.0.dist-info/METADATA,sha256=PaLI_RGtc8TJeHm1gv4Mg7sYe8vK1zwEdDHVm_8hr1Y,6886
59
+ pangea_sdk-6.1.0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
60
+ pangea_sdk-6.1.0.dist-info/RECORD,,