Files
breakpilot-core/consent-service/internal/session/rbac_middleware.go
Benjamin Boenisch ad111d5e69 Initial commit: breakpilot-core - Shared Infrastructure
Docker Compose with 24+ services:
- PostgreSQL (PostGIS), Valkey, MinIO, Qdrant
- Vault (PKI/TLS), Nginx (Reverse Proxy)
- Backend Core API, Consent Service, Billing Service
- RAG Service, Embedding Service
- Gitea, Woodpecker CI/CD
- Night Scheduler, Health Aggregator
- Jitsi (Web/XMPP/JVB/Jicofo), Mailpit

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:47:13 +01:00

404 lines
9.2 KiB
Go

package session
import (
"net/http"
"github.com/gin-gonic/gin"
)
// Employee permissions
var EmployeePermissions = []string{
"grades:read", "grades:write",
"attendance:read", "attendance:write",
"students:read", "students:write",
"reports:generate", "consent:admin",
"corrections:read", "corrections:write",
"classes:read", "classes:write",
"timetable:read", "timetable:write",
"substitutions:read", "substitutions:write",
"parent_communication:read", "parent_communication:write",
}
// Customer permissions
var CustomerPermissions = []string{
"own_data:read", "own_grades:read", "own_attendance:read",
"consent:manage",
"meetings:join",
"messages:read", "messages:write",
"children:read", "children:grades:read", "children:attendance:read",
}
// Admin permissions
var AdminPermissions = []string{
"users:read", "users:write", "users:manage",
"rbac:read", "rbac:write",
"audit:read",
"settings:read", "settings:write",
"dsr:read", "dsr:process",
}
// Employee roles
var EmployeeRoles = map[string]bool{
"admin": true,
"schul_admin": true,
"schulleitung": true,
"pruefungsvorsitz": true,
"klassenlehrer": true,
"fachlehrer": true,
"sekretariat": true,
"erstkorrektor": true,
"zweitkorrektor": true,
"drittkorrektor": true,
"teacher_assistant": true,
"teacher": true,
"lehrer": true,
"data_protection_officer": true,
}
// Customer roles
var CustomerRoles = map[string]bool{
"parent": true,
"student": true,
"user": true,
"guardian": true,
}
// RequireEmployee requires the user to be an employee
func RequireEmployee() gin.HandlerFunc {
return func(c *gin.Context) {
session := GetSession(c)
if session == nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"error": "unauthorized",
"message": "Authentication required",
})
return
}
if !session.IsEmployee() {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
"error": "forbidden",
"message": "Employee access required",
})
return
}
c.Next()
}
}
// RequireCustomer requires the user to be a customer
func RequireCustomer() gin.HandlerFunc {
return func(c *gin.Context) {
session := GetSession(c)
if session == nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"error": "unauthorized",
"message": "Authentication required",
})
return
}
if !session.IsCustomer() {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
"error": "forbidden",
"message": "Customer access required",
})
return
}
c.Next()
}
}
// RequireUserType requires a specific user type
func RequireUserType(userType UserType) gin.HandlerFunc {
return func(c *gin.Context) {
session := GetSession(c)
if session == nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"error": "unauthorized",
"message": "Authentication required",
})
return
}
if session.UserType != userType {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
"error": "forbidden",
"message": "User type '" + string(userType) + "' required",
})
return
}
c.Next()
}
}
// RequirePermission requires a specific permission
func RequirePermission(permission string) gin.HandlerFunc {
return func(c *gin.Context) {
session := GetSession(c)
if session == nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"error": "unauthorized",
"message": "Authentication required",
})
return
}
if !session.HasPermission(permission) {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
"error": "forbidden",
"message": "Permission '" + permission + "' required",
})
return
}
c.Next()
}
}
// RequireAnyPermission requires at least one of the permissions
func RequireAnyPermission(permissions ...string) gin.HandlerFunc {
return func(c *gin.Context) {
session := GetSession(c)
if session == nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"error": "unauthorized",
"message": "Authentication required",
})
return
}
if !session.HasAnyPermission(permissions) {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
"error": "forbidden",
"message": "One of the required permissions is missing",
})
return
}
c.Next()
}
}
// RequireAllPermissions requires all specified permissions
func RequireAllPermissions(permissions ...string) gin.HandlerFunc {
return func(c *gin.Context) {
session := GetSession(c)
if session == nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"error": "unauthorized",
"message": "Authentication required",
})
return
}
if !session.HasAllPermissions(permissions) {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
"error": "forbidden",
"message": "Missing required permissions",
})
return
}
c.Next()
}
}
// RequireRole requires a specific role
func RequireRole(role string) gin.HandlerFunc {
return func(c *gin.Context) {
session := GetSession(c)
if session == nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"error": "unauthorized",
"message": "Authentication required",
})
return
}
if !session.HasRole(role) {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
"error": "forbidden",
"message": "Role '" + role + "' required",
})
return
}
c.Next()
}
}
// RequireAnyRole requires at least one of the roles
func RequireAnyRole(roles ...string) gin.HandlerFunc {
return func(c *gin.Context) {
session := GetSession(c)
if session == nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"error": "unauthorized",
"message": "Authentication required",
})
return
}
for _, role := range roles {
if session.HasRole(role) {
c.Next()
return
}
}
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
"error": "forbidden",
"message": "One of the required roles is missing",
})
}
}
// RequireSameTenant ensures user can only access their tenant's data
func RequireSameTenant(tenantIDParam string) gin.HandlerFunc {
return func(c *gin.Context) {
session := GetSession(c)
if session == nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"error": "unauthorized",
"message": "Authentication required",
})
return
}
requestTenantID := c.Param(tenantIDParam)
if requestTenantID == "" {
requestTenantID = c.Query(tenantIDParam)
}
if requestTenantID != "" && session.TenantID != nil && *session.TenantID != requestTenantID {
// Check if user is super admin
if !session.HasRole("super_admin") {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
"error": "forbidden",
"message": "Access denied to this tenant",
})
return
}
}
c.Next()
}
}
// DetermineUserType determines user type based on roles
func DetermineUserType(roles []string) UserType {
for _, role := range roles {
if EmployeeRoles[role] {
return UserTypeEmployee
}
}
for _, role := range roles {
if CustomerRoles[role] {
return UserTypeCustomer
}
}
return UserTypeCustomer
}
// GetPermissionsForRoles returns permissions based on roles and user type
func GetPermissionsForRoles(roles []string, userType UserType) []string {
permSet := make(map[string]bool)
// Base permissions by user type
if userType == UserTypeEmployee {
for _, p := range EmployeePermissions {
permSet[p] = true
}
} else {
for _, p := range CustomerPermissions {
permSet[p] = true
}
}
// Admin permissions
adminRoles := map[string]bool{
"admin": true,
"schul_admin": true,
"super_admin": true,
"data_protection_officer": true,
}
for _, role := range roles {
if adminRoles[role] {
for _, p := range AdminPermissions {
permSet[p] = true
}
break
}
}
// Convert to slice
permissions := make([]string, 0, len(permSet))
for p := range permSet {
permissions = append(permissions, p)
}
return permissions
}
// CheckResourceOwnership checks if user owns a resource or is admin
func CheckResourceOwnership(session *Session, resourceUserID string, allowAdmin bool) bool {
if session == nil {
return false
}
// User owns the resource
if session.UserID == resourceUserID {
return true
}
// Admin can access all
if allowAdmin && (session.HasRole("admin") || session.HasRole("super_admin")) {
return true
}
return false
}
// IsSessionEmployee checks if current session belongs to an employee
func IsSessionEmployee(c *gin.Context) bool {
session := GetSession(c)
if session == nil {
return false
}
return session.IsEmployee()
}
// IsSessionCustomer checks if current session belongs to a customer
func IsSessionCustomer(c *gin.Context) bool {
session := GetSession(c)
if session == nil {
return false
}
return session.IsCustomer()
}
// HasSessionPermission checks if session has a permission
func HasSessionPermission(c *gin.Context, permission string) bool {
session := GetSession(c)
if session == nil {
return false
}
return session.HasPermission(permission)
}
// HasSessionRole checks if session has a role
func HasSessionRole(c *gin.Context, role string) bool {
session := GetSession(c)
if session == nil {
return false
}
return session.HasRole(role)
}