Kiwi TCMS configuration settings

All sensible settings are defined in tcms/settings/common.py. You will have to update some of them for your particular production environment.

Note

After adjusting settings for your environment you have to configure Kiwi TCMS via its web interface! As a minimum you have to Configuration of Kiwi TCMS domain and Configure external bug trackers!

Note

Additional information how to override Kiwi TCMS behavior can be found at http://kiwitcms.org/blog/tags/customization/!

# -*- coding: utf-8 -*-

import os
import pathlib
import sys
import tempfile
from importlib import import_module

import pkg_resources
from django.contrib.messages import constants as messages
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _

import tcms
from tcms.utils.secrets import get_secret

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~ You have to override the following settings in product.py


# Set to False for production
DEBUG = True


# Make this unique, and don't share it with anybody.
SECRET_KEY = "^8y!)$0t7yq2+65%&_#@i^_o)eb3^q--y_$e7a_=t$%$1i)zuv"  # nosec:B105


# Database settings
DATABASES = {
    "default": {
        "ENGINE": get_secret("KIWI_DB_ENGINE", "django.db.backends.mysql"),
        "NAME": get_secret("KIWI_DB_NAME", "kiwi"),
        "USER": get_secret("KIWI_DB_USER", "kiwi"),
        "PASSWORD": get_secret("KIWI_DB_PASSWORD", "kiwi"),
        "HOST": get_secret("KIWI_DB_HOST", ""),
        "PORT": get_secret("KIWI_DB_PORT", ""),
        "OPTIONS": {},
    },
}

# handle MariaDB only options
if DATABASES["default"]["ENGINE"].find("mysql") > -1:
    DATABASES["default"]["OPTIONS"].update(  # pylint: disable=objects-update-used
        {
            "init_command": "SET sql_mode='STRICT_TRANS_TABLES'",
            "charset": "utf8mb4",
        }
    )


# New setting since Django 3.2, used internally
# DO NOT CHANGE
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"


# Administrators error report email settings
ADMINS = [
    # ('Your Name', 'your_email@example.com'),
]


# Email settings
# DEFAULT_FROM_EMAIL must be defined if you want Kiwi TCMS to send emails.
# You also need to configure the email backend. For more information see:
# https://docs.djangoproject.com/en/3.0/topics/email/
# SERVER_EMAIL is used by the logging backend to send exceptions to ADMINS
SERVER_EMAIL = DEFAULT_FROM_EMAIL = "kiwi@example.com"
EMAIL_SUBJECT_PREFIX = "[Kiwi-TCMS] "

# functions which can perform custom email address validation on the registration page
# see tcms/kiwi_auth/tests/test_views.py::TestRegistration.test_custom_email_validators
EMAIL_VALIDATORS = ()

#  SMTP specific settings
#  EMAIL_HOST = 'smtp.example.com'
#  EMAIL_PORT = 25
#  EMAIL_HOST_USER = 'smtp_username'
#  EMAIL_HOST_PASSWORD = 'smtp_password'


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~ You may want to override the following settings as well


# Hosts/domain names that are valid for this site; required if DEBUG is False
# See https://docs.djangoproject.com/en/1.11/ref/settings/#allowed-hosts
ALLOWED_HOSTS = ["*"]


# default group in which new users will be created
DEFAULT_GROUPS = ["Tester"]


# When set to False site administrators will have to manually approve
# new users. You can combine this with tcms.signals.notify_admins() signal
# handler!
AUTO_APPROVE_NEW_USERS = True

# Password validation rules, see
# https://docs.djangoproject.com/en/4.1/topics/auth/passwords/#enabling-password-validation
AUTH_PASSWORD_VALIDATORS = [
    {
        "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
        "OPTIONS": {
            "min_length": 10,
        },
    },
    {
        "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
    },
]

# Set to False if you want to enforce account creation by admins.
REGISTRATION_ENABLED = (
    os.environ.get("KIWI_REGISTRATION_ENABLED", "True").lower() == "true"
)


