Add an "access_token" option to the ntfy monitoring hook for authenticating without username/password (#811).

This commit is contained in:
Dan Helfman 2024-03-11 12:48:58 -07:00
parent a08c7fc77a
commit 035e96156a
5 changed files with 84 additions and 14 deletions

2
NEWS
View file

@ -1,5 +1,7 @@
1.8.9.dev0 1.8.9.dev0
* #311: Add custom dump/restore command options for MySQL and MariaDB. * #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 * #827: When the "--json" flag is given, suppress console escape codes so as not to
interfere with JSON output. interfere with JSON output.
* #829: Fix "--override" values containing deprecated section headers not actually overriding * #829: Fix "--override" values containing deprecated section headers not actually overriding

View file

@ -1318,6 +1318,12 @@ properties:
description: | description: |
The password used for authentication. The password used for authentication.
example: fakepassword example: fakepassword
access_token:
type: string
description: |
An ntfy access token to authenticate with instead of
username/password.
example: tk_AgQdq7mVBoFD37zQVN29RhuMzNIz2
start: start:
type: object type: object
properties: properties:

View file

@ -50,9 +50,16 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev
username = hook_config.get('username') username = hook_config.get('username')
password = hook_config.get('password') password = hook_config.get('password')
access_token = hook_config.get('access_token')
auth = None 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) auth = requests.auth.HTTPBasicAuth(username, password)
logger.info(f'{config_filename}: Using basic auth with user {username} for ntfy') logger.info(f'{config_filename}: Using basic auth with user {username} for ntfy')
elif username is not None: elif username is not None:

View file

@ -298,22 +298,22 @@ platforms including [web](https://ntfy.sh/stats),
[iOS](https://apps.apple.com/us/app/ntfy/id1625396347). [iOS](https://apps.apple.com/us/app/ntfy/id1625396347).
Since push notifications for regular events might soon become quite annoying, 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. this hook only fires on any errors by default in order to instantly alert you
The `states` list can override this. 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 An example configuration is shown here with all the available options,
so the default messages are intentionally generic. These can be overridden, depending including [priorities](https://ntfy.sh/docs/publish/#message-priority) and
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
[tags](https://ntfy.sh/docs/publish/#tags-emojis): [tags](https://ntfy.sh/docs/publish/#tags-emojis):
```yaml ```yaml
ntfy: ntfy:
topic: my-unique-topic topic: my-unique-topic
server: https://ntfy.my-domain.com server: https://ntfy.my-domain.com
username: myuser
password: secret
start: start:
title: A borgmatic backup started title: A borgmatic backup started
message: Watch this space... message: Watch this space...
@ -338,6 +338,16 @@ ntfy:
<span class="minilink minilink-addedin">Prior to version 1.8.0</span> Put <span class="minilink minilink-addedin">Prior to version 1.8.0</span> Put
the `ntfy:` option in the `hooks:` section of your configuration. 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 ## Loki hook

View file

@ -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 = { hook_config = {
'topic': topic, 'topic': topic,
'username': 'testuser', 'username': 'testuser',
@ -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'} hook_config = {'topic': topic, 'password': 'fakepassword'}
flexmock(module.requests).should_receive('post').with_args( flexmock(module.requests).should_receive('post').with_args(
f'{default_base_url}/{topic}', f'{default_base_url}/{topic}',
@ -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'} hook_config = {'topic': topic, 'username': 'testuser'}
flexmock(module.requests).should_receive('post').with_args( flexmock(module.requests).should_receive('post').with_args(
f'{default_base_url}/{topic}', f'{default_base_url}/{topic}',