Start User management
This commit is contained in:
26
src/allmende_payment_system/api/admin.py
Normal file
26
src/allmende_payment_system/api/admin.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from fastapi import APIRouter, HTTPException, Request
|
||||||
|
from sqlalchemy import select
|
||||||
|
from starlette import status
|
||||||
|
from starlette.responses import RedirectResponse
|
||||||
|
|
||||||
|
from allmende_payment_system.api.dependencies import SessionDep, UserDep
|
||||||
|
from allmende_payment_system.models import Area, Order, OrderItem, Product, User
|
||||||
|
from allmende_payment_system.tools import get_jinja_renderer
|
||||||
|
|
||||||
|
admin_router = APIRouter(prefix="/admin")
|
||||||
|
|
||||||
|
|
||||||
|
@admin_router.get("/users")
|
||||||
|
async def user_list(request: Request, session: SessionDep, user: UserDep):
|
||||||
|
if not user.has_permission("user", "edit"):
|
||||||
|
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||||||
|
|
||||||
|
query = select(User)
|
||||||
|
users = session.scalars(query).all()
|
||||||
|
templates = get_jinja_renderer()
|
||||||
|
return templates.TemplateResponse(
|
||||||
|
"users.html.jinja",
|
||||||
|
context={"request": request, "users": users},
|
||||||
|
)
|
||||||
@@ -4,6 +4,7 @@ from fastapi import Depends, FastAPI
|
|||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
|
||||||
from allmende_payment_system.api import root_router
|
from allmende_payment_system.api import root_router
|
||||||
|
from allmende_payment_system.api.admin import admin_router
|
||||||
from allmende_payment_system.api.dependencies import get_user_object
|
from allmende_payment_system.api.dependencies import get_user_object
|
||||||
from allmende_payment_system.api.shop import shop_router
|
from allmende_payment_system.api.shop import shop_router
|
||||||
|
|
||||||
@@ -20,3 +21,4 @@ app.mount(
|
|||||||
|
|
||||||
app.include_router(root_router)
|
app.include_router(root_router)
|
||||||
app.include_router(shop_router)
|
app.include_router(shop_router)
|
||||||
|
app.include_router(admin_router)
|
||||||
|
|||||||
@@ -55,6 +55,12 @@ class User(Base):
|
|||||||
)
|
)
|
||||||
orders: Mapped[list["Order"]] = relationship("Order", back_populates="user")
|
orders: Mapped[list["Order"]] = relationship("Order", back_populates="user")
|
||||||
|
|
||||||
|
user_groups: Mapped[list["UserGroup"]] = relationship(
|
||||||
|
"UserGroup",
|
||||||
|
secondary=TABLE_PREFIX + "user_user_group_association",
|
||||||
|
back_populates="users",
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def shopping_cart(self):
|
def shopping_cart(self):
|
||||||
for order in self.orders:
|
for order in self.orders:
|
||||||
@@ -68,6 +74,52 @@ class User(Base):
|
|||||||
|
|
||||||
return cart
|
return cart
|
||||||
|
|
||||||
|
def has_permission(self, scope: str, action: str) -> bool:
|
||||||
|
for group in self.user_groups:
|
||||||
|
for permission in group.permissions:
|
||||||
|
if permission.scope == scope and permission.action == action:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class UserGroup(Base):
|
||||||
|
__tablename__ = TABLE_PREFIX + "user_group"
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||||
|
name: Mapped[str] = mapped_column(nullable=False, unique=True)
|
||||||
|
description: Mapped[str] = mapped_column(nullable=True)
|
||||||
|
|
||||||
|
permissions = relationship("Permission", back_populates="user_group")
|
||||||
|
|
||||||
|
users: Mapped[list["User"]] = relationship(
|
||||||
|
"User",
|
||||||
|
secondary=TABLE_PREFIX + "user_user_group_association",
|
||||||
|
back_populates="user_groups",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
user_group_association = Table(
|
||||||
|
TABLE_PREFIX + "user_user_group_association",
|
||||||
|
Base.metadata,
|
||||||
|
Column("user_id", ForeignKey(TABLE_PREFIX + "user.id"), primary_key=True),
|
||||||
|
Column(
|
||||||
|
"user_group_id", ForeignKey(TABLE_PREFIX + "user_group.id"), primary_key=True
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Permission(Base):
|
||||||
|
__tablename__ = TABLE_PREFIX + "permission"
|
||||||
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||||
|
scope: Mapped[str] = mapped_column(nullable=False)
|
||||||
|
action: Mapped[str] = mapped_column(nullable=False)
|
||||||
|
|
||||||
|
user_group_id: Mapped[int] = mapped_column(
|
||||||
|
ForeignKey(TABLE_PREFIX + "user_group.id")
|
||||||
|
)
|
||||||
|
user_group: Mapped["UserGroup"] = relationship(
|
||||||
|
"UserGroup", back_populates="permissions"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Area(Base):
|
class Area(Base):
|
||||||
__tablename__ = TABLE_PREFIX + "area"
|
__tablename__ = TABLE_PREFIX + "area"
|
||||||
|
|||||||
@@ -29,16 +29,13 @@
|
|||||||
Einkaufen
|
Einkaufen
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{# <li class="nav-item">#}
|
{% if request.state.user.has_permission("user", "edit") %}
|
||||||
{# <a href="#" class="nav-link">#}
|
<li class="nav-item">
|
||||||
{# Lorem#}
|
<a href="/admin/users" class="nav-link{% if request.url.path.startswith("/admin/users")%} active{% endif %}">
|
||||||
{# </a>#}
|
Nutzerverwaltung
|
||||||
{# </li>#}
|
</a>
|
||||||
{# <li class="nav-item">#}
|
</li>
|
||||||
{# <a href="#" class="nav-link">#}
|
{% endif %}
|
||||||
{# Ipsum#}
|
|
||||||
{# </a>#}
|
|
||||||
{# </li>#}
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<!-- Shopping Cart at Bottom -->
|
<!-- Shopping Cart at Bottom -->
|
||||||
|
|||||||
39
src/allmende_payment_system/templates/users.html.jinja
Normal file
39
src/allmende_payment_system/templates/users.html.jinja
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{% extends "base.html.jinja" %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="mb-4">
|
||||||
|
<h2 class="h4 mb-3">Benutzer verwalten</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if users|length == 0 %}
|
||||||
|
<div class="alert alert-info">Keine Benutzer vorhanden.</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table align-middle">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Username</th>
|
||||||
|
<th>Azeigename</th>
|
||||||
|
<th>Gruppen</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for user in users %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ user.id }}</td>
|
||||||
|
<td>{{ user.username }}</td>
|
||||||
|
<td>{{ user.display_name }}</td>
|
||||||
|
<td>{{ user.user_groups | map(attribute='name') | join(', ') }}</td>
|
||||||
|
<td class="text-end">
|
||||||
|
<a href="/users/edit/{{ user.id }}" class="btn btn-sm btn-outline-primary me-1">Bearbeiten</a>
|
||||||
|
<a href="/users/delete/{{ user.id }}" class="btn btn-sm btn-outline-danger">Löschen</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from allmende_payment_system.models import Account, User
|
from allmende_payment_system.models import Account, Permission, User, UserGroup
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
@@ -26,3 +26,21 @@ def test_user_shopping_cart_new(test_db, test_user):
|
|||||||
cart = test_user.shopping_cart
|
cart = test_user.shopping_cart
|
||||||
|
|
||||||
assert len(cart.items) == 0
|
assert len(cart.items) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_user_permissions(test_db):
|
||||||
|
user = User(username="normie", display_name="Normal User")
|
||||||
|
admin = User(username="admin", display_name="Admin User")
|
||||||
|
test_db.add(user)
|
||||||
|
|
||||||
|
group = UserGroup(name="Admins", description="A group for admins")
|
||||||
|
group.permissions.append(Permission(scope="area", action="edit"))
|
||||||
|
test_db.add(group)
|
||||||
|
|
||||||
|
admin.user_groups.append(group)
|
||||||
|
test_db.flush()
|
||||||
|
|
||||||
|
assert len(admin.user_groups) == 1
|
||||||
|
|
||||||
|
assert not user.has_permission("area", "edit")
|
||||||
|
assert admin.has_permission("area", "edit")
|
||||||
|
|||||||
Reference in New Issue
Block a user