# By default a simple CAPTCHA challenge is enabled in registration page
USE_CAPTCHA = True


# How often will session cookies expire? We set this to 24hrs by default.
# You may override based on your security policies
# https://docs.djangoproject.com/en/4.2/ref/settings/#csrf-cookie-age
# https://docs.djangoproject.com/en/4.2/ref/settings/#session-cookie-age
CSRF_COOKIE_AGE = SESSION_COOKIE_AGE = 86400

# There should be no need to override these settings
# https://docs.djangoproject.com/en/4.2/ref/settings/#csrf-cookie-httponly
# https://docs.djangoproject.com/en/4.2/ref/settings/#session-cookie-httponly
CSRF_COOKIE_HTTPONLY = SESSION_COOKIE_HTTPONLY = True

# Kiwi TCMS no longer supports plain/text so this shouldn't really change
# https://docs.djangoproject.com/en/4.2/ref/settings/#csrf-cookie-secure
# https://docs.djangoproject.com/en/4.2/ref/settings/#session-cookie-secure
CSRF_COOKIE_SECURE = SESSION_COOKIE_SECURE = True

# Maximum upload file size, default set to 5MB.
FILE_UPLOAD_MAX_SIZE = 5242880

# DO NOT MODIFY! Maximum size of POST request
# x1.5 to be able to upload base64 encoded attachments via RPC
DATA_UPLOAD_MAX_MEMORY_SIZE = int(FILE_UPLOAD_MAX_SIZE * 1.5)

# Controls if django-attachments deletes files from disk
DELETE_ATTACHMENTS_FROM_DISK = True


# Configure a caching backend. ATM only used to cache bug details b/c
# external issue trackers may be slow. If you want to override see:
# https://docs.djangoproject.com/en/2.2/topics/cache/
# https://docs.djangoproject.com/en/2.2/ref/settings/#std:setting-CACHES
CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
        "LOCATION": "kiwitcms",
        "TIMEOUT": 3600,
    }
}

# Absolute path to the directory static files should be collected to.
# Don't put anything in this directory yourself; store your static files
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
# Example: "/var/www/example.com/static/"
STATIC_ROOT = "/Kiwi/static/"


AUTHENTICATION_BACKENDS = [
    "django.contrib.auth.backends.ModelBackend",
    "guardian.backends.ObjectPermissionBackend",
]


# WARNING: Do not change this unless you know what you are doing !!!
MIDDLEWARE = [
    "tcms.core.middleware.CheckDBStructureExistsMiddleware",
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.locale.LocaleMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
    "simple_history.middleware.HistoryRequestMiddleware",
]

# https://docs.djangoproject.com/en/4.2/ref/settings/#x-frame-options
X_FRAME_OPTIONS = "DENY"

# https://docs.djangoproject.com/en/4.2/ref/settings/#secure-content-type-nosniff
SECURE_CONTENT_TYPE_NOSNIFF = True

# Kiwi TCMS doesn't serve plain/text HTTP anymore
# https://docs.djangoproject.com/en/4.2/ref/settings/#secure-ssl-redirect
SECURE_SSL_REDIRECT = "runserver" not in sys.argv and "test" not in sys.argv

# See https://github.com/kiwitcms/Kiwi/issues/2717
# and tcms/issuetracker/azure_boards.py
AZURE_BOARDS_API_VERSION = os.environ.get("AZURE_BOARDS_API_VERSION", "6.0")

# A list of fully qualified dotted paths to functions which will be executed after
# a new issue has been automatically opened in an external bug tracker via the
# 1-click bug report integration!
# See tcms.issuetracker.base.IssueTrackerType.post_process_new_issue() and
# tcms.issuetracker.tests.redmine_post_processing for hints!
EXTERNAL_ISSUE_POST_PROCESSORS = []

# a fully qualified dotted path which overrides the implementation of
# tcms.issutracker.base.IssueTrackerType.rpc_credentials
EXTERNAL_ISSUE_RPC_CREDENTIALS = ""

