2
2
3
3
import logging
4
4
import uuid
5
- from typing import Any , Dict , List , Literal , Optional , Union , cast
5
+ from typing import TYPE_CHECKING , Any , Callable , Dict , List , Literal , Optional , Set , TypeVar , Union , cast
6
6
7
7
import boto3
8
8
9
9
from awswrangler import _utils , exceptions , sts
10
- from awswrangler .quicksight ._get_list import get_data_source_arn , get_dataset_id , list_users
11
- from awswrangler .quicksight ._utils import extract_athena_query_columns , extract_athena_table_columns
10
+ from awswrangler .quicksight ._get_list import get_data_source_arn , get_dataset_id , list_groups , list_users
11
+ from awswrangler .quicksight ._utils import (
12
+ _QuicksightPrincipalList ,
13
+ extract_athena_query_columns ,
14
+ extract_athena_table_columns ,
15
+ )
16
+
17
+ if TYPE_CHECKING :
18
+ from mypy_boto3_quicksight .type_defs import GroupTypeDef , UserTypeDef
19
+
12
20
13
21
_logger : logging .Logger = logging .getLogger (__name__ )
14
22
23
+
15
24
_ALLOWED_ACTIONS : Dict [str , Dict [str , List [str ]]] = {
16
25
"data_source" : {
17
26
"allowed_to_use" : [
52
61
}
53
62
54
63
55
- def _usernames_to_arns ( user_names : List [str ], all_users : List [Dict [ str , Any ] ]) -> List [str ]:
56
- return [cast ( str , u ["Arn" ]) for u in all_users if u .get ("UserName " ) in user_names ]
64
+ def _groupnames_to_arns ( group_names : Set [str ], all_groups : List ["GroupTypeDef" ]) -> List [str ]:
65
+ return [u ["Arn" ] for u in all_groups if u .get ("GroupName " ) in group_names ]
57
66
58
67
59
- def _generate_permissions (
68
+ def _usernames_to_arns (user_names : Set [str ], all_users : List ["UserTypeDef" ]) -> List [str ]:
69
+ return [u ["Arn" ] for u in all_users if u .get ("UserName" ) in user_names ]
70
+
71
+
72
+ _PrincipalTypeDef = TypeVar ("_PrincipalTypeDef" , "UserTypeDef" , "GroupTypeDef" )
73
+
74
+
75
+ def _generate_permissions_base (
60
76
resource : str ,
61
77
namespace : str ,
62
78
account_id : str ,
63
79
boto3_session : Optional [boto3 .Session ],
64
- allowed_to_use : Optional [List [str ]] = None ,
65
- allowed_to_manage : Optional [List [str ]] = None ,
80
+ allowed_to_use : Optional [List [str ]],
81
+ allowed_to_manage : Optional [List [str ]],
82
+ principal_names_to_arns_func : Callable [[Set [str ], List [_PrincipalTypeDef ]], List [str ]],
83
+ list_principals : Callable [[str , str , Optional [boto3 .Session ]], List [Dict [str , Any ]]],
66
84
) -> List [Dict [str , Union [str , List [str ]]]]:
67
85
permissions : List [Dict [str , Union [str , List [str ]]]] = []
68
86
if (allowed_to_use is None ) and (allowed_to_manage is None ):
69
87
return permissions
70
88
71
- # Forcing same user not be in both lists at the same time.
72
- if (allowed_to_use is not None ) and (allowed_to_manage is not None ):
73
- allowed_to_use = list (set (allowed_to_use ) - set (allowed_to_manage ))
89
+ allowed_to_use_set = set (allowed_to_use ) if allowed_to_use else None
90
+ allowed_to_manage_set = set (allowed_to_manage ) if allowed_to_manage else None
74
91
75
- all_users : List [Dict [str , Any ]] = list_users (
76
- namespace = namespace , account_id = account_id , boto3_session = boto3_session
77
- )
92
+ # Forcing same principal not be in both lists at the same time.
93
+ if (allowed_to_use_set is not None ) and (allowed_to_manage_set is not None ):
94
+ allowed_to_use_set = allowed_to_use_set - allowed_to_manage_set
95
+
96
+ all_principals = cast (List [_PrincipalTypeDef ], list_principals (namespace , account_id , boto3_session ))
78
97
79
- if allowed_to_use is not None :
80
- allowed_arns : List [str ] = _usernames_to_arns ( user_names = allowed_to_use , all_users = all_users )
98
+ if allowed_to_use_set is not None :
99
+ allowed_arns : List [str ] = principal_names_to_arns_func ( allowed_to_use_set , all_principals )
81
100
permissions += [
82
101
{
83
102
"Principal" : arn ,
84
103
"Actions" : _ALLOWED_ACTIONS [resource ]["allowed_to_use" ],
85
104
}
86
105
for arn in allowed_arns
87
106
]
88
- if allowed_to_manage is not None :
89
- allowed_arns = _usernames_to_arns ( user_names = allowed_to_manage , all_users = all_users )
107
+ if allowed_to_manage_set is not None :
108
+ allowed_arns = principal_names_to_arns_func ( allowed_to_manage_set , all_principals )
90
109
permissions += [
91
110
{
92
111
"Principal" : arn ,
@@ -97,6 +116,41 @@ def _generate_permissions(
97
116
return permissions
98
117
99
118
119
+ def _generate_permissions (
120
+ resource : str ,
121
+ namespace : str ,
122
+ account_id : str ,
123
+ boto3_session : Optional [boto3 .Session ],
124
+ allowed_users_to_use : Optional [List [str ]] = None ,
125
+ allowed_groups_to_use : Optional [List [str ]] = None ,
126
+ allowed_users_to_manage : Optional [List [str ]] = None ,
127
+ allowed_groups_to_manage : Optional [List [str ]] = None ,
128
+ ) -> List [Dict [str , Union [str , List [str ]]]]:
129
+ permissions_users = _generate_permissions_base (
130
+ resource = resource ,
131
+ namespace = namespace ,
132
+ account_id = account_id ,
133
+ boto3_session = boto3_session ,
134
+ allowed_to_use = allowed_users_to_use ,
135
+ allowed_to_manage = allowed_users_to_manage ,
136
+ principal_names_to_arns_func = _usernames_to_arns ,
137
+ list_principals = list_users ,
138
+ )
139
+
140
+ permissions_groups = _generate_permissions_base (
141
+ resource = resource ,
142
+ namespace = namespace ,
143
+ account_id = account_id ,
144
+ boto3_session = boto3_session ,
145
+ allowed_to_use = allowed_groups_to_use ,
146
+ allowed_to_manage = allowed_groups_to_manage ,
147
+ principal_names_to_arns_func = _groupnames_to_arns ,
148
+ list_principals = list_groups ,
149
+ )
150
+
151
+ return permissions_users + permissions_groups
152
+
153
+
100
154
def _generate_transformations (
101
155
rename_columns : Optional [Dict [str , str ]],
102
156
cast_columns_types : Optional [Dict [str , str ]],
@@ -115,11 +169,27 @@ def _generate_transformations(
115
169
return trans
116
170
117
171
172
+ _AllowedType = Optional [Union [List [str ], _QuicksightPrincipalList ]]
173
+
174
+
175
+ def _get_principal_names (principals : _AllowedType , type : Literal ["users" , "groups" ]) -> Optional [List [str ]]:
176
+ if principals is None :
177
+ return None
178
+
179
+ if isinstance (principals , list ):
180
+ if type == "users" :
181
+ return principals
182
+ else :
183
+ return None
184
+
185
+ return principals .get (type )
186
+
187
+
118
188
def create_athena_data_source (
119
189
name : str ,
120
190
workgroup : str = "primary" ,
121
- allowed_to_use : Optional [ List [ str ]] = None ,
122
- allowed_to_manage : Optional [ List [ str ]] = None ,
191
+ allowed_to_use : _AllowedType = None ,
192
+ allowed_to_manage : _AllowedType = None ,
123
193
tags : Optional [Dict [str , str ]] = None ,
124
194
account_id : Optional [str ] = None ,
125
195
boto3_session : Optional [boto3 .Session ] = None ,
@@ -140,13 +210,19 @@ def create_athena_data_source(
140
210
Athena workgroup.
141
211
tags : Dict[str, str], optional
142
212
Key/Value collection to put on the Cluster.
143
- e.g. {"foo": "boo", "bar": "xoo"})
144
- allowed_to_use : optional
145
- List of principals that will be allowed to see and use the data source.
146
- e.g. ["John"]
147
- allowed_to_manage : optional
148
- List of principals that will be allowed to see, use, update and delete the data source.
149
- e.g. ["Mary"]
213
+ e.g. ```{"foo": "boo", "bar": "xoo"})```
214
+ allowed_to_use: dict["users" | "groups", list[str]], optional
215
+ Dictionary containing usernames and groups that will be allowed to see and
216
+ use the data.
217
+ e.g. ```{"users": ["john", "Mary"], "groups": ["engineering", "customers"]}```
218
+ Alternatively, if a list of string is passed,
219
+ it will be interpreted as a list of usernames only.
220
+ allowed_to_manage: dict["users" | "groups", list[str]], optional
221
+ Dictionary containing usernames and groups that will be allowed to see, use,
222
+ update and delete the data source.
223
+ e.g. ```{"users": ["Mary"], "groups": ["engineering"]}```
224
+ Alternatively, if a list of string is passed,
225
+ it will be interpreted as a list of usernames only.
150
226
account_id : str, optional
151
227
If None, the account ID will be inferred from your boto3 session.
152
228
boto3_session : boto3.Session(), optional
@@ -180,11 +256,13 @@ def create_athena_data_source(
180
256
}
181
257
permissions : List [Dict [str , Union [str , List [str ]]]] = _generate_permissions (
182
258
resource = "data_source" ,
259
+ namespace = namespace ,
183
260
account_id = account_id ,
184
261
boto3_session = boto3_session ,
185
- allowed_to_use = allowed_to_use ,
186
- allowed_to_manage = allowed_to_manage ,
187
- namespace = namespace ,
262
+ allowed_users_to_use = _get_principal_names (allowed_to_use , "users" ),
263
+ allowed_users_to_manage = _get_principal_names (allowed_to_manage , "users" ),
264
+ allowed_groups_to_use = _get_principal_names (allowed_to_use , "groups" ),
265
+ allowed_groups_to_manage = _get_principal_names (allowed_to_manage , "groups" ),
188
266
)
189
267
if permissions :
190
268
args ["Permissions" ] = permissions
@@ -203,8 +281,8 @@ def create_athena_dataset(
203
281
data_source_name : Optional [str ] = None ,
204
282
data_source_arn : Optional [str ] = None ,
205
283
import_mode : Literal ["SPICE" , "DIRECT_QUERY" ] = "DIRECT_QUERY" ,
206
- allowed_to_use : Optional [ List [ str ]] = None ,
207
- allowed_to_manage : Optional [ List [ str ]] = None ,
284
+ allowed_to_use : _AllowedType = None ,
285
+ allowed_to_manage : _AllowedType = None ,
208
286
logical_table_alias : str = "LogicalTable" ,
209
287
rename_columns : Optional [Dict [str , str ]] = None ,
210
288
cast_columns_types : Optional [Dict [str , str ]] = None ,
@@ -251,12 +329,18 @@ def create_athena_dataset(
251
329
tags : Dict[str, str], optional
252
330
Key/Value collection to put on the Cluster.
253
331
e.g. {"foo": "boo", "bar": "xoo"}
254
- allowed_to_use : optional
255
- List of usernames that will be allowed to see and use the data source.
256
- e.g. ["john", "Mary"]
257
- allowed_to_manage : optional
258
- List of usernames that will be allowed to see, use, update and delete the data source.
259
- e.g. ["Mary"]
332
+ allowed_to_use: dict["users" | "groups", list[str]], optional
333
+ Dictionary containing usernames and groups that will be allowed to see and
334
+ use the data.
335
+ e.g. ```{"users": ["john", "Mary"], "groups": ["engineering", "customers"]}```
336
+ Alternatively, if a list of string is passed,
337
+ it will be interpreted as a list of usernames only.
338
+ allowed_to_manage: dict["users" | "groups", list[str]], optional
339
+ Dictionary containing usernames and groups that will be allowed to see, use,
340
+ update and delete the data source.
341
+ e.g. ```{"users": ["Mary"], "groups": ["engineering"]}```
342
+ Alternatively, if a list of string is passed,
343
+ it will be interpreted as a list of usernames only.
260
344
logical_table_alias : str
261
345
A display name for the logical table.
262
346
rename_columns : Dict[str, str], optional
@@ -347,13 +431,16 @@ def create_athena_dataset(
347
431
)
348
432
if trans :
349
433
args ["LogicalTableMap" ][table_uuid ]["DataTransforms" ] = trans
434
+
350
435
permissions : List [Dict [str , Union [str , List [str ]]]] = _generate_permissions (
351
436
resource = "dataset" ,
437
+ namespace = namespace ,
352
438
account_id = account_id ,
353
439
boto3_session = boto3_session ,
354
- allowed_to_use = allowed_to_use ,
355
- allowed_to_manage = allowed_to_manage ,
356
- namespace = namespace ,
440
+ allowed_users_to_use = _get_principal_names (allowed_to_use , "users" ),
441
+ allowed_users_to_manage = _get_principal_names (allowed_to_manage , "users" ),
442
+ allowed_groups_to_use = _get_principal_names (allowed_to_use , "groups" ),
443
+ allowed_groups_to_manage = _get_principal_names (allowed_to_manage , "groups" ),
357
444
)
358
445
if permissions :
359
446
args ["Permissions" ] = permissions
0 commit comments