#db.host=dba-postgres-qa-21.ist.berkeley.edu\r
#db.host=173.255.228.202\r
db.host=169.229.248.106\r
+#db.host=localhost\r
#db.host=169.229.199.44\r
db.jdbc.baseurl=jdbc:${db}://${db.host}:${db.port}\r
\r
import org.collectionspace.services.person.PersonAuthorityResource;
import org.collectionspace.services.citation.CitationAuthorityResource;
-//import org.collectionspace.services.query.QueryResource;
-
import javax.servlet.ServletContext;
import javax.ws.rs.core.Application;
/*
singletons.add(new WorkflowResource());
*/
-// singletons.add(new QueryResource());
// singletons.add(new DomainIdentifierResource());
// singletons.add(new PingResource());
}
log4j.logger.org.collectionspace=INFO\r
log4j.logger.org.collectionspace.services.nuxeo.client.java=TRACE\r
log4j.logger.org.collectionspace.services.common.storage.JDBCTools=ERROR\r
+log4j.logger.org.collectionspace.services.common.profile.CSpaceFilter=DEBUG\r
+\r
#log4j.logger.org.collectionspace.services.common.vocabulary.nuxeo=TRACE\r
\r
#\r
rolesQuery="select r.rolename, 'Role' from roles as r, accounts_roles as ar where ar.user_id=? and ar.role_id=r.csid"\r
tenantsQueryWithDisabled="select t.id, t.name, 'Tenants' from accounts_common as a, accounts_tenants as at, tenants as t where a.userid=? and a.csid = at.TENANTS_ACCOUNTSCOMMON_CSID and at.tenant_id = t.id"\r
tenantsQueryNoDisabled="select t.id, t.name, 'Tenants' from accounts_common as a, accounts_tenants as at, tenants as t where a.userid=? and a.csid = at.TENANTS_ACCOUNTSCOMMON_CSID and at.tenant_id = t.id and NOT t.disabled"\r
+ maxRetrySeconds="5"\r
+ delayBetweenAttemptsMillis="200"\r
debug=true;\r
};\r
\r
+ /**\r
+ * The JAAS login configuration.\r
+ */\r
cspace {\r
org.collectionspace.authentication.jaas.CSpaceJBossDBLoginModule required\r
dsJndiName="CspaceDS"\r
rolesQuery="select r.rolename, 'Role' from roles as r, accounts_roles as ar where ar.user_id=? and ar.role_id=r.csid"\r
tenantsQueryWithDisabled="select t.id, t.name, 'Tenants' from accounts_common as a, accounts_tenants as at, tenants as t where a.userid=? and a.csid = at.TENANTS_ACCOUNTSCOMMON_CSID and at.tenant_id = t.id"\r
tenantsQueryNoDisabled="select t.id, t.name, 'Tenants' from accounts_common as a, accounts_tenants as at, tenants as t where a.userid=? and a.csid = at.TENANTS_ACCOUNTSCOMMON_CSID and at.tenant_id = t.id and NOT t.disabled"\r
+ maxRetrySeconds="5"\r
+ delayBetweenAttemptsMillis="200"\r
debug=true;\r
};\r
</param-value>\r
</context-param>\r
\r
+ <!--\r
+ Spring Security Filter\r
+ -->\r
<filter>\r
<filter-name>springSecurityFilterChain</filter-name>\r
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>\r
<url-pattern>/*</url-pattern>\r
</filter-mapping>\r
\r
+ <!--\r
+ - A filter that will attempt to retry requests that fail due to network errors.\r
+ -\r
+ - maxRetrySeconds - How long to keep retrying a request.\r
+ - delayBetweenAttemptsMillis - How long to wait between retries.\r
+ -\r
+ -->\r
+ <filter>\r
+ <filter-name>networkErrorRetryFilter</filter-name>\r
+ <filter-class>org.collectionspace.services.common.NetworkErrorRetryFilter</filter-class>\r
+ <init-param>\r
+ <param-name>maxRetrySeconds</param-name>\r
+ <param-value>5</param-value>\r
+ </init-param>\r
+ <init-param>\r
+ <param-name>delayBetweenAttemptsMillis</param-name>\r
+ <param-value>200</param-value>\r
+ </init-param>\r
+ </filter>\r
+ \r
+ <filter-mapping>\r
+ <filter-name>networkErrorRetryFilter</filter-name>\r
+ <url-pattern>/*</url-pattern>\r
+ </filter-mapping>\r
+ \r
+ <!--\r
+ A filter that logs profiling information.\r
+ -->\r
<filter>\r
<filter-name>CSpaceFilter</filter-name>\r
<filter-class>org.collectionspace.services.common.profile.CSpaceFilter</filter-class>\r
import java.util.Collection;\r
import java.util.List;\r
import java.util.Map;\r
-\r
import java.security.acl.Group;\r
+\r
import javax.security.auth.Subject;\r
import javax.security.auth.callback.CallbackHandler;\r
import javax.security.auth.login.LoginException;\r
\r
import org.collectionspace.authentication.realm.db.CSpaceDbRealm;\r
import org.jboss.security.auth.spi.UsernamePasswordLoginModule;\r
-\r
import org.slf4j.Logger;\r
import org.slf4j.LoggerFactory;\r
\r
realm = new CSpaceDbRealm(options);\r
}\r
\r
+ @Override\r
+ protected String createPasswordHash(String username, String password,\r
+ String digestOption)\r
+ throws LoginException {\r
+ String result = super.createPasswordHash(username, password, digestOption);\r
+ \r
+ if (result == null) {\r
+ String message = "Could not create a password hash for the supplied password. Check your login.conf configuration's hash algorithm setting.";\r
+ log.error(message);\r
+ throw new LoginException(message);\r
+ }\r
+ \r
+ return result;\r
+ }\r
+ \r
protected String getUsersPassword() throws LoginException {\r
\r
String username = getUsername();\r
String password = null;\r
+ \r
try {\r
password = realm.getUsersPassword(username);\r
password = convertRawPassword(password);\r
logger.debug("Obtained user password for: " + username);\r
}\r
} catch (LoginException lex) {\r
+ log.error("Could not retrieve user password for: " + username, lex);\r
throw lex;\r
} catch (Exception ex) {\r
+ log.error("Could not retrieve user password for: " + username, ex);\r
LoginException le = new LoginException("Unknown Exception");\r
le.initCause(ex);\r
throw le;\r
}\r
+ \r
return password;\r
}\r
\r
package org.collectionspace.authentication.realm.db;
import java.lang.reflect.Constructor;
+import java.net.ConnectException;
import java.security.Principal;
import java.security.acl.Group;
import java.sql.Connection;
//import org.apache.commons.logging.Log;
//import org.apache.commons.logging.LogFactory;
+
+
+
+
import org.collectionspace.authentication.AuthN;
import org.collectionspace.authentication.CSpaceTenant;
import org.collectionspace.authentication.realm.CSpaceRealm;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
* @author
*/
public class CSpaceDbRealm implements CSpaceRealm {
-
- private Logger logger = LoggerFactory.getLogger(CSpaceDbRealm.class);
+ private Logger logger = LoggerFactory.getLogger(CSpaceDbRealm.class);
private String datasourceName;
private String principalsQuery;
private String tenantsQueryWithDisabled;
private boolean suspendResume;
+ private long maxRetrySeconds = MAX_RETRY_SECONDS;
+ private static final int MAX_RETRY_SECONDS = 5;
+ private static final String MAX_RETRY_SECONDS_STR = "maxRetrySeconds";
+
+ private long delayBetweenAttemptsMillis = DELAY_BETWEEN_ATTEMPTS_MILLISECONDS;
+ private static final String DELAY_BETWEEN_ATTEMPTS_MILLISECONDS_STR = "delayBetweenAttemptsMillis";
+ private static final long DELAY_BETWEEN_ATTEMPTS_MILLISECONDS = 200;
+
+ protected void setMaxRetrySeconds(Map options) {
+ Object optionsObj = options.get(MAX_RETRY_SECONDS_STR);
+ if (optionsObj != null) {
+ String paramValue = optionsObj.toString();
+ try {
+ maxRetrySeconds = Long.parseLong(paramValue);
+ } catch (NumberFormatException e) {
+ logger.warn(String.format("The Spring Security login authentication parameter '%s' with value '%s' could not be parsed to a long value. The default value of '%d' will be used instead.",
+ MAX_RETRY_SECONDS_STR, paramValue, maxRetrySeconds));
+ }
+ }
+ }
+
+ protected long getMaxRetrySeconds() {
+ return this.maxRetrySeconds;
+ }
+
+ protected void setDelayBetweenAttemptsMillis(Map options) {
+ Object optionsObj = options.get(DELAY_BETWEEN_ATTEMPTS_MILLISECONDS_STR);
+ if (optionsObj != null) {
+ String paramValue = optionsObj.toString();
+ try {
+ delayBetweenAttemptsMillis = Long.parseLong(paramValue);
+ } catch (NumberFormatException e) {
+ logger.warn(String.format("The Spring Security login authentication parameter '%s' with value '%s' could not be parsed to a long value. The default value of '%d' will be used instead.",
+ MAX_RETRY_SECONDS_STR, paramValue, delayBetweenAttemptsMillis));
+ }
+ }
+ }
+
+ protected long getDelayBetweenAttemptsMillis() {
+ return this.delayBetweenAttemptsMillis;
+ }
+
/**
* CSpace Database Realm
* @param datasourceName datasource name
if (tmp != null) {
suspendResume = Boolean.valueOf(tmp.toString()).booleanValue();
}
+
+ this.setMaxRetrySeconds(options);
+ this.setDelayBetweenAttemptsMillis(options);
+
if (logger.isTraceEnabled()) {
logger.trace("DatabaseServerLoginModule, dsJndiName=" + datasourceName);
logger.trace("principalsQuery=" + principalsQuery);
return p;
}
- private Connection getConnection() throws LoginException, SQLException {
+ /*
+ * This method will attempt to get a connection. If a network error prevents it from getting a connection on the first try
+ * it will retry for the next 'getMaxRetrySeconds()' seconds. If it is unable to get the connection then it will timeout and
+ * throw an exception.
+ */
+ private Connection getConnection() throws Exception {
+ Connection result = null;
+ boolean failed = true;
+ Exception lastException = null;
+ int requestAttempts = 0;
+
+ long quittingTime = System.currentTimeMillis() + getMaxRetrySeconds() * 1000; // This is how long we attempt retries
+ do {
+ if (requestAttempts > 0) {
+ Thread.sleep(getDelayBetweenAttemptsMillis()); // Wait a little time between reattempts.
+ }
+
+ try {
+ // proceed to the original request by calling doFilter()
+ result = this.getConnection(getDataSourceName());
+ if (result != null) {
+ failed = false;
+ break; // the request was successfully executed, so we can break out of this retry loop
+ } else {
+ failed = true;
+ throw new ConnectException(); // The 'response' argument indicated a network related failure, so let's throw a generic connection exception
+ }
+ } catch (Exception e) {
+ lastException = e;
+ if (exceptionChainContainsNetworkError(lastException) == false) {
+ // Break if the exception chain does not contain a
+ // network related exception because we don't want to retry if it's not a network related failure
+ break;
+ }
+ requestAttempts++; // keep track of how many times we've tried the request
+ }
+ } while (System.currentTimeMillis() < quittingTime); // keep trying until we run out of time
+
+ //
+ // Add a warning to the logs if we encountered *any* failures on our re-attempts. Only add the warning
+ // if we were eventually successful.
+ //
+ if (requestAttempts > 0 && failed == false) {
+ logger.warn(String.format("Request to get a connection from data source '%s' failed with exception '%s' at attempt number '%d' before finally succeeding on the next attempt.",
+ getDataSourceName(),
+ lastException.getClass().getName(),
+ requestAttempts));
+ }
+
+ if (failed == true) {
+ // If we get here, it means all of our attempts to get a successful call to chain.doFilter() have failed.
+ throw lastException;
+ }
+
+ return result;
+ }
+
+ /*
+ * Don't call this method directly. Instead, use the getConnection() method that take no arguments.
+ */
+ private Connection getConnection(String dataSourceName) throws LoginException, SQLException {
InitialContext ctx = null;
Connection conn = null;
- String dataSourceName = getDataSourceName();
DataSource ds = null;
+
try {
ctx = new InitialContext();
try {
if (conn == null) {
conn = AuthN.getDataSource().getConnection(); //FIXME:REM - This is the result of some type of JNDI mess. Should try to solve this problem and clean up this code.
}
+
return conn;
+
} catch (NamingException ex) {
LoginException le = new LoginException("Error looking up DataSource from: " + dataSourceName);
le.initCause(ex);
try {
ctx.close();
} catch (Exception e) {
- e.printStackTrace();
+ e.printStackTrace(); // We should be using a logger here instead.
}
}
}
this.tenantsQueryNoDisabled = tenantQuery;
}
*/
+
+ /*
+ * This method crawls the exception chain looking for network related exceptions and
+ * returns 'true' if it finds one.
+ */
+ public static boolean exceptionChainContainsNetworkError(Throwable exceptionChain) {
+ boolean result = false;
+ Throwable cause = exceptionChain;
+
+ while (cause != null) {
+ if (isExceptionNetworkRelated(cause) == true) {
+ result = true;
+ break;
+ }
+
+ cause = cause.getCause();
+ }
+
+ return result;
+ }
+
+ /*
+ * Return 'true' if the exception is in the "java.net" package.
+ */
+ private static boolean isExceptionNetworkRelated(Throwable cause) {
+ boolean result = false;
+
+ String className = cause.getClass().getCanonicalName();
+ if (className.contains("java.net") == true) {
+ result = true;
+ }
+
+ return result;
+ }
+
}
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
+
import org.springframework.security.authentication.jaas.AuthorityGranter;
/**
Set<String> authorities = new HashSet<String>();
if (principal instanceof Group) {
Group g = (Group) principal;
- Enumeration members = g.members();
+ Enumeration<? extends Principal> members = g.members();
while (members.hasMoreElements()) {
Principal p = (Principal) members.nextElement();
authorities.add(p.getName());
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
- Copyright 2009 University of California at Berkeley
- Licensed under the Educational Community License (ECL), Version 2.0.
- You may not use this file except in compliance with this License.
-
- Document : jboss-login-config.xml
- Created on : June 19, 2009, 9:58 AM
- Author :
- Description:
- jboss config for JAAS DatabaseServerLoginModule
--->
-<!--
-copy the following snippet into $JBOSS_HOME/server/cspace/conf/login-config.xml
-copy before the "other" application-policy
--->
-
-<application-policy name="cspace">
- <authentication>
- <login-module code="org.collectionspace.authentication.jaas.CSpaceJBossDBLoginModule"
- flag="required">
- <module-option name="dsJndiName">CspaceDS</module-option>
- <module-option name="hashAlgorithm">SHA-256</module-option>
- <module-option name="ignorePasswordCase">false</module-option>
- <module-option name = "principalClass">org.collectionspace.authentication.CSpacePrincipal</module-option>
- <module-option name="principalsQuery">
- select passwd from users where username=?
- </module-option>
- <module-option name="rolesQuery">
- select r.rolename, 'Role' from roles as r, accounts_roles as ar where ar.user_id=? and ar.role_id=r.csid
- </module-option>
- <module-option name="tenantsQuery">
- select t.id, t.name, 'Tenants' from accounts_common as a, accounts_tenants as at, tenants as t where a.userid=? and a.csid = at.TENANTS_ACCOUNTSCOMMON_CSID and at.tenant_id = t.id
- </module-option>
- </login-module>
- </authentication>
-</application-policy>
+++ /dev/null
-<?xml version='1.0'?>
-<!DOCTYPE policy PUBLIC
- "-//JBoss//DTD JBOSS Security Config 3.0//EN"
- "http://www.jboss.org/j2ee/dtd/security_config.dtd">
-<!-- The XML based JAAS login configuration read by the
-org.jboss.security.auth.login.XMLLoginConfig mbean. Add
-an application-policy element for each security domain.
-
-The outline of the application-policy is:
-<application-policy name="security-domain-name">
- <authentication>
- <login-module code="login.module1.class.name" flag="control_flag">
- <module-option name = "option1-name">option1-value</module-option>
- <module-option name = "option2-name">option2-value</module-option>
- ...
- </login-module>
-
- <login-module code="login.module2.class.name" flag="control_flag">
- ...
- </login-module>
- ...
- </authentication>
-</application-policy>
-
-$Revision: 64598 $
--->
-
-<policy>
- <!-- Used by clients within the application server VM such as
- mbeans and servlets that access EJBs.
- -->
- <application-policy name = "client-login">
- <authentication>
- <login-module code = "org.jboss.security.ClientLoginModule"
- flag = "required">
- <!-- Any existing security context will be restored on logout -->
- <module-option name="restore-login-identity">true</module-option>
- </login-module>
- </authentication>
- </application-policy>
-
- <!-- Security domain for JBossMQ -->
- <application-policy name = "jbossmq">
- <authentication>
- <login-module code = "org.jboss.security.auth.spi.DatabaseServerLoginModule"
- flag = "required">
- <module-option name = "unauthenticatedIdentity">guest</module-option>
- <module-option name = "dsJndiName">java:/DefaultDS</module-option>
- <module-option name = "principalsQuery">SELECT PASSWD FROM JMS_USERS WHERE USERID=?</module-option>
- <module-option name = "rolesQuery">SELECT ROLEID, 'Roles' FROM JMS_ROLES WHERE USERID=?</module-option>
- </login-module>
- </authentication>
- </application-policy>
-
- <!-- Security domain for JBossMQ when using file-state-service.xml
- <application-policy name = "jbossmq">
- <authentication>
- <login-module code = "org.jboss.mq.sm.file.DynamicLoginModule"
- flag = "required">
- <module-option name = "unauthenticatedIdentity">guest</module-option>
- <module-option name = "sm.objectname">jboss.mq:service=StateManager</module-option>
- </login-module>
- </authentication>
- </application-policy>
- -->
-
- <!-- Security domains for testing new jca framework -->
- <application-policy name = "HsqlDbRealm">
- <authentication>
- <login-module code = "org.jboss.resource.security.ConfiguredIdentityLoginModule"
- flag = "required">
- <module-option name = "principal">sa</module-option>
- <module-option name = "userName">sa</module-option>
- <module-option name = "password"></module-option>
- <module-option name = "managedConnectionFactoryName">jboss.jca:service=LocalTxCM,name=DefaultDS</module-option>
- </login-module>
- </authentication>
- </application-policy>
-
- <application-policy name = "JmsXARealm">
- <authentication>
- <login-module code = "org.jboss.resource.security.ConfiguredIdentityLoginModule"
- flag = "required">
- <module-option name = "principal">guest</module-option>
- <module-option name = "userName">guest</module-option>
- <module-option name = "password">guest</module-option>
- <module-option name = "managedConnectionFactoryName">jboss.jca:service=TxCM,name=JmsXA</module-option>
- </login-module>
- </authentication>
- </application-policy>
-
- <!-- A template configuration for the jmx-console web application. This
- defaults to the UsersRolesLoginModule the same as other and should be
- changed to a stronger authentication mechanism as required.
- -->
- <application-policy name = "jmx-console">
- <authentication>
- <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"
- flag = "required">
- <module-option name="usersProperties">props/jmx-console-users.properties</module-option>
- <module-option name="rolesProperties">props/jmx-console-roles.properties</module-option>
- </login-module>
- </authentication>
- </application-policy>
-
- <!-- A template configuration for the web-console web application. This
- defaults to the UsersRolesLoginModule the same as other and should be
- changed to a stronger authentication mechanism as required.
- -->
- <application-policy name = "web-console">
- <authentication>
- <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"
- flag = "required">
- <module-option name="usersProperties">web-console-users.properties</module-option>
- <module-option name="rolesProperties">web-console-roles.properties</module-option>
- </login-module>
- </authentication>
- </application-policy>
-
- <!--
- A template configuration for the JBossWS security domain.
- This defaults to the UsersRolesLoginModule the same as other and should be
- changed to a stronger authentication mechanism as required.
- -->
- <application-policy name="JBossWS">
- <authentication>
- <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"
- flag="required">
- <module-option name="usersProperties">props/jbossws-users.properties</module-option>
- <module-option name="rolesProperties">props/jbossws-roles.properties</module-option>
- <module-option name="unauthenticatedIdentity">anonymous</module-option>
- </login-module>
- </authentication>
- </application-policy>
-
-<application-policy name="cspace">
- <authentication>
- <login-module code="org.collectionspace.authentication.jaas.CSpaceJBossDBLoginModule"
- flag="required">
- <module-option name="dsJndiName">CspaceDS</module-option>
- <module-option name="hashAlgorithm">SHA-256</module-option>
- <module-option name="ignorePasswordCase">false</module-option>
- <module-option name = "principalClass">org.collectionspace.authentication.CSpacePrincipal</module-option>
- <module-option name="principalsQuery">
- select passwd from users where username=?
- </module-option>
- <module-option name="rolesQuery">
- select r.rolename, 'Role' from roles as r, accounts_roles as ar where ar.user_id=? and ar.role_id=r.csid
- </module-option>
- <module-option name="tenantsQueryNoDisabled">
- select t.id, t.name, 'Tenants' from accounts_common as a, accounts_tenants as at, tenants as t where a.userid=? and a.csid = at.TENANTS_ACCOUNTSCOMMON_CSID and at.tenant_id = t.id and NOT t.disabled
- </module-option>
- <module-option name="tenantsQueryWithDisabled">
- select t.id, t.name, 'Tenants' from accounts_common as a, accounts_tenants as at, tenants as t where a.userid=? and a.csid = at.TENANTS_ACCOUNTSCOMMON_CSID and at.tenant_id = t.id
- </module-option>
- </login-module>
- </authentication>
-</application-policy>
-
- <!-- The default login configuration used by any security domain that
- does not have a application-policy entry with a matching name
- -->
- <application-policy name = "other">
- <!-- A simple server login module, which can be used when the number
- of users is relatively small. It uses two properties files:
- users.properties, which holds users (key) and their password (value).
- roles.properties, which holds users (key) and a comma-separated list of
- their roles (value).
- The unauthenticatedIdentity property defines the name of the principal
- that will be used when a null username and password are presented as is
- the case for an unuathenticated web client or MDB. If you want to
- allow such users to be authenticated add the property, e.g.,
- unauthenticatedIdentity="nobody"
- -->
- <authentication>
- <login-module code = "org.jboss.security.auth.spi.UsersRolesLoginModule"
- flag = "required" />
- </authentication>
- </application-policy>
-
-</policy>
-
import java.util.List;
import java.io.Serializable;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.collectionspace.services.authorization.CSpaceAction;
import org.collectionspace.services.authorization.spi.CSpacePermissionEvaluator;
-
import org.collectionspace.services.authorization.CSpaceResource;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.acls.model.Permission;
debug(res, authToken, objectIdId, objectIdType, perm);
result = eval.hasPermission(authToken,
objectIdId, objectIdType, perm);
- } catch (Exception e) {
- //
- // If this exception is caused by a network timeout, we may want to reattempt
- //
+ } catch (Throwable e) {
+ if (exceptionChainContainsNetworkError(e) == true) {
+ //
+ // If this exception is caused by a network timeout, we want to re-attempt
+ //
+ throw e;
+ }
log.error("Unexpected exception encountered while evaluating permissions.", e);
}
System.out.println("");
}
}
+
+ public static boolean exceptionChainContainsNetworkError(Throwable exceptionChain) {
+ boolean result = false;
+ Throwable cause = exceptionChain;
+
+ while (cause != null) {
+ if (isCauseNetworkRelated(cause) == true) {
+ result = true;
+ break;
+ }
+
+ cause = cause.getCause();
+ }
+
+ return result;
+ }
+
+ private static boolean isCauseNetworkRelated(Throwable cause) {
+ boolean result = false;
+
+ String className = cause.getClass().getCanonicalName();
+ if (className.contains("java.net") || className.contains("java.io")) {
+ result = true;
+ }
+
+ return result;
+ }
+
}
--- /dev/null
+package org.collectionspace.services.common;
+
+import java.net.SocketException;
+import java.util.Random;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+
+import org.collectionspace.services.common.document.DocumentException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CSWebApplicationException extends WebApplicationException {
+ static final Logger logger = LoggerFactory.getLogger(CSWebApplicationException.class);
+ public final static String NETWORK_ERROR_TYPE = "text/neterr";
+ public final static String TEXT_MIME_TYPE = "text/plain";
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ private Response response;
+
+ //
+ // Override of constructors
+ //
+ public CSWebApplicationException(Response response) {
+ super(response); // this set's our parent's private response member
+ this.response = response; // we need to set our copy since we override the getResponse() method.
+ logger.warn("CSWebApplicationException instance created without an underlying 'cause' exception.");
+ }
+
+ public CSWebApplicationException(Throwable cause) {
+ super(cause);
+ this.response = getFinalResponse(cause, null);
+ }
+
+ public CSWebApplicationException(Throwable cause, Response response) {
+ super(cause);
+ this.response = getFinalResponse(cause, response);
+ }
+
+ //
+ // Overrides and custom methods
+ //
+
+ @Override
+ public Response getResponse() {
+ return response;
+ }
+
+ private Response getFinalResponse(Throwable cause, Response response) {
+ Response finalResponse = response;
+
+ if (exceptionChainContainsNetworkError(cause) == true) {
+ if (response != null) {
+ finalResponse = Response.fromResponse(response).entity(cause.getMessage()).type(NETWORK_ERROR_TYPE).build();
+ } else {
+ finalResponse = Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
+ cause.getMessage()).type(NETWORK_ERROR_TYPE).build();
+
+ }
+ if (logger.isTraceEnabled() == true) {
+ logger.trace(cause.getMessage(), cause);
+ }
+ }
+
+ if (finalResponse == null) {
+ finalResponse = Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
+ cause.getMessage()).type(TEXT_MIME_TYPE).build();
+ }
+
+ return finalResponse;
+ }
+
+ /*
+ * This method crawls the exception chain looking for network related exceptions and
+ * returns 'true' if it finds one.
+ */
+ public static boolean exceptionChainContainsNetworkError(Throwable exceptionChain) {
+ boolean result = false;
+
+ Throwable cause = exceptionChain;
+ while (cause != null) {
+ if (isExceptionNetworkRelated(cause) == true) {
+ result = true;
+ break;
+ }
+
+ Throwable nextCause = cause.getCause();
+ if (nextCause == null) {
+ // Since we reached the end of the exception chain, we can see if the last code
+ // executed was network related.
+ StackTraceElement[] finalStackTrace = cause.getStackTrace();
+ }
+ }
+
+ return result;
+ }
+
+ private static boolean isStackTraceNetworkRelated(StackTraceElement[] stackTraceElementList) {
+ boolean result = false;
+
+ for (StackTraceElement stackTraceElement : stackTraceElementList) {
+ if (stackTraceElement.getClassName().contains("") == true) {
+
+ }
+ }
+
+ return result;
+ }
+
+ /*
+ * Return 'true' if the exception is in the "java.net" package.
+ */
+ private static boolean isExceptionNetworkRelated(Throwable cause) {
+ boolean result = false;
+
+ if (cause != null) {
+ String className = cause.getClass().getCanonicalName();
+ if (cause instanceof DocumentException) {
+ className = ((DocumentException) cause).getCausesClassName(); // Since Nuxeo wraps the real exception, we needed to create this special getCausesClassName() method -see NuxeoDocumentException for details
+ }
+ if (className != null && className.contains("java.net") == true) {
+ result = true;
+ }
+ }
+
+ return result;
+ }
+
+ /*
+ * This Method is intended only for testing the robustness of our network exception handling
+ * and retry code. You can place a call to this method is various locations throughout the code
+ * and it will randomly throw an exception.
+ */
+ public static void throwAnException(int chance) throws SocketException {
+ Random generator = new Random(System.currentTimeMillis());
+ int roll = generator.nextInt(chance) + 1;
+ if (roll != 1) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Throwing a SocketException at random to test our network robustness.");
+ }
+ throw new SocketException();
+ }
+ }
+
+}
--- /dev/null
+/**
+ * This document is a part of the source code and related artifacts
+ * for CollectionSpace, an open source collections management system
+ * for museums and related institutions:
+ *
+ * http://www.collectionspace.org
+ * http://wiki.collectionspace.org
+ *
+ * Copyright © 2009 {Contributing Institution}
+ *
+ * Licensed under the Educational Community License (ECL), Version 2.0.
+ * You may not use this file except in compliance with this License.
+ *
+ * You may obtain a copy of the ECL 2.0 License at
+ * https://source.collectionspace.org/collection-space/LICENSE.txt
+ */
+package org.collectionspace.services.common;
+
+import java.io.IOException;
+import java.net.ConnectException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+import org.collectionspace.services.common.CSWebApplicationException;
+import org.collectionspace.services.common.ServletTools;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * CSpaceFilter.java
+ *
+ * A filter that performs specified actions at the time
+ * each new request is received by the servlet container.
+ *
+ * This filter is currently used for recording performance
+ * metrics for requests to the CollectionSpace services.
+ *
+ * $LastChangedRevision: $
+ * $LastChangedDate: $
+ *
+ */
+public class NetworkErrorRetryFilter implements Filter {
+ final Logger logger = LoggerFactory.getLogger(NetworkErrorRetryFilter.class);
+
+ /** The filter config. */
+ FilterConfig filterConfig = null;
+ private final String CLASS_NAME = this.getClass().getSimpleName();
+
+ private long maxRetrySeconds = MAX_RETRY_SECONDS;
+ private static final int MAX_RETRY_SECONDS = 5;
+ private static final String MAX_RETRY_SECONDS_STR = "maxRetrySeconds";
+
+ private long delayBetweenAttemptsMillis = DELAY_BETWEEN_ATTEMPTS_MILLISECONDS;
+ private static final String DELAY_BETWEEN_ATTEMPTS_MILLISECONDS_STR = "delayBetweenAttemptsMillis";
+ private static final long DELAY_BETWEEN_ATTEMPTS_MILLISECONDS = 200;
+
+ protected void setMaxRetrySeconds(FilterConfig filterConfig) {
+ String paramValue = filterConfig.getInitParameter(MAX_RETRY_SECONDS_STR);
+ if (paramValue != null) {
+ try {
+ maxRetrySeconds = Long.parseLong(paramValue);
+ } catch (NumberFormatException e) {
+ logger.warn(String.format("The init parameter '%s' with value '%s' of the servlet filter '%s' could not be parsed to a long value. The default value of '%d' will be used instead.",
+ MAX_RETRY_SECONDS_STR, paramValue, CLASS_NAME, maxRetrySeconds));
+ }
+ }
+ }
+
+ protected long getMaxRetrySeconds() {
+ return this.maxRetrySeconds;
+ }
+
+ protected void setDelayBetweenAttemptsMillis(FilterConfig filterConfig) {
+ String paramValue = filterConfig.getInitParameter(DELAY_BETWEEN_ATTEMPTS_MILLISECONDS_STR);
+ if (paramValue != null) {
+ try {
+ delayBetweenAttemptsMillis = Long.parseLong(paramValue);
+ } catch (NumberFormatException e) {
+ logger.warn(String.format("The init parameter '%s' with value '%s' of the servlet filter '%s' could not be parsed to a long value. The default value of '%d' will be used instead.",
+ MAX_RETRY_SECONDS_STR, paramValue, CLASS_NAME, delayBetweenAttemptsMillis));
+ }
+ }
+ }
+
+ protected long getDelayBetweenAttemptsMillis() {
+ return this.delayBetweenAttemptsMillis;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
+ */
+ @Override
+ public void init(FilterConfig theFilterConfig) throws ServletException {
+ filterConfig = theFilterConfig;
+
+ if (filterConfig != null) {
+ setMaxRetrySeconds(theFilterConfig);
+ setDelayBetweenAttemptsMillis(theFilterConfig);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.Filter#destroy()
+ */
+ @Override
+ public void destroy() {
+ // Empty method.
+ }
+
+ /* (non-Javadoc)
+ * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
+ */
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response,
+ FilterChain chain) throws IOException, ServletException {
+ if (request != null) {
+ try {
+ doWrappedFilter(request, response, chain);
+ } catch (Throwable e) {
+ if (logger.isDebugEnabled() == true) {
+ logger.debug(CLASS_NAME, e);
+ }
+ throw new ServletException(e);
+ }
+ }
+ }
+
+ //
+ // This method will attempt to repeat the chain.doFilter() method if it fails because of a
+ // network related reason. It looks for failures in two ways:
+ //
+ // 1. It looks at the response content type for a special string that we set in the CSWebApplicationException class.
+ // 2. If we catch an exception, we look at the exception chain for network related exceptions.
+ //
+ public void doWrappedFilter(ServletRequest request, ServletResponse response,
+ FilterChain chain) throws Throwable {
+ boolean failed = true;
+ Throwable lastException = null;
+ int requestAttempts = 0;
+
+ long quittingTime = System.currentTimeMillis() + getMaxRetrySeconds() * 1000; // This is how long we attempt retries
+ do {
+ if (requestAttempts > 0) {
+ response.reset(); // This will reset the response instance from the last failed attempt -i.e., we're retrying the request so we don't care about the last response
+ Thread.sleep(getDelayBetweenAttemptsMillis()); // Wait a little time between reattempts.
+ }
+
+ try {
+ // proceed to the original request by calling doFilter()
+ chain.doFilter(request, response);
+ // check the response for network related errors
+ if (hasNetworkRelatedError(response) == false) {
+ failed = false;
+ break; // the request was successfully executed, so we can break out of this retry loop
+ } else {
+ throw new ConnectException(); // The 'response' argument indicated a network related failure, so let's throw a generic connection exception
+ }
+ } catch (Exception e) {
+ lastException = e;
+ if (CSWebApplicationException.exceptionChainContainsNetworkError(lastException) == false) {
+ // Break if the exception chain does not contain a
+ // SocketException because we don't want to retry if it's not a network related failure
+ break;
+ }
+ requestAttempts++; // keep track of how many times we've tried the request
+ }
+ } while (System.currentTimeMillis() < quittingTime); // keep trying until we run out of time
+
+ //
+ // Add a warning to the logs if we encountered *any* failures on our re-attempts. Only add the warning
+ // if we were eventually successful.
+ //
+ if (requestAttempts > 0 && failed == false) {
+ logger.warn(String.format("Request to '%s' URL failed with exception '%s' at attempt number '%d' before finally succeeding on the next attempt.",
+ ServletTools.getURL((HttpServletRequest) request),
+ lastException.getClass().getName(),
+ requestAttempts));
+ }
+
+ if (failed == true) {
+ // If we get here, it means all of our attempts to get a successful call to chain.doFilter() have failed.
+ throw lastException;
+ }
+ }
+
+ private boolean hasNetworkRelatedError(ServletResponse response) {
+ boolean result = false;
+
+ String contentType = response.getContentType();
+ if (contentType != null && contentType.equalsIgnoreCase(CSWebApplicationException.NETWORK_ERROR_TYPE) == true) {
+ result = true;
+ }
+
+ if (contentType == null) {
+ logger.debug("Response had no content type specified. Probably just a successful POST which will have content type.");
+ }
+
+ return result;
+ }
+}
public DocumentException(Throwable cause) {
super(cause);
}
+
+ public String getCausesClassName() {
+ String result = null;
+
+ Throwable cause = super.getCause();
+ if (cause != null) {
+ result = cause.getClass().getCanonicalName();
+ }
+
+ return result;
+ }
}
package org.collectionspace.services.common.profile;\r
\r
import java.io.IOException;\r
-import java.net.SocketException;\r
\r
import javax.servlet.Filter;\r
import javax.servlet.FilterChain;\r
import javax.servlet.ServletRequest;\r
import javax.servlet.ServletResponse;\r
import javax.servlet.http.HttpServletRequest;\r
-//import javax.servlet.ServletContext;\r
-\r
\r
import org.collectionspace.services.client.Profiler;\r
import org.collectionspace.services.common.ServletTools;\r
+\r
import org.slf4j.Logger;\r
import org.slf4j.LoggerFactory;\r
\r
*\r
*/\r
public class CSpaceFilter implements Filter {\r
+ final Logger logger = LoggerFactory.getLogger(CSpaceFilter.class);\r
\r
/** The filter config. */\r
FilterConfig filterConfig = null;\r
-\r
private final String CLASS_NAME = this.getClass().getSimpleName();\r
- private static final int MAX_RETRY_SECONDS = 5;\r
-\r
\r
/* (non-Javadoc)\r
* @see javax.servlet.Filter#destroy()\r
profiler.log(csvMsg, FORMAT_LOG_MESSAGE);\r
\r
// Process the request.\r
- //chain.doFilter(request, response);\r
- try {\r
- invoke(request, response, chain);\r
- } catch (Throwable e) {\r
- // TODO Auto-generated catch block\r
- e.printStackTrace();\r
- }\r
+ chain.doFilter(request, response);\r
\r
// Stop timing and log performance-related metrics.\r
profiler.stop();\r
}\r
}\r
\r
- public void invoke(ServletRequest request, ServletResponse response,\r
- FilterChain chain) throws Throwable {\r
- Throwable lastException = null;\r
- int attempt = 0;\r
-\r
- long quittingTime = System.currentTimeMillis() + MAX_RETRY_SECONDS * 1000;\r
- do {\r
- try {\r
- // proceed to original method call\r
- chain.doFilter(request, response);\r
- String contentType = response.getContentType();\r
- lastException = null;\r
- break;\r
- } catch (Exception e) {\r
- lastException = e;\r
- if (exceptionChainContains(lastException, SocketException.class) == false) {\r
- // Break if the exception chain does not contain a\r
- // SocketException.\r
- break;\r
- }\r
- attempt++;\r
- System.out\r
- .println(String\r
- .format("'%s' URL request failed with exception '%s' at attemp '%d'",\r
- ServletTools.getURL((HttpServletRequest) request),\r
- lastException.getClass().getName(),\r
- attempt));\r
- }\r
- } while (System.currentTimeMillis() < quittingTime);\r
-\r
- if (lastException != null) {\r
- throw lastException;\r
- }\r
-\r
- System.out.println("Success!");\r
- }\r
- \r
- private boolean exceptionChainContains(Throwable exceptionChain,\r
- Class<?> target) {\r
- boolean result = false;\r
- Throwable top = exceptionChain;\r
-\r
- while (top != null) {\r
- System.out.println(top.getClass().getCanonicalName());\r
- if (target.isInstance(top) == true) {\r
- result = true;\r
- break;\r
- }\r
- top = top.getCause();\r
- }\r
-\r
- return result;\r
- }\r
- \r
-\r
/* (non-Javadoc)\r
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)\r
*/\r
\r
import org.collectionspace.services.client.IQueryManager;\r
import org.collectionspace.services.common.ServiceMain;\r
-import org.collectionspace.services.common.api.Tools;\r
import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;\r
import org.collectionspace.services.common.config.TenantBindingUtils;\r
import org.collectionspace.services.common.context.ServiceContext;\r
public class QueryManager {\r
static private final IQueryManager queryManager = new QueryManagerNuxeoImpl();\r
\r
- static public void execQuery(String queryString) {\r
- queryManager.execQuery(queryString);\r
- }\r
- \r
/**\r
* Creates the where clause from keywords.\r
* \r
import java.util.regex.Matcher;\r
import java.util.regex.Pattern;\r
\r
-import org.nuxeo.ecm.core.api.DocumentModel;\r
-import org.nuxeo.ecm.core.api.DocumentModelList;\r
-import org.nuxeo.ecm.core.api.repository.RepositoryInstance;\r
//import org.nuxeo.ecm.core.client.NuxeoClient;\r
\r
import org.collectionspace.services.jaxb.InvocableJAXBSchema;\r
//import org.collectionspace.services.nuxeo.client.java.NuxeoConnector;\r
//import org.collectionspace.services.nuxeo.client.java.NxConnect;\r
-import org.collectionspace.services.nuxeo.client.java.NuxeoClientEmbedded;\r
-import org.collectionspace.services.nuxeo.client.java.NuxeoConnectorEmbedded;\r
\r
import org.collectionspace.services.client.IQueryManager;\r
import org.collectionspace.services.common.invocable.InvocableUtils;\r
@Override\r
@Deprecated\r
public void execQuery(String queryString) {\r
- NuxeoClientEmbedded client = null;\r
- try {\r
- client = NuxeoConnectorEmbedded.getInstance().getClient();\r
- RepositoryInstance repoSession = client.openRepository();\r
-\r
- DocumentModelList docModelList = repoSession\r
- .query("SELECT * FROM Relation WHERE relations_common:subjectCsid='updated-Subject-1'");\r
- // DocumentModelList docModelList =\r
- // repoSession.query("SELECT * FROM Relation");\r
- // DocumentModelList docModelList =\r
- // repoSession.query("SELECT * FROM CollectionObject WHERE collectionobject:objectNumber='objectNumber-1251305545865'");\r
- for (DocumentModel docModel : docModelList) {\r
- System.out\r
- .println("--------------------------------------------");\r
- System.out.println(docModel.getPathAsString());\r
- System.out.println(docModel.getName());\r
- System.out.println(docModel.getPropertyValue("dc:title"));\r
- // System.out.println("subjectCsid=" +\r
- // docModel.getProperty("relations_common",\r
- // "subjectCsid").toString());\r
- }\r
-\r
- } catch (Exception e) {\r
- // TODO Auto-generated catch block\r
- e.printStackTrace();\r
- }\r
+ // Intentionally left blank\r
}\r
\r
@Override\r
import org.jboss.resteasy.annotations.interception.ServerInterceptor;
import org.jboss.resteasy.spi.Failure;
import org.jboss.resteasy.spi.HttpRequest;
+
import org.nuxeo.runtime.api.Framework;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
-import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import org.collectionspace.services.common.document.JaxbUtils;
import org.collectionspace.services.common.storage.jpa.JpaStorageUtils;
import org.collectionspace.services.common.security.SecurityUtils;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//
private static final String ERROR_NUXEO_LOGOUT = "Attempt to logout when Nuxeo login context was null";
private static final String ERROR_UNBALANCED_LOGINS = "The number of Logins vs Logouts to the Nuxeo framework was unbalanced.";
-
- private boolean implementsInterface(Class<?> clazz, Class<?> interfaze) {
- boolean result = false;
-
- Class<?>[] interfaces = clazz.getInterfaces();
- for (Class<?> interfaceItem : interfaces) {
- if (interfaceItem.equals(interfaze)) {
- result = true;
- break;
- }
- }
-
- return result;
- }
-
+
private boolean isAnonymousRequest(HttpRequest request, ResourceMethod resourceMethod) {
boolean result = false;
throws Failure, CSWebApplicationException {
ServerResponse result = null; // A null value essentially means success for this method
- if (isAnonymousRequest(request, resourceMethod) == true) {
- // We don't need to check credentials for anonymous requests. Just login to Nuxeo and
- // exit
- nuxeoPreProcess(request, resourceMethod);
-
- return result;
- }
-
- final String servicesResource = "/cspace-services/"; // HACK - this is configured in war
- final int servicesResourceLen = servicesResource.length();
- String httpMethod = request.getHttpMethod();
- String uriPath = request.getUri().getPath();
-
- if (logger.isDebugEnabled()) {
- String fullRequest = request.getUri().getRequestUri().toString();
- int servicesResourceIdx = fullRequest.indexOf(servicesResource);
- String relativeRequest = (servicesResourceIdx<=0)? fullRequest
- : fullRequest.substring(servicesResourceIdx+servicesResourceLen);
- logger.debug("received " + httpMethod + " on " + relativeRequest);
- }
-
- String resName = SecurityUtils.getResourceName(request.getUri());
- String resEntity = SecurityUtils.getResourceEntity(resName);
-
- //
- // If the resource entity is acting as a proxy then all sub-resource will map to the resource itself.
- // This essentially means that the sub-resource inherit all the authz permissions of the entity.
- //
- if (SecurityUtils.isEntityProxy() == true && !resName.equalsIgnoreCase(ACCOUNT_PERMISSIONS)) {
- resName = resEntity;
- }
- //
- // Make sure the account is current and active
- //
- checkActive();
-
- //
- // All active users are allowed to the *their* (we enforce this) current list of permissions. If this is not
- // the request, then we'll do a full AuthZ check.
- //
- if (resName.equalsIgnoreCase(ACCOUNT_PERMISSIONS) != true) { //see comment immediately above
- AuthZ authZ = AuthZ.get();
- CSpaceResource res = new URIResourceImpl(AuthN.get().getCurrentTenantId(), resName, httpMethod);
- if (authZ.isAccessAllowed(res) == false) {
- logger.error("Access to " + res.getId() + " is NOT allowed to "
- + " user=" + AuthN.get().getUserId());
- Response response = Response.status(
- Response.Status.FORBIDDEN).entity(uriPath + " " + httpMethod).type("text/plain").build();
- throw new CSWebApplicationException(response);
- } else {
- //
- // They passed the first round of security checks, so now let's check to see if they're trying
- // to perform a workflow state change and make sure they are allowed to to this.
- //
- if (uriPath.contains(WorkflowClient.SERVICE_PATH) == true) {
- String workflowProxyResource = SecurityUtils.getWorkflowResourceName(request);
- res = new URIResourceImpl(AuthN.get().getCurrentTenantId(), workflowProxyResource, httpMethod);
- if (authZ.isAccessAllowed(res) == false) {
- logger.error("Access to " + resName + ":" + res.getId() + " is NOT allowed to "
+ try {
+ if (isAnonymousRequest(request, resourceMethod) == true) {
+ // We don't need to check credentials for anonymous requests. Just login to Nuxeo and
+ // exit
+ nuxeoPreProcess(request, resourceMethod);
+
+ return result;
+ }
+
+ final String servicesResource = "/cspace-services/"; // HACK - this is configured in war
+ final int servicesResourceLen = servicesResource.length();
+ String httpMethod = request.getHttpMethod();
+ String uriPath = request.getUri().getPath();
+
+ if (logger.isDebugEnabled()) {
+ String fullRequest = request.getUri().getRequestUri().toString();
+ int servicesResourceIdx = fullRequest.indexOf(servicesResource);
+ String relativeRequest = (servicesResourceIdx<=0)? fullRequest
+ : fullRequest.substring(servicesResourceIdx+servicesResourceLen);
+ logger.debug("received " + httpMethod + " on " + relativeRequest);
+ }
+
+ String resName = SecurityUtils.getResourceName(request.getUri());
+ String resEntity = SecurityUtils.getResourceEntity(resName);
+
+ //
+ // If the resource entity is acting as a proxy then all sub-resource will map to the resource itself.
+ // This essentially means that the sub-resource inherit all the authz permissions of the entity.
+ //
+ if (SecurityUtils.isEntityProxy() == true && !resName.equalsIgnoreCase(ACCOUNT_PERMISSIONS)) {
+ resName = resEntity;
+ }
+ //
+ // Make sure the account is current and active
+ //
+ checkActive();
+
+ //
+ // All active users are allowed to the *their* (we enforce this) current list of permissions. If this is not
+ // the request, then we'll do a full AuthZ check.
+ //
+ if (resName.equalsIgnoreCase(ACCOUNT_PERMISSIONS) != true) { //see comment immediately above
+ AuthZ authZ = AuthZ.get();
+ CSpaceResource res = new URIResourceImpl(AuthN.get().getCurrentTenantId(), resName, httpMethod);
+ if (authZ.isAccessAllowed(res) == false) {
+ logger.error("Access to " + res.getId() + " is NOT allowed to "
+ " user=" + AuthN.get().getUserId());
Response response = Response.status(
Response.Status.FORBIDDEN).entity(uriPath + " " + httpMethod).type("text/plain").build();
throw new CSWebApplicationException(response);
+ } else {
+ //
+ // They passed the first round of security checks, so now let's check to see if they're trying
+ // to perform a workflow state change and make sure they are allowed to to this.
+ //
+ if (uriPath.contains(WorkflowClient.SERVICE_PATH) == true) {
+ String workflowProxyResource = SecurityUtils.getWorkflowResourceName(request);
+ res = new URIResourceImpl(AuthN.get().getCurrentTenantId(), workflowProxyResource, httpMethod);
+ if (authZ.isAccessAllowed(res) == false) {
+ logger.error("Access to " + resName + ":" + res.getId() + " is NOT allowed to "
+ + " user=" + AuthN.get().getUserId());
+ Response response = Response.status(
+ Response.Status.FORBIDDEN).entity(uriPath + " " + httpMethod).type("text/plain").build();
+ throw new CSWebApplicationException(response);
+ }
}
}
+ //
+ // Login to Nuxeo
+ //
+ nuxeoPreProcess(request, resourceMethod);
+
+ //
+ // We've passed all the checks. Now just log the results
+ //
+ if (logger.isTraceEnabled()) {
+ logger.trace("Access to " + res.getId() + " is allowed to " +
+ " user=" + AuthN.get().getUserId() +
+ " for tenant id=" + AuthN.get().getCurrentTenantName());
+ }
}
- //
- // Login to Nuxeo
- //
- nuxeoPreProcess(request, resourceMethod);
-
- //
- // We've passed all the checks. Now just log the results
- //
- if (logger.isTraceEnabled()) {
- logger.trace("Access to " + res.getId() + " is allowed to " +
- " user=" + AuthN.get().getUserId() +
- " for tenant id=" + AuthN.get().getCurrentTenantName());
+ } catch (Throwable t) {
+ if (logger.isTraceEnabled() == true) {
+ t.printStackTrace();
}
+ throw t;
}
return result;
/**
* checkActive check if account is active
- * @throws CSWebApplicationException
+ * @throws CSWebApplicationException
*/
private void checkActive() throws CSWebApplicationException {
String userId = AuthN.get().getUserId();
-
- if (true)
- throw new CSWebApplicationException(new java.net.SocketException("An faux exception thrown for testing."));
-
+
try {
// Need to ensure that user is associated to a tenant
String tenantId = AuthN.get().getCurrentTenantId();
+ threadLocalLoginContext.get());
}
}
+
LoginContext loginContext = threadLocalLoginContext.get();
+
if (loginContext == null) {
loginContext = Framework.loginAs(user);
frameworkLogins++;
return openRepository(repoName, -1);\r
} \r
\r
- /*\r
- * Open a Nuxeo repo session using the default repo with the specified (passed in) tx timeout period\r
- */\r
- @Deprecated\r
- public RepositoryInstance openRepository(int timeoutSeconds) throws Exception {\r
- return openRepository(null, timeoutSeconds);\r
- }\r
-\r
- /*\r
- * Open a Nuxeo repo session using the default repo with the default tx timeout period\r
- */\r
- @Deprecated\r
- public RepositoryInstance openRepository() throws Exception {\r
- return openRepository(null, -1 /*default timeout period*/);\r
- }\r
-\r
public RepositoryInstance openRepository(String repoName, int timeoutSeconds) throws Exception {\r
RepositoryInstance result = null;\r
\r
--- /dev/null
+package org.collectionspace.services.nuxeo.client.java;
+
+import org.collectionspace.services.common.document.DocumentException;
+import org.nuxeo.ecm.core.api.WrappedException;
+
+public class NuxeoDocumentException extends DocumentException {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public NuxeoDocumentException() {
+ super();
+ }
+
+ public NuxeoDocumentException(String msg) {
+ super(msg);
+ // TODO Auto-generated constructor stub
+ }
+
+ public NuxeoDocumentException(int errorCode) {
+ super(errorCode);
+ // TODO Auto-generated constructor stub
+ }
+
+ public NuxeoDocumentException(int errorCode, String errorReason) {
+ super(errorCode, errorReason);
+ // TODO Auto-generated constructor stub
+ }
+
+ public NuxeoDocumentException(String message, Throwable cause) {
+ super(message, cause);
+ // TODO Auto-generated constructor stub
+ }
+
+ public NuxeoDocumentException(Throwable cause) {
+ super(cause);
+ // TODO Auto-generated constructor stub
+ }
+
+ @Override
+ public String getCausesClassName() {
+ String result = null;
+ Throwable cause = super.getCause();
+
+ if (cause != null && cause instanceof WrappedException) {
+ WrappedException wrappedException = (WrappedException)cause;
+ result = wrappedException.getClassName();
+ } else {
+ result = cause != null ? super.getCausesClassName() : null;
+ }
+
+ return result;
+ }
+
+ protected boolean isNuxeoWrappedException(Throwable cause) {
+ boolean result = false;
+
+ String className = cause.getClass().getCanonicalName();
+ if (className.contains("org.nuxeo.ecm.core.api.WrappedException") == true) {
+ result = true;
+ }
+
+ return result;
+ }
+
+}
throw bre;
} catch (Exception e) {
logger.error("Caught exception ", e);
- throw new DocumentException(e);
+ throw new NuxeoDocumentException(e);
} finally {
if (repoSession != null) {
releaseRepositorySession(ctx, repoSession);
if (logger.isDebugEnabled()) {
logger.debug("Caught exception ", e);
}
- throw new DocumentException(e);
+ throw new NuxeoDocumentException(e);
} finally {
if (repoSession != null) {
releaseRepositorySession(ctx, repoSession);
if (logger.isDebugEnabled()) {
logger.debug("Caught exception ", e);
}
- throw new DocumentException(e);
+ throw new NuxeoDocumentException(e);
} finally {
if (repoSession != null) {
releaseRepositorySession(ctx, repoSession);
if (logger.isDebugEnabled()) {
logger.debug("Caught exception ", e);
}
- throw new DocumentException(e);
+ throw new NuxeoDocumentException(e);
} finally {
if (repoSession != null) {
releaseRepositorySession(ctx, repoSession);
if (logger.isDebugEnabled()) {
logger.debug("Caught exception ", e);
}
- throw new DocumentException(e);
+ throw new NuxeoDocumentException(e);
}
return wrapDoc;
repoSession = getRepositorySession(ctx);
wrapDoc = findDoc(repoSession, ctx, whereClause);
} catch (Exception e) {
- throw new DocumentException("Unable to create a Nuxeo repository session.", e);
+ throw new NuxeoDocumentException("Unable to create a Nuxeo repository session.", e);
} finally {
if (repoSession != null) {
releaseRepositorySession(ctx, repoSession);
if (logger.isDebugEnabled()) {
logger.debug("Caught exception ", e);
}
- throw new DocumentException(e);
+ throw new NuxeoDocumentException(e);
} finally {
if (releaseSession && (repoSession != null)) {
this.releaseRepositorySession(ctx, repoSession);
if (logger.isDebugEnabled()) {
logger.debug("Caught exception ", e);
}
- throw new DocumentException(e);
+ throw new NuxeoDocumentException(e);
}
return wrapDoc;
if (logger.isDebugEnabled()) {
logger.debug("Caught exception ", e);
}
- throw new DocumentException(e);
+ throw new NuxeoDocumentException(e);
}
return wrapDoc;
if (logger.isDebugEnabled()) {
logger.debug("Caught exception ", e);
}
- throw new DocumentException(e);
+ throw new NuxeoDocumentException(e);
} finally {
if (repoSession != null) {
releaseRepositorySession(ctx, repoSession);
if (logger.isDebugEnabled()) {
logger.debug("Caught exception ", e);
}
- throw new DocumentException(e);
+ throw new NuxeoDocumentException(e);
} finally {
if (repoSession != null) {
releaseRepositorySession(ctx, repoSession);
if (logger.isDebugEnabled()) {
logger.debug("Caught exception ", e);
}
- throw new DocumentException(e);
+ throw new NuxeoDocumentException(e);
} finally {
if (repoSession != null) {
releaseRepositorySession(ctx, repoSession);
if (logger.isDebugEnabled()) {
logger.debug("Caught exception ", e); // REM - 1/17/2014: Check for org.nuxeo.ecm.core.api.ClientException and re-attempt
}
- throw new DocumentException(e);
+ throw new NuxeoDocumentException(e);
} finally {
if (repoSession != null) {
releaseRepositorySession(ctx, repoSession);
if (logger.isDebugEnabled()) {
logger.debug("Caught exception ", e);
}
- throw new DocumentException(e);
+ throw new NuxeoDocumentException(e);
}
//
/* Once we advance to 5.5 or later, we can add this.
* See also https://jira.nuxeo.com/browse/NXP-8506
if(!doc.isVersionable()) {
- throw new DocumentException("Configuration for: "
+ throw new NuxeoDocumentException("Configuration for: "
+handler.getServiceContextPath()+" supports versioning, but Nuxeo config does not!");
}
*/
if (logger.isDebugEnabled()) {
logger.debug("Caught exception ", e);
}
- throw new DocumentException(e);
+ throw new NuxeoDocumentException(e);
} finally {
if (repoSession != null) {
releaseRepositorySession(ctx, repoSession);
if (logger.isDebugEnabled()) {
logger.debug("Caught exception ", e);
}
- throw new DocumentException(e);
+ throw new NuxeoDocumentException(e);
}
}
throw ce;
} catch (Exception e) {
logger.error("Caught exception ", e);
- throw new DocumentException(e);
+ throw new NuxeoDocumentException(e);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Caught exception ", e);
}
- throw new DocumentException(e);
+ throw new NuxeoDocumentException(e);
} finally {
if (repoSession != null) {
releaseRepositorySession(ctx, repoSession);
if (logger.isDebugEnabled()) {
logger.debug("Caught exception ", e);
}
- throw new DocumentException(e);
+ throw new NuxeoDocumentException(e);
} finally {
if (repoSession != null) {
releaseRepositorySession(null, repoSession);
+++ /dev/null
-/**\r
- * This document is a part of the source code and related artifacts\r
- * for CollectionSpace, an open source collections management system\r
- * for museums and related institutions:\r
-\r
- * http://www.collectionspace.org\r
- * http://wiki.collectionspace.org\r
-\r
- * Copyright 2009 University of California at Berkeley\r
-\r
- * Licensed under the Educational Community License (ECL), Version 2.0.\r
- * You may not use this file except in compliance with this License.\r
-\r
- * You may obtain a copy of the ECL 2.0 License at\r
-\r
- * https://source.collectionspace.org/collection-space/LICENSE.txt\r
-\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package org.collectionspace.services.query;\r
-\r
-import javax.ws.rs.Consumes;\r
-import javax.ws.rs.GET;\r
-import javax.ws.rs.Path;\r
-import javax.ws.rs.Produces;\r
-import javax.ws.rs.PathParam;\r
-import javax.ws.rs.WebApplicationException;\r
-import javax.ws.rs.core.Response;\r
-//import javax.xml.bind.JAXBContext;\r
-//import javax.xml.bind.Marshaller;\r
-\r
-\r
-import org.collectionspace.services.common.CSWebApplicationException;\r
-import org.collectionspace.services.common.query.QueryManager;\r
-//import org.collectionspace.services.common.NuxeoClientType;\r
-/*import org.collectionspace.services.common.ServiceMain;\r
-import org.collectionspace.services.common.document.DocumentNotFoundException;\r
-import org.collectionspace.services.common.document.DocumentHandler;\r
-import org.collectionspace.services.common.repository.RepositoryClient;\r
-import org.collectionspace.services.common.repository.RepositoryClientFactory;\r
-import org.jboss.resteasy.util.HttpResponseCodes;\r
-*/\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-@Path("/query")\r
-@Consumes("application/xml")\r
-@Produces("application/xml")\r
-public class QueryResource {\r
-\r
- public final static String SERVICE_NAME = "query";\r
- final Logger logger = LoggerFactory.getLogger(QueryResource.class);\r
- //FIXME retrieve client type from configuration\r
- //final static NuxeoClientType CLIENT_TYPE = ServiceMain.getInstance().getNuxeoClientType();\r
-\r
- public QueryResource() {\r
- // do nothing\r
- }\r
-\r
- @GET\r
- @Path("{csid}")\r
- public void getQuery(\r
- @PathParam("csid") String csid) {\r
- if(logger.isDebugEnabled()){\r
- verbose("getQuery with csid=" + csid);\r
- }\r
- if (csid == null || "".equals(csid)){\r
- logger.error("getQuery: missing csid!");\r
- Response response = Response.status(Response.Status.BAD_REQUEST).entity(\r
- "get failed on getQuery csid=" + csid).type(\r
- "text/plain").build();\r
- throw new CSWebApplicationException(response);\r
- }\r
-\r
- try {\r
- QueryManager.execQuery(csid);\r
- } catch(Exception e){\r
- if(logger.isDebugEnabled()){\r
- logger.debug("getQuery", e);\r
- }\r
- Response response = Response.status(Response.Status.NOT_FOUND).entity(\r
- "Get failed on query csid=" + csid).type(\r
- "text/plain").build();\r
- throw new CSWebApplicationException(e, response);\r
- }\r
-\r
- if (false) { // REM - 1/29/2014 : Huh? Why is this essentially commented out? Should probably clean up.\r
- Response response = Response.status(Response.Status.NOT_FOUND).entity(\r
- "Get failed, the requested CSID:" + csid + ": was not found.").type(\r
- "text/plain").build();\r
- throw new CSWebApplicationException(response);\r
- }\r
-\r
-// return intakeObject;\r
- }\r
-\r
-// private void verbose(String msg, Intake intakeObject) {\r
-// try{\r
-// verbose(msg);\r
-// JAXBContext jc = JAXBContext.newInstance(\r
-// Intake.class);\r
-//\r
-// Marshaller m = jc.createMarshaller();\r
-// m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);\r
-// m.marshal(intakeObject, System.out);\r
-// }catch(Exception e){\r
-// e.printStackTrace();\r
-// }\r
-//\r
-// }\r
-\r
- private void verbose(String msg) {\r
- System.out.println("QueryResource. " + msg);\r
- }\r
-}\r