diff --git a/src/components/object-storage/BucketListItem.vue b/src/components/object-storage/BucketListItem.vue
index 7833d851fe1df08e42bd70353df79bee010eb4e5..0650a4c35ab93069487ac4862744d295f9afe01d 100644
--- a/src/components/object-storage/BucketListItem.vue
+++ b/src/components/object-storage/BucketListItem.vue
@@ -1,9 +1,5 @@
 <script setup lang="ts">
-import type {
-  BucketOut,
-  BucketPermissionIn,
-  BucketPermissionOut,
-} from "@/client/s3proxy";
+import type { BucketOut, BucketPermissionOut } from "@/client/s3proxy";
 import { Constraint } from "@/client/s3proxy";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import PermissionModal from "@/components/object-storage/modals/PermissionModal.vue";
@@ -15,6 +11,7 @@ import { Tooltip } from "bootstrap";
 import { useBucketStore } from "@/stores/buckets";
 import { useRouter } from "vue-router";
 import { useAuthStore } from "@/stores/users";
+import type { FolderTree } from "@/types/PseudoFolder";
 
 const props = defineProps<{
   active: boolean;
@@ -31,13 +28,19 @@ const router = useRouter();
 const permission = computed<BucketPermissionOut | undefined>(
   () => permissionRepository.ownPermissions[props.bucket.name],
 );
+const subFolder = computed<FolderTree>(() => {
+  const subFolders: Record<string, FolderTree> = {};
+  if (permission.value?.file_prefix != null) {
+    subFolders[permission.value.file_prefix] = { subFolders: {}, files: [] };
+  }
+  return { subFolders: subFolders, files: [] };
+});
 
 const emit = defineEmits<{
   (e: "delete-bucket", bucketName: string): void;
 }>();
 
-function permissionDeleted(perm: BucketPermissionIn) {
-  permissionRepository.deleteOwnPermission(perm.bucket_name);
+function permissionDeleted() {
   router.push({ name: "buckets" });
 }
 
@@ -55,7 +58,7 @@ onMounted(() => {
     v-if="permission != undefined && props.active"
     :modalID="'view-permission-modal' + randomIDSuffix"
     :bucket-name="props.bucket.name"
-    :sub-folders="{ subFolders: {}, files: [] }"
+    :sub-folders="subFolder"
     :edit-user-permission="permission"
     :readonly="true"
     :editable="false"
diff --git a/src/components/object-storage/modals/CopyObjectModal.vue b/src/components/object-storage/modals/CopyObjectModal.vue
index 32313142e8a8b9acfaadebd1353f0b67fa12c124..0687aaa6a9ceee3c848e94eac1a588108848a6fb 100644
--- a/src/components/object-storage/modals/CopyObjectModal.vue
+++ b/src/components/object-storage/modals/CopyObjectModal.vue
@@ -1,17 +1,17 @@
 <script setup lang="ts">
-import type { S3Client } from "@aws-sdk/client-s3";
-import { CopyObjectCommand } from "@aws-sdk/client-s3";
 import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 import { Modal, Toast } from "bootstrap";
 import { onMounted, reactive, watch } from "vue";
-import type { S3ObjectMetaInformation } from "@/client/s3proxy";
-import dayjs from "dayjs";
+import type { _Object as S3Object } from "@aws-sdk/client-s3";
 import { useBucketStore } from "@/stores/buckets";
+import { useS3ObjectStore } from "@/stores/s3objects";
+
+const objectRepository = useS3ObjectStore();
 
 const props = defineProps<{
   modalID: string;
-  sourceObject: S3ObjectMetaInformation;
-  s3Client: S3Client;
+  srcObject: S3Object;
+  srcBucket: string;
 }>();
 
 const formState = reactive<{
@@ -25,39 +25,32 @@ const formState = reactive<{
 });
 const bucketRepository = useBucketStore();
 
-const emit = defineEmits<{
-  (e: "object-copied", object: S3ObjectMetaInformation): void;
-}>();
-
 const randomIDSuffix = Math.random().toString(16).substring(2, 8);
 let copyModal: Modal | null = null;
 let successToast: Toast | null = null;
 let errorToast: Toast | null = null;
 
-function getFileName(key: string): string {
+function getFileName(key?: string): string {
+  if (key == undefined) {
+    return "";
+  }
   const splittedKey = key.split("/");
   return splittedKey[splittedKey.length - 1];
 }
 
 function copyObject() {
-  const command = new CopyObjectCommand({
-    Bucket: formState.destBucket,
-    CopySource: encodeURI(
-      `/${props.sourceObject.bucket}/${props.sourceObject.key}`,
-    ),
-    Key: formState.destKey,
-  });
+  if (props.srcObject.Key == undefined) {
+    return;
+  }
   formState.uploading = true;
-  props.s3Client
-    .send(command)
+  objectRepository
+    .copyObject(
+      props.srcBucket,
+      props.srcObject,
+      formState.destBucket,
+      formState.destKey,
+    )
     .then(() => {
-      emit("object-copied", {
-        key: formState.destKey,
-        bucket: formState.destBucket,
-        size: props.sourceObject.size,
-        last_modified: dayjs().toISOString(),
-        content_type: props.sourceObject.content_type,
-      });
       copyModal?.hide();
       successToast?.show();
       formState.destBucket = "";
@@ -76,9 +69,9 @@ function modalClosed() {
 }
 
 watch(
-  () => props.sourceObject.key,
+  () => props.srcObject.Key,
   (newKey) => {
-    formState.destKey = newKey;
+    formState.destKey = newKey ?? "";
   },
 );
 
@@ -140,7 +133,7 @@ onMounted(() => {
     v-on="{ 'hidden.bs.modal': modalClosed }"
   >
     <template v-slot:header>
-      <h4>Copy file {{ getFileName(props.sourceObject.key) }}</h4>
+      <h4>Copy file {{ getFileName(props.srcObject.Key) }}</h4>
     </template>
     <template v-slot:body>
       <div class="container-fluid">
diff --git a/src/components/object-storage/modals/CreateFolderModal.vue b/src/components/object-storage/modals/CreateFolderModal.vue
index 627b3a8428c672e99c2cffeb8c9e25c7bfd51203..cd26d2c2a94ff03d6abe9a68e0f2fb5e623a081a 100644
--- a/src/components/object-storage/modals/CreateFolderModal.vue
+++ b/src/components/object-storage/modals/CreateFolderModal.vue
@@ -1,17 +1,16 @@
 <script setup lang="ts">
-import type { S3Client } from "@aws-sdk/client-s3";
-import { PutObjectCommand } from "@aws-sdk/client-s3";
 import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 import { computed, onMounted, reactive } from "vue";
-import type { S3ObjectMetaInformation } from "@/client/s3proxy";
-import dayjs from "dayjs";
+
 import { Modal, Toast } from "bootstrap";
+import { useS3ObjectStore } from "@/stores/s3objects";
+
+const objectRepository = useS3ObjectStore();
 
 const props = defineProps<{
   modalID: string;
   bucketName: string;
   keyPrefix: string;
-  s3Client: S3Client;
 }>();
 
 const randomIDSuffix = Math.random().toString(16).substring(2, 8);
@@ -21,10 +20,6 @@ let errorToast: Toast | null = null;
 
 const currentFolders = computed<string[]>(() => props.keyPrefix.split("/"));
 
-const emit = defineEmits<{
-  (e: "folder-created", object: S3ObjectMetaInformation): void;
-}>();
-
 const formState = reactive<{
   folderName: string;
   uploading: boolean;
@@ -47,25 +42,12 @@ function uploadFolder() {
     return;
   }
   const realKey = reversedKey.slice(firstLetterIndex).reverse().join("") + "/";
-  const command = new PutObjectCommand({
-    Bucket: props.bucketName,
-    Body: "",
-    ContentType: "text/plain",
-    Key: realKey,
-  });
   formState.uploading = true;
-  props.s3Client
-    .send(command)
+  objectRepository
+    .createFolder(props.bucketName, realKey)
     .then(() => {
       uploadModal?.hide();
       successToast?.show();
-      emit("folder-created", {
-        key: realKey,
-        bucket: props.bucketName,
-        size: 0,
-        last_modified: dayjs().toISOString(),
-        content_type: "text/plain",
-      });
       formState.folderName = "";
     })
     .catch((e) => {
diff --git a/src/components/object-storage/modals/ObjectDetailModal.vue b/src/components/object-storage/modals/ObjectDetailModal.vue
index 023b1ca1d910f949ee621711136373a879236b78..9de29087f9c9d5cabc4e6f4d56aaba864808f23a 100644
--- a/src/components/object-storage/modals/ObjectDetailModal.vue
+++ b/src/components/object-storage/modals/ObjectDetailModal.vue
@@ -1,12 +1,13 @@
 <script setup lang="ts">
 import BootstrapModal from "@/components/modals/BootstrapModal.vue";
-import type { S3ObjectMetaInformation } from "@/client/s3proxy";
+import type { _Object as S3Object } from "@aws-sdk/client-s3";
 import dayjs from "dayjs";
 import { filesize } from "filesize";
 
 const props = defineProps<{
   modalID: string;
-  s3Object: S3ObjectMetaInformation;
+  s3Object: S3Object;
+  bucket: string;
 }>();
 </script>
 
@@ -25,17 +26,20 @@ const props = defineProps<{
           <tbody>
             <tr>
               <th scope="row" class="col-4">Bucket</th>
-              <td class="col-8">{{ props.s3Object.bucket }}</td>
+              <td class="col-8">{{ props.bucket }}</td>
             </tr>
             <tr>
               <th scope="row">Name</th>
-              <td>{{ props.s3Object.key }}</td>
+              <td>{{ props.s3Object.Key }}</td>
             </tr>
             <tr>
               <th scope="row">Size</th>
               <td>
                 {{
-                  filesize(props.s3Object.size, { base: 2, standard: "jedec" })
+                  filesize(props.s3Object.Size ?? 0, {
+                    base: 2,
+                    standard: "jedec",
+                  })
                 }}
               </td>
             </tr>
@@ -43,7 +47,7 @@ const props = defineProps<{
               <th scope="row">Timestamp</th>
               <td>
                 {{
-                  dayjs(props.s3Object.last_modified).format(
+                  dayjs(props.s3Object.LastModified).format(
                     "YYYY-MM-DD HH:mm:ss",
                   )
                 }}
@@ -51,7 +55,7 @@ const props = defineProps<{
             </tr>
             <tr>
               <th scope="row">Content Type</th>
-              <td>{{ props.s3Object.content_type }}</td>
+              <td>binary/octet-stream</td>
             </tr>
           </tbody>
         </table>
diff --git a/src/components/object-storage/modals/PermissionModal.vue b/src/components/object-storage/modals/PermissionModal.vue
index 1f9596cf1a313664b15198f2aedcaf3fa7a92b0b..485f1ace427930c8cd8bb0e36af5a00ec1ec9b8f 100644
--- a/src/components/object-storage/modals/PermissionModal.vue
+++ b/src/components/object-storage/modals/PermissionModal.vue
@@ -31,6 +31,8 @@ const props = defineProps<{
 }>();
 
 const bucketRepository = useBucketStore();
+
+const emit = defineEmits<{ (e: "permission-deleted"): void }>();
 // Variables
 // -----------------------------------------------------------------------------
 const randomIDSuffix = Math.random().toString(16).substring(2, 8);
@@ -158,7 +160,7 @@ function findSubFolders(
     const subFolderString =
       (parentFolders.length > 0 ? parentFolders.join("/") + "/" : "") +
       subFolder +
-      "/";
+      (subFolder.endsWith("/") ? "" : "/");
     arr.push(
       subFolderString,
       ...findSubFolders(
@@ -216,6 +218,7 @@ function confirmedDeletePermission(bucketName: string, uid: string) {
       .deleteBucketPermission(bucketName, uid)
       .then(() => {
         permissionDeleted.value = true;
+        emit("permission-deleted");
         permissionModal?.hide();
         successToast?.show();
       })
diff --git a/src/components/object-storage/modals/UploadObjectModal.vue b/src/components/object-storage/modals/UploadObjectModal.vue
index e59d9e99346ba442ea9e8383d659f58cd6ca793e..adafe9a20af1735e997005d1737f9b3dc2edaa1b 100644
--- a/src/components/object-storage/modals/UploadObjectModal.vue
+++ b/src/components/object-storage/modals/UploadObjectModal.vue
@@ -1,20 +1,17 @@
 <script setup lang="ts">
-import type { S3Client } from "@aws-sdk/client-s3";
-import { Upload } from "@aws-sdk/lib-storage";
 import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 import { computed, onMounted, reactive, ref, watch } from "vue";
-import type { S3ObjectMetaInformation } from "@/client/s3proxy";
-import dayjs from "dayjs";
 import { Modal, Toast } from "bootstrap";
 import { partial } from "filesize";
+import { useS3ObjectStore } from "@/stores/s3objects";
 
 const fsize = partial({ base: 2, standard: "jedec" });
+const objectRepository = useS3ObjectStore();
 
 const props = defineProps<{
   modalID: string;
   bucketName: string;
   keyPrefix: string;
-  s3Client: S3Client;
   editObjectFileName?: string;
 }>();
 
@@ -26,10 +23,6 @@ let errorToast: Toast | null = null;
 
 const currentFolders = computed<string[]>(() => props.keyPrefix.split("/"));
 
-const emit = defineEmits<{
-  (e: "object-created", object: S3ObjectMetaInformation): void;
-}>();
-
 watch(
   () => props.editObjectFileName,
   (nextFileName) => {
@@ -59,52 +52,35 @@ const editObject = computed<boolean>(
   () => props.editObjectFileName !== undefined,
 );
 
-async function uploadObject() {
+function uploadObject() {
   const key =
     props.keyPrefix.length > 0
       ? props.keyPrefix + "/" + formState.key
       : formState.key;
-  try {
-    formState.uploadDone = 0;
-    formState.uploading = true;
-    const parallelUploads3 = new Upload({
-      client: props.s3Client,
-      params: {
-        Bucket: props.bucketName,
-        Body: formState.file,
-        ContentType: formState.file.type,
-        Key: key,
-      },
-      queueSize: 4, // optional concurrency configuration
-      leavePartsOnError: false, // optional manually handle dropped parts
-    });
-
-    parallelUploads3.on("httpUploadProgress", (progress) => {
+  formState.uploadDone = 0;
+  formState.uploading = true;
+  objectRepository
+    .uploadObjectFile(props.bucketName, key, formState.file, (progress) => {
       if (progress.loaded != null && progress.total != null) {
         formState.uploadDone = progress.loaded;
         formState.uploadTotal = progress.total;
       }
+    })
+    .then(() => {
+      uploadModal?.hide();
+      successToast?.show();
+      formState.key = "";
+      if (objectFileInput.value != undefined) {
+        objectFileInput.value.value = "";
+      }
+    })
+    .catch((e) => {
+      console.error(e);
+      errorToast?.show();
+    })
+    .finally(() => {
+      formState.uploading = false;
     });
-    await parallelUploads3.done();
-    uploadModal?.hide();
-    successToast?.show();
-    emit("object-created", {
-      key: key,
-      bucket: props.bucketName,
-      size: formState.file?.size ?? 0,
-      last_modified: dayjs().toISOString(),
-      content_type: formState.file?.type ?? "binary/octet-stream",
-    });
-    formState.key = "";
-    if (objectFileInput.value != undefined) {
-      objectFileInput.value.value = "";
-    }
-  } catch (e) {
-    console.error(e);
-    errorToast?.show();
-  } finally {
-    formState.uploading = false;
-  }
 }
 
 function fileChange() {
diff --git a/src/components/parameter-schema/ParameterSchemaFormComponent.vue b/src/components/parameter-schema/ParameterSchemaFormComponent.vue
index ae1a09d8a07e4f99cb4d865598e5c3876254d727..fea414f4cff689c1cd46af8bc682c17dd62b1d78 100644
--- a/src/components/parameter-schema/ParameterSchemaFormComponent.vue
+++ b/src/components/parameter-schema/ParameterSchemaFormComponent.vue
@@ -6,6 +6,11 @@ import Ajv from "ajv";
 import type { ValidateFunction } from "ajv";
 import ParameterStringInput from "@/components/parameter-schema/form-mode/ParameterStringInput.vue";
 import { Toast } from "bootstrap";
+import { useBucketStore } from "@/stores/buckets";
+import { useS3KeyStore } from "@/stores/s3keys";
+
+const bucketRepository = useBucketStore();
+const s3KeyRepository = useS3KeyStore();
 
 // Props
 // =============================================================================
@@ -165,6 +170,9 @@ function startWorkflow() {
 // =============================================================================
 onMounted(() => {
   if (props.schema) updateSchema(props.schema);
+  bucketRepository.fetchBuckets();
+  bucketRepository.fetchOwnPermissions();
+  s3KeyRepository.fetchS3Keys();
   errorToast = new Toast("#workflowExecutionErrorToast");
 });
 </script>
@@ -208,6 +216,18 @@ onMounted(() => {
       @submit.prevent="startWorkflow"
       novalidate
     >
+      <template v-for="(group, groupName) in parameterGroups" :key="groupName">
+        <parameter-group-form
+          :modelValue="formState.formInput[groupName]"
+          @update:model-value="
+            (newValue) => (formState.formInput[groupName] = newValue)
+          "
+          v-if="formState.formInput[groupName]"
+          :parameter-group-name="groupName"
+          :parameter-group="group"
+          :showHidden="formState.showHidden"
+        />
+      </template>
       <div class="card mb-3">
         <h2 class="card-header" id="pipelineGeneralOptions">
           <font-awesome-icon icon="fa-solid fa-gear" class="me-2" />
@@ -256,18 +276,6 @@ onMounted(() => {
           </label>
         </div>
       </div>
-      <template v-for="(group, groupName) in parameterGroups" :key="groupName">
-        <parameter-group-form
-          :modelValue="formState.formInput[groupName]"
-          @update:model-value="
-            (newValue) => (formState.formInput[groupName] = newValue)
-          "
-          v-if="formState.formInput[groupName]"
-          :parameter-group-name="groupName"
-          :parameter-group="group"
-          :showHidden="formState.showHidden"
-        />
-      </template>
     </form>
     <!-- Loading card -->
     <div v-else class="col-9">
@@ -317,12 +325,6 @@ onMounted(() => {
       <nav class="h-100">
         <nav v-if="props.schema" class="nav">
           <ul class="ps-0">
-            <li class="nav-link">
-              <a href="#pipelineGeneralOptions">
-                <font-awesome-icon icon="fa-solid fa-gear" class="me-2" />
-                General Pipeline Options
-              </a>
-            </li>
             <li
               class="nav-link"
               v-for="group in navParameterGroups"
@@ -337,6 +339,12 @@ onMounted(() => {
                 {{ group.title }}</a
               >
             </li>
+            <li class="nav-link">
+              <a href="#pipelineGeneralOptions">
+                <font-awesome-icon icon="fa-solid fa-gear" class="me-2" />
+                General Pipeline Options
+              </a>
+            </li>
           </ul>
           <div class="mx-auto mb-3">
             <input
diff --git a/src/components/parameter-schema/form-mode/ParameterStringInput.vue b/src/components/parameter-schema/form-mode/ParameterStringInput.vue
index 7eca836677b4f78b8d6e2069333c6e9a706b5016..cbfa064affa80a82cc437d637b36fa95eaf2ccf8 100644
--- a/src/components/parameter-schema/form-mode/ParameterStringInput.vue
+++ b/src/components/parameter-schema/form-mode/ParameterStringInput.vue
@@ -1,9 +1,10 @@
 <script setup lang="ts">
 import { computed, watch, ref, onMounted, reactive } from "vue";
 import { useBucketStore } from "@/stores/buckets";
-import { ObjectService } from "@/client/s3proxy";
+import { useS3ObjectStore } from "@/stores/s3objects";
 
 const bucketRepository = useBucketStore();
+const s3objectRepository = useS3ObjectStore();
 
 const props = defineProps({
   parameter: {
@@ -38,8 +39,6 @@ const s3Path = reactive<{
   key: undefined,
 });
 
-const keysInBucket = ref<string[]>([]);
-
 watch(defaultValue, (newVal, oldVal) => {
   if (newVal != oldVal && newVal != undefined) {
     emit("update:modelValue", newVal);
@@ -72,13 +71,18 @@ const stringInput = ref<HTMLInputElement | undefined>(undefined);
 const format = computed<string | undefined>(() => props.parameter["format"]);
 
 const filesInBucket = computed<string[]>(() =>
-  keysInBucket.value.filter((obj) => !obj.endsWith("/")),
+  (s3objectRepository.objectMapping[s3Path.bucket ?? ""] ?? [])
+    .filter((obj) => !obj.Key?.endsWith("/"))
+    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+    .map((obj) => obj.Key!),
 );
 
 const foldersInBucket = computed<string[]>(() =>
-  keysInBucket.value
+  (s3objectRepository.objectMapping[s3Path.bucket ?? ""] ?? [])
+    .filter((obj) => obj.Key != undefined)
     .map((obj) => {
-      const parts = obj.split("/");
+      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+      const parts = obj.Key!.split("/");
       return parts
         .slice(0, parts.length - 1)
         .map((part, index) =>
@@ -126,17 +130,14 @@ const helpTextPresent = computed<boolean>(() => props.parameter["help_text"]);
 
 function updateKeysInBucket(bucketName?: string) {
   if (bucketName != null) {
-    ObjectService.objectGetBucketObjects(bucketName).then((objs) => {
-      keysInBucket.value = objs.map((obj) => obj.key);
-    });
-  } else {
-    keysInBucket.value = [];
+    s3objectRepository.fetchS3Objects(
+      bucketName,
+      bucketRepository.ownPermissions[bucketName]?.file_prefix ?? undefined,
+    );
   }
 }
 
 onMounted(() => {
-  bucketRepository.fetchBuckets();
-  bucketRepository.fetchOwnPermissions();
   if (format.value) {
     s3Path.key = defaultValue.value;
   }
diff --git a/src/stores/buckets.ts b/src/stores/buckets.ts
index c33eda777fa16e9ee958e48ca0ff60df044177a1..8389d86ceab16aa00201b6e4ecd36e42d900d682 100644
--- a/src/stores/buckets.ts
+++ b/src/stores/buckets.ts
@@ -209,6 +209,10 @@ export const useBucketStore = defineStore({
         bucketName,
         uid,
       ).then(() => {
+        const userRepository = useAuthStore();
+        if (uid == userRepository.currentUID) {
+          this.deleteOwnPermission(bucketName);
+        }
         if (this.bucketPermissionsMapping[bucketName] == undefined) {
           this.fetchBucketPermissions(bucketName);
         } else {
diff --git a/src/stores/s3keys.ts b/src/stores/s3keys.ts
index 7b6b1871a8aafc00e907c3132711fde54c164f13..915426f43a5128a7912be1ce316397d144c1a70b 100644
--- a/src/stores/s3keys.ts
+++ b/src/stores/s3keys.ts
@@ -2,6 +2,7 @@ import { defineStore } from "pinia";
 import { useAuthStore } from "@/stores/users";
 import type { S3Key } from "@/client/s3proxy";
 import { S3KeyService } from "@/client/s3proxy";
+import { useS3ObjectStore } from "@/stores/s3objects";
 
 export const useS3KeyStore = defineStore({
   id: "s3keys",
@@ -21,12 +22,14 @@ export const useS3KeyStore = defineStore({
     },
   },
   actions: {
-    fetchS3Keys(uid: string, onFinally?: () => void): Promise<S3Key[]> {
+    fetchS3Keys(onFinally?: () => void): Promise<S3Key[]> {
       if (this.keys.length > 0) {
         onFinally?.();
       }
-      return S3KeyService.s3KeyGetUserKeys(uid)
+      return S3KeyService.s3KeyGetUserKeys(useAuthStore().currentUID)
         .then((keys) => {
+          const s3ObjectRepository = useS3ObjectStore();
+          s3ObjectRepository.updateS3Client(keys[0]);
           const newMapping: Record<string, S3Key> = {};
           for (const key of keys) {
             newMapping[key.access_key] = key;
@@ -42,7 +45,9 @@ export const useS3KeyStore = defineStore({
         access_id,
         userRepository.currentUID,
       ).then(() => {
+        const s3ObjectRepository = useS3ObjectStore();
         delete this.keyMapping[access_id];
+        s3ObjectRepository.updateS3Client(this.keys[0]);
       });
     },
     createS3Key(): Promise<S3Key> {
diff --git a/src/stores/s3objects.ts b/src/stores/s3objects.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c2ed8e1fb2698b3e4ecfd3fca1745655d24c5221
--- /dev/null
+++ b/src/stores/s3objects.ts
@@ -0,0 +1,221 @@
+import { defineStore } from "pinia";
+import type { _Object as S3Object } from "@aws-sdk/client-s3";
+import {
+  CopyObjectCommand,
+  GetObjectCommand,
+  PutObjectCommand,
+  S3Client,
+} from "@aws-sdk/client-s3";
+import {
+  paginateListObjectsV2,
+  DeleteObjectCommand,
+  DeleteObjectsCommand,
+} from "@aws-sdk/client-s3";
+import { environment } from "@/environment";
+import type { S3Key } from "@/client/s3proxy";
+import { useBucketStore } from "@/stores/buckets";
+import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
+import dayjs from "dayjs";
+import { Upload } from "@aws-sdk/lib-storage";
+import type { Progress } from "@aws-sdk/lib-storage";
+
+export const useS3ObjectStore = defineStore({
+  id: "s3objects",
+  state: () =>
+    ({
+      objectMapping: {},
+      client: new S3Client({
+        region: "us-east-1",
+        endpoint: environment.S3_URL,
+        forcePathStyle: true,
+        credentials: {
+          accessKeyId: "",
+          secretAccessKey: "",
+        },
+      }),
+    }) as {
+      objectMapping: Record<string, S3Object[]>;
+      client: S3Client;
+    },
+  getters: {
+    getPresignedUrl(): (bucketName: string, key: string) => Promise<string> {
+      return (bucketName, key) => {
+        const command = new GetObjectCommand({
+          Bucket: bucketName,
+          Key: key,
+        });
+        return getSignedUrl(this.client, command, {
+          expiresIn: 30,
+        });
+      };
+    },
+  },
+  actions: {
+    _pushObject(bucketName: string, newObj: S3Object) {
+      if (this.objectMapping[bucketName] == undefined) {
+        this.fetchS3Objects(bucketName);
+      } else {
+        const objIndex = this.objectMapping[bucketName].findIndex(
+          (obj) => obj.Key === newObj.Key,
+        );
+        if (objIndex > -1) {
+          this.objectMapping[bucketName][objIndex] = newObj;
+        } else {
+          this.objectMapping[bucketName].push(newObj);
+        }
+      }
+    },
+    updateS3Client(s3Key: S3Key) {
+      this.client = new S3Client({
+        region: "us-east-1",
+        endpoint: environment.S3_URL,
+        forcePathStyle: true,
+        credentials: {
+          accessKeyId: s3Key.access_key,
+          secretAccessKey: s3Key.secret_key,
+        },
+      });
+    },
+    async fetchS3Objects(
+      bucketName: string,
+      prefix?: string,
+      onFinally?: () => void,
+    ): Promise<S3Object[]> {
+      if (this.objectMapping[bucketName] != undefined) {
+        onFinally?.();
+      }
+      const pag = paginateListObjectsV2(
+        { client: this.client },
+        { Bucket: bucketName, Prefix: prefix },
+      );
+      const objs: S3Object[] = [];
+      try {
+        for await (const page of pag) {
+          objs.push(...(page.Contents ?? []));
+        }
+        this.objectMapping[bucketName] = objs;
+      } finally {
+        onFinally?.();
+      }
+      return objs;
+    },
+    deleteObject(bucketName: string, key: string): Promise<void> {
+      const command = new DeleteObjectCommand({
+        Bucket: bucketName,
+        Key: key,
+      });
+      return this.client
+        .send(command)
+        .then(() => {
+          const bucketRepository = useBucketStore();
+          bucketRepository.fetchBucket(bucketName);
+          if (this.objectMapping[bucketName] == undefined) {
+            this.fetchS3Objects(bucketName);
+          } else {
+            this.objectMapping[bucketName] = this.objectMapping[
+              bucketName
+            ].filter((obj) => obj.Key !== key);
+          }
+        })
+        .catch((err) => {
+          console.error(err);
+        });
+    },
+    deleteObjectsWithPrefix(bucketName: string, prefix: string): Promise<void> {
+      if (this.objectMapping[bucketName] == undefined) {
+        return Promise.resolve();
+      }
+      const command = new DeleteObjectsCommand({
+        Bucket: bucketName,
+        Delete: {
+          Objects: this.objectMapping[bucketName]
+            .filter((obj) => obj.Key?.startsWith(prefix))
+            .map((obj) => {
+              return { Key: obj.Key };
+            }),
+        },
+      });
+      return this.client.send(command).then(() => {
+        const bucketRepository = useBucketStore();
+        bucketRepository.fetchBucket(bucketName);
+        if (this.objectMapping[bucketName] == undefined) {
+          this.fetchS3Objects(bucketName);
+        } else {
+          this.objectMapping[bucketName] = this.objectMapping[
+            bucketName
+          ].filter((obj) => !obj.Key?.startsWith(prefix));
+        }
+      });
+    },
+    copyObject(
+      srcBucket: string,
+      srcObject: S3Object,
+      destBucket: string,
+      destKey: string,
+    ): Promise<S3Object> {
+      if (srcObject.Key == undefined) {
+        return Promise.resolve({});
+      }
+      const command = new CopyObjectCommand({
+        Bucket: destBucket,
+        CopySource: encodeURI(`/${srcBucket}/${srcObject.Key}`),
+        Key: destKey,
+      });
+      return this.client.send(command).then(() => {
+        const newObj = {
+          Key: destKey,
+          Size: srcObject.Size,
+          LastModified: dayjs().toDate(),
+        };
+        this._pushObject(destBucket, newObj);
+        return newObj;
+      });
+    },
+    async uploadObjectFile(
+      bucketName: string,
+      key: string,
+      file: File,
+      onProgress?: (progress: Progress) => void,
+    ): Promise<S3Object> {
+      const parallelUploads3 = new Upload({
+        client: this.client,
+        params: {
+          Bucket: bucketName,
+          Body: file,
+          ContentType: file.type,
+          Key: key,
+        },
+        queueSize: 4, // optional concurrency configuration
+        leavePartsOnError: false, // optional manually handle dropped parts
+      });
+      if (onProgress != undefined) {
+        parallelUploads3.on("httpUploadProgress", onProgress);
+      }
+      await parallelUploads3.done();
+      const newObj = {
+        Key: key,
+        Size: file.size ?? 0,
+        LastModified: dayjs().toDate(),
+      };
+      this._pushObject(bucketName, newObj);
+      return newObj;
+    },
+    createFolder(bucketName: string, key: string): Promise<S3Object> {
+      const command = new PutObjectCommand({
+        Bucket: bucketName,
+        Body: "",
+        ContentType: "text/plain",
+        Key: key,
+      });
+      return this.client.send(command).then(() => {
+        const newObj = {
+          Key: key,
+          Size: 0,
+          LastModified: dayjs().toDate(),
+        };
+        this._pushObject(bucketName, newObj);
+        return newObj;
+      });
+    },
+  },
+});
diff --git a/src/stores/users.ts b/src/stores/users.ts
index 9a96af0cf942662ba4d22841e630f5bff364f7e4..76e987a733661dbacabfd58c096ad5bd7adacb6c 100644
--- a/src/stores/users.ts
+++ b/src/stores/users.ts
@@ -8,6 +8,7 @@ import { useWorkflowExecutionStore } from "@/stores/workflowExecutions";
 import { useBucketStore } from "@/stores/buckets";
 import { useWorkflowStore } from "@/stores/workflows";
 import { useS3KeyStore } from "@/stores/s3keys";
+import { useS3ObjectStore } from "@/stores/s3objects";
 
 type DecodedToken = {
   exp: number;
@@ -104,8 +105,6 @@ export const useAuthStore = defineStore({
     },
     updateUser(user: User) {
       this.user = user;
-      const keyRepository = useS3KeyStore();
-      keyRepository.fetchS3Keys(user.uid);
     },
     logout() {
       this.$reset();
@@ -113,6 +112,7 @@ export const useAuthStore = defineStore({
       useBucketStore().$reset();
       useWorkflowStore().$reset();
       useS3KeyStore().$reset();
+      useS3ObjectStore().$reset();
     },
     fetchUsernames(uids: string[]): Promise<User[]> {
       const filteredIds = uids
diff --git a/src/types/PseudoFolder.ts b/src/types/PseudoFolder.ts
index 1d363fe2c05b8daab9325cf2da6009d71e936d0f..c65e1f950c9a0825fae56ef479820bb9bff2ee4c 100644
--- a/src/types/PseudoFolder.ts
+++ b/src/types/PseudoFolder.ts
@@ -1,16 +1,16 @@
-import type { S3ObjectMetaInformation } from "@/client/s3proxy";
+import type { _Object as S3Object } from "@aws-sdk/client-s3";
 
-export interface S3ObjectWithFolder extends S3ObjectMetaInformation {
+export interface S3ObjectWithFolder extends S3Object {
   folder: string[];
   pseudoFileName: string;
 }
 
 export type S3PseudoFolder = {
-  size: number;
+  Size: number;
   parentFolder: string[];
-  last_modified: string;
+  LastModified: Date;
   name: string;
-  key: string;
+  Key: string;
 };
 
 export type FolderTree = {
diff --git a/src/views/LoginView.vue b/src/views/LoginView.vue
index 20c717db83793b7a28918d699f8759f6acdc1173..2f0d18aec0d76b59123fa8501ec39cceee14cb50 100644
--- a/src/views/LoginView.vue
+++ b/src/views/LoginView.vue
@@ -64,7 +64,7 @@ onMounted(() => {
       class="img-fluid mb-3"
       width="128"
       height="128"
-      alt="..."
+      alt="CloWM Logo"
     />
     <h1>
       <span class="blue fw-bold">Clo</span><span class="red fw-bold">W</span
@@ -98,7 +98,7 @@ onMounted(() => {
           <img
             src="/src/assets/images/nfdi.svg"
             alt="NFDI4Microbiota Logo"
-            height="70"
+            height="50"
           />
         </a>
       </div>
@@ -108,25 +108,25 @@ onMounted(() => {
           <img
             src="/src/assets/images/denbi.svg"
             alt="de.NBI Logo"
-            height="70"
+            height="50"
           />
         </a>
       </div>
       <div class="border rounded p-4 icon text-center">
         <h4 class="mb-4">Hosted By</h4>
         <a href="https://bibi.uni-bielefeld.de/">
-          <img src="/src/assets/images/bibi.png" alt="BiBi Logo" height="70" />
+          <img src="/src/assets/images/bibi.png" alt="BiBi Logo" height="50" />
         </a>
       </div>
       <div class="border rounded p-4 icon text-center">
         <h4 class="mb-4">Funded By</h4>
-        <img src="/src/assets/images/dfg.png" alt="DFG Logo" height="70" />
+        <img src="/src/assets/images/dfg.png" alt="DFG Logo" height="50" />
       </div>
       <div class="border rounded p-4 icon text-center">
         <img
           src="/src/assets/images/unibi.svg"
           alt="Bielefeld University Logo"
-          height="70"
+          height="50"
         />
       </div>
     </div>
diff --git a/src/views/object-storage/BucketView.vue b/src/views/object-storage/BucketView.vue
index cafdf0596bb62b1d9e0ff6c90b41c07a9d242827..39d238f7e2f3aa0226e057458c84910d1776a563 100644
--- a/src/views/object-storage/BucketView.vue
+++ b/src/views/object-storage/BucketView.vue
@@ -1,15 +1,10 @@
 <script setup lang="ts">
 import { onMounted, reactive, watch, computed } from "vue";
-import type {
-  S3ObjectMetaInformation,
-  BucketPermissionOut,
-} from "@/client/s3proxy";
 import type {
   FolderTree,
   S3PseudoFolder,
   S3ObjectWithFolder,
 } from "@/types/PseudoFolder";
-import { ObjectService } from "@/client/s3proxy";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
 import { filesize } from "filesize";
 import dayjs from "dayjs";
@@ -21,34 +16,16 @@ import PermissionModal from "@/components/object-storage/modals/PermissionModal.
 import ObjectDetailModal from "@/components/object-storage/modals/ObjectDetailModal.vue";
 import CreateFolderModal from "@/components/object-storage/modals/CreateFolderModal.vue";
 import DeleteModal from "@/components/modals/DeleteModal.vue";
-import {
-  S3Client,
-  DeleteObjectCommand,
-  DeleteObjectsCommand,
-  GetObjectCommand,
-} from "@aws-sdk/client-s3";
-import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
+import type { _Object as S3Object } from "@aws-sdk/client-s3";
 import { useAuthStore } from "@/stores/users";
 import { useBucketStore } from "@/stores/buckets";
-import { environment } from "@/environment";
+import { useS3ObjectStore } from "@/stores/s3objects";
 import { useS3KeyStore } from "@/stores/s3keys";
 
 const authStore = useAuthStore();
 const bucketRepository = useBucketStore();
-const keyRepository = useS3KeyStore();
-
-const client = computed<S3Client>(
-  () =>
-    new S3Client({
-      region: "us-east-1",
-      endpoint: environment.S3_URL,
-      forcePathStyle: true,
-      credentials: {
-        accessKeyId: keyRepository.keys[0]?.access_key ?? "",
-        secretAccessKey: keyRepository.keys[0]?.secret_key ?? "",
-      },
-    }),
-);
+const objectRepository = useS3ObjectStore();
+const s3KeyRepository = useS3KeyStore();
 
 // Constants
 // -----------------------------------------------------------------------------
@@ -56,14 +33,14 @@ const client = computed<S3Client>(
 const props = defineProps<{
   bucketName: string;
   subFolders: string[] | string;
-  permission?: BucketPermissionOut;
 }>();
+
 const randomIDSuffix = Math.random().toString(16).substring(2, 8);
 let successToast: Toast | null = null;
+let refreshTimeout: NodeJS.Timeout | undefined = undefined;
 
 // Reactive State
 // -----------------------------------------------------------------------------
-
 const deleteObjectsState = reactive<{
   deletedItem: string;
   potentialObjectToDelete: string;
@@ -75,36 +52,28 @@ const deleteObjectsState = reactive<{
 });
 
 const objectState = reactive<{
-  objects: S3ObjectMetaInformation[];
   loading: boolean;
   filterString: string;
   bucketNotFoundError: boolean;
   bucketPermissionError: boolean;
-  createdPermission: undefined | BucketPermissionOut;
   editObjectKey: string;
-  copyObject: S3ObjectMetaInformation;
-  viewDetailObject: S3ObjectMetaInformation;
+  copyObject: S3Object;
+  viewDetailObject: S3Object;
 }>({
-  objects: [],
   loading: true,
   filterString: "",
   bucketNotFoundError: false,
   bucketPermissionError: false,
-  createdPermission: undefined,
   editObjectKey: "",
   copyObject: {
-    key: "",
-    size: 0,
-    bucket: "",
-    last_modified: "2022-01-01",
-    content_type: "text/plain",
+    Key: "",
+    Size: 0,
+    LastModified: new Date(),
   },
   viewDetailObject: {
-    key: "",
-    size: 0,
-    bucket: "",
-    last_modified: "2022-01-01",
-    content_type: "text/plain",
+    Key: "",
+    Size: 0,
+    LastModified: new Date(),
   },
 });
 
@@ -113,13 +82,17 @@ const objectState = reactive<{
 const filteredObjects = computed<(S3ObjectWithFolder | S3PseudoFolder)[]>(
   () => {
     return objectState.filterString.length > 0
-      ? visibleObjects.value.filter((obj) =>
-          obj.key.includes(objectState.filterString),
+      ? visibleObjects.value.filter(
+          (obj) => obj.Key?.includes(objectState.filterString),
         )
       : visibleObjects.value;
   },
 );
 
+const s3Objects = computed<S3Object[]>(
+  () => objectRepository.objectMapping[props.bucketName] ?? [],
+);
+
 const folderStructure = computed<FolderTree>(() => {
   /**
    * Store the entire folder structure in a bucket in a tree-like data structure
@@ -170,8 +143,8 @@ const objectsWithFolders = computed<S3ObjectWithFolder[]>(() => {
    *  folder: dir1, dir2
    *  filename: text.txt
    */
-  return objectState.objects.map((obj) => {
-    const splittedKey = obj.key.split("/");
+  return s3Objects.value.map((obj) => {
+    const splittedKey = obj.Key?.split("/") ?? [""];
     return {
       ...obj,
       pseudoFileName: splittedKey[splittedKey.length - 1],
@@ -216,17 +189,19 @@ const visibleObjects = computed<(S3ObjectWithFolder | S3PseudoFolder)[]>(() => {
       );
       const folderLastModified = dayjs(
         calculateFolderLastModified(currentFolder.subFolders[subFolderName]),
-      ).toISOString();
+      ).toDate();
       return {
         name: subFolderName,
-        size: folderSize,
-        key: subFolderName,
+        Size: folderSize,
+        Key: subFolderName,
         parentFolder: currentSubFolders.value,
-        last_modified: folderLastModified,
+        LastModified: folderLastModified,
       } as S3PseudoFolder;
     }),
   );
-  return arr.filter((obj) => !obj.key.endsWith("/") && obj.key.length > 0);
+  return arr.filter(
+    (obj) => !obj.Key?.endsWith("/") && (obj.Key?.length ?? 0) > 0,
+  );
 });
 
 const subFolderInUrl = computed<boolean>(
@@ -235,9 +210,18 @@ const subFolderInUrl = computed<boolean>(
 const errorLoadingObjects = computed<boolean>(
   () => objectState.bucketPermissionError || objectState.bucketNotFoundError,
 );
-const writableBucket = computed<boolean>(() =>
-  bucketRepository.writableBucket(props.bucketName),
-);
+const writableBucket = computed<boolean>(() => {
+  // Allow only upload in bucket folder with respect to permission prefix
+  let prefixWritable = true;
+  if (
+    bucketRepository.ownPermissions[props.bucketName]?.file_prefix != undefined
+  ) {
+    prefixWritable =
+      bucketRepository.ownPermissions[props.bucketName]?.file_prefix ===
+      currentSubFolders.value.join("/") + "/";
+  }
+  return bucketRepository.writableBucket(props.bucketName) && prefixWritable;
+});
 const readableBucket = computed<boolean>(() =>
   bucketRepository.readableBucket(props.bucketName),
 );
@@ -249,7 +233,9 @@ watch(
   (newBucketName, oldBucketName) => {
     if (oldBucketName !== newBucketName) {
       // If bucket is changed, update the objects
-      updateObjects(newBucketName);
+      objectState.bucketPermissionError = false;
+      objectState.bucketNotFoundError = false;
+      fetchObjects();
       objectState.filterString = "";
     }
   },
@@ -273,7 +259,17 @@ watch(
 // Lifecycle Hooks
 // -----------------------------------------------------------------------------
 onMounted(() => {
-  updateObjects(props.bucketName);
+  let counter = 0;
+  const onFinally = () => {
+    counter++;
+    if (counter > 1) {
+      fetchObjects();
+    }
+  };
+  // wait till s3keys and ownPermissions are available before fetching objects
+  s3KeyRepository.fetchS3Keys(onFinally);
+  bucketRepository.fetchOwnPermissions(onFinally);
+
   document
     .querySelectorAll(".tooltip-container")
     .forEach(
@@ -291,7 +287,7 @@ onMounted(() => {
  */
 function calculateFolderSize(folder: FolderTree): number {
   let folderSize = 0;
-  folderSize += folder.files.reduce((acc, file) => acc + file.size, 0);
+  folderSize += folder.files.reduce((acc, file) => acc + (file.Size ?? 0), 0);
   for (const subFolderName of Object.keys(folder.subFolders)) {
     folderSize += calculateFolderSize(folder.subFolders[subFolderName]);
   }
@@ -306,7 +302,7 @@ function calculateFolderSize(folder: FolderTree): number {
 function calculateFolderLastModified(folder: FolderTree): string {
   let lastModified: dayjs.Dayjs;
   lastModified = folder.files
-    .map((f) => dayjs(f.last_modified))
+    .map((f) => dayjs(f.LastModified))
     .reduce(
       (acc, fileAccessed) => (fileAccessed.isAfter(acc) ? fileAccessed : acc),
       dayjs("2000-01-01"),
@@ -323,63 +319,45 @@ function calculateFolderLastModified(folder: FolderTree): string {
 }
 
 /**
- * Load the meta information about objects from a bucket
- * @param bucketName Name of a bucket
+ * Fetch object from bucket with loading animation
  */
-function updateObjects(bucketName: string) {
-  objectState.bucketNotFoundError = false;
-  objectState.bucketPermissionError = false;
+function fetchObjects() {
   objectState.loading = true;
-  ObjectService.objectGetBucketObjects(bucketName)
-    .then((objs) => {
-      objectState.objects = objs;
+  const prefix: string | undefined =
+    bucketRepository.ownPermissions[props.bucketName]?.file_prefix ?? undefined;
+  objectRepository
+    .fetchS3Objects(props.bucketName, prefix, () => {
+      objectState.loading = false;
     })
     .catch((error) => {
-      if (error.status === 404) {
-        objectState.bucketNotFoundError = true;
-      } else if (error.status == 403) {
+      if (error.Code == "AccessDenied") {
         objectState.bucketPermissionError = true;
+      } else {
+        objectState.bucketNotFoundError = true;
       }
-    })
-    .finally(() => {
-      objectState.loading = false;
     });
 }
 
+/**
+ * Fetch the meta information about objects from a bucket
+ */
+function refreshObjects() {
+  clearTimeout(refreshTimeout);
+  refreshTimeout = setTimeout(() => {
+    fetchObjects();
+  }, 500);
+}
+
 function isS3Object(
   obj: S3PseudoFolder | S3ObjectWithFolder,
 ): obj is S3ObjectWithFolder {
   return (obj as S3ObjectWithFolder).folder !== undefined;
 }
 
-/**
- * callback function when an object has been uploaded
- * @param newObject Uploaded object
- */
-function objectUploaded(newObject: S3ObjectMetaInformation) {
-  bucketRepository.fetchBucket(newObject.bucket);
-  const index = objectState.objects.findIndex(
-    (obj) => obj.key === newObject.key,
-  );
-  if (index > -1) {
-    objectState.objects[index] = newObject;
-  } else {
-    objectState.objects.push(newObject);
+function deleteObject(key?: string) {
+  if (key == undefined) {
+    return;
   }
-}
-
-/**
- * callback function when an object has been copied
- * @param copiedObject Uploaded object
- */
-function objectCopied(copiedObject: S3ObjectMetaInformation) {
-  bucketRepository.fetchBucket(copiedObject.bucket);
-  if (copiedObject.bucket === props.bucketName) {
-    objectState.objects.push(copiedObject);
-  }
-}
-
-function deleteObject(key: string) {
   deleteObjectsState.potentialObjectToDelete = key;
   deleteObjectsState.deleteFolder = false;
 }
@@ -389,24 +367,11 @@ function deleteObject(key: string) {
  * @param key Key of the Object
  */
 function confirmedDeleteObject(key: string) {
-  const command = new DeleteObjectCommand({
-    Bucket: props.bucketName,
-    Key: key,
+  objectRepository.deleteObject(props.bucketName, key).then(() => {
+    const splittedKey = key.split("/");
+    deleteObjectsState.deletedItem = splittedKey[splittedKey.length - 1];
+    successToast?.show();
   });
-  client.value
-    .send(command)
-    .then(() => {
-      bucketRepository.fetchBucket(props.bucketName);
-      const splittedKey = key.split("/");
-      deleteObjectsState.deletedItem = splittedKey[splittedKey.length - 1];
-      successToast?.show();
-      objectState.objects = objectState.objects.filter(
-        (obj) => obj.key !== key,
-      );
-    })
-    .catch((err) => {
-      console.error(err);
-    });
 }
 
 /**
@@ -414,12 +379,11 @@ function confirmedDeleteObject(key: string) {
  * @param key Key of the object
  * @param bucket Bucket of the object
  */
-async function downloadObject(key: string, bucket: string) {
-  const command = new GetObjectCommand({
-    Bucket: bucket,
-    Key: key,
-  });
-  const url = await getSignedUrl(client.value, command, { expiresIn: 30 });
+async function downloadObject(bucket: string, key?: string) {
+  if (key == undefined) {
+    return;
+  }
+  const url = await objectRepository.getPresignedUrl(bucket, key);
   //creating an invisible element
   const element = document.createElement("a");
   element.setAttribute("href", url);
@@ -439,29 +403,12 @@ function deleteFolder(folderPath: string) {
  * @param folderPath Path to the folder with a trailing "/", e.g. some/path/to/a/folder/
  */
 function confirmedDeleteFolder(folderPath: string) {
-  const command = new DeleteObjectsCommand({
-    Bucket: props.bucketName,
-    Delete: {
-      Objects: objectState.objects
-        .filter((obj) => obj.key.startsWith(folderPath))
-        .map((obj) => {
-          return { Key: obj.key };
-        }),
-    },
-  });
-  client.value
-    .send(command)
+  objectRepository
+    .deleteObjectsWithPrefix(props.bucketName, folderPath)
     .then(() => {
-      bucketRepository.fetchBucket(props.bucketName);
       const splittedPath = folderPath.split("/");
       deleteObjectsState.deletedItem = splittedPath[splittedPath.length - 2];
       successToast?.show();
-      objectState.objects = objectState.objects.filter(
-        (obj) => !obj.key.startsWith(folderPath),
-      );
-    })
-    .catch((err) => {
-      console.error(err);
     });
 }
 
@@ -541,9 +488,9 @@ function getObjectFileName(key: string): string {
   </nav>
   <!-- Inputs on top -->
   <!-- Search bucket text input -->
-  <div class="row">
-    <div class="col-8">
-      <div class="input-group mt-2 rounded shadow-sm">
+  <div class="d-flex justify-content-between align-items-center">
+    <div class="flex-grow-1 me-2">
+      <div class="input-group rounded shadow-sm">
         <span class="input-group-text" id="objects-search-wrapping"
           ><font-awesome-icon icon="fa-solid fa-magnifying-glass"
         /></span>
@@ -559,7 +506,18 @@ function getObjectFileName(key: string): string {
       </div>
     </div>
     <!-- Upload object button -->
-    <div id="BucketViewButtons" class="col-auto">
+    <div id="BucketViewButtons" class="">
+      <button
+        type="button"
+        class="btn btn-light me-3 tooltip-container border shadow-sm"
+        :disabled="errorLoadingObjects"
+        data-bs-toggle="tooltip"
+        data-bs-title="Refresh Objects"
+        @click="refreshObjects"
+      >
+        <font-awesome-icon icon="fa-solid fa-arrow-rotate-right" />
+        <span class="visually-hidden">Refresh Objects</span>
+      </button>
       <button
         type="button"
         class="btn btn-light me-2 tooltip-container border shadow-sm"
@@ -573,16 +531,14 @@ function getObjectFileName(key: string): string {
       </button>
       <upload-object-modal
         :bucket-name="props.bucketName"
-        :s3-client="client"
         modalID="upload-object-modal"
         :key-prefix="currentSubFolders.join('/')"
         :edit-object-file-name="undefined"
-        @object-created="objectUploaded"
       />
       <!-- Add folder button -->
       <button
         type="button"
-        class="btn btn-light m-2 tooltip-container border shadow-sm"
+        class="btn btn-light me-3 tooltip-container border shadow-sm"
         :disabled="errorLoadingObjects || !writableBucket"
         data-bs-toggle="modal"
         data-bs-title="Create Folder"
@@ -594,17 +550,15 @@ function getObjectFileName(key: string): string {
       </button>
       <create-folder-modal
         :bucket-name="props.bucketName"
-        :s3-client="client"
         modalID="create-folder-modal"
         :key-prefix="currentSubFolders.join('/')"
-        @folder-created="objectUploaded"
       />
       <!-- Add bucket permission button -->
       <button
         v-if="!authStore.foreignUser"
         :hidden="!bucketRepository.permissionFeatureAllowed(props.bucketName)"
         type="button"
-        class="btn btn-light m-2 tooltip-container border shadow-sm"
+        class="btn btn-light me-2 tooltip-container border shadow-sm"
         :disabled="errorLoadingObjects"
         data-bs-toggle="modal"
         data-bs-title="Create Bucket Permission"
@@ -628,7 +582,7 @@ function getObjectFileName(key: string): string {
         v-if="!authStore.foreignUser"
         :hidden="!bucketRepository.permissionFeatureAllowed(props.bucketName)"
         type="button"
-        class="btn btn-light m-2 tooltip-container border shadow-sm"
+        class="btn btn-light tooltip-container border shadow-sm"
         :disabled="errorLoadingObjects"
         data-bs-title="List Bucket Permission"
         data-bs-toggle="modal"
@@ -639,6 +593,7 @@ function getObjectFileName(key: string): string {
       </button>
       <permission-list-modal
         v-if="
+          objectState.loading == false &&
           bucketRepository.ownPermissions[props.bucketName] == undefined &&
           !authStore.foreignUser
         "
@@ -713,7 +668,7 @@ function getObjectFileName(key: string): string {
         </tbody>
         <!-- Table body when showing objects -->
         <tbody v-else>
-          <tr v-for="obj in filteredObjects" :key="obj.key">
+          <tr v-for="obj in filteredObjects" :key="obj.Key">
             <th scope="row" class="text-truncate">
               <!-- Show file name if row is an object -->
               <div v-if="isS3Object(obj)">{{ obj.pseudoFileName }}</div>
@@ -737,12 +692,14 @@ function getObjectFileName(key: string): string {
                 class="date-tooltip"
                 data-bs-toggle="tooltip"
                 :data-bs-title="
-                  dayjs(obj.last_modified).format('DD.MM.YYYY HH:mm:ss')
+                  dayjs(obj.LastModified).format('DD.MM.YYYY HH:mm:ss')
                 "
-                >{{ dayjs(obj.last_modified).fromNow() }}</span
+                >{{ dayjs(obj.LastModified).fromNow() }}</span
               >
             </td>
-            <td>{{ filesize(obj.size, { base: 2, standard: "jedec" }) }}</td>
+            <td>
+              {{ filesize(obj.Size ?? 0, { base: 2, standard: "jedec" }) }}
+            </td>
             <!-- Show buttons with dropdown menu if row is an object -->
             <td class="text-end">
               <div
@@ -753,7 +710,7 @@ function getObjectFileName(key: string): string {
                 <button
                   type="button"
                   class="btn btn-secondary"
-                  @click="downloadObject(obj.key, props.bucketName)"
+                  @click="downloadObject(props.bucketName, obj.Key)"
                   :disabled="!readableBucket"
                 >
                   Download
@@ -786,7 +743,7 @@ function getObjectFileName(key: string): string {
                       :disabled="!writableBucket"
                       data-bs-toggle="modal"
                       data-bs-target="#edit-object-modal"
-                      @click="objectState.editObjectKey = obj.key"
+                      @click="objectState.editObjectKey = obj.Key ?? ''"
                     >
                       Edit
                     </button>
@@ -807,7 +764,7 @@ function getObjectFileName(key: string): string {
                     <button
                       class="dropdown-item text-danger align-middle"
                       type="button"
-                      @click="deleteObject(obj.key)"
+                      @click="deleteObject(obj.Key)"
                       data-bs-toggle="modal"
                       data-bs-target="#delete-object-modal"
                       :disabled="!writableBucket"
@@ -842,19 +799,17 @@ function getObjectFileName(key: string): string {
       </table>
       <upload-object-modal
         :bucket-name="props.bucketName"
-        :s3-client="client"
         modalID="edit-object-modal"
         :key-prefix="currentSubFolders.join('/')"
         :edit-object-file-name="getObjectFileName(objectState.editObjectKey)"
-        @object-created="objectUploaded"
       />
       <copy-object-modal
-        :source-object="objectState.copyObject"
-        :s3-client="client"
+        :src-object="objectState.copyObject"
+        :src-bucket="bucketName"
         modalID="copy-object-modal"
-        @object-copied="objectCopied"
       />
       <object-detail-modal
+        :bucket="bucketName"
         :s3-object="objectState.viewDetailObject"
         modalID="detail-object-modal"
       />
diff --git a/src/views/object-storage/S3KeysView.vue b/src/views/object-storage/S3KeysView.vue
index a9072b88e90fbb975502e71637ef1312eb2788eb..ffade6583732a910ce744c1a132d472383203c85 100644
--- a/src/views/object-storage/S3KeysView.vue
+++ b/src/views/object-storage/S3KeysView.vue
@@ -26,7 +26,7 @@ const allowKeyDeletion = computed<boolean>(() => keyRepository.keys.length > 1);
 
 function fetchKeys() {
   keyRepository
-    .fetchS3Keys(authStore.currentUID, () => (keyState.initialLoading = false))
+    .fetchS3Keys(() => (keyState.initialLoading = false))
     .then((keys) => {
       if (keyState.activeKey >= keys.length) {
         keyState.activeKey = keys.length - 1;
diff --git a/src/views/workflows/ListWorkflowExecutionsView.vue b/src/views/workflows/ListWorkflowExecutionsView.vue
index 51445454d9bae200ba7c345d32e0d9ec6186d3a4..691068cdcbebb185d4a77036c4e4016d984c738d 100644
--- a/src/views/workflows/ListWorkflowExecutionsView.vue
+++ b/src/views/workflows/ListWorkflowExecutionsView.vue
@@ -1,6 +1,6 @@
 <script setup lang="ts">
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
-import { onMounted, reactive, computed } from "vue";
+import { onMounted, reactive, computed, onUnmounted } from "vue";
 import type { WorkflowExecutionOut } from "@/client/workflow";
 import { WorkflowExecutionStatus } from "@/client/workflow";
 import dayjs from "dayjs";
@@ -13,6 +13,7 @@ const workflowRepository = useWorkflowStore();
 const executionRepository = useWorkflowExecutionStore();
 
 let refreshTimeout: NodeJS.Timeout | undefined = undefined;
+let intervalId: NodeJS.Timer | undefined = undefined;
 
 const executionsState = reactive<{
   loading: boolean;
@@ -115,11 +116,26 @@ function cancelWorkflowExecution(executionId: string) {
   executionRepository.cancelExecution(executionId);
 }
 
+function refreshRunningWorkflowExecution() {
+  Promise.all(
+    executionRepository.executions
+      .filter((execution) => workflowExecutionCancelable(execution.status))
+      .map((execution) =>
+        executionRepository.fetchExecution(execution.execution_id),
+      ),
+  );
+}
+
 onMounted(() => {
   workflowRepository.fetchWorkflows();
   updateExecutions();
+  intervalId = setInterval(refreshRunningWorkflowExecution, 5000);
   new Tooltip("#refreshExecutionsButton");
 });
+
+onUnmounted(() => {
+  clearInterval(intervalId);
+});
 </script>
 
 <template>
diff --git a/src/views/workflows/WorkflowView.vue b/src/views/workflows/WorkflowView.vue
index ceea0bad90b512ee882398a19e26bb781f628217..4b668301b2a23a4157d19d3d9fcbfc84870fe3f7 100644
--- a/src/views/workflows/WorkflowView.vue
+++ b/src/views/workflows/WorkflowView.vue
@@ -255,7 +255,7 @@ onMounted(() => {
       v-if="activeVersionModeIds.length > 0"
       class="row align-items-center mb-3 fs-5"
     >
-      <label class="col-sm-1 col-form-label">Mode:</label>
+      <label class="col-sm-1 col-form-label"><b>Mode:</b></label>
       <div class="col-sm-11">
         <select class="form-select w-fit" v-model="workflowState.activeModeId">
           <option