diff --git a/backend/areas/users/users.py b/backend/areas/users/users.py index 08f22c6f5f9266e6efc2c9dbfcfcb115a6501388..b1740ebd57eba73348a1ac4127d2f8652261368c 100644 --- a/backend/areas/users/users.py +++ b/backend/areas/users/users.py @@ -1,7 +1,7 @@ from flask import jsonify, request -from flask_jwt_extended import get_jwt, jwt_required from flask_cors import cross_origin from flask_expects_json import expects_json +from flask_jwt_extended import get_jwt, jwt_required from areas import api_v1 from helpers import KratosApi diff --git a/backend/config.py b/backend/config.py index 1972c59497b1229589837c66ca6973bb1e7fd408..6e5d37ac1221925e8403612674225bd0ccf5314d 100644 --- a/backend/config.py +++ b/backend/config.py @@ -21,3 +21,5 @@ SQLALCHEMY_TRACK_MODIFICATIONS = False # running in a Kubernetes pod. Set it to "false" to load the config from the # `KUBECONFIG` environment variable. LOAD_INCLUSTER_CONFIG = os.environ.get("LOAD_INCLUSTER_CONFIG").lower() == "true" + +DEMO_INSTANCE = os.environ.get("DASHBOARD_DEMO_INSTANCE", "False").lower() in ('true', '1') diff --git a/backend/web/login/login.py b/backend/web/login/login.py index 12d4ff1aecaa1aa69fc960ff9033aa30b09006bf..25f09a006ace2a225fdf17d3f674a810a6a64fa4 100644 --- a/backend/web/login/login.py +++ b/backend/web/login/login.py @@ -16,7 +16,7 @@ from ory_hydra_client.models import AcceptConsentRequest, AcceptLoginRequest, Co import ory_hydra_client.exceptions as hydra_exceptions import ory_kratos_client from ory_kratos_client.api import frontend_api, identity_api -from flask import abort, redirect, render_template, request, current_app +from flask import abort, current_app, jsonify, redirect, render_template, request from database import db from helpers import KratosUser @@ -24,6 +24,8 @@ from config import * from web import web from areas.apps import AppRole, App, OAuthClientApp from areas.roles import RoleService +from areas.roles.models import Role +from areas.users.user_service import UserService # This is a circular import and should be solved differently @@ -159,7 +161,7 @@ def login(): # or `not identity` # User is not logged in yet. # In either case, we present the login screen now. - return render_template("login.html", api_url=KRATOS_PUBLIC_URL, dashboard_url=DASHBOARD_URL, refresh=refresh) + return render_template("login.html", api_url=KRATOS_PUBLIC_URL, dashboard_url=DASHBOARD_URL, refresh=refresh, demo=DEMO_INSTANCE) @web.route("/auth", methods=["GET", "POST"]) @@ -543,3 +545,14 @@ def logout(): ex) return redirect(kratos_api_response.logout_url) + +if DEMO_INSTANCE: + @web.route("/demo-user", methods=["POST"]) + def demo_user(): + data = request.get_json() + defaults = { + "name": "", + "app_roles": [{"name": "dashboard", "role_id": Role.ADMIN_ROLE_ID}], + } + UserService.post_user({**defaults, **data}) + return jsonify("User created successfully. You should receive an email to confirm your address and set a password.") diff --git a/backend/web/templates/login.html b/backend/web/templates/login.html index 515472f1103544e17d36b44d9d67665c09db8c0d..11d9f8a5315dfb97662604ad906c3022e2729971 100644 --- a/backend/web/templates/login.html +++ b/backend/web/templates/login.html @@ -24,5 +24,63 @@ <div id="contentHelp"> <a href='recovery'>Set new password</a> | <a href='https://stackspin.net'>About stackspin</a> </div> +{% if demo %} + <br> + <script> + function submitSignup() { + let result = document.querySelector('#signup-result'); + let email = document.querySelector('#signup-email'); + let xhr = new XMLHttpRequest(); + xhr.responseType = 'json'; + let url = "/web/demo-user"; + xhr.open("POST", url, true); + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + // In the success case, we get a plain (json) string; in the error + // case, we get an object with `errorMessage` property. + if (typeof(this.response) == 'object' && 'errorMessage' in this.response) { + window.console.log("Error in sign-up request."); + result.classList.remove('alert-success'); + result.classList.add('alert-danger'); + result.innerHTML = this.response.errorMessage; + } else { + result.classList.add('alert-success'); + result.classList.remove('alert-danger'); + result.innerHTML = this.response; + } + result.style.visibility = 'visible'; + } + }; + // Converting JSON data to string + var data = JSON.stringify({"email": email.value }); + // Sending data with the request + xhr.send(data); + } + </script> + <h4>Sign up for this demo instance</h4> + Enter your email address here to create an account on this Stackspin + instance. + <div class="alert alert-warning" style="margin-top: 1em;"> + Warning: this is a demo instance! That means that: + <ul> + <li>Anyone can create an account on this same instance, like yourself, + and will share the same set of users and data. So any data you create + or upload, including the email address you enter here, becomes + essentially public information.</li> + <li>Every night (Europe/Amsterdam time), this instance gets automatically + reset to an empty state, so any data you create or upload will be + destroyed.</li> + </ul> + </div> + <div class="form-group"> + <label for="signup-email">Email address</label> + <input type="email" class="form-control" id="signup-email" name="signup-email" placeholder="Your email address to sign up with."> + </div> + <div class="form-group"> + <button class="btn btn-primary" onclick="submitSignup()">Sign up</button> + <div id="signup-result" class="alert" style="visibility: hidden; margin-top: 1em;"></div> + </div> +{% endif %} {% endblock %}