Skip to content

Commit 4a0b238

Browse files
Prepare keycloak
1 parent 9e2efd1 commit 4a0b238

28 files changed

Lines changed: 800 additions & 262 deletions
Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1-
name: Build Backend container
1+
name: backend-ci
22

33
on:
44
push:
55
branches:
66
- main
77
tags:
88
- "v*"
9+
paths:
10+
- "backend/**"
911
pull_request:
1012
branches:
1113
- "main"
14+
paths:
15+
- "backend/**"
1216
env:
1317
IMAGE_NAME: 'bureaublad-api'
14-
PYTHON_VERSION: "3.12"
1518

1619
jobs:
1720
format:
@@ -20,9 +23,9 @@ jobs:
2023
- uses: actions/checkout@v5
2124

2225
- name: Set up Python ${{ env.PYTHON_VERSION }}
23-
uses: actions/setup-python@v5
26+
uses: actions/setup-python@v6
2427
with:
25-
python-version: ${{ env.PYTHON_VERSION }}
28+
python-version-file: 'backend/pyproject.toml'
2629

2730
- name: Install uv
2831
run: pipx install uv
@@ -43,9 +46,9 @@ jobs:
4346
- uses: actions/checkout@v5
4447

4548
- name: Set up Python ${{ env.PYTHON_VERSION }}
46-
uses: actions/setup-python@v5
49+
uses: actions/setup-python@v6
4750
with:
48-
python-version: ${{ env.PYTHON_VERSION }}
51+
python-version-file: 'backend/pyproject.toml'
4952

5053
- name: Install uv
5154
run: pipx install uv
@@ -55,10 +58,21 @@ jobs:
5558
cd backend/
5659
uv run sync
5760
58-
- name: Run ruff
61+
- name: Run ruff linter
5962
run: |
6063
cd backend/
6164
uv run ruff check
65+
66+
- name: Run hadolint linter
67+
uses: hadolint/hadolint-action@v3.1.0
68+
with:
69+
dockerfile: backend/Dockerfile
70+
format: json
71+
failure-threshold: warning
72+
73+
# - name: Run pyright type checker
74+
# run: |
75+
# uv run pyright
6276

6377
security:
6478
runs-on: ubuntu-latest
@@ -74,6 +88,7 @@ jobs:
7488

7589
build:
7690
needs: [format, lint, security]
91+
if: github.event_name != 'pull_request'
7792
name: Push Docker image to github registries
7893
runs-on: ubuntu-latest
7994
permissions:
@@ -93,7 +108,7 @@ jobs:
93108
94109
- name: Make changes to project to inject commit hash
95110
run: |
96-
sed -i 's/VERSION: str = .*$/VERSION: str = "${{ steps.get_commit_hash.outputs.commit_hash }}"/g' backend/app/config.py
111+
sed -i 's/VERSION: str = .*$/VERSION: str = "${{ steps.get_commit_hash.outputs.commit_hash }}"/g' backend/app/const.py
97112
98113
99114
- name: Log in to the Container registry
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
1-
name: Build Frontend container
1+
name: frontend-ci
22

33
on:
44
push:
55
branches:
66
- main
77
tags:
88
- "v*"
9-
pull_request:
10-
branches:
11-
- "main"
9+
paths:
10+
- "frontend/**"
11+
1212

1313
env:
1414
IMAGE_NAME: 'bureaublad-frontend'
1515
URL: 'https://bureaublad.apps.digilab.network'
16-
NEXT_PUBLIC_KEYCLOAK_URL: 'https://id.la-suite.apps.digilab.network'
17-
NEXT_PUBLIC_KEYCLOAK_REALM: lasuite
18-
NEXT_PUBLIC_KEYCLOAK_CLIENT: bureaublad-frontend
19-
NEXT_PUBLIC_BACKEND_BASE_URL: 'https://bureaublad.apps.digilab.network/api'
16+
NEXT_PUBLIC_KEYCLOAK_URL: 'http://localhost:8080'
17+
NEXT_PUBLIC_KEYCLOAK_REALM: mijnbureau
18+
NEXT_PUBLIC_KEYCLOAK_CLIENT: bureaublad
19+
NEXT_PUBLIC_BACKEND_BASE_URL: http://localhost:8000'
2020

