Skip to content

Latest commit

 

History

History
270 lines (196 loc) · 14 KB

08_konzepte.md

File metadata and controls

270 lines (196 loc) · 14 KB

Konzepte

Fachliche Strukturen

Diese Sicht zeigt die Hauptmodelle in hitobito. Ein vollständiges und aktuelles Datenmodell kann mit dem Befehl rake erd generiert werden.

Fachliches Modell

Group: Modelliert die Baumstruktur der Gruppen eines Verbandes. Die konkreten Gruppentypen werden als Subklassen von den jeweiligen Verbandsplugins definiert und mittels Single Table Inheritance persistiert. Verschiedene Klassenattribute können zur Spezifizierung eines Gruppentyps herangezogen werden, wie beispielsweise die jeweils erlaubten Rollentypen. Die Baumstruktur ist als Nested Set persistiert. Es wird unterschieden zwischen einfachen Gruppen und Ebenen/Layer. Ebenen bilden jeweils einen Berechtigungsbereich.

Person: Eine Person kann mehrere Rollen in mehreren Gruppen haben (via Role), an verschiedenen Events teilnehmen (via Event::Participation) und bei mehreren MailingLists angemeldet sein (via Subscription). Jede Person kann ein Login haben, die Rollen bestimmen ihre Berechtigungen. Änderungen an personenspezifischen Daten werden mit Paper Trail aufgezeichnet. Personen können sowohl natürliche wie auch juristische (Firmen) sein.

Event: Ein einfacher Anlass, ein Kurs oder beliebiger weiterer verbandspezifischer Event. Dieser kann von mehreren Gruppen durchgeführt werden. Die Eventtypen werden wie die Gruppen über Klassenattribute spezifiziert und mittels Single Table Inheritance persistiert. Kurse verfügen darüber hinaus noch über eine Kursart und damit über Qualifikationseigenschaften.

MailingList: Jede Gruppe kann beliebig viele Abos haben, welche optional eine E-Mail Adresse haben und dadurch ebenfalls als E-Mail Liste verwendet werden können. Einzelne Personen, jedoch auch bestimmte Rollen einer Gruppe oder Teilnehmende eines Events können Abonnenten sein.

Wagons

Die Applikation ist aufgeteilt in Core (generischer Teil) und Wagons (Verbandsspezifische Erweiterungen). Zur Funktionsweise von Wagons allgemein siehe auch wagons. Falls die Applikation für weitere Verbände customized werden soll, können einfach weitere Wagons erstellt werden.

In einem Wagon können Tabellen um weitere Attribute ergänzt werden, Funktionalitäten, Berechtigungen und Darstellungen angepasst und hinzugefügt werden.

Gruppen- und Rollentypen

Hitobito verfügt über ein mächtiges Metamodell um Gruppenstrukturen zu beschreiben. Gruppen sind immer von einem spezifischen Typ und in einem Baum angeordnet. Jeder Gruppentyp kann verschiedene Rollentypen definieren.

Der Core von hitobito beinhaltet keine konkreten Gruppen- oder Rollentypen. Diese müssen in separaten Wagons definiert werden, entsprechend der spezifischen Verbandsstruktur. Ein Beispiel für einen Gruppentyp könnte wie folgt aussehen:

class Group::Layer < Group
  self.layer = true
  
  children Group::Layer, Group::Board, Group::Basic

  class Leader < Role
    self.permissions = [:layer_full, :contact_data]
  end

  class Member < Role
    self.permissions = [:group_read]
  end

  class External < Role
    self.permissions = []
    self.visible_from_above = false
    self.kind = :external
  end
  
  roles Leader, Member
end

Ein Gruppentyp erbt immer von der Klasse Group. Er kann eine Ebene sein (self.layer = true), welche mehrere Gruppen zu einem gemeinsamen Berechtigungsbereich zusammenfasst. Alle Untergruppen einer Ebene gehören zu diesem Bereich, ausser sie sind selbst wieder Ebenen.

Danach sind alle möglichen Untergruppentypen des Gruppentyps definiert (children Group::Layer, Group::Board, Group::Basic). Als Untergruppen sind nur diese Typen erlaubt. Wie im Beispiel gezeigt, können Gruppentypen rekursiv organisiert sein.

Die Rollentypen können direkt in einem Gruppentyp definiert werden und erben von der Klasse Role. Jeder Rollentyp hat eine Liste von Grundberechtigungen (self.permissions = [:layer_full, :contact_data]). Alle spezifischen Möglichkeiten eines Benutzenden sind von den Rollenberechtigungen abgeleitet, welche sie oder er in den verschiedenen Gruppen hat. Ausserdem ist es möglich, eine Rolle vor dem Zugriff von übergeordneten Ebenen aus zu schützen (self.visible_from_above = false).

Ein Rollentyp kann zusätzlich von einer spezifischen Art sein (self.kind = :external), welche bei der Aufteilung der Personen einer Gruppe in verschiedene Bereiche heran gezogen wird. Im Core sind die Bereiche Mitglieder, Passive und Externe vordefiniert. Die Art hat keinen Einfluss auf die Berechtigungen.

