From 206b6f89f47ec8fc2b87bf05941e23c9bdb7ebf6 Mon Sep 17 00:00:00 2001
From: Timm Fitschen <timm.fitschen@ds.mpg.de>
Date: Tue, 4 Dec 2018 01:33:07 +0100
Subject: [PATCH] ENH: UserInfo in Server Response

---
 .../server/accessControl/UserSources.java     |  6 ++
 .../AbstractCaosDBServerResource.java         | 71 +++++++++++++++---
 .../resource/TestCaseFileSystemResource.java  |  4 +-
 .../server/resource/TestCaseResource.java     |  4 +-
 .../TestAbstractCaosDBServerResource.java     | 74 +++++++++++++++++++
 5 files changed, 145 insertions(+), 14 deletions(-)
 create mode 100644 src/test/java/caosdb/server/resource/TestAbstractCaosDBServerResource.java

diff --git a/src/main/java/caosdb/server/accessControl/UserSources.java b/src/main/java/caosdb/server/accessControl/UserSources.java
index 44a540da..3aea63cb 100644
--- a/src/main/java/caosdb/server/accessControl/UserSources.java
+++ b/src/main/java/caosdb/server/accessControl/UserSources.java
@@ -120,6 +120,12 @@ public class UserSources extends HashMap<String, UserSource> {
     }
   }
 
