mirror of
https://github.com/OpenBankProject/API-Manager.git
synced 2026-02-06 18:56:48 +00:00
Merge remote-tracking branch 'OpenBankProject/develop' into develop
This commit is contained in:
commit
3491f17e75
@ -15,7 +15,6 @@ import os
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.urls import reverse_lazy
|
||||
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
@ -77,6 +76,7 @@ INSTALLED_APPS = [
|
||||
|
||||
MIDDLEWARE = [
|
||||
# 'django.middleware.cache.UpdateCacheMiddleware',
|
||||
'csp.middleware.CSPMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
@ -123,7 +123,8 @@ TEMPLATES = [
|
||||
'base.context_processors.api_tester_url',
|
||||
'base.context_processors.portal_page',
|
||||
'base.context_processors.logo_url',
|
||||
'base.context_processors.override_css_url'
|
||||
'base.context_processors.override_css_url',
|
||||
'csp.context_processors.nonce'
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -268,6 +269,16 @@ SHOW_API_TESTER = False
|
||||
# Always save session$
|
||||
SESSION_SAVE_EVERY_REQUEST = True
|
||||
|
||||
# Session Cookie Settings
|
||||
SESSION_COOKIE_SECURE = True
|
||||
SESSION_COOKIE_HTTPONLY = True
|
||||
SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"
|
||||
SESSION_COOKIE_AGE = 300
|
||||
|
||||
# CSRF Cookie Settings
|
||||
CSRF_COOKIE_HTTPONLY = True
|
||||
CSRF_COOKIE_SECURE = True
|
||||
|
||||
# Paths on API_HOST to OAuth
|
||||
OAUTH_TOKEN_PATH = '/oauth/initiate'
|
||||
OAUTH_AUTHORIZATION_PATH = '/oauth/authorize'
|
||||
@ -308,6 +319,8 @@ CALLBACK_BASE_URL = ""
|
||||
# Global
|
||||
UNDEFINED = "<undefined>"
|
||||
|
||||
API_ROOT_KEY = "v500"
|
||||
|
||||
# Local settings can replace any value ABOVE
|
||||
try:
|
||||
from apimanager.local_settings import * # noqa
|
||||
@ -330,3 +343,19 @@ if not OAUTH_CONSUMER_KEY:
|
||||
raise ImproperlyConfigured('Missing settings for OAUTH_CONSUMER_KEY')
|
||||
if not OAUTH_CONSUMER_SECRET:
|
||||
raise ImproperlyConfigured('Missing settings for OAUTH_CONSUMER_SECRET')
|
||||
|
||||
#This has been moved to after API_HOST is imported so that connections to the API are allowed by the csp
|
||||
# Content Security Policy - External Urls for scripts, styles, and images should be included here
|
||||
#TODO these outside scripts should really just be loaded when we run "manage.py collectstatic"
|
||||
# Or the whole static folder could be uploaded to github, this prevents API manager breaking when
|
||||
# we run it on a server that may not connect to these sites
|
||||
|
||||
# Inline styles loaded by jsoneditor.min.js have been allowed by adding their hashes to CSP_STYLE_SRC
|
||||
|
||||
CSP_IMG_SRC = ("'self' data:", 'https://static.openbankproject.com')
|
||||
CSP_STYLE_SRC = ("'self' 'sha256-z2a+NIknPDE7NIEqE1lfrnG39eWOhJXWsXHYGGNb5oU=' 'sha256-Dn0vMZLidJplZ4cSlBMg/F5aa7Vol9dBMHzBF4fGEtk=' 'sha256-sA0hymKbXmMTpnYi15KmDw4u6uRdLXqHyoYIaORFtjU=' 'sha256-jUuiwf3ITuJc/jfynxWHLwTZifHIlhddD8NPmmVBztk=' 'sha256-RqzjtXRBqP4i+ruV3IRuHFq6eGIACITqGbu05VSVXsI='", 'https://cdnjs.cloudflare.com', )
|
||||
CSP_SCRIPT_SRC = ("'self' 'unsafe-eval' 'sha256-CAykt4V7LQN6lEkjV8hZQx0GV6LTZZGUvQDqamuUq2Q=' 'sha256-4Hr8ttnXaUA4A6o0hGi3NUGNP2Is3Ep0W+rvm+W7BAk=' 'sha256-GgQWQ4Ejk4g9XpAZJ4YxIgZDgp7CdQCmqjMOMh9hD2g=' 'sha256-05NIAwVBHkAzKcXTfkYqTnBPtkpX+AmQvM/raql3qo0='", 'http://code.jquery.com', 'https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/', 'https://cdnjs.cloudflare.com')
|
||||
CSP_FONT_SRC = ("'self'", 'http://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/fonts/')
|
||||
CSP_FRAME_ANCESTORS = ("'self'")
|
||||
CSP_FORM_ACTION = ("'self'")
|
||||
CSP_CONNECT_SRC = ("'self'", API_HOST)
|
||||
|
||||
@ -31,6 +31,9 @@ footer a:hover, .footer a:focus {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.footer-content-wrapper {
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.navbar-brand img {
|
||||
height: 20px;
|
||||
@ -74,6 +77,20 @@ footer a:hover, .footer a:focus {
|
||||
margin-top: -6px;
|
||||
}
|
||||
|
||||
.navbar-inner {
|
||||
margin-left:15% !important;
|
||||
}
|
||||
|
||||
.navbar-nav {
|
||||
margin-left:8rem;
|
||||
}
|
||||
|
||||
.obp-home-button {
|
||||
position:absolute;
|
||||
margin-left: -70px !important;
|
||||
top:-5px;
|
||||
}
|
||||
|
||||
/*.dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:active {*/
|
||||
/*background-color: #53c4ef;*/
|
||||
/*}*/
|
||||
@ -211,6 +228,12 @@ table.tablesorter thead tr .headerSortDown, table.tablesorter thead tr .headerSo
|
||||
margin-left:5rem;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.language-select > a {
|
||||
color:#fff;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
#uk {
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
1314
apimanager/base/static/css/jquery-ui.css
vendored
Normal file
1314
apimanager/base/static/css/jquery-ui.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
12
apimanager/base/static/js/bootstrap.min.js
vendored
12
apimanager/base/static/js/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
41
apimanager/base/static/js/inactivity-timer.js
Normal file
41
apimanager/base/static/js/inactivity-timer.js
Normal file
@ -0,0 +1,41 @@
|
||||
function addSeconds(date, seconds) {
|
||||
let oldDate = date;
|
||||
let addSeconds = seconds;
|
||||
var newSeconds = oldDate + addSeconds;
|
||||
//console.log(addSeconds);
|
||||
date.setSeconds(date.getSeconds() + seconds);
|
||||
return date;
|
||||
}
|
||||
|
||||
export function showCountdownTimer() {
|
||||
//TODO rather than display a timer the whole time in a span, make it only show when there are e.g. 30 seconds left.
|
||||
// Maybe a whole page alert that the user will be logged out soon.
|
||||
|
||||
// Get current date and time
|
||||
var now = new Date().getTime();
|
||||
let distance = countDownDate - now;
|
||||
// Output the result in an element with id="countdown-timer-span"
|
||||
let elementId = ("countdown-timer-span");
|
||||
document.getElementById(elementId).innerHTML = Math.floor(distance / 1000) + "s";
|
||||
|
||||
// If the count down is over release resources
|
||||
if (distance < 0) {
|
||||
destroyCountdownTimer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Set the date we're counting down to
|
||||
let countDownDate = addSeconds(new Date(), 5);
|
||||
|
||||
let showTimerInterval = null;
|
||||
|
||||
export function destroyCountdownTimer() {
|
||||
clearInterval(showTimerInterval);
|
||||
}
|
||||
|
||||
export function resetCountdownTimer(seconds) {
|
||||
destroyCountdownTimer(); // Destroy previous timer if any
|
||||
countDownDate = addSeconds(new Date(), seconds); // Set the date we're counting down to
|
||||
showTimerInterval = setInterval(showCountdownTimer, 1000); // Update the count down every 1 second
|
||||
}
|
||||
107
apimanager/base/static/js/inactivity.js
Normal file
107
apimanager/base/static/js/inactivity.js
Normal file
@ -0,0 +1,107 @@
|
||||
import * as countdownTimer from './inactivity-timer.js'
|
||||
|
||||
// holds the idle duration in ms (current value = 301 seconds)
|
||||
var timeoutIntervalInMillis = 5 * 60 * 1000 + 1000;
|
||||
// holds the timeout variables for easy destruction and reconstruction of the setTimeout hooks
|
||||
var timeHook = null;
|
||||
|
||||
function initializeTimeHook() {
|
||||
// this method has the purpose of creating our timehooks and scheduling the call to our logout function when the idle time has been reached
|
||||
if (timeHook == null) {
|
||||
timeHook = setTimeout( function () { destroyTimeHook(); logout()}.bind(this), timeoutIntervalInMillis);
|
||||
}
|
||||
}
|
||||
|
||||
function destroyTimeHook() {
|
||||
// this method has the sole purpose of destroying any time hooks we might have created
|
||||
clearTimeout(timeHook);
|
||||
timeHook = null;
|
||||
}
|
||||
|
||||
function resetTimeHook(event) {
|
||||
// this method replaces the current time hook with a new time hook
|
||||
destroyTimeHook();
|
||||
initializeTimeHook();
|
||||
countdownTimer.resetCountdownTimer(timeoutIntervalInMillis / 1000);
|
||||
// show event type, element and coordinates of the click
|
||||
// console.log(event.type + " at " + event.currentTarget);
|
||||
// console.log("Coordinates: " + event.clientX + ":" + event.clientY);
|
||||
console.log("Reset inactivity of a user");
|
||||
}
|
||||
|
||||
function setupListeners() {
|
||||
// here we setup the event listener for the mouse click operation
|
||||
document.addEventListener("click", resetTimeHook);
|
||||
document.addEventListener("mousemove", resetTimeHook);
|
||||
document.addEventListener("mousedown", resetTimeHook);
|
||||
document.addEventListener("keypress", resetTimeHook);
|
||||
document.addEventListener("touchmove", resetTimeHook);
|
||||
console.log("Listeners for user inactivity activated");
|
||||
}
|
||||
|
||||
function destroyListeners() {
|
||||
// here we destroy event listeners for the mouse click operation
|
||||
document.removeEventListener("click", resetTimeHook);
|
||||
document.removeEventListener("mousemove", resetTimeHook);
|
||||
document.removeEventListener("mousedown", resetTimeHook);
|
||||
document.removeEventListener("keypress", resetTimeHook);
|
||||
document.removeEventListener("touchmove", resetTimeHook);
|
||||
console.log("Listeners for user inactivity deactivated");
|
||||
}
|
||||
|
||||
function logout() {
|
||||
destroyListeners();
|
||||
countdownTimer.destroyCountdownTimer();
|
||||
console.log("Logging you out due to inactivity..");
|
||||
const logoffButton = document.getElementById("logout");
|
||||
logoffButton.click();
|
||||
}
|
||||
|
||||
async function makeObpApiCall() {
|
||||
let timeoutInSeconds;
|
||||
try {
|
||||
let obpApiHost = document.getElementById("api_home_link");
|
||||
if(obpApiHost) {
|
||||
obpApiHost = obpApiHost.href.split("?")[0];
|
||||
}
|
||||
console.log(obpApiHost);
|
||||
const response = await fetch(`${obpApiHost}/obp/v5.1.0/ui/suggested-session-timeout`);
|
||||
const json = await response.json();
|
||||
if(json.timeout_in_seconds) {
|
||||
timeoutInSeconds = json.timeout_in_seconds;
|
||||
console.log(`Suggested value ${timeoutInSeconds} is used`);
|
||||
} else {
|
||||
timeoutInSeconds = 5 * 60 + 1; // Set default value to 301 seconds
|
||||
console.log(`Default value ${timeoutInSeconds} is used`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
timeoutInSeconds = 5 * 60 + 1; // Set default value to 301 seconds, even if the session timeout endpoint is not reachable for whatever reason
|
||||
console.log(`Default value ${timeoutInSeconds} is used`);
|
||||
}
|
||||
return timeoutInSeconds;
|
||||
}
|
||||
|
||||
async function getSuggestedSessionTimeout() {
|
||||
if(!sessionStorage.getItem("suggested-session-timeout-in-seconds")) {
|
||||
let timeoutInSeconds = await makeObpApiCall();
|
||||
sessionStorage.setItem("suggested-session-timeout-in-seconds", timeoutInSeconds);
|
||||
}
|
||||
return sessionStorage.getItem("suggested-session-timeout-in-seconds") * 1000 + 1000; // We need timeout in millis
|
||||
}
|
||||
|
||||
// self executing function to trigger the operation on page load
|
||||
(async function () {
|
||||
timeoutIntervalInMillis = await getSuggestedSessionTimeout(); // Try to get suggested value
|
||||
const logoffButton = document.getElementById("countdown-timer-span");
|
||||
if(logoffButton) {
|
||||
// to prevent any lingering timeout handlers preventing memory leaks
|
||||
destroyTimeHook();
|
||||
// setup a fresh time hook
|
||||
initializeTimeHook();
|
||||
// setup initial event listeners
|
||||
setupListeners();
|
||||
// Reset countdown timer
|
||||
countdownTimer.resetCountdownTimer(timeoutIntervalInMillis / 1000);
|
||||
}
|
||||
})();
|
||||
18706
apimanager/base/static/js/jquery-ui.js
vendored
Normal file
18706
apimanager/base/static/js/jquery-ui.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
6
apimanager/base/static/js/jquery.min.js
vendored
6
apimanager/base/static/js/jquery.min.js
vendored
File diff suppressed because one or more lines are too long
@ -14,12 +14,13 @@
|
||||
<link href="{% static 'css/base.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'css/jsoneditor.min.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'css/obpjsoneditor.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'css/jquery-ui.css' %}" rel="stylesheet">
|
||||
{% block extracss %}{% endblock extracss %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
|
||||
<div style="margin-left:15% !important;">
|
||||
<div class="navbar-inner">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
@ -29,9 +30,9 @@
|
||||
</button>
|
||||
</div>
|
||||
<div id="navbar" class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav" style="margin-left:8rem">
|
||||
<li> <a href="{% url 'home' %}" style="position:absolute; margin-left: -70px !important; top:-5px"><img src="{{ logo_url }}" alt="brand"></a></li>
|
||||
<li><a href="{{ API_PORTAL }}">{% trans "Home" %}</a></li>
|
||||
<ul class="nav navbar-nav">
|
||||
<li> <a class="obp-home-button" href="{% url 'home' %}"><img src="{{ logo_url }}" alt="brand"></a></li>
|
||||
<li><a id="api_home_link" href="{{ API_PORTAL }}">{% trans "Home" %}</a></li>
|
||||
{% url "consumers-index" as consumers_index_url %}
|
||||
<li {% if consumers_index_url in request.path %} class="active" {% endif %}><a href="{{ consumers_index_url }}">{% trans "Consumers" %}</a></li>
|
||||
{% url "entitlementrequests-index" as entitlementrequests_index_url %}
|
||||
@ -104,10 +105,14 @@
|
||||
{% endif %}
|
||||
<li>
|
||||
{% if user.is_authenticated %}
|
||||
<p class="navbar-right button-select"><span id="navbar-login-username">{{API_USERNAME}}</span> <a href="/logout" class="btn btn-default">{% trans "Logout" %} </a></p>
|
||||
<p class="navbar-right button-select">
|
||||
<span id="navbar-login-username">{{API_USERNAME}}</span>
|
||||
<a id="logout" href="/logout" class="btn btn-default">{% trans "Logout" %}</a>
|
||||
<span id="countdown-timer-span"></span>
|
||||
</p>
|
||||
{% endif %}
|
||||
</li>
|
||||
<li class="language-select language_underline_format"><a style="color:#fff; text-decoration: none !important;">Language
|
||||
<li class="language-select language_underline_format"><a>Language
|
||||
<span id="gb">EN</span>
|
||||
|
|
||||
<span id="es">ES</span></a></li>
|
||||
@ -128,13 +133,13 @@
|
||||
{% endif %}
|
||||
<div class="container" id="body-container">
|
||||
{% block content %}{% endblock content %}
|
||||
<div class="footer-content-wrapper" data-lift="WebUI.homePage" style="cursor:pointer">
|
||||
<div class="footer-content-wrapper" data-lift="WebUI.homePage">
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="container">
|
||||
<p class="text-muted">
|
||||
<a title="API ROOT" href="{{ API_ROOT }}">API ROOT: {{ API_ROOT }}</a> |
|
||||
<a title="API host" href="{{ API_HOST }}">API host: {{ API_HOST }}</a> |
|
||||
<a title="Open Bank Project" href="https://openbankproject.com?locale=en_GB">Open Bank Project</a> | Powered by <a title="TESOBE" href="http://tesobe.com">TESOBE</a> |
|
||||
<small>Copyright © 2016 - 2023</small>
|
||||
</p>
|
||||
@ -144,8 +149,10 @@
|
||||
<script type="text/javascript" src="{% static 'js/jquery.min.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'js/bootstrap.min.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'js/jquery.tablesorter.min.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'js/jquery-ui.js' %}"></script>
|
||||
<script src="{% static 'js/base.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'js/jsoneditor.min.js' %}"></script>
|
||||
<script type="module" defer src="{% static 'js/inactivity.js' %}"></script>
|
||||
{% block extrajs %}{% endblock extrajs %}
|
||||
</body>
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ Views for base app
|
||||
"""
|
||||
from django.contrib import messages
|
||||
from django.conf import settings
|
||||
from django.views.generic import TemplateView
|
||||
from django.views.generic import TemplateView, View
|
||||
from django.shortcuts import render
|
||||
from obp.forms import DirectLoginForm, GatewayLoginForm
|
||||
from obp.api import API, APIError
|
||||
|
||||
@ -187,8 +187,6 @@ class EnableDisableView(LoginRequiredMixin, RedirectView):
|
||||
messages.success(self.request, self.success)
|
||||
except APIError as err:
|
||||
messages.error(self.request, err)
|
||||
except APIError as err:
|
||||
messages.error(self.request, err)
|
||||
|
||||
urlpath = self.request.POST.get('next', reverse('consumers-index'))
|
||||
query = self.request.GET.urlencode()
|
||||
|
||||
@ -6,3 +6,7 @@ input#id_kyc_status {
|
||||
width: auto;
|
||||
margin: -4px 0;
|
||||
}
|
||||
|
||||
.displaynone {
|
||||
display:none;
|
||||
}
|
||||
@ -96,7 +96,7 @@
|
||||
{{ form.date_of_birth_date }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4" style="display:none">
|
||||
<div class="col-xs-12 col-sm-4 displaynone">
|
||||
{% if form.date_of_birth_time.errors %}<div class="alert alert-danger">{{ form.date_of_birth_time.errors }}</div>{% endif %}
|
||||
<div class="form-group">
|
||||
{{ form.date_of_birth_time.label_tag }}
|
||||
|
||||
@ -9,3 +9,7 @@
|
||||
#metrics #metrics-list ul {
|
||||
margin-left: -25px;
|
||||
}
|
||||
|
||||
.hiddenRow {
|
||||
padding: 0 !important;
|
||||
}
|
||||
@ -150,40 +150,70 @@
|
||||
<div class="tab-content">
|
||||
{% block tab_content %}
|
||||
<div class="tab-pane active">
|
||||
<div class="table-responsive">
|
||||
<div class="table-responsive-md">
|
||||
<table class="table table-hover tablesorter" aria-describedby="api list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="sortless"></th>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">{% trans "Verb Select" %}</th>
|
||||
<th scope="col">{% trans "URL" %}</th>
|
||||
<th scope="col" class="sortless">{% trans "Verb Select" %}</th>
|
||||
<th scope="col" class="col-2">{% trans "URL" %}</th>
|
||||
<th scope="col">{% trans "Source IP" %}</th>
|
||||
<th scope="col">{% trans "Target IP" %}</th>
|
||||
<th scope="col">{% trans "Date" %}</th>
|
||||
<th scope="col">{% trans "Duration(ms)" %}</th>
|
||||
<th scope="col">{% trans "Details" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for metric in metrics %}
|
||||
<tr>
|
||||
{% with i=forloop.counter|stringformat:"s" %}
|
||||
{% with data_id="data"|add:i %}
|
||||
{% with data_id_selector="#"|add:data_id %}
|
||||
<tr data-toggle="collapse" data-target={{ data_id_selector }} class="accordion-toggle" data-toggle="tooltip" data-placement="top" title="Show Detail">
|
||||
<td><button type="button" class="btn btn-default btn-xs"><span class="glyphicon glyphicon-eye-open"></span></button></td>
|
||||
<td>{{ forloop.counter }}</td>
|
||||
<td>{{ metric.verb_selection }}</td>
|
||||
<td>
|
||||
{{ metric.url }}
|
||||
</td>
|
||||
<td>{{ metric.url }}</td>
|
||||
<td>{{ metric.source_ip}}</td>
|
||||
<td>{{ metric.target_ip }}</td>
|
||||
<td>{{ metric.date|date:"Y-m-d H:i:s.u" }}</td>
|
||||
<td>{{ metric.duration }}</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>{% trans "User Name" %}: {{ metric.user_name }}</li>
|
||||
<li>{% trans "User ID" %}: {{ metric.user_id }}</li>
|
||||
<li>{% trans "Developer Email" %}: {{ metric.developer_email }}</li>
|
||||
<li>{% trans "App Name" %}: {{ metric.app_name }}</li>
|
||||
<li>{% trans "Consumer ID" %}: {{ metric.consumer_id }}</li>
|
||||
<li>{% trans "Implemented by Partial Function" %}: {{ metric.implemented_by_partial_function }}</li>
|
||||
<li>{% trans "Implemented In Version" %}: {{ metric.implemented_in_version }}</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="expand-child">
|
||||
<td colspan="12" class="hiddenRow">
|
||||
<div class="accordian-body collapse" id={{ data_id }}>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr class="info filteredRow">
|
||||
<th scope="col">{% trans "Response Body" %}</th>
|
||||
<th scope="col">{% trans "Details" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ metric.response_body }}</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>{% trans "User Name" %}: {{ metric.user_name }}</li>
|
||||
<li>{% trans "User ID" %}: {{ metric.user_id }}</li>
|
||||
<li>{% trans "Developer Email" %}: {{ metric.developer_email }}</li>
|
||||
<li>{% trans "App Name" %}: {{ metric.app_name }}</li>
|
||||
<li>{% trans "Consumer ID" %}: {{ metric.consumer_id }}</li>
|
||||
<li>{% trans "Implemented by Partial Function" %}: {{ metric.implemented_by_partial_function }}</li>
|
||||
<li>{% trans "Implemented In Version" %}: {{ metric.implemented_in_version }}</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
@ -198,4 +228,4 @@
|
||||
|
||||
{% block extracss %}
|
||||
<link href="{% static 'metrics/css/metrics.css' %}" rel="stylesheet">
|
||||
{% endblock extracss %}
|
||||
{% endblock extracss %}
|
||||
@ -43,7 +43,7 @@ class API(object):
|
||||
self.start_session(session_data)
|
||||
self.session_data = session_data
|
||||
|
||||
def call(self, method='GET', url='', payload=None, version=settings.API_ROOT['v500']):
|
||||
def call(self, method='GET', url='', payload=None, version=settings.API_ROOT[settings.API_ROOT_KEY]):
|
||||
"""Workhorse which actually calls the API"""
|
||||
log(logging.INFO, '{} {}'.format(method, url))
|
||||
if payload:
|
||||
@ -64,7 +64,7 @@ class API(object):
|
||||
response.execution_time = elapsed
|
||||
return response
|
||||
|
||||
def get(self, urlpath='', version=settings.API_ROOT['v500']):
|
||||
def get(self, urlpath='', version=settings.API_ROOT[settings.API_ROOT_KEY]):
|
||||
"""
|
||||
Gets data from the API
|
||||
|
||||
@ -77,7 +77,7 @@ class API(object):
|
||||
else:
|
||||
return response
|
||||
|
||||
def delete(self, urlpath, version=settings.API_ROOT['v500']):
|
||||
def delete(self, urlpath, version=settings.API_ROOT[settings.API_ROOT_KEY]):
|
||||
"""
|
||||
Deletes data from the API
|
||||
|
||||
@ -87,7 +87,7 @@ class API(object):
|
||||
response = self.call('DELETE', url)
|
||||
return self.handle_response(response)
|
||||
|
||||
def post(self, urlpath, payload, version=settings.API_ROOT['v500']):
|
||||
def post(self, urlpath, payload, version=settings.API_ROOT[settings.API_ROOT_KEY]):
|
||||
"""
|
||||
Posts data to given urlpath with given payload
|
||||
|
||||
@ -97,7 +97,7 @@ class API(object):
|
||||
response = self.call('POST', url, payload)
|
||||
return self.handle_response(response)
|
||||
|
||||
def put(self, urlpath, payload, version=settings.API_ROOT['v500']):
|
||||
def put(self, urlpath, payload, version=settings.API_ROOT[settings.API_ROOT_KEY]):
|
||||
"""
|
||||
Puts data on given urlpath with given payload
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -133,5 +133,5 @@
|
||||
|
||||
|
||||
{% block extracss %}
|
||||
<link href="{% static 'users/css/users.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'users/css/users.css' %}" rel="stylesheet">
|
||||
{% endblock extracss %}
|
||||
|
||||
@ -9,5 +9,5 @@
|
||||
<input type="number" class="form-control" name="limit" id="limit" placeholder="50" value="{{ limit }}">
|
||||
</div>
|
||||
<input type="submit" class="btn btn-default" value ='{% trans "Refresh" %} ' onclick="javascript: form.action='';"></input>
|
||||
<input type="submit" class="btn btn-default" value ='{% trans "Export CSV" %} ' onclick="javascript: form.action='{% url 'export-csv' %}';"></input>
|
||||
<input type="submit" class="btn btn-default" value ='{% trans "Export CSV" %} ' onclick="javascript: form.action='{% url 'export-csv-users' %}';"></input>
|
||||
</form>
|
||||
@ -4,9 +4,10 @@ URLs for users app
|
||||
"""
|
||||
|
||||
from django.conf.urls import url
|
||||
from django.urls import path
|
||||
|
||||
from .views import IndexView, DetailView, MyDetailView, DeleteEntitlementView, InvitationView, UserStatusUpdateView, \
|
||||
ExportCsvView
|
||||
ExportCsvView, AutocompleteFieldView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^all$',
|
||||
@ -29,5 +30,5 @@ urlpatterns = [
|
||||
name='user-status-update'),
|
||||
url(r'^export_csv$',
|
||||
ExportCsvView.as_view(),
|
||||
name='export-csv')
|
||||
name='export-csv-users'),
|
||||
]
|
||||
|
||||
@ -5,7 +5,7 @@ Views of users app
|
||||
import datetime
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.http import HttpResponseRedirect, HttpResponse
|
||||
from django.http import HttpResponseRedirect, HttpResponse, JsonResponse
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.views.generic import FormView, TemplateView, View
|
||||
|
||||
@ -24,7 +24,6 @@ class FilterRoleName(BaseFilter):
|
||||
]]
|
||||
return filtered
|
||||
|
||||
|
||||
class FilterEmail(BaseFilter):
|
||||
"""Filter users by email address"""
|
||||
filter_type = 'email'
|
||||
@ -439,3 +438,22 @@ class ExportCsvView(LoginRequiredMixin, View):
|
||||
writer.writerow([user['username'], user['user_id'], user['email'], user['provider_id'], user['provider'],
|
||||
user['last_marketing_agreement_signed_date']])
|
||||
return response
|
||||
|
||||
# This below code is not yet working, it is intended to provide a json list of results to feed to jquery-ui autocomplete feature
|
||||
|
||||
class AutocompleteFieldView(View):
|
||||
"""Autocompletes a Field Form based on what endpoint the field is filtering"""
|
||||
def autocomplete_form_field(self, request, *args, **kwargs):
|
||||
api = API(self.request.session.get('obp'))
|
||||
term = self.request.GET.get('term', '')
|
||||
try:
|
||||
urlpath = '/roles'
|
||||
response = api.get(urlpath)
|
||||
if 'code' in response and response['code'] >= 400:
|
||||
messages.error(self.request, response['message'])
|
||||
else:
|
||||
suggestions = response.json()
|
||||
return JsonResponse(suggestions, safe=False)
|
||||
except APIError as err:
|
||||
messages.error(self.request, err)
|
||||
return [], []
|
||||
@ -3,9 +3,10 @@ Django==2.2.28
|
||||
oauthlib==3.2.2
|
||||
requests==2.27.1
|
||||
requests-oauthlib==1.3.1
|
||||
PyJWT==1.5.3
|
||||
PyJWT==2.8.0
|
||||
gunicorn==19.6.0
|
||||
matplotlib
|
||||
django-bootstrap-datepicker-plus==3.0.5
|
||||
django-mathfilters
|
||||
django-bootstrap3
|
||||
django-csp
|
||||
Loading…
Reference in New Issue
Block a user