~jan0sch/smederee
Showing details for patch 622a533f666731bf769afce362d2a26cc4b8d589.
diff -rN -u old-smederee/modules/hub/src/main/scala/de/smederee/hub/AccountManagementRoutes.scala new-smederee/modules/hub/src/main/scala/de/smederee/hub/AccountManagementRoutes.scala --- old-smederee/modules/hub/src/main/scala/de/smederee/hub/AccountManagementRoutes.scala 2025-02-02 03:48:06.901248297 +0000 +++ new-smederee/modules/hub/src/main/scala/de/smederee/hub/AccountManagementRoutes.scala 2025-02-02 03:48:06.901248297 +0000 @@ -17,6 +17,9 @@ package de.smederee.hub +import java.io.IOException +import java.nio.file.{ FileVisitResult, FileVisitor, Files } + import cats._ import cats.data._ import cats.effect._ @@ -55,6 +58,60 @@ ) extends Http4sDsl[F] { private val log = LoggerFactory.getLogger(getClass) + /** Delete the given directory recursively. It is checked if the given directory is a direct sub directory + * of the `repositoriesDirectory` and only if this is the case is the directory removed. + * + * @param userDirectory + * The path on the filesystem to the directory that shall be deleted. + * @return + * `true` if the directory was deleted. + */ + protected def deleteUserDirectory(userDirectory: java.nio.file.Path): F[Boolean] = + for { + _ <- Sync[F].delay(log.debug(s"Request to delete user dir: $userDirectory")) + reposDirPath <- Sync[F].delay(configuration.darcs.repositoriesDirectory.toPath) + isSubDir <- Sync[F].delay(reposDirPath.equals(userDirectory.getParent())) + deleted <- + Sync[F].delay { + if (isSubDir) { + Files.walkFileTree( + userDirectory, + new FileVisitor[java.nio.file.Path] { + override def visitFileFailed(file: java.nio.file.Path, exc: IOException): FileVisitResult = + FileVisitResult.CONTINUE + + override def visitFile( + file: java.nio.file.Path, + attrs: java.nio.file.attribute.BasicFileAttributes + ): FileVisitResult = { + Files.delete(file) + FileVisitResult.CONTINUE + } + + override def preVisitDirectory( + dir: java.nio.file.Path, + attrs: java.nio.file.attribute.BasicFileAttributes + ): FileVisitResult = FileVisitResult.CONTINUE + + override def postVisitDirectory( + dir: java.nio.file.Path, + exc: IOException + ): FileVisitResult = { + Files.delete(dir) + FileVisitResult.CONTINUE + } + } + ) + Files.deleteIfExists(userDirectory) + } else { + log.warn( + s"Refused requested removal of directory $userDirectory which is not a direct sub directory of the configured repositories directory!" + ) + false + } + } + } yield deleted + private val deleteAccount: AuthedRoutes[Account, F] = AuthedRoutes.of { case ar @ POST -> Root / "user" / "settings" / "delete" as user => ar.req.decodeStrict[F, UrlForm] { urlForm => @@ -86,12 +143,18 @@ ) response <- if (passwordCorrect && userIsSure) { - // Delete the user account and redirect the user ot the start page removing the authentication cookie in the process. - // FIXME Also delete the repositories on disk! + // First we delete the user directory and all sub directories. + // Afterwards we delete the user account from the database and redirect the user + // to the start page, removing the authentication cookie in the process. for { _ <- Sync[F].delay( log.info(s"Going to delete account ${user.name} as requested by the user.") ) + userDir <- Sync[F].delay( + java.nio.file.Paths + .get(configuration.darcs.repositoriesDirectory.toPath.toString, user.name.toString) + ) + _ <- deleteUserDirectory(userDir) response <- accountManagementRepo.deleteAccount(user.uid) *> SeeOther(Location(rootUri)).map( _.removeCookie(Constants.authenticationCookieName.toString) )