Skip to content

Authorization Model

This document explains how access control works in the Wippidu Kita App.

Role hierarchy

The system defines six roles, seeded in internal/model/db.go:

Role Description Inherits from
Anonymous Unauthenticated users
Parent Parents/guardians of enrolled children
Employee Staff members assigned to locations
GroupLead Group leader (Gruppenleiter) Employee
LocationLead Location leader (Standortleiter) Employee
Admin System administrator Full access

Users can hold multiple roles simultaneously (many-to-many via user_roles table). All GroupLead and LocationLead users automatically have the Employee role as well.

Role convenience methods

The User model provides helpers in internal/model/user.go:

user.HasRole("Admin")    // general check
user.IsParent()          // shorthand
user.IsEmployee()
user.IsGroupLead()
user.IsLocationLead()
user.IsAdmin()

Location-scoped access

Access for staff is location-scoped, not group-scoped. All staff at a location can see all children at that location, regardless of their specific group assignment.

Why: In daycare facilities, staff regularly help across groups within the same physical location. Location isolation provides security between separate facilities.

How it works

  1. Staff are assigned to groups via the group_teachers join table
  2. Groups belong to locations
  3. CanUserAccessChild() in internal/model/user.go checks:
    • If user is a parent: must have a UserChild relationship with the child
    • If user is staff (Employee/GroupLead/LocationLead): must be assigned to a group at the same location as the child
    • If user is Admin: access granted

Example

Location Alpha
├── The Bees      ← teacher.bees (GroupLead), employee.bees1, employee.bees2
├── The Butterflies ← teacher.butterflies (GroupLead)
└── The Ladybugs  ← teacher.ladybugs (GroupLead)
    teacher.alpha (LocationLead) — assigned to all Alpha groups

Location Beta
├── The Dolphins  ← teacher.dolphins (GroupLead)
└── The Turtles   ← teacher.turtles (GroupLead)
    teacher.beta (LocationLead) — assigned to all Beta groups
  • teacher.bees can access all 30 children at Location Alpha (not just The Bees)
  • employee.bees1 can also access all 30 children at Location Alpha
  • Neither can access any children at Location Beta

Employee location access levels

The EmployeeLocationAccess model controls which staff members employees can see at each location. Three levels:

Level Value Who is visible
Location leads only location_leads Only location leaders
Group leads and above group_leads Location leads + group leads
All employees all_employees All staff at the location

This controls the staff directory visibility, not child access.

Delegation

GroupLeads and LocationLeads can delegate specific capabilities to other employees:

  • Letter writing delegation — A GroupLead can authorize a regular Employee to create parental letters for their group
  • Delegation is tracked per action and per group/location scope
  • Delegated permissions can be revoked at any time

The delegation system is managed via internal/controller/admin_delegation.go.

Parent access periods

Parent-child relationships have optional validity periods:

type UserChild struct {
    UserID           uint
    ChildID          uint
    ValidFrom        *time.Time
    ValidUntil       *time.Time
    RelationshipRole RelationshipRole  // mother, father, guardian, etc.
}

LocationLeads manage these periods via the access times UI (/admin/access-times). When ValidUntil is set and in the past, the parent loses access to the child.

Account approval

New accounts can require admin approval before gaining access. The require_approval middleware blocks unapproved users from accessing protected routes, redirecting them to a "pending approval" page.

Relationship roles

Each parent-child relationship has a role describing the guardian type:

  • mother, father
  • stepmother, stepfather
  • grandmother, grandfather
  • guardian (legal guardian)
  • foster (foster parent)
  • other

These are stored as the RelationshipRole type in internal/model/db.go and are primarily informational.