Commit graph

165 commits

Author SHA1 Message Date
122bc387d8 Time out requests to the OTP server
None of the calls to the OTP server set a timeout, so a hanging OTP
server blocked a web worker indefinitely. Each call now times out after
OTP_TIMEOUT seconds (default 10).
2026-11-06 15:26:48 +03:00
f8567b2a41 Read all configuration in settings and document it in env.sample
The OTP server variables were read straight from the environment in
views.py. They are now OTP_* settings next to everything else, under
the same environment variable names, so existing deployments are
unaffected.

Remove LDAP_SERVER and LDAP_USERS_DN, which nothing used.
env.sample moves to the repository root (where .env is read from) and
lists every variable settings.py reads, including defaults and which
values are secrets.
2026-11-06 15:26:26 +03:00
38eec4d5c7 Log to stdout instead of rotating files
Containers should log to stdout. The RotatingFileHandler wrote to
logs/debug.log, which is lost with the pod and crashed startup when
the directory was missing.

All logging now goes to a console handler. LOG_LEVEL sets the root
level (default INFO), DJANGO_LOG_LEVEL sets django.* (default INFO),
and LDAP3_LOG=1 turns on ldap3 protocol logging. ENABLE_DEBUG_LOG and
MODULES_TO_LOG are gone.
2026-11-06 15:25:21 +03:00
db10408c08 Remove the unused nameko RPC service
nameko-func.py is the 2019 RPC backend that the views replaced with
direct ldap3 calls. Nothing imports it, and the nameko.conf it reads
does not exist.
2026-11-06 15:24:18 +03:00
29d4b5b089 Remove the unfinished v2 app (ungleichuser)
The v2 rewrite started in December 2021 and never got past a register
view that stored users in Django's database instead of LDAP. Its goals
(ldap3 only, multiple LDAP servers, a current Django) now live in the
dal app, which is what production runs.
2026-11-06 15:24:18 +03:00
30703f9fa5 Configure email through Django 6.1 MAILERS
EMAIL_HOST, EMAIL_PORT and EMAIL_USE_TLS are deprecated in 6.1 in favour
of the MAILERS setting. The same environment variables now feed the
default SMTP mailer, so existing deployments keep working. Also adds
optional EMAIL_HOST_USER, EMAIL_HOST_PASSWORD and EMAIL_TIMEOUT
(default 10s; previously no timeout).
2026-11-06 15:23:52 +03:00
de5e0842c5 Upgrade to Django 6.1 on Python 3.15
Step 4 of the upgrade and the final target. No code changes needed;
all dependencies are pure Python now. The remaining deprecations come
from Django 6.1's new MAILERS setting and are handled separately.
2026-11-06 15:23:02 +03:00
187dd8d6cc Support multiple LDAP servers with failover
LDAPSERVER was documented as a space separated list, but the whole
string went into a single ldap3.Server. Logins worked (python-ldap
accepts lists), while registration and every write would have failed
with more than one URI.

settings.LDAP_SERVERS is now the split list, used through one ldap3
ServerPool per process: requests go to the first reachable server, a
server that is down is skipped for 60s, connections time out after 5s,
and with all servers down the pool gives up after two passes instead
of retrying forever.
2026-11-06 15:21:45 +03:00
ab45535f38 Replace django-auth-ldap and python-ldap with an ldap3 backend
python-ldap is a C extension that needs the OpenLDAP headers to build
(and needed a workaround in the Dockerfile). ldap3, which the app
already used for all writes, is pure Python.

dal.backends.LdapBackend keeps the django-auth-ldap behaviour: search
the uid in every LDAPSEARCH base as the admin DN, require exactly one
match, log in with the user's DN, and mirror givenName, sn and mail into
the local Django user. Empty passwords are rejected (LDAP treats them
as anonymous logins, which succeed).