+  /**
+   * Return the roles of a given user.
+   * @param realm
+   * @param username
+   * @return
+   */
   public static Set<String> resolve(String realm, final String username) {
 
     if (realm == null) {
diff --git a/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java b/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
index 27d81e72..a9a48545 100644
--- a/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
+++ b/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
@@ -28,6 +28,7 @@ import static java.net.URLDecoder.decode;
 import caosdb.server.CaosDBException;
 import caosdb.server.accessControl.AuthenticationUtils;
 import caosdb.server.accessControl.Principal;
+import caosdb.server.accessControl.UserSources;
 import caosdb.server.database.backend.implementation.MySQL.ConnectionException;
 import caosdb.server.entity.Message;
 import caosdb.server.utils.ServerMessages;
@@ -74,7 +75,7 @@ public abstract class AbstractCaosDBServerResource extends ServerResource {
   private Long timestamp = null;
   private static final XMLParser xmlparser = new XMLParser();
   protected String sRID = null;
-  protected String cRID = null;
+  private String cRID = null;
   private String[] requestedItems = null;
   private ArrayList<Integer> requestedIDs = new ArrayList<Integer>();
   private ArrayList<String> requestedNames = new ArrayList<String>();
@@ -109,7 +110,7 @@ public abstract class AbstractCaosDBServerResource extends ServerResource {
 
     final Series<Header> headers = getRequest().getHeaders();
 
-    this.cRID = headers.getFirstValue("crequestid");
+    this.setCRID(headers.getFirstValue("crequestid"));
 
     String specifier = (String) getRequestAttributes().get("specifier");
     if (specifier != null && !specifier.equals("")) {
@@ -157,21 +158,63 @@ public abstract class AbstractCaosDBServerResource extends ServerResource {
   protected Element generateRootElement() {
     final Element retRoot = new Element("Response");
 
-    if (getUser() != null
-        && getUser().isAuthenticated()
-        && !getUser().getPrincipal().equals(AuthenticationUtils.ANONYMOUS_USER.getPrincipal())) {
-      retRoot.setAttribute("username", ((Principal) getUser().getPrincipal()).getUsername());
-      retRoot.setAttribute("realm", ((Principal) getUser().getPrincipal()).getRealm());
-    }
+    addUserInfo(retRoot, getUser());
     retRoot.setAttribute("srid", getSRID());
-    if (this.cRID != null) {
-      retRoot.setAttribute("crid", this.cRID);
+    if (this.getCRID() != null) {
+      retRoot.setAttribute("crid", this.getCRID());
     }
     retRoot.setAttribute("timestamp", getTimestamp().toString());
     retRoot.setAttribute("baseuri", getRootRef().toString());
     return retRoot;
   }
 
+  /**
+   * Add the user info to the Response Element.
+   * @param retRoot
+   * @param user
+   */
+  private void addUserInfo(Element retRoot, Subject user) {
+    
+    if (user != null && user.isAuthenticated()){
+      Element userInfo = new Element("UserInfo");
+      if(!user.getPrincipal().equals(AuthenticationUtils.ANONYMOUS_USER.getPrincipal())) {
+        // TODO: deprecated
+        addNameAndRealm(retRoot, user);
+        
+        // this is the new, correct way
+        addNameAndRealm(userInfo, user);
+      } 
+      
+      addRoles(userInfo, user);
+    }
+  }
+  
+  /**
+   * Add all roles of the current user to the user info, like this:
+   * `<UserInfo><Roles><Role>role1</Role><Role>role2</Role>...</Roles></UserInfo>`
+   * 
+   * @param userInfo
+   * @param user
+   */
+  private void addRoles(Element userInfo, Subject user) {
+    Element roles = new Element("Roles");
+    for(String role : UserSources.resolve(user.getPrincipals())){
+      Element r = new Element("Role");
+      r.addContent(role);
+      roles.addContent(r);
+    }
+    userInfo.addContent(roles);
+  }
+
+  /**
+   * Add the username and the realm of the current user to the user info (as attributes).
+   * @param userInfo
+   */
+  private void addNameAndRealm(Element userInfo, Subject user) {
+    userInfo.setAttribute("username", ((Principal) user.getPrincipal()).getUsername());
+    userInfo.setAttribute("realm", ((Principal) user.getPrincipal()).getRealm());
+  }
+
   @Get
   public Representation httpGet() {
     try {
@@ -354,6 +397,14 @@ public abstract class AbstractCaosDBServerResource extends ServerResource {
     return root;
   }
 
+  public String getCRID() {
+    return cRID;
+  }
+
+  public void setCRID(String cRID) {
+    this.cRID = cRID;
+  }
+
   public static class XMLParser {
     private final LinkedList<SAXBuilder> pool = new LinkedList<SAXBuilder>();
     private final int max = 25;
diff --git a/src/main/java/caosdb/server/resource/TestCaseFileSystemResource.java b/src/main/java/caosdb/server/resource/TestCaseFileSystemResource.java
index d239d570..903ef0d5 100644
--- a/src/main/java/caosdb/server/resource/TestCaseFileSystemResource.java
+++ b/src/main/java/caosdb/server/resource/TestCaseFileSystemResource.java
@@ -58,8 +58,8 @@ public class TestCaseFileSystemResource extends FileSystemResource {
       retRoot.setAttribute("realm", ((Principal) getUser().getPrincipal()).getRealm());
     }
     retRoot.setAttribute("srid", getSRID());
-    if (this.cRID != null) {
-      retRoot.setAttribute("crid", this.cRID);
+    if (this.getCRID() != null) {
+      retRoot.setAttribute("crid", this.getCRID());
     }
     retRoot.setAttribute("timestamp", getTimestamp().toString());
     retRoot.setAttribute("baseuri", getRootRef().toString() + "/TestCase/");
diff --git a/src/main/java/caosdb/server/resource/TestCaseResource.java b/src/main/java/caosdb/server/resource/TestCaseResource.java
index ed7e5461..e8aef1f0 100644
--- a/src/main/java/caosdb/server/resource/TestCaseResource.java
+++ b/src/main/java/caosdb/server/resource/TestCaseResource.java
@@ -84,8 +84,8 @@ public class TestCaseResource extends AbstractCaosDBServerResource {
     retRoot.setAttribute("username", "TestUser");
     retRoot.setAttribute("realm", "PAM");
     retRoot.setAttribute("srid", getSRID());
-    if (this.cRID != null) {
-      retRoot.setAttribute("crid", this.cRID);
+    if (this.getCRID() != null) {
+      retRoot.setAttribute("crid", this.getCRID());
     }
     retRoot.setAttribute("timestamp", getTimestamp().toString());
     retRoot.setAttribute("baseuri", getRootRef().toString() + "/TestCase/");
diff --git a/src/test/java/caosdb/server/resource/TestAbstractCaosDBServerResource.java b/src/test/java/caosdb/server/resource/TestAbstractCaosDBServerResource.java
new file mode 100644
index 00000000..034ac59d
--- /dev/null
+++ b/src/test/java/caosdb/server/resource/TestAbstractCaosDBServerResource.java
@@ -0,0 +1,74 @@
+package caosdb.server.resource;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.sql.SQLException;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authc.SimpleAccount;
+import org.apache.shiro.mgt.DefaultSecurityManager;
+import org.apache.shiro.session.mgt.DelegatingSession;
+import org.apache.shiro.subject.Subject;
+import org.apache.shiro.subject.support.DelegatingSubject;
+import org.jdom2.Element;
+import org.junit.Test;
+import org.restlet.data.Reference;
+import org.restlet.representation.Representation;
+import caosdb.server.CaosDBException;
+import caosdb.server.accessControl.AnonymousAuthenticationToken;
+import caosdb.server.accessControl.AnonymousRealm;
+import caosdb.server.accessControl.CaosDBAuthorizingRealm;
+import caosdb.server.database.backend.implementation.MySQL.ConnectionException;
+
+public class TestAbstractCaosDBServerResource {
+
+ @Test 
+  public void testReponseRootElement(){
+    final Subject user = new DelegatingSubject(new DefaultSecurityManager(new AnonymousRealm()));
+    user.login(AnonymousAuthenticationToken.getInstance());
+    AbstractCaosDBServerResource s = new AbstractCaosDBServerResource() {
+      
+      @Override
+      protected Representation httpGetInChildClass() throws ConnectionException, IOException,
+          SQLException, CaosDBException, NoSuchAlgorithmException, Exception {
+        // TODO Auto-generated method stub
+        return null;
+      }
+      
+      @Override
+      public String getSRID() {
+        return "TEST-SRID";
+      }
+
+      @Override
+      public String getCRID() {
+        return "TEST-CRID";
+      }
+      
+      @Override
+      public Long getTimestamp() {
+        return 0L;
+      }
+      
+      @Override
+      public Reference getRootRef() {
+        return new Reference("https://example.com/root/");
+      }
+      
+      @Override
+      public Subject getUser() {
+        // TODO Auto-generated method stub
+        return user;
+      }
+    };
+    Element response = s.generateRootElement();
+    assertNotNull(response);
+    assertEquals("TEST-SRID", response.getAttribute("srid").getValue());
+    assertEquals("TEST-CRID", response.getAttribute("crid").getValue());
+    assertEquals("0", response.getAttribute("timestamp").getValue());
+    assertEquals("https://example.com/root/", response.getAttributeValue("baseuri"));
+    Element userInfo = response.getChild("UserInfo");
+    assertNotNull(userInfo);
+    }
+}
-- 
GitLab