pyceo/ceod/db/PostgreSQLService.py

87 lines
3.3 KiB
Python
Raw Normal View History

2021-08-21 00:34:10 -04:00
from zope.interface import implementer
from zope import component
2021-08-26 15:42:18 -04:00
from contextlib import contextmanager
2021-08-24 02:14:28 -04:00
from ceo_common.interfaces import IDatabaseService, IConfig
2021-08-29 12:31:43 -04:00
from ceo_common.errors import DatabaseConnectionError, DatabasePermissionError, \
UserAlreadyExistsError, UserNotFoundError
from ceo_common.logger_factory import logger_factory
2021-08-24 02:14:28 -04:00
from ceod.utils import gen_password
from ceod.db.utils import response_is_empty
2021-08-26 15:42:18 -04:00
2021-08-24 02:14:28 -04:00
from psycopg2 import connect, OperationalError, ProgrammingError
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT
2021-08-21 00:34:10 -04:00
2021-08-29 12:31:43 -04:00
logger = logger_factory(__name__)
2021-08-21 00:34:10 -04:00
@implementer(IDatabaseService)
class PostgreSQLService:
2021-08-26 02:02:47 -04:00
type = 'postgresql'
2021-08-21 00:34:10 -04:00
def __init__(self):
config = component.getUtility(IConfig)
self.auth_username = config.get('postgresql_username')
self.auth_password = config.get('postgresql_password')
2021-08-29 12:31:43 -04:00
self.host = config.get('postgresql_host')
2021-08-26 15:42:18 -04:00
@contextmanager
def psql_connection(self):
2021-08-29 12:31:43 -04:00
con = None
2021-08-21 00:34:10 -04:00
try:
2021-08-29 12:31:43 -04:00
# Don't use the connection as a context manager, because that
# creates a new transaction.
con = connect(
host=self.host,
2021-08-24 22:31:50 -04:00
user=self.auth_username,
password=self.auth_password,
2021-08-29 12:31:43 -04:00
)
con.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
yield con
except OperationalError as e:
logger.error(e)
2021-08-21 00:34:10 -04:00
raise DatabaseConnectionError()
2021-08-29 12:31:43 -04:00
except ProgrammingError as e:
logger.error(e)
2021-08-24 02:14:28 -04:00
raise DatabasePermissionError()
2021-08-29 12:31:43 -04:00
finally:
if con is not None:
con.close()
2021-08-24 02:14:28 -04:00
2021-08-26 15:42:18 -04:00
def create_db(self, username: str) -> str:
password = gen_password()
search_for_user = f"SELECT FROM pg_roles WHERE rolname='{username}'"
search_for_db = f"SELECT FROM pg_database WHERE datname='{username}'"
create_user = f"CREATE USER {username} WITH PASSWORD %(password)s"
2021-08-29 12:31:43 -04:00
create_database = f"CREATE DATABASE {username} OWNER {username}"
revoke_perms = f"REVOKE ALL ON DATABASE {username} FROM PUBLIC"
2021-08-26 15:42:18 -04:00
2021-08-29 12:31:43 -04:00
with self.psql_connection() as con, con.cursor() as cursor:
if not response_is_empty(search_for_user, con):
raise UserAlreadyExistsError()
cursor.execute(create_user, {'password': password})
if response_is_empty(search_for_db, con):
cursor.execute(create_database)
cursor.execute(revoke_perms)
return password
2021-08-26 15:42:18 -04:00
def reset_db_passwd(self, username: str) -> str:
password = gen_password()
search_for_user = f"SELECT FROM pg_roles WHERE rolname='{username}'"
reset_password = f"ALTER USER {username} WITH PASSWORD %(password)s"
2021-08-29 12:31:43 -04:00
with self.psql_connection() as con, con.cursor() as cursor:
if response_is_empty(search_for_user, con):
raise UserNotFoundError(username)
cursor.execute(reset_password, {'password': password})
return password
2021-08-26 15:42:18 -04:00
2021-08-24 22:31:50 -04:00
def delete_db(self, username: str):
drop_db = f"DROP DATABASE IF EXISTS {username}"
2021-08-26 16:45:24 -04:00
drop_user = f"DROP USER IF EXISTS {username}"
2021-08-24 22:31:50 -04:00
2021-08-29 12:31:43 -04:00
with self.psql_connection() as con, con.cursor() as cursor:
cursor.execute(drop_db)
cursor.execute(drop_user)