|
All checks were successful
continuous-integration/drone/push Build is passing
## Description of the implemented changes The changes were: - [x] Bugfixing - [x] New Feature - [ ] Breaking Change - [x] Refactoring **OIDC-only mode improvements and UX tweaks (success toasts, unauthenticated redirect).** ## What has been changed? ### OIDC-only mode (new feature) - **Admin settings:** "Only OIDC sign-in" is an immediate toggle at the top of the OIDC section (no save button). Enabling it also turns off "Allow direct registration". When OIDC-only is on, the registration checkbox is disabled and shows a tooltip (DaisyUI `<.tooltip>`). - **Backend:** Password sign-in is forbidden via Ash policy (`OidcOnlyActive` check). Password registration is blocked via validation `OidcOnlyBlocksPasswordRegistration`. New plug `OidcOnlySignInRedirect`: when OIDC-only and OIDC are configured, GET `/sign-in` redirects to the OIDC flow; GET `/auth/user/password/sign_in_with_token` is rejected with redirect + flash. `AuthController.success/4` also rejects password sign-in when OIDC-only. - **Tests:** GlobalSettingsLive (OIDC-only UI), AuthController (redirect and password sign-in rejection), User authentication (register_with_password blocked when OIDC-only). ### UX / behaviour (no new feature flag) - **Success toasts:** Success flash messages auto-dismiss after 5 seconds via JS hook `FlashAutoDismiss` and optional `auto_clear_ms` on `<.flash>` (used for success in root layout and `flash_group`). - **Unauthenticated users:** Redirect to sign-in without the "You don't have permission to access this page" flash; that message is only shown to logged-in users who lack access. Logic in `LiveHelpers` and `CheckPagePermission` plug; test updated accordingly. ### Other - Layouts: comment about unprocessed join-request count no longer uses "TODO" (Credo). - Gettext: German translation for "Home" (Startseite); POT/PO kept in sync. - CHANGELOG: Unreleased section updated with the above. ## Definition of Done ### Code Quality - [x] No new technical depths - [x] Linting passed - [x] Documentation is added where needed (module docs, comments where non-obvious) ### Accessibility - [x] New elements are properly defined with html-tags (labels, aria-label on checkboxes) - [x] Colour contrast follows WCAG criteria (unchanged) - [x] Aria labels are added when needed (e.g. oidc-only and registration checkboxes) - [x] Everything is accessible by keyboard (toggles and buttons unchanged) - [x] Tab-Order is comprehensible - [x] All interactive elements have a visible focus (existing patterns) ### Testing - [x] Tests for new code are written (OIDC-only UI, auth controller, user auth; SMTP config builder and mailer) - [x] All tests pass - [ ] axe-core dev tools show no critical or major issues (not re-run for this PR; suggest spot-check on settings and sign-in) ## Additional Notes - **OIDC-only:** When the `OIDC_ONLY` env var is set, the toggle is read-only and shows "(From OIDC_ONLY)". When OIDC is not configured, the toggle is disabled. - **Invalidation:** Enabling OIDC-only sets `registration_enabled: false` in one update; disabling OIDC-only only updates `oidc_only` (registration left as-is). - **Review focus:** Plug order in router (OidcOnlySignInRedirect), policy/validation order in User, and that all OIDC-only paths (form, plug, controller) stay consistent. Reviewed-on: #474 Co-authored-by: Simon <s.thiessen@local-it.org> Co-committed-by: Simon <s.thiessen@local-it.org> |
||
|---|---|---|
| .forgejo | ||
| assets | ||
| config | ||
| docs | ||
| lib | ||
| priv | ||
| rel/overlays/bin | ||
| test | ||
| .credo.exs | ||
| .dockerignore | ||
| .drone.yml | ||
| .editorconfig | ||
| .env.example | ||
| .formatter.exs | ||
| .gitignore | ||
| .igniter.exs | ||
| .sobelow-conf | ||
| .tool-versions | ||
| CHANGELOG.md | ||
| CODE_GUIDELINES.md | ||
| DESIGN_GUIDELINES.md | ||
| docker-compose.prod.yml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| Justfile | ||
| LICENSE | ||
| mix.exs | ||
| mix.lock | ||
| README.md | ||
| renovate.json | ||
| renovate_backend_config.js | ||
Mila
Mila — simple, usable, self-hostable membership management for small to mid-sized clubs.
🚧 Project Status
⚠️ First Version — Expect breaking changes.
Contributions and feedback are welcome!
✨ Overview
Mila is a free and open-source membership management tool designed for real club needs.
It is self-hosting friendly, aims for accessibility and GDPR compliance, and focuses on usability instead of feature overload.
💡 Why Mila?
Most membership tools for clubs are either:
- Too complex — overloaded with features small and mid-sized clubs don’t need
- Too expensive — hidden fees, closed ecosystems, vendor lock-in
- Too rigid — no way to adapt fields, processes, or roles to your club’s reality
Mila is different:
- Simple: Focused on what clubs really need — members, dues, communication.
- Usable: Clean, accessible UI, GDPR-compliant, designed with everyday volunteers in mind.
- Flexible: Customize what data you collect about members, role-based permissions, and self-service for members.
- Truly open: 100% free and open source — no lock-in, transparent code, self-host friendly.
Our philosophy: software should help people spend less time on administration and more time on their community.
User Documentation (German)
You can find our documentation for users here: https://wiki.local-it.org/s/mila-user-dokumentation
🔑 Features
- ✅ Manage member data with ease
- ✅ Membership fees & payment status tracking
- ✅ Full-text search with fuzzy matching
- ✅ Sorting & filtering
- ✅ Roles & permissions (RBAC system with 4 permission sets)
- ✅ Custom fields (flexible per club needs)
- ✅ SSO via OIDC (works with Authentik, Rauthy, Keycloak, etc.)
- ✅ Sidebar navigation (standard-compliant, accessible)
- ✅ Global settings management
- ✅ Self-service & online application
- ✅ Accessibility improvements (WCAG 2.1 AA compliant keyboard navigation)
- ✅ Email sending
- ✅ Integration of Accounting-Software (Vereinfacht)
🚀 Quick Start (Development)
Prerequisites
We recommend using asdf for managing tool versions.
- Tested with:
asdf 0.16.5 - Required versions are documented in
.tool-versionsin this repo
Install system dependencies (Debian/Ubuntu)
# Debian 12
apt-get -y install build-essential autoconf m4 libncurses-dev libwxgtk3.2-dev libwxgtk-webview3.2-dev libgl1-mesa-dev libglu1-mesa-dev libpng-dev libssh-dev unixodbc-dev xsltproc fop libxml2-utils openjdk-17-jdk icu-devtools bison flex pkg-config
# Ubuntu 24
apt-get -y install build-essential autoconf m4 libwxgtk3.2-dev libwxgtk-webview3.2-dev libgl1-mesa-dev libglu1-mesa-dev libpng-dev libssh-dev unixodbc-dev xsltproc fop libxml2-utils libncurses-dev openjdk-11-jdk icu-devtools bison flex libreadline-dev
Install asdf
mkdir ~/.asdf
cd ~/.asdf
wget https://github.com/asdf-vm/asdf/releases/download/v0.16.5/asdf-v0.16.5-linux-amd64.tar.gz
tar -xvf asdf-v0.16.5-linux-amd64.tar.gz
ln -s ~/.asdf/asdf ~/.local/bin/asdf
Then follow the official “Shell Configuration” steps in the asdf docs.
Fish example (~/.config/fish/config.fish):
asdf completion fish > ~/.config/fish/completions/asdf.fish
set -gx PATH "$HOME/.asdf/shims" $PATH
Bash example (~/.bash_profile and ~/.bashrc):
export PATH="${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH"
. <(asdf completion bash)
Install project dependencies
git clone https://git.local-it.org/local-it/mitgliederverwaltung.git mila
cd mila
asdf install
# Inside the repo folder:
mix local.hex
mix archive.install hex phx_new
Note: running
mix local.hexmust be done inside the repo folder,
because.tool-versionsdefines the Erlang/Elixir versions.
Run the app
-
Copy env file:
cp .env.example .env # Set OIDC_CLIENT_SECRET inside .env -
Start everything (database, Mailcrab, Rauthy, app):
just run -
Services will be available at:
- App: http://localhost:4000
- Mail UI: http://localhost:1080
- Postgres:
localhost:5000
🔐 Testing SSO locally
Mila uses OIDC for Single Sign-On. In development, a local Rauthy instance is provided.
just run- go to localhost:8080, go to the Admin area
- Login with "admin@localhost" and password from
BOOTSTRAP_ADMIN_PASSWORD_PLAINin docker-compose.yml - add client from the admin panel
- Client ID: mv
- redirect uris: http://localhost:4000/auth/user/oidc/callback
- Authorization Flows: authorization_code
- allowed origins: http://localhost:4000
- access/id token algortihm: RS256 (EDDSA did not work for me, found just few infos in the ashauthentication docs)
- copy client secret to
.envfile - abort and run
just runagain
Now you can log in to Mila via OIDC!
OIDC with other providers (Authentik, Keycloak, etc.)
Mila works with any OIDC-compliant provider. The internal strategy is named :oidc — it works with any OIDC-compliant provider.
Important: The redirect URI must always end with /auth/user/oidc/callback.
Example for Authentik:
- Create an OAuth2/OpenID Provider in Authentik
- Set the redirect URI to:
https://your-domain.com/auth/user/oidc/callback - Configure environment variables:
DOMAIN=your-domain.com # or PHX_HOST=your-domain.com OIDC_CLIENT_ID=your-client-id OIDC_BASE_URL=https://auth.example.com/application/o/your-app OIDC_CLIENT_SECRET=your-client-secret # or use OIDC_CLIENT_SECRET_FILE
The OIDC_REDIRECT_URI is auto-generated as https://{DOMAIN}/auth/user/oidc/callback if not explicitly set.
⚙️ Configuration
- Env vars: see
.env.example
🏗️ Architecture
Tech Stack Overview:
- Backend: Elixir + Phoenix + Ash Framework
- Frontend: Phoenix LiveView + Tailwind CSS + DaisyUI
- Database: PostgreSQL
- Auth: AshAuthentication (OIDC + password)
Code Structure:
lib/accounts/&lib/membership/&lib/membership_fees/&lib/mv/authorization/— Ash resources and domainslib/mv_web/— Phoenix controllers, LiveViews, componentslib/mv/— Shared helpers and business logicassets/— Tailwind, JavaScript, static filestest/— All tests
📚 Full tech stack details: See CODE_GUIDELINES.md
📖 Implementation history: See docs/development-progress-log.md
🗄️ Database schema: See docs/database-schema-readme.md
🧑💻 Development
Common commands:
just run # Start full dev environment
just test # Run test suite
just lint # Code style checks
just audit # Security audits
just reset-database # Reset local DB
📚 Full development guidelines: See CODE_GUIDELINES.md
📦 Production Deployment
Local Production Testing
For testing the production Docker build locally:
-
Generate secrets:
mix phx.gen.secret # for SECRET_KEY_BASE mix phx.gen.secret # for TOKEN_SIGNING_SECRET -
Create
.envfile:# Copy template and edit cp .env.example .env nano .env -
Start production environment:
docker compose -f docker-compose.prod.yml up -
Database migrations run automatically on app start. For manual migration:
docker compose -f docker-compose.prod.yml exec app /app/bin/mv eval "Mv.Release.migrate" -
Access the production app:
- Production App: http://localhost:4001
- Uses same Rauthy instance as dev (localhost:8080)
Note: The local production setup uses network_mode: host to share localhost with the development Rauthy instance. For real production deployment, configure an external OIDC provider and remove network_mode: host.
Real Production Deployment
For actual production deployment:
- Use an external OIDC provider (not the local Rauthy)
- Update
docker-compose.prod.yml:- Remove
network_mode: host - Set
OIDC_BASE_URLto your production OIDC provider - Configure proper Docker networks
- Remove
- Set up SSL/TLS (e.g., via reverse proxy like Nginx/Traefik)
- Use secure secrets management — All sensitive environment variables support a
_FILEsuffix for Docker secrets (e.g.,SECRET_KEY_BASE_FILE=/run/secrets/secret_key_base). Seedocker-compose.prod.ymlfor an example setup with Docker secrets. - Configure database backups
🤝 Contributing
We welcome contributions!
- Open issues and PRs in this repo
- Please follow existing code style and conventions
- Expect breaking changes while the project is in early development
📄 License
License: AGPLv3
See the LICENSE file for details.
📬 Contact
- Issues: GitLab Issues
- E-Mail: info@local-it.org