From 816b08be86ee8cb9907780d61263d7da0ec0a633 Mon Sep 17 00:00:00 2001 From: Jorik Kraaikamp Date: Wed, 9 Mar 2022 11:49:54 +0100 Subject: [PATCH 01/11] Updated the expire message --- package-lock.json | 70 +++---- .../components/Session/session_timeout.html | 10 + .../components/templatetags/session_tags.py | 17 ++ src/open_inwoner/conf/base.py | 3 + src/open_inwoner/js/components/index.js | 1 + .../js/components/session/index.js | 178 ++++++++++++++++++ src/open_inwoner/templates/master.html | 5 +- 7 files changed, 250 insertions(+), 34 deletions(-) create mode 100644 src/open_inwoner/components/templates/components/Session/session_timeout.html create mode 100644 src/open_inwoner/components/templatetags/session_tags.py create mode 100644 src/open_inwoner/js/components/session/index.js diff --git a/package-lock.json b/package-lock.json index a54f0e914e..9745b7ba01 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,9 +28,9 @@ "dev": true }, "@babel/core": { - "version": "7.17.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.4.tgz", - "integrity": "sha512-R9x5r4t4+hBqZTmioSnkrW+I6NmbojwjGT8p4G2Gw1thWbXIHGDnmGdLdFw0/7ljucdIrNRp7Npgb4CyBYzzJg==", + "version": "7.17.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.17.5.tgz", + "integrity": "sha512-/BBMw4EvjmyquN5O+t5eh0+YqB3XXJkYD2cjKpYtWOfFy4lQ4UozNSmxAcWT8r2XtZs0ewG+zrfsqeR15i1ajA==", "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", @@ -2627,6 +2627,12 @@ "lodash-es": "^4.17.15" } }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true + }, "@csstools/convert-colors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-2.0.0.tgz", @@ -5436,9 +5442,9 @@ } }, "date-format": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.3.tgz", - "integrity": "sha512-7P3FyqDcfeznLZp2b+OMitV9Sz2lUnsT87WaTat9nVwqsBkTzPG3lPLNwW3en6F4pHUiWzr6vb8CLhjdK9bcxQ==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.4.tgz", + "integrity": "sha512-/jyf4rhB17ge328HJuJjAcmRtCsGd+NDeAtahRBTaK6vSPR6MO5HlrAit3Nn7dVjaa6sowW0WXt8yQtLyZQFRg==", "dev": true }, "debug": { @@ -5785,9 +5791,9 @@ "dev": true }, "engine.io": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.2.tgz", - "integrity": "sha512-v/7eGHxPvO2AWsksyx2PUsQvBafuvqs0jJJQ0FdmJG1b9qIvgSbqDRGwNhfk2XHaTTbTXiC4quRE8Q9nRjsrQQ==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.3.tgz", + "integrity": "sha512-rqs60YwkvWTLLnfazqgZqLa/aKo+9cueVfEi/dZ8PyGyaf8TLOxj++4QMIgeG3Gn0AhrWiFXvghsoY9L9h25GA==", "dev": true, "requires": { "@types/cookie": "^0.4.1", @@ -5798,7 +5804,7 @@ "cookie": "~0.4.1", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~5.0.0", + "engine.io-parser": "~5.0.3", "ws": "~8.2.3" }, "dependencies": { @@ -8404,15 +8410,15 @@ "dev": true }, "karma": { - "version": "6.3.16", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.16.tgz", - "integrity": "sha512-nEU50jLvDe5yvXqkEJRf8IuvddUkOY2x5Xc4WXHz6dxINgGDrgD2uqQWeVrJs4hbfNaotn+HQ1LZJ4yOXrL7xQ==", + "version": "6.3.17", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.17.tgz", + "integrity": "sha512-2TfjHwrRExC8yHoWlPBULyaLwAFmXmxQrcuFImt/JsAsSZu1uOWTZ1ZsWjqQtWpHLiatJOHL5jFjXSJIgCd01g==", "dev": true, "requires": { + "@colors/colors": "1.5.0", "body-parser": "^1.19.0", "braces": "^3.0.2", "chokidar": "^3.5.1", - "colors": "1.4.0", "connect": "^3.7.0", "di": "^0.0.1", "dom-serialize": "^2.2.1", @@ -8788,16 +8794,16 @@ } }, "log4js": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.4.1.tgz", - "integrity": "sha512-iUiYnXqAmNKiIZ1XSAitQ4TmNs8CdZYTAWINARF3LjnsLN8tY5m0vRwd6uuWj/yNY0YHxeZodnbmxKFUOM2rMg==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.4.2.tgz", + "integrity": "sha512-k80cggS2sZQLBwllpT1p06GtfvzMmSdUCkW96f0Hj83rKGJDAu2vZjt9B9ag2vx8Zz1IXzxoLgqvRJCdMKybGg==", "dev": true, "requires": { - "date-format": "^4.0.3", + "date-format": "^4.0.4", "debug": "^4.3.3", - "flatted": "^3.2.4", + "flatted": "^3.2.5", "rfdc": "^1.3.0", - "streamroller": "^3.0.2" + "streamroller": "^3.0.4" }, "dependencies": { "debug": { @@ -13023,9 +13029,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass": { - "version": "1.49.7", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.7.tgz", - "integrity": "sha512-13dml55EMIR2rS4d/RDHHP0sXMY3+30e1TKsyXaSz3iLWVoDWEoboY8WzJd5JMnxrRHffKO3wq2mpJ0jxRJiEQ==", + "version": "1.49.9", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.9.tgz", + "integrity": "sha512-YlYWkkHP9fbwaFRZQRXgDi3mXZShslVmmo+FVK3kHLUELHHEYrCmL1x6IUjC7wLS6VuJSAFXRQS/DxdsC4xL1A==", "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0", @@ -13795,14 +13801,14 @@ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, "streamroller": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.0.2.tgz", - "integrity": "sha512-ur6y5S5dopOaRXBuRIZ1u6GC5bcEXHRZKgfBjfCglMhmIf+roVCECjvkEYzNQOXIN2/JPnkMPW/8B3CZoKaEPA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.0.4.tgz", + "integrity": "sha512-GI9NzeD+D88UFuIlJkKNDH/IsuR+qIN7Qh8EsmhoRZr9bQoehTraRgwtLUkZbpcAw+hLPfHOypmppz8YyGK68w==", "dev": true, "requires": { - "date-format": "^4.0.3", - "debug": "^4.1.1", - "fs-extra": "^10.0.0" + "date-format": "^4.0.4", + "debug": "^4.3.3", + "fs-extra": "^10.0.1" }, "dependencies": { "debug": { @@ -13815,9 +13821,9 @@ } }, "fs-extra": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", - "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz", + "integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==", "dev": true, "requires": { "graceful-fs": "^4.2.0", diff --git a/src/open_inwoner/components/templates/components/Session/session_timeout.html b/src/open_inwoner/components/templates/components/Session/session_timeout.html new file mode 100644 index 0000000000..0434ccbdc0 --- /dev/null +++ b/src/open_inwoner/components/templates/components/Session/session_timeout.html @@ -0,0 +1,10 @@ +{% load l10n %} + +{% if user.is_authenticated %} +
+
+{% endif %} diff --git a/src/open_inwoner/components/templatetags/session_tags.py b/src/open_inwoner/components/templatetags/session_tags.py new file mode 100644 index 0000000000..2248589306 --- /dev/null +++ b/src/open_inwoner/components/templatetags/session_tags.py @@ -0,0 +1,17 @@ +from django import template +from django.conf import settings + +register = template.Library() + + +@register.inclusion_tag("components/Session/session_timeout.html", takes_context=True) +def session_timeout(context): + session = context["request"].session + context.update( + { + "expiry_age": session.get_expiry_age() + + 1, # Add a second to make sure the session has expired. + "warn_time": session.get_expiry_age() - settings.SESSION_WARN_DELTA, + } + ) + return context diff --git a/src/open_inwoner/conf/base.py b/src/open_inwoner/conf/base.py index 78076f7f47..beb7fbef82 100644 --- a/src/open_inwoner/conf/base.py +++ b/src/open_inwoner/conf/base.py @@ -653,3 +653,6 @@ ZGW_CONSUMERS_TEST_SCHEMA_DIRS = [ os.path.join(DJANGO_PROJECT_DIR, "openzaak", "tests", "files"), ] + + +SESSION_WARN_DELTA = 60 # Warn 1 minute before end of session. diff --git a/src/open_inwoner/js/components/index.js b/src/open_inwoner/js/components/index.js index ba2f9a7170..8f9cbb6e61 100644 --- a/src/open_inwoner/js/components/index.js +++ b/src/open_inwoner/js/components/index.js @@ -14,3 +14,4 @@ import './search' import './autosumbit' import './dropdown' import './toggle' +import './session' diff --git a/src/open_inwoner/js/components/session/index.js b/src/open_inwoner/js/components/session/index.js new file mode 100644 index 0000000000..a1f26ff06a --- /dev/null +++ b/src/open_inwoner/js/components/session/index.js @@ -0,0 +1,178 @@ +import swal from 'sweetalert' + +function currentTime() { + return Math.floor(Date.now() / 1000) +} + +/* + * Show a message to the user 1 minutes before the session timeout + */ +class SessionTimeout { + constructor() { + this.element = document.getElementById('session-timeout') + if (!this.element) { + return + } + + this.configureTimeout() + this.configureActivityCheck() + } + + configureTimeout() { + console.log('Session started.') + this.userActive = null + + this.setDataset() + + // When the session has been restarted, there can be lingering timeouts. + clearTimeout(this.warningTimeout) + clearTimeout(this.expiredTimeout) + + this.warningTimeout = setTimeout( + this.showWarningModal.bind(this), + 1000 // this.warnTime * 1000 + ) + this.expiredTimeout = setTimeout( + this.showExpiredModal, + (this.expiryAge + 1) * 1000 + ) + } + + setDataset() { + console.log('setDataset') + this.expiryAge = parseInt(this.element.dataset.expiryAge) + this.warnTime = parseInt(this.element.dataset.warnTime) + } + + showWarningModal() { + console.log('showWarningModal') + if (this.userActive) { + this.restartSession() + return + } + + this.configureModal( + 'Uw sessie verloopt spoedig', + 'Klik op de knop "Doorgaan" om verder te gaan met de huidige sessie.', + 'Doorgaan', + this.restartSession + ) + } + + showExpiredModal() { + this.configureModal( + 'Uw sessie is verlopen', + 'Klik op de knop "Doorgaan" om opnieuw in te loggen.', + 'Doorgaan', + this.reloadPage + ) + } + + /* + * Restart the HTTP session by doing an HTTP-request. + */ + restartSession() { + console.log('Restarting session.') + + let restartRequest = new XMLHttpRequest() + let sessionTimeout = this + restartRequest.addEventListener('load', function (event) { + sessionTimeout.resetWarnTime(this, event) + }) + restartRequest.open('GET', '/accounts/restart_session/') + restartRequest.send() + } + + reloadPage() { + location.reload() + } + + //! Not using yet.... + + configureModal(title, bodyText, buttonText, callback) { + swal({ + title: title, + text: bodyText, + buttons: { + continue: buttonText, + }, + }).then(callback) + } + + configureActivityCheck() { + /* + * Based on if there is user activity restart the HTTP session. To avoid + * the user being logged out while still entering data into a form. + */ + + /* + * Note: 'keyup' does not account for tablet/phone users (and probably other devices). + */ + document.addEventListener('keyup', this.setUserActivity) + + this.configureActivityTimeout() + } + + configureActivityTimeout() { + clearTimeout(this.activityRestart) + + this.activityRestart = setTimeout( + this.restartNoActivity.bind(this), + 30 * 1000 + ) + } + + restartNoActivity() { + this.configureActivityTimeout() + + /* + * After a session restart we register if a user was active (in setUserActivity). + * If this was the case, and if it was more than a minute ago, restart the session. + * + * If the user is still active, we restart the session right before it + * expires in showWarningModal. + * + * This latest step is done to avoid sending a lot of requests to the server. + */ + + if (this.userActive === null) { + console.log('No user activity after the session started.') + return + } + + let latestActivity = currentTime() - this.userActive + console.log( + `Latest activity after the session started was ${latestActivity} seconds ago` + ) + + if (latestActivity >= 60) { + this.restartSession() + } + } + + setUserActivity() { + this.userActive = currentTime() + console.log(`Registered user activity, timestamp: ${this.userActive}`) + } + + resetWarnTime(response, event) { + // If we are redirected to a login page. + if (response.response !== 'restarted') { + clearTimeout(this.expiredTimeout) + + // Wait for the current modal to close and then open the expired one. + // Ensure that the event is unbound after, to avoid an additional pop up before reload. + const callback = () => { + this.showExpiredModal() + jQuery(this.element).off('hidden.bs.modal', callback) + } + jQuery(this.element).on('hidden.bs.modal', callback) + } else { + this.configureTimeout() + } + } +} + +document.addEventListener('DOMContentLoaded', function () { + const s = new SessionTimeout() +}) diff --git a/src/open_inwoner/templates/master.html b/src/open_inwoner/templates/master.html index ddec80b39d..d51621ec5d 100644 --- a/src/open_inwoner/templates/master.html +++ b/src/open_inwoner/templates/master.html @@ -1,4 +1,4 @@ -{% load static i18n header_tags card_tags footer_tags button_tags notification_tags utils %} +{% load static i18n header_tags card_tags footer_tags button_tags notification_tags session_tags utils %} @@ -29,7 +29,7 @@ {% block extra_css %}{% endblock %} - + {% include "analytics/google.html" %} {% include "analytics/matomo.html" %} @@ -58,6 +58,7 @@ {% endblock main_outer %} {% footer logo_url=site_logo footer_texts=configurable_text.footer %} + {% session_timeout %} From df67bf49e0521f36614a685338c2ded1aab9badb Mon Sep 17 00:00:00 2001 From: Jorik Kraaikamp Date: Wed, 20 Apr 2022 11:13:42 +0200 Subject: [PATCH 02/11] Made some small fixes --- package-lock.json | 36 +++++++++---------- .../js/components/session/index.js | 11 +++--- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/package-lock.json b/package-lock.json index 97f85b438b..ccd1c56980 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1767,9 +1767,9 @@ "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz", - "integrity": "sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.7.tgz", + "integrity": "sha512-8XC0l0PwCbdg2Uc8zIIf6djNX3lYiz9GqQlC1LJ9WQvTYvcfP8IA9K2IKRnPm5tAX6X/+orF+WwKZ0doGcgJlg==", "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", @@ -2736,9 +2736,9 @@ } }, "babel-loader": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.4.tgz", - "integrity": "sha512-8dytA3gcvPPPv4Grjhnt8b5IIiTcq/zeXOPk4iTYI0SVXcsmuGg7JtBRDp8S9X+gJfhQ8ektjXZlDu1Bb33U8A==", + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", "dev": true, "requires": { "find-cache-dir": "^3.3.1", @@ -4714,9 +4714,9 @@ } }, "enhanced-resolve": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz", - "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz", + "integrity": "sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==", "dev": true, "requires": { "graceful-fs": "^4.2.4", @@ -7204,9 +7204,9 @@ } }, "loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true }, "loader-utils": { @@ -11286,9 +11286,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass": { - "version": "1.50.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.50.0.tgz", - "integrity": "sha512-cLsD6MEZ5URXHStxApajEh7gW189kkjn4Rc8DQweMyF+o5HF5nfEz8QYLMlPsTOD88DknatTmBWkOcw5/LnJLQ==", + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.50.1.tgz", + "integrity": "sha512-noTnY41KnlW2A9P8sdwESpDmo+KBNkukI1i8+hOK3footBUcohNHtdOJbckp46XO95nuvcHDDZ+4tmOnpK3hjw==", "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0", @@ -14423,9 +14423,9 @@ "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" }, "yargs": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", - "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz", + "integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==", "dev": true, "requires": { "cliui": "^7.0.2", diff --git a/src/open_inwoner/js/components/session/index.js b/src/open_inwoner/js/components/session/index.js index a1f26ff06a..ba833596e3 100644 --- a/src/open_inwoner/js/components/session/index.js +++ b/src/open_inwoner/js/components/session/index.js @@ -1,4 +1,4 @@ -import swal from 'sweetalert' +import Swal from 'sweetalert2' function currentTime() { return Math.floor(Date.now() / 1000) @@ -90,12 +90,11 @@ class SessionTimeout { //! Not using yet.... configureModal(title, bodyText, buttonText, callback) { - swal({ + Swal.fire({ title: title, - text: bodyText, - buttons: { - continue: buttonText, - }, + html: bodyText, + showConfirmButton: true, + confirmButtonText: callback, }).then(callback) } From 3db1699e37b8366d69899f532850a5d91e495130 Mon Sep 17 00:00:00 2001 From: Jorik Kraaikamp Date: Mon, 25 Apr 2022 16:52:33 +0200 Subject: [PATCH 03/11] Fixed the popup, admins will be logged in longer, moved to a session app --- src/open_inwoner/conf/base.py | 8 +++-- src/open_inwoner/conf/dev.py | 4 ++- src/open_inwoner/conf/production.py | 2 +- .../js/components/session/index.js | 34 +++++++------------ src/open_inwoner/session/__init__.py | 0 src/open_inwoner/session/backends/__init__.py | 0 src/open_inwoner/session/backends/base.py | 17 ++++++++++ src/open_inwoner/session/backends/cache.py | 10 ++++++ .../session/backends/cached_db.py | 10 ++++++ src/open_inwoner/session/backends/db.py | 10 ++++++ src/open_inwoner/session/backends/file.py | 10 ++++++ .../session/backends/signed_cookies.py | 12 +++++++ .../templates/session}/session_timeout.html | 0 .../session/templatetags/__init__.py | 0 .../templatetags/session_tags.py | 2 +- src/open_inwoner/session/urls.py | 7 ++++ src/open_inwoner/session/views.py | 23 +++++++++++++ src/open_inwoner/templates/master.html | 2 +- src/open_inwoner/urls.py | 1 + src/open_inwoner/utils/sessions.py | 1 + 20 files changed, 124 insertions(+), 29 deletions(-) create mode 100644 src/open_inwoner/session/__init__.py create mode 100644 src/open_inwoner/session/backends/__init__.py create mode 100644 src/open_inwoner/session/backends/base.py create mode 100644 src/open_inwoner/session/backends/cache.py create mode 100644 src/open_inwoner/session/backends/cached_db.py create mode 100644 src/open_inwoner/session/backends/db.py create mode 100644 src/open_inwoner/session/backends/file.py create mode 100644 src/open_inwoner/session/backends/signed_cookies.py rename src/open_inwoner/{components/templates/components/Session => session/templates/session}/session_timeout.html (100%) create mode 100644 src/open_inwoner/session/templatetags/__init__.py rename src/open_inwoner/{components => session}/templatetags/session_tags.py (83%) create mode 100644 src/open_inwoner/session/urls.py create mode 100644 src/open_inwoner/session/views.py create mode 100644 src/open_inwoner/utils/sessions.py diff --git a/src/open_inwoner/conf/base.py b/src/open_inwoner/conf/base.py index ad9fab94b4..d2b68f693c 100644 --- a/src/open_inwoner/conf/base.py +++ b/src/open_inwoner/conf/base.py @@ -159,6 +159,7 @@ "open_inwoner.haalcentraal", "open_inwoner.openzaak", "open_inwoner.questionnaire", + "open_inwoner.session", ] MIDDLEWARE = [ @@ -357,7 +358,10 @@ ] SESSION_COOKIE_NAME = "open_inwoner_sessionid" -SESSION_ENGINE = "django.contrib.sessions.backends.cache" +SESSION_ENGINE = "open_inwoner.session.backends.cache" +SESSION_COOKIE_AGE_ADMIN = 86400 +SESSION_COOKIE_AGE = 900 # Set to 15 minutes +SESSION_WARN_DELTA = 60 # Warn 1 minute before end of session. LOGIN_REDIRECT_URL = reverse_lazy("root") LOGOUT_REDIRECT_URL = reverse_lazy("root") @@ -762,8 +766,6 @@ os.path.join(DJANGO_PROJECT_DIR, "openzaak", "tests", "files"), ] -SESSION_WARN_DELTA = 60 # Warn 1 minute before end of session. - # # Maykin fork of DJANGO-TWO-FACTOR-AUTH # diff --git a/src/open_inwoner/conf/dev.py b/src/open_inwoner/conf/dev.py index e75c4d4e96..ccceaa7464 100644 --- a/src/open_inwoner/conf/dev.py +++ b/src/open_inwoner/conf/dev.py @@ -24,7 +24,9 @@ # # Standard Django settings. # -SESSION_ENGINE = "django.contrib.sessions.backends.cached_db" +SESSION_ENGINE = "open_inwoner.session.backends.cached_db" +SESSION_COOKIE_AGE_ADMIN = 240 +SESSION_COOKIE_AGE = 120 EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" # This is commented out because it causes tests in the CI to fail. It can be enabled in the local.py settings. diff --git a/src/open_inwoner/conf/production.py b/src/open_inwoner/conf/production.py index f9b93d9e2e..7e2ec41fd2 100644 --- a/src/open_inwoner/conf/production.py +++ b/src/open_inwoner/conf/production.py @@ -24,7 +24,7 @@ db_config["CONN_MAX_AGE"] = 60 # Lifetime of a database connection for performance. # Caching sessions. -SESSION_ENGINE = "django.contrib.sessions.backends.cache" +SESSION_ENGINE = "open_inwoner.session.backends.cache" SESSION_CACHE_ALIAS = "default" # Caching templates. diff --git a/src/open_inwoner/js/components/session/index.js b/src/open_inwoner/js/components/session/index.js index ba833596e3..b2199a0ee1 100644 --- a/src/open_inwoner/js/components/session/index.js +++ b/src/open_inwoner/js/components/session/index.js @@ -30,7 +30,7 @@ class SessionTimeout { this.warningTimeout = setTimeout( this.showWarningModal.bind(this), - 1000 // this.warnTime * 1000 + this.warnTime * 1000 ) this.expiredTimeout = setTimeout( this.showExpiredModal, @@ -42,6 +42,8 @@ class SessionTimeout { console.log('setDataset') this.expiryAge = parseInt(this.element.dataset.expiryAge) this.warnTime = parseInt(this.element.dataset.warnTime) + console.log('this.expiryAge', this.expiryAge) + console.log('this.warnTime', this.warnTime) } showWarningModal() { @@ -72,30 +74,22 @@ class SessionTimeout { * Restart the HTTP session by doing an HTTP-request. */ restartSession() { - console.log('Restarting session.') - - let restartRequest = new XMLHttpRequest() - let sessionTimeout = this - restartRequest.addEventListener('load', function (event) { - sessionTimeout.resetWarnTime(this, event) - }) - restartRequest.open('GET', '/accounts/restart_session/') - restartRequest.send() + fetch('/sessions/restart/') + .then((response) => response.text()) + .then((data) => this.resetWarnTime(data)) } reloadPage() { - location.reload() + window.location.reload() } - //! Not using yet.... - configureModal(title, bodyText, buttonText, callback) { Swal.fire({ title: title, html: bodyText, showConfirmButton: true, - confirmButtonText: callback, - }).then(callback) + confirmButtonText: buttonText, + }).then(callback.bind(this)) } configureActivityCheck() { @@ -154,18 +148,14 @@ class SessionTimeout { console.log(`Registered user activity, timestamp: ${this.userActive}`) } - resetWarnTime(response, event) { + resetWarnTime(response) { // If we are redirected to a login page. - if (response.response !== 'restarted') { + if (response !== 'restarted') { clearTimeout(this.expiredTimeout) // Wait for the current modal to close and then open the expired one. // Ensure that the event is unbound after, to avoid an additional pop up before reload. - const callback = () => { - this.showExpiredModal() - jQuery(this.element).off('hidden.bs.modal', callback) - } - jQuery(this.element).on('hidden.bs.modal', callback) + this.showExpiredModal() } else { this.configureTimeout() } diff --git a/src/open_inwoner/session/__init__.py b/src/open_inwoner/session/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/open_inwoner/session/backends/__init__.py b/src/open_inwoner/session/backends/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/open_inwoner/session/backends/base.py b/src/open_inwoner/session/backends/base.py new file mode 100644 index 0000000000..524287211f --- /dev/null +++ b/src/open_inwoner/session/backends/base.py @@ -0,0 +1,17 @@ +from django.conf import settings + +from open_inwoner.accounts.models import User + + +class SplitSessionAge: + def get_session_cookie_age(self): + user_id = self.get("_auth_user_id") + + try: + is_staff = User.objects.get(pk=user_id).is_staff + except Exception: + is_staff = False + + if hasattr(settings, "SESSION_COOKIE_AGE_ADMIN") and is_staff: + return settings.SESSION_COOKIE_AGE_ADMIN + return settings.SESSION_COOKIE_AGE diff --git a/src/open_inwoner/session/backends/cache.py b/src/open_inwoner/session/backends/cache.py new file mode 100644 index 0000000000..a2527e8006 --- /dev/null +++ b/src/open_inwoner/session/backends/cache.py @@ -0,0 +1,10 @@ +from django.conf import settings +from django.contrib.sessions.backends.cache import SessionStore as BaseSessionStore + +from open_inwoner.accounts.models import User + +from .base import SplitSessionAge + + +class SessionStore(SplitSessionAge, BaseSessionStore): + pass diff --git a/src/open_inwoner/session/backends/cached_db.py b/src/open_inwoner/session/backends/cached_db.py new file mode 100644 index 0000000000..53c17659c6 --- /dev/null +++ b/src/open_inwoner/session/backends/cached_db.py @@ -0,0 +1,10 @@ +from django.conf import settings +from django.contrib.sessions.backends.cached_db import SessionStore as BaseSessionStore + +from open_inwoner.accounts.models import User + +from .base import SplitSessionAge + + +class SessionStore(SplitSessionAge, BaseSessionStore): + pass diff --git a/src/open_inwoner/session/backends/db.py b/src/open_inwoner/session/backends/db.py new file mode 100644 index 0000000000..c6633afb8d --- /dev/null +++ b/src/open_inwoner/session/backends/db.py @@ -0,0 +1,10 @@ +from django.conf import settings +from django.contrib.sessions.backends.db import SessionStore as BaseSessionStore + +from open_inwoner.accounts.models import User + +from .base import SplitSessionAge + + +class SessionStore(SplitSessionAge, BaseSessionStore): + pass diff --git a/src/open_inwoner/session/backends/file.py b/src/open_inwoner/session/backends/file.py new file mode 100644 index 0000000000..73b85cbd55 --- /dev/null +++ b/src/open_inwoner/session/backends/file.py @@ -0,0 +1,10 @@ +from django.conf import settings +from django.contrib.sessions.backends.file import SessionStore as BaseSessionStore + +from open_inwoner.accounts.models import User + +from .base import SplitSessionAge + + +class SessionStore(SplitSessionAge, BaseSessionStore): + pass diff --git a/src/open_inwoner/session/backends/signed_cookies.py b/src/open_inwoner/session/backends/signed_cookies.py new file mode 100644 index 0000000000..0a58d65d99 --- /dev/null +++ b/src/open_inwoner/session/backends/signed_cookies.py @@ -0,0 +1,12 @@ +from django.conf import settings +from django.contrib.sessions.backends.signed_cookies import ( + SessionStore as BaseSessionStore, +) + +from open_inwoner.accounts.models import User + +from .base import SplitSessionAge + + +class SessionStore(SplitSessionAge, BaseSessionStore): + pass diff --git a/src/open_inwoner/components/templates/components/Session/session_timeout.html b/src/open_inwoner/session/templates/session/session_timeout.html similarity index 100% rename from src/open_inwoner/components/templates/components/Session/session_timeout.html rename to src/open_inwoner/session/templates/session/session_timeout.html diff --git a/src/open_inwoner/session/templatetags/__init__.py b/src/open_inwoner/session/templatetags/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/open_inwoner/components/templatetags/session_tags.py b/src/open_inwoner/session/templatetags/session_tags.py similarity index 83% rename from src/open_inwoner/components/templatetags/session_tags.py rename to src/open_inwoner/session/templatetags/session_tags.py index 2248589306..663939009e 100644 --- a/src/open_inwoner/components/templatetags/session_tags.py +++ b/src/open_inwoner/session/templatetags/session_tags.py @@ -4,7 +4,7 @@ register = template.Library() -@register.inclusion_tag("components/Session/session_timeout.html", takes_context=True) +@register.inclusion_tag("session/session_timeout.html", takes_context=True) def session_timeout(context): session = context["request"].session context.update( diff --git a/src/open_inwoner/session/urls.py b/src/open_inwoner/session/urls.py new file mode 100644 index 0000000000..f64f400df0 --- /dev/null +++ b/src/open_inwoner/session/urls.py @@ -0,0 +1,7 @@ +from django.urls import path + +from .views import RestartSessionView + +urlpatterns = [ + path("restart/", RestartSessionView.as_view(), name="restart-session"), +] diff --git a/src/open_inwoner/session/views.py b/src/open_inwoner/session/views.py new file mode 100644 index 0000000000..997c5287c0 --- /dev/null +++ b/src/open_inwoner/session/views.py @@ -0,0 +1,23 @@ +from django.http import HttpResponse +from django.views.generic import View + + +class RestartSessionView(View): + """ + This view is used from the SessionTimeout Javascript + class to determine if the user is logged in or not. + + This used to be done by doing a XMLHttpRequest to '/' and + checking the 'responseURL' if it was redirected to the + login page. However, Internet Explorer does not support + this method. + """ + + http_method_names = [ + "get", + ] + + def get(self, request): + if request.user.is_authenticated: + return HttpResponse("restarted") + return HttpResponse() diff --git a/src/open_inwoner/templates/master.html b/src/open_inwoner/templates/master.html index cf1d821eef..d7e7a0c2a4 100644 --- a/src/open_inwoner/templates/master.html +++ b/src/open_inwoner/templates/master.html @@ -1,4 +1,4 @@ -{% load static i18n header_tags card_tags footer_tags button_tags notification_tags anchor_menu_tags view_breadcrumbs utils %} +{% load static i18n header_tags card_tags footer_tags button_tags notification_tags anchor_menu_tags view_breadcrumbs utils session_tags %} diff --git a/src/open_inwoner/urls.py b/src/open_inwoner/urls.py index d969036a01..9f10ddd7be 100644 --- a/src/open_inwoner/urls.py +++ b/src/open_inwoner/urls.py @@ -59,6 +59,7 @@ "questionnaire/", include("open_inwoner.questionnaire.urls", namespace="questionnaire"), ), + path("sessions/", include("open_inwoner.session.urls"), name="sessions"), path("faq/", FAQView.as_view(), name="general_faq"), path("", include("open_inwoner.pdc.urls", namespace="pdc")), path("", include("open_inwoner.search.urls", namespace="search")), diff --git a/src/open_inwoner/utils/sessions.py b/src/open_inwoner/utils/sessions.py new file mode 100644 index 0000000000..2870fc8ba8 --- /dev/null +++ b/src/open_inwoner/utils/sessions.py @@ -0,0 +1 @@ +from django.contrib.sessions.middleware import SessionMiddleware From 7fc00aab950034bdef5a93f396c832fce7bf46fa Mon Sep 17 00:00:00 2001 From: Jorik Kraaikamp Date: Mon, 25 Apr 2022 17:01:59 +0200 Subject: [PATCH 04/11] removed testing values --- src/open_inwoner/conf/dev.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/open_inwoner/conf/dev.py b/src/open_inwoner/conf/dev.py index ccceaa7464..94dea07b2c 100644 --- a/src/open_inwoner/conf/dev.py +++ b/src/open_inwoner/conf/dev.py @@ -25,8 +25,8 @@ # Standard Django settings. # SESSION_ENGINE = "open_inwoner.session.backends.cached_db" -SESSION_COOKIE_AGE_ADMIN = 240 -SESSION_COOKIE_AGE = 120 +# SESSION_COOKIE_AGE_ADMIN = 240 +# SESSION_COOKIE_AGE = 120 EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" # This is commented out because it causes tests in the CI to fail. It can be enabled in the local.py settings. From 89f3b228635104cfd0c4f7d141939b44928dcb92 Mon Sep 17 00:00:00 2001 From: Jorik Kraaikamp Date: Wed, 4 May 2022 15:51:10 +0200 Subject: [PATCH 05/11] moved to external package --- requirements/base.in | 1 + requirements/base.txt | 5 +++- requirements/ci.txt | 7 +++++- requirements/dev.txt | 7 +++++- src/open_inwoner/conf/base.py | 6 +++-- src/open_inwoner/conf/dev.py | 2 -- src/open_inwoner/session/__init__.py | 0 src/open_inwoner/session/backends/__init__.py | 0 src/open_inwoner/session/backends/base.py | 17 -------------- src/open_inwoner/session/backends/cache.py | 10 -------- .../session/backends/cached_db.py | 10 -------- src/open_inwoner/session/backends/db.py | 10 -------- src/open_inwoner/session/backends/file.py | 10 -------- .../session/backends/signed_cookies.py | 12 ---------- .../templates/session/session_timeout.html | 10 -------- .../session/templatetags/__init__.py | 0 .../session/templatetags/session_tags.py | 17 -------------- src/open_inwoner/session/urls.py | 7 ------ src/open_inwoner/session/views.py | 23 ------------------- src/open_inwoner/urls.py | 2 +- src/open_inwoner/utils/sessions.py | 1 - 21 files changed, 22 insertions(+), 135 deletions(-) delete mode 100644 src/open_inwoner/session/__init__.py delete mode 100644 src/open_inwoner/session/backends/__init__.py delete mode 100644 src/open_inwoner/session/backends/base.py delete mode 100644 src/open_inwoner/session/backends/cache.py delete mode 100644 src/open_inwoner/session/backends/cached_db.py delete mode 100644 src/open_inwoner/session/backends/db.py delete mode 100644 src/open_inwoner/session/backends/file.py delete mode 100644 src/open_inwoner/session/backends/signed_cookies.py delete mode 100644 src/open_inwoner/session/templates/session/session_timeout.html delete mode 100644 src/open_inwoner/session/templatetags/__init__.py delete mode 100644 src/open_inwoner/session/templatetags/session_tags.py delete mode 100644 src/open_inwoner/session/urls.py delete mode 100644 src/open_inwoner/session/views.py delete mode 100644 src/open_inwoner/utils/sessions.py diff --git a/requirements/base.in b/requirements/base.in index 2999c3bd89..0d8e7e4134 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -42,6 +42,7 @@ humanfriendly git+https://github.com/maykinmedia/mail-editor.git@0b4621b5c7f434586115b8e722af8940cfa70195#egg=mail-editor fontawesomefree django-timeline-logger +extended-admin-session # API libraries djangorestframework diff --git a/requirements/base.txt b/requirements/base.txt index 733dd12bf4..77e64a42fb 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -45,7 +45,7 @@ defusedxml==0.7.1 # python3-openid diff-match-patch==20200713 # via django-import-export -digid-eherkenning @ git+https://bitbucket.org/maykinmedia/django-digid-eherkenning.git@02ac61c42f6dd2f229ba9f0f687fa1a4160511be +digid_eherkenning @ git+https://bitbucket.org/maykinmedia/django-digid-eherkenning.git@02ac61c42f6dd2f229ba9f0f687fa1a4160511be # via -r requirements/base.in dj-rest-auth==2.1.11 # via -r requirements/base.in @@ -82,6 +82,7 @@ django==3.2.12 # djangorestframework # drf-spectacular # easy-thumbnails + # extended-admin-session # mail-editor # maykin-django-two-factor-auth # zgw-consumers @@ -189,6 +190,8 @@ elasticsearch-dsl==7.4.0 # via django-elasticsearch-dsl et-xmlfile==1.1.0 # via openpyxl +extended-admin-session==0.1.1 + # via -r requirements/base.in face==20.1.1 # via glom faker==9.9.0 diff --git a/requirements/ci.txt b/requirements/ci.txt index 70daabed83..596b9b955c 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -84,7 +84,7 @@ diff-match-patch==20200713 # -c requirements/base.txt # -r requirements/base.txt # django-import-export -digid-eherkenning @ git+https://bitbucket.org/maykinmedia/django-digid-eherkenning.git@02ac61c42f6dd2f229ba9f0f687fa1a4160511be +digid_eherkenning @ git+https://bitbucket.org/maykinmedia/django-digid-eherkenning.git@02ac61c42f6dd2f229ba9f0f687fa1a4160511be # via # -c requirements/base.txt # -r requirements/base.txt @@ -126,6 +126,7 @@ django==3.2.12 # djangorestframework # drf-spectacular # easy-thumbnails + # extended-admin-session # mail-editor # maykin-django-two-factor-auth # zgw-consumers @@ -332,6 +333,10 @@ et-xmlfile==1.1.0 # -c requirements/base.txt # -r requirements/base.txt # openpyxl +extended-admin-session==0.1.1 + # via + # -c requirements/base.txt + # -r requirements/base.txt face==20.1.1 # via # -c requirements/base.txt diff --git a/requirements/dev.txt b/requirements/dev.txt index 6154e58347..c3e4e0c80d 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -104,7 +104,7 @@ diff-match-patch==20200713 # -c requirements/ci.txt # -r requirements/ci.txt # django-import-export -digid-eherkenning @ git+https://bitbucket.org/maykinmedia/django-digid-eherkenning.git@02ac61c42f6dd2f229ba9f0f687fa1a4160511be +digid_eherkenning @ git+https://bitbucket.org/maykinmedia/django-digid-eherkenning.git@02ac61c42f6dd2f229ba9f0f687fa1a4160511be # via # -c requirements/ci.txt # -r requirements/ci.txt @@ -149,6 +149,7 @@ django==3.2.12 # djangorestframework # drf-spectacular # easy-thumbnails + # extended-admin-session # mail-editor # maykin-django-two-factor-auth # zgw-consumers @@ -365,6 +366,10 @@ et-xmlfile==1.1.0 # -c requirements/ci.txt # -r requirements/ci.txt # openpyxl +extended-admin-session==0.1.1 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt face==20.1.1 # via # -c requirements/ci.txt diff --git a/src/open_inwoner/conf/base.py b/src/open_inwoner/conf/base.py index d2b68f693c..2e7ddf25bc 100644 --- a/src/open_inwoner/conf/base.py +++ b/src/open_inwoner/conf/base.py @@ -147,6 +147,7 @@ "privates", "fontawesomefree", "timeline_logger", + "extended_admin_session", # Project applications. "open_inwoner.accounts", "open_inwoner.components", @@ -359,9 +360,10 @@ SESSION_COOKIE_NAME = "open_inwoner_sessionid" SESSION_ENGINE = "open_inwoner.session.backends.cache" -SESSION_COOKIE_AGE_ADMIN = 86400 +EXTENDED_ADMIN_SESSION_ON_STAFF_USER = True +EXTENDED_ADMIN_SESSION_COOKIE_AGE = 86400 +EXTENDED_ADMIN_SESSION_WARN_DELTA = 60 # Warn 1 minute before end of session. SESSION_COOKIE_AGE = 900 # Set to 15 minutes -SESSION_WARN_DELTA = 60 # Warn 1 minute before end of session. LOGIN_REDIRECT_URL = reverse_lazy("root") LOGOUT_REDIRECT_URL = reverse_lazy("root") diff --git a/src/open_inwoner/conf/dev.py b/src/open_inwoner/conf/dev.py index 94dea07b2c..4ca67cd398 100644 --- a/src/open_inwoner/conf/dev.py +++ b/src/open_inwoner/conf/dev.py @@ -25,8 +25,6 @@ # Standard Django settings. # SESSION_ENGINE = "open_inwoner.session.backends.cached_db" -# SESSION_COOKIE_AGE_ADMIN = 240 -# SESSION_COOKIE_AGE = 120 EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" # This is commented out because it causes tests in the CI to fail. It can be enabled in the local.py settings. diff --git a/src/open_inwoner/session/__init__.py b/src/open_inwoner/session/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/open_inwoner/session/backends/__init__.py b/src/open_inwoner/session/backends/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/open_inwoner/session/backends/base.py b/src/open_inwoner/session/backends/base.py deleted file mode 100644 index 524287211f..0000000000 --- a/src/open_inwoner/session/backends/base.py +++ /dev/null @@ -1,17 +0,0 @@ -from django.conf import settings - -from open_inwoner.accounts.models import User - - -class SplitSessionAge: - def get_session_cookie_age(self): - user_id = self.get("_auth_user_id") - - try: - is_staff = User.objects.get(pk=user_id).is_staff - except Exception: - is_staff = False - - if hasattr(settings, "SESSION_COOKIE_AGE_ADMIN") and is_staff: - return settings.SESSION_COOKIE_AGE_ADMIN - return settings.SESSION_COOKIE_AGE diff --git a/src/open_inwoner/session/backends/cache.py b/src/open_inwoner/session/backends/cache.py deleted file mode 100644 index a2527e8006..0000000000 --- a/src/open_inwoner/session/backends/cache.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.conf import settings -from django.contrib.sessions.backends.cache import SessionStore as BaseSessionStore - -from open_inwoner.accounts.models import User - -from .base import SplitSessionAge - - -class SessionStore(SplitSessionAge, BaseSessionStore): - pass diff --git a/src/open_inwoner/session/backends/cached_db.py b/src/open_inwoner/session/backends/cached_db.py deleted file mode 100644 index 53c17659c6..0000000000 --- a/src/open_inwoner/session/backends/cached_db.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.conf import settings -from django.contrib.sessions.backends.cached_db import SessionStore as BaseSessionStore - -from open_inwoner.accounts.models import User - -from .base import SplitSessionAge - - -class SessionStore(SplitSessionAge, BaseSessionStore): - pass diff --git a/src/open_inwoner/session/backends/db.py b/src/open_inwoner/session/backends/db.py deleted file mode 100644 index c6633afb8d..0000000000 --- a/src/open_inwoner/session/backends/db.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.conf import settings -from django.contrib.sessions.backends.db import SessionStore as BaseSessionStore - -from open_inwoner.accounts.models import User - -from .base import SplitSessionAge - - -class SessionStore(SplitSessionAge, BaseSessionStore): - pass diff --git a/src/open_inwoner/session/backends/file.py b/src/open_inwoner/session/backends/file.py deleted file mode 100644 index 73b85cbd55..0000000000 --- a/src/open_inwoner/session/backends/file.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.conf import settings -from django.contrib.sessions.backends.file import SessionStore as BaseSessionStore - -from open_inwoner.accounts.models import User - -from .base import SplitSessionAge - - -class SessionStore(SplitSessionAge, BaseSessionStore): - pass diff --git a/src/open_inwoner/session/backends/signed_cookies.py b/src/open_inwoner/session/backends/signed_cookies.py deleted file mode 100644 index 0a58d65d99..0000000000 --- a/src/open_inwoner/session/backends/signed_cookies.py +++ /dev/null @@ -1,12 +0,0 @@ -from django.conf import settings -from django.contrib.sessions.backends.signed_cookies import ( - SessionStore as BaseSessionStore, -) - -from open_inwoner.accounts.models import User - -from .base import SplitSessionAge - - -class SessionStore(SplitSessionAge, BaseSessionStore): - pass diff --git a/src/open_inwoner/session/templates/session/session_timeout.html b/src/open_inwoner/session/templates/session/session_timeout.html deleted file mode 100644 index 0434ccbdc0..0000000000 --- a/src/open_inwoner/session/templates/session/session_timeout.html +++ /dev/null @@ -1,10 +0,0 @@ -{% load l10n %} - -{% if user.is_authenticated %} -
-
-{% endif %} diff --git a/src/open_inwoner/session/templatetags/__init__.py b/src/open_inwoner/session/templatetags/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/open_inwoner/session/templatetags/session_tags.py b/src/open_inwoner/session/templatetags/session_tags.py deleted file mode 100644 index 663939009e..0000000000 --- a/src/open_inwoner/session/templatetags/session_tags.py +++ /dev/null @@ -1,17 +0,0 @@ -from django import template -from django.conf import settings - -register = template.Library() - - -@register.inclusion_tag("session/session_timeout.html", takes_context=True) -def session_timeout(context): - session = context["request"].session - context.update( - { - "expiry_age": session.get_expiry_age() - + 1, # Add a second to make sure the session has expired. - "warn_time": session.get_expiry_age() - settings.SESSION_WARN_DELTA, - } - ) - return context diff --git a/src/open_inwoner/session/urls.py b/src/open_inwoner/session/urls.py deleted file mode 100644 index f64f400df0..0000000000 --- a/src/open_inwoner/session/urls.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.urls import path - -from .views import RestartSessionView - -urlpatterns = [ - path("restart/", RestartSessionView.as_view(), name="restart-session"), -] diff --git a/src/open_inwoner/session/views.py b/src/open_inwoner/session/views.py deleted file mode 100644 index 997c5287c0..0000000000 --- a/src/open_inwoner/session/views.py +++ /dev/null @@ -1,23 +0,0 @@ -from django.http import HttpResponse -from django.views.generic import View - - -class RestartSessionView(View): - """ - This view is used from the SessionTimeout Javascript - class to determine if the user is logged in or not. - - This used to be done by doing a XMLHttpRequest to '/' and - checking the 'responseURL' if it was redirected to the - login page. However, Internet Explorer does not support - this method. - """ - - http_method_names = [ - "get", - ] - - def get(self, request): - if request.user.is_authenticated: - return HttpResponse("restarted") - return HttpResponse() diff --git a/src/open_inwoner/urls.py b/src/open_inwoner/urls.py index 9f10ddd7be..7a098aa716 100644 --- a/src/open_inwoner/urls.py +++ b/src/open_inwoner/urls.py @@ -59,7 +59,7 @@ "questionnaire/", include("open_inwoner.questionnaire.urls", namespace="questionnaire"), ), - path("sessions/", include("open_inwoner.session.urls"), name="sessions"), + path("sessions/", include("extended_admin_session.urls", namespace="extended")), path("faq/", FAQView.as_view(), name="general_faq"), path("", include("open_inwoner.pdc.urls", namespace="pdc")), path("", include("open_inwoner.search.urls", namespace="search")), diff --git a/src/open_inwoner/utils/sessions.py b/src/open_inwoner/utils/sessions.py deleted file mode 100644 index 2870fc8ba8..0000000000 --- a/src/open_inwoner/utils/sessions.py +++ /dev/null @@ -1 +0,0 @@ -from django.contrib.sessions.middleware import SessionMiddleware From 1a243fd014b488259c49188d4216c7129e71b3a0 Mon Sep 17 00:00:00 2001 From: Jorik Kraaikamp Date: Wed, 4 May 2022 15:52:52 +0200 Subject: [PATCH 06/11] Overwritten the session --- src/open_inwoner/conf/base.py | 2 +- src/open_inwoner/conf/dev.py | 2 +- src/open_inwoner/conf/production.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/open_inwoner/conf/base.py b/src/open_inwoner/conf/base.py index 2e7ddf25bc..4cfecd92f4 100644 --- a/src/open_inwoner/conf/base.py +++ b/src/open_inwoner/conf/base.py @@ -359,7 +359,7 @@ ] SESSION_COOKIE_NAME = "open_inwoner_sessionid" -SESSION_ENGINE = "open_inwoner.session.backends.cache" +SESSION_ENGINE = "extended_admin_session.backends.cache" EXTENDED_ADMIN_SESSION_ON_STAFF_USER = True EXTENDED_ADMIN_SESSION_COOKIE_AGE = 86400 EXTENDED_ADMIN_SESSION_WARN_DELTA = 60 # Warn 1 minute before end of session. diff --git a/src/open_inwoner/conf/dev.py b/src/open_inwoner/conf/dev.py index 4ca67cd398..00526b136d 100644 --- a/src/open_inwoner/conf/dev.py +++ b/src/open_inwoner/conf/dev.py @@ -24,7 +24,7 @@ # # Standard Django settings. # -SESSION_ENGINE = "open_inwoner.session.backends.cached_db" +SESSION_ENGINE = "extended_admin_session.backends.cached_db" EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" # This is commented out because it causes tests in the CI to fail. It can be enabled in the local.py settings. diff --git a/src/open_inwoner/conf/production.py b/src/open_inwoner/conf/production.py index 7e2ec41fd2..880efc9af4 100644 --- a/src/open_inwoner/conf/production.py +++ b/src/open_inwoner/conf/production.py @@ -24,7 +24,7 @@ db_config["CONN_MAX_AGE"] = 60 # Lifetime of a database connection for performance. # Caching sessions. -SESSION_ENGINE = "open_inwoner.session.backends.cache" +SESSION_ENGINE = "extended_admin_session.backends.cache" SESSION_CACHE_ALIAS = "default" # Caching templates. From 7922b0c7ff50190ddc68c1f218231b8ea437286c Mon Sep 17 00:00:00 2001 From: Jorik Kraaikamp Date: Wed, 4 May 2022 15:56:11 +0200 Subject: [PATCH 07/11] removed open_inwoner.session --- src/open_inwoner/conf/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/open_inwoner/conf/base.py b/src/open_inwoner/conf/base.py index 4cfecd92f4..8b7829bf48 100644 --- a/src/open_inwoner/conf/base.py +++ b/src/open_inwoner/conf/base.py @@ -160,7 +160,6 @@ "open_inwoner.haalcentraal", "open_inwoner.openzaak", "open_inwoner.questionnaire", - "open_inwoner.session", ] MIDDLEWARE = [ From 8850780b13f985c34111bea52ea535428568a73e Mon Sep 17 00:00:00 2001 From: Jorik Kraaikamp Date: Wed, 4 May 2022 15:56:35 +0200 Subject: [PATCH 08/11] Fixed digid to pypi package --- requirements/base.in | 2 +- requirements/base.txt | 15 +++++++++------ requirements/ci.txt | 16 +++++++++------- requirements/dev.txt | 16 +++++++++------- 4 files changed, 28 insertions(+), 21 deletions(-) diff --git a/requirements/base.in b/requirements/base.in index 0d8e7e4134..d4d2dfa18c 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -25,7 +25,7 @@ maykin-django-two-factor-auth phonenumbers django-localflavor django-privates -git+https://bitbucket.org/maykinmedia/django-digid-eherkenning.git@02ac61c42f6dd2f229ba9f0f687fa1a4160511be#egg=digid_eherkenning +django-digid-eherkenning django-cors-headers dj-rest-auth django-allauth diff --git a/requirements/base.txt b/requirements/base.txt index 77e64a42fb..a0d0cea589 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -40,25 +40,23 @@ cssselect2==0.4.1 # via weasyprint defusedxml==0.7.1 # via - # digid-eherkenning + # django-digid-eherkenning # odfpy # python3-openid diff-match-patch==20200713 # via django-import-export -digid_eherkenning @ git+https://bitbucket.org/maykinmedia/django-digid-eherkenning.git@02ac61c42f6dd2f229ba9f0f687fa1a4160511be - # via -r requirements/base.in dj-rest-auth==2.1.11 # via -r requirements/base.in django==3.2.12 # via # -r requirements/base.in - # digid-eherkenning # dj-rest-auth # django-allauth # django-appconf # django-axes # django-choices # django-cors-headers + # django-digid-eherkenning # django-extra-fields # django-filer # django-filter @@ -101,7 +99,7 @@ django-better-admin-arrayfield==1.4.2 django-choices==1.7.2 # via # -r requirements/base.in - # digid-eherkenning + # django-digid-eherkenning # mail-editor # zgw-consumers django-ckeditor==6.2.0 @@ -110,6 +108,8 @@ django-colorfield==0.4.5 # via -r requirements/base.in django-cors-headers==3.10.0 # via -r requirements/base.in +django-digid-eherkenning==0.3.1 + # via -r requirements/base.in django-elasticsearch-dsl==7.2.1 # via -r requirements/base.in django-extra-fields==3.0.2 @@ -201,7 +201,9 @@ fontawesomefree==6.1.1 fonttools[woff]==4.29.1 # via weasyprint furl==2.1.3 - # via -r requirements/base.in + # via + # -r requirements/base.in + # django-digid-eherkenning gemma-zds-client==1.0.1 # via zgw-consumers geographiclib==1.52 @@ -226,6 +228,7 @@ jsonschema==4.1.0 # via drf-spectacular lxml==4.6.3 # via + # django-digid-eherkenning # python3-saml # xmlsec mail-editor @ git+https://github.com/maykinmedia/mail-editor.git@0b4621b5c7f434586115b8e722af8940cfa70195 diff --git a/requirements/ci.txt b/requirements/ci.txt index 596b9b955c..fc0ec9acc2 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -76,7 +76,7 @@ defusedxml==0.7.1 # via # -c requirements/base.txt # -r requirements/base.txt - # digid-eherkenning + # django-digid-eherkenning # odfpy # python3-openid diff-match-patch==20200713 @@ -84,10 +84,6 @@ diff-match-patch==20200713 # -c requirements/base.txt # -r requirements/base.txt # django-import-export -digid_eherkenning @ git+https://bitbucket.org/maykinmedia/django-digid-eherkenning.git@02ac61c42f6dd2f229ba9f0f687fa1a4160511be - # via - # -c requirements/base.txt - # -r requirements/base.txt dj-rest-auth==2.1.11 # via # -c requirements/base.txt @@ -96,13 +92,13 @@ django==3.2.12 # via # -c requirements/base.txt # -r requirements/base.txt - # digid-eherkenning # dj-rest-auth # django-allauth # django-appconf # django-axes # django-choices # django-cors-headers + # django-digid-eherkenning # django-extra-fields # django-filer # django-filter @@ -159,7 +155,7 @@ django-choices==1.7.2 # via # -c requirements/base.txt # -r requirements/base.txt - # digid-eherkenning + # django-digid-eherkenning # mail-editor # zgw-consumers django-ckeditor==6.2.0 @@ -175,6 +171,10 @@ django-cors-headers==3.10.0 # via # -c requirements/base.txt # -r requirements/base.txt +django-digid-eherkenning==0.3.1 + # via + # -c requirements/base.txt + # -r requirements/base.txt django-elasticsearch-dsl==7.2.1 # via # -c requirements/base.txt @@ -365,6 +365,7 @@ furl==2.1.3 # via # -c requirements/base.txt # -r requirements/base.txt + # django-digid-eherkenning gemma-zds-client==1.0.1 # via # -c requirements/base.txt @@ -425,6 +426,7 @@ lxml==4.6.3 # via # -c requirements/base.txt # -r requirements/base.txt + # django-digid-eherkenning # pyquery # python3-saml # xmlsec diff --git a/requirements/dev.txt b/requirements/dev.txt index c3e4e0c80d..9a6d7798cf 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -96,7 +96,7 @@ defusedxml==0.7.1 # via # -c requirements/ci.txt # -r requirements/ci.txt - # digid-eherkenning + # django-digid-eherkenning # odfpy # python3-openid diff-match-patch==20200713 @@ -104,10 +104,6 @@ diff-match-patch==20200713 # -c requirements/ci.txt # -r requirements/ci.txt # django-import-export -digid_eherkenning @ git+https://bitbucket.org/maykinmedia/django-digid-eherkenning.git@02ac61c42f6dd2f229ba9f0f687fa1a4160511be - # via - # -c requirements/ci.txt - # -r requirements/ci.txt dj-rest-auth==2.1.11 # via # -c requirements/ci.txt @@ -117,7 +113,6 @@ django==3.2.12 # -c requirements/ci.txt # -r requirements/ci.txt # ddt-api-calls - # digid-eherkenning # dj-rest-auth # django-allauth # django-appconf @@ -125,6 +120,7 @@ django==3.2.12 # django-choices # django-cors-headers # django-debug-toolbar + # django-digid-eherkenning # django-extensions # django-extra-fields # django-filer @@ -182,7 +178,7 @@ django-choices==1.7.2 # via # -c requirements/ci.txt # -r requirements/ci.txt - # digid-eherkenning + # django-digid-eherkenning # mail-editor # zgw-consumers django-ckeditor==6.2.0 @@ -200,6 +196,10 @@ django-cors-headers==3.10.0 # -r requirements/ci.txt django-debug-toolbar==3.2.2 # via -r requirements/dev.in +django-digid-eherkenning==0.3.1 + # via + # -c requirements/ci.txt + # -r requirements/ci.txt django-elasticsearch-dsl==7.2.1 # via # -c requirements/ci.txt @@ -404,6 +404,7 @@ furl==2.1.3 # via # -c requirements/ci.txt # -r requirements/ci.txt + # django-digid-eherkenning gemma-zds-client==1.0.1 # via # -c requirements/ci.txt @@ -475,6 +476,7 @@ lxml==4.6.3 # via # -c requirements/ci.txt # -r requirements/ci.txt + # django-digid-eherkenning # pyquery # python3-saml # xmlsec From 4f35591909c7aa3e00c20b6096c59312d31015ac Mon Sep 17 00:00:00 2001 From: Jorik Kraaikamp Date: Mon, 9 May 2022 09:52:00 +0200 Subject: [PATCH 09/11] improved sessions --- requirements/base.in | 1 - requirements/base.txt | 3 -- requirements/ci.txt | 5 --- requirements/dev.txt | 5 --- src/open_inwoner/conf/base.py | 10 +++--- src/open_inwoner/conf/dev.py | 2 +- src/open_inwoner/conf/production.py | 2 +- .../extended_sessions/__init__.py | 0 .../extended_sessions/middleware.py | 29 ++++++++++++++++ .../templates/sessions/session_timeout.html | 10 ++++++ .../templatetags/__init__.py | 0 .../templatetags/session_tags.py | 18 ++++++++++ .../extended_sessions/tests/__init__.py | 0 .../tests/test_extended_session.py | 33 +++++++++++++++++++ .../extended_sessions/tests/test_views.py | 22 +++++++++++++ src/open_inwoner/extended_sessions/urls.py | 8 +++++ src/open_inwoner/extended_sessions/views.py | 24 ++++++++++++++ src/open_inwoner/urls.py | 2 +- 18 files changed, 152 insertions(+), 22 deletions(-) create mode 100644 src/open_inwoner/extended_sessions/__init__.py create mode 100644 src/open_inwoner/extended_sessions/middleware.py create mode 100644 src/open_inwoner/extended_sessions/templates/sessions/session_timeout.html create mode 100644 src/open_inwoner/extended_sessions/templatetags/__init__.py create mode 100644 src/open_inwoner/extended_sessions/templatetags/session_tags.py create mode 100644 src/open_inwoner/extended_sessions/tests/__init__.py create mode 100644 src/open_inwoner/extended_sessions/tests/test_extended_session.py create mode 100644 src/open_inwoner/extended_sessions/tests/test_views.py create mode 100644 src/open_inwoner/extended_sessions/urls.py create mode 100644 src/open_inwoner/extended_sessions/views.py diff --git a/requirements/base.in b/requirements/base.in index d4d2dfa18c..15de1a0218 100644 --- a/requirements/base.in +++ b/requirements/base.in @@ -42,7 +42,6 @@ humanfriendly git+https://github.com/maykinmedia/mail-editor.git@0b4621b5c7f434586115b8e722af8940cfa70195#egg=mail-editor fontawesomefree django-timeline-logger -extended-admin-session # API libraries djangorestframework diff --git a/requirements/base.txt b/requirements/base.txt index a0d0cea589..8b92cb1635 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -80,7 +80,6 @@ django==3.2.12 # djangorestframework # drf-spectacular # easy-thumbnails - # extended-admin-session # mail-editor # maykin-django-two-factor-auth # zgw-consumers @@ -190,8 +189,6 @@ elasticsearch-dsl==7.4.0 # via django-elasticsearch-dsl et-xmlfile==1.1.0 # via openpyxl -extended-admin-session==0.1.1 - # via -r requirements/base.in face==20.1.1 # via glom faker==9.9.0 diff --git a/requirements/ci.txt b/requirements/ci.txt index fc0ec9acc2..f835842991 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -122,7 +122,6 @@ django==3.2.12 # djangorestframework # drf-spectacular # easy-thumbnails - # extended-admin-session # mail-editor # maykin-django-two-factor-auth # zgw-consumers @@ -333,10 +332,6 @@ et-xmlfile==1.1.0 # -c requirements/base.txt # -r requirements/base.txt # openpyxl -extended-admin-session==0.1.1 - # via - # -c requirements/base.txt - # -r requirements/base.txt face==20.1.1 # via # -c requirements/base.txt diff --git a/requirements/dev.txt b/requirements/dev.txt index 9a6d7798cf..aea1e842d1 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -145,7 +145,6 @@ django==3.2.12 # djangorestframework # drf-spectacular # easy-thumbnails - # extended-admin-session # mail-editor # maykin-django-two-factor-auth # zgw-consumers @@ -366,10 +365,6 @@ et-xmlfile==1.1.0 # -c requirements/ci.txt # -r requirements/ci.txt # openpyxl -extended-admin-session==0.1.1 - # via - # -c requirements/ci.txt - # -r requirements/ci.txt face==20.1.1 # via # -c requirements/ci.txt diff --git a/src/open_inwoner/conf/base.py b/src/open_inwoner/conf/base.py index 8b7829bf48..8fbf8cbf9c 100644 --- a/src/open_inwoner/conf/base.py +++ b/src/open_inwoner/conf/base.py @@ -147,7 +147,6 @@ "privates", "fontawesomefree", "timeline_logger", - "extended_admin_session", # Project applications. "open_inwoner.accounts", "open_inwoner.components", @@ -160,6 +159,7 @@ "open_inwoner.haalcentraal", "open_inwoner.openzaak", "open_inwoner.questionnaire", + "open_inwoner.extended_sessions", ] MIDDLEWARE = [ @@ -176,6 +176,7 @@ "hijack.middleware.HijackUserMiddleware", "django_otp.middleware.OTPMiddleware", "django.contrib.flatpages.middleware.FlatpageFallbackMiddleware", + "open_inwoner.extended_sessions.middleware.SessionTimeoutMiddleware", ] ROOT_URLCONF = "open_inwoner.urls" @@ -358,10 +359,9 @@ ] SESSION_COOKIE_NAME = "open_inwoner_sessionid" -SESSION_ENGINE = "extended_admin_session.backends.cache" -EXTENDED_ADMIN_SESSION_ON_STAFF_USER = True -EXTENDED_ADMIN_SESSION_COOKIE_AGE = 86400 -EXTENDED_ADMIN_SESSION_WARN_DELTA = 60 # Warn 1 minute before end of session. +SESSION_ENGINE = "django.contrib.sessions.backends.cache" +ADMIN_SESSION_COOKIE_AGE = 86400 +SESSION_WARN_DELTA = 60 # Warn 1 minute before end of session. SESSION_COOKIE_AGE = 900 # Set to 15 minutes LOGIN_REDIRECT_URL = reverse_lazy("root") diff --git a/src/open_inwoner/conf/dev.py b/src/open_inwoner/conf/dev.py index 00526b136d..e75c4d4e96 100644 --- a/src/open_inwoner/conf/dev.py +++ b/src/open_inwoner/conf/dev.py @@ -24,7 +24,7 @@ # # Standard Django settings. # -SESSION_ENGINE = "extended_admin_session.backends.cached_db" +SESSION_ENGINE = "django.contrib.sessions.backends.cached_db" EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" # This is commented out because it causes tests in the CI to fail. It can be enabled in the local.py settings. diff --git a/src/open_inwoner/conf/production.py b/src/open_inwoner/conf/production.py index 880efc9af4..f9b93d9e2e 100644 --- a/src/open_inwoner/conf/production.py +++ b/src/open_inwoner/conf/production.py @@ -24,7 +24,7 @@ db_config["CONN_MAX_AGE"] = 60 # Lifetime of a database connection for performance. # Caching sessions. -SESSION_ENGINE = "extended_admin_session.backends.cache" +SESSION_ENGINE = "django.contrib.sessions.backends.cache" SESSION_CACHE_ALIAS = "default" # Caching templates. diff --git a/src/open_inwoner/extended_sessions/__init__.py b/src/open_inwoner/extended_sessions/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/open_inwoner/extended_sessions/middleware.py b/src/open_inwoner/extended_sessions/middleware.py new file mode 100644 index 0000000000..ee16698232 --- /dev/null +++ b/src/open_inwoner/extended_sessions/middleware.py @@ -0,0 +1,29 @@ +from datetime import timedelta + +from django.conf import settings + + +SESSION_EXPIRES_IN_HEADER = "X-Session-Expires-In" + + +class SessionTimeoutMiddleware: + """ + Allows us to set the expiry time of the session based on what + is configured in our GlobalConfiguration + """ + + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + timeout = ( + settings.ADMIN_SESSION_COOKIE_AGE + if request.user.is_staff + else settings.SESSION_COOKIE_AGE + ) + # https://docs.djangoproject.com/en/2.2/topics/http/sessions/#django.contrib.sessions.backends.base.SessionBase.set_expiry + print(request.session) + request.session.set_expiry(timeout) + response = self.get_response(request) + response[SESSION_EXPIRES_IN_HEADER] = timeout + return response diff --git a/src/open_inwoner/extended_sessions/templates/sessions/session_timeout.html b/src/open_inwoner/extended_sessions/templates/sessions/session_timeout.html new file mode 100644 index 0000000000..0434ccbdc0 --- /dev/null +++ b/src/open_inwoner/extended_sessions/templates/sessions/session_timeout.html @@ -0,0 +1,10 @@ +{% load l10n %} + +{% if user.is_authenticated %} +
+
+{% endif %} diff --git a/src/open_inwoner/extended_sessions/templatetags/__init__.py b/src/open_inwoner/extended_sessions/templatetags/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/open_inwoner/extended_sessions/templatetags/session_tags.py b/src/open_inwoner/extended_sessions/templatetags/session_tags.py new file mode 100644 index 0000000000..45568b5ac6 --- /dev/null +++ b/src/open_inwoner/extended_sessions/templatetags/session_tags.py @@ -0,0 +1,18 @@ +from django import template +from django.conf import settings + +register = template.Library() + + +@register.inclusion_tag("sessions/session_timeout.html", takes_context=True) +def session_timeout(context): + session = context["request"].session + context.update( + { + "expiry_age": session.get_expiry_age() + + 1, # Add a second to make sure the session has expired. + "warn_time": session.get_expiry_age() + - settings.SESSION_WARN_DELTA, + } + ) + return context diff --git a/src/open_inwoner/extended_sessions/tests/__init__.py b/src/open_inwoner/extended_sessions/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/open_inwoner/extended_sessions/tests/test_extended_session.py b/src/open_inwoner/extended_sessions/tests/test_extended_session.py new file mode 100644 index 0000000000..43ddf71895 --- /dev/null +++ b/src/open_inwoner/extended_sessions/tests/test_extended_session.py @@ -0,0 +1,33 @@ +from django.conf import settings +from django.urls import reverse + +from django_webtest import WebTest + +from open_inwoner.accounts.tests.factories import UserFactory + + +class SessionBackendTest(WebTest): + def setUp(self): + self.url = reverse("sessions:restart-session") + + def test_default_session_length_when_not_logged_in(self): + response = self.app.get(self.url) + self.assertEqual(response.status_code, 200) + self.assertEqual(self.app.session._get_session().get("_session_expiry"), 900) + + def test_default_session_length_when_regular_user(self): + user = UserFactory(is_staff=False) + response = self.app.get(self.url, user=user) + self.assertEqual(response.status_code, 200) + self.assertEqual( + self.app.session._get_session().get("_session_expiry"), settings.SESSION_COOKIE_AGE + ) + + def test_extended_session_length_when_staff_user(self): + user = UserFactory(is_staff=True) + response = self.app.get(self.url, user=user) + self.assertEqual(response.status_code, 200) + self.assertEqual( + self.app.session._get_session().get("_session_expiry"), + settings.ADMIN_SESSION_COOKIE_AGE, + ) diff --git a/src/open_inwoner/extended_sessions/tests/test_views.py b/src/open_inwoner/extended_sessions/tests/test_views.py new file mode 100644 index 0000000000..add8d7a45d --- /dev/null +++ b/src/open_inwoner/extended_sessions/tests/test_views.py @@ -0,0 +1,22 @@ +from django.test import TestCase +from django.urls import reverse + +from django_webtest import WebTest + +from open_inwoner.accounts.tests.factories import UserFactory + + +class ViewTest(WebTest): + def setUp(self): + self.url = reverse("sessions:restart-session") + + def test_when_not_logged_in(self): + response = self.app.get(self.url) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, b"") + + def test_when_logged_in(self): + user = UserFactory() + response = self.app.get(self.url, user=user) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, b"restarted") diff --git a/src/open_inwoner/extended_sessions/urls.py b/src/open_inwoner/extended_sessions/urls.py new file mode 100644 index 0000000000..461b9bd711 --- /dev/null +++ b/src/open_inwoner/extended_sessions/urls.py @@ -0,0 +1,8 @@ +from django.urls import path + +from .views import RestartSessionView + +app_name = "sessions" +urlpatterns = [ + path("restart/", RestartSessionView.as_view(), name="restart-session"), +] diff --git a/src/open_inwoner/extended_sessions/views.py b/src/open_inwoner/extended_sessions/views.py new file mode 100644 index 0000000000..c4ab7be251 --- /dev/null +++ b/src/open_inwoner/extended_sessions/views.py @@ -0,0 +1,24 @@ +from django.http import HttpResponse +from django.views.generic import View + + +class RestartSessionView(View): + """ + This view is used from the SessionTimeout Javascript + class to determine if the user is logged in or not. + + This used to be done by doing a XMLHttpRequest to '/' and + checking the 'responseURL' if it was redirected to the + login page. However, Internet Explorer does not support + this method. + """ + + http_method_names = [ + "get", + ] + + def get(self, request): + print(request.user) + if request.user.is_authenticated: + return HttpResponse("restarted") + return HttpResponse() diff --git a/src/open_inwoner/urls.py b/src/open_inwoner/urls.py index 7a098aa716..d2771cffc7 100644 --- a/src/open_inwoner/urls.py +++ b/src/open_inwoner/urls.py @@ -59,7 +59,7 @@ "questionnaire/", include("open_inwoner.questionnaire.urls", namespace="questionnaire"), ), - path("sessions/", include("extended_admin_session.urls", namespace="extended")), + path("sessions/", include("open_inwoner.extended_sessions.urls", namespace="sessions")), path("faq/", FAQView.as_view(), name="general_faq"), path("", include("open_inwoner.pdc.urls", namespace="pdc")), path("", include("open_inwoner.search.urls", namespace="search")), From d663b9445f197f1c6252ab3c62a7bd6ae276afac Mon Sep 17 00:00:00 2001 From: Jorik Kraaikamp Date: Mon, 9 May 2022 09:56:59 +0200 Subject: [PATCH 10/11] fixed the linting issues --- src/open_inwoner/extended_sessions/middleware.py | 1 - .../extended_sessions/templatetags/session_tags.py | 3 +-- .../extended_sessions/tests/test_extended_session.py | 3 ++- src/open_inwoner/urls.py | 5 ++++- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/open_inwoner/extended_sessions/middleware.py b/src/open_inwoner/extended_sessions/middleware.py index ee16698232..a40e894caf 100644 --- a/src/open_inwoner/extended_sessions/middleware.py +++ b/src/open_inwoner/extended_sessions/middleware.py @@ -2,7 +2,6 @@ from django.conf import settings - SESSION_EXPIRES_IN_HEADER = "X-Session-Expires-In" diff --git a/src/open_inwoner/extended_sessions/templatetags/session_tags.py b/src/open_inwoner/extended_sessions/templatetags/session_tags.py index 45568b5ac6..af073e9582 100644 --- a/src/open_inwoner/extended_sessions/templatetags/session_tags.py +++ b/src/open_inwoner/extended_sessions/templatetags/session_tags.py @@ -11,8 +11,7 @@ def session_timeout(context): { "expiry_age": session.get_expiry_age() + 1, # Add a second to make sure the session has expired. - "warn_time": session.get_expiry_age() - - settings.SESSION_WARN_DELTA, + "warn_time": session.get_expiry_age() - settings.SESSION_WARN_DELTA, } ) return context diff --git a/src/open_inwoner/extended_sessions/tests/test_extended_session.py b/src/open_inwoner/extended_sessions/tests/test_extended_session.py index 43ddf71895..f5489e9fb6 100644 --- a/src/open_inwoner/extended_sessions/tests/test_extended_session.py +++ b/src/open_inwoner/extended_sessions/tests/test_extended_session.py @@ -20,7 +20,8 @@ def test_default_session_length_when_regular_user(self): response = self.app.get(self.url, user=user) self.assertEqual(response.status_code, 200) self.assertEqual( - self.app.session._get_session().get("_session_expiry"), settings.SESSION_COOKIE_AGE + self.app.session._get_session().get("_session_expiry"), + settings.SESSION_COOKIE_AGE, ) def test_extended_session_length_when_staff_user(self): diff --git a/src/open_inwoner/urls.py b/src/open_inwoner/urls.py index d2771cffc7..539720e029 100644 --- a/src/open_inwoner/urls.py +++ b/src/open_inwoner/urls.py @@ -59,7 +59,10 @@ "questionnaire/", include("open_inwoner.questionnaire.urls", namespace="questionnaire"), ), - path("sessions/", include("open_inwoner.extended_sessions.urls", namespace="sessions")), + path( + "sessions/", + include("open_inwoner.extended_sessions.urls", namespace="sessions"), + ), path("faq/", FAQView.as_view(), name="general_faq"), path("", include("open_inwoner.pdc.urls", namespace="pdc")), path("", include("open_inwoner.search.urls", namespace="search")), From 7dbb7eb758a986b6dc492933fd9e53766fa86daa Mon Sep 17 00:00:00 2001 From: Jorik Kraaikamp Date: Mon, 9 May 2022 17:12:30 +0200 Subject: [PATCH 11/11] Removed 2 print statements --- src/open_inwoner/extended_sessions/middleware.py | 1 - src/open_inwoner/extended_sessions/views.py | 1 - 2 files changed, 2 deletions(-) diff --git a/src/open_inwoner/extended_sessions/middleware.py b/src/open_inwoner/extended_sessions/middleware.py index a40e894caf..9b4ac62672 100644 --- a/src/open_inwoner/extended_sessions/middleware.py +++ b/src/open_inwoner/extended_sessions/middleware.py @@ -21,7 +21,6 @@ def __call__(self, request): else settings.SESSION_COOKIE_AGE ) # https://docs.djangoproject.com/en/2.2/topics/http/sessions/#django.contrib.sessions.backends.base.SessionBase.set_expiry - print(request.session) request.session.set_expiry(timeout) response = self.get_response(request) response[SESSION_EXPIRES_IN_HEADER] = timeout diff --git a/src/open_inwoner/extended_sessions/views.py b/src/open_inwoner/extended_sessions/views.py index c4ab7be251..997c5287c0 100644 --- a/src/open_inwoner/extended_sessions/views.py +++ b/src/open_inwoner/extended_sessions/views.py @@ -18,7 +18,6 @@ class to determine if the user is logged in or not. ] def get(self, request): - print(request.user) if request.user.is_authenticated: return HttpResponse("restarted") return HttpResponse()