# Controls the default issue type for newly created issues in Jira.
# See JIRA.get_issue_from_jira() method
JIRA_ISSUE_TYPE = "Bug"

# Controls the default Tracker in which Redmine issues will be automatically created
# via 1-click integration.
REDMINE_TRACKER_NAME = "Bugs"

# Anonymous/GDPR compliant analytics via https://plausible.io/
# see https://plausible.io/privacy-focused-web-analytics for more details
ANONYMOUS_ANALYTICS = "runserver" not in sys.argv


# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~ DANGER: Don't change the settings below!

SITE_ID = 1

KIWI_VERSION = tcms.__version__

MANAGERS = ADMINS

LOGIN_REDIRECT_URL = reverse_lazy("core-views-index")

# internal
TCMS_ROOT_PATH = os.path.abspath(
    os.path.join(os.path.dirname(__file__), "..").replace("\\", "/")
)

# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True

# Language code for this installation. All choices can be found here:
# See https://code.djangoproject.com/ticket/29713
LANGUAGE_CODE = "en-us"

LOCALE_PATHS = [
    os.path.join(TCMS_ROOT_PATH, "locale"),
]

# If you set this to False, Django will not use timezone-aware datetimes.
# See https://docs.djangoproject.com/en/2.2/topics/i18n/timezones/
USE_TZ = os.environ.get("KIWI_USE_TZ", "False").lower() == "true"

# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
TIME_ZONE = os.environ.get("KIWI_TIME_ZONE", "Etc/UTC")

# Absolute filesystem path to the directory that will hold user-uploaded files.
MEDIA_ROOT = "/Kiwi/uploads"

# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash.
# Examples: "http://example.com/media/", "http://media.example.com/"
MEDIA_URL = "/uploads/"

# URL prefix for static files.
# Example: "http://example.com/static/", "http://static.example.com/"
STATIC_URL = "/static/"

# Additional locations of static files
STATICFILES_DIRS = [
    # Put strings here, like "/home/html/static" or "C:/www/django/static".
    # Always use forward slashes, even on Windows.
    # Don't forget to use absolute paths, not relative paths.
    os.path.join(TCMS_ROOT_PATH, "static").replace("\\", "/"),
    os.path.join(TCMS_ROOT_PATH, "node_modules").replace("\\", "/"),
]

# List of finder classes that know how to find static files in
# various locations.
STATICFILES_FINDERS = [
    "django.contrib.staticfiles.finders.FileSystemFinder",
    "django.contrib.staticfiles.finders.AppDirectoriesFinder",
]

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [
            os.path.join(TCMS_ROOT_PATH, "templates/").replace("\\", "/"),
        ],
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.static",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
                "django.template.context_processors.i18n",
                "tcms.core.context_processors.request_contents_processor",
                "tcms.core.context_processors.settings_processor",
                "tcms.core.context_processors.server_time_processor",
            ],
            "loaders": [
                "django.template.loaders.filesystem.Loader",
                "django.template.loaders.app_directories.Loader",
            ],
        },
    },
]

ROOT_URLCONF = "tcms.urls"

# Python dotted path to the WSGI application used by Django's runserver.
WSGI_APPLICATION = "tcms.wsgi.application"

# DANGER: DO NOT EDIT, see git log !!!
# this is consumed by kiwitcms-tenants/django-tenants
TENANT_APPS = [
    "django.contrib.sites",
    "guardian",
    "django_comments",
    "modernrpc",
    "simple_history",
    "tcms.kiwi_attachments.apps.AppConfig",
    "tcms.core.contrib.linkreference",
    "tcms.management",
    "tcms.testcases.apps.AppConfig",
    "tcms.testplans.apps.AppConfig",
    "tcms.testruns.apps.AppConfig",
]

# if you wish to disable Kiwi TCMS bug tracker
# define the KIWI_DISABLE_BUGTRACKER ENV variable
if os.environ.get("KIWI_DISABLE_BUGTRACKER") is None:
    TENANT_APPS.append("tcms.bugs.apps.AppConfig")


