lcdp-postgres-utils 1.0.4.dev36__tar.gz → 1.5.8__tar.gz
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.
- {lcdp_postgres_utils-1.0.4.dev36 → lcdp_postgres_utils-1.5.8}/PKG-INFO +6 -3
- {lcdp_postgres_utils-1.0.4.dev36 → lcdp_postgres_utils-1.5.8}/lcdp_postgres_utils/executor_factory.py +1 -1
- {lcdp_postgres_utils-1.0.4.dev36 → lcdp_postgres_utils-1.5.8}/lcdp_postgres_utils/executors/__init__.py +1 -0
- {lcdp_postgres_utils-1.0.4.dev36 → lcdp_postgres_utils-1.5.8}/lcdp_postgres_utils/executors/executor13.py +293 -27
- {lcdp_postgres_utils-1.0.4.dev36 → lcdp_postgres_utils-1.5.8}/lcdp_postgres_utils/executors/executor14.py +15 -0
- lcdp_postgres_utils-1.5.8/lcdp_postgres_utils/executors/executor16.py +7 -0
- lcdp_postgres_utils-1.5.8/lcdp_postgres_utils/restricted_user.py +16 -0
- {lcdp_postgres_utils-1.0.4.dev36 → lcdp_postgres_utils-1.5.8}/lcdp_postgres_utils/user.py +1 -1
- {lcdp_postgres_utils-1.0.4.dev36 → lcdp_postgres_utils-1.5.8}/pyproject.toml +7 -7
- lcdp_postgres_utils-1.0.4.dev36/setup.py +0 -32
- {lcdp_postgres_utils-1.0.4.dev36 → lcdp_postgres_utils-1.5.8}/lcdp_postgres_utils/__init__.py +0 -0
- {lcdp_postgres_utils-1.0.4.dev36 → lcdp_postgres_utils-1.5.8}/lcdp_postgres_utils/utils/__init__.py +0 -0
- {lcdp_postgres_utils-1.0.4.dev36 → lcdp_postgres_utils-1.5.8}/lcdp_postgres_utils/utils/postgres.py +0 -0
- {lcdp_postgres_utils-1.0.4.dev36 → lcdp_postgres_utils-1.5.8}/lcdp_postgres_utils/utils/ssm.py +0 -0
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: lcdp-postgres-utils
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.8
|
|
4
4
|
Summary: Postgres Utils to create users, databases, functions, ...
|
|
5
5
|
Author: Le Comptoir Des Pharmacies
|
|
6
6
|
Author-email: webmaster@lecomptoirdespharmacies.fr
|
|
7
|
-
Requires-Python: >=3.8
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.8
|
|
10
10
|
Classifier: Programming Language :: Python :: 3.9
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
16
|
Requires-Dist: boto3 (>=1.26.55,<2.0.0)
|
|
14
17
|
Requires-Dist: pg8000 (>=1.29.4,<2.0.0)
|
|
@@ -5,7 +5,7 @@ class ExecutorFactory:
|
|
|
5
5
|
def __init__(self, engine_version="13"):
|
|
6
6
|
self.engine_version = engine_version.split('.')[0]
|
|
7
7
|
|
|
8
|
-
def build(self, database_name, endpoint, user_name, db_password, decrypt_func):
|
|
8
|
+
def build(self, database_name, endpoint, user_name, db_password, decrypt_func=None):
|
|
9
9
|
try:
|
|
10
10
|
klass = globals()["Executor{0}".format(self.engine_version)]
|
|
11
11
|
return klass(database_name, endpoint, user_name, db_password, decrypt_func)
|
|
@@ -3,11 +3,15 @@ from ..utils import get_connection
|
|
|
3
3
|
|
|
4
4
|
# Executor for postgres 13
|
|
5
5
|
class Executor13:
|
|
6
|
-
|
|
6
|
+
dbz_publication_prefix = "dbz"
|
|
7
7
|
dbz_signal_tablename = "debezium_signal"
|
|
8
|
+
dbz_heartbeat_tablename = "debezium_heartbeat"
|
|
9
|
+
|
|
10
|
+
ROLE_READ_ONLY = "role_read_only"
|
|
8
11
|
|
|
9
12
|
def __init__(self, database_name, endpoint, user_name, db_password, decrypt_func):
|
|
10
13
|
self.database_name = database_name
|
|
14
|
+
self.connected_user_name = user_name
|
|
11
15
|
self.__connection = get_connection(database_name, endpoint, user_name, db_password, decrypt_func)
|
|
12
16
|
self.cursor = self.__connection.cursor()
|
|
13
17
|
self.logs = []
|
|
@@ -32,9 +36,19 @@ class Executor13:
|
|
|
32
36
|
type varchar(32) NOT NULL,
|
|
33
37
|
data varchar(2048)
|
|
34
38
|
);
|
|
35
|
-
GRANT SELECT, INSERT, UPDATE, DELETE, TRUNCATE ON \"{
|
|
39
|
+
GRANT SELECT, INSERT, UPDATE, DELETE, TRUNCATE ON \"{0}\" TO \"{1}\";
|
|
36
40
|
""".format(Executor13.dbz_signal_tablename,
|
|
37
|
-
|
|
41
|
+
cdc_user_name)
|
|
42
|
+
|
|
43
|
+
def __create_debezium_heartbeat_table_sql(self, cdc_user_name):
|
|
44
|
+
return """
|
|
45
|
+
CREATE TABLE \"{0}\" (
|
|
46
|
+
id uuid DEFAULT gen_random_uuid() CONSTRAINT debezium_heartbeat_pk PRIMARY KEY,
|
|
47
|
+
last_update timestamp not null
|
|
48
|
+
);
|
|
49
|
+
INSERT INTO \"{0}\" (last_update) VALUES (now());
|
|
50
|
+
GRANT SELECT, INSERT, UPDATE, DELETE, TRUNCATE ON \"{0}\" TO \"{1}\";
|
|
51
|
+
""".format(Executor13.dbz_heartbeat_tablename,
|
|
38
52
|
cdc_user_name)
|
|
39
53
|
|
|
40
54
|
def __create_immutable_unaccent_function(self):
|
|
@@ -42,7 +56,7 @@ class Executor13:
|
|
|
42
56
|
CREATE OR REPLACE FUNCTION immutable_unaccent(text)
|
|
43
57
|
RETURNS text AS
|
|
44
58
|
$func$
|
|
45
|
-
SELECT unaccent(
|
|
59
|
+
SELECT public.unaccent($1)
|
|
46
60
|
$func$ LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT;
|
|
47
61
|
"""
|
|
48
62
|
|
|
@@ -108,18 +122,54 @@ class Executor13:
|
|
|
108
122
|
def __create_pg_trgm_extension(self):
|
|
109
123
|
return "CREATE EXTENSION IF NOT EXISTS pg_trgm;"
|
|
110
124
|
|
|
125
|
+
def __create_hstore_extension(self):
|
|
126
|
+
return "CREATE EXTENSION IF NOT EXISTS hstore;"
|
|
127
|
+
|
|
128
|
+
def __create_role_sql(self, role):
|
|
129
|
+
return "CREATE ROLE \"{0}\";".format(role)
|
|
130
|
+
|
|
111
131
|
def __create_publication(self, value):
|
|
112
132
|
return "CREATE PUBLICATION \"{0}\" FOR ALL TABLES;".format(value)
|
|
113
133
|
|
|
114
134
|
def __create_schema(self, schema_name):
|
|
115
135
|
return "CREATE SCHEMA IF NOT EXISTS \"{0}\";".format(schema_name)
|
|
116
136
|
|
|
137
|
+
def __grant_create(self):
|
|
138
|
+
return "GRANT CREATE ON DATABASE \"{0}\" TO \"{1}\";".format(self.database_name, self.connected_user_name)
|
|
139
|
+
|
|
117
140
|
def __create_user_sql(self, user_name, user_password):
|
|
118
141
|
return "CREATE USER \"{0}\" WITH PASSWORD '{1}';".format(user_name, user_password)
|
|
119
142
|
|
|
120
143
|
def __alter_database_owner_sql(self, database_name, user_name):
|
|
121
144
|
return "ALTER DATABASE \"{0}\" OWNER TO \"{1}\";".format(database_name, user_name)
|
|
122
145
|
|
|
146
|
+
def __alter_grant_read_only_sql(self, role):
|
|
147
|
+
return """
|
|
148
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO \"{0}\";
|
|
149
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON SEQUENCES TO \"{0}\";
|
|
150
|
+
""".format(role)
|
|
151
|
+
|
|
152
|
+
def __grant_select_read_only_sql(self, reader):
|
|
153
|
+
return """
|
|
154
|
+
GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{0}\";
|
|
155
|
+
GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO \"{0}\";
|
|
156
|
+
""".format(reader)
|
|
157
|
+
|
|
158
|
+
def __grant_select_on_table(self, user_name, table_name):
|
|
159
|
+
return """
|
|
160
|
+
GRANT SELECT ON TABLE \"{1}\" TO \"{0}\";
|
|
161
|
+
""".format(user_name, table_name)
|
|
162
|
+
|
|
163
|
+
def __grant_write_on_table(self, user_name, table_name):
|
|
164
|
+
return """
|
|
165
|
+
GRANT INSERT, UPDATE, DELETE ON TABLE \"{1}\" TO \"{0}\";
|
|
166
|
+
""".format(user_name, table_name)
|
|
167
|
+
|
|
168
|
+
def __grant_usage_on_all_sequences(self, user_name):
|
|
169
|
+
return """
|
|
170
|
+
GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO \"{0}\";
|
|
171
|
+
""".format(user_name)
|
|
172
|
+
|
|
123
173
|
def __alter_database_work_mem_sql(self, value):
|
|
124
174
|
return "ALTER DATABASE \"{0}\" SET work_mem TO \"{1}\";".format(self.database_name, value)
|
|
125
175
|
|
|
@@ -127,24 +177,18 @@ class Executor13:
|
|
|
127
177
|
return """
|
|
128
178
|
GRANT CONNECT ON DATABASE \"{2}\" TO \"{1}\";
|
|
129
179
|
GRANT USAGE ON SCHEMA \"{0}\" TO \"{1}\";
|
|
130
|
-
GRANT SELECT ON ALL TABLES IN SCHEMA \"{0}\" TO \"{1}\";
|
|
131
|
-
GRANT SELECT ON ALL SEQUENCES IN SCHEMA \"{0}\" TO \"{1}\";
|
|
132
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA \"{0}\" GRANT SELECT ON TABLES TO \"{1}\";
|
|
133
|
-
ALTER DEFAULT PRIVILEGES IN SCHEMA \"{0}\" GRANT SELECT ON SEQUENCES TO \"{1}\";
|
|
134
180
|
""".format(schema_name, user_name, self.database_name)
|
|
135
181
|
|
|
136
182
|
def __grant_create_table(self, user_name):
|
|
137
183
|
return f"GRANT CREATE, TEMPORARY ON DATABASE \"{self.database_name}\" TO \"{user_name}\";"
|
|
138
184
|
|
|
139
|
-
def
|
|
140
|
-
return "GRANT
|
|
141
|
-
|
|
142
|
-
def __grant_superuser_user_sql(self, user_name):
|
|
143
|
-
return "GRANT rds_superuser TO \"{0}\";".format(user_name)
|
|
185
|
+
def __grant_role_to_user_sql(self, role, user_name):
|
|
186
|
+
return "GRANT \"{0}\" TO \"{1}\";".format(role, user_name)
|
|
144
187
|
|
|
145
188
|
def __grant_schema_all_access(self, user_name, schema_name):
|
|
146
189
|
return """
|
|
147
190
|
GRANT CONNECT ON DATABASE \"{2}\" TO \"{1}\";
|
|
191
|
+
GRANT CREATE ON DATABASE \"{2}\" TO \"{1}\";
|
|
148
192
|
GRANT USAGE ON SCHEMA \"{0}\" TO \"{1}\";
|
|
149
193
|
GRANT ALL ON ALL TABLES IN SCHEMA \"{0}\" TO \"{1}\";
|
|
150
194
|
GRANT ALL ON ALL SEQUENCES IN SCHEMA \"{0}\" TO \"{1}\";
|
|
@@ -152,36 +196,51 @@ class Executor13:
|
|
|
152
196
|
ALTER DEFAULT PRIVILEGES IN SCHEMA \"{0}\" GRANT ALL ON SEQUENCES TO \"{1}\";
|
|
153
197
|
""".format(schema_name, user_name, self.database_name)
|
|
154
198
|
|
|
199
|
+
def __check_role_sql(self, role):
|
|
200
|
+
return "select * from pg_roles where rolname = '{0}';".format(role)
|
|
201
|
+
|
|
155
202
|
def __check_user_sql(self, user_name):
|
|
156
203
|
return "select * from pg_user where usename = '{0}';".format(user_name)
|
|
157
204
|
|
|
158
205
|
def __check_database_sql(self, database_name):
|
|
159
206
|
return "select * from pg_database where datname = '{0}';".format(database_name)
|
|
160
207
|
|
|
208
|
+
def __check_replication_slot_sql(self, slot_name):
|
|
209
|
+
return "select * from pg_replication_slots where slot_name = '{0}';".format(slot_name)
|
|
210
|
+
|
|
161
211
|
def __check_table_sql(self, value):
|
|
162
212
|
return "select * from pg_tables where tablename = '{0}';".format(value)
|
|
163
213
|
|
|
164
214
|
def __check_debezium_signal_table_sql(self):
|
|
165
215
|
return self.__check_table_sql(Executor13.dbz_signal_tablename)
|
|
166
216
|
|
|
217
|
+
def __check_debezium_heartbeat_table_sql(self):
|
|
218
|
+
return self.__check_table_sql(Executor13.dbz_heartbeat_tablename)
|
|
219
|
+
|
|
167
220
|
def __check_database_unaccent_extension(self):
|
|
168
221
|
return "select * from pg_extension where extname = 'unaccent';"
|
|
169
222
|
|
|
170
223
|
def __check_database_pg_trgm_extension(self):
|
|
171
224
|
return "select * from pg_extension where extname = 'pg_trgm';"
|
|
172
225
|
|
|
226
|
+
def __check_database_hstore_extension(self):
|
|
227
|
+
return "select * from pg_extension where extname = 'hstore';"
|
|
228
|
+
|
|
173
229
|
def __check_publication_sql(self, value):
|
|
174
230
|
return "select * from pg_publication where pubname = '{0}';".format(value)
|
|
175
231
|
|
|
176
232
|
# ~~~~~ Log action ~~~~~ #
|
|
233
|
+
def __log_create_role(self, role):
|
|
234
|
+
return "Creation du role : {0} \n".format(role)
|
|
235
|
+
|
|
177
236
|
def __log_create_user(self, user_name):
|
|
178
237
|
return "Creation de l'utilisateur : {0} \n".format(user_name)
|
|
179
238
|
|
|
180
239
|
def __log_create_database(self, database_name):
|
|
181
240
|
return "Creation de la base de donnees : {0} \n".format(database_name)
|
|
182
241
|
|
|
183
|
-
def
|
|
184
|
-
return "Creation de la table {0} pour la base de donnees : {1} \n".format(
|
|
242
|
+
def __log_create_table(self, tablename):
|
|
243
|
+
return "Creation de la table {0} pour la base de donnees : {1} \n".format(tablename, self.database_name)
|
|
185
244
|
|
|
186
245
|
def __log_create_unaccent_extension(self):
|
|
187
246
|
return "Creation de l'extension unaccent pour la base de donnees : {0} \n".format(self.database_name)
|
|
@@ -201,6 +260,9 @@ class Executor13:
|
|
|
201
260
|
def __log_create_pg_trgm_extension(self):
|
|
202
261
|
return "Creation de l'extension pg_trgm pour la base de donnees : {0} \n".format(self.database_name)
|
|
203
262
|
|
|
263
|
+
def __log_create_hstore_extension(self):
|
|
264
|
+
return "Creation de l'extension hstore pour la base de donnees : {0} \n".format(self.database_name)
|
|
265
|
+
|
|
204
266
|
def __log_create_publication(self, value):
|
|
205
267
|
return "Creation de la publication {0} pour la base de donnees : {1} \n".format(value, self.database_name)
|
|
206
268
|
|
|
@@ -211,6 +273,26 @@ class Executor13:
|
|
|
211
273
|
return "L'utilisateur {0} est proprietaire de la base {1} \n" \
|
|
212
274
|
.format(user_name, database_name)
|
|
213
275
|
|
|
276
|
+
def __log_alter_database_read_to_role(self, role_owner, role_reader):
|
|
277
|
+
return "Le role {0} rend sa base accessible en lecture au role {1} \n" \
|
|
278
|
+
.format(role_owner, role_reader)
|
|
279
|
+
|
|
280
|
+
def __log_alter_grant_read_only(self, reader):
|
|
281
|
+
return "La base {0} est accessible en lecture pour role {1} \n" \
|
|
282
|
+
.format(self.database_name, reader)
|
|
283
|
+
|
|
284
|
+
def __log_grant_select_on_table(self, user_name, table_name):
|
|
285
|
+
return "L'utilisateur {0} a herite des droits select sur la table {1} \n".format(user_name, table_name)
|
|
286
|
+
|
|
287
|
+
def __log_grant_write_on_table(self, user_name, table_name):
|
|
288
|
+
return "L'utilisateur {0} a herite des droits insert, update, delete sur la table {1} \n".format(user_name, table_name)
|
|
289
|
+
|
|
290
|
+
def __log_grant_usage_on_all_sequences(self, user_name):
|
|
291
|
+
return "L'utilisateur {0} a herite des droits usage sur le schema public \n".format(user_name)
|
|
292
|
+
|
|
293
|
+
def __log_grant_select_read_only(self, reader):
|
|
294
|
+
return "{0} a herite des droits read only \n".format(reader)
|
|
295
|
+
|
|
214
296
|
def __log_grant_schema_read_only_user(self, user_name, schema_name):
|
|
215
297
|
return "L'utilisateur {0} a herite des droits read only sur le schema {1} \n".format(user_name, schema_name)
|
|
216
298
|
|
|
@@ -218,6 +300,9 @@ class Executor13:
|
|
|
218
300
|
return "L'utilisateur {0} a herite des droits de creation de base de table sur la base de données {1} \n"\
|
|
219
301
|
.format(user_name, self.database_name)
|
|
220
302
|
|
|
303
|
+
def __log_grant_role_to_user(self, role, user_name):
|
|
304
|
+
return "L'utilisateur {0} a herite du role {1} \n".format(user_name, role)
|
|
305
|
+
|
|
221
306
|
def __log_grant_replication_user(self, user_name):
|
|
222
307
|
return "L'utilisateur {0} a herite des droits replication \n".format(user_name)
|
|
223
308
|
|
|
@@ -235,6 +320,11 @@ class Executor13:
|
|
|
235
320
|
self.cursor.execute(query)
|
|
236
321
|
return self.cursor.fetchone()
|
|
237
322
|
|
|
323
|
+
def __execute_create_role(self, role):
|
|
324
|
+
if not self.__execute_select_query(self.__check_role_sql(role)):
|
|
325
|
+
self.cursor.execute(self.__create_role_sql(role))
|
|
326
|
+
return self.__log_create_role(role)
|
|
327
|
+
|
|
238
328
|
def __execute_create_user(self, user_name, user_password):
|
|
239
329
|
if not self.__execute_select_query(self.__check_user_sql(user_name)):
|
|
240
330
|
self.cursor.execute(self.__create_user_sql(user_name, user_password))
|
|
@@ -271,20 +361,51 @@ class Executor13:
|
|
|
271
361
|
self.cursor.execute(self.__create_pg_trgm_extension())
|
|
272
362
|
return self.__log_create_pg_trgm_extension()
|
|
273
363
|
|
|
274
|
-
def
|
|
275
|
-
if not self.__execute_select_query(self.
|
|
276
|
-
self.cursor.execute(self.
|
|
277
|
-
return self.
|
|
364
|
+
def __execute_create_hstore_extension(self):
|
|
365
|
+
if not self.__execute_select_query(self.__check_database_hstore_extension()):
|
|
366
|
+
self.cursor.execute(self.__create_hstore_extension())
|
|
367
|
+
return self.__log_create_hstore_extension()
|
|
368
|
+
|
|
369
|
+
def __execute_create_publication(self, prefix):
|
|
370
|
+
publication_name = '{0}_{1}'.format(prefix, 'publication')
|
|
371
|
+
if not self.__execute_select_query(self.__check_publication_sql(publication_name)):
|
|
372
|
+
self.cursor.execute(self.__create_publication(publication_name))
|
|
373
|
+
return self.__log_create_publication(publication_name)
|
|
278
374
|
|
|
279
375
|
def __execute_create_debezium_signal_table(self, cdc_user_name):
|
|
280
376
|
if not self.__execute_select_query(self.__check_debezium_signal_table_sql()):
|
|
281
377
|
self.cursor.execute(self.__create_debezium_signal_table_sql(cdc_user_name))
|
|
282
|
-
return self.
|
|
378
|
+
return self.__log_create_table(Executor13.dbz_signal_tablename)
|
|
379
|
+
|
|
380
|
+
def __execute_create_debezium_heartbeat_table(self, cdc_user_name):
|
|
381
|
+
if not self.__execute_select_query(self.__check_debezium_heartbeat_table_sql()):
|
|
382
|
+
self.cursor.execute(self.__create_debezium_heartbeat_table_sql(cdc_user_name))
|
|
383
|
+
return self.__log_create_table(Executor13.dbz_heartbeat_tablename)
|
|
283
384
|
|
|
284
385
|
def __alter_database_owner(self, database_name, user_name):
|
|
285
386
|
self.cursor.execute(self.__alter_database_owner_sql(database_name, user_name))
|
|
286
387
|
return self.__log_alter_database_owner(database_name, user_name)
|
|
287
388
|
|
|
389
|
+
def __alter_grant_read_only(self, reader):
|
|
390
|
+
self.cursor.execute(self.__alter_grant_read_only_sql(reader))
|
|
391
|
+
return self.__log_alter_grant_read_only(reader)
|
|
392
|
+
|
|
393
|
+
def __execute_grant_read_on_table(self, user_name, table_name):
|
|
394
|
+
self.cursor.execute(self.__grant_select_on_table(user_name, table_name))
|
|
395
|
+
return self.__log_grant_select_on_table(user_name, table_name)
|
|
396
|
+
|
|
397
|
+
def __execute_grant_write_on_table(self, user_name, table_name):
|
|
398
|
+
self.cursor.execute(self.__grant_write_on_table(user_name, table_name))
|
|
399
|
+
return self.__log_grant_write_on_table(user_name, table_name)
|
|
400
|
+
|
|
401
|
+
def __execute_grant_usage_on_all_sequences(self, user_name):
|
|
402
|
+
self.cursor.execute(self.__grant_usage_on_all_sequences(user_name))
|
|
403
|
+
return self.__log_grant_usage_on_all_sequences(user_name)
|
|
404
|
+
|
|
405
|
+
def __grant_select_read_only(self, reader):
|
|
406
|
+
self.cursor.execute(self.__grant_select_read_only_sql(reader))
|
|
407
|
+
return self.__log_grant_select_read_only(reader)
|
|
408
|
+
|
|
288
409
|
def __alter_database_work_mem(self, value):
|
|
289
410
|
self.cursor.execute(self.__alter_database_work_mem_sql(value))
|
|
290
411
|
return self.__log_alter_database_work_mem()
|
|
@@ -297,15 +418,20 @@ class Executor13:
|
|
|
297
418
|
self.cursor.execute(self.__grant_create_table(user_name))
|
|
298
419
|
return self.__log_grant_create_table(user_name)
|
|
299
420
|
|
|
421
|
+
def __execute_grant_read_only(self, user_name):
|
|
422
|
+
self.cursor.execute(self.__grant_role_to_user_sql(Executor13.ROLE_READ_ONLY, user_name))
|
|
423
|
+
return self.__log_grant_role_to_user(Executor13.ROLE_READ_ONLY, user_name)
|
|
424
|
+
|
|
300
425
|
def __execute_grant_replication(self, user_name):
|
|
301
|
-
self.cursor.execute(self.
|
|
426
|
+
self.cursor.execute(self.__grant_role_to_user_sql("rds_replication", user_name))
|
|
302
427
|
return self.__log_grant_replication_user(user_name)
|
|
303
428
|
|
|
304
429
|
def __execute_grant_superuser(self, user_name):
|
|
305
|
-
self.cursor.execute(self.
|
|
430
|
+
self.cursor.execute(self.__grant_role_to_user_sql("rds_superuser", user_name))
|
|
306
431
|
return self.__log_grant_superuser_user(user_name)
|
|
307
432
|
|
|
308
433
|
def __execute_create_schema(self, schema_name):
|
|
434
|
+
self.cursor.execute(self.__grant_create())
|
|
309
435
|
self.cursor.execute(self.__create_schema(schema_name))
|
|
310
436
|
return self.__log_create_schema(schema_name)
|
|
311
437
|
|
|
@@ -317,8 +443,25 @@ class Executor13:
|
|
|
317
443
|
def create_service(self, database_name, user_name, user_password):
|
|
318
444
|
log_user = self.__execute_create_user(user_name, user_password)
|
|
319
445
|
log_database = self.__execute_create_database(database_name)
|
|
320
|
-
|
|
321
|
-
self.logs.extend(list(filter(None, [log_user, log_database,
|
|
446
|
+
log_alter_owner = self.__alter_database_owner(database_name, user_name)
|
|
447
|
+
self.logs.extend(list(filter(None, [log_user, log_database, log_alter_owner])))
|
|
448
|
+
|
|
449
|
+
def create_role(self, role):
|
|
450
|
+
log = self.__execute_create_role(role)
|
|
451
|
+
self.logs.extend(list(filter(None, [log])))
|
|
452
|
+
|
|
453
|
+
def create_read_only_role(self):
|
|
454
|
+
log = self.__execute_create_role(Executor13.ROLE_READ_ONLY)
|
|
455
|
+
self.logs.extend(list(filter(None, [log])))
|
|
456
|
+
|
|
457
|
+
# Grant read capabilities to objects of the database for read only role
|
|
458
|
+
# If 'existing' = True then will give the read access to already existing objects
|
|
459
|
+
def grant_read_only_capabilities(self, existing=False):
|
|
460
|
+
log_alter_read = self.__alter_grant_read_only(Executor13.ROLE_READ_ONLY)
|
|
461
|
+
log_grant_read = None
|
|
462
|
+
if existing:
|
|
463
|
+
log_grant_read = self.__grant_select_read_only(Executor13.ROLE_READ_ONLY)
|
|
464
|
+
self.logs.extend(list(filter(None, [log_alter_read, log_grant_read])))
|
|
322
465
|
|
|
323
466
|
def create_unaccent_extension(self):
|
|
324
467
|
log_unaccent_extension = self.__execute_create_unaccent_extension()
|
|
@@ -344,6 +487,10 @@ class Executor13:
|
|
|
344
487
|
log_pg_trgm_extension = self.__execute_create_pg_trgm_extension()
|
|
345
488
|
self.logs.extend(list(filter(None, [log_pg_trgm_extension])))
|
|
346
489
|
|
|
490
|
+
def create_hstore_extension(self):
|
|
491
|
+
log_hstore_extension = self.__execute_create_hstore_extension()
|
|
492
|
+
self.logs.extend(list(filter(None, [log_hstore_extension])))
|
|
493
|
+
|
|
347
494
|
def create_user(self, user_name, user_password):
|
|
348
495
|
log_user = self.__execute_create_user(user_name, user_password)
|
|
349
496
|
self.logs.extend(list(filter(None, [log_user])))
|
|
@@ -351,7 +498,39 @@ class Executor13:
|
|
|
351
498
|
def create_cdc_user(self, user_name, user_password):
|
|
352
499
|
log_user = self.__execute_create_user(user_name, user_password)
|
|
353
500
|
log_grant_replication = self.__execute_grant_replication(user_name)
|
|
354
|
-
self.
|
|
501
|
+
log_grant_read_only = self.__execute_grant_read_only(user_name)
|
|
502
|
+
self.logs.extend(list(filter(None, [log_user, log_grant_replication, log_grant_read_only])))
|
|
503
|
+
|
|
504
|
+
def create_etl_user(self, user_name, user_password):
|
|
505
|
+
log_user = self.__execute_create_user(user_name, user_password)
|
|
506
|
+
log_grant_replication = self.__execute_grant_replication(user_name)
|
|
507
|
+
log_grant_read_only = self.__execute_grant_read_only(user_name)
|
|
508
|
+
self.logs.extend(list(filter(None, [log_user, log_grant_replication, log_grant_read_only])))
|
|
509
|
+
|
|
510
|
+
def create_read_only_user(self, user_name, user_password):
|
|
511
|
+
log_user = self.__execute_create_user(user_name, user_password)
|
|
512
|
+
log_grant_read_only = self.__execute_grant_read_only(user_name)
|
|
513
|
+
self.logs.extend(list(filter(None, [log_user, log_grant_read_only])))
|
|
514
|
+
|
|
515
|
+
def create_restricted_user(self, user_name, user_password):
|
|
516
|
+
log_user = self.__execute_create_user(user_name, user_password)
|
|
517
|
+
self.logs.extend(list(filter(None, [log_user])))
|
|
518
|
+
|
|
519
|
+
# permissions = { <table_name:string>: <'r' | 'w' | 'rw'> }
|
|
520
|
+
# permissions = { 'product': 'r', 'order': 'w', 'message': 'rw' }
|
|
521
|
+
def setup_restricted_user(self, user_name, permissions):
|
|
522
|
+
logs = []
|
|
523
|
+
for table_name, rights in permissions.items():
|
|
524
|
+
if self.__execute_select_query(self.__check_table_sql(table_name)) is not None:
|
|
525
|
+
if 'r' in rights:
|
|
526
|
+
logs.append(self.__execute_grant_read_on_table(user_name, table_name))
|
|
527
|
+
if 'w' in rights:
|
|
528
|
+
logs.append(self.__execute_grant_write_on_table(user_name, table_name))
|
|
529
|
+
if any('w' in value for value in permissions.values()):
|
|
530
|
+
# Because we will not enumerate permissions for every sequence,
|
|
531
|
+
# if user has write access, allow usage on all sequences
|
|
532
|
+
logs.append(self.__execute_grant_usage_on_all_sequences(user_name))
|
|
533
|
+
self.logs.extend(list(filter(None, logs)))
|
|
355
534
|
|
|
356
535
|
def create_schema(self, schema_name):
|
|
357
536
|
log_create_schema = self.__execute_create_schema(schema_name)
|
|
@@ -367,9 +546,29 @@ class Executor13:
|
|
|
367
546
|
|
|
368
547
|
def setup_cdc(self, cdc_user_name):
|
|
369
548
|
log_grant_read_only = self.__execute_grant_schema_read_only(cdc_user_name, "public")
|
|
370
|
-
log_publication = self.__execute_create_publication(Executor13.
|
|
549
|
+
log_publication = self.__execute_create_publication(Executor13.dbz_publication_prefix)
|
|
371
550
|
log_debezium_signal_table = self.__execute_create_debezium_signal_table(cdc_user_name)
|
|
372
|
-
self.
|
|
551
|
+
log_debezium_heartbeat_table = self.__execute_create_debezium_heartbeat_table(cdc_user_name)
|
|
552
|
+
self.logs.extend(list(filter(None, [log_grant_read_only, log_publication, log_debezium_signal_table, log_debezium_heartbeat_table])))
|
|
553
|
+
|
|
554
|
+
def setup_stitch(self):
|
|
555
|
+
log_create_replication_slot = self.__execute_create_database_replication_slot('stitch', slot_type='wal2json')
|
|
556
|
+
self.logs.extend(list(filter(None, [log_create_replication_slot])))
|
|
557
|
+
|
|
558
|
+
def setup_airbyte(self):
|
|
559
|
+
log_create_publication = self.__execute_create_publication('airbyte')
|
|
560
|
+
log_create_replication_slot = self.__execute_create_database_replication_slot('airbyte', slot_type='pgoutput')
|
|
561
|
+
self.logs.extend(list(filter(None, [log_create_publication, log_create_replication_slot])))
|
|
562
|
+
|
|
563
|
+
def setup_fivetran(self):
|
|
564
|
+
log_create_publication = self.__execute_create_publication('fivetran')
|
|
565
|
+
log_create_replication_slot = self.__execute_create_database_replication_slot('fivetran', slot_type='pgoutput')
|
|
566
|
+
log_grant_replication = self.__execute_grant_replication('user-fivetran')
|
|
567
|
+
self.logs.extend(list(filter(None, [log_create_publication, log_create_replication_slot, log_grant_replication])))
|
|
568
|
+
|
|
569
|
+
def setup_etl(self, etl_user_name):
|
|
570
|
+
log_grant_read_only = self.__execute_grant_schema_read_only(etl_user_name, "public")
|
|
571
|
+
self.logs.extend(list(filter(None, [log_grant_read_only])))
|
|
373
572
|
|
|
374
573
|
def setup_schema_write_user(self, user_name, schema_name):
|
|
375
574
|
log_grant_access = self.__execute_grant_schema_all_access(user_name, schema_name)
|
|
@@ -382,3 +581,70 @@ class Executor13:
|
|
|
382
581
|
def setup_etl_user(self, user_name):
|
|
383
582
|
log_grant_create_table = self.__execute_grant_create_table(user_name)
|
|
384
583
|
self.logs.extend(list(filter(None, [log_grant_create_table])))
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
def create_database_publication(self, prefix):
|
|
587
|
+
log_create_publication = self.__execute_create_publication(prefix)
|
|
588
|
+
self.logs.extend(list(filter(None, [log_create_publication])))
|
|
589
|
+
|
|
590
|
+
def create_database_replication_slot(self, prefix, slot_type='pgoutput'):
|
|
591
|
+
log_create_slot = self.__execute_create_database_replication_slot(prefix, slot_type)
|
|
592
|
+
self.logs.extend(list(filter(None, [log_create_slot])))
|
|
593
|
+
|
|
594
|
+
def __execute_create_database_replication_slot(self, prefix, slot_type):
|
|
595
|
+
slot_name = '{0}_{1}'.format(prefix, self.database_name)
|
|
596
|
+
if not self.__execute_select_query(self.__check_replication_slot_sql(slot_name)):
|
|
597
|
+
self.cursor.execute(self.__create_database_replication_slot(slot_name, slot_type))
|
|
598
|
+
return self.__log_create_database_replication_slot(slot_name, slot_type)
|
|
599
|
+
|
|
600
|
+
def __create_database_replication_slot(self, slot_name, slot_type):
|
|
601
|
+
return "SELECT * FROM pg_create_logical_replication_slot('{0}', '{1}');"\
|
|
602
|
+
.format(slot_name, slot_type)
|
|
603
|
+
|
|
604
|
+
def __log_create_database_replication_slot(self, slot_name, slot_type):
|
|
605
|
+
return "Creation du slot de replication logique {0} {1} \n".format(slot_type, slot_name)
|
|
606
|
+
|
|
607
|
+
def change_schema_owner(self, schema_name, user_name):
|
|
608
|
+
log_change_schema_owner = self._change_schema_owner(schema_name, user_name)
|
|
609
|
+
self.logs.extend(list(filter(None, [log_change_schema_owner])))
|
|
610
|
+
|
|
611
|
+
def _change_schema_owner(self, schema_name, user_name):
|
|
612
|
+
self.cursor.execute("ALTER SCHEMA \"{0}\" OWNER TO \"{1}\"".format(schema_name, user_name))
|
|
613
|
+
return "Set db {0} schema owner: {1} ".format(schema_name, user_name)
|
|
614
|
+
|
|
615
|
+
def setup_datadog(self, datadog_user_name):
|
|
616
|
+
log_grant_datadog = self._setup_datadog(datadog_user_name)
|
|
617
|
+
self.logs.extend(list(filter(None, [log_grant_datadog])))
|
|
618
|
+
|
|
619
|
+
def _setup_datadog(self, datadog_user_name):
|
|
620
|
+
self.cursor.execute(
|
|
621
|
+
"""
|
|
622
|
+
CREATE SCHEMA IF NOT EXISTS datadog;
|
|
623
|
+
GRANT USAGE ON SCHEMA public TO \"{0}\";
|
|
624
|
+
GRANT USAGE ON SCHEMA datadog TO \"{0}\";
|
|
625
|
+
GRANT pg_monitor TO \"{0}\";
|
|
626
|
+
CREATE EXTENSION IF NOT EXISTS pg_stat_statements schema public;
|
|
627
|
+
|
|
628
|
+
CREATE OR REPLACE FUNCTION datadog.explain_statement(
|
|
629
|
+
l_query TEXT,
|
|
630
|
+
OUT explain JSON
|
|
631
|
+
)
|
|
632
|
+
RETURNS SETOF JSON AS
|
|
633
|
+
$$
|
|
634
|
+
DECLARE
|
|
635
|
+
curs REFCURSOR;
|
|
636
|
+
plan JSON;
|
|
637
|
+
|
|
638
|
+
BEGIN
|
|
639
|
+
OPEN curs FOR EXECUTE pg_catalog.concat('EXPLAIN (FORMAT JSON) ', l_query);
|
|
640
|
+
FETCH curs INTO plan;
|
|
641
|
+
CLOSE curs;
|
|
642
|
+
RETURN QUERY SELECT plan;
|
|
643
|
+
END;
|
|
644
|
+
$$
|
|
645
|
+
LANGUAGE 'plpgsql'
|
|
646
|
+
RETURNS NULL ON NULL INPUT
|
|
647
|
+
SECURITY DEFINER;
|
|
648
|
+
""".format(datadog_user_name)
|
|
649
|
+
)
|
|
650
|
+
return "Setup datadog Database monitoring"
|
|
@@ -9,14 +9,29 @@ class Executor14(Executor13):
|
|
|
9
9
|
def __grant_read_only_roles(self, user_name):
|
|
10
10
|
return "GRANT pg_read_all_data TO \"{0}\";".format(user_name)
|
|
11
11
|
|
|
12
|
+
def __grant_write_all_data_role(self, user_name):
|
|
13
|
+
return f"GRANT pg_write_all_data TO \"{user_name}\";"
|
|
14
|
+
|
|
12
15
|
def __log_grant_read_only_roles(self, user_name):
|
|
13
16
|
return "L'utilisateur {0} a herite des droits read only sur toutes les tables, vues et sequences \n"\
|
|
14
17
|
.format(user_name)
|
|
15
18
|
|
|
19
|
+
def __log_grant_write_all_data_role(self, user_name):
|
|
20
|
+
return "L'utilisateur {0} a herite des droits d'écriture sur toutes les tables, vues et sequences \n"\
|
|
21
|
+
.format(user_name)
|
|
22
|
+
|
|
16
23
|
def __execute_grant_read_only_roles(self, user_name):
|
|
17
24
|
self.cursor.execute(self.__grant_read_only_roles(user_name))
|
|
18
25
|
return self.__log_grant_read_only_roles(user_name)
|
|
19
26
|
|
|
27
|
+
def __execute_grant_write_all_data_role(self, user_name):
|
|
28
|
+
self.cursor.execute(self.__grant_write_all_data_role(user_name))
|
|
29
|
+
return self.__log_grant_write_all_data_role(user_name)
|
|
30
|
+
|
|
31
|
+
def grant_write_all_data_role(self, user_name):
|
|
32
|
+
log = self.__execute_grant_write_all_data_role(user_name)
|
|
33
|
+
self.logs.extend(list(filter(None, [log])))
|
|
34
|
+
|
|
20
35
|
def setup_hawking(self, hawking_user_name):
|
|
21
36
|
log_grant_read_only = self.__execute_grant_read_only_roles(hawking_user_name)
|
|
22
37
|
self.logs.extend(list(filter(None, [log_grant_read_only])))
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from .user import User
|
|
2
|
+
|
|
3
|
+
class RestrictedUser(User):
|
|
4
|
+
database = None
|
|
5
|
+
permissions = {}
|
|
6
|
+
|
|
7
|
+
def __init__(self, credentials, decrypt_func=None):
|
|
8
|
+
super().__init__(credentials, decrypt_func)
|
|
9
|
+
self.database = credentials['database']
|
|
10
|
+
self.permissions = credentials['permissions']
|
|
11
|
+
|
|
12
|
+
def get_database(self):
|
|
13
|
+
return self.database
|
|
14
|
+
|
|
15
|
+
def get_permissions(self):
|
|
16
|
+
return self.permissions
|
|
@@ -2,7 +2,7 @@ class User:
|
|
|
2
2
|
name = None
|
|
3
3
|
password = None
|
|
4
4
|
|
|
5
|
-
def __init__(self, credentials, decrypt_func):
|
|
5
|
+
def __init__(self, credentials, decrypt_func=None):
|
|
6
6
|
self.name = credentials['user_name']
|
|
7
7
|
self.password = decrypt_func(credentials["user_password"]) if decrypt_func else credentials["user_password"]
|
|
8
8
|
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "lcdp-postgres-utils"
|
|
3
3
|
# https://github.com/python-poetry/poetry/issues/1208
|
|
4
|
-
version = "1.
|
|
4
|
+
version = "1.5.8"
|
|
5
5
|
description = "Postgres Utils to create users, databases, functions, ..."
|
|
6
6
|
authors = ["Le Comptoir Des Pharmacies <webmaster@lecomptoirdespharmacies.fr>"]
|
|
7
7
|
|
|
8
8
|
[tool.poetry-dynamic-versioning]
|
|
9
9
|
enable = false
|
|
10
10
|
vcs = "git"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
|
|
12
|
+
[tool.poetry.requires-plugins]
|
|
13
|
+
poetry-dynamic-versioning = { version = ">=1.0.0,<2.0.0", extras = ["plugin"] }
|
|
14
14
|
|
|
15
15
|
[tool.poetry.dependencies]
|
|
16
|
-
python = "
|
|
16
|
+
python = ">=3.8"
|
|
17
17
|
boto3 = "^1.26.55"
|
|
18
18
|
pg8000 = "^1.29.4"
|
|
19
19
|
|
|
20
20
|
[tool.poetry.dev-dependencies]
|
|
21
21
|
|
|
22
22
|
[build-system]
|
|
23
|
-
requires = ["poetry>=1.
|
|
24
|
-
build-backend = "
|
|
23
|
+
requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning>=1.0.0,<2.0.0"]
|
|
24
|
+
build-backend = "poetry_dynamic_versioning.backend"
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
from setuptools import setup
|
|
3
|
-
|
|
4
|
-
packages = \
|
|
5
|
-
['lcdp_postgres_utils',
|
|
6
|
-
'lcdp_postgres_utils.executors',
|
|
7
|
-
'lcdp_postgres_utils.utils']
|
|
8
|
-
|
|
9
|
-
package_data = \
|
|
10
|
-
{'': ['*']}
|
|
11
|
-
|
|
12
|
-
install_requires = \
|
|
13
|
-
['boto3>=1.26.55,<2.0.0', 'pg8000>=1.29.4,<2.0.0']
|
|
14
|
-
|
|
15
|
-
setup_kwargs = {
|
|
16
|
-
'name': 'lcdp-postgres-utils',
|
|
17
|
-
'version': '1.0.4.dev36',
|
|
18
|
-
'description': 'Postgres Utils to create users, databases, functions, ...',
|
|
19
|
-
'long_description': 'None',
|
|
20
|
-
'author': 'Le Comptoir Des Pharmacies',
|
|
21
|
-
'author_email': 'webmaster@lecomptoirdespharmacies.fr',
|
|
22
|
-
'maintainer': 'None',
|
|
23
|
-
'maintainer_email': 'None',
|
|
24
|
-
'url': 'None',
|
|
25
|
-
'packages': packages,
|
|
26
|
-
'package_data': package_data,
|
|
27
|
-
'install_requires': install_requires,
|
|
28
|
-
'python_requires': '>=3.8,<4.0',
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
setup(**setup_kwargs)
|
{lcdp_postgres_utils-1.0.4.dev36 → lcdp_postgres_utils-1.5.8}/lcdp_postgres_utils/__init__.py
RENAMED
|
File without changes
|
{lcdp_postgres_utils-1.0.4.dev36 → lcdp_postgres_utils-1.5.8}/lcdp_postgres_utils/utils/__init__.py
RENAMED
|
File without changes
|
{lcdp_postgres_utils-1.0.4.dev36 → lcdp_postgres_utils-1.5.8}/lcdp_postgres_utils/utils/postgres.py
RENAMED
|
File without changes
|
{lcdp_postgres_utils-1.0.4.dev36 → lcdp_postgres_utils-1.5.8}/lcdp_postgres_utils/utils/ssm.py
RENAMED
|
File without changes
|