From a39d87eb3956b92ac6c985dc31c22818eaf7919d Mon Sep 17 00:00:00 2001
From: Timm Fitschen <timm.fitschen@ds.mpg.de>
Date: Mon, 19 Nov 2018 16:32:35 +0100
Subject: [PATCH] ADD anonymous user

---
 .../java/caosdb/datetime/UTCDateTime.java     |  3 +++
 .../java/caosdb/server/CaosAuthenticator.java | 25 ++++++++-----------
 src/main/java/caosdb/server/CaosDBServer.java |  6 +++--
 .../java/caosdb/server/ServerProperties.java  |  1 +
 .../accessControl/AuthenticationUtils.java    |  3 +++
 .../accessControl/CaosDBAuthorizingRealm.java | 13 ++++++----
 .../accessControl/CaosDBDefaultRealm.java     |  5 +---
 .../accessControl/SessionTokenRealm.java      |  9 +------
 .../server/accessControl/UserSources.java     | 15 +++++++++--
 .../caosdb/server/permissions/EntityACL.java  |  5 +++-
 .../AbstractCaosDBServerResource.java         |  3 ++-
 .../server/transaction/Transaction.java       |  7 ++++--
 12 files changed, 55 insertions(+), 40 deletions(-)

diff --git a/src/main/java/caosdb/datetime/UTCDateTime.java b/src/main/java/caosdb/datetime/UTCDateTime.java
index 4da38a4f..3b07af14 100644
--- a/src/main/java/caosdb/datetime/UTCDateTime.java
+++ b/src/main/java/caosdb/datetime/UTCDateTime.java
@@ -213,6 +213,9 @@ public class UTCDateTime implements Interval {
 
     // june 2015
     addLeapSecond(2015, 06, 30);
+    
+    // dec 2016
+    addLeapSecond(2016, 12, 31);
   }
 
   public long getUTCSeconds() {
diff --git a/src/main/java/caosdb/server/CaosAuthenticator.java b/src/main/java/caosdb/server/CaosAuthenticator.java
index 87290564..ed3646a0 100644
--- a/src/main/java/caosdb/server/CaosAuthenticator.java
+++ b/src/main/java/caosdb/server/CaosAuthenticator.java
@@ -22,21 +22,19 @@
  */
 package caosdb.server;
 
-import caosdb.server.accessControl.AuthenticationUtils;
-import caosdb.server.accessControl.OneTimeAuthenticationToken;
-import caosdb.server.accessControl.SessionToken;
-import caosdb.server.resource.DefaultResource;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import org.apache.shiro.SecurityUtils;
-import org.apache.shiro.authc.AccountException;
 import org.apache.shiro.authc.AuthenticationException;
-import org.apache.shiro.authc.CredentialsException;
 import org.apache.shiro.subject.Subject;
 import org.restlet.Context;
 import org.restlet.Request;
 import org.restlet.Response;
 import org.restlet.security.Authenticator;
+import caosdb.server.accessControl.AuthenticationUtils;
+import caosdb.server.accessControl.OneTimeAuthenticationToken;
+import caosdb.server.accessControl.SessionToken;
+import caosdb.server.resource.DefaultResource;
 
 public class CaosAuthenticator extends Authenticator {
 
@@ -62,11 +60,12 @@ public class CaosAuthenticator extends Authenticator {
       if (sessionToken != null) {
         subject.login(sessionToken);
       }
-    } catch (final CredentialsException e) {
-      logger.log(Level.INFO, "LOGIN_FAILED", e);
-    } catch (final AccountException e) {
-      logger.log(Level.INFO, "LOGIN_FAILED", e);
-    } catch (final AuthenticationException e) {
+      
+      // anonymous users
+      if(!subject.isAuthenticated() && CaosDBServer.getServerProperty(ServerProperties.KEY_AUTH_OPTIONAL).equalsIgnoreCase("TRUE")){
+        subject.login(AuthenticationUtils.ANONYMOUS_USER);
+      }
+    } catch (AuthenticationException e) {
       logger.log(Level.INFO, "LOGIN_FAILED", e);
     }
     return subject.isAuthenticated();
@@ -91,10 +90,6 @@ public class CaosAuthenticator extends Authenticator {
       if (oneTimeToken != null) {
         subject.login(oneTimeToken);
       }
-    } catch (final CredentialsException e) {
-      logger.log(Level.INFO, "LOGIN_FAILED", e);
-    } catch (final AccountException e) {
-      logger.log(Level.INFO, "LOGIN_FAILED", e);
     } catch (final AuthenticationException e) {
       logger.log(Level.INFO, "LOGIN_FAILED", e);
     }
diff --git a/src/main/java/caosdb/server/CaosDBServer.java b/src/main/java/caosdb/server/CaosDBServer.java
index 5f2d3140..5f907b65 100644
--- a/src/main/java/caosdb/server/CaosDBServer.java
+++ b/src/main/java/caosdb/server/CaosDBServer.java
@@ -22,6 +22,7 @@
  */
 package caosdb.server;
 
+import caosdb.server.accessControl.AnonymousRealm;
 import caosdb.server.accessControl.AuthenticationUtils;
 import caosdb.server.accessControl.CaosDBAuthorizingRealm;
 import caosdb.server.accessControl.CaosDBDefaultRealm;
@@ -154,9 +155,10 @@ public class CaosDBServer extends Application {
     mainSec.put("SessionTokenValidator", SessionTokenRealm.class.getCanonicalName());
     mainSec.put("OneTimeTokenValidator", OneTimeTokenRealm.class.getCanonicalName());
     mainSec.put("CaosDBAuthorizingRealm", CaosDBAuthorizingRealm.class.getCanonicalName());
+    mainSec.put("AnonymousRealm", AnonymousRealm.class.getCanonicalName());
     mainSec.put(
         "securityManager.realms",
-        "$CaosDB, $SessionTokenValidator, $OneTimeTokenValidator, $CaosDBAuthorizingRealm");
+        "$CaosDB, $SessionTokenValidator, $OneTimeTokenValidator, $CaosDBAuthorizingRealm, $AnonymousRealm");
 
     // disable shiro's default session management. We have quasi-stateless
     // sessions
@@ -435,7 +437,7 @@ public class CaosDBServer extends Application {
           private void setSessionCookies(final Response response) {
 
             final Subject subject = SecurityUtils.getSubject();
-            if (subject.isAuthenticated()) {
+            if (subject.isAuthenticated() && subject.getPrincipal() != AuthenticationUtils.ANONYMOUS_USER.getPrincipal()) {
               final SessionToken sessionToken =
                   SessionToken.generate((Principal) subject.getPrincipal(), null);
 
diff --git a/src/main/java/caosdb/server/ServerProperties.java b/src/main/java/caosdb/server/ServerProperties.java
index 9a33d228..c1e12afc 100644
--- a/src/main/java/caosdb/server/ServerProperties.java
+++ b/src/main/java/caosdb/server/ServerProperties.java
@@ -130,6 +130,7 @@ public class ServerProperties extends Properties {
     serverProperties.setProperty(KEY_USER_SOURCES_INI_FILE, basepath + "/conf/usersources.ini");
     serverProperties.setProperty(KEY_USER_FOLDERS, "FALSE");
     serverProperties.setProperty(KEY_NEW_USER_DEFAULT_ACTIVITY, "INACTIVE");
+    serverProperties.setProperty(KEY_AUTH_OPTIONAL, "FALSE");
 
     serverProperties.setProperty(KEY_MYSQL_HOST, "localhost");
     serverProperties.setProperty(KEY_MYSQL_PORT, "3306");
diff --git a/src/main/java/caosdb/server/accessControl/AuthenticationUtils.java b/src/main/java/caosdb/server/accessControl/AuthenticationUtils.java
index 78aeda06..d1c70c1b 100644
--- a/src/main/java/caosdb/server/accessControl/AuthenticationUtils.java
+++ b/src/main/java/caosdb/server/accessControl/AuthenticationUtils.java
@@ -36,6 +36,7 @@ import java.util.Collection;
 import java.util.LinkedList;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+import org.apache.shiro.authc.AuthenticationToken;
 import org.restlet.data.Cookie;
 import org.restlet.data.CookieSetting;
 
@@ -55,6 +56,8 @@ public class AuthenticationUtils {
   public static final String SESSION_TOKEN_COOKIE = "SessionToken";
   public static final String SESSION_TIMEOUT_COOKIE = "SessionTimeOut";
 
+  public static final AuthenticationToken ANONYMOUS_USER = AnonymousAuthenticationToken.getInstance();
+
   /**
    * Create a cookie for a {@link SelfValidatingAuthenticationToken}. Returns null if the parameter
    * is null or the token is invalid. The cookie will have the httpOnly and secure flags enabled.
diff --git a/src/main/java/caosdb/server/accessControl/CaosDBAuthorizingRealm.java b/src/main/java/caosdb/server/accessControl/CaosDBAuthorizingRealm.java
index a166d723..d23ae70e 100644
--- a/src/main/java/caosdb/server/accessControl/CaosDBAuthorizingRealm.java
+++ b/src/main/java/caosdb/server/accessControl/CaosDBAuthorizingRealm.java
@@ -22,11 +22,9 @@
  */
 package caosdb.server.accessControl;
 
-import com.google.common.base.Objects;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
-import org.apache.shiro.authc.AuthenticationException;
 import org.apache.shiro.authc.AuthenticationInfo;
 import org.apache.shiro.authc.AuthenticationToken;
 import org.apache.shiro.authz.AuthorizationInfo;
@@ -34,6 +32,7 @@ import org.apache.shiro.authz.SimpleAuthorizationInfo;
 import org.apache.shiro.realm.AuthorizingRealm;
 import org.apache.shiro.subject.PrincipalCollection;
 import org.apache.shiro.subject.SimplePrincipalCollection;
+import com.google.common.base.Objects;
 
 public class CaosDBAuthorizingRealm extends AuthorizingRealm {
 
@@ -98,6 +97,11 @@ public class CaosDBAuthorizingRealm extends AuthorizingRealm {
         return false;
       }
     }
+    
+    @Override
+    public int hashCode() {
+      return this.principalCollection.hashCode();
+    }
   }
 
   private static final CaosDBRolePermissionResolver role_permission_resolver =
@@ -116,7 +120,7 @@ public class CaosDBAuthorizingRealm extends AuthorizingRealm {
 
     // find all roles which are associated with this principal in this
     // realm.
-    final Set<String> roles = UserSources.resolve((Principal) principals.getPrimaryPrincipal());
+    final Set<String> roles = UserSources.resolve(principals);
     if (roles != null) {
       authzInfo.setRoles(roles);
 
@@ -139,8 +143,7 @@ public class CaosDBAuthorizingRealm extends AuthorizingRealm {
   }
 
   @Override
-  protected AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken token)
-      throws AuthenticationException {
+  protected AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken token) {
     return null;
   }
 }
diff --git a/src/main/java/caosdb/server/accessControl/CaosDBDefaultRealm.java b/src/main/java/caosdb/server/accessControl/CaosDBDefaultRealm.java
index 7eebee42..209487b7 100644
--- a/src/main/java/caosdb/server/accessControl/CaosDBDefaultRealm.java
+++ b/src/main/java/caosdb/server/accessControl/CaosDBDefaultRealm.java
@@ -22,7 +22,6 @@
  */
 package caosdb.server.accessControl;
 
-import org.apache.shiro.authc.AuthenticationException;
 import org.apache.shiro.authc.AuthenticationInfo;
 import org.apache.shiro.authc.AuthenticationToken;
 import org.apache.shiro.authc.SimpleAuthenticationInfo;
@@ -32,8 +31,7 @@ import org.apache.shiro.realm.AuthenticatingRealm;
 public class CaosDBDefaultRealm extends AuthenticatingRealm {
 
   @Override
-  protected AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken token)
-      throws AuthenticationException {
+  protected AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken token) {
     final String realm = ((RealmUsernamePasswordToken) token).getRealm();
     final String username = ((RealmUsernamePasswordToken) token).getUsername();
     final String password = new String(((RealmUsernamePasswordToken) token).getCredentials());
@@ -50,7 +48,6 @@ public class CaosDBDefaultRealm extends AuthenticatingRealm {
 
     setCachingEnabled(false);
     setAuthenticationCachingEnabled(false);
-    // setAuthorizationCachingEnabled(false);
     setAuthenticationTokenClass(RealmUsernamePasswordToken.class);
   }
 }
diff --git a/src/main/java/caosdb/server/accessControl/SessionTokenRealm.java b/src/main/java/caosdb/server/accessControl/SessionTokenRealm.java
index 95c3642d..97b4437d 100644
--- a/src/main/java/caosdb/server/accessControl/SessionTokenRealm.java
+++ b/src/main/java/caosdb/server/accessControl/SessionTokenRealm.java
@@ -22,7 +22,6 @@
  */
 package caosdb.server.accessControl;
 
-import org.apache.shiro.authc.AuthenticationException;
 import org.apache.shiro.authc.AuthenticationInfo;
 import org.apache.shiro.authc.AuthenticationToken;
 import org.apache.shiro.authc.SimpleAuthenticationInfo;
@@ -32,9 +31,7 @@ import org.apache.shiro.realm.AuthenticatingRealm;
 public class SessionTokenRealm extends AuthenticatingRealm {
 
   @Override
-  protected AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken token)
-      throws AuthenticationException {
-    try {
+  protected AuthenticationInfo doGetAuthenticationInfo(final AuthenticationToken token) {
 
       final SelfValidatingAuthenticationToken sessionToken =
           (SelfValidatingAuthenticationToken) token;
@@ -42,9 +39,6 @@ public class SessionTokenRealm extends AuthenticatingRealm {
       if (sessionToken.isValid()) {
         return new SimpleAuthenticationInfo(sessionToken.getPrincipal(), null, getName());
       }
-    } catch (final Exception e) {
-      e.printStackTrace();
-    }
     return null;
   }
 
@@ -53,6 +47,5 @@ public class SessionTokenRealm extends AuthenticatingRealm {
     setCredentialsMatcher(new AllowAllCredentialsMatcher());
     setCachingEnabled(false);
     setAuthenticationCachingEnabled(false);
-    // setAuthorizationCachingEnabled(false);
   }
 }
diff --git a/src/main/java/caosdb/server/accessControl/UserSources.java b/src/main/java/caosdb/server/accessControl/UserSources.java
index 55b76c46..ad6581e5 100644
--- a/src/main/java/caosdb/server/accessControl/UserSources.java
+++ b/src/main/java/caosdb/server/accessControl/UserSources.java
@@ -33,12 +33,15 @@ import caosdb.server.utils.ServerMessages;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Set;
 import java.util.logging.LogManager;
 import java.util.logging.Logger;
 import org.apache.shiro.authc.AuthenticationException;
 import org.apache.shiro.config.Ini;
+import org.apache.shiro.subject.PrincipalCollection;
 
 public class UserSources extends HashMap<String, UserSource> {
 
@@ -158,8 +161,16 @@ public class UserSources extends HashMap<String, UserSource> {
     return instance.map.getSectionProperty(Ini.DEFAULT_SECTION_NAME, KEY_DEAULT_REALM);
   }
 
-  public static Set<String> resolve(final Principal p) {
-    return resolve(p.getRealm(), p.getUsername());
+  public static Set<String> resolve(final PrincipalCollection principals) {
+    if(principals.getPrimaryPrincipal() == AuthenticationUtils.ANONYMOUS_USER.getPrincipal()){
+      // anymous has one role
+      Set<String> roles = new HashSet<>();
+      roles.add("anonymous");
+      return roles;
+    }
+    
+    Principal primaryPrincipal = (Principal) principals.getPrimaryPrincipal();
+    return resolve(primaryPrincipal.getRealm(), primaryPrincipal.getUsername());
   }
 
   public static boolean isRoleExisting(final String role) {
diff --git a/src/main/java/caosdb/server/permissions/EntityACL.java b/src/main/java/caosdb/server/permissions/EntityACL.java
index d389a216..e4037a80 100644
--- a/src/main/java/caosdb/server/permissions/EntityACL.java
+++ b/src/main/java/caosdb/server/permissions/EntityACL.java
@@ -24,7 +24,7 @@ package caosdb.server.permissions;
 
 import static caosdb.server.permissions.Role.OTHER_ROLE;
 import static caosdb.server.permissions.Role.OWNER_ROLE;
-
+import caosdb.server.accessControl.AuthenticationUtils;
 import caosdb.server.accessControl.Principal;
 import caosdb.server.database.exceptions.TransactionException;
 import java.util.ArrayList;
@@ -74,6 +74,9 @@ public class EntityACL {
   }
 
   public static final EntityACL getOwnerACLFor(final Subject subject) {
+    if(subject.getPrincipal() == AuthenticationUtils.ANONYMOUS_USER.getPrincipal()){
+      return new EntityACLFactory().create();
+    }
     return getOwnerACLFor((Principal) subject.getPrincipal());
   }
 
diff --git a/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java b/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
index fb819684..9ba1fb68 100644
--- a/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
+++ b/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java
@@ -26,6 +26,7 @@ import static caosdb.server.utils.Utils.isNonNullInteger;
 import static java.net.URLDecoder.decode;
 
 import caosdb.server.CaosDBException;
+import caosdb.server.accessControl.AuthenticationUtils;
 import caosdb.server.accessControl.Principal;
 import caosdb.server.database.backend.implementation.MySQL.ConnectionException;
 import caosdb.server.entity.Message;
@@ -155,7 +156,7 @@ public abstract class AbstractCaosDBServerResource extends ServerResource {
   protected Element generateRootElement() {
     final Element retRoot = new Element("Response");
 
-    if (getUser() != null && getUser().isAuthenticated()) {
+    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());
     }
diff --git a/src/main/java/caosdb/server/transaction/Transaction.java b/src/main/java/caosdb/server/transaction/Transaction.java
index 2a7562c1..c29efac3 100644
--- a/src/main/java/caosdb/server/transaction/Transaction.java
+++ b/src/main/java/caosdb/server/transaction/Transaction.java
@@ -23,6 +23,7 @@
 package caosdb.server.transaction;
 
 import caosdb.datetime.UTCDateTime;
+import caosdb.server.accessControl.AuthenticationUtils;
 import caosdb.server.accessControl.Principal;
 import caosdb.server.database.Database;
 import caosdb.server.database.DatabaseMonitor;
@@ -222,12 +223,14 @@ public abstract class Transaction<C extends TransactionContainer> extends Abstra
   // TODO move to post-transaction job
   private void writeHistory() throws TransactionException, Message {
     if (logHistory()) {
+      String realm = getTransactor().getPrincipal() == AuthenticationUtils.ANONYMOUS_USER.getPrincipal() ? "" :((Principal) getTransactor().getPrincipal()).getRealm();
+      String username = getTransactor().getPrincipal() == AuthenticationUtils.ANONYMOUS_USER.getPrincipal() ? "anonymous" : ((Principal) getTransactor().getPrincipal()).getUsername();
       Database.execute(
           new InsertTransactionHistory(
               getContainer(),
               this.getClass().getSimpleName(),
-              ((Principal) getTransactor().getPrincipal()).getRealm(),
-              ((Principal) getTransactor().getPrincipal()).getUsername(),
+              realm,
+              username,
               getTimestamp()),
           getAccess());
     }
-- 
GitLab