2121
jobs:
2222
push_to_registries:

.github/workflows/sonarcube.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ jobs:
1515
- uses: actions/checkout@v5
1616
with:
1717
fetch-depth: 0
18+
1819
- name: SonarQube Scan
19-
uses: SonarSource/sonarqube-scan-action@v5
20+
uses: SonarSource/sonarqube-scan-action@v6
2021
env:
2122
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
2223

README.md

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,36 @@
11
# Bureaublad
22

3+
![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/minbzk/bureaublad/build-backend.yaml?label=backend)
4+
![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/minbzk/bureaublad/build-frontend.yaml.yaml?label=frontend)
5+
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=MinBZK_bureaublad&metric=coverage)](https://sonarcloud.io/summary/new_code?id=MinBZK_bureaublad)
6+
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=MinBZK_bureaublad&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=MinBZK_bureaublad)
7+
![GitHub License](https://img.shields.io/github/license/minbzk/bureaublad)
8+
39
Bureaublad is a flexible dashboard that aggregates information from multiple open-source components, providing users with a unified interface to access essential tools and data.
410

511
It is a stateless application, so all data stays at the source.
612

713
## Integrated Tools
814

9-
- [CalDav](https://datatracker.ietf.org/doc/html/rfc4791) (calendar protocol)
15+
- [CalDav protocol](https://datatracker.ietf.org/doc/html/rfc4791) (calendar protocol)
16+
- calendar
17+
- tasks
1018
- [Docs](https://github.com/suitenumerique/docs)
1119
- [Ocs](https://docs.nextcloud.com/server/stable/developer_manual/client_apis/OCS/ocs-api-overview.html) (Nextcloud & OwnCloud protocol)
12-
- [OpenZaak](https://github.com/open-zaak/open-zaak)
13-
- OpenAI Chat Completion API
20+
- activity app
21+
- [Drive](https://github.com/suitenumerique/drive)
22+
- [Meet](https://github.com/suitenumerique/meet)
23+
- [OpenAI interface](https://github.com/openai/openai-python)
1424

1525
## Planned Integrations
1626

1727
- [Synapse](https://github.com/element-hq/synapse)
18-
- [Drive](https://github.com/suitenumerique/drive)
19-
- [Meet](https://github.com/suitenumerique/meet)
2028

2129
## Getting Started
2230

2331
To run Bureaublad, ensure you have [Docker](https://docs.docker.com/get-started/get-docker/) installed.
2432

25-
The copy the example.env in the backend to .env
33+
copy the example.env in the backend to .env
2634

2735
```bash
2836
cp backend/example.env backend/.env
@@ -35,19 +43,27 @@ docker compose build
3543
docker compose up
3644
```
3745

38-
When the application start you will get 3 urls:
46+
When the application start you will get 4 urls:
3947

40-
1. <http://localhost:3000>
41-
2. <http://localhost:8081>
42-
3. <http://localhost:8080>
48+
- [frontend](http://localhost:3000)
49+
- [backend](http://localhost:8000)
50+
- [keycloak master](http://localhost:8080)
51+
- [keycloak mijnbureau](http://localhost:8080/realms/mijnbureau/account/)
4352

44-
You can use the admin user to login:
53+
In the master realm you can login with the admin credentials and manage all realm, including mijnbureau
4554

4655
```
4756
username: admin
4857
password: admin
4958
```
5059

60+
in the MijnBureau realm you can login with 4 users:
61+
62+
```
63+
username: jane@mijnbureau.nl password: jane
64+
username: john@mijnbureau.nl password: john
65+
```
66+
5167
## Solution Architecture
5268

5369
Bureaublad consolidates data from various tools into a single platform, making it easier for users to access the information they need, when they need it.
@@ -64,7 +80,6 @@ In the future we plan to add a advanced search feature and make the page more co
6480

6581
- **Frontend:** [React](https://react.dev/)
6682
- **Backend:** [FastAPI](https://fastapi.tiangolo.com/)
67-
- **Design System:** [Rijkshuiststyle NL-Design System](https://github.com/nl-design-system/rijkshuisstijl-community)
6883

6984
**Note:** Your identity provider must support Token Exchange for authentication. Verify compatibility before deployment.
7085

backend/Dockerfile

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
FROM --platform=$BUILDPLATFORM python:3.12-slim
1+
FROM --platform=$BUILDPLATFORM python:3.13-slim
22

33
# Install uv.
44
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
55

66
# Copy the application into the container.
7-
COPY app/ /app
7+
COPY app/ /app/app
88
COPY uv.lock pyproject.toml /app/
99

1010
# Install the application dependencies.
1111
WORKDIR /app
12+
ENV UV_PROJECT_ENVIRONMENT=/venv
1213
RUN uv sync --frozen --no-cache
1314

14-
WORKDIR /
15-
ENV PYTHONPATH=/
15+
ENV PYTHONPATH=/app/
1616

1717
# Run the application.
18-
CMD ["/app/.venv/bin/uvicorn", "--host", "0.0.0.0", "app.main:app", "--port", "8080"]
18+
ENTRYPOINT [ "/venv/bin/uvicorn" ]
19+
CMD ["--host", "0.0.0.0", "app.main:app", "--port", "8000"]

backend/app/authentication.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import httpx
66
from fastapi import Depends, Request
7-
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
7+
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer, OAuth2AuthorizationCodeBearer
88
from jose import JWTError, jwt
99
from jose.constants import ALGORITHMS
1010
from jose.jws import get_unverified_header
@@ -15,11 +15,21 @@
1515

1616
logger = logging.getLogger(__name__)
1717

18-
security = HTTPBearer(bearerFormat="JWT", scheme_name="bearer", description="OpenID Connect JWT token", auto_error=True)
18+
bearer_scheme = HTTPBearer(
19+
bearerFormat="JWT", scheme_name="Bearer", description="OpenID Connect JWT token", auto_error=True
20+
)
21+
openid_scheme = OAuth2AuthorizationCodeBearer(
22+
authorizationUrl=str(settings.OIDC_AUTHORIZATION_ENDPOINT),
23+
refreshUrl=str(settings.parsed_oidc_public_token_endpoint),
24+
tokenUrl=str(settings.parsed_oidc_public_token_endpoint),
25+
auto_error=False,
26+
scheme_name="OpenID Connect",
27+
)
1928

2029

2130
@cache
2231
def get_public_key(kid: str) -> dict[str, str]:
32+
logger.debug(f"Fetching public key from {settings.OIDC_JWKS_ENDPOINT}")
2333
response = httpx.get(settings.OIDC_JWKS_ENDPOINT)
2434
response.raise_for_status()
2535
jwks = response.json()
@@ -32,8 +42,12 @@ def get_public_key(kid: str) -> dict[str, str]:
3242
return public_key
3343

3444

35-
def get_current_user(request: Request, credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)]) -> str:
36-
token = credentials.credentials
45+
def get_current_user(
46+
request: Request,
47+
credentials: Annotated[HTTPAuthorizationCredentials, Depends(bearer_scheme)],
48+
openid_token: str = Depends(openid_scheme),
49+
) -> str:
50+
token = openid_token or (credentials.credentials if credentials else None)
3751

3852
jtw_decode_options = {
3953
"require_iat": True,
@@ -44,15 +58,18 @@ def get_current_user(request: Request, credentials: Annotated[HTTPAuthorizationC
4458
}
4559

4660
try:
47-
header = get_unverified_header(token)
61+
# Get header info for key selection - signature verified in jwt.decode below
62+
header = get_unverified_header(token) # NOSONAR
4863
alg = header.get("alg", None)
4964

65+
# Determine verification key based on algorithm
5066
if alg == ALGORITHMS.RS256:
5167
kid = header.get("kid")
5268
key = get_public_key(kid=kid)
5369
else:
5470
key = str(settings.OIDC_CLIENT_SECRET)
5571

72+
# Full signature verification happens here
5673
claims = jwt.decode(
5774
token,
5875
key,

0 commit comments

Comments
 (0)