INSTALLED_APPS = TENANT_APPS + [
    "grappelli",
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.messages",
    "django.contrib.sessions",
    "django.contrib.staticfiles",
    "django.forms",
    "captcha",
    "colorfield",
    "django_extensions",
    "tree_queries",
    "vinaigrette",
    "tcms.core",
    "tcms.kiwi_auth",
    "tcms.telemetry",
    "tcms.rpc",
]

for plugin in pkg_resources.iter_entry_points("kiwitcms.plugins"):
    INSTALLED_APPS.append(plugin.module_name)

# this is the main navigation menu
MENU_ITEMS = [
    (
        _("TESTING"),
        [
            (_("New Test Plan"), reverse_lazy("plans-new")),
            ("-", "-"),
            (_("New Test Case"), reverse_lazy("testcases-new")),
            ("-", "-"),
            (_("New Test Run"), reverse_lazy("testruns-new")),
            ("-", "-") if "tcms.bugs.apps.AppConfig" in INSTALLED_APPS else (),
            (_("New Bug"), reverse_lazy("bugs-new"))
            if "tcms.bugs.apps.AppConfig" in INSTALLED_APPS
            else (),
        ],
    ),
    (
        _("SEARCH"),
        [
            (_("Search Test Plans"), reverse_lazy("plans-search")),
            (_("Search Test Cases"), reverse_lazy("testcases-search")),
            (_("Search Test Runs"), reverse_lazy("testruns-search")),
            (_("Search Bugs"), reverse_lazy("bugs-search"))
            if "tcms.bugs.apps.AppConfig" in INSTALLED_APPS
            else (),
        ],
    ),
    (
        _("TELEMETRY"),
        [
            (
                _("Testing"),
                [
                    (_("Breakdown"), reverse_lazy("testing-breakdown")),
                    (
                        _("Execution"),
                        [
                            (_("Dashboard"), reverse_lazy("execution-dashboard")),
                            (_("Matrix"), reverse_lazy("testing-status-matrix")),
                            (_("Trends"), reverse_lazy("testing-execution-trends")),
                        ],
                    ),
                    (_("TestCase health"), reverse_lazy("test-case-health")),
                ],
            ),
        ],
    ),
    (
        _("ADMIN"),
        [
            (_("Users"), reverse_lazy("admin-users-router")),
            (_("Groups"), reverse_lazy("admin-groups-router")),
            ("-", "-"),
            (_("Everything else"), "/admin/"),
        ],
    ),
    (_("PLUGINS"), []),
]

# last element is always PLUGINS so we can easily extend & override it
for plugin in pkg_resources.iter_entry_points("kiwitcms.plugins"):
    plugin_menu = import_module(f"{plugin.module_name}.menu")
    MENU_ITEMS[-1][1].extend(plugin_menu.MENU_ITEMS)

# redefine the help menu in the navigation bar
HELP_MENU_ITEMS = [
    ("https://github.com/kiwitcms/Kiwi/issues/new/choose", _("Report an Issue")),
    (
        "https://stackoverflow.com/questions/tagged/kiwi-tcms",
        _("Ask for help on StackOverflow"),
    ),
    (
        "https://opencollective.com/kiwitcms#section-contribute",
        _("Donate €5 via Open Collective"),
    ),
    ("http://kiwitcms.readthedocs.io/en/latest/admin.html", _("Administration Guide")),
    ("http://kiwitcms.readthedocs.io/en/latest/tutorial.html", _("User Guide")),
    ("http://kiwitcms.readthedocs.io/en/latest/api/index.html", _("API Help")),
]

SESSION_SERIALIZER = "django.contrib.sessions.serializers.JSONSerializer"

SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"

# WARNING: do not edit. The stock JSONRPC handler does not HTML escape !!!
# Stock handlers don't serialize timedelta into a meaningfull value
MODERNRPC_HANDLERS = [
    "tcms.handlers.KiwiTCMSXmlRpcHandler",
    "tcms.handlers.KiwiTCMSJsonRpcHandler",
]

