diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 251a04a8c997cca666f1a0f06e7ce86054c9c69a..dc9685d859412869f83356767ff481053032f9ec 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -33,7 +33,7 @@ build:
     paths:
       - dist
 
-build-publish-dev-docker-container-job:
+publish-main-docker-container-job:
   stage: deploy
   image:
     name: gcr.io/kaniko-project/executor:v1.20.0-debug
@@ -50,7 +50,7 @@ build-publish-dev-docker-container-job:
       --destination "${CI_REGISTRY_IMAGE}:main-${CI_COMMIT_SHA}"
       --destination "${CI_REGISTRY_IMAGE}:main-latest"
 
-build-publish-docker-container-job:
+publish-docker-container-job:
   stage: deploy
   image:
     name: gcr.io/kaniko-project/executor:v1.20.0-debug
diff --git a/Dockerfile b/Dockerfile
index 19606527adbac48def6672dd4f1518ac96526048..dda87caaa5cc450b2fbb2bd8d0a3fc1bb37838a2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -10,9 +10,9 @@ RUN npm run build-only
 # production stage
 FROM nginx:stable-alpine as production-stage
 EXPOSE 80
-HEALTHCHECK --interval=30s --timeout=4s CMD curl --head -f http://localhost || exit 1
+HEALTHCHECK --interval=30s --timeout=2s CMD curl --head -f http://localhost || exit 1
+COPY nginx.conf /etc/nginx/conf.d/default.conf
 COPY --from=build-stage /app/dist /usr/share/nginx/html
 COPY --from=build-stage /app/src/assets/env.template.js /tmp
-COPY nginx.conf /etc/nginx/conf.d/default.conf
 
 CMD ["/bin/sh",  "-c",  "envsubst < /tmp/env.template.js > /usr/share/nginx/html/env.js && exec nginx -g 'daemon off;'"]
diff --git a/src/client/resource/services/ResourceVersionService.ts b/src/client/resource/services/ResourceVersionService.ts
index a4b9866b1c6df1f421e2b033f3f65ab822138216..ced4ef7ecdd1a8b80a17db35ff0f9bb0c6040656 100644
--- a/src/client/resource/services/ResourceVersionService.ts
+++ b/src/client/resource/services/ResourceVersionService.ts
@@ -129,11 +129,40 @@ export class ResourceVersionService {
             },
         });
     }
+    /**
+     * Deny synchronization request to cluster
+     * Deny the synchronization request of the resource version to the cluster.
+     *
+     * Permission `resource:sync` required.
+     * @param rid
+     * @param rvid
+     * @returns ResourceVersionOut Successful Response
+     * @throws ApiError
+     */
+    public static resourceVersionResourceVersionSyncDeny(
+        rid: string,
+        rvid: string,
+    ): CancelablePromise<ResourceVersionOut> {
+        return __request(OpenAPI, {
+            method: 'PUT',
+            url: '/resources/{rid}/versions/{rvid}/deny',
+            path: {
+                'rid': rid,
+                'rvid': rvid,
+            },
+            errors: {
+                400: `Error decoding JWT Token`,
+                403: `Not authenticated`,
+                404: `Entity not Found`,
+                422: `Validation Error`,
+            },
+        });
+    }
     /**
      * Synchronize resource version with cluster
      * Synchronize the resource version to the cluster.
      *
-     * Permission `resource:sync` required.
+     * Permission `resource:sync` required and `resource:sync_denied` if the status is `DENIED`.
      * @param rid
      * @param rvid
      * @returns ResourceVersionOut Successful Response
diff --git a/src/client/workflow/models/AnonymizedWorkflowExecution.ts b/src/client/workflow/models/AnonymizedWorkflowExecution.ts
index 8fadc75c02b6cd5d3a1555528b1f341d5ef1c6eb..cc68ea3be9a98c612dae5a8a1fdc646e26b039ac 100644
--- a/src/client/workflow/models/AnonymizedWorkflowExecution.ts
+++ b/src/client/workflow/models/AnonymizedWorkflowExecution.ts
@@ -15,7 +15,7 @@ export type AnonymizedWorkflowExecution = {
     /**
      * ID of the workflow mode this workflow execution ran in
      */
