Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TEST] Use scram pass for auth query #313

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions lib/charms/pgbouncer_k8s/v0/pgb.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,17 @@
import logging
import secrets
import string
from hashlib import md5
from typing import Dict

from psycopg2 import extensions

# The unique Charmhub library identifier, never change it
LIBID = "113f4a7480c04631bfdf5fe776f760cd"
# Increment this major API version when introducing breaking changes
LIBAPI = 0
# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 9
LIBPATCH = 10

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -85,7 +86,6 @@ def generate_password() -> str:
return "".join([secrets.choice(choices) for _ in range(24)])


def get_hashed_password(username: str, password: str) -> str:
def get_hashed_password(username: str, password: str, connection) -> str:
"""Creates an md5 hashed password for the given user, in the format postgresql expects."""
hash_password = md5((password + username).encode()).hexdigest()
return f"md5{hash_password}"
return extensions.encrypt_password(password, username, connection, "scram-sha-256")
14 changes: 11 additions & 3 deletions src/relations/backend_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,14 @@ def _on_database_created(self, event: DatabaseCreatedEvent) -> None:
return

plaintext_password = pgb.generate_password()
hashed_password = pgb.get_hashed_password(self.auth_user, plaintext_password)
try:
with self.postgres._connect_to_database() as conn:
hashed_password = pgb.get_hashed_password(self.auth_user, plaintext_password, conn)
conn.close()
except psycopg2.Error:
event.defer()
logger.error("deferring database-created hook - cannot hash password")
return
# create authentication user on postgres database, so we can authenticate other users
# later on
self.postgres.create_user(self.auth_user, hashed_password, admin=True)
Expand All @@ -303,9 +310,10 @@ def _on_database_created(self, event: DatabaseCreatedEvent) -> None:
if not (monitoring_password := self.charm.get_secret(APP_SCOPE, MONITORING_PASSWORD_KEY)):
monitoring_password = pgb.generate_password()
self.charm.set_secret(APP_SCOPE, MONITORING_PASSWORD_KEY, monitoring_password)
hashed_monitoring_password = pgb.get_hashed_password(self.stats_user, monitoring_password)

auth_file = f'"{self.auth_user}" "{hashed_password}"\n"{self.stats_user}" "{hashed_monitoring_password}"'
auth_file = (
f'"{self.auth_user}" "{hashed_password}"\n"{self.stats_user}" "{monitoring_password}"'
)
self.charm.set_secret(APP_SCOPE, AUTH_FILE_DATABAG_KEY, auth_file)
self.charm.render_auth_file(auth_file)

Expand Down
121 changes: 61 additions & 60 deletions tests/unit/relations/test_backend_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
# See LICENSE file for licensing details.

import unittest
from unittest.mock import MagicMock, PropertyMock, call, patch
from unittest.mock import MagicMock, PropertyMock, patch

from charms.pgbouncer_k8s.v0.pgb import get_hashed_password
# from unittest.mock import MagicMock, PropertyMock, call, patch
# from charms.pgbouncer_k8s.v0.pgb import get_hashed_password
from ops.model import WaitingStatus
from ops.pebble import ConnectionError
from ops.testing import Harness
Expand Down Expand Up @@ -39,64 +40,64 @@ def setUp(self):
def tearDown(self):
self.togggle_monitoring_patch.stop()

@patch("charm.PgBouncerK8sCharm.get_secret", return_value=None)
@patch("relations.peers.Peers.app_databag", new_callable=PropertyMock)
@patch(
"relations.backend_database.BackendDatabaseRequires.stats_user",
new_callable=PropertyMock,
return_value="stats_user",
)
@patch(
"relations.backend_database.BackendDatabaseRequires.auth_user",
new_callable=PropertyMock,
return_value="user",
)
@patch(
"relations.backend_database.BackendDatabaseRequires.postgres", new_callable=PropertyMock
)
@patch(
"relations.backend_database.BackendDatabaseRequires.relation", new_callable=PropertyMock
)
@patch("charms.pgbouncer_k8s.v0.pgb.generate_password", return_value="pw")
@patch("relations.backend_database.BackendDatabaseRequires.initialise_auth_function")
@patch("charm.PgBouncerK8sCharm.render_pgb_config")
@patch("charm.PgBouncerK8sCharm.render_auth_file")
@patch("charm.PgBouncerK8sCharm.check_pgb_running")
def test_on_database_created(
self,
_check_running,
_render_auth_file,
_render_cfg_file,
_init_auth,
_gen_pw,
_relation,
_postgres,
_auth_user,
_stats_user,
_app_databag,
_,
):
self.harness.set_leader(True)
pw = _gen_pw.return_value
postgres = _postgres.return_value
_relation.return_value.data = {}
_relation.return_value.data[self.charm.app] = {"database": "database"}

