Electronic building permit application for Swiss cantons.
This repository contains the source code for the web applications used to handle electronic building permits and comparable processes in the Swiss cantons of Berne, Grisons, Schwyz, Solothurn and Uri.
The following image shows a high-level overview of the architecture:
- The application is composed of various Docker containers, which are shown in light blue in the architecture overview.
- The frontend consists of two Ember.js apps, one for applicants submitting building permit applications ("portal"), and another used by members of the public authorities ("internal area"). The two apps can share code through the Ember Addon
ember-ebau-core
. - The backend is based on Python/Django and exposes a GraphQL API for forms and workflows based on Caluma and set of domain-specific REST endpoints (Django REST Framework).
- PostgreSQL is used as database.
├── compose # docker-compose files
├── db # database Dockerfile and utils
├── django # backend code, containing both API and Caluma
├── document-merge-service # document generation templates and config
├── ember-caluma-portal # Caluma-based portal
├── ember-camac-ng # Ember.js app optimized for embedding in other applications
├── ember-ebau # Ember.js based application for internal area
├── ember-ebau-core # Ember.js addon for code sharing between multiple Ember.js apps
├── keycloak # Keycloak configuration for local development
├── proxy # Nginx configuration for local development
└── tools # miscellaneous utilities
Due to ongoing modernization work, some Frontend modules are not yet integrated in ember-ebau
, but instead are still part of ember-camac-ng
. Few Frontend modules are not part of this repository yet at all. The following table lists the most important modules in the "internal" part of the application and their respective completeness / integration state (in the demo
configuration).
Module | Description | Backend | Frontend | Part of ember-ebau |
---|---|---|---|---|
Main Nav (resource) | ||||
Dossier list | Show a list of dossiers | ✔️ | ✔️ | ✔️ |
Task list | Show a list of tasks | ✔️ | ✔️ | ✔️ |
Templates | Manage document templates (docx) | ✔️ | ✔️ | ✔️ |
Organization / Permissions | Manage own organization and permissions | ✔️ | ✔️ | ✔️ |
Static content | Static content, markdown editor | ✔️ | ✔️ | ✔️ |
Text components | Manage snippets for usage in text fields | ✔️ | ⏳ | ⏳ |
Subnav (instance resource) | ||||
Tasks | View and manage tasks | ✔️ | ✔️ | ✔️ |
Form | View and edit main form | ✔️ | ✔️ | ✔️ |
Distribution | Get feedback from other organizations | ✔️ | ✔️ | ✔️ |
Alexandria | Document management | ✔️ | ✔️ | ✔️ |
Template | Generate document from template | ✔️ | ✔️ | ✔️ |
Journal | Collaborative notebook | ✔️ | ✔️ | ✔️ |
History | Shows milestones and historical data | ✔️ | ✔️ | ✔️ |
Publication | Manage publication in newspaper | ✔️ | ✔️ | ✔️ |
Objections | Manage objections | ✔️ | ✔️ | ✔️ |
Responsible | Assign responsible users | ✔️ | ✔️ | ✔️ |
Claims | Ask applicant for additional info | ✔️ | ✔️ | ✔️ |
Rejection | Reject instance | ✔️ | ✔️ | ✔️ |
Billing | Manage billing entries | ✔️ | ✔️ | ✔️ |
Audit | Perform structured audit | ✔️ | ✔️ | ⏳ |
Audit-Log | Shows form changes | ✔️ | ⏳ | ⏳ |
The preferred development environment is based on Docker.
- Docker >= 20.04
- Docker-Compose
For local development:
Python:
- python 3.8
- pyenv/virtualenv
Ember:
- current LTS of Node.js
- pnpm
Docker can be used to get eBau up and running quickly. The following script guides you through the setup process. We recommend using the kt_gr
or kt_so
config for now, since other cantons still rely on legacy components for the internal area that are not part of this repository.
make start-dev-env
In case you want to manually modify /etc/hosts following domains need to point to 127.0.0.1 (localhost):
ebau-portal.local ebau.local ebau-keycloak.local ember-ebau.local ebau-rest-portal.local
For automatic checks during commit (formatting, linting) you can setup a git hook with the following commands:
pip install pre-commit
pre-commit install
After, you should be able to use to the following services:
- ember-ebau.local - new main application used for "internal" users
- ebau-portal.local - public-facing portal (Caluma-based, default choice for new projects, used in Kt. BE, UR)
- ebau.local/django/admin/ - Django admin interface
- ebau-keycloak.local/auth - IAM solution
- ember-ebau.local/mailpit/ - Mailpit UI
- ember-ebau.local/minio/ui/ - MinIO Admin UI
The following administrator accounts are present in Keycloak or the DB, respectively:
Application | Role | Username | Password | Notes |
---|---|---|---|---|
demo | Admin | user | user | |
kt_schwyz | Admin | admin | admin | |
Publikation | adsy | adsy | ||
kt_uri | Admin | admin | admin | |
PortalUser | portal | portal | ||
kt_bern | Admin | user | user | |
kt_gr | Admin | admin | admin | |
Applicant | editor | editor | Applicant editor role | |
Applicant | readonly | readonly | Applicant readonly role | |
kt_so | Admin | admin | admin | |
Applicant | editor | editor | Applicant editor role | |
Applicant | readonly | readonly | Applicant readonly role |
make debug-django
With docker compose you can attach to a running container (basically equivalent to docker compose up
without the -d
flag)
and interact via stdin.
docker compose attach django
This will allow you to
a. send signals to the container
b. drop to a pdb shell when the application runs into a breakpoint
Since the dev config runs the django development-server that reloads on file changes, inserting those breakpoints is effective immediately after saving the file.
press CTRL-p + CTRL-q
NOTE: since by default the attach
process will forward signals to the container you'll have to exit pressing
said sequence (which is the default setting for --detach-keys
and can be overridden). Pressing CTRL-c
however
will not only kill the TTY but also send the SIGINT to the container and stop it.
DOCS: https://docs.docker.com/reference/cli/docker/container/attach/
docker-compose up -d --build db django
cd {ember|ember-camac-ng|ember-caluma-portal|ember-ebau} # Enter ember from the top level of the repo
pnpm install # Install dependencies
pnpm test # Run tests
pnpm start-proxy # Run dev server with proxy to django api
Since this is a large project with lots of files, the default setup of ember will possibly fail to rebuild properly as it can't watch all of those files.
In order to fix that, install watchman as file watcher and
adjust the inotify
settings:
echo fs.inotify.max_user_watches=1000000 | sudo tee -a /etc/sysctl.conf # adjust settings
sudo sysctl -p # re-read config
Make sure that the latter command only returns one value, otherwise the settings is duplicated and needs to be cleaned up.
Note however that the apps ember-caluma-portal
, ember-camac-ng
, ember-ebau
and the addon ember-ebau-core
share the same node modules tree through a pnpm workspace.
The common pnpm workspace allows us to share code (e.g. addons) between the apps which are part of this repo (instead of following the typical approach of publishing releases on npm). This also means that
- (+) we save some disk space because of the avoided duplication in the
node_modules
directory - (-) the docker build processes of the two frontend containers have to run in the context of the root of the repo, in order to access the shared dependencies during build time
- (-) the ember versions
ember-caluma-portal
andember-camac-ng
need to be kept in sync
To enable django-silk
for profiling, simply add DJANGO_ENABLE_SILK=True
to your django/.env
file. Then restart the django container and browse to
http://ebau.local/api/silk/.
To switch from the demo
config to kt_bern
, one has to make sure that the frontend apps take up the right
environment variables.
- Stop the frontend servers started with
pnpm start-proxy
- Run
make kt_bern
- Run
docker-compose up -d && make loadconfig
- Start using command from step 1
- Run
docker-compose down
- Run
make kt_bern
- Run
docker-compose build
- Run
docker-compose up -d
The remote debugger settings for VS Code are committed to the repository.
- The configuration file is located at
.vscode/launch.json
. - The keyboard shortcut to launch the debugger is F5.
- Information on VS Code debugging
To enable debugging in the django container the ptvsd server must be started.
Since this debug server collides with other setups (PyCharm, PyDev) it will
only be started if the env var ENABLE_PTVSD_DEBUGGER
is set to True
in
django/.env
.
In order to talk to the /graphql
endpoint with authentication, you can install
a GraphQL Tool (much like Postman). Tools you might consider here:
The GWR module is developed in two separate repositories:
- Frontend: inosca/ember-ebau-gwr
- Backend: inosca/ebau-gwr
If you use the GWR module, you need to generate a Fernet key according to the documentation of the gwr backend.
You need to set this key in each environment/server in your env file. Generate a separate key for each environment, since this is used to store / read the gwr user passwords.
The API should be designed in a way, that allows it to be used by any eBau project. For needed customization, the following rules apply:
- each permission may be mapped to a specific role in the specific project. In case a role may have different set of permissions than already available, introduce a new one and adjust the different views accordingly.
- for features which may not be covered by permissions, introduce feature flags.
For different feature flags and permissions, see APPLICATIONS
in settings.py.
In development mode, the application is configured to send all email to a Mailpit instance, so unless you specify something else, no email will be sent out from the development environment.
You can access the Mailpit via http://ebau.local/mailpit/ . Any email sent out will be instantly visible there.
Section to collect information on modules and cantons. This section is intended to facilitate know-how transfer, vacation handovers and debugging support cases.
Module used in Kt. SZ and Kt. UR (soon-ish), which accompanies the construction process after the decision. The municipality (up to now only cases are covered where the lead authority is the municipality) and the applicant interact through a series of work-items with documents.
There are construction stages ("Bauetappen"), which consist of dynamically selectable construction steps. Construction steps are a series of work-items, which usually follow the pattern of starting with a work-item addressed to the applicant, followed by one or more work-items addressed to the municipality. The applicant confirms, that they have adhered to the defined regulations, and the muncipality verifies it. The final work-item allows the muncipality to decide whether to continue the process or re-iterate to the beginning of the construction step.
The module is heavily defined by the configured workflow. Which construction steps and therefore which work-items are performed is handled through dynamic tasks. Construction step configuration (such as which task belongs to which construction step) is configured in the meta of tasks belonging to a construction step. Construction steps are essentially a grouping of tasks, there is no model representing them.
Construction stages are a multiple instance work-item with a child-case. The child-case contains the construction step work-items. The first construction stage is created when initializing the construction monitoring process. After that, a new construction stage can be initialized by a create work-item mutation on the existing work-item (in the status ready). BEWARE: To ensure that a new construction stage can always be created as long as the construction monitoring process isn't completed, the construction stage work items remain ready, while the construction stage child case has already been completed.
The core logic is contained mainly in the construction monitoring workflow and form configuration of the canton, the caluma events for the construction monitoring, module settings, some custom visibility and permission logic.
In the canton of Solothurn we use a custom authorization mechanism for the eBau Portal. The eBau portal can only be used with a login from my.so.ch, their eGov portal software. Since they do not offer OIDC authorization, we had to implement a custom solution using Keycloak's token exchange and direct naked impersonation features.
The authorization is designed to retreive an encrypted and signed JWT token which is then converted to a regular OIDC JWT token by Keycloak:
sequenceDiagram
autonumber
participant F as eBau Portal
participant M as eGov Portal
participant B as eBau API
participant K as Keycloak
F->>+M: Redirect to prestation
Note right of M: Encrypted and signed JWT token with user data
M->>-F: Redirect to login with eGov token
F->>+B: Send token (POST to /api/v1/auth/token-exchange)
B-->>B: Decrypt and verify eGov token, extract user data from token
B-->>+K: Create or update user
K-->>B: Return user
B-->>K: Token exchange with direct naked impersonation
K-->>-B: Return token for user
B->>-F: Return token
To enable the feature, the following configuration must be done:
By default Keycloak is already properly configured to support this authorization mechanism. In order to configure another environment, please refer to the documentation
# .env
ENABLE_TOKEN_EXCHANGE=true
This will enable the feature with a dummy eGov portal hosted on our NGINX proxy. In order to test with the eGov portal test environment we need to set some more environment variables (the censored values can be found in Vault):
# .env
EGOV_PORTAL_URL=****
EGOV_PRESTATION_PATH=****
# django/.env
TOKEN_EXCHANGE_JWT_ISSUER=****
TOKEN_EXCHANGE_JWT_SECRET=****
TOKEN_EXCHANGE_JWE_SECRET=****
This project is licensed under the EUPL-1.2-or-later. See LICENSE for details.