~jan0sch/smederee

Showing details for patch 622a533f666731bf769afce362d2a26cc4b8d589.
2022-10-11 (Tue), 11:30 AM - Jens Grassel - 622a533f666731bf769afce362d2a26cc4b8d589

Account Management: Also delete user directory on account removal.

- check if dir is sub dir of configured repos dir before removing
- remove directory before account removal from database
Summary of changes
1 files modified with 65 lines added and 2 lines removed
  • modules/hub/src/main/scala/de/smederee/hub/AccountManagementRoutes.scala with 65 added and 2 removed lines
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)
                 )