Skip to content

Commit

Permalink
Add an "access_token" option to the ntfy monitoring hook for authenti…
Browse files Browse the repository at this point in the history
…cating without username/password (#811).
  • Loading branch information
witten committed Mar 11, 2024
1 parent a08c7fc commit 035e961
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 14 deletions.
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
1.8.9.dev0
* #311: Add custom dump/restore command options for MySQL and MariaDB.
* #811: Add an "access_token" option to the ntfy monitoring hook for authenticating
without username/password.
* #827: When the "--json" flag is given, suppress console escape codes so as not to
interfere with JSON output.
* #829: Fix "--override" values containing deprecated section headers not actually overriding
Expand Down
6 changes: 6 additions & 0 deletions borgmatic/config/schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1318,6 +1318,12 @@ properties:
description: |
The password used for authentication.
example: fakepassword
access_token:
type: string
description: |
An ntfy access token to authenticate with instead of
username/password.
example: tk_AgQdq7mVBoFD37zQVN29RhuMzNIz2
start:
type: object
properties:
Expand Down
11 changes: 9 additions & 2 deletions borgmatic/hooks/ntfy.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,16 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev

username = hook_config.get('username')
password = hook_config.get('password')

access_token = hook_config.get('access_token')
auth = None
if (username and password) is not None:

if access_token is not None:
if username or password:
logger.warning(
f'{config_filename}: ntfy access_token is set but so is username/password, only using access_token'
)
auth = requests.auth.HTTPBasicAuth('', access_token)
elif (username and password) is not None:
auth = requests.auth.HTTPBasicAuth(username, password)
logger.info(f'{config_filename}: Using basic auth with user {username} for ntfy')
elif username is not None:
Expand Down
28 changes: 19 additions & 9 deletions docs/how-to/monitor-your-backups.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,22 +298,22 @@ platforms including [web](https://ntfy.sh/stats),
[iOS](https://apps.apple.com/us/app/ntfy/id1625396347).

Since push notifications for regular events might soon become quite annoying,
this hook only fires on any errors by default in order to instantly alert you to issues.
The `states` list can override this.
this hook only fires on any errors by default in order to instantly alert you
to issues. The `states` list can override this. Each state can have its own
custom messages, priorities and tags or, if none are provided, will use the
default.

As ntfy is unauthenticated, it isn't a suitable channel for any private information
so the default messages are intentionally generic. These can be overridden, depending
on your risk assessment. Each `state` can have its own custom messages, priorities and tags
or, if none are provided, will use the default.

An example configuration is shown here, with all the available options, including
[priorities](https://ntfy.sh/docs/publish/#message-priority) and
An example configuration is shown here with all the available options,
including [priorities](https://ntfy.sh/docs/publish/#message-priority) and
[tags](https://ntfy.sh/docs/publish/#tags-emojis):

```yaml
ntfy:
topic: my-unique-topic
server: https://ntfy.my-domain.com
username: myuser
password: secret
start:
title: A borgmatic backup started
message: Watch this space...
Expand All @@ -338,6 +338,16 @@ ntfy:
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> Put
the `ntfy:` option in the `hooks:` section of your configuration.

<span class="minilink minilink-addedin">New in version 1.8.9</span> Instead of
`username`/`password`, you can specify an [ntfy access
token](https://docs.ntfy.sh/config/#access-tokens):

```yaml
ntfy:
topic: my-unique-topic
server: https://ntfy.my-domain.com
access_token: tk_AgQdq7mVBoFD37zQVN29RhuMzNIz2
````
## Loki hook
Expand Down
51 changes: 48 additions & 3 deletions tests/unit/hooks/test_ntfy.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,52 @@ def test_ping_monitor_minimal_config_hits_hosted_ntfy_on_fail():
)


def test_ping_monitor_with_auth_hits_hosted_ntfy_on_fail():
def test_ping_monitor_with_access_token_hits_hosted_ntfy_on_fail():
hook_config = {
'topic': topic,
'access_token': 'abc123',
}
flexmock(module.requests).should_receive('post').with_args(
f'{default_base_url}/{topic}',
headers=return_default_message_headers(borgmatic.hooks.monitor.State.FAIL),
auth=module.requests.auth.HTTPBasicAuth('', 'abc123'),
).and_return(flexmock(ok=True)).once()

module.ping_monitor(
hook_config,
{},
'config.yaml',
borgmatic.hooks.monitor.State.FAIL,
monitoring_log_level=1,
dry_run=False,
)


def test_ping_monitor_with_username_password_and_access_token_ignores_username_password():
hook_config = {
'topic': topic,
'username': 'testuser',
'password': 'fakepassword',
'access_token': 'abc123',
}
flexmock(module.requests).should_receive('post').with_args(
f'{default_base_url}/{topic}',
headers=return_default_message_headers(borgmatic.hooks.monitor.State.FAIL),
auth=module.requests.auth.HTTPBasicAuth('', 'abc123'),
).and_return(flexmock(ok=True)).once()
flexmock(module.logger).should_receive('warning').once()

module.ping_monitor(
hook_config,
{},
'config.yaml',
borgmatic.hooks.monitor.State.FAIL,
monitoring_log_level=1,
dry_run=False,
)


def test_ping_monitor_with_username_password_hits_hosted_ntfy_on_fail():
hook_config = {
'topic': topic,
'username': 'testuser',
Expand All @@ -74,7 +119,7 @@ def test_ping_monitor_with_auth_hits_hosted_ntfy_on_fail():
)


def test_ping_monitor_auth_with_no_username_warning():
def test_ping_monitor_with_password_but_no_username_warns():
hook_config = {'topic': topic, 'password': 'fakepassword'}
flexmock(module.requests).should_receive('post').with_args(
f'{default_base_url}/{topic}',
Expand All @@ -93,7 +138,7 @@ def test_ping_monitor_auth_with_no_username_warning():
)


def test_ping_monitor_auth_with_no_password_warning():
def test_ping_monitor_with_username_but_no_password_warns():
hook_config = {'topic': topic, 'username': 'testuser'}
flexmock(module.requests).should_receive('post').with_args(
f'{default_base_url}/{topic}',
Expand Down

0 comments on commit 035e961

Please sign in to comment.