diff --git a/.github/actions/build_and_push/action.yml b/.github/actions/build_and_push/action.yml
new file mode 100644
index 00000000000..355ba5741a6
--- /dev/null
+++ b/.github/actions/build_and_push/action.yml
@@ -0,0 +1,85 @@
+name: Builds and pushes Docker image
+description: Action to build and push images to docker
+
+
+inputs:
+ dockerfile:
+ description: Path to the Dockerfile
+ default: ./Dockerfile
+ required: false
+ dockercontext:
+ description: Path to the Docker context
+ default: ./
+ required: false
+ # image coordinates
+ image:
+ description: Name of the image to build
+ required: true
+ tags:
+ description: Image Tag(s)
+ required: false
+ default: latest
+ # OCI metadata annotations
+ oci_title:
+ description: Image Title (OCI annotation)
+ required: true
+ oci_description:
+ description: Image Description (OCI annotation)
+ required: true
+ # registry credentials
+ registry_username:
+ description: The username for Docker hub sign-in
+ required: true
+ registry_password:
+ description: The password for Docker hub sign-in
+ required: true
+
+
+runs:
+ using: "composite"
+ steps:
+ -
+ name: Checkout
+ uses: actions/checkout@v3
+ -
+ uses: dorny/paths-filter@v2
+ id: changes
+ with:
+ filters: |
+ src:
+ - '${{ inputs.dockercontext }}/**'
+ # -
+ # name: Set up Docker Buildx
+ # uses: docker/setup-buildx-action@v2
+ # -
+ # name: Extract metadata (tags, labels) for Docker
+ # id: meta
+ # uses: docker/metadata-action@v4
+ # with:
+ # images: ${{ inputs.image }}
+ # labels: |
+ # "org.opencontainers.image.vendor=52°North GmbH"
+ # "org.opencontainers.image.authors=https://52North.org/"
+ # "org.opencontainers.image.source=https://github.com/52North/geonode"
+ # "org.opencontainers.image.description=${{ inputs.oci_description }}"
+ # "org.opencontainers.image.title=${{ inputs.oci_title }}"
+ # "org.opencontainers.image.licenses=GPL-3.0"
+ # tags: |
+ # ${{ inputs.tags }}
+ # -
+ # name: Login to Docker registry
+ # uses: docker/login-action@v2
+ # with:
+ # username: ${{ inputs.registry_username }}
+ # password: ${{ inputs.registry_password }}
+ # -
+ # name: Build and push
+ # uses: docker/build-push-action@v4
+ # with:
+ # context: .
+ # file: ${{ inputs.dockerfile }}
+ # push: true
+ # tags: ${{ steps.meta.outputs.tags }}
+ # labels: ${{ steps.meta.outputs.labels }}
+ # cache-from: type=registry,ref=${{ inputs.image }}:buildcache
+ # cache-to: type=registry,ref=${{ inputs.image }}:buildcache,mode=max
diff --git a/.github/workflows/52n-build-master.yml b/.github/workflows/52n-build-master.yml
new file mode 100644
index 00000000000..9c19abc1d4c
--- /dev/null
+++ b/.github/workflows/52n-build-master.yml
@@ -0,0 +1,71 @@
+name: "[52n-master -> latest] Builds GeoNode Docker Images"
+
+concurrency:
+ group: "geonode_build_master"
+ cancel-in-progress: true
+
+env:
+ TAG: latest
+
+on:
+ push:
+ branches:
+ - "52n-master"
+
+jobs:
+ build_and_push_geonode:
+ runs-on: ubuntu-22.04
+ steps:
+ -
+ name: Checkout
+ uses: actions/checkout@v3
+ -
+ name: build and push geonode
+ uses: ./.github/actions/build_and_push
+ with:
+ image: 52north/geonode
+ tags: ${{ env.TAG }}
+ oci_title: "52°North GeoNode image"
+ oci_description: "GeoNode built from 52n fork"
+ registry_username: ${{ secrets.DOCKERHUB_USERNAME }}
+ registry_password: ${{ secrets.DOCKERHUB_TOKEN_52N_MASTER }}
+
+ build_and_push_nginx:
+ runs-on: ubuntu-22.04
+ steps:
+ -
+ name: Checkout
+ uses: actions/checkout@v3
+ -
+ name: build and push nginx
+ uses: ./.github/actions/build_and_push
+ with:
+ dockerfile: ./scripts/docker/nginx/Dockerfile
+ dockercontext: ./scripts/docker/nginx/
+ image: 52north/geonode-nginx
+ tags: ${{ env.TAG }}
+ oci_title: "52°North Nginx image for GeoNode"
+ oci_description: "Nginx built for GeoNode from a 52n fork"
+ registry_username: ${{ secrets.DOCKERHUB_USERNAME }}
+ registry_password: ${{ secrets.DOCKERHUB_TOKEN_52N_MASTER }}
+
+ build_and_push_geoserver:
+ runs-on: ubuntu-22.04
+ env:
+ IMAGE: 52north/geonode-geoserver
+ steps:
+ -
+ name: Checkout
+ uses: actions/checkout@v3
+ -
+ name: build and push geoserver
+ uses: ./.github/actions/build_and_push
+ with:
+ dockerfile: ./scripts/docker/geoserver/Dockerfile
+ dockercontext: ./scripts/docker/geoserver/
+ image: 52north/geonode-geoserver
+ tags: ${{ env.TAG }}
+ oci_title: "52°North GeoServer image for GeoNode"
+ oci_description: "GeoServer built for GeoNode from a 52n fork"
+ registry_username: ${{ secrets.DOCKERHUB_USERNAME }}
+ registry_password: ${{ secrets.DOCKERHUB_TOKEN_52N_MASTER }}
diff --git a/.github/workflows/dockerhub-description.yml b/.github/workflows/dockerhub-description.yml
new file mode 100644
index 00000000000..3dbe8fbb583
--- /dev/null
+++ b/.github/workflows/dockerhub-description.yml
@@ -0,0 +1,23 @@
+name: Update Docker Hub Description
+on:
+ push:
+ branches:
+ - 52n-master
+ paths:
+ - README_52n.md
+ - .github/workflows/dockerhub-description.yml
+jobs:
+ dockerHubDescription:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Docker Hub Description
+ uses: peter-evans/dockerhub-description@v3
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN_52N_MASTER }}
+ repository: 52north/geonode
+ short-description: "Geospatial content management system"
+ readme-filepath: ./README_52n.md
+ enable-url-completion: true
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 7a455624878..60f95d0efc8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -96,4 +96,4 @@ scripts/spcgeonode/_volume_*
!hooks/*
.env
-
+.secret
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ee54d4a6d2e..e2ac7e72bd1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,63 @@
# Change Log
+## [4.1.2](https://github.com/GeoNode/geonode/tree/4.1.2) (2023-08-11)
+## Bug Fixes
+- Upgrade to importer 1.0.5 which fixes the import with filenames longer then 63 chars
+- Fixed parsing and rendering of ISO TC211 spatial representetion type
+## [4.1.1](https://github.com/GeoNode/geonode/tree/4.1.1) (2023-06-05)
+## Security and Bug Fixes
+- Upgrade to Ubuntu 22.10
+- Upgrade to Django 3.2.20
+- Fixed direct download URL not working in some cases when local files are not available
+- Fixed assignment of regions crossing the dateline
+- Fixed harvesting of ArcGIS REST ImageServer services
+- Fixed some italian translations
+- Disabling of the (deprecated) Monitoring module
+## [4.1.0](https://github.com/GeoNode/geonode/tree/4.1.0) (2023-06-05)
+### New upload engine
+GeoNode integrates a brand new importer module based on [GDAL/OGR](https://gdal.org/), which offers increased robustness and reliability to the upload UI and API services. GeoPackage (vector), GeoJSON, KML/KMZ formats and a new CSV handler have been implemented.
+
+### Thesaurus faceting and date filtering
+If thesaurus and thesaurus keywords are configured and assigned to resources, they will be available inside the filters panel, along with the number of associated resources.
+Date filtering (from/top) has also been added.
+
+### Time series configurable after the upload
+The configuration of (potential) time series at upload time was confusing for users, and not very robust.
+With the new importer, the optional configuration of vector time series can be done afterward, through the Settings tab inside the Metadata editing page
+Only vector fomats that provide date(time) fields natively are supported. Conversion from string fields is not implemented.
+
+### Related resources
+This restore a functionality available in previous versions of GeoNode.
+A tab inside the info panel has been added where relationships between datasets, maps and documents are reported.
+
+### Vector dataset attributes
+A tab inside the info panel has been added showing the attributes of vector datasets
+
+### Remote documents
+The API has been extended to permit the creation of document resources referencing remote URLs
+
+### ISO-19115 XML upload via API
+The API now supports the upload of a metadata XML file along with the resource data
+
+### Software upgrades
+
+ - [Geoserver 2.23.0](https://geoserver.org/announcements/2023/04/05/geoserver-2-23-0-released.html) is now the reference version. This version includes Geofence WPS rules which are employed by GeoNode to strengthen the security of the OGC/WPS processes.
+- [MapStore 2022.02.xx](https://github.com/geosolutions-it/MapStore2/tree/2022.02.xx)
+- [Django 3.2.19](https://docs.djangoproject.com/en/4.2/releases/3.2.19/)
+- PostgreSQL 13 and PostGIS 3.3.3
+
+
+## Security and Bug Fixes
+- [CVE-2023-26043](https://github.com/GeoNode/geonode/security/advisories/GHSA-mcmc-c59m-pqq8)
+Fixed a vulnerability to XML External Entity (XXE) injection
+- [CVE-2023-28442](https://github.com/GeoNode/geonode/security/advisories/GHSA-87mh-vw7c-5v6w)
+Fixed information leak
+
+You can see the **full list of closed issues [here](https://github.com/GeoNode/geonode/compare/4.1.0...4.0.3)**.
+
+## System requirements
+Python >3.9 is required to run GeoNode 4.1.0, since many of its dependencies have dropped support for older versions.
+
## [4.0.2](https://github.com/GeoNode/geonode/tree/4.0.2) (2022-12-20)
### Breaking Changes
diff --git a/README_52n.md b/README_52n.md
new file mode 100644
index 00000000000..3e92b9009b5
--- /dev/null
+++ b/README_52n.md
@@ -0,0 +1,25 @@
+# 52°North Fork of GeoNode
+
+This image is built from a fork of [Geonode](https://github.com/geonode/geonode).
+[52°North GmbH](https://52north.org) maintains an own fork of GeoNode in order to make necessary adjustments within projects which are not part of GeoNode core.
+
+However, we are interested to stay as close to upstream as possible, to benefit from ongoing development, but also to contribute features and fixes we develop in our projects.
+
+Starting from version `4` this image is built from the `52n-master` branch of the [`52north/geonode` repository](https://github.com/52North/geonode/tree/52n-master).
+The repository builds and publishes three images:
+
+* [`52north/geonode`](https://hub.docker.com/r/52north/geonode) (this image)
+* [`52north/geonode-nginx`](https://hub.docker.com/r/52north/geonode-nginx)
+* [`52north/geonode-geoserver`](https://hub.docker.com/r/52north/geonode-geoserver)
+
+The Dockerfiles can be found under the [`./scripts/docker` folder](https://github.com/52North/geonode/tree/52n-master/scripts/docker).
+
+The official Docker configuration of [GeoServer for GeoNode](https://github.com/GeoNode/geoserver-docker) seems to be outdated.
+Therefore, our fork adds a `./scripts/docker/geoserver` Docker config which is based on [the geonode-project](https://github.com/geonode/geonode-project) template.
+
+Depending on our current project contexts we merge regularly from upstream, and create new pull requests based on this fork.
+
+> **Note on version `3` tags**
+>
+> Images containing a `3.x` version tag were experimental and do have a different code base.
+> These image are considered to be removed in the near future.
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 39864fb3294..fe499acc592 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,7 +9,7 @@ amqp==5.1.1
beautifulsoup4==4.12.2
httplib2<0.22.1
hyperlink==21.0.0
-idna>=2.5,<3.5
+idna>=2.5,<3.7
urllib3==1.26.17
Paver==1.3.4
python-slugify==8.0.1
diff --git a/scripts/docker/geoserver/Dockerfile b/scripts/docker/geoserver/Dockerfile
new file mode 100644
index 00000000000..cf69384629f
--- /dev/null
+++ b/scripts/docker/geoserver/Dockerfile
@@ -0,0 +1,113 @@
+ARG IMAGE_VERSION=9.0-jdk11-openjdk-slim-bullseye
+ARG JAVA_HOME=/usr/local/openjdk-11
+FROM tomcat:$IMAGE_VERSION
+LABEL GeoNode Development Team
+
+ARG GEOSERVER_CORS_ENABLED=False
+ARG GEOSERVER_CORS_ALLOWED_ORIGINS=*
+ARG GEOSERVER_CORS_ALLOWED_METHODS=GET,POST,PUT,DELETE,HEAD,OPTIONS
+ARG GEOSERVER_CORS_ALLOWED_HEADERS=*
+#
+# Set GeoServer version and data directory
+#
+ENV GEOSERVER_VERSION=2.23.0
+ENV GEOSERVER_DATA_DIR="/geoserver_data/data"
+ENV GEOSERVER_CORS_ENABLED=$GEOSERVER_CORS_ENABLED
+ENV GEOSERVER_CORS_ALLOWED_ORIGINS=$GEOSERVER_CORS_ALLOWED_ORIGINS
+ENV GEOSERVER_CORS_ALLOWED_METHODS=$GEOSERVER_CORS_ALLOWED_METHODS
+ENV GEOSERVER_CORS_ALLOWED_HEADERS=$GEOSERVER_CORS_ALLOWED_HEADERS
+#
+# Download and install GeoServer
+#
+RUN apt-get update -y && apt-get install curl wget unzip -y
+RUN cd /usr/local/tomcat/webapps \
+ && wget --no-check-certificate --progress=bar:force:noscroll https://artifacts.geonode.org/geoserver/${GEOSERVER_VERSION}/geoserver.war -O geoserver.war \
+ && unzip -q geoserver.war -d geoserver \
+ && rm geoserver.war \
+ && mkdir -p $GEOSERVER_DATA_DIR
+
+VOLUME $GEOSERVER_DATA_DIR
+
+# added by simonelanucara https://github.com/simonelanucara
+# Optionally add JAI, ImageIO and Marlin Render for improved Geoserver performance
+WORKDIR /tmp
+
+RUN wget --no-check-certificate https://repo1.maven.org/maven2/org/postgis/postgis-jdbc/1.3.3/postgis-jdbc-1.3.3.jar -O postgis-jdbc-1.3.3.jar && \
+ wget --no-check-certificate https://maven.geo-solutions.it/org/hibernatespatial/hibernate-spatial-postgis/1.1.3.2/hibernate-spatial-postgis-1.1.3.2.jar -O hibernate-spatial-postgis-1.1.3.2.jar && \
+ rm /usr/local/tomcat/webapps/geoserver/WEB-INF/lib/hibernate-spatial-h2-geodb-1.1.3.2.jar && \
+ mv hibernate-spatial-postgis-1.1.3.2.jar /usr/local/tomcat/webapps/geoserver/WEB-INF/lib/ && \
+ mv postgis-jdbc-1.3.3.jar /usr/local/tomcat/webapps/geoserver/WEB-INF/lib/
+
+###########docker host###############
+# Set DOCKERHOST variable if DOCKER_HOST exists
+ARG DOCKERHOST=${DOCKERHOST}
+# for debugging
+RUN echo -n #1===>DOCKERHOST=${DOCKERHOST}
+#
+ENV DOCKERHOST ${DOCKERHOST}
+# for debugging
+RUN echo -n #2===>DOCKERHOST=${DOCKERHOST}
+
+###########docker host ip#############
+# Set GEONODE_HOST_IP address if it exists
+ARG GEONODE_HOST_IP=${GEONODE_HOST_IP}
+# for debugging
+RUN echo -n #1===>GEONODE_HOST_IP=${GEONODE_HOST_IP}
+#
+ENV GEONODE_HOST_IP ${GEONODE_HOST_IP}
+# for debugging
+RUN echo -n #2===>GEONODE_HOST_IP=${GEONODE_HOST_IP}
+# If empty set DOCKER_HOST_IP to GEONODE_HOST_IP
+ENV DOCKER_HOST_IP=${DOCKER_HOST_IP:-${GEONODE_HOST_IP}}
+# for debugging
+RUN echo -n #1===>DOCKER_HOST_IP=${DOCKER_HOST_IP}
+# Trying to set the value of DOCKER_HOST_IP from DOCKER_HOST
+RUN if ! [ -z ${DOCKER_HOST_IP} ]; \
+ then echo export DOCKER_HOST_IP=${DOCKERHOST} | \
+ sed 's/tcp:\/\/\([^:]*\).*/\1/' >> /root/.bashrc; \
+ else echo "DOCKER_HOST_IP is already set!"; fi
+# for debugging
+RUN echo -n #2===>DOCKER_HOST_IP=${DOCKER_HOST_IP}
+
+# Set WEBSERVER public port
+ARG PUBLIC_PORT=${PUBLIC_PORT}
+# for debugging
+RUN echo -n #1===>PUBLIC_PORT=${PUBLIC_PORT}
+#
+ENV PUBLIC_PORT=${PUBLIC_PORT}
+# for debugging
+RUN echo -n #2===>PUBLIC_PORT=${PUBLIC_PORT}
+
+# set nginx base url for geoserver
+RUN echo export NGINX_BASE_URL=http://${NGINX_HOST}:${NGINX_PORT}/ | \
+ sed 's/tcp:\/\/\([^:]*\).*/\1/' >> /root/.bashrc
+
+# copy the script and perform the run of scripts from entrypoint.sh
+RUN mkdir -p /usr/local/tomcat/tmp
+WORKDIR /usr/local/tomcat/tmp
+COPY set_geoserver_auth.sh /usr/local/tomcat/tmp
+COPY setup_auth.sh /usr/local/tomcat/tmp
+COPY requirements.txt /usr/local/tomcat/tmp
+COPY get_dockerhost_ip.py /usr/local/tomcat/tmp
+COPY get_nginxhost_ip.py /usr/local/tomcat/tmp
+COPY entrypoint.sh /usr/local/tomcat/tmp
+COPY ./templates /templates
+COPY multidump.sh /usr/local/tomcat/tmp
+COPY multidump-alt.sh /usr/local/tomcat/tmp
+
+RUN apt-get update \
+ && apt-get install -y procps less \
+ && apt-get install -y python3 python3-pip python3-dev \
+ && chmod +x /usr/local/tomcat/tmp/set_geoserver_auth.sh \
+ && chmod +x /usr/local/tomcat/tmp/setup_auth.sh \
+ && chmod +x /usr/local/tomcat/tmp/entrypoint.sh \
+ && pip3 install pip --upgrade \
+ && pip3 install -r requirements.txt \
+ && chmod +x /usr/local/tomcat/tmp/get_dockerhost_ip.py \
+ && chmod +x /usr/local/tomcat/tmp/get_nginxhost_ip.py
+
+RUN pip install j2cli
+
+ENV JAVA_OPTS="-Djava.awt.headless=true -Dgwc.context.suffix=gwc -XX:+UnlockDiagnosticVMOptions -XX:+LogVMOutput -XX:LogFile=/var/log/jvm.log -XX:MaxPermSize=512m -XX:PermSize=256m -Xms512m -Xmx2048m -XX:+UseConcMarkSweepGC -XX:ParallelGCThreads=4 -Dfile.encoding=UTF8 -Djavax.servlet.request.encoding=UTF-8 -Djavax.servlet.response.encoding=UTF-8 -Duser.timezone=GMT -Dorg.geotools.shapefile.datetime=false -DGS-SHAPEFILE-CHARSET=UTF-8 -DGEOSERVER_CSRF_DISABLED=true -DPRINT_BASE_URL=http://geoserver:8080/geoserver/pdf -Xbootclasspath/a:/usr/local/tomcat/webapps/geoserver/WEB-INF/lib/marlin-0.9.3.jar -Dsun.java2d.renderer=org.marlin.pisces.MarlinRenderingEngine"
+
+CMD ["/usr/local/tomcat/tmp/entrypoint.sh"]
\ No newline at end of file
diff --git a/scripts/docker/geoserver/README.md b/scripts/docker/geoserver/README.md
new file mode 100644
index 00000000000..9d00a465066
--- /dev/null
+++ b/scripts/docker/geoserver/README.md
@@ -0,0 +1,132 @@
+# geoserver-docker
+
+
+**The scripts/docker/geonode folder is a copy from geonode-project* to be able to build GeoServer image from this repository directly. In case of an update, just replace the whole folder.**
+
+
+[GeoServer](http://geoserver.org) is an open source server for sharing geospatial data.
+This is a docker image that eases setting up a GeoServer running specifically for [GeoNode](https://github.com/GeoNode/geoserver-geonode-ext) with an additional separated data directory.
+
+The image is based on the official Tomcat 9 image
+
+## Installation
+
+This image is available as a [trusted build on the docker hub](https://registry.hub.docker.com/r/geonode/geoserver/), and is the recommended method of installation.
+Simple pull the image from the docker hub.
+
+```bash
+$ docker pull geonode/geoserver
+```
+
+Alternatively you can build the image locally
+
+```bash
+$ git clone https://github.com/geonode/geoserver-docker.git
+$ cd geoserver-docker
+$ docker build -t "geonode/geoserver" .
+```
+
+## Quick start
+
+You can quick start the image using the command line
+
+```bash
+$ docker run --name "geoserver" -v /var/run/docker.sock:/var/run/docker.sock -d -p 8080:8080 geonode/geoserver
+```
+
+Point your browser to `http://localhost:8080/geoserver` and login using GeoServer's default username and password:
+
+* Username: admin
+* Password: geoserver
+
+## How to use different versions
+
+There are mainly two different versions of this image which are useful for running **GeoNode** with different authentication system types. These versions are released as specific tags for two authentication mechanisms:
+
+**Cookie based authn**:
+- [geonode/geoserver:2.9.x](https://hub.docker.com/r/geonode/geoserver/builds/bx7ydhghnlrfnsppduyva73/)
+
+**Oauth2 based authn**:
+- [geonode/geoserver:2.9.x-oauth2](https://hub.docker.com/r/geonode/geoserver/builds/bwca5rtexeoegzgroavftdr/)
+- [geonode/geoserver:2.10.x](https://hub.docker.com/r/geonode/geoserver/builds/bjohcnc29vm69acqjrvndxf/)
+- [geonode/geoserver:2.12.x](https://hub.docker.com/r/geonode/geoserver/builds/bh7pyw5atmkcljurwsnzbs7/)
+- [geonode/geoserver:2.13.x](https://hub.docker.com/r/geonode/geoserver/builds/btmjctbuvrjfnnrxrs4wyrs/)
+- [geonode/geoserver:2.14.x](https://hub.docker.com/r/geonode/geoserver/builds/bj53pi8he8uksz6ggvrs3wc/)
+
+You can declare what version to use along with the data directory tag which corresponds to the same version.
+
+## Configuration
+
+### Data volume
+
+This GeoServer container keeps its configuration data at `/geoserver_data/data` which is exposed as volume in the dockerfile.
+The volume allows for stopping and starting new containers from the same image without losing all the data and custom configuration.
+
+You may want to map this volume to a directory on the host. It will also ease the upgrade process in the future. Volumes can be mounted by passing the `-v` flag to the docker run command:
+
+```bash
+-v /your/host/data/path:/geoserver_data/data
+```
+
+### Data volume container
+
+In case you are running Compose for automatically having GeoServer up and running then a data volume container will be mounted with a default preloaded *GEOSERVER_DATA_DIR* at the configuration data directory of the container.
+Make sure that the image from the repository [data-docker](https://github.com/GeoNode/data-docker) is available from the [GeoNode Docker Hub](https://hub.docker.com/u/geonode/) or has been built locally:
+
+```bash
+docker build -t geonode/geoserver_data .
+```
+
+#### Persistance behavior
+
+If you run:
+
+```bash
+docker-compose stop
+```
+
+Data are retained in the *GEOSERVER_DATA_DIR* and can then be mounted in a new GeoServer instance by running again:
+
+```bash
+docker-compose up
+```
+
+If you run:
+
+```bash
+docker-compose down
+```
+
+Data are completely gone but you can ever start from the base GeoServer Data Directory built for Geonode.
+
+#### Data directory versions
+
+There has to be a correspondence one-to-one between the data directory version and the tag of the GeoServer image used in the Docker compose file. So at the end you can consume these images below:
+
+* **2.9.x**: [geonode/geoserver_data:2.9.x](https://hub.docker.com/r/geonode/geoserver_data/builds/bsus6alnddg4bc7icwymevp/)
+* **2.9.x-oauth2**: [geonode/geoserver_data:2.9.x-oauth2](https://hub.docker.com/r/geonode/geoserver_data/builds/bwkxcupsunvuitzusi9gsnt/)
+* **2.10.x**: [geonode/geoserver_data:2.10.x](https://hub.docker.com/r/geonode/geoserver_data/builds/b5jqhpzapkqxzyevjizccug/)
+* **2.12.x**: [geonode/geoserver_data:2.12.x](https://hub.docker.com/r/geonode/geoserver_data/builds/byaaalw3lnasunpveyg3x4i/)
+* **2.13.x**: [geonode/geoserver_data:2.13.x](https://hub.docker.com/r/geonode/geoserver_data/builds/bunuqzq7a7dk65iumjhkbtc/)
+* **2.14.x**: [geonode/geoserver_data:2.14.x](https://hub.docker.com/r/geonode/geoserver_data/builds/blpdjzkrv7pm3stunzpn4pp/)
+
+### Database
+
+GeoServer recommends the usage of a spatial database
+
+#### PostGIS container (PostgreSQL + GIS Extension)
+
+If you want to use a [PostGIS](http://postgis.org/) container, you can link it to this image. You're free to use any PostGIS container.
+An example with [kartooza/postgis](https://registry.hub.docker.com/u/kartoza/postgis/) image:
+
+```bash
+$ docker run -d --name="postgis" kartoza/postgis
+```
+
+For further information see [kartooza/postgis](https://registry.hub.docker.com/u/kartoza/postgis/).
+
+Now start the GeoServer instance by adding the `--link` option to the docker run command:
+
+```bash
+--link postgis:postgis
+```
diff --git a/scripts/docker/geoserver/docker-compose.yml b/scripts/docker/geoserver/docker-compose.yml
new file mode 100644
index 00000000000..5f3dc1cf34e
--- /dev/null
+++ b/scripts/docker/geoserver/docker-compose.yml
@@ -0,0 +1,61 @@
+version: '3.9'
+
+services:
+
+ postgis:
+ image: geonode/postgis:13
+ ports:
+ - "25432:5432"
+ volumes:
+ - /srv/docker/geoserver/postgis:/var/lib/postgresql
+ #volumes_from:
+ #- pgstore
+ healthcheck:
+ test: "pg_isready -d postgres -U postgres"
+ restart: on-failure
+
+ geoserver:
+ image: geonode/geoserver:2.23.0
+ build:
+ context: .
+ args:
+ - DOCKERHOST
+ - GEONODE_HOST_IP
+ - PUBLIC_PORT=80
+ links:
+ - postgis
+ ports:
+ - "8080:8080"
+ volumes:
+ - /geoserver_data/data
+ environment:
+ - DOCKERHOST
+ - GEONODE_HOST_IP
+ - PUBLIC_PORT=80
+ - DOCKER_HOST_IP
+ - DJANGO_URL=http://localhost/
+ depends_on:
+ postgis:
+ condition: service_completed_successfully
+ data-dir-conf:
+ condition: service_healthy
+ healthcheck:
+ test: curl --fail -s http://localhost:8080/geoserver/rest/workspaces/geonode.html || exit 1
+ interval: 1m30s
+ timeout: 10s
+ retries: 3
+ restart: on-failure
+
+ data-dir-conf:
+ image: geonode/geoserver_data:2.23.0
+ container_name: geoserver_data_dir # named data container
+ entrypoint: sleep infinity
+ volumes:
+ - /geoserver_data/data
+ healthcheck:
+ test: "ls -A '/geoserver_data/data' | wc -l"
+ restart: on-failure
+
+volumes:
+ # reference to the named data container that holds the preloaded geoserver data directory
+ geoserver_data_dir:
\ No newline at end of file
diff --git a/scripts/docker/geoserver/entrypoint.sh b/scripts/docker/geoserver/entrypoint.sh
new file mode 100644
index 00000000000..f9af5f42024
--- /dev/null
+++ b/scripts/docker/geoserver/entrypoint.sh
@@ -0,0 +1,152 @@
+#!/bin/bash
+set -e
+
+source /root/.bashrc
+
+# control the value of DOCKER_HOST_IP variable
+if [ -z ${DOCKER_HOST_IP} ]
+then
+
+ echo "DOCKER_HOST_IP is empty so I'll run the python utility \n"
+ echo export DOCKER_HOST_IP=`python3 /usr/local/tomcat/tmp/get_dockerhost_ip.py` >> /root/.override_env
+ echo "The calculated value is now DOCKER_HOST_IP='$DOCKER_HOST_IP' \n"
+
+else
+
+ echo "DOCKER_HOST_IP is filled so I'll leave the found value '$DOCKER_HOST_IP' \n"
+
+fi
+
+# control the values of LB settings if present
+if [ ${GEONODE_LB_HOST_IP} ]
+then
+
+ echo "GEONODE_LB_HOST_IP is filled so I replace the value of '$DOCKER_HOST_IP' with '$GEONODE_LB_HOST_IP' \n"
+ echo export DOCKER_HOST_IP=${GEONODE_LB_HOST_IP} >> /root/.override_env
+
+fi
+
+if [ ${GEONODE_LB_PORT} ]
+then
+
+ echo "GEONODE_LB_PORT is filled so I replace the value of '$PUBLIC_PORT' with '$GEONODE_LB_PORT' \n"
+ echo export PUBLIC_PORT=${GEONODE_LB_PORT} >> /root/.override_env
+
+fi
+
+if [ ! -z "${GEOSERVER_JAVA_OPTS}" ]
+then
+
+ echo "GEOSERVER_JAVA_OPTS is filled so I replace the value of '$JAVA_OPTS' with '$GEOSERVER_JAVA_OPTS' \n"
+ JAVA_OPTS=${GEOSERVER_JAVA_OPTS}
+
+fi
+
+# control the value of NGINX_BASE_URL variable
+if [ -z `echo ${NGINX_BASE_URL} | sed 's/http:\/\/\([^:]*\).*/\1/'` ]
+then
+ echo "NGINX_BASE_URL is empty so I'll use the static nginx hostname \n"
+ # echo export NGINX_BASE_URL=`python3 /usr/local/tomcat/tmp/get_nginxhost_ip.py` >> /root/.override_env
+ # TODO rework get_nginxhost_ip to get URL with static hostname from nginx service name
+ # + exposed port of that container i.e. http://geonode:80
+ echo export NGINX_BASE_URL=http://geonode:80 >> /root/.override_env
+ echo "The calculated value is now NGINX_BASE_URL='$NGINX_BASE_URL' \n"
+else
+ echo "NGINX_BASE_URL is filled so I'll leave the found value '$NGINX_BASE_URL' \n"
+fi
+
+# set basic tagname
+TAGNAME=( "baseUrl" )
+
+if ! [ -f ${GEOSERVER_DATA_DIR}/security/auth/geonodeAuthProvider/config.xml ]
+then
+
+ echo "Configuration file '$GEOSERVER_DATA_DIR'/security/auth/geonodeAuthProvider/config.xml is not available so it is gone to skip \n"
+
+else
+
+ # backup geonodeAuthProvider config.xml
+ cp ${GEOSERVER_DATA_DIR}/security/auth/geonodeAuthProvider/config.xml ${GEOSERVER_DATA_DIR}/security/auth/geonodeAuthProvider/config.xml.orig
+ # run the setting script for geonodeAuthProvider
+ /usr/local/tomcat/tmp/set_geoserver_auth.sh ${GEOSERVER_DATA_DIR}/security/auth/geonodeAuthProvider/config.xml ${GEOSERVER_DATA_DIR}/security/auth/geonodeAuthProvider/ ${TAGNAME} > /dev/null 2>&1
+
+fi
+
+# backup geonode REST role service config.xml
+cp "${GEOSERVER_DATA_DIR}/security/role/geonode REST role service/config.xml" "${GEOSERVER_DATA_DIR}/security/role/geonode REST role service/config.xml.orig"
+# run the setting script for geonode REST role service
+/usr/local/tomcat/tmp/set_geoserver_auth.sh "${GEOSERVER_DATA_DIR}/security/role/geonode REST role service/config.xml" "${GEOSERVER_DATA_DIR}/security/role/geonode REST role service/" ${TAGNAME} > /dev/null 2>&1
+
+# set oauth2 filter tagname
+TAGNAME=( "accessTokenUri" "userAuthorizationUri" "redirectUri" "checkTokenEndpointUrl" "logoutUri" )
+
+# backup geonode-oauth2 config.xml
+cp ${GEOSERVER_DATA_DIR}/security/filter/geonode-oauth2/config.xml ${GEOSERVER_DATA_DIR}/security/filter/geonode-oauth2/config.xml.orig
+# run the setting script for geonode-oauth2
+/usr/local/tomcat/tmp/set_geoserver_auth.sh ${GEOSERVER_DATA_DIR}/security/filter/geonode-oauth2/config.xml ${GEOSERVER_DATA_DIR}/security/filter/geonode-oauth2/ "${TAGNAME[@]}" > /dev/null 2>&1
+
+# set global tagname
+TAGNAME=( "proxyBaseUrl" )
+
+# backup global.xml
+cp ${GEOSERVER_DATA_DIR}/global.xml ${GEOSERVER_DATA_DIR}/global.xml.orig
+# run the setting script for global configuration
+/usr/local/tomcat/tmp/set_geoserver_auth.sh ${GEOSERVER_DATA_DIR}/global.xml ${GEOSERVER_DATA_DIR}/ ${TAGNAME} > /dev/null 2>&1
+
+# set correct amqp broker url
+sed -i -e 's/localhost/rabbitmq/g' ${GEOSERVER_DATA_DIR}/notifier/notifier.xml
+
+# exclude wrong dependencies
+sed -i -e 's/xom-\*\.jar/xom-\*\.jar,bcprov\*\.jar/g' /usr/local/tomcat/conf/catalina.properties
+
+# J2 templating for this docker image we should also do it for other configuration files in /usr/local/tomcat/tmp
+
+declare -a geoserver_datadir_template_dirs=("geofence")
+
+for template in in ${geoserver_datadir_template_dirs[*]}; do
+ #Geofence templates
+ if [ "$template" == "geofence" ]; then
+ cp -R /templates/$template/* ${GEOSERVER_DATA_DIR}/geofence
+
+ for f in $(find ${GEOSERVER_DATA_DIR}/geofence/ -type f -name "*.j2"); do
+ echo -e "Evaluating template\n\tSource: $f\n\tDest: ${f%.j2}"
+ /usr/local/bin/j2 $f > ${f%.j2}
+ rm -f $f
+ done
+
+ fi
+done
+
+# configure CORS (inspired by https://github.com/oscarfonts/docker-geoserver)
+# if enabled, this will add the filter definitions
+# to the end of the web.xml
+# (this will only happen if our filter has not yet been added before)
+if [ "${GEOSERVER_CORS_ENABLED}" = "true" ] || [ "${GEOSERVER_CORS_ENABLED}" = "True" ]; then
+ if ! grep -q DockerGeoServerCorsFilter "$CATALINA_HOME/webapps/geoserver/WEB-INF/web.xml"; then
+ echo "Enable CORS for $CATALINA_HOME/webapps/geoserver/WEB-INF/web.xml"
+ sed -i "\::i\\
+ \n\
+ DockerGeoServerCorsFilter\n\
+ org.apache.catalina.filters.CorsFilter\n\
+ \n\
+ cors.allowed.origins\n\
+ ${GEOSERVER_CORS_ALLOWED_ORIGINS}\n\
+ \n\
+ \n\
+ cors.allowed.methods\n\
+ ${GEOSERVER_CORS_ALLOWED_METHODS}\n\
+ \n\
+ \n\
+ cors.allowed.headers\n\
+ ${GEOSERVER_CORS_ALLOWED_HEADERS}\n\
+ \n\
+ \n\
+ \n\
+ DockerGeoServerCorsFilter\n\
+ /*\n\
+ " "$CATALINA_HOME/webapps/geoserver/WEB-INF/web.xml";
+ fi
+fi
+
+# start tomcat
+exec env JAVA_OPTS="${JAVA_OPTS}" catalina.sh run
\ No newline at end of file
diff --git a/scripts/docker/geoserver/get_dockerhost_ip.py b/scripts/docker/geoserver/get_dockerhost_ip.py
new file mode 100644
index 00000000000..7b5a42ed310
--- /dev/null
+++ b/scripts/docker/geoserver/get_dockerhost_ip.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python3
+
+import logging
+
+import docker
+
+BOOTSTRAP_IMAGE_CHEIP = 'codenvy/che-ip:nightly'
+# AF: why call before definition? print _docker_host_ip()
+
+def _docker_host_ip():
+ client = docker.from_env()
+ ip_list = client.containers.run(BOOTSTRAP_IMAGE_CHEIP,
+ network_mode='host'
+ ).split("\n")
+ if len(ip_list) > 1:
+ logging.info("Docker daemon is running on more than one \
+address {0}".format(ip_list))
+ logging.info("Only the first address:{0} will be returned!".format(
+ ip_list[0]
+ ))
+ else:
+ logging.info("Docker daemon is running at the following \
+address {0}".format(ip_list[0]))
+ return ip_list[0]
diff --git a/scripts/docker/geoserver/get_nginxhost_ip.py b/scripts/docker/geoserver/get_nginxhost_ip.py
new file mode 100644
index 00000000000..c6a67d8490d
--- /dev/null
+++ b/scripts/docker/geoserver/get_nginxhost_ip.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+
+import logging
+import os
+
+import docker
+
+client = docker.from_env()
+# print(client.info())
+# TODO avoid this script can fail and fall in the loop where the geoserver
+# service is not available and consequently the nginx too which has geoserver
+# as a reference link
+for network in client.networks.list():
+ if 'geonode' in network.name:
+ geonode_network = network.name
+ else:
+ geonode_network = 'geonode_default'
+
+try:
+ containers = {
+ c.attrs['Config']['Image']: c.attrs['NetworkSettings']['\
+Networks'][geonode_network]['\
+IPAddress'] for c in client.containers.list() if c.status in 'running'
+ }
+ for item in containers.items():
+ if "geonode/nginx" in item[0]:
+ ipaddr = item[1]
+
+ try:
+ os.environ["NGINX_BASE_URL"] = "http://" + ipaddr + ":" + "80"
+ nginx_base_url = "http://{}:80".format(ipaddr)
+ except NameError as er:
+ logging.info("NGINX container is not running maybe exited! Running\
+containers are:{0}".format(containers))
+except KeyError as ke:
+ logging.info("There has been a problem with the docker\
+network which has raised the following exception: {0}".format(ke))
+else:
+ # nginx_base_url = None
+ pass
+finally:
+ try:
+ print(nginx_base_url)
+ except NameError as ne:
+ print("http://geonode:80")
diff --git a/scripts/docker/geoserver/multidump-alt.sh b/scripts/docker/geoserver/multidump-alt.sh
new file mode 100644
index 00000000000..cc237e17bec
--- /dev/null
+++ b/scripts/docker/geoserver/multidump-alt.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+if [ $# -ne 3 ]; then
+ echo "Usage: $0 pid interval count"
+ exit 1
+fi
+
+PID=$1
+INTERVAL=$2
+COUNT=$3
+
+top -bH -d $INTERVAL -n $COUNT -p $PID >> top.out 2>&1 &
+for i in `seq $COUNT`; do
+ kill -3 $PID
+ sleep $INTERVAL
+done
diff --git a/scripts/docker/geoserver/multidump.sh b/scripts/docker/geoserver/multidump.sh
new file mode 100644
index 00000000000..21dfd2ba660
--- /dev/null
+++ b/scripts/docker/geoserver/multidump.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+if [ $# -ne 3 ]; then
+ echo "Usage: $0 pid interval count"
+ exit 1
+fi
+
+PID=$1
+INTERVAL=$2
+COUNT=$3
+
+top -bH -d $INTERVAL -n $COUNT -p $PID >> top.out 2>&1 &
+for i in `seq $COUNT`; do
+ echo "stack trace $i of $COUNT" >> jstack.out
+ jstack -l $PID >> jstack.out
+ echo "--------------------" >> jstack.out
+ sleep $INTERVAL
+done
diff --git a/scripts/docker/geoserver/requirements.txt b/scripts/docker/geoserver/requirements.txt
new file mode 100644
index 00000000000..0b31242fdae
--- /dev/null
+++ b/scripts/docker/geoserver/requirements.txt
@@ -0,0 +1 @@
+docker==3.1.1
diff --git a/scripts/docker/geoserver/set_geoserver_auth.sh b/scripts/docker/geoserver/set_geoserver_auth.sh
new file mode 100644
index 00000000000..27dd11ef54e
--- /dev/null
+++ b/scripts/docker/geoserver/set_geoserver_auth.sh
@@ -0,0 +1,91 @@
+#!/bin/bash
+
+auth_conf_source="$1"
+auth_conf_target="$2"
+# Creating a temporary file for sed to write the changes to
+temp_file="xml.tmp"
+touch $temp_file
+
+source /root/.bashrc
+source /root/.override_env
+
+test -z "$auth_conf_source" && echo "You must specify a source file" && exit 1
+test -z "$auth_conf_target" && echo "You must specify a target conf directory" && exit 1
+
+test ! -f "$auth_conf_source" && echo "Source $auth_conf_source does not exist or is not a file" && exit 1
+test ! -d "$auth_conf_target" && echo "Target directory $auth_conf_target does not exist or is not a directory" && exit 1
+
+# for debugging
+echo -e "NGINX_BASE_URL=${NGINX_BASE_URL}\n"
+if [ "$PUBLIC_PORT" == "443" ]; then
+ SUBSTITUTION_URL="https://${DOCKER_HOST_IP}"
+ if [ "$PUBLIC_PORT" != "443" ]; then
+ SUBSTITUTION_URL="https://${DOCKER_HOST_IP}:${PUBLIC_PORT}"
+ fi
+else
+ SUBSTITUTION_URL="http://${DOCKER_HOST_IP}"
+ if [ "$PUBLIC_PORT" != "80" ]; then
+ SUBSTITUTION_URL="http://${DOCKER_HOST_IP}:${PUBLIC_PORT}"
+ fi
+fi
+
+echo -e "SUBSTITUTION_URL=$SUBSTITUTION_URL\n"
+echo -e "auth_conf_source=$auth_conf_source\n"
+echo -e "auth_conf_target=$auth_conf_target\n"
+
+# Elegance is the key -> adding an empty last line for Mr. “sed” to pick up
+echo " " >> "$auth_conf_source"
+
+cat "$auth_conf_source"
+
+tagname=( ${@:3:5} )
+
+# for debugging
+for i in "${tagname[@]}"
+do
+ echo "tagname=<$i>"
+done
+
+echo "DEBUG: Starting... [Ok]\n"
+
+for i in "${tagname[@]}"
+do
+ echo "DEBUG: Working on '$auth_conf_source' for tagname <$i>"
+ # Extracting the value from the <$tagname> element
+ # echo -ne "<$i>$tagvalue$i>" | xmlstarlet sel -t -m "//a" -v . -n
+ tagvalue=`grep "<$i>.*<.$i>" "$auth_conf_source" | sed -e "s/^.*<$i/<$i/" | cut -f2 -d">"| cut -f1 -d"<"`
+
+ echo "DEBUG: Found the current value for the element <$i> - '$tagvalue'"
+
+ # Setting new substituted value
+ case $i in
+ proxyBaseUrl )
+ if [ ${GEONODE_LB_HOST_IP} ]
+ then
+ echo "DEBUG: Editing '$auth_conf_source' for tagname <$i> and replacing its value with '$SUBSTITUTION_URL'"
+ newvalue=`echo -ne "$tagvalue" | sed -re "s@http://localhost(:8.*0)@$SUBSTITUTION_URL@"`
+ else
+ echo "DEBUG: Editing '$auth_conf_source' for tagname <$i> and replacing its value with '$NGINX_BASE_URL'"
+ newvalue=`echo -ne "$tagvalue" | sed -re "s@http://localhost(:8.*0)@$NGINX_BASE_URL@"`
+ fi;;
+ accessTokenUri | checkTokenEndpointUrl | baseUrl )
+ echo "DEBUG: Editing '$auth_conf_source' for tagname <$i> and replacing its value with '$NGINX_BASE_URL'"
+ newvalue=`echo -ne "$tagvalue" | sed -re "s@http://localhost(:8.*0)@$NGINX_BASE_URL@"`;;
+ userAuthorizationUri | redirectUri | logoutUri )
+ echo "DEBUG: Editing '$auth_conf_source' for tagname <$i> and replacing its value with '$SUBSTITUTION_URL'"
+ newvalue=`echo -ne "$tagvalue" | sed -re "s@http://localhost(:8.*0)@$SUBSTITUTION_URL@"`;;
+ *) echo -n "an unknown variable has been found";;
+ esac
+
+ echo "DEBUG: Found the new value for the element <$i> - '$newvalue'"
+ # Replacing element’s value with $SUBSTITUTION_URL
+ # echo -ne "<$i>$tagvalue$i>" | xmlstarlet sel -t -m "//a" -v . -n
+ sed -e "s@<$i>$tagvalue<\/$i>@<$i>$newvalue<\/$i>@g" "$auth_conf_source" > "$temp_file"
+ cp "$temp_file" "$auth_conf_source"
+done
+# Writing our changes back to the original file ($auth_conf_source)
+# no longer needed
+# mv $temp_file $auth_conf_source
+
+echo "DEBUG: Finished... [Ok] --- Final xml file is \n"
+cat "$auth_conf_source"
diff --git a/scripts/docker/geoserver/setup_auth.sh b/scripts/docker/geoserver/setup_auth.sh
new file mode 100644
index 00000000000..6f9373b978c
--- /dev/null
+++ b/scripts/docker/geoserver/setup_auth.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+sed -i.bak 's@\([^<][^<]*\)@'"$DJANGO_URL"'@'\
+ /geoserver_data/data/security/auth/geonodeAuthProvider/config.xml
\ No newline at end of file
diff --git a/scripts/docker/geoserver/templates/geofence/geofence-datasource-ovr.properties.j2 b/scripts/docker/geoserver/templates/geofence/geofence-datasource-ovr.properties.j2
new file mode 100644
index 00000000000..7b18d3e55f3
--- /dev/null
+++ b/scripts/docker/geoserver/templates/geofence/geofence-datasource-ovr.properties.j2
@@ -0,0 +1,12 @@
+geofenceVendorAdapter.databasePlatform=org.hibernatespatial.postgis.PostgisDialect
+geofenceDataSource.driverClassName=org.postgresql.Driver
+geofenceDataSource.url=jdbc:postgresql://{{ DATABASE_HOST }}:{{ DATABASE_PORT }}/{{ GEONODE_GEODATABASE }}
+geofenceDataSource.username={{ GEONODE_GEODATABASE }}
+geofenceDataSource.password={{ GEONODE_GEODATABASE_PASSWORD }}
+geofenceEntityManagerFactory.jpaPropertyMap[hibernate.default_schema]={{ GEONODE_GEODATABASE_SCHEMA }}
+
+# avoid hibernate transaction issues
+geofenceDataSource.testOnBorrow=true
+geofenceDataSource.validationQuery=SELECT 1
+geofenceEntityManagerFactory.jpaPropertyMap[hibernate.testOnBorrow]=true
+geofenceEntityManagerFactory.jpaPropertyMap[hibernate.validationQuery]=SELECT 1
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 498d391199e..86f10350f9e 100644
--- a/setup.py
+++ b/setup.py
@@ -28,6 +28,7 @@
setup(
version=__import__("geonode").get_version(),
long_description=open("README.md").read(),
+ long_description_content_type='text/markdown',
package_data={
"": ["*.*"], # noqa
"": ["static/*.*"], # noqa
@@ -35,4 +36,8 @@
"": ["templates/*.*"], # noqa
"templates": ["*.*"],
},
+ exclude_package_data={
+ "": ["uploaded/*.*"], # noqa
+ "uploaded": ["*.*"],
+ }
)