Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
d218232
working on adding tenants
msouff Oct 31, 2025
4d3301a
working on adding tenants
msouff Oct 31, 2025
b647cff
simplified tethys_tenant base structure
msouff Nov 3, 2025
8336a6c
harverster or middleware won't work for tenant-aware apps
romer8 Oct 28, 2024
5eee441
more simplifications to the core tethys_tenants
msouff Nov 7, 2025
03f8244
fixing broken tests
msouff Nov 7, 2025
0fe616c
revert test_harvester.py
msouff Nov 7, 2025
8267196
tests
msouff Nov 10, 2025
b05d84a
context_processor.request is already in there
msouff Nov 11, 2025
3944006
removed tenants postgis backend. Not needed
msouff Nov 11, 2025
2b97459
tests
msouff Nov 12, 2025
baee361
linting
msouff Nov 12, 2025
ccc1f67
apply tenant migration only if has_module django tenants; more test u…
msouff Nov 13, 2025
0d62232
moved tethys_tenants app to django tenants settings
msouff Nov 13, 2025
a479437
working on documentation
msouff Nov 14, 2025
fb02b92
documentation
msouff Nov 18, 2025
a3e1b9a
fixed tests; more documentation
msouff Nov 19, 2025
54e87d6
Updated migration to only be detected if django_tenants is installed
jakeymac Dec 18, 2025
7ab9d65
Updated whats new page in docs
jakeymac Dec 18, 2025
36bc74d
Added warning in settings to make sure users are using django_tenants…
jakeymac Dec 18, 2025
0275ad3
Updated order of sections in production doc page
jakeymac Dec 18, 2025
0d372f6
Updates to documentation
jakeymac Dec 18, 2025
176258a
Fixed documentation
jakeymac Dec 18, 2025
88f2d29
Merge remote-tracking branch 'upstream/main' into tethys_tenants
jakeymac Dec 18, 2025
f788438
Updated tethys installation script to use django_tenants postgresql b…
jakeymac Dec 18, 2025
c88c451
Formatting fixes
jakeymac Dec 19, 2025
ee310b0
Fixed tests
jakeymac Dec 19, 2025
32718be
Updated configurations to display warnings to user concening tethys t…
jakeymac Dec 26, 2025
8c3c5c5
Formatting
jakeymac Dec 26, 2025
32a1eee
Merge branch 'main' into tethys_tenants
jakeymac Dec 26, 2025
a71e231
Updated documentation for tenants configurations
jakeymac Dec 26, 2025
91f6c7c
flake fix
jakeymac Dec 27, 2025
09af76a
Converted tests to pytest
jakeymac Dec 27, 2025
1c97e0c
formatting fixes
jakeymac Dec 27, 2025
21ac0b6
Updated default portal config in installation script to enable tenants
jakeymac Dec 27, 2025
ef3ddf1
Fixed config in install script
jakeymac Dec 27, 2025
2cca9e7
One more portal_config fix in install script
jakeymac Dec 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion docs/installation/production.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Production Installation Guide
*****************************

**Last Updated:** September 2024
**Last Updated:** December 2025

A **production installation**, sometimes called **deployment**, is an installation of Tethys Platform that is configured to for being hosted on a live server. This guide provides an explanation of the difference between Production and Development installations and provides several methods for installing Tethys Platform in production.

Expand Down Expand Up @@ -51,6 +51,22 @@ This method involves using Docker to package and automate the deployment of a Te

production/docker

Advanced and Optional Capabilities
==================================

The following sections provide information on configuring advanced and optional capabilities for your production Tethys Platform installation.

* :doc:`Configure HTTPS <production/manual/configuration/advanced/https_config>`: For setting up a secured connection for your portal.
* :doc:`Customize Portal Theme <production/manual/configuration/advanced/customize>`: Customize the Tethys Portal to reflect your organization's branding.
* :doc:`Django Channels Layer <production/manual/configuration/advanced/django_channels_layer>`: For production installations that use the WebSockets and/or Bokeh Server functionality that comes with Tethys Platform.
* :doc:`Forgotten Password Recovery <production/manual/configuration/advanced/email_config>`: Set up email capabilities to allow users to recover forgotten passwords.
* :doc:`Lockout <production/manual/configuration/advanced/lockout>`: Prevent brute-force login attempts
* :doc:`Multi Factor Authentication <production/manual/configuration/advanced/multi_factor_auth>`: Enable and enforce multi-factor authentication through apps such as LastPass Authenticator or Google Authenticator.
* :doc:`Multi Tenancy <production/manual/configuration/advanced/multi_tenancy>`: Enable multiple tenants with a single portal deployment, and customize resources based on tenant.
* :doc:`Self Hosted Dependency Mode <production/manual/configuration/advanced/self_hosted_js_deps>`: Configure Tethys Portal to host JavaScript dependencies locally.
* :doc:`Single Sign On <production/manual/configuration/advanced/social_auth>`: Set up social authentication and single sign-on with providers including Google, Facebook, or LinkedIn.
* :doc:`Web Analytics <production/manual/configuration/advanced/webanalytics>`: Track how users interact with the Tethys portal and its applications using web-based analytical services.

