diff --git a/.gitignore b/.gitignore index 84f2c0e6a..c137b82cc 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ coldfront.db *.code-workspace .vscode db.json +.env diff --git a/CHANGELOG.md b/CHANGELOG.md index 61c98adc2..4be7b3207 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # ColdFront Changelog -## [Unreleased] +## [1.0.3] - Unreleased + +- Refactor ColdFront settings. See [PR #264](https://github.com/ubccr/coldfront/pull/264) ## [1.0.2] - 2021-02-15 @@ -39,3 +41,4 @@ [0.0.1]: https://github.com/ubccr/coldfront/releases/tag/v0.0.1 [1.0.1]: https://github.com/ubccr/coldfront/releases/tag/v1.0.1 [1.0.2]: https://github.com/ubccr/coldfront/releases/tag/v1.0.2 +[1.0.3]: https://github.com/ubccr/coldfront/releases/tag/v1.0.3 diff --git a/README.md b/README.md index 60407b11c..80aaebd9e 100644 --- a/README.md +++ b/README.md @@ -18,30 +18,18 @@ Python and released under the GPLv3 license. - Integration with 3rd party systems for automation and access control - Center director approval system and annual project reviews -## Plug-in Documentation - - [Slurm](coldfront/plugins/slurm) - - [FreeIPA](coldfront/plugins/freeipa) - - [LDAP](coldfront/plugins/ldap_user_search) - - [Mokey/Hydra OpenID Connect](coldfront/plugins/mokey_oidc) - - [iQuota](coldfront/plugins/iquota) - - [Open OnDemand](coldfront/plugins/ondemand) - - [Open XDMoD](coldfront/plugins/xdmod) - - [System Monitor](coldfront/plugins/system_monitor) (example of ways to integrate your own plug-ins) +## Documentation - -## Installation - -[Quick Start Instructions](docs/pages/quickstart.md) - - -## ColdFront Demos - -[Animated gifs demonstrating ColdFront features](docs/pages/demos.md) +For more information on installing and using ColdFront see our [documentation here](https://coldfront.readthedocs.io) ## Contact Information -If you would like a live demo followed by QA, please contact us at ccr-coldfront-admin-list@listserv.buffalo.edu. You can also contact us for general inquiries and installation troubleshooting. +If you would like a live demo followed by QA, please contact us at +ccr-coldfront-admin-list@listserv.buffalo.edu. You can also contact us for +general inquiries and installation troubleshooting. -If you would like to join our mailing list to receive news and updates, please send an email to listserv@listserv.buffalo.edu with no subject, and the following command in the body of the message: +If you would like to join our mailing list to receive news and updates, please +send an email to listserv@listserv.buffalo.edu with no subject, and the +following command in the body of the message: subscribe ccr-open-coldfront-list@listserv.buffalo.edu first_name last_name diff --git a/coldfront/__init__.py b/coldfront/__init__.py index 115ea85fd..d471c984e 100644 --- a/coldfront/__init__.py +++ b/coldfront/__init__.py @@ -1,7 +1,7 @@ import os import sys -__version__ = '1.0.2' +__version__ = '1.0.3' VERSION = __version__ diff --git a/coldfront/config/auth.py b/coldfront/config/auth.py new file mode 100644 index 000000000..6439ed845 --- /dev/null +++ b/coldfront/config/auth.py @@ -0,0 +1,29 @@ +from coldfront.config.env import ENV +from coldfront.config.base import INSTALLED_APPS, AUTHENTICATION_BACKENDS, TEMPLATES + +#------------------------------------------------------------------------------ +# ColdFront default authentication settings +#------------------------------------------------------------------------------ +AUTHENTICATION_BACKENDS += [ + 'django.contrib.auth.backends.ModelBackend', +] + +LOGIN_URL = '/user/login' +LOGIN_REDIRECT_URL = '/' +LOGOUT_REDIRECT_URL = '/' + +SU_LOGIN_CALLBACK = "coldfront.core.utils.common.su_login_callback" +SU_LOGOUT_REDIRECT_URL = "/admin/auth/user/" + +SESSION_COOKIE_AGE = 60 * 15 +SESSION_SAVE_EVERY_REQUEST = True +SESSION_COOKIE_SAMESITE = 'Strict' +SESSION_COOKIE_SECURE = True + +#------------------------------------------------------------------------------ +# Enable administrators to login as other users +#------------------------------------------------------------------------------ +if ENV.bool('ENABLE_SU', default=True): + AUTHENTICATION_BACKENDS += ['django_su.backends.SuBackend',] + INSTALLED_APPS.insert(0, 'django_su') + TEMPLATES[0]['OPTIONS']['context_processors'].extend(['django_su.context_processors.is_su', ]) diff --git a/coldfront/config/base.py b/coldfront/config/base.py new file mode 100644 index 000000000..c00e0c79d --- /dev/null +++ b/coldfront/config/base.py @@ -0,0 +1,138 @@ +""" +Base Django settings for ColdFront project. +""" +import os +import coldfront +from django.core.exceptions import ImproperlyConfigured +from django.core.management.utils import get_random_secret_key +from coldfront.config.env import ENV, PROJECT_ROOT + +#------------------------------------------------------------------------------ +# Base Django config for ColdFront +#------------------------------------------------------------------------------ +VERSION = coldfront.VERSION +BASE_DIR = PROJECT_ROOT() +ALLOWED_HOSTS = ENV.list('ALLOWED_HOSTS', default=['*']) +DEBUG = ENV.bool('DEBUG', default=False) +WSGI_APPLICATION = 'coldfront.config.wsgi.application' +ROOT_URLCONF = 'coldfront.config.urls' + +SECRET_KEY = ENV.str('SECRET_KEY', default='') +if len(SECRET_KEY) == 0: + SECRET_KEY = get_random_secret_key() + +#------------------------------------------------------------------------------ +# Locale settings +#------------------------------------------------------------------------------ +LANGUAGE_CODE = ENV.str('LANGUAGE_CODE', default='en-us') +TIME_ZONE = ENV.str('TIME_ZONE', default='America/New_York') +USE_I18N = True +USE_L10N = True +USE_TZ = True + +#------------------------------------------------------------------------------ +# Django Apps +#------------------------------------------------------------------------------ +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'django.contrib.humanize', +] + +# Additional Apps +INSTALLED_APPS += [ + 'crispy_forms', + 'sslserver', + 'django_q', + 'simple_history', +] + +# ColdFront Apps +INSTALLED_APPS += [ + 'coldfront.core.user', + 'coldfront.core.field_of_science', + 'coldfront.core.utils', + 'coldfront.core.portal', + 'coldfront.core.project', + 'coldfront.core.resource', + 'coldfront.core.allocation', + 'coldfront.core.grant', + 'coldfront.core.publication', + 'coldfront.core.research_output', +] + +#------------------------------------------------------------------------------ +# Django Middleware +#------------------------------------------------------------------------------ +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + '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', + 'simple_history.middleware.HistoryRequestMiddleware', +] + +#------------------------------------------------------------------------------ +# Django authentication backend. See auth.py +#------------------------------------------------------------------------------ +AUTHENTICATION_BACKENDS = [] + +#------------------------------------------------------------------------------ +# Django template and site settings +#------------------------------------------------------------------------------ +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [ + PROJECT_ROOT('site/templates'), + '/usr/share/coldfront/site/templates', + PROJECT_ROOT('coldfront/templates'), + ], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + 'django_settings_export.settings_export', + ], + }, + }, +] + +# Add local site templates files if set +SITE_TEMPLATES = ENV.str('SITE_TEMPLATES', default='') +if len(SITE_TEMPLATES) > 0: + if os.path.isdir(SITE_TEMPLATES): + TEMPLATES[0].DIRS.insert(0, SITE_TEMPLATES) + else: + raise ImproperlyConfigured('SITE_TEMPLATES should be a path to a directory') + +CRISPY_TEMPLATE_PACK = 'bootstrap4' +SETTINGS_EXPORT = [] + +STATIC_URL = '/static/' +STATIC_ROOT = ENV.str('STATIC_ROOT', default=PROJECT_ROOT('static_root')) +STATICFILES_DIRS = [ + PROJECT_ROOT('coldfront/static'), +] + +# Add local site static files if set +SITE_STATIC = ENV.str('SITE_STATIC', default='') +if len(SITE_STATIC) > 0: + if os.path.isdir(SITE_STATIC): + STATICFILES_DIRS.insert(0, SITE_STATIC) + else: + raise ImproperlyConfigured('SITE_STATIC should be a path to a directory') + +# Add system site static files +if os.path.isdir('/usr/share/coldfront/site/static'): + STATICFILES_DIRS.insert(0, '/usr/share/coldfront/site/static') diff --git a/coldfront/config/core.py b/coldfront/config/core.py new file mode 100644 index 000000000..19615f437 --- /dev/null +++ b/coldfront/config/core.py @@ -0,0 +1,76 @@ +from coldfront.config.base import SETTINGS_EXPORT +from coldfront.config.env import ENV + +#------------------------------------------------------------------------------ +# Advanced ColdFront configurations +#------------------------------------------------------------------------------ + +#------------------------------------------------------------------------------ +# General Center Information +#------------------------------------------------------------------------------ +CENTER_NAME = ENV.str('CENTER_NAME', default='HPC Resources') +CENTER_HELP_URL = ENV.str('CENTER_HELP_URL', default='') +CENTER_PROJECT_RENEWAL_HELP_URL = ENV.str('CENTER_PROJECT_RENEWAL_HELP_URL', default='') +CENTER_BASE_URL = ENV.str('CENTER_BASE_URL', default='') + +#------------------------------------------------------------------------------ +# Enable Project Review +#------------------------------------------------------------------------------ +PROJECT_ENABLE_PROJECT_REVIEW = ENV.bool('PROJECT_ENABLE_PROJECT_REVIEW', default=True) + +#------------------------------------------------------------------------------ +# Allocation related +#------------------------------------------------------------------------------ +ALLOCATION_ENABLE_ALLOCATION_RENEWAL = ENV.bool('ALLOCATION_ENABLE_ALLOCATION_RENEWAL', default=True) +ALLOCATION_FUNCS_ON_EXPIRE = ['coldfront.core.allocation.utils.test_allocation_function', ] + +# This is in days +ALLOCATION_DEFAULT_ALLOCATION_LENGTH = ENV.int('ALLOCATION_DEFAULT_ALLOCATION_LENGTH', default=365) + + +#------------------------------------------------------------------------------ +# Allow user to select account name for allocation +#------------------------------------------------------------------------------ +ALLOCATION_ACCOUNT_ENABLED = ENV.bool('ALLOCATION_ACCOUNT_ENABLED', default=False) +ALLOCATION_ACCOUNT_MAPPING = ENV.dict('ALLOCATION_ACCOUNT_MAPPING', default={}) + +SETTINGS_EXPORT += [ + 'ALLOCATION_ACCOUNT_ENABLED' +] + +ADMIN_COMMENTS_SHOW_EMPTY = ENV.bool('ADMIN_COMMENTS_SHOW_EMPTY', default=True) + +#------------------------------------------------------------------------------ +# Enable invoice functionality +#------------------------------------------------------------------------------ +INVOICE_ENABLED = ENV.bool('INVOICE_ENABLED', default=True) +# Override default 'Pending Payment' status +INVOICE_DEFAULT_STATUS = ENV.str('INVOICE_DEFAULT_STATUS', default='New') + +#------------------------------------------------------------------------------ +# Enable Open OnDemand integration +#------------------------------------------------------------------------------ +ONDEMAND_URL = ENV.str('ONDEMAND_URL', default=None) + +#------------------------------------------------------------------------------ +# Default Strings. Override these in local_settings.py +#------------------------------------------------------------------------------ +LOGIN_FAIL_MESSAGE = ENV.str('LOGIN_FAIL_MESSAGE', '') + +EMAIL_DIRECTOR_PENDING_PROJECT_REVIEW_EMAIL = """ +You recently applied for renewal of your account, however, to date you have not entered any publication nor grant info in the ColdFront system. I am reluctant to approve your renewal without understanding why. If there are no relevant publications or grants yet, then please let me know. If there are, then I would appreciate it if you would take the time to enter the data (I have done it myself and it took about 15 minutes). We use this information to help make the case to the university for continued investment in our department and it is therefore important that faculty enter the data when appropriate. Please email xxx-helpexample.com if you need technical assistance. + +As always, I am available to discuss any of this. + +Best regards +Director + + +xxx@example.edu +Phone: (xxx) xxx-xxx +""" + +ACCOUNT_CREATION_TEXT = '''University faculty can submit a help ticket to request an account. +Please see instructions on our website. Staff, students, and external collaborators must +request an account through a university faculty member. +''' diff --git a/coldfront/config/database.py b/coldfront/config/database.py new file mode 100644 index 000000000..fdc6216c0 --- /dev/null +++ b/coldfront/config/database.py @@ -0,0 +1,54 @@ +import os +from coldfront.config.env import ENV + +#------------------------------------------------------------------------------ +# Database settings +#------------------------------------------------------------------------------ +# Set this using the DB_URL env variable. Defaults to sqlite. +# +# Examples: +# +# MariaDB: +# DB_URL=mysql://user:password@127.0.0.1:3306/database +# +# Postgresql: +# DB_URL=psql://user:password@127.0.0.1:8458/database +#------------------------------------------------------------------------------ +DATABASES = { + 'default': ENV.db_url( + var='DB_URL', + default='sqlite:///'+os.path.join(os.getcwd(), 'coldfront.db') + ) +} + + +#------------------------------------------------------------------------------ +# Custom Database settings +#------------------------------------------------------------------------------ +# You can also override this manually in local_settings.py, for example: +# +# NOTE: For mysql you need to: pip install mysqlclient +# +# DATABASES = { +# 'default': { +# 'ENGINE': 'django.db.backends.mysql', +# 'NAME': 'coldfront', +# 'USER': '', +# 'PASSWORD': '', +# 'HOST': 'localhost', +# 'PORT': '', +# }, +# } +# +# NOTE: For postgresql you need to: pip install psycopg2 +# +# DATABASES = { +# 'default': { +# 'ENGINE': 'django.db.backends.postgresql_psycopg2', +# 'NAME': 'coldfront', +# 'USER': '', +# 'PASSWORD': '', +# 'HOST': 'localhost', +# 'PORT': '5432', +# }, +# } diff --git a/coldfront/config/email.py b/coldfront/config/email.py new file mode 100644 index 000000000..1a39c28d3 --- /dev/null +++ b/coldfront/config/email.py @@ -0,0 +1,23 @@ +from coldfront.config.env import ENV + +#------------------------------------------------------------------------------ +# Email/Notification settings +#------------------------------------------------------------------------------ +EMAIL_ENABLED = ENV.bool('EMAIL_ENABLED', default=False) +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' +EMAIL_HOST = ENV.str('EMAIL_HOST', default='localhost') +EMAIL_PORT = ENV.int('EMAIL_PORT', default=25) +EMAIL_HOST_USER = ENV.str('EMAIL_HOST_USER', default='') +EMAIL_HOST_PASSWORD = ENV.str('EMAIL_HOST_PASSWORD', default='') +EMAIL_USE_TLS = ENV.bool('EMAIL_USE_TLS', default=False) +EMAIL_TIMEOUT = ENV.int('EMAIL_TIMEOUT', default=3) +EMAIL_SUBJECT_PREFIX = ENV.str('EMAIL_SUBJECT_PREFIX', default='[ColdFront]') +EMAIL_ADMIN_LIST = ENV.list('EMAIL_ADMIN_LIST') +EMAIL_SENDER = ENV.str('EMAIL_SENDER') +EMAIL_TICKET_SYSTEM_ADDRESS = ENV.str('EMAIL_TICKET_SYSTEM_ADDRESS') +EMAIL_DIRECTOR_EMAIL_ADDRESS = ENV.str('EMAIL_DIRECTOR_EMAIL_ADDRESS') +EMAIL_PROJECT_REVIEW_CONTACT = ENV.str('EMAIL_PROJECT_REVIEW_CONTACT') +EMAIL_DEVELOPMENT_EMAIL_LIST = ENV.list('EMAIL_DEVELOPMENT_EMAIL_LIST') +EMAIL_OPT_OUT_INSTRUCTION_URL = ENV.str('EMAIL_OPT_OUT_INSTRUCTION_URL', default='') +EMAIL_ALLOCATION_EXPIRING_NOTIFICATION_DAYS = ENV.list('EMAIL_ALLOCATION_EXPIRING_NOTIFICATION_DAYS', cast=int, default=[7, 14, 30]) +EMAIL_SIGNATURE = ENV.str('EMAIL_SIGNATURE', default='', multiline=True) diff --git a/coldfront/config/env.py b/coldfront/config/env.py new file mode 100644 index 000000000..c53822c6f --- /dev/null +++ b/coldfront/config/env.py @@ -0,0 +1,22 @@ +import environ + +ENV = environ.Env() +PROJECT_ROOT = environ.Path(__file__) - 3 + +# Default paths to environment files +env_paths = [ + PROJECT_ROOT.path('.env'), + environ.Path('/etc/coldfront/coldfront.env'), +] + +if ENV.str('COLDFRONT_ENV', default='') != '': + env_paths.insert(0, environ.Path(ENV.str('COLDFRONT_ENV'))) + +# Read in any environment files +for e in env_paths: + try: + e.file('') + ENV.read_env(e()) + except FileNotFoundError: + pass + diff --git a/coldfront/config/local_settings.py.sample b/coldfront/config/local_settings.py.sample deleted file mode 100644 index 1a9d6ca71..000000000 --- a/coldfront/config/local_settings.py.sample +++ /dev/null @@ -1,287 +0,0 @@ -""" -Local ColdFront settings - -Here you can define custom database settings, add authentication backends, -configure logging, and ColdFront plugins. -""" -#------------------------------------------------------------------------------ -# Secret Key -- Generate new key using: https://www.miniwebtool.com/django-secret-key-generator/ -# and replace -#------------------------------------------------------------------------------ -SECRET_KEY = 'vtri&lztlbinerr4+yg1yzm23ez@+ub6=4*63z1%d!)fg(g4x$' # REPLACE - -#------------------------------------------------------------------------------ -# Enable debugging. WARNING: These should be set to False in production -#------------------------------------------------------------------------------ -DEBUG = True -DEVELOP = True - -if DEBUG is False and SECRET_KEY is 'vtri&lztlbinerr4+yg1yzm23ez@+ub6=4*63z1%d!)fg(g4x$': - from django.core.exceptions import ImproperlyConfigured - raise ImproperlyConfigured("The SECRET_KEY setting is using the preset value. Please update it!") - -#------------------------------------------------------------------------------ -# Session settings -#------------------------------------------------------------------------------ -# This should be set to True in production when using HTTPS -SESSION_COOKIE_SECURE = False - -#------------------------------------------------------------------------------ -# General Center Information -#------------------------------------------------------------------------------ -CENTER_NAME = 'HPC Resources' -# CENTER_HELP_URL = 'http://localhost/help' -# CENTER_PROJECT_RENEWAL_HELP_URL = 'http://localhost/help' -# CENTER_BASE_URL = 'https://coldfront.io' - -#------------------------------------------------------------------------------ -# Locale settings -#------------------------------------------------------------------------------ -# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones -LANGUAGE_CODE = 'en-us' -TIME_ZONE = 'America/New_York' - -#------------------------------------------------------------------------------ -# Logging -#------------------------------------------------------------------------------ -LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, - 'handlers': { - 'console': { - 'class': 'logging.StreamHandler', - }, - # 'file': { - # 'class': 'logging.FileHandler', - # 'filename': '/tmp/debug.log', - # }, - }, - 'loggers': { - 'django_auth_ldap': { - 'level': 'WARN', - # 'handlers': ['console', 'file'], - 'handlers': ['console', ], - }, - 'django': { - 'handlers': ['console'], - 'level': 'INFO', - }, - }, -} - -#------------------------------------------------------------------------------ -# Advanced ColdFront configurations -#------------------------------------------------------------------------------ - - -#------------------------------------------------------------------------------ -# Enable Project Review -#------------------------------------------------------------------------------ -PROJECT_ENABLE_PROJECT_REVIEW = True - -#------------------------------------------------------------------------------ -# Allocation related -#------------------------------------------------------------------------------ -ALLOCATION_ENABLE_ALLOCATION_RENEWAL = True -ALLOCATION_FUNCS_ON_EXPIRE = ['coldfront.core.allocation.utils.test_allocation_function', ] -ALLOCATION_DEFAULT_ALLOCATION_LENGTH = 365 # DAYS - - -#------------------------------------------------------------------------------ -# Custom Database settings -#------------------------------------------------------------------------------ -# NOTE: For mysql you need to: pip install mysqlclient -# -# DATABASES = { -# 'default': { -# 'ENGINE': 'django.db.backends.mysql', -# 'NAME': 'coldfront', -# 'USER': '', -# 'PASSWORD': '', -# 'HOST': 'localhost', -# 'PORT': '', -# }, -# } -# -# NOTE: For postgresql you need to: pip install psycopg2 -# -# DATABASES = { -# 'default': { -# 'ENGINE': 'django.db.backends.postgresql_psycopg2', -# 'NAME': 'coldfront', -# 'USER': '', -# 'PASSWORD': '', -# 'HOST': 'localhost', -# 'PORT': '5432', -# }, -# } - -EXTRA_APPS = [] -EXTRA_MIDDLEWARE = [] -EXTRA_AUTHENTICATION_BACKENDS = [] -LOCAL_SETTINGS_EXPORT = [] - - -#------------------------------------------------------------------------------ -# Email/Notification settings -#------------------------------------------------------------------------------ -# EMAIL_ENABLED = True -# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' -# EMAIL_HOST = 'localhost' -# EMAIL_PORT = 25 -# EMAIL_HOST_USER = '' -# EMAIL_HOST_PASSWORD = '' -# EMAIL_USE_TLS = False -# EMAIL_TIMEOUT = 3 -# EMAIL_SUBJECT_PREFIX = '[ColdFront]' -# EMAIL_ADMIN_LIST = ['admin@localhost'] -# EMAIL_SENDER = 'coldfront@localhost' -# EMAIL_TICKET_SYSTEM_ADDRESS = 'help@localhost' -# EMAIL_DIRECTOR_EMAIL_ADDRESS = 'director@localhost' -# EMAIL_PROJECT_REVIEW_CONTACT = 'review@localhost' -# EMAIL_DEVELOPMENT_EMAIL_LIST = ['dev1@localhost', 'dev2@localhost'] -# EMAIL_OPT_OUT_INSTRUCTION_URL = 'http://localhost/optout' -# EMAIL_ALLOCATION_EXPIRING_NOTIFICATION_DAYS = [7, 14, 30] -# EMAIL_SIGNATURE = """ -# HPC Resources -# http://localhost -# """ - - -#------------------------------------------------------------------------------ -# Enable administrators to login as other users -#------------------------------------------------------------------------------ -EXTRA_AUTHENTICATION_BACKENDS += ['django_su.backends.SuBackend',] - -#------------------------------------------------------------------------------ -# Example config for enabling LDAP user authentication using django-auth-ldap. -# This will enable LDAP user/password logins. -#------------------------------------------------------------------------------ -# import ldap -# from django_auth_ldap.config import GroupOfNamesType, LDAPSearch -# -# AUTH_LDAP_SERVER_URI = 'ldap://localhost' -# AUTH_LDAP_USER_SEARCH_BASE = 'cn=users,cn=accounts,dc=localhost,dc=localdomain' -# AUTH_LDAP_START_TLS = True -# AUTH_LDAP_BIND_AS_AUTHENTICATING_USER=True -# AUTH_LDAP_MIRROR_GROUPS = True -# AUTH_LDAP_USER_SEARCH = LDAPSearch( -# AUTH_LDAP_USER_SEARCH_BASE, ldap.SCOPE_ONELEVEL, '(uid=%(user)s)') -# AUTH_LDAP_GROUP_SEARCH_BASE = 'cn=groups,cn=accounts,dc=localhost,dc=localdomain' -# AUTH_LDAP_GROUP_SEARCH = LDAPSearch( -# AUTH_LDAP_GROUP_SEARCH_BASE, ldap.SCOPE_ONELEVEL, '(objectClass=groupOfNames)') -# AUTH_LDAP_GROUP_TYPE = GroupOfNamesType() -# AUTH_LDAP_USER_ATTR_MAP = { -# 'username': 'uid', -# 'first_name': 'givenName', -# 'last_name': 'sn', -# 'email': 'mail', -# } -# -# EXTRA_AUTHENTICATION_BACKENDS += ['django_auth_ldap.backend.LDAPBackend',] - -# ------------------------------------------------------------------------------ -# Enable invoice functionality -# ------------------------------------------------------------------------------ -# INVOICE_ENABLED = True -# INVOICE_DEFAULT_STATUS = 'New' # Override default 'Pending Payment' status - -# ------------------------------------------------------------------------------ -# Allow user to select account name for allocation -# ------------------------------------------------------------------------------ -ALLOCATION_ACCOUNT_ENABLED = False -# ALLOCATION_ACCOUNT_MAPPING = { -# 'University HPC': 'slurm_account_name', -# 'University Cloud': 'Cloud Account Name', -# } - -LOCAL_SETTINGS_EXPORT += [ - 'ALLOCATION_ACCOUNT_ENABLED' -] - - -#=============================================================================== -# ColdFront Plugin Settings -#=============================================================================== - -#------------------------------------------------------------------------------ -# Enable iquota reporting -#------------------------------------------------------------------------------ -# EXTRA_APPS += [ -# 'coldfront.plugins.iquota' -# ] -# -# IQUOTA_KEYTAB = '/path/to/user.keytab' -# IQUOTA_CA_CERT = '/etc/ipa/ca.crt' -# IQUOTA_API_HOST = 'localhost' -# IQUOTA_API_PORT = '8080' -# IQUOTA_USER_PATH = '/ifs/user' -# IQUOTA_GROUP_PATH = '/ifs/projects' - -#------------------------------------------------------------------------------ -# Enable system monitor reporting -#------------------------------------------------------------------------------ -# EXTRA_APPS += [ -# 'coldfront.plugins.system_monitor' -# ] -# SYSTEM_MONITOR_PANEL_TITLE = 'HPC Cluster Status' -# SYSTEM_MONITOR_ENDPOINT = 'http://localhost/status/status.html' -# SYSTEM_MONITOR_DISPLAY_MORE_STATUS_INFO_LINK = 'http://localhost/status' -# SYSTEM_MONITOR_DISPLAY_XDMOD_LINK = 'https://localhost/xdmod' - - -#------------------------------------------------------------------------------ -# Enable FreeIPA app for updating group membership and user search -#------------------------------------------------------------------------------ -# EXTRA_APPS += [ -# 'coldfront.plugins.freeipa', -# ] -# FREEIPA_KTNAME = '/path/to/user.keytab' -# FREEIPA_SERVER = 'freeipa.localhost.localdomain' -# FREEIPA_USER_SEARCH_BASE = 'cn=users,cn=accounts,dc=example,dc=edu' -# FREEIPA_ENABLE_SIGNALS = False -# ADDITIONAL_USER_SEARCH_CLASSES = ['coldfront.plugins.freeipa.search.LDAPUserSearch',] - -#------------------------------------------------------------------------------ -# Enable Mokey/Hydra OpenID Connect Authentication Backend -#------------------------------------------------------------------------------ -# EXTRA_APPS += [ -# 'mozilla_django_oidc', -# 'coldfront.plugins.mokey_oidc', -# ] -# -# EXTRA_AUTHENTICATION_BACKENDS += [ -# 'coldfront.plugins.mokey_oidc.auth.OIDCMokeyAuthenticationBackend', -# ] -# -# EXTRA_MIDDLEWARE += [ -# 'mozilla_django_oidc.middleware.SessionRefresh', -# ] -# -# OIDC_OP_JWKS_ENDPOINT = "https://localhost/hydra/.well-known/jwks.json" -# OIDC_RP_SIGN_ALGO = 'RS256' -# OIDC_RP_CLIENT_ID = '' -# OIDC_RP_CLIENT_SECRET = '' -# OIDC_OP_AUTHORIZATION_ENDPOINT = "https://localhost/hydra/oauth2/auth" -# OIDC_OP_TOKEN_ENDPOINT = "https://localhost/hydra/oauth2/token" -# OIDC_OP_USER_ENDPOINT = "https://localhost/hydra/userinfo" -# OIDC_VERIFY_SSL = True -# OIDC_RENEW_ID_TOKEN_EXPIRY_SECONDS = 60 * 60 - -#------------------------------------------------------------------------------ -# Enable Slurm support -#------------------------------------------------------------------------------ -# EXTRA_APPS += [ -# 'coldfront.plugins.slurm', -# ] -# SLURM_SACCTMGR_PATH = '/usr/bin/sacctmgr' - -#------------------------------------------------------------------------------ -# Enable XDMoD support -#------------------------------------------------------------------------------ -# EXTRA_APPS += [ -# 'coldfront.plugins.xdmod', -# ] - -# XDMOD_API_URL = 'http://localhost' -# ONDEMAND_URL = "https://ondemand.ccr.buffalo.edu" diff --git a/coldfront/config/local_strings.py.sample b/coldfront/config/local_strings.py.sample deleted file mode 100644 index 41f0b47a7..000000000 --- a/coldfront/config/local_strings.py.sample +++ /dev/null @@ -1,21 +0,0 @@ -#------------------------------------------------------------------------------ -# Default Strings. Override these in local settings -#------------------------------------------------------------------------------ - -EMAIL_DIRECTOR_PENDING_PROJECT_REVIEW_EMAIL = """ -You recently applied for renewal of your account, however, to date you have not entered any publication nor grant info in the ColdFront system. I am reluctant to approve your renewal without understanding why. If there are no relevant publications or grants yet, then please let me know. If there are, then I would appreciate it if you would take the time to enter the data (I have done it myself and it took about 15 minutes). We use this information to help make the case to the university for continued investment in our department and it is therefore important that faculty enter the data when appropriate. Please email xxx-helpexample.com if you need technical assistance. - -As always, I am available to discuss any of this. - -Best regards -Director - - -xxx@example.edu -Phone: (xxx) xxx-xxx -""" -LOGIN_FAIL_MESSAGE = 'The username or password you entered is incorrect. Reset password' -ACCOUNT_CREATION_TEXT = '''University faculty can submit a help ticket to request an account. -Please see instructions on our website. Staff, students, and external collaborators must -request an account through a university faculty member. -''' diff --git a/coldfront/config/logging.py b/coldfront/config/logging.py new file mode 100644 index 000000000..8d533a776 --- /dev/null +++ b/coldfront/config/logging.py @@ -0,0 +1,38 @@ +from django.contrib.messages import constants as messages + +#------------------------------------------------------------------------------ +# ColdFront logging config +#------------------------------------------------------------------------------ + +MESSAGE_TAGS = { + messages.DEBUG: 'info', + messages.INFO: 'info', + messages.SUCCESS: 'success', + messages.WARNING: 'warning', + messages.ERROR: 'danger', +} + +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'handlers': { + 'console': { + 'class': 'logging.StreamHandler', + }, + # 'file': { + # 'class': 'logging.FileHandler', + # 'filename': '/tmp/debug.log', + # }, + }, + 'loggers': { + 'django_auth_ldap': { + 'level': 'WARN', + # 'handlers': ['console', 'file'], + 'handlers': ['console', ], + }, + 'django': { + 'handlers': ['console'], + 'level': 'INFO', + }, + }, +} diff --git a/coldfront/config/plugins/__init__.py b/coldfront/config/plugins/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/coldfront/config/plugins/freeipa.py b/coldfront/config/plugins/freeipa.py new file mode 100644 index 000000000..c38656dac --- /dev/null +++ b/coldfront/config/plugins/freeipa.py @@ -0,0 +1,12 @@ +from coldfront.config.base import INSTALLED_APPS, ENV +from coldfront.config.env import ENV + +INSTALLED_APPS += [ + 'coldfront.plugins.freeipa', +] + +FREEIPA_KTNAME = ENV.str('FREEIPA_KTNAME') +FREEIPA_SERVER = ENV.str('FREEIPA_SERVER') +FREEIPA_USER_SEARCH_BASE = ENV.str('FREEIPA_USER_SEARCH_BASE') +FREEIPA_ENABLE_SIGNALS = False +ADDITIONAL_USER_SEARCH_CLASSES = ['coldfront.plugins.freeipa.search.LDAPUserSearch',] diff --git a/coldfront/config/plugins/iquota.py b/coldfront/config/plugins/iquota.py new file mode 100644 index 000000000..ca572ff0d --- /dev/null +++ b/coldfront/config/plugins/iquota.py @@ -0,0 +1,11 @@ +from coldfront.config.base import INSTALLED_APPS +from coldfront.config.env import ENV + +INSTALLED_APPS += [ + 'coldfront.plugins.iquota', +] + +IQUOTA_KEYTAB = ENV.str('IQUOTA_KEYTAB') +IQUOTA_CA_CERT = ENV.str('IQUOTA_CA_CERT') +IQUOTA_API_HOST = ENV.str('IQUOTA_API_HOST') +IQUOTA_API_PORT = ENV.str('IQUOTA_API_PORT', default='8080') diff --git a/coldfront/config/plugins/ldap.py b/coldfront/config/plugins/ldap.py new file mode 100644 index 000000000..ac9803fc0 --- /dev/null +++ b/coldfront/config/plugins/ldap.py @@ -0,0 +1,31 @@ +from coldfront.config.auth import AUTHENTICATION_BACKENDS +from coldfront.config.env import ENV +from django.core.exceptions import ImproperlyConfigured + +try: + import ldap + from django_auth_ldap.config import GroupOfNamesType, LDAPSearch +except ImportError: + raise ImproperlyConfigured('Please run: pip install ldap3 django_auth_ldap') + +#------------------------------------------------------------------------------ +# LDAP user authentication using django-auth-ldap. This will enable LDAP +# user/password logins. You can also override this in local_settings.py +#------------------------------------------------------------------------------ +AUTH_LDAP_SERVER_URI = ENV.str('AUTH_LDAP_SERVER_URI') +AUTH_LDAP_USER_SEARCH_BASE = ENV.str('AUTH_LDAP_USER_SEARCH_BASE') +AUTH_LDAP_START_TLS = ENV.bool('AUTH_LDAP_START_TLS', default=True) +AUTH_LDAP_BIND_AS_AUTHENTICATING_USER = True +AUTH_LDAP_MIRROR_GROUPS = ENV.bool('AUTH_LDAP_MIRROR_GROUPS', default=True) +AUTH_LDAP_GROUP_SEARCH_BASE = ENV.str('AUTH_LDAP_GROUP_SEARCH_BASE') +AUTH_LDAP_USER_SEARCH = LDAPSearch(AUTH_LDAP_USER_SEARCH_BASE, ldap.SCOPE_ONELEVEL, '(uid=%(user)s)') +AUTH_LDAP_GROUP_SEARCH = LDAPSearch(AUTH_LDAP_GROUP_SEARCH_BASE, ldap.SCOPE_ONELEVEL, '(objectClass=groupOfNames)') +AUTH_LDAP_GROUP_TYPE = GroupOfNamesType() +AUTH_LDAP_USER_ATTR_MAP = ENV.dict('AUTH_LDAP_USER_ATTR_MAP', default ={ + 'username': 'uid', + 'first_name': 'givenName', + 'last_name': 'sn', + 'email': 'mail', + }) + +AUTHENTICATION_BACKENDS += ['django_auth_ldap.backend.LDAPBackend',] diff --git a/coldfront/config/plugins/openid.py b/coldfront/config/plugins/openid.py new file mode 100644 index 000000000..45971d0cb --- /dev/null +++ b/coldfront/config/plugins/openid.py @@ -0,0 +1,39 @@ +from coldfront.config.base import INSTALLED_APPS, MIDDLEWARE, AUTHENTICATION_BACKENDS +from coldfront.config.env import ENV + +#------------------------------------------------------------------------------ +# Enable OpenID Connect Authentication Backend +#------------------------------------------------------------------------------ +INSTALLED_APPS += [ + 'mozilla_django_oidc', +] + +if ENV.bool('PLUGIN_MOKEY', default=False): + #------------------------------------------------------------------------------ + # Enable Mokey/Hydra OpenID Connect Authentication Backend + #------------------------------------------------------------------------------ + INSTALLED_APPS += [ + 'coldfront.plugins.mokey_oidc', + ] + + AUTHENTICATION_BACKENDS += [ + 'coldfront.plugins.mokey_oidc.auth.OIDCMokeyAuthenticationBackend', + ] +else: + AUTHENTICATION_BACKENDS += [ + 'mozilla_django_oidc.auth.OIDCAuthenticationBackend', + ] + +MIDDLEWARE += [ + 'mozilla_django_oidc.middleware.SessionRefresh', +] + +OIDC_OP_JWKS_ENDPOINT = ENV.str('OIDC_OP_JWKS_ENDPOINT') +OIDC_RP_SIGN_ALGO = ENV.str('OIDC_RP_SIGN_ALGO') +OIDC_RP_CLIENT_ID = ENV.str('OIDC_RP_CLIENT_ID') +OIDC_RP_CLIENT_SECRET = ENV.str('OIDC_RP_CLIENT_SECRET') +OIDC_OP_AUTHORIZATION_ENDPOINT = ENV.str('OIDC_OP_AUTHORIZATION_ENDPOINT') +OIDC_OP_TOKEN_ENDPOINT = ENV.str('OIDC_OP_TOKEN_ENDPOINT') +OIDC_OP_USER_ENDPOINT = ENV.str('OIDC_OP_USER_ENDPOINT') +OIDC_VERIFY_SSL = ENV.bool('OIDC_VERIFY_SSL', default=True) +OIDC_RENEW_ID_TOKEN_EXPIRY_SECONDS = ENV.int('OIDC_RENEW_ID_TOKEN_EXPIRY_SECONDS', default=3600) diff --git a/coldfront/config/plugins/slurm.py b/coldfront/config/plugins/slurm.py new file mode 100644 index 000000000..2ce0bb7ab --- /dev/null +++ b/coldfront/config/plugins/slurm.py @@ -0,0 +1,11 @@ +from coldfront.config.base import INSTALLED_APPS +from coldfront.config.env import ENV + +INSTALLED_APPS += [ + 'coldfront.plugins.slurm', +] + +SLURM_SACCTMGR_PATH = ENV.str('SLURM_SACCTMGR_PATH', default='/usr/bin/sacctmgr') +SLURM_NOOP = ENV.str('SLURM_NOOP', False) +SLURM_IGNORE_USERS = ENV.list('SLURM_IGNORE_USERS', default=['root']) +SLURM_IGNORE_ACCOUNTS = ENV.list('SLURM_IGNORE_ACCOUNTS', default=[]) diff --git a/coldfront/config/plugins/system_monitor.py b/coldfront/config/plugins/system_monitor.py new file mode 100644 index 000000000..d2db6552b --- /dev/null +++ b/coldfront/config/plugins/system_monitor.py @@ -0,0 +1,11 @@ +from coldfront.config.base import INSTALLED_APPS +from coldfront.config.env import ENV + +INSTALLED_APPS += [ + 'coldfront.plugins.system_monitor' +] + +SYSTEM_MONITOR_PANEL_TITLE = ENV.str('SYSMON_TITLE', default='HPC Cluster Status') +SYSTEM_MONITOR_ENDPOINT = ENV.str('SYSMON_ENDPOINT') +SYSTEM_MONITOR_DISPLAY_MORE_STATUS_INFO_LINK = ENV.str('SYSMON_LINK') +SYSTEM_MONITOR_DISPLAY_XDMOD_LINK = ENV.str('SYSMON_XDMOD_LINK') diff --git a/coldfront/config/plugins/xdmod.py b/coldfront/config/plugins/xdmod.py new file mode 100644 index 000000000..f5931dc65 --- /dev/null +++ b/coldfront/config/plugins/xdmod.py @@ -0,0 +1,11 @@ +from coldfront.config.base import INSTALLED_APPS +from coldfront.config.env import ENV + +#------------------------------------------------------------------------------ +# Enable XDMoD support +#------------------------------------------------------------------------------ +INSTALLED_APPS += [ + 'coldfront.plugins.xdmod', +] + +XDMOD_API_URL = ENV.str('XDMOD_API_URL') diff --git a/coldfront/config/settings.py b/coldfront/config/settings.py index 3faa906d6..708fb9377 100644 --- a/coldfront/config/settings.py +++ b/coldfront/config/settings.py @@ -1,197 +1,53 @@ -""" -Default Django settings for ColdFront project. -""" import os -import sys - -from django.contrib.messages import constants as messages - -#------------------------------------------------------------------------------ -# Django config for ColdFront -#------------------------------------------------------------------------------ - -BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -ALLOWED_HOSTS = ['*'] - -#------------------------------------------------------------------------------ -# Django Apps -#------------------------------------------------------------------------------ -INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'django.contrib.humanize', +from split_settings.tools import optional, include +from coldfront.config.env import ENV, PROJECT_ROOT + +# ColdFront split settings +coldfront_configs = [ + 'base.py', + 'database.py', + 'auth.py', + 'logging.py', + 'core.py', ] - -# Additional Apps -INSTALLED_APPS += [ - 'crispy_forms', - 'sslserver', - 'django_q', - 'simple_history', -] - -# ColdFront Apps -INSTALLED_APPS += [ - 'coldfront.core.user', - 'coldfront.core.field_of_science', - 'coldfront.core.utils', - 'coldfront.core.portal', - 'coldfront.core.project', - 'coldfront.core.resource', - 'coldfront.core.allocation', - 'coldfront.core.grant', - 'coldfront.core.publication', - 'coldfront.core.research_output', -] - -#------------------------------------------------------------------------------ -# Django Middleware -#------------------------------------------------------------------------------ -MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - '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', - 'simple_history.middleware.HistoryRequestMiddleware', -] - -#------------------------------------------------------------------------------ -# Database settings -#------------------------------------------------------------------------------ -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'coldfront.db'), - } +if ENV.bool('EMAIL_ENABLED', default=False): + coldfront_configs.append('email.py') + +# ColdFront plugin settings +plugin_configs = { + 'PLUGIN_SLURM': 'plugins/slurm.py', + 'PLUGIN_IQUOTA': 'plugins/iquota.py', + 'PLUGIN_FREEIPA': 'plugins/freeipa.py', + 'PLUGIN_SYSMON': 'plugins/system_montior.py', + 'PLUGIN_XDMOD': 'plugins/xdmod.py', + 'PLUGIN_AUTH_OIDC': 'plugins/openid.py', + 'PLUGIN_AUTH_LDAP': 'plugins/ldap.py', } -#------------------------------------------------------------------------------ -# Authentication backends -#------------------------------------------------------------------------------ -AUTHENTICATION_BACKENDS = [ - 'django.contrib.auth.backends.ModelBackend', -] - -#------------------------------------------------------------------------------ -# Django site settings -#------------------------------------------------------------------------------ -ROOT_URLCONF = 'coldfront.config.urls' - -TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [ - os.path.join(BASE_DIR, 'site/templates'), - '/usr/share/coldfront/site/templates', - os.path.join(BASE_DIR, 'coldfront/templates'), - ], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - 'django_settings_export.settings_export', - ], - }, - }, -] - -SESSION_COOKIE_AGE = 60 * 15 -SESSION_SAVE_EVERY_REQUEST = True -SESSION_COOKIE_SAMESITE = 'Strict' +# This allows plugins to be enabled via environment variables. Can alternatively +# add the relevant configs to local_settings.py +for key, pc in plugin_configs.items(): + if ENV.bool(key, default=False): + coldfront_configs.append(pc) -WSGI_APPLICATION = 'coldfront.config.wsgi.application' +# Local settings overrides +local_configs = [ + # Local settings relative to coldfront.config package + 'local_settings.py', -USE_I18N = True -USE_L10N = True -USE_TZ = True + # System wide settings for production deployments + '/etc/coldfront/local_settings.py', -ADMIN_COMMENTS_SHOW_EMPTY = True - -MESSAGE_TAGS = { - messages.DEBUG: 'info', - messages.INFO: 'info', - messages.SUCCESS: 'success', - messages.WARNING: 'warning', - messages.ERROR: 'danger', -} - -CRISPY_TEMPLATE_PACK = 'bootstrap4' - -STATIC_URL = '/static/' -STATIC_ROOT = os.path.join(BASE_DIR, 'static_root') -STATICFILES_DIRS = [ - os.path.join(BASE_DIR, 'coldfront/static'), + # Local settings relative to coldfront project root + PROJECT_ROOT('local_settings.py') ] -# Add local site static files -if os.path.isdir(os.path.join(BASE_DIR, 'site/static')): - STATICFILES_DIRS.insert(0, os.path.join(BASE_DIR, 'site/static')) - -# Add system site static files -if os.path.isdir('/usr/share/coldfront/site/static'): - STATICFILES_DIRS.insert(0, '/usr/share/coldfront/site/static') - -LOGIN_URL = '/user/login' -LOGIN_REDIRECT_URL = '/' -LOGOUT_REDIRECT_URL = '/' - -SU_LOGIN_CALLBACK = "coldfront.core.utils.common.su_login_callback" -SU_LOGOUT_REDIRECT_URL = "/admin/auth/user/" - - -SETTINGS_EXPORT = [] - -#------------------------------------------------------------------------------ -# Local settings overrides (see local_settings.py.sample) -#------------------------------------------------------------------------------ -try: - from coldfront.config.local_strings import * -except ImportError: - print("local_strings.py file is required. Copy coldfront/config/local_strings.py.sample to local_strings.py") - sys.exit() - -try: - from coldfront.config.local_settings import * -except ImportError: - print("local_settings.py file is required. Copy coldfront/config/local_settings.py.sample to local_settings.py") - sys.exit() - -try: - INSTALLED_APPS = INSTALLED_APPS + EXTRA_APPS -except NameError: - INSTALLED_APPS = INSTALLED_APPS - -try: - MIDDLEWARE = MIDDLEWARE + EXTRA_MIDDLEWARE -except NameError: - MIDDLEWARE = MIDDLEWARE - -try: - AUTHENTICATION_BACKENDS = AUTHENTICATION_BACKENDS + EXTRA_AUTHENTICATION_BACKENDS -except NameError: - AUTHENTICATION_BACKENDS = AUTHENTICATION_BACKENDS - - -if 'django_su.backends.SuBackend' in EXTRA_AUTHENTICATION_BACKENDS: - INSTALLED_APPS.insert(0, 'django_su') - TEMPLATES[0]['OPTIONS']['context_processors'].extend(['django_su.context_processors.is_su', ]) +if ENV.str('COLDFRONT_CONFIG', default='') != '': + # Local settings from path specified via environment variable + local_configs.append(environ.Path(ENV.str('COLDFRONT_CONFIG'))()) -import coldfront -VERSION = coldfront.__version__ +for lc in local_configs: + coldfront_configs.append(optional(lc)) -try: - SETTINGS_EXPORT = SETTINGS_EXPORT + LOCAL_SETTINGS_EXPORT -except NameError: - SETTINGS_EXPORT = SETTINGS_EXPORT +include(*coldfront_configs) diff --git a/coldfront/config/urls.py b/coldfront/config/urls.py index ac19c8b09..b8a45a3b4 100644 --- a/coldfront/config/urls.py +++ b/coldfront/config/urls.py @@ -27,11 +27,11 @@ ] -if 'coldfront.plugins.iquota' in settings.EXTRA_APPS: +if 'coldfront.plugins.iquota' in settings.INSTALLED_APPS: urlpatterns.append(path('iquota/', include('coldfront.plugins.iquota.urls'))) -if 'mozilla_django_oidc' in settings.EXTRA_APPS: +if 'mozilla_django_oidc' in settings.INSTALLED_APPS: urlpatterns.append(path('oidc/', include('mozilla_django_oidc.urls'))) -if 'django_su.backends.SuBackend' in settings.EXTRA_AUTHENTICATION_BACKENDS: +if 'django_su.backends.SuBackend' in settings.AUTHENTICATION_BACKENDS: urlpatterns.append(path('su/', include('django_su.urls'))) diff --git a/coldfront/core/portal/views.py b/coldfront/core/portal/views.py index e69ed1ab5..6c861e0b0 100644 --- a/coldfront/core/portal/views.py +++ b/coldfront/core/portal/views.py @@ -41,17 +41,15 @@ def home(request): context['project_list'] = project_list context['allocation_list'] = allocation_list try: - if(settings.ONDEMAND_URL != None): - print(settings.ONDEMAND_URL) - context['ondemand_url'] = settings.ONDEMAND_URL + context['ondemand_url'] = settings.ONDEMAND_URL except AttributeError: - print("No Ondemand URL provided") + pass else: template_name = 'portal/nonauthorized_home.html' - context['EXTRA_APPS'] = settings.EXTRA_APPS + context['EXTRA_APPS'] = settings.INSTALLED_APPS - if 'coldfront.plugins.system_monitor' in settings.EXTRA_APPS: + if 'coldfront.plugins.system_monitor' in settings.INSTALLED_APPS: from coldfront.plugins.system_monitor.utils import get_system_monitor_context context.update(get_system_monitor_context()) diff --git a/coldfront/core/project/views.py b/coldfront/core/project/views.py index ee7580337..cb2ef06df 100644 --- a/coldfront/core/project/views.py +++ b/coldfront/core/project/views.py @@ -130,12 +130,12 @@ def get_context_data(self, **kwargs): context['allocations'] = allocations context['project_users'] = project_users context['ALLOCATION_ENABLE_ALLOCATION_RENEWAL'] = ALLOCATION_ENABLE_ALLOCATION_RENEWAL + try: - if(settings.ONDEMAND_URL != None): - print(settings.ONDEMAND_URL) - context['ondemand_url'] = settings.ONDEMAND_URL + context['ondemand_url'] = settings.ONDEMAND_URL except AttributeError: - print("NO ONDEMAND URL provided") + pass + return context diff --git a/coldfront/core/user/urls.py b/coldfront/core/user/urls.py index 69f61d319..9d5b85f6b 100644 --- a/coldfront/core/user/urls.py +++ b/coldfront/core/user/urls.py @@ -4,7 +4,7 @@ import coldfront.core.user.views as user_views -EXTRA_APPS = settings.EXTRA_APPS +EXTRA_APPS = settings.INSTALLED_APPS urlpatterns = [ diff --git a/coldfront/core/utils/management/commands/add_scheduled_tasks.py b/coldfront/core/utils/management/commands/add_scheduled_tasks.py index 62765e9f1..0f78c8ea7 100644 --- a/coldfront/core/utils/management/commands/add_scheduled_tasks.py +++ b/coldfront/core/utils/management/commands/add_scheduled_tasks.py @@ -3,6 +3,7 @@ from django.conf import settings from django.core.management.base import BaseCommand, CommandError +from django.utils import timezone from django_q.models import Schedule from django_q.tasks import schedule @@ -13,11 +14,12 @@ class Command(BaseCommand): def handle(self, *args, **options): - date = datetime.datetime.now() + datetime.timedelta(days=1) + date = timezone.now() + datetime.timedelta(days=1) + date = date.replace(hour=0, minute=0, second=0, microsecond=0) schedule('coldfront.core.allocation.tasks.update_statuses', schedule_type=Schedule.DAILY, - next_run=datetime.datetime(date.year, date.month, date.day, 00, 00, 00, 000000)) + next_run=date) schedule('coldfront.core.allocation.tasks.send_expiry_emails', schedule_type=Schedule.DAILY, - next_run=datetime.datetime(date.year, date.month, date.day, 00, 00, 00, 000000)) + next_run=date) diff --git a/coldfront/plugins/freeipa/README.md b/coldfront/plugins/freeipa/README.md index b11c9f209..7512956e5 100644 --- a/coldfront/plugins/freeipa/README.md +++ b/coldfront/plugins/freeipa/README.md @@ -17,7 +17,7 @@ discrepancies. ## Design -FreeIPA unix groups can be set on a per allocation basis using using a +FreeIPA unix groups can be set on a per allocation basis using an allocation attribute named "freeipa\_group". The value of this attribute must be a valid unix group in FreeIPA. Any users added/removed from the allocation will then automatically be added/removed from the group in @@ -62,20 +62,13 @@ $ sss_cache -E ## Usage -To enable this plugin add or uncomment the following in your local\_settings.py -file: +To enable this plugin set the following environment variables: ``` - EXTRA_APPS += [ - 'coldfront.plugin.freeipa', - ] - FREEIPA_NOOP = False - FREEIPA_ENABLE_SIGNALS = False - FREEIPA_KTNAME = '/path/to/user.keytab' - FREEIPA_GROUP_ATTRIBUTE_NAME = 'freeipa_group' - FREEIPA_SERVER = 'freeipa.localhost.localdomain' - FREEIPA_USER_SEARCH_BASE = 'cn=users,cn=accounts,dc=example,dc=edu' - ADDITIONAL_USER_SEARCH_CLASSES = ['coldfront.plugin.freeipa.search.LDAPUserSearch',] +PLUGIN_FREEIPA=True +FREEIPA_KTNAME='/path/to/user.keytab' +FREEIPA_SERVER='freeipa.localhost.localdomain' +FREEIPA_USER_SEARCH_BASE='cn=users,cn=accounts,dc=example,dc=edu' ``` The "FREEIPA\_KTNAME" should be the path to the keytab file for a user in @@ -83,9 +76,7 @@ FreeIPA with the appropriate permissions for modifying group membership. The easiest way to do this in FreeIPA is to create a new role for ColdFront with the "Modify Group membership" privilege. Then create a user account specifically for use with ColdFront and assign them this role. Then export a -keytab for that user. "FREEIPA\_GROUP\_ATTRIBUTE\_NAME" is optional and is the -name of the allocation attribute for the unix group. Default is -"freeipa\_group". +keytab for that user. ## CLI Usage diff --git a/coldfront/plugins/iquota/README.md b/coldfront/plugins/iquota/README.md index f3b5fb48d..fa662a9d5 100644 --- a/coldfront/plugins/iquota/README.md +++ b/coldfront/plugins/iquota/README.md @@ -16,18 +16,12 @@ to authenticate to the API and a valid keytab file is required. ## Usage -To enable this plugin add or uncomment the following in your local\_settings.py -file: +To enable this plugin set the following environment variables: ``` - EXTRA_APPS += [ - 'coldfront.plugins.iquota' - ] - - IQUOTA_KEYTAB = '/path/to/user.keytab' - IQUOTA_CA_CERT = '/etc/ipa/ca.crt' - IQUOTA_API_HOST = 'localhost' - IQUOTA_API_PORT = '8080' - IQUOTA_USER_PATH = '/nfs/user' - IQUOTA_GROUP_PATH = '/nfs/projects' +PLUGIN_IQUOTA=True +IQUOTA_KEYTAB='/path/to/user.keytab' +IQUOTA_CA_CERT='/etc/ipa/ca.crt' +IQUOTA_API_HOST='localhost' +IQUOTA_API_PORT='8080' ``` diff --git a/coldfront/plugins/iquota/utils.py b/coldfront/plugins/iquota/utils.py index 916f9d36d..8ecb3f2d5 100644 --- a/coldfront/plugins/iquota/utils.py +++ b/coldfront/plugins/iquota/utils.py @@ -15,8 +15,6 @@ def __init__(self, username, groups): self.IQUOTA_API_HOST = import_from_settings('IQUOTA_API_HOST') self.IQUOTA_API_PORT = import_from_settings('IQUOTA_API_PORT') self.IQUOTA_CA_CERT = import_from_settings('IQUOTA_CA_CERT') - self.IQUOTA_USER_PATH = import_from_settings('IQUOTA_USER_PATH') - self.IQUOTA_GROUP_PATH = import_from_settings('IQUOTA_GROUP_PATH') self.IQUOTA_KEYTAB = import_from_settings('IQUOTA_KEYTAB') self.username = username self.groups = groups diff --git a/coldfront/plugins/ldap_user_search/README.md b/coldfront/plugins/ldap_user_search/README.md index c19d42b6b..d92278d76 100644 --- a/coldfront/plugins/ldap_user_search/README.md +++ b/coldfront/plugins/ldap_user_search/README.md @@ -21,9 +21,8 @@ in local\_settings.py. ## Usage -To enable this plugin add or uncomment the following in your -local\_settings.py file: +To enable this plugin add the following in your `local_settings.py` file: ``` - ADDITIONAL_USER_SEARCH_CLASSES = ['coldfront.plugins.ldap_user_search.utils.LDAPUserSearch',] +ADDITIONAL_USER_SEARCH_CLASSES = ['coldfront.plugins.ldap_user_search.utils.LDAPUserSearch',] ``` diff --git a/coldfront/plugins/mokey_oidc/README.md b/coldfront/plugins/mokey_oidc/README.md index 65912e0e1..0e5eb10ab 100644 --- a/coldfront/plugins/mokey_oidc/README.md +++ b/coldfront/plugins/mokey_oidc/README.md @@ -21,33 +21,17 @@ Django users. ## Usage -To enable this plugin add or uncomment the following in your -local\_settings.py file: +To enable this plugin set the following environment variables: ``` - EXTRA_APPS += [ - 'mozilla_django_oidc', - 'coldfront.plugins.mokey_oidc', - ] - - EXTRA_AUTHENTICATION_BACKENDS += [ - 'coldfront.plugins.mokey_oidc.auth.OIDCMokeyAuthenticationBackend', - ] - - EXTRA_MIDDLEWARE += [ - 'mozilla_django_oidc.middleware.SessionRefresh', - ] - - OIDC_OP_JWKS_ENDPOINT = "https://hydra.local/.well-known/jwks.json" - OIDC_RP_SIGN_ALGO = 'RS256' - OIDC_RP_CLIENT_ID = 'coldfront-client-id' - OIDC_RP_CLIENT_SECRET = 'xxx' - OIDC_OP_AUTHORIZATION_ENDPOINT = "https://hydra.local/oauth2/auth" - OIDC_OP_TOKEN_ENDPOINT = "https://hydra.local/oauth2/token" - OIDC_OP_USER_ENDPOINT = "https://hydra.local/userinfo" - - # Optional config settings - MOKEY_OIDC_PI_GROUP = 'pi' - MOKEY_OIDC_ALLOWED_GROUPS = ['academic'] - MOKEY_OIDC_DENY_GROUPS = ['badguys'] + +PLUGIN_AUTH_OIDC=True +PLUGIN_MOKEY=True +OIDC_OP_JWKS_ENDPOINT="https://hydra.local/.well-known/jwks.json" +OIDC_RP_SIGN_ALGO='RS256' +OIDC_RP_CLIENT_ID='coldfront-client-id' +OIDC_RP_CLIENT_SECRET='xxx' +OIDC_OP_AUTHORIZATION_ENDPOINT="https://hydra.local/oauth2/auth" +OIDC_OP_TOKEN_ENDPOINT="https://hydra.local/oauth2/token" +OIDC_OP_USER_ENDPOINT="https://hydra.local/userinfo" ``` diff --git a/coldfront/plugins/ondemand/README.md b/coldfront/plugins/ondemand/README.md index b3f5b056a..46758e6a6 100644 --- a/coldfront/plugins/ondemand/README.md +++ b/coldfront/plugins/ondemand/README.md @@ -10,12 +10,10 @@ This plugin allows for resources in ColdFront to be "OnDemand enabled" so users ## Usage -To enable this plugin add or uncomment the following in your local\_settings.py -file and update with your center's OnDemand URL: +To enable this plugin set the following environment variable: ``` -vi coldfront/config/local_settings.py -ONDEMAND_URL = "https://ondemand.example.com" +ONDEMAND_URL="https://ondemand.example.com" ``` To configure a resource as "OnDemand enabled" add the OnDemand attribute to the resource in the ColdFront admin interface as shown here using the sample data: Resource=cluster diff --git a/coldfront/plugins/slurm/README.md b/coldfront/plugins/slurm/README.md index 47842b53f..294bd30e1 100644 --- a/coldfront/plugins/slurm/README.md +++ b/coldfront/plugins/slurm/README.md @@ -33,15 +33,11 @@ specifications on an individual user basis is not currently supported. ## Usage -To enable this plugin add or uncomment the following in your local\_settings.py -file: +To enable this plugin set the following environment variables: ``` - EXTRA_APPS += [ - 'coldfront.plugins.slurm', - ] - SLURM_NOOP = False - SLURM_SACCTMGR_PATH = '/usr/bin/sacctmgr' +PLUGIN_SLURM=True +SLURM_SACCTMGR_PATH='/usr/bin/sacctmgr' ``` To generate Slurm association data from ColdFront run the following command: diff --git a/coldfront/plugins/xdmod/README.md b/coldfront/plugins/xdmod/README.md index 86be604b6..4bca21a05 100644 --- a/coldfront/plugins/xdmod/README.md +++ b/coldfront/plugins/xdmod/README.md @@ -14,14 +14,11 @@ Cloud core time. ## Usage -To enable this plugin add or uncomment the following in your local\_settings.py -file: +To enable this plugin set the following environment variables: ``` - EXTRA_APPS += [ - 'coldfront.plugins.xdmod', - ] - XDMOD_API_URL = 'https://localhost' +PLUGIN_XDMOD=True +XDMOD_API_URL='https://localhost' ``` ## CLI Usage diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 77e81f32c..3f09131f7 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -20,6 +20,10 @@ theme: - navigation.tabs.sticky - navigation.indexes +plugins: + - search + - awesome-pages + markdown_extensions: - footnotes - admonition diff --git a/docs/pages/.pages b/docs/pages/.pages new file mode 100644 index 000000000..74b32faff --- /dev/null +++ b/docs/pages/.pages @@ -0,0 +1,8 @@ +nav: + - index.md + - install.md + - config.md + - upgrading.md + - plugin.md + - Deployment: deploy.md + - manual diff --git a/docs/pages/config.md b/docs/pages/config.md new file mode 100644 index 000000000..3be90e43e --- /dev/null +++ b/docs/pages/config.md @@ -0,0 +1,278 @@ +# Configuration + +ColdFront can be configured via environment variables, an environment file, or +a python file which can be used for more advanced configuration settings. This +document covers the configuration settings available in ColdFront and the core +plugins. For information on installing ColdFront [see here](install.md). + +## Configuration files + +You can set environment variables via a file and ColdFront by default will look +for the following files: + +- `.env` in the ColdFront project root +- `/etc/coldfront/coldfront.env` + +You can also specify the path to an environment file using the `COLDFRONT_ENV` +environment variable. For example + +``` +COLDFRONT_ENV=/opt/coldfront/coldfront.env +``` + +For more advanced configurations, you can create a python file to override +ColdFront settings: + +- `local_settings.py` relative to coldfront.config package +- `/etc/coldfront/local_settings.py` +- `local_settings.py` in the ColdFront project root + +You can also specify the path to `local_settings.py` using the +`COLDFRONT_CONFIG` environment variable. For example: + +``` +COLDFRONT_CONFIG=/opt/coldfront/mysettings.py +``` + +## Simple Example + +Here's a very simple example demonstrating how to configure ColdFront using +environment variables: + +``` +$ tee coldfront.env <= 3.8.3 from source since it is absent from the CentOS software repositories -wget https://sqlite.org/2020/sqlite-autoconf-XXXXXXX.tar.gz -tar -xzf sqlite-autoconf-XXXXXXX -cd sqlite-autoconf-XXXXXXX -./configure -make -make install - -#Check version -sqlite3 --version -``` - - -##### CentOS 8 - -Install EPEL then install required packages: - -``` -sudo yum install epel-release -sudo yum install python36 python36-devel memcached redis -``` - -##### Ubuntu (16.04) -``` -sudo add-apt-repository ppa:jonathonf/python-3.6 -sudo apt-get update -sudo apt-get install python3.6 python3.6-venv memcached redis-server -``` - -#### Step 2 Clone ColdFront -Clone ColdFront github repo in a new directory and create a Python virtual environment for ColdFront -``` -mkdir coldfront_app -cd coldfront_app -git clone https://github.com/ubccr/coldfront.git -python3.6 -mvenv venv -``` - -#### Step 3 - Install required Python packages -Activate the virtual environment and install the required Python packages -``` -source venv/bin/activate -cd coldfront -pip install --upgrade pip -pip install wheel -pip install -r requirements.txt -``` - -#### Step 4 - ColdFront base settings -Copy coldfront/config/local_settings.py.sample to coldfront/config/local_settings.py -``` -cp coldfront/config/local_settings.py.sample coldfront/config/local_settings.py -``` -Open coldfront/config/local_settings.py: -Update `SECRET_KEY` - Consider making the length at least 50 characters long -Update `TIME_ZONE` if necessary - - -#### Step 5 - Coldfront optional configuration -Copy config/local_strings.py.sample to config/local_strings.py and update if desired. -``` -cp coldfront/config/local_strings.py.sample coldfront/config/local_strings.py -``` - -#### Step 6 - Run initial setup -Run initial setup - This will create the necessary tables in the ColdFront database. -``` -python manage.py initial_setup -``` -!!! tip "Optional" - Load in some test data for testing out ColdFront features by running - `coldfront load_test_data` - -#### Step 7 - Start development server -``` -python manage.py runserver 0.0.0.0:8000 -``` - -#### Step 8 - Login -Point your browser to http://localhost:8000 - -- You can log in as `admin` with password `test1234`. -- You can log in as a PI using username `ccollins` with password `test1234`. -- You can log in as center director using username `michardson` with password `test1234`. -- Password for all users is also `test1234`. diff --git a/docs/pages/upgrading.md b/docs/pages/upgrading.md new file mode 100644 index 000000000..ecd4176e5 --- /dev/null +++ b/docs/pages/upgrading.md @@ -0,0 +1,45 @@ +# Upgrading + +This document describes upgrading ColdFront. New releases of ColdFront may +introduce breaking changes so please refer to this document before upgrading. + +## v1.0.3 + +This release changed the way ColdFront is configured. Before, there were two +files `local_settings.py` and `local_strings.py` that were used for custom +configuration settings. This release now uses environment variables. For more +details please see the documentation on [configuring ColdFront](config.md). +After upgrading to `v1.0.3` you'll need to migrate any custom configs to use +environment variables or modify your existing `local_settings.py` to conform to +the new settings. Here's a simple example of a `local_settings.py` file prior +to `v1.0.3`: + +```python +EXTRA_APPS += [ + 'coldfront.plugins.slurm', +] +SLURM_IGNORE_USERS = ['root'] +SLURM_SACCTMGR_PATH = '/usr/bin/sacctmgr' +``` + +After upgrading to `v1.0.3` you'll need to modify your `local_settings.py` file +as follows: + +```python +from coldfront.config.base import INSTALLED_APPS + +INSTALLED_APPS += [ + 'coldfront.plugins.slurm', +] + +SLURM_IGNORE_USERS = ['root'] +SLURM_SACCTMGR_PATH = '/usr/bin/sacctmgr' +``` + +Or change to using environment variables: + +``` +PLUGIN_SLURM=True +SLURM_IGNORE_USERS=root,admin,testuser +SLURM_SACCTMGR_PATH=/usr/bin/sacctmgr +``` diff --git a/docs/requirements.txt b/docs/requirements.txt index b26a76d49..f1cb34d54 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -6,6 +6,7 @@ MarkupSafe==1.1.1 mkdocs==1.1.2 mkdocs-material==7.0.2 mkdocs-minify-plugin==0.2.1 +mkdocs-awesome-pages-plugin==2.5.0 Pygments==2.5.2 pymdown-extensions==8.1.1 PyYAML==5.3 diff --git a/requirements.txt b/requirements.txt index e5f498c95..d308d35cf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,11 +4,13 @@ blessed==1.15.0 chardet==3.0.4 Django==2.2.13 django-crispy-forms==1.7.2 +django-environ==0.4.5 django-model-utils==3.1.2 django-picklefield==2.0 django-q==1.0.1 django-settings-export==1.2.1 django-simple-history==2.7.2 +django-split-settings==1.0.1 django-sslserver==0.20 django-su==0.8.0 doi2bib==0.3.0 diff --git a/setup.py b/setup.py index ec64d70d4..af0c98469 100644 --- a/setup.py +++ b/setup.py @@ -1,13 +1,14 @@ #!/usr/bin/env python from setuptools import setup, find_packages +import coldfront with open("README.md", "r") as fh: long_description = fh.read() setup( name='coldfront', - version='1.0.2', + version=coldfront.VERSION, description='HPC Resource Allocation System ', long_description=long_description, long_description_content_type="text/markdown", @@ -20,6 +21,7 @@ }, author='Andrew E. Bruno, Dori Sajdak, Mohammad Zia', license='GNU General Public License v3 (GPLv3)', + python_requires='>=3.6', packages=find_packages(), install_requires=[ 'arrow==0.13.1', @@ -28,11 +30,13 @@ 'chardet==3.0.4', 'Django==2.2.13', 'django-crispy-forms==1.7.2', + 'django-environ==0.4.5', 'django-model-utils==3.1.2', 'django-picklefield==2.0', 'django-q==1.0.1', 'django-settings-export==1.2.1', 'django-simple-history==2.7.2', + 'django-split-settings==1.0.1', 'django-sslserver==0.20', 'django-su==0.8.0', 'doi2bib==0.3.0',