Skip to content

Commit

Permalink
Merge pull request #94 from ropable/master
Browse files Browse the repository at this point in the history
Bump project dependency versions, tweak HealthCheckMiddleware
  • Loading branch information
ropable authored Sep 30, 2024
2 parents 2eeeafd + e5940e7 commit 22f44d8
Show file tree
Hide file tree
Showing 11 changed files with 741 additions and 696 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
# Prepare the base environment.
FROM python:3.11.9-slim AS builder_base_csw
FROM python:3.11.10-slim AS builder_base_csw
LABEL [email protected]
LABEL org.opencontainers.image.source=https://github.com/dbca-wa/csw

Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Catalogue service for spatial records over HTTP in the Department of
Biodiversity, Conservation and Attractions.

# Installation
## Installation

The recommended way to set up this project for development is using
[Poetry](https://python-poetry.org/docs/) to install and manage a virtual Python
Expand All @@ -23,15 +23,15 @@ Manage new or updating project dependencies with Poetry also, like so:

poetry add newpackage==1.0

# Environment variables
## Environment variables

This project uses environment variables (in a `.env` file) to define application settings.
Required settings are as follows:
This project uses environment variables (in a `.env` file) to define application
settings. Required settings are as follows:

DATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DATABASE_NAME"
SECRET_KEY="ThisIsASecretKey"

# Running
## Running

Use `runserver` to run a local copy of the application:

Expand All @@ -41,7 +41,7 @@ Run console commands manually:

python manage.py shell_plus

# Media uploads
## Media uploads

The production system stores media uploads in Azure blob storage.
Credentials for doing so should be defined in the following environment
Expand All @@ -55,7 +55,7 @@ To bypass this and use local media storage (for development, etc.), set
the `LOCAL_MEDIA_STORAGE=True` environment variable and create a writable
`media` directory in the project directory.

# Docker image
## Docker image

To build a new Docker image from the `Dockerfile`:

Expand Down
14 changes: 7 additions & 7 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ do not, please follow up via email to ensure we received your original message.
Please include the requested information listed below (as much as you can provide)
to help us better understand the nature and scope of the possible issue:

* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
* Full paths of source file(s) related to the manifestation of the issue
* The location of the affected source code (tag/branch/commit or direct URL)
* Any special configuration required to reproduce the issue
* Step-by-step instructions to reproduce the issue
* Proof-of-concept or exploit code (if possible)
* Impact of the issue, including how an attacker might exploit the issue
- Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
- Full paths of source file(s) related to the manifestation of the issue
- The location of the affected source code (tag/branch/commit or direct URL)
- Any special configuration required to reproduce the issue
- Step-by-step instructions to reproduce the issue
- Proof-of-concept or exploit code (if possible)
- Impact of the issue, including how an attacker might exploit the issue

This information will help us triage your report more quickly. Please note that
we prefer all communications to be in English.
Expand Down
9 changes: 4 additions & 5 deletions csw/middleware.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
from django.db import connections
from django.http import HttpResponse, HttpResponseServerError
import logging

from django.db import connections
from django.http import HttpResponse, HttpResponseServerError

LOGGER = logging.getLogger("django")


class HealthCheckMiddleware(object):

def __init__(self, get_response):
self.get_response = get_response

Expand All @@ -20,8 +19,7 @@ def __call__(self, request):
return self.get_response(request)

def liveness(self, request):
"""Returns that the server is alive.
"""
"""Returns that the server is alive."""
return HttpResponse("OK")

def readiness(self, request):
Expand All @@ -33,6 +31,7 @@ def readiness(self, request):
cursor = connections["default"].cursor()
cursor.execute("SELECT 1;")
row = cursor.fetchone()
cursor.close()
if row is None:
return HttpResponseServerError("Database: invalid response")
except Exception as e:
Expand Down
180 changes: 87 additions & 93 deletions csw/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,147 +13,141 @@
sys.path.insert(0, PROJECT_DIR)

# Settings defined in environment variables.
DEBUG = env('DEBUG', False)
SECRET_KEY = env('SECRET_KEY', 'PlaceholderSecretKey')
CSRF_COOKIE_SECURE = env('CSRF_COOKIE_SECURE', False)
CSRF_TRUSTED_ORIGINS = env('CSRF_TRUSTED_ORIGINS', 'http://127.0.0.1').split(',')
SESSION_COOKIE_SECURE = env('SESSION_COOKIE_SECURE', False)
SECURE_SSL_REDIRECT = env('SECURE_SSL_REDIRECT', False)
SECURE_REFERRER_POLICY = env('SECURE_REFERRER_POLICY', None)
SECURE_HSTS_SECONDS = env('SECURE_HSTS_SECONDS', 0)
DEBUG = env("DEBUG", False)
SECRET_KEY = env("SECRET_KEY", "PlaceholderSecretKey")
CSRF_COOKIE_SECURE = env("CSRF_COOKIE_SECURE", False)
CSRF_TRUSTED_ORIGINS = env("CSRF_TRUSTED_ORIGINS", "http://127.0.0.1").split(",")
SESSION_COOKIE_SECURE = env("SESSION_COOKIE_SECURE", False)
SECURE_SSL_REDIRECT = env("SECURE_SSL_REDIRECT", False)
SECURE_REFERRER_POLICY = env("SECURE_REFERRER_POLICY", None)
SECURE_HSTS_SECONDS = env("SECURE_HSTS_SECONDS", 0)
if not DEBUG:
ALLOWED_HOSTS = env('ALLOWED_HOSTS', 'localhost').split(',')
ALLOWED_HOSTS = env("ALLOWED_HOSTS", "localhost").split(",")
else:
ALLOWED_HOSTS = ['*']
INTERNAL_IPS = ['127.0.0.1', '::1']
ROOT_URLCONF = 'csw.urls'
WSGI_APPLICATION = 'csw.wsgi.application'
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
ALLOWED_HOSTS = ["*"]
INTERNAL_IPS = ["127.0.0.1", "::1"]
ROOT_URLCONF = "csw.urls"
WSGI_APPLICATION = "csw.wsgi.application"
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"

# Assume Azure blob storage is used for media uploads, unless explicitly set as local storage.
LOCAL_MEDIA_STORAGE = env('LOCAL_MEDIA_STORAGE', False)
LOCAL_MEDIA_STORAGE = env("LOCAL_MEDIA_STORAGE", False)
if LOCAL_MEDIA_STORAGE:
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
DEFAULT_FILE_STORAGE = "django.core.files.storage.FileSystemStorage"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
else:
DEFAULT_FILE_STORAGE = 'storages.backends.azure_storage.AzureStorage'
AZURE_ACCOUNT_NAME = env('AZURE_ACCOUNT_NAME', 'name')
AZURE_ACCOUNT_KEY = env('AZURE_ACCOUNT_KEY', 'key')
AZURE_CONTAINER = env('AZURE_CONTAINER', 'container')
AZURE_URL_EXPIRATION_SECS = env('AZURE_URL_EXPIRATION_SECS', 3600) # Default one hour.
DEFAULT_FILE_STORAGE = "storages.backends.azure_storage.AzureStorage"
AZURE_ACCOUNT_NAME = env("AZURE_ACCOUNT_NAME", "name")
AZURE_ACCOUNT_KEY = env("AZURE_ACCOUNT_KEY", "key")
AZURE_CONTAINER = env("AZURE_CONTAINER", "container")
AZURE_URL_EXPIRATION_SECS = env("AZURE_URL_EXPIRATION_SECS", 3600) # Default one hour.

BASE_URL = env('BASE_URL', 'https://csw.dbca.wa.gov.au')
BORG_URL = env('BORG_URL', 'https://borg.dbca.wa.gov.au')
CORS_URL = env('CORS_URL', 'https://sss.dbca.wa.gov.au')
BASE_URL = env("BASE_URL", "https://csw.dbca.wa.gov.au")
BORG_URL = env("BORG_URL", "https://borg.dbca.wa.gov.au")
CORS_URL = env("CORS_URL", "https://sss.dbca.wa.gov.au")

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django_extensions',
'reversion',
'django_filters',
'rest_framework',
'catalogue',
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django_extensions",
"reversion",
"django_filters",
"rest_framework",
"catalogue",
]

