diff --git a/caosdb-proto b/caosdb-proto
index 69733f6dc6ac4370fed0a38c59f17fa24a74425b..0aa6278dbdf5a18e1909050eedf8b2134b194810 160000
--- a/caosdb-proto
+++ b/caosdb-proto
@@ -1 +1 @@
-Subproject commit 69733f6dc6ac4370fed0a38c59f17fa24a74425b
+Subproject commit 0aa6278dbdf5a18e1909050eedf8b2134b194810
diff --git a/src/main/java/org/caosdb/server/accessControl/CaosDBAuthorizingRealm.java b/src/main/java/org/caosdb/server/accessControl/CaosDBAuthorizingRealm.java
index e10a0b29b38b2e246bde8c89c43a8b82a9307b7e..ef1c656bafc6aab034bbfa1c9e43d76994b572a4 100644
--- a/src/main/java/org/caosdb/server/accessControl/CaosDBAuthorizingRealm.java
+++ b/src/main/java/org/caosdb/server/accessControl/CaosDBAuthorizingRealm.java
@@ -63,7 +63,7 @@ public class CaosDBAuthorizingRealm extends AuthorizingRealm {
 
     // Find all roles which are associated with this principal in this realm.
     final Set<String> principalRoles =
-        UserSources.resolve((Principal) principals.getPrimaryPrincipal());
+        UserSources.resolveRoles((Principal) principals.getPrimaryPrincipal());
     if (principalRoles != null) {
       authzInfo.addRoles(principalRoles);
     }
diff --git a/src/main/java/org/caosdb/server/accessControl/Role.java b/src/main/java/org/caosdb/server/accessControl/Role.java
index 1a34b49974804ae202ceaf4044ff1f620eaa5d12..1938d9f18926601a9eb4a0fb081c9738d5c7f501 100644
--- a/src/main/java/org/caosdb/server/accessControl/Role.java
+++ b/src/main/java/org/caosdb/server/accessControl/Role.java
@@ -23,6 +23,8 @@
 package org.caosdb.server.accessControl;
 
 import java.io.Serializable;
+import java.util.LinkedList;
+import org.caosdb.server.permissions.PermissionRule;
 import org.jdom2.Element;
 
 public class Role implements Serializable {
@@ -30,6 +32,7 @@ public class Role implements Serializable {
   private static final long serialVersionUID = 8968219504349206982L;
   public String name = null;
   public String description = null;
+  public LinkedList<PermissionRule> permission_rules = null;
 
   public Element toElement() {
     final Element ret = new Element("Role");
diff --git a/src/main/java/org/caosdb/server/accessControl/UserSources.java b/src/main/java/org/caosdb/server/accessControl/UserSources.java
index a7abd1405f3d566bb672befc86b853dcf2a17357..aabb5d9b93ea1b43c7ecd3b2e2b0794e4f751ab4 100644
--- a/src/main/java/org/caosdb/server/accessControl/UserSources.java
+++ b/src/main/java/org/caosdb/server/accessControl/UserSources.java
@@ -36,6 +36,7 @@ import org.caosdb.server.CaosDBServer;
 import org.caosdb.server.ServerProperties;
 import org.caosdb.server.entity.Message;
 import org.caosdb.server.permissions.Role;
+import org.caosdb.server.transaction.LogUserVisitTransaction;
 import org.caosdb.server.transaction.RetrieveRoleTransaction;
 import org.caosdb.server.transaction.RetrieveUserTransaction;
 import org.caosdb.server.utils.ServerMessages;
@@ -69,6 +70,7 @@ import org.slf4j.LoggerFactory;
  */
 public class UserSources extends HashMap<String, UserSource> {
 
+  public static final String USERNAME_PASSWORD_AUTHENTICATION = "USERNAME_PASSWORD_AUTHENTICATION";
   private static final Logger logger = LoggerFactory.getLogger(UserSources.class);
   public static final String KEY_DEFAULT_REALM = "defaultRealm";
   public static final String KEY_REALMS = "realms";
@@ -206,8 +208,13 @@ public class UserSources extends HashMap<String, UserSource> {
     return instance.map.getSectionProperty(Ini.DEFAULT_SECTION_NAME, KEY_DEFAULT_REALM, "CaosDB");
   }
 
-  // @todo Refactor name: resolveRoles(...)?
-  public static Set<String> resolve(final Principal principal) {
+  /**
+   * Return the roles of a given user.
+   *
+   * @param principal
+   * @return A set of role names.
+   */
+  public static Set<String> resolveRoles(final Principal principal) {
     if (AnonymousAuthenticationToken.PRINCIPAL == principal) {
       // anymous has one role
       Set<String> roles = new HashSet<>();
@@ -251,7 +258,22 @@ public class UserSources extends HashMap<String, UserSource> {
     }
 
     final boolean isValid = instance.get(realm).isValid(username, password);
-    return isValid && isActive(realm, username);
+
+    if (isValid && isActive(realm, username)) {
+      logUserVisit(realm, username, USERNAME_PASSWORD_AUTHENTICATION);
+      return true;
+    }
+    return false;
+  }
+
+  public static void logUserVisit(String realm, String username, String type) {
+    try {
+      LogUserVisitTransaction t =
+          new LogUserVisitTransaction(System.currentTimeMillis(), realm, username, type);
+      t.execute();
+    } catch (final Exception e) {
+      throw new AuthenticationException(e);
+    }
   }
 
   private static boolean isActive(final String realm, final String username) {
diff --git a/src/main/java/org/caosdb/server/database/BackendTransaction.java b/src/main/java/org/caosdb/server/database/BackendTransaction.java
index 1e627ebafa2d273fafbc1762c70877df3f8fc2b5..15be37bb077cd0ae0d0a55444597694e360e64b2 100644
--- a/src/main/java/org/caosdb/server/database/BackendTransaction.java
+++ b/src/main/java/org/caosdb/server/database/BackendTransaction.java
@@ -46,6 +46,7 @@ import org.caosdb.server.database.backend.implementation.MySQL.MySQLInsertTransa
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLIsSubType;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLListRoles;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLListUsers;
+import org.caosdb.server.database.backend.implementation.MySQL.MySQLLogUserVisit;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLRegisterSubDomain;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveAll;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveAllUncheckedFiles;
@@ -103,6 +104,7 @@ import org.caosdb.server.database.backend.interfaces.InsertTransactionHistoryImp
 import org.caosdb.server.database.backend.interfaces.IsSubTypeImpl;
 import org.caosdb.server.database.backend.interfaces.ListRolesImpl;
 import org.caosdb.server.database.backend.interfaces.ListUsersImpl;
+import org.caosdb.server.database.backend.interfaces.LogUserVisitImpl;
 import org.caosdb.server.database.backend.interfaces.RegisterSubDomainImpl;
 import org.caosdb.server.database.backend.interfaces.RetrieveAllImpl;
 import org.caosdb.server.database.backend.interfaces.RetrieveAllUncheckedFilesImpl;
@@ -210,6 +212,7 @@ public abstract class BackendTransaction implements Undoable {
       setImpl(RetrieveVersionHistoryImpl.class, MySQLRetrieveVersionHistory.class);
       setImpl(SetFileChecksumImpl.class, MySQLSetFileChecksum.class);
       setImpl(ListUsersImpl.class, MySQLListUsers.class);
+      setImpl(LogUserVisitImpl.class, MySQLLogUserVisit.class);
     }
   }
 
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLLogUserVisit.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLLogUserVisit.java
new file mode 100644
index 0000000000000000000000000000000000000000..caa5f167df1fc2ffefbfaba4e769052ce3836c20
--- /dev/null
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLLogUserVisit.java
@@ -0,0 +1,32 @@
+package org.caosdb.server.database.backend.implementation.MySQL;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import org.caosdb.server.database.access.Access;
+import org.caosdb.server.database.backend.interfaces.LogUserVisitImpl;
+import org.caosdb.server.database.exceptions.TransactionException;
+
+public class MySQLLogUserVisit extends MySQLTransaction implements LogUserVisitImpl {
+
+  public MySQLLogUserVisit(Access access) {
+    super(access);
+  }
+
+  public static final String LOG_USER_VISIT =
+      "INSERT IGNORE INTO user_info (status, realm, name) VALUES ('ACTIVE',?,?)";
+
+  @Override
+  public void log(long timestamp, String realm, String username, String type) {
+    try {
+      // TODO actually log the user visit
+      final PreparedStatement stmt = prepareStatement(LOG_USER_VISIT);
+      stmt.setString(1, realm);
+      stmt.setString(2, username);
+      stmt.execute();
+    } catch (final SQLException e) {
+      throw new TransactionException(e);
+    } catch (final ConnectionException e) {
+      throw new TransactionException(e);
+    }
+  }
+}
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveRole.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveRole.java
index f0219040c47bc09fe2052d8e95a3ea83637e6aae..95dfd2cfeecceb6d3751cba876484c7f5d7a66f9 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveRole.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLRetrieveRole.java
@@ -25,10 +25,14 @@ package org.caosdb.server.database.backend.implementation.MySQL;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.util.LinkedList;
+import java.util.Map;
 import org.caosdb.server.accessControl.Role;
 import org.caosdb.server.database.access.Access;
 import org.caosdb.server.database.backend.interfaces.RetrieveRoleImpl;
 import org.caosdb.server.database.exceptions.TransactionException;
+import org.caosdb.server.permissions.PermissionRule;
+import org.eclipse.jetty.util.ajax.JSON;
 
 public class MySQLRetrieveRole extends MySQLTransaction implements RetrieveRoleImpl {
 
@@ -36,7 +40,8 @@ public class MySQLRetrieveRole extends MySQLTransaction implements RetrieveRoleI
     super(access);
   }
 
-  public static final String STMT_RETRIEVE_ROLE = "SELECT description FROM roles WHERE name=?";
+  public static final String STMT_RETRIEVE_ROLE =
+      "SELECT r.description AS description, p.permissions AS permissions FROM roles AS r LEFT JOIN permissions AS p ON (r.name = p.role) WHERE r.name=?";
 
   @Override
   public Role retrieve(final String role) throws TransactionException {
@@ -49,6 +54,7 @@ public class MySQLRetrieveRole extends MySQLTransaction implements RetrieveRoleI
           final Role ret = new Role();
           ret.name = role;
           ret.description = rs.getString("description");
+          ret.permission_rules = parse(rs.getString("permissions"));
           return ret;
         } else {
           return null;
@@ -62,4 +68,16 @@ public class MySQLRetrieveRole extends MySQLTransaction implements RetrieveRoleI
       throw new TransactionException(e);
     }
   }
+
+  @SuppressWarnings("unchecked")
+  private LinkedList<PermissionRule> parse(String string) {
+    if (string == null) return null;
+    final Object[] maps = (Object[]) JSON.parse(string);
+    final LinkedList<PermissionRule> ret = new LinkedList<>();
+    for (final Object map : maps) {
+      ret.add(PermissionRule.parse((Map<String, String>) map));
+    }
+
+    return ret;
+  }
 }
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateUserRoles.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateUserRoles.java
index 62cb68bf58fc8269cc365fa167929ff79726499b..2e41e24ab0e9457d0dca8bea31ae9379b2e62f5b 100644
--- a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateUserRoles.java
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLUpdateUserRoles.java
@@ -24,7 +24,7 @@ package org.caosdb.server.database.backend.implementation.MySQL;
 
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
-import java.util.HashSet;
+import java.util.Set;
 import org.caosdb.server.database.access.Access;
 import org.caosdb.server.database.backend.interfaces.UpdateUserRolesImpl;
 import org.caosdb.server.database.exceptions.TransactionException;
@@ -41,7 +41,7 @@ public class MySQLUpdateUserRoles extends MySQLTransaction implements UpdateUser
       "INSERT INTO user_roles (realm, user, role) VALUES (?,?,?);";
 
   @Override
-  public void updateUserRoles(final String realm, final String user, final HashSet<String> roles)
+  public void updateUserRoles(final String realm, final String user, final Set<String> roles)
       throws TransactionException {
     try {
       final PreparedStatement delete_stmt = prepareStatement(STMT_DELETE_USER_ROLES);
diff --git a/src/main/java/org/caosdb/server/database/backend/interfaces/LogUserVisitImpl.java b/src/main/java/org/caosdb/server/database/backend/interfaces/LogUserVisitImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..6c0f3cb504b8d985c717974be700e3062242d62d
--- /dev/null
+++ b/src/main/java/org/caosdb/server/database/backend/interfaces/LogUserVisitImpl.java
@@ -0,0 +1,25 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@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
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * 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/>.
+ */
+package org.caosdb.server.database.backend.interfaces;
+
+public interface LogUserVisitImpl extends BackendTransactionImpl {
+
+  void log(long timestamp, String realm, String username, String type);
+}
diff --git a/src/main/java/org/caosdb/server/database/backend/interfaces/UpdateUserRolesImpl.java b/src/main/java/org/caosdb/server/database/backend/interfaces/UpdateUserRolesImpl.java
index f2abfbe3cb7846f16e21d8f9a719ad708884841c..891df301e992f42c38f7c52d867353a65cddd1b2 100644
--- a/src/main/java/org/caosdb/server/database/backend/interfaces/UpdateUserRolesImpl.java
+++ b/src/main/java/org/caosdb/server/database/backend/interfaces/UpdateUserRolesImpl.java
@@ -22,11 +22,11 @@
  */
 package org.caosdb.server.database.backend.interfaces;
 
-import java.util.HashSet;
+import java.util.Set;
 import org.caosdb.server.database.exceptions.TransactionException;
 
 public interface UpdateUserRolesImpl extends BackendTransactionImpl {
 
-  public void updateUserRoles(String realm, String user, HashSet<String> roles)
+  public void updateUserRoles(String realm, String user, Set<String> roles)
       throws TransactionException;
 }
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/LogUserVisit.java b/src/main/java/org/caosdb/server/database/backend/transaction/LogUserVisit.java
new file mode 100644
index 0000000000000000000000000000000000000000..c5d107c2936d467be775175cac5f6b81e3cbce70
--- /dev/null
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/LogUserVisit.java
@@ -0,0 +1,47 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@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
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * 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.database.backend.transaction;
+
+import org.caosdb.server.database.BackendTransaction;
+import org.caosdb.server.database.backend.interfaces.LogUserVisitImpl;
+import org.caosdb.server.database.exceptions.TransactionException;
+
+public class LogUserVisit extends BackendTransaction {
+
+  private String realm;
+  private String username;
+  private String type;
+  private long timestamp;
+
+  public LogUserVisit(long timestamp, String realm, String username, String type) {
+    this.timestamp = timestamp;
+    this.realm = realm;
+    this.username = username;
+    this.type = type;
+  }
+
+  @Override
+  protected void execute() throws TransactionException {
+    final LogUserVisitImpl t = getImplementation(LogUserVisitImpl.class);
+    t.log(timestamp, realm, username, type);
+  }
+}
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/UpdateUserRoles.java b/src/main/java/org/caosdb/server/database/backend/transaction/UpdateUserRoles.java
index 66b3cb8e6f03c3abde0ccaa051a1228057d973f7..13b4906e48cd5c7a93e03683d4b57b7381d14f16 100644
--- a/src/main/java/org/caosdb/server/database/backend/transaction/UpdateUserRoles.java
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/UpdateUserRoles.java
@@ -22,7 +22,7 @@
  */
 package org.caosdb.server.database.backend.transaction;
 
-import java.util.HashSet;
+import java.util.Set;
 import org.caosdb.server.accessControl.Principal;
 import org.caosdb.server.database.BackendTransaction;
 import org.caosdb.server.database.backend.interfaces.UpdateUserRolesImpl;
@@ -31,10 +31,10 @@ import org.caosdb.server.database.exceptions.TransactionException;
 public class UpdateUserRoles extends BackendTransaction {
 
   private final String user;
-  private final HashSet<String> roles;
+  private final Set<String> roles;
   private final String realm;
 
-  public UpdateUserRoles(final String realm, final String user, final HashSet<String> roles) {
+  public UpdateUserRoles(final String realm, final String user, final Set<String> roles) {
     this.realm = realm;
     this.user = user;
     this.roles = roles;
diff --git a/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java b/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java
index 258cb8ada62ffdb5dd01beae1ceff8639fb6a988..182d5cb5dd194db4475f3a923a9bf207eb7ab498 100644
--- a/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java
+++ b/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java
@@ -1,6 +1,8 @@
 package org.caosdb.server.grpc;
 
 import io.grpc.stub.StreamObserver;
+import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import org.caosdb.api.acm.v1alpha1.AccessControlManagementServiceGrpc.AccessControlManagementServiceImplBase;
 import org.caosdb.api.acm.v1alpha1.CreateSingleRoleRequest;
@@ -11,6 +13,11 @@ import org.caosdb.api.acm.v1alpha1.ListRolesRequest;
 import org.caosdb.api.acm.v1alpha1.ListRolesResponse;
 import org.caosdb.api.acm.v1alpha1.ListUsersRequest;
 import org.caosdb.api.acm.v1alpha1.ListUsersResponse;
+import org.caosdb.api.acm.v1alpha1.PermissionRule;
+import org.caosdb.api.acm.v1alpha1.RetrieveSingleRoleRequest;
+import org.caosdb.api.acm.v1alpha1.RetrieveSingleRoleResponse;
+import org.caosdb.api.acm.v1alpha1.RetrieveSingleUserRequest;
+import org.caosdb.api.acm.v1alpha1.RetrieveSingleUserResponse;
 import org.caosdb.api.acm.v1alpha1.User;
 import org.caosdb.api.acm.v1alpha1.UserStatus;
 import org.caosdb.server.accessControl.Role;
@@ -19,6 +26,8 @@ import org.caosdb.server.transaction.InsertRoleTransaction;
 import org.caosdb.server.transaction.InsertUserTransaction;
 import org.caosdb.server.transaction.ListRolesTransaction;
 import org.caosdb.server.transaction.ListUsersTransaction;
+import org.caosdb.server.transaction.RetrieveRoleTransaction;
+import org.caosdb.server.transaction.RetrieveUserTransaction;
 
 public class AccessControlManagementServiceImpl extends AccessControlManagementServiceImplBase {
 
@@ -29,13 +38,26 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS
     result.realm = user.getRealm();
     result.name = user.getName();
     result.email = user.getEmail();
-    result.status =
-        (user.getStatus() == UserStatus.USER_STATUS_ACTIVE
-            ? org.caosdb.server.accessControl.UserStatus.ACTIVE
-            : org.caosdb.server.accessControl.UserStatus.INACTIVE);
+    result.status = convert(user.getStatus());
+    if (user.getRolesCount() >= 0) {
+      result.roles = new HashSet<String>();
+      user.getRolesList().forEach(result.roles::add);
+    }
     return result;
   }
 
+  private org.caosdb.server.accessControl.UserStatus convert(UserStatus status) {
+    switch (status) {
+      case USER_STATUS_ACTIVE:
+        return org.caosdb.server.accessControl.UserStatus.ACTIVE;
+      case USER_STATUS_INACTIVE:
+        return org.caosdb.server.accessControl.UserStatus.INACTIVE;
+      default:
+        break;
+    }
+    return org.caosdb.server.accessControl.UserStatus.INACTIVE;
+  }
+
   private ListUsersResponse convertUsers(List<ProtoUser> users) {
     ListUsersResponse.Builder response = ListUsersResponse.newBuilder();
     users.forEach(
@@ -57,11 +79,21 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS
     if (user.entity != null) {
       result.setEntityId(Integer.toString(user.entity));
     }
+    if (user.roles != null) {
+      result.addAllRoles(user.roles);
+    }
     return result;
   }
 
   private UserStatus convert(org.caosdb.server.accessControl.UserStatus status) {
-    return UserStatus.valueOf(status.toString());
+    switch (status) {
+      case ACTIVE:
+        return UserStatus.USER_STATUS_ACTIVE;
+      case INACTIVE:
+        return UserStatus.USER_STATUS_INACTIVE;
+      default:
+        return UserStatus.USER_STATUS_UNSPECIFIED;
+    }
   }
 
   private Role convert(org.caosdb.api.acm.v1alpha1.Role role) {
@@ -86,7 +118,26 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS
     org.caosdb.api.acm.v1alpha1.Role.Builder result = org.caosdb.api.acm.v1alpha1.Role.newBuilder();
     result.setDescription(role.description);
     result.setName(role.name);
+    if (role.permission_rules != null) result.addAllPermissionRules(convert(role.permission_rules));
+    return result;
+  }
+
+  private PermissionRule convert(org.caosdb.server.permissions.PermissionRule rule) {
+    PermissionRule.Builder result = PermissionRule.newBuilder();
+    result.setGrant(rule.isGrant());
+    result.setPriority(rule.isPriority());
+    result.setPermission(rule.getPermission());
+    return result.build();
+  }
+
+  private Iterable<PermissionRule> convert(
+      LinkedList<org.caosdb.server.permissions.PermissionRule> permission_rules) {
 
+    List<PermissionRule> result = new LinkedList<>();
+    permission_rules.forEach(
+        (rule) -> {
+          result.add(convert(rule));
+        });
     return result;
   }
 
@@ -111,6 +162,14 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS
     return CreateSingleRoleResponse.newBuilder().build();
   }
 
+  private RetrieveSingleRoleResponse retrieveSingleRoleTransaction(
+      RetrieveSingleRoleRequest request) throws Exception {
+    RetrieveRoleTransaction transaction = new RetrieveRoleTransaction(request.getName());
+    transaction.execute();
+
+    return RetrieveSingleRoleResponse.newBuilder().setRole(convert(transaction.getRole())).build();
+  }
+
   ////////////////// ... for users
 
   private ListUsersResponse listUsersTransaction(ListUsersRequest request) throws Exception {
@@ -123,13 +182,21 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS
   private CreateSingleUserResponse createSingleUserTransaction(CreateSingleUserRequest request)
       throws Exception {
     ProtoUser user = convert(request.getUser());
-    InsertUserTransaction transaction =
-        new InsertUserTransaction(user, request.getUser().getPassword());
+    InsertUserTransaction transaction = new InsertUserTransaction(user, request.getPassword());
     transaction.execute();
 
     return CreateSingleUserResponse.newBuilder().build();
   }
 
+  private RetrieveSingleUserResponse retrieveSingleUserTransaction(
+      RetrieveSingleUserRequest request) throws Exception {
+    RetrieveUserTransaction transaction =
+        new RetrieveUserTransaction(request.getRealm(), request.getName());
+    transaction.execute();
+
+    return RetrieveSingleUserResponse.newBuilder().setUser(convert(transaction.getUser())).build();
+  }
+
   ///////////////////////////////////// RPC Methods (API)
 
   @Override
@@ -174,6 +241,21 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS
     }
   }
 
+  @Override
+  public void retrieveSingleRole(
+      RetrieveSingleRoleRequest request,
+      StreamObserver<RetrieveSingleRoleResponse> responseObserver) {
+    try {
+      final RetrieveSingleRoleResponse response = retrieveSingleRoleTransaction(request);
+      responseObserver.onNext(response);
+      responseObserver.onCompleted();
+
+    } catch (final Exception e) {
+      e.printStackTrace();
+      responseObserver.onError(e);
+    }
+  }
+
   @Override
   public void createSingleUser(
       CreateSingleUserRequest request, StreamObserver<CreateSingleUserResponse> responseObserver) {
@@ -187,4 +269,19 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS
       responseObserver.onError(e);
     }
   }
+
+  @Override
+  public void retrieveSingleUser(
+      RetrieveSingleUserRequest request,
+      StreamObserver<RetrieveSingleUserResponse> responseObserver) {
+    try {
+      final RetrieveSingleUserResponse response = retrieveSingleUserTransaction(request);
+      responseObserver.onNext(response);
+      responseObserver.onCompleted();
+
+    } catch (final Exception e) {
+      e.printStackTrace();
+      responseObserver.onError(e);
+    }
+  }
 }
diff --git a/src/main/java/org/caosdb/server/permissions/PermissionRule.java b/src/main/java/org/caosdb/server/permissions/PermissionRule.java
index 85d3b62834a67a4fc46ea9b3c38d19e4b8261d74..7a8d4e26dc4cb0c4e4b8a7e2eea1de8a2bee2a7e 100644
--- a/src/main/java/org/caosdb/server/permissions/PermissionRule.java
+++ b/src/main/java/org/caosdb/server/permissions/PermissionRule.java
@@ -54,6 +54,10 @@ public class PermissionRule {
     return this.priority;
   }
 
+  public String getPermission() {
+    return permission;
+  }
+
   public Permission getPermission(String realm, String username) {
     return new WildcardPermission(
         permission.replaceAll("\\?REALM\\?", realm).replaceAll("\\?USERNAME\\?", username));
diff --git a/src/main/java/org/caosdb/server/transaction/InsertUserTransaction.java b/src/main/java/org/caosdb/server/transaction/InsertUserTransaction.java
index d63cd60fc94e09bb0230551bf9fd432b9bee82bf..235598b9f522d4aa04af2d899847392d82994702 100644
--- a/src/main/java/org/caosdb/server/transaction/InsertUserTransaction.java
+++ b/src/main/java/org/caosdb/server/transaction/InsertUserTransaction.java
@@ -29,6 +29,7 @@ import org.caosdb.server.accessControl.UserStatus;
 import org.caosdb.server.database.backend.transaction.RetrievePasswordValidator;
 import org.caosdb.server.database.backend.transaction.SetPassword;
 import org.caosdb.server.database.backend.transaction.UpdateUser;
+import org.caosdb.server.database.backend.transaction.UpdateUserRoles;
 import org.caosdb.server.database.proto.ProtoUser;
 import org.caosdb.server.utils.ServerMessages;
 import org.caosdb.server.utils.Utils;
@@ -79,6 +80,7 @@ public class InsertUserTransaction extends AccessControlTransaction {
 
       execute(new SetPassword(this.user.name, this.password), getAccess());
       execute(new UpdateUser(this.user), getAccess());
+      execute(new UpdateUserRoles(this.user.realm, this.user.name, this.user.roles), getAccess());
     } else {
       throw ServerMessages.ACCOUNT_NAME_NOT_UNIQUE;
     }
diff --git a/src/main/java/org/caosdb/server/transaction/LogUserVisitTransaction.java b/src/main/java/org/caosdb/server/transaction/LogUserVisitTransaction.java
new file mode 100644
index 0000000000000000000000000000000000000000..dff0d34fed5bab445bfd761817aecc474def6025
--- /dev/null
+++ b/src/main/java/org/caosdb/server/transaction/LogUserVisitTransaction.java
@@ -0,0 +1,42 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2021 Timm Fitschen <t.fitschen@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
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * 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/>.
+ */
+package org.caosdb.server.transaction;
+
+import org.caosdb.server.database.backend.transaction.LogUserVisit;
+
+public class LogUserVisitTransaction extends AccessControlTransaction {
+
+  private String realm;
+  private String username;
+  private String type;
+  private long timestamp;
+
+  public LogUserVisitTransaction(long timestamp, String realm, String username, String type) {
+    this.timestamp = timestamp;
+    this.realm = realm;
+    this.username = username;
+    this.type = type;
+  }
+
+  @Override
+  protected void transaction() throws Exception {
+    execute(new LogUserVisit(timestamp, realm, username, type), getAccess());
+  }
+}
diff --git a/src/main/java/org/caosdb/server/transaction/RetrieveUserTransaction.java b/src/main/java/org/caosdb/server/transaction/RetrieveUserTransaction.java
index 228b1585233ae71d3b7130196f68c3b9f83f5ed2..f66cbe695013b539093eeb81ef85f52b04fd08bd 100644
--- a/src/main/java/org/caosdb/server/transaction/RetrieveUserTransaction.java
+++ b/src/main/java/org/caosdb/server/transaction/RetrieveUserTransaction.java
@@ -89,4 +89,8 @@ public class RetrieveUserTransaction extends AccessControlTransaction {
   public boolean isActive() {
     return this.user.status == UserStatus.ACTIVE;
   }
+
+  public ProtoUser getUser() {
+    return user;
+  }
 }
diff --git a/src/test/java/org/caosdb/server/authentication/AuthTokenTest.java b/src/test/java/org/caosdb/server/authentication/AuthTokenTest.java
index 6ec17c6812f62c4ea5205b8efea488f804d8d0db..0aa2781f26c17788604c10aa4fbe48fbd08076c2 100644
--- a/src/test/java/org/caosdb/server/authentication/AuthTokenTest.java
+++ b/src/test/java/org/caosdb/server/authentication/AuthTokenTest.java
@@ -57,7 +57,7 @@ import org.caosdb.server.database.backend.interfaces.RetrieveUserImpl;
 import org.caosdb.server.grpc.AuthInterceptor;
 import org.caosdb.server.resource.TestScriptingResource.RetrievePasswordValidator;
 import org.caosdb.server.resource.TestScriptingResource.RetrievePermissionRules;
-import org.caosdb.server.resource.TestScriptingResource.RetrieveRole;
+import org.caosdb.server.resource.TestScriptingResource.RetrieveRoleMockup;
 import org.caosdb.server.resource.TestScriptingResource.RetrieveUser;
 import org.junit.Assert;
 import org.junit.Before;
@@ -73,7 +73,7 @@ public class AuthTokenTest {
 
   @BeforeClass
   public static void setupShiro() throws IOException {
-    BackendTransaction.setImpl(RetrieveRoleImpl.class, RetrieveRole.class);
+    BackendTransaction.setImpl(RetrieveRoleImpl.class, RetrieveRoleMockup.class);
     BackendTransaction.setImpl(RetrievePermissionRulesImpl.class, RetrievePermissionRules.class);
     BackendTransaction.setImpl(RetrieveUserImpl.class, RetrieveUser.class);
     BackendTransaction.setImpl(
diff --git a/src/test/java/org/caosdb/server/resource/TestScriptingResource.java b/src/test/java/org/caosdb/server/resource/TestScriptingResource.java
index 7f7434528678dbd2e1886fade2116d0c9f766740..abb5e8023ade1dcd3364b2d7ea4ef7805732444e 100644
--- a/src/test/java/org/caosdb/server/resource/TestScriptingResource.java
+++ b/src/test/java/org/caosdb/server/resource/TestScriptingResource.java
@@ -64,9 +64,9 @@ import org.restlet.representation.StringRepresentation;
 
 public class TestScriptingResource {
 
-  public static class RetrieveRole implements RetrieveRoleImpl {
+  public static class RetrieveRoleMockup implements RetrieveRoleImpl {
 
-    public RetrieveRole(Access a) {}
+    public RetrieveRoleMockup(Access a) {}
 
     @Override
     public Role retrieve(String role) throws TransactionException {
@@ -154,7 +154,7 @@ public class TestScriptingResource {
     CaosDBServer.initServerProperties();
     CaosDBServer.initShiro();
 
-    BackendTransaction.setImpl(RetrieveRoleImpl.class, RetrieveRole.class);
+    BackendTransaction.setImpl(RetrieveRoleImpl.class, RetrieveRoleMockup.class);
     BackendTransaction.setImpl(RetrievePermissionRulesImpl.class, RetrievePermissionRules.class);
     BackendTransaction.setImpl(RetrieveUserImpl.class, RetrieveUser.class);
     BackendTransaction.setImpl(