LDAP_USE_TLS now applies StartTLS to every connection, including the
admin connection used for writes, not just logins.
2026-11-06 15:15:03 +03:00
91ee7732a0 Upgrade to Django 5.2 LTS
Step 3 of the upgrade (Python 3.12). No code changes needed: the
previous commits removed everything Django 5.x drops, and the tests
run with no Django deprecation warnings.
2026-11-06 15:13:52 +03:00
cab4b1d709 Replace deprecated datetime.utcnow() with time.time()
utcnow() is deprecated since Python 3.12. Calling .timestamp() on its
naive result also read it as local time, so token ages were off by the
host's UTC offset everywhere except on UTC hosts. Unix time from
time.time() is what the code meant.
2026-11-06 15:11:01 +03:00
bf7c6bf239 Remove APIs that Django 5.0 and 5.1 drop
Remove USE_L10N; localized formatting is always on from 5.0. Replace
User.objects.make_random_password() (removed in 5.1) with a
secrets-based generator for the create API's initial password, using
the same unambiguous alphabet and 16 characters instead of 10.
2026-11-06 15:10:43 +03:00
09cf05d158 Upgrade to Django 4.2 LTS on Python 3.12
Step 2 of the upgrade. Dependencies are bumped to their newest versions
compatible with Django 4.2.

Django 4.0 rejects cache_control applied directly to a view method, so
Index.get now wraps it in method_decorator. Without this the landing
page returns 500. A test now checks the no-store header.
2026-11-06 15:10:07 +03:00
a7664fd6d8 Remove APIs that Django 4.0 drops
ugettext_lazy becomes gettext_lazy. The unused django.conf.urls.url
import would fail to import on 4.0.
2026-11-06 15:08:02 +03:00
3ceff001bb Upgrade to Django 3.2 LTS
Step 1 of the upgrade (Python 3.9). Dependencies are bumped to their
newest versions compatible with Django 3.2.

DEFAULT_AUTO_FIELD is set to AutoField, which matches the existing
tables, so no migration is needed and the W042 warnings go away.
2026-11-06 15:07:37 +03:00
7fa0fac3db Drop unused django-filter dependency
It was never imported or listed in INSTALLED_APPS.
2026-11-06 15:06:30 +03:00
4f00971f84 Use {% load static %} in templates
{% load staticfiles %} is deprecated in Django 2.2 and removed in 3.0.
2026-11-06 15:06:00 +03:00
15af9f8d9c Stop writing passwords and reset links to logs
SeedRetrieveCreate printed every submitted password to stdout, so it
ended up in container and server logs. The reset view logged complete
reset URLs at debug level, and those work as credentials until used.
2026-11-06 15:04:23 +03:00
0cb0344ac8 Compute the next uidNumber from LDAP instead of a local file
The highest uidNumber was cached in dal/ldap_max_uid_file. That breaks
with more than one replica and is lost on every container restart. The
uniqueness check behind it searched base '' (the root DSE), which
OpenLDAP never matches, so nothing caught a stale file. The error path
also referenced a setting that does not exist.

Now a paged search over ENTIRE_SEARCH_BASE finds the highest uidNumber
on each activation. Numbering is unchanged: the next number is above
both LDAP_DEFAULT_START_UID and every existing uidNumber.
LDAP_MAX_UID_FILE_PATH is no longer used.
2026-11-06 15:03:57 +03:00
f675ce9e31 Reject usernames that exist anywhere in the directory
Registration only collided inside ou=customers, so a staff username
from ou=users could be registered again as a customer, creating two
accounts with the same uid. Check the whole tree (ENTIRE_SEARCH_BASE)
on the register form, in the create API, and again at activation in
case the name was taken in between.
2026-11-06 15:03:14 +03:00
dad66040ed Keep passwords out of activation links
The activation link carried base64-encoded username, password, names
and email, so plaintext passwords ended up in mail archives, browser
history and proxy logs.

Registration now hashes the password to {SSHA} immediately and stores
it in a new PendingRegistration table (migration 0002). The emailed
link is /activate/<token>/ with a random token only, and activation
writes the stored hash into LDAP. Pending entries expire after 24h,
like reset tokens. This also drops the PseudoUser/token-generator
workaround.

Links in emails sent before deployment stop working; those users must
register again.
2026-11-06 15:02:41 +03:00
23ddf33d41 Require a verified reset link to set a new password
ResetRequest.post took the username from a hidden form field and
changed that user's password without checking any token, so anyone
could take over any account with a single POST to /reset/.

Opening a valid reset link now stores the verified username in the
session (valid for one hour, cleared after use). The POST only acts on
that user and ignores the form field. Reset tokens now come from
secrets.token_urlsafe, and malformed links return 404 instead of 500.
2026-11-06 15:01:47 +03:00
b44e2ffcf4 Escape user input in LDAP filters and DNs
The uid was formatted straight into search filters, so a password
reset for user '*' matched and changed the first entry in the tree
(staff accounts included). Escape filter values with
escape_filter_chars and DN values with escape_rdn.

