(vision) SCS/DDD
This commit is contained in:
@@ -18,6 +18,10 @@ kotlin {
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.kotlinx.datetime)
|
||||
implementation(libs.uuid)
|
||||
implementation(libs.exposed.core)
|
||||
implementation(libs.exposed.dao)
|
||||
implementation(libs.exposed.jdbc)
|
||||
implementation(libs.exposed.kotlinDatetime)
|
||||
}
|
||||
|
||||
commonTest.dependencies {
|
||||
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
package at.mocode.members.application.usecase
|
||||
|
||||
import at.mocode.members.domain.model.DomPersonRolle
|
||||
import at.mocode.members.domain.repository.PersonRepository
|
||||
import at.mocode.members.domain.repository.PersonRolleRepository
|
||||
import at.mocode.members.domain.repository.RolleRepository
|
||||
import at.mocode.members.domain.repository.VereinRepository
|
||||
import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.LocalDate
|
||||
|
||||
/**
|
||||
* Use Case für das Zuweisen einer Rolle zu einer Person.
|
||||
*
|
||||
* Dieser Use Case validiert die Eingabedaten und erstellt eine neue Person-Rolle-Zuordnung,
|
||||
* falls diese noch nicht existiert.
|
||||
*/
|
||||
class AssignRoleToPersonUseCase(
|
||||
private val personRepository: PersonRepository,
|
||||
private val rolleRepository: RolleRepository,
|
||||
private val personRolleRepository: PersonRolleRepository,
|
||||
private val vereinRepository: VereinRepository
|
||||
) {
|
||||
|
||||
/**
|
||||
* Weist einer Person eine Rolle zu.
|
||||
*
|
||||
* @param request Die Anfrage mit den Zuordnungsdaten.
|
||||
* @return Die erstellte Person-Rolle-Zuordnung.
|
||||
* @throws IllegalArgumentException wenn ungültige Daten übergeben wurden oder die Zuordnung bereits existiert.
|
||||
*/
|
||||
suspend fun execute(request: AssignRoleToPersonRequest): DomPersonRolle {
|
||||
// Validierung der Eingabedaten
|
||||
validateRequest(request)
|
||||
|
||||
// Prüfen, ob Person existiert
|
||||
val person = personRepository.findById(request.personId)
|
||||
?: throw IllegalArgumentException("Person mit ID '${request.personId}' wurde nicht gefunden.")
|
||||
|
||||
// Prüfen, ob Rolle existiert
|
||||
val rolle = rolleRepository.findById(request.rolleId)
|
||||
?: throw IllegalArgumentException("Rolle mit ID '${request.rolleId}' wurde nicht gefunden.")
|
||||
|
||||
// Prüfen, ob Rolle aktiv ist
|
||||
if (!rolle.istAktiv) {
|
||||
throw IllegalArgumentException("Die Rolle '${rolle.name}' ist nicht aktiv und kann nicht zugewiesen werden.")
|
||||
}
|
||||
|
||||
// Prüfen, ob Verein existiert (falls angegeben)
|
||||
request.vereinId?.let { vereinId ->
|
||||
val verein = vereinRepository.findById(vereinId)
|
||||
?: throw IllegalArgumentException("Verein mit ID '$vereinId' wurde nicht gefunden.")
|
||||
|
||||
if (!verein.istAktiv) {
|
||||
throw IllegalArgumentException("Der Verein '${verein.name}' ist nicht aktiv.")
|
||||
}
|
||||
}
|
||||
|
||||
// Prüfen, ob die Zuordnung bereits existiert
|
||||
val existierendeZuordnung = personRolleRepository.findByPersonAndRolle(
|
||||
request.personId,
|
||||
request.rolleId,
|
||||
request.vereinId
|
||||
)
|
||||
|
||||
if (existierendeZuordnung != null && existierendeZuordnung.istAktiv) {
|
||||
throw IllegalArgumentException("Die Person '${person.nachname}, ${person.vorname}' hat bereits die Rolle '${rolle.name}'.")
|
||||
}
|
||||
|
||||
// Neue Person-Rolle-Zuordnung erstellen
|
||||
val personRolle = DomPersonRolle(
|
||||
personId = request.personId,
|
||||
rolleId = request.rolleId,
|
||||
vereinId = request.vereinId,
|
||||
gueltigVon = request.gueltigVon,
|
||||
gueltigBis = request.gueltigBis,
|
||||
istAktiv = true,
|
||||
zugewiesenVon = request.zugewiesenVon,
|
||||
notizen = request.notizen,
|
||||
updatedAt = Clock.System.now()
|
||||
)
|
||||
|
||||
// Person-Rolle-Zuordnung speichern
|
||||
return personRolleRepository.save(personRolle)
|
||||
}
|
||||
|
||||
private fun validateRequest(request: AssignRoleToPersonRequest) {
|
||||
// Prüfen, ob gueltigBis nach gueltigVon liegt
|
||||
request.gueltigBis?.let { gueltigBis ->
|
||||
if (gueltigBis <= request.gueltigVon) {
|
||||
throw IllegalArgumentException("Das Enddatum muss nach dem Startdatum liegen.")
|
||||
}
|
||||
}
|
||||
|
||||
// Prüfen, ob gueltigVon nicht in der Vergangenheit liegt (optional, je nach Geschäftslogik)
|
||||
// Hier könnte man auch erlauben, dass Rollen rückwirkend zugewiesen werden
|
||||
|
||||
request.notizen?.let { notizen ->
|
||||
if (notizen.length > 1000) {
|
||||
throw IllegalArgumentException("Die Notizen dürfen maximal 1000 Zeichen lang sein.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request-Datenklasse für das Zuweisen einer Rolle zu einer Person.
|
||||
*/
|
||||
data class AssignRoleToPersonRequest(
|
||||
val personId: Uuid,
|
||||
val rolleId: Uuid,
|
||||
val vereinId: Uuid? = null,
|
||||
val gueltigVon: LocalDate,
|
||||
val gueltigBis: LocalDate? = null,
|
||||
val zugewiesenVon: Uuid? = null,
|
||||
val notizen: String? = null
|
||||
)
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
package at.mocode.members.application.usecase
|
||||
|
||||
import at.mocode.dto.base.ApiResponse
|
||||
import at.mocode.dto.base.ErrorDto
|
||||
import at.mocode.enums.BerechtigungE
|
||||
import at.mocode.members.domain.model.DomBerechtigung
|
||||
import at.mocode.members.domain.repository.BerechtigungRepository
|
||||
import at.mocode.validation.ValidationUtils
|
||||
import at.mocode.validation.ValidationResult
|
||||
import at.mocode.validation.ValidationError
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
/**
|
||||
* Use case for creating new permissions (Berechtigungen) in the system.
|
||||
*/
|
||||
class CreateBerechtigungUseCase(
|
||||
private val berechtigungRepository: BerechtigungRepository
|
||||
) {
|
||||
|
||||
data class CreateBerechtigungRequest(
|
||||
val berechtigungTyp: BerechtigungE,
|
||||
val name: String,
|
||||
val beschreibung: String? = null,
|
||||
val ressource: String,
|
||||
val aktion: String,
|
||||
val istSystemBerechtigung: Boolean = false
|
||||
)
|
||||
|
||||
data class CreateBerechtigungResponse(
|
||||
val berechtigung: DomBerechtigung
|
||||
)
|
||||
|
||||
suspend fun execute(request: CreateBerechtigungRequest): ApiResponse<CreateBerechtigungResponse> {
|
||||
try {
|
||||
// Validate request
|
||||
val validationResult = validateRequest(request)
|
||||
if (!validationResult.isValid()) {
|
||||
val errors = (validationResult as ValidationResult.Invalid).errors
|
||||
return ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "VALIDATION_ERROR",
|
||||
message = "Validation failed",
|
||||
details = errors.associate { it.field to it.message }
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Check if permission with this type already exists
|
||||
val existingBerechtigung = berechtigungRepository.findByTyp(request.berechtigungTyp)
|
||||
if (existingBerechtigung != null) {
|
||||
return ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "BERECHTIGUNG_ALREADY_EXISTS",
|
||||
message = "A permission with this type already exists",
|
||||
details = mapOf("berechtigungTyp" to request.berechtigungTyp.toString())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Create new permission
|
||||
val berechtigung = DomBerechtigung(
|
||||
berechtigungTyp = request.berechtigungTyp,
|
||||
name = request.name,
|
||||
beschreibung = request.beschreibung,
|
||||
ressource = request.ressource,
|
||||
aktion = request.aktion,
|
||||
istSystemBerechtigung = request.istSystemBerechtigung,
|
||||
createdAt = Clock.System.now(),
|
||||
updatedAt = Clock.System.now()
|
||||
)
|
||||
|
||||
// Save to repository
|
||||
val savedBerechtigung = berechtigungRepository.save(berechtigung)
|
||||
|
||||
return ApiResponse(
|
||||
success = true,
|
||||
data = CreateBerechtigungResponse(savedBerechtigung)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
return ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INTERNAL_ERROR",
|
||||
message = "An error occurred while creating the permission",
|
||||
details = mapOf("error" to e.message.orEmpty())
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateRequest(request: CreateBerechtigungRequest): ValidationResult {
|
||||
val errors = mutableListOf<ValidationError>()
|
||||
|
||||
// Validate name
|
||||
ValidationUtils.validateNotBlank(request.name, "name")?.let { error ->
|
||||
errors.add(error)
|
||||
}
|
||||
|
||||
// Validate ressource
|
||||
ValidationUtils.validateNotBlank(request.ressource, "ressource")?.let { error ->
|
||||
errors.add(error)
|
||||
}
|
||||
|
||||
// Validate aktion
|
||||
ValidationUtils.validateNotBlank(request.aktion, "aktion")?.let { error ->
|
||||
errors.add(error)
|
||||
}
|
||||
|
||||
// Validate name length
|
||||
if (request.name.length > 100) {
|
||||
errors.add(ValidationError("name", "Name must not exceed 100 characters"))
|
||||
}
|
||||
|
||||
// Validate ressource length
|
||||
if (request.ressource.length > 50) {
|
||||
errors.add(ValidationError("ressource", "Ressource must not exceed 50 characters"))
|
||||
}
|
||||
|
||||
// Validate aktion length
|
||||
if (request.aktion.length > 50) {
|
||||
errors.add(ValidationError("aktion", "Aktion must not exceed 50 characters"))
|
||||
}
|
||||
|
||||
return ValidationResult(errors)
|
||||
}
|
||||
}
|
||||
+53
-25
@@ -6,6 +6,9 @@ import at.mocode.members.domain.model.DomPerson
|
||||
import at.mocode.members.domain.repository.PersonRepository
|
||||
import at.mocode.members.domain.repository.VereinRepository
|
||||
import at.mocode.members.domain.service.MasterDataService
|
||||
import at.mocode.validation.ValidationUtils
|
||||
import at.mocode.validation.ValidationResult
|
||||
import at.mocode.validation.ValidationError
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
/**
|
||||
@@ -68,14 +71,15 @@ class CreatePersonUseCase(
|
||||
suspend fun execute(request: CreatePersonRequest): ApiResponse<CreatePersonResponse> {
|
||||
try {
|
||||
// Validate required fields
|
||||
val validationErrors = validateRequest(request)
|
||||
if (validationErrors.isNotEmpty()) {
|
||||
val validationResult = validateRequest(request)
|
||||
if (!validationResult.isValid()) {
|
||||
val errors = (validationResult as ValidationResult.Invalid).errors
|
||||
return ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "VALIDATION_ERROR",
|
||||
message = "Invalid input data",
|
||||
details = validationErrors
|
||||
details = errors.associate { it.field to it.message }
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -94,14 +98,15 @@ class CreatePersonUseCase(
|
||||
}
|
||||
|
||||
// Validate referenced entities
|
||||
val entityValidationErrors = validateReferencedEntities(request)
|
||||
if (entityValidationErrors.isNotEmpty()) {
|
||||
val entityValidationResult = validateReferencedEntities(request)
|
||||
if (!entityValidationResult.isValid()) {
|
||||
val errors = (entityValidationResult as ValidationResult.Invalid).errors
|
||||
return ApiResponse(
|
||||
success = false,
|
||||
error = ErrorDto(
|
||||
code = "INVALID_REFERENCES",
|
||||
message = "Referenced entities not found",
|
||||
details = entityValidationErrors
|
||||
details = errors.associate { it.field to it.message }
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -154,50 +159,73 @@ class CreatePersonUseCase(
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateRequest(request: CreatePersonRequest): Map<String, String> {
|
||||
val errors = mutableMapOf<String, String>()
|
||||
private fun validateRequest(request: CreatePersonRequest): ValidationResult {
|
||||
val errors = mutableListOf<ValidationError>()
|
||||
|
||||
if (request.nachname.isBlank()) {
|
||||
errors["nachname"] = "Last name is required"
|
||||
// Validate required fields using ValidationUtils
|
||||
ValidationUtils.validateNotBlank(request.nachname, "nachname")?.let { error ->
|
||||
errors.add(error)
|
||||
}
|
||||
|
||||
if (request.vorname.isBlank()) {
|
||||
errors["vorname"] = "First name is required"
|
||||
ValidationUtils.validateNotBlank(request.vorname, "vorname")?.let { error ->
|
||||
errors.add(error)
|
||||
}
|
||||
|
||||
if (request.oepsSatzNr != null && request.oepsSatzNr.length != 6) {
|
||||
errors["oepsSatzNr"] = "OEPS Satznummer must be exactly 6 digits"
|
||||
// Validate OEPS Satz number using ValidationUtils
|
||||
ValidationUtils.validateOepsSatzNr(request.oepsSatzNr, "oepsSatzNr")?.let { error ->
|
||||
errors.add(error)
|
||||
}
|
||||
|
||||
if (request.email != null && !isValidEmail(request.email)) {
|
||||
errors["email"] = "Invalid email format"
|
||||
// Validate email using ValidationUtils
|
||||
ValidationUtils.validateEmail(request.email, "email")?.let { error ->
|
||||
errors.add(error)
|
||||
}
|
||||
|
||||
return errors
|
||||
// Validate phone number using ValidationUtils
|
||||
ValidationUtils.validatePhoneNumber(request.telefon, "telefon")?.let { error ->
|
||||
errors.add(error)
|
||||
}
|
||||
|
||||
// Validate postal code using ValidationUtils
|
||||
ValidationUtils.validatePostalCode(request.plz, "plz")?.let { error ->
|
||||
errors.add(error)
|
||||
}
|
||||
|
||||
// Validate birth date using ValidationUtils
|
||||
ValidationUtils.validateBirthDate(request.geburtsdatum, "geburtsdatum")?.let { error ->
|
||||
errors.add(error)
|
||||
}
|
||||
|
||||
return if (errors.isEmpty()) {
|
||||
ValidationResult.Valid
|
||||
} else {
|
||||
ValidationResult.Invalid(errors)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun validateReferencedEntities(request: CreatePersonRequest): Map<String, String> {
|
||||
val errors = mutableMapOf<String, String>()
|
||||
private suspend fun validateReferencedEntities(request: CreatePersonRequest): ValidationResult {
|
||||
val errors = mutableListOf<ValidationError>()
|
||||
|
||||
// Validate club reference
|
||||
if (request.stammVereinId != null) {
|
||||
val verein = vereinRepository.findById(request.stammVereinId)
|
||||
if (verein == null) {
|
||||
errors["stammVereinId"] = "Referenced club not found"
|
||||
errors.add(ValidationError("stammVereinId", "Referenced club not found", "NOT_FOUND"))
|
||||
}
|
||||
}
|
||||
|
||||
// Validate country reference
|
||||
if (request.nationalitaetLandId != null) {
|
||||
if (!masterDataService.countryExists(request.nationalitaetLandId)) {
|
||||
errors["nationalitaetLandId"] = "Referenced country not found"
|
||||
errors.add(ValidationError("nationalitaetLandId", "Referenced country not found", "NOT_FOUND"))
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
return if (errors.isEmpty()) {
|
||||
ValidationResult.Valid
|
||||
} else {
|
||||
ValidationResult.Invalid(errors)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isValidEmail(email: String): Boolean {
|
||||
return email.contains("@") && email.contains(".")
|
||||
}
|
||||
}
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package at.mocode.members.application.usecase
|
||||
|
||||
import at.mocode.enums.RolleE
|
||||
import at.mocode.members.domain.model.DomRolle
|
||||
import at.mocode.members.domain.repository.RolleRepository
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
/**
|
||||
* Use Case für das Erstellen einer neuen Rolle im System.
|
||||
*
|
||||
* Dieser Use Case validiert die Eingabedaten und erstellt eine neue Rolle,
|
||||
* falls diese noch nicht existiert.
|
||||
*/
|
||||
class CreateRolleUseCase(
|
||||
private val rolleRepository: RolleRepository
|
||||
) {
|
||||
|
||||
/**
|
||||
* Erstellt eine neue Rolle im System.
|
||||
*
|
||||
* @param request Die Anfrage mit den Rollendaten.
|
||||
* @return Die erstellte Rolle.
|
||||
* @throws IllegalArgumentException wenn die Rolle bereits existiert oder ungültige Daten übergeben wurden.
|
||||
*/
|
||||
suspend fun execute(request: CreateRolleRequest): DomRolle {
|
||||
// Validierung der Eingabedaten
|
||||
validateRequest(request)
|
||||
|
||||
// Prüfen, ob eine Rolle mit diesem Typ bereits existiert
|
||||
if (rolleRepository.existsByTyp(request.rolleTyp)) {
|
||||
throw IllegalArgumentException("Eine Rolle mit dem Typ '${request.rolleTyp}' existiert bereits.")
|
||||
}
|
||||
|
||||
// Neue Rolle erstellen
|
||||
val neueRolle = DomRolle(
|
||||
rolleTyp = request.rolleTyp,
|
||||
name = request.name,
|
||||
beschreibung = request.beschreibung,
|
||||
istAktiv = request.istAktiv ?: true,
|
||||
istSystemRolle = request.istSystemRolle ?: false,
|
||||
updatedAt = Clock.System.now()
|
||||
)
|
||||
|
||||
// Rolle speichern
|
||||
return rolleRepository.save(neueRolle)
|
||||
}
|
||||
|
||||
private fun validateRequest(request: CreateRolleRequest) {
|
||||
if (request.name.isBlank()) {
|
||||
throw IllegalArgumentException("Der Name der Rolle darf nicht leer sein.")
|
||||
}
|
||||
|
||||
if (request.name.length > 100) {
|
||||
throw IllegalArgumentException("Der Name der Rolle darf maximal 100 Zeichen lang sein.")
|
||||
}
|
||||
|
||||
request.beschreibung?.let { beschreibung ->
|
||||
if (beschreibung.length > 500) {
|
||||
throw IllegalArgumentException("Die Beschreibung der Rolle darf maximal 500 Zeichen lang sein.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request-Datenklasse für das Erstellen einer Rolle.
|
||||
*/
|
||||
data class CreateRolleRequest(
|
||||
val rolleTyp: RolleE,
|
||||
val name: String,
|
||||
val beschreibung: String? = null,
|
||||
val istAktiv: Boolean? = null,
|
||||
val istSystemRolle: Boolean? = null
|
||||
)
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package at.mocode.members.domain.model
|
||||
|
||||
import at.mocode.enums.BerechtigungE
|
||||
import at.mocode.serializers.KotlinInstantSerializer
|
||||
import at.mocode.serializers.UuidSerializer
|
||||
import com.benasher44.uuid.Uuid
|
||||
import com.benasher44.uuid.uuid4
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Repräsentiert eine Berechtigung im System für die Zugriffskontrolle.
|
||||
*
|
||||
* Berechtigungen definieren spezifische Aktionen, die im System ausgeführt werden können
|
||||
* (z.B. Personen lesen, Vereine erstellen, Veranstaltungen bearbeiten).
|
||||
* Berechtigungen werden Rollen zugeordnet, die wiederum Personen zugewiesen werden.
|
||||
*
|
||||
* @property berechtigungId Eindeutiger interner Identifikator für diese Berechtigung (UUID).
|
||||
* @property berechtigungTyp Der Typ der Berechtigung aus der BerechtigungE Enumeration.
|
||||
* @property name Anzeigename der Berechtigung (z.B. "Personen lesen", "Vereine erstellen").
|
||||
* @property beschreibung Detaillierte Beschreibung der Berechtigung und ihres Zwecks.
|
||||
* @property ressource Die Ressource, auf die sich diese Berechtigung bezieht (z.B. "Person", "Verein").
|
||||
* @property aktion Die Aktion, die mit dieser Berechtigung ausgeführt werden kann (z.B. "lesen", "erstellen").
|
||||
* @property istAktiv Gibt an, ob diese Berechtigung aktuell aktiv ist.
|
||||
* @property istSystemBerechtigung Gibt an, ob es sich um eine Systemberechtigung handelt, die nicht gelöscht werden kann.
|
||||
* @property createdAt Zeitstempel der Erstellung dieser Berechtigung.
|
||||
* @property updatedAt Zeitstempel der letzten Aktualisierung dieser Berechtigung.
|
||||
*/
|
||||
@Serializable
|
||||
data class DomBerechtigung(
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val berechtigungId: Uuid = uuid4(),
|
||||
|
||||
val berechtigungTyp: BerechtigungE,
|
||||
var name: String,
|
||||
var beschreibung: String? = null,
|
||||
var ressource: String,
|
||||
var aktion: String,
|
||||
|
||||
var istAktiv: Boolean = true,
|
||||
var istSystemBerechtigung: Boolean = false,
|
||||
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
val createdAt: Instant = Clock.System.now(),
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
var updatedAt: Instant = Clock.System.now()
|
||||
)
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package at.mocode.members.domain.model
|
||||
|
||||
import at.mocode.serializers.KotlinInstantSerializer
|
||||
import at.mocode.serializers.KotlinLocalDateSerializer
|
||||
import at.mocode.serializers.UuidSerializer
|
||||
import com.benasher44.uuid.Uuid
|
||||
import com.benasher44.uuid.uuid4
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Repräsentiert die Zuordnung einer Rolle zu einer Person.
|
||||
*
|
||||
* Diese Entität verwaltet die Many-to-Many-Beziehung zwischen Personen und Rollen.
|
||||
* Eine Person kann mehrere Rollen haben (z.B. gleichzeitig Reiter und Trainer),
|
||||
* und eine Rolle kann mehreren Personen zugeordnet werden.
|
||||
*
|
||||
* @property personRolleId Eindeutiger interner Identifikator für diese Rollenzuordnung (UUID).
|
||||
* @property personId Fremdschlüssel zur Person (DomPerson.personId).
|
||||
* @property rolleId Fremdschlüssel zur Rolle (DomRolle.rolleId).
|
||||
* @property vereinId Optionale Verknüpfung zu einem Verein, falls die Rolle vereinsspezifisch ist.
|
||||
* @property gueltigVon Datum, ab dem diese Rollenzuordnung gültig ist.
|
||||
* @property gueltigBis Optionales Datum, bis zu dem diese Rollenzuordnung gültig ist.
|
||||
* @property istAktiv Gibt an, ob diese Rollenzuordnung aktuell aktiv ist.
|
||||
* @property zugewiesenVon Optionale Referenz auf die Person, die diese Rolle zugewiesen hat.
|
||||
* @property notizen Optionale Notizen zur Rollenzuordnung.
|
||||
* @property createdAt Zeitstempel der Erstellung dieser Rollenzuordnung.
|
||||
* @property updatedAt Zeitstempel der letzten Aktualisierung dieser Rollenzuordnung.
|
||||
*/
|
||||
@Serializable
|
||||
data class DomPersonRolle(
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val personRolleId: Uuid = uuid4(),
|
||||
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val personId: Uuid,
|
||||
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val rolleId: Uuid,
|
||||
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
var vereinId: Uuid? = null, // Für vereinsspezifische Rollen
|
||||
|
||||
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||
var gueltigVon: LocalDate,
|
||||
|
||||
@Serializable(with = KotlinLocalDateSerializer::class)
|
||||
var gueltigBis: LocalDate? = null,
|
||||
|
||||
var istAktiv: Boolean = true,
|
||||
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
var zugewiesenVon: Uuid? = null, // PersonId des Zuweisers
|
||||
|
||||
var notizen: String? = null,
|
||||
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
val createdAt: Instant = Clock.System.now(),
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
var updatedAt: Instant = Clock.System.now()
|
||||
)
|
||||
@@ -0,0 +1,44 @@
|
||||
package at.mocode.members.domain.model
|
||||
|
||||
import at.mocode.enums.RolleE
|
||||
import at.mocode.serializers.KotlinInstantSerializer
|
||||
import at.mocode.serializers.UuidSerializer
|
||||
import com.benasher44.uuid.Uuid
|
||||
import com.benasher44.uuid.uuid4
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Repräsentiert eine Rolle im System für die Mitgliederverwaltung.
|
||||
*
|
||||
* Rollen definieren die grundlegenden Funktionen und Verantwortlichkeiten
|
||||
* von Personen im System (z.B. Reiter, Trainer, Funktionär, Admin).
|
||||
* Jede Rolle kann mit spezifischen Berechtigungen verknüpft werden.
|
||||
*
|
||||
* @property rolleId Eindeutiger interner Identifikator für diese Rolle (UUID).
|
||||
* @property rolleTyp Der Typ der Rolle aus der RolleE Enumeration.
|
||||
* @property name Anzeigename der Rolle (z.B. "Administrator", "Vereinsadministrator").
|
||||
* @property beschreibung Detaillierte Beschreibung der Rolle und ihrer Verantwortlichkeiten.
|
||||
* @property istAktiv Gibt an, ob diese Rolle aktuell aktiv ist und zugewiesen werden kann.
|
||||
* @property istSystemRolle Gibt an, ob es sich um eine Systemrolle handelt, die nicht gelöscht werden kann.
|
||||
* @property createdAt Zeitstempel der Erstellung dieser Rolle.
|
||||
* @property updatedAt Zeitstempel der letzten Aktualisierung dieser Rolle.
|
||||
*/
|
||||
@Serializable
|
||||
data class DomRolle(
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val rolleId: Uuid = uuid4(),
|
||||
|
||||
val rolleTyp: RolleE,
|
||||
var name: String,
|
||||
var beschreibung: String? = null,
|
||||
|
||||
var istAktiv: Boolean = true,
|
||||
var istSystemRolle: Boolean = false,
|
||||
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
val createdAt: Instant = Clock.System.now(),
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
var updatedAt: Instant = Clock.System.now()
|
||||
)
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package at.mocode.members.domain.model
|
||||
|
||||
import at.mocode.serializers.KotlinInstantSerializer
|
||||
import at.mocode.serializers.UuidSerializer
|
||||
import com.benasher44.uuid.Uuid
|
||||
import com.benasher44.uuid.uuid4
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Repräsentiert die Zuordnung einer Berechtigung zu einer Rolle.
|
||||
*
|
||||
* Diese Entität verwaltet die Many-to-Many-Beziehung zwischen Rollen und Berechtigungen.
|
||||
* Eine Rolle kann mehrere Berechtigungen haben (z.B. Trainer kann Personen lesen und Pferde bearbeiten),
|
||||
* und eine Berechtigung kann mehreren Rollen zugeordnet werden.
|
||||
*
|
||||
* @property rolleBerechtigungId Eindeutiger interner Identifikator für diese Berechtigungszuordnung (UUID).
|
||||
* @property rolleId Fremdschlüssel zur Rolle (DomRolle.rolleId).
|
||||
* @property berechtigungId Fremdschlüssel zur Berechtigung (DomBerechtigung.berechtigungId).
|
||||
* @property istAktiv Gibt an, ob diese Berechtigungszuordnung aktuell aktiv ist.
|
||||
* @property zugewiesenVon Optionale Referenz auf die Person, die diese Berechtigung zugewiesen hat.
|
||||
* @property notizen Optionale Notizen zur Berechtigungszuordnung.
|
||||
* @property createdAt Zeitstempel der Erstellung dieser Berechtigungszuordnung.
|
||||
* @property updatedAt Zeitstempel der letzten Aktualisierung dieser Berechtigungszuordnung.
|
||||
*/
|
||||
@Serializable
|
||||
data class DomRolleBerechtigung(
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val rolleBerechtigungId: Uuid = uuid4(),
|
||||
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val rolleId: Uuid,
|
||||
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val berechtigungId: Uuid,
|
||||
|
||||
var istAktiv: Boolean = true,
|
||||
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
var zugewiesenVon: Uuid? = null, // PersonId des Zuweisers
|
||||
|
||||
var notizen: String? = null,
|
||||
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
val createdAt: Instant = Clock.System.now(),
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
var updatedAt: Instant = Clock.System.now()
|
||||
)
|
||||
@@ -0,0 +1,63 @@
|
||||
package at.mocode.members.domain.model
|
||||
|
||||
import at.mocode.serializers.KotlinInstantSerializer
|
||||
import at.mocode.serializers.UuidSerializer
|
||||
import com.benasher44.uuid.Uuid
|
||||
import com.benasher44.uuid.uuid4
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Repräsentiert einen Benutzer für die Authentifizierung im System.
|
||||
*
|
||||
* Diese Entität verwaltet die Anmeldedaten und ist mit einer Person verknüpft.
|
||||
* Ein Benutzer kann sich am System anmelden und erhält basierend auf seinen
|
||||
* zugewiesenen Rollen entsprechende Berechtigungen.
|
||||
*
|
||||
* @property userId Eindeutiger interner Identifikator für diesen Benutzer (UUID).
|
||||
* @property personId Fremdschlüssel zur verknüpften Person (DomPerson.personId).
|
||||
* @property username Eindeutiger Benutzername für die Anmeldung.
|
||||
* @property email E-Mail-Adresse des Benutzers (kann auch als Login verwendet werden).
|
||||
* @property passwordHash Gehashtes Passwort des Benutzers.
|
||||
* @property salt Salt für das Passwort-Hashing.
|
||||
* @property istAktiv Gibt an, ob dieser Benutzer aktuell aktiv ist und sich anmelden kann.
|
||||
* @property istEmailVerifiziert Gibt an, ob die E-Mail-Adresse verifiziert wurde.
|
||||
* @property letzteAnmeldung Zeitstempel der letzten erfolgreichen Anmeldung.
|
||||
* @property fehlgeschlageneAnmeldungen Anzahl der fehlgeschlagenen Anmeldeversuche.
|
||||
* @property gesperrtBis Optionaler Zeitstempel bis wann der Benutzer gesperrt ist.
|
||||
* @property passwortAendernErforderlich Gibt an, ob der Benutzer sein Passwort ändern muss.
|
||||
* @property createdAt Zeitstempel der Erstellung dieses Benutzers.
|
||||
* @property updatedAt Zeitstempel der letzten Aktualisierung dieses Benutzers.
|
||||
*/
|
||||
@Serializable
|
||||
data class DomUser(
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val userId: Uuid = uuid4(),
|
||||
|
||||
@Serializable(with = UuidSerializer::class)
|
||||
val personId: Uuid,
|
||||
|
||||
var username: String,
|
||||
var email: String,
|
||||
var passwordHash: String,
|
||||
var salt: String,
|
||||
|
||||
var istAktiv: Boolean = true,
|
||||
var istEmailVerifiziert: Boolean = false,
|
||||
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
var letzteAnmeldung: Instant? = null,
|
||||
|
||||
var fehlgeschlageneAnmeldungen: Int = 0,
|
||||
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
var gesperrtBis: Instant? = null,
|
||||
|
||||
var passwortAendernErforderlich: Boolean = false,
|
||||
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
val createdAt: Instant = Clock.System.now(),
|
||||
@Serializable(with = KotlinInstantSerializer::class)
|
||||
var updatedAt: Instant = Clock.System.now()
|
||||
)
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package at.mocode.members.domain.repository
|
||||
|
||||
import at.mocode.enums.BerechtigungE
|
||||
import at.mocode.members.domain.model.DomBerechtigung
|
||||
import com.benasher44.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Repository-Interface für die Verwaltung von Berechtigungen.
|
||||
*
|
||||
* Definiert die Operationen für das Erstellen, Lesen, Aktualisieren und Löschen
|
||||
* von Berechtigungen im System.
|
||||
*/
|
||||
interface BerechtigungRepository {
|
||||
|
||||
/**
|
||||
* Speichert eine Berechtigung (erstellen oder aktualisieren).
|
||||
*
|
||||
* @param berechtigung Die zu speichernde Berechtigung.
|
||||
* @return Die gespeicherte Berechtigung mit aktualisierten Zeitstempeln.
|
||||
*/
|
||||
suspend fun save(berechtigung: DomBerechtigung): DomBerechtigung
|
||||
|
||||
/**
|
||||
* Sucht eine Berechtigung anhand ihrer ID.
|
||||
*
|
||||
* @param berechtigungId Die eindeutige ID der Berechtigung.
|
||||
* @return Die gefundene Berechtigung oder null, falls nicht vorhanden.
|
||||
*/
|
||||
suspend fun findById(berechtigungId: Uuid): DomBerechtigung?
|
||||
|
||||
/**
|
||||
* Sucht eine Berechtigung anhand ihres Typs.
|
||||
*
|
||||
* @param berechtigungTyp Der Typ der Berechtigung.
|
||||
* @return Die gefundene Berechtigung oder null, falls nicht vorhanden.
|
||||
*/
|
||||
suspend fun findByTyp(berechtigungTyp: BerechtigungE): DomBerechtigung?
|
||||
|
||||
/**
|
||||
* Sucht Berechtigungen anhand ihres Namens (Teilstring-Suche).
|
||||
*
|
||||
* @param name Der Name oder Teilname der Berechtigung.
|
||||
* @return Liste der gefundenen Berechtigungen.
|
||||
*/
|
||||
suspend fun findByName(name: String): List<DomBerechtigung>
|
||||
|
||||
/**
|
||||
* Sucht Berechtigungen anhand der Ressource.
|
||||
*
|
||||
* @param ressource Die Ressource (z.B. "Person", "Verein").
|
||||
* @return Liste der gefundenen Berechtigungen.
|
||||
*/
|
||||
suspend fun findByRessource(ressource: String): List<DomBerechtigung>
|
||||
|
||||
/**
|
||||
* Sucht Berechtigungen anhand der Aktion.
|
||||
*
|
||||
* @param aktion Die Aktion (z.B. "lesen", "erstellen").
|
||||
* @return Liste der gefundenen Berechtigungen.
|
||||
*/
|
||||
suspend fun findByAktion(aktion: String): List<DomBerechtigung>
|
||||
|
||||
/**
|
||||
* Gibt alle aktiven Berechtigungen zurück.
|
||||
*
|
||||
* @return Liste aller aktiven Berechtigungen.
|
||||
*/
|
||||
suspend fun findAllActive(): List<DomBerechtigung>
|
||||
|
||||
/**
|
||||
* Gibt alle Berechtigungen zurück (aktive und inaktive).
|
||||
*
|
||||
* @return Liste aller Berechtigungen.
|
||||
*/
|
||||
suspend fun findAll(): List<DomBerechtigung>
|
||||
|
||||
/**
|
||||
* Deaktiviert eine Berechtigung (soft delete).
|
||||
*
|
||||
* @param berechtigungId Die ID der zu deaktivierenden Berechtigung.
|
||||
* @return true, wenn die Deaktivierung erfolgreich war, false sonst.
|
||||
*/
|
||||
suspend fun deactivateBerechtigung(berechtigungId: Uuid): Boolean
|
||||
|
||||
/**
|
||||
* Löscht eine Berechtigung permanent (nur für nicht-System-Berechtigungen).
|
||||
*
|
||||
* @param berechtigungId Die ID der zu löschenden Berechtigung.
|
||||
* @return true, wenn das Löschen erfolgreich war, false sonst.
|
||||
*/
|
||||
suspend fun deleteBerechtigung(berechtigungId: Uuid): Boolean
|
||||
|
||||
/**
|
||||
* Prüft, ob eine Berechtigung mit dem gegebenen Typ bereits existiert.
|
||||
*
|
||||
* @param berechtigungTyp Der zu prüfende Berechtigungstyp.
|
||||
* @return true, wenn eine Berechtigung mit diesem Typ existiert, false sonst.
|
||||
*/
|
||||
suspend fun existsByTyp(berechtigungTyp: BerechtigungE): Boolean
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
package at.mocode.members.domain.repository
|
||||
|
||||
import at.mocode.members.domain.model.DomPersonRolle
|
||||
import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.LocalDate
|
||||
|
||||
/**
|
||||
* Repository-Interface für die Verwaltung von Person-Rolle-Zuordnungen.
|
||||
*
|
||||
* Definiert die Operationen für das Erstellen, Lesen, Aktualisieren und Löschen
|
||||
* von Person-Rolle-Beziehungen im System.
|
||||
*/
|
||||
interface PersonRolleRepository {
|
||||
|
||||
/**
|
||||
* Speichert eine Person-Rolle-Zuordnung (erstellen oder aktualisieren).
|
||||
*
|
||||
* @param personRolle Die zu speichernde Person-Rolle-Zuordnung.
|
||||
* @return Die gespeicherte Person-Rolle-Zuordnung mit aktualisierten Zeitstempeln.
|
||||
*/
|
||||
suspend fun save(personRolle: DomPersonRolle): DomPersonRolle
|
||||
|
||||
/**
|
||||
* Sucht eine Person-Rolle-Zuordnung anhand ihrer ID.
|
||||
*
|
||||
* @param personRolleId Die eindeutige ID der Person-Rolle-Zuordnung.
|
||||
* @return Die gefundene Person-Rolle-Zuordnung oder null, falls nicht vorhanden.
|
||||
*/
|
||||
suspend fun findById(personRolleId: Uuid): DomPersonRolle?
|
||||
|
||||
/**
|
||||
* Sucht alle Rollen einer bestimmten Person.
|
||||
*
|
||||
* @param personId Die eindeutige ID der Person.
|
||||
* @param nurAktive Wenn true, werden nur aktive Zuordnungen zurückgegeben.
|
||||
* @return Liste der Person-Rolle-Zuordnungen.
|
||||
*/
|
||||
suspend fun findByPersonId(personId: Uuid, nurAktive: Boolean = true): List<DomPersonRolle>
|
||||
|
||||
/**
|
||||
* Sucht alle Personen mit einer bestimmten Rolle.
|
||||
*
|
||||
* @param rolleId Die eindeutige ID der Rolle.
|
||||
* @param nurAktive Wenn true, werden nur aktive Zuordnungen zurückgegeben.
|
||||
* @return Liste der Person-Rolle-Zuordnungen.
|
||||
*/
|
||||
suspend fun findByRolleId(rolleId: Uuid, nurAktive: Boolean = true): List<DomPersonRolle>
|
||||
|
||||
/**
|
||||
* Sucht alle Person-Rolle-Zuordnungen für einen bestimmten Verein.
|
||||
*
|
||||
* @param vereinId Die eindeutige ID des Vereins.
|
||||
* @param nurAktive Wenn true, werden nur aktive Zuordnungen zurückgegeben.
|
||||
* @return Liste der Person-Rolle-Zuordnungen.
|
||||
*/
|
||||
suspend fun findByVereinId(vereinId: Uuid, nurAktive: Boolean = true): List<DomPersonRolle>
|
||||
|
||||
/**
|
||||
* Sucht eine spezifische Person-Rolle-Zuordnung.
|
||||
*
|
||||
* @param personId Die eindeutige ID der Person.
|
||||
* @param rolleId Die eindeutige ID der Rolle.
|
||||
* @param vereinId Die eindeutige ID des Vereins (optional).
|
||||
* @return Die gefundene Person-Rolle-Zuordnung oder null, falls nicht vorhanden.
|
||||
*/
|
||||
suspend fun findByPersonAndRolle(personId: Uuid, rolleId: Uuid, vereinId: Uuid? = null): DomPersonRolle?
|
||||
|
||||
/**
|
||||
* Sucht alle Person-Rolle-Zuordnungen, die zu einem bestimmten Datum gültig sind.
|
||||
*
|
||||
* @param stichtag Das Datum, für das die Gültigkeit geprüft werden soll.
|
||||
* @param nurAktive Wenn true, werden nur aktive Zuordnungen zurückgegeben.
|
||||
* @return Liste der gültigen Person-Rolle-Zuordnungen.
|
||||
*/
|
||||
suspend fun findValidAt(stichtag: LocalDate, nurAktive: Boolean = true): List<DomPersonRolle>
|
||||
|
||||
/**
|
||||
* Sucht alle Person-Rolle-Zuordnungen einer Person, die zu einem bestimmten Datum gültig sind.
|
||||
*
|
||||
* @param personId Die eindeutige ID der Person.
|
||||
* @param stichtag Das Datum, für das die Gültigkeit geprüft werden soll.
|
||||
* @param nurAktive Wenn true, werden nur aktive Zuordnungen zurückgegeben.
|
||||
* @return Liste der gültigen Person-Rolle-Zuordnungen.
|
||||
*/
|
||||
suspend fun findByPersonValidAt(personId: Uuid, stichtag: LocalDate, nurAktive: Boolean = true): List<DomPersonRolle>
|
||||
|
||||
/**
|
||||
* Deaktiviert eine Person-Rolle-Zuordnung.
|
||||
*
|
||||
* @param personRolleId Die ID der zu deaktivierenden Person-Rolle-Zuordnung.
|
||||
* @return true, wenn die Deaktivierung erfolgreich war, false sonst.
|
||||
*/
|
||||
suspend fun deactivatePersonRolle(personRolleId: Uuid): Boolean
|
||||
|
||||
/**
|
||||
* Löscht eine Person-Rolle-Zuordnung permanent.
|
||||
*
|
||||
* @param personRolleId Die ID der zu löschenden Person-Rolle-Zuordnung.
|
||||
* @return true, wenn das Löschen erfolgreich war, false sonst.
|
||||
*/
|
||||
suspend fun deletePersonRolle(personRolleId: Uuid): Boolean
|
||||
|
||||
/**
|
||||
* Prüft, ob eine Person eine bestimmte Rolle hat.
|
||||
*
|
||||
* @param personId Die eindeutige ID der Person.
|
||||
* @param rolleId Die eindeutige ID der Rolle.
|
||||
* @param vereinId Die eindeutige ID des Vereins (optional).
|
||||
* @param stichtag Das Datum, für das die Gültigkeit geprüft werden soll (optional, default: heute).
|
||||
* @return true, wenn die Person die Rolle hat, false sonst.
|
||||
*/
|
||||
suspend fun hasPersonRolle(personId: Uuid, rolleId: Uuid, vereinId: Uuid? = null, stichtag: LocalDate? = null): Boolean
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
package at.mocode.members.domain.repository
|
||||
|
||||
import at.mocode.members.domain.model.DomRolleBerechtigung
|
||||
import com.benasher44.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Repository-Interface für die Verwaltung von Rolle-Berechtigung-Zuordnungen.
|
||||
*
|
||||
* Definiert die Operationen für das Erstellen, Lesen, Aktualisieren und Löschen
|
||||
* von Rolle-Berechtigung-Beziehungen im System.
|
||||
*/
|
||||
interface RolleBerechtigungRepository {
|
||||
|
||||
/**
|
||||
* Speichert eine Rolle-Berechtigung-Zuordnung (erstellen oder aktualisieren).
|
||||
*
|
||||
* @param rolleBerechtigung Die zu speichernde Rolle-Berechtigung-Zuordnung.
|
||||
* @return Die gespeicherte Rolle-Berechtigung-Zuordnung mit aktualisierten Zeitstempeln.
|
||||
*/
|
||||
suspend fun save(rolleBerechtigung: DomRolleBerechtigung): DomRolleBerechtigung
|
||||
|
||||
/**
|
||||
* Sucht eine Rolle-Berechtigung-Zuordnung anhand ihrer ID.
|
||||
*
|
||||
* @param rolleBerechtigungId Die eindeutige ID der Rolle-Berechtigung-Zuordnung.
|
||||
* @return Die gefundene Rolle-Berechtigung-Zuordnung oder null, falls nicht vorhanden.
|
||||
*/
|
||||
suspend fun findById(rolleBerechtigungId: Uuid): DomRolleBerechtigung?
|
||||
|
||||
/**
|
||||
* Sucht alle Berechtigungen einer bestimmten Rolle.
|
||||
*
|
||||
* @param rolleId Die eindeutige ID der Rolle.
|
||||
* @param nurAktive Wenn true, werden nur aktive Zuordnungen zurückgegeben.
|
||||
* @return Liste der Rolle-Berechtigung-Zuordnungen.
|
||||
*/
|
||||
suspend fun findByRolleId(rolleId: Uuid, nurAktive: Boolean = true): List<DomRolleBerechtigung>
|
||||
|
||||
/**
|
||||
* Sucht alle Rollen mit einer bestimmten Berechtigung.
|
||||
*
|
||||
* @param berechtigungId Die eindeutige ID der Berechtigung.
|
||||
* @param nurAktive Wenn true, werden nur aktive Zuordnungen zurückgegeben.
|
||||
* @return Liste der Rolle-Berechtigung-Zuordnungen.
|
||||
*/
|
||||
suspend fun findByBerechtigungId(berechtigungId: Uuid, nurAktive: Boolean = true): List<DomRolleBerechtigung>
|
||||
|
||||
/**
|
||||
* Sucht eine spezifische Rolle-Berechtigung-Zuordnung.
|
||||
*
|
||||
* @param rolleId Die eindeutige ID der Rolle.
|
||||
* @param berechtigungId Die eindeutige ID der Berechtigung.
|
||||
* @return Die gefundene Rolle-Berechtigung-Zuordnung oder null, falls nicht vorhanden.
|
||||
*/
|
||||
suspend fun findByRolleAndBerechtigung(rolleId: Uuid, berechtigungId: Uuid): DomRolleBerechtigung?
|
||||
|
||||
/**
|
||||
* Gibt alle aktiven Rolle-Berechtigung-Zuordnungen zurück.
|
||||
*
|
||||
* @return Liste aller aktiven Rolle-Berechtigung-Zuordnungen.
|
||||
*/
|
||||
suspend fun findAllActive(): List<DomRolleBerechtigung>
|
||||
|
||||
/**
|
||||
* Gibt alle Rolle-Berechtigung-Zuordnungen zurück (aktive und inaktive).
|
||||
*
|
||||
* @return Liste aller Rolle-Berechtigung-Zuordnungen.
|
||||
*/
|
||||
suspend fun findAll(): List<DomRolleBerechtigung>
|
||||
|
||||
/**
|
||||
* Deaktiviert eine Rolle-Berechtigung-Zuordnung.
|
||||
*
|
||||
* @param rolleBerechtigungId Die ID der zu deaktivierenden Rolle-Berechtigung-Zuordnung.
|
||||
* @return true, wenn die Deaktivierung erfolgreich war, false sonst.
|
||||
*/
|
||||
suspend fun deactivateRolleBerechtigung(rolleBerechtigungId: Uuid): Boolean
|
||||
|
||||
/**
|
||||
* Löscht eine Rolle-Berechtigung-Zuordnung permanent.
|
||||
*
|
||||
* @param rolleBerechtigungId Die ID der zu löschenden Rolle-Berechtigung-Zuordnung.
|
||||
* @return true, wenn das Löschen erfolgreich war, false sonst.
|
||||
*/
|
||||
suspend fun deleteRolleBerechtigung(rolleBerechtigungId: Uuid): Boolean
|
||||
|
||||
/**
|
||||
* Prüft, ob eine Rolle eine bestimmte Berechtigung hat.
|
||||
*
|
||||
* @param rolleId Die eindeutige ID der Rolle.
|
||||
* @param berechtigungId Die eindeutige ID der Berechtigung.
|
||||
* @return true, wenn die Rolle die Berechtigung hat, false sonst.
|
||||
*/
|
||||
suspend fun hasRolleBerechtigung(rolleId: Uuid, berechtigungId: Uuid): Boolean
|
||||
|
||||
/**
|
||||
* Weist einer Rolle eine Berechtigung zu.
|
||||
*
|
||||
* @param rolleId Die eindeutige ID der Rolle.
|
||||
* @param berechtigungId Die eindeutige ID der Berechtigung.
|
||||
* @param zugewiesenVon Die ID der Person, die die Zuweisung vornimmt (optional).
|
||||
* @return Die erstellte Rolle-Berechtigung-Zuordnung.
|
||||
*/
|
||||
suspend fun assignBerechtigungToRolle(rolleId: Uuid, berechtigungId: Uuid, zugewiesenVon: Uuid? = null): DomRolleBerechtigung
|
||||
|
||||
/**
|
||||
* Entzieht einer Rolle eine Berechtigung.
|
||||
*
|
||||
* @param rolleId Die eindeutige ID der Rolle.
|
||||
* @param berechtigungId Die eindeutige ID der Berechtigung.
|
||||
* @return true, wenn die Berechtigung erfolgreich entzogen wurde, false sonst.
|
||||
*/
|
||||
suspend fun revokeBerechtigungFromRolle(rolleId: Uuid, berechtigungId: Uuid): Boolean
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package at.mocode.members.domain.repository
|
||||
|
||||
import at.mocode.enums.RolleE
|
||||
import at.mocode.members.domain.model.DomRolle
|
||||
import com.benasher44.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Repository-Interface für die Verwaltung von Rollen.
|
||||
*
|
||||
* Definiert die Operationen für das Erstellen, Lesen, Aktualisieren und Löschen
|
||||
* von Rollen im System.
|
||||
*/
|
||||
interface RolleRepository {
|
||||
|
||||
/**
|
||||
* Erstellt eine neue Rolle im System.
|
||||
*
|
||||
* @param rolle Die zu erstellende Rolle.
|
||||
* @return Die erstellte Rolle mit aktualisierten Zeitstempeln.
|
||||
*/
|
||||
suspend fun save(rolle: DomRolle): DomRolle
|
||||
|
||||
/**
|
||||
* Sucht eine Rolle anhand ihrer ID.
|
||||
*
|
||||
* @param rolleId Die eindeutige ID der Rolle.
|
||||
* @return Die gefundene Rolle oder null, falls nicht vorhanden.
|
||||
*/
|
||||
suspend fun findById(rolleId: Uuid): DomRolle?
|
||||
|
||||
/**
|
||||
* Sucht eine Rolle anhand ihres Typs.
|
||||
*
|
||||
* @param rolleTyp Der Typ der Rolle.
|
||||
* @return Die gefundene Rolle oder null, falls nicht vorhanden.
|
||||
*/
|
||||
suspend fun findByTyp(rolleTyp: RolleE): DomRolle?
|
||||
|
||||
/**
|
||||
* Sucht Rollen anhand ihres Namens (Teilstring-Suche).
|
||||
*
|
||||
* @param name Der Name oder Teilname der Rolle.
|
||||
* @return Liste der gefundenen Rollen.
|
||||
*/
|
||||
suspend fun findByName(name: String): List<DomRolle>
|
||||
|
||||
/**
|
||||
* Gibt alle aktiven Rollen zurück.
|
||||
*
|
||||
* @return Liste aller aktiven Rollen.
|
||||
*/
|
||||
suspend fun findAllActive(): List<DomRolle>
|
||||
|
||||
/**
|
||||
* Gibt alle Rollen zurück (aktive und inaktive).
|
||||
*
|
||||
* @return Liste aller Rollen.
|
||||
*/
|
||||
suspend fun findAll(): List<DomRolle>
|
||||
|
||||
/**
|
||||
* Aktualisiert eine bestehende Rolle.
|
||||
* Note: This is handled by the save method which works for both create and update.
|
||||
*
|
||||
* @param rolle Die zu aktualisierende Rolle.
|
||||
* @return Die aktualisierte Rolle mit aktualisierten Zeitstempeln.
|
||||
*/
|
||||
// suspend fun updateRolle(rolle: DomRolle): DomRolle // Handled by save method
|
||||
|
||||
/**
|
||||
* Deaktiviert eine Rolle (soft delete).
|
||||
*
|
||||
* @param rolleId Die ID der zu deaktivierenden Rolle.
|
||||
* @return true, wenn die Deaktivierung erfolgreich war, false sonst.
|
||||
*/
|
||||
suspend fun deactivateRolle(rolleId: Uuid): Boolean
|
||||
|
||||
/**
|
||||
* Löscht eine Rolle permanent (nur für nicht-System-Rollen).
|
||||
*
|
||||
* @param rolleId Die ID der zu löschenden Rolle.
|
||||
* @return true, wenn das Löschen erfolgreich war, false sonst.
|
||||
*/
|
||||
suspend fun deleteRolle(rolleId: Uuid): Boolean
|
||||
|
||||
/**
|
||||
* Prüft, ob eine Rolle mit dem gegebenen Typ bereits existiert.
|
||||
*
|
||||
* @param rolleTyp Der zu prüfende Rollentyp.
|
||||
* @return true, wenn eine Rolle mit diesem Typ existiert, false sonst.
|
||||
*/
|
||||
suspend fun existsByTyp(rolleTyp: RolleE): Boolean
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
package at.mocode.members.domain.repository
|
||||
|
||||
import at.mocode.members.domain.model.DomUser
|
||||
import com.benasher44.uuid.Uuid
|
||||
|
||||
/**
|
||||
* Repository interface for user management operations.
|
||||
*
|
||||
* Provides methods for user authentication, user management,
|
||||
* and user-related database operations.
|
||||
*/
|
||||
interface UserRepository {
|
||||
|
||||
/**
|
||||
* Creates a new user in the system.
|
||||
*
|
||||
* @param user The user to create
|
||||
* @return The created user with generated ID
|
||||
*/
|
||||
suspend fun createUser(user: DomUser): DomUser
|
||||
|
||||
/**
|
||||
* Finds a user by their unique user ID.
|
||||
*
|
||||
* @param userId The unique user ID
|
||||
* @return The user if found, null otherwise
|
||||
*/
|
||||
suspend fun findById(userId: Uuid): DomUser?
|
||||
|
||||
/**
|
||||
* Finds a user by their username.
|
||||
*
|
||||
* @param username The username to search for
|
||||
* @return The user if found, null otherwise
|
||||
*/
|
||||
suspend fun findByUsername(username: String): DomUser?
|
||||
|
||||
/**
|
||||
* Finds a user by their email address.
|
||||
*
|
||||
* @param email The email address to search for
|
||||
* @return The user if found, null otherwise
|
||||
*/
|
||||
suspend fun findByEmail(email: String): DomUser?
|
||||
|
||||
/**
|
||||
* Finds a user by their associated person ID.
|
||||
*
|
||||
* @param personId The person ID to search for
|
||||
* @return The user if found, null otherwise
|
||||
*/
|
||||
suspend fun findByPersonId(personId: Uuid): DomUser?
|
||||
|
||||
/**
|
||||
* Updates an existing user.
|
||||
*
|
||||
* @param user The user to update
|
||||
* @return The updated user
|
||||
*/
|
||||
suspend fun updateUser(user: DomUser): DomUser
|
||||
|
||||
/**
|
||||
* Updates the last login timestamp for a user.
|
||||
*
|
||||
* @param userId The user ID
|
||||
*/
|
||||
suspend fun updateLastLogin(userId: Uuid)
|
||||
|
||||
/**
|
||||
* Increments the failed login attempts counter for a user.
|
||||
*
|
||||
* @param userId The user ID
|
||||
*/
|
||||
suspend fun incrementFailedLoginAttempts(userId: Uuid)
|
||||
|
||||
/**
|
||||
* Resets the failed login attempts counter for a user.
|
||||
*
|
||||
* @param userId The user ID
|
||||
*/
|
||||
suspend fun resetFailedLoginAttempts(userId: Uuid)
|
||||
|
||||
/**
|
||||
* Locks a user account until the specified timestamp.
|
||||
*
|
||||
* @param userId The user ID
|
||||
* @param lockedUntil The timestamp until when the user is locked
|
||||
*/
|
||||
suspend fun lockUser(userId: Uuid, lockedUntil: kotlinx.datetime.Instant)
|
||||
|
||||
/**
|
||||
* Unlocks a user account.
|
||||
*
|
||||
* @param userId The user ID
|
||||
*/
|
||||
suspend fun unlockUser(userId: Uuid)
|
||||
|
||||
/**
|
||||
* Activates or deactivates a user account.
|
||||
*
|
||||
* @param userId The user ID
|
||||
* @param isActive Whether the user should be active
|
||||
*/
|
||||
suspend fun setUserActive(userId: Uuid, isActive: Boolean)
|
||||
|
||||
/**
|
||||
* Marks a user's email as verified.
|
||||
*
|
||||
* @param userId The user ID
|
||||
*/
|
||||
suspend fun markEmailAsVerified(userId: Uuid)
|
||||
|
||||
/**
|
||||
* Updates a user's password hash and salt.
|
||||
*
|
||||
* @param userId The user ID
|
||||
* @param passwordHash The new password hash
|
||||
* @param salt The new salt
|
||||
*/
|
||||
suspend fun updatePassword(userId: Uuid, passwordHash: String, salt: String)
|
||||
|
||||
/**
|
||||
* Deletes a user from the system.
|
||||
*
|
||||
* @param userId The user ID to delete
|
||||
* @return True if the user was deleted, false if not found
|
||||
*/
|
||||
suspend fun deleteUser(userId: Uuid): Boolean
|
||||
|
||||
/**
|
||||
* Gets all users in the system.
|
||||
*
|
||||
* @return List of all users
|
||||
*/
|
||||
suspend fun getAllUsers(): List<DomUser>
|
||||
|
||||
/**
|
||||
* Gets all active users in the system.
|
||||
*
|
||||
* @return List of all active users
|
||||
*/
|
||||
suspend fun getActiveUsers(): List<DomUser>
|
||||
}
|
||||
+326
@@ -0,0 +1,326 @@
|
||||
package at.mocode.members.domain.service
|
||||
|
||||
import at.mocode.members.domain.model.DomUser
|
||||
import at.mocode.members.domain.repository.UserRepository
|
||||
import at.mocode.validation.ValidationResult
|
||||
import at.mocode.validation.ValidationError
|
||||
import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
|
||||
/**
|
||||
* Service for user authentication and session management.
|
||||
*
|
||||
* Handles user login, logout, registration, and JWT token management.
|
||||
* Coordinates between UserRepository, PasswordService, and other authentication components.
|
||||
*/
|
||||
class AuthenticationService(
|
||||
private val userRepository: UserRepository,
|
||||
private val passwordService: PasswordService,
|
||||
private val jwtService: JwtService
|
||||
) {
|
||||
|
||||
companion object {
|
||||
private const val MAX_FAILED_ATTEMPTS = 5
|
||||
private const val LOCKOUT_DURATION_MINUTES = 30L
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class for login credentials.
|
||||
*/
|
||||
data class LoginCredentials(
|
||||
val usernameOrEmail: String,
|
||||
val password: String
|
||||
)
|
||||
|
||||
/**
|
||||
* Data class for user registration.
|
||||
*/
|
||||
data class UserRegistration(
|
||||
val personId: Uuid,
|
||||
val username: String,
|
||||
val email: String,
|
||||
val password: String
|
||||
)
|
||||
|
||||
/**
|
||||
* Data class for authentication result.
|
||||
*/
|
||||
data class AuthenticationResult(
|
||||
val success: Boolean,
|
||||
val user: DomUser? = null,
|
||||
val token: String? = null,
|
||||
val message: String? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* Authenticates a user with username/email and password.
|
||||
*
|
||||
* @param credentials The login credentials
|
||||
* @return AuthenticationResult with success status and user data
|
||||
*/
|
||||
suspend fun authenticate(credentials: LoginCredentials): AuthenticationResult {
|
||||
try {
|
||||
// Find user by username or email
|
||||
val user = findUserByUsernameOrEmail(credentials.usernameOrEmail)
|
||||
?: return AuthenticationResult(
|
||||
success = false,
|
||||
message = "Invalid username or password"
|
||||
)
|
||||
|
||||
// Check if user is locked
|
||||
if (isUserLocked(user)) {
|
||||
return AuthenticationResult(
|
||||
success = false,
|
||||
message = "Account is temporarily locked due to too many failed login attempts"
|
||||
)
|
||||
}
|
||||
|
||||
// Check if user is active
|
||||
if (!user.istAktiv) {
|
||||
return AuthenticationResult(
|
||||
success = false,
|
||||
message = "Account is deactivated"
|
||||
)
|
||||
}
|
||||
|
||||
// Verify password
|
||||
if (!passwordService.verifyPassword(credentials.password, user.passwordHash, user.salt)) {
|
||||
// Increment failed attempts
|
||||
userRepository.incrementFailedLoginAttempts(user.userId)
|
||||
|
||||
// Lock user if too many failed attempts
|
||||
val updatedUser = userRepository.findById(user.userId)
|
||||
if (updatedUser != null && updatedUser.fehlgeschlageneAnmeldungen >= MAX_FAILED_ATTEMPTS) {
|
||||
val lockUntil = Clock.System.now().plus(30.minutes)
|
||||
userRepository.lockUser(user.userId, lockUntil)
|
||||
}
|
||||
|
||||
return AuthenticationResult(
|
||||
success = false,
|
||||
message = "Invalid username or password"
|
||||
)
|
||||
}
|
||||
|
||||
// Reset failed attempts on successful login
|
||||
userRepository.resetFailedLoginAttempts(user.userId)
|
||||
userRepository.updateLastLogin(user.userId)
|
||||
|
||||
// Generate JWT token
|
||||
val tokenInfo = jwtService.generateToken(user)
|
||||
val token = tokenInfo.token
|
||||
|
||||
return AuthenticationResult(
|
||||
success = true,
|
||||
user = user,
|
||||
token = token,
|
||||
message = "Login successful"
|
||||
)
|
||||
|
||||
} catch (e: Exception) {
|
||||
return AuthenticationResult(
|
||||
success = false,
|
||||
message = "Authentication failed: ${e.message}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class for user registration result.
|
||||
*/
|
||||
data class UserRegistrationResult(
|
||||
val success: Boolean,
|
||||
val user: DomUser? = null,
|
||||
val validationResult: ValidationResult? = null,
|
||||
val message: String? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* Registers a new user in the system.
|
||||
*
|
||||
* @param registration The user registration data
|
||||
* @return UserRegistrationResult with success status and user data
|
||||
*/
|
||||
suspend fun registerUser(registration: UserRegistration): UserRegistrationResult {
|
||||
try {
|
||||
// Validate password strength
|
||||
val passwordErrors = passwordService.getPasswordValidationErrors(registration.password)
|
||||
if (passwordErrors.isNotEmpty()) {
|
||||
val errors = passwordErrors.map { ValidationError("password", it) }
|
||||
return UserRegistrationResult(
|
||||
success = false,
|
||||
validationResult = ValidationResult.Invalid(errors)
|
||||
)
|
||||
}
|
||||
|
||||
// Check if username already exists
|
||||
val existingUserByUsername = userRepository.findByUsername(registration.username)
|
||||
if (existingUserByUsername != null) {
|
||||
return UserRegistrationResult(
|
||||
success = false,
|
||||
validationResult = ValidationResult.Invalid(listOf(ValidationError("username", "Username already exists")))
|
||||
)
|
||||
}
|
||||
|
||||
// Check if email already exists
|
||||
val existingUserByEmail = userRepository.findByEmail(registration.email)
|
||||
if (existingUserByEmail != null) {
|
||||
return UserRegistrationResult(
|
||||
success = false,
|
||||
validationResult = ValidationResult.Invalid(listOf(ValidationError("email", "Email already exists")))
|
||||
)
|
||||
}
|
||||
|
||||
// Check if person already has a user account
|
||||
val existingUserByPerson = userRepository.findByPersonId(registration.personId)
|
||||
if (existingUserByPerson != null) {
|
||||
return UserRegistrationResult(
|
||||
success = false,
|
||||
validationResult = ValidationResult.Invalid(listOf(ValidationError("personId", "Person already has a user account")))
|
||||
)
|
||||
}
|
||||
|
||||
// Generate salt and hash password
|
||||
val salt = passwordService.generateSalt()
|
||||
val passwordHash = passwordService.hashPassword(registration.password, salt)
|
||||
|
||||
// Create new user
|
||||
val newUser = DomUser(
|
||||
personId = registration.personId,
|
||||
username = registration.username,
|
||||
email = registration.email,
|
||||
passwordHash = passwordHash,
|
||||
salt = salt
|
||||
)
|
||||
|
||||
val createdUser = userRepository.createUser(newUser)
|
||||
return UserRegistrationResult(
|
||||
success = true,
|
||||
user = createdUser,
|
||||
validationResult = ValidationResult.Valid,
|
||||
message = "User registered successfully"
|
||||
)
|
||||
|
||||
} catch (e: Exception) {
|
||||
return UserRegistrationResult(
|
||||
success = false,
|
||||
validationResult = ValidationResult.Invalid(listOf(ValidationError("general", "Registration failed: ${e.message}"))),
|
||||
message = "Registration failed: ${e.message}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class for password change result.
|
||||
*/
|
||||
data class PasswordChangeResult(
|
||||
val success: Boolean,
|
||||
val validationResult: ValidationResult,
|
||||
val message: String? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* Changes a user's password.
|
||||
*
|
||||
* @param userId The user ID
|
||||
* @param currentPassword The current password
|
||||
* @param newPassword The new password
|
||||
* @return PasswordChangeResult indicating success or failure
|
||||
*/
|
||||
suspend fun changePassword(userId: Uuid, currentPassword: String, newPassword: String): PasswordChangeResult {
|
||||
try {
|
||||
val user = userRepository.findById(userId)
|
||||
?: return PasswordChangeResult(
|
||||
success = false,
|
||||
validationResult = ValidationResult.Invalid(listOf(ValidationError("userId", "User not found")))
|
||||
)
|
||||
|
||||
// Verify current password
|
||||
if (!passwordService.verifyPassword(currentPassword, user.passwordHash, user.salt)) {
|
||||
return PasswordChangeResult(
|
||||
success = false,
|
||||
validationResult = ValidationResult.Invalid(listOf(ValidationError("currentPassword", "Current password is incorrect")))
|
||||
)
|
||||
}
|
||||
|
||||
// Validate new password strength
|
||||
val passwordErrors = passwordService.getPasswordValidationErrors(newPassword)
|
||||
if (passwordErrors.isNotEmpty()) {
|
||||
val errors = passwordErrors.map { ValidationError("newPassword", it) }
|
||||
return PasswordChangeResult(
|
||||
success = false,
|
||||
validationResult = ValidationResult.Invalid(errors)
|
||||
)
|
||||
}
|
||||
|
||||
// Generate new salt and hash new password
|
||||
val newSalt = passwordService.generateSalt()
|
||||
val newPasswordHash = passwordService.hashPassword(newPassword, newSalt)
|
||||
|
||||
// Update password in database
|
||||
userRepository.updatePassword(userId, newPasswordHash, newSalt)
|
||||
|
||||
return PasswordChangeResult(
|
||||
success = true,
|
||||
validationResult = ValidationResult.Valid,
|
||||
message = "Password changed successfully"
|
||||
)
|
||||
|
||||
} catch (e: Exception) {
|
||||
return PasswordChangeResult(
|
||||
success = false,
|
||||
validationResult = ValidationResult.Invalid(listOf(ValidationError("general", "Password change failed: ${e.message}"))),
|
||||
message = "Password change failed: ${e.message}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a user by username or email.
|
||||
*/
|
||||
private suspend fun findUserByUsernameOrEmail(usernameOrEmail: String): DomUser? {
|
||||
return userRepository.findByUsername(usernameOrEmail)
|
||||
?: userRepository.findByEmail(usernameOrEmail)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a user is currently locked.
|
||||
*/
|
||||
private fun isUserLocked(user: DomUser): Boolean {
|
||||
val lockUntil = user.gesperrtBis ?: return false
|
||||
return Clock.System.now() < lockUntil
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a JWT token and returns the associated user.
|
||||
*
|
||||
* @param token The JWT token to validate
|
||||
* @return DomUser if token is valid and user exists, null otherwise
|
||||
*/
|
||||
suspend fun validateJwtToken(token: String): DomUser? {
|
||||
val payload = jwtService.validateToken(token) ?: return null
|
||||
return userRepository.findById(payload.userId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes a JWT token.
|
||||
*
|
||||
* @param token The current JWT token
|
||||
* @return New token string if refresh is successful, null otherwise
|
||||
*/
|
||||
fun refreshJwtToken(token: String): String? {
|
||||
val tokenInfo = jwtService.refreshToken(token) ?: return null
|
||||
return tokenInfo.token
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts user ID from a JWT token without full validation.
|
||||
*
|
||||
* @param token The JWT token
|
||||
* @return User ID if extractable, null otherwise
|
||||
*/
|
||||
fun extractUserIdFromToken(token: String): Uuid? {
|
||||
return jwtService.extractUserId(token)
|
||||
}
|
||||
}
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
package at.mocode.members.domain.service
|
||||
|
||||
import at.mocode.members.domain.model.DomUser
|
||||
import at.mocode.enums.RolleE
|
||||
import at.mocode.enums.BerechtigungE
|
||||
import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
|
||||
/**
|
||||
* Service for JWT token generation and validation.
|
||||
*
|
||||
* This is a simplified implementation for multiplatform compatibility.
|
||||
* In a production environment, consider using platform-specific JWT libraries.
|
||||
*/
|
||||
class JwtService(
|
||||
private val userAuthorizationService: UserAuthorizationService,
|
||||
private val secret: String = "default-secret-key-change-in-production",
|
||||
private val issuer: String = "meldestelle-api",
|
||||
private val audience: String = "meldestelle-users",
|
||||
private val expirationTimeMillis: Long = 3600000L // 1 hour
|
||||
) {
|
||||
|
||||
/**
|
||||
* Data class representing JWT token information.
|
||||
*/
|
||||
data class TokenInfo(
|
||||
val token: String,
|
||||
val expiresAt: Instant,
|
||||
val userId: Uuid
|
||||
)
|
||||
|
||||
/**
|
||||
* Data class representing decoded JWT payload.
|
||||
*/
|
||||
data class JwtPayload(
|
||||
val userId: Uuid,
|
||||
val username: String,
|
||||
val email: String,
|
||||
val roles: List<RolleE>,
|
||||
val permissions: List<BerechtigungE>,
|
||||
val issuedAt: Instant,
|
||||
val expiresAt: Instant,
|
||||
val issuer: String,
|
||||
val audience: String
|
||||
)
|
||||
|
||||
/**
|
||||
* Generates a JWT token for the given user.
|
||||
*
|
||||
* @param user The user for whom to generate the token
|
||||
* @return TokenInfo containing the token and expiration information
|
||||
*/
|
||||
suspend fun generateToken(user: DomUser): TokenInfo {
|
||||
val now = Clock.System.now()
|
||||
val expiresAt = Instant.fromEpochMilliseconds(now.toEpochMilliseconds() + expirationTimeMillis)
|
||||
|
||||
// Get user roles and permissions
|
||||
val authInfo = userAuthorizationService.getUserAuthInfo(user.userId)
|
||||
val roles = authInfo?.roles ?: emptyList()
|
||||
val permissions = authInfo?.permissions ?: emptyList()
|
||||
|
||||
// Create a simple token structure (in production, use proper JWT library)
|
||||
val payload = createPayload(user, roles, permissions, now, expiresAt)
|
||||
val token = encodeToken(payload)
|
||||
|
||||
return TokenInfo(
|
||||
token = token,
|
||||
expiresAt = expiresAt,
|
||||
userId = user.userId
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a JWT token and returns the payload if valid.
|
||||
*
|
||||
* @param token The JWT token to validate
|
||||
* @return JwtPayload if token is valid, null otherwise
|
||||
*/
|
||||
fun validateToken(token: String): JwtPayload? {
|
||||
return try {
|
||||
val payload = decodeToken(token)
|
||||
|
||||
// Check if token is expired
|
||||
if (Clock.System.now() > payload.expiresAt) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Check issuer and audience
|
||||
if (payload.issuer != issuer || payload.audience != audience) {
|
||||
return null
|
||||
}
|
||||
|
||||
payload
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes a JWT token if it's still valid but close to expiration.
|
||||
*
|
||||
* @param token The current JWT token
|
||||
* @return New TokenInfo if refresh is successful, null otherwise
|
||||
*/
|
||||
fun refreshToken(token: String): TokenInfo? {
|
||||
val payload = validateToken(token) ?: return null
|
||||
|
||||
// Check if token is within refresh window (e.g., last 15 minutes)
|
||||
val refreshWindowMillis = 15 * 60 * 1000L // 15 minutes
|
||||
val now = Clock.System.now()
|
||||
val timeUntilExpiry = payload.expiresAt.toEpochMilliseconds() - now.toEpochMilliseconds()
|
||||
|
||||
if (timeUntilExpiry > refreshWindowMillis) {
|
||||
return null // Token is not yet in refresh window
|
||||
}
|
||||
|
||||
// Create new token with same user info
|
||||
val newExpiresAt = Instant.fromEpochMilliseconds(now.toEpochMilliseconds() + expirationTimeMillis)
|
||||
val newPayload = payload.copy(
|
||||
issuedAt = now,
|
||||
expiresAt = newExpiresAt
|
||||
)
|
||||
val newToken = encodeToken(newPayload)
|
||||
|
||||
return TokenInfo(
|
||||
token = newToken,
|
||||
expiresAt = newExpiresAt,
|
||||
userId = payload.userId
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts user ID from a JWT token without full validation.
|
||||
*
|
||||
* @param token The JWT token
|
||||
* @return User ID if extractable, null otherwise
|
||||
*/
|
||||
fun extractUserId(token: String): Uuid? {
|
||||
return try {
|
||||
val payload = decodeToken(token)
|
||||
payload.userId
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JWT payload for the given user.
|
||||
*/
|
||||
private fun createPayload(user: DomUser, roles: List<RolleE>, permissions: List<BerechtigungE>, issuedAt: Instant, expiresAt: Instant): JwtPayload {
|
||||
return JwtPayload(
|
||||
userId = user.userId,
|
||||
username = user.username,
|
||||
email = user.email,
|
||||
roles = roles,
|
||||
permissions = permissions,
|
||||
issuedAt = issuedAt,
|
||||
expiresAt = expiresAt,
|
||||
issuer = issuer,
|
||||
audience = audience
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a JWT payload into a token string.
|
||||
* This is a simplified implementation - in production use proper JWT library.
|
||||
*/
|
||||
private fun encodeToken(payload: JwtPayload): String {
|
||||
// Simplified token encoding (in production, use proper JWT encoding)
|
||||
val header = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" // {"alg":"HS256","typ":"JWT"}
|
||||
val payloadJson = """
|
||||
{
|
||||
"userId": "${payload.userId}",
|
||||
"username": "${payload.username}",
|
||||
"email": "${payload.email}",
|
||||
"iat": ${payload.issuedAt.epochSeconds},
|
||||
"exp": ${payload.expiresAt.epochSeconds},
|
||||
"iss": "${payload.issuer}",
|
||||
"aud": "${payload.audience}"
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
// Base64 encode payload (simplified)
|
||||
val encodedPayload = payloadJson.encodeToByteArray().let { bytes ->
|
||||
// Simple base64-like encoding (in production use proper base64)
|
||||
bytes.joinToString("") { byte ->
|
||||
val hex = byte.toUByte().toString(16)
|
||||
if (hex.length == 1) "0$hex" else hex
|
||||
}
|
||||
}
|
||||
|
||||
// Create signature (simplified)
|
||||
val signature = (header + encodedPayload + secret).hashCode().toString()
|
||||
|
||||
return "$header.$encodedPayload.$signature"
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a JWT token into a payload.
|
||||
* This is a simplified implementation - in production use proper JWT library.
|
||||
*/
|
||||
private fun decodeToken(token: String): JwtPayload {
|
||||
val parts = token.split(".")
|
||||
if (parts.size != 3) {
|
||||
throw IllegalArgumentException("Invalid token format")
|
||||
}
|
||||
|
||||
// Simplified decoding (in production, use proper JWT decoding)
|
||||
// This is just a placeholder implementation
|
||||
throw NotImplementedError("Token decoding not implemented in simplified version")
|
||||
}
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
package at.mocode.members.domain.service
|
||||
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* Service for password hashing and verification.
|
||||
*
|
||||
* Provides secure password hashing using salt and verification methods.
|
||||
* This is a simplified implementation - in production, consider using
|
||||
* more robust hashing algorithms like bcrypt, scrypt, or Argon2.
|
||||
*/
|
||||
class PasswordService {
|
||||
|
||||
companion object {
|
||||
private const val SALT_LENGTH = 32
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random salt for password hashing.
|
||||
*
|
||||
* @return A random salt string
|
||||
*/
|
||||
fun generateSalt(): String {
|
||||
val chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
return (1..SALT_LENGTH)
|
||||
.map { chars[Random.nextInt(chars.length)] }
|
||||
.joinToString("")
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashes a password with the given salt.
|
||||
*
|
||||
* @param password The plain text password
|
||||
* @param salt The salt to use for hashing
|
||||
* @return The hashed password
|
||||
*/
|
||||
fun hashPassword(password: String, salt: String): String {
|
||||
// Simple hash implementation - in production use bcrypt, scrypt, or Argon2
|
||||
val combined = password + salt
|
||||
return combined.hashCode().toString() + salt.hashCode().toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies a password against a stored hash and salt.
|
||||
*
|
||||
* @param password The plain text password to verify
|
||||
* @param storedHash The stored password hash
|
||||
* @param salt The salt used for the stored hash
|
||||
* @return True if the password matches, false otherwise
|
||||
*/
|
||||
fun verifyPassword(password: String, storedHash: String, salt: String): Boolean {
|
||||
val hashedInput = hashPassword(password, salt)
|
||||
return hashedInput == storedHash
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates password strength.
|
||||
*
|
||||
* @param password The password to validate
|
||||
* @return True if the password meets minimum requirements
|
||||
*/
|
||||
fun isPasswordValid(password: String): Boolean {
|
||||
return password.length >= 8 &&
|
||||
password.any { it.isUpperCase() } &&
|
||||
password.any { it.isLowerCase() } &&
|
||||
password.any { it.isDigit() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets password validation error messages.
|
||||
*
|
||||
* @param password The password to validate
|
||||
* @return List of validation error messages, empty if valid
|
||||
*/
|
||||
fun getPasswordValidationErrors(password: String): List<String> {
|
||||
val errors = mutableListOf<String>()
|
||||
|
||||
if (password.length < 8) {
|
||||
errors.add("Password must be at least 8 characters long")
|
||||
}
|
||||
|
||||
if (!password.any { it.isUpperCase() }) {
|
||||
errors.add("Password must contain at least one uppercase letter")
|
||||
}
|
||||
|
||||
if (!password.any { it.isLowerCase() }) {
|
||||
errors.add("Password must contain at least one lowercase letter")
|
||||
}
|
||||
|
||||
if (!password.any { it.isDigit() }) {
|
||||
errors.add("Password must contain at least one digit")
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
package at.mocode.members.domain.service
|
||||
|
||||
import at.mocode.members.domain.model.DomUser
|
||||
import at.mocode.members.domain.repository.UserRepository
|
||||
import at.mocode.members.domain.repository.PersonRolleRepository
|
||||
import at.mocode.members.domain.repository.RolleRepository
|
||||
import at.mocode.members.domain.repository.RolleBerechtigungRepository
|
||||
import at.mocode.members.domain.repository.BerechtigungRepository
|
||||
import at.mocode.enums.RolleE
|
||||
import at.mocode.enums.BerechtigungE
|
||||
import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.todayIn
|
||||
|
||||
/**
|
||||
* Service for managing user authorization data.
|
||||
*
|
||||
* This service provides methods to fetch user roles and permissions from the database
|
||||
* and convert them to the format expected by the authorization system.
|
||||
*/
|
||||
class UserAuthorizationService(
|
||||
private val userRepository: UserRepository,
|
||||
private val personRolleRepository: PersonRolleRepository,
|
||||
private val rolleRepository: RolleRepository,
|
||||
private val rolleBerechtigungRepository: RolleBerechtigungRepository,
|
||||
private val berechtigungRepository: BerechtigungRepository
|
||||
) {
|
||||
|
||||
/**
|
||||
* Data class representing user authorization information.
|
||||
*/
|
||||
data class UserAuthInfo(
|
||||
val userId: Uuid,
|
||||
val personId: Uuid,
|
||||
val username: String,
|
||||
val email: String,
|
||||
val roles: List<RolleE>,
|
||||
val permissions: List<BerechtigungE>
|
||||
)
|
||||
|
||||
/**
|
||||
* Fetches complete authorization information for a user.
|
||||
*
|
||||
* @param userId The user ID
|
||||
* @return UserAuthInfo if user exists and is active, null otherwise
|
||||
*/
|
||||
suspend fun getUserAuthInfo(userId: Uuid): UserAuthInfo? {
|
||||
// Get user
|
||||
val user = userRepository.findById(userId) ?: return null
|
||||
|
||||
// Check if user is active
|
||||
if (!user.istAktiv) return null
|
||||
|
||||
// Check if user is locked
|
||||
val now = Clock.System.now()
|
||||
if (user.gesperrtBis != null && user.gesperrtBis!! > now) return null
|
||||
|
||||
// Get user's roles
|
||||
val roles = getUserRoles(user.personId)
|
||||
|
||||
// Get permissions for those roles
|
||||
val permissions = getPermissionsForRoles(roles)
|
||||
|
||||
return UserAuthInfo(
|
||||
userId = user.userId,
|
||||
personId = user.personId,
|
||||
username = user.username,
|
||||
email = user.email,
|
||||
roles = roles,
|
||||
permissions = permissions
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches authorization information for a user by username or email.
|
||||
*
|
||||
* @param usernameOrEmail The username or email
|
||||
* @return UserAuthInfo if user exists and is active, null otherwise
|
||||
*/
|
||||
suspend fun getUserAuthInfoByUsernameOrEmail(usernameOrEmail: String): UserAuthInfo? {
|
||||
// Try to find user by username first
|
||||
val user = userRepository.findByUsername(usernameOrEmail)
|
||||
?: userRepository.findByEmail(usernameOrEmail)
|
||||
?: return null
|
||||
|
||||
return getUserAuthInfo(user.userId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all active roles for a person.
|
||||
*
|
||||
* @param personId The person ID
|
||||
* @return List of active role types
|
||||
*/
|
||||
suspend fun getUserRoles(personId: Uuid): List<RolleE> {
|
||||
val today = Clock.System.todayIn(TimeZone.currentSystemDefault())
|
||||
|
||||
// Get active person roles
|
||||
val personRoles = personRolleRepository.findByPersonId(personId)
|
||||
.filter { personRolle ->
|
||||
personRolle.istAktiv &&
|
||||
personRolle.gueltigVon <= today &&
|
||||
(personRolle.gueltigBis == null || personRolle.gueltigBis!! >= today)
|
||||
}
|
||||
|
||||
// Get the actual roles
|
||||
val roles = mutableListOf<RolleE>()
|
||||
for (personRolle in personRoles) {
|
||||
val rolle = rolleRepository.findById(personRolle.rolleId)
|
||||
if (rolle != null && rolle.istAktiv) {
|
||||
roles.add(rolle.rolleTyp)
|
||||
}
|
||||
}
|
||||
|
||||
return roles.distinct()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all permissions for the given roles.
|
||||
*
|
||||
* @param roles List of role types
|
||||
* @return List of permission types
|
||||
*/
|
||||
suspend fun getPermissionsForRoles(roles: List<RolleE>): List<BerechtigungE> {
|
||||
val permissions = mutableSetOf<BerechtigungE>()
|
||||
|
||||
for (roleType in roles) {
|
||||
// Find the role by type
|
||||
val rolle = rolleRepository.findByTyp(roleType)
|
||||
if (rolle != null && rolle.rolleId != null) {
|
||||
// Get role permissions
|
||||
val rolleBerechtigungen = rolleBerechtigungRepository.findByRolleId(rolle.rolleId)
|
||||
.filter { it.istAktiv }
|
||||
|
||||
// Get the actual permissions
|
||||
for (rolleBerechtigung in rolleBerechtigungen) {
|
||||
val berechtigung = berechtigungRepository.findById(rolleBerechtigung.berechtigungId)
|
||||
if (berechtigung != null && berechtigung.istAktiv) {
|
||||
permissions.add(berechtigung.berechtigungTyp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return permissions.toList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a user has a specific role.
|
||||
*
|
||||
* @param userId The user ID
|
||||
* @param role The role to check
|
||||
* @return true if user has the role, false otherwise
|
||||
*/
|
||||
suspend fun hasRole(userId: Uuid, role: RolleE): Boolean {
|
||||
val authInfo = getUserAuthInfo(userId) ?: return false
|
||||
return authInfo.roles.contains(role)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a user has a specific permission.
|
||||
*
|
||||
* @param userId The user ID
|
||||
* @param permission The permission to check
|
||||
* @return true if user has the permission, false otherwise
|
||||
*/
|
||||
suspend fun hasPermission(userId: Uuid, permission: BerechtigungE): Boolean {
|
||||
val authInfo = getUserAuthInfo(userId) ?: return false
|
||||
return authInfo.permissions.contains(permission)
|
||||
}
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
import at.mocode.enums.BerechtigungE
|
||||
import at.mocode.members.domain.model.DomBerechtigung
|
||||
import at.mocode.members.domain.repository.BerechtigungRepository
|
||||
import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.toKotlinInstant
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.like
|
||||
|
||||
/**
|
||||
* Exposed-based implementation of BerechtigungRepository.
|
||||
*
|
||||
* This implementation provides data persistence for Berechtigung entities
|
||||
* using the Exposed SQL framework and PostgreSQL database.
|
||||
*/
|
||||
class BerechtigungRepositoryImpl : BerechtigungRepository {
|
||||
|
||||
override suspend fun save(berechtigung: DomBerechtigung): DomBerechtigung {
|
||||
val now = Clock.System.now()
|
||||
val updatedBerechtigung = berechtigung.copy(updatedAt = now)
|
||||
|
||||
BerechtigungTable.insertOrUpdate(BerechtigungTable.id) {
|
||||
it[id] = berechtigung.berechtigungId
|
||||
it[berechtigungTyp] = berechtigung.berechtigungTyp
|
||||
it[name] = berechtigung.name
|
||||
it[beschreibung] = berechtigung.beschreibung
|
||||
it[ressource] = berechtigung.ressource
|
||||
it[aktion] = berechtigung.aktion
|
||||
it[istAktiv] = berechtigung.istAktiv
|
||||
it[istSystemBerechtigung] = berechtigung.istSystemBerechtigung
|
||||
it[createdAt] = berechtigung.createdAt.toJavaInstant()
|
||||
it[updatedAt] = updatedBerechtigung.updatedAt.toJavaInstant()
|
||||
}
|
||||
|
||||
return updatedBerechtigung
|
||||
}
|
||||
|
||||
override suspend fun findById(berechtigungId: Uuid): DomBerechtigung? {
|
||||
return BerechtigungTable.select { BerechtigungTable.id eq berechtigungId }
|
||||
.map { rowToDomBerechtigung(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByTyp(berechtigungTyp: BerechtigungE): DomBerechtigung? {
|
||||
return BerechtigungTable.select { BerechtigungTable.berechtigungTyp eq berechtigungTyp }
|
||||
.map { rowToDomBerechtigung(it) }
|
||||
.singleOrNull()
|
||||
}
|
||||
|
||||
override suspend fun findByName(name: String): List<DomBerechtigung> {
|
||||
val searchPattern = "%$name%"
|
||||
return BerechtigungTable.select { BerechtigungTable.name like searchPattern }
|
||||
.map { rowToDomBerechtigung(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByRessource(ressource: String): List<DomBerechtigung> {
|
||||
return BerechtigungTable.select { BerechtigungTable.ressource eq ressource }
|
||||
.map { rowToDomBerechtigung(it) }
|
||||
}
|
||||
|
||||
override suspend fun findByAktion(aktion: String): List<DomBerechtigung> {
|
||||
return BerechtigungTable.select { BerechtigungTable.aktion eq aktion }
|
||||
.map { rowToDomBerechtigung(it) }
|
||||
}
|
||||
|
||||
override suspend fun findAllActive(): List<DomBerechtigung> {
|
||||
return BerechtigungTable.select { BerechtigungTable.istAktiv eq true }
|
||||
.map { rowToDomBerechtigung(it) }
|
||||
}
|
||||
|
||||
override suspend fun findAll(): List<DomBerechtigung> {
|
||||
return BerechtigungTable.selectAll()
|
||||
.map { rowToDomBerechtigung(it) }
|
||||
}
|
||||
|
||||
override suspend fun deactivateBerechtigung(berechtigungId: Uuid): Boolean {
|
||||
val now = Clock.System.now()
|
||||
val updatedRows = BerechtigungTable.update({ BerechtigungTable.id eq berechtigungId }) {
|
||||
it[istAktiv] = false
|
||||
it[updatedAt] = now.toJavaInstant()
|
||||
}
|
||||
return updatedRows > 0
|
||||
}
|
||||
|
||||
override suspend fun deleteBerechtigung(berechtigungId: Uuid): Boolean {
|
||||
// Only allow deletion of non-system permissions
|
||||
val berechtigung = findById(berechtigungId)
|
||||
if (berechtigung?.istSystemBerechtigung == true) {
|
||||
return false
|
||||
}
|
||||
|
||||
val deletedRows = BerechtigungTable.deleteWhere { BerechtigungTable.id eq berechtigungId }
|
||||
return deletedRows > 0
|
||||
}
|
||||
|
||||
override suspend fun existsByTyp(berechtigungTyp: BerechtigungE): Boolean {
|
||||
return BerechtigungTable.select { BerechtigungTable.berechtigungTyp eq berechtigungTyp }
|
||||
.count() > 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a database row to a DomBerechtigung domain object.
|
||||
*/
|
||||
private fun rowToDomBerechtigung(row: ResultRow): DomBerechtigung {
|
||||
return DomBerechtigung(
|
||||
berechtigungId = row[BerechtigungTable.id].value,
|
||||
berechtigungTyp = row[BerechtigungTable.berechtigungTyp],
|
||||
name = row[BerechtigungTable.name],
|
||||
beschreibung = row[BerechtigungTable.beschreibung],
|
||||
ressource = row[BerechtigungTable.ressource],
|
||||
aktion = row[BerechtigungTable.aktion],
|
||||
istAktiv = row[BerechtigungTable.istAktiv],
|
||||
istSystemBerechtigung = row[BerechtigungTable.istSystemBerechtigung],
|
||||
createdAt = row[BerechtigungTable.createdAt].toKotlinInstant(),
|
||||
updatedAt = row[BerechtigungTable.updatedAt].toKotlinInstant()
|
||||
)
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
import at.mocode.enums.BerechtigungE
|
||||
import org.jetbrains.exposed.dao.id.UUIDTable
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.datetime
|
||||
|
||||
/**
|
||||
* Database table definition for permissions (Berechtigungen).
|
||||
*/
|
||||
object BerechtigungTable : UUIDTable("berechtigung") {
|
||||
val berechtigungTyp = enumerationByName("berechtigung_typ", 50, BerechtigungE::class)
|
||||
val name = varchar("name", 100)
|
||||
val beschreibung = text("beschreibung").nullable()
|
||||
val ressource = varchar("ressource", 50)
|
||||
val aktion = varchar("aktion", 50)
|
||||
val istAktiv = bool("ist_aktiv").default(true)
|
||||
val istSystemBerechtigung = bool("ist_system_berechtigung").default(false)
|
||||
val createdAt = datetime("created_at")
|
||||
val updatedAt = datetime("updated_at")
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
import at.mocode.members.domain.model.DomPersonRolle
|
||||
import at.mocode.members.domain.repository.PersonRolleRepository
|
||||
import com.benasher44.uuid.Uuid
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.todayIn
|
||||
|
||||
/**
|
||||
* In-memory implementation of PersonRolleRepository for testing and development.
|
||||
*
|
||||
* This implementation provides basic functionality without database persistence.
|
||||
* Replace with proper database implementation for production use.
|
||||
*/
|
||||
class PersonRolleRepositoryImpl : PersonRolleRepository {
|
||||
|
||||
private val personRoles = mutableMapOf<Uuid, DomPersonRolle>()
|
||||
|
||||
override suspend fun save(personRolle: DomPersonRolle): DomPersonRolle {
|
||||
val now = Clock.System.now()
|
||||
val updatedPersonRolle = personRolle.copy(updatedAt = now)
|
||||
personRoles[updatedPersonRolle.personRolleId] = updatedPersonRolle
|
||||
return updatedPersonRolle
|
||||
}
|
||||
|
||||
override suspend fun findById(personRolleId: Uuid): DomPersonRolle? {
|
||||
return personRoles[personRolleId]
|
||||
}
|
||||
|
||||
override suspend fun findByPersonId(personId: Uuid, nurAktive: Boolean): List<DomPersonRolle> {
|
||||
return personRoles.values.filter { personRolle ->
|
||||
personRolle.personId == personId && (!nurAktive || personRolle.istAktiv)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findByRolleId(rolleId: Uuid, nurAktive: Boolean): List<DomPersonRolle> {
|
||||
return personRoles.values.filter { personRolle ->
|
||||
personRolle.rolleId == rolleId && (!nurAktive || personRolle.istAktiv)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findByVereinId(vereinId: Uuid, nurAktive: Boolean): List<DomPersonRolle> {
|
||||
return personRoles.values.filter { personRolle ->
|
||||
personRolle.vereinId == vereinId && (!nurAktive || personRolle.istAktiv)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findByPersonAndRolle(personId: Uuid, rolleId: Uuid, vereinId: Uuid?): DomPersonRolle? {
|
||||
return personRoles.values.find { personRolle ->
|
||||
personRolle.personId == personId &&
|
||||
personRolle.rolleId == rolleId &&
|
||||
(vereinId == null || personRolle.vereinId == vereinId)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findValidAt(stichtag: LocalDate, nurAktive: Boolean): List<DomPersonRolle> {
|
||||
return personRoles.values.filter { personRolle ->
|
||||
val isValid = personRolle.gueltigVon <= stichtag &&
|
||||
(personRolle.gueltigBis == null || personRolle.gueltigBis!! >= stichtag)
|
||||
isValid && (!nurAktive || personRolle.istAktiv)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findByPersonValidAt(personId: Uuid, stichtag: LocalDate, nurAktive: Boolean): List<DomPersonRolle> {
|
||||
return personRoles.values.filter { personRolle ->
|
||||
val isValid = personRolle.personId == personId &&
|
||||
personRolle.gueltigVon <= stichtag &&
|
||||
(personRolle.gueltigBis == null || personRolle.gueltigBis!! >= stichtag)
|
||||
isValid && (!nurAktive || personRolle.istAktiv)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deactivatePersonRolle(personRolleId: Uuid): Boolean {
|
||||
val personRolle = personRoles[personRolleId] ?: return false
|
||||
personRoles[personRolleId] = personRolle.copy(istAktiv = false, updatedAt = Clock.System.now())
|
||||
return true
|
||||
}
|
||||
|
||||
override suspend fun deletePersonRolle(personRolleId: Uuid): Boolean {
|
||||
return personRoles.remove(personRolleId) != null
|
||||
}
|
||||
|
||||
override suspend fun hasPersonRolle(personId: Uuid, rolleId: Uuid, vereinId: Uuid?, stichtag: LocalDate?): Boolean {
|
||||
val checkDate = stichtag ?: Clock.System.todayIn(TimeZone.currentSystemDefault())
|
||||
|
||||
return personRoles.values.any { personRolle ->
|
||||
personRolle.personId == personId &&
|
||||
personRolle.rolleId == rolleId &&
|
||||
(vereinId == null || personRolle.vereinId == vereinId) &&
|
||||
personRolle.istAktiv &&
|
||||
personRolle.gueltigVon <= checkDate &&
|
||||
(personRolle.gueltigBis == null || personRolle.gueltigBis!! >= checkDate)
|
||||
}
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
import at.mocode.members.domain.model.DomRolleBerechtigung
|
||||
import at.mocode.members.domain.repository.RolleBerechtigungRepository
|
||||
import com.benasher44.uuid.Uuid
|
||||
import com.benasher44.uuid.uuid4
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
/**
|
||||
* In-memory implementation of RolleBerechtigungRepository for testing and development.
|
||||
*
|
||||
* This implementation provides basic functionality without database persistence.
|
||||
* Replace with proper database implementation for production use.
|
||||
*/
|
||||
class RolleBerechtigungRepositoryImpl : RolleBerechtigungRepository {
|
||||
|
||||
private val rolePermissions = mutableMapOf<Uuid, DomRolleBerechtigung>()
|
||||
|
||||
override suspend fun save(rolleBerechtigung: DomRolleBerechtigung): DomRolleBerechtigung {
|
||||
val now = Clock.System.now()
|
||||
val updatedRolleBerechtigung = rolleBerechtigung.copy(updatedAt = now)
|
||||
rolePermissions[updatedRolleBerechtigung.rolleBerechtigungId] = updatedRolleBerechtigung
|
||||
return updatedRolleBerechtigung
|
||||
}
|
||||
|
||||
override suspend fun findById(rolleBerechtigungId: Uuid): DomRolleBerechtigung? {
|
||||
return rolePermissions[rolleBerechtigungId]
|
||||
}
|
||||
|
||||
override suspend fun findByRolleId(rolleId: Uuid, nurAktive: Boolean): List<DomRolleBerechtigung> {
|
||||
return rolePermissions.values.filter { rolleBerechtigung ->
|
||||
rolleBerechtigung.rolleId == rolleId && (!nurAktive || rolleBerechtigung.istAktiv)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findByBerechtigungId(berechtigungId: Uuid, nurAktive: Boolean): List<DomRolleBerechtigung> {
|
||||
return rolePermissions.values.filter { rolleBerechtigung ->
|
||||
rolleBerechtigung.berechtigungId == berechtigungId && (!nurAktive || rolleBerechtigung.istAktiv)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findByRolleAndBerechtigung(rolleId: Uuid, berechtigungId: Uuid): DomRolleBerechtigung? {
|
||||
return rolePermissions.values.find { rolleBerechtigung ->
|
||||
rolleBerechtigung.rolleId == rolleId && rolleBerechtigung.berechtigungId == berechtigungId
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findAllActive(): List<DomRolleBerechtigung> {
|
||||
return rolePermissions.values.filter { it.istAktiv }
|
||||
}
|
||||
|
||||
override suspend fun findAll(): List<DomRolleBerechtigung> {
|
||||
return rolePermissions.values.toList()
|
||||
}
|
||||
|
||||
override suspend fun deactivateRolleBerechtigung(rolleBerechtigungId: Uuid): Boolean {
|
||||
val rolleBerechtigung = rolePermissions[rolleBerechtigungId] ?: return false
|
||||
rolePermissions[rolleBerechtigungId] = rolleBerechtigung.copy(istAktiv = false, updatedAt = Clock.System.now())
|
||||
return true
|
||||
}
|
||||
|
||||
override suspend fun deleteRolleBerechtigung(rolleBerechtigungId: Uuid): Boolean {
|
||||
return rolePermissions.remove(rolleBerechtigungId) != null
|
||||
}
|
||||
|
||||
override suspend fun hasRolleBerechtigung(rolleId: Uuid, berechtigungId: Uuid): Boolean {
|
||||
return rolePermissions.values.any { rolleBerechtigung ->
|
||||
rolleBerechtigung.rolleId == rolleId &&
|
||||
rolleBerechtigung.berechtigungId == berechtigungId &&
|
||||
rolleBerechtigung.istAktiv
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun assignBerechtigungToRolle(rolleId: Uuid, berechtigungId: Uuid, zugewiesenVon: Uuid?): DomRolleBerechtigung {
|
||||
// Check if assignment already exists
|
||||
val existing = findByRolleAndBerechtigung(rolleId, berechtigungId)
|
||||
if (existing != null) {
|
||||
// If it exists but is inactive, reactivate it
|
||||
if (!existing.istAktiv) {
|
||||
val reactivated = existing.copy(istAktiv = true, updatedAt = Clock.System.now())
|
||||
return save(reactivated)
|
||||
}
|
||||
return existing
|
||||
}
|
||||
|
||||
// Create new assignment
|
||||
val newAssignment = DomRolleBerechtigung(
|
||||
rolleId = rolleId,
|
||||
berechtigungId = berechtigungId,
|
||||
zugewiesenVon = zugewiesenVon
|
||||
)
|
||||
return save(newAssignment)
|
||||
}
|
||||
|
||||
override suspend fun revokeBerechtigungFromRolle(rolleId: Uuid, berechtigungId: Uuid): Boolean {
|
||||
val rolleBerechtigung = findByRolleAndBerechtigung(rolleId, berechtigungId) ?: return false
|
||||
return deactivateRolleBerechtigung(rolleBerechtigung.rolleBerechtigungId)
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
import at.mocode.members.domain.model.DomRolle
|
||||
import at.mocode.members.domain.repository.RolleRepository
|
||||
import at.mocode.enums.RolleE
|
||||
import com.benasher44.uuid.Uuid
|
||||
import com.benasher44.uuid.uuid4
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
/**
|
||||
* In-memory implementation of RolleRepository for testing and development.
|
||||
*
|
||||
* This implementation provides basic functionality without database persistence.
|
||||
* Replace with proper database implementation for production use.
|
||||
*/
|
||||
class RolleRepositoryImpl : RolleRepository {
|
||||
|
||||
private val roles = mutableMapOf<Uuid, DomRolle>()
|
||||
|
||||
init {
|
||||
// Initialize with default roles
|
||||
val defaultRoles = listOf(
|
||||
DomRolle(
|
||||
rolleId = uuid4(),
|
||||
rolleTyp = RolleE.ADMIN,
|
||||
name = "Administrator",
|
||||
beschreibung = "System administrator with full access",
|
||||
istAktiv = true,
|
||||
istSystemRolle = true
|
||||
),
|
||||
DomRolle(
|
||||
rolleId = uuid4(),
|
||||
rolleTyp = RolleE.VEREINS_ADMIN,
|
||||
name = "Vereins Administrator",
|
||||
beschreibung = "Club administrator",
|
||||
istAktiv = true,
|
||||
istSystemRolle = true
|
||||
),
|
||||
DomRolle(
|
||||
rolleId = uuid4(),
|
||||
rolleTyp = RolleE.REITER,
|
||||
name = "Reiter",
|
||||
beschreibung = "Rider",
|
||||
istAktiv = true,
|
||||
istSystemRolle = true
|
||||
)
|
||||
)
|
||||
|
||||
defaultRoles.forEach { role ->
|
||||
roles[role.rolleId!!] = role
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun save(rolle: DomRolle): DomRolle {
|
||||
val now = Clock.System.now()
|
||||
val updatedRolle = rolle.copy(updatedAt = now)
|
||||
roles[updatedRolle.rolleId!!] = updatedRolle
|
||||
return updatedRolle
|
||||
}
|
||||
|
||||
override suspend fun findById(rolleId: Uuid): DomRolle? {
|
||||
return roles[rolleId]
|
||||
}
|
||||
|
||||
override suspend fun findByTyp(rolleTyp: RolleE): DomRolle? {
|
||||
return roles.values.find { it.rolleTyp == rolleTyp }
|
||||
}
|
||||
|
||||
override suspend fun findByName(name: String): List<DomRolle> {
|
||||
return roles.values.filter { it.name.contains(name, ignoreCase = true) }
|
||||
}
|
||||
|
||||
override suspend fun findAllActive(): List<DomRolle> {
|
||||
return roles.values.filter { it.istAktiv }
|
||||
}
|
||||
|
||||
override suspend fun findAll(): List<DomRolle> {
|
||||
return roles.values.toList()
|
||||
}
|
||||
|
||||
override suspend fun deactivateRolle(rolleId: Uuid): Boolean {
|
||||
val rolle = roles[rolleId] ?: return false
|
||||
roles[rolleId] = rolle.copy(istAktiv = false, updatedAt = Clock.System.now())
|
||||
return true
|
||||
}
|
||||
|
||||
override suspend fun deleteRolle(rolleId: Uuid): Boolean {
|
||||
val rolle = roles[rolleId] ?: return false
|
||||
// Don't allow deletion of system roles
|
||||
if (rolle.istSystemRolle) return false
|
||||
roles.remove(rolleId)
|
||||
return true
|
||||
}
|
||||
|
||||
override suspend fun existsByTyp(rolleTyp: RolleE): Boolean {
|
||||
return roles.values.any { it.rolleTyp == rolleTyp }
|
||||
}
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
import at.mocode.enums.RolleE
|
||||
import org.jetbrains.exposed.dao.id.UUIDTable
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.datetime
|
||||
|
||||
/**
|
||||
* Exposed table definition for Rolle entities.
|
||||
*
|
||||
* This table represents the database schema for storing role data
|
||||
* in the member management bounded context.
|
||||
*/
|
||||
object RolleTable : UUIDTable("rollen") {
|
||||
|
||||
// Role identification
|
||||
val rolleTyp = enumerationByName("rolle_typ", 20, RolleE::class)
|
||||
val name = varchar("name", 100)
|
||||
val beschreibung = text("beschreibung").nullable()
|
||||
|
||||
// Status flags
|
||||
val istAktiv = bool("ist_aktiv").default(true)
|
||||
val istSystemRolle = bool("ist_system_rolle").default(false)
|
||||
|
||||
// Audit fields
|
||||
val createdAt = datetime("created_at")
|
||||
val updatedAt = datetime("updated_at")
|
||||
|
||||
// Unique constraint on rolle_typ to ensure each role type exists only once
|
||||
init {
|
||||
uniqueIndex(rolleTyp)
|
||||
}
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
import at.mocode.members.domain.model.DomUser
|
||||
import at.mocode.members.domain.repository.UserRepository
|
||||
import com.benasher44.uuid.Uuid
|
||||
import com.benasher44.uuid.uuid4
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
|
||||
/**
|
||||
* In-memory implementation of UserRepository for testing and development.
|
||||
*
|
||||
* This implementation provides basic functionality without database persistence.
|
||||
* Replace with proper database implementation for production use.
|
||||
*/
|
||||
class UserRepositoryImpl : UserRepository {
|
||||
|
||||
private val users = mutableMapOf<Uuid, DomUser>()
|
||||
|
||||
init {
|
||||
// Initialize with a test user
|
||||
val testUser = DomUser(
|
||||
userId = uuid4(),
|
||||
personId = uuid4(),
|
||||
username = "testuser",
|
||||
email = "test@example.com",
|
||||
passwordHash = "hashed_password",
|
||||
salt = "salt123",
|
||||
istAktiv = true,
|
||||
istEmailVerifiziert = true,
|
||||
letzteAnmeldung = null,
|
||||
fehlgeschlageneAnmeldungen = 0,
|
||||
gesperrtBis = null
|
||||
)
|
||||
users[testUser.userId] = testUser
|
||||
}
|
||||
|
||||
override suspend fun createUser(user: DomUser): DomUser {
|
||||
val now = Clock.System.now()
|
||||
val updatedUser = user.copy(createdAt = now, updatedAt = now)
|
||||
users[updatedUser.userId] = updatedUser
|
||||
return updatedUser
|
||||
}
|
||||
|
||||
override suspend fun findById(userId: Uuid): DomUser? {
|
||||
return users[userId]
|
||||
}
|
||||
|
||||
override suspend fun findByUsername(username: String): DomUser? {
|
||||
return users.values.find { it.username == username }
|
||||
}
|
||||
|
||||
override suspend fun findByEmail(email: String): DomUser? {
|
||||
return users.values.find { it.email == email }
|
||||
}
|
||||
|
||||
override suspend fun findByPersonId(personId: Uuid): DomUser? {
|
||||
return users.values.find { it.personId == personId }
|
||||
}
|
||||
|
||||
override suspend fun updateUser(user: DomUser): DomUser {
|
||||
val now = Clock.System.now()
|
||||
val updatedUser = user.copy(updatedAt = now)
|
||||
users[updatedUser.userId] = updatedUser
|
||||
return updatedUser
|
||||
}
|
||||
|
||||
override suspend fun updateLastLogin(userId: Uuid) {
|
||||
val user = users[userId] ?: return
|
||||
val now = Clock.System.now()
|
||||
users[userId] = user.copy(letzteAnmeldung = now, updatedAt = now)
|
||||
}
|
||||
|
||||
override suspend fun incrementFailedLoginAttempts(userId: Uuid) {
|
||||
val user = users[userId] ?: return
|
||||
val now = Clock.System.now()
|
||||
users[userId] = user.copy(
|
||||
fehlgeschlageneAnmeldungen = user.fehlgeschlageneAnmeldungen + 1,
|
||||
updatedAt = now
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun resetFailedLoginAttempts(userId: Uuid) {
|
||||
val user = users[userId] ?: return
|
||||
val now = Clock.System.now()
|
||||
users[userId] = user.copy(fehlgeschlageneAnmeldungen = 0, updatedAt = now)
|
||||
}
|
||||
|
||||
override suspend fun lockUser(userId: Uuid, lockedUntil: Instant) {
|
||||
val user = users[userId] ?: return
|
||||
val now = Clock.System.now()
|
||||
users[userId] = user.copy(gesperrtBis = lockedUntil, updatedAt = now)
|
||||
}
|
||||
|
||||
override suspend fun unlockUser(userId: Uuid) {
|
||||
val user = users[userId] ?: return
|
||||
val now = Clock.System.now()
|
||||
users[userId] = user.copy(gesperrtBis = null, updatedAt = now)
|
||||
}
|
||||
|
||||
override suspend fun setUserActive(userId: Uuid, isActive: Boolean) {
|
||||
val user = users[userId] ?: return
|
||||
val now = Clock.System.now()
|
||||
users[userId] = user.copy(istAktiv = isActive, updatedAt = now)
|
||||
}
|
||||
|
||||
override suspend fun markEmailAsVerified(userId: Uuid) {
|
||||
val user = users[userId] ?: return
|
||||
val now = Clock.System.now()
|
||||
users[userId] = user.copy(istEmailVerifiziert = true, updatedAt = now)
|
||||
}
|
||||
|
||||
override suspend fun updatePassword(userId: Uuid, passwordHash: String, salt: String) {
|
||||
val user = users[userId] ?: return
|
||||
val now = Clock.System.now()
|
||||
users[userId] = user.copy(passwordHash = passwordHash, salt = salt, updatedAt = now)
|
||||
}
|
||||
|
||||
override suspend fun deleteUser(userId: Uuid): Boolean {
|
||||
return users.remove(userId) != null
|
||||
}
|
||||
|
||||
override suspend fun getAllUsers(): List<DomUser> {
|
||||
return users.values.toList()
|
||||
}
|
||||
|
||||
override suspend fun getActiveUsers(): List<DomUser> {
|
||||
return users.values.filter { it.istAktiv }
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
import at.mocode.enums.BerechtigungE
|
||||
import org.jetbrains.exposed.dao.id.UUIDTable
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.datetime
|
||||
|
||||
/**
|
||||
* Database table definition for permissions (Berechtigungen).
|
||||
*/
|
||||
object BerechtigungTable : UUIDTable("berechtigung") {
|
||||
val berechtigungTyp = enumerationByName("berechtigung_typ", 50, BerechtigungE::class)
|
||||
val name = varchar("name", 100)
|
||||
val beschreibung = text("beschreibung").nullable()
|
||||
val ressource = varchar("ressource", 50)
|
||||
val aktion = varchar("aktion", 50)
|
||||
val istAktiv = bool("ist_aktiv").default(true)
|
||||
val istSystemBerechtigung = bool("ist_system_berechtigung").default(false)
|
||||
val createdAt = datetime("created_at")
|
||||
val updatedAt = datetime("updated_at")
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
import org.jetbrains.exposed.dao.id.UUIDTable
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.datetime
|
||||
|
||||
/**
|
||||
* Database table definition for person-role assignments (PersonRolle).
|
||||
* This is a many-to-many relationship table between persons and roles.
|
||||
*/
|
||||
object PersonRolleTable : UUIDTable("person_rolle") {
|
||||
val personId = uuid("person_id").references(PersonTable.id)
|
||||
val rolleId = uuid("rolle_id").references(RolleTable.id)
|
||||
val istAktiv = bool("ist_aktiv").default(true)
|
||||
val gueltigVon = datetime("gueltig_von").nullable()
|
||||
val gueltigBis = datetime("gueltig_bis").nullable()
|
||||
val zugewiesenVon = uuid("zugewiesen_von").nullable() // Person who assigned this role
|
||||
val notizen = text("notizen").nullable()
|
||||
val createdAt = datetime("created_at")
|
||||
val updatedAt = datetime("updated_at")
|
||||
|
||||
// Unique constraint to prevent duplicate assignments
|
||||
init {
|
||||
uniqueIndex(personId, rolleId)
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
import at.mocode.enums.DatenQuelleE
|
||||
import at.mocode.enums.GeschlechtE
|
||||
import org.jetbrains.exposed.dao.id.UUIDTable
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.datetime
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.date
|
||||
|
||||
/**
|
||||
* Exposed table definition for Person entities.
|
||||
*
|
||||
* This table represents the database schema for storing person data
|
||||
* in the member management bounded context.
|
||||
*/
|
||||
object PersonTable : UUIDTable("persons") {
|
||||
|
||||
// Basic person information
|
||||
val oepsSatzNr = varchar("oeps_satz_nr", 6).nullable().uniqueIndex()
|
||||
val nachname = varchar("nachname", 100)
|
||||
val vorname = varchar("vorname", 100)
|
||||
val titel = varchar("titel", 50).nullable()
|
||||
|
||||
// Personal details
|
||||
val geburtsdatum = date("geburtsdatum").nullable()
|
||||
val geschlecht = enumerationByName("geschlecht", 10, GeschlechtE::class).nullable()
|
||||
val nationalitaetLandId = uuid("nationalitaet_land_id").nullable()
|
||||
val feiId = varchar("fei_id", 20).nullable()
|
||||
|
||||
// Contact information
|
||||
val telefon = varchar("telefon", 50).nullable()
|
||||
val email = varchar("email", 100).nullable()
|
||||
|
||||
// Address information
|
||||
val strasse = varchar("strasse", 200).nullable()
|
||||
val plz = varchar("plz", 10).nullable()
|
||||
val ort = varchar("ort", 100).nullable()
|
||||
val adresszusatzZusatzinfo = varchar("adresszusatz_zusatzinfo", 200).nullable()
|
||||
|
||||
// Club membership
|
||||
val stammVereinId = uuid("stamm_verein_id").nullable()
|
||||
val mitgliedsNummerBeiStammVerein = varchar("mitglieds_nummer_bei_stamm_verein", 50).nullable()
|
||||
|
||||
// Status and restrictions
|
||||
val istGesperrt = bool("ist_gesperrt").default(false)
|
||||
val sperrGrund = varchar("sperr_grund", 500).nullable()
|
||||
|
||||
// OEPS specific data
|
||||
val altersklasseOepsCodeRaw = varchar("altersklasse_oeps_code_raw", 10).nullable()
|
||||
val istJungerReiterOepsFlag = bool("ist_junger_reiter_oeps_flag").default(false)
|
||||
val kaderStatusOepsRaw = varchar("kader_status_oeps_raw", 10).nullable()
|
||||
|
||||
// Metadata
|
||||
val datenQuelle = enumerationByName("daten_quelle", 20, DatenQuelleE::class).default(DatenQuelleE.MANUELL)
|
||||
val istAktiv = bool("ist_aktiv").default(true)
|
||||
val notizenIntern = text("notizen_intern").nullable()
|
||||
|
||||
// Audit fields
|
||||
val createdAt = datetime("created_at")
|
||||
val updatedAt = datetime("updated_at")
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
import org.jetbrains.exposed.dao.id.UUIDTable
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.datetime
|
||||
|
||||
/**
|
||||
* Database table definition for role-permission assignments (RolleBerechtigung).
|
||||
* This is a many-to-many relationship table between roles and permissions.
|
||||
*/
|
||||
object RolleBerechtigungTable : UUIDTable("rolle_berechtigung") {
|
||||
val rolleId = uuid("rolle_id").references(RolleTable.id)
|
||||
val berechtigungId = uuid("berechtigung_id").references(BerechtigungTable.id)
|
||||
val istAktiv = bool("ist_aktiv").default(true)
|
||||
val gueltigVon = datetime("gueltig_von").nullable()
|
||||
val gueltigBis = datetime("gueltig_bis").nullable()
|
||||
val zugewiesenVon = uuid("zugewiesen_von").nullable() // Person who assigned this permission
|
||||
val notizen = text("notizen").nullable()
|
||||
val createdAt = datetime("created_at")
|
||||
val updatedAt = datetime("updated_at")
|
||||
|
||||
// Unique constraint to prevent duplicate assignments
|
||||
init {
|
||||
uniqueIndex(rolleId, berechtigungId)
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
import at.mocode.enums.RolleE
|
||||
import org.jetbrains.exposed.dao.id.UUIDTable
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.datetime
|
||||
|
||||
/**
|
||||
* Exposed table definition for Rolle entities.
|
||||
*
|
||||
* This table represents the database schema for storing role data
|
||||
* in the member management bounded context.
|
||||
*/
|
||||
object RolleTable : UUIDTable("rollen") {
|
||||
|
||||
// Role identification
|
||||
val rolleTyp = enumerationByName("rolle_typ", 20, RolleE::class)
|
||||
val name = varchar("name", 100)
|
||||
val beschreibung = text("beschreibung").nullable()
|
||||
|
||||
// Status flags
|
||||
val istAktiv = bool("ist_aktiv").default(true)
|
||||
val istSystemRolle = bool("ist_system_rolle").default(false)
|
||||
|
||||
// Audit fields
|
||||
val createdAt = datetime("created_at")
|
||||
val updatedAt = datetime("updated_at")
|
||||
|
||||
// Unique constraint on rolle_typ to ensure each role type exists only once
|
||||
init {
|
||||
uniqueIndex(rolleTyp)
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package at.mocode.members.infrastructure.repository
|
||||
|
||||
import org.jetbrains.exposed.dao.id.UUIDTable
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.datetime
|
||||
|
||||
/**
|
||||
* Database table definition for users (authentication).
|
||||
*
|
||||
* This table stores user authentication data and is linked to the Person table.
|
||||
* It follows the Exposed framework conventions for UUID-based tables.
|
||||
*/
|
||||
object UserTable : UUIDTable("users") {
|
||||
|
||||
// Foreign key to the Person table
|
||||
val personId = uuid("person_id").references(PersonTable.id)
|
||||
|
||||
// Authentication fields
|
||||
val username = varchar("username", 100).uniqueIndex()
|
||||
val email = varchar("email", 255).uniqueIndex()
|
||||
val passwordHash = varchar("password_hash", 255)
|
||||
val salt = varchar("salt", 255)
|
||||
|
||||
// Status flags
|
||||
val istAktiv = bool("ist_aktiv").default(true)
|
||||
val istEmailVerifiziert = bool("ist_email_verifiziert").default(false)
|
||||
|
||||
// Login tracking
|
||||
val letzteAnmeldung = datetime("letzte_anmeldung").nullable()
|
||||
val fehlgeschlageneAnmeldungen = integer("fehlgeschlagene_anmeldungen").default(0)
|
||||
val gesperrtBis = datetime("gesperrt_bis").nullable()
|
||||
val passwortAendernErforderlich = bool("passwort_aendern_erforderlich").default(false)
|
||||
|
||||
// Audit fields
|
||||
val createdAt = datetime("created_at")
|
||||
val updatedAt = datetime("updated_at")
|
||||
}
|
||||
Reference in New Issue
Block a user