Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,12 @@ internal suspend fun RoutingContext.handleActions(panels: List<AdminPanel>) {
panel == null || panel.isShowInAdminPanel().not() -> {
call.notFound("No panel found with plural name: $pluralName")
}
// Handle case when action is not found
action == null -> {
call.notFound("Action '$actionName' not found in panel with plural name: $pluralName")
}
// Process the action if both panel and action exist
else -> {
call.checkHasRole(panel) {
if (action == null) {
call.notFound("Action '$actionName' not found in panel with plural name: $pluralName")
return@checkHasRole
}
runCatching {
// Parse form data and validate CSRF token
val form = call.receiveParameters()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ internal suspend fun RoutingContext.handleAddRequest(panels: List<AdminPanel>) {
call.respondText { "No table found with plural name: $pluralName" }
return
}
if (panel.hasAddAction.not()) {
call.badRequest("Add action is disabled")
return
}
call.checkHasRole(panel) {
if (panel.hasAddAction.not()) {
call.badRequest("Add action is disabled")
return@checkHasRole
}
when (panel) {
is AdminJdbcTable -> insertData(pluralName, panel, panels)
is AdminMongoCollection -> insertData(pluralName, panel)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import ir.amirroid.ktoradmin.panels.AdminPanel
import ir.amirroid.ktoradmin.panels.findWithPluralName
import ir.amirroid.ktoradmin.repository.JdbcQueriesRepository
import ir.amirroid.ktoradmin.utils.withAuthenticate
import ir.amirroid.ktoradmin.validators.checkHasRole
import kotlinx.serialization.Serializable

@Serializable
Expand Down Expand Up @@ -66,47 +67,49 @@ internal fun Routing.configureAutoCompleteRouting(
return@post
}

val columnSet = ownerTable.getAllColumns().find { it.columnName == columnName }
if (columnSet == null || !columnSet.hasAutoComplete) {
call.respondText { "Autocomplete field not found: $columnName" }
return@post
}
call.checkHasRole(ownerTable) {
val columnSet = ownerTable.getAllColumns().find { it.columnName == columnName }
if (columnSet == null || !columnSet.hasAutoComplete) {
call.respondText { "Autocomplete field not found: $columnName" }
return@checkHasRole
}

val reference = columnSet.reference
if (reference == null || (reference !is Reference.ManyToOne && reference !is Reference.OneToOne)) {
call.respondText { "Invalid reference type for autocomplete field: $columnName" }
return@post
}
val reference = columnSet.reference
if (reference == null || (reference !is Reference.ManyToOne && reference !is Reference.OneToOne)) {
call.respondText { "Invalid reference type for autocomplete field: $columnName" }
return@checkHasRole
}

val referencedTableName = reference.tableName
val referencedTable =
panels.find { panel ->
panel is AdminJdbcTable && panel.getTableName() == referencedTableName
} as? AdminJdbcTable
val referencedTableName = reference.tableName
val referencedTable =
panels.find { panel ->
panel is AdminJdbcTable && panel.getTableName() == referencedTableName
} as? AdminJdbcTable

if (referencedTable == null) {
call.respondText { "Referenced table not found: $referencedTableName" }
return@post
}
if (referencedTable == null) {
call.respondText { "Referenced table not found: $referencedTableName" }
return@checkHasRole
}

val request = call.receive<AutoCompleteRequest>()
val search = request.search.takeIf { it.isNotBlank() }
val page = request.page.coerceAtLeast(0)
val request = call.receive<AutoCompleteRequest>()
val search = request.search.takeIf { it.isNotBlank() }
val page = request.page.coerceAtLeast(0)

val pageSize = DynamicConfiguration.autocompletePageSize
val pageSize = DynamicConfiguration.autocompletePageSize

val searchFields = columnSet.autoCompleteSearchFields
val searchFields = columnSet.autoCompleteSearchFields

val results =
JdbcQueriesRepository.searchReferences(
table = referencedTable,
search = search,
page = page,
pageSize = pageSize,
searchFields = searchFields,
)
val results =
JdbcQueriesRepository.searchReferences(
table = referencedTable,
search = search,
page = page,
pageSize = pageSize,
searchFields = searchFields,
)

call.respond(results)
call.respond(results)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,11 @@ internal suspend fun RoutingContext.handleSaveConfirmation(panels: List<AdminPan
call.respondText("Primary key is missing for: $pluralName", status = HttpStatusCode.BadRequest)

else -> {
if (!panel.hasEditAction) {
call.badRequest("Edit action is disabled")
return
}

call.checkHasRole(panel) {
if (!panel.hasEditAction) {
call.badRequest("Edit action is disabled")
return@checkHasRole
}
val parameters = call.receiveParameters()
val csrfToken = parameters[CSRF_TOKEN_FIELD_NAME]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ internal suspend fun ApplicationCall.handleCustomPage(
val user = principal<KtorAdminPrincipal>()
val username = user?.name

if (page.permissions != null && user != null) {
val userRoles = user.roles ?: emptyList()
if (page.permissions.none { it in userRoles }) {
if (page.permissions != null) {
val userRoles = user?.roles ?: emptyList()
if (user == null || page.permissions.none { it in userRoles }) {
return forbidden("You do not have permission to access this page.")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import ir.amirroid.ktoradmin.utils.invalidateRequest
import ir.amirroid.ktoradmin.utils.notFound
import ir.amirroid.ktoradmin.utils.serverError
import ir.amirroid.ktoradmin.utils.withAuthenticate
import ir.amirroid.ktoradmin.validators.checkHasRole

fun Routing.configureDownloadFilesRouting(
authenticateName: String?,
Expand All @@ -32,26 +33,28 @@ fun Routing.configureDownloadFilesRouting(
return@get call.badRequest("To use this feature, please enable this option in the configuration.")
}
val pluralName = call.parameters["pluralName"]
val csrfToken = call.parameters[CSRF_TOKEN_FIELD_NAME]
if (CsrfManager.validateToken(csrfToken).not()) {
return@get call.invalidateRequest()
}
val panel = panels.find { it.getPluralName() == pluralName }
if (panel == null || panel.isShowInAdminPanel().not()) {
call.notFound("No table found with plural name: $pluralName")
} else {
call.response.header(
HttpHeaders.ContentDisposition,
"attachment; filename=\"${pluralName}_data.csv\"",
)
val file =
when (panel) {
is AdminJdbcTable -> JdbcQueriesRepository.getAllDataAsCsvFile(panel)
is AdminMongoCollection -> MongoClientRepository.getAllDataAsCsvFile(panel)
else -> "return@get"
call.checkHasRole(panel) {
val csrfToken = call.parameters[CSRF_TOKEN_FIELD_NAME]
if (CsrfManager.validateToken(csrfToken).not()) {
return@checkHasRole call.invalidateRequest()
}
val bytes = file.toByteArray()
call.respondBytes(contentType = ContentType.Text.CSV) { bytes }
call.response.header(
HttpHeaders.ContentDisposition,
"attachment; filename=\"${pluralName}_data.csv\"",
)
val file =
when (panel) {
is AdminJdbcTable -> JdbcQueriesRepository.getAllDataAsCsvFile(panel)
is AdminMongoCollection -> MongoClientRepository.getAllDataAsCsvFile(panel)
else -> "return@checkHasRole"
}
val bytes = file.toByteArray()
call.respondBytes(contentType = ContentType.Text.CSV) { bytes }
}
}
}.onFailure {
call.serverError(it.message.orEmpty(), it)
Expand All @@ -66,33 +69,36 @@ fun Routing.configureDownloadFilesRouting(
}
val pluralName = call.parameters["pluralName"]
val primaryKey = call.parameters["primaryKey"] ?: return@get call.badRequest("Primary key is missing")
val csrfToken = call.parameters[CSRF_TOKEN_FIELD_NAME]

if (!CsrfManager.validateToken(csrfToken)) {
return@get call.invalidateRequest()
}

val panel =
panels.find { it.getPluralName() == pluralName }?.takeIf { it.isShowInAdminPanel() }
?: return@get call.notFound("No table found with plural name: $pluralName")

val font =
(DynamicConfiguration.template as? DefaultAdminTemplate)
?.settings
?.typography
?.font
val regularFontPath = font?.regular ?: "/static/font/IstokWeb-Regular.ttf"
val boldFontPath = font?.bold ?: "/static/font/IstokWeb-Bold.ttf"
call.checkHasRole(panel) {
val csrfToken = call.parameters[CSRF_TOKEN_FIELD_NAME]

val pdfData =
PdfHelper.generatePdf(panel, primaryKey, call, regularFontPath, boldFontPath)
?: return@get call.badRequest("Error generating PDF")
if (!CsrfManager.validateToken(csrfToken)) {
return@checkHasRole call.invalidateRequest()
}

call.response.header(
HttpHeaders.ContentDisposition,
"attachment; filename=\"output_${pluralName}_$primaryKey.pdf\"",
)
call.respondBytes(pdfData, ContentType.Application.Pdf)
val font =
(DynamicConfiguration.template as? DefaultAdminTemplate)
?.settings
?.typography
?.font
val regularFontPath = font?.regular ?: "/static/font/IstokWeb-Regular.ttf"
val boldFontPath = font?.bold ?: "/static/font/IstokWeb-Bold.ttf"

val pdfData =
PdfHelper.generatePdf(panel, primaryKey, this, regularFontPath, boldFontPath)
?: return@checkHasRole call.badRequest("Error generating PDF")

call.response.header(
HttpHeaders.ContentDisposition,
"attachment; filename=\"output_${pluralName}_$primaryKey.pdf\"",
)
call.respondBytes(pdfData, ContentType.Application.Pdf)
}
}.onFailure {
call.serverError(it.message.orEmpty(), it)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ir.amirroid.ktoradmin.panels.AdminPanel
import ir.amirroid.ktoradmin.repository.FileRepository
import ir.amirroid.ktoradmin.utils.notFound
import ir.amirroid.ktoradmin.utils.withAuthenticate
import ir.amirroid.ktoradmin.validators.checkHasRole

fun Routing.handleGenerateFileUrl(
panels: List<AdminPanel>,
Expand All @@ -36,49 +37,47 @@ fun Routing.handleGenerateFileUrl(
return@post
}
val (pluralName, itemName) = field
val itemUploadTarget =
panels
.firstOrNull {
it.getPluralName() == pluralName
}?.let { panel ->
if (panel.isShowInAdminPanel().not()) {
return@post call.notFound("No table found with plural name: $pluralName")
}
when (panel) {
is AdminJdbcTable ->
panel
.getAllColumns()
.firstOrNull {
it.columnName == itemName
}?.uploadTarget
val panel = panels.firstOrNull { it.getPluralName() == pluralName }
if (panel == null || panel.isShowInAdminPanel().not()) {
return@post call.notFound("No table found with plural name: $pluralName")
}
call.checkHasRole(panel) {
val itemUploadTarget =
when (panel) {
is AdminJdbcTable ->
panel
.getAllColumns()
.firstOrNull {
it.columnName == itemName
}?.uploadTarget

is AdminMongoCollection ->
panel
.getAllFields()
.firstOrNull {
it.fieldName == itemName
}?.uploadTarget
is AdminMongoCollection ->
panel
.getAllFields()
.firstOrNull {
it.fieldName == itemName
}?.uploadTarget

else -> null
}
else -> null
}
if (itemUploadTarget == null) {
call.respond(
message = mapOf("ir/amirroid/ktoradmin/errorirroid/ktoradmin/error" to "Field does not exist"),
status = HttpStatusCode.BadRequest,
)
return@post
}
FileRepository
.generateMediaUrl(
fileName = fileName,
uploadTarget = itemUploadTarget,
call = call,
).let {
if (itemUploadTarget == null) {
call.respond(
mapOf("url" to it),
message = mapOf("ir/amirroid/ktoradmin/errorirroid/ktoradmin/error" to "Field does not exist"),
status = HttpStatusCode.BadRequest,
)
return@checkHasRole
}
FileRepository
.generateMediaUrl(
fileName = fileName,
uploadTarget = itemUploadTarget,
call = call,
).let {
call.respond(
mapOf("url" to it),
)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ internal suspend fun RoutingContext.handleUpdateRequest(panels: List<AdminPanel>
call.notFound("No table found with plural name: $pluralName")
return
}
if (panel.hasEditAction.not()) {
call.badRequest("Edit action is disabled")
return
}
call.checkHasRole(panel) {
if (panel.hasEditAction.not()) {
call.badRequest("Edit action is disabled")
return@checkHasRole
}
runCatching {
when (panel) {
is AdminJdbcTable -> updateData(pluralName, primaryKey, panel, panels)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ private fun generateErrorHtml(
"""
<html>
<head>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
<style>
* {
margin: 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ internal suspend fun ApplicationCall.checkHasRole(
) {
val user = principal<KtorAdminPrincipal>()
val accessRoles = panel.getAccessRoles()
if (accessRoles != null && user != null && user.roles?.any { it in accessRoles } != true) {
if (accessRoles != null && (user == null || user.roles?.any { it in accessRoles } != true)) {
forbidden("Access denied: You do not have permission to perform this action.")
return
}
Expand Down
Loading