This is an automated email from the ASF dual-hosted git repository.
gstein pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/steve.git
The following commit(s) were added to refs/heads/trunk by this push:
new d836a6d Add sign-in/out management to the webapp.
d836a6d is described below
commit d836a6d39527b03ccb4ffe4adb455c3327802f1a
Author: Greg Stein <[email protected]>
AuthorDate: Mon Sep 29 13:46:35 2025 -0500
Add sign-in/out management to the webapp.
* pages.py:
(signin_info): set up basic template data for the signed-in-state
(*): all handlers now include the sign-in info in their result dict
(sign_out): clear the session and redirect to Home
(sign_in): new func to force a sign-in
* header.ezt:
- use the .svg for the icon in the navbar
- switch on UID to display a sign-in link, or the name/dropdown
---
v3/server/pages.py | 90 +++++++++++++++++++++++++++++-------------
v3/server/templates/header.ezt | 15 ++++++-
2 files changed, 75 insertions(+), 30 deletions(-)
diff --git a/v3/server/pages.py b/v3/server/pages.py
index 757745e..33f2174 100644
--- a/v3/server/pages.py
+++ b/v3/server/pages.py
@@ -21,7 +21,7 @@ import pathlib
from easydict import EasyDict as edict
import asfpy.stopwatch
import quart
-import asfquart
+import asfquart.session
from asfquart.auth import Requirements as R
APP = asfquart.APP
@@ -33,12 +33,27 @@ sys.path.insert(0, str(THIS_DIR.parent))
import steve.election
+async def signin_info():
+ "Return EZT template data for the Sign-In, in the upper right."
+ s = await asfquart.session.read()
+ if s:
+ return {
+ 'uid': s['uid'],
+ 'name': s['fullname'],
+ 'email': s['email'],
+ }
+
+ # No session.
+ return { 'uid': None, 'name': None, 'email': None, }
+
+
@APP.get('/')
@APP.use_template('templates/home.ezt')
async def home_page():
- return {
- 'title': 'Home',
- }
+ result = edict(await signin_info())
+ result.title = 'Home'
+
+ return result
@APP.get('/voter')
@@ -49,64 +64,83 @@ async def voter_page():
election = steve.election.Election.open_to_pid(DB_FNAME, 'gstein')
owned = steve.election.Election.owned_elections(DB_FNAME, 'gstein')
- election = [ edict(eid='123', title='test election') ]
- owned = [ edict(eid='456', title='another', authz=None, closed=None) ]
+ result = edict(await signin_info())
+ result.title = 'Voting'
- return {
- 'title': 'Voting',
- 'election': election,
- 'owned': owned,
- }
+ result.election = [ edict(eid='123', title='test election') ]
+ result.owned = [ edict(eid='456', title='another', authz=None,
closed=None) ]
+ return result
+
+### NOTE: this is for ASF committers only. Obviously, this is not a
+### general purpose solution. Something for the future, to figure out
+### how we'd like to do configuration authorization for various install
+### scenarios and authn systems.
@APP.get('/admin')
@asfquart.auth.require({R.committer})
@APP.use_template('templates/admin.ezt')
async def admin_page():
- return {
- 'title': 'Administration',
- }
+ result = edict(await signin_info())
+ result.title = 'Administration'
+
+ return result
@APP.get('/profile')
@asfquart.auth.require # Bare decorator means just require a valid session
@APP.use_template('templates/profile.ezt')
async def profile_page():
- return {
- 'title': 'Profile',
- }
+ result = edict(await signin_info())
+ result.title = 'Profile'
+
+ return result
@APP.get('/settings')
@asfquart.auth.require # Bare decorator means just require a valid session
@APP.use_template('templates/settings.ezt')
async def settings_page():
- return {
- 'title': 'Settings',
- }
+ result = edict(await signin_info())
+ result.title = 'Settings'
+
+ return result
@APP.get('/sign-out')
@asfquart.auth.require # Bare decorator means just require a valid session
async def sign_out():
- ### clear the cookie?
- return '', 204
+ asfquart.session.clear()
+
+ # When signing out, go to the Home page.
+ return quart.redirect('/')
+
+
[email protected]('/sign-in')
[email protected] # Bare decorator means just require a valid session
+async def sign_in():
+ "Forces sign-in (via OAuth), then redirects to the Home page."
+
+ # And if we are back here, we are signed-in. Go to the Home page.
+ return quart.redirect('/')
@APP.get('/privacy')
@APP.use_template('templates/privacy.ezt')
async def privacy_page():
- return {
- 'title': 'Privacy',
- }
+ result = edict(await signin_info())
+ result.title = 'Privacy'
+
+ return result
@APP.get('/about')
@APP.use_template('templates/about.ezt')
async def about_page():
- return {
- 'title': 'About',
- }
+ result = edict(await signin_info())
+ result.title = 'About'
+
+ return result
# Route to serve static files (CSS and JS)
diff --git a/v3/server/templates/header.ezt b/v3/server/templates/header.ezt
index 17805c4..fd72de1 100644
--- a/v3/server/templates/header.ezt
+++ b/v3/server/templates/header.ezt
@@ -12,19 +12,20 @@
<div class="container-fluid">
<!-- Left-aligned icon and title -->
<a class="navbar-brand" href="/">
- <img src="https://www.apache.org/foundation/press/kit/feather.png"
alt="Logo" width="30" height="30" class="d-inline-block align-text-top">
+ <img src="https://www.apache.org/foundation/press/kit/feather.svg"
alt="Logo" width="30" height="30" class="d-inline-block align-text-top">
Apache STeVe
</a>
<!-- Toggler for mobile view -->
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarContent" aria-controls="navbarContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
+ [if-any uid]
<!-- Right-aligned user name and dropdown -->
<div class="collapse navbar-collapse" id="navbarContent">
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#"
id="userDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
- John Doe
+ [name]
</a>
<ul class="dropdown-menu dropdown-menu-end"
aria-labelledby="userDropdown">
<li><a class="dropdown-item"
href="/profile">Profile</a></li>
@@ -35,6 +36,16 @@
</li>
</ul>
</div>
+ [else]
+ <!-- Sign-in link for users not signed in -->
+ <div class="collapse navbar-collapse" id="navbarContent">
+ <ul class="navbar-nav ms-auto mb-2 mb-lg-0">
+ <li class="nav-item">
+ <a class="nav-link" href="/sign-in">Sign In</a>
+ </li>
+ </ul>
+ </div>
+ [end]
</div>
</nav>