MIDDLEWARE = [
'csw.middleware.HealthCheckMiddleware',
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'reversion.middleware.RevisionMiddleware',
'dbca_utils.middleware.SSOLoginMiddleware',
"csw.middleware.HealthCheckMiddleware",
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"reversion.middleware.RevisionMiddleware",
"dbca_utils.middleware.SSOLoginMiddleware",
]

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'debug': DEBUG,
'context_processors': [
'django.contrib.auth.context_processors.auth',
'django.template.context_processors.debug',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
'django.template.context_processors.static',
'django.template.context_processors.tz',
'django.template.context_processors.request',
'django.template.context_processors.csrf',
'django.contrib.messages.context_processors.messages',
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {
"debug": DEBUG,
"context_processors": [
"django.contrib.auth.context_processors.auth",
"django.template.context_processors.debug",
"django.template.context_processors.i18n",
"django.template.context_processors.media",
"django.template.context_processors.static",
"django.template.context_processors.tz",
"django.template.context_processors.request",
"django.template.context_processors.csrf",
"django.contrib.messages.context_processors.messages",
],
},
},
]

LOGIN_URL = '/login/'
LOGIN_REDIRECT_URL = '/'
LOGIN_URL = "/login/"
LOGIN_REDIRECT_URL = "/"


# Database configuration
DATABASES = {
# Defined in DATABASE_URL env variable.
'default': dj_database_url.config(),
"default": dj_database_url.config(),
}


