4.9 KiB
4.9 KiB
05 — Auth: Users Service + Role-Gated UI
Goal:
- Require login for certain actions.
- Show/hide UI components based on user role.
- Gate sensitive server functions so only specific roles can run them.
1) What You’ll Build
- A login/logout button set.
- UI elements that only appear for specific roles (
editor
,admin
). - A server callable (
dangerous_action
) that only certain roles can call. - Friendly messages when a user tries to overstep their privileges.
2) Enable the Users Service
In your Anvil app:
- App → Settings → Users Service → Enable.
- Add a few test users in the Users table:
- Columns:
email
,password
,roles
(Text, Comma-separated, e.g.,editor
,admin
).
- Columns:
- Assign roles for each user. Example:
alice@example.com
→ roles:editor
bob@example.com
→ roles:viewer
root@example.com
→ roles:admin
3) Server-Side Role Helper
Add to ServerModule1
(or create auth_helpers.py
if you prefer):
import anvil.server
import anvil.users
def require_role(*roles):
"""
Ensure the logged-in user has at least one of the given roles.
"""
user = anvil.users.get_user()
if not user:
raise PermissionError("You must be logged in.")
user_roles = set((user.get('roles') or "").replace(" ", "").split(","))
if not set(roles).intersection(user_roles):
raise PermissionError(f"You need one of these roles: {', '.join(roles)}")
@anvil.server.callable
def dangerous_action():
"""
Only 'editor' or 'admin' can run this.
"""
require_role('editor', 'admin')
return "Sensitive operation completed successfully."
4) Client UI Setup
On Form1
:
-
Add two Buttons:
button_login
→ text: “Login”button_logout
→ text: “Logout”
-
Add a Label:
label_user_info
(for current user/roles). -
Add a Button:
button_danger
→ text: “Run Dangerous Action” (editor/admin only). -
Arrange them neatly in a ColumnPanel or RowPanel.
5) Client Code (Form1)
from anvil import *
import anvil.server
import anvil.users
class Form1(Form1Template):
def __init__(self, **properties):
self.init_components(**properties)
self.refresh_ui()
def refresh_ui(self):
"""Refreshes UI based on login status and roles."""
user = anvil.users.get_user()
if user:
roles = (user.get('roles') or "").replace(" ", "")
self.label_user_info.text = f"Logged in as: {user['email']} ({roles})"
self.button_login.visible = False
self.button_logout.visible = True
# Show dangerous button only if editor/admin
role_list = roles.split(",")
self.button_danger.visible = any(r in ('editor', 'admin') for r in role_list)
else:
self.label_user_info.text = "Not logged in"
self.button_login.visible = True
self.button_logout.visible = False
self.button_danger.visible = False
def button_login_click(self, **event_args):
"""Log the user in."""
anvil.users.login_with_form()
self.refresh_ui()
def button_logout_click(self, **event_args):
"""Log the user out."""
anvil.users.logout()
self.refresh_ui()
def button_danger_click(self, **event_args):
"""Attempt to run the dangerous action."""
try:
res = anvil.server.call('dangerous_action')
Notification(res, style='success', timeout=3).show()
except PermissionError as e:
Notification(str(e), style='danger', timeout=3).show()
except Exception as e:
Notification(f"Error: {e}", style='danger').show()
6) Test Scenarios
- Not logged in → No dangerous button visible; clicking login prompts login form.
- Logged in as viewer → Sees dangerous button hidden.
- Logged in as editor/admin → Sees dangerous button; clicking it runs
dangerous_action
and shows success message. - Force test: Temporarily show dangerous button for all roles, click it as a viewer, verify server rejects with
PermissionError
.
7) Bonus: Role Badge
- Add a Label next to
label_user_info
styled with a background color based on role. - Example:
editor
→ blue badge,admin
→ red badge.
8) Demo Asset
Record a short GIF showing:
- Logging in as viewer → no dangerous button.
- Logging in as editor → dangerous button appears and works.
- Logging in as admin → same, but maybe with admin-only UI section.
Save as: /assets/auth-roles-demo.gif
Reference in this tutorial:

9) Takeaways
- Roles allow fine-grained control without changing client code.
- Always check roles server-side — the client can be modified by anyone.
- Hide UI for UX; enforce on server for security.