Zum Inhalt

Berechtigungssystem

Bifolk implementiert Rollenbasierte Zugriffskontrolle (RBAC), bei der Berechtigungen basierend auf der Rolle eines Benutzers innerhalb einer Organisation vergeben werden.

Zielgruppe: Softwareentwickler


Kernkonzepte

  1. Organisationen: Alle Geschäftsdaten gehören zu einer Organisation
  2. Mitgliedschaft: Benutzer gehören Organisationen über OrganizationMembership an
  3. Rollen: Jede Mitgliedschaft hat eine Rolle (Besitzer, Admin, Mitglied, Betrachter)
  4. Datenisolierung: Benutzer sehen nur Daten ihrer Organisationen
Benutzer → OrganizationMembership (mit Rolle) → Organisation → Ressourcen

Rollenhierarchie

Besitzer > Admin > Mitglied > Betrachter
Rolle Ansehen Erstellen Eigene bearbeiten Alle bearbeiten Löschen Mitglieder verwalten Org löschen
Besitzer Ja Ja Ja Ja Ja Ja Ja
Admin Ja Ja Ja Ja Ja Ja Nein
Mitglied Ja Ja Ja Nein Nein Nein Nein
Betrachter Ja Nein Nein Nein Nein Nein Nein

Berechtigungs-Hilfsfunktionen

Alle zentralisiert in organizations/utils.py.

Kernfunktionen

from organizations.utils import (
    get_user_role,
    get_user_organizations,
    user_is_member,
)
Funktion Rückgabe Beschreibung
get_user_role(user, organization) str oder None Benutzerrolle: 'owner', 'admin', 'member', 'viewer'
get_user_organizations(user) QuerySet Alle Organisationen des Benutzers
user_is_member(user, organization) bool Prüfung auf aktive Mitgliedschaft

Ressourcen-Berechtigungsfunktionen

from organizations.utils import (
    user_can_view,
    user_can_create,
    user_can_edit,
    user_can_delete,
)
Funktion Parameter Regeln
user_can_view(user, resource) Ressource mit organization-Attribut Jedes Mitglied kann ansehen
user_can_create(user, organization) Organisation Mitglied+ kann erstellen
user_can_edit(user, resource) Ressource mit organization, created_by Besitzer/Admin: alle; Mitglied: nur eigene
user_can_delete(user, resource) Ressource mit organization Nur Besitzer/Admin

Organisationsverwaltungs-Funktionen

from organizations.utils import (
    user_can_manage_members,
    user_can_delete_org,
    user_can_change_org_settings,
)
Funktion Regeln
user_can_manage_members(user, org) Besitzer/Admin
user_can_delete_org(user, org) Nur Besitzer
user_can_change_org_settings(user, org) Besitzer/Admin

Abfragefilterung

from organizations.utils import filter_by_user_organizations

user_hives = filter_by_user_organizations(Hive.objects.all(), request.user)

Berechtigungen auf Modellebene

Alle Geschäftsmodelle implementieren Berechtigungsmethoden:

class Hive(models.Model):
    organization = models.ForeignKey(Organization, on_delete=models.CASCADE)
    created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)

    def user_can_view(self, user):
        # Jedes aktive Mitglied der Organisation
        ...

    def user_can_edit(self, user):
        # Besitzer/Admin: alle; Mitglied: nur eigene; Betrachter: nein
        ...

    def user_can_delete(self, user):
        # Nur Besitzer/Admin
        ...

Modelle mit diesen Methoden: Hive, Queen, HiveInspection, HarvestRecord, alle BeekeepingOperation-Unterklassen, QueenBreeding, ColonySplit, InventoryItem, InventoryTransaction, ProductSale.


Berechtigungen auf View-Ebene

In Views

from django.http import HttpResponseForbidden
from organizations.utils import user_can_edit

def edit_hive(request, hive_id):
    hive = get_object_or_404(Hive, id=hive_id)
    if not user_can_edit(request.user, hive):
        return HttpResponseForbidden()
    # mit Bearbeitung fortfahren...

QuerySets filtern

Immer nach aktueller Organisation filtern:

def hive_list(request):
    if request.current_organization:
        hives = Hive.objects.filter(organization=request.current_organization)
    else:
        hives = Hive.objects.none()
    return render(request, 'hives/hive_list.html', {'hives': hives})

Berechtigungen auf Template-Ebene

{% load organization_tags %}

{% if hive.user_can_edit user %}
    <a href="{% url 'hives:edit' hive.id %}">Bearbeiten</a>
{% endif %}

{% if hive.user_can_delete user %}
    <a href="{% url 'hives:delete' hive.id %}">Löschen</a>
{% endif %}

Organizations-Middleware

OrganizationMiddleware (organizations/middleware.py) läuft nach AuthenticationMiddleware und stellt bereit:

  • request.current_organization - Aktive Organisation (aus Session oder Standard)
  • request.user_organizations - Alle Organisationen des Benutzers

Wechseln: Benutzer wechseln Organisationen über den Seitenleisten-Wähler. Die ausgewählte Organisation wird in der Session gespeichert.


Best Practices

  1. Immer Berechtigungen prüfen in Views vor der Datenänderung
  2. Immer QuerySets filtern nach request.current_organization
  3. Organisation beim Erstellen setzen: obj.organization = request.current_organization
  4. Die Hilfsfunktionen verwenden statt Berechtigungslogik neu zu implementieren
  5. None-Organisation behandeln für Benutzer ohne Organisationen
  6. Nicht existierende Ressourcen verbergen: QuerySets filtern, um Ressourcenexistenz nicht preiszugeben
# Bevorzugt (verbirgt Existenz)
hive = get_object_or_404(Hive, id=hive_id, organization__in=request.user_organizations)

# Statt (gibt Existenz preis)
hive = get_object_or_404(Hive, id=hive_id)
if not hive.user_can_view(request.user):
    return HttpResponseForbidden()

Verwandte Dokumentation