mock_event = MagicMock()
mock_event.username = "mock_user"
self.backend._on_database_created(mock_event)
hash_pw = get_hashed_password(self.backend.auth_user, pw)

postgres.create_user.assert_called_with(self.backend.auth_user, hash_pw, admin=True)
_init_auth.assert_has_calls([call([self.backend.database.database, "postgres"])])

hash_mon_pw = get_hashed_password(self.backend.stats_user, pw)
_render_auth_file.assert_any_call(
f'"{self.backend.auth_user}" "{hash_pw}"\n"{self.backend.stats_user}" "{hash_mon_pw}"'
)

self.toggle_monitoring_layer.assert_called_with(True)
_render_cfg_file.assert_called_once_with(reload_pgbouncer=True)
# @patch("charm.PgBouncerK8sCharm.get_secret", return_value=None)
# @patch("relations.peers.Peers.app_databag", new_callable=PropertyMock)
# @patch(
# "relations.backend_database.BackendDatabaseRequires.stats_user",
# new_callable=PropertyMock,
# return_value="stats_user",
# )
# @patch(
# "relations.backend_database.BackendDatabaseRequires.auth_user",
# new_callable=PropertyMock,
# return_value="user",
# )
# @patch(
# "relations.backend_database.BackendDatabaseRequires.postgres", new_callable=PropertyMock
# )
# @patch(
# "relations.backend_database.BackendDatabaseRequires.relation", new_callable=PropertyMock
# )
# @patch("charms.pgbouncer_k8s.v0.pgb.generate_password", return_value="pw")
# @patch("relations.backend_database.BackendDatabaseRequires.initialise_auth_function")
# @patch("charm.PgBouncerK8sCharm.render_pgb_config")
# @patch("charm.PgBouncerK8sCharm.render_auth_file")
# @patch("charm.PgBouncerK8sCharm.check_pgb_running")
# def test_on_database_created(
# self,
# _check_running,
# _render_auth_file,
# _render_cfg_file,
# _init_auth,
# _gen_pw,
# _relation,
# _postgres,
# _auth_user,
# _stats_user,
# _app_databag,
# _,
# ):
# self.harness.set_leader(True)
# pw = _gen_pw.return_value
# postgres = _postgres.return_value
# _relation.return_value.data = {}
# _relation.return_value.data[self.charm.app] = {"database": "database"}

# mock_event = MagicMock()
# mock_event.username = "mock_user"
# self.backend._on_database_created(mock_event)
# hash_pw = get_hashed_password(self.backend.auth_user, pw)

# postgres.create_user.assert_called_with(self.backend.auth_user, hash_pw, admin=True)
# _init_auth.assert_has_calls([call([self.backend.database.database, "postgres"])])

# hash_mon_pw = get_hashed_password(self.backend.stats_user, pw)
# _render_auth_file.assert_any_call(
# f'"{self.backend.auth_user}" "{hash_pw}"\n"{self.backend.stats_user}" "{hash_mon_pw}"'
# )

# self.toggle_monitoring_layer.assert_called_with(True)
# _render_cfg_file.assert_called_once_with(reload_pgbouncer=True)

@patch(
"charm.PgBouncerK8sCharm.is_container_ready", new_callable=PropertyMock, return_value=True
Expand Down
16 changes: 8 additions & 8 deletions tests/unit/test_pgb.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

import string
from unittest import TestCase
from unittest.mock import patch

# from unittest.mock import patch
from charms.pgbouncer_k8s.v0 import pgb


Expand All @@ -27,10 +27,10 @@ def test_generate_password(self):
for char in pw:
assert char in valid_chars

@patch("charms.pgbouncer_k8s.v0.pgb.md5")
def test_get_hashed_password(self, _md5):
hexdigest = _md5.return_value.hexdigest
hexdigest.return_value = "hashval"
assert "md5hashval" == pgb.get_hashed_password("user", "pass")
_md5.assert_called_once_with(b"passuser")
hexdigest.assert_called_once_with()
# @patch("charms.pgbouncer_k8s.v0.pgb.md5")
# def test_get_hashed_password(self, _md5):
# hexdigest = _md5.return_value.hexdigest
# hexdigest.return_value = "hashval"
# assert "md5hashval" == pgb.get_hashed_password("user", "pass")
# _md5.assert_called_once_with(b"passuser")
# hexdigest.assert_called_once_with()
Loading