References
==========

Expand Down
9 changes: 9 additions & 0 deletions docs/installation/production/manual/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,25 @@ Advanced Configuration

These guides describe additional configuration that you can perform to add more capabilities to your Tethys Portal.

**Recommended Configuration**

.. toctree::
:maxdepth: 1

configuration/advanced/https_config
configuration/advanced/cookie_consent
configuration/advanced/customize

**Optional Configuration**

.. toctree::
:maxdepth: 1

configuration/advanced/email_config
configuration/advanced/lockout
configuration/advanced/self_hosted_js_deps
configuration/advanced/social_auth
configuration/advanced/multi_factor_auth
configuration/advanced/multi_tenancy
configuration/advanced/webanalytics
configuration/advanced/django_channels_layer
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
.. _multi_tenancy_config:

************************
Multi Tenancy (Optional)
************************

**Last Updated:** December 2025

.. important::

This capability requires the ``django-tenants`` third-party library to be installed. Starting with Tethys 5.0 or if you are using ``micro-tethys-platform``, you will need to install ``django-tenants`` using conda or pip as follows:

.. code-block:: bash

# conda: conda-forge channel strongly recommended
conda install -c conda-forge django-tenants

# pip
pip install django-tenants

.. important::

Multi-tenancy requires PostgreSQL as the database backend. SQLite is not supported.

Tethys Portal supports only one tenant per portal by default. Multi-tenancy allows you to run multiple tenants (isolated instances of Tethys Portal) within a single deployment. This is useful for organizations or providers that want to provide separate environments for different groups or customers. In addition, multi-tenancy enables the ability to separate and customize the look and resources of the Tethys Portal based on each tenant. This functionality extends to the app level.


Enable Multi-Tenancy
====================

Use the following instructions to setup multi-tenancy for your Tethys Portal deployment. See the `Django-tenants Documentation <https://django-tenants.readthedocs.io/en/latest/use.html>`_ for more information.

Configuration
-------------

Enable multi-tenancy by making the following changes to your settings:

Begin by enabling Tethys Tenants:

.. code-block:: yaml
:emphasize-lines: 3-4

settings:
...
TENANTS_CONFIG:
ENABLED: true

Next, override the database engine to use the ``django-tenants`` backend:

.. code-block:: yaml
:emphasize-lines: 4

settings:
DATABASES:
default:
ENGINE: django_tenants.postgresql_backend

You can customize the multi-tenancy behavior with the following settings:

.. code-block:: yaml
:emphasize-lines: 4-9

settings:
TENANTS_CONFIG:
ENABLED: true
TENANT_APPS:
- "tethys_apps"
- "tethys_config"
TENANT_LIMIT_SET_CALLS: false
TENANT_COLOR_ADMIN_APPS: true
SHOW_PUBLIC_IF_NO_TENANT_FOUND: false

Configuration Options
=====================

**TENANT_APPS**
List of Django apps that should be isolated per tenant. These apps will have their database tables created in each tenant's schema.

**TENANT_LIMIT_SET_CALLS**
Boolean (default: ``false``). When ``true``, limits database SET calls for performance optimization.

**TENANT_COLOR_ADMIN_APPS**
Boolean (default: ``true``). When ``true``, colors tenant-enabled sections dark green in the site admin.

**SHOW_PUBLIC_IF_NO_TENANT_FOUND**
Boolean (default: ``false``). When ``true``, shows the public schema when no tenant is found instead of returning a 404 error.

Working with Tenants
====================

**Run migrations**:

If the Tethys database is being created for the first time, new tenant tables are created as part of it by detecting and applying migrations in the following way.

.. code-block:: bash

tethys manage makemigrations <app_label>
tethys manage migrate_schemas

If existing django-apps become tenant aware (are moved to the `TENANT_APPS` list) later on, django will not recognize that new migrations need to be applied by default. Use the ``migrate`` command to first unapply the migrations at the tenant level and then reapply them properly using the ``zero`` parameter and the ``--tenant``` flag in the following way.

.. code-block:: bash

tethys manage migrate <app_label> zero --fake --tenant
tethys manage migrate <app_label> --tenant

After updating your :file:`portal_config.yml` file:

**Create tenant**:

.. code-block:: bash

tethys manage create_tenant

See the `Django-tenants Documentation <https://django-tenants.readthedocs.io/en/latest/use.html#create-tenant>`_ for more details on using the `create_tenant` command or run ``tethys manage create_tenant --django-help`` on the active tethys environment terminal.

Even though it already exists, the default public schema must be added to the Tenants table using the `create_tenant` command. See the example below:

.. code-block:: bash

tethys manage create_tenant --schema_name public --name Public --domain-domain localhost

**Create tenant superuser**:

Each tenant requires its own portal admin account. Create a superuser for a specific tenant by running the following command:

.. code-block:: bash

tethys manage create_tenant_superuser

New tenants and tenant domains can be added via the Tethys Portal Admin interface once multi-tenancy is enabled. The Tethys Tenants admin block is only visible to superusers of the public schema.

.. figure:: ./images/tethys_tenants_admin.png
:width: 800px

Management
----------

Django-tenants includes two very useful commands to help manage database schemas.

- `tenant_command <https://django-tenants.readthedocs.io/en/latest/use.html#tenant-command>`_: Runs any django manage command on an individual schema
- `all_tenants_command <https://django-tenants.readthedocs.io/en/latest/use.html#all-tenants-command>`_: Runs any django manage command on all schemas

For example, to show the applied migrations for a specific tenant schema or for all tenant schemas, use the following commands:

.. code-block:: bash

tethys manage tenant_command showmigrations <app_label> --schema <schema_name>
tethys manage all_tenants_command showmigrations <app_label>
8 changes: 8 additions & 0 deletions docs/supplementary/optional_features.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ Allows users to enable multi-factor authentication for their Tethys Portal accou
- ``arrow``
- ``isodate``

Multi Tenancy
-------------

Enable multiple tenants with a single portal deployment and customize resources based on tenant.

**dependencies**
- ``django-tenants``

Single Sign On with Social Accounts
-----------------------------------

Expand Down
7 changes: 7 additions & 0 deletions docs/whats_new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ Recipes

See: :ref:`recipes`

Multi-Tenancy Support
.....................

* Tethys Platform now supports multi-tenancy using the third-party `django-tenants` library. This allows you to run multiple isolated configurations of Tethys Portal within a single portal.
See: :ref:`Multi Tenancy <multi_tenancy_config>`

For a full list of changes in version 4.4 refer to `<https://github.com/tethysplatform/tethys/releases/tag/4.4.0>`_
Automated PyPI Package Uploads
------------------------------

Expand Down
1 change: 1 addition & 0 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ dependencies:
- hs_restclient # Used with HydroShare Social backend
- python-jose # required by django-mfa2 - used for onelogin backend
- django-oauth-toolkit
- django-tenants # enables multi-tenant support

# datetime dependencies for "humanize" template filter (used with MFA2)
- arrow
Expand Down
3 changes: 2 additions & 1 deletion scripts/install_tethys.sh
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,8 @@ then
--set DATABASES.default.PASSWORD ${TETHYS_DB_PASSWORD} \
--set DATABASES.default.PORT ${TETHYS_DB_PORT} \
--set DATABASES.default.DIR ${TETHYS_DB_DIR} \
--set DATABASES.default.ENGINE django.db.backends.postgresql
--set DATABASES.default.ENGINE django_tenants.postgresql_backend \
--set TENANTS_CONFIG.ENABLED true
cat ${TETHYS_HOME}/portal_config.yml
fi

Expand Down
30 changes: 28 additions & 2 deletions tests/unit_tests/test_tethys_portal/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,12 +178,13 @@ def test_cors_config(self, _):
reload(settings)
self.assertListEqual(settings.CORS_ALLOWED_ORIGINS, ["http://example.com"])

@mock.patch("tethys_portal.optional_dependencies.has_module", return_value=False)
@mock.patch(
"tethys_portal.settings.yaml.safe_load",
return_value={"settings": {}},
)
@mock.patch("tethys_apps.utilities.relative_to_tethys_home")
def test_db_config_default(self, mock_home, _):
def test_db_config_default(self, mock_home, _, __):
name = mock.MagicMock()
name.exists.return_value = False
mock_home.return_value = name
Expand All @@ -196,6 +197,7 @@ def test_db_config_default(self, mock_home, _):
},
)

@mock.patch("tethys_portal.optional_dependencies.has_module", return_value=False)
@mock.patch(
"tethys_portal.settings.yaml.safe_load",
return_value={
Expand All @@ -204,7 +206,7 @@ def test_db_config_default(self, mock_home, _):
}
},
)
def test_db_config_postgres(self, _):
def test_db_config_postgres(self, _, __):
reload(settings)
self.assertDictEqual(
settings.DATABASES["default"],
Expand All @@ -218,6 +220,30 @@ def test_db_config_postgres(self, _):
},
)

@mock.patch(
"tethys_portal.settings.yaml.safe_load",
return_value={
"settings": {
"DATABASES": {
"default": {"ENGINE": "django_tenants.postgresql_backend"}
}
}
},
)
def test_db_config_tenants_postgres(self, _):
reload(settings)
self.assertDictEqual(
settings.DATABASES["default"],
{
"ENGINE": "django_tenants.postgresql_backend",
"NAME": "tethys_platform",
"USER": "tethys_default",
"PASSWORD": "pass",
"HOST": "localhost",
"PORT": 5436,
},
)

# TODO remove compatibility code tests with Tethys5.0 (or 4.2?)
@mock.patch(
"tethys_portal.settings.yaml.safe_load",
Expand Down
Loading
Loading