Berechtigungen

Das Berechtigungssystem definiert, ob ein Benutzer eine spezifische Aktion aufrufen darf oder nicht. Dafür sind die jeweiligen Rollen sowie deren zugeordnete Grundberechtigungen / Permissions relevant. Diese sind allgemeine Angaben für ein Was und Wo, auf welchen die konkreten Berechtigungen aufbauen. Eine Permission soll immer einen ganzen Bereich von Aktionen abdecken, nie nur eine einzelne.

Folgende Permissions existieren momentan:

admin: Administration von applikationsweiten Einstellungen wie Kursarten oder Etikettenformate.

layer_and_below_full: Alles Lesen und Schreiben auf dieser Ebene und allen darunter liegenden Ebenen. Erstellen von Anlässen und Abos (Mailinglisten) auf dieser Ebene.

layer_and_below_read: Alles Lesen auf dieser Ebene und allen darunter liegenden Ebenen.

layer_full: Alles Lesen und Schreiben auf dieser Ebene. Erstellen von Anlässen und Abos (Mailinglisten) auf dieser Ebene.

layer_read: Alles Lesen auf dieser Ebene.

group_and_below_full: Lesen und Schreiben auf dieser und allen darunter liegenden Gruppen (ohne Ebenen). Inkl. Erstellen von Anlässen und Abos (Mailinglisten).

group_and_below_read: Lesen auf dieser und allen darunter liegenden Gruppen (ohne Ebenen).

group_full: Lesen und Schreiben nur auf dieser Gruppe. Erstellen von Anlässen und Abos (Mailinglisten) auf der Gruppe.

group_read: Lesen nur auf dieser Gruppe.

contact_data: Lesen der Kontaktdaten aller anderen Personen mit Kontaktdatenberechtigung.

approve_applications: Bestätigen der Kursanmeldungen für Personen dieser Ebene.

Die Definition von Berchtigungen geschieht in sogenannten Abilities. Diese verknüpfen ein Modell sowie die zugehörigen Aktionen mit den entsprechenden Permissions, wobei durch sogenannte Constraints alle Details geregelt werden. Constraints sind nichts anderes als sprechende Methoden, welche aufgrund Modelleigenschaften und Benutzerrollen die genauen Bedingungen der Berechtigung festlegen. Dies sieht wie folgt aus:

on(Person) do
  permission(:group_full).may(:update).in_same_group
end

def in_same_group
  roles_with_permission = user.roles.select {|r| r.permissions.include?(permission) }
  (roles_with_permission.collect(&:group_id) & subject.group_ids).present?
end

Diese Deklaration erteilt einer Rolle mit der Permission :group_full die Berechtigung, die update Aktion auf einer Person auszuführen, falls diese in_same_group ist. Hier prüft z.B. die Constraint in_same_group, dass die Person (subject) in der selben Gruppe wie die Benutzerrolle mit der zugehörigen Permission sein muss. Zur Deklaration von Berechtigungen sind zusätzlich die beiden abstrakten Permissions any und general verfügbar. any trifft auf alle Benutzenden unabhängig ihrer Permissions zu. Damit können für alle Benutzer geltende Berechtigungen definiert werden oder, in der Constraint, Einschränkungen nach spezifischen Rollentypen unabhängig ihrer Permissions vorgenommen werden. Die abstrakte Permission general wird zusätzlich zu allen anderen Berechtigungsdeklarationen dieser Aktion ebenfalls geprüft. Die Aktionen entsprechen denjenigen von CanCanCan, Aliase wie manage oder read sind ebenfalls möglich. Von allen definierten Berechtigungen muss mindestens eine Constraint erfüllt sein, damit ein Benutzer die gewünschte Aktion ausführen kann.

Jede neu implementierte Aktion erfordert in der Regel eine neue Berechtiungsdeklaration. Diese sollte möglichst auf den bestehenden Constraints und Permissions aufbauen. Ansonsten können neue Constraints dafür definiert werden, neue Permissions sind äusserst zurückhaltend einzuführen.

Folgende zwei Rake Tasks helfen bei der Dokumentation der Rollen und Berechtigungen:

rake hitobito:roles

Gibt alle Gruppen und zugehörigen Rollen und deren Grundberechtigungen aus. Strukturierung nach Ebene, Gruppen, Rollen und Permissions. Globale Gruppen können bei jeder Gruppe als Untergruppe erstellt werden, Globale Rollen (Global Global) sind bei allen Gruppen verfügbar.

rake hitobito:abilities

Gibt alle Berechtigungen entsprechend den Permissions aus. Übersicht über die Definition der Berechtigungen, welche ein Benutzer benötigt, um eine bestimmte Aktion auf einem bestimmten Modell auszuführen.