-    workflow_mode_id?: string;
+    workflow_mode_id?: (string | null);
     /**
      * Hash of the git commit
      */
diff --git a/src/client/workflow/models/DocumentationEnum.ts b/src/client/workflow/models/DocumentationEnum.ts
index 2f5b3e3d5833fb1775dd5d45ccef997b2e9b471f..5540c023bc93402dd8d236a41632abe9dbbfe904 100644
--- a/src/client/workflow/models/DocumentationEnum.ts
+++ b/src/client/workflow/models/DocumentationEnum.ts
@@ -8,4 +8,5 @@ export enum DocumentationEnum {
     OUTPUT = 'output',
     CHANGELOG = 'changelog',
     PARAMETER_SCHEMA = 'parameter_schema',
+    CLOWM_INFO = 'clowm_info',
 }
diff --git a/src/client/workflow/models/WorkflowExecutionOut.ts b/src/client/workflow/models/WorkflowExecutionOut.ts
index 2323c17cfb325b5652047fce1d003568f5582cb3..29bf964a4428e93d6330b90678de154661c47766 100644
--- a/src/client/workflow/models/WorkflowExecutionOut.ts
+++ b/src/client/workflow/models/WorkflowExecutionOut.ts
@@ -15,7 +15,7 @@ export type WorkflowExecutionOut = {
     /**
      * ID of the workflow mode this workflow execution runs in
      */
-    mode_id?: string;
+    mode_id?: (string | null);
     /**
      * ID of the workflow execution
      */
@@ -39,7 +39,7 @@ export type WorkflowExecutionOut = {
     /**
      * Id of the workflow
      */
-    workflow_id?: string;
+    workflow_id?: (string | null);
     /**
      * S3 Path where logs and reports are saved.
      */
diff --git a/src/client/workflow/models/WorkflowOut.ts b/src/client/workflow/models/WorkflowOut.ts
index 2946f95b1b600083338889777bde0c71d130cf9b..79bc4687872e3d36d2e962038a848e6f49d1e923 100644
--- a/src/client/workflow/models/WorkflowOut.ts
+++ b/src/client/workflow/models/WorkflowOut.ts
@@ -27,7 +27,7 @@ export type WorkflowOut = {
     /**
      * ID of developer of the workflow
      */
-    developer_id?: string;
+    developer_id?: (string | null);
     /**
      * Flag if the workflow is hosted in a private git repository
      */
diff --git a/src/components/NavbarTop.vue b/src/components/NavbarTop.vue
index 37a3540c874d27e715d88a1b9e3b8e32cb2acbf1..0cb939d9815cc761f00d5390e64aa65ab9ccace3 100644
--- a/src/components/NavbarTop.vue
+++ b/src/components/NavbarTop.vue
@@ -211,7 +211,11 @@ watch(
               class="dropdown-menu shadow m-0"
               aria-labelledby="adminDropdown"
             >
-              <li><a class="dropdown-item disabled" href="#">User</a></li>
+              <li>
+                <router-link class="dropdown-item" :to="{ name: 'admin-users' }"
+                  >Users
+                </router-link>
+              </li>
               <li><a class="dropdown-item disabled" href="#">Bucket</a></li>
               <li><a class="dropdown-item disabled" href="#">Workflow</a></li>
               <li>
diff --git a/src/components/admin/UserRoleMark.vue b/src/components/admin/UserRoleMark.vue
new file mode 100644
index 0000000000000000000000000000000000000000..b0d0a82fd9ad67a9a253a2b8ddfac42988ec4fc7
--- /dev/null
+++ b/src/components/admin/UserRoleMark.vue
@@ -0,0 +1,20 @@
+<script setup lang="ts">
+import { RoleEnum, type User } from "@/client/auth";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
+
+const props = defineProps<{
+  role: RoleEnum;
+  user: User;
+}>();
+</script>
+
+<template>
+  <font-awesome-icon
+    v-if="!props.user.roles?.includes(props.role)"
+    icon="fa-solid fa-check"
+    class="text-success fs-5"
+  />
+  <font-awesome-icon v-else icon="fa-solid fa-xmark" class="text-danger fs-5" />
+</template>
+
+<style scoped></style>
diff --git a/src/components/modals/SearchUserModal.vue b/src/components/modals/SearchUserModal.vue
index 414369508f1be90c59a4dab83446ddddb89450b7..b6396eb488c24adc477b6fc2ce6c575a1e443135 100644
--- a/src/components/modals/SearchUserModal.vue
+++ b/src/components/modals/SearchUserModal.vue
@@ -2,10 +2,8 @@
 import { reactive, ref, watch } from "vue";
 import BootstrapModal from "@/components/modals/BootstrapModal.vue";
 import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
-import { UserService } from "@/client/auth";
 import type { User } from "@/client/auth";
 import { useAuthStore } from "@/stores/users";
-import { useNameStore } from "@/stores/names";
 
 const props = defineProps<{
   modalId: string;
@@ -15,7 +13,6 @@ const props = defineProps<{
 const randomIDSuffix = Math.random().toString(16).substring(2, 8);
 
 const store = useAuthStore();
-const nameStore = useNameStore();
 const textInputElement = ref<HTMLInputElement | undefined>(undefined);
 
 const formState = reactive<{
@@ -63,7 +60,8 @@ function modalShown() {
 
 function searchUser(name: string) {
   formState.error = false;
-  UserService.userListUsers(name)
+  store
+    .fetchUsers(name)
     .then((userSuggestions) => {
       if (props.filterUserSelf) {
         formState.potentialUsers = userSuggestions.filter(
@@ -72,9 +70,6 @@ function searchUser(name: string) {
       } else {
         formState.potentialUsers = userSuggestions;
       }
-      for (const user of userSuggestions) {
-        nameStore.addNameToMapping(user.uid, user.display_name);
-      }
     })
     .catch((err) => {
       formState.error = true;
diff --git a/src/router/adminRoutes.ts b/src/router/adminRoutes.ts
index a7a4384781a425879efc121e17f1c2ea24253195..e146270b70d3204f470c139a12c7299becaed214 100644
--- a/src/router/adminRoutes.ts
+++ b/src/router/adminRoutes.ts
@@ -9,4 +9,12 @@ export const adminRoutes: RouteRecordRaw[] = [
       requiresAdminRole: true,
     },
   },
+  {
+    path: "admin/users",
+    name: "admin-users",
+    component: () => import("../views/admin/AdminUsersView.vue"),
+    meta: {
+      requiresAdminRole: true,
+    },
+  },
 ];
diff --git a/src/stores/names.ts b/src/stores/names.ts
index e2b76aa70e58f926db013beb7b5f106950fd6cdd..f83bb9d2aa6c0af9e08eced3108d2ee8525efaec 100644
--- a/src/stores/names.ts
+++ b/src/stores/names.ts
@@ -9,7 +9,7 @@ export const useNameStore = defineStore({
       nameMapping: Record<string, string>;
     },
   getters: {
-    getName(): (objectID?: string) => string | undefined {
+    getName(): (objectID?: string | null) => string | undefined {
       return (objectID) => {
         if (objectID) {
           return this.nameMapping[objectID] ?? localStorage.getItem(objectID);
diff --git a/src/stores/resources.ts b/src/stores/resources.ts
index 03f153a607db56a037c47928699ce8ca18dc643d..e0377b4617142256c98526fb7a0a875686bbc24a 100644
--- a/src/stores/resources.ts
+++ b/src/stores/resources.ts
@@ -219,30 +219,37 @@ export const useResourceStore = defineStore({
       return ResourceVersionService.resourceVersionResourceVersionSync(
         resourceVersion.resource_id,
         resourceVersion.resource_version_id,
-      ).then((changedVersion) => {
-        if (
-          this.reviewableResourceMapping[changedVersion.resource_id] ==
-          undefined
-        ) {
-          return changedVersion;
-        }
-        const versionIndex = this.reviewableResourceMapping[
-          changedVersion.resource_id
-        ].versions.findIndex(
-          (version) =>
-            version.resource_version_id == changedVersion.resource_version_id,
+      ).then(this._updateReviewableResourceVersion);
+    },
+    denyResource(
+      resourceVersion: ResourceVersionOut,
+    ): Promise<ResourceVersionOut> {
+      return ResourceVersionService.resourceVersionResourceVersionSyncDeny(
+        resourceVersion.resource_id,
+        resourceVersion.resource_version_id,
+      ).then(this._updateReviewableResourceVersion);
+    },
+    _updateReviewableResourceVersion(
+      version: ResourceVersionOut,
+    ): ResourceVersionOut {
+      if (this.reviewableResourceMapping[version.resource_id] == undefined) {
+        return version;
+      }
+      const versionIndex = this.reviewableResourceMapping[
+        version.resource_id
+      ].versions.findIndex(
+        (version) => version.resource_version_id == version.resource_version_id,
+      );
+      if (versionIndex > -1) {
+        this.reviewableResourceMapping[version.resource_id].versions[
+          versionIndex
+        ] = version;
+      } else {
+        this.reviewableResourceMapping[version.resource_id].versions.push(
+          version,
         );
-        if (versionIndex > -1) {
-          this.reviewableResourceMapping[changedVersion.resource_id].versions[
-            versionIndex
-          ] = changedVersion;
-        } else {
-          this.reviewableResourceMapping[
-            changedVersion.resource_id
-          ].versions.push(changedVersion);
-        }
-        return changedVersion;
-      });
+      }
+      return version;
     },
     setLatestResource(
       resourceVersion: ResourceVersionOut,
diff --git a/src/stores/users.ts b/src/stores/users.ts
index 928ed2956df6f5a9d322d219b329c4f0f1b59fe3..0eff50e59fa08c12400596982a060f10d00b5591 100644
--- a/src/stores/users.ts
+++ b/src/stores/users.ts
@@ -125,6 +125,23 @@ export const useAuthStore = defineStore({
       useS3KeyStore().$reset();
       useS3ObjectStore().$reset();
     },
+    fetchUsers(
+      searchString?: string,
+      filterRoles?: RoleEnum[],
+      includeRoles = false,
+    ): Promise<User[]> {
+      return UserService.userListUsers(
+        searchString,
+        filterRoles,
+        includeRoles,
+      ).then((users) => {
+        const nameStore = useNameStore();
+        for (const user of users) {
+          nameStore.addNameToMapping(user.uid, user.display_name);
+        }
+        return users;
+      });
+    },
     async fetchUsernames(uids: string[]): Promise<string[]> {
       const nameStore = useNameStore();
       const filteredIds = uids
diff --git a/src/views/admin/AdminResourcesView.vue b/src/views/admin/AdminResourcesView.vue
index 2906d8507f98ad88efe1b781f3c9941d06bd38f0..095336b74f2cd76e2dec1d12bd3100184a4698c3 100644
--- a/src/views/admin/AdminResourcesView.vue
+++ b/src/views/admin/AdminResourcesView.vue
@@ -117,6 +117,16 @@ function syncToCluster(resourceVersion: ResourceVersionOut) {
     });
 }
 
+function denyResource(resourceVersion: ResourceVersionOut) {
+  resourceState.loading = true;
+  resourceRepository
+    .denyResource(resourceVersion)
+    .then(replaceResourceVersion)
+    .finally(() => {
+      resourceState.loading = false;
+    });
+}
+
 function resetForm() {
   resourceState.maintainerId = "";
   resourceState.searchString = "";
@@ -226,8 +236,8 @@ function resetForm() {
       <tr>
         <td colspan="5" class="text-center fst-italic fw-light">
           <template v-if="resourceState.searched"
-            >No resource found with specified filters</template
-          >
+            >No resource found with specified filters
+          </template>
           <template v-else>Select a filter and search for Resources</template>
         </td>
       </tr>
@@ -306,7 +316,8 @@ function resetForm() {
                         <li
                           v-if="
                             version.status === Status.CLUSTER_DELETED ||
-                            version.status === Status.SYNC_REQUESTED
+                            version.status === Status.SYNC_REQUESTED ||
+                            version.status === Status.DENIED
                           "
                         >
                           <button
@@ -320,6 +331,19 @@ function resetForm() {
                             <span class="ms-1">Sync to Cluster</span>
                           </button>
                         </li>
+                        <li v-if="version.status === Status.SYNC_REQUESTED">
+                          <button
+                            class="dropdown-item"
+                            type="button"
+                            @click="denyResource(version)"
+                          >
+                            <font-awesome-icon
+                              icon="fa-solid fa-xmark"
+                              class="text-danger"
+                            />
+                            <span class="ms-1">Deny Synchronization</span>
+                          </button>
+                        </li>
                         <li
                           v-if="
                             version.status === Status.SYNCHRONIZED ||
@@ -335,7 +359,12 @@ function resetForm() {
                             <span class="ms-1">Delete on Cluster</span>
                           </button>
                         </li>
-                        <li v-if="version.status === Status.CLUSTER_DELETED">
+                        <li
+                          v-if="
+                            version.status === Status.CLUSTER_DELETED ||
+                            version.status === Status.DENIED
+                          "
+                        >
                           <button
                             class="dropdown-item text-danger align-middle"
                             type="button"
diff --git a/src/views/admin/AdminUsersView.vue b/src/views/admin/AdminUsersView.vue
new file mode 100644
index 0000000000000000000000000000000000000000..0f89f1f8945bb095c330c91799b1d131da9cb38b
--- /dev/null
+++ b/src/views/admin/AdminUsersView.vue
@@ -0,0 +1,141 @@
+<script setup lang="ts">
+import { useAuthStore } from "@/stores/users";
+import { reactive } from "vue";
+import { RoleEnum, type User } from "@/client/auth";
+import FontAwesomeIcon from "@/components/FontAwesomeIcon.vue";
+import UserRoleMark from "@/components/admin/UserRoleMark.vue";
+
+const userRepository = useAuthStore();
+
+const userState = reactive<{
+  loading: boolean;
+  users: User[];
+  searchString: string;
+  userRoles: RoleEnum[];
+  inspectUser?: User;
+  searched: boolean;
+}>({
+  loading: false,
+  users: [],
+  searchString: "",
+  userRoles: [],
+  inspectUser: undefined,
+  searched: false,
+});
+
+function searchUsers() {
+  userState.loading = true;
+  userRepository
+    .fetchUsers(
+      userState.searchString ? userState.searchString : undefined,
+      userState.userRoles ? userState.userRoles : undefined,
+      true,
+    )
+    .then((users) => {
+      userState.users = users;
+    })
+    .finally(() => {
+      userState.loading = false;
+      userState.searched = true;
+    });
+}
+</script>
+
+<template>
+  <div
+    class="row m-2 border-bottom mb-4 justify-content-between align-items-center"
+  >
+    <h2>Manage Users</h2>
+  </div>
+  <form @submit.prevent="searchUsers" id="admin-user-search-form">
+    <div class="d-flex justify-content-evenly align-content-center">
+      <div class="mx-2">
+        <label for="admin-user-state-select" class="form-label"
+          >User Roles</label
+        >
+        <select
+          v-model="userState.userRoles"
+          multiple
+          class="form-select mb-4 w-fit"
+          id="admin-user-state-select"
+        >
+          <option v-for="role in Object.values(RoleEnum)" :key="role">
+            {{ role }}
+          </option>
+        </select>
+      </div>
+      <div class="flex-fill mx-2">
+        <label for="admin-user-name-search" class="form-label"
+          >Name of a user</label
+        >
+        <div class="input-group">
+          <div class="input-group-text">
+            <font-awesome-icon icon="fa-solid fa-search" />
+          </div>
+          <input
+            id="admin-user-name-search"
+            type="text"
+            class="form-control"
+            maxlength="32"
+            minlength="3"
+            v-model="userState.searchString"
+            placeholder="Search for user name"
+          />
+        </div>
+      </div>
+    </div>
+    <button
+      type="submit"
+      class="btn btn-primary w-fit"
+      :disabled="userState.loading"
+    >
+      Search
+    </button>
+  </form>
+  <table class="table table-striped align-middle" v-if="userState.users">
+    <thead>
+      <tr>
+        <th scope="col"><b>Name</b></th>
+        <th scope="col">UID</th>
+        <th scope="col" class="text-center">Normal User</th>
+        <th scope="col" class="text-center">Developer</th>
+        <th scope="col" class="text-center">Resource Maintainer</th>
+        <th scope="col" class="text-center">Reviewer</th>
+        <th scope="col" class="text-center">Admin</th>
+      </tr>
+    </thead>
+    <tbody v-if="userState.users.length === 0">
+      <tr>
+        <td colspan="7" class="text-center fst-italic fw-light">
+          <template v-if="userState.searched"
+            >No Users found with specified filters
+          </template>
+          <template v-else>Select a filter and search for Users</template>
+        </td>
+      </tr>
+    </tbody>
+    <tbody v-else>
+      <tr v-for="user in userState.users" :key="user.uid">
+        <th scope="row">{{ user.display_name }}</th>
+        <td>{{ user.uid }}</td>
+        <td class="text-center">
+          <user-role-mark :role="RoleEnum.FOREIGN_USER" :user="user" />
+        </td>
+        <td class="text-center">
+          <user-role-mark :role="RoleEnum.DEVELOPER" :user="user" />
+        </td>
+        <td class="text-center">
+          <user-role-mark :role="RoleEnum.DB_MAINTAINER" :user="user" />
+        </td>
+        <td class="text-center">
+          <user-role-mark :role="RoleEnum.REVIEWER" :user="user" />
+        </td>
+        <td class="text-center">
+          <user-role-mark :role="RoleEnum.ADMINISTRATOR" :user="user" />
+        </td>
+      </tr>
+    </tbody>
+  </table>
+</template>
+
+<style scoped></style>
diff --git a/src/views/resources/ReviewResourceView.vue b/src/views/resources/ReviewResourceView.vue
index 4e9a33a08a34f0fe619d57d0e1ca129f19e75d14..78f678528a10dcbf1b3a1dc995355a91e1194343 100644
--- a/src/views/resources/ReviewResourceView.vue
+++ b/src/views/resources/ReviewResourceView.vue
@@ -55,6 +55,13 @@ function syncResource(resourceVersion: ResourceVersionOut) {
   });
 }
 
+function denyResource(resourceVersion: ResourceVersionOut) {
+  resourceState.sendingRequest = true;
+  resourceRepository.denyResource(resourceVersion).finally(() => {
+    resourceState.sendingRequest = false;
+  });
+}
+
 onMounted(() => {
   fetchResources();
   new Tooltip("#refreshReviewableResourcesButton");
@@ -155,7 +162,12 @@ onMounted(() => {
               >
                 Synchronize
               </button>
-              <button type="button" class="btn btn-danger btn-sm" disabled>
+              <button
+                type="button"
+                class="btn btn-danger btn-sm"
+                @click="denyResource(version)"
+                :disabled="resourceState.sendingRequest"
+              >
                 Deny
               </button>
             </div>
diff --git a/src/views/workflows/ReviewWorkflowsView.vue b/src/views/workflows/ReviewWorkflowsView.vue
index f3235e3727d60333d8ff73aae4b39c752cd7b669..1ff20c478c5d69385d447584a9649908000d6220 100644
--- a/src/views/workflows/ReviewWorkflowsView.vue
+++ b/src/views/workflows/ReviewWorkflowsView.vue
@@ -33,8 +33,8 @@ function updateWorkflowVersionStatus(
     });
 }
 
-function isDefined<T>(argument: T | undefined): argument is T {
-  return argument !== undefined;
+function isDefined<T>(argument: T | undefined | null): argument is T {
+  return argument != undefined;
 }
 
 onMounted(() => {