# in alphabetic order
MODERNRPC_METHODS_MODULES = [
    "tcms.telemetry.api",
    "tcms.rpc.api.attachment",
    "tcms.rpc.api.auth",
    "tcms.rpc.api.bug",
    "tcms.rpc.api.build",
    "tcms.rpc.api.category",
    "tcms.rpc.api.classification",
    "tcms.rpc.api.component",
    "tcms.rpc.api.environment",
    "tcms.rpc.api.markdown",
    "tcms.rpc.api.kiwitcms",
    "tcms.rpc.api.plantype",
    "tcms.rpc.api.priority",
    "tcms.rpc.api.product",
    "tcms.rpc.api.tag",
    "tcms.rpc.api.testcase",
    "tcms.rpc.api.testexecution",
    "tcms.rpc.api.testexecutionstatus",
    "tcms.rpc.api.testcasestatus",
    "tcms.rpc.api.testplan",
    "tcms.rpc.api.testrun",
    "tcms.rpc.api.user",
    "tcms.rpc.api.version",
]

if "tcms.bugs.apps.AppConfig" in INSTALLED_APPS:
    MODERNRPC_METHODS_MODULES.append("tcms.bugs.api")

# This is a list of dotted class names providing integration with
# external bug trackers. Plugins and downstream installation can augment
# this list with their own integration classes!
# https://kiwitcms.readthedocs.io/en/latest/admin.html#configure-external-bug-trackers
EXTERNAL_BUG_TRACKERS = [
    "tcms.issuetracker.azure_boards.AzureBoards",
    "tcms.issuetracker.bitbucket.BitBucket",
    "tcms.issuetracker.types.Bugzilla",
    "tcms.issuetracker.types.JIRA",
    "tcms.issuetracker.types.GitHub",
    "tcms.issuetracker.types.Gitlab",
    "tcms.issuetracker.types.Redmine",
]
if "tcms.bugs.apps.AppConfig" in INSTALLED_APPS:
    EXTERNAL_BUG_TRACKERS.append("tcms.issuetracker.types.KiwiTCMS")

# Enable the administrator delete permission
# In another word it's set the admin to super user or not.
SET_ADMIN_AS_SUPERUSER = False

# Allows history_change_reason to be a TextField so we can
# support a changelog-like feature! DO NOT EDIT b/c migrations
# depend on this setting!
SIMPLE_HISTORY_HISTORY_CHANGE_REASON_USE_TEXT_FIELD = True

# Default page size when paginating queries
DEFAULT_PAGE_SIZE = 100

# A sample logging configuration. The only tangible logging
# performed by this configuration is to send an email to
# the site admins on every HTTP 500 error when DEBUG=False.
# See http://docs.djangoproject.com/en/dev/topics/logging for
# more details on how to customize your logging configuration.
LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "verbose": {
            "format": "%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s"
        },
        "simple": {"format": "[%(asctime)s] %(levelname)s %(message)s"},
    },
    "filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}},
    "handlers": {
        "console": {
            "level": "DEBUG",
            "class": "logging.StreamHandler",
            "formatter": "simple",
        },
        "mail_admins": {
            "level": "ERROR",
            "filters": ["require_debug_false"],
            "class": "django.utils.log.AdminEmailHandler",
        },
    },
    "loggers": {
        "django.request": {
            "handlers": ["mail_admins"],
            "level": "ERROR",
            "propagate": True,
        },
    },
}

# override default message tags to match Patternfly class names
MESSAGE_TAGS = {
    messages.ERROR: "danger",
}

if os.path.isdir("/var/tmp"):  # nosec: B108
    TEMP_DIR = pathlib.Path("/var/tmp")  # nosec: B108
else:
    TEMP_DIR = pathlib.Path(tempfile.gettempdir())

# See https://github.com/django-guardian/django-guardian/issues/726
ANONYMOUS_USER_NAME = "AnonymousUser"

