diff --git a/detekt.yml b/detekt.yml index 7cd8a74..ed8a0ed 100644 --- a/detekt.yml +++ b/detekt.yml @@ -454,7 +454,7 @@ style: active: true ReturnCount: active: true - max: 5 + max: 10 excludedFunctions: "equals" SafeCast: active: true diff --git a/src/main/kotlin/pl/edu/uj/ii/ksi/mordor/configuration/WebSecurityConfig.kt b/src/main/kotlin/pl/edu/uj/ii/ksi/mordor/configuration/WebSecurityConfig.kt index 07c3c7d..dae81cc 100644 --- a/src/main/kotlin/pl/edu/uj/ii/ksi/mordor/configuration/WebSecurityConfig.kt +++ b/src/main/kotlin/pl/edu/uj/ii/ksi/mordor/configuration/WebSecurityConfig.kt @@ -64,6 +64,7 @@ class WebSecurityConfig( } http.authorizeRequests().antMatchers(*allowUrls.toTypedArray()).permitAll() .anyRequest().authenticated() + .and().headers().frameOptions().sameOrigin() } override fun configure(web: WebSecurity) { diff --git a/src/main/kotlin/pl/edu/uj/ii/ksi/mordor/controllers/FilesystemController.kt b/src/main/kotlin/pl/edu/uj/ii/ksi/mordor/controllers/FilesystemController.kt index 595c60d..ad03e5a 100644 --- a/src/main/kotlin/pl/edu/uj/ii/ksi/mordor/controllers/FilesystemController.kt +++ b/src/main/kotlin/pl/edu/uj/ii/ksi/mordor/controllers/FilesystemController.kt @@ -84,6 +84,14 @@ class FilesystemController( )) } + private fun previewPage(entity: RepositoryFile, path: String): ModelAndView { + return ModelAndView("preview_page", mapOf( + "raw" to "/raw/$path", + "path" to createBreadcrumb(entity), + "download" to "/download/${entity.relativePath}" + )) + } + @Secured(Permission.READ_STR) @GetMapping("/file/**") fun fileIndex(request: HttpServletRequest): ModelAndView { @@ -106,6 +114,7 @@ class FilesystemController( return ModelAndView("tree", mapOf("children" to sortedChildren, "path" to createBreadcrumb(entity))) } else if (entity is RepositoryFile) { when { + entity.isPage -> return previewPage(entity, path) entity.mimeType.startsWith("text/") || entity.isCode -> return previewText(entity) entity.isDisplayableImage -> return previewImage(entity) } @@ -132,6 +141,24 @@ class FilesystemController( response.flushBuffer() } + @Secured(Permission.READ_STR) + @GetMapping("/raw/**") + fun raw(request: HttpServletRequest, response: HttpServletResponse) { + val path = request.servletPath.removePrefix("/raw/") + val entity = (repoService.getEntity(path) + ?: throw NotFoundException(path)) as? RepositoryFile + ?: throw BadRequestException("not a file") + + response.contentType = entity.mimeType + response.setContentLengthLong(entity.file.length()) + + val stream = entity.file.inputStream() + stream.use { + IOUtils.copy(stream, response.outputStream) + } + response.flushBuffer() + } + @ExceptionHandler(value = [NotFoundException::class]) fun notFoundException(ex: NotFoundException): ModelAndView { return ModelAndView("404", "path", ex.path) diff --git a/src/main/kotlin/pl/edu/uj/ii/ksi/mordor/services/repository/RepositoryFile.kt b/src/main/kotlin/pl/edu/uj/ii/ksi/mordor/services/repository/RepositoryFile.kt index 38678d8..bb8d408 100644 --- a/src/main/kotlin/pl/edu/uj/ii/ksi/mordor/services/repository/RepositoryFile.kt +++ b/src/main/kotlin/pl/edu/uj/ii/ksi/mordor/services/repository/RepositoryFile.kt @@ -20,8 +20,8 @@ class RepositoryFile( private val logger = LoggerFactory.getLogger(RepositoryFile::class.java) private val displayableImageTypes = setOf( - // Based on: https://en.wikipedia.org/wiki/Comparison_of_web_browsers#Image_format_support - "image/png", "image/jpeg", "image/gif", "image/svg+xml" + // Based on: https://en.wikipedia.org/wiki/Comparison_of_web_browsers#Image_format_support + "image/png", "image/jpeg", "image/gif", "image/svg+xml" ) private val safeMimetype = setOf( @@ -45,6 +45,10 @@ class RepositoryFile( "Makefile", "Dockerfile", "CMakeLists.txt" // TODO: expand ) + + private val pageFileNames = setOf( + "html", "htm" + ) } val mimeType: String @@ -64,6 +68,11 @@ class RepositoryFile( return codeFileNames.contains(name) || codeFileExts.contains(FilenameUtils.getExtension(name)) } + val isPage: Boolean + get() { + return pageFileNames.contains(FilenameUtils.getExtension(name.toLowerCase())) + } + val isDisplayableImage: Boolean get() { return displayableImageTypes.contains(mimeType) diff --git a/src/main/resources/static/style.css b/src/main/resources/static/style.css index 633af64..88b7c68 100644 --- a/src/main/resources/static/style.css +++ b/src/main/resources/static/style.css @@ -40,3 +40,10 @@ code.hljs { padding: 1px; background-color: #fff; } + +.page-frame { + display: block; + width: 100%; + border: none; + outline: none; +} diff --git a/src/main/resources/templates/preview_page.html b/src/main/resources/templates/preview_page.html new file mode 100644 index 0000000..0368b06 --- /dev/null +++ b/src/main/resources/templates/preview_page.html @@ -0,0 +1,40 @@ + + +
++ ++