You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ma...@apache.org on 2021/06/14 20:09:06 UTC
[airavata-django-portal] 01/04: AIRAVATA-3383 settings_local.py
download
This is an automated email from the ASF dual-hosted git repository.
machristie pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git
commit 8cce97c2b08bc672c9939491e95b82766e9e46f4
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Mon Jun 14 15:58:51 2021 -0400
AIRAVATA-3383 settings_local.py download
---
django_airavata/apps/admin/apps.py | 8 ++
.../components/developers/DevelopersContainer.vue | 18 +++
.../static/django_airavata_admin/src/router.js | 8 +-
django_airavata/apps/admin/urls.py | 2 +
django_airavata/apps/admin/views.py | 6 +
.../settings_local.py.template | 137 +++++++++++++++++++++
django_airavata/apps/auth/urls.py | 2 +
django_airavata/apps/auth/views.py | 99 ++++++++++++++-
8 files changed, 277 insertions(+), 3 deletions(-)
diff --git a/django_airavata/apps/admin/apps.py b/django_airavata/apps/admin/apps.py
index 5501733..82876b9 100644
--- a/django_airavata/apps/admin/apps.py
+++ b/django_airavata/apps/admin/apps.py
@@ -65,4 +65,12 @@ class AdminConfig(AiravataAppConfig):
'enabled': lambda req: (req.is_gateway_admin or
req.is_read_only_gateway_admin)
},
+ {
+ 'label': 'Developer Console',
+ 'icon': 'fa fa-code',
+ 'url': 'django_airavata_admin:developers',
+ 'active_prefixes': ['developers'],
+ 'enabled': lambda req: (req.is_gateway_admin or
+ req.is_read_only_gateway_admin)
+ },
]
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/developers/DevelopersContainer.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/developers/DevelopersContainer.vue
new file mode 100644
index 0000000..674676f
--- /dev/null
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/developers/DevelopersContainer.vue
@@ -0,0 +1,18 @@
+<template>
+ <div>
+ <div class="row">
+ <div class="col">
+ <h1 class="h4 mb-4">Developer Console</h1>
+ </div>
+ </div>
+ <b-card header="Download a settings_local.py file for local development">
+ <b-link href="/auth/settings-local">
+ <i class="fas fa-download"></i>
+ Download
+ </b-link>
+ </b-card>
+ </div>
+</template>
+<script>
+export default {};
+</script>
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/router.js b/django_airavata/apps/admin/static/django_airavata_admin/src/router.js
index 6d803b5..de0c0d4 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/router.js
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/router.js
@@ -6,8 +6,9 @@ import ApplicationModuleEditor from "./components/applications/ApplicationModule
import ApplicationsDashboard from "./components/dashboards/ApplicationsDashboard.vue";
import ComputePreference from "./components/admin/group_resource_preferences/ComputePreference";
import ComputeResourcePreferenceDashboard from "./components/dashboards/ComputeResourcePreferenceDashboard";
-import ExperimentStatisticsContainer from "./components/statistics/ExperimentStatisticsContainer";
import CredentialStoreDashboard from "./components/dashboards/CredentialStoreDashboard";
+import DevelopersContainer from "./components/developers//DevelopersContainer.vue";
+import ExperimentStatisticsContainer from "./components/statistics/ExperimentStatisticsContainer";
import GatewayResourceProfileEditorContainer from "./components/gatewayprofile/GatewayResourceProfileEditorContainer.vue";
import GroupComputeResourcePreference from "./components/admin/group_resource_preferences/GroupComputeResourcePreference";
import IdentityServiceUserManagementContainer from "./components/users/IdentityServiceUserManagementContainer.vue";
@@ -149,6 +150,11 @@ const routes = [
component: ExperimentStatisticsContainer,
name: "experiment-statistics",
},
+ {
+ path: "/developers",
+ component: DevelopersContainer,
+ name: "developers",
+ }
];
export default new VueRouter({
mode: "history",
diff --git a/django_airavata/apps/admin/urls.py b/django_airavata/apps/admin/urls.py
index 2c5d481..9404e64 100644
--- a/django_airavata/apps/admin/urls.py
+++ b/django_airavata/apps/admin/urls.py
@@ -1,4 +1,5 @@
from django.conf.urls import url
+from django.urls import path
from . import views
@@ -15,4 +16,5 @@ urlpatterns = [
name='gateway_resource_profile'),
url(r'^notices/', views.notices, name='notices'),
url(r'^users/', views.users, name='users'),
+ path('developers/', views.developers, name='developers'),
]
diff --git a/django_airavata/apps/admin/views.py b/django_airavata/apps/admin/views.py
index 59ed496..caca63a 100644
--- a/django_airavata/apps/admin/views.py
+++ b/django_airavata/apps/admin/views.py
@@ -57,3 +57,9 @@ def users(request):
def experiment_statistics(request):
request.active_nav_item = 'experiment-statistics'
return render(request, 'admin/admin_base.html')
+
+
+@login_required
+def developers(request):
+ request.active_nav_item = 'developers'
+ return render(request, 'admin/admin_base.html')
diff --git a/django_airavata/apps/auth/templates/django_airavata_auth/settings_local.py.template b/django_airavata/apps/auth/templates/django_airavata_auth/settings_local.py.template
new file mode 100644
index 0000000..cdda4ce
--- /dev/null
+++ b/django_airavata/apps/auth/templates/django_airavata_auth/settings_local.py.template
@@ -0,0 +1,137 @@
+"""
+Override default Django settings for a particular instance.
+
+Copy this file to settings_local.py and modify as appropriate. This file will
+be imported into settings.py last of all so settings in this file override any
+defaults specified in settings.py.
+"""
+
+{% autoescape off %}
+import os
+
+# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
+BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+# Django - general settings
+# Uncomment and specify for production deployments
+# DEBUG = False
+# STATIC_ROOT = "/var/www/path/to/sitename/static/"
+# ALLOWED_HOSTS = ['production.hostname']
+
+# Django - database settings
+# MySQL - to use MySQL, uncomment and specify the settings
+# DATABASES = {
+# 'default': {
+# 'ENGINE': 'django.db.backends.mysql',
+# 'NAME': '...',
+# 'HOST': '...',
+# 'USER': '...',
+# 'PASSWORD': '...',
+# 'OPTIONS': {
+# 'init_command': 'SET default_storage_engine=INNODB,collation_connection=utf8_bin',
+# }
+# }
+
+# Django - Email settings
+# Uncomment and specify the following for sending emails (default email backend
+# just prints to the console)
+# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
+# EMAIL_HOST = '...'
+# EMAIL_PORT = '...'
+# EMAIL_HOST_USER = '...'
+# EMAIL_HOST_PASSWORD = '...'
+# EMAIL_USE_TLS = True
+# ADMINS receive error emails
+# ADMINS = [('Admin Name', 'admin@example.com')]
+# Optional: PORTAL_ADMINS receive administrative emails, like when a new user is created
+# This can be set to a different value than ADMINS so that the PORTAL_ADMINS
+# don't receive error emails. Defaults to the same value as ADMINS.
+# PORTAL_ADMINS = ADMINS
+# SERVER_EMAIL = 'portal@example.com'
+
+# Keycloak Configuration
+KEYCLOAK_CLIENT_ID = '{{ keycloak_client_id }}'
+KEYCLOAK_CLIENT_SECRET = '{{ keycloak_client_secret }}'
+KEYCLOAK_AUTHORIZE_URL = '{{ KEYCLOAK_AUTHORIZE_URL }}'
+KEYCLOAK_TOKEN_URL = '{{ KEYCLOAK_TOKEN_URL }}'
+KEYCLOAK_USERINFO_URL = '{{ KEYCLOAK_USERINFO_URL }}'
+KEYCLOAK_LOGOUT_URL = '{{ KEYCLOAK_LOGOUT_URL }}'
+# Optional: specify if using self-signed certificate or certificate from unrecognized CA
+#KEYCLOAK_CA_CERTFILE = os.path.join(BASE_DIR, "django_airavata", "resources", "incommon_rsa_server_ca.pem")
+KEYCLOAK_VERIFY_SSL = True
+
+AUTHENTICATION_OPTIONS = {{ AUTHENTICATION_OPTIONS }}
+
+# Airavata API Configuration
+GATEWAY_ID = '{{ GATEWAY_ID }}'
+AIRAVATA_API_HOST = '{{ AIRAVATA_API_HOST }}'
+AIRAVATA_API_PORT = {{ AIRAVATA_API_PORT }}
+AIRAVATA_API_SECURE = {{ AIRAVATA_API_SECURE }}
+GATEWAY_DATA_STORE_RESOURCE_ID = '{{ GATEWAY_DATA_STORE_RESOURCE_ID }}'
+GATEWAY_DATA_STORE_DIR = '/tmp'
+GATEWAY_DATA_STORE_REMOTE_API = '{{ GATEWAY_DATA_STORE_REMOTE_API }}'
+# FILE_UPLOAD_TEMP_DIR = '/tmp'
+
+# Profile Service Configuration
+PROFILE_SERVICE_HOST = '{{ PROFILE_SERVICE_HOST }}'
+PROFILE_SERVICE_PORT = {{ PROFILE_SERVICE_PORT }}
+PROFILE_SERVICE_SECURE = {{ PROFILE_SERVICE_SECURE }}
+
+# Portal settings
+PORTAL_TITLE = '{{ PORTAL_TITLE }}'
+
+# Tus upload - uncomment the following to enable tus uploads
+# Override and set to a valid tus endpoint
+# TUS_ENDPOINT = "https://tus.domainname.org/files/"
+# Override and set to the directory where tus uploads will be stored.
+# TUS_DATA_DIR = "/path/to/tus-temp-dir"
+
+# Legacy (PGA) Portal link - uncomment to provide a link to the legacy portal
+# PGA_URL = '...'
+
+# Google Analytics Tracking ID ("UA-XXXXXXXX-X"). If this setting is set, then
+# Google Analytics tracking will be added to all pages.
+# GOOGLE_ANALYTICS_TRACKING_ID = '...'
+
+# Logging configuration. Uncomment following to override default log configuration
+# LOGGING = {
+# 'version': 1,
+# 'disable_existing_loggers': False,
+# 'filters': {
+# 'require_debug_false': {
+# '()': 'django.utils.log.RequireDebugFalse',
+# },
+# 'require_debug_true': {
+# '()': 'django.utils.log.RequireDebugTrue',
+# },
+# },
+# 'formatters': {
+# 'verbose': {
+# 'format': '[%(asctime)s %(name)s:%(lineno)d %(levelname)s] %(message)s'
+# },
+# },
+# 'handlers': {
+# 'console': {
+# 'class': 'logging.StreamHandler',
+# 'formatter': 'verbose'
+# },
+# 'mail_admins': {
+# 'filters': ['require_debug_false'],
+# 'level': 'ERROR',
+# 'class': 'django.utils.log.AdminEmailHandler',
+# 'include_html': True,
+# }
+# },
+# 'loggers': {
+# 'django_airavata': {
+# 'handlers': ['console', 'mail_admins'],
+# 'level': 'DEBUG' if DEBUG else 'INFO'
+# },
+# 'root': {
+# 'handlers': ['console', 'mail_admins'],
+# 'level': 'WARNING'
+# }
+# },
+# }
+
+{% endautoescape %}
diff --git a/django_airavata/apps/auth/urls.py b/django_airavata/apps/auth/urls.py
index b5d874d..25454ad 100644
--- a/django_airavata/apps/auth/urls.py
+++ b/django_airavata/apps/auth/urls.py
@@ -1,5 +1,6 @@
from django.conf.urls import url
+from django.urls import path
from . import views
@@ -28,4 +29,5 @@ urlpatterns = [
views.login_desktop_success, name="login_desktop_success"),
url(r'^refreshed-token-desktop$', views.refreshed_token_desktop,
name="refreshed_token_desktop"),
+ path('settings-local/', views.download_settings_local, name="download_settings_local"),
]
diff --git a/django_airavata/apps/auth/views.py b/django_airavata/apps/auth/views.py
index 0fe55b0..f005a67 100644
--- a/django_airavata/apps/auth/views.py
+++ b/django_airavata/apps/auth/views.py
@@ -1,16 +1,20 @@
+import io
import logging
import time
from datetime import datetime, timedelta, timezone
from urllib.parse import quote, urlencode
+import requests
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import authenticate, login, logout
-from django.core.exceptions import ObjectDoesNotExist
+from django.contrib.auth.decorators import login_required
+from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
from django.forms import ValidationError
-from django.http import HttpResponseBadRequest, JsonResponse
+from django.http import FileResponse, HttpResponseBadRequest, JsonResponse
from django.shortcuts import redirect, render, resolve_url
from django.template import Context
+from django.template.loader import render_to_string
from django.urls import reverse
from django.views.decorators.debug import sensitive_variables
from requests_oauthlib import OAuth2Session
@@ -502,3 +506,94 @@ def _create_login_desktop_failed_response(request, idp_alias=None):
params['username'] = request.POST['username']
return redirect(reverse('django_airavata_auth:login_desktop') +
"?" + urlencode(params))
+
+
+@login_required
+def download_settings_local(request):
+
+ if not request.is_gateway_admin or not request.is_read_only_gateway_admin:
+ raise PermissionDenied()
+
+ if settings.DEBUG:
+ raise Exception("Downloading a settings_local.py file isn't allowed in DEBUG mode.")
+
+ development_client_id = f"local-django-{request.user.username}"
+ access_token = utils.get_service_account_authz_token().accessToken
+ clients_endpoint = get_clients_endpoint()
+ development_client = get_client(access_token, clients_endpoint, development_client_id)
+ if development_client is None:
+ development_client_endpoint = create_client(access_token, clients_endpoint, development_client_id)
+ else:
+ development_client_endpoint = get_client_endpoint(development_client)
+ development_client_secret = get_client_secret(access_token, development_client_endpoint)
+
+ context = {}
+ context['AUTHENTICATION_OPTIONS'] = settings.AUTHENTICATION_OPTIONS
+ context['keycloak_client_id'] = development_client_id
+ context['keycloak_client_secret'] = development_client_secret
+ context['KEYCLOAK_AUTHORIZE_URL'] = settings.KEYCLOAK_AUTHORIZE_URL
+ context['KEYCLOAK_TOKEN_URL'] = settings.KEYCLOAK_TOKEN_URL
+ context['KEYCLOAK_USERINFO_URL'] = settings.KEYCLOAK_USERINFO_URL
+ context['KEYCLOAK_LOGOUT_URL'] = settings.KEYCLOAK_LOGOUT_URL
+ context['GATEWAY_ID'] = settings.GATEWAY_ID
+ context['AIRAVATA_API_HOST'] = settings.AIRAVATA_API_HOST
+ context['AIRAVATA_API_PORT'] = settings.AIRAVATA_API_PORT
+ context['AIRAVATA_API_SECURE'] = settings.AIRAVATA_API_SECURE
+ context['GATEWAY_DATA_STORE_RESOURCE_ID'] = settings.GATEWAY_DATA_STORE_RESOURCE_ID
+ if hasattr(settings, 'GATEWAY_DATA_STORE_REMOTE_API'):
+ context['GATEWAY_DATA_STORE_REMOTE_API'] = settings.GATEWAY_DATA_STORE_REMOTE_API
+ else:
+ context['GATEWAY_DATA_STORE_REMOTE_API'] = request.build_absolute_uri("/api")
+ context['PROFILE_SERVICE_HOST'] = settings.PROFILE_SERVICE_HOST
+ context['PROFILE_SERVICE_PORT'] = settings.PROFILE_SERVICE_PORT
+ context['PROFILE_SERVICE_SECURE'] = settings.PROFILE_SERVICE_SECURE
+ context['PORTAL_TITLE'] = settings.PORTAL_TITLE
+ settings_local_str = render_to_string("django_airavata_auth/settings_local.py.template", context)
+ settings_local_bytesio = io.BytesIO(settings_local_str.encode())
+ return FileResponse(settings_local_bytesio, as_attachment=True, filename="settings_local.py")
+
+
+def get_client(access_token, clients_endpoint, client_id):
+ headers = {'Authorization': f'Bearer {access_token}', 'Content-Type': 'application/json'}
+ r = requests.get(clients_endpoint, {'clientId': client_id}, headers=headers)
+ r.raise_for_status()
+ clients = r.json()
+ if len(clients) == 0:
+ return None
+ else:
+ return clients[0]
+
+
+def get_clients_endpoint():
+ realm = settings.GATEWAY_ID
+ clients_endpoint = f"https://iamdev.scigap.org/auth/admin/realms/{realm}/clients"
+ return clients_endpoint
+
+
+def get_client_endpoint(client):
+ return f"{get_clients_endpoint()}/{client['id']}"
+
+
+def create_client(access_token, clients_endpoint, client_id):
+ client = {
+ 'clientId': client_id,
+ "redirectUris": [
+ "http://localhost:8000/",
+ "http://localhost:8000/auth/callback*",
+ "http://127.0.0.1:8000/",
+ "http://127.0.0.1:8000/auth/callback*"
+ ],
+ "directAccessGrantsEnabled": True
+ }
+ headers = {'Authorization': f'Bearer {access_token}', 'Content-Type': 'application/json'}
+ r = requests.post(clients_endpoint, json=client, headers=headers)
+ r.raise_for_status()
+ return r.headers['Location']
+
+
+def get_client_secret(access_token, client_endpoint):
+
+ headers = {'Authorization': f'Bearer {access_token}'}
+ r = requests.get(client_endpoint + "/client-secret", headers=headers)
+ r.raise_for_status()
+ return r.json()['value']