# https://plausible.io/kiwitcms-container
PLAUSIBLE_DOMAIN = "kiwitcms-container"

Augmenting behavior with signal handlers

Kiwi TCMS defines signals which are triggered during specific events. Administrators may override the default handlers and/or connect additional ones to augment the default behavior of Kiwi TCMS. For more information refer to tcms.signals.

Language and emojis

By default our docker-compose.yml file is configured with MariaDB and uses charset utf8mb4 and collation utf8mb4_unicode_ci. That should be sufficient to support many languages plus emojis. If you are having troubles consult MariaDB Character Sets and Collations documentation.

If you need to change the default settings see docker-compose.yml or /etc/mysql/conf.d/mariadb.cnf if using a stand alone DB server! On the application side see DATABASES['default']['OPTIONS'] in tcms/settings/common.py.

Time zone settings

By default Kiwi TCMS is configured to use the UTC time zone. /etc/localtime inside the docker image also points to /usr/share/zoneinfo/Etc/UTC!

The settings which control this behavior are:

  • USE_TZ - controls whether or not Django uses timezone-aware datetime objects internally. Can also be controlled via KIWI_USE_TZ environment variable

  • TIME_ZONE - a string representing the time zone for this application. This should match the actual configuration of your server/docker container! Can also be controlled via KIWI_TIME_ZONE environment variable.

Your server/docker container clock should also match the accurate time of day!

Important

Unlike language there is no way to automatically receive time zone information from the browser and display different values for users. Instead Kiwi TCMS displays the current server time & time zone in the navigation bar! All other timestamps are in the same time zone!

E-mail settings

Kiwi TCMS supports email notifications which by default are sent over SMTP and need to be configured via the following settings:

  • Common settings:

    SERVER_EMAIL = DEFAULT_FROM_EMAIL = 'kiwi@example.com'
    # additional Kiwi TCMS setting
    EMAIL_SUBJECT_PREFIX = '[Kiwi-TCMS] '
    
  • SMTP specific settings:

    EMAIL_HOST = 'smtp.example.com'
    EMAIL_PORT = 25
    EMAIL_HOST_USER = 'smtp_username'
    EMAIL_HOST_PASSWORD = 'smtp_password'
    

To enable SSL or TLS support include one of the following:

EMAIL_USE_TLS = True
EMAIL_USE_SSL = True

For more details refer to Django documentation at https://docs.djangoproject.com/en/3.0/topics/email/

Testing and debugging e-mail configuration

Sending test e-mail to one or more addresses:

docker exec -it kiwi_web /Kiwi/manage.py sendtestemail user1@example1.tld user2@example2.tld ...

More details at: https://docs.djangoproject.com/en/3.0/ref/django-admin/#sendtestemail

Using Amazon SES instead of SMTP email

If you’d like to use an external email service, like Amazon SES you need to configure the following settings too:

EMAIL_BACKEND = 'django_ses.SESBackend'
AWS_SES_ACCESS_KEY_ID = 'xxxxxxxxxxxxxxxxxxxx'
AWS_SES_SECRET_ACCESS_KEY = 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY'

Also modify Dockerfile to include the following lines:

RUN pip install django_ses

Warning

The django-ses add-on is available as part of Kiwi TCMS Enterprise!

Kerberos authentication

See kiwitcms-auth-kerberos.

Anonymous read-only access

Changed in version 8.7.

By default Kiwi TCMS requires permissions, including view permissions which means users must be logged in! If you wish to allow anonymous read-only access then define the following setting:

AUTHENTICATION_BACKENDS = [
    'django.contrib.auth.backends.ModelBackend',
    'guardian.backends.ObjectPermissionBackend',
    'tcms.kiwi_auth.backends.AnonymousViewBackend',
]

New in version 8.7: tcms.kiwi_auth.backends.AnonymousViewBackend was added instead of the previous PUBLIC_VIEWS setting.

Changed in version 8.8: guardian.backends.ObjectPermissionBackend was added and the AUTHENTICATION_BACKENDS setting is now explicitly specified!