feat/adding-h5p-tasks #10

Merged
DrunkenPinguin merged 6 commits from feat/adding-h5p-tasks into main 2026-09-15 13:00:22 +00:00
28 changed files with 74 additions and 77 deletions
Showing only changes of commit 0696ce409f - Show all commits

Binary file not shown.

View file

@ -1,2 +0,0 @@
#Wed Sep 02 15:03:29 CEST 2026
gradle.version=9.5.1

Binary file not shown.

10
.idea/.gitignore vendored
View file

@ -1,10 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" />
</component>
</project>

View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="kidio_db@82.165.11.162" uuid="c8ad70de-d5fc-407a-9165-4f24328fa311">
<driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
<jdbc-url>jdbc:postgresql://82.165.11.162:5432/kidio_db</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

View file

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="externalSystemId" value="Gradle" />
<option name="version" value="2.4.0" />
</component>
</project>

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="homebrew-26" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View file

@ -14,6 +14,26 @@ import io.ktor.server.routing.*
import org.mindrot.jbcrypt.BCrypt import org.mindrot.jbcrypt.BCrypt
import java.util.UUID import java.util.UUID
fun isValidPassword(password: String): Boolean {
if (password.length < 8) return false
val hasUppercase = password.any { it.isUpperCase() }
val hasEmoji =
password.any { char ->
val type = Character.getType(char).toByte()
type == Character.SURROGATE ||
type == Character.OTHER_SYMBOL ||
char.code in 0x1F600..0x1F64F ||
char.code in 0x1F300..0x1F5FF ||
char.code in 0x1F680..0x1F6FF ||
char.code in 0x2600..0x26FF
}
val hasSpecialChar = password.any { !it.isLetterOrDigit() && !hasEmoji }
return hasUppercase && hasSpecialChar && !hasEmoji
}
fun Application.configureRouting( fun Application.configureRouting(
repository: KidioRepository, repository: KidioRepository,
jwtService: JwtService, jwtService: JwtService,
@ -27,6 +47,14 @@ fun Application.configureRouting(
call.respond(HttpStatusCode.BadRequest, "Bitte füllen Sie alle Felder aus") call.respond(HttpStatusCode.BadRequest, "Bitte füllen Sie alle Felder aus")
return@post return@post
} }
if (!isValidPassword(registerRequest.passwordPlain)) {
call.respond(
HttpStatusCode.BadRequest,
"Das Passwort muss mindestens 8 Zeichen lang sein, " +
"mindestens einen Großbuchstaben und ein Sonderzeichen enthalten.",
)
return@post
}
if (registerRequest.parentPin.length != 4) { if (registerRequest.parentPin.length != 4) {
call.respond(HttpStatusCode.BadRequest, "Die Eltern-PIN muss genau 4 Ziffern lang sein") call.respond(HttpStatusCode.BadRequest, "Die Eltern-PIN muss genau 4 Ziffern lang sein")
return@post return@post

View file

@ -112,7 +112,7 @@ class ServerTest {
setBody( setBody(
"""{ """{
"email": "test@family.de", "email": "test@family.de",
"passwordPlain": "strengGeheim1", "passwordPlain": "StrengGeheim1!",
"familyName": "Familie Schmidt", "familyName": "Familie Schmidt",
"childAge": 8, "childAge": 8,
"parentPin": "4321" "parentPin": "4321"
@ -129,7 +129,7 @@ class ServerTest {
setBody( setBody(
"""{ """{
"email": "test@family.de", "email": "test@family.de",
"passwordPlain": "strengGeheim1" "passwordPlain": "StrengGeheim1!"
}""", }""",
) )
} }
@ -139,6 +139,50 @@ class ServerTest {
assertEquals("Familie Schmidt", loginJson.familyName) assertEquals("Familie Schmidt", loginJson.familyName)
} }
@Test
fun `test registration rejects invalid passwords`() =
testApplication {
val jwtService = JwtService()
val repository = InMemoryKidioRepository()
application {
install(ServerContentNegotiation) {
json()
}
configureSecurity(jwtService)
configureRouting(repository, jwtService)
}
val invalidPasswords =
listOf(
"Kurz1!",
"keingrossbuch1!",
"KeinSonder12",
"Passwort123!😊",
)
for (password in invalidPasswords) {
val response =
client.post("/api/v1/auth/register") {
header(HttpHeaders.ContentType, ContentType.Application.Json.toString())
setBody(
"""{
"email": "test_${System.currentTimeMillis()}@family.de",
"passwordPlain": "$password",
"familyName": "Familie Schmidt",
"childAge": 8,
"parentPin": "4321"
}""",
)
}
assertEquals(
HttpStatusCode.BadRequest,
response.status,
"Passwort '$password' hätte abgelehnt werden müssen!",
)
}
}
@Test @Test
fun `test tasks endpoint and verify with jwt`() = fun `test tasks endpoint and verify with jwt`() =
testApplication { testApplication {