Lesebeispiel am Beispiel Jubla: Kann ein Mitglied der Bundesleitung einen Anlass einer Schar bearbeiten?

  • Ein Mitglied der Bundesleitung hat die Permissions [:admin, :layer_and_below_full, :contact_data].
  • Bearbeiten (update) eines Events erfordert any/for_leaded_events, group_full/in_same_group, layer_and_below_full/in_same_layer_or_below sowie die allgemeine Constraint at_least_one_group_not_deleted_and_not_closed_or_admin.
  • Der Anlass einer Schar ist unterhalb der Ebene Bund des Bulei Mitglieds, somit kommt layer_and_below_full/in_same_layer_or_below zum Zug.
  • Die allgemeine Constraint wirkt in jedem Fall. Falls die Schar also nicht gelöscht ist (at_least_one_group_not_deleted_and_not_closed_or_admin), kann dieser Benutzer den Anlass bearbeiten. (Das not_closed_.. trifft nur auf Kurse zu).

Anlässe und Kurse

Anlässe sind ähnlich wie Gruppen immer von einem bestimmten Anlasstyp. Jeder Gruppentyp definiert, welche Anlasstypen in einer jeweiligen Gruppe erstellt werden können. Ein Anlass kann neben Grundinformationen mehrere Daten (Event::Date) und beliebige Zusatzangaben (Event::Question) definieren, welche die Teilnehmenden bei der Anmeldung angeben müssen (Event::Answer). Eine Person, welche an einem Anlass teilnimmt (Event::Participation), kann dort mehrere Rollen (Event::Role) inhaben.

Der Anlasstyp bestimmt die möglichen Rollentypen, welche die Teilnehmenden eines Anlasses haben können.

Ein Anlass-Rollentyp definiert ähnlich wie ein Gruppen-Rollentyp Grundberechtigungen (permissions), allerdings nur spezifisch für den jeweiligen Anlass. Folgende sind in hitobito definiert:

event_full: Darf den Anlass bearbeiten.

participations_full: Sieht alle Informationen der Teilnehmenden und darf die Teilnahmedaten bearbeiten.

participations_read: Sieht die öffentlichen Informationen der Teilnehmenden.

qualify: Darf den Teilnehmenden eines Kurses die definierten Qualifikationen vergeben.

Ein Rollentyp ist von einer spezifischen Art, welche an gibt, was die Funktion im Anlass ist. Hitobito definiert die Arten Leiter/-in, Helfer/-in und Teilnehmer/-in. Die Art wird unter anderem verwendet bei der Unterteilung in Leitungsteam und Teilnehmende und hat keinen Einfluss auf die Berechtigungen.

Ein Anlass kann selber auch von einer spezifischen Art sein. Dies wird bei den Kursen verwendet, welche einer bestimmten Kursart (Event::Kind) zugeordnet sind. Über die Kursart werden Aufnahmebedingungen sowie die erteilten und verlängerten Qualifikationsarten (QualificationKind) definiert.

Weiter kann konfigurativ definiert werden, ob ein Anlass Anmeldungen unterstützt. Damit kommt die Möglichkeit, Personen zuzuweisen oder abzulehnen sowie an andere, ähnliche Anlässe zu verweisen. Falls Anmeldungen unterstützt werden, kann eine Teilnahme (Event::Participation) aktiviert sein oder nicht. Sonst ist sie immer aktiv.

Mailing Listen / Abos

Hitobito stellt eine simple Implementation von Mailing Listen zur Verfügung. Diese können in der Applikation beliebig erstellt und verwaltet werden. Dies geschieht in den Modellen MailingList und Subscription.

Alle E-Mails an die Applikationsdomain (z.B [email protected]) werden über einen Catch-All Mail Account gesammelt. Dabei muss der Mailserver den zusätzlichen E-Mail Header X-Envelope-To setzen, welcher den ursprünglichen Empfänger enthält (z.B. news). Von der Applikation wird dieser Account in einem Background Job über POP3 regelmässig gepollt. Die eingetroffenen E-Mails werden danach wie folgt verarbeitet:

  1. Verwerfe das Email, falls der Empfänger keine definierte Mailing Liste ist.
  2. Sende eine Rückweisungsemail, falls der Absender nicht berechtigt ist.
  3. Leite das Email weiter an alle Empfänger der Mailing Liste.

Die Berechtigung, um auf eine Mailing Liste zu schreiben, kann konfiguriert werden. Der Absender wird über seine Haupt- oder zusätzlichen E-Mail Adressen identifiziert. Standardmässig können alle Personen, welche die Liste Bearbeiten können, sowie die Gruppe, welcher das Abo gehört, E-Mails schreiben. Optional können zusätzlich spezifische E-Mail Adressen, alle Abonnenten der Gruppe oder beliebige Absender (auch nicht in hitobito erfasste) berechtigt werden.

Single Table Inheritance

Verschiedene Modelle in hitobito verwenden Single Table Inheritance, z.B. Group, Role und Event. Dabei werden verschiedene Ruby Klassen, welche alle von der selben Hauptklasse erben, in einer Tabelle gespeichert. Nicht alle Subklassen müssen dabei alle in der Tabelle vorhandenen Spalten verwenden. Zur Dokumentation wie auch zum Bestimmen, welche Attribute angezeigt werden sollen, werden die verwendeten Spalten in der jeweiligen Klassenvariable used_attributes angegeben.