diff --git a/src/main/java/caosdb/server/CaosDBServer.java b/src/main/java/caosdb/server/CaosDBServer.java index 4a6a3ad06eaebab0de808552bf0f8a684a70d8e8..21685533b1f8c3ded743caf9c9c890c1f715cf75 100644 --- a/src/main/java/caosdb/server/CaosDBServer.java +++ b/src/main/java/caosdb/server/CaosDBServer.java @@ -19,49 +19,6 @@ */ package caosdb.server; -import java.io.BufferedReader; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.time.ZoneId; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Properties; -import java.util.TimeZone; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.LogRecord; -import org.apache.shiro.SecurityUtils; -import org.apache.shiro.config.Ini; -import org.apache.shiro.config.Ini.Section; -import org.apache.shiro.config.IniSecurityManagerFactory; -import org.apache.shiro.mgt.SecurityManager; -import org.apache.shiro.subject.Subject; -import org.apache.shiro.util.Factory; -import org.apache.shiro.util.ThreadContext; -import org.restlet.Application; -import org.restlet.Component; -import org.restlet.Context; -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.Restlet; -import org.restlet.Server; -import org.restlet.data.CookieSetting; -import org.restlet.data.Parameter; -import org.restlet.data.Protocol; -import org.restlet.data.Reference; -import org.restlet.data.ServerInfo; -import org.restlet.data.Status; -import org.restlet.engine.Engine; -import org.restlet.routing.Route; -import org.restlet.routing.Router; -import org.restlet.routing.Template; -import org.restlet.routing.TemplateRoute; -import org.restlet.routing.Variable; -import org.restlet.util.Series; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import caosdb.server.accessControl.AnonymousRealm; import caosdb.server.accessControl.AuthenticationUtils; import caosdb.server.accessControl.CaosDBAuthorizingRealm; @@ -108,6 +65,49 @@ import caosdb.server.utils.FileUtils; import caosdb.server.utils.Initialization; import caosdb.server.utils.NullPrintStream; import caosdb.server.utils.Utils; +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Properties; +import java.util.TimeZone; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.config.Ini; +import org.apache.shiro.config.Ini.Section; +import org.apache.shiro.config.IniSecurityManagerFactory; +import org.apache.shiro.mgt.SecurityManager; +import org.apache.shiro.subject.Subject; +import org.apache.shiro.util.Factory; +import org.apache.shiro.util.ThreadContext; +import org.restlet.Application; +import org.restlet.Component; +import org.restlet.Context; +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.Restlet; +import org.restlet.Server; +import org.restlet.data.CookieSetting; +import org.restlet.data.Parameter; +import org.restlet.data.Protocol; +import org.restlet.data.Reference; +import org.restlet.data.ServerInfo; +import org.restlet.data.Status; +import org.restlet.engine.Engine; +import org.restlet.routing.Route; +import org.restlet.routing.Router; +import org.restlet.routing.Template; +import org.restlet.routing.TemplateRoute; +import org.restlet.routing.Variable; +import org.restlet.util.Series; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class CaosDBServer extends Application { diff --git a/src/main/java/caosdb/server/accessControl/OneTimeAuthenticationToken.java b/src/main/java/caosdb/server/accessControl/OneTimeAuthenticationToken.java index ddb930808fe487d986c15e7112d950652e5778f1..0a0f71d654ec056e58b9d60f547e73d8624e2873 100644 --- a/src/main/java/caosdb/server/accessControl/OneTimeAuthenticationToken.java +++ b/src/main/java/caosdb/server/accessControl/OneTimeAuthenticationToken.java @@ -22,6 +22,11 @@ */ package caosdb.server.accessControl; +import caosdb.server.CaosDBServer; +import caosdb.server.ServerProperties; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -34,11 +39,6 @@ import java.util.Map; import java.util.UUID; import org.apache.shiro.subject.Subject; import org.eclipse.jetty.util.ajax.JSON; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectReader; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import caosdb.server.CaosDBServer; -import caosdb.server.ServerProperties; public class OneTimeAuthenticationToken extends SelfValidatingAuthenticationToken { @@ -159,23 +159,23 @@ public class OneTimeAuthenticationToken extends SelfValidatingAuthenticationToke permissions, roles); } - - + public static List<Config> loadConfig(InputStream input) throws Exception { List<Config> results = new LinkedList<>(); ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); - ObjectReader reader = mapper.readerFor(Config.class); - Iterator<Config> configs = reader.readValues(input); - configs.forEachRemaining(results::add); + ObjectReader reader = mapper.readerFor(Config.class); + Iterator<Config> configs = reader.readValues(input); + configs.forEachRemaining(results::add); return results; } - + public static Map<String, Config> getPurposeMap(List<Config> configs) throws Exception { Map<String, Config> result = new HashMap<>(); - for(Config config : configs) { - if(config.getPurpose() != null && !config.getPurpose().isEmpty()) { - if(result.containsKey(config.getPurpose())) { - throw new Exception("OneTimeAuthToken configuration contains duplicate values for the 'purpose' property."); + for (Config config : configs) { + if (config.getPurpose() != null && !config.getPurpose().isEmpty()) { + if (result.containsKey(config.getPurpose())) { + throw new Exception( + "OneTimeAuthToken configuration contains duplicate values for the 'purpose' property."); } result.put(config.getPurpose(), config); } @@ -187,7 +187,8 @@ public class OneTimeAuthenticationToken extends SelfValidatingAuthenticationToke return generate(c, new Principal("anonymous", "anonymous"), null); } - public static OneTimeAuthenticationToken generateForPurpose(String purpose, Subject user, String curry) { + public static OneTimeAuthenticationToken generateForPurpose( + String purpose, Subject user, String curry) { Config c = purposes.get(purpose); if (c != null) { Principal principal = (Principal) user.getPrincipal(); @@ -204,9 +205,8 @@ public class OneTimeAuthenticationToken extends SelfValidatingAuthenticationToke public static class Output { private String file = null; - - public Output() { - } + + public Output() {} public void output(OneTimeAuthenticationToken t) throws IOException { try (PrintWriter writer = new PrintWriter(file, "utf-8")) { @@ -222,31 +222,35 @@ public class OneTimeAuthenticationToken extends SelfValidatingAuthenticationToke this.file = file; } } - + public static class Config { private String[] permissions = {}; private String[] roles = {}; private String purpose = null; private Output output = null; - - public Config() { - } + + public Config() {} public String[] getPermissions() { return permissions; } + public String getPurpose() { return purpose; } + public void setPermissions(String[] permissions) { this.permissions = permissions; } + public String[] getRoles() { return roles; } + public void setRoles(String[] roles) { this.roles = roles; } + public void setPurpose(String purpose) { this.purpose = purpose; } @@ -258,14 +262,12 @@ public class OneTimeAuthenticationToken extends SelfValidatingAuthenticationToke public void setOutput(Output output) { this.output = output; } - } - + public static Map<String, Config> getPurposeMap() { return purposes; } - - + public static void init(InputStream yamlConfig) throws Exception { List<Config> configs = loadConfig(yamlConfig); output(configs); @@ -274,16 +276,15 @@ public class OneTimeAuthenticationToken extends SelfValidatingAuthenticationToke private static void output(List<Config> configs) throws IOException { for (Config config : configs) { - if(config.getOutput() != null) { + if (config.getOutput() != null) { config.getOutput().output(generate(config)); } } } public static void init() throws Exception { - try(FileInputStream f = new FileInputStream("conf/ext/authtoken.yaml")) { + try (FileInputStream f = new FileInputStream("conf/ext/authtoken.yaml")) { init(f); } } - -} \ No newline at end of file +} diff --git a/src/main/java/caosdb/server/accessControl/OneTimeTokenRealm.java b/src/main/java/caosdb/server/accessControl/OneTimeTokenRealm.java index 1c16bf0c4dea161743c9f8e5f9fc8337bac01589..468a8d8d212ca5546f1940a4b3de38c1da50b3d8 100644 --- a/src/main/java/caosdb/server/accessControl/OneTimeTokenRealm.java +++ b/src/main/java/caosdb/server/accessControl/OneTimeTokenRealm.java @@ -22,11 +22,11 @@ */ package caosdb.server.accessControl; +import caosdb.server.accessControl.CaosDBAuthorizingRealm.PermissionAuthenticationInfo; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher; -import caosdb.server.accessControl.CaosDBAuthorizingRealm.PermissionAuthenticationInfo; public class OneTimeTokenRealm extends SessionTokenRealm { diff --git a/src/main/java/caosdb/server/accessControl/SessionToken.java b/src/main/java/caosdb/server/accessControl/SessionToken.java index 82b11f2262b38a5d65092bde972e32fe4eb3f237..273bd50ba7c4ef89fecd0e2b9b8817acbf6d9efa 100644 --- a/src/main/java/caosdb/server/accessControl/SessionToken.java +++ b/src/main/java/caosdb/server/accessControl/SessionToken.java @@ -22,9 +22,9 @@ */ package caosdb.server.accessControl; -import org.eclipse.jetty.util.ajax.JSON; import caosdb.server.CaosDBServer; import caosdb.server.ServerProperties; +import org.eclipse.jetty.util.ajax.JSON; public class SessionToken extends SelfValidatingAuthenticationToken { diff --git a/src/main/java/caosdb/server/resource/ScriptingResource.java b/src/main/java/caosdb/server/resource/ScriptingResource.java index d967d0319a780244e4eea7987224f8489816da2a..75baec81a1641748fbf27e300e7d01cc37beb63d 100644 --- a/src/main/java/caosdb/server/resource/ScriptingResource.java +++ b/src/main/java/caosdb/server/resource/ScriptingResource.java @@ -24,6 +24,20 @@ package caosdb.server.resource; +import caosdb.server.FileSystem; +import caosdb.server.accessControl.OneTimeAuthenticationToken; +import caosdb.server.accessControl.Principal; +import caosdb.server.accessControl.SessionToken; +import caosdb.server.accessControl.UserSources; +import caosdb.server.entity.FileProperties; +import caosdb.server.entity.Message; +import caosdb.server.scripting.CallerSerializer; +import caosdb.server.scripting.ScriptingPermissions; +import caosdb.server.scripting.ServerSideScriptingCaller; +import caosdb.server.utils.Serializer; +import caosdb.server.utils.ServerMessages; +import caosdb.server.utils.Utils; +import com.ibm.icu.text.Collator; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; @@ -46,20 +60,6 @@ import org.restlet.data.Status; import org.restlet.engine.header.ContentType; import org.restlet.ext.fileupload.RestletFileUpload; import org.restlet.representation.Representation; -import com.ibm.icu.text.Collator; -import caosdb.server.FileSystem; -import caosdb.server.accessControl.OneTimeAuthenticationToken; -import caosdb.server.accessControl.Principal; -import caosdb.server.accessControl.SessionToken; -import caosdb.server.accessControl.UserSources; -import caosdb.server.entity.FileProperties; -import caosdb.server.entity.Message; -import caosdb.server.scripting.CallerSerializer; -import caosdb.server.scripting.ScriptingPermissions; -import caosdb.server.scripting.ServerSideScriptingCaller; -import caosdb.server.utils.Serializer; -import caosdb.server.utils.ServerMessages; -import caosdb.server.utils.Utils; public class ScriptingResource extends AbstractCaosDBServerResource { diff --git a/src/test/java/caosdb/server/authentication/AuthTokenTest.java b/src/test/java/caosdb/server/authentication/AuthTokenTest.java index 7337d6e3ee36fe43bd572e7a00b8608b7efec0ad..288b405c6910eaf30aafb266383d11a4b8b4be35 100644 --- a/src/test/java/caosdb/server/authentication/AuthTokenTest.java +++ b/src/test/java/caosdb/server/authentication/AuthTokenTest.java @@ -24,6 +24,13 @@ package caosdb.server.authentication; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; + +import caosdb.server.CaosDBServer; +import caosdb.server.accessControl.AuthenticationUtils; +import caosdb.server.accessControl.OneTimeAuthenticationToken; +import caosdb.server.accessControl.OneTimeAuthenticationToken.Config; +import caosdb.server.accessControl.Principal; +import caosdb.server.accessControl.SessionToken; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; @@ -33,12 +40,6 @@ import org.apache.commons.io.input.CharSequenceInputStream; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import caosdb.server.CaosDBServer; -import caosdb.server.accessControl.AuthenticationUtils; -import caosdb.server.accessControl.OneTimeAuthenticationToken; -import caosdb.server.accessControl.OneTimeAuthenticationToken.Config; -import caosdb.server.accessControl.Principal; -import caosdb.server.accessControl.SessionToken; public class AuthTokenTest { @@ -222,7 +223,7 @@ public class AuthTokenTest { Assert.assertTrue(OneTimeAuthenticationToken.parse(t1.toString(), curry).isHashValid()); Assert.assertTrue(OneTimeAuthenticationToken.parse(t1.toString(), curry).isValid()); } - + @Test public void testOneTimeTokenConfigBasic() throws Exception { StringBuilder testYaml = new StringBuilder(); @@ -240,8 +241,11 @@ public class AuthTokenTest { Assert.assertTrue("parsing to Config object", map.get("test purpose 1") instanceof Config); Config config = map.get("test purpose 1"); - Assert.assertArrayEquals("permissions parsed", new String[] {"permission1", "permission2", "permission3", "permission with white space"}, config.getPermissions()); - Assert.assertArrayEquals("roles parsed", new String[] {"role1", "role2" }, config.getRoles()); + Assert.assertArrayEquals( + "permissions parsed", + new String[] {"permission1", "permission2", "permission3", "permission with white space"}, + config.getPermissions()); + Assert.assertArrayEquals("roles parsed", new String[] {"role1", "role2"}, config.getRoles()); } @Test @@ -288,7 +292,7 @@ public class AuthTokenTest { Assert.assertEquals("four items", 4, map.size()); Assert.assertTrue(map.get("purpose 2") instanceof Config); } - + @Test public void testOneTimeTokenConfigOutputFile() throws Exception { File tempFile = File.createTempFile("authtoken", "json"); @@ -298,17 +302,18 @@ public class AuthTokenTest { testYaml.append("- output:\n"); testYaml.append(" file: " + tempFile.getAbsolutePath() + "\n"); testYaml.append(" permissions: [ permission1 ]\n"); - + // write the token OneTimeAuthenticationToken.init(new CharSequenceInputStream(testYaml, "utf-8")); Assert.assertTrue(tempFile.exists()); - try ( BufferedReader reader = new BufferedReader(new FileReader(tempFile))) { + try (BufferedReader reader = new BufferedReader(new FileReader(tempFile))) { OneTimeAuthenticationToken token = OneTimeAuthenticationToken.parse(reader.readLine(), null); assertEquals("Token has anonymous username", "anonymous", token.getPrincipal().getUsername()); assertEquals("Token has anonymous realm", "anonymous", token.getPrincipal().getRealm()); - assertArrayEquals("Permissions array has been written and read", new String[] { "permission1" }, token.getPermissions()); + assertArrayEquals( + "Permissions array has been written and read", + new String[] {"permission1"}, + token.getPermissions()); } - } - } diff --git a/src/test/java/caosdb/server/resource/TestScriptingResource.java b/src/test/java/caosdb/server/resource/TestScriptingResource.java index e1538573dc20a918edc3ed940b00583670253052..b9797eb85ef779a2b957f65a5c96b56787d48421 100644 --- a/src/test/java/caosdb/server/resource/TestScriptingResource.java +++ b/src/test/java/caosdb/server/resource/TestScriptingResource.java @@ -41,16 +41,20 @@ import caosdb.server.database.misc.TransactionBenchmark; import caosdb.server.database.proto.ProtoUser; import caosdb.server.entity.Message; import caosdb.server.permissions.PermissionRule; +import caosdb.server.scripting.ScriptingPermissions; +import caosdb.server.scripting.ServerSideScriptingCaller; import java.io.IOException; import java.util.Date; import java.util.HashSet; import java.util.List; import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authz.permission.WildcardPermission; import org.apache.shiro.config.Ini; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; +import org.jdom2.Element; import org.junit.BeforeClass; import org.junit.Test; import org.restlet.Request; @@ -92,7 +96,13 @@ public class TestScriptingResource { @Override public HashSet<PermissionRule> retrievePermissionRule(String role) throws TransactionException { - return new HashSet<>(); + HashSet<PermissionRule> result = new HashSet<>(); + result.add( + new PermissionRule( + true, + false, + new WildcardPermission(ScriptingPermissions.PERMISSION_EXECUTION("anonymous_ok")))); + return result; } @Override @@ -172,9 +182,17 @@ public class TestScriptingResource { java.util.List<caosdb.server.entity.FileProperties> files, Object authToken) throws Message { + if (invokation.get(0).equals("anonymous_ok")) { + return 0; + } return -1; }; + @Override + public Element generateRootElement(ServerSideScriptingCaller caller) { + return new Element("OK"); + }; + @Override public Object generateAuthToken(String purpose) { return ""; @@ -203,8 +221,8 @@ public class TestScriptingResource { public void testAnonymousWithOutPermission() { Subject user = SecurityUtils.getSubject(); user.login(AuthenticationUtils.ANONYMOUS_USER); - Representation entity = new StringRepresentation("asdf"); - entity.setMediaType(MediaType.TEXT_ALL); + Form form = new Form("call=anonymous_no_permission"); + Representation entity = form.getWebRepresentation(); Request request = new Request(Method.POST, "../test", entity); request.setRootRef(new Reference("bla")); request.getAttributes().put("SRID", "asdf1234"); @@ -220,8 +238,8 @@ public class TestScriptingResource { public void testAnonymousWithPermission() { Subject user = SecurityUtils.getSubject(); user.login(AuthenticationUtils.ANONYMOUS_USER); - Representation entity = new StringRepresentation("asdf"); - entity.setMediaType(MediaType.TEXT_ALL); + Form form = new Form("call=anonymous_ok"); + Representation entity = form.getWebRepresentation(); Request request = new Request(Method.POST, "../test", entity); request.setRootRef(new Reference("bla")); request.getAttributes().put("SRID", "asdf1234"); @@ -230,7 +248,7 @@ public class TestScriptingResource { resource.init(null, request, new Response(null)); resource.handle(); - assertEquals(Status.CLIENT_ERROR_FORBIDDEN, resource.getResponse().getStatus()); + assertEquals(Status.SUCCESS_OK, resource.getResponse().getStatus()); } @Test @@ -250,8 +268,9 @@ public class TestScriptingResource { @Test public void testHandleForm() throws Message, IOException { - Form form = - new Form("-Ooption=OPTION&call=CALL&-Ooption2=OPTION2&-p=POS1&-p4=POS3&-p2=POS2&IGNORED"); - assertEquals(-1, resource.handleForm(form)); + Subject user = SecurityUtils.getSubject(); + user.login(AuthenticationUtils.ANONYMOUS_USER); + Form form = new Form("call=anonymous_ok"); + assertEquals(0, resource.handleForm(form)); } }