diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4a4083ceccc44ec0806916c47a1eb0b055f9484c..fa6f59e5281bfcb9b4fbc2ad1e558afbcdf00071 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -81,6 +81,7 @@ test:server: dependencies: - build:server script: + - echo "defaultRealm = CaosDB" > conf/usersources.ini - mvn test ########### diff --git a/README.md b/README.md index 6af9cfc463d86618ab0457a4c32ee51bc93046a0..dab09859fc312a91641154515ec9b98e777dae6c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +<!--THIS FILE HAS BEEN GENERATED BY A SCRIPT. PLEASE DON'T CHANGE IT MANUALLY.--> + # Welcome This is the **CaosDB Server** repository and a part of the CaosDB project. diff --git a/caosdb-webui b/caosdb-webui index 7df3c8cdf945e5b98df8b04a084fabbdaf058946..32fa3b8af945d4d0bf4e98818b2456bdc8f375c0 160000 --- a/caosdb-webui +++ b/caosdb-webui @@ -1 +1 @@ -Subproject commit 7df3c8cdf945e5b98df8b04a084fabbdaf058946 +Subproject commit 32fa3b8af945d4d0bf4e98818b2456bdc8f375c0 diff --git a/src/main/java/caosdb/datetime/UTCDateTime.java b/src/main/java/caosdb/datetime/UTCDateTime.java index 4da38a4f4ca93635533b305d608811494a657bff..846c8f9f266d4b834314b95e00973993d9031224 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 87290564a0d6bc26d5d976d528e006f87ed34fd5..0cc6cc3ddfedf6e45a708d3eeed2ed99b1b076d2 100644 --- a/src/main/java/caosdb/server/CaosAuthenticator.java +++ b/src/main/java/caosdb/server/CaosAuthenticator.java @@ -29,9 +29,7 @@ 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; @@ -62,13 +60,15 @@ 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) { + } catch (AuthenticationException e) { logger.log(Level.INFO, "LOGIN_FAILED", e); } + // anonymous users + if (!subject.isAuthenticated() + && CaosDBServer.getServerProperty(ServerProperties.KEY_AUTH_OPTIONAL) + .equalsIgnoreCase("TRUE")) { + subject.login(AuthenticationUtils.ANONYMOUS_USER); + } return subject.isAuthenticated(); } @@ -91,10 +91,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 811137780cee9c83e787af92d0d9f50b67272ed0..2aa6ad3a5b6d89aa74bc10d2654327f5919aca45 100644 --- a/src/main/java/caosdb/server/CaosDBServer.java +++ b/src/main/java/caosdb/server/CaosDBServer.java @@ -19,6 +19,7 @@ */ package caosdb.server; +import caosdb.server.accessControl.AnonymousRealm; import caosdb.server.accessControl.AuthenticationUtils; import caosdb.server.accessControl.CaosDBAuthorizingRealm; import caosdb.server.accessControl.CaosDBDefaultRealm; @@ -47,6 +48,7 @@ import caosdb.server.resource.PermissionRulesResource; import caosdb.server.resource.RolesResource; import caosdb.server.resource.ScriptingResource; import caosdb.server.resource.ServerLogsResource; +import caosdb.server.resource.ServerPropertiesResource; import caosdb.server.resource.TestCaseFileSystemResource; import caosdb.server.resource.TestCaseResource; import caosdb.server.resource.ThumbnailsResource; @@ -110,10 +112,7 @@ public class CaosDBServer extends Application { private static boolean INSECURE = false; public static String getServerProperty(final String key) { - if (SERVER_PROPERTIES == null) { - SERVER_PROPERTIES = ServerProperties.initServerProperties(); - } - return SERVER_PROPERTIES.getProperty(key); + return getServerProperties().getProperty(key); } /** @@ -152,9 +151,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 @@ -277,17 +277,14 @@ public class CaosDBServer extends Application { // Create an application (this class). final Application application = new CaosDBServer(); - application.getStatusService().setContactEmail("timm.fitschen@ds.mpg.de"); application .getStatusService() - .setHomeRef(new Reference(getServerProperty(ServerProperties.KEY_CONTEXT_ROOT) + "/")); - - // Attach the application to the component with a defined contextRoot. - application.getStatusService().setContactEmail("timm.fitschen@ds.mpg.de"); + .setContactEmail(getServerProperty(ServerProperties.KEY_ADMIN_EMAIL)); application .getStatusService() .setHomeRef(new Reference(getServerProperty(ServerProperties.KEY_CONTEXT_ROOT) + "/")); + // Attach the application to the component with a defined contextRoot. component .getDefaultHost() .attach(getServerProperty(ServerProperties.KEY_CONTEXT_ROOT), application); @@ -433,7 +430,8 @@ 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); @@ -578,6 +576,7 @@ public class CaosDBServer extends Application { .setMatchingMode(Template.MODE_STARTS_WITH); protectedRouter.attach("/login?username={username}", AuthenticationResource.class); protectedRouter.attach("/logout", LogoutResource.class); + protectedRouter.attach("/_server_properties", ServerPropertiesResource.class); protectedRouter.attachDefault(DefaultResource.class); /* @@ -708,6 +707,13 @@ public class CaosDBServer extends Application { } SERVER_PROPERTIES.setProperty(key, value); } + + public static Properties getServerProperties() { + if (SERVER_PROPERTIES == null) { + SERVER_PROPERTIES = ServerProperties.initServerProperties(); + } + return SERVER_PROPERTIES; + } } class CaosDBComponent extends Component { diff --git a/src/main/java/caosdb/server/ServerProperties.java b/src/main/java/caosdb/server/ServerProperties.java index 011c9a6f599b596895298b2b11b54f9d7351665c..a4f93ec1c1207e21b0d282974fbf23c1a028053e 100644 --- a/src/main/java/caosdb/server/ServerProperties.java +++ b/src/main/java/caosdb/server/ServerProperties.java @@ -137,6 +137,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"); @@ -144,7 +145,7 @@ public class ServerProperties extends Properties { serverProperties.setProperty(KEY_MYSQL_USER_NAME, "CaosDB"); serverProperties.setProperty(KEY_MYSQL_USER_PASSWORD, "CaosDB"); - serverProperties.setProperty(KEY_MYSQL_SCHEMA_VERSION, "v2.0.30"); + serverProperties.setProperty(KEY_MYSQL_SCHEMA_VERSION, "v2.1.0"); serverProperties.setProperty(KEY_BASE_PATH, basepath); serverProperties.setProperty(KEY_CONTEXT_ROOT, ""); diff --git a/src/main/java/caosdb/server/ServerPropertiesSerializer.java b/src/main/java/caosdb/server/ServerPropertiesSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..10189f99e2ff30a2d9306ad9dc007810607747dd --- /dev/null +++ b/src/main/java/caosdb/server/ServerPropertiesSerializer.java @@ -0,0 +1,36 @@ +package caosdb.server; + +import caosdb.server.utils.Serializer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Properties; +import org.jdom2.Element; + +public class ServerPropertiesSerializer implements Serializer<Properties, Element> { + + private boolean sort; + + public ServerPropertiesSerializer(boolean sort) { + this.sort = sort; + } + + public ServerPropertiesSerializer() { + this(true); + } + + @Override + public Element serialize(Properties object) { + Element root = new Element("Properties"); + List<String> names = new ArrayList<>(object.stringPropertyNames()); + if (sort) { + Collections.sort(names); + } + for (String prop : names) { + Element propElem = new Element(prop); + propElem.addContent(object.getProperty(prop)); + root.addContent(propElem); + } + return root; + } +} diff --git a/src/main/java/caosdb/server/accessControl/ACMPermissions.java b/src/main/java/caosdb/server/accessControl/ACMPermissions.java index 40b063f16932c457fe176006c5b7f438a7a2252e..29988106b842fbd2b825899093e102a7448e8951 100644 --- a/src/main/java/caosdb/server/accessControl/ACMPermissions.java +++ b/src/main/java/caosdb/server/accessControl/ACMPermissions.java @@ -24,6 +24,7 @@ package caosdb.server.accessControl; public class ACMPermissions { + public static final String PERMISSION_ACCESS_SERVER_PROPERTIES = "ACCESS_SERVER_PROPERTIES"; public static final String PERMISSION_RETRIEVE_SERVERLOGS = "SERVERLOGS:RETRIEVE"; public static final String PERMISSION_RETRIEVE_USER_ROLES( diff --git a/src/main/java/caosdb/server/accessControl/AnonymousAuthenticationToken.java b/src/main/java/caosdb/server/accessControl/AnonymousAuthenticationToken.java new file mode 100644 index 0000000000000000000000000000000000000000..cd3f86f61eb66759b3eb7d0c91c29dc23637000b --- /dev/null +++ b/src/main/java/caosdb/server/accessControl/AnonymousAuthenticationToken.java @@ -0,0 +1,53 @@ +/* + * ** 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 + * + * 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 caosdb.server.accessControl; + +import org.apache.shiro.authc.AuthenticationToken; + +public class AnonymousAuthenticationToken implements AuthenticationToken { + + private static final long serialVersionUID = 1424325396819592888L; + private static final AnonymousAuthenticationToken INSTANCE = new AnonymousAuthenticationToken(); + public static final Object PRINCIPAL = new Object(); + + private AnonymousAuthenticationToken() {} + + public static AnonymousAuthenticationToken getInstance() { + return INSTANCE; + } + + @Override + public Object getPrincipal() { + return PRINCIPAL; + } + + @Override + public Object getCredentials() { + return null; + } + + @Override + public boolean equals(Object obj) { + return obj == this; + } +} diff --git a/src/main/java/caosdb/server/accessControl/AnonymousRealm.java b/src/main/java/caosdb/server/accessControl/AnonymousRealm.java new file mode 100644 index 0000000000000000000000000000000000000000..831d45a727c95277c9d64624e136b2a2e120b3b4 --- /dev/null +++ b/src/main/java/caosdb/server/accessControl/AnonymousRealm.java @@ -0,0 +1,44 @@ +/* + * ** 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 + * + * 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 caosdb.server.accessControl; + +import org.apache.shiro.authc.AuthenticationInfo; +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; + +public class AnonymousRealm extends AuthenticatingRealm { + + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) { + return new SimpleAuthenticationInfo(token.getPrincipal(), null, getName()); + } + + public AnonymousRealm() { + setAuthenticationTokenClass(AnonymousAuthenticationToken.class); + setCredentialsMatcher(new AllowAllCredentialsMatcher()); + setCachingEnabled(false); + setAuthenticationCachingEnabled(false); + } +} diff --git a/src/main/java/caosdb/server/accessControl/AuthenticationUtils.java b/src/main/java/caosdb/server/accessControl/AuthenticationUtils.java index 78aeda064ba413328f9e10d40e507e3fe132c441..31d180a452aafad393fe9d1619705e0e2b49dd9f 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,9 @@ 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 a166d723654f1089734f42d405027ad297464e7c..5cfa425ac235405a0c861e54c9d97ae8ffab58f5 100644 --- a/src/main/java/caosdb/server/accessControl/CaosDBAuthorizingRealm.java +++ b/src/main/java/caosdb/server/accessControl/CaosDBAuthorizingRealm.java @@ -26,7 +26,6 @@ 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; @@ -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 7eebee4244eb9d59f86ce7a4263503a91ec12c9a..209487b751c0b73b5fd9085827295d5eb2ce831d 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 95c3642ddbac498be414b3293b8161beb580d4a5..6ee72d0295153051e5ad31a6fd0fa092ab53d6e3 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,18 +31,13 @@ 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; + final SelfValidatingAuthenticationToken sessionToken = + (SelfValidatingAuthenticationToken) token; - if (sessionToken.isValid()) { - return new SimpleAuthenticationInfo(sessionToken.getPrincipal(), null, getName()); - } - } catch (final Exception e) { - e.printStackTrace(); + if (sessionToken.isValid()) { + return new SimpleAuthenticationInfo(sessionToken.getPrincipal(), null, getName()); } 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 55b76c4653f7a7339b33bf98cd5f13c7fc6e574a..32b851e8a69f9382c589ceabdafc419bcaaf2c47 100644 --- a/src/main/java/caosdb/server/accessControl/UserSources.java +++ b/src/main/java/caosdb/server/accessControl/UserSources.java @@ -34,11 +34,13 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; 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> { @@ -118,6 +120,13 @@ 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) { @@ -158,8 +167,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/database/BackendTransaction.java b/src/main/java/caosdb/server/database/BackendTransaction.java index a551d5b184ef9c35045697f6f36196665cf648c0..1d5d5c2e68274e664c392a24a901aa4bcd65623b 100644 --- a/src/main/java/caosdb/server/database/BackendTransaction.java +++ b/src/main/java/caosdb/server/database/BackendTransaction.java @@ -34,6 +34,7 @@ import caosdb.server.database.backend.implementation.MySQL.MySQLGetFileRecordByP import caosdb.server.database.backend.implementation.MySQL.MySQLGetIDByName; import caosdb.server.database.backend.implementation.MySQL.MySQLGetInfo; import caosdb.server.database.backend.implementation.MySQL.MySQLGetUpdateableChecksums; +import caosdb.server.database.backend.implementation.MySQL.MySQLInsertEntityDatatype; import caosdb.server.database.backend.implementation.MySQL.MySQLInsertEntityProperties; import caosdb.server.database.backend.implementation.MySQL.MySQLInsertLinCon; import caosdb.server.database.backend.implementation.MySQL.MySQLInsertLogRecord; @@ -87,6 +88,7 @@ import caosdb.server.database.backend.interfaces.GetFileRecordByPathImpl; import caosdb.server.database.backend.interfaces.GetIDByNameImpl; import caosdb.server.database.backend.interfaces.GetInfoImpl; import caosdb.server.database.backend.interfaces.GetUpdateableChecksumsImpl; +import caosdb.server.database.backend.interfaces.InsertEntityDatatypeImpl; import caosdb.server.database.backend.interfaces.InsertEntityPropertiesImpl; import caosdb.server.database.backend.interfaces.InsertLinConImpl; import caosdb.server.database.backend.interfaces.InsertLogRecordImpl; @@ -131,13 +133,11 @@ public abstract class BackendTransaction implements Undoable { BackendTransaction parent = null; private static HashMap< Class<? extends BackendTransactionImpl>, Class<? extends BackendTransactionImpl>> - impl = - new HashMap< - Class<? extends BackendTransactionImpl>, Class<? extends BackendTransactionImpl>>(); + impl = new HashMap<>(); - protected abstract void execute() throws TransactionException; + protected abstract void execute(); - final void executeTransaction() throws TransactionException { + final void executeTransaction() { final long t1 = System.currentTimeMillis(); execute(); final long t2 = System.currentTimeMillis(); @@ -194,10 +194,11 @@ public abstract class BackendTransaction implements Undoable { setImpl(SetQueryTemplateDefinitionImpl.class, MySQLSetQueryTemplateDefinition.class); setImpl( RetrieveQueryTemplateDefinitionImpl.class, MySQLRetrieveQueryTemplateDefinition.class); + setImpl(InsertEntityDatatypeImpl.class, MySQLInsertEntityDatatype.class); } } - protected <K extends BackendTransaction> K execute(final K t) throws TransactionException { + protected <K extends BackendTransaction> K execute(final K t) { assert t != this; this.undoHandler.append(t); t.setAccess(this.access); @@ -219,8 +220,7 @@ public abstract class BackendTransaction implements Undoable { } @SuppressWarnings("unchecked") - protected <T extends BackendTransactionImpl> T getImplementation(final Class<T> clz) - throws TransactionException { + protected <T extends BackendTransactionImpl> T getImplementation(final Class<T> clz) { init(); try { final T ret = (T) impl.get(clz).getConstructor(Access.class).newInstance(this.access); diff --git a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityDatatype.java b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityDatatype.java new file mode 100644 index 0000000000000000000000000000000000000000..a2682e80b1997b53496e3ac2f8e6147726fb77c9 --- /dev/null +++ b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLInsertEntityDatatype.java @@ -0,0 +1,52 @@ +package caosdb.server.database.backend.implementation.MySQL; + +import caosdb.server.database.access.Access; +import caosdb.server.database.backend.interfaces.InsertEntityDatatypeImpl; +import caosdb.server.database.exceptions.IntegrityException; +import caosdb.server.database.exceptions.TransactionException; +import caosdb.server.database.proto.SparseEntity; +import java.sql.PreparedStatement; +import java.sql.SQLIntegrityConstraintViolationException; + +public class MySQLInsertEntityDatatype extends MySQLTransaction + implements InsertEntityDatatypeImpl { + + public MySQLInsertEntityDatatype(Access access) { + super(access); + } + + public static final String STMT_INSERT_ENTITY_DATATYPE = + "INSERT INTO data_type (domain_id, entity_id, property_id, datatype) SELECT 0, 0, ?, ( SELECT id from entities where name = ? LIMIT 1);"; + public static final String STMT_INSERT_ENTITY_COLLECTION = + "INSERT INTO collection_type (domain_id, entity_id, property_id, collection) SELECT 0, 0, ?, ?;"; + + @Override + public void execute(final SparseEntity entity) { + try { + final PreparedStatement insertEntityDatatypeStmt = + prepareStatement(STMT_INSERT_ENTITY_DATATYPE); + + insertEntityDatatypeStmt.setInt(1, entity.id); + insertEntityDatatypeStmt.setString(2, entity.datatype); + + insertEntityDatatypeStmt.execute(); + + if (entity.collection != null) { + final PreparedStatement insertEntityCollectionStmt = + prepareStatement(STMT_INSERT_ENTITY_COLLECTION); + + insertEntityCollectionStmt.setInt(1, entity.id); + insertEntityCollectionStmt.setString(2, entity.collection); + + insertEntityCollectionStmt.execute(); + } + + } catch (final SQLIntegrityConstraintViolationException exc) { + throw new IntegrityException(exc); + } catch (final TransactionException exc) { + throw exc; + } catch (final Exception exc) { + throw new TransactionException(exc); + } + } +} diff --git a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java index dbe3893945ae09cadfda0657f9d22d02bd6a9371..4ad9f5b6a3cc3e51c05bc83bbc9332807b79484d 100644 --- a/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java +++ b/src/main/java/caosdb/server/database/backend/implementation/MySQL/MySQLInsertSparseEntity.java @@ -38,12 +38,12 @@ public class MySQLInsertSparseEntity extends MySQLTransaction implements InsertS super(access); } - public static final String STMT_INSERT_SPARSE_ENTITY = "call insertEntity(?,?,?,?,?,?)"; + public static final String STMT_INSERT_SPARSE_ENTITY = "call insertEntity(?,?,?,?)"; public static final String STMT_INSERT_FILE_PROPERTIES = "INSERT INTO files (file_id, hash, size, path) VALUES (?, unhex(?), ?, ?);"; @Override - public void execute(final SparseEntity entity) throws TransactionException { + public void execute(final SparseEntity entity) { try { final PreparedStatement insertEntityStmt = prepareStatement(STMT_INSERT_SPARSE_ENTITY); final PreparedStatement insertFilePropsStmt = prepareStatement(STMT_INSERT_FILE_PROPERTIES); @@ -51,15 +51,14 @@ public class MySQLInsertSparseEntity extends MySQLTransaction implements InsertS insertEntityStmt.setString(1, entity.name); insertEntityStmt.setString(2, entity.description); insertEntityStmt.setString(3, entity.role); - insertEntityStmt.setString(4, entity.datatype); - insertEntityStmt.setString(5, entity.collection); - insertEntityStmt.setString(6, entity.acl); + insertEntityStmt.setString(4, entity.acl); - final ResultSet rs = insertEntityStmt.executeQuery(); - if (rs.next()) { - entity.id = rs.getInt("EntityID"); - } else { - throw new TransactionException("Didn't get new EntityID back."); + try (final ResultSet rs = insertEntityStmt.executeQuery()) { + if (rs.next()) { + entity.id = rs.getInt("EntityID"); + } else { + throw new TransactionException("Didn't get new EntityID back."); + } } if (entity.filePath != null) { diff --git a/src/main/java/caosdb/server/database/backend/interfaces/InsertEntityDatatypeImpl.java b/src/main/java/caosdb/server/database/backend/interfaces/InsertEntityDatatypeImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..2f6f7c859281344997e1c781e9a56aafa5d03fc4 --- /dev/null +++ b/src/main/java/caosdb/server/database/backend/interfaces/InsertEntityDatatypeImpl.java @@ -0,0 +1,8 @@ +package caosdb.server.database.backend.interfaces; + +import caosdb.server.database.proto.SparseEntity; +import caosdb.server.utils.Undoable; + +public interface InsertEntityDatatypeImpl extends BackendTransactionImpl, Undoable { + public abstract void execute(SparseEntity entity); +} diff --git a/src/main/java/caosdb/server/database/backend/interfaces/InsertSparseEntityImpl.java b/src/main/java/caosdb/server/database/backend/interfaces/InsertSparseEntityImpl.java index 7ba147058b8c4675d58b66a5c11b7dd66311cabf..8ad9a9f7ac0608c4e1e51ba4783a985d0e20d985 100644 --- a/src/main/java/caosdb/server/database/backend/interfaces/InsertSparseEntityImpl.java +++ b/src/main/java/caosdb/server/database/backend/interfaces/InsertSparseEntityImpl.java @@ -22,11 +22,10 @@ */ package caosdb.server.database.backend.interfaces; -import caosdb.server.database.exceptions.TransactionException; import caosdb.server.database.proto.SparseEntity; import caosdb.server.utils.Undoable; public interface InsertSparseEntityImpl extends BackendTransactionImpl, Undoable { - public abstract void execute(SparseEntity entity) throws TransactionException; + public abstract void execute(SparseEntity entity); } diff --git a/src/main/java/caosdb/server/database/backend/transaction/InsertEntity.java b/src/main/java/caosdb/server/database/backend/transaction/InsertEntity.java index 65611a4193e8bb494666fb91c3a07a59b4aabd99..e258504908b44c8054a56315fa26e8dacae8b023 100644 --- a/src/main/java/caosdb/server/database/backend/transaction/InsertEntity.java +++ b/src/main/java/caosdb/server/database/backend/transaction/InsertEntity.java @@ -23,7 +23,6 @@ package caosdb.server.database.backend.transaction; import caosdb.server.database.BackendTransaction; -import caosdb.server.database.exceptions.TransactionException; import caosdb.server.entity.EntityInterface; import caosdb.server.entity.container.TransactionContainer; import caosdb.server.utils.EntityStatus; @@ -37,12 +36,17 @@ public class InsertEntity extends BackendTransaction { } @Override - public void execute() throws TransactionException { + public void execute() { for (final EntityInterface newEntity : this.container) { if (newEntity.getEntityStatus() == EntityStatus.QUALIFIED) { execute(new InsertSparseEntity(newEntity)); } } + for (final EntityInterface newEntity : this.container) { + if (newEntity.getEntityStatus() == EntityStatus.QUALIFIED && newEntity.hasDatatype()) { + execute(new InsertEntityDatatype(newEntity)); + } + } for (final EntityInterface newEntity : this.container) { if (newEntity.getEntityStatus() == EntityStatus.QUALIFIED) { if (newEntity.getQueryTemplateDefinition() != null) { @@ -51,7 +55,6 @@ public class InsertEntity extends BackendTransaction { if (newEntity.hasFileProperties()) { execute(new InsertFile(newEntity)); } - // execute(new InsertEntityValue(newEntity)); execute(new InsertEntityProperties(newEntity)); execute(new InsertParents(newEntity)); diff --git a/src/main/java/caosdb/server/database/backend/transaction/InsertEntityDatatype.java b/src/main/java/caosdb/server/database/backend/transaction/InsertEntityDatatype.java new file mode 100644 index 0000000000000000000000000000000000000000..39c46583cc277eb81128d9fae22d97e296d8bbc1 --- /dev/null +++ b/src/main/java/caosdb/server/database/backend/transaction/InsertEntityDatatype.java @@ -0,0 +1,36 @@ +package caosdb.server.database.backend.transaction; + +import static caosdb.server.transaction.Transaction.ERROR_INTEGRITY_VIOLATION; + +import caosdb.server.database.BackendTransaction; +import caosdb.server.database.backend.interfaces.InsertEntityDatatypeImpl; +import caosdb.server.database.exceptions.IntegrityException; +import caosdb.server.database.proto.SparseEntity; +import caosdb.server.entity.EntityInterface; +import caosdb.server.utils.EntityStatus; + +public class InsertEntityDatatype extends BackendTransaction { + + private final EntityInterface entity; + + public InsertEntityDatatype(final EntityInterface entity) { + this.entity = entity; + } + + @Override + public void execute() { + final InsertEntityDatatypeImpl t = getImplementation(InsertEntityDatatypeImpl.class); + + final SparseEntity e = this.entity.getSparseEntity(); + + try { + t.execute(e); + } catch (final IntegrityException exc) { + this.entity.addError(ERROR_INTEGRITY_VIOLATION); + this.entity.setEntityStatus(EntityStatus.CORRUPT); + throw exc; + } + + this.entity.setId(e.id); + } +} diff --git a/src/main/java/caosdb/server/entity/FileProperties.java b/src/main/java/caosdb/server/entity/FileProperties.java index 7263b9b2b5e14f51988fa13dd969cf47f941478c..fbf8031f56566ea5a6df263d97611812bab113d0 100644 --- a/src/main/java/caosdb/server/entity/FileProperties.java +++ b/src/main/java/caosdb/server/entity/FileProperties.java @@ -216,8 +216,7 @@ public class FileProperties { }; } - private static Undoable delete(final File file) - throws IOException, InterruptedException { + private static Undoable delete(final File file) throws IOException, InterruptedException { if (file.getAbsolutePath().startsWith(FileSystem.getBasepath())) { final Undoable d; final File parent = file.getParentFile(); diff --git a/src/main/java/caosdb/server/permissions/EntityACL.java b/src/main/java/caosdb/server/permissions/EntityACL.java index d389a2163aeb46c013f8845abda104779a67d8eb..79008947823e9625283c03e1dd539328a58fef7f 100644 --- a/src/main/java/caosdb/server/permissions/EntityACL.java +++ b/src/main/java/caosdb/server/permissions/EntityACL.java @@ -25,6 +25,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 +75,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/query/POV.java b/src/main/java/caosdb/server/query/POV.java index 51ff02c9bb5bea648dd88dd671fd74bef609b364..a24aee4e7153e2b84b3e122b2a848fb79fe16367 100644 --- a/src/main/java/caosdb/server/query/POV.java +++ b/src/main/java/caosdb/server/query/POV.java @@ -329,7 +329,7 @@ public class POV implements EntityFilterInterface { query.getConnection().prepareCall("call initPOVRefidsTable(?,?)")) { // stmt = this.connection.prepareCall("call initPOV(?,?,?,?,?)"); // initPOVRefidsTable(in vInt INT, in vText VARCHAR(255)) - if (this.vInt != null) { + if (this.vInt != null && this.vInt > 0) { stmt.setInt(1, this.vInt); } else { stmt.setNull(1, Types.INTEGER); diff --git a/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java b/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java index e390d3c55576e774a78e89f17de73ed950b78cf4..709825c8176e0e759249efb41c5e8fd075829959 100644 --- a/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java +++ b/src/main/java/caosdb/server/resource/AbstractCaosDBServerResource.java @@ -26,7 +26,9 @@ 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.accessControl.UserSources; import caosdb.server.database.backend.implementation.MySQL.ConnectionException; import caosdb.server.entity.Message; import caosdb.server.utils.ServerMessages; @@ -36,6 +38,7 @@ import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.NoSuchElementException; @@ -73,7 +76,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>(); @@ -108,7 +111,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("")) { @@ -156,19 +159,68 @@ public abstract class AbstractCaosDBServerResource extends ServerResource { protected Element generateRootElement() { final Element retRoot = new Element("Response"); - if (getUser() != null && getUser().isAuthenticated()) { - 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); + retRoot.addContent(userInfo); + } + } + + /** + * 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) { + Collection<String> roles = UserSources.resolve(user.getPrincipals()); + if (roles == null) return; + Element rs = new Element("Roles"); + for (String role : roles) { + Element r = new Element("Role"); + r.addContent(role); + rs.addContent(r); + } + userInfo.addContent(rs); + } + + /** + * 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 { @@ -264,19 +316,29 @@ public abstract class AbstractCaosDBServerResource extends ServerResource { return new JdomRepresentation(doc, MediaType.TEXT_XML, " ", getReference(), getXSLScript()); } - protected JdomRepresentation error(final Message m, final Status status) { + protected Representation error(final Message m, final Status status) { final Document doc = new Document(); final Element root = generateRootElement(); root.addContent(m.toElement().setName("Error")); doc.setRootElement(root); + return error( + new JdomRepresentation(doc, MediaType.TEXT_XML, " ", getReference(), getXSLScript()), + status); + } + + protected Representation error(Representation entity, Status status) { getResponse().setStatus(status); - return new JdomRepresentation(doc, MediaType.TEXT_XML, " ", getReference(), getXSLScript()); + return entity; } - protected JdomRepresentation error(final Message m) { + protected Representation error(final Message m) { return error(m, Status.SERVER_ERROR_INTERNAL); } + protected Representation error(final Status status) { + return error((Representation) null, status); + } + protected JdomRepresentation warning(final Message m) { final Document doc = new Document(); final Element root = generateRootElement(); @@ -285,19 +347,19 @@ public abstract class AbstractCaosDBServerResource extends ServerResource { return new JdomRepresentation(doc, MediaType.TEXT_XML, " ", getReference(), getXSLScript()); } - protected JdomRepresentation noWellFormedNess() { + protected Representation noWellFormedNess() { return error(ServerMessages.REQUEST_BODY_NOT_WELLFORMED); } - protected JdomRepresentation emptyEntity() { + protected Representation emptyEntity() { return error(ServerMessages.REQUEST_BODY_EMPTY); } - protected JdomRepresentation connectionFailed() { + protected Representation connectionFailed() { return error(ServerMessages.CANNOT_CONNECT_TO_DATABASE); } - public JdomRepresentation handleThrowable(final Throwable t) { + public Representation handleThrowable(final Throwable t) { try { getRequest().getAttributes().put("THROWN", t); throw t; @@ -351,6 +413,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/ScriptingResource.java b/src/main/java/caosdb/server/resource/ScriptingResource.java index 4687b2c5ad241baddc6662c5fb043ac955839ff6..b5a82ef33d9a50da0568defd614d6c86048c8cd9 100644 --- a/src/main/java/caosdb/server/resource/ScriptingResource.java +++ b/src/main/java/caosdb/server/resource/ScriptingResource.java @@ -96,10 +96,12 @@ public class ScriptingResource extends AbstractCaosDBServerResource { private void deleteTmpFiles() { for (FileProperties p : deleteFiles) { try { - p.getFile().delete(); + p.getFile().delete(); } catch (Exception t) { if (getLogger().isLoggable(Level.WARNING)) { - getLogger().warning("Could not delete tmp file: " + p.getPath() + "\nException: " + t.toString()); + getLogger() + .warning( + "Could not delete tmp file: " + p.getPath() + "\nException: " + t.toString()); } } } diff --git a/src/main/java/caosdb/server/resource/ServerPropertiesResource.java b/src/main/java/caosdb/server/resource/ServerPropertiesResource.java new file mode 100644 index 0000000000000000000000000000000000000000..9b878e3338a9d0b0c74704acb412ac92ceb1f0c4 --- /dev/null +++ b/src/main/java/caosdb/server/resource/ServerPropertiesResource.java @@ -0,0 +1,42 @@ +package caosdb.server.resource; + +import caosdb.server.CaosDBServer; +import caosdb.server.ServerPropertiesSerializer; +import caosdb.server.accessControl.ACMPermissions; +import org.restlet.data.Form; +import org.restlet.data.Parameter; +import org.restlet.data.Status; +import org.restlet.representation.Representation; + +public class ServerPropertiesResource extends AbstractCaosDBServerResource { + + @Override + protected void doInit() { + super.doInit(); + setXSLScript("xsl/server_properties.xsl"); + } + + @Override + protected Representation httpGetInChildClass() { + if (CaosDBServer.isDebugMode()) { + getUser().checkPermission(ACMPermissions.PERMISSION_ACCESS_SERVER_PROPERTIES); + return super.ok( + new ServerPropertiesSerializer().serialize(CaosDBServer.getServerProperties())); + } + return error(Status.CLIENT_ERROR_NOT_FOUND); + } + + @Override + protected Representation httpPostInChildClass(Representation entity) { + if (CaosDBServer.isDebugMode()) { + getUser().checkPermission(ACMPermissions.PERMISSION_ACCESS_SERVER_PROPERTIES); + Form form = new Form(entity); + for (Parameter param : form) { + CaosDBServer.setProperty(param.getName(), param.getValue()); + } + return super.ok( + new ServerPropertiesSerializer().serialize(CaosDBServer.getServerProperties())); + } + return error(Status.CLIENT_ERROR_NOT_FOUND); + } +} diff --git a/src/main/java/caosdb/server/resource/TestCaseFileSystemResource.java b/src/main/java/caosdb/server/resource/TestCaseFileSystemResource.java index d239d570db89aa6c86b015a70474fb36c0080dcb..903ef0d5aca9a91eaeaff3693b7f77ef51e6cc46 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 ed7e546147bd717ebe92821e39abe4e256b470fb..e8aef1f00e57335185b7b204c8de081570e6e188 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/main/java/caosdb/server/scripting/CallerSerializer.java b/src/main/java/caosdb/server/scripting/CallerSerializer.java index 1bd265650bed0d5de865cc91b29db3327b56174b..1ab96ca0ab1a3f2dfefa14175d8423fea9bd4109 100644 --- a/src/main/java/caosdb/server/scripting/CallerSerializer.java +++ b/src/main/java/caosdb/server/scripting/CallerSerializer.java @@ -1,3 +1,25 @@ +/* + * ** 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 + * + * 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 caosdb.server.scripting; import caosdb.server.CaosDBException; diff --git a/src/main/java/caosdb/server/transaction/Transaction.java b/src/main/java/caosdb/server/transaction/Transaction.java index 2a7562c146aaca1edabb85127d488ffd2994233e..347e806e059edd124e9efde065c5509de3658c4c 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,13 +223,17 @@ 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(), - getTimestamp()), + getContainer(), this.getClass().getSimpleName(), realm, username, getTimestamp()), getAccess()); } } diff --git a/src/main/java/caosdb/server/utils/Serializer.java b/src/main/java/caosdb/server/utils/Serializer.java index 5508e99388a9e4191fd524ccbfc3e30007f6759a..26cd970c54d2bc4efcb7742ab9405a717e3ef112 100644 --- a/src/main/java/caosdb/server/utils/Serializer.java +++ b/src/main/java/caosdb/server/utils/Serializer.java @@ -1,3 +1,25 @@ +/* + * ** 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 + * + * 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 caosdb.server.utils; public interface Serializer<T, S> { diff --git a/src/test/java/caosdb/datetime/DateTimeTest.java b/src/test/java/caosdb/datetime/DateTimeTest.java index 2c66bcb14729514c7812ab6a64b388df652cff63..6366dccd5910c972e68201e7314275825f0f84b6 100644 --- a/src/test/java/caosdb/datetime/DateTimeTest.java +++ b/src/test/java/caosdb/datetime/DateTimeTest.java @@ -408,7 +408,7 @@ public class DateTimeTest { public void testUTCRange() { Interval d = (Interval) DateTimeFactory2.valueOf("9999-01-01T23:59:59UTC"); assertTrue(d instanceof UTCDateTime); - assertEquals("253370851225UTC", d.getILB_NF1()); + assertEquals("253370851226UTC", d.getILB_NF1()); Interval d2 = UTCDateTime.UTCSeconds(253370851225L, null); assertTrue(d instanceof UTCDateTime); assertEquals("253370851225UTC", d2.getILB_NF1()); @@ -418,7 +418,7 @@ public class DateTimeTest { d = (Interval) DateTimeFactory2.valueOf("9999-01-01T23:59:59.999999999UTC"); assertTrue(d instanceof UTCDateTime); - assertEquals("253370851225UTC999999999", d.getILB_NF1()); + assertEquals("253370851226UTC999999999", d.getILB_NF1()); d2 = UTCDateTime.UTCSeconds(253370851225L, 999999999); assertTrue(d instanceof UTCDateTime); assertEquals("253370851225UTC999999999", d2.getILB_NF1()); 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 0000000000000000000000000000000000000000..57031f088d9ca9f939b9772ae8d613df914208c4 --- /dev/null +++ b/src/test/java/caosdb/server/resource/TestAbstractCaosDBServerResource.java @@ -0,0 +1,73 @@ +package caosdb.server.resource; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import caosdb.server.CaosDBException; +import caosdb.server.accessControl.AnonymousAuthenticationToken; +import caosdb.server.accessControl.AnonymousRealm; +import caosdb.server.database.backend.implementation.MySQL.ConnectionException; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.sql.SQLException; +import org.apache.shiro.mgt.DefaultSecurityManager; +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; + +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); + } +}