From 5356f47c9cfd2a0c8db2a4d621c35e70ef384a30 Mon Sep 17 00:00:00 2001 From: Stefan Schallerl Date: Fri, 7 Feb 2025 14:53:17 +0100 Subject: [PATCH] Adds registration. --- .../kotlin/net/h34t/filemure/FilemureApp.kt | 5 +- .../main/kotlin/net/h34t/filemure/Types.kt | 7 ++ .../filemure/controller/AccountController.kt | 82 +++++++++++++++++++ .../filemure/controller/LoginController.kt | 41 ---------- .../filemure/repository/SqliteRepository.kt | 54 +++++++++++- .../net/h34t/filemure/db/Database.sq | 8 +- .../tpl/net.h34t.filemure.tpl/Limbo.tpl.html | 8 +- .../tpl/net.h34t.filemure.tpl/Login.tpl.html | 10 ++- .../net.h34t.filemure.tpl/Overview.tpl.html | 32 ++++---- .../net.h34t.filemure.tpl/Register.tpl.html | 25 ++++++ 10 files changed, 205 insertions(+), 67 deletions(-) create mode 100644 app/src/main/kotlin/net/h34t/filemure/controller/AccountController.kt delete mode 100644 app/src/main/kotlin/net/h34t/filemure/controller/LoginController.kt create mode 100644 app/src/main/tpl/net.h34t.filemure.tpl/Register.tpl.html diff --git a/app/src/main/kotlin/net/h34t/filemure/FilemureApp.kt b/app/src/main/kotlin/net/h34t/filemure/FilemureApp.kt index a905425..fc135df 100644 --- a/app/src/main/kotlin/net/h34t/filemure/FilemureApp.kt +++ b/app/src/main/kotlin/net/h34t/filemure/FilemureApp.kt @@ -11,7 +11,7 @@ class FilemureApp(repository: SqliteRepository) { private val modifiers = TemplateModifiers() - private val loginPageController = LoginController(modifiers, repository) + private val loginPageController = AccountController(modifiers, repository) private val overviewController = OverviewController(modifiers, repository) private val limboController = LimboController(modifiers, repository) @@ -33,6 +33,9 @@ class FilemureApp(repository: SqliteRepository) { server.post("/login", loginPageController::doLogin, Role.ANON, Role.USER) server.post("/logout", loginPageController::doLogout, Role.USER) + server.get("/register", loginPageController::formRegister, Role.ANON, Role.USER) + server.post("/register", loginPageController::doRegister, Role.ANON, Role.USER) + server.post("/upload", uploadController::upload, Role.USER) server.get("/limbo", limboController::formLimbo, Role.USER) diff --git a/app/src/main/kotlin/net/h34t/filemure/Types.kt b/app/src/main/kotlin/net/h34t/filemure/Types.kt index bd271f2..a43361a 100644 --- a/app/src/main/kotlin/net/h34t/filemure/Types.kt +++ b/app/src/main/kotlin/net/h34t/filemure/Types.kt @@ -2,6 +2,13 @@ package net.h34t.filemure import java.time.LocalDateTime +data class AccountRef( + val id: Long, + val extId: ExtId, + val email: String, + val created: LocalDateTime, + val state: State +) @JvmInline value class ExtId(val value: String) { diff --git a/app/src/main/kotlin/net/h34t/filemure/controller/AccountController.kt b/app/src/main/kotlin/net/h34t/filemure/controller/AccountController.kt new file mode 100644 index 0000000..ed220e8 --- /dev/null +++ b/app/src/main/kotlin/net/h34t/filemure/controller/AccountController.kt @@ -0,0 +1,82 @@ +package net.h34t.filemure.controller + +import io.javalin.http.BadRequestResponse +import io.javalin.http.Context +import net.h34t.filemure.* +import net.h34t.filemure.repository.SqliteRepository +import net.h34t.filemure.tpl.Frame +import net.h34t.filemure.tpl.Login +import net.h34t.filemure.tpl.Register + +class AccountController(private val modifiers: TemplateModifiers, private val repository: SqliteRepository) { + + fun formLogin(ctx: Context) { + val loginError = ctx.queryParam("login_error") != null + + ctx.tempolin( + Frame( + modifiers = modifiers, + title = "Login", + target = "", + back = "", + logout = false, + content = Login( + loginError = loginError + ) + ) + ) + } + + fun doLogin(ctx: Context) { + val username = ctx.formParam("username") ?: throw BadRequestResponse() + val password = ctx.formParam("password") ?: throw BadRequestResponse() + + val acc = repository.loginAccount(email = username, password = password.trim().toCharArray()) + + if (acc != null) { + ctx.setSession(Session(id = acc.id, email = acc.email)) + ctx.redirectPRG("/") + } else { + ctx.setSession(null) + ctx.redirectPRG("/login/?login_error") + } + } + + fun doLogout(ctx: Context) { + ctx.setSession(null) + ctx.redirectPRG("/login") + } + + fun formRegister(ctx: Context) { + val registerError = ctx.queryParam("register_error") != null + + ctx.tempolin( + Frame( + modifiers = modifiers, + title = "Register", + target = "", + back = "/", + logout = false, + content = Register( + registerError = registerError + ) + ) + ) + } + + fun doRegister(ctx: Context) { + val username = ctx.formParam("username") ?: throw BadRequestResponse() + val password = ctx.formParam("password") ?: throw BadRequestResponse() + + try { + require(username.isNotBlank()) + require(password.isNotBlank() && password.length >= 6) + + repository.addAccount(email = username, password = password.trim().toCharArray()) + ctx.redirectPRG("/login") + } catch (e: Exception) { + e.printStackTrace() + ctx.redirectPRG("/register?register_error") + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/net/h34t/filemure/controller/LoginController.kt b/app/src/main/kotlin/net/h34t/filemure/controller/LoginController.kt deleted file mode 100644 index 4bf5245..0000000 --- a/app/src/main/kotlin/net/h34t/filemure/controller/LoginController.kt +++ /dev/null @@ -1,41 +0,0 @@ -package net.h34t.filemure.controller - -import io.javalin.http.Context -import net.h34t.filemure.* -import net.h34t.filemure.repository.SqliteRepository -import net.h34t.filemure.tpl.Frame -import net.h34t.filemure.tpl.Login - -class LoginController(val modifiers: TemplateModifiers, val repository: SqliteRepository) { - - fun formLogin(ctx: Context) { - ctx.tempolin( - Frame( - modifiers = modifiers, - title = "Hello to Filemure", - target = "", - back = "", - logout = false, - content = Login() - ) - ) - } - - fun doLogin(ctx: Context) { - val username = ctx.formParam("username") - val password = ctx.formParam("password") - - if (username == "stefan@schallerl.com" && password == "foobar") { - ctx.setSession(Session(id = 1, email = username)) - ctx.redirectPRG("/") - } else { - ctx.setSession(null) - ctx.redirectPRG("/") - } - } - - fun doLogout(ctx: Context) { - ctx.setSession(null) - ctx.redirectPRG("/login") - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/net/h34t/filemure/repository/SqliteRepository.kt b/app/src/main/kotlin/net/h34t/filemure/repository/SqliteRepository.kt index 79509e2..9ac75ae 100644 --- a/app/src/main/kotlin/net/h34t/filemure/repository/SqliteRepository.kt +++ b/app/src/main/kotlin/net/h34t/filemure/repository/SqliteRepository.kt @@ -3,8 +3,10 @@ package net.h34t.filemure.repository import app.cash.sqldelight.ColumnAdapter import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver +import at.favre.lib.crypto.bcrypt.BCrypt import net.h34t.filemure.* import net.h34t.filemure.TagAdapter.serialize +import net.h34t.filemure.db.Account import net.h34t.filemure.db.Database import net.h34t.filemure.db.File_ import java.io.InputStream @@ -54,6 +56,11 @@ class SqliteRepository(url: String) { database = Database( driver = driver, + accountAdapter = Account.Adapter( + ext_idAdapter = extIdAdapter, + createdAdapter = localDateTimeAdapter, + stateAdapter = stateAdapter + ), documentAdapter = net.h34t.filemure.db.Document.Adapter( tagsAdapter = tagsAdapter, createdAdapter = localDateTimeAdapter, @@ -69,6 +76,47 @@ class SqliteRepository(url: String) { ) } + private val bcrypt = BCrypt.withDefaults() + + private fun generateExtId(): ExtId = ExtId.generate() + + fun addAccount( + email: String, + password: CharArray + ) { + val bcryptPassword = bcrypt.hashToString(6, password) + val extId = generateExtId() + database.databaseQueries.addAccount( + extId = extId, + email = email, + password = bcryptPassword, + state = State.ACTIVE + ) + } + + fun loginAccount(email: String, password: CharArray): AccountRef? { + return database.databaseQueries.loginAccount(email).executeAsList().firstOrNull()?.let { account -> + if (account.state != State.ACTIVE) + return null + + val verify = BCrypt.verifyer().verify(password, account.password) + + if (verify.verified) { + account.let { + AccountRef( + id = it.id, + extId = it.ext_id, + email = it.email, + created = it.created, + state = it.state + ) + } + } else { + null + } + } + } + fun addFileToLimbo( accountId: Long, filename: String, @@ -77,7 +125,7 @@ class SqliteRepository(url: String) { content: InputStream ): IdPair = database.databaseQueries.transactionWithResult { - val extId = ExtId.generate() + val extId = generateExtId() database.databaseQueries.insertFileIntoLimbo( account_id = accountId, ext_id = extId, @@ -102,7 +150,7 @@ class SqliteRepository(url: String) { content: InputStream ) = database.databaseQueries.transactionWithResult { - ExtId.generate().let { extId -> + generateExtId().let { extId -> database.databaseQueries.insertFileForDocument( account_id = accountId, document_id = documentId, @@ -148,7 +196,7 @@ class SqliteRepository(url: String) { description: String, fileExtIds: List ): ExtId { - val extId = ExtId.generate() + val extId = generateExtId() database.databaseQueries.transaction { database.databaseQueries.addDocument( account_id = accountId, diff --git a/app/src/main/sqldelight/net/h34t/filemure/db/Database.sq b/app/src/main/sqldelight/net/h34t/filemure/db/Database.sq index 0078be9..075e31d 100644 --- a/app/src/main/sqldelight/net/h34t/filemure/db/Database.sq +++ b/app/src/main/sqldelight/net/h34t/filemure/db/Database.sq @@ -8,7 +8,7 @@ import net.h34t.filemure.Tag; CREATE TABLE account ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - ext_id TEXT AS ExtId NOT NULL, + ext_id TEXT AS ExtId NOT NULL, email TEXT NOT NULL, password TEXT NOT NULL, created TEXT AS LocalDateTime DEFAULT (CURRENT_TIMESTAMP) NOT NULL, @@ -59,6 +59,12 @@ CREATE TABLE file ( CREATE INDEX file_state_IDX ON file (state); CREATE UNIQUE INDEX file_ext_id_IDX ON file (ext_id); +addAccount: +INSERT INTO account (ext_id, email, password, state) VALUES (:extId, :email, :password, :state); + +loginAccount: +SELECT id, ext_id, email, password, created, state FROM account WHERE email=:email; + --- insertFileForDocument: INSERT INTO file (account_id, document_id, ext_id, filename, content_type, file_size, content) VALUES (?, ?,?,?,?,?,?); diff --git a/app/src/main/tpl/net.h34t.filemure.tpl/Limbo.tpl.html b/app/src/main/tpl/net.h34t.filemure.tpl/Limbo.tpl.html index e77a39d..f9d74e6 100644 --- a/app/src/main/tpl/net.h34t.filemure.tpl/Limbo.tpl.html +++ b/app/src/main/tpl/net.h34t.filemure.tpl/Limbo.tpl.html @@ -30,7 +30,13 @@ -
+ + + +
+ + +
diff --git a/app/src/main/tpl/net.h34t.filemure.tpl/Login.tpl.html b/app/src/main/tpl/net.h34t.filemure.tpl/Login.tpl.html index 266424e..68cb9eb 100644 --- a/app/src/main/tpl/net.h34t.filemure.tpl/Login.tpl.html +++ b/app/src/main/tpl/net.h34t.filemure.tpl/Login.tpl.html @@ -1,7 +1,4 @@ -

Hello to Filemure

-
-
Log In @@ -21,6 +18,11 @@ - +

Or register a new account.

+ + {if $loginError} +
Email or password wrong or the account doesn't exist.
+ {/if} +
\ No newline at end of file diff --git a/app/src/main/tpl/net.h34t.filemure.tpl/Overview.tpl.html b/app/src/main/tpl/net.h34t.filemure.tpl/Overview.tpl.html index f05d103..0102620 100644 --- a/app/src/main/tpl/net.h34t.filemure.tpl/Overview.tpl.html +++ b/app/src/main/tpl/net.h34t.filemure.tpl/Overview.tpl.html @@ -10,7 +10,9 @@ Files in limbo: {$limboFileCount} @@ -40,27 +42,25 @@ - - - + + + - {for $year} - - - - - - {for $month} + {for $year} - - - + + {for $month} + + + + + {/for} + {/for} - {/for} - {/for}
DateFilesViewDateFilesView
{*$year}
{*$monthHuman}{*$count} +
{*$year}
{*$monthHuman}{*$count}
diff --git a/app/src/main/tpl/net.h34t.filemure.tpl/Register.tpl.html b/app/src/main/tpl/net.h34t.filemure.tpl/Register.tpl.html new file mode 100644 index 0000000..ee8d925 --- /dev/null +++ b/app/src/main/tpl/net.h34t.filemure.tpl/Register.tpl.html @@ -0,0 +1,25 @@ +
+
+ Register + +
+ + +
+ +
+ + +
+ + + + {if $registerError} +
Email or password invalid or account already exists.
+ {/if} + +
+
\ No newline at end of file