# Internationalization
USE_I18N = False
USE_TZ = True
TIME_ZONE = 'Australia/Perth'
LANGUAGE_CODE = 'en-us'
TIME_ZONE = "Australia/Perth"
LANGUAGE_CODE = "en-us"


# Static files configuration
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATIC_URL = '/static/'
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
STATIC_URL = "/static/"
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
WHITENOISE_ROOT = STATIC_ROOT

# Media uploads
MEDIA_URL = '/media/'
MEDIA_URL = "/media/"

# Logging settings - log to stdout/stderr
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'console': {'format': '%(asctime)s %(levelname)-8s %(name)-8s %(message)s'},
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"console": {"format": "%(asctime)s %(levelname)-8s %(name)-8s %(message)s"},
},
'handlers': {
'console': {
'level': 'INFO',
'class': 'logging.StreamHandler',
'formatter': 'console'
"handlers": {
"console": {"level": "INFO", "class": "logging.StreamHandler", "formatter": "console"},
"null": {
"class": "logging.NullHandler",
},
'null': {
'class': 'logging.NullHandler',
}
},
'loggers': {
'pycsw': {
'handlers': ['null'],
"loggers": {
"pycsw": {
"handlers": ["null"],
},
'django': {
'handlers': ['console'],
'level': 'INFO',
"django": {
"handlers": ["console"],
"level": "INFO",
},
}
},
}


REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': (
'django_filters.rest_framework.DjangoFilterBackend',
),
"DEFAULT_FILTER_BACKENDS": ("django_filters.rest_framework.DjangoFilterBackend",),
}
7 changes: 4 additions & 3 deletions csw/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
from django.contrib import admin
from django.urls import include, path
from django.views.generic import RedirectView

admin.autodiscover()

urlpatterns = [
path('admin/', admin.site.urls),
path('catalogue/', include('catalogue.urls')),
path('', RedirectView.as_view(url='/admin/')),
path("admin/", admin.site.urls),
path("catalogue/", include("catalogue.urls")),
path("", RedirectView.as_view(url="/admin/")),
]

# Serve media using Django (only works when DEBUG==True).
Expand Down
7 changes: 5 additions & 2 deletions csw/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@
WSGI config for csw project.
It exposes the WSGI callable as a module-level variable named ``application``.
"""
from django.core.wsgi import get_wsgi_application

import os
from pathlib import Path

from django.core.wsgi import get_wsgi_application

# These lines are required for interoperability between local and container environments.
d = Path(__file__).resolve().parents[1]
dot_env = os.path.join(str(d), '.env')
dot_env = os.path.join(str(d), ".env")
if os.path.exists(dot_env):
from dotenv import load_dotenv

load_dotenv()

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "csw.settings")
Expand Down
2 changes: 1 addition & 1 deletion kustomize/overlays/prod/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ patches:
- path: service_patch.yaml
images:
- name: ghcr.io/dbca-wa/csw
newTag: 1.3.12
newTag: 1.3.13
Loading

0 comments on commit 22f44d8

Please sign in to comment.