Skip to content

Commit

Permalink
Add federation_domain_whitelist configuration (#94)
Browse files Browse the repository at this point in the history
  • Loading branch information
amandahla authored Nov 20, 2023
1 parent 26450c7 commit 6101873
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 6 deletions.
2 changes: 2 additions & 0 deletions .woke.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ ignore_files:
rules:
# Ignore "grandfathered" used by SAML configuration.
- name: grandfathered
# Ignore "whitelist" used by Synapse configuration.
- name: whitelist
4 changes: 4 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ options:
Defaults to true. If Synapse uses a single sign-on provider for
authentication, this option can be used to disable the regular login
flow.
federation_domain_whitelist:
type: string
description: Comma separated list of domains to be allowed to federate.
default: ''
public_baseurl:
type: string
description: |
Expand Down
9 changes: 5 additions & 4 deletions src-docs/charm_state.py.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Exception raised when a charm configuration is found to be invalid.

Attrs: msg (str): Explanation of the error.

<a href="../src/charm_state.py#L49"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>
<a href="../src/charm_state.py#L50"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>function</kbd> `__init__`

Expand Down Expand Up @@ -67,7 +67,7 @@ Get charm proxy information from juju charm environment.

---

<a href="../src/charm_state.py#L176"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>
<a href="../src/charm_state.py#L179"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>classmethod</kbd> `from_charm`

Expand Down Expand Up @@ -129,6 +129,7 @@ Represent Synapse builtin configuration values.
- <b>`public_baseurl`</b>: public_baseurl config.
- <b>`enable_mjolnir`</b>: enable_mjolnir config.
- <b>`enable_password_config`</b>: enable_password_config config.
- <b>`federation_domain_whitelist`</b>: federation_domain_whitelist config.
- <b>`smtp_enable_tls`</b>: enable tls while connecting to SMTP server.
- <b>`smtp_host`</b>: SMTP host.
- <b>`smtp_notif_from`</b>: defines the "From" address to use when sending emails.
Expand All @@ -141,7 +142,7 @@ Represent Synapse builtin configuration values.

---

<a href="../src/charm_state.py#L110"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>
<a href="../src/charm_state.py#L113"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>classmethod</kbd> `set_default_smtp_notif_from`

Expand All @@ -168,7 +169,7 @@ Set server_name as default value to smtp_notif_from.

---

<a href="../src/charm_state.py#L129"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>
<a href="../src/charm_state.py#L132"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>classmethod</kbd> `to_yes_or_no`

Expand Down
4 changes: 2 additions & 2 deletions src-docs/pebble.py.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Change the configuration.

---

<a href="../src/pebble.py#L100"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>
<a href="../src/pebble.py#L104"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>function</kbd> `enable_saml`

Expand Down Expand Up @@ -117,7 +117,7 @@ Replan Synapse NGINX service.

---

<a href="../src/pebble.py#L116"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>
<a href="../src/pebble.py#L120"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>function</kbd> `reset_instance`

Expand Down
3 changes: 3 additions & 0 deletions src/charm_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
KNOWN_CHARM_CONFIG = (
"enable_mjolnir",
"enable_password_config",
"federation_domain_whitelist",
"public_baseurl",
"report_stats",
"server_name",
Expand Down Expand Up @@ -78,6 +79,7 @@ class SynapseConfig(BaseModel): # pylint: disable=too-few-public-methods
public_baseurl: public_baseurl config.
enable_mjolnir: enable_mjolnir config.
enable_password_config: enable_password_config config.
federation_domain_whitelist: federation_domain_whitelist config.
smtp_enable_tls: enable tls while connecting to SMTP server.
smtp_host: SMTP host.
smtp_notif_from: defines the "From" address to use when sending emails.
Expand All @@ -91,6 +93,7 @@ class SynapseConfig(BaseModel): # pylint: disable=too-few-public-methods
public_baseurl: str | None = Field(None)
enable_mjolnir: bool = False
enable_password_config: bool = True
federation_domain_whitelist: str | None = Field(None)
smtp_enable_tls: bool = True
smtp_host: str | None = Field(None)
smtp_notif_from: str | None = Field(None)
Expand Down
4 changes: 4 additions & 0 deletions src/pebble.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ def change_config(self, container: ops.model.Container) -> None:
synapse.enable_smtp(container=container, charm_state=self._charm_state)
if not self._charm_state.synapse_config.enable_password_config:
synapse.disable_password_config(container=container)
if self._charm_state.synapse_config.federation_domain_whitelist:
synapse.enable_federation_domain_whitelist(
container=container, charm_state=self._charm_state
)
self.restart_synapse(container)
except (synapse.WorkloadError, ops.pebble.PathError) as exc:
raise PebbleServiceError(str(exc)) from exc
Expand Down
1 change: 1 addition & 0 deletions src/synapse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
check_ready,
create_mjolnir_config,
disable_password_config,
enable_federation_domain_whitelist,
enable_metrics,
enable_saml,
enable_serve_server_wellknown,
Expand Down
22 changes: 22 additions & 0 deletions src/synapse/workload.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,28 @@ def enable_serve_server_wellknown(container: ops.Container) -> None:
raise WorkloadError(str(exc)) from exc


def enable_federation_domain_whitelist(container: ops.Container, charm_state: CharmState) -> None:
"""Change the Synapse configuration to enable federation_domain_whitelist.
Args:
container: Container of the charm.
charm_state: Instance of CharmState.
Raises:
WorkloadError: something went wrong enabling configuration.
"""
try:
config = container.pull(SYNAPSE_CONFIG_PATH).read()
current_yaml = yaml.safe_load(config)
if charm_state.synapse_config.federation_domain_whitelist is not None:
current_yaml[
"federation_domain_whitelist"
] = charm_state.synapse_config.federation_domain_whitelist.split(",")
container.push(SYNAPSE_CONFIG_PATH, yaml.safe_dump(current_yaml))
except ops.pebble.PathError as exc:
raise WorkloadError(str(exc)) from exc


def _get_mjolnir_config(access_token: str, room_id: str) -> typing.Dict:
"""Create config as expected by mjolnir.
Expand Down
48 changes: 48 additions & 0 deletions tests/unit/test_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,3 +370,51 @@ def test_get_admin_access_token_existing_peer_data(harness: Harness) -> None:

assert admin_access_token == admin_access_token_expected
assert isinstance(harness.model.unit.status, ops.ActiveStatus)


def test_enable_federation_domain_whitelist_is_called(
harness: Harness,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""
arrange: start the Synapse charm, set Synapse container to be ready,
set server_name and federation_domain_whitelist.
act: call pebble change_config.
assert: enable_federation_domain_whitelist is called.
"""
harness.update_config({"federation_domain_whitelist": "foo"})
harness.begin()
harness.set_leader(True)
monkeypatch.setattr(synapse, "execute_migrate_config", MagicMock())
monkeypatch.setattr(synapse, "enable_metrics", MagicMock())
monkeypatch.setattr(synapse, "enable_serve_server_wellknown", MagicMock())
enable_federation_mock = MagicMock()
monkeypatch.setattr(synapse, "enable_federation_domain_whitelist", enable_federation_mock)

harness.charm.pebble_service.change_config(container=MagicMock())

enable_federation_mock.assert_called_once()


def test_disable_password_config_is_called(
harness: Harness,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""
arrange: start the Synapse charm, set Synapse container to be ready,
set server_name and enable_password_config.
act: call pebble change_config.
assert: disable_password_config is called.
"""
harness.update_config({"enable_password_config": False})
harness.begin()
harness.set_leader(True)
monkeypatch.setattr(synapse, "execute_migrate_config", MagicMock())
monkeypatch.setattr(synapse, "enable_metrics", MagicMock())
monkeypatch.setattr(synapse, "enable_serve_server_wellknown", MagicMock())
disable_password_config_mock = MagicMock()
monkeypatch.setattr(synapse, "disable_password_config", disable_password_config_mock)

harness.charm.pebble_service.change_config(container=MagicMock())

disable_password_config_mock.assert_called_once()
65 changes: 65 additions & 0 deletions tests/unit/test_synapse_workload.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,71 @@
from .conftest import TEST_SERVER_NAME


def test_enable_federation_domain_whitelist_success(
harness: Harness, monkeypatch: pytest.MonkeyPatch
):
"""
arrange: set mock container with file.
act: update federation_domain_whitelist config and call enable_federation_domain_whitelist.
assert: new configuration file is pushed and federation_domain_whitelist is enabled.
"""
config_content = """
listeners:
- type: http
port: 8080
bind_addresses:
- "::"
"""
text_io_mock = io.StringIO(config_content)
pull_mock = Mock(return_value=text_io_mock)
push_mock = MagicMock()
container_mock = MagicMock()
monkeypatch.setattr(container_mock, "pull", pull_mock)
monkeypatch.setattr(container_mock, "push", push_mock)

expected_first_domain = "foo1"
expected_second_domain = "foo2"
harness.update_config(
{"federation_domain_whitelist": f"{expected_first_domain},{expected_second_domain}"}
)
harness.begin()
synapse.enable_federation_domain_whitelist(container_mock, harness.charm._charm_state)

assert pull_mock.call_args[0][0] == synapse.SYNAPSE_CONFIG_PATH
assert push_mock.call_args[0][0] == synapse.SYNAPSE_CONFIG_PATH
expected_config_content = {
"listeners": [
{"type": "http", "port": 8080, "bind_addresses": ["::"]},
],
"federation_domain_whitelist": [expected_first_domain, expected_second_domain],
}
assert push_mock.call_args[0][1] == yaml.safe_dump(expected_config_content)


def test_enable_federation_domain_whitelist_error(
harness: Harness, monkeypatch: pytest.MonkeyPatch
):
"""
arrange: set mock container with file.
act: update federation_domain_whitelist config and call enable_smtp.
assert: raise WorkloadError in case of error.
"""
error_message = "Error pulling file"
path_error = ops.pebble.PathError(kind="fake", message=error_message)
pull_mock = MagicMock(side_effect=path_error)
container_mock = MagicMock()
monkeypatch.setattr(container_mock, "pull", pull_mock)

with pytest.raises(synapse.WorkloadError, match=error_message):
expected_first_domain = "foo1"
expected_second_domain = "foo2"
harness.update_config(
{"federation_domain_whitelist": f"{expected_first_domain},{expected_second_domain}"}
)
harness.begin()
synapse.enable_federation_domain_whitelist(container_mock, harness.charm._charm_state)


def test_enable_metrics_success(monkeypatch: pytest.MonkeyPatch):
"""
arrange: set mock container with file.
Expand Down

0 comments on commit 6101873

Please sign in to comment.