diff --git a/CHANGELOG.md b/CHANGELOG.md index 11dd40c..c262c31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # EasyWKS Changelog + +## Version 0.1.5 + +The messages sent by EasyWKS can now be customized. + +### Changes + + +- Add `responses` config option. + + + + ## Version 0.1.4 diff --git a/README.md b/README.md index 33ed5a4..56e27a5 100644 --- a/README.md +++ b/README.md @@ -112,10 +112,34 @@ smtp: # Omit username/password if authentication is not needed. username: webkey password: SuperS3curePassword123 + # Configure the LMTP server -lmtpds: +lmtpd: host: "::1" port: 8024 + +# You can override the mail response templates with your own text. +# The following templates can be overridden: +# - "header": Placed in front of every message. +# - "footer": Appended to every message. +# - "confirm": Sent with the confirmation request. +# - "done": Sent after a key was published. +# - "error": Sent when an error occurs. +# The following placeholders can be used (enclosed in curly braces): +# - {domain}: The email domain for with the request is processed. +# - {sender}: The submitter's mail address. +# - {submission}: The submission address. +# When overriding the "error" template, theres an additional +# placeholder you can use: +# - {error}: The error message. +#responses: +# error: | +# An error has occurred while processing your request: +# +# {error} +# +# If this error persists, please contact admin@example.org for help. + # Every domain served by EasyWKS must be listed here domains: example.org: diff --git a/easywks/__init__.py b/easywks/__init__.py index de3e537..46a7260 100644 --- a/easywks/__init__.py +++ b/easywks/__init__.py @@ -1,2 +1,2 @@ -__version__ = '0.1.4' +__version__ = '0.1.5' diff --git a/easywks/config.py b/easywks/config.py index 6a7b57a..fe662e8 100644 --- a/easywks/config.py +++ b/easywks/config.py @@ -79,6 +79,58 @@ def _validate_policy_flags(value): return f'has invalid key {flag}' +def _validate_responses(value): + if not isinstance(value, dict): + return f'must be a map, got {type(value)}' + if 'header' not in value: + value['header'] = '''Hi there! + +This is the EasyWKS system at {domain}. +''' + if 'footer' not in value: + value['footer'] = '''For more information on WKD and WKS see: + + https://gnupg.org/faq/wkd.html + https://gnupg.org/faq/wks.html + + +Regards +EasyWKS + +-- +Dance like nobody is watching. +Encrypt live everybody is. +''' + if 'confirm' not in value: + value['confirm'] = '''You appear to have submitted your key for publication in the Web Key +Directory. There's one more step you need to complete. If you did not +request this, you can simply ignore this message. + +If your email client doesn't automatically complete this challenge, you +can perform this step manually: Please verify that you can decrypt the +second part of this message and that the fingerprint listed in the +encrypted part matches your key. If everything looks ok, please reply +to this message with an **encrypted and signed PGP/MIME message** with +the following content (without the <> brackets) + + type: confirmation-response + sender: + nonce: +''' + if 'done' not in value: + value['done'] = '''Your key has been published to the Web Key Directory. +You can test WKD key retrieval e.g. with: + +gpg --auto-key-locate=wkd,nodefault --locate-key {sender} +''' + if 'error' not in value: + value['error'] = '''An error has occurred while processing your request: + +{error} + +If this error persists, please contact your administrator for help.''' + + class _ConfigOption: def __init__(self, key, typ, default, validator=None): @@ -165,4 +217,12 @@ Config = _GlobalConfig( 'host': 'localhost', 'port': 25, }, validator=_validate_lmtpd_config), + responses=_ConfigOption('responses', dict, {}, validator=_validate_responses), ) + + +def render_message(key, **kwargs): + header = Config.responses['header'].format(**kwargs) + content = Config.responses[key].format(**kwargs) + footer = Config.responses['footer'].format(**kwargs) + return f'{header}\n{content}\n{footer}' diff --git a/easywks/types.py b/easywks/types.py index 9f63d27..af57ee9 100644 --- a/easywks/types.py +++ b/easywks/types.py @@ -10,7 +10,7 @@ from pgpy import PGPKey, PGPMessage, PGPUID from pgpy.types import SignatureVerification from .crypto import pgp_sign -from .config import Config +from .config import Config, render_message from .util import create_nonce, fingerprint @@ -42,39 +42,6 @@ class SubmissionRequest: class ConfirmationRequest: - MAIL_TEXT = '''Hi there! - -This is the EasyWKS system at {domain}. - -You appear to have submitted your key for publication in the Web Key -Directory. There's one more step you need to complete. If you did not -request this, you can simply ignore this message. - -If your email client doesn't automatically complete this challenge, you -can perform this step manually: Please verify that you can decrypt the -second part of this message and that the fingerprint listed in the -encrypted part matches your key. If everything looks ok, please reply -to this message with an **encrypted and signed PGP/MIME message** with -the following content (without the <> brackets) - - type: confirmation-response - sender: - nonce: - -For more information on WKD and WKS see: - - https://gnupg.org/faq/wkd.html - https://gnupg.org/faq/wks.html - - -Regards -EasyWKS - --- -Dance like nobody is watching. -Encrypt live everybody is. -''' - def __init__(self, submitter_addr: str, submission_addr: str, key: PGPKey, nonce: str = None): self._domain = submitter_addr.split('@')[1] self._submitter_addr = submitter_addr @@ -103,7 +70,11 @@ Encrypt live everybody is. return self._nonce def create_signed_message(self): - mpplain = MIMEText(ConfirmationRequest.MAIL_TEXT.format(domain=self.domain), _subtype='plain') + mail_text = render_message('confirm', + domain=self.domain, + sender=self.submitter_address, + submission=self.submission_address) + mpplain = MIMEText(mail_text, _subtype='plain') ps = '\r\n'.join([ 'type: confirmation-request', f'sender: {self._submission_addr}', @@ -181,27 +152,6 @@ class ConfirmationResponse: class PublishResponse: - MAIL_TEXT = '''Hi there! - -This is the EasyWKS system at {domain}. - -Your key has been published to the Web Key Directory. -You can test WKD key retrieval e.g. with: - -gpg --auto-key-locate=wkd,nodefault --locate-key {uid} - -For more information on WKD and WKS see: - - https://gnupg.org/faq/wkd.html - https://gnupg.org/faq/wks.html - -Regards -EasyWKS - --- -Dance like nobody is watching. -Encrypt live everybody is. -''' def __init__(self, submitter_addr: str, submission_addr: str, key: PGPKey): self._domain = submitter_addr.split('@')[1] @@ -226,8 +176,11 @@ Encrypt live everybody is. return self._domain def create_signed_message(self): - mpplain = MIMEText(PublishResponse.MAIL_TEXT.format(domain=self.domain, uid=self.submitter_address), - _subtype='plain') + mail_text = render_message('done', + domain=self.domain, + sender=self.submitter_address, + submission=self.submission_address) + mpplain = MIMEText(mail_text, _subtype='plain') to_encrypt = PGPMessage.new(mpplain.as_string(policy=default)) encrypted: PGPMessage = self.key.encrypt(to_encrypt) encrypted |= pgp_sign(self.domain, encrypted) @@ -248,30 +201,6 @@ Encrypt live everybody is. class EasyWksError(BaseException): - MAIL_TEXT = '''Hi there! - -This is the EasyWKS system at {domain}. - -An error has occurred while processing your request. - -{message} - -If this error persists, please contact your administrator for help. - -For more information on WKD and WKS see: - - https://gnupg.org/faq/wkd.html - https://gnupg.org/faq/wks.html - - -Regards -EasyWKS - --- -Dance like nobody is watching. -Encrypt live everybody is. -''' - def __init__(self, msg: str, ): super().__init__() self._msg = msg @@ -281,8 +210,12 @@ Encrypt live everybody is. def create_message(self, submitter_addr: str, submission_addr: str) -> MIMEText: domain = submission_addr.split('@', 1)[1] - payload = EasyWksError.MAIL_TEXT.format(domain=domain, message=self._msg) - email = MIMEText(payload) + mail_text = render_message('error', + domain=domain, + sender=submitter_addr, + submission=submission_addr, + error=self._msg) + email = MIMEText(mail_text) email['Subject'] = 'An error has occurred while processing your request' email['From'] = submission_addr email['To'] = submitter_addr diff --git a/package/debian/easywks/etc/easywks.yml b/package/debian/easywks/etc/easywks.yml index fff33a3..86d7530 100644 --- a/package/debian/easywks/etc/easywks.yml +++ b/package/debian/easywks/etc/easywks.yml @@ -42,6 +42,28 @@ lmtpd: host: "::1" port: 8024 +# You can override the mail response templates with your own text. +# The following templates can be overridden: +# - "header": Placed in front of every message. +# - "footer": Appended to every message. +# - "confirm": Sent with the confirmation request. +# - "done": Sent after a key was published. +# - "error": Sent when an error occurs. +# The following placeholders can be used (enclosed in curly braces): +# - {domain}: The email domain for with the request is processed. +# - {sender}: The submitter's mail address. +# - {submission}: The submission address. +# When overriding the "error" template, theres an additional +# placeholder you can use: +# - {error}: The error message. +#responses: +# error: | +# An error has occurred while processing your request: +# +# {error} +# +# If this error persists, please contact admin@example.org for help. + # Every domain served by EasyWKS must be listed here domains: # Defaults are gpgwks@ and no password protection.