diff --git a/caosdb-proto b/caosdb-proto
index a66508445c9fe9c4bc3b55eb3bdaff147670bd39..2f3e4ad1cf515450fcfedb300f66198b82122b7e 160000
--- a/caosdb-proto
+++ b/caosdb-proto
@@ -1 +1 @@
-Subproject commit a66508445c9fe9c4bc3b55eb3bdaff147670bd39
+Subproject commit 2f3e4ad1cf515450fcfedb300f66198b82122b7e
diff --git a/src/main/java/org/caosdb/server/accessControl/ACMPermissions.java b/src/main/java/org/caosdb/server/accessControl/ACMPermissions.java
index 84844e892297e68d2ad22cbf5dad935612d1c4f4..264af44456586fc84e4d391047d5f9c18c8cc28a 100644
--- a/src/main/java/org/caosdb/server/accessControl/ACMPermissions.java
+++ b/src/main/java/org/caosdb/server/accessControl/ACMPermissions.java
@@ -1,9 +1,10 @@
 /*
- * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -17,79 +18,269 @@
  *
  * You should have received a copy of the GNU Affero General Public License
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * ** end header
  */
 package org.caosdb.server.accessControl;
 
-public class ACMPermissions {
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import org.caosdb.server.jobs.core.AccessControl;
+import org.caosdb.server.jobs.core.CheckStateTransition;
+import org.caosdb.server.scripting.ScriptingPermissions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ACMPermissions implements Comparable<ACMPermissions> {
+
+  private static Logger LOGGER = LoggerFactory.getLogger(ACMPermissions.class);
+  public static final String USER_PARAMETER = "?USER?";
+  public static final String REALM_PARAMETER = "?REALM?";
+  public static final String ROLE_PARAMETER = "?ROLE?";
+  public static Set<ACMPermissions> ALL = new HashSet<>();
+  public static final ACMPermissions GENERIC_ACM_PERMISSION =
+      new ACMPermissions(
+          "ACM:*",
+          "Permissions to administrate the access controll management system. That includes managing users, roles, and assigning permissions to roles and roles to users.");
+
+  protected final String permission;
+  protected final String description;
+
+  public ACMPermissions(String permission, String description) {
+    if (permission == null) {
+      throw new NullPointerException("Permission must no be null");
+    }
+    this.permission = permission;
+    this.description = description;
+    ALL.add(this);
+  }
+
+  @Override
+  public final boolean equals(Object obj) {
+    if (obj instanceof ACMPermissions) {
+      ACMPermissions that = (ACMPermissions) obj;
+      return this.permission.equals(that.permission);
+    }
+    return false;
+  }
+
+  @Override
+  public final int hashCode() {
+    return permission.hashCode();
+  }
+
+  @Override
+  public final String toString() {
+    return this.permission;
+  }
+
+  public final String getDescription() {
+    return description;
+  }
+
+  public static final class UserPermission extends ACMPermissions {
+
+    public static final ACMPermissions GENERIC_USER_PERMISSION =
+        new ACMPermissions(
+            "ACM:USER:*",
+            "Permissions to manage users, i.e. create, retrieve, update and delete users.");
+
+    public UserPermission(String permission, String description) {
+      super(permission, description);
+    }
+
+    public String toString(String realm) {
+      return toString().replace(REALM_PARAMETER, realm);
+    }
+
+    public String toString(String realm, String user) {
+      return toString(realm).replace(USER_PARAMETER, user);
+    }
+  }
+
+  public static final class RolePermission extends ACMPermissions {
+
+    public static final ACMPermissions GENERIC_ROLE_PERMISSION =
+        new ACMPermissions(
+            "ACM:ROLE:*",
+            "Permissions to manage roles, i.e. create, retrieve, update and delete roles and assign them to users.");
+
+    public RolePermission(String permission, String description) {
+      super(permission, description);
+    }
+
+    public String toString(String role) {
+      return toString().replace(ROLE_PARAMETER, role);
+    }
+  }
+
+  public static final String PERMISSION_ACCESS_SERVER_PROPERTIES =
+      new ACMPermissions("ACCESS_SERVER_PROPERTIES", "Permission to read the server properties.")
+          .toString();
+
+  @Deprecated
+  public static final String PERMISSION_RETRIEVE_SERVERLOGS =
+      new ACMPermissions("SERVERLOGS:RETRIEVE", "Permission to read the server logs. (DEPRECATED)")
+          .toString();
 
-  public static final String PERMISSION_ACCESS_SERVER_PROPERTIES = "ACCESS_SERVER_PROPERTIES";
-  public static final String PERMISSION_RETRIEVE_SERVERLOGS = "SERVERLOGS:RETRIEVE";
+  private static UserPermission retrieve_user_roles =
+      new UserPermission(
+          "ACM:USER:RETRIEVE:ROLES:" + REALM_PARAMETER + ":" + USER_PARAMETER,
+          "Permission to retrieve the roles of a user");
 
   public static final String PERMISSION_RETRIEVE_USER_ROLES(
       final String realm, final String username) {
-    return "ACM:USER:RETRIEVE:ROLES:" + realm + ":" + username;
+    return retrieve_user_roles.toString(realm, username);
   }
 
+  private static UserPermission retrieve_user_info =
+      new UserPermission(
+          "ACM:USER:RETRIEVE:INFO:" + REALM_PARAMETER + ":" + USER_PARAMETER,
+          "Permission to retrieve the user info (email, entity, status)");
+
   public static final String PERMISSION_RETRIEVE_USER_INFO(
       final String realm, final String username) {
-    return "ACM:USER:RETRIEVE:INFO:" + realm + ":" + username;
+    return retrieve_user_info.toString(realm, username);
   }
 
+  private static UserPermission delete_user =
+      new UserPermission(
+          "ACM:USER:DELETE:" + REALM_PARAMETER + ":" + USER_PARAMETER,
+          "Permission to delete a user");
+
   public static String PERMISSION_DELETE_USER(final String realm, final String username) {
-    return "ACM:USER:DELETE:" + realm + ":" + username;
+    return delete_user.toString(realm, username);
   }
 
+  private static final UserPermission insert_user =
+      new UserPermission(
+          "ACM:USER:INSERT:" + REALM_PARAMETER, "Permission to create a user in the given realm");
+
   public static String PERMISSION_INSERT_USER(final String realm) {
-    return "ACM:USER:INSERT:" + realm;
+    return insert_user.toString(realm);
   }
 
+  private static final UserPermission update_user_password =
+      new UserPermission(
+          "ACM:USER:UPDATE_PASSWORD:" + REALM_PARAMETER + ":" + USER_PARAMETER,
+          "Permission to set the password of a user.");
+
   public static String PERMISSION_UPDATE_USER_PASSWORD(final String realm, final String username) {
-    return "ACM:USER:UPDATE_PASSWORD:" + realm + ":" + username;
+    return update_user_password.toString(realm, username);
   }
 
+  private static final UserPermission update_user_email =
+      new UserPermission(
+          "ACM:USER:UPDATE:EMAIL:" + REALM_PARAMETER + ":" + USER_PARAMETER,
+          "Permission to update the email address of a user.");
+
   public static String PERMISSION_UPDATE_USER_EMAIL(final String realm, final String username) {
-    return "ACM:USER:UPDATE:EMAIL:" + realm + ":" + username;
+    return update_user_email.toString(realm, username);
   }
 
+  private static final UserPermission update_user_status =
+      new UserPermission(
+          "ACM:USER:UPDATE:STATUS:" + REALM_PARAMETER + ":" + USER_PARAMETER,
+          "Permission to update the status of a user, i.e. marking them as ACTIVE or INACTIVE.");
+
   public static String PERMISSION_UPDATE_USER_STATUS(final String realm, final String username) {
-    return "ACM:USER:UPDATE:STATUS:" + realm + ":" + username;
+    return update_user_status.toString(realm, username);
   }
 
+  private static final UserPermission update_user_entity =
+      new UserPermission(
+          "ACM:USER:UPDATE:ENTITY:" + REALM_PARAMETER + ":" + USER_PARAMETER,
+          "Permission to set the entity which is associated with a user.");
+
   public static String PERMISSION_UPDATE_USER_ENTITY(final String realm, final String username) {
-    return "ACM:USER:UPDATE:ENTITY:" + realm + ":" + username;
+    return update_user_entity.toString(realm, username);
   }
 
+  private static final UserPermission update_user_roles =
+      new UserPermission(
+          "ACM:USER:UPDATE:ROLES:" + REALM_PARAMETER + ":" + USER_PARAMETER,
+          "Permission to change the roles of a user.");
+
   public static String PERMISSION_UPDATE_USER_ROLES(final String realm, final String username) {
-    return "ACM:USER:UPDATE:ROLES:" + realm + ":" + username;
+    return update_user_roles.toString(realm, username);
   }
 
+  private static final RolePermission insert_role =
+      new RolePermission("ACM:ROLE:INSERT", "Permission to create a new role.");
+
   public static String PERMISSION_INSERT_ROLE() {
-    return "ACM:ROLE:INSERT";
+    return insert_role.toString();
   }
 
+  private static final RolePermission update_role_description =
+      new RolePermission(
+          "ACM:ROLE:UPDATE:DESCRIPTION:" + ROLE_PARAMETER,
+          "Permission to update the description of a role.");
+
   public static String PERMISSION_UPDATE_ROLE_DESCRIPTION(final String role) {
-    return "ACM:ROLE:UPDATE:DESCRIPTION:" + role;
+    return update_role_description.toString(role);
   }
 
+  private static final RolePermission retrieve_role_description =
+      new RolePermission(
+          "ACM:ROLE:RETRIEVE:DESCRIPTION:" + ROLE_PARAMETER,
+          "Permission to retrieve the description of a role.");
+
   public static String PERMISSION_RETRIEVE_ROLE_DESCRIPTION(final String role) {
-    return "ACM:ROLE:RETRIEVE:DESCRIPTION:" + role;
+    return retrieve_role_description.toString(role);
   }
 
+  private static final RolePermission delete_role =
+      new RolePermission("ACM:ROLE:DELETE:" + ROLE_PARAMETER, "Permission to delete a role.");
+
   public static String PERMISSION_DELETE_ROLE(final String role) {
-    return "ACM:ROLE:DELETE:" + role;
+    return delete_role.toString(role);
   }
 
+  private static final RolePermission update_role_permissions =
+      new RolePermission(
+          "ACM:ROLE:UPDATE:PERMISSIONS:" + ROLE_PARAMETER,
+          "Permission to set the permissions of a role.");
+
   public static String PERMISSION_UPDATE_ROLE_PERMISSIONS(final String role) {
-    return "ACM:ROLE:UPDATE:PERMISSIONS:" + role;
+    return update_role_permissions.toString(role);
   }
 
+  private static final RolePermission retrieve_role_permissions =
+      new RolePermission(
+          "ACM:ROLE:RETRIEVE:PERMISSIONS:" + ROLE_PARAMETER,
+          "Permission to read the permissions of a role.");
+
   public static String PERMISSION_RETRIEVE_ROLE_PERMISSIONS(final String role) {
-    return "ACM:ROLE:RETRIEVE:PERMISSIONS:" + role;
+    return retrieve_role_permissions.toString(role);
   }
 
+  private static final RolePermission assign_role =
+      new RolePermission(
+          "ACM:ROLE:ASSIGN:" + ROLE_PARAMETER, "Permission to assign a role (to a user).");
+
   public static String PERMISSION_ASSIGN_ROLE(final String role) {
-    return "ACM:ROLE:ASSIGN:" + role;
+    return assign_role.toString(role);
+  }
+
+  static {
+    // trigger adding all permissions to ALL
+    LOGGER.debug("Register permissions: ", ScriptingPermissions.PERMISSION_EXECUTION("*"));
+    LOGGER.debug("Register permissions: ", CheckStateTransition.STATE_PERMISSIONS.toString());
+    LOGGER.debug(
+        "Register permissions: ", CheckStateTransition.PERMISSION_STATE_FORCE_FINAL.toString());
+    LOGGER.debug("Register permissions: ", AccessControl.TRANSACTION_PERMISSIONS.toString());
+  }
+
+  @Override
+  public int compareTo(ACMPermissions that) {
+    return this.toString().compareToIgnoreCase(that.toString());
+  }
+
+  public static List<ACMPermissions> getAll() {
+    LinkedList<ACMPermissions> result = new LinkedList<>(ALL);
+    Collections.sort(result);
+    return result;
   }
 }
diff --git a/src/main/java/org/caosdb/server/accessControl/AnonymousRealm.java b/src/main/java/org/caosdb/server/accessControl/AnonymousRealm.java
index 006400bf649619390d5eaa642642a73b6a94f337..92b37c0ec15b110670a55ea46b6efa1a418bb597 100644
--- a/src/main/java/org/caosdb/server/accessControl/AnonymousRealm.java
+++ b/src/main/java/org/caosdb/server/accessControl/AnonymousRealm.java
@@ -27,12 +27,19 @@ import org.apache.shiro.authc.AuthenticationToken;
 import org.apache.shiro.authc.SimpleAuthenticationInfo;
 import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher;
 import org.apache.shiro.realm.AuthenticatingRealm;
+import org.caosdb.server.CaosDBServer;
+import org.caosdb.server.ServerProperties;
 
 public class AnonymousRealm extends AuthenticatingRealm {
 
   @Override
   protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
-    return new SimpleAuthenticationInfo(token.getPrincipal(), null, getName());
+
+    if (CaosDBServer.getServerProperty(ServerProperties.KEY_AUTH_OPTIONAL)
+        .equalsIgnoreCase("true")) {
+      return new SimpleAuthenticationInfo(token.getPrincipal(), null, getName());
+    }
+    return null;
   }
 
   public AnonymousRealm() {
diff --git a/src/main/java/org/caosdb/server/accessControl/Principal.java b/src/main/java/org/caosdb/server/accessControl/Principal.java
index fc96fb99670b51d0a128710722e2c10effed152a..7d4144557d89ddf90ec99edb190abb66ec0119aa 100644
--- a/src/main/java/org/caosdb/server/accessControl/Principal.java
+++ b/src/main/java/org/caosdb/server/accessControl/Principal.java
@@ -88,6 +88,6 @@ public class Principal implements ResponsibleAgent {
 
   @Override
   public String toString() {
-    return "[[" + this.realm + "]]" + this.username;
+    return this.username + REALM_SEPARATOR + this.realm;
   }
 }
diff --git a/src/main/java/org/caosdb/server/accessControl/SessionTokenRealm.java b/src/main/java/org/caosdb/server/accessControl/SessionTokenRealm.java
index d78ffb405a8470a005da54736e25fbd69340b7e5..270565be3dd8fc25408a1d7992638ef24bcfaa1c 100644
--- a/src/main/java/org/caosdb/server/accessControl/SessionTokenRealm.java
+++ b/src/main/java/org/caosdb/server/accessControl/SessionTokenRealm.java
@@ -36,7 +36,7 @@ public class SessionTokenRealm extends AuthenticatingRealm {
     final SelfValidatingAuthenticationToken sessionToken =
         (SelfValidatingAuthenticationToken) token;
 
-    if (sessionToken.isValid()) {
+    if (sessionToken.isValid() && UserSources.isActive(sessionToken)) {
       return new SimpleAuthenticationInfo(sessionToken, null, getName());
     }
     return null;
diff --git a/src/main/java/org/caosdb/server/accessControl/UserSources.java b/src/main/java/org/caosdb/server/accessControl/UserSources.java
index 760c3e901d85c863504d1d2732083656f4502194..ad6173618770b3093e3e64d16791999b893a1a65 100644
--- a/src/main/java/org/caosdb/server/accessControl/UserSources.java
+++ b/src/main/java/org/caosdb/server/accessControl/UserSources.java
@@ -92,6 +92,11 @@ public class UserSources extends HashMap<String, UserSource> {
     if (principal.getRealm().equals(OneTimeAuthenticationToken.REALM_NAME)) {
       return true;
     }
+    if (principal.toString().equals(AnonymousAuthenticationToken.PRINCIPAL.toString())
+        && CaosDBServer.getServerProperty(ServerProperties.KEY_AUTH_OPTIONAL)
+            .equalsIgnoreCase("true")) {
+      return true;
+    }
     UserSource userSource = instance.get(principal.getRealm());
     if (userSource != null) {
       return userSource.isUserExisting(principal.getUsername());
@@ -315,4 +320,17 @@ public class UserSources extends HashMap<String, UserSource> {
       throw new AuthenticationException(e);
     }
   }
+
+  public static boolean isActive(Principal principal) {
+    if (principal.getRealm().equals(OneTimeAuthenticationToken.REALM_NAME)) {
+      return true;
+    }
+    if (principal.getUsername().equals(AnonymousAuthenticationToken.PRINCIPAL.getUsername())
+        && principal.getRealm().equals(AnonymousAuthenticationToken.PRINCIPAL.getRealm())
+        && CaosDBServer.getServerProperty(ServerProperties.KEY_AUTH_OPTIONAL)
+            .equalsIgnoreCase("true")) {
+      return true;
+    }
+    return isActive(principal.getRealm(), principal.getUsername());
+  }
 }
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLHelper.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLHelper.java
index b1f445e2b8ad2d79b756e1dd662296d8e6c9a49c..568e0e53a7b80a76c533229e13c6700c4c4ff47f 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLHelper.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLHelper.java
@@ -35,7 +35,7 @@ import org.caosdb.server.accessControl.Principal;
 import org.caosdb.server.database.misc.DBHelper;
 import org.caosdb.server.transaction.ChecksumUpdater;
 import org.caosdb.server.transaction.TransactionInterface;
-import org.caosdb.server.transaction.WriteTransaction;
+import org.caosdb.server.transaction.WriteTransactionInterface;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -55,7 +55,7 @@ public class MySQLHelper implements DBHelper {
    *
    * <p>In the database, this adds a row to the transaction table with SRID, user and timestamp.
    */
-  public void initTransaction(Connection connection, WriteTransaction transaction)
+  public void initTransaction(Connection connection, WriteTransactionInterface transaction)
       throws SQLException {
     try (CallableStatement call = connection.prepareCall("CALL set_transaction(?,?,?,?,?)")) {
 
@@ -86,10 +86,10 @@ public class MySQLHelper implements DBHelper {
     if (transaction instanceof ChecksumUpdater) {
       connection.setReadOnly(false);
       connection.setAutoCommit(false);
-    } else if (transaction instanceof WriteTransaction) {
+    } else if (transaction instanceof WriteTransactionInterface) {
       connection.setReadOnly(false);
       connection.setAutoCommit(false);
-      initTransaction(connection, (WriteTransaction) transaction);
+      initTransaction(connection, (WriteTransactionInterface) transaction);
     } else {
       connection.setReadOnly(false);
       connection.setAutoCommit(true);
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveRole.java b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveRole.java
index 1e855663590d85610860fcee246a4f1ed78a31b2..f4059832095acc38d3c65b828412c09cc58b3194 100644
--- a/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveRole.java
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/RetrieveRole.java
@@ -24,6 +24,7 @@
  */
 package org.caosdb.server.database.backend.transaction;
 
+import java.util.Set;
 import org.apache.commons.jcs.access.behavior.ICacheAccess;
 import org.caosdb.server.accessControl.Role;
 import org.caosdb.server.caching.Cache;
@@ -66,4 +67,8 @@ public class RetrieveRole extends CacheableBackendTransaction<String, Role> {
   public static void removeCached(final String name) {
     cache.remove(name);
   }
+
+  public static void removeCached(Set<String> roles) {
+    roles.forEach(RetrieveRole::removeCached);
+  }
 }
diff --git a/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java b/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java
index cd8e1b8791fa7ceafcc7ccc96c3d915b173fe637..88429828f27ac843903cfcfc801847c35eedb607 100644
--- a/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java
+++ b/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java
@@ -230,23 +230,13 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS
 
   private Iterable<PermissionDescription> listKnownPermissions() {
     List<PermissionDescription> result = new LinkedList<>();
-    result.add(
-        PermissionDescription.newBuilder()
-            .setPermission(ACMPermissions.PERMISSION_INSERT_ROLE())
-            .setDescription("Create a new user role.")
-            .build());
-    result.add(
-        PermissionDescription.newBuilder()
-            .setPermission(ACMPermissions.PERMISSION_INSERT_USER("?REALM?"))
-            .setDescription("Create a new user in the given realm.")
-            .build());
-    result.add(
-        PermissionDescription.newBuilder()
-            .setPermission(ACMPermissions.PERMISSION_ACCESS_SERVER_PROPERTIES)
-            .setDescription(
-                "Read and set server properties on the fly when the server is in debug mode.")
-            .build());
-    // TODO
+    for (ACMPermissions p : ACMPermissions.getAll()) {
+      result.add(
+          PermissionDescription.newBuilder()
+              .setPermission(p.toString())
+              .setDescription(p.getDescription())
+              .build());
+    }
     return result;
   }
 
diff --git a/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java b/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java
index 4bd8f2950003e7bec8eae88bf51ed454d8b6b705..ce8a62210d3c8e849397600fc112cd3103f15df4 100644
--- a/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java
+++ b/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java
@@ -124,14 +124,6 @@ public class AuthInterceptor implements ServerInterceptor {
       final Metadata headers,
       final ServerCallHandler<ReqT, RespT> next) {
     ThreadContext.remove();
-    Subject user = SecurityUtils.getSubject();
-    System.out.println(
-        "interceptCall: "
-            + Long.toString(Thread.currentThread().getId())
-            + " "
-            + Thread.currentThread().getName()
-            + " subject: "
-            + user.toString());
 
     String authentication = headers.get(AUTHENTICATION_HEADER);
     if (authentication == null) {
diff --git a/src/main/java/org/caosdb/server/grpc/CaosDBToGrpcConverters.java b/src/main/java/org/caosdb/server/grpc/CaosDBToGrpcConverters.java
index 6d1beea5de3ed107d45e03fe6461e9999402b3a2..913f677f20124a602fed834ba7738941c928e89e 100644
--- a/src/main/java/org/caosdb/server/grpc/CaosDBToGrpcConverters.java
+++ b/src/main/java/org/caosdb/server/grpc/CaosDBToGrpcConverters.java
@@ -31,6 +31,7 @@ import org.caosdb.api.entity.v1.Entity;
 import org.caosdb.api.entity.v1.Entity.Builder;
 import org.caosdb.api.entity.v1.EntityACL;
 import org.caosdb.api.entity.v1.EntityPermissionRule;
+import org.caosdb.api.entity.v1.EntityPermissionRuleCapability;
 import org.caosdb.api.entity.v1.EntityResponse;
 import org.caosdb.api.entity.v1.EntityRole;
 import org.caosdb.api.entity.v1.Importance;
@@ -473,24 +474,45 @@ public class CaosDBToGrpcConverters {
     EntityACL.Builder builder = EntityACL.newBuilder();
     builder.setId(e.getId().toString());
     if (e.hasEntityACL()) {
-      builder.addAllRules(convert(e.getEntityACL()));
+      builder.addAllRules(convert(e.getEntityACL(), true));
     }
-    builder.addAllRules(convert(org.caosdb.server.permissions.EntityACL.GLOBAL_PERMISSIONS));
+    builder.addAllRules(convert(org.caosdb.server.permissions.EntityACL.GLOBAL_PERMISSIONS, false));
+    builder.addAllPermissions(getCurrentACLPermissions(e));
     // TODO errors?
     return builder.build();
   }
 
+  private Iterable<? extends org.caosdb.api.entity.v1.EntityPermission> getCurrentACLPermissions(
+      EntityInterface e) {
+    List<org.caosdb.api.entity.v1.EntityPermission> result = new LinkedList<>();
+    if (e.hasPermission(EntityPermission.EDIT_ACL)) {
+      org.caosdb.api.entity.v1.EntityPermission.Builder builder =
+          org.caosdb.api.entity.v1.EntityPermission.newBuilder();
+      result.add(builder.setName(EntityPermission.EDIT_ACL.getShortName()).build());
+    }
+    if (e.hasPermission(EntityPermission.EDIT_PRIORITY_ACL)) {
+      org.caosdb.api.entity.v1.EntityPermission.Builder builder =
+          org.caosdb.api.entity.v1.EntityPermission.newBuilder();
+      result.add(builder.setName(EntityPermission.EDIT_PRIORITY_ACL.getShortName()).build());
+    }
+    return result;
+  }
+
   private Iterable<? extends EntityPermissionRule> convert(
-      org.caosdb.server.permissions.EntityACL entityACL) {
+      org.caosdb.server.permissions.EntityACL entityACL, boolean deletable) {
     List<EntityPermissionRule> result = new LinkedList<>();
     for (EntityACI aci : entityACL.getRules()) {
-      result.add(
+      EntityPermissionRule.Builder builder =
           EntityPermissionRule.newBuilder()
               .setGrant(aci.isGrant())
               .setPriority(aci.isPriority())
               .setRole(aci.getResponsibleAgent().toString())
-              .addAllPermissions(convert(aci))
-              .build());
+              .addAllPermissions(convert(aci));
+      if (deletable) {
+        builder.addCapabilities(
+            EntityPermissionRuleCapability.ENTITY_PERMISSION_RULE_CAPABILITY_DELETE);
+      }
+      result.add(builder.build());
     }
     return result;
   }
diff --git a/src/main/java/org/caosdb/server/grpc/GrpcToCaosDBConverters.java b/src/main/java/org/caosdb/server/grpc/GrpcToCaosDBConverters.java
index 5ec896fd44899cd59520f2f45c6ceec6c55042a5..5420a8d8e809194ce2dbe894faaded794cc3a632 100644
--- a/src/main/java/org/caosdb/server/grpc/GrpcToCaosDBConverters.java
+++ b/src/main/java/org/caosdb/server/grpc/GrpcToCaosDBConverters.java
@@ -333,9 +333,16 @@ public class GrpcToCaosDBConverters {
   }
 
   private EntityInterface convert(EntityACL acl) {
-    UpdateEntity result = new UpdateEntity(Integer.parseInt(acl.getId()), null);
-    result.setEntityACL(convertAcl(acl));
-    return result;
+    try {
+      Integer id = getId(acl.getId());
+      UpdateEntity result = new UpdateEntity(id, null);
+      result.setEntityACL(convertAcl(acl));
+      return result;
+    } catch (NumberFormatException exc) {
+      UpdateEntity result = new UpdateEntity(null, null);
+      result.addError(ServerMessages.ENTITY_DOES_NOT_EXIST);
+      return result;
+    }
   }
 
   private org.caosdb.server.permissions.EntityACL convertAcl(EntityACL acl) {
diff --git a/src/main/java/org/caosdb/server/jobs/core/AccessControl.java b/src/main/java/org/caosdb/server/jobs/core/AccessControl.java
index 408384f416e1249501d5383ea1028ced56914ded..dc596e50883dd5cd2210332b7cbab954a1e76c92 100644
--- a/src/main/java/org/caosdb/server/jobs/core/AccessControl.java
+++ b/src/main/java/org/caosdb/server/jobs/core/AccessControl.java
@@ -24,6 +24,7 @@ package org.caosdb.server.jobs.core;
 
 import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.subject.Subject;
+import org.caosdb.server.accessControl.ACMPermissions;
 import org.caosdb.server.database.backend.transaction.RetrieveSparseEntity;
 import org.caosdb.server.entity.EntityInterface;
 import org.caosdb.server.entity.wrapper.Parent;
@@ -37,12 +38,47 @@ import org.caosdb.server.utils.ServerMessages;
 @JobAnnotation(stage = TransactionStage.INIT)
 public class AccessControl extends ContainerJob {
 
+  public static class TransactionPermission extends ACMPermissions {
+
+    public static final String ENTITY_ROLE_PARAMETER = "?ENTITY_ROLE?";
+
+    public TransactionPermission(String permission, String description) {
+      super(permission, description);
+    }
+
+    public final String toString(String entityRole) {
+      return toString().replace(ENTITY_ROLE_PARAMETER, entityRole);
+    }
+
+    public final String toString(String transaction, String entityRole) {
+      return "TRANSACTION:" + transaction + (entityRole != null ? (":" + entityRole) : "");
+    }
+  }
+
+  public static final TransactionPermission TRANSACTION_PERMISSIONS =
+      new TransactionPermission(
+          "TRANSACTION:*",
+          "Permission to execute any writable transaction. This permission only allows to execute these transactions in general. The necessary entities permissions are not implied.");
+  public static final TransactionPermission UPDATE =
+      new TransactionPermission(
+          "TRANSACTION:UPDATE:" + TransactionPermission.ENTITY_ROLE_PARAMETER,
+          "Permission to update entities of a given role (e.g. Record, File, RecordType, or Property).");
+  public static final TransactionPermission DELETE =
+      new TransactionPermission(
+          "TRANSACTION:DELETE:" + TransactionPermission.ENTITY_ROLE_PARAMETER,
+          "Permission to delete entities of a given role (e.g. Record, File, RecordType, or Property).");
+  public static final TransactionPermission INSERT =
+      new TransactionPermission(
+          "TRANSACTION:INSERT:" + TransactionPermission.ENTITY_ROLE_PARAMETER,
+          "Permission to insert entities of a given role (e.g. Record, File, RecordType, or Property).");
+
   @Override
   protected void run() {
     final Subject subject = SecurityUtils.getSubject();
 
     // subject has complete permissions for this kind of transaction
-    if (subject.isPermitted("TRANSACTION:" + getTransaction().getClass().getSimpleName())) {
+    if (subject.isPermitted(
+        TRANSACTION_PERMISSIONS.toString(getTransaction().getClass().getSimpleName(), null))) {
       return;
     }
 
@@ -54,10 +90,8 @@ public class AccessControl extends ContainerJob {
 
       // per role permission
       if (subject.isPermitted(
-          "TRANSACTION:"
-              + getTransaction().getClass().getSimpleName()
-              + ":"
-              + e.getRole().toString())) {
+          TRANSACTION_PERMISSIONS.toString(
+              getTransaction().getClass().getSimpleName(), e.getRole().toString()))) {
         continue;
       }
 
diff --git a/src/main/java/org/caosdb/server/jobs/core/CheckStateTransition.java b/src/main/java/org/caosdb/server/jobs/core/CheckStateTransition.java
index 1a159910044a079baba3b64c62176883ad31b8ef..fa21ed3a72c22237b2d10a51b566e9a753f01480 100644
--- a/src/main/java/org/caosdb/server/jobs/core/CheckStateTransition.java
+++ b/src/main/java/org/caosdb/server/jobs/core/CheckStateTransition.java
@@ -22,6 +22,7 @@ package org.caosdb.server.jobs.core;
 
 import java.util.Map;
 import org.apache.shiro.authz.AuthorizationException;
+import org.caosdb.server.accessControl.ACMPermissions;
 import org.caosdb.server.entity.DeleteEntity;
 import org.caosdb.server.entity.Message;
 import org.caosdb.server.entity.Message.MessageType;
@@ -42,9 +43,31 @@ import org.caosdb.server.utils.ServerMessages;
 @JobAnnotation(stage = TransactionStage.POST_CHECK, transaction = WriteTransaction.class)
 public class CheckStateTransition extends EntityStateJob {
 
-  private static final String PERMISSION_STATE_FORCE_FINAL = "STATE:FORCE:FINAL";
-  private static final String PERMISSION_STATE_UNASSIGN = "STATE:UNASSIGN:";
-  private static final String PERMISSION_STATE_ASSIGN = "STATE:ASSIGN:";
+  public static final class StateModelPermission extends ACMPermissions {
+
+    public static final String STATE_MODEL_PARAMETER = "?STATE_MODEL?";
+
+    public StateModelPermission(String permission, String description) {
+      super(permission, description);
+    }
+
+    public final String toString(String state_model) {
+      return toString().replace(STATE_MODEL_PARAMETER, state_model);
+    }
+  }
+
+  public static final StateModelPermission PERMISSION_STATE_FORCE_FINAL =
+      new StateModelPermission(
+          "STATE:FORCE:FINAL",
+          "Permission to force to leave a state models specified life-cycle even though the currrent state isn't a final state in the that model.");
+  public static final StateModelPermission PERMISSION_STATE_UNASSIGN =
+      new StateModelPermission(
+          "STATE:UNASSIGN:" + StateModelPermission.STATE_MODEL_PARAMETER,
+          "Permission to unassign a state model.");
+  public static final StateModelPermission PERMISSION_STATE_ASSIGN =
+      new StateModelPermission(
+          "STATE:ASSIGN:" + StateModelPermission.STATE_MODEL_PARAMETER,
+          "Permission to assign a state model.");
   private static final Message TRANSITION_NOT_ALLOWED =
       new Message(MessageType.Error, "Transition not allowed.");
   private static final Message INITIAL_STATE_NOT_ALLOWED =
@@ -167,12 +190,12 @@ public class CheckStateTransition extends EntityStateJob {
   private void checkFinalState(State oldState) throws Message {
     if (!oldState.isFinal()) {
       if (isForceFinal()) {
-        getUser().checkPermission(PERMISSION_STATE_FORCE_FINAL);
+        getUser().checkPermission(PERMISSION_STATE_FORCE_FINAL.toString());
       } else {
         throw FINAL_STATE_NOT_ALLOWED;
       }
     }
-    getUser().checkPermission(PERMISSION_STATE_UNASSIGN + oldState.getStateModelName());
+    getUser().checkPermission(PERMISSION_STATE_UNASSIGN.toString(oldState.getStateModelName()));
   }
 
   /**
@@ -185,7 +208,7 @@ public class CheckStateTransition extends EntityStateJob {
     if (!newState.isInitial()) {
       throw INITIAL_STATE_NOT_ALLOWED;
     }
-    getUser().checkPermission(PERMISSION_STATE_ASSIGN + newState.getStateModelName());
+    getUser().checkPermission(PERMISSION_STATE_ASSIGN.toString(newState.getStateModelName()));
   }
 
   private boolean isForceFinal() {
diff --git a/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java b/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java
index e2f59a8827a682df508a26176a45a9fc1a874b4e..16fe593654aad760edc2b8efc2e6889222bfa78e 100644
--- a/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java
+++ b/src/main/java/org/caosdb/server/jobs/core/EntityStateJob.java
@@ -33,6 +33,7 @@ import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
 import org.apache.shiro.subject.Subject;
+import org.caosdb.server.accessControl.ACMPermissions;
 import org.caosdb.server.database.exceptions.EntityDoesNotExistException;
 import org.caosdb.server.datatype.AbstractCollectionDatatype;
 import org.caosdb.server.datatype.CollectionValue;
@@ -82,6 +83,19 @@ import org.jdom2.Element;
  */
 public abstract class EntityStateJob extends EntityJob {
 
+  public static final class TransitionPermission extends ACMPermissions {
+
+    public static final String TRANSITION_PARAMETER = "?TRANSITION?";
+
+    public TransitionPermission(String permission, String description) {
+      super(permission, description);
+    }
+
+    public final String toString(String transition) {
+      return toString().replace(TRANSITION_PARAMETER, transition);
+    }
+  }
+
   protected static final String SERVER_PROPERTY_EXT_ENTITY_STATE = "EXT_ENTITY_STATE";
 
   public static final String TO_STATE_PROPERTY_NAME = "to";
@@ -102,7 +116,13 @@ public abstract class EntityStateJob extends EntityJob {
   public static final String STATE_ATTRIBUTE_DESCRIPTION = "description";
   public static final String STATE_ATTRIBUTE_ID = "id";
   public static final String ENTITY_STATE_ROLE_MARKER = "?STATE?";
-  public static final String PERMISSION_STATE_TRANSION = "STATE:TRANSITION:";
+  public static final ACMPermissions STATE_PERMISSIONS =
+      new ACMPermissions(
+          "STATE:*", "Permissions to manage state models and the states of entities.");
+  public static final TransitionPermission PERMISSION_STATE_TRANSION =
+      new TransitionPermission(
+          "STATE:TRANSITION:" + TransitionPermission.TRANSITION_PARAMETER,
+          "Permission to initiate a transition.");
 
   public static final Message STATE_MODEL_NOT_FOUND =
       new Message(MessageType.Error, "StateModel not found.");
@@ -253,7 +273,7 @@ public abstract class EntityStateJob extends EntityJob {
     }
 
     public boolean isPermitted(Subject user) {
-      return user.isPermitted(PERMISSION_STATE_TRANSION + this.name);
+      return user.isPermitted(PERMISSION_STATE_TRANSION.toString(this.name));
     }
   }
 
diff --git a/src/main/java/org/caosdb/server/permissions/EntityPermission.java b/src/main/java/org/caosdb/server/permissions/EntityPermission.java
index 1f84dce34a611f3b25bea5ddaab0d40c9723a280..31e9cc471632ce1908e77df27b7121fdb1e6915e 100644
--- a/src/main/java/org/caosdb/server/permissions/EntityPermission.java
+++ b/src/main/java/org/caosdb/server/permissions/EntityPermission.java
@@ -37,6 +37,10 @@ public class EntityPermission extends Permission {
   private static final long serialVersionUID = -8935713878537140286L;
   private static List<EntityPermission> instances = new ArrayList<>();
   private final int bitNumber;
+  public static final Permission EDIT_PRIORITY_ACL =
+      new Permission(
+          "ADMIN:ENTITY:EDIT:PRIORITY_ACL",
+          "The permission to edit (add/delete) the prioritized rules of an acl of an entity.");
 
   public static ToElementable getAllEntityPermissions() {
     final Element entityPermissionsElement = new Element("EntityPermissions");
diff --git a/src/main/java/org/caosdb/server/permissions/Permission.java b/src/main/java/org/caosdb/server/permissions/Permission.java
index 44eee1c68a073c405233195aee491bcf10fcd38c..e8f33a3ae3f9eb0956bae6bc7c7eceefdc5663b7 100644
--- a/src/main/java/org/caosdb/server/permissions/Permission.java
+++ b/src/main/java/org/caosdb/server/permissions/Permission.java
@@ -28,11 +28,6 @@ public class Permission extends WildcardPermission {
 
   private static final long serialVersionUID = -7471830472441416012L;
 
-  public static final org.apache.shiro.authz.Permission EDIT_PRIORITY_ACL =
-      new Permission(
-          "ADMIN:ENTITY:EDIT:PRIORITY_ACL",
-          "The permission to edit (add/delete) the prioritized rules of an acl of an entity.");
-
   private final String description;
 
   private final String shortName;
diff --git a/src/main/java/org/caosdb/server/query/Query.java b/src/main/java/org/caosdb/server/query/Query.java
index 95f853d607edffa29b69e4f84657560ba3a52a48..07a165a352ba9a07a345018e7d287cd961f4a00b 100644
--- a/src/main/java/org/caosdb/server/query/Query.java
+++ b/src/main/java/org/caosdb/server/query/Query.java
@@ -47,6 +47,7 @@ import org.antlr.v4.runtime.CommonTokenStream;
 import org.apache.commons.jcs.access.behavior.ICacheAccess;
 import org.apache.shiro.subject.Subject;
 import org.caosdb.api.entity.v1.MessageCode;
+import org.caosdb.datetime.UTCDateTime;
 import org.caosdb.server.CaosDBServer;
 import org.caosdb.server.ServerProperties;
 import org.caosdb.server.caching.Cache;
@@ -1047,4 +1048,9 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
       return -1;
     }
   }
+
+  @Override
+  public UTCDateTime getTimestamp() {
+    return null;
+  }
 }
diff --git a/src/main/java/org/caosdb/server/scripting/ScriptingPermissions.java b/src/main/java/org/caosdb/server/scripting/ScriptingPermissions.java
index 78ad99d77d06b681838e455ad391c62e7ce85ca6..ff596a794a6b646003422e63e86b78cf80c475c4 100644
--- a/src/main/java/org/caosdb/server/scripting/ScriptingPermissions.java
+++ b/src/main/java/org/caosdb/server/scripting/ScriptingPermissions.java
@@ -1,11 +1,25 @@
 package org.caosdb.server.scripting;
 
-public class ScriptingPermissions {
+import org.caosdb.server.accessControl.ACMPermissions;
+
+public class ScriptingPermissions extends ACMPermissions {
+
+  public static final String PATH_PARAMETER = "?PATH?";
+
+  public ScriptingPermissions(String permission, String description) {
+    super(permission, description);
+  }
+
+  public String toString(String path) {
+    return toString().replace(PATH_PARAMETER, path.replace("/", ":"));
+  }
+
+  private static final ScriptingPermissions execution =
+      new ScriptingPermissions(
+          "SCRIPTING:EXECUTE:" + PATH_PARAMETER,
+          "Permission to execute a server-side script under the given path. Note that, for utilizing the wild cards feature, you have to use ':' as path separator. E.g. 'SCRIPTING:EXECUTE:my_scripts:*' would be the permission to execute all executables below the my_scripts directory.");
 
   public static final String PERMISSION_EXECUTION(final String call) {
-    StringBuilder ret = new StringBuilder(18 + call.length());
-    ret.append("SCRIPTING:EXECUTE:");
-    ret.append(call.replace("/", ":"));
-    return ret.toString();
+    return execution.toString(call);
   }
 }
diff --git a/src/main/java/org/caosdb/server/transaction/AccessControlTransaction.java b/src/main/java/org/caosdb/server/transaction/AccessControlTransaction.java
index 4897cd65620577e36a98f2db5e5530a42b3f43d0..1cf9f8f70df2eaa384e4074cc3b80a4816a88142 100644
--- a/src/main/java/org/caosdb/server/transaction/AccessControlTransaction.java
+++ b/src/main/java/org/caosdb/server/transaction/AccessControlTransaction.java
@@ -22,6 +22,7 @@
  */
 package org.caosdb.server.transaction;
 
+import org.caosdb.datetime.UTCDateTime;
 import org.caosdb.server.database.DatabaseAccessManager;
 import org.caosdb.server.database.access.Access;
 import org.caosdb.server.database.misc.RollBackHandler;
@@ -30,9 +31,11 @@ import org.caosdb.server.entity.Message;
 public abstract class AccessControlTransaction implements TransactionInterface {
 
   private Access access;
+  private UTCDateTime timestamp;
 
   @Override
   public final void execute() throws Exception {
+    this.timestamp = UTCDateTime.SystemMillisToUTCDateTime(System.currentTimeMillis());
     this.access = DatabaseAccessManager.getAccountAccess(this);
 
     try {
@@ -54,4 +57,9 @@ public abstract class AccessControlTransaction implements TransactionInterface {
   }
 
   protected abstract void transaction() throws Exception;
+
+  @Override
+  public UTCDateTime getTimestamp() {
+    return timestamp;
+  }
 }
diff --git a/src/main/java/org/caosdb/server/transaction/DeleteUserTransaction.java b/src/main/java/org/caosdb/server/transaction/DeleteUserTransaction.java
index f9e8ab74ff99bd283755fe891c15ea5a9e55f8a7..24aac16b9c529130520518822e6bf3a6a3e8b72d 100644
--- a/src/main/java/org/caosdb/server/transaction/DeleteUserTransaction.java
+++ b/src/main/java/org/caosdb/server/transaction/DeleteUserTransaction.java
@@ -24,8 +24,10 @@
 package org.caosdb.server.transaction;
 
 import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.subject.Subject;
 import org.caosdb.server.accessControl.ACMPermissions;
 import org.caosdb.server.accessControl.CredentialsValidator;
+import org.caosdb.server.accessControl.Principal;
 import org.caosdb.server.accessControl.UserSources;
 import org.caosdb.server.database.backend.transaction.DeletePassword;
 import org.caosdb.server.database.backend.transaction.DeleteUser;
@@ -50,8 +52,11 @@ public class DeleteUserTransaction extends AccessControlTransaction {
 
   @Override
   protected void transaction() throws Exception {
-    SecurityUtils.getSubject()
-        .checkPermission(ACMPermissions.PERMISSION_DELETE_USER(this.realm, this.name));
+    Subject subject = SecurityUtils.getSubject();
+    if (subject.getPrincipal().equals(new Principal(realm, name))) {
+      throw ServerMessages.CANNOT_DELETE_YOURSELF();
+    }
+    subject.checkPermission(ACMPermissions.PERMISSION_DELETE_USER(this.realm, this.name));
 
     final CredentialsValidator<String> validator =
         execute(new RetrievePasswordValidator(this.name), getAccess()).getValidator();
diff --git a/src/main/java/org/caosdb/server/transaction/FileStorageConsistencyCheck.java b/src/main/java/org/caosdb/server/transaction/FileStorageConsistencyCheck.java
index e066feb3028eb6dcb9bf645070cadefa9cc45047..1ce1aa566ad28d048a36f91d50bb297b1a709d14 100644
--- a/src/main/java/org/caosdb/server/transaction/FileStorageConsistencyCheck.java
+++ b/src/main/java/org/caosdb/server/transaction/FileStorageConsistencyCheck.java
@@ -53,6 +53,7 @@ public class FileStorageConsistencyCheck extends Thread
   private Runnable finishRunnable = null;
   private final String location;
   private Long ts = null;
+  private UTCDateTime timestamp = null;
 
   public Exception getException() {
     return this.exception;
@@ -60,6 +61,7 @@ public class FileStorageConsistencyCheck extends Thread
 
   public FileStorageConsistencyCheck(final String location) {
     setDaemon(true);
+    this.timestamp = UTCDateTime.SystemMillisToUTCDateTime(System.currentTimeMillis());
     this.location = location.startsWith("/") ? location.replaceFirst("^/", "") : location;
   }
 
@@ -245,4 +247,9 @@ public class FileStorageConsistencyCheck extends Thread
   public void execute() throws Exception {
     run();
   }
+
+  @Override
+  public UTCDateTime getTimestamp() {
+    return timestamp;
+  }
 }
diff --git a/src/main/java/org/caosdb/server/transaction/InsertLogRecordTransaction.java b/src/main/java/org/caosdb/server/transaction/InsertLogRecordTransaction.java
index c1128135b8620ebfe08328cd4901e9c0e6062053..950d9b8d25688e47833b60c01335f8dd525a0630 100644
--- a/src/main/java/org/caosdb/server/transaction/InsertLogRecordTransaction.java
+++ b/src/main/java/org/caosdb/server/transaction/InsertLogRecordTransaction.java
@@ -24,6 +24,7 @@ package org.caosdb.server.transaction;
 
 import java.util.List;
 import java.util.logging.LogRecord;
+import org.caosdb.datetime.UTCDateTime;
 import org.caosdb.server.database.DatabaseAccessManager;
 import org.caosdb.server.database.access.Access;
 import org.caosdb.server.database.backend.transaction.InsertLogRecord;
@@ -31,8 +32,10 @@ import org.caosdb.server.database.backend.transaction.InsertLogRecord;
 public class InsertLogRecordTransaction implements TransactionInterface {
 
   private final List<LogRecord> toBeFlushed;
+  private UTCDateTime timestamp;
 
   public InsertLogRecordTransaction(final List<LogRecord> toBeFlushed) {
+    this.timestamp = UTCDateTime.SystemMillisToUTCDateTime(System.currentTimeMillis());
     this.toBeFlushed = toBeFlushed;
   }
 
@@ -45,4 +48,9 @@ public class InsertLogRecordTransaction implements TransactionInterface {
       access.release();
     }
   }
+
+  @Override
+  public UTCDateTime getTimestamp() {
+    return timestamp;
+  }
 }
diff --git a/src/main/java/org/caosdb/server/transaction/InsertRoleTransaction.java b/src/main/java/org/caosdb/server/transaction/InsertRoleTransaction.java
index b70e2bd71259645a8f619cb25d36998579e3dca7..44288af5888c2dacbf9e97c235a76e0d9992a969 100644
--- a/src/main/java/org/caosdb/server/transaction/InsertRoleTransaction.java
+++ b/src/main/java/org/caosdb/server/transaction/InsertRoleTransaction.java
@@ -22,11 +22,14 @@
  */
 package org.caosdb.server.transaction;
 
+import java.util.Set;
 import org.apache.shiro.SecurityUtils;
 import org.caosdb.server.accessControl.ACMPermissions;
 import org.caosdb.server.accessControl.Role;
 import org.caosdb.server.database.backend.transaction.InsertRole;
 import org.caosdb.server.database.backend.transaction.RetrieveRole;
+import org.caosdb.server.database.backend.transaction.SetPermissionRules;
+import org.caosdb.server.entity.Message;
 import org.caosdb.server.utils.ServerMessages;
 
 public class InsertRoleTransaction extends AccessControlTransaction {
@@ -37,14 +40,24 @@ public class InsertRoleTransaction extends AccessControlTransaction {
     this.role = role;
   }
 
-  @Override
-  protected void transaction() throws Exception {
+  private void checkPermissions() throws Message {
     SecurityUtils.getSubject().checkPermission(ACMPermissions.PERMISSION_INSERT_ROLE());
 
     if (execute(new RetrieveRole(this.role.name), getAccess()).getRole() != null) {
       throw ServerMessages.ROLE_NAME_IS_NOT_UNIQUE;
     }
+  }
+
+  @Override
+  protected void transaction() throws Exception {
+    checkPermissions();
 
     execute(new InsertRole(this.role), getAccess());
+    if (this.role.permission_rules != null) {
+      execute(
+          new SetPermissionRules(this.role.name, Set.copyOf(this.role.permission_rules)),
+          getAccess());
+    }
+    RetrieveRole.removeCached(this.role.name);
   }
 }
diff --git a/src/main/java/org/caosdb/server/transaction/ListRolesTransaction.java b/src/main/java/org/caosdb/server/transaction/ListRolesTransaction.java
index d8c962a000b0d97d6b2c4066d8369b7fd05e7b60..ed3cd3cc5a154e82946e76f9934ee8608fe6980c 100644
--- a/src/main/java/org/caosdb/server/transaction/ListRolesTransaction.java
+++ b/src/main/java/org/caosdb/server/transaction/ListRolesTransaction.java
@@ -48,7 +48,7 @@ public class ListRolesTransaction extends AccessControlTransaction {
                         ACMPermissions.PERMISSION_RETRIEVE_ROLE_DESCRIPTION(role.name)))
             .collect(Collectors.toList());
 
-    // remove known users
+    // remove users. the list will only contain name and description.
     for (Role role : roles) {
       if (role.users != null) {
         Iterator<ProtoUser> iterator = role.users.iterator();
diff --git a/src/main/java/org/caosdb/server/transaction/RetrieveLogRecordTransaction.java b/src/main/java/org/caosdb/server/transaction/RetrieveLogRecordTransaction.java
index 7e6c527865566795c397377409b1fc63020ca1dd..2f6cafd6e4e55ab868f69f1d3414a2196f0a4fea 100644
--- a/src/main/java/org/caosdb/server/transaction/RetrieveLogRecordTransaction.java
+++ b/src/main/java/org/caosdb/server/transaction/RetrieveLogRecordTransaction.java
@@ -26,6 +26,7 @@ import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
 import org.apache.shiro.SecurityUtils;
+import org.caosdb.datetime.UTCDateTime;
 import org.caosdb.server.accessControl.ACMPermissions;
 import org.caosdb.server.database.DatabaseAccessManager;
 import org.caosdb.server.database.access.Access;
@@ -37,10 +38,12 @@ public class RetrieveLogRecordTransaction implements TransactionInterface {
   private final String logger;
   private final Level level;
   private final String message;
+  private UTCDateTime timestamp;
 
   public RetrieveLogRecordTransaction(
       final String logger, final Level level, final String message) {
     this.level = level;
+    this.timestamp = UTCDateTime.SystemMillisToUTCDateTime(System.currentTimeMillis());
     if (message != null && message.isEmpty()) {
       this.message = null;
     } else if (message != null) {
@@ -73,4 +76,9 @@ public class RetrieveLogRecordTransaction implements TransactionInterface {
   public List<LogRecord> getLogRecords() {
     return this.logRecords;
   }
+
+  @Override
+  public UTCDateTime getTimestamp() {
+    return timestamp;
+  }
 }
diff --git a/src/main/java/org/caosdb/server/transaction/RetrieveUserRolesTransaction.java b/src/main/java/org/caosdb/server/transaction/RetrieveUserRolesTransaction.java
index 97e518d06ac9ad5766b3801be8b1f1403cf8af3f..e26e29830a6e3c5f135b467f4622febda195da89 100644
--- a/src/main/java/org/caosdb/server/transaction/RetrieveUserRolesTransaction.java
+++ b/src/main/java/org/caosdb/server/transaction/RetrieveUserRolesTransaction.java
@@ -23,6 +23,7 @@
 package org.caosdb.server.transaction;
 
 import java.util.Set;
+import org.caosdb.datetime.UTCDateTime;
 import org.caosdb.server.accessControl.Principal;
 import org.caosdb.server.accessControl.UserSources;
 import org.caosdb.server.utils.ServerMessages;
@@ -33,8 +34,10 @@ public class RetrieveUserRolesTransaction implements TransactionInterface {
   private final String user;
   private Set<String> roles;
   private final String realm;
+  private UTCDateTime timestamp;
 
   public RetrieveUserRolesTransaction(final String realm, final String user) {
+    this.timestamp = UTCDateTime.SystemMillisToUTCDateTime(System.currentTimeMillis());
     this.realm = realm;
     this.user = user;
   }
@@ -67,4 +70,9 @@ public class RetrieveUserRolesTransaction implements TransactionInterface {
   public Set<String> getRoles() {
     return this.roles;
   }
+
+  @Override
+  public UTCDateTime getTimestamp() {
+    return timestamp;
+  }
 }
diff --git a/src/main/java/org/caosdb/server/transaction/TransactionInterface.java b/src/main/java/org/caosdb/server/transaction/TransactionInterface.java
index d56407ca854c756a956fe11d9bc98a72f05a4b50..8e01c7d9b407927ebdb6a0528f7dcbc6d5bea40d 100644
--- a/src/main/java/org/caosdb/server/transaction/TransactionInterface.java
+++ b/src/main/java/org/caosdb/server/transaction/TransactionInterface.java
@@ -22,6 +22,7 @@
  */
 package org.caosdb.server.transaction;
 
+import org.caosdb.datetime.UTCDateTime;
 import org.caosdb.server.database.BackendTransaction;
 import org.caosdb.server.database.access.Access;
 import org.caosdb.server.database.misc.RollBackHandler;
@@ -48,4 +49,6 @@ public interface TransactionInterface {
     t.executeTransaction();
     return t;
   }
+
+  public UTCDateTime getTimestamp();
 }
diff --git a/src/main/java/org/caosdb/server/transaction/UpdateACL.java b/src/main/java/org/caosdb/server/transaction/UpdateACL.java
index 49125a2b67abac4fd640e7d07b2d4ddc2960c72f..e0b0c5cf7b1d1cfd95ef48f9823e01aaae69afce 100644
--- a/src/main/java/org/caosdb/server/transaction/UpdateACL.java
+++ b/src/main/java/org/caosdb/server/transaction/UpdateACL.java
@@ -21,6 +21,8 @@
 
 package org.caosdb.server.transaction;
 
+import static org.caosdb.server.query.Query.clearCache;
+
 import org.caosdb.server.database.backend.transaction.RetrieveFullEntityTransaction;
 import org.caosdb.server.database.backend.transaction.UpdateEntityTransaction;
 import org.caosdb.server.entity.EntityInterface;
@@ -67,23 +69,35 @@ public class UpdateACL extends Transaction<TransactionContainer>
     RetrieveFullEntityTransaction t = new RetrieveFullEntityTransaction(oldContainer);
     execute(t, getAccess());
 
+    // the entities in this container only have an id and an ACL. -> Replace
+    // with full entity and move ACL to full entity if permissions are
+    // sufficient, otherwise add an error.
     getContainer()
         .replaceAll(
             (e) -> {
               EntityInterface result = oldContainer.getEntityById(e.getId());
 
               // Check ACL update is permitted (against the old ACL) and set the new ACL afterwards.
-              EntityACL acl = result.getEntityACL();
-              if (acl != null && acl.isPermitted(getTransactor(), EntityPermission.EDIT_ACL)) {
-                if (acl.equals(e.getEntityACL())) {
+              EntityACL oldAcl = result.getEntityACL();
+              EntityACL newAcl = e.getEntityACL();
+              if (oldAcl != null
+                  && oldAcl.isPermitted(getTransactor(), EntityPermission.EDIT_ACL)) {
+                if (oldAcl.equals(newAcl)) {
                   // nothing to be done
                   result.setEntityStatus(EntityStatus.IGNORE);
                 } else {
+                  if (!oldAcl.getPriorityEntityACL().equals(newAcl.getPriorityEntityACL())
+                      && !oldAcl.isPermitted(getTransactor(), EntityPermission.EDIT_PRIORITY_ACL)) {
+                    // the user is now permitted to update the prioriy acl.
+                    result.addError(org.caosdb.server.utils.ServerMessages.AUTHORIZATION_ERROR);
+                  }
+
                   // we're good to go. set new entity acl
-                  result.setEntityACL(e.getEntityACL());
+                  result.setEntityACL(newAcl);
+                  result.setEntityStatus(EntityStatus.QUALIFIED);
                 }
-              } else if (acl != null
-                  && acl.isPermitted(getTransactor(), EntityPermission.RETRIEVE_ENTITY)) {
+              } else if (oldAcl != null
+                  && oldAcl.isPermitted(getTransactor(), EntityPermission.RETRIEVE_ENTITY)) {
                 // the user knows that this entity exists
                 result.addError(org.caosdb.server.utils.ServerMessages.AUTHORIZATION_ERROR);
               } else {
@@ -116,4 +130,15 @@ public class UpdateACL extends Transaction<TransactionContainer>
   protected void cleanUp() {
     getAccess().release();
   }
+
+  @Override
+  protected void commit() throws Exception {
+    getAccess().commit();
+    clearCache();
+  }
+
+  @Override
+  public String getSRID() {
+    return getContainer().getRequestId();
+  }
 }
diff --git a/src/main/java/org/caosdb/server/transaction/UpdateRoleTransaction.java b/src/main/java/org/caosdb/server/transaction/UpdateRoleTransaction.java
index b1988ffabe8be60c7c2bc38d82e32dbaacee93e4..a62aff5bb6e90120e6cfd6651acde231cf3519b1 100644
--- a/src/main/java/org/caosdb/server/transaction/UpdateRoleTransaction.java
+++ b/src/main/java/org/caosdb/server/transaction/UpdateRoleTransaction.java
@@ -25,36 +25,55 @@ package org.caosdb.server.transaction;
 
 import java.util.Set;
 import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.subject.Subject;
 import org.caosdb.server.accessControl.ACMPermissions;
 import org.caosdb.server.accessControl.Role;
-import org.caosdb.server.accessControl.UserSources;
 import org.caosdb.server.database.backend.transaction.InsertRole;
 import org.caosdb.server.database.backend.transaction.RetrieveRole;
 import org.caosdb.server.database.backend.transaction.SetPermissionRules;
+import org.caosdb.server.entity.Message;
+import org.caosdb.server.permissions.PermissionRule;
 import org.caosdb.server.utils.ServerMessages;
 
 public class UpdateRoleTransaction extends AccessControlTransaction {
 
   private final Role role;
+  private Set<PermissionRule> newPermissionRules = null;
 
   public UpdateRoleTransaction(final Role role) {
     this.role = role;
   }
 
-  @Override
-  protected void transaction() throws Exception {
-    SecurityUtils.getSubject()
-        .checkPermission(ACMPermissions.PERMISSION_UPDATE_ROLE_DESCRIPTION(this.role.name));
+  private void checkPermissions() throws Message {
+
+    Subject subject = SecurityUtils.getSubject();
+    subject.checkPermission(ACMPermissions.PERMISSION_UPDATE_ROLE_DESCRIPTION(this.role.name));
 
-    if (!UserSources.isRoleExisting(this.role.name)) {
+    Role oldRole = execute(new RetrieveRole(this.role.name), getAccess()).getRole();
+    if (oldRole == null) {
       throw ServerMessages.ROLE_DOES_NOT_EXIST;
     }
 
-    execute(new InsertRole(this.role), getAccess());
     if (this.role.permission_rules != null) {
-      execute(
-          new SetPermissionRules(this.role.name, Set.copyOf(this.role.permission_rules)),
-          getAccess());
+      Set<PermissionRule> oldPermissions = Set.copyOf(oldRole.permission_rules);
+      Set<PermissionRule> newPermissions = Set.copyOf(role.permission_rules);
+      if (!oldPermissions.equals(newPermissions)) {
+        if (org.caosdb.server.permissions.Role.ADMINISTRATION.toString().equals(this.role.name)) {
+          throw ServerMessages.SPECIAL_ROLE_PERMISSIONS_CANNOT_BE_CHANGED();
+        }
+        subject.checkPermission(ACMPermissions.PERMISSION_UPDATE_ROLE_PERMISSIONS(this.role.name));
+        this.newPermissionRules = newPermissions;
+      }
+    }
+  }
+
+  @Override
+  protected void transaction() throws Exception {
+    checkPermissions();
+
+    execute(new InsertRole(this.role), getAccess());
+    if (this.newPermissionRules != null) {
+      execute(new SetPermissionRules(this.role.name, newPermissionRules), getAccess());
     }
     RetrieveRole.removeCached(this.role.name);
   }
diff --git a/src/main/java/org/caosdb/server/transaction/UpdateUserTransaction.java b/src/main/java/org/caosdb/server/transaction/UpdateUserTransaction.java
index 1fca0525f0e82e60c7124dc541ec0e0c09dabf71..6e945118c9b0eef7460f41b5062ccb308b0fd956 100644
--- a/src/main/java/org/caosdb/server/transaction/UpdateUserTransaction.java
+++ b/src/main/java/org/caosdb/server/transaction/UpdateUserTransaction.java
@@ -23,12 +23,15 @@
 
 package org.caosdb.server.transaction;
 
+import java.util.HashSet;
+import java.util.Set;
 import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.subject.Subject;
 import org.caosdb.server.accessControl.ACMPermissions;
 import org.caosdb.server.accessControl.Principal;
 import org.caosdb.server.accessControl.UserSources;
 import org.caosdb.server.accessControl.UserStatus;
+import org.caosdb.server.database.backend.transaction.RetrieveRole;
 import org.caosdb.server.database.backend.transaction.RetrieveUser;
 import org.caosdb.server.database.backend.transaction.SetPassword;
 import org.caosdb.server.database.backend.transaction.UpdateUser;
@@ -36,6 +39,7 @@ import org.caosdb.server.database.backend.transaction.UpdateUserRoles;
 import org.caosdb.server.database.exceptions.TransactionException;
 import org.caosdb.server.database.proto.ProtoUser;
 import org.caosdb.server.entity.Entity;
+import org.caosdb.server.entity.Message;
 import org.caosdb.server.entity.RetrieveEntity;
 import org.caosdb.server.entity.container.RetrieveContainer;
 import org.caosdb.server.utils.EntityStatus;
@@ -50,6 +54,8 @@ public class UpdateUserTransaction extends AccessControlTransaction {
 
   private final String password;
   private final ProtoUser user;
+  private HashSet<String> newRoles;
+  private Set<String> oldRoles;
 
   public UpdateUserTransaction(
       final String realm,
@@ -79,9 +85,10 @@ public class UpdateUserTransaction extends AccessControlTransaction {
     this.password = password;
   }
 
-  @Override
-  protected void transaction() throws Exception {
-    if (!UserSources.isUserExisting(new Principal(this.user.realm, this.user.name))) {
+  private void checkPermissions() throws Message {
+    Principal principal = new Principal(this.user.realm, this.user.name);
+    ProtoUser oldUser = execute(new RetrieveUser(principal), getAccess()).getUser();
+    if (oldUser == null) {
       throw ServerMessages.ACCOUNT_DOES_NOT_EXIST;
     }
 
@@ -111,15 +118,30 @@ public class UpdateUserTransaction extends AccessControlTransaction {
     }
 
     if (this.user.roles != null) {
-      SecurityUtils.getSubject()
-          .checkPermission(
-              ACMPermissions.PERMISSION_UPDATE_USER_ROLES(this.user.realm, this.user.name));
+      Set<String> oldRoles = oldUser.roles;
+      if (!this.user.roles.equals(oldRoles)) {
+        SecurityUtils.getSubject()
+            .checkPermission(
+                ACMPermissions.PERMISSION_UPDATE_USER_ROLES(this.user.realm, this.user.name));
+      }
+      this.oldRoles = oldRoles;
+      this.newRoles = this.user.roles;
     }
+  }
+
+  @Override
+  protected void transaction() throws Exception {
+    checkPermissions();
+
     if (isToBeUpdated()) {
       execute(new UpdateUser(this.user), getAccess());
     }
-    if (this.user.roles != null) {
+    if (this.newRoles != null) {
       execute(new UpdateUserRoles(this.user.realm, this.user.name, this.user.roles), getAccess());
+      RetrieveRole.removeCached(newRoles);
+      if (this.oldRoles != null) {
+        RetrieveRole.removeCached(oldRoles);
+      }
     }
   }
 
diff --git a/src/main/java/org/caosdb/server/transaction/WriteTransaction.java b/src/main/java/org/caosdb/server/transaction/WriteTransaction.java
index 1540ca79fdf557c3998f4b66072b4cb5466633a1..ee9aa81e49c99b15ac30a8164a4b9afc18b666f2 100644
--- a/src/main/java/org/caosdb/server/transaction/WriteTransaction.java
+++ b/src/main/java/org/caosdb/server/transaction/WriteTransaction.java
@@ -376,7 +376,7 @@ public class WriteTransaction extends Transaction<WritableContainer>
           .getPriorityEntityACL()
           .equals(oldEntity.getEntityACL().getPriorityEntityACL())) {
         // priority acl is to be changed?
-        oldEntity.checkPermission(Permission.EDIT_PRIORITY_ACL);
+        oldEntity.checkPermission(EntityPermission.EDIT_PRIORITY_ACL);
       }
       updatetable = true;
     } else if (!newEntity.hasEntityACL()) {
@@ -568,6 +568,7 @@ public class WriteTransaction extends Transaction<WritableContainer>
     return null;
   }
 
+  @Override
   public String getSRID() {
     return getContainer().getRequestId();
   }
diff --git a/src/main/java/org/caosdb/server/transaction/WriteTransactionInterface.java b/src/main/java/org/caosdb/server/transaction/WriteTransactionInterface.java
index 165acb408776a720c6887d31b550cf57e3d6fa0c..4e2938e18d061cbd1a7575baa59322356731859a 100644
--- a/src/main/java/org/caosdb/server/transaction/WriteTransactionInterface.java
+++ b/src/main/java/org/caosdb/server/transaction/WriteTransactionInterface.java
@@ -1,8 +1,13 @@
 package org.caosdb.server.transaction;
 
+import org.apache.shiro.subject.Subject;
 import org.caosdb.server.database.access.Access;
 
 public interface WriteTransactionInterface extends TransactionInterface {
 
   public Access getAccess();
+
+  public Subject getTransactor();
+
+  public String getSRID();
 }
diff --git a/src/main/java/org/caosdb/server/utils/Info.java b/src/main/java/org/caosdb/server/utils/Info.java
index 18ee09828006c0394dec315ca77483f8298ba86b..644239fc662c1b6a05dc1aac37af1bfe26e7972e 100644
--- a/src/main/java/org/caosdb/server/utils/Info.java
+++ b/src/main/java/org/caosdb/server/utils/Info.java
@@ -28,6 +28,7 @@ import java.sql.SQLException;
 import java.util.LinkedList;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
+import org.caosdb.datetime.UTCDateTime;
 import org.caosdb.server.CaosDBServer;
 import org.caosdb.server.FileSystem;
 import org.caosdb.server.database.DatabaseAccessManager;
@@ -46,6 +47,7 @@ public class Info extends AbstractObservable implements Observer, TransactionInt
   public static final String SYNC_DATABASE_EVENT = "SyncDatabaseEvent";
   private final Access access;
   public Logger logger = LogManager.getLogger(getClass());
+  private UTCDateTime timestamp;
 
   @Override
   public boolean notifyObserver(final String e, final Observable o) {
@@ -58,6 +60,7 @@ public class Info extends AbstractObservable implements Observer, TransactionInt
   }
 
   private Info() {
+    this.timestamp = UTCDateTime.SystemMillisToUTCDateTime(System.currentTimeMillis());
     this.access = DatabaseAccessManager.getInfoAccess(this);
     try {
       syncDatabase();
@@ -246,4 +249,9 @@ public class Info extends AbstractObservable implements Observer, TransactionInt
   public void execute() throws Exception {
     syncDatabase();
   }
+
+  @Override
+  public UTCDateTime getTimestamp() {
+    return timestamp;
+  }
 }
diff --git a/src/main/java/org/caosdb/server/utils/Initialization.java b/src/main/java/org/caosdb/server/utils/Initialization.java
index cb1a36307928a72b9da95b48737196e569c43346..4f28f206bd5f272887fb78b661c6dc46d5cff6bc 100644
--- a/src/main/java/org/caosdb/server/utils/Initialization.java
+++ b/src/main/java/org/caosdb/server/utils/Initialization.java
@@ -24,6 +24,7 @@
  */
 package org.caosdb.server.utils;
 
+import org.caosdb.datetime.UTCDateTime;
 import org.caosdb.server.database.DatabaseAccessManager;
 import org.caosdb.server.database.access.Access;
 import org.caosdb.server.transaction.TransactionInterface;
@@ -31,9 +32,11 @@ import org.caosdb.server.transaction.TransactionInterface;
 public final class Initialization implements TransactionInterface, AutoCloseable {
 
   private Access access;
+  private UTCDateTime timestamp;
   private static final Initialization instance = new Initialization();
 
   private Initialization() {
+    this.timestamp = UTCDateTime.SystemMillisToUTCDateTime(System.currentTimeMillis());
     this.access = DatabaseAccessManager.getInitAccess(this);
   }
 
@@ -55,4 +58,9 @@ public final class Initialization implements TransactionInterface, AutoCloseable
       this.access = null;
     }
   }
+
+  @Override
+  public UTCDateTime getTimestamp() {
+    return timestamp;
+  }
 }
diff --git a/src/main/java/org/caosdb/server/utils/ServerMessages.java b/src/main/java/org/caosdb/server/utils/ServerMessages.java
index ea5222896895e22ef190ee4d04ea6b3e1c282e1a..151f55face7edbfab793606474f183a65f0468be 100644
--- a/src/main/java/org/caosdb/server/utils/ServerMessages.java
+++ b/src/main/java/org/caosdb/server/utils/ServerMessages.java
@@ -283,6 +283,13 @@ public class ServerMessages {
           MessageCode.MESSAGE_CODE_UNKNOWN,
           "This special role cannot be deleted. Ever.");
 
+  public static final Message SPECIAL_ROLE_PERMISSIONS_CANNOT_BE_CHANGED() {
+    return new Message(
+        MessageType.Error,
+        MessageCode.MESSAGE_CODE_UNKNOWN,
+        "This special role's permissions cannot be changed. Ever.");
+  }
+
   public static final Message QUERY_EXCEPTION =
       new Message(
           MessageType.Error,
@@ -604,4 +611,9 @@ public class ServerMessages {
         MessageCode.MESSAGE_CODE_UNKNOWN,
         "The user name does not comply with the policies for user names: " + policy);
   }
+
+  public static final Message CANNOT_DELETE_YOURSELF() {
+    return new Message(
+        MessageType.Error, MessageCode.MESSAGE_CODE_UNKNOWN, "You cannot delete yourself.");
+  }
 }