Skip to content
Snippets Groups Projects
Verified Commit 4de847c3 authored by Timm Fitschen's avatar Timm Fitschen
Browse files

Merge branch 'dev' into f-cleanup-awi

parents 02260cad 6449bbd2
Branches
Tags
No related merge requests found
Showing
with 470 additions and 178 deletions
......@@ -29,6 +29,7 @@ log/
OUTBOX
ConsistencyTest.xml
testlog/
authtoken/
# python
__pycache__
......@@ -60,7 +60,6 @@ test:
- make easy-units
- mvn antlr4:antlr4
- mvn compile
- echo "defaultRealm = CaosDB" > conf/ext/usersources.ini
- mvn test
# Deploy: Trigger building of server image and integration tests
......
......@@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Support for deeply nested selectors in SELECT queries.
- One-time Authentication Tokens for login without credentials and login with
particular permissions and roles for the course of the session.
- `Entity/names` resource for retrieving all known entity names.
- Scripting is simplified by adding a `home` directory, of which a copy is
created for each called script and set as the `HOME` environment variable.
......@@ -19,6 +22,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
Script for moving files (change their path) in the internal file system based
on a two-column tsv file (with columns "from" and "to"). See
[README.md](misc/move_files/README.md).
- LDAP server may now be given and may be different from LDAP domain. See
`misc/pam_authentication/ldap.conf`
- #47 - Sub-properties can now be queried, such as in
`SELECT window.width FROM house`.
- Added support for versioning, if it is enabled on the backend.
### Changed
......@@ -28,7 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Deprecated
-
- CaosDBTerminal
### Removed
......@@ -46,6 +55,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
returned without ID but with a notice now.
- #11 - pam_authentication leaks the password to unprivileged processes on the
same machine.
- #39 - quotes around datetimes in queries
- #99 - Checksum updating resulted in infinite loop on server.
### Security (in case of vulnerabilities)
......
......@@ -13,3 +13,9 @@ The default configuration can be overriden by
in this order.
# One-time Authentication Tokens
One-time Authentication Tokens can be configure to be issued for special purposes (e.g. a call of a server-side script) or to be written to a file on a regular basis.
An example of a configuration is located at `./conf/core/authtoken.example.yaml`.
......@@ -12,6 +12,7 @@
* libpam (if PAM authentication is required)
* unzip
* openpyxl (for XLS/ODS export)
* openssl (if a custom TLS certificate is required)
### Install the requirements on Debian
On Debian, the required packages can be installed with:
......
caosdb-webui @ 66026626
Subproject commit 136582641fb1b675d9630b4eacea54fbf7765eea
Subproject commit 66026626089e2b514538510a1a6744868f46b661
# OneTimeAuthenticationToken Config
#
# One-Time Authentication (OTA) Tokens are a means to authenticate a client
# without a password.
#
# This example config file illustrates several use cases of One-Time
# Authentication Tokens.
#
# This yaml file contains an array of configuration objects which may have the
# properties that are defined by the caosdb.server.accessControl.Config class.
#
# These properties are:
#
# - expiresAfter:: An integer timespan in milliseconds after the OTA Token was
# generated by the server when the token expires. So the token will not be
# valid after <creationDate> + <expiresAfter>.
# - expiresAfterSeconds:: A convenient option which has the same meaning as
# expiresAfter but measured in seconds instead of milliseconds.
# - roles:: A list of strings which are the user roles that the client has when
# it authenticates with this token. This is a way to give a client a set of
# roles which it wouldn't have otherwise. Consequently, the client also has
# all permissions of its roles.
# - permissions:: A list of string which are the permissions that the client has
# when it authenticates with this token. This is another way to give a
# client permissions which it wouldn't have otherwise.
# - maxReplays:: Normally a One-Time token can be used exactly once and
# invalidates the moment when the server recognizes that a client uses it.
# However, when a network connection is unreliable, it should be possible to
# attempt another login with the same token for a few times. And in another
# use case, it might be practical to authenticate several clients at the
# same time with the same authentication token. The maxReplays value is the
# number of times that a server accepts an OTA token as valid. The default
# is, of course, 1.
# - replayTimeout:: An integer timespan in milliseconds. Because replays are a
# possible attack vector, the time span in which the same token may be used
# is limited by this value. The default value is configured by the server
# property 'ONE_TIME_TOKEN_REPLAYS_TIMEOUT_MS' which has a default of 30000,
# which is 30 seconds.
# - replayTimeoutSeconds:: A convenient option which has the same meaning as
# replayTimeout but measured in seconds instead of milliseconds.
# - output:: Defines how the OTA token is output by the server. If this property
# is not present the OTA is not output in any way but only used internally
# (see purpose). The 'output' object has the following properties:
# - file:: An absolute or relative path in the server's file system. This
# property means that the OTA is written to that file on server start.
# - schedule:: A string formatted according to the Quartz[1] library
# indicating when the OTA is renewed and the file is being overridden
# with the new OTA.
# - purpose:: A string which is used (only internally so far) to generate an OTA
# for a special purpose, e.g. the execution of a server-side script with
# particular permissions. This way, an otherwise unprivileged client may
# execute a server-side script with all necessary permissions, if the client
# has the "SCRIPTING:EXECUTE:<script-path>" permission and this config file
# has a configuration with "purpose: SCRIPTING:EXECUTE:<script-path>"
# (case-sensitive).
#
# [1] http://www.quartz-scheduler.org/api/2.3.0/org/quartz/CronExpression.html
#
#
# Examples:
#
# 1. Every client with the SCRIPTING:EXECUTE:administration/diagnostics.py
# permission can execute the administration/diagnostics.py script with the
# administration role (and not the client's roles).
- purpose: SCRIPTING:EXECUTE:administration/diagnostics.py
roles:
- administration
# 2. The server writes an OTA token with the administration role to
# "authtoken/admin_token_crud.txt" and refreshes that token every 10 seconds.
- roles:
- administration
output:
file: "authtoken/admin_token_crud.txt"
schedule: "0/10 * * ? * * *"
# 3. The server writes an OTA token with the administration role to
# "authtoken/admin_token_3_attempts.txt" which can be replayed 3 times in 10
# seconds. The OTA token is refreshed every 10 seconds.
- roles:
- administration
output:
file: "authtoken/admin_token_3_attempts.txt"
schedule: "0/10 * * ? * * *"
maxReplays: 3
replayTimeout: 10000
# 4. The server writes an OTA token with the administration role to
# "authtoken/admin_token_expired.txt" which expires immediately. Of course
# this is only useful for testing.
- roles:
- administration
output:
file: "authtoken/admin_token_expired.txt"
expiresAfterSeconds: 0
......@@ -28,6 +28,8 @@ jcs.region.BACKEND_JobRules.cacheattributes.MaxObjects=103
jcs.region.BACKEND_SparseEntities
jcs.region.BACKEND_SparseEntities.cacheattributes.MaxObjects=1002
jcs.region.BACKEND_RetrieveFullVersionInfo
jcs.region.BACKEND_RetrieveFullVersionInfo.cacheattributes.MaxObjects=1006
# PAM UserSource Caching: Cached Items expire after 60 seconds if they are not requested (idle) and after 600 seconds max.
# PAM_UnixUserGroups
......
......@@ -67,7 +67,7 @@ MYSQL_USER_NAME=caosdb
# Password for the user
MYSQL_USER_PASSWORD=caosdb
# Schema of mysql procedures and tables which is required by this CaosDB instance
MYSQL_SCHEMA_VERSION=v3.0.0-rc1
MYSQL_SCHEMA_VERSION=v3.0.0-rc2
# --------------------------------------------------
......@@ -114,10 +114,21 @@ CERTIFICATES_KEY_STORE_PASSWORD=
# 10 min
SESSION_TIMEOUT_MS=600000
# Time after which activation tokens for the activation of new users (internal
# user sources) expire.
# Time after which one-time tokens expire.
# This is only a default value. The actual timeout of tokens can be
# configured otherwise, for example in authtoken.yml.
# 7days
ACTIVATION_TIMEOUT_MS=604800000
ONE_TIME_TOKEN_EXPIRES_MS=604800000
# Path to config file for one time tokens, for example authtoken.yml.
AUTHTOKEN_CONFIG=
# Timeout after which a one-time token expires once it has been first consumed,
# regardless of the maximum of replays that are allowed for that token. This is
# only a default value. The actual timeout of tokens can be configured
# otherwise.
# 30 s
ONE_TIME_TOKEN_REPLAYS_TIMEOUT_MS=30000
# The value for the HTTP cache directive "max-age"
WEBUI_HTTP_HEADER_CACHE_MAX_AGE=28800
......@@ -170,3 +181,4 @@ CHECK_ENTITY_ACL_ROLES_MODE=MUST
# part of any Entity ACL.
GLOBAL_ENTITY_PERMISSIONS_FILE=./conf/core/global_entity_permissions.xml
ENTITY_VERSIONING_ENABLED=true
......@@ -47,6 +47,7 @@ run-single:
formatting:
mvn fmt:format
autopep8 -ari scripting/
# Compile into a standalone jar file
jar: easy-units
......
# This file is sourced by the LDAP authentication script
# Set the ldap server here. This is also used to generate a fully qualified
# user name: <USER>@$LDAP_SERVER
# Set the ldap server here.
# LDAP_SERVER="example.com"
# Set the ldap domain here. This is used to generate a fully qualified
# user name: <USER>@$LDAP_DOMAIN
# LDAP_DOMAIN="example.com"
......@@ -35,7 +35,7 @@ exe_dir=$(dirname $0)
# If the second argument is empty or "-", take password from stdin, else use the argument as a file.
testpw() {
username="${1}@${LDAP_SERVER}"
username="${1}@${LDAP_DOMAIN}"
pwfile="$2"
pwargs=("-w" "$pwfile")
if [[ $pwfile == "-" ]] ; then
......
......@@ -35,13 +35,13 @@
<repositories>
<repository>
<id>maven-central</id>
<url>http://central.maven.org/maven2/</url>
<url>https://repo1.maven.org/maven2/</url>
<name>Maven Central</name>
</repository>
<repository>
<id>maven-restlet</id>
<name>Public online Restlet repository</name>
<url>http://maven.restlet.com</url>
<url>https://maven.restlet.com</url>
</repository>
<repository>
<id>local-maven-repo</id>
......@@ -54,10 +54,20 @@
<artifactId>easy-units</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.1</version>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
......@@ -90,7 +100,7 @@
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
<version>8.0.19</version>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
......@@ -140,7 +150,7 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-jcs-core</artifactId>
<version>2.1</version>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.kohsuke</groupId>
......
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# ** 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
# Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
# Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# ** end header
#
"""diagnostics.py
A script which returns a json representation of various parameters which might
be interesting for debugging the server-side scripting functionality and which
should not be executable for non-admin users.
"""
import sys
TEST_MODULES = [
"caosdb",
"numpy",
"pandas",
"validate_email"
]
def get_files():
from os import walk
from os.path import join
result = []
for p, dirs, files in walk("."):
for f in files:
result.append(join(p, f))
for d in dirs:
result.append(join(p, d))
return result
def get_option(name, default=None):
for arg in sys.argv:
if arg.startswith("--{}=".format(name)):
index = len(name) + 3
return arg[index:]
return default
def get_exit_code():
return int(get_option("exit", 0))
def get_auth_token():
return get_option("auth-token")
def get_query():
return get_option("query")
def get_caosdb_info(auth_token):
import caosdb as db
result = dict()
result["version"] = db.version.version
try:
db.configure_connection(
auth_token=auth_token,
password_method="auth_token")
info = db.Info()
result["info"] = str(info)
result["username"] = info.user_info.name
result["realm"] = info.user_info.realm
result["roles"] = info.user_info.roles
# execute a query and return the results
query = get_query()
if query is not None:
query_result = db.execute_query(query)
result["query"] = (query, str(query_result))
except Exception as e:
result["exception"] = str(e)
return result
def test_imports(modules):
result = dict()
for m in modules:
try:
i = __import__(m)
if hasattr(i, "__version__") and i.__version__ is not None:
v = i.__version__
else:
v = "unknown version"
result[m] = (True, v)
except ImportError as e:
result[m] = (False, str(e))
return result
def main():
try:
import json
except ImportError:
print('{{"python_version":"{v}",'
'"python_path":["{p}"]}}'.format(v=sys.version,
p='","'.join(sys.path)))
raise
try:
diagnostics = dict()
diagnostics["python_version"] = sys.version
diagnostics["python_path"] = sys.path
diagnostics["call"] = sys.argv
diagnostics["import"] = test_imports(TEST_MODULES)
diagnostics["files"] = get_files()
auth_token = get_auth_token()
diagnostics["auth_token"] = auth_token
if diagnostics["import"]["caosdb"][0] is True:
diagnostics["caosdb"] = get_caosdb_info(auth_token)
finally:
json.dump(diagnostics, sys.stdout)
sys.exit(get_exit_code())
if __name__ == "__main__":
main()
......@@ -93,7 +93,8 @@ def _parse_arguments():
parser.add_argument('-t', '--tempdir', required=False, default=tempdir,
help="Temporary dir for saving the result.")
parser.add_argument('-a', '--auth-token', required=False,
help="An authentication token (not needed, only for compatibility).")
help=("An authentication token (not needed, only for "
"compatibility)."))
parser.add_argument('tsv', help="The tsv file.")
return parser.parse_args()
......@@ -104,5 +105,6 @@ def main():
filename = _write_xls(dataframe, directory=args.tempdir)
print(filename)
if __name__ == "__main__":
main()
......@@ -296,7 +296,7 @@ public class UTCDateTime implements Interval {
throw new NullPointerException("toString method!!!");
}
public static UTCDateTime UTCSeconds(final Long utcseconds, final Integer nanosecond) {
public static UTCDateTime UTCSeconds(final Long utcseconds, final Integer nanoseconds) {
if (LEAP_SECONDS.isEmpty()) {
initLeapSeconds();
}
......@@ -310,10 +310,10 @@ public class UTCDateTime implements Interval {
if (leapSeconds2 != leapSeconds && LEAP_SECONDS.contains(systemSeconds)) {
gc.add(Calendar.SECOND, -1);
return new UTCDateTime(
systemSeconds, leapSeconds, nanosecond, new LeapSecondDateTimeStringStrategy(gc, 1));
systemSeconds, leapSeconds, nanoseconds, new LeapSecondDateTimeStringStrategy(gc, 1));
} else {
return new UTCDateTime(
systemSeconds, leapSeconds, nanosecond, new GregorianCalendarDateTimeStringStrategy(gc));
systemSeconds, leapSeconds, nanoseconds, new GregorianCalendarDateTimeStringStrategy(gc));
}
}
......
......@@ -22,12 +22,13 @@
*/
package caosdb.server;
import caosdb.server.accessControl.AnonymousAuthenticationToken;
import caosdb.server.accessControl.AuthenticationUtils;
import caosdb.server.accessControl.OneTimeAuthenticationToken;
import caosdb.server.accessControl.SessionToken;
import caosdb.server.resource.DefaultResource;
import caosdb.server.utils.ServerMessages;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.restlet.Context;
import org.restlet.Request;
......@@ -48,50 +49,26 @@ public class CaosAuthenticator extends Authenticator {
protected boolean authenticate(final Request request, final Response response) {
final Subject subject = SecurityUtils.getSubject();
return attemptOneTimeTokenLogin(subject, request) || attemptSessionValidation(subject, request);
return attemptSessionValidation(subject, request);
}
private static boolean attemptSessionValidation(final Subject subject, final Request request) {
try {
final SessionToken sessionToken =
final AuthenticationToken sessionToken =
AuthenticationUtils.parseSessionTokenCookie(
request.getCookies().getFirst(AuthenticationUtils.SESSION_TOKEN_COOKIE), null);
request.getCookies().getFirst(AuthenticationUtils.SESSION_TOKEN_COOKIE));
if (sessionToken != null) {
subject.login(sessionToken);
}
} catch (AuthenticationException e) {
logger.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();
}
private static boolean attemptOneTimeTokenLogin(final Subject subject, final Request request) {
try {
OneTimeAuthenticationToken oneTimeToken = null;
// try and parse from the query segment of the uri
oneTimeToken =
AuthenticationUtils.parseOneTimeTokenQuerySegment(
request.getResourceRef().getQueryAsForm().getFirstValue("AuthToken"), null);
// try and parse from cookie
if (oneTimeToken == null) {
oneTimeToken =
AuthenticationUtils.parseOneTimeTokenCookie(
request.getCookies().getFirst(AuthenticationUtils.ONE_TIME_TOKEN_COOKIE), null);
// anonymous users
if (!subject.isAuthenticated()
&& CaosDBServer.getServerProperty(ServerProperties.KEY_AUTH_OPTIONAL)
.equalsIgnoreCase("TRUE")) {
subject.login(AnonymousAuthenticationToken.getInstance());
}
if (oneTimeToken != null) {
subject.login(oneTimeToken);
}
} catch (final AuthenticationException e) {
} catch (AuthenticationException e) {
logger.info("LOGIN_FAILED", e);
}
return subject.isAuthenticated();
......@@ -100,7 +77,7 @@ public class CaosAuthenticator extends Authenticator {
@Override
protected int unauthenticated(final Request request, final Response response) {
final DefaultResource defaultResource =
new DefaultResource(AuthenticationUtils.UNAUTHENTICATED.toElement());
new DefaultResource(ServerMessages.UNAUTHENTICATED.toElement());
defaultResource.init(getContext(), request, response);
defaultResource.handle();
response.setStatus(org.restlet.data.Status.CLIENT_ERROR_UNAUTHORIZED);
......
......@@ -19,12 +19,13 @@
*/
package caosdb.server;
import caosdb.server.accessControl.AnonymousAuthenticationToken;
import caosdb.server.accessControl.AnonymousRealm;
import caosdb.server.accessControl.AuthenticationUtils;
import caosdb.server.accessControl.CaosDBAuthorizingRealm;
import caosdb.server.accessControl.CaosDBDefaultRealm;
import caosdb.server.accessControl.OneTimeTokenRealm;
import caosdb.server.accessControl.Principal;
import caosdb.server.accessControl.ConsumedInfoCleanupJob;
import caosdb.server.accessControl.OneTimeAuthenticationToken;
import caosdb.server.accessControl.SessionToken;
import caosdb.server.accessControl.SessionTokenRealm;
import caosdb.server.database.BackendTransaction;
......@@ -63,7 +64,6 @@ import caosdb.server.transaction.ChecksumUpdater;
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;
......@@ -74,17 +74,22 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Properties;
import java.util.TimeZone;
import java.util.UUID;
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.env.BasicIniEnvironment;
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.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import org.restlet.Application;
import org.restlet.Component;
import org.restlet.Context;
......@@ -118,11 +123,68 @@ public class CaosDBServer extends Application {
private static ArrayList<Runnable> preShutdownHooks = new ArrayList<Runnable>();
private static boolean START_BACKEND = true;
private static boolean INSECURE = false;
public static final String REQUEST_TIME_LOGGER = "REQUEST_TIME_LOGGER";
public static final String REQUEST_ERRORS_LOGGER = "REQUEST_ERRORS_LOGGER";
private static Scheduler SCHEDULER;
public static String getServerProperty(final String key) {
return getServerProperties().getProperty(key);
}
/**
* This main method starts up a web application that will listen on a port defined in the config
* file.
*
* @param args One option temporarily (for testing) available: silent: If present: disable
* System.out-stream (stream to a NullPrintStream). This makes the response of the database
* amazingly faster.
* @throws IOException
* @throws FileNotFoundException
* @throws SecurityException
* @throws Exception If problems occur.
*/
public static void main(final String[] args)
throws SecurityException, FileNotFoundException, IOException {
try {
init(args);
initScheduler();
initServerProperties();
initTimeZone();
initOneTimeTokens();
initShiro();
initBackend();
initWebServer();
initShutDownHook();
} catch (Exception e1) {
logger.error("Could not start the server.", e1);
System.exit(1);
}
}
private static void init(final String[] args) {
// Important change:
// Make silent the default option
START_GUI = false;
for (final String s : args) {
if (s.equals("silent")) {
START_GUI = false;
} else if (s.equals("gui")) {
START_GUI = true;
} else if (s.equals("nobackend")) {
START_BACKEND = false;
} else if (s.equals("insecure")) {
INSECURE = true;
}
}
INSECURE = INSECURE && isDebugMode(); // only allow insecure in debug mode
START_BACKEND = START_BACKEND || !isDebugMode(); // always start backend if not in debug mode
}
private static void initScheduler() throws SchedulerException {
SCHEDULER = StdSchedulerFactory.getDefaultScheduler();
SCHEDULER.start();
}
public static void initServerProperties() throws IOException {
SERVER_PROPERTIES = ServerProperties.initServerProperties();
}
......@@ -206,21 +268,15 @@ public class CaosDBServer extends Application {
}
}
private static void init(final String[] args) {
// Important change:
// Make silent the default option
START_GUI = false;
for (final String s : args) {
if (s.equals("silent")) {
START_GUI = false;
} else if (s.equals("gui")) {
START_GUI = true;
} else if (s.equals("nobackend")) {
START_BACKEND = false;
} else if (s.equals("insecure")) {
INSECURE = true;
}
}
public static void initOneTimeTokens() throws Exception {
OneTimeAuthenticationToken.initConfig();
ConsumedInfoCleanupJob.scheduleDaily();
}
public static void initShiro() {
// init Shiro (user authentication/authorization and session management)
final Ini config = getShiroConfig();
initShiro(config);
}
public static Ini getShiroConfig() {
......@@ -228,12 +284,11 @@ public class CaosDBServer extends Application {
final Section mainSec = config.addSection("main");
mainSec.put("CaosDB", CaosDBDefaultRealm.class.getCanonicalName());
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, $AnonymousRealm");
"$CaosDB, $SessionTokenValidator, $CaosDBAuthorizingRealm, $AnonymousRealm");
// disable shiro's default session management. We have quasi-stateless
// sessions
......@@ -243,43 +298,15 @@ public class CaosDBServer extends Application {
return config;
}
/**
* This main method starts up a web application that will listen on a port defined in the config
* file.
*
* @param args One option temporarily (for testing) available: silent: If present: disable
* System.out-stream (stream to a NullPrintStream). This makes the response of the database
* amazingly faster.
* @throws IOException
* @throws FileNotFoundException
* @throws SecurityException
* @throws Exception If problems occur.
*/
public static void main(final String[] args)
throws SecurityException, FileNotFoundException, IOException {
try {
init(args);
initServerProperties();
initTimeZone();
} catch (IOException | InterruptedException e1) {
logger.error("Could not configure the server.", e1);
System.exit(1);
}
INSECURE = INSECURE && isDebugMode(); // only allow insecure in debug mode
START_BACKEND = START_BACKEND || !isDebugMode(); // always start backend if
// not in debug mode
// init Shiro (user authentication/authorization and session management)
final Ini config = getShiroConfig();
final Factory<SecurityManager> factory = new IniSecurityManagerFactory(config);
final SecurityManager securityManager = factory.getInstance();
public static void initShiro(Ini config) {
BasicIniEnvironment env = new BasicIniEnvironment(config);
final SecurityManager securityManager = env.getSecurityManager();
SecurityUtils.setSecurityManager(securityManager);
}
final Initialization init = Initialization.setUp();
try {
// init backend
if (START_BACKEND) {
public static void initBackend() throws Exception {
if (START_BACKEND) {
try (final Initialization init = Initialization.setUp()) {
BackendTransaction.init();
// init benchmark
......@@ -296,65 +323,60 @@ public class CaosDBServer extends Application {
// ChecksumUpdater
ChecksumUpdater.start();
} else {
logger.info("NO BACKEND");
}
} else {
logger.info("NO BACKEND");
}
}
// GUI
if (START_GUI) {
final CaosDBTerminal caosDBTerminal = new CaosDBTerminal();
caosDBTerminal.setName("CaosDBTerminal");
caosDBTerminal.start();
private static void initWebServer() throws Exception {
final int port_https =
Integer.parseInt(getServerProperty(ServerProperties.KEY_SERVER_PORT_HTTPS));
final int port_http =
Integer.parseInt(getServerProperty(ServerProperties.KEY_SERVER_PORT_HTTP));
int port_redirect_https;
try {
port_redirect_https =
Integer.parseInt(getServerProperty(ServerProperties.KEY_REDIRECT_HTTP_TO_HTTPS_PORT));
} catch (NumberFormatException e) {
port_redirect_https = port_https;
}
final int initialConnections =
Integer.parseInt(getServerProperty(ServerProperties.KEY_INITIAL_CONNECTIONS));
final int maxTotalConnections =
Integer.parseInt(getServerProperty(ServerProperties.KEY_MAX_CONNECTIONS));
addPreShutdownHook(
new Runnable() {
if (INSECURE) {
runHTTPServer(port_http, initialConnections, maxTotalConnections);
} else {
runHTTPSServer(
port_https, port_http, port_redirect_https, initialConnections, maxTotalConnections);
}
}
@Override
public void run() {
caosDBTerminal.shutDown();
SystemErrPanel.close();
}
});
// wait until the terminal is initialized.
Thread.sleep(1000);
// add Benchmark
StatsPanel.addStat("TransactionBenchmark", TransactionBenchmark.getRootInstance());
} else {
logger.info("NO GUI");
System.setOut(new NullPrintStream());
}
public static void initGUI() throws InterruptedException {
if (START_GUI) {
final CaosDBTerminal caosDBTerminal = new CaosDBTerminal();
caosDBTerminal.setName("CaosDBTerminal");
caosDBTerminal.start();
// Web server properties
final int port_https =
Integer.parseInt(getServerProperty(ServerProperties.KEY_SERVER_PORT_HTTPS));
final int port_http =
Integer.parseInt(getServerProperty(ServerProperties.KEY_SERVER_PORT_HTTP));
int port_redirect_https;
try {
port_redirect_https =
Integer.parseInt(getServerProperty(ServerProperties.KEY_REDIRECT_HTTP_TO_HTTPS_PORT));
} catch (NumberFormatException e) {
port_redirect_https = port_https;
}
final int initialConnections =
Integer.parseInt(getServerProperty(ServerProperties.KEY_INITIAL_CONNECTIONS));
final int maxTotalConnections =
Integer.parseInt(getServerProperty(ServerProperties.KEY_MAX_CONNECTIONS));
init.release();
if (INSECURE) {
runHTTPServer(port_http, initialConnections, maxTotalConnections);
} else {
runHTTPSServer(
port_https, port_http, port_redirect_https, initialConnections, maxTotalConnections);
}
initShutDownHook();
} catch (final Exception e) {
logger.error("Server start failed.", e);
init.release();
System.exit(1);
addPreShutdownHook(
new Runnable() {
@Override
public void run() {
caosDBTerminal.shutDown();
SystemErrPanel.close();
}
});
// wait until the terminal is initialized.
Thread.sleep(1000);
// add Benchmark
StatsPanel.addStat("TransactionBenchmark", TransactionBenchmark.getRootInstance());
} else {
logger.info("NO GUI");
System.setOut(new NullPrintStream());
}
}
......@@ -508,9 +530,6 @@ public class CaosDBServer extends Application {
}
}
public static final String REQUEST_TIME_LOGGER = "REQUEST_TIME_LOGGER";
public static final String REQUEST_ERRORS_LOGGER = "REQUEST_ERRORS_LOGGER";
/**
* Specify the dispatching restlet that maps URIs to their associated resources for processing.
*
......@@ -560,10 +579,10 @@ public class CaosDBServer extends Application {
private void setSessionCookies(final Response response) {
final Subject subject = SecurityUtils.getSubject();
// if authenticated as a normal user: generate and set session cookie.
if (subject.isAuthenticated()
&& subject.getPrincipal() != AuthenticationUtils.ANONYMOUS_USER.getPrincipal()) {
final SessionToken sessionToken =
SessionToken.generate((Principal) subject.getPrincipal(), null);
&& subject.getPrincipal() != AnonymousAuthenticationToken.PRINCIPAL) {
final SessionToken sessionToken = SessionToken.generate(subject);
// set session token cookie (httpOnly, secure cookie which
// is used to recognize a user session)
......@@ -823,6 +842,10 @@ public class CaosDBServer extends Application {
public static Properties getServerProperties() {
return SERVER_PROPERTIES;
}
public static void scheduleJob(JobDetail job, Trigger trigger) throws SchedulerException {
SCHEDULER.scheduleJob(job, trigger);
}
}
class CaosDBComponent extends Component {
......@@ -852,7 +875,7 @@ class CaosDBComponent extends Component {
public void handle(final Request request, final Response response) {
long t1 = System.currentTimeMillis();
// The server request ID is just a long random number
request.getAttributes().put("SRID", Utils.getUID());
request.getAttributes().put("SRID", UUID.randomUUID().toString());
response.setServerInfo(CaosDBServer.getServerInfo());
super.handle(request, response);
log(request, response, t1);
......
......@@ -88,7 +88,9 @@ public class ServerProperties extends Properties {
public static final String KEY_BUGTRACKER_URI = "BUGTRACKER_URI";
public static final String KEY_SESSION_TIMEOUT_MS = "SESSION_TIMEOUT_MS";
public static final String KEY_ACTIVATION_TIMEOUT_MS = "ACTIVATION_TIMEOUT_MS";
public static final String KEY_ONE_TIME_TOKEN_EXPIRES_MS = "ONE_TIME_TOKEN_EXPIRES_MS";
public static final String KEY_ONE_TIME_TOKEN_REPLAYS_TIMEOUT_MS =
"ONE_TIME_TOKEN_REPLAYS_TIMEOUT_MS";
public static final String KEY_CACHE_CONF_LOC = "CACHE_CONF_LOC";
public static final String KEY_CACHE_DISABLE = "CACHE_DISABLE";
......@@ -129,6 +131,7 @@ public class ServerProperties extends Properties {
public static final String KEY_TIMEZONE = "TIMEZONE";
public static final String KEY_WEBUI_HTTP_HEADER_CACHE_MAX_AGE =
"WEBUI_HTTP_HEADER_CACHE_MAX_AGE";
public static final String KEY_AUTHTOKEN_CONFIG = "AUTHTOKEN_CONFIG";
/**
* Read the config files and initialize the server properties.
......
......@@ -28,7 +28,7 @@ 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();
public static final Principal PRINCIPAL = new Principal("anonymous", "anonymous");
private AnonymousAuthenticationToken() {}
......@@ -37,7 +37,7 @@ public class AnonymousAuthenticationToken implements AuthenticationToken {
}
@Override
public Object getPrincipal() {
public Principal getPrincipal() {
return PRINCIPAL;
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment