Compare commits

..

1 Commits

Author SHA1 Message Date
Anthony LC 9aa0cb7788 ⬆️(docker) upgrade to recommended version
Docker DX linter and trivy were pointing
vulnerabilities in our Dockerfile.
We bumped the base image to a more secure version.
2025-05-15 16:13:06 +02:00
8 changed files with 12 additions and 138 deletions
+3 -2
View File
@@ -1,7 +1,7 @@
# Django impress
# ---- base image to inherit from ----
FROM python:3.12.6-alpine3.20 AS base
FROM python:3.12.10-alpine AS base
# Upgrade pip to its latest release to speed up dependencies installation
RUN python -m pip install --upgrade pip setuptools
@@ -30,12 +30,13 @@ RUN mkdir /install && \
# ---- mails ----
FROM node:20 AS mail-builder
FROM node:24-alpine AS mail-builder
COPY ./src/mail /mail/app
WORKDIR /mail/app
RUN apk update && apk add --no-cache bash
RUN yarn install --frozen-lockfile && \
yarn build
-37
View File
@@ -1,11 +1,9 @@
"""Permission handlers for the impress core app."""
from django.conf import settings
from django.core import exceptions
from django.db.models import Q
from django.http import Http404
from lasuite.oidc_resource_server.authentication import ResourceServerAuthentication
from rest_framework import permissions
from core.models import DocumentAccess, RoleChoices, get_trashbin_cutoff
@@ -136,38 +134,3 @@ class DocumentAccessPermission(AccessPermission):
raise Http404
return has_permission
class ResourceServerClientPermission(permissions.BasePermission):
"""
Permission class for resource server views.
This provides a way to open the resource server views to a limited set of
Service Providers.
Note: we might add a more complex permission system in the future, based on
the Service Provider ID and the requested scopes.
"""
def has_permission(self, request, view):
"""
Check if the user is authenticated and the token introspection
provides an authorized Service Provider.
"""
if not isinstance(
request.successful_authenticator, ResourceServerAuthentication
):
# Not a resource server request
return True
# Check if the user is authenticated
if not request.user.is_authenticated:
return False
if view.action not in view.resource_server_actions:
return False
# When used as a resource server, the request has a token audience
return (
request.resource_server_token_audience in settings.OIDC_RS_ALLOWED_AUDIENCES
)
+1 -19
View File
@@ -25,7 +25,6 @@ import requests
import rest_framework as drf
from botocore.exceptions import ClientError
from lasuite.malware_detection import malware_detection
from lasuite.oidc_resource_server.authentication import ResourceServerAuthentication
from rest_framework import filters, status, viewsets
from rest_framework import response as drf_response
from rest_framework.permissions import AllowAny
@@ -432,7 +431,6 @@ class DocumentViewSet(
pagination_class = Pagination
permission_classes = [
permissions.DocumentAccessPermission,
permissions.ResourceServerClientPermission,
]
queryset = models.Document.objects.all()
serializer_class = serializers.DocumentSerializer
@@ -442,22 +440,6 @@ class DocumentViewSet(
list_serializer_class = serializers.ListDocumentSerializer
trashbin_serializer_class = serializers.ListDocumentSerializer
tree_serializer_class = serializers.ListDocumentSerializer
resource_server_actions = {
"list",
"retrieve",
"create_for_owner",
}
def get_authenticators(self):
"""Allow resource server authentication for very specific actions."""
authenticators = super().get_authenticators()
# self.action does not exist yet
action = self.action_map[self.request.method.lower()]
if action in self.resource_server_actions:
authenticators.append(ResourceServerAuthentication())
return authenticators
def annotate_is_favorite(self, queryset):
"""
@@ -689,7 +671,7 @@ class DocumentViewSet(
authentication_classes=[authentication.ServerToServerAuthentication],
detail=False,
methods=["post"],
permission_classes=[permissions.IsAuthenticated],
permission_classes=[],
url_path="create-for-owner",
)
@transaction.atomic
+3 -15
View File
@@ -6,15 +6,6 @@ from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class AuthenticatedServer:
"""
Simple class to represent an authenticated server to be used along the
IsAuthenticated permission.
"""
is_authenticated = True
class ServerToServerAuthentication(BaseAuthentication):
"""
Custom authentication class for server-to-server requests.
@@ -48,16 +39,13 @@ class ServerToServerAuthentication(BaseAuthentication):
# Validate token format and existence
auth_parts = auth_header.split(" ")
if len(auth_parts) != 2 or auth_parts[0] != self.TOKEN_TYPE:
# Do not raise here to leave the door open for other authentication methods
return None
raise AuthenticationFailed("Invalid authorization header.")
token = auth_parts[1]
if token not in settings.SERVER_TO_SERVER_API_TOKENS:
# Do not raise here to leave the door open for other authentication methods
return None
raise AuthenticationFailed("Invalid server-to-server token.")
# Authentication is successful
return AuthenticatedServer(), token
# Authentication is successful, but no user is authenticated
def authenticate_header(self, request):
"""Return the WWW-Authenticate header value."""
-2
View File
@@ -4,7 +4,6 @@ from django.conf import settings
from django.urls import include, path, re_path
from lasuite.oidc_login.urls import urlpatterns as oidc_urls
from lasuite.oidc_resource_server.urls import urlpatterns as resource_server_urls
from rest_framework.routers import DefaultRouter
from core.api import viewsets
@@ -45,7 +44,6 @@ urlpatterns = [
[
*router.urls,
*oidc_urls,
*resource_server_urls,
re_path(
r"^documents/(?P<resource_id>[0-9a-z-]*)/",
include(document_related_router.urls),
+1 -59
View File
@@ -327,6 +327,7 @@ class Base(Configuration):
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"mozilla_django_oidc.contrib.drf.OIDCAuthentication",
"rest_framework.authentication.SessionAuthentication",
),
"DEFAULT_PARSER_CLASSES": [
@@ -585,65 +586,6 @@ class Base(Configuration):
default=True, environ_name="ALLOW_LOGOUT_GET_METHOD", environ_prefix=None
)
# OIDC - Docs as a resource server
OIDC_OP_URL = values.Value(
default=None, environ_name="OIDC_OP_URL", environ_prefix=None
)
OIDC_OP_INTROSPECTION_ENDPOINT = values.Value(
environ_name="OIDC_OP_INTROSPECTION_ENDPOINT", environ_prefix=None
)
OIDC_VERIFY_SSL = values.BooleanValue(
default=True, environ_name="OIDC_VERIFY_SSL", environ_prefix=None
)
OIDC_TIMEOUT = values.IntegerValue(
default=3, environ_name="OIDC_TIMEOUT", environ_prefix=None
)
OIDC_PROXY = values.Value(None, environ_name="OIDC_PROXY", environ_prefix=None)
OIDC_RS_BACKEND_CLASS = "lasuite.oidc_resource_server.backend.ResourceServerBackend"
OIDC_RS_AUDIENCE_CLAIM = values.Value( # The claim used to identify the audience
default="client_id", environ_name="OIDC_RS_AUDIENCE_CLAIM", environ_prefix=None
)
OIDC_RS_PRIVATE_KEY_STR = values.Value(
default=None,
environ_name="OIDC_RS_PRIVATE_KEY_STR",
environ_prefix=None,
)
OIDC_RS_ENCRYPTION_KEY_TYPE = values.Value(
default="RSA",
environ_name="OIDC_RS_ENCRYPTION_KEY_TYPE",
environ_prefix=None,
)
OIDC_RS_ENCRYPTION_ALGO = values.Value(
default="RSA-OAEP",
environ_name="OIDC_RS_ENCRYPTION_ALGO",
environ_prefix=None,
)
OIDC_RS_ENCRYPTION_ENCODING = values.Value(
default="A256GCM",
environ_name="OIDC_RS_ENCRYPTION_ENCODING",
environ_prefix=None,
)
OIDC_RS_CLIENT_ID = values.Value(
None, environ_name="OIDC_RS_CLIENT_ID", environ_prefix=None
)
OIDC_RS_CLIENT_SECRET = values.Value(
None,
environ_name="OIDC_RS_CLIENT_SECRET",
environ_prefix=None,
)
OIDC_RS_SIGNING_ALGO = values.Value(
default="ES256", environ_name="OIDC_RS_SIGNING_ALGO", environ_prefix=None
)
OIDC_RS_SCOPES = values.ListValue(
[], environ_name="OIDC_RS_SCOPES", environ_prefix=None
)
OIDC_RS_ALLOWED_AUDIENCES = values.ListValue(
default=[],
environ_name="OIDC_RS_ALLOWED_AUDIENCES",
environ_prefix=None,
)
# AI service
AI_FEATURE_ENABLED = values.BooleanValue(
default=False, environ_name="AI_FEATURE_ENABLED", environ_prefix=None
+2 -2
View File
@@ -1,4 +1,4 @@
FROM node:20-alpine AS frontend-deps
FROM node:24-alpine AS frontend-deps
WORKDIR /home/frontend/
@@ -45,7 +45,7 @@ ENV NEXT_PUBLIC_PUBLISH_AS_MIT=${PUBLISH_AS_MIT}
RUN yarn build
# ---- Front-end image ----
FROM nginxinc/nginx-unprivileged:1.26-alpine AS frontend-production
FROM nginxinc/nginx-unprivileged:1.27-alpine AS frontend-production
# Un-privileged user running the application
ARG DOCKER_USER
+2 -2
View File
@@ -1,4 +1,4 @@
FROM node:20-alpine AS y-provider-builder
FROM node:24-alpine AS y-provider-builder
WORKDIR /home/frontend/
@@ -15,7 +15,7 @@ COPY ./src/frontend/servers/y-provider ./servers/y-provider
WORKDIR /home/frontend/servers/y-provider
RUN yarn build
FROM node:20-alpine AS y-provider
FROM node:24-alpine AS y-provider
WORKDIR /home/frontend/