mirror of
https://github.com/chatmail/relay.git
synced 2026-05-10 16:04:37 +00:00
Compare commits
11 Commits
1.9.0
...
invite-onl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a7b425958 | ||
|
|
cb87c85c03 | ||
|
|
5b4eb1701e | ||
|
|
f578704069 | ||
|
|
7319977527 | ||
|
|
7dcd109bec | ||
|
|
6940175b06 | ||
|
|
de139bde18 | ||
|
|
a92c9ff275 | ||
|
|
1afdab7b20 | ||
|
|
56cbd6f35b |
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
## untagged
|
## untagged
|
||||||
|
|
||||||
|
- Enable invite-only chatmail relays with invite tokens
|
||||||
|
that can override disabled account creation
|
||||||
|
([#600](https://github.com/chatmail/relay/pull/600))
|
||||||
|
|
||||||
- dovecot: keep mailbox index only in memory to avoid unnecessary disc usage
|
- dovecot: keep mailbox index only in memory to avoid unnecessary disc usage
|
||||||
([#632](https://github.com/chatmail/relay/pull/632))
|
([#632](https://github.com/chatmail/relay/pull/632))
|
||||||
|
|
||||||
|
|||||||
17
README.md
17
README.md
@@ -284,8 +284,23 @@ Fresh chatmail addresses have a mailbox directory that contains:
|
|||||||
will typically be empty unless the user of that address hasn't been online
|
will typically be empty unless the user of that address hasn't been online
|
||||||
for a while.
|
for a while.
|
||||||
|
|
||||||
|
## Restrict address creation
|
||||||
|
|
||||||
## Emergency Commands to disable automatic address creation
|
### Only allow new addresses with an invite token
|
||||||
|
|
||||||
|
To restrict address creation for anyone who doesn't have the invite link/QR code:
|
||||||
|
|
||||||
|
1. Use the `invite_token` option to add
|
||||||
|
one or more tokens of your choice to `chatmail.ini`:
|
||||||
|
`invite_token = s3cr3t privil3g3`
|
||||||
|
- (recommendation: choose 9 or more letters, or it will be easily bruteforced)
|
||||||
|
2. Run `scripts/cmdeploy run`
|
||||||
|
3. Distribute a `dcaccount` invite link/QR code
|
||||||
|
(like the one on your web page)
|
||||||
|
with one of your invite tokens added at the end,
|
||||||
|
for example: `dcaccount:https://example.org/new?s3cr3t`
|
||||||
|
|
||||||
|
### Emergency Command to disable automatic address creation
|
||||||
|
|
||||||
If you need to stop address creation,
|
If you need to stop address creation,
|
||||||
e.g. because some script is wildly creating addresses,
|
e.g. because some script is wildly creating addresses,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ class Config:
|
|||||||
self.username_min_length = int(params["username_min_length"])
|
self.username_min_length = int(params["username_min_length"])
|
||||||
self.username_max_length = int(params["username_max_length"])
|
self.username_max_length = int(params["username_max_length"])
|
||||||
self.password_min_length = int(params["password_min_length"])
|
self.password_min_length = int(params["password_min_length"])
|
||||||
|
self.invite_token = params.get("invite_token", "")
|
||||||
self.passthrough_senders = params["passthrough_senders"].split()
|
self.passthrough_senders = params["passthrough_senders"].split()
|
||||||
self.passthrough_recipients = params["passthrough_recipients"].split()
|
self.passthrough_recipients = params["passthrough_recipients"].split()
|
||||||
self.www_folder = params.get("www_folder", "")
|
self.www_folder = params.get("www_folder", "")
|
||||||
|
|||||||
@@ -26,8 +26,19 @@ def is_allowed_to_create(config: Config, user, cleartext_password) -> bool:
|
|||||||
if os.path.exists(NOCREATE_FILE):
|
if os.path.exists(NOCREATE_FILE):
|
||||||
logging.warning(f"blocked account creation because {NOCREATE_FILE!r} exists.")
|
logging.warning(f"blocked account creation because {NOCREATE_FILE!r} exists.")
|
||||||
return False
|
return False
|
||||||
|
password_length = len(cleartext_password)
|
||||||
|
if config.invite_token:
|
||||||
|
for inv_token in config.invite_token.split():
|
||||||
|
if cleartext_password.startswith(inv_token):
|
||||||
|
password_length = len(cleartext_password) - len(inv_token)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
logging.warning(
|
||||||
|
"blocked account creation because password didn't contain invite token(s)."
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
if len(cleartext_password) < config.password_min_length:
|
if password_length < config.password_min_length:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"Password needs to be at least %s characters long",
|
"Password needs to be at least %s characters long",
|
||||||
config.password_min_length,
|
config.password_min_length,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"""CGI script for creating new accounts."""
|
"""CGI script for creating new accounts."""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import random
|
import random
|
||||||
import secrets
|
import secrets
|
||||||
import string
|
import string
|
||||||
@@ -20,7 +21,11 @@ def create_newemail_dict(config: Config):
|
|||||||
secrets.choice(ALPHANUMERIC_PUNCT)
|
secrets.choice(ALPHANUMERIC_PUNCT)
|
||||||
for _ in range(config.password_min_length + 3)
|
for _ in range(config.password_min_length + 3)
|
||||||
)
|
)
|
||||||
return dict(email=f"{user}@{config.mail_domain}", password=f"{password}")
|
redirect_uri = os.getenv("REQUEST_URI", "/new")
|
||||||
|
invite_token = "" if redirect_uri == "/new" else redirect_uri[5:]
|
||||||
|
return dict(
|
||||||
|
email=f"{user}@{config.mail_domain}", password=f"{invite_token}{password}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def print_new_account():
|
def print_new_account():
|
||||||
|
|||||||
@@ -64,12 +64,38 @@ def test_dont_overwrite_password_on_wrong_login(dictproxy):
|
|||||||
assert res["password"] == res2["password"]
|
assert res["password"] == res2["password"]
|
||||||
|
|
||||||
|
|
||||||
def test_nocreate_file(monkeypatch, tmpdir, dictproxy):
|
@pytest.mark.parametrize(
|
||||||
p = tmpdir.join("nocreate")
|
["nocreate_file", "account", "invite_token", "password"],
|
||||||
p.write("")
|
[
|
||||||
monkeypatch.setattr(chatmaild.doveauth, "NOCREATE_FILE", str(p))
|
(False, True, "asdf", "asdfasdmaimfelsgwerw"),
|
||||||
dictproxy.lookup_passdb("newuser12@chat.example.org", "zequ0Aimuchoodaechik")
|
(False, False, "asdf", "z9873240187420913798"),
|
||||||
assert not dictproxy.lookup_userdb("newuser12@chat.example.org")
|
(False, True, "", "dsaiujfw9fjiwf9w"),
|
||||||
|
(False, False, "asdf", "z987324018742asdf0913798"),
|
||||||
|
(False, True, "as df", "asj0wiefkj0ofkeefok"),
|
||||||
|
(False, True, "as df", "dfj0wiefkj0ofkeefok"),
|
||||||
|
(False, False, "as df", "j0wiefkj0ofas dfkeefok"),
|
||||||
|
(True, False, "asdf", "asdfmosadkdkfwdofkw"),
|
||||||
|
(True, False, "asdf", "z9873240187420913798"),
|
||||||
|
(True, False, "", "dsaiujfw9fjiwf9w"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_nocreate_file(
|
||||||
|
monkeypatch,
|
||||||
|
tmpdir,
|
||||||
|
dictproxy,
|
||||||
|
example_config,
|
||||||
|
nocreate_file: bool,
|
||||||
|
account: bool,
|
||||||
|
invite_token: str,
|
||||||
|
password: str,
|
||||||
|
):
|
||||||
|
if nocreate_file:
|
||||||
|
p = tmpdir.join("nocreate")
|
||||||
|
p.write("")
|
||||||
|
monkeypatch.setattr(chatmaild.doveauth, "NOCREATE_FILE", str(p))
|
||||||
|
example_config.invite_token = invite_token
|
||||||
|
dictproxy.lookup_passdb("newuser12@chat.example.org", password)
|
||||||
|
assert bool(dictproxy.lookup_userdb("newuser12@chat.example.org")) == account
|
||||||
|
|
||||||
|
|
||||||
def test_handle_dovecot_request(dictproxy):
|
def test_handle_dovecot_request(dictproxy):
|
||||||
|
|||||||
@@ -84,12 +84,13 @@ http {
|
|||||||
if ($request_method = GET) {
|
if ($request_method = GET) {
|
||||||
# Redirect to Delta Chat,
|
# Redirect to Delta Chat,
|
||||||
# which will in turn do a POST request.
|
# which will in turn do a POST request.
|
||||||
return 301 dcaccount:https://{{ config.domain_name }}/new;
|
return 301 dcaccount:https://{{ config.domain_name }}$request_uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
fastcgi_pass unix:/run/fcgiwrap.socket;
|
fastcgi_pass unix:/run/fcgiwrap.socket;
|
||||||
include /etc/nginx/fastcgi_params;
|
include /etc/nginx/fastcgi_params;
|
||||||
fastcgi_param SCRIPT_FILENAME /usr/lib/cgi-bin/newemail.py;
|
fastcgi_param SCRIPT_FILENAME /usr/lib/cgi-bin/newemail.py;
|
||||||
|
fastcgi_param QUERY_STRING $query_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Old URL for compatibility with e.g. printed QR codes.
|
# Old URL for compatibility with e.g. printed QR codes.
|
||||||
@@ -100,7 +101,7 @@ http {
|
|||||||
# Redirects are only for browsers.
|
# Redirects are only for browsers.
|
||||||
location /cgi-bin/newemail.py {
|
location /cgi-bin/newemail.py {
|
||||||
if ($request_method = GET) {
|
if ($request_method = GET) {
|
||||||
return 301 dcaccount:https://{{ config.domain_name }}/new;
|
return 301 dcaccount:https://{{ config.domain_name }}$request_uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
fastcgi_pass unix:/run/fcgiwrap.socket;
|
fastcgi_pass unix:/run/fcgiwrap.socket;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ for Delta Chat users. For details how it avoids storing personal information
|
|||||||
please see our [privacy policy](privacy.html).
|
please see our [privacy policy](privacy.html).
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if not config.invite_token %}
|
||||||
<a class="cta-button" href="DCACCOUNT:https://{{ config.mail_domain }}/new">Get a {{config.mail_domain}} chat profile</a>
|
<a class="cta-button" href="DCACCOUNT:https://{{ config.mail_domain }}/new">Get a {{config.mail_domain}} chat profile</a>
|
||||||
|
|
||||||
If you are viewing this page on a different device
|
If you are viewing this page on a different device
|
||||||
@@ -23,6 +24,10 @@ you can also **scan this QR code** with Delta Chat:
|
|||||||
🐣 **Choose** your Avatar and Name
|
🐣 **Choose** your Avatar and Name
|
||||||
|
|
||||||
💬 **Start** chatting with any Delta Chat contacts using [QR invite codes](https://delta.chat/en/help#howtoe2ee)
|
💬 **Start** chatting with any Delta Chat contacts using [QR invite codes](https://delta.chat/en/help#howtoe2ee)
|
||||||
|
{% else %}
|
||||||
|
**To join this instance, you need an invite link or QR code -
|
||||||
|
ask the admin for an invite.**
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if config.mail_domain != "nine.testrun.org" %}
|
{% if config.mail_domain != "nine.testrun.org" %}
|
||||||
<div class="experimental">Note: this is only a temporary development chatmail service</div>
|
<div class="experimental">Note: this is only a temporary development chatmail service</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user