Also fix the username pattern: [a-z|0-9|\-|_] allowed '|'.
2026-11-06 15:01:06 +03:00
13b1d9c23a Add end-to-end tests and pin production baseline dependencies
Restore root requirements.txt pinned to what production runs
(Django 2.2, last patch 2.2.28) so the upgrade starts from a
reproducible baseline. Restore logs/.keep; the logging config fails
on a fresh clone without the directory.

The tests drive every view over HTTP against the real test LDAP:
login, registration and activation, data and password changes,
password reset, account deletion, the create API and OTP seeds.
Known bugs are recorded as expected failures:
- password reset POST changes any user's password without a token
- LDAP filter injection in reset (user='*' hits the first entry)
- activation link carries the base64-encoded password
- a username from ou=users can be registered again under ou=customers
2026-11-06 14:46:24 +03:00
692c1b1657 Add local OpenLDAP 2.6 test server for development
Alpine 3.24 based slapd with the production directory layout
(dc=ungleich,dc=ch, ou=customers, ou=users) and the openssh-lpk schema.
The app runs natively against it on ldap://localhost:3389.
Includes dev/env.dev with dev-only settings (cp dev/env.dev .env).
2026-11-06 14:46:24 +03:00
Nico Schottelius
e88cd4bc59 Register add 2021-12-12 20:39:32 +01:00
Nico Schottelius
12ed80292b in between commit 2021-12-12 20:07:32 +01:00
Nico Schottelius
196ab2787e ++notes 2021-12-05 20:53:41 +01:00
Nico Schottelius
bdd97f4eeb Begin setup of new auth system 2021-12-05 18:59:19 +01:00
Nico Schottelius
01e8c827ab Add template based on Django 4.0rc1
Signed-off-by: Nico Schottelius <nico@nico-notebook.schottelius.org>
2021-12-05 18:34:25 +01:00
Nico Schottelius
9247b627fd Begin cleanup 2021-12-05 18:19:35 +01:00
Nico Schottelius
27d15a5ddf Add initial Dockerfile 2021-12-05 18:14:32 +01:00
73bf95fe56 Merge branch 'master' into 'master' v1.1.2
Upgrade django version to 2.2.16 from 2.1.7 as seen on prod

See merge request ungleich-public/ungleich-user!8
2020-09-02 17:13:35 +02:00
PCoder
6a3d48e6e1 Upgrade django version to 2.2.16 from 2.1.7 as seen on prod
This upgrade is to patch security fixes included in 2.2.16
2020-09-02 20:24:01 +05:30
Timothee Floure
a7b9d70b9d Merge upstream repository and code running on production... v1.1.1 2020-05-24 08:04:25 +02:00
0ded060220 Merge branch 'uid-validation' into 'master' v1.1.0
Minimal README, validate username format on registration, fix field name on login

See merge request ungleich-public/ungleich-user!7
2020-05-01 11:52:12 +02:00
26916f7def Validate username format in what seem to be WIP API? 2020-05-01 11:11:24 +02:00
ac7d9df7a9 Remove what seems to be useless 'dump-via-wget' directory 2020-05-01 11:08:40 +02:00
4cf0161d7c Ask for username instead of email for login, validate username on
registration
2020-05-01 11:08:13 +02:00
7cbdf62b96 Populate README, refresh configuration template 2020-05-01 11:07:29 +02:00
406ea23436 Fix django dependency requirement 2020-05-01 11:06:57 +02:00
8a8cc5d20e better response messsage 2019-05-30 05:17:20 -04:00
e1b4aac222 fix typho response -> Response 2019-05-30 05:07:06 -04:00
f6b723b76a fix typho in creation link 2019-05-30 05:04:11 -04:00
ce5f88b134 fix hardwritten test 2019-05-26 16:59:52 -04:00
36d10deaab Added buttons for back to index and show seeds 2019-05-26 16:50:56 -04:00
d37144c541 seed listing view and template 2019-05-26 16:15:07 -04:00
321d8548da url for seed list 2019-05-26 16:13:58 -04:00
9576ae9064 Same messages in creation/deletion preventing scape chars errors in password 2019-05-06 11:42:58 -04:00
9711dc1ecc fix show message error when not valid credentials 2019-05-05 18:12:43 -04:00