()
{
- public Object execute() throws Throwable
+ public Object execute()
{
if (cInfo.getLogonType() == ClientInfo.LogonNormal
&& getAuthorityService().isAdminAuthority(cInfo.getUserName()))
diff --git a/source/java/org/alfresco/filesys/auth/cifs/EnterpriseCifsAuthenticator.java b/source/java/org/alfresco/filesys/auth/cifs/EnterpriseCifsAuthenticator.java
index f171737737..ef2d68dda4 100644
--- a/source/java/org/alfresco/filesys/auth/cifs/EnterpriseCifsAuthenticator.java
+++ b/source/java/org/alfresco/filesys/auth/cifs/EnterpriseCifsAuthenticator.java
@@ -38,7 +38,6 @@ import javax.security.sasl.RealmCallback;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.filesys.alfresco.AlfrescoClientInfo;
-import org.alfresco.jlan.debug.Debug;
import org.alfresco.jlan.server.auth.AuthenticatorException;
import org.alfresco.jlan.server.auth.ClientInfo;
import org.alfresco.jlan.server.auth.NTLanManAuthContext;
@@ -73,6 +72,8 @@ import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.NTLMMode;
import org.alfresco.repo.security.authentication.ntlm.NLTMAuthenticator;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.ietf.jgss.Oid;
import org.springframework.extensions.config.ConfigElement;
@@ -88,6 +89,9 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Constants
//
// Default login configuration entry name
+ protected static final Log logger = LogFactory.getLog(EnterpriseCifsAuthenticator.class);
+
+ // logger is defined in base class.
private static final String LoginConfigEntry = "AlfrescoCIFS";
@@ -221,7 +225,9 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Debug
if ( logger.isInfoEnabled() && enaTktCracking)
+ {
logger.info("CIFS Kerberos authentication, ticket cracking enabled (for mutual authentication)");
+ }
}
/**
@@ -251,8 +257,10 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
setPassword(srvPassword.getValue());
}
else
+ {
throw new InvalidConfigurationException("CIFS service account password not specified");
-
+ }
+
// Get the login configuration entry name
ConfigElement loginEntry = params.getChild("LoginEntry");
@@ -265,7 +273,9 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
setJaasConfigEntryName(loginEntry.getValue());
}
else
+ {
throw new InvalidConfigurationException("Invalid login entry specified");
+ }
}
setDisableNTLM(params.getChild("disableNTLM") != null);
@@ -338,7 +348,9 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Debug
if (logger.isErrorEnabled())
+ {
logger.error("CIFS Kerberos authenticator error", ex);
+ }
throw new InvalidConfigurationException("Failed to login CIFS server service");
}
@@ -350,10 +362,10 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
m_accountName = princ.getName();
- // DEBUG
-
if (logger.isDebugEnabled())
+ {
logger.debug("Logged on using principal " + m_accountName);
+ }
// Create the Oid list for the SPNEGO NegTokenInit, include NTLMSSP for fallback
@@ -363,9 +375,7 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
if (logger.isDebugEnabled())
{
- logger.debug("Enabling mechTypes :-");
- logger.debug(" Kerberos5");
- logger.debug(" MS-Kerberos5");
+ logger.debug("Enabling mechTypes :-Kerberos5 MS-Kerberos5");
}
// Always enable Kerberos
@@ -380,7 +390,9 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// DEBUG
if (logger.isDebugEnabled())
- logger.debug(" NTLMSSP");
+ {
+ logger.debug(" Enabling NTLMSSP");
+ }
}
// Indicate that SPNEGO security blobs are being used
@@ -409,14 +421,13 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
if (!isKerberosEnabled() && (!(getAuthenticationComponent() instanceof NLTMAuthenticator) || getNTLMAuthenticator().getNTLMMode() != NTLMMode.MD4_PROVIDER))
{
- // Log an error
-
- logger.error("No valid CIFS authentication combination available");
- logger.error("Either enable Kerberos support or use an SSO-enabled authentication component that supports MD4 hashed passwords");
-
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("No valid CIFS authentication combination available, Either enable Kerberos support or use an SSO-enabled authentication component that supports MD4 hashed passwords");
+ }
// Throw an exception to stop the CIFS server startup
- throw new AlfrescoRuntimeException("Invalid CIFS authenticator configuration");
+ throw new AlfrescoRuntimeException("No valid CIFS authentication combination available, Either enable Kerberos support or use an SSO-enabled authentication component that supports MD4 hashed passwords");
}
}
@@ -488,8 +499,9 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Debug
if (logger.isErrorEnabled())
- logger.error("Error creating SPNEGO NegTokenInit blob", ex);
-
+ {
+ logger.error("Unable to create SPNEGO NegTokenInit blob", ex);
+ }
throw new AuthenticatorException("Failed to create SPNEGO NegTokenInit blob");
}
@@ -644,49 +656,97 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
}
/**
- * Process the CIFS session setup request packet and build the session setup response
- *
+ * Process the CIFS session setup request packet and build the session setup response.
+ *
+ * This is the boundary between alfresco and JLAN. So is responsible for logging and
+ * ensuring that the exceptions are correct for JLAN.
+ *
* @param sess SMBSrvSession
* @param reqPkt SMBSrvPacket
* @exception SMBSrvException
*/
public void processSessionSetup(final SMBSrvSession sess, final SMBSrvPacket reqPkt)
+ throws SMBSrvException
+ {
+ try
+ {
+ processAlfrescoSessionSetup(sess, reqPkt);
+ }
+ catch (SMBSrvException e)
+ {
+ /*
+ * A JLAN SMBSrvException is not human readable so we need to log the
+ * error before throwing, rather than logging the error here.
+ */
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Returning SMBSrvException to JLAN", e);
+ }
+ throw e;
+ }
+ catch (AlfrescoRuntimeException a)
+ {
+ Throwable c = a.getCause();
+
+ if(c != null)
+ {
+ if(a.getCause() instanceof SMBSrvException)
+ {
+ logger.error(c.getMessage(), c);
+ throw (SMBSrvException)c;
+ }
+ }
+ logger.error(a.getMessage(), a);
+ throw new SMBSrvException( SMBStatus.NTAccessDenied, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
+
+ }
+ catch (Throwable t)
+ {
+ logger.error(t.getMessage(), t);
+ throw new SMBSrvException( SMBStatus.NTAccessDenied, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
+ }
+ }
+
+ /**
+ * Internal setup method for alfresco. There's lots of CIFS specific stuff here that should
+ * be in JLAN.
+ *
+ * @param sess the JLAN session.
+ * @param reqPkt the CIFS request packet
+ * @throws SMBSrvException
+ */
+ private void processAlfrescoSessionSetup(final SMBSrvSession sess, final SMBSrvPacket reqPkt)
throws SMBSrvException
{
+ logger.debug("Start process Alfresco Session Setup");
+
// Check that the received packet looks like a valid NT session setup andX request
-
if (reqPkt.checkPacketIsValid(12, 0) == false)
- throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
-
+ {
+ if(logger.isErrorEnabled())
+ {
+ logger.error("Invalid packet received, return SMBStatus.NTInvalidParameter");
+ }
+ throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.ErrSrv, SMBStatus.SRVNonSpecificError);
+ }
// Check if the request is using security blobs or the older hashed password format
if ( reqPkt.getParameterCount() == 13)
{
- try
+ logger.debug("parameter count == 13, do Hashed Password Logon");
+
+ doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback()
{
- // Start a transaction
-
- doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback()
- {
-
- public Object execute() throws Throwable
- {
- // Process the hashed password session setup
-
- doHashedPasswordLogon(sess, reqPkt);
- return null;
- }
- });
- }
- catch ( Exception ex)
- {
- // Convert to an access denied exception
-
- throw new SMBSrvException( SMBStatus.NTAccessDenied, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
- }
+ public Object execute() throws SMBSrvException
+ {
+ // Process the hashed password session setup
+ logger.debug("about to call doHashedPasswordLogon");
+ doHashedPasswordLogon(sess, reqPkt);
+ return null;
+ }
+ });
// Hashed password processing complete
-
return;
}
@@ -718,35 +778,40 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
String domain = "";
- if (reqPkt.hasMoreData()) {
-
+ if (reqPkt.hasMoreData())
+ {
// Extract the callers domain name
-
domain = reqPkt.unpackString(isUni);
if (domain == null)
- throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
+ {
+ logger.error("domain is null, return SMBStatus.NTInvalidParameter");
+ throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.ErrSrv, SMBStatus.SRVNonSpecificError);
+ }
}
// Extract the clients native operating system
String clientOS = "";
- if (reqPkt.hasMoreData()) {
+ if (reqPkt.hasMoreData())
+ {
// Extract the callers operating system name
clientOS = reqPkt.unpackString(isUni);
if (clientOS == null)
- throw new SMBSrvException( SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
+ {
+ logger.error("clientOS is null, return SMBStatus.NTInvalidParameter");
+ throw new SMBSrvException( SMBStatus.NTInvalidParameter, SMBStatus.ErrSrv, SMBStatus.SRVNonSpecificError);
+ }
}
- // DEBUG
-
if (logger.isDebugEnabled())
- logger.debug("NT Session setup " + (useRawNTLMSSP() ? "NTLMSSP" : "SPNEGO") + ", MID=" + reqPkt.getMultiplexId() + ", UID=" + reqPkt.getUserId() + ", PID=" + reqPkt.getProcessId());
-
+ {
+ logger.debug("NT Session setup " + (useRawNTLMSSP() ? "NTLMSSP" : "SPNEGO") + ", MID=" + reqPkt.getMultiplexId() + ", UID=" + reqPkt.getUserId() + ", PID=" + reqPkt.getProcessId());
+ }
// Store the client maximum buffer size, maximum multiplexed requests count and client capability flags
sess.setClientMaximumBufferSize(maxBufSize != 0 ? maxBufSize : SMBSrvSession.DefaultBufferSize);
@@ -764,7 +829,9 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Set the remote address, if available
if ( sess.hasRemoteAddress())
- client.setClientAddress(sess.getRemoteAddress().getHostAddress());
+ {
+ client.setClientAddress(sess.getRemoteAddress().getHostAddress());
+ }
// Set the process id for this client, for multi-stage logons
@@ -780,27 +847,28 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
final boolean isNTLMSSP;
try
- {
-
+ {
// Check if the blob has the NTLMSSP signature
-
- if ( secBlobLen >= NTLM.Signature.length) {
-
+ if ( secBlobLen >= NTLM.Signature.length)
+ {
// Check for the NTLMSSP signature
-
int idx = 0;
while ( idx < NTLM.Signature.length && buf[secBlobPos + idx] == NTLM.Signature[ idx])
+ {
idx++;
+ }
isNTLMSSP = ( idx == NTLM.Signature.length);
}
- else {
+ else
+ {
isNTLMSSP = false;
}
-
- // Start a transaction
- respBlob = doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback()
+ /**
+ * Extracted parameters from CIFS, Now try and do the logon
+ */
+ respBlob = doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback()
{
public byte[] execute() throws Throwable
@@ -820,15 +888,17 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
return doSpnegoSessionSetup(sess, client, buf, secBlobPos, secBlobLen, isUni);
}
}
- });
-
+ });
}
catch ( Exception ex)
{
- // Cleanup any stored context
-
sess.removeSetupObject( client.getProcessId());
+ if( ex instanceof SMBSrvException)
+ {
+ throw (SMBSrvException)ex;
+ }
+
// Convert to an access denied exception if necessary
if (ex instanceof AlfrescoRuntimeException && ex.getCause() instanceof SMBSrvException)
@@ -837,15 +907,20 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
}
else
{
+ logger.error("Access denied", ex);
throw new SMBSrvException( SMBStatus.NTAccessDenied, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
}
- // Debug
+ /*
+ * We have logged on - so set up the response
+ */
- if ( logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_NEGOTIATE))
+ if ( logger.isDebugEnabled())
+ {
logger.debug("User " + client.getUserName() + " logged on " + (client != null ? " (type " + client.getLogonTypeString() + ")" : ""));
-
+ }
+
// Update the client information if not already set
if ( sess.getClientInformation() == null ||
@@ -893,23 +968,24 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
if ( reqLen > respPkt.getAvailableLength()) {
- try {
+ try
+ {
// Allocate a new buffer for the response
respPkt = sess.getPacketPool().allocatePacket(respPkt.getByteOffset() + reqLen, reqPkt);
}
- catch (NoPooledMemoryException ex) {
-
- // DEBUG
-
- if ( Debug.EnableDbg && hasDebug())
- Debug.println("Authenticator failed to allocate packet from pool, reqSiz="
- + (respPkt.getByteOffset() + respLen));
+ catch (NoPooledMemoryException ex)
+ {
+ if(logger.isErrorEnabled())
+ {
+ logger.error("Authenticator failed to allocate packet from pool, reqSiz="
+ + (respPkt.getByteOffset() + respLen));
+ }
// Return a server error to the client
- throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.SRVNoBuffers, SMBStatus.ErrSrv);
+ throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrSrv, SMBStatus.SRVNoBuffers);
}
}
@@ -972,19 +1048,13 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
if ( uid == VirtualCircuit.InvalidUID)
{
- // DEBUG
-
- if ( logger.isDebugEnabled() && sess.hasDebug( SMBSrvSession.DBG_NEGOTIATE))
- logger.debug("Failed to allocate UID for virtual circuit, " + vc);
-
- // Failed to allocate a UID
-
- throw new SMBSrvException(SMBStatus.NTTooManySessions, SMBStatus.SRVTooManyUIDs, SMBStatus.ErrSrv);
+ logger.error("Failed to allocate UID for virtual circuit, " + vc);
+ // Failed to allocate a UID
+ throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrSrv, SMBStatus.SRVTooManyUIDs);
+
}
- else if ( logger.isDebugEnabled() && sess.hasDebug( SMBSrvSession.DBG_NEGOTIATE)) {
-
- // DEBUG
-
+ else if ( logger.isDebugEnabled())
+ {
logger.debug("Allocated UID=" + uid + " for VC=" + vc);
}
}
@@ -1046,6 +1116,7 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
byte[] secbuf, int secpos, int seclen, boolean unicode) throws SMBSrvException
{
// Determine the NTLmSSP message type
+ logger.debug("Start doNTLmsspSessionSetup");
int msgType = NTLMMessage.isNTLMType( secbuf, secpos);
byte[] respBlob = null;
@@ -1054,21 +1125,25 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
{
// DEBUG
- if ( logger.isDebugEnabled())
+ if ( logger.isErrorEnabled())
{
- logger.debug("Invalid NTLMSSP token received");
- logger.debug(" Token=" + HexDump.hexString( secbuf, secpos, seclen, " "));
+ logger.error("Invalid NTLMSSP token received, Token= " + HexDump.hexString( secbuf, secpos, seclen, " "));
}
// Return a logon failure status
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
// Check for a type 1 NTLMSSP message
else if ( msgType == NTLM.Type1)
{
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("NTLMsspSessionSetup Type1");
+ }
+
// Create the type 1 NTLM message from the token
Type1NTLMMessage type1Msg = new Type1NTLMMessage( secbuf, secpos, seclen);
@@ -1117,6 +1192,10 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
}
else if ( msgType == NTLM.Type3)
{
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("NTLmsspSessionSetup Type3");
+ }
// Create the type 3 NTLM message from the token
Type3NTLMMessage type3Msg = new Type3NTLMMessage( secbuf, secpos, seclen, unicode);
@@ -1130,8 +1209,9 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
sess.removeSetupObject( client.getProcessId());
// Return a logon failure
+ logger.error("NTLMSSP Logon failure - type 2 message not found");
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
// Determine if the client sent us NTLMv1 or NTLMv2
@@ -1149,7 +1229,9 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Debug
if ( logger.isDebugEnabled())
+ {
logger.debug("Logged on using NTLMSSP/NTLMv2");
+ }
}
else
{
@@ -1160,7 +1242,9 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Debug
if ( logger.isDebugEnabled())
+ {
logger.debug("Logged on using NTLMSSP/NTLMv2SessKey");
+ }
}
}
else
@@ -1172,7 +1256,9 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Debug
if ( logger.isDebugEnabled())
+ {
logger.debug("Logged on using NTLMSSP/NTLMv1");
+ }
}
}
@@ -1228,11 +1314,11 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
{
// Log the error
- logger.error(ex);
+ logger.error("I/O Error with SPNEGO authentication", ex);
// Return a logon failure status
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
// Get the second stage NTLMSSP blob
@@ -1270,11 +1356,11 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
{
// Log the error
- logger.error(ex);
+ logger.error("Unable to decode the SPNEGO token", ex);
// Return a logon failure status
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
// Determine the authentication mechanism the client is using and logon
@@ -1321,8 +1407,9 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
}
// No valid authentication mechanism
+ logger.error("No authentication mechanism for SPNEGO found");
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
}
else
@@ -1333,7 +1420,7 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Return a logon failure status
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
// Generate the NegTokenTarg blob
@@ -1348,14 +1435,9 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
}
catch ( IOException ex)
{
- // Debug
+ logger.error("SPNEGO unable to encode NegTokenTarg", ex);
- if ( logger.isDebugEnabled())
- logger.debug("Failed to encode NegTokenTarg", ex);
-
- // Failed to build response blob
-
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
// Return the SPNEGO response blob
@@ -1416,8 +1498,8 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
{
// Failed to parse AP-REQ
- if ( logger.isDebugEnabled())
- logger.debug("Failed to parse AP-REQ, " + ex.toString());
+
+ logger.error("Kerberos Failed to parse AP-REQ ", ex);
// Return a logon failure status
@@ -1527,15 +1609,15 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Set the current user to be authenticated, save the authentication token
try
- {
+ {
AlfrescoClientInfo alfClient = (AlfrescoClientInfo) client;
- getAuthenticationComponent().setCurrentUser( mapUserNameToPerson(krbDetails.getUserName()));
+ getAuthenticationComponent().setCurrentUser( mapUserNameToPerson(krbDetails.getUserName(), true));
alfClient.setAuthenticationTicket(getAuthenticationService().getCurrentTicket() );
}
catch (AuthenticationException e)
{
// Invalid user or max tickets exceeded. Return a logon failure status
-
+ logger.error("invalid user or tickets exceeded");
throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
@@ -1570,26 +1652,22 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
}
else
{
- // Debug
+ logger.error( "No SPNEGO response, Kerberos logon failed");
- if ( logger.isDebugEnabled())
- logger.debug( "No SPNEGO response, Kerberos logon failed");
-
- // Return a logon failure status
-
+ // Return a logon failure status
throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
}
catch (Exception ex)
{
- // Log the error
-
- if ( logger.isErrorEnabled()) {
- logger.error("Kerberos logon error");
- logger.error(ex);
- }
+
+ if(ex instanceof SMBSrvException)
+ {
+ throw (SMBSrvException)ex;
+ }
// Return a logon failure status
+ logger.error("Error during kerberos authentication", ex);
throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
@@ -1599,6 +1677,20 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
return negTokenTarg;
}
+ private String normalizeUserId(String externalUserId) throws SMBSrvException
+ {
+ try
+ {
+ return mapUserNameToPerson(externalUserId, true);
+ }
+ catch (AuthenticationException e)
+ {
+ // Invalid user. Return a logon failure status
+ logger.debug("Authentication Exception", e);
+ throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
+ }
+ }
+
/**
* Perform an NTLMv1 logon using the NTLMSSP type3 message
*
@@ -1616,11 +1708,11 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
{
// NTLMv1 password hashes not accepted
- logger.warn("NTLMv1 not accepted, client " + sess.getRemoteName());
+ logger.error("NTLMv1 not accepted, client " + sess.getRemoteName());
// Return a logon failure
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
// Get the type 2 message that contains the challenge sent to the client
@@ -1653,7 +1745,8 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Get the stored MD4 hashed password for the user, or null if the user does not exist
- String md4hash = getNTLMAuthenticator().getMD4HashedPassword(userName);
+ String normalized = normalizeUserId(userName);
+ String md4hash = getNTLMAuthenticator().getMD4HashedPassword(normalized);
if ( md4hash != null)
{
@@ -1689,23 +1782,31 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
if ( i != clientHash.length)
{
// Return a logon failure
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("NTLMV1 - hash was not equal");
+ }
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
}
- // Setup the Acegi authenticated user
-
+ /*
+ * Setup the Alfresco Security (Acegi) context
+ */
try
{
- AlfrescoClientInfo alfClient = (AlfrescoClientInfo) client;
- getAuthenticationComponent().setCurrentUser( mapUserNameToPerson(userName));
- alfClient.setAuthenticationTicket(getAuthenticationService().getCurrentTicket());
+ AlfrescoClientInfo alfClient = (AlfrescoClientInfo) client;
+ getAuthenticationComponent().setCurrentUser( normalized);
+ alfClient.setAuthenticationTicket(getAuthenticationService().getCurrentTicket() );
+
+
}
catch (AuthenticationException e)
{
// Invalid user or max tickets exceeded. Return a logon failure status
-
+ logger.debug("Authentication Exception", e);
throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
@@ -1720,13 +1821,14 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
}
else
{
- // Log a warning, user does not exist
-
- logger.warn("User does not exist, " + userName);
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("User does not exist, " + userName);
+ }
// Return a logon failure
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
}
else
@@ -1737,7 +1839,7 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Return a logon failure
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
}
@@ -1761,7 +1863,12 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Return a logon failure
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
+ }
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("START doNTLMv1Logon:" + client);
}
// Check if we are using local MD4 password hashes or passthru authentication
@@ -1775,7 +1882,9 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// DEBUG
if ( logger.isDebugEnabled())
+ {
logger.debug("Null logon");
+ }
// Indicate a null logon in the client information
@@ -1785,7 +1894,8 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Get the stored MD4 hashed password for the user, or null if the user does not exist
- String md4hash = getNTLMAuthenticator().getMD4HashedPassword(client.getUserName());
+ String normalized = normalizeUserId(client.getUserName());
+ String md4hash = getNTLMAuthenticator().getMD4HashedPassword(normalized);
if ( md4hash != null)
{
@@ -1816,6 +1926,9 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
}
catch (NoSuchAlgorithmException ex)
{
+ logger.error("Unable to encrypt challenge", ex);
+
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrSrv, SMBStatus.SRVInternalServerError);
}
// Validate the password
@@ -1832,8 +1945,12 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
if ( i != clientHash.length)
{
// Return a logon failure
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("NTLMV1 access denied - wrong password");
+ }
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrSrv, SMBStatus.SRVBadPassword);
}
}
@@ -1842,12 +1959,13 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
try
{
AlfrescoClientInfo alfClient = (AlfrescoClientInfo) client;
- getAuthenticationComponent().setCurrentUser( mapUserNameToPerson(client.getUserName()));
+ getAuthenticationComponent().setCurrentUser( normalized);
alfClient.setAuthenticationTicket(getAuthenticationService().getCurrentTicket());
}
catch (AuthenticationException e)
{
// Invalid user or max tickets exceeded. Return a logon failure status
+ logger.debug("Authentication exception", e);
throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
@@ -1867,18 +1985,18 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Return a logon failure
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
}
else
{
// Log a warning, authentication component does not support MD4 hashed passwords
- logger.warn("Authentication component does not support MD4 password hashes");
+ logger.error("Authentication component does not support MD4 password hashes");
// Return a logon failure
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
}
@@ -1900,6 +2018,11 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Check if we are using local MD4 password hashes or passthru authentication
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("START doNTLMv2Logon:" + client);
+ }
+
if ( getNTLMAuthenticator().getNTLMMode() == NTLMMode.MD4_PROVIDER)
{
// Get the NTLM logon details
@@ -1913,7 +2036,9 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// DEBUG
if ( logger.isDebugEnabled())
+ {
logger.debug("Null logon");
+ }
// Indicate a null logon in the client information
@@ -1923,7 +2048,8 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Get the stored MD4 hashed password for the user, or null if the user does not exist
- String md4hash = getNTLMAuthenticator().getMD4HashedPassword(userName);
+ String normalized = normalizeUserId(userName);
+ String md4hash = getNTLMAuthenticator().getMD4HashedPassword(normalized);
if ( md4hash != null)
{
@@ -1953,19 +2079,24 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
if ( i != clientHmac.length)
{
// Return a logon failure
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("wrong password");
+ }
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrSrv, SMBStatus.SRVBadPassword);
}
}
// Setup the Acegi authenticated user
AlfrescoClientInfo alfClient = (AlfrescoClientInfo) client;
- getAuthenticationComponent().setCurrentUser( mapUserNameToPerson( userName));
+ getAuthenticationComponent().setCurrentUser( normalized);
alfClient.setAuthenticationTicket(getAuthenticationService().getCurrentTicket());
+
// Store the full user name in the client information, indicate that this is not a guest logon
-
+
client.setUserName( userName.toLowerCase());
client.setGuest( false);
@@ -1975,31 +2106,35 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
}
catch ( Exception ex)
{
+ if(ex instanceof SMBSrvException)
+ {
+ throw (SMBSrvException)ex;
+ }
+
// Log the error
if (ex instanceof AuthenticationException)
{
- logger.debug(ex);
+ logger.debug(ex.getMessage(), ex);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
else
{
- logger.error(ex);
+ logger.error(ex.getMessage(), ex);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
- // Return a logon failure
-
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
}
}
else
{
// Log a warning, user does not exist
- logger.warn("User does not exist, " + userName);
+ logger.warn("MD4Hash for User does not exist, " + userName);
// Return a logon failure
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
}
else
@@ -2010,7 +2145,7 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Return a logon failure
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
}
@@ -2026,6 +2161,11 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
{
// Check if we are using local MD4 password hashes or passthru authentication
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("START doNTLMv2Logon:" + client);
+ }
+
if ( getNTLMAuthenticator().getNTLMMode() == NTLMMode.MD4_PROVIDER)
{
// Check for a null logon
@@ -2035,7 +2175,9 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// DEBUG
if ( logger.isDebugEnabled())
+ {
logger.debug("Null logon");
+ }
// Indicate a null logon in the client information
@@ -2045,7 +2187,8 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Get the stored MD4 hashed password for the user, or null if the user does not exist
- String md4hash = getNTLMAuthenticator().getMD4HashedPassword(client.getUserName());
+ String normalized = normalizeUserId(client.getUserName());
+ String md4hash = getNTLMAuthenticator().getMD4HashedPassword(normalized);
if ( md4hash != null)
{
@@ -2084,16 +2227,17 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
if ( i != clientHmac.length)
{
+ logger.debug("bad client hmac");
+
// Return a logon failure
-
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrSrv, SMBStatus.SRVBadPassword);
}
}
// Setup the Acegi authenticated user
AlfrescoClientInfo alfClient = (AlfrescoClientInfo) client;
- getAuthenticationComponent().setCurrentUser( mapUserNameToPerson( client.getUserName()));
+ getAuthenticationComponent().setCurrentUser( normalized);
alfClient.setAuthenticationTicket(getAuthenticationService().getCurrentTicket());
// Store the full user name in the client information, indicate that this is not a guest logon
@@ -2110,16 +2254,20 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
if (ex instanceof AuthenticationException)
{
- logger.debug(ex);
+ // Return a logon failure
+ logger.debug(ex.getMessage(), ex);
+
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
else
{
- logger.error(ex);
+ logger.error(ex.getMessage(), ex);
+
+ // Return a logon failure
+
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
- // Return a logon failure
-
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
}
}
else
@@ -2130,7 +2278,7 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Return a logon failure
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
}
else
@@ -2141,7 +2289,7 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Return a logon failure
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
}
@@ -2186,7 +2334,8 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Get the stored MD4 hashed password for the user, or null if the user does not exist
- String md4hash = getNTLMAuthenticator().getMD4HashedPassword(userName);
+ String normalized = normalizeUserId(userName);
+ String md4hash = getNTLMAuthenticator().getMD4HashedPassword(normalized);
if ( md4hash != null)
{
@@ -2217,8 +2366,6 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
}
catch ( NoSuchAlgorithmException ex)
{
- // Log the error
-
logger.error( ex);
}
@@ -2256,9 +2403,8 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
if ( i != clientHash.length)
{
- // Return a logon failure
-
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ logger.debug("bad client hash");
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrSrv, SMBStatus.SRVBadPassword);
}
}
@@ -2267,12 +2413,14 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
try
{
AlfrescoClientInfo alfClient = (AlfrescoClientInfo) client;
- getAuthenticationComponent().setCurrentUser( mapUserNameToPerson( userName));
+ getAuthenticationComponent().setCurrentUser( normalized);
alfClient.setAuthenticationTicket(getAuthenticationService().getCurrentTicket());
}
catch (AuthenticationException e)
{
// Invalid user or max tickets exceeded. Return a logon failure status
+
+ logger.debug("Authentication exception", e);
throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
@@ -2294,7 +2442,7 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Return a logon failure
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
}
else
@@ -2305,7 +2453,7 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Return a logon failure
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
}
@@ -2323,7 +2471,7 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
if (reqPkt.checkPacketIsValid(13, 0) == false)
{
- throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
+ throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.ErrSrv, SMBStatus.SRVNonSpecificError);
}
// Extract the session details
@@ -2354,7 +2502,8 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
if (user == null)
{
- throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
+ logger.error("User not specified");
+ throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.ErrSrv, SMBStatus.SRVNonSpecificError);
}
// Extract the clients primary domain name string
@@ -2370,7 +2519,8 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
if (domain == null)
{
- throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
+ logger.error("Domain not specified");
+ throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.ErrSrv, SMBStatus.SRVNonSpecificError);
}
}
@@ -2387,13 +2537,14 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
if (clientOS == null)
{
- throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
+ logger.error("client OS not specified");
+ throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.ErrSrv, SMBStatus.SRVNonSpecificError);
}
}
// DEBUG
- if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_NEGOTIATE))
+ if (logger.isDebugEnabled())
{
logger.debug("NT Session setup from user=" + user + ", password="
+ (uniPwd != null ? HexDump.hexString(uniPwd) : "none") + ", ANSIpwd="
@@ -2441,7 +2592,9 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Debug
if ( logger.isDebugEnabled())
+ {
logger.debug("Logged on using Hashed/NTLMv1");
+ }
}
else if ( uniPwd.length > 0)
{
@@ -2452,7 +2605,9 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// Debug
if ( logger.isDebugEnabled())
+ {
logger.debug("Logged on using Hashed/NTLMv2");
+ }
}
}
@@ -2467,7 +2622,7 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
// DEBUG
- if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_NEGOTIATE))
+ if (logger.isDebugEnabled())
logger.debug("User " + user + ", logged on as guest");
}
@@ -2478,20 +2633,13 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticatorBase implement
if ( uid == VirtualCircuit.InvalidUID)
{
-
- // DEBUG
+ logger.error("Failed to allocate UID for virtual circuit, " + vc);
- if ( logger.isDebugEnabled() && sess.hasDebug( SMBSrvSession.DBG_NEGOTIATE))
- logger.debug("Failed to allocate UID for virtual circuit, " + vc);
-
- // Failed to allocate a UID
-
- throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ // Failed to allocate a UID
+ throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
- else if ( logger.isDebugEnabled() && sess.hasDebug( SMBSrvSession.DBG_NEGOTIATE))
- {
- // DEBUG
-
+ else if ( logger.isDebugEnabled())
+ {
logger.debug("Allocated UID=" + uid + " for VC=" + vc);
}
diff --git a/source/java/org/alfresco/filesys/auth/cifs/PassthruCifsAuthenticator.java b/source/java/org/alfresco/filesys/auth/cifs/PassthruCifsAuthenticator.java
index 84939fc8a4..d0d50c82e8 100644
--- a/source/java/org/alfresco/filesys/auth/cifs/PassthruCifsAuthenticator.java
+++ b/source/java/org/alfresco/filesys/auth/cifs/PassthruCifsAuthenticator.java
@@ -77,7 +77,7 @@ public class PassthruCifsAuthenticator extends CifsAuthenticatorBase implements
{
// Debug logging
- private static final Log logger = LogFactory.getLog("org.alfresco.smb.protocol.auth");
+ private static final Log logger = LogFactory.getLog(PassthruCifsAuthenticator.class);
// Constants
@@ -465,6 +465,58 @@ public class PassthruCifsAuthenticator extends CifsAuthenticatorBase implements
respPkt.setByteCount(pos - respPkt.getByteOffset());
}
+ /**
+ * Process the CIFS session setup request packet and build the session setup response.
+ *
+ * This is the boundary between alfresco and JLAN. So is responsible for logging and
+ * ensuring that the exceptions are correct for JLAN.
+ *
+ * @param sess SMBSrvSession
+ * @param reqPkt SMBSrvPacket
+ * @exception SMBSrvException
+ */
+ public void processSessionSetup(final SMBSrvSession sess, final SMBSrvPacket reqPkt)
+ throws SMBSrvException
+ {
+ try
+ {
+ processAlfrescoSessionSetup(sess, reqPkt);
+ }
+ catch (SMBSrvException e)
+ {
+ /*
+ * A JLAN SMBSrvException is not human readable so we need to log the
+ * error before throwing, rather than logging the error here.
+ */
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Returning SMBSrvException to JLAN", e);
+ }
+ throw e;
+ }
+ catch (AlfrescoRuntimeException a)
+ {
+ Throwable c = a.getCause();
+
+ if(c != null)
+ {
+ if(a.getCause() instanceof SMBSrvException)
+ {
+ logger.error(c.getMessage(), c);
+ throw (SMBSrvException)c;
+ }
+ }
+ logger.error(a.getMessage(), a);
+ throw new SMBSrvException( SMBStatus.NTAccessDenied, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
+
+ }
+ catch (Throwable t)
+ {
+ logger.error(t.getMessage(), t);
+ throw new SMBSrvException( SMBStatus.NTAccessDenied, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
+ }
+ }
+
/**
* Process the CIFS session setup request packet and build the session setup response
*
@@ -472,14 +524,20 @@ public class PassthruCifsAuthenticator extends CifsAuthenticatorBase implements
* @param reqPkt SMBSrvPacket
* @exception SMBSrvException
*/
- public void processSessionSetup(SMBSrvSession sess, SMBSrvPacket reqPkt)
+ public void processAlfrescoSessionSetup(SMBSrvSession sess, SMBSrvPacket reqPkt)
throws SMBSrvException
{
// Check that the received packet looks like a valid NT session setup andX request
if (reqPkt.checkPacketIsValid(12, 0) == false)
- throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
-
+ {
+ if(logger.isErrorEnabled())
+ {
+ logger.error("Invalid packet received, return SMBStatus.NTInvalidParameter");
+ }
+ throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.ErrSrv, SMBStatus.SRVNonSpecificError);
+ }
+
// Check if the request is using security blobs or the older hashed password format
if ( reqPkt.getParameterCount() == 13)
@@ -518,28 +576,42 @@ public class PassthruCifsAuthenticator extends CifsAuthenticatorBase implements
String domain = "";
- if (reqPkt.hasMoreData()) {
+ if (reqPkt.hasMoreData())
+ {
// Extract the callers domain name
domain = reqPkt.unpackString(isUni);
if (domain == null)
- throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
+ {
+ if(logger.isErrorEnabled())
+ {
+ logger.error("Invalid packet received, domain is null");
+ }
+ throw new SMBSrvException(SMBStatus.NTInvalidParameter, SMBStatus.ErrSrv, SMBStatus.SRVNonSpecificError);
+ }
}
// Extract the clients native operating system
String clientOS = "";
- if (reqPkt.hasMoreData()) {
+ if (reqPkt.hasMoreData())
+ {
// Extract the callers operating system name
clientOS = reqPkt.unpackString(isUni);
if (clientOS == null)
- throw new SMBSrvException( SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
+ {
+ if(logger.isErrorEnabled())
+ {
+ logger.error("Invalid packet received, client OS is null");
+ }
+ throw new SMBSrvException( SMBStatus.NTInvalidParameter, SMBStatus.ErrSrv, SMBStatus.SRVNonSpecificError);
+ }
}
// Store the client maximum buffer size, maximum multiplexed requests count and client capability flags
@@ -559,8 +631,9 @@ public class PassthruCifsAuthenticator extends CifsAuthenticatorBase implements
// Set the remote address, if available
if ( sess.hasRemoteAddress())
- client.setClientAddress(sess.getRemoteAddress().getHostAddress());
-
+ {
+ client.setClientAddress(sess.getRemoteAddress().getHostAddress());
+ }
// Set the process id for this client, for multi-stage logons
client.setProcessId( reqPkt.getProcessId());
@@ -598,8 +671,9 @@ public class PassthruCifsAuthenticator extends CifsAuthenticatorBase implements
// DEBUG
if (logger.isDebugEnabled())
- logger.debug("NT Session setup NTLMSSP, MID=" + reqPkt.getMultiplexId() + ", UID=" + reqPkt.getUserId() + ", PID=" + reqPkt.getProcessId());
-
+ {
+ logger.debug("NT Session setup NTLMSSP, MID=" + reqPkt.getMultiplexId() + ", UID=" + reqPkt.getUserId() + ", PID=" + reqPkt.getProcessId());
+ }
// Process an NTLMSSP security blob
respBlob = doNtlmsspSessionSetup( sess, client, buf, secBlobPos, secBlobLen, isUni);
@@ -608,7 +682,12 @@ public class PassthruCifsAuthenticator extends CifsAuthenticatorBase implements
{
// Invalid blob type
- throw new SMBSrvException( SMBStatus.NTInvalidParameter, SMBStatus.SRVNonSpecificError, SMBStatus.ErrSrv);
+ if(logger.isErrorEnabled())
+ {
+ logger.error("Invalid packet received for Passthru Cifs Autenticator, not of type NTLMSSP");
+ }
+
+ throw new SMBSrvException( SMBStatus.NTInvalidParameter, SMBStatus.ErrSrv, SMBStatus.SRVNonSpecificError);
}
}
catch (SMBSrvException ex)
@@ -624,9 +703,11 @@ public class PassthruCifsAuthenticator extends CifsAuthenticatorBase implements
// Debug
- if ( logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_NEGOTIATE))
+ if ( logger.isDebugEnabled())
+ {
logger.debug("User " + client.getUserName() + " logged on " + (client != null ? " (type " + client.getLogonTypeString() + ")" : ""));
-
+ }
+
// Update the client information if not already set
if ( sess.getClientInformation() == null ||
@@ -719,17 +800,14 @@ public class PassthruCifsAuthenticator extends CifsAuthenticatorBase implements
uid = sess.addVirtualCircuit( vc);
if ( uid == VirtualCircuit.InvalidUID)
- {
- // DEBUG
-
- if ( logger.isDebugEnabled() && sess.hasDebug( SMBSrvSession.DBG_NEGOTIATE))
- logger.debug("Failed to allocate UID for virtual circuit, " + vc);
+ {
+ logger.error("Failed to allocate UID for virtual circuit, " + vc);
// Failed to allocate a UID
-
- throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException(SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
- else if ( logger.isDebugEnabled() && sess.hasDebug( SMBSrvSession.DBG_NEGOTIATE)) {
+ else if ( logger.isDebugEnabled())
+ {
// DEBUG
@@ -800,12 +878,9 @@ public class PassthruCifsAuthenticator extends CifsAuthenticatorBase implements
if ( msgType == -1)
{
- // DEBUG
-
- if ( logger.isDebugEnabled())
+ if ( logger.isErrorEnabled())
{
- logger.debug("Invalid NTLMSSP token received");
- logger.debug(" Token=" + HexDump.hexString( secbuf, secpos, seclen, " "));
+ logger.error("Invalid NTLMSSP token received, Token=" + HexDump.hexString( secbuf, secpos, seclen, " ") );
}
// Return a logon failure status
@@ -875,22 +950,20 @@ public class PassthruCifsAuthenticator extends CifsAuthenticatorBase implements
sess.removeSetupObject( client.getProcessId());
// Return a logon failure
-
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ logger.error("NTLMSSP Logon failure - type 2 message not found");
+
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
// Determine if the client sent us NTLMv1 or NTLMv2
if ( type3Msg.hasFlag( NTLM.Flag128Bit) && type3Msg.hasFlag( NTLM.FlagNTLM2Key))
{
- // Debug
-
- if ( logger.isDebugEnabled())
- logger.debug("Received NTLMSSP/NTLMv2, not supported");
+ logger.error("Received NTLMSSP/NTLMv2, not supported");
// Return a logon failure
- throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.DOSAccessDenied, SMBStatus.ErrDos);
+ throw new SMBSrvException( SMBStatus.NTLogonFailure, SMBStatus.ErrDos, SMBStatus.DOSAccessDenied);
}
else
{
@@ -1042,10 +1115,12 @@ public class PassthruCifsAuthenticator extends CifsAuthenticatorBase implements
}
catch (Exception ex)
{
+ if(ex instanceof SMBSrvException)
+ {
+ throw (SMBSrvException)ex;
+ }
- // Debug
-
- logger.error(ex.getMessage());
+ logger.error("unable to log on "+ ex.getMessage(), ex);
// Indicate logon failure
@@ -1067,27 +1142,21 @@ public class PassthruCifsAuthenticator extends CifsAuthenticatorBase implements
AuthenticateSession authSess = passDetails.getAuthenticateSession();
authSess.CloseSession();
- // DEBUG
-
if (logger.isDebugEnabled())
+ {
logger.debug("Closed auth session, sessId=" + authSess.getSessionId());
+ }
}
catch (Exception ex)
{
- // Debug
-
logger.error("Passthru error closing session (auth user)", ex);
}
}
}
else
{
-
- // DEBUG
-
- if (logger.isDebugEnabled())
- logger.debug(" No PassthruDetails for " + sess.getUniqueId() + ", check server list/domain mappings");
+ logger.error(" No PassthruDetails for " + sess.getUniqueId() + ", check server list/domain mappings");
// Indicate logon failure
diff --git a/source/java/org/alfresco/filesys/auth/cifs/package-info.java b/source/java/org/alfresco/filesys/auth/cifs/package-info.java
index b5e6184554..725016bc23 100644
--- a/source/java/org/alfresco/filesys/auth/cifs/package-info.java
+++ b/source/java/org/alfresco/filesys/auth/cifs/package-info.java
@@ -1,3 +1,29 @@
+/*
+ * Copyright (C) 2005-2012 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
/**
+ * Provides authentication implementations for CIFS
+ *
+ * AlfrescoCifsAuthenticator
+ * EnterpriseCifsAuthenticator deals with Kerberos, NTLMv1 and NTLMv2
+ * PassthruCifsAuthenticator deals with authenticating against an external system
+ *
+ *
+ * CifsAuthenticatorBase abstract base class.
*/
package org.alfresco.filesys.auth.cifs;
diff --git a/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java b/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java
index f3f7923d1a..6011e7f23c 100644
--- a/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java
+++ b/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java
@@ -2027,7 +2027,7 @@ public class AVMDiskDriver extends AlfrescoTxDiskDriver implements DiskInterface
{
// Check if the file is a directory, or only has read access
- if (file.getGrantedAccess() == NetworkFile.READONLY)
+ if (file.getGrantedAccess() <= NetworkFile.READONLY)
throw new AccessDeniedException();
// If the content channel is not open for the file then start a transaction
@@ -2081,7 +2081,7 @@ public class AVMDiskDriver extends AlfrescoTxDiskDriver implements DiskInterface
{
// Check if the file is a directory, or only has read access
- if (file.isDirectory() || file.getGrantedAccess() == NetworkFile.READONLY)
+ if (file.isDirectory() || file.getGrantedAccess() <= NetworkFile.READONLY)
throw new AccessDeniedException();
// If the content channel is not open for the file, or the channel is not writable, then start a transaction
diff --git a/source/java/org/alfresco/filesys/avm/AVMNetworkFile.java b/source/java/org/alfresco/filesys/avm/AVMNetworkFile.java
index 1f57488937..800922534d 100644
--- a/source/java/org/alfresco/filesys/avm/AVMNetworkFile.java
+++ b/source/java/org/alfresco/filesys/avm/AVMNetworkFile.java
@@ -462,7 +462,7 @@ public class AVMNetworkFile extends AlfrescoNetworkFile {
// We need to create the channel
- if (write && getGrantedAccess() == NetworkFile.READONLY)
+ if (write && getGrantedAccess() <= NetworkFile.READONLY)
throw new AccessDeniedException("The network file was created for read-only: " + this);
// Access the content data and get a file channel to the data
diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java
index d98c2d67d0..cb3eb2a2e8 100644
--- a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java
+++ b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java
@@ -1027,8 +1027,8 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter
// Copy cached timestamps
- if ( fstate.hasAccessDateTime())
- finfo.setAccessDateTime(fstate.getAccessDateTime());
+// if ( fstate.hasAccessDateTime())
+// finfo.setAccessDateTime(fstate.getAccessDateTime());
if ( fstate.hasChangeDateTime())
finfo.setChangeDateTime(fstate.getChangeDateTime());
if ( fstate.hasModifyDateTime())
@@ -1731,7 +1731,7 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter
throw new AccessDeniedException("Invalid access mode");
}
- if ( fstate.getOpenCount() > 0) {
+ if ( fstate.getOpenCount() > 0 && params.isAttributesOnlyAccess() == false) {
// Check for impersonation security level from the original process that opened the file
@@ -1819,7 +1819,7 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter
{
// Check if the file is already opened by this client/process
- if ( tree.openFileCount() > 0) {
+ if ( tree.openFileCount() > 0 && params.isAttributesOnlyAccess() == false) {
// Search the open file table for this session/virtual circuit
@@ -1856,7 +1856,7 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter
if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
logger.debug("Re-use existing file open Path " + params.getPath() + ", PID=" + params.getProcessId() + ", params=" +
( params.isReadOnlyAccess() ? "ReadOnly" : "Write") + ", file=" +
- ( contentFile.getGrantedAccess() == NetworkFile.READONLY ? "ReadOnly" : "Write"));
+ ( contentFile.getGrantedAccess() <= NetworkFile.READONLY ? "ReadOnly" : "Write"));
}
else if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
logger.debug("Not re-using file path=" + params.getPath() + ", readWrite=" + (params.isReadWriteAccess() ? "true" : "false") +
@@ -1877,7 +1877,7 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter
// Create a new network file for the open request
- netFile = ContentNetworkFile.createFile(nodeService, contentService, mimetypeService, cifsHelper, nodeRef, params.getPath(), params.isReadOnlyAccess(), sess);
+ netFile = ContentNetworkFile.createFile(nodeService, contentService, mimetypeService, cifsHelper, nodeRef, params.getPath(), params.isReadOnlyAccess(), params.isAttributesOnlyAccess(), sess);
}
}
else
@@ -1961,7 +1961,8 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter
// Update the file state, cache the node
- fstate.incrementOpenCount();
+ if ( netFile.getGrantedAccess() > NetworkFile.ATTRIBUTESONLY)
+ fstate.incrementOpenCount();
fstate.setFilesystemObject(nodeRef);
// Store the state with the file
@@ -1970,8 +1971,8 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter
// Set the file access date/time, if available
- if ( fstate.hasAccessDateTime())
- netFile.setAccessDate( fstate.getAccessDateTime());
+// if ( fstate.hasAccessDateTime())
+// netFile.setAccessDate( fstate.getAccessDateTime());
}
// Debug
@@ -2090,7 +2091,7 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter
// Create the network file
- ContentNetworkFile netFile = ContentNetworkFile.createFile(nodeService, contentService, mimetypeService, cifsHelper, result.getSecond(), params.getPath(), params.isReadOnlyAccess(), sess);
+ ContentNetworkFile netFile = ContentNetworkFile.createFile(nodeService, contentService, mimetypeService, cifsHelper, result.getSecond(), params.getPath(), params.isReadOnlyAccess(), params.isAttributesOnlyAccess(), sess);
// Always allow write access to a newly created file
@@ -2480,7 +2481,7 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter
// If the file open count is now zero then reset the stored sharing mode
- if ( fstate.decrementOpenCount() == 0)
+ if ( file.getGrantedAccess() > NetworkFile.ATTRIBUTESONLY && fstate.decrementOpenCount() == 0)
fstate.setSharedAccess( SharingMode.READWRITE + SharingMode.DELETE);
// Check if there is a cached modification timestamp to be written out
@@ -3433,11 +3434,9 @@ public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInter
}
catch (AlfrescoRuntimeException ex)
{
- // Debug
-
- if (logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_RENAME))
- logger.debug("Rename file", ex);
-
+ // Unexpected Exception being consumed here - hence the error logging.
+ logger.error("Unable to rename file" + oldName, ex);
+
// Convert to a general I/O exception
throw new AccessDeniedException("Rename file " + oldName);
diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskDriver2.java b/source/java/org/alfresco/filesys/repo/ContentDiskDriver2.java
index 41b86929ec..52343746da 100644
--- a/source/java/org/alfresco/filesys/repo/ContentDiskDriver2.java
+++ b/source/java/org/alfresco/filesys/repo/ContentDiskDriver2.java
@@ -2100,6 +2100,11 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD
diskDev.setTotalUnits( totalSpace / DiskSizeInterfaceConsts.DiskAllocationUnit);
diskDev.setFreeUnits( freeSpace / DiskSizeInterfaceConsts.DiskAllocationUnit);
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("getDiskInformation returning diskDev:" + diskDev);
+ }
}
public void setCifsHelper(CifsHelper cifsHelper)
@@ -2145,16 +2150,6 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD
logger.debug("processIOControl ctrlCode: 0x" + Integer.toHexString(ctrlCode) + ", fid:" + fid);
}
- NetworkFile netFile = tree.findFile(fid);
- if ( netFile == null || netFile.isDirectory() == false)
- {
- if(logger.isDebugEnabled())
- {
- logger.debug("net file is null or not a directory");
- }
- throw new SMBException(SMBStatus.NTErr, SMBStatus.NTInvalidParameter);
- }
-
final ContentContext ctx = (ContentContext) tree.getContext();
try
{
@@ -2170,7 +2165,14 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD
}
throw smbException;
}
-
+ catch(IOControlNotImplementedException ioException)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("IO Control Not Implemented Exception fid:" + fid, ioException);
+ }
+ throw ioException;
+ }
}
@@ -2446,7 +2448,7 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD
case READ_ONLY:
logger.debug("open file for read only");
- netFile = ContentNetworkFile.createFile(nodeService, contentService, mimetypeService, getCifsHelper(), nodeRef, path, true, session);
+ netFile = ContentNetworkFile.createFile(nodeService, contentService, mimetypeService, getCifsHelper(), nodeRef, path, true, false, session);
netFile.setGrantedAccess( NetworkFile.READONLY);
break;
@@ -2484,14 +2486,14 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD
case ATTRIBUTES_ONLY:
logger.debug("open file for attributes only");
- netFile = ContentNetworkFile.createFile(nodeService, contentService, mimetypeService, getCifsHelper(), nodeRef, path, true, session);
+ netFile = ContentNetworkFile.createFile(nodeService, contentService, mimetypeService, getCifsHelper(), nodeRef, path, true, true, session);
netFile.setGrantedAccess( NetworkFile.READONLY);
break;
case DELETE:
//TODO Not sure about this one.
logger.debug("open file for delete");
- netFile = ContentNetworkFile.createFile(nodeService, contentService, mimetypeService, getCifsHelper(), nodeRef, path,true , session);
+ netFile = ContentNetworkFile.createFile(nodeService, contentService, mimetypeService, getCifsHelper(), nodeRef, path, true, false, session);
netFile.setGrantedAccess( NetworkFile.READONLY);
break;
@@ -2551,7 +2553,7 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD
urlStr.append( srvName);
urlStr.append("/");
urlStr.append( tree.getSharedDevice().getName());
- urlStr.append( pathl);
+ urlStr.append( path);
urlStr.append("\r\n");
// Create the in memory pseudo file for the URL link
diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskDriverTest.java b/source/java/org/alfresco/filesys/repo/ContentDiskDriverTest.java
index e820644f99..dffcaf542d 100644
--- a/source/java/org/alfresco/filesys/repo/ContentDiskDriverTest.java
+++ b/source/java/org/alfresco/filesys/repo/ContentDiskDriverTest.java
@@ -2467,6 +2467,252 @@ public class ContentDiskDriverTest extends TestCase
} // testScenarioShuffleMetadataExtraction
+
+ /**
+ * ALF-12812
+ *
+ * This test tries to simulate the shuffling that is done by MS Word 2011 for Mac
+ * with regard to metadata extraction. In particular the temporary file names are
+ * different.
+ *
+ * 1: Setup an update rule for ContentMetadataExtractor.
+ * Simulate a WORD 2011 for Mac Create
+ * 2: Write "Word Work File D_1725484373.tmp"
+ * 3: Close file
+ * 4: Rename "Word Work File D_1725484373.tmp" to ContentDiskDriver.docx
+ * 5: Check metadata extraction
+ */
+ public void testMetadataExtractionForMac() throws Exception
+ {
+ logger.debug("testMetadataExtractionForMac");
+ final String FILE_NAME = "ContentDiskDriver.docx";
+ //final String FILE_OLD_TEMP = "._Word Work File D_1725484373.tmp";
+ final String FILE_NEW_TEMP = "Word Work File D_1725484373.tmp";
+
+ class TestContext
+ {
+ NodeRef testDirNodeRef;
+ NodeRef testNodeRef;
+ NetworkFile firstFileHandle;
+// NetworkFile secondFileHandle;
+ };
+
+ final TestContext testContext = new TestContext();
+
+ final String TEST_DIR = TEST_ROOT_DOS_PATH + "\\testMetadataExtractionForMac";
+
+ ServerConfiguration scfg = new ServerConfiguration("testServer");
+ TestServer testServer = new TestServer("testServer", scfg);
+ final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
+ DiskSharedDevice share = getDiskSharedDevice();
+ final TreeConnection testConnection = testServer.getTreeConnection(share);
+ final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
+
+ /**
+ * Clean up just in case garbage is left from a previous run
+ */
+ RetryingTransactionCallback deleteGarbageDirCB = new RetryingTransactionCallback() {
+
+ @Override
+ public Void execute() throws Throwable
+ {
+ driver.deleteDirectory(testSession, testConnection, TEST_DIR);
+ return null;
+ }
+ };
+
+ try
+ {
+ tran.doInTransaction(deleteGarbageDirCB);
+ }
+ catch (Exception e)
+ {
+ // expect to go here
+ }
+
+ logger.debug("create Test directory" + TEST_DIR);
+ RetryingTransactionCallback createTestDirCB = new RetryingTransactionCallback() {
+
+ @Override
+ public Void execute() throws Throwable
+ {
+ /**
+ * Create the test directory we are going to use
+ */
+ FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
+ FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
+ driver.createDirectory(testSession, testConnection, createRootDirParams);
+ driver.createDirectory(testSession, testConnection, createDirParams);
+
+ testContext.testDirNodeRef = getNodeForPath(testConnection, TEST_DIR);
+ assertNotNull("testDirNodeRef is null", testContext.testDirNodeRef);
+
+ UserTransaction txn = transactionService.getUserTransaction();
+
+ return null;
+
+
+ }
+ };
+ tran.doInTransaction(createTestDirCB);
+ logger.debug("Create rule on test dir");
+
+ RetryingTransactionCallback createRuleCB = new RetryingTransactionCallback() {
+
+ @Override
+ public Void execute() throws Throwable
+ {
+ Rule rule = new Rule();
+ rule.setRuleType(RuleType.UPDATE);
+ rule.applyToChildren(true);
+ rule.setRuleDisabled(false);
+ rule.setTitle("Extract Metadata from update content");
+ rule.setDescription("ContentDiskDriverTest");
+
+ Map props = new HashMap(1);
+ Action extractAction = actionService.createAction("extract-metadata", props);
+
+ ActionCondition noCondition1 = actionService.createActionCondition(NoConditionEvaluator.NAME);
+ extractAction.addActionCondition(noCondition1);
+
+ ActionCondition noCondition2 = actionService.createActionCondition(NoConditionEvaluator.NAME);
+ CompositeAction compAction = actionService.createCompositeAction();
+ compAction.setTitle("Extract Metadata");
+ compAction.setDescription("Content Disk Driver Test - Extract Metadata");
+ compAction.addAction(extractAction);
+ compAction.addActionCondition(noCondition2);
+
+ rule.setAction(compAction);
+
+ ruleService.saveRule(testContext.testDirNodeRef, rule);
+
+ logger.debug("rule created");
+
+ return null;
+ }
+ };
+ tran.doInTransaction(createRuleCB, false, true);
+
+ /**
+ * Create a file in the test directory
+ */
+ logger.debug("create test file in test directory");
+ RetryingTransactionCallback createFileCB = new RetryingTransactionCallback() {
+
+ @Override
+ public Void execute() throws Throwable
+ {
+ /**
+ * Create the file we are going to use to test
+ */
+ FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NEW_TEMP, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
+ testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams);
+ assertNotNull("first file Handle is null", testContext.firstFileHandle);
+
+ // now load up the node with lots of other stuff that we will test to see if it gets preserved during the
+ // shuffle.
+ testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP);
+ assertNotNull("testContext.testNodeRef is null", testContext.testNodeRef);
+
+ // test non CM namespace property
+ nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true);
+
+ // Check that the temporary aspect has been applied.
+ assertTrue("temporary aspect not applied", nodeService.hasAspect(testContext.testNodeRef, ContentModel.ASPECT_TEMPORARY));
+ return null;
+ }
+ };
+ tran.doInTransaction(createFileCB, false, true);
+
+ logger.debug("step b: write content to test file");
+
+ /**
+ * Write ContentDiskDriverTest1.docx to the test file,
+ */
+ RetryingTransactionCallback writeFileCB = new RetryingTransactionCallback() {
+
+ @Override
+ public Void execute() throws Throwable
+ {
+ ClassPathResource fileResource = new ClassPathResource("filesys/ContentDiskDriverTest1.docx");
+ assertNotNull("unable to find test resource filesys/ContentDiskDriverTest1.docx", fileResource);
+
+ byte[] buffer= new byte[1000];
+ InputStream is = fileResource.getInputStream();
+ try
+ {
+ long offset = 0;
+ int i = is.read(buffer, 0, buffer.length);
+ while(i > 0)
+ {
+ testContext.firstFileHandle.writeFile(buffer, i, 0, offset);
+ offset += i;
+ i = is.read(buffer, 0, buffer.length);
+ }
+ }
+ finally
+ {
+ is.close();
+ }
+
+ driver.closeFile(testSession, testConnection, testContext.firstFileHandle);
+
+ return null;
+ }
+ };
+ tran.doInTransaction(writeFileCB, false, true);
+
+ logger.debug("Step b: rename the test file.");
+
+ /**
+ * Move the new file into place, stuff should get shuffled
+ */
+ RetryingTransactionCallback moveNewFileCB = new RetryingTransactionCallback() {
+
+ @Override
+ public Void execute() throws Throwable
+ {
+ driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NEW_TEMP, TEST_DIR + "\\" + FILE_NAME);
+ return null;
+ }
+ };
+
+ tran.doInTransaction(moveNewFileCB, false, true);
+
+ logger.debug("Step c: validate metadata has been extracted.");
+ /**
+ * c: check simple case of meta-data extraction has worked.
+ */
+ RetryingTransactionCallback validateFirstExtractionCB = new RetryingTransactionCallback() {
+
+ @Override
+ public Void execute() throws Throwable
+ {
+ Map props = nodeService.getProperties(testContext.testNodeRef);
+
+ assertTrue("Enabled property has been lost", props.containsKey(TransferModel.PROP_ENABLED));
+
+ // Check that the temporary aspect has been applied.
+ assertTrue("temporary aspect has not been removed", !nodeService.hasAspect(testContext.testNodeRef, ContentModel.ASPECT_TEMPORARY));
+
+
+ // These metadata values should be extracted.
+ assertEquals("description is not correct", "This is a test file", nodeService.getProperty(testContext.testNodeRef, ContentModel.PROP_DESCRIPTION));
+ assertEquals("title is not correct", "ContentDiskDriverTest", nodeService.getProperty(testContext.testNodeRef, ContentModel.PROP_TITLE));
+ assertEquals("author is not correct", "mrogers", nodeService.getProperty(testContext.testNodeRef, ContentModel.PROP_AUTHOR));
+
+ ContentData data = (ContentData)props.get(ContentModel.PROP_CONTENT);
+ assertEquals("mimeType is wrong", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", data.getMimetype());
+ assertEquals("size is wrong", 11302, data.getSize());
+
+ return null;
+ }
+ };
+ tran.doInTransaction(validateFirstExtractionCB, false, true);
+
+
+ } // testScenarioMetadataExtractionForMac
+
public void testDirListing()throws Exception
{
logger.debug("testDirListing");
@@ -4352,7 +4598,7 @@ public class ContentDiskDriverTest extends TestCase
* e) Lock file deleted
*
*/
- public void testScenarioMacLionTextEdit() throws Exception
+ public void DISABLED_TestScenarioMacLionTextEdit() throws Exception
{
logger.debug("testScenarioLionTextEdit");
final String FILE_NAME = "test.txt";
diff --git a/source/java/org/alfresco/filesys/repo/ContentIOControlHandler.java b/source/java/org/alfresco/filesys/repo/ContentIOControlHandler.java
index 29df048bac..6c80f5d8b7 100644
--- a/source/java/org/alfresco/filesys/repo/ContentIOControlHandler.java
+++ b/source/java/org/alfresco/filesys/repo/ContentIOControlHandler.java
@@ -116,7 +116,8 @@ public class ContentIOControlHandler implements IOControlHandler
* @param dataBuf I/O control specific input data
* @param isFSCtrl true if this is a filesystem control, or false for a device control
* @param filter if bit0 is set indicates that the control applies to the share root handle
- * @return DataBuffer
+ * @return DataBuffer or null if there is no response buffer.
+ *
* @exception IOControlNotImplementedException
* @exception SMBException
*/
@@ -125,10 +126,12 @@ public class ContentIOControlHandler implements IOControlHandler
throws IOControlNotImplementedException, SMBException
{
// Validate the file id
-
NetworkFile netFile = tree.findFile(fid);
- if ( netFile == null || netFile.isDirectory() == false)
+ if ( netFile == null )
+ {
+ logger.debug("IO Control Handler called with missing file");
throw new SMBException(SMBStatus.NTErr, SMBStatus.NTInvalidParameter);
+ }
// Split the control code
@@ -144,23 +147,36 @@ public class ContentIOControlHandler implements IOControlHandler
// Create or get object id
if ( ioFunc == NTIOCtl.FsCtlCreateOrGetObjectId)
- return null;
+ {
+ logger.debug("Create or Get Object Id - return null");
+ return null;
+//
+// logger.debug("Create or Get Object Id - throw not implemented exception");
+// throw new IOControlNotImplementedException("Create or Get Object Id not implemented");
+// //return null;
+ }
}
// Check if the I/O control looks like a custom I/O control request
if ( devType != NTIOCtl.DeviceFileSystem || dataBuf == null)
- throw new IOControlNotImplementedException();
+ {
+ throw new IOControlNotImplementedException("Custom IO control request not implemented");
+ }
// Check if the request has a valid signature for an Alfresco CIFS server I/O control
if ( dataBuf.getLength() < IOControl.Signature.length())
+ {
throw new IOControlNotImplementedException("Bad request length");
+ }
String sig = dataBuf.getFixedString(IOControl.Signature.length(), false);
if ( sig == null || sig.compareTo(IOControl.Signature) != 0)
+ {
throw new IOControlNotImplementedException("Bad request signature");
+ }
// Get the node for the parent folder, make sure it is a folder
@@ -171,7 +187,9 @@ public class ContentIOControlHandler implements IOControlHandler
folderNode = getNodeForPath(tree, netFile.getFullName());
if ( getCifsHelper().isDirectory( folderNode) == false)
+ {
folderNode = null;
+ }
}
catch ( FileNotFoundException ex)
{
@@ -181,14 +199,17 @@ public class ContentIOControlHandler implements IOControlHandler
// If the folder node is not valid return an error
if ( folderNode == null)
+ {
+ logger.debug("unable to get parent folder - return access denied");
throw new SMBException(SMBStatus.NTErr, SMBStatus.NTAccessDenied);
+ }
// Debug
if ( logger.isDebugEnabled())
{
- logger.debug("IO control func=0x" + Integer.toHexString(ioFunc) + ", fid=" + fid + ", buffer=" + dataBuf);
- logger.debug(" Folder nodeRef=" + folderNode);
+ logger.debug("IO control func=0x" + Integer.toHexString(ioFunc) + ", fid=" + fid + ", buffer=" + dataBuf +
+ " Folder nodeRef=" + folderNode);
}
// Check if the I/O control code is one of our custom codes
@@ -217,8 +238,11 @@ public class ContentIOControlHandler implements IOControlHandler
// Get file information for a file within the current folder
case IOControl.CmdFileStatus:
-
-
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("CmdFileStatus");
+ }
// Process the file status request
retBuffer = procIOFileStatus( sess, tree, dataBuf, folderNode, contentDriver, contentContext);
@@ -229,6 +253,10 @@ public class ContentIOControlHandler implements IOControlHandler
case IOControl.CmdGetActionInfo:
// Process the get action information request
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("GetActionInfo");
+ }
retBuffer = procGetActionInfo(sess, tree, dataBuf, folderNode, netFile, contentDriver, contentContext);
break;
@@ -238,6 +266,10 @@ public class ContentIOControlHandler implements IOControlHandler
case IOControl.CmdRunAction:
// Process the run action request
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("RunAction");
+ }
retBuffer = procRunAction(sess, tree, dataBuf, folderNode, netFile, contentDriver, contentContext);
break;
@@ -247,6 +279,10 @@ public class ContentIOControlHandler implements IOControlHandler
case IOControl.CmdGetAuthTicket:
// Process the get auth ticket request
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("GetAuthTicket");
+ }
retBuffer = procGetAuthTicket(sess, tree, dataBuf, folderNode, netFile, contentDriver, contentContext);
break;
diff --git a/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java b/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java
index 70ff97c51d..602c559700 100644
--- a/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java
+++ b/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java
@@ -88,7 +88,7 @@ public class ContentNetworkFile extends NodeRefNetworkFile
* Helper method to create a {@link NetworkFile network file} given a node reference.
*/
public static ContentNetworkFile createFile( NodeService nodeService, ContentService contentService, MimetypeService mimetypeService,
- CifsHelper cifsHelper, NodeRef nodeRef, String path, boolean readOnly, SrvSession sess)
+ CifsHelper cifsHelper, NodeRef nodeRef, String path, boolean readOnly, boolean attributesOnly, SrvSession sess)
{
// Create the file
@@ -116,12 +116,13 @@ public class ContentNetworkFile extends NodeRefNetworkFile
// Set relevant parameters
- if (readOnly)
- {
+ if (attributesOnly) {
+ netFile.setGrantedAccess( NetworkFile.ATTRIBUTESONLY);
+ }
+ else if (readOnly) {
netFile.setGrantedAccess(NetworkFile.READONLY);
}
- else
- {
+ else {
netFile.setGrantedAccess(NetworkFile.READWRITE);
}
@@ -153,11 +154,15 @@ public class ContentNetworkFile extends NodeRefNetworkFile
if ( fileInfo.hasCreationDateTime())
netFile.setCreationDate( fileInfo.getCreationDateTime());
- if ( fileInfo.hasModifyDateTime())
+ if ( fileInfo.hasModifyDateTime() && fileInfo.getModifyDateTime() > 0L)
netFile.setModifyDate(fileInfo.getModifyDateTime());
+ else
+ netFile.setModifyDate(fileInfo.getCreationDateTime());
- if ( fileInfo.hasAccessDateTime())
+ if ( fileInfo.hasAccessDateTime() && fileInfo.getAccessDateTime() > 0L)
netFile.setAccessDate(fileInfo.getAccessDateTime());
+ else
+ netFile.setAccessDate(fileInfo.getCreationDateTime());
// Set the file attributes
diff --git a/source/java/org/alfresco/filesys/repo/ContentSearchContext.java b/source/java/org/alfresco/filesys/repo/ContentSearchContext.java
index 8d12657ad1..595107dbc8 100644
--- a/source/java/org/alfresco/filesys/repo/ContentSearchContext.java
+++ b/source/java/org/alfresco/filesys/repo/ContentSearchContext.java
@@ -42,7 +42,8 @@ import org.apache.commons.logging.LogFactory;
*
* @author Derek Hulley
*/
-public class ContentSearchContext extends SearchContext
+public class ContentSearchContext extends SearchContext
+ implements InFlightCorrectable
{
// Debug logging
@@ -54,6 +55,13 @@ public class ContentSearchContext extends SearchContext
public final static int LinkFileSize = 512;
+ private InFlightCorrector corrector;
+
+ public void setInFlightCorrector(InFlightCorrector corrector)
+ {
+ this.corrector = corrector;
+ }
+
// List of nodes returned from the folder search
private CifsHelper cifsHelper;
@@ -163,7 +171,9 @@ public class ContentSearchContext extends SearchContext
// Check if there is anything else to return
if (!hasMoreFiles())
+ {
return false;
+ }
// Increment the index and resume id
@@ -231,17 +241,28 @@ public class ContentSearchContext extends SearchContext
try {
- // Get the file information and copy across to the callers file info
+ // Get the file information and copy across to the caller's file info
nextInfo = cifsHelper.getFileInformation(nextNodeRef, "", false, false);
info.copyFrom(nextInfo);
+
+ /**
+ * Apply in flight correction
+ */
+ if(corrector != null)
+ {
+ corrector.correct(info, m_relPath);
+ }
+
}
catch ( InvalidNodeRefException ex) {
// Log a warning
if ( logger.isWarnEnabled())
+ {
logger.warn("Noderef " + nextNodeRef + " no longer valid, ignoring");
+ }
// Update the node index, node no longer exists, try the next node in the search
@@ -253,7 +274,9 @@ public class ContentSearchContext extends SearchContext
// Check if we have finished returning file info
if ( nextInfo == null)
+ {
return false;
+ }
// Generate a file id for the current file
diff --git a/source/java/org/alfresco/filesys/repo/FilesystemTransactionAdvice.java b/source/java/org/alfresco/filesys/repo/FilesystemTransactionAdvice.java
index 2f36ae4111..ca934eab72 100644
--- a/source/java/org/alfresco/filesys/repo/FilesystemTransactionAdvice.java
+++ b/source/java/org/alfresco/filesys/repo/FilesystemTransactionAdvice.java
@@ -45,8 +45,6 @@ public class FilesystemTransactionAdvice implements MethodInterceptor
{
private boolean readOnly;
-// private AlfrescoDiskDriver driver;
-
private TransactionService transactionService;
public FilesystemTransactionAdvice()
@@ -92,64 +90,34 @@ public class FilesystemTransactionAdvice implements MethodInterceptor
}
};
- if(readOnly)
+ try
{
- // read only transaction
- try
- {
- return tran.doInTransaction(callback, true);
- }
- catch(PropagatingException pe)
- {
- Throwable t = pe.getCause();
- if(t != null)
- {
- if(t instanceof IOException)
- {
- throw (IOException) pe.getCause();
- }
- if(t instanceof SMBException)
- {
- throw pe.getCause();
- }
- if(t instanceof DeviceContextException)
- {
- throw pe.getCause();
- }
- throw t;
- }
- throw pe;
- }
+ return tran.doInTransaction(callback, readOnly);
}
- else
+ catch(PropagatingException pe)
{
- // read/write only transaction
- try
+ Throwable t = pe.getCause();
+ if(t != null)
{
- return tran.doInTransaction(callback);
- }
- catch(PropagatingException pe)
- {
- Throwable t = pe.getCause();
- if(t != null)
+ if(t instanceof IOException)
+ {
+ throw (IOException) t;
+ }
+ if(t instanceof IOControlNotImplementedException)
+ {
+ throw (IOControlNotImplementedException) t;
+ }
+ if(t instanceof SMBException)
+ {
+ throw (SMBException)t;
+ }
+ if(t instanceof DeviceContextException)
{
- if(t instanceof IOException)
- {
- // Unwrap checked exceptions
- throw pe.getCause();
- }
- if(t instanceof SMBException)
- {
- throw pe.getCause();
- }
- if(t instanceof DeviceContextException)
- {
- throw pe.getCause();
- }
throw t;
}
- throw pe;
- }
+ throw t;
+ }
+ throw pe;
}
}
diff --git a/source/java/org/alfresco/filesys/repo/InFlightCorrectable.java b/source/java/org/alfresco/filesys/repo/InFlightCorrectable.java
new file mode 100644
index 0000000000..999ecb0b6b
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/InFlightCorrectable.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2012 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+package org.alfresco.filesys.repo;
+
+/**
+ *
+ * @author mrogers
+ *
+ */
+public interface InFlightCorrectable
+{
+ public void setInFlightCorrector(InFlightCorrector correctable);
+}
diff --git a/source/java/org/alfresco/filesys/repo/InFlightCorrector.java b/source/java/org/alfresco/filesys/repo/InFlightCorrector.java
new file mode 100644
index 0000000000..5643ce384d
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/InFlightCorrector.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2012 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.filesys.repo;
+
+import java.util.Date;
+
+import org.alfresco.jlan.server.filesys.FileInfo;
+import org.alfresco.jlan.server.filesys.TreeConnection;
+import org.alfresco.jlan.server.filesys.cache.FileState;
+import org.alfresco.jlan.server.filesys.cache.FileStateCache;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * The in flight corrector corrects search results that have not yet been committed to the
+ * repository.
+ *
+ * It substitutes the "in flight" valuses from the state cache in place of the values committed to
+ * the repo
+ *
+ * @author mrogers
+ */
+public interface InFlightCorrector
+{
+ /**
+ * Correct thr results with in flight details.
+ * @param info
+ * @param folderPath
+ */
+ public void correct(FileInfo info, String folderPath);
+}
diff --git a/source/java/org/alfresco/filesys/repo/InFlightCorrectorImpl.java b/source/java/org/alfresco/filesys/repo/InFlightCorrectorImpl.java
new file mode 100644
index 0000000000..e309436b37
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/InFlightCorrectorImpl.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2012 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.filesys.repo;
+
+import java.util.Date;
+
+import org.alfresco.jlan.server.filesys.FileInfo;
+import org.alfresco.jlan.server.filesys.TreeConnection;
+import org.alfresco.jlan.server.filesys.cache.FileState;
+import org.alfresco.jlan.server.filesys.cache.FileStateCache;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * The in flight corrector corrects search results that have not yet been committed to the
+ * repository.
+ *
+ * It substitutes the "in flight" valuses from the state cache in place of the values committed to
+ * the repo
+ *
+ * @author mrogers
+ */
+public class InFlightCorrectorImpl implements InFlightCorrector
+{
+ TreeConnection tree;
+
+ private static final Log logger = LogFactory.getLog(InFlightCorrectorImpl.class);
+
+ public InFlightCorrectorImpl(TreeConnection tree)
+ {
+ this.tree = tree;
+ }
+ public void correct(FileInfo info, String folderPath)
+ {
+ ContentContext tctx = (ContentContext) tree.getContext();
+
+ String path = folderPath + info.getFileName();
+
+ if(tctx.hasStateCache())
+ {
+ FileStateCache cache = tctx.getStateCache();
+ FileState fstate = cache.findFileState( path, true);
+
+ if(fstate != null)
+ {
+ logger.debug("correct " + path);
+ /*
+ * What about stale file state values here?
+ */
+ if(fstate.hasFileSize())
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("replace file size " + info.getSize() + " with " + fstate.getFileSize());
+ }
+ info.setFileSize(fstate.getFileSize());
+ }
+ if ( fstate.hasAccessDateTime())
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("replace access date " + new Date(info.getAccessDateTime()) + " with " + new Date(fstate.getAccessDateTime()));
+ }
+ info.setAccessDateTime(fstate.getAccessDateTime());
+ }
+ if ( fstate.hasChangeDateTime())
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("replace change date " + new Date(info.getChangeDateTime()) + " with " + new Date(fstate.getChangeDateTime()));
+ }
+ info.setChangeDateTime(fstate.getChangeDateTime());
+ }
+ if ( fstate.hasModifyDateTime())
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("replace modified date " + new Date(info.getModifyDateTime()) + " with " + new Date(fstate.getModifyDateTime()));
+ }
+ info.setModifyDateTime(fstate.getModifyDateTime());
+ }
+ if ( fstate.hasAllocationSize())
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("replace allocation size" + info.getAllocationSize() + " with " + fstate.getAllocationSize());
+ }
+ info.setAllocationSize(fstate.getAllocationSize());
+ }
+ }
+ }
+ }
+
+}
diff --git a/source/java/org/alfresco/filesys/repo/LegacyFileStateDriver.java b/source/java/org/alfresco/filesys/repo/LegacyFileStateDriver.java
index 44c5831f54..c5464d4add 100644
--- a/source/java/org/alfresco/filesys/repo/LegacyFileStateDriver.java
+++ b/source/java/org/alfresco/filesys/repo/LegacyFileStateDriver.java
@@ -301,28 +301,25 @@ public class LegacyFileStateDriver implements ExtendedDiskInterface
{
FileStateCache cache = tctx.getStateCache();
FileState fstate = cache.findFileState( param.getFullName(), true);
-
- if(fstate.getOpenCount() ==0 )
+
+ if(fstate != null && param.getAccessToken() != null)
{
- logger.debug("OpenCount = 0, reset shared access to READ WRITE DELETE");
- fstate.setSharedAccess( SharingMode.READWRITE + SharingMode.DELETE);
-
+ FileAccessToken token = param.getAccessToken();
+ if(logger.isDebugEnabled() && token != null)
+ {
+ logger.debug("close file, release access token:" + token);
+ }
+ cache.releaseFileAccess(fstate, token);
+ }
+
+ if(fstate.getOpenCount() == 0 )
+ {
+ logger.debug("fstate OpenCount == 0, reset in-flight state");
fstate.setAllocationSize(-1);
fstate.setFileSize(-1);
fstate.updateChangeDateTime(0);
fstate.updateModifyDateTime(0);
}
-
- if(fstate != null && param.getAccessToken() != null)
- {
-
- FileAccessToken token = param.getAccessToken();
- if(logger.isDebugEnabled() && token != null)
- {
- logger.debug("close file release access token:" + token);
- }
- cache.releaseFileAccess(fstate, token);
- }
}
}
catch(IOException ie)
@@ -459,7 +456,17 @@ public class LegacyFileStateDriver implements ExtendedDiskInterface
public SearchContext startSearch(SrvSession sess, TreeConnection tree,
String searchPath, int attrib) throws FileNotFoundException
{
- return diskInterface.startSearch(sess, tree, searchPath, attrib);
+ InFlightCorrector t = new InFlightCorrectorImpl(tree);
+
+ SearchContext ctx = diskInterface.startSearch(sess, tree, searchPath, attrib);
+
+ if(ctx instanceof InFlightCorrectable)
+ {
+ InFlightCorrectable thingable = (InFlightCorrectable)ctx;
+ thingable.setInFlightCorrector(t);
+ }
+
+ return ctx;
}
diff --git a/source/java/org/alfresco/filesys/repo/TempNetworkFile.java b/source/java/org/alfresco/filesys/repo/TempNetworkFile.java
index 62d4192f21..500e57b89d 100644
--- a/source/java/org/alfresco/filesys/repo/TempNetworkFile.java
+++ b/source/java/org/alfresco/filesys/repo/TempNetworkFile.java
@@ -84,6 +84,7 @@ public class TempNetworkFile extends JavaNetworkFile implements NetworkFileState
fileState.updateModifyDateTime();
fileState.updateAccessDateTime();
fileState.setFileSize(size);
+ fileState.setAllocationSize((size + 512L) & 0xFFFFFFFFFFFFFE00L);
}
}
@@ -102,6 +103,7 @@ public class TempNetworkFile extends JavaNetworkFile implements NetworkFileState
fileState.updateModifyDateTime();
fileState.updateAccessDateTime();
fileState.setFileSize(size);
+ fileState.setAllocationSize((size + 512L) & 0xFFFFFFFFFFFFFE00L);
}
}
@@ -121,6 +123,7 @@ public class TempNetworkFile extends JavaNetworkFile implements NetworkFileState
fileState.updateModifyDateTime();
fileState.updateAccessDateTime();
fileState.setFileSize(size);
+ fileState.setAllocationSize((size + 512L) & 0xFFFFFFFFFFFFFE00L);
}
}
diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateDeleteRenameShuffle.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateDeleteRenameShuffle.java
new file mode 100644
index 0000000000..ad004c279d
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateDeleteRenameShuffle.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2005-2010 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.filesys.repo.rules;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.alfresco.filesys.repo.rules.ScenarioInstance.Ranking;
+import org.alfresco.filesys.repo.rules.operations.CreateFileOperation;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A create, delete, rename, shuffle
+ *
+ * a) New file created.
+ * b) Existing file deleted
+ * c) New file moved into place.
+ */
+public class ScenarioCreateDeleteRenameShuffle implements Scenario
+{
+ private static Log logger = LogFactory.getLog(ScenarioCreateDeleteRenameShuffle.class);
+
+ /**
+ * The regex pattern of a create that will trigger a new instance of
+ * the scenario.
+ */
+ private Pattern pattern;
+ private String strPattern;
+
+
+ private long timeout = 30000;
+
+ private Ranking ranking = Ranking.HIGH;
+
+ @Override
+ public ScenarioInstance createInstance(final List currentInstances, Operation operation)
+ {
+ /**
+ * This scenario is triggered by a create of a file matching
+ * the pattern
+ */
+ if(operation instanceof CreateFileOperation)
+ {
+ CreateFileOperation c = (CreateFileOperation)operation;
+
+ Matcher m = pattern.matcher(c.getName());
+ if(m.matches())
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("New Scenario Create Delete Rename Shuffle Instance pattern:" + strPattern);
+ }
+
+ ScenarioCreateDeleteRenameShuffleInstance instance = new ScenarioCreateDeleteRenameShuffleInstance() ;
+ instance.setTimeout(timeout);
+ instance.setRanking(ranking);
+ return instance;
+ }
+ }
+
+ // No not interested.
+ return null;
+
+ }
+
+ public void setPattern(String pattern)
+ {
+ this.pattern = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
+ this.strPattern = pattern;
+ }
+
+ public String getPattern()
+ {
+ return this.strPattern;
+ }
+
+ public void setTimeout(long timeout)
+ {
+ this.timeout = timeout;
+ }
+
+ public long getTimeout()
+ {
+ return timeout;
+ }
+
+ public void setRanking(Ranking ranking)
+ {
+ this.ranking = ranking;
+ }
+
+ public Ranking getRanking()
+ {
+ return ranking;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateDeleteRenameShuffleInstance.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateDeleteRenameShuffleInstance.java
new file mode 100644
index 0000000000..179f70336f
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateDeleteRenameShuffleInstance.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2005-2010 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.filesys.repo.rules;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.alfresco.filesys.repo.rules.ScenarioLockedDeleteShuffleInstance.InternalState;
+import org.alfresco.filesys.repo.rules.commands.CompoundCommand;
+import org.alfresco.filesys.repo.rules.commands.CopyContentCommand;
+import org.alfresco.filesys.repo.rules.commands.DeleteFileCommand;
+import org.alfresco.filesys.repo.rules.commands.RenameFileCommand;
+import org.alfresco.filesys.repo.rules.operations.CreateFileOperation;
+import org.alfresco.filesys.repo.rules.operations.DeleteFileOperation;
+import org.alfresco.filesys.repo.rules.operations.MoveFileOperation;
+import org.alfresco.filesys.repo.rules.operations.RenameFileOperation;
+import org.alfresco.jlan.server.filesys.FileName;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * This is an instance of a create, delete, rename shuffle" triggered by a create of a
+ * file matching a specified pattern.
+ *
+ * a) New file created. Typically with an obscure name.
+ * b) Existing file deleted
+ * c) New file moved into place.
+ *
+ *
+ * If this filter is active then this is what happens.
+ * a) New file created. New file created (X).
+ * b) Existing file deleted (Y to Z). File moved to temporary location.
+ * c) Rename - Scenario fires
+ * - File moved back from temporary location
+ * - Content updated/
+ * - temporary file deleted
+ */
+public class ScenarioCreateDeleteRenameShuffleInstance implements ScenarioInstance
+{
+ private static Log logger = LogFactory.getLog(ScenarioCreateDeleteRenameShuffleInstance.class);
+
+ enum InternalState
+ {
+ NONE,
+ ACTIVE
+ }
+
+ InternalState internalState = InternalState.NONE;
+
+ private Date startTime = new Date();
+
+ private String createName;
+ private Ranking ranking;
+ private boolean checkFilename=true;
+
+ /**
+ * Timeout in ms. Default 30 seconds.
+ */
+ private long timeout = 60000;
+
+ private boolean isComplete;
+
+ /**
+ * Keep track of deletes that we substitute with a rename
+ * could be more than one if scenarios overlap
+ *
+ * From, TempFileName
+ */
+ private Map deletes = new HashMap();
+
+ /**
+ * Evaluate the next operation
+ * @param operation
+ */
+ public Command evaluate(Operation operation)
+ {
+
+ /**
+ * Anti-pattern : timeout
+ */
+ Date now = new Date();
+ if(now.getTime() > startTime.getTime() + getTimeout())
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Instance timed out createName:" + createName);
+ isComplete = true;
+ return null;
+ }
+ }
+
+ /**
+ * Anti-pattern for all states - delete the file we are
+ * shuffling
+ */
+ if(createName != null)
+ {
+ if(operation instanceof DeleteFileOperation)
+ {
+ DeleteFileOperation d = (DeleteFileOperation)operation;
+ if(d.getName().equals(createName))
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Anti-pattern : Shuffle file deleted createName:" + createName);
+ }
+ isComplete = true;
+ return null;
+ }
+ }
+ }
+
+ switch (internalState)
+ {
+
+ case NONE:
+ // Looking for a create transition
+ if(operation instanceof CreateFileOperation)
+ {
+ CreateFileOperation c = (CreateFileOperation)operation;
+ this.createName = c.getName();
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("entering ACTIVE state: " + createName);
+ }
+ internalState = InternalState.ACTIVE;
+ return null;
+ }
+ else
+ {
+ // anything else bomb out
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("State error, expected a CREATE");
+ }
+ isComplete = true;
+ }
+ break;
+
+ case ACTIVE:
+
+ /**
+ * Looking for deletes and renames
+ */
+
+ /**
+ * Looking for target file being deleted
+ *
+ * Need to intervene and replace delete with a rename to temp file.
+ */
+ if(operation instanceof DeleteFileOperation)
+ {
+ DeleteFileOperation d = (DeleteFileOperation)operation;
+
+ String deleteName = d.getName();
+
+ /**
+ * For Mac 2011 powerpoint files - add an extra check based on the filename
+ * e.g FileA1 - delete FileA.
+ */
+ if(checkFilename)
+ {
+ int i = deleteName.lastIndexOf('.');
+
+ if(i > 0 && deleteName.substring(0, i).equalsIgnoreCase(createName.substring(0,i)))
+ {
+ logger.debug("check filenames - does match");
+ }
+ else
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("check filename patterns do not match - Ignore" + createName + deleteName);
+ }
+ return null;
+ }
+ }
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("got a delete : replace with rename createName:" + createName + "deleteName:" + deleteName);
+ }
+
+ String tempName = ".shuffle" + d.getName();
+
+ deletes.put(d.getName(), tempName);
+
+ String[] paths = FileName.splitPath(d.getPath());
+ String currentFolder = paths[0];
+
+ RenameFileCommand r1 = new RenameFileCommand(d.getName(), tempName, d.getRootNodeRef(), d.getPath(), currentFolder + "\\" + tempName);
+
+ return r1;
+ }
+
+ /**
+ *
+ */
+ if(operation instanceof RenameFileOperation)
+ {
+ RenameFileOperation m = (RenameFileOperation)operation;
+
+ String targetFile = m.getTo();
+
+ if(deletes.containsKey(targetFile))
+ {
+ String tempName = deletes.get(targetFile);
+
+ String[] paths = FileName.splitPath(m.getToPath());
+ String currentFolder = paths[0];
+
+ /**
+ * This is where the scenario fires.
+ * a) Rename the temp file back to the targetFile
+ * b) Copy content from moved file
+ * c) Delete rather than move file
+ */
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("scenario fires:" + createName);
+ }
+ ArrayList commands = new ArrayList();
+
+ RenameFileCommand r1 = new RenameFileCommand(tempName, targetFile, m.getRootNodeRef(), currentFolder + "\\" + tempName, m.getToPath());
+
+ CopyContentCommand copyContent = new CopyContentCommand(m.getFrom(), targetFile, m.getRootNodeRef(), m.getFromPath(), m.getToPath());
+
+ DeleteFileCommand d1 = new DeleteFileCommand(m.getFrom(), m.getRootNodeRef(), m.getFromPath());
+
+ commands.add(r1);
+ commands.add(copyContent);
+ commands.add(d1);
+
+ logger.debug("Scenario complete");
+ isComplete = true;
+
+ return new CompoundCommand(commands);
+ }
+ }
+
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean isComplete()
+ {
+ return isComplete;
+ }
+
+ @Override
+ public Ranking getRanking()
+ {
+ return ranking;
+ }
+
+ public void setRanking(Ranking ranking)
+ {
+ this.ranking = ranking;
+ }
+
+ public String toString()
+ {
+ return "ScenarioShuffleInstance: createName:" + createName;
+ }
+
+ public void setTimeout(long timeout)
+ {
+ this.timeout = timeout;
+ }
+
+ public long getTimeout()
+ {
+ return timeout;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateShuffleInstance.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateShuffleInstance.java
index b782a74c4a..1428a2ec96 100644
--- a/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateShuffleInstance.java
+++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateShuffleInstance.java
@@ -72,7 +72,7 @@ public class ScenarioCreateShuffleInstance implements ScenarioInstance
/**
* Timeout in ms. Default 30 seconds.
*/
- private long timeout = 30000;
+ private long timeout = 60000;
private boolean isComplete;
@@ -88,24 +88,6 @@ public class ScenarioCreateShuffleInstance implements ScenarioInstance
public Command evaluate(Operation operation)
{
-// /**
-// * Anti-pattern for all states - delete the file we are
-// * shuffling
-// */
-// if(createName != null)
-// {
-// if(operation instanceof DeleteFileOperation)
-// {
-// DeleteFileOperation d = (DeleteFileOperation)operation;
-// if(d.getName().equals(createName))
-// {
-// logger.debug("Anti-pattern : Shuffle file deleted");
-// isComplete = true;
-// return null;
-// }
-// }
-// }
-
/**
* Anti-pattern : timeout
*/
@@ -114,7 +96,30 @@ public class ScenarioCreateShuffleInstance implements ScenarioInstance
{
if(logger.isDebugEnabled())
{
- logger.debug("Instance timed out");
+ logger.debug("Instance timed out createName:" + createName);
+ isComplete = true;
+ return null;
+ }
+ }
+
+ /**
+ * Anti-pattern for all states - delete the file we are
+ * shuffling
+ */
+ if(createName != null)
+ {
+ if(operation instanceof DeleteFileOperation)
+ {
+ DeleteFileOperation d = (DeleteFileOperation)operation;
+ if(d.getName().equals(createName))
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Anti-pattern : Shuffle file deleted createName:" + createName);
+ }
+ isComplete = true;
+ return null;
+ }
}
}
@@ -229,11 +234,11 @@ public class ScenarioCreateShuffleInstance implements ScenarioInstance
DeleteFileOperation d = (DeleteFileOperation)operation;
if(d.getName().equals(move2))
{
- logger.debug("Scenario complete");
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Scenario complete createName:" + createName);
+ }
isComplete = true;
-// ArrayList commands = new ArrayList();
-// // missing stuff here
-// return new CompoundCommand(commands);
}
}
@@ -262,7 +267,7 @@ public class ScenarioCreateShuffleInstance implements ScenarioInstance
public String toString()
{
- return "ScenarioShuffleInstance:" + createName;
+ return "ScenarioShuffleInstance: createName:" + createName;
}
public void setTimeout(long timeout)
diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioDoubleRenameShuffleInstance.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioDoubleRenameShuffleInstance.java
index f79f3a341e..31fe2f3213 100644
--- a/source/java/org/alfresco/filesys/repo/rules/ScenarioDoubleRenameShuffleInstance.java
+++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioDoubleRenameShuffleInstance.java
@@ -95,6 +95,7 @@ public class ScenarioDoubleRenameShuffleInstance implements ScenarioInstance
if(logger.isDebugEnabled())
{
logger.debug("Instance timed out");
+
}
}
diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioLockedDeleteShuffleInstance.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioLockedDeleteShuffleInstance.java
index d83ad8d0c3..8d0636a801 100644
--- a/source/java/org/alfresco/filesys/repo/rules/ScenarioLockedDeleteShuffleInstance.java
+++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioLockedDeleteShuffleInstance.java
@@ -79,11 +79,10 @@ public class ScenarioLockedDeleteShuffleInstance implements ScenarioInstance
private Ranking ranking;
-
/**
* Timeout in ms. Default 30 seconds.
*/
- private long timeout = 30000;
+ private long timeout = 60000;
private boolean isComplete;
@@ -127,7 +126,9 @@ public class ScenarioLockedDeleteShuffleInstance implements ScenarioInstance
{
if(logger.isDebugEnabled())
{
- logger.debug("Instance timed out");
+ logger.debug("Instance timed out lockName:" + lockName);
+ isComplete = true;
+ return null;
}
}
@@ -212,7 +213,8 @@ public class ScenarioLockedDeleteShuffleInstance implements ScenarioInstance
* a) Rename the temp file back to the targetFile
* b) Copy content from moved file
* c) Delete rather than move file
- */
+ */
+ logger.debug("scenario fires");
ArrayList commands = new ArrayList();
RenameFileCommand r1 = new RenameFileCommand(tempName, targetFile, m.getRootNodeRef(), currentFolder + "\\" + tempName, m.getToPath());
@@ -228,17 +230,8 @@ public class ScenarioLockedDeleteShuffleInstance implements ScenarioInstance
logger.debug("Scenario complete");
isComplete = true;
- return new CompoundCommand(commands);
-
+ return new CompoundCommand(commands);
}
-
- //TODO - Need to consider error cases and "overlap"
-
-// if(logger.isDebugEnabled())
-// {
-// logger.debug("entering MOVED state: " + lockName);
-// }
-// internalState = InternalState.MOVED;
}
diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioOpenFile.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioOpenFile.java
index ce3e0bd571..1e43724699 100644
--- a/source/java/org/alfresco/filesys/repo/rules/ScenarioOpenFile.java
+++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioOpenFile.java
@@ -169,7 +169,7 @@ public class ScenarioOpenFile implements Scenario
ScenarioOpenFileInstance i = (ScenarioOpenFileInstance)instance;
if(i.getName() != null && name != null)
{
- if(i.getName().equalsIgnoreCase(name));
+ if(i.getName().equalsIgnoreCase(name))
{
return true;
}
diff --git a/source/java/org/alfresco/repo/avm/AVMLockingAwareService.java b/source/java/org/alfresco/repo/avm/AVMLockingAwareService.java
index a9bb46e692..13e1b2e0d6 100644
--- a/source/java/org/alfresco/repo/avm/AVMLockingAwareService.java
+++ b/source/java/org/alfresco/repo/avm/AVMLockingAwareService.java
@@ -31,6 +31,7 @@ import org.alfresco.repo.avm.util.AVMUtil;
import org.alfresco.repo.domain.PropertyValue;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.permissions.AccessDeniedException;
+import org.alfresco.service.cmr.avm.AVMExistsException;
import org.alfresco.service.cmr.avm.AVMNodeDescriptor;
import org.alfresco.service.cmr.avm.AVMService;
import org.alfresco.service.cmr.avm.AVMStoreDescriptor;
@@ -64,6 +65,8 @@ public class AVMLockingAwareService implements AVMService, ApplicationContextAwa
public static final String STORE_WORKFLOW = "workflow";
+ public static final String STORE_PREVIEW = "preview";
+
private AVMService fService;
private AVMLockingService fLockingService;
@@ -668,14 +671,27 @@ public class AVMLockingAwareService implements AVMService, ApplicationContextAwa
{
String userName = AuthenticationUtil.getFullyAuthenticatedUser();
LockState lockState = fLockingService.getLockState(webProject, storePath[1], userName);
- // Managers can edit any file in any sandbox, look into ALF-11440
String wpStoreId = WCMUtil.getWebProjectStoreId(webProject);
- if (lockState == AVMLockingService.LockState.LOCK_NOT_OWNER && wpService.isContentManager(wpStoreId, userName))
- lockState = AVMLockingService.LockState.LOCK_OWNER;
+
+ // ALF-11440 PM 18-Dec-2011:
+ // 1. Managers may edit any unlocked file - it becomes locked.
+ // 2. Managers may edit any locked file in any sandbox in which it is locked
+ // but not in sandboxes where it is unlocked.
+ // ALF-8787 and ALF-12766 are consistent with 2.
+ // A Manager should only be able to create a file in a sandbox
+ // if it is NOT locked somewhere else.
switch (lockState)
{
case LOCK_NOT_OWNER:
String lockOwner = fLockingService.getLockOwner(webProject, storePath[1]);
+ if ((wpService.isContentManager(wpStoreId, userName)) &&
+ (avmStore.equals(wpStoreId + STORE_SEPARATOR + lockOwner) ||
+ avmStore.equals(wpStoreId + STORE_SEPARATOR + lockOwner
+ + STORE_SEPARATOR + STORE_PREVIEW)))
+ {
+ // Handle as if LOCK_OWNER
+ break;
+ }
throw new AVMLockingException("avmlockservice.locked", path, lockOwner);
case NO_LOCK:
Map lockAttributes = Collections.singletonMap(WCMUtil.LOCK_KEY_STORE_NAME, avmStore);
diff --git a/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java b/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java
index 1e9c0e7452..375c3ae2e9 100644
--- a/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java
+++ b/source/java/org/alfresco/repo/avm/AVMSyncServiceImpl.java
@@ -232,7 +232,7 @@ public class AVMSyncServiceImpl implements AVMSyncService
dirDiffCode));
// Also add all child items if necessary and any exists
- if (expandDirs)
+ if (expandDirs && srcDesc.isDirectory())
{
addNewChildrenIfAny(srcVersion, srcDesc, dstVersion, AVMNodeConverter.ExtendAVMPath(dstPath, dstDesc.getName()), result);
}
@@ -283,7 +283,7 @@ public class AVMSyncServiceImpl implements AVMSyncService
AVMDifference.NEWER));
// Also add all child items if necessary and any exists
- if (expandDirs)
+ if (expandDirs && srcChild.isDirectory())
{
addNewChildrenIfAny(srcVersion, srcChild, dstVersion, dstChildPath, result);
}
diff --git a/source/java/org/alfresco/repo/content/AbstractContentReader.java b/source/java/org/alfresco/repo/content/AbstractContentReader.java
index 5e4ae7432e..5e7e2688c0 100644
--- a/source/java/org/alfresco/repo/content/AbstractContentReader.java
+++ b/source/java/org/alfresco/repo/content/AbstractContentReader.java
@@ -69,7 +69,7 @@ import sun.nio.ch.ChannelInputStream;
public abstract class AbstractContentReader extends AbstractContentAccessor implements ContentReader
{
private static final Log logger = LogFactory.getLog(AbstractContentReader.class);
- private static final Timer timer = new Timer();
+ private static final Timer timer = new Timer(true);
private List listeners;
private ReadableByteChannel channel;
diff --git a/source/java/org/alfresco/repo/content/transform/PdfToImageContentTransformer.java b/source/java/org/alfresco/repo/content/transform/PdfToImageContentTransformer.java
index 6acb28be1e..1eedb7656f 100644
--- a/source/java/org/alfresco/repo/content/transform/PdfToImageContentTransformer.java
+++ b/source/java/org/alfresco/repo/content/transform/PdfToImageContentTransformer.java
@@ -93,6 +93,11 @@ public class PdfToImageContentTransformer extends AbstractContentTransformer2
}
PDFPage page = pdffile.getPage(0, true);
+ if (page == null)
+ {
+ throw new AlfrescoRuntimeException("Unable to create image from pdf file."+
+ "A PDFRender error took place which should have been sent to stdout.");
+ }
//get the width and height for the doc at the default zoom
int width=(int)page.getBBox().getWidth();
diff --git a/source/java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformerWorker.java b/source/java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformerWorker.java
index 9f3ffda96f..f277ed5b72 100644
--- a/source/java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformerWorker.java
+++ b/source/java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformerWorker.java
@@ -154,6 +154,10 @@ public class ImageMagickContentTransformerWorker extends AbstractImageMagickCont
ImageCropOptions cropOptions = imageOptions.getCropOptions();
ImageResizeOptions resizeOptions = imageOptions.getResizeOptions();
String commandOptions = imageOptions.getCommandOptions();
+ if (commandOptions == null)
+ {
+ commandOptions = "";
+ }
if (imageOptions.isAutoOrient())
{
commandOptions = commandOptions + " -auto-orient";
diff --git a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
index 7ac064e6e4..cc758ce17a 100644
--- a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
@@ -57,10 +57,10 @@ import org.alfresco.repo.domain.usage.UsageDAO;
import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.repo.security.permissions.AccessControlListProperties;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
-import org.alfresco.repo.transaction.TransactionAwareSingleton;
-import org.alfresco.repo.transaction.TransactionListenerAdapter;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
+import org.alfresco.repo.transaction.TransactionAwareSingleton;
+import org.alfresco.repo.transaction.TransactionListenerAdapter;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.InvalidTypeException;
@@ -74,20 +74,20 @@ import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.InvalidStoreRefException;
import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeRef.Status;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.repository.StoreRef;
-import org.alfresco.service.cmr.repository.NodeRef.Status;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.ReadOnlyServerException;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.EqualsHelper;
+import org.alfresco.util.EqualsHelper.MapValueComparison;
import org.alfresco.util.GUID;
import org.alfresco.util.Pair;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.ReadWriteLockExecuter;
import org.alfresco.util.ValueProtectingMap;
-import org.alfresco.util.EqualsHelper.MapValueComparison;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.ConcurrencyFailureException;
@@ -957,12 +957,8 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
Long nodeId = dbNode.getId();
if (dbNode.getDeleted())
{
- // The node is actually deleted as the cache said. Could still be a race condition, so let's allow the
- // transaction to be retried by attaching a cause to our InvalidNodeRefException
- InvalidNodeRefException e = new InvalidNodeRefException(nodeRef);
- e.initCause(new ConcurrencyFailureException("Attempt to follow reference " + nodeRef
- + " to deleted node " + nodeId));
- throw e;
+ // The node is actually deleted as the cache said.
+ throw new InvalidNodeRefException(nodeRef);
}
else
{
@@ -4248,7 +4244,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
{
return selectTxnsUnused(minTxnId, maxCommitTime, count);
}
-
+
public void purgeTxn(Long txnId)
{
deleteTransaction(txnId);
@@ -4268,6 +4264,24 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
return (time == null ? LONG_ZERO : time);
}
+ public Long getMinTxnId()
+ {
+ Long id = selectMinTxnId();
+ return (id == null ? LONG_ZERO : id);
+ }
+
+ public Long getMinUnusedTxnCommitTime()
+ {
+ Long id = selectMinUnusedTxnCommitTime();
+ return (id == null ? LONG_ZERO : id);
+ }
+
+ public Long getMaxTxnId()
+ {
+ Long id = selectMaxTxnId();
+ return (id == null ? LONG_ZERO : id);
+ }
+
/*
* Abstract methods for underlying CRUD
*/
@@ -4438,4 +4452,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
protected abstract List selectTxnsUnused(Long minTxnId, Long maxCommitTime, Integer count);
protected abstract Long selectMinTxnCommitTime();
protected abstract Long selectMaxTxnCommitTime();
+ protected abstract Long selectMinTxnId();
+ protected abstract Long selectMaxTxnId();
+ protected abstract Long selectMinUnusedTxnCommitTime();
}
diff --git a/source/java/org/alfresco/repo/domain/node/NodeDAO.java b/source/java/org/alfresco/repo/domain/node/NodeDAO.java
index 003f674401..bd0043725d 100644
--- a/source/java/org/alfresco/repo/domain/node/NodeDAO.java
+++ b/source/java/org/alfresco/repo/domain/node/NodeDAO.java
@@ -710,6 +710,16 @@ public interface NodeDAO extends NodeBulkLoader
public List getTxnsUnused(Long minTxnId, long maxCommitTime, int count);
+ /**
+ * Remove unused transactions from commit time 'fromCommitTime' to commit time 'toCommitTime'
+ *
+ * @param fromCommitTime delete unused transactions from commit time
+ * @param toCommitTime delete unused transactions to commit time
+ *
+ * @return
+ */
+ public int deleteTxnsUnused(long fromCommitTime, long toCommitTime);
+
public void purgeTxn(Long txnId);
/**
@@ -722,6 +732,22 @@ public interface NodeDAO extends NodeBulkLoader
*/
public Long getMaxTxnCommitTime();
+ /**
+ * @return Returns the minimum id or 0 if there are no transactions
+ */
+ public Long getMinTxnId();
+
+ /**
+ *
+ * @return the commit time of the oldest unused transaction
+ */
+ public Long getMinUnusedTxnCommitTime();
+
+ /**
+ * @return Returns the maximum id or 0 if there are no transactions
+ */
+ public Long getMaxTxnId();
+
/**
* Select children by property values
*/
diff --git a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java
index b92e3223e8..f381c2bcc7 100644
--- a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java
@@ -142,8 +142,12 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
private static final String SELECT_TXN_COUNT = "alfresco.node.select_TxnCount";
private static final String SELECT_TXN_NODE_COUNT = "alfresco.node.select_TxnNodeCount";
private static final String SELECT_TXNS_UNUSED = "alfresco.node.select_TxnsUnused";
+ private static final String DELETE_TXNS_UNUSED = "alfresco.node.delete_Txns_Unused";
private static final String SELECT_TXN_MIN_COMMIT_TIME = "alfresco.node.select_TxnMinCommitTime";
private static final String SELECT_TXN_MAX_COMMIT_TIME = "alfresco.node.select_TxnMaxCommitTime";
+ private static final String SELECT_TXN_MIN_ID = "alfresco.node.select_TxnMinId";
+ private static final String SELECT_TXN_MAX_ID = "alfresco.node.select_TxnMaxId";
+ private static final String SELECT_TXN_UNUSED_MIN_COMMIT_TIME = "alfresco.node.select_TxnMinUnusedCommitTime";
private QNameDAO qnameDAO;
private DictionaryService dictionaryService;
@@ -1605,6 +1609,16 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
}
}
+ @Override
+ public int deleteTxnsUnused(long fromCommitTime, long toCommitTime)
+ {
+ TransactionQueryEntity txnQuery = new TransactionQueryEntity();
+ txnQuery.setMinCommitTime(fromCommitTime);
+ txnQuery.setMaxCommitTime(toCommitTime);
+ int numDeleted = template.delete(DELETE_TXNS_UNUSED, txnQuery);
+ return numDeleted;
+ }
+
@Override
protected Long selectMinTxnCommitTime()
{
@@ -1616,6 +1630,24 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
{
return (Long) template.selectOne(SELECT_TXN_MAX_COMMIT_TIME);
}
+
+ @Override
+ protected Long selectMinTxnId()
+ {
+ return (Long) template.selectOne(SELECT_TXN_MIN_ID);
+ }
+
+ @Override
+ protected Long selectMinUnusedTxnCommitTime()
+ {
+ return (Long) template.selectOne(SELECT_TXN_UNUSED_MIN_COMMIT_TIME);
+ }
+
+ @Override
+ protected Long selectMaxTxnId()
+ {
+ return (Long) template.selectOne(SELECT_TXN_MAX_ID);
+ }
@Override
public List selectProperties(Collection propertyDefs)
diff --git a/source/java/org/alfresco/repo/domain/permissions/AbstractAclCrudDAOImpl.java b/source/java/org/alfresco/repo/domain/permissions/AbstractAclCrudDAOImpl.java
index 35822aac7d..1ea9317d2b 100644
--- a/source/java/org/alfresco/repo/domain/permissions/AbstractAclCrudDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/permissions/AbstractAclCrudDAOImpl.java
@@ -1021,4 +1021,38 @@ public abstract class AbstractAclCrudDAOImpl implements AclCrudDAO
protected abstract long createAuthorityAliasEntity(AuthorityAliasEntity entity);
protected abstract int deleteAuthorityAliasEntity(long id);
+
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.domain.permissions.AclCrudDAO#getMaxChangeSetCommitTime()
+ */
+ @Override
+ public Long getMaxChangeSetCommitTime()
+ {
+ Long time = selectMaxChangeSetCommitTime();
+ return (time == null ? 0L : time);
+ }
+
+
+ /**
+ * @return
+ */
+ protected abstract Long selectMaxChangeSetCommitTime();
+
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.domain.permissions.AclCrudDAO#getMaxChangeSetIdByCommitTime(long)
+ */
+ @Override
+ public Long getMaxChangeSetIdByCommitTime(long maxCommitTime)
+ {
+ Long id = selectMaxChangeSetIdBeforeCommitTime(maxCommitTime);
+ return (id == null ? 0L : id);
+ }
+
+ /**
+ * @param maxCommitTime
+ * @return
+ */
+ protected abstract Long selectMaxChangeSetIdBeforeCommitTime(long maxCommitTime);
}
diff --git a/source/java/org/alfresco/repo/domain/permissions/AclCrudDAO.java b/source/java/org/alfresco/repo/domain/permissions/AclCrudDAO.java
index d57018ce5b..b68a6ab7bc 100644
--- a/source/java/org/alfresco/repo/domain/permissions/AclCrudDAO.java
+++ b/source/java/org/alfresco/repo/domain/permissions/AclCrudDAO.java
@@ -112,6 +112,15 @@ public interface AclCrudDAO
public Authority getOrCreateAuthority(String authorityName);
public void renameAuthority(String authorityNameBefore, String authorityAfter);
public void deleteAuthority(long authorityEntityId);
+ /**
+ * @return
+ */
+ public Long getMaxChangeSetCommitTime();
+ /**
+ * @param maxCommitTime
+ * @return
+ */
+ public Long getMaxChangeSetIdByCommitTime(long maxCommitTime);
// AceContext (NOTE: currently unused - intended for possible future enhancement)
// AuthorityAlias (NOTE: currently unused - intended for possible future enhancement)
diff --git a/source/java/org/alfresco/repo/domain/permissions/AclDAO.java b/source/java/org/alfresco/repo/domain/permissions/AclDAO.java
index 52ff222c69..b455c7eafc 100644
--- a/source/java/org/alfresco/repo/domain/permissions/AclDAO.java
+++ b/source/java/org/alfresco/repo/domain/permissions/AclDAO.java
@@ -160,4 +160,15 @@ public interface AclDAO
* @param aclId
*/
public void fixSharedAcl(Long shared, Long defining);
+
+ /**
+ * @return
+ */
+ public Long getMaxChangeSetCommitTime();
+
+ /**
+ * @param maxCommitTime
+ * @return
+ */
+ public Long getMaxChangeSetIdByCommitTime(long maxCommitTime);
}
diff --git a/source/java/org/alfresco/repo/domain/permissions/AclDAOImpl.java b/source/java/org/alfresco/repo/domain/permissions/AclDAOImpl.java
index 4d7d9553e3..13b2fecd2e 100644
--- a/source/java/org/alfresco/repo/domain/permissions/AclDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/permissions/AclDAOImpl.java
@@ -72,6 +72,8 @@ public class AclDAOImpl implements AclDAO
private TenantService tenantService;
private SimpleCache aclCache;
private SimpleCache> readersCache;
+
+ private SimpleCache> readersDeniedCache;
private enum WriteMode
{
@@ -142,6 +144,14 @@ public class AclDAOImpl implements AclDAO
{
this.readersCache = readersCache;
}
+
+ /**
+ * @param readersDeniedCache the readersDeniedCache to set
+ */
+ public void setReadersDeniedCache(SimpleCache> readersDeniedCache)
+ {
+ this.readersDeniedCache = readersDeniedCache;
+ }
/**
* {@inheritDoc}
@@ -434,6 +444,7 @@ public class AclDAOImpl implements AclDAO
{
aclCache.remove(id);
readersCache.remove(id);
+ readersDeniedCache.remove(id);
return new AclChangeImpl(id, id, acl.getAclType(), acl.getAclType());
}
@@ -484,6 +495,7 @@ public class AclDAOImpl implements AclDAO
aclCrudDAO.updateAcl(acl);
aclCache.remove(id);
readersCache.remove(id);
+ readersDeniedCache.remove(id);
return new AclChangeImpl(id, id, acl.getAclType(), acl.getAclType());
}
else if ((acl.getAclChangeSetId() == getCurrentChangeSetId()) && (!requiresVersion) && (!acl.getRequiresVersion()))
@@ -523,6 +535,7 @@ public class AclDAOImpl implements AclDAO
aclCrudDAO.updateAcl(acl);
aclCache.remove(id);
readersCache.remove(id);
+ readersDeniedCache.remove(id);
return new AclChangeImpl(id, id, acl.getAclType(), acl.getAclType());
}
else
@@ -612,6 +625,7 @@ public class AclDAOImpl implements AclDAO
aclCrudDAO.updateAcl(acl);
aclCache.remove(id);
readersCache.remove(id);
+ readersDeniedCache.remove(id);
return new AclChangeImpl(id, created, acl.getAclType(), newAcl.getAclType());
}
}
@@ -803,6 +817,7 @@ public class AclDAOImpl implements AclDAO
{
aclCache.remove(aclId);
readersCache.remove(aclId);
+ readersDeniedCache.remove(aclId);
Acl list = aclCrudDAO.getAcl(aclId);
acls.add(new AclChangeImpl(aclId, aclId, list.getAclType(), list.getAclType()));
@@ -860,6 +875,7 @@ public class AclDAOImpl implements AclDAO
aclCache.remove(aclId);
readersCache.remove(aclId);
+ readersDeniedCache.remove(aclId);
}
if (dbAcl.getAclType() == ACLType.SHARED)
{
@@ -878,6 +894,7 @@ public class AclDAOImpl implements AclDAO
aclCache.remove(aclId);
readersCache.remove(aclId);
+ readersDeniedCache.remove(aclId);
}
}
else
@@ -992,6 +1009,7 @@ public class AclDAOImpl implements AclDAO
// remove the deleted acl from the cache
aclCache.remove(id);
readersCache.remove(id);
+ readersDeniedCache.remove(id);
acls.add(new AclChangeImpl(id, null, acl.getAclType(), null));
return acls;
}
@@ -1337,6 +1355,7 @@ public class AclDAOImpl implements AclDAO
aclCrudDAO.updateAcl(acl);
aclCache.remove(id);
readersCache.remove(id);
+ readersDeniedCache.remove(id);
changes.add(new AclChangeImpl(id, id, acl.getAclType(), acl.getAclType()));
return changes;
case SHARED:
@@ -1387,6 +1406,7 @@ public class AclDAOImpl implements AclDAO
aclCrudDAO.updateAcl(acl);
aclCache.remove(id);
readersCache.remove(id);
+ readersDeniedCache.remove(id);
changes.add(new AclChangeImpl(id, id, acl.getAclType(), acl.getAclType()));
return changes;
case SHARED:
@@ -1422,6 +1442,7 @@ public class AclDAOImpl implements AclDAO
aclCrudDAO.updateAcl(aclToCopy);
aclCache.remove(toCopy);
readersCache.remove(toCopy);
+ readersDeniedCache.remove(toCopy);
inheritedId = getInheritedAccessControlList(toCopy);
if ((inheritedId != null) && (!inheritedId.equals(toCopy)))
{
@@ -1431,6 +1452,7 @@ public class AclDAOImpl implements AclDAO
aclCrudDAO.updateAcl(inheritedAcl);
aclCache.remove(inheritedId);
readersCache.remove(inheritedId);
+ readersDeniedCache.remove(inheritedId);
}
return toCopy;
case REDIRECT:
@@ -1908,4 +1930,22 @@ public class AclDAOImpl implements AclDAO
List changes = new ArrayList();
getWritable(shared, defining, null, null, defining, true, changes, WriteMode.CHANGE_INHERITED);
}
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.domain.permissions.AclDAO#getMaxChangeSetCommitTime()
+ */
+ @Override
+ public Long getMaxChangeSetCommitTime()
+ {
+ return aclCrudDAO.getMaxChangeSetCommitTime();
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.domain.permissions.AclDAO#getMaxChangeSetIdByCommitTime(long)
+ */
+ @Override
+ public Long getMaxChangeSetIdByCommitTime(long maxCommitTime)
+ {
+ return aclCrudDAO.getMaxChangeSetIdByCommitTime(maxCommitTime);
+ }
}
diff --git a/source/java/org/alfresco/repo/domain/permissions/ibatis/AclCrudDAOImpl.java b/source/java/org/alfresco/repo/domain/permissions/ibatis/AclCrudDAOImpl.java
index f8760f1ab0..9ee24b737a 100644
--- a/source/java/org/alfresco/repo/domain/permissions/ibatis/AclCrudDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/permissions/ibatis/AclCrudDAOImpl.java
@@ -35,6 +35,7 @@ import org.alfresco.repo.domain.permissions.PermissionEntity;
import org.alfresco.repo.security.permissions.ACEType;
import org.apache.ibatis.session.RowBounds;
import org.mybatis.spring.SqlSessionTemplate;
+import org.springframework.util.Assert;
/**
* iBatis-specific implementation of the ACL Crud DAO.
@@ -91,6 +92,9 @@ public class AclCrudDAOImpl extends AbstractAclCrudDAOImpl
private static final String INSERT_AUTHORITY_ALIAS = "alfresco.permissions.insert.insert_AuthorityAlias";
private static final String DELETE_AUTHORITY_ALIAS = "alfresco.permissions.delete_AuthorityAlias";
+ private static final String SELECT_CHANGE_SET_LAST = "alfresco.permissions.select_ChangeSetLast";
+ private static final String SELECT_CHANGE_SET_MAX_COMMIT_TIME = "alfresco.permissions.select_ChangeSetMaxCommitTime";;
+
private SqlSessionTemplate template;
@@ -472,4 +476,40 @@ public class AclCrudDAOImpl extends AbstractAclCrudDAOImpl
return template.delete(DELETE_AUTHORITY_ALIAS, params);
}
+
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.domain.permissions.AbstractAclCrudDAOImpl#selectMaxChangeSetCommitTime()
+ */
+ @Override
+ protected Long selectMaxChangeSetCommitTime()
+ {
+ return (Long) template.selectOne(SELECT_CHANGE_SET_MAX_COMMIT_TIME);
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.domain.permissions.AbstractAclCrudDAOImpl#selectMaxChangeSetIdBeforeCommitTime(long)
+ */
+ @Override
+ protected Long selectMaxChangeSetIdBeforeCommitTime(long maxCommitTime)
+ {
+ Assert.notNull(maxCommitTime, "maxCommitTime");
+
+ Map params = new HashMap(1);
+ params.put("commit_time_ms", maxCommitTime);
+
+ List sets = (List) template.selectList(SELECT_CHANGE_SET_LAST, params, new RowBounds(0, 1));
+ if (sets.size() > 0)
+ {
+ return sets.get(0);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+
+
}
diff --git a/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java b/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java
index 2c346cef8d..4229a6c50a 100644
--- a/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java
+++ b/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java
@@ -32,13 +32,16 @@ import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.admin.SysAdminParams;
import org.alfresco.repo.node.NodeServicePolicies;
+import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy;
import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.PasswordGenerator;
import org.alfresco.repo.security.authentication.UserNameGenerator;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.site.SiteModel;
+import org.alfresco.repo.transaction.TransactionalResourceHelper;
import org.alfresco.repo.workflow.CancelWorkflowActionExecuter;
import org.alfresco.repo.workflow.WorkflowModel;
import org.alfresco.repo.workflow.activiti.ActivitiConstants;
@@ -151,6 +154,7 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
//
this.policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"),
SiteModel.TYPE_SITE, new JavaBehaviour(this, "beforeDeleteNode"));
+ this.policyComponent.bindClassBehaviour(BeforeDeleteNodePolicy.QNAME, ContentModel.TYPE_PERSON, new JavaBehaviour(this, "beforeDeleteNode"));
}
/**
@@ -362,23 +366,27 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
private void endInvitation(WorkflowTask startTask, String transition, Map properties, QName... taskTypes )
{
- List tasks = workflowService.getTasksForWorkflowPath(startTask.getPath().getId());
- if(tasks.size()==1)
- {
- WorkflowTask task = tasks.get(0);
- if(taskTypeMatches(task, taskTypes))
+ // Deleting a person can cancel their invitations. Cancelling invitations can delete inactive persons! So prevent infinite looping here
+ if (TransactionalResourceHelper.getSet(getClass().getName()).add(startTask.getPath().getInstance().getId()))
+ {
+ List tasks = workflowService.getTasksForWorkflowPath(startTask.getPath().getId());
+ if(tasks.size()==1)
{
- if(properties != null)
+ WorkflowTask task = tasks.get(0);
+ if(taskTypeMatches(task, taskTypes))
{
- workflowService.updateTask(task.getId(), properties, null, null);
+ if(properties != null)
+ {
+ workflowService.updateTask(task.getId(), properties, null, null);
+ }
+ workflowService.endTask(task.getId(), transition);
+ return;
}
- workflowService.endTask(task.getId(), transition);
- return;
}
+ // Throw exception if the task not found.
+ Object objs[] = { startTask.getPath().getInstance().getId() };
+ throw new InvitationExceptionUserError("invitation.invite.already_finished", objs);
}
- // Throw exception if the task not found.
- Object objs[] = { startTask.getPath().getInstance().getId() };
- throw new InvitationExceptionUserError("invitation.invite.already_finished", objs);
}
/**
@@ -476,11 +484,19 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
{
ModeratedInvitation invitation = getModeratedInvitation(startTask);
String currentUserName = this.authenticationService.getCurrentUserName();
- if (false == currentUserName.equals(invitation.getInviteeUserName()))
+ if (!AuthenticationUtil.isRunAsUserTheSystemUser())
{
- checkManagerRole(currentUserName, invitation.getResourceType(), invitation.getResourceName());
+ if (false == currentUserName.equals(invitation.getInviteeUserName()))
+ {
+ checkManagerRole(currentUserName, invitation.getResourceType(), invitation.getResourceName());
+ }
+ }
+ // Only proceed with the cancel if the site still exists (the site may have been deleted and invitations may be
+ // getting cancelled in the background)
+ if (this.siteService.getSite(invitation.getResourceName()) != null)
+ {
+ workflowService.cancelWorkflow(invitation.getInviteId());
}
- workflowService.cancelWorkflow(invitation.getInviteId());
return invitation;
}
@@ -488,13 +504,21 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
{
NominatedInvitation invitation = getNominatedInvitation(startTask);
String currentUserName = this.authenticationService.getCurrentUserName();
- if (false == currentUserName.equals(invitation.getInviterUserName()))
+ if (!AuthenticationUtil.isRunAsUserTheSystemUser())
{
- checkManagerRole(currentUserName, invitation.getResourceType(), invitation.getResourceName());
+ if (false == currentUserName.equals(invitation.getInviterUserName()))
+ {
+ checkManagerRole(currentUserName, invitation.getResourceType(), invitation.getResourceName());
+ }
+ }
+ // Only proceed with the cancel if the site still exists (the site may have been deleted and invitations may be
+ // getting cancelled in the background)
+ if (this.siteService.getSite(invitation.getResourceName()) != null)
+ {
+ endInvitation(startTask, WorkflowModelNominatedInvitation.WF_TRANSITION_CANCEL, null,
+ WorkflowModelNominatedInvitation.WF_TASK_INVITE_PENDING,
+ WorkflowModelNominatedInvitation.WF_TASK_ACTIVIT_INVITE_PENDING);
}
- endInvitation(startTask,
- WorkflowModelNominatedInvitation.WF_TRANSITION_CANCEL, null,
- WorkflowModelNominatedInvitation.WF_TASK_INVITE_PENDING, WorkflowModelNominatedInvitation.WF_TASK_ACTIVIT_INVITE_PENDING);
return invitation;
}
@@ -1468,11 +1492,26 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli
}
}
}
+ else if (dictionaryService.isSubClass(type, ContentModel.TYPE_PERSON))
+ {
+ // this is a user being deleted.
+ String userName = (String) nodeService.getProperty(siteRef, ContentModel.PROP_USERNAME);
+ invalidateTasksByUser(userName);
+ }
return null;
}
}, AuthenticationUtil.SYSTEM_USER_NAME);
}
+ private void invalidateTasksByUser(String userName) throws AuthenticationException
+ {
+ List listForInvitee = listPendingInvitationsForInvitee(userName);
+ for (Invitation inv : listForInvitee)
+ {
+ cancel(inv.getInviteId());
+ }
+ }
+
/**
* Generates a description for the workflow
*
diff --git a/source/java/org/alfresco/repo/invitation/InviteHelper.java b/source/java/org/alfresco/repo/invitation/InviteHelper.java
index b87dad16c5..af0a34cee5 100644
--- a/source/java/org/alfresco/repo/invitation/InviteHelper.java
+++ b/source/java/org/alfresco/repo/invitation/InviteHelper.java
@@ -39,6 +39,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.executer.MailActionExecuter;
import org.alfresco.repo.i18n.MessageService;
import org.alfresco.repo.invitation.site.InviteInfo;
@@ -57,6 +58,7 @@ import org.alfresco.service.cmr.invitation.Invitation;
import org.alfresco.service.cmr.invitation.InvitationExceptionForbidden;
import org.alfresco.service.cmr.invitation.InvitationService;
import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.TemplateService;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
@@ -112,6 +114,7 @@ public class InviteHelper implements InitializingBean
private SiteService siteService;
private TemplateService templateService;
private WorkflowService workflowService;
+ private NodeService nodeService;
private InviteSender inviteSender;
@@ -125,6 +128,7 @@ public class InviteHelper implements InitializingBean
this.siteService = serviceRegistry.getSiteService();
this.templateService = serviceRegistry.getTemplateService();
this.workflowService = serviceRegistry.getWorkflowService();
+ this.nodeService = serviceRegistry.getNodeService();
this.inviteSender = new InviteSender(serviceRegistry, repositoryHelper, messageService);
}
@@ -139,7 +143,7 @@ public class InviteHelper implements InitializingBean
{
public Void doWork() throws Exception
{
- if (false==authenticationService.getAuthenticationEnabled(invitee))
+ if (authenticationService.isAuthenticationMutable(invitee))
{
authenticationService.setAuthenticationEnabled(invitee, true);
}
@@ -282,7 +286,7 @@ public class InviteHelper implements InitializingBean
// if invitee's user account is still disabled and there are no pending invites outstanding
// for the invitee, then remove the account and delete the invitee's person node
- if ((authenticationService.authenticationExists(inviteeUserName))
+ if ((authenticationService.isAuthenticationMutable(inviteeUserName))
&& (authenticationService.getAuthenticationEnabled(inviteeUserName) == false)
&& (invitesPending == false))
{
@@ -309,14 +313,17 @@ public class InviteHelper implements InitializingBean
String inviteeUserName = (String) executionVariables.get(wfVarInviteeUserName);
String siteShortName = (String) executionVariables.get(wfVarResourceName);
- String currentUserName = authenticationService.getCurrentUserName();
- String currentUserSiteRole = siteService.getMembersRole(siteShortName, currentUserName);
- if (SiteModel.SITE_MANAGER.equals(currentUserSiteRole)== false)
+ if (!AuthenticationUtil.isRunAsUserTheSystemUser())
{
- // The current user is not the site manager
- String inviteId = (String) executionVariables.get(wfVarWorkflowInstanceId);
- Object[] args = {currentUserName, inviteId, siteShortName};
- throw new InvitationExceptionForbidden(MSG_NOT_SITE_MANAGER, args);
+ String currentUserName = authenticationService.getCurrentUserName();
+ String currentUserSiteRole = siteService.getMembersRole(siteShortName, currentUserName);
+ if (SiteModel.SITE_MANAGER.equals(currentUserSiteRole)== false)
+ {
+ // The current user is not the site manager
+ String inviteId = (String) executionVariables.get(wfVarWorkflowInstanceId);
+ Object[] args = {currentUserName, inviteId, siteShortName};
+ throw new InvitationExceptionForbidden(MSG_NOT_SITE_MANAGER, args);
+ }
}
// Clean up invitee's user account and person node if they are not in use i.e.
@@ -395,7 +402,7 @@ public class InviteHelper implements InitializingBean
// Send
Action emailAction = actionService.createAction("mail");
- emailAction.setParameterValue(MailActionExecuter.PARAM_TO, inviteeUserName);
+ emailAction.setParameterValue(MailActionExecuter.PARAM_TO, nodeService.getProperty(personService.getPerson(inviteeUserName), ContentModel.PROP_EMAIL));
emailAction.setParameterValue(MailActionExecuter.PARAM_FROM, reviewer);
//TODO Localize this.
emailAction.setParameterValue(MailActionExecuter.PARAM_SUBJECT, "Rejected invitation to web site:" + resourceName);
diff --git a/source/java/org/alfresco/repo/jscript/ScriptNode.java b/source/java/org/alfresco/repo/jscript/ScriptNode.java
index 4412fbb5bc..0866c46437 100644
--- a/source/java/org/alfresco/repo/jscript/ScriptNode.java
+++ b/source/java/org/alfresco/repo/jscript/ScriptNode.java
@@ -26,10 +26,12 @@ import java.io.OutputStream;
import java.io.Reader;
import java.io.Serializable;
import java.nio.charset.Charset;
+import java.text.Collator;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -112,6 +114,7 @@ import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.UniqueTag;
import org.mozilla.javascript.Wrapper;
import org.springframework.extensions.surf.util.Content;
+import org.springframework.extensions.surf.util.I18NUtil;
import org.springframework.extensions.surf.util.ParameterCheck;
import org.springframework.extensions.surf.util.URLEncoder;
@@ -425,6 +428,10 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider
// create our Node representation from the NodeRef
children[i] = newInstance(childRefs.get(i).getChildRef(), this.services, this.scope);
}
+
+ // Do a locale-sensitive sort by name
+ sort(children);
+
this.children = Context.getCurrentContext().newArray(this.scope, children);
this.hasChildren = (children.length != 0);
}
@@ -432,6 +439,21 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider
return this.children;
}
+ /**
+ * Performs a locale-sensitive sort by name of a node array
+ * @param nodes the node array
+ */
+ private static void sort(Object[] nodes)
+ {
+ final Collator col = Collator.getInstance(I18NUtil.getLocale());
+ Arrays.sort(nodes, new Comparator(){
+ @Override
+ public int compare(Object o1, Object o2)
+ {
+ return col.compare(((ScriptNode)o1).getName(), ((ScriptNode)o2).getName());
+ }});
+ }
+
/**
* @return true if the Node has children
*/
diff --git a/source/java/org/alfresco/repo/jscript/Search.java b/source/java/org/alfresco/repo/jscript/Search.java
index 31e2324b29..5f8a1e5018 100644
--- a/source/java/org/alfresco/repo/jscript/Search.java
+++ b/source/java/org/alfresco/repo/jscript/Search.java
@@ -46,12 +46,11 @@ import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
+import org.jaxen.saxpath.base.XPathReader;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.springframework.extensions.surf.util.ParameterCheck;
-import com.werken.saxpath.XPathReader;
-
/**
* Search component for use by the ScriptService.
*
diff --git a/source/java/org/alfresco/repo/model/Repository.java b/source/java/org/alfresco/repo/model/Repository.java
index e827085324..181b874d83 100644
--- a/source/java/org/alfresco/repo/model/Repository.java
+++ b/source/java/org/alfresco/repo/model/Repository.java
@@ -263,9 +263,12 @@ public class Repository implements ApplicationContextAware, ApplicationListener,
{
NodeRef person = null;
String currentUserName = AuthenticationUtil.getRunAsUser();
- if (personService.personExists(currentUserName))
+ if (currentUserName != null)
{
- person = personService.getPerson(currentUserName);
+ if (personService.personExists(currentUserName))
+ {
+ person = personService.getPerson(currentUserName);
+ }
}
return person;
}
diff --git a/source/java/org/alfresco/repo/model/filefolder/FilenameFilteringInterceptor.java b/source/java/org/alfresco/repo/model/filefolder/FilenameFilteringInterceptor.java
index 983b9ea3dc..5af9dd1718 100644
--- a/source/java/org/alfresco/repo/model/filefolder/FilenameFilteringInterceptor.java
+++ b/source/java/org/alfresco/repo/model/filefolder/FilenameFilteringInterceptor.java
@@ -19,6 +19,7 @@ package org.alfresco.repo.model.filefolder;
import java.util.Iterator;
+import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.model.filefolder.HiddenAspect.Visibility;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
@@ -110,10 +111,13 @@ public class FilenameFilteringInterceptor implements MethodInterceptor
this.permissionService = permissionService;
}
- private void checkTemporaryAspect(boolean isTemporary, FileInfo fileInfo)
+ private void checkTemporaryAspect(boolean isTemporary, FileInfo fileInfo)
+ {
+ checkTemporaryAspect(isTemporary, fileInfo.getNodeRef());
+ }
+
+ private void checkTemporaryAspect(boolean isTemporary, NodeRef nodeRef)
{
- NodeRef nodeRef = fileInfo.getNodeRef();
-
if(isTemporary)
{
// it matched, so apply the temporary and hidden aspects
@@ -121,7 +125,7 @@ public class FilenameFilteringInterceptor implements MethodInterceptor
if (logger.isDebugEnabled())
{
- logger.debug("Applied temporary marker: " + fileInfo);
+ logger.debug("Applied temporary marker: " + nodeRef);
}
}
else
@@ -135,7 +139,7 @@ public class FilenameFilteringInterceptor implements MethodInterceptor
if (logger.isDebugEnabled())
{
- logger.debug("Removed temporary marker: " + fileInfo);
+ logger.debug("Removed temporary marker: " + nodeRef);
}
}
}
@@ -243,17 +247,37 @@ public class FilenameFilteringInterceptor implements MethodInterceptor
else
{
ret = invocation.proceed();
+
FileInfoImpl fileInfo = (FileInfoImpl)ret;
checkTemporaryAspect(temporaryFiles.isFiltered(filename), fileInfo);
}
}
- else if (methodName.startsWith("rename") ||
- methodName.startsWith("move") ||
- methodName.startsWith("copy"))
+ else if (methodName.startsWith("move"))
+ {
+ Object[] args = invocation.getArguments();
+ NodeRef sourceNodeRef = (NodeRef)args[0];
+ String newName = (String)args[args.length -1];
+
+ if(newName != null)
+ {
+ // Name is changing
+ // check against all the regular expressions
+ checkTemporaryAspect(temporaryFiles.isFiltered(newName), sourceNodeRef);
+ if(getMode() == Mode.ENHANCED)
+ {
+ hiddenAspect.checkHidden(sourceNodeRef, true);
+ }
+ }
+
+ // now do the move
+ ret = invocation.proceed();
+ }
+ else if (methodName.startsWith("copy"))
{
ret = invocation.proceed();
+
FileInfoImpl fileInfo = (FileInfoImpl) ret;
String filename = fileInfo.getName();
@@ -269,6 +293,46 @@ public class FilenameFilteringInterceptor implements MethodInterceptor
{
hiddenAspect.checkHidden(fileInfo, true);
}
+ /*
+ * TODO should these two calls be before the proceed? However its the same problem as create
+ * The node needs to be created before we can add aspects.
+ */
+ }
+ else if (methodName.startsWith("rename"))
+ {
+ Object[] args = invocation.getArguments();
+
+ if(args != null && args.length == 2)
+ {
+ /**
+ * Expecting rename(NodeRef, newName)
+ */
+ String newName = (String)args[1];
+ NodeRef sourceNodeRef = (NodeRef)args[0];
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Checking filename returned by " + methodName + ": " + newName);
+ }
+
+ // check against all the regular expressions
+ checkTemporaryAspect(temporaryFiles.isFiltered(newName), sourceNodeRef);
+ if(getMode() == Mode.ENHANCED)
+ {
+ hiddenAspect.checkHidden(sourceNodeRef, true);
+ }
+
+ ret = invocation.proceed();
+
+ return ret;
+ }
+ else
+ {
+ /**
+ * expected rename(NodeRef, String) - got something else...
+ */
+ throw new AlfrescoRuntimeException("FilenameFilteringInterceptor: unknown rename method");
+ }
}
else
{
diff --git a/source/java/org/alfresco/repo/model/filefolder/HiddenAspect.java b/source/java/org/alfresco/repo/model/filefolder/HiddenAspect.java
index 54a6c88e3c..b2580012b8 100644
--- a/source/java/org/alfresco/repo/model/filefolder/HiddenAspect.java
+++ b/source/java/org/alfresco/repo/model/filefolder/HiddenAspect.java
@@ -559,16 +559,6 @@ public class HiddenAspect
return filter.pattern();
}
- public Set getVisibility()
- {
- return clientVisibility;
- }
-
- public Set getHiddenAttribute()
- {
- return hiddenAttribute;
- }
-
public int getVisibilityMask()
{
return visibilityMask;
diff --git a/source/java/org/alfresco/repo/node/cleanup/TransactionCleanupTest.java b/source/java/org/alfresco/repo/node/cleanup/TransactionCleanupTest.java
new file mode 100644
index 0000000000..e3da0d993c
--- /dev/null
+++ b/source/java/org/alfresco/repo/node/cleanup/TransactionCleanupTest.java
@@ -0,0 +1,242 @@
+package org.alfresco.repo.node.cleanup;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.domain.node.NodeDAO;
+import org.alfresco.repo.domain.node.Transaction;
+import org.alfresco.repo.node.db.DeletedNodeCleanupWorker;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
+import org.alfresco.repo.transaction.RetryingTransactionHelper;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
+import org.alfresco.service.ServiceRegistry;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.cmr.search.ResultSet;
+import org.alfresco.service.cmr.search.SearchService;
+import org.alfresco.service.cmr.security.MutableAuthenticationService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.transaction.TransactionService;
+import org.alfresco.util.ApplicationContextHelper;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.context.ApplicationContext;
+import org.springframework.extensions.webscripts.GUID;
+
+public class TransactionCleanupTest
+{
+ private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
+
+ private TransactionService transactionService;
+ private NodeService nodeService;
+ private SearchService searchService;
+ private MutableAuthenticationService authenticationService;
+ private NodeDAO nodeDAO;
+ private DeletedNodeCleanupWorker worker;
+
+ private NodeRef nodeRef1;
+ private NodeRef nodeRef2;
+ private NodeRef nodeRef3;
+ private NodeRef nodeRef4;
+ private NodeRef nodeRef5;
+ private RetryingTransactionHelper helper;
+
+ @Before
+ public void before()
+ {
+ ServiceRegistry serviceRegistry = (ServiceRegistry)ctx.getBean("ServiceRegistry");
+ this.transactionService = serviceRegistry.getTransactionService();
+ this.authenticationService = (MutableAuthenticationService)ctx.getBean("authenticationService");
+ this.nodeService = serviceRegistry.getNodeService();
+ this.searchService = serviceRegistry.getSearchService();
+ this.nodeDAO = (NodeDAO)ctx.getBean("nodeDAO");
+ this.worker = (DeletedNodeCleanupWorker)ctx.getBean("nodeCleanup.deletedNodeCleanup");
+ this.worker.setMinPurgeAgeDays(0);
+
+ this.helper = transactionService.getRetryingTransactionHelper();
+ authenticationService.authenticate("admin", "admin".toCharArray());
+
+ StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
+ ResultSet resultSet = searchService.query(storeRef, SearchService.LANGUAGE_LUCENE, "PATH:\"/app:company_home\"");
+ final NodeRef companyHome = resultSet.getNodeRef(0);
+ resultSet.close();
+
+ RetryingTransactionHelper.RetryingTransactionCallback createNode = new RetryingTransactionHelper.RetryingTransactionCallback()
+ {
+ @Override
+ public NodeRef execute() throws Throwable
+ {
+ return nodeService.createNode(companyHome, ContentModel.ASSOC_CONTAINS, QName.createQName("test", GUID.generate()), ContentModel.TYPE_CONTENT).getChildRef();
+ }
+ };
+ this.nodeRef1 = helper.doInTransaction(createNode, false, true);
+ this.nodeRef2 = helper.doInTransaction(createNode, false, true);
+ this.nodeRef3 = helper.doInTransaction(createNode, false, true);
+ this.nodeRef4 = helper.doInTransaction(createNode, false, true);
+ this.nodeRef5 = helper.doInTransaction(createNode, false, true);
+ }
+
+ private Map> createTransactions()
+ {
+ Map> txnIds = new HashMap>();
+
+ UpdateNode updateNode1 = new UpdateNode(nodeRef1);
+ UpdateNode updateNode2 = new UpdateNode(nodeRef2);
+ UpdateNode updateNode3 = new UpdateNode(nodeRef3);
+ UpdateNode updateNode4 = new UpdateNode(nodeRef4);
+ UpdateNode updateNode5 = new UpdateNode(nodeRef5);
+
+ List txnIds1 = new ArrayList();
+ List txnIds2 = new ArrayList();
+ List txnIds3 = new ArrayList();
+ List txnIds4 = new ArrayList();
+ List txnIds5 = new ArrayList();
+ txnIds.put(nodeRef1, txnIds1);
+ txnIds.put(nodeRef2, txnIds2);
+ txnIds.put(nodeRef3, txnIds3);
+ txnIds.put(nodeRef4, txnIds4);
+ txnIds.put(nodeRef5, txnIds5);
+
+ for(int i = 0; i < 10; i++)
+ {
+ String txnId1 = helper.doInTransaction(updateNode1, false, true);
+ txnIds1.add(txnId1);
+ if(i == 0)
+ {
+ String txnId2 = helper.doInTransaction(updateNode2, false, true);
+ txnIds2.add(txnId2);
+ }
+ if(i == 2)
+ {
+ String txnId3 = helper.doInTransaction(updateNode3, false, true);
+ txnIds3.add(txnId3);
+ }
+ if(i == 3)
+ {
+ String txnId4 = helper.doInTransaction(updateNode4, false, true);
+ txnIds4.add(txnId4);
+ }
+ if(i == 4)
+ {
+ String txnId5 = helper.doInTransaction(updateNode5, false, true);
+ txnIds5.add(txnId5);
+ }
+ }
+
+ return txnIds;
+ }
+
+ private boolean containsTransaction(List txns, String txnId)
+ {
+ boolean found = false;
+ for(Transaction tx : txns)
+ {
+ if(tx.getChangeTxnId().equals(txnId))
+ {
+ found = true;
+ break;
+ }
+ }
+ return found;
+ }
+
+ @Test
+ public void testPurgeUnusedTransactions() throws Exception
+ {
+ // Execute transactions that update a number of nodes. For nodeRef1, all but the last txn will be unused.
+
+ // run the transaction cleaner to clean up any existing unused transactions
+ worker.doClean();
+
+ long start = System.currentTimeMillis();
+ Long minTxnId = nodeDAO.getMinTxnId();
+
+ final Map> txnIds = createTransactions();
+ final List txnIds1 = txnIds.get(nodeRef1);
+ final List txnIds2 = txnIds.get(nodeRef2);
+ final List txnIds3 = txnIds.get(nodeRef3);
+ final List txnIds4 = txnIds.get(nodeRef4);
+ final List txnIds5 = txnIds.get(nodeRef5);
+
+ // run the transaction cleaner
+ worker.setPurgeSize(5); // small purge size
+ worker.doClean();
+
+ // Get transactions committed after the test started
+ List txns = nodeDAO.getTxnsByCommitTimeAscending(Long.valueOf(start), Long.valueOf(Long.MAX_VALUE), Integer.MAX_VALUE, null, false);
+
+ List expectedUnusedTxnIds = new ArrayList(10);
+ expectedUnusedTxnIds.addAll(txnIds1.subList(0, txnIds1.size() - 1));
+
+ List expectedUsedTxnIds = new ArrayList(5);
+ expectedUsedTxnIds.add(txnIds1.get(txnIds1.size() - 1));
+ expectedUsedTxnIds.addAll(txnIds2);
+ expectedUsedTxnIds.addAll(txnIds3);
+ expectedUsedTxnIds.addAll(txnIds4);
+ expectedUsedTxnIds.addAll(txnIds5);
+
+ // check that the correct transactions have been purged i.e. all except the last one to update the node
+ // i.e. in this case, all but the last one in txnIds1
+ int numFoundUnusedTxnIds = 0;
+ for(String txnId : expectedUnusedTxnIds)
+ {
+ if(!containsTransaction(txns, txnId))
+ {
+ numFoundUnusedTxnIds++;
+ }
+ else if(txnIds1.contains(txnId))
+ {
+ fail("Unused transaction(s) were not purged: " + txnId);
+ }
+ }
+ assertEquals(9, numFoundUnusedTxnIds);
+
+ // check that the correct transactions remain i.e. all those in txnIds2, txnIds3, txnIds4 and txnIds5
+ int numFoundUsedTxnIds = 0;
+ for(String txnId : expectedUsedTxnIds)
+ {
+ if(containsTransaction(txns, txnId))
+ {
+ numFoundUsedTxnIds++;
+ }
+ }
+
+ assertEquals(5, numFoundUsedTxnIds);
+
+ List txnsUnused = nodeDAO.getTxnsUnused(minTxnId, Long.MAX_VALUE, Integer.MAX_VALUE);
+ assertEquals(0, txnsUnused.size());
+ }
+
+ @After
+ public void after()
+ {
+ ApplicationContextHelper.closeApplicationContext();
+ }
+
+ private class UpdateNode implements RetryingTransactionCallback
+ {
+ private NodeRef nodeRef;
+
+ UpdateNode(NodeRef nodeRef)
+ {
+ this.nodeRef = nodeRef;
+ }
+
+ @Override
+ public String execute() throws Throwable
+ {
+ nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, GUID.generate());
+ String txnId = AlfrescoTransactionSupport.getTransactionId();
+
+ return txnId;
+ }
+ };
+}
diff --git a/source/java/org/alfresco/repo/node/db/DeletedNodeCleanupWorker.java b/source/java/org/alfresco/repo/node/db/DeletedNodeCleanupWorker.java
index f06094ad53..86d95f79d7 100644
--- a/source/java/org/alfresco/repo/node/db/DeletedNodeCleanupWorker.java
+++ b/source/java/org/alfresco/repo/node/db/DeletedNodeCleanupWorker.java
@@ -22,10 +22,10 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import org.alfresco.repo.domain.node.NodeDAO;
import org.alfresco.repo.node.cleanup.AbstractNodeCleanupWorker;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
-import org.apache.commons.lang.mutable.MutableLong;
/**
* Cleans up deleted nodes and dangling transactions that are old enough.
@@ -37,6 +37,10 @@ public class DeletedNodeCleanupWorker extends AbstractNodeCleanupWorker
{
private long minPurgeAgeMs;
+ // Unused transactions will be purged in chunks determined by commit time boundaries. 'index.tracking.purgeSize' specifies the size
+ // of the chunk (in ms). Default is a couple of hours.
+ private int purgeSize = 7200000; // ms
+
/**
* Default constructor
*/
@@ -78,6 +82,16 @@ public class DeletedNodeCleanupWorker extends AbstractNodeCleanupWorker
}
/**
+ * Set the purge transaction block size. This determines how many unused transactions are purged in one go.
+ *
+ * @param txnBlockSize
+ */
+ public void setPurgeSize(int purgeSize)
+ {
+ this.purgeSize = purgeSize;
+ }
+
+ /**
* Cleans up deleted nodes that are older than the given minimum age.
*
* @param minAge the minimum age of a transaction or deleted node
@@ -139,7 +153,6 @@ public class DeletedNodeCleanupWorker extends AbstractNodeCleanupWorker
return results;
}
- private static final int TXN_PURGE_BATCH_SIZE = 50;
/**
* Cleans up unused transactions that are older than the given minimum age.
*
@@ -153,50 +166,37 @@ public class DeletedNodeCleanupWorker extends AbstractNodeCleanupWorker
return Collections.emptyList();
}
final List results = new ArrayList(100);
- final MutableLong minTxnId = new MutableLong(0L);
final long maxCommitTime = System.currentTimeMillis() - minAge;
- RetryingTransactionCallback purgeTxnsCallback = new RetryingTransactionCallback()
- {
- public Integer execute() throws Throwable
- {
- final List txnIds = nodeDAO.getTxnsUnused(
- minTxnId.longValue(),
- maxCommitTime,
- TXN_PURGE_BATCH_SIZE);
- for (Long txnId : txnIds)
- {
- nodeDAO.purgeTxn(txnId);
- // Update the min node ID for the next query
- if (txnId.longValue() > minTxnId.longValue())
- {
- minTxnId.setValue(txnId.longValue());
- }
- }
- return txnIds.size();
- }
- };
+ long fromCommitTime = nodeDAO.getMinUnusedTxnCommitTime().longValue();
+
+ // delete unused transactions in batches of size 'purgeTxnBlockSize'
while (true)
{
// Ensure we keep the lock
refreshLock();
-
+
RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
txnHelper.setMaxRetries(5); // Limit number of retries
txnHelper.setRetryWaitIncrementMs(1000); // 1 second to allow other cleanups time to get through
- // Get nodes to delete
- Integer purgeCount = new Integer(0);
- // Purge nodes
+
+ long toCommitTime = fromCommitTime + purgeSize;
+ if(toCommitTime >= maxCommitTime)
+ {
+ toCommitTime = maxCommitTime;
+ }
+
+ // Purge transactions
try
{
- purgeCount = txnHelper.doInTransaction(purgeTxnsCallback, false, true);
- if (purgeCount.intValue() > 0)
+ DeleteTransactionsCallback purgeTxnsCallback = new DeleteTransactionsCallback(nodeDAO, fromCommitTime, toCommitTime);
+ long purgeCount = txnHelper.doInTransaction(purgeTxnsCallback, false, true);
+ if (purgeCount > 0)
{
String msg =
"Purged old txns: \n" +
- " Min txn ID: " + minTxnId.longValue() + "\n" +
- " Batch size: " + TXN_PURGE_BATCH_SIZE + "\n" +
- " Max commit time: " + maxCommitTime + "\n" +
+ " From commit time (ms): " + fromCommitTime + "\n" +
+ " To commit time (ms): " + toCommitTime + "\n" +
" Purge count: " + purgeCount;
results.add(msg);
}
@@ -206,9 +206,8 @@ public class DeletedNodeCleanupWorker extends AbstractNodeCleanupWorker
String msg =
"Failed to purge txns." +
" Set log level to WARN for this class to get exception log: \n" +
- " Min txn ID: " + minTxnId.longValue() + "\n" +
- " Batch size: " + TXN_PURGE_BATCH_SIZE + "\n" +
- " Max commit time: " + maxCommitTime + "\n" +
+ " From commit time: " + fromCommitTime + "\n" +
+ " To commit time (ms): " + toCommitTime + "\n" +
" Error: " + e.getMessage();
// It failed; do a full log in WARN mode
if (logger.isWarnEnabled())
@@ -222,12 +221,37 @@ public class DeletedNodeCleanupWorker extends AbstractNodeCleanupWorker
results.add(msg);
break;
}
- if (purgeCount.intValue() == 0)
+
+ fromCommitTime += purgeSize;
+ if(fromCommitTime >= maxCommitTime)
{
- break;
+ break;
}
}
// Done
return results;
}
+
+ /*
+ * Delete a block of unused transactions
+ */
+ private static class DeleteTransactionsCallback implements RetryingTransactionCallback
+ {
+ private NodeDAO nodeDAO;
+ private long fromCommitTime;
+ private long toCommitTime;
+
+ DeleteTransactionsCallback(NodeDAO nodeDAO, long fromCommitTime, long toCommitTime)
+ {
+ this.nodeDAO = nodeDAO;
+ this.fromCommitTime = fromCommitTime;
+ this.toCommitTime = toCommitTime;
+ }
+
+ public Long execute() throws Throwable
+ {
+ long count = nodeDAO.deleteTxnsUnused(fromCommitTime, toCommitTime);
+ return count;
+ }
+ }
}
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/node/index/AbstractReindexComponent.java b/source/java/org/alfresco/repo/node/index/AbstractReindexComponent.java
index c276195615..bbcc3b1708 100644
--- a/source/java/org/alfresco/repo/node/index/AbstractReindexComponent.java
+++ b/source/java/org/alfresco/repo/node/index/AbstractReindexComponent.java
@@ -21,7 +21,7 @@ package org.alfresco.repo.node.index;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
-import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -53,6 +53,7 @@ import org.alfresco.repo.transaction.TransactionServiceImpl;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
@@ -62,7 +63,6 @@ import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.ResultSetRow;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
-import org.alfresco.util.Pair;
import org.alfresco.util.ParameterCheck;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.VmShutdownListener;
@@ -70,6 +70,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
+import org.springframework.dao.ConcurrencyFailureException;
/**
* Abstract helper for reindexing.
@@ -278,6 +279,31 @@ public abstract class AbstractReindexComponent implements IndexRecovery
*
*/
protected abstract void reindexImpl();
+
+ /**
+ * To allow for possible 'read committed' behaviour in some databases, where a node that previously existed during a
+ * transaction can disappear from existence, we treat InvalidNodeRefExceptions as concurrency conditions.
+ */
+ protected T2 doInRetryingTransaction(final RetryingTransactionCallback callback, boolean isReadThrough)
+ {
+ return transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback()
+ {
+ @Override
+ public T2 execute() throws Throwable
+ {
+ try
+ {
+ return callback.execute();
+ }
+ catch (InvalidNodeRefException e)
+ {
+ // Turn InvalidNodeRefExceptions into retryable exceptions.
+ throw new ConcurrencyFailureException("Possible cache integrity issue during reindexing", e);
+ }
+
+ }
+ }, true, isReadThrough);
+ }
/**
* If this object is currently busy, then it just nothing
@@ -316,7 +342,7 @@ public abstract class AbstractReindexComponent implements IndexRecovery
};
if (requireTransaction())
{
- transactionService.getRetryingTransactionHelper().doInTransaction(reindexWork, true);
+ doInRetryingTransaction(reindexWork, false);
}
else
{
@@ -611,14 +637,14 @@ public abstract class AbstractReindexComponent implements IndexRecovery
public InIndex isTxnPresentInIndex(final Transaction txn, final boolean readThrough)
{
- return transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback()
+ return doInRetryingTransaction(new RetryingTransactionCallback()
{
@Override
public InIndex execute() throws Throwable
{
return isTxnPresentInIndex(txn);
}
- }, true, readThrough);
+ }, readThrough);
}
/**
@@ -758,7 +784,7 @@ public abstract class AbstractReindexComponent implements IndexRecovery
*
* @throws ReindexTerminatedException if the VM is shutdown during the reindex
*/
- protected void reindexTransaction(final long txnId, ReindexNodeCallback callback, boolean isFull)
+ protected void reindexTransaction(final long txnId, final ReindexNodeCallback callback, final boolean isFull)
{
ParameterCheck.mandatory("txnId", txnId);
if (logger.isDebugEnabled())
@@ -772,91 +798,153 @@ public abstract class AbstractReindexComponent implements IndexRecovery
// The indexer will 'read through' to the latest database changes for the rest of this transaction
indexer.setReadThrough(true);
+
+ // Compile the complete set of transaction changes - we need to 'read through' here too
+ final Collection deletedNodes = new LinkedList();
+ final Collection updatedNodes = new LinkedList();
+ final Collection createdNodes = new LinkedList();
+ final Collection deletedParents = new LinkedList();
+ final Collection addedParents = new LinkedList();
- // get the node references pertinent to the transaction - We need to 'read through' here too
- List> nodePairs = transactionService.getRetryingTransactionHelper().doInTransaction(
- new RetryingTransactionCallback>>()
- {
-
- @Override
- public List> execute() throws Throwable
- {
- List nodeStatuses = nodeDAO.getTxnChanges(txnId);
- List> nodePairs = new ArrayList>(nodeStatuses.size());
- for (NodeRef.Status nodeStatus : nodeStatuses)
- {
- ChildAssociationRef parent = nodeStatus.isDeleted() ? null : nodeService.getPrimaryParent(nodeStatus.getNodeRef());
- nodePairs.add(new Pair(nodeStatus, parent));
- }
- return nodePairs;
- }
- }, true, true);
-
- // reindex each node
- int nodeCount = 0;
- for (Pair nodePair: nodePairs)
+ doInRetryingTransaction(new RetryingTransactionCallback()
{
- NodeRef.Status nodeStatus = nodePair.getFirst();
- NodeRef nodeRef = nodeStatus.getNodeRef();
-
- if (nodeStatus.isDeleted()) // node deleted
+ @Override
+ public Void execute() throws Throwable
{
- if(isFull == false)
+ // process the node references pertinent to the transaction
+ List nodeStatuses = nodeDAO.getTxnChanges(txnId);
+ int nodeCount = 0;
+ for (NodeRef.Status nodeStatus : nodeStatuses)
{
- // only the child node ref is relevant
- ChildAssociationRef assocRef = new ChildAssociationRef(
- ContentModel.ASSOC_CHILDREN,
- null,
- null,
- nodeRef);
- indexer.deleteNode(assocRef);
- if (logger.isDebugEnabled())
+ NodeRef nodeRef = nodeStatus.getNodeRef();
+ if (isFull)
{
- logger.debug("DELETE: " + nodeRef);
- }
- }
- }
- else // node created
- {
- if(isFull)
- {
- ChildAssociationRef assocRef = new ChildAssociationRef(
- ContentModel.ASSOC_CHILDREN,
- null,
- null,
- nodeRef);
- indexer.createNode(assocRef);
- if (logger.isDebugEnabled())
- {
- logger.debug("CREATE: " + nodeRef);
- }
- }
- else
- {
- // reindex - force a cascade reindex if possible (to account for a possible move)
- ChildAssociationRef parent = nodePair.getSecond();
- if (parent == null)
- {
- indexer.updateNode(nodeRef);
- if (logger.isDebugEnabled())
+ if (!nodeStatus.isDeleted())
{
- logger.debug("UPDATE: " + nodeRef);
+ createdNodes.add(new ChildAssociationRef(ContentModel.ASSOC_CHILDREN, null, null, nodeRef));
}
}
+ else if (nodeStatus.isDeleted()) // node deleted
+ {
+ // only the child node ref is relevant
+ deletedNodes.add(new ChildAssociationRef(ContentModel.ASSOC_CHILDREN, null, null, nodeRef));
+ }
else
{
- indexer.createChildRelationship(parent);
- if (logger.isDebugEnabled())
- {
- logger.debug("MOVE: " + nodeRef + ", " + parent);
- }
+ // Do a DB / index comparision to determine indexing operations required
+ indexer.detectNodeChanges(nodeRef, searcher, addedParents, deletedParents, createdNodes,
+ updatedNodes);
+ }
+
+ // Check for VM shutdown every 100 nodes
+ if (++nodeCount % 100 == 0 && isShuttingDown())
+ {
+ // We can't fail gracefully and run the risk of committing a half-baked transaction
+ logger.info("Reindexing of transaction " + txnId + " terminated by VM shutdown.");
+ throw new ReindexTerminatedException();
}
}
+ return null;
}
+ }, true);
+
+ int nodeCount = 0;
+ for (ChildAssociationRef deletedNode: deletedNodes)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("DELETE: " + deletedNode.getChildRef());
+ }
+ indexer.deleteNode(deletedNode);
+
// Make the callback
if (callback != null)
{
- callback.reindexedNode(nodeRef);
+ callback.reindexedNode(deletedNode.getChildRef());
+ }
+ // Check for VM shutdown every 100 nodes
+ if (++nodeCount % 100 == 0 && isShuttingDown())
+ {
+ // We can't fail gracefully and run the risk of committing a half-baked transaction
+ logger.info("Reindexing of transaction " + txnId + " terminated by VM shutdown.");
+ throw new ReindexTerminatedException();
+ }
+ }
+ for (NodeRef updatedNode: updatedNodes)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("UPDATE: " + updatedNode);
+ }
+ indexer.updateNode(updatedNode);
+
+ // Make the callback
+ if (callback != null)
+ {
+ callback.reindexedNode(updatedNode);
+ }
+ // Check for VM shutdown every 100 nodes
+ if (++nodeCount % 100 == 0 && isShuttingDown())
+ {
+ // We can't fail gracefully and run the risk of committing a half-baked transaction
+ logger.info("Reindexing of transaction " + txnId + " terminated by VM shutdown.");
+ throw new ReindexTerminatedException();
+ }
+ }
+ for (ChildAssociationRef createdNode: createdNodes)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("CREATE: " + createdNode.getChildRef());
+ }
+ indexer.createNode(createdNode);
+
+ // Make the callback
+ if (callback != null)
+ {
+ callback.reindexedNode(createdNode.getChildRef());
+ }
+ // Check for VM shutdown every 100 nodes
+ if (++nodeCount % 100 == 0 && isShuttingDown())
+ {
+ // We can't fail gracefully and run the risk of committing a half-baked transaction
+ logger.info("Reindexing of transaction " + txnId + " terminated by VM shutdown.");
+ throw new ReindexTerminatedException();
+ }
+ }
+ for (ChildAssociationRef deletedParent: deletedParents)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("UNLINK: " + deletedParent);
+ }
+ indexer.deleteChildRelationship(deletedParent);
+
+ // Make the callback
+ if (callback != null)
+ {
+ callback.reindexedNode(deletedParent.getChildRef());
+ }
+ // Check for VM shutdown every 100 nodes
+ if (++nodeCount % 100 == 0 && isShuttingDown())
+ {
+ // We can't fail gracefully and run the risk of committing a half-baked transaction
+ logger.info("Reindexing of transaction " + txnId + " terminated by VM shutdown.");
+ throw new ReindexTerminatedException();
+ }
+ }
+ for (ChildAssociationRef addedParent: addedParents)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("LINK: " + addedParents);
+ }
+ indexer.createChildRelationship(addedParent);
+
+ // Make the callback
+ if (callback != null)
+ {
+ callback.reindexedNode(addedParent.getChildRef());
}
// Check for VM shutdown every 100 nodes
if (++nodeCount % 100 == 0 && isShuttingDown())
@@ -999,7 +1087,7 @@ public abstract class AbstractReindexComponent implements IndexRecovery
loggerOnThread.debug(msg);
}
// Do the work
- transactionService.getRetryingTransactionHelper().doInTransaction(reindexCallback, true, true);
+ doInRetryingTransaction(reindexCallback, true);
}
catch (ReindexTerminatedException e)
{
@@ -1200,7 +1288,7 @@ public abstract class AbstractReindexComponent implements IndexRecovery
return null;
}
};
- transactionService.getRetryingTransactionHelper().doInTransaction(reindexCallback, true, true);
+ doInRetryingTransaction(reindexCallback, true);
return;
}
diff --git a/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponent.java b/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponent.java
index cfe6d49d38..922f197253 100644
--- a/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponent.java
+++ b/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponent.java
@@ -19,6 +19,7 @@
package org.alfresco.repo.node.index;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
@@ -185,17 +186,12 @@ public class FullIndexRecoveryComponent extends AbstractReindexComponent
transactionService.setAllowWrite(false, vetoName);
}
-
- List startTxns = nodeDAO.getTxnsByCommitTimeAscending(
- Long.MIN_VALUE, Long.MAX_VALUE, 1000, null, false);
+ List startTxns = getTxnsByCommitTimeWindowAscending(MIN_SAMPLE_TIME, 1000);
InIndex startAllPresent = areTxnsInStartSample(startTxns);
-
- List endTxns = nodeDAO.getTxnsByCommitTimeDescending(
- Long.MIN_VALUE, Long.MAX_VALUE, 1000, null, false);
+ List endTxns = getTxnsByCommitTimeWindowDescending(MIN_SAMPLE_TIME, 1000);
InIndex endAllPresent = areAllTxnsInEndSample(endTxns);
-
-
+
// check the level of cover required
switch (recoveryMode)
{
@@ -231,6 +227,135 @@ public class FullIndexRecoveryComponent extends AbstractReindexComponent
}
+ /**
+ * List transactions up to the specified amount using a sliding time based window.
+ * This will create smaller result sets which circumvents performance problems using
+ * sql LIMIT on some jdbc drivers and databases.
+ *
+ * @param windowSize the size of collection window in milliseconds.
+ * @param count the number of transctions to attempt to collect
+ * @return returns a list of transactions
+ */
+ private List getTxnsByCommitTimeWindowDescending(final long minWindowSize, final int count) {
+ if (minWindowSize == 0 || count == 0)
+ {
+ return Collections.emptyList();
+ }
+ final long startTxns = nodeDAO.getMinTxnCommitTime();
+ final long endTxns = nodeDAO.getMaxTxnCommitTime() + 1;
+
+ List list = new ArrayList(count);
+ long toTimeExclusive = endTxns;
+ long window = minWindowSize;
+ //start at the end move backward by sample increment
+ while (true)
+ {
+ //slide window start backward by sample increment (windowsSize is a negative number)
+ long fromTimeInclusive = toTimeExclusive - window;
+ if (fromTimeInclusive <= startTxns)
+ {
+ //if we have moved back past the first transaction set to zero
+ fromTimeInclusive = startTxns;
+ }
+ List txns = nodeDAO.getTxnsByCommitTimeDescending(
+ fromTimeInclusive, toTimeExclusive, count, null, false);
+
+ for (Transaction txn : txns)
+ {
+ list.add(txn);
+ //bail out if we have enough
+ if (list.size() >= count) break;
+ }
+ //bail out of main loop if we have enough or there are no more transactions
+ if (list.size() >= count || fromTimeInclusive == startTxns)
+ {
+ break;
+ }
+ //calculate new window size
+ if (list.size() == 0)
+ {
+ window = minWindowSize;
+ }
+ else
+ {
+ //calculate rate of transactions found (start to end of current window)
+ window = Math.max(minWindowSize, count
+ * (endTxns - fromTimeInclusive) / list.size());
+ }
+
+ //slide window end back to last inclusive time window
+ toTimeExclusive = fromTimeInclusive;
+ }
+ return list;
+ }
+
+ /**
+ * List transactions up to the specified amount using a sliding time based window.
+ * This will create smaller result sets which circumvents performance problems using
+ * sql LIMIT on some jdbc drivers and databases.
+ *
+ * @param windowSize the size of collection window in milliseconds.
+ * @param count the number of transctions to attempt to collect
+ * @return returns a list of transactions
+ */
+ private List getTxnsByCommitTimeWindowAscending(final long minWindowSize, final int count) {
+ if (minWindowSize == 0 || count == 0)
+ {
+ return Collections.emptyList();
+ }
+
+ //TODO: these return null for no transactions
+ final long startTxns = nodeDAO.getMinTxnCommitTime();
+ final long endTxns = nodeDAO.getMaxTxnCommitTime() + 1;
+
+ long window = minWindowSize;
+ List list = new ArrayList(count);
+ if ( window > 0)
+ {
+ long fromTimeInclusive = startTxns;
+ //start at the beginning move forward by sample increment
+ while (true)
+ {
+ //slide window end out from window start to window start + increment
+ long toTimeExclusive = fromTimeInclusive + window;
+ //if window size pushes window beyond end transaction then clip it to end
+ if (toTimeExclusive >= endTxns) {
+ toTimeExclusive = endTxns;
+ }
+ List txns = nodeDAO.getTxnsByCommitTimeAscending(
+ fromTimeInclusive, toTimeExclusive, count, null, false);
+
+ for (Transaction txn : txns)
+ {
+ list.add(txn);
+ //bail out if we have enough
+ if (list.size() >= count) break;
+ }
+ //bail out of main loop if we have enough or there are no more transactions
+ if (list.size() >= count || toTimeExclusive >= endTxns)
+ {
+ break;
+ }
+ //calculate new window size
+ if (list.size() == 0)
+ {
+ window = minWindowSize;
+ }
+ else
+ {
+ //calculate rate of transactions found (start to end of current window)
+ window = Math.max(minWindowSize, count
+ * (toTimeExclusive - startTxns) / list.size());
+ }
+
+ //slide window start forward to last exclusive time window
+ fromTimeInclusive = toTimeExclusive;
+ }
+ }
+ return list;
+ }
+
+
/**
* @return Returns false if any one of the transactions aren't in the index.
*/
diff --git a/source/java/org/alfresco/repo/node/index/IndexTransactionTrackerTest.java b/source/java/org/alfresco/repo/node/index/IndexTransactionTrackerTest.java
index eaaee714b6..150f1aff1a 100644
--- a/source/java/org/alfresco/repo/node/index/IndexTransactionTrackerTest.java
+++ b/source/java/org/alfresco/repo/node/index/IndexTransactionTrackerTest.java
@@ -29,6 +29,7 @@ import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
import org.alfresco.repo.search.Indexer;
import org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
+import org.alfresco.repo.tenant.TenantService;
import org.alfresco.repo.transaction.TransactionServiceImpl;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry;
@@ -90,6 +91,7 @@ public class IndexTransactionTrackerTest extends TestCase
indexTracker.setThreadPoolExecutor(threadPoolExecutor);
indexTracker.setSearcher(searchService);
indexTracker.setTransactionService((TransactionServiceImpl)transactionService);
+ indexTracker.setTenantService((TenantService) ctx.getBean("tenantService"));
// authenticate
authenticationComponent.setSystemUserAsCurrentUser();
diff --git a/source/java/org/alfresco/repo/search/Indexer.java b/source/java/org/alfresco/repo/search/Indexer.java
index c21111b934..6068a2983d 100644
--- a/source/java/org/alfresco/repo/search/Indexer.java
+++ b/source/java/org/alfresco/repo/search/Indexer.java
@@ -18,9 +18,13 @@
*/
package org.alfresco.repo.search;
+import java.util.Collection;
+
+import org.alfresco.repo.search.impl.lucene.LuceneIndexException;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.cmr.search.SearchService;
/**
* This interface abstracts how indexing is used from within the node service
@@ -103,6 +107,27 @@ public interface Indexer
*/
public void deleteChildRelationship(ChildAssociationRef relationshipRef);
+ /**
+ * Does a database vs index comparison for the given created/updated/renamed/referenced nodeRef in order to
+ * determine the set of indexing operations required
+ *
+ * @param nodeRef
+ * the nodeRef to process
+ * @param searcher
+ * searcher to query the indexes
+ * @param addedParents
+ * set to add new secondary parent associations to
+ * @param deletedParents
+ * set to add removed secondary parent associations to
+ * @param createdNodes
+ * set to add created nodes to
+ * @param updatedNodes
+ * set to add updated nodes to
+ */
+ public void detectNodeChanges(NodeRef nodeRef, SearchService searcher,
+ Collection addedParents, Collection deletedParents,
+ Collection createdNodes, Collection updatedNodes);
+
/**
* Delete the index for a store
* @param storeRef
diff --git a/source/java/org/alfresco/repo/search/IndexerComponent.java b/source/java/org/alfresco/repo/search/IndexerComponent.java
index 50de7a0059..5b0ec5b1b9 100644
--- a/source/java/org/alfresco/repo/search/IndexerComponent.java
+++ b/source/java/org/alfresco/repo/search/IndexerComponent.java
@@ -18,11 +18,14 @@
*/
package org.alfresco.repo.search;
+import java.util.Collection;
+
import org.alfresco.repo.service.StoreRedirectorProxyFactory;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.cmr.search.SearchService;
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
import org.springframework.context.ApplicationEvent;
@@ -118,6 +121,14 @@ public class IndexerComponent extends AbstractLifecycleBean implements Indexer
Indexer indexer = getIndexer(relationshipRef.getChildRef().getStoreRef());
indexer.deleteChildRelationship(relationshipRef);
}
+
+ public void detectNodeChanges(NodeRef nodeRef, SearchService searcher,
+ Collection addedParents, Collection deletedParents,
+ Collection createdNodes, Collection updatedNodes)
+ {
+ Indexer indexer = getIndexer(nodeRef.getStoreRef());
+ indexer.detectNodeChanges(nodeRef, searcher, addedParents, deletedParents, createdNodes, updatedNodes);
+ }
/* (non-Javadoc)
* @see org.alfresco.repo.search.Indexer#deleteIndex(org.alfresco.service.cmr.repository.StoreRef)
diff --git a/source/java/org/alfresco/repo/search/NodeServiceXPath.java b/source/java/org/alfresco/repo/search/NodeServiceXPath.java
index faaf9b4155..312cb14a99 100644
--- a/source/java/org/alfresco/repo/search/NodeServiceXPath.java
+++ b/source/java/org/alfresco/repo/search/NodeServiceXPath.java
@@ -75,7 +75,6 @@ import org.jaxen.function.TrueFunction;
import org.jaxen.function.ext.EndsWithFunction;
import org.jaxen.function.ext.EvaluateFunction;
import org.jaxen.function.ext.LowerFunction;
-import org.jaxen.function.ext.MatrixConcatFunction;
import org.jaxen.function.ext.UpperFunction;
import org.jaxen.function.xslt.DocumentFunction;
@@ -602,6 +601,9 @@ public class NodeServiceXPath extends BaseXPath
registerFunction("", // namespace URI
"document", new DocumentFunction());
+
+ registerFunction("", // namespace URI
+ "ends-with", new EndsWithFunction());
registerFunction("", // namespace URI
"false", new FalseFunction());
@@ -673,9 +675,6 @@ public class NodeServiceXPath extends BaseXPath
// extension functions should go into a namespace, but which one?
// for now, keep them in default namespace to not break any code
- registerFunction("", // namespace URI
- "matrix-concat", new MatrixConcatFunction());
-
registerFunction("", // namespace URI
"evaluate", new EvaluateFunction());
diff --git a/source/java/org/alfresco/repo/search/impl/NoActionIndexer.java b/source/java/org/alfresco/repo/search/impl/NoActionIndexer.java
index bb7f4dbdf1..3b98dbe428 100644
--- a/source/java/org/alfresco/repo/search/impl/NoActionIndexer.java
+++ b/source/java/org/alfresco/repo/search/impl/NoActionIndexer.java
@@ -18,10 +18,13 @@
*/
package org.alfresco.repo.search.impl;
+import java.util.Collection;
+
import org.alfresco.repo.search.Indexer;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.cmr.search.SearchService;
/**
* A no action indexer - the indexing is done automatically along with
@@ -68,6 +71,13 @@ public class NoActionIndexer implements Indexer
{
return;
}
+
+ public void detectNodeChanges(NodeRef nodeRef, SearchService searcher,
+ Collection addedParents, Collection deletedParents,
+ Collection createdNodes, Collection updatedNodes)
+ {
+ return;
+ }
/* (non-Javadoc)
* @see org.alfresco.repo.search.Indexer#deleteIndex(org.alfresco.service.cmr.repository.StoreRef)
diff --git a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java
index ebb01f8298..2054c48b30 100644
--- a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java
+++ b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java
@@ -79,6 +79,10 @@ import org.alfresco.service.cmr.repository.TransformationOptions;
import org.alfresco.service.cmr.repository.Path.ChildAssocElement;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.repository.datatype.TypeConversionException;
+import org.alfresco.service.cmr.search.ResultSet;
+import org.alfresco.service.cmr.search.ResultSetRow;
+import org.alfresco.service.cmr.search.SearchParameters;
+import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.CachingDateFormat;
import org.alfresco.util.EqualsHelper;
@@ -345,10 +349,10 @@ public class ADMLuceneIndexerImpl extends AbstractLuceneIndexerImpl imp
throw new LuceneIndexException(event + " failed - node is not in the required store");
}
final NodeRef parentRef = relationshipRef.getParentRef();
- final NodeRef childRef = relationshipRef.getChildRef();
- if (parentRef != null)
+ final NodeRef childRef = relationshipRef.getChildRef();
+ if (parentRef != null && !relationshipRef.isPrimary())
{
- // If the child has a lot of secondary parents, its cheaper to cascade reindex its parent
+ // If a SECONDARY child has a lot of secondary parents, its cheaper to cascade reindex the secondary parent
// rather than itself
if (doInReadthroughTransaction(new RetryingTransactionCallback()
{
@@ -648,6 +652,80 @@ public class ADMLuceneIndexerImpl extends AbstractLuceneIndexerImpl imp
}
}
+ public void detectNodeChanges(NodeRef nodeRef, SearchService searcher,
+ Collection addedParents, Collection deletedParents,
+ Collection createdNodes, Collection updatedNodes) throws LuceneIndexException
+ {
+ boolean nodeExisted = false;
+ boolean relationshipsChanged = false;
+
+ ResultSet results = null;
+ SearchParameters sp = new SearchParameters();
+ sp.setLanguage(SearchService.LANGUAGE_LUCENE);
+ sp.addStore(nodeRef.getStoreRef());
+ try
+ {
+ sp.setQuery("ID:" + LuceneQueryParser.escape(nodeRef.toString()));
+ results = searcher.query(sp);
+ for (ResultSetRow row : results)
+ {
+ nodeExisted = true;
+ Document document = ((LuceneResultSetRow) row).getDocument();
+ Field qname = document.getField("QNAME");
+ if (qname == null)
+ {
+ continue;
+ }
+ Collection> allParents = getAllParents(nodeRef, nodeService.getProperties(nodeRef));
+ Set dbParents = new HashSet(allParents.size() * 2);
+ for (Pair pair : allParents)
+ {
+ ChildAssociationRef qNameRef = tenantService.getName(pair.getFirst());
+ if ((qNameRef != null) && (qNameRef.getParentRef() != null) && (qNameRef.getQName() != null))
+ {
+ dbParents.add(new ChildAssociationRef(ContentModel.ASSOC_CHILDREN, qNameRef.getParentRef(), qNameRef.getQName(), qNameRef.getChildRef()));
+ }
+ }
+
+ Field[] parents = document.getFields("PARENT");
+ String[] qnames = qname.stringValue().split(";/");
+ Set addedParentsSet = new HashSet(dbParents);
+ for (int i=0; i createDocumentsImpl(final String stringNodeRef, FTSStatus ftsStatus,
boolean indexAllProperties, boolean includeDirectoryDocuments, final boolean cascade,
final Set pathsProcessedSinceFlush,
diff --git a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneSearcherImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneSearcherImpl.java
index 5eb003e7c0..40b4d0899d 100644
--- a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneSearcherImpl.java
+++ b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneSearcherImpl.java
@@ -90,9 +90,8 @@ import org.apache.lucene.search.Query;
import org.apache.lucene.search.Searcher;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
-import org.saxpath.SAXPathException;
-
-import com.werken.saxpath.XPathReader;
+import org.jaxen.saxpath.SAXPathException;
+import org.jaxen.saxpath.base.XPathReader;
/**
* The Lucene implementation of Searcher At the moment we support only lucene based queries. TODO: Support for other
diff --git a/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerImpl.java
index 0cfdbfcb3c..c51e5352fe 100644
--- a/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerImpl.java
+++ b/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerImpl.java
@@ -28,6 +28,7 @@ import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -82,6 +83,7 @@ import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.TransformationOptions;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.repository.datatype.TypeConversionException;
+import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.CachingDateFormat;
import org.alfresco.util.EqualsHelper;
@@ -1591,6 +1593,14 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl
throw new LuceneIndexException("Create node failed", e);
}
}
+
+ @Override
+ public void detectNodeChanges(NodeRef nodeRef, SearchService searcher,
+ Collection addedParents, Collection deletedParents,
+ Collection createdNodes, Collection updatedNodes)
+ {
+ updatedNodes.add(nodeRef);
+ }
public void updateNode(NodeRef nodeRef)
{
diff --git a/source/java/org/alfresco/repo/search/impl/lucene/DebugXPathHandler.java b/source/java/org/alfresco/repo/search/impl/lucene/DebugXPathHandler.java
index 78f4355b27..0a6092a291 100644
--- a/source/java/org/alfresco/repo/search/impl/lucene/DebugXPathHandler.java
+++ b/source/java/org/alfresco/repo/search/impl/lucene/DebugXPathHandler.java
@@ -18,11 +18,11 @@
*/
package org.alfresco.repo.search.impl.lucene;
-import org.saxpath.Axis;
-import org.saxpath.SAXPathException;
-import org.saxpath.XPathHandler;
+import org.jaxen.saxpath.Axis;
+import org.jaxen.saxpath.SAXPathException;
+import org.jaxen.saxpath.XPathHandler;
+import org.jaxen.saxpath.base.XPathReader;
-import com.werken.saxpath.XPathReader;
public class DebugXPathHandler implements XPathHandler
{
diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneAlfrescoXPathQueryLanguage.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneAlfrescoXPathQueryLanguage.java
index 37ab6e92b2..4749d9da9c 100644
--- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneAlfrescoXPathQueryLanguage.java
+++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneAlfrescoXPathQueryLanguage.java
@@ -19,42 +19,17 @@
package org.alfresco.repo.search.impl.lucene;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
import org.alfresco.repo.search.EmptyResultSet;
import org.alfresco.repo.search.SearcherException;
-import org.alfresco.repo.search.impl.parsers.AlfrescoFunctionEvaluationContext;
-import org.alfresco.repo.search.impl.parsers.FTSParser;
-import org.alfresco.repo.search.impl.parsers.FTSQueryParser;
-import org.alfresco.repo.search.impl.querymodel.Argument;
-import org.alfresco.repo.search.impl.querymodel.Column;
-import org.alfresco.repo.search.impl.querymodel.Constraint;
-import org.alfresco.repo.search.impl.querymodel.Function;
-import org.alfresco.repo.search.impl.querymodel.Order;
-import org.alfresco.repo.search.impl.querymodel.Ordering;
-import org.alfresco.repo.search.impl.querymodel.QueryEngine;
-import org.alfresco.repo.search.impl.querymodel.QueryEngineResults;
-import org.alfresco.repo.search.impl.querymodel.QueryModelFactory;
-import org.alfresco.repo.search.impl.querymodel.QueryOptions;
-import org.alfresco.repo.search.impl.querymodel.QueryOptions.Connective;
-import org.alfresco.repo.search.impl.querymodel.impl.functions.PropertyAccessor;
-import org.alfresco.repo.search.impl.querymodel.impl.functions.Score;
-import org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneOrdering;
-import org.alfresco.service.cmr.search.LimitBy;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
-import org.alfresco.service.cmr.search.SearchParameters.SortDefinition;
-import org.alfresco.service.cmr.search.SearchParameters.SortDefinition.SortType;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Searcher;
-import org.saxpath.SAXPathException;
-
-import com.werken.saxpath.XPathReader;
+import org.jaxen.saxpath.SAXPathException;
+import org.jaxen.saxpath.base.XPathReader;
/**
* Alfresco FTS Query language support
diff --git a/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java b/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java
index 4b35b0aaac..74ad5bd4ef 100644
--- a/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java
+++ b/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java
@@ -94,15 +94,15 @@ import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.IndexInput;
import org.apache.lucene.store.IndexOutput;
import org.apache.lucene.store.RAMDirectory;
+import org.jaxen.saxpath.SAXPathException;
+import org.jaxen.saxpath.base.XPathReader;
import org.safehaus.uuid.UUID;
-import org.saxpath.SAXPathException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
-import com.werken.saxpath.XPathReader;
/**
* The information that makes up an index. IndexInfoVersion Repeated information of the form
diff --git a/source/java/org/alfresco/repo/search/impl/noindex/NoIndexIndexer.java b/source/java/org/alfresco/repo/search/impl/noindex/NoIndexIndexer.java
index 078f8305b7..61ea5683eb 100644
--- a/source/java/org/alfresco/repo/search/impl/noindex/NoIndexIndexer.java
+++ b/source/java/org/alfresco/repo/search/impl/noindex/NoIndexIndexer.java
@@ -18,11 +18,14 @@
*/
package org.alfresco.repo.search.impl.noindex;
+import java.util.Collection;
+
import org.alfresco.error.StackTraceUtil;
import org.alfresco.repo.search.Indexer;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.cmr.search.SearchService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -127,6 +130,21 @@ public class NoIndexIndexer implements Indexer
trace();
return;
}
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.search.Indexer#detectNodeChanges(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.search.SearchService, java.util.Collection, java.util.Collection, java.util.Collection, java.util.Collection)
+ */
+ @Override
+ public void detectNodeChanges(NodeRef nodeRef, SearchService searcher,
+ Collection addedParents, Collection deletedParents,
+ Collection createdNodes, Collection updatedNodes)
+ {
+ if(s_logger.isDebugEnabled())
+ {
+ s_logger.debug("detectNodeChanges = "+nodeRef);
+ }
+ trace();
+ }
/* (non-Javadoc)
* @see org.alfresco.repo.search.Indexer#deleteIndex(org.alfresco.service.cmr.repository.StoreRef)
diff --git a/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java b/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java
index d9528910d4..3f07d77fae 100644
--- a/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java
+++ b/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java
@@ -185,7 +185,7 @@ public class SolrQueryHTTPClient
url.append(encoder.encode(searchParameters.getQuery(), "UTF-8"));
url.append("&wt=").append(encoder.encode("json", "UTF-8"));
- url.append("&fl=").append(encoder.encode("*,score", "UTF-8"));
+ url.append("&fl=").append(encoder.encode("DBID,score", "UTF-8"));
if (searchParameters.getMaxItems() >= 0)
{
diff --git a/source/java/org/alfresco/repo/security/authentication/TicketCleanupJob.java b/source/java/org/alfresco/repo/security/authentication/TicketCleanupJob.java
new file mode 100644
index 0000000000..0f37a3f2e5
--- /dev/null
+++ b/source/java/org/alfresco/repo/security/authentication/TicketCleanupJob.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2005-2010 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.repo.security.authentication;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.quartz.Job;
+import org.quartz.JobDataMap;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+
+/**
+ * @author Andy
+ *
+ */
+public class TicketCleanupJob implements Job
+{
+
+ public TicketCleanupJob()
+ {
+ }
+
+ /**
+ * Calls the cleaner to do its work
+ */
+ public void execute(JobExecutionContext context) throws JobExecutionException
+ {
+ JobDataMap jobData = context.getJobDetail().getJobDataMap();
+ // extract the content cleaner to use
+ Object abstractAuthenticationServiceRef = jobData.get("abstractAuthenticationService");
+ if (abstractAuthenticationServiceRef == null || !(abstractAuthenticationServiceRef instanceof AbstractAuthenticationService))
+ {
+ throw new AlfrescoRuntimeException(
+ "ContentStoreCleanupJob data must contain valid 'contentStoreCleaner' reference");
+ }
+ AbstractAuthenticationService abstractAuthenticationService = (AbstractAuthenticationService) abstractAuthenticationServiceRef;
+ abstractAuthenticationService.invalidateTickets(true);
+ }
+
+}
diff --git a/source/java/org/alfresco/repo/security/permissions/impl/AbstractPermissionTest.java b/source/java/org/alfresco/repo/security/permissions/impl/AbstractPermissionTest.java
index 1d96068cda..f24cb82ded 100644
--- a/source/java/org/alfresco/repo/security/permissions/impl/AbstractPermissionTest.java
+++ b/source/java/org/alfresco/repo/security/permissions/impl/AbstractPermissionTest.java
@@ -101,6 +101,8 @@ public class AbstractPermissionTest extends TestCase
private UserTransaction testTX;
+ protected PermissionServiceImpl permissionServiceImpl;
+
public AbstractPermissionTest()
{
super();
@@ -120,6 +122,7 @@ public class AbstractPermissionTest extends TestCase
dictionaryService = (DictionaryService) applicationContext.getBean(ServiceRegistry.DICTIONARY_SERVICE
.getLocalName());
permissionService = (PermissionServiceSPI) applicationContext.getBean("permissionService");
+ permissionServiceImpl = (PermissionServiceImpl) applicationContext.getBean("permissionServiceImpl");
namespacePrefixResolver = (NamespacePrefixResolver) applicationContext
.getBean(ServiceRegistry.NAMESPACE_SERVICE.getLocalName());
authenticationService = (MutableAuthenticationService) applicationContext.getBean("authenticationService");
@@ -178,6 +181,8 @@ public class AbstractPermissionTest extends TestCase
authenticationService.createAuthentication(AuthenticationUtil.getAdminUserName(), "admin".toCharArray());
authenticationComponent.clearCurrentSecurityContext();
+
+ assertTrue(permissionServiceImpl.getAnyDenyDenies());
}
@Override
diff --git a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java
index 24c217805d..f6f5dfd6c6 100644
--- a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java
+++ b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java
@@ -93,6 +93,8 @@ public class PermissionServiceImpl extends AbstractLifecycleBean implements Perm
private SimpleCache accessCache;
private SimpleCache> readersCache;
+
+ private SimpleCache> readersDeniedCache;
/*
* Access to the model
@@ -140,6 +142,8 @@ public class PermissionServiceImpl extends AbstractLifecycleBean implements Perm
private PermissionReference allPermissionReference;
+ private boolean anyDenyDenies = false;
+
/**
* Standard spring construction.
*/
@@ -152,6 +156,8 @@ public class PermissionServiceImpl extends AbstractLifecycleBean implements Perm
// Inversion of control
//
+
+
/**
* Set the dictionary service
* @param dictionaryService
@@ -161,6 +167,22 @@ public class PermissionServiceImpl extends AbstractLifecycleBean implements Perm
this.dictionaryService = dictionaryService;
}
+ /**
+ * @param anyDenyDenies the anyDenyDenies to set
+ */
+ public void setAnyDenyDenies(boolean anyDenyDenies)
+ {
+ this.anyDenyDenies = anyDenyDenies;
+ accessCache.clear();
+ readersCache.clear();
+ readersDeniedCache.clear();
+ }
+
+ public boolean getAnyDenyDenies()
+ {
+ return anyDenyDenies;
+ }
+
/**
* Set the permissions model dao
*
@@ -259,6 +281,15 @@ public class PermissionServiceImpl extends AbstractLifecycleBean implements Perm
this.readersCache = readersCache;
}
+
+ /**
+ * @param readersDeniedCache the readersDeniedCache to set
+ */
+ public void setReadersDeniedCache(SimpleCache> readersDeniedCache)
+ {
+ this.readersDeniedCache = readersDeniedCache;
+ }
+
/**
* Set the policy component
*
@@ -1117,6 +1148,10 @@ public class PermissionServiceImpl extends AbstractLifecycleBean implements Perm
private AccessStatus ownerRead(String username, NodeRef nodeRef)
{
+ // Reviewed the behaviour of deny and ownership with Mike F
+ // ATM ownership takes precendence over READ deny
+ // TODO: check that global owner rights are set
+
AccessStatus result = AccessStatus.DENIED;
String owner = ownableService.getOwner(nodeRef);
@@ -1184,14 +1219,70 @@ public class PermissionServiceImpl extends AbstractLifecycleBean implements Perm
return Collections.unmodifiableSet(readers);
}
+
+ /**
+ * @param aclId
+ * @return set of authorities with read permission on the ACL
+ */
+ private Set buildReadersDenied(Long aclId)
+ {
+ HashSet assigned = new HashSet();
+ HashSet denied = new HashSet();
+ AccessControlList acl = aclDaoComponent.getAccessControlList(aclId);
+
+ if (acl == null)
+ {
+ return denied;
+ }
+
+ for (AccessControlEntry ace : acl.getEntries())
+ {
+ assigned.add(ace.getAuthority());
+ }
+
+ for(String authority : assigned)
+ {
+ UnconditionalDeniedAclTest test = new UnconditionalDeniedAclTest(getPermissionReference(PermissionService.READ));
+ if(test.evaluate(authority, aclId))
+ {
+ denied.add(authority);
+ }
+ }
+
+ return denied;
+ }
private AccessStatus canRead(Long aclId)
{
Set authorities = getAuthorisations();
+ // test denied
+
+ if(anyDenyDenies)
+ {
+
+ Set aclReadersDenied = readersDeniedCache.get(aclId);
+ if(aclReadersDenied == null)
+ {
+ aclReadersDenied = buildReadersDenied(aclId);
+ readersDeniedCache.put(aclId, aclReadersDenied);
+ }
+
+ for(String auth : aclReadersDenied)
+ {
+ if(authorities.contains(auth))
+ {
+ return AccessStatus.DENIED;
+ }
+ }
+
+ }
+
+
+
// test acl readers
Set aclReaders = getReaders(aclId);
-
+
for(String auth : aclReaders)
{
if(authorities.contains(auth))
@@ -1210,6 +1301,8 @@ public class PermissionServiceImpl extends AbstractLifecycleBean implements Perm
/**
* Support class to test the permission on a node.
*
+ * Not fixed up for deny as should not be used
+ *
* @author Andy Hind
*/
private class NodeTest
@@ -1658,6 +1751,29 @@ public class PermissionServiceImpl extends AbstractLifecycleBean implements Perm
// any deny denies
+ if (anyDenyDenies)
+ {
+ if (denied != null)
+ {
+ for (String auth : authorisations)
+ {
+ Pair specific = new Pair(auth, required);
+ if (denied.contains(specific))
+ {
+ return false;
+ }
+ for (PermissionReference perm : granters)
+ {
+ specific = new Pair(auth, perm);
+ if (denied.contains(specific))
+ {
+ return false;
+ }
+ }
+ }
+ }
+ }
+
// If the permission has a match in both the authorities and
// granters list it is allowed
// It applies to the current user and it is granted
@@ -1837,6 +1953,21 @@ public class PermissionServiceImpl extends AbstractLifecycleBean implements Perm
return false;
}
+ if(anyDenyDenies)
+ {
+ Set> allowed = new HashSet>();
+
+ // Check if each permission allows - the first wins.
+ // We could have other voting style mechanisms here
+ for (AccessControlEntry ace : acl.getEntries())
+ {
+ if (isDenied(ace, authorisations, allowed, context))
+ {
+ return false;
+ }
+ }
+ }
+
Set> denied = new HashSet>();
// Check if each permission allows - the first wins.
@@ -1910,8 +2041,6 @@ public class PermissionServiceImpl extends AbstractLifecycleBean implements Perm
}
}
- // any deny denies
-
// If the permission has a match in both the authorities and
// granters list it is allowed
// It applies to the current user and it is granted
@@ -1926,6 +2055,81 @@ public class PermissionServiceImpl extends AbstractLifecycleBean implements Perm
return false;
}
+ /**
+ * Is a permission granted
+ *
+ * @param pe -
+ * the permissions entry to consider
+ * @param granters -
+ * the set of granters
+ * @param authorisations -
+ * the set of authorities
+ * @param denied -
+ * the set of denied permissions/authority pais
+ * @return true if granted
+ */
+ private boolean isDenied(AccessControlEntry ace, Set authorisations, Set> allowed, PermissionContext context)
+ {
+ // If the permission entry denies then we just deny
+ if (ace.getAccessStatus() == AccessStatus.ALLOWED)
+ {
+ allowed.add(new Pair(ace.getAuthority(), ace.getPermission()));
+
+ Set granters = modelDAO.getGrantingPermissions(ace.getPermission());
+ for (PermissionReference granter : granters)
+ {
+ allowed.add(new Pair(ace.getAuthority(), granter));
+ }
+
+ // All the things granted by this permission must be
+ // denied
+ Set grantees = modelDAO.getGranteePermissions(ace.getPermission());
+ for (PermissionReference grantee : grantees)
+ {
+ allowed.add(new Pair(ace.getAuthority(), grantee));
+ }
+
+ // All permission excludes all permissions available for
+ // the node.
+ if (ace.getPermission().equals(getAllPermissionReference()) || ace.getPermission().equals(OLD_ALL_PERMISSIONS_REFERENCE))
+ {
+ for (PermissionReference deny : modelDAO.getAllPermissions(context.getType(), context.getAspects()))
+ {
+ allowed.add(new Pair(ace.getAuthority(), deny));
+ }
+ }
+
+ return false;
+ }
+
+ // The permission is denied but we allow it as it is in the allowed
+ // set
+
+ if (allowed != null)
+ {
+ Pair specific = new Pair(ace.getAuthority(), required);
+ if (allowed.contains(specific))
+ {
+ return false;
+ }
+ }
+
+
+ // If the permission has a match in both the authorities and
+ // granters list it is allowed
+ // It applies to the current user and it is granted
+ if (authorisations.contains(ace.getAuthority()) && granters.contains(ace.getPermission()))
+ {
+ {
+ return true;
+ }
+ }
+
+ // Default allow
+ return false;
+ }
+
+
private boolean isGranted(PermissionEntry pe, Set authorisations)
{
// If the permission entry denies then we just deny
@@ -2177,31 +2381,6 @@ public class PermissionServiceImpl extends AbstractLifecycleBean implements Perm
}
}
- // any deny denies
-
-// if (false)
-// {
-// if (denied != null)
-// {
-// for (String auth : authorisations)
-// {
-// Pair specific = new Pair(auth, required);
-// if (denied.contains(specific))
-// {
-// return false;
-// }
-// for (PermissionReference perm : granters)
-// {
-// specific = new Pair(auth, perm);
-// if (denied.contains(specific))
-// {
-// return false;
-// }
-// }
-// }
-// }
-// }
-
// If the permission has a match in both the authorities and
// granters list it is allowed
// It applies to the current user and it is granted
@@ -2239,6 +2418,271 @@ public class PermissionServiceImpl extends AbstractLifecycleBean implements Perm
}
}
+ /**
+ * Ignores type and aspect requirements on the node
+ *
+ */
+ private class UnconditionalDeniedAclTest
+ {
+ /*
+ * The required permission.
+ */
+ PermissionReference required;
+
+ /*
+ * Granters of the permission
+ */
+ Set granters;
+
+ /*
+ * The additional permissions required at the node level.
+ */
+ Set nodeRequirements = new HashSet();
+
+ /*
+ * Constructor just gets the additional requirements
+ */
+ UnconditionalDeniedAclTest(PermissionReference required)
+ {
+ this.required = required;
+
+ // Set the required node permissions
+ if (required.equals(getPermissionReference(ALL_PERMISSIONS)))
+ {
+ nodeRequirements = modelDAO.getUnconditionalRequiredPermissions(getPermissionReference(PermissionService.FULL_CONTROL), RequiredPermission.On.NODE);
+ }
+ else
+ {
+ nodeRequirements = modelDAO.getUnconditionalRequiredPermissions(required, RequiredPermission.On.NODE);
+ }
+
+ if (modelDAO.getUnconditionalRequiredPermissions(required, RequiredPermission.On.PARENT).size() > 0)
+ {
+ throw new IllegalStateException("Parent permissions can not be checked for an acl");
+ }
+
+ if (modelDAO.getUnconditionalRequiredPermissions(required, RequiredPermission.On.CHILDREN).size() > 0)
+ {
+ throw new IllegalStateException("Child permissions can not be checked for an acl");
+ }
+
+ // Find all the permissions that grant the allowed permission
+ // All permissions are treated specially.
+ granters = new LinkedHashSet(128, 1.0f);
+ granters.addAll(modelDAO.getGrantingPermissions(required));
+ granters.add(getAllPermissionReference());
+ granters.add(OLD_ALL_PERMISSIONS_REFERENCE);
+ }
+
+ /**
+ * Internal hook point for recursion
+ *
+ * @param authorisations
+ * @param nodeRef
+ * @param denied
+ * @param recursiveIn
+ * @return true if granted
+ */
+ boolean evaluate(String authority, Long aclId)
+ {
+ // Start out true and "and" all other results
+ boolean success = true;
+
+ // Check the required permissions but not for sets they rely on
+ // their underlying permissions
+ //if (modelDAO.checkPermission(required))
+ //{
+
+ // We have to do the test as no parent will help us out
+ success &= hasSinglePermission(authority, aclId);
+
+ if (!success)
+ {
+ return false;
+ }
+ //}
+
+ // Check the other permissions required on the node
+ for (PermissionReference pr : nodeRequirements)
+ {
+ // Build a new test
+ UnconditionalDeniedAclTest nt = new UnconditionalDeniedAclTest(pr);
+ success &= nt.evaluate(authority, aclId);
+ if (!success)
+ {
+ return false;
+ }
+ }
+
+ return success;
+ }
+
+ boolean hasSinglePermission(String authority, Long aclId)
+ {
+ // Check global permission
+
+ if (checkGlobalPermissions(authority))
+ {
+ return true;
+ }
+
+ if(aclId == null)
+ {
+ return false;
+ }
+ else
+ {
+ return checkRequired(authority, aclId);
+ }
+
+ }
+
+ /**
+ * Check if we have a global permission
+ *
+ * @param authorisations
+ * @return true if granted
+ */
+ private boolean checkGlobalPermissions(String authority)
+ {
+ for (PermissionEntry pe : modelDAO.getGlobalPermissionEntries())
+ {
+ if (isDenied(pe, authority))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check that a given authentication is available on a node
+ *
+ * @param authorisations
+ * @param nodeRef
+ * @param denied
+ * @return true if a check is required
+ */
+ boolean checkRequired(String authority, Long aclId)
+ {
+ AccessControlList acl = aclDaoComponent.getAccessControlList(aclId);
+
+ if (acl == null)
+ {
+ return false;
+ }
+
+ Set> allowed = new HashSet>();
+
+ // Check if each permission allows - the first wins.
+ // We could have other voting style mechanisms here
+ for (AccessControlEntry ace : acl.getEntries())
+ {
+ if (isDenied(ace, authority, allowed))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Is a permission granted
+ *
+ * @param pe -
+ * the permissions entry to consider
+ * @param granters -
+ * the set of granters
+ * @param authorisations -
+ * the set of authorities
+ * @param denied -
+ * the set of denied permissions/authority pais
+ * @return true if granted
+ */
+ private boolean isDenied(AccessControlEntry ace, String authority, Set> allowed)
+ {
+ // If the permission entry denies then we just deny
+ if (ace.getAccessStatus() == AccessStatus.ALLOWED)
+ {
+ allowed.add(new Pair(ace.getAuthority(), ace.getPermission()));
+
+ Set granters = modelDAO.getGrantingPermissions(ace.getPermission());
+ for (PermissionReference granter : granters)
+ {
+ allowed.add(new Pair(ace.getAuthority(), granter));
+ }
+
+ // All the things granted by this permission must be
+ // denied
+ Set grantees = modelDAO.getGranteePermissions(ace.getPermission());
+ for (PermissionReference grantee : grantees)
+ {
+ allowed.add(new Pair(ace.getAuthority(), grantee));
+ }
+
+ // All permission excludes all permissions available for
+ // the node.
+ if (ace.getPermission().equals(getAllPermissionReference()) || ace.getPermission().equals(OLD_ALL_PERMISSIONS_REFERENCE))
+ {
+ for (PermissionReference deny : modelDAO.getAllPermissions())
+ {
+ allowed.add(new Pair(ace.getAuthority(), deny));
+ }
+ }
+
+ return false;
+ }
+
+ // The permission is allowed but we deny it as it is in the denied
+ // set
+
+ if (allowed != null)
+ {
+ Pair specific = new Pair(ace.getAuthority(), required);
+ if (allowed.contains(specific))
+ {
+ return false;
+ }
+ }
+
+ // If the permission has a match in both the authorities and
+ // granters list it is allowed
+ // It applies to the current user and it is granted
+ if (authority.equals(ace.getAuthority()) && granters.contains(ace.getPermission()))
+ {
+ {
+ return true;
+ }
+ }
+
+ // Default deny
+ return false;
+ }
+
+ private boolean isDenied(PermissionEntry pe, String authority)
+ {
+ // If the permission entry denies then we just deny
+ if (pe.isAllowed())
+ {
+ return false;
+ }
+
+ // If the permission has a match in both the authorities and
+ // granters list it is allowed
+ // It applies to the current user and it is granted
+ if (granters.contains(pe.getPermissionReference()) && authority.equals(pe.getAuthority()))
+ {
+ {
+ return true;
+ }
+ }
+
+ // Default deny
+ return false;
+ }
+ }
+
+
private static class MutableBoolean
{
private boolean value;
diff --git a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java
index 71476629fd..e48fd052a9 100644
--- a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java
+++ b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java
@@ -33,6 +33,7 @@ import org.alfresco.repo.security.permissions.ACLType;
import org.alfresco.repo.security.permissions.AccessControlEntry;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.repo.security.permissions.PermissionEntry;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AccessPermission;
@@ -63,6 +64,288 @@ public class PermissionServiceTest extends AbstractPermissionTest
// TODO Auto-generated constructor stub
}
+ public void testAnyDenyDeniesAndRead()
+ {
+ personService.getPerson("andy");
+ runAs("admin");
+
+ authorityService.createAuthority(AuthorityType.GROUP, "ONE");
+ authorityService.addAuthority("GROUP_ONE", "andy");
+
+ authorityService.createAuthority(AuthorityType.GROUP, "TWO");
+ authorityService.addAuthority("GROUP_TWO", "andy");
+
+ authorityService.createAuthority(AuthorityType.GROUP, "THREE");
+ authorityService.addAuthority("GROUP_THREE", "andy");
+
+ authorityService.createAuthority(AuthorityType.GROUP, "GEN");
+ authorityService.addAuthority("GROUP_GEN", "andy");
+
+ NodeRef one = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}one"), ContentModel.TYPE_FOLDER).getChildRef();
+ permissionService.setPermission(one, "andy", PermissionService.READ, true);
+ permissionService.setPermission(one, "GROUP_ONE", PermissionService.READ, true);
+
+ NodeRef two = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}two"), ContentModel.TYPE_FOLDER).getChildRef();
+ permissionService.setPermission(two, "andy", PermissionService.READ, true);
+ permissionService.setPermission(two, "GROUP_TWO", PermissionService.READ, true);
+
+ NodeRef three = nodeService.createNode(one, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}three"), ContentModel.TYPE_FOLDER).getChildRef();
+ permissionService.setPermission(three, "andy", PermissionService.READ, true);
+ permissionService.setPermission(three, "GROUP_THREE", PermissionService.READ, true);
+
+ NodeRef four = nodeService.createNode(three, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}four"), ContentModel.TYPE_FOLDER).getChildRef();
+
+ permissionServiceImpl.setAnyDenyDenies(false);
+ try
+ {
+
+
+ runAs("andy");
+ assertEquals("andy", authenticationComponent.getCurrentUserName());
+ assertTrue(permissionService.hasPermission(one, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(two, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(three, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(four, PermissionService.READ) == AccessStatus.ALLOWED);
+
+ assertTrue(permissionService.hasReadPermission(one) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(two) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(three) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(four) == AccessStatus.ALLOWED);
+
+ // Deny group on one
+ runAs("admin");
+ permissionService.setPermission(one, "GROUP_ONE", PermissionService.READ, false);
+
+ runAs("andy");
+ assertEquals("andy", authenticationComponent.getCurrentUserName());
+ assertTrue(permissionService.hasPermission(one, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(two, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(three, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(four, PermissionService.READ) == AccessStatus.ALLOWED);
+
+ assertTrue(permissionService.hasReadPermission(one) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(two) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(three) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(four) == AccessStatus.ALLOWED);
+
+ }
+ finally
+ {
+ // ANY DENY DENIES
+
+ permissionServiceImpl.setAnyDenyDenies(true);
+ }
+
+ runAs("andy");
+ assertEquals("andy", authenticationComponent.getCurrentUserName());
+ assertTrue(permissionService.hasPermission(one, PermissionService.READ) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(two, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(three, PermissionService.READ) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(four, PermissionService.READ) == AccessStatus.DENIED);
+
+ assertTrue(permissionService.hasReadPermission(one) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasReadPermission(two) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(three) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasReadPermission(four) == AccessStatus.DENIED);
+
+ runAs("admin");
+ permissionService.deletePermission(one, "GROUP_ONE", PermissionService.READ);
+ permissionService.setPermission(one, "GROUP_ONE", PermissionService.READ, true);
+
+ // deny andy on one but explicitly allowed on three
+
+ runAs("admin");
+ permissionService.setPermission(one, "andy", PermissionService.READ, false);
+
+
+ runAs("andy");
+ assertEquals("andy", authenticationComponent.getCurrentUserName());
+ assertTrue(permissionService.hasPermission(one, PermissionService.READ) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(two, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(three, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(four, PermissionService.READ) == AccessStatus.ALLOWED);
+
+ assertTrue(permissionService.hasReadPermission(one) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasReadPermission(two) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(three) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(four) == AccessStatus.ALLOWED);
+
+ runAs("admin");
+ permissionService.deletePermission(one, "andy", PermissionService.READ);
+ permissionService.setPermission(one, "andy", PermissionService.READ, true);
+
+ // Deny by all - the underlying allow should win
+
+ runAs("admin");
+ permissionService.setPermission(one, "andy", PermissionService.ALL_PERMISSIONS, false);
+
+ runAs("andy");
+ assertEquals("andy", authenticationComponent.getCurrentUserName());
+ assertTrue(permissionService.hasPermission(one, PermissionService.READ) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(two, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(three, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(four, PermissionService.READ) == AccessStatus.ALLOWED);
+
+ assertTrue(permissionService.hasReadPermission(one) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasReadPermission(two) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(three) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(four) == AccessStatus.ALLOWED);
+
+ runAs("admin");
+ permissionService.deletePermission(one, "andy", PermissionService.ALL_PERMISSIONS);
+
+ // Deny by all - should win as no underlying mask
+
+ runAs("admin");
+ permissionService.setPermission(one, "GROUP_GEN", PermissionService.ALL_PERMISSIONS, false);
+
+ runAs("andy");
+ assertEquals("andy", authenticationComponent.getCurrentUserName());
+ assertTrue(permissionService.hasPermission(one, PermissionService.READ) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(two, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(three, PermissionService.READ) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(four, PermissionService.READ) == AccessStatus.DENIED);
+
+ assertTrue(permissionService.hasReadPermission(one) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasReadPermission(two) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(three) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasReadPermission(four) == AccessStatus.DENIED);
+
+ runAs("admin");
+ permissionService.setInheritParentPermissions(three, false);
+
+ runAs("andy");
+ assertEquals("andy", authenticationComponent.getCurrentUserName());
+ assertTrue(permissionService.hasPermission(one, PermissionService.READ) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(two, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(three, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(four, PermissionService.READ) == AccessStatus.ALLOWED);
+
+ assertTrue(permissionService.hasReadPermission(one) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasReadPermission(two) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(three) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(four) == AccessStatus.ALLOWED);
+
+ runAs("admin");
+ permissionService.setInheritParentPermissions(three, true);
+ permissionService.deletePermission(one, "GROUP_GEN", PermissionService.ALL_PERMISSIONS);
+
+ // direct deny
+
+ runAs("admin");
+ permissionService.setPermission(three, "andy", PermissionService.READ, false);
+
+ runAs("andy");
+ assertEquals("andy", authenticationComponent.getCurrentUserName());
+ assertTrue(permissionService.hasPermission(one, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(two, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(three, PermissionService.READ) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(four, PermissionService.READ) == AccessStatus.DENIED);
+
+ assertTrue(permissionService.hasReadPermission(one) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(two) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(three) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasReadPermission(four) == AccessStatus.DENIED);
+
+ runAs("admin");
+ permissionService.deletePermission(three, "andy", PermissionService.READ);
+ permissionService.setPermission(three, "andy", PermissionService.READ, true);
+
+ permissionService.setPermission(one, "GROUP_ONE", PermissionService.READ, false);
+ permissionService.setPermission(two, "GROUP_TWO", PermissionService.READ, false);
+ permissionService.setPermission(three, "GROUP_THREE", PermissionService.READ, false);
+
+ runAs("andy");
+ assertEquals("andy", authenticationComponent.getCurrentUserName());
+ assertTrue(permissionService.hasPermission(one, PermissionService.READ) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(two, PermissionService.READ) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(three, PermissionService.READ) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(four, PermissionService.READ) == AccessStatus.DENIED);
+
+ assertTrue(permissionService.hasReadPermission(one) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasReadPermission(two) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasReadPermission(three) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasReadPermission(four) == AccessStatus.DENIED);
+
+ runAs("admin");
+ authorityService.removeAuthority("GROUP_ONE", "andy");
+ AlfrescoTransactionSupport.bindResource("MyAuthCache", null);
+
+ runAs("andy");
+ assertEquals("andy", authenticationComponent.getCurrentUserName());
+ assertTrue(permissionService.hasPermission(one, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(two, PermissionService.READ) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(three, PermissionService.READ) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(four, PermissionService.READ) == AccessStatus.DENIED);
+
+ assertTrue(permissionService.hasReadPermission(one) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(two) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasReadPermission(three) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasReadPermission(four) == AccessStatus.DENIED);
+
+ runAs("admin");
+ authorityService.removeAuthority("GROUP_TWO", "andy");
+ AlfrescoTransactionSupport.bindResource("MyAuthCache", null);
+
+ runAs("andy");
+ assertEquals("andy", authenticationComponent.getCurrentUserName());
+ assertTrue(permissionService.hasPermission(one, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(two, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(three, PermissionService.READ) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(four, PermissionService.READ) == AccessStatus.DENIED);
+
+ assertTrue(permissionService.hasReadPermission(one) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(two) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(three) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasReadPermission(four) == AccessStatus.DENIED);
+
+ runAs("admin");
+ authorityService.removeAuthority("GROUP_THREE", "andy");
+ AlfrescoTransactionSupport.bindResource("MyAuthCache", null);
+
+ runAs("andy");
+ assertEquals("andy", authenticationComponent.getCurrentUserName());
+ assertTrue(permissionService.hasPermission(one, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(two, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(three, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(four, PermissionService.READ) == AccessStatus.ALLOWED);
+
+ assertTrue(permissionService.hasReadPermission(one) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(two) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(three) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(four) == AccessStatus.ALLOWED);
+
+ runAs("admin");
+ permissionService.setPermission(one, PermissionService.ALL_AUTHORITIES, PermissionService.READ, false);
+
+ runAs("andy");
+ assertEquals("andy", authenticationComponent.getCurrentUserName());
+ assertTrue(permissionService.hasPermission(one, PermissionService.READ) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(two, PermissionService.READ) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(three, PermissionService.READ) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(four, PermissionService.READ) == AccessStatus.DENIED);
+
+ assertTrue(permissionService.hasReadPermission(one) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasReadPermission(two) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasReadPermission(three) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasReadPermission(four) == AccessStatus.DENIED);
+
+ runAs("admin");
+ permissionService.setPermission(rootNodeRef, PermissionService.ALL_AUTHORITIES, PermissionService.READ, false);
+
+ runAs("andy");
+ assertEquals("andy", authenticationComponent.getCurrentUserName());
+ assertTrue(permissionService.hasPermission(one, PermissionService.READ) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(two, PermissionService.READ) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(three, PermissionService.READ) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(four, PermissionService.READ) == AccessStatus.DENIED);
+
+ assertTrue(permissionService.hasReadPermission(one) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasReadPermission(two) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasReadPermission(three) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasReadPermission(four) == AccessStatus.DENIED);
+ }
+
/*
* Tests that the current user is contained in the current authorisations set
*/
@@ -83,243 +366,243 @@ public class PermissionServiceTest extends AbstractPermissionTest
NodeRef two = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}two"), ContentModel.TYPE_FOLDER).getChildRef();
permissionService.setPermission(two, "andy", PermissionService.WRITE, true);
NodeRef three = nodeService.createNode(one, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}two"), ContentModel.TYPE_FOLDER).getChildRef();
-
-
+
+
NodeRef test = nodeService.createNode(one, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}test"), ContentModel.TYPE_FOLDER).getChildRef();
-
+
// test has shared acl
-
-
+
+
// under 1
// start
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.ALLOWED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.DENIED);
-
+
//under 2
// def parent -> def parent
runAs("admin");
nodeService.moveNode(test, two, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}test"));
assertEquals(two, nodeService.getPrimaryParent(test).getParentRef());
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.ALLOWED);
-
+
// under 3
// def parent -> shared parent
runAs("admin");
nodeService.moveNode(test, three, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}test"));
assertEquals(three, nodeService.getPrimaryParent(test).getParentRef());
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.ALLOWED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.DENIED);
-
+
//under 2
// shared parent -> def parent
runAs("admin");
nodeService.moveNode(test, two, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}test"));
assertEquals(two, nodeService.getPrimaryParent(test).getParentRef());
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.ALLOWED);
-
+
//under 1
// def parent -> def parent
runAs("admin");
nodeService.moveNode(test, one, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}test"));
assertEquals(one, nodeService.getPrimaryParent(test).getParentRef());
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.ALLOWED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.DENIED);
-
-
+
+
// test has defining acl
-
+
runAs("admin");
permissionService.setPermission(test, "andy", PermissionService.CHANGE_PERMISSIONS, true);
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.ALLOWED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED);
-
+
// 2
runAs("admin");
nodeService.moveNode(test, two, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}test"));
assertEquals(two, nodeService.getPrimaryParent(test).getParentRef());
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.ALLOWED);
assertTrue(permissionService.hasPermission(test, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED);
-
+
// 3
runAs("admin");
nodeService.moveNode(test, three, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}test"));
assertEquals(three, nodeService.getPrimaryParent(test).getParentRef());
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.ALLOWED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED);
-
+
// 2
runAs("admin");
nodeService.moveNode(test, two, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}test"));
assertEquals(two, nodeService.getPrimaryParent(test).getParentRef());
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.ALLOWED);
assertTrue(permissionService.hasPermission(test, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED);
-
+
// 1
runAs("admin");
nodeService.moveNode(test, one, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}test"));
assertEquals(one, nodeService.getPrimaryParent(test).getParentRef());
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.ALLOWED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED);
-
+
// 1 inherit
-
+
runAs("admin");
permissionService.setInheritParentPermissions(test, true);
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.ALLOWED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED);
-
+
runAs("admin");
permissionService.setInheritParentPermissions(test, false);
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED);
-
+
runAs("admin");
permissionService.setInheritParentPermissions(test, true);
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.ALLOWED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED);
-
+
// 2 inherit
-
+
runAs("admin");
nodeService.moveNode(test, two, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}test"));
assertEquals(two, nodeService.getPrimaryParent(test).getParentRef());
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.ALLOWED);
assertTrue(permissionService.hasPermission(test, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED);
-
-
+
+
runAs("admin");
permissionService.setInheritParentPermissions(test, true);
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.ALLOWED);
assertTrue(permissionService.hasPermission(test, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED);
-
+
runAs("admin");
permissionService.setInheritParentPermissions(test, false);
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED);
-
+
runAs("admin");
permissionService.setInheritParentPermissions(test, true);
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.ALLOWED);
assertTrue(permissionService.hasPermission(test, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED);
-
+
// 3 inherit
-
+
runAs("admin");
nodeService.moveNode(test, three, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}test"));
assertEquals(three, nodeService.getPrimaryParent(test).getParentRef());
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.ALLOWED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED);
-
+
runAs("admin");
permissionService.setInheritParentPermissions(test, true);
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.ALLOWED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED);
-
+
runAs("admin");
permissionService.setInheritParentPermissions(test, false);
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED);
-
+
// move to 2 without inherit
-
+
runAs("admin");
nodeService.moveNode(test, two, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}test"));
assertEquals(two, nodeService.getPrimaryParent(test).getParentRef());
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED);
-
+
// move to 3 without inherit
-
+
runAs("admin");
nodeService.moveNode(test, three, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}test"));
assertEquals(three, nodeService.getPrimaryParent(test).getParentRef());
-
+
runAs("andy");
assertEquals("andy", authenticationComponent.getCurrentUserName());
assertTrue(permissionService.hasPermission(test, PermissionService.READ) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.WRITE) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(test, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED);
-
+
}
public void testChangePersonUid()
@@ -357,7 +640,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
}
}
assertTrue(found);
-
+
try
{
nodeService.setProperty(andy, ContentModel.PROP_USERNAME, "Bob");
@@ -708,7 +991,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
assertEquals("andy", AuthenticationUtil.getRunAsUser());
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork()
- {
+ {
public Object doWork() throws Exception
{
assertEquals("andy", AuthenticationUtil.getFullyAuthenticatedUser());
@@ -720,7 +1003,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
assertEquals(AuthenticationUtil.getAdminUserName(), AuthenticationUtil.getRunAsUser());
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork()
- {
+ {
public Object doWork() throws Exception
{
@@ -730,7 +1013,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
assertEquals("lemur", AuthenticationUtil.getRunAsUser());
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork()
- {
+ {
public Object doWork() throws Exception
{
@@ -740,7 +1023,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
assertEquals(AuthenticationUtil.getAdminUserName(), AuthenticationUtil.getRunAsUser());
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork()
- {
+ {
public Object doWork() throws Exception
{
@@ -751,28 +1034,28 @@ public class PermissionServiceTest extends AbstractPermissionTest
return null;
}
- }, "andy");
+ }, "andy");
assertEquals("andy", AuthenticationUtil.getFullyAuthenticatedUser());
assertEquals(AuthenticationUtil.getAdminUserName(), AuthenticationUtil.getRunAsUser());
return null;
}
- }, AuthenticationUtil.getAdminUserName());
+ }, AuthenticationUtil.getAdminUserName());
assertEquals("andy", AuthenticationUtil.getFullyAuthenticatedUser());
assertEquals("lemur", AuthenticationUtil.getRunAsUser());
return null;
}
- }, "lemur");
+ }, "lemur");
assertEquals("andy", AuthenticationUtil.getFullyAuthenticatedUser());
assertEquals(AuthenticationUtil.getAdminUserName(), AuthenticationUtil.getRunAsUser());
return null;
}
- }, AuthenticationUtil.getAdminUserName());
+ }, AuthenticationUtil.getAdminUserName());
assertEquals("andy", AuthenticationUtil.getFullyAuthenticatedUser());
assertEquals("andy", AuthenticationUtil.getRunAsUser());
@@ -792,7 +1075,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
assertEquals("andy", AuthenticationUtil.getRunAsUser());
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork()
- {
+ {
public Object doWork() throws Exception
{
@@ -802,7 +1085,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
assertEquals(AuthenticationUtil.getAdminUserName(), AuthenticationUtil.getRunAsUser());
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork()
- {
+ {
public Object doWork() throws Exception
{
@@ -812,7 +1095,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
assertEquals("lemur", AuthenticationUtil.getRunAsUser());
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork()
- {
+ {
public Object doWork() throws Exception
{
@@ -822,7 +1105,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
assertEquals(AuthenticationUtil.getAdminUserName(), AuthenticationUtil.getRunAsUser());
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork()
- {
+ {
public Object doWork() throws Exception
{
@@ -833,28 +1116,28 @@ public class PermissionServiceTest extends AbstractPermissionTest
return null;
}
- }, "andy");
+ }, "andy");
assertEquals("andy", AuthenticationUtil.getFullyAuthenticatedUser());
assertEquals(AuthenticationUtil.getAdminUserName(), AuthenticationUtil.getRunAsUser());
return null;
}
- }, AuthenticationUtil.getAdminUserName());
+ }, AuthenticationUtil.getAdminUserName());
assertEquals("andy", AuthenticationUtil.getFullyAuthenticatedUser());
assertEquals("lemur", AuthenticationUtil.getRunAsUser());
return null;
}
- }, "lemur");
+ }, "lemur");
assertEquals("andy", AuthenticationUtil.getFullyAuthenticatedUser());
assertEquals(AuthenticationUtil.getAdminUserName(), AuthenticationUtil.getRunAsUser());
return null;
}
- }, AuthenticationUtil.getAdminUserName());
+ }, AuthenticationUtil.getAdminUserName());
assertEquals("andy", AuthenticationUtil.getFullyAuthenticatedUser());
assertEquals("andy", AuthenticationUtil.getRunAsUser());
@@ -873,7 +1156,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
assertNull(AuthenticationUtil.getRunAsUser());
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork()
- {
+ {
public Object doWork() throws Exception
{
@@ -883,7 +1166,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
assertEquals(AuthenticationUtil.getAdminUserName(), AuthenticationUtil.getRunAsUser());
return null;
}
- }, AuthenticationUtil.getAdminUserName());
+ }, AuthenticationUtil.getAdminUserName());
assertNull(AuthenticationUtil.getFullyAuthenticatedUser());
assertNull(AuthenticationUtil.getRunAsUser());
@@ -901,7 +1184,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
assertNull(AuthenticationUtil.getRunAsUser());
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork()
- {
+ {
public Object doWork() throws Exception
{
@@ -911,7 +1194,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
assertEquals(AuthenticationUtil.getAdminUserName(), AuthenticationUtil.getRunAsUser());
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork()
- {
+ {
public Object doWork() throws Exception
{
@@ -921,7 +1204,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
assertEquals("lemur", AuthenticationUtil.getRunAsUser());
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork()
- {
+ {
public Object doWork() throws Exception
{
@@ -931,7 +1214,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
assertEquals(AuthenticationUtil.getAdminUserName(), AuthenticationUtil.getRunAsUser());
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork()
- {
+ {
public Object doWork() throws Exception
{
@@ -942,28 +1225,28 @@ public class PermissionServiceTest extends AbstractPermissionTest
return null;
}
- }, "andy");
+ }, "andy");
assertEquals(AuthenticationUtil.getAdminUserName(), AuthenticationUtil.getFullyAuthenticatedUser());
assertEquals(AuthenticationUtil.getAdminUserName(), AuthenticationUtil.getRunAsUser());
return null;
}
- }, AuthenticationUtil.getAdminUserName());
+ }, AuthenticationUtil.getAdminUserName());
assertEquals(AuthenticationUtil.getAdminUserName(), AuthenticationUtil.getFullyAuthenticatedUser());
assertEquals("lemur", AuthenticationUtil.getRunAsUser());
return null;
}
- }, "lemur");
+ }, "lemur");
assertEquals(AuthenticationUtil.getAdminUserName(), AuthenticationUtil.getFullyAuthenticatedUser());
assertEquals(AuthenticationUtil.getAdminUserName(), AuthenticationUtil.getRunAsUser());
return null;
}
- }, AuthenticationUtil.getAdminUserName());
+ }, AuthenticationUtil.getAdminUserName());
assertNull(AuthenticationUtil.getFullyAuthenticatedUser());
assertNull(AuthenticationUtil.getRunAsUser());
@@ -1042,7 +1325,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
runAs(AuthenticationUtil.getAdminUserName());
NodeRef n1 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}one"), ContentModel.TYPE_FOLDER).getChildRef();
-
+
permissionService.setPermission(new SimplePermissionEntry(n1, getPermission(PermissionService.READ), "andy", AccessStatus.ALLOWED));
permissionService.setPermission(new SimplePermissionEntry(n1, getPermission(PermissionService.READ), "Andy", AccessStatus.ALLOWED));
permissionService.setPermission(new SimplePermissionEntry(n1, getPermission(PermissionService.READ), "ANDY", AccessStatus.ALLOWED));
@@ -1051,11 +1334,11 @@ public class PermissionServiceTest extends AbstractPermissionTest
permissionService.setPermission(new SimplePermissionEntry(n1, getPermission(PermissionService.READ), "woof/ADOBE", AccessStatus.ALLOWED));
permissionService.setPermission(new SimplePermissionEntry(n1, getPermission(PermissionService.READ), "Woof/Adobe", AccessStatus.ALLOWED));
permissionService.setPermission(new SimplePermissionEntry(n1, getPermission(PermissionService.READ), "WOOF/ADOBE", AccessStatus.ALLOWED));
-
+
assertEquals(8, permissionService.getAllSetPermissions(n1).size());
}
-
-
+
+
public void testGetAllSetPermissions()
{
runAs("andy");
@@ -1109,15 +1392,15 @@ public class PermissionServiceTest extends AbstractPermissionTest
NodeRef n1 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}one"), ContentModel.TYPE_FOLDER).getChildRef();
NodeRef n2 = nodeService.createNode(n1, ContentModel.ASSOC_CONTAINS, QName.createQName("{namespace}two"), ContentModel.TYPE_FOLDER).getChildRef();
-
+
runAs("andy");
-
+
assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(n1, getPermission(PermissionService.READ)) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(n2, getPermission(PermissionService.READ)) == AccessStatus.DENIED);
-
+
runAs(AuthenticationUtil.getAdminUserName());
-
+
permissionService.setPermission(new SimplePermissionEntry(n1, getPermission(PermissionService.READ), "andy", AccessStatus.ALLOWED));
runAs("andy");
@@ -1943,16 +2226,16 @@ public class PermissionServiceTest extends AbstractPermissionTest
long start;
long end;
long time = 0;
-// for (int i = 0; i < 1000; i++)
-// {
-// getSession().flush();
-// // getSession().clear();
-// start = System.nanoTime();
-// assertTrue(permissionService.hasPermission(n10, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED);
-// end = System.nanoTime();
-// time += (end - start);
-// }
-// System.out.println("Time is " + (time / 1000000000.0));
+ // for (int i = 0; i < 1000; i++)
+ // {
+ // getSession().flush();
+ // // getSession().clear();
+ // start = System.nanoTime();
+ // assertTrue(permissionService.hasPermission(n10, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED);
+ // end = System.nanoTime();
+ // time += (end - start);
+ // }
+ // System.out.println("Time is " + (time / 1000000000.0));
// assertTrue((time / 1000000000.0) < 60.0);
time = 0;
@@ -2313,8 +2596,8 @@ public class PermissionServiceTest extends AbstractPermissionTest
permissionService.setPermission(new SimplePermissionEntry(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES), "andy", AccessStatus.DENIED));
runAs("andy");
- assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED);
- assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED);
assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED);
runAs("lemur");
@@ -2325,8 +2608,8 @@ public class PermissionServiceTest extends AbstractPermissionTest
permissionService.setPermission(new SimplePermissionEntry(rootNodeRef, getPermission(PermissionService.READ_CHILDREN), "andy", AccessStatus.ALLOWED));
runAs("andy");
- assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED);
- assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.DENIED);
assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED);
assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED);
runAs("lemur");
@@ -2337,10 +2620,10 @@ public class PermissionServiceTest extends AbstractPermissionTest
permissionService.setPermission(new SimplePermissionEntry(rootNodeRef, getPermission(PermissionService.READ), "andy", AccessStatus.DENIED));
runAs("andy");
- assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED);
- assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED);
- assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED);
- assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.DENIED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.DENIED);
runAs("lemur");
assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED);
assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED);
@@ -2348,6 +2631,89 @@ public class PermissionServiceTest extends AbstractPermissionTest
assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED);
}
+ public void testGroupAndUserInteractionAnyAllowAllows()
+ {
+
+ permissionServiceImpl.setAnyDenyDenies(false);
+ try
+ {
+ runAs("andy");
+ assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED);
+ assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED);
+ assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED);
+ assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED);
+ runAs("lemur");
+ assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED);
+ assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED);
+ assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED);
+ assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED);
+
+ permissionService.setPermission(new SimplePermissionEntry(rootNodeRef, getPermission(PermissionService.READ), "andy", AccessStatus.ALLOWED));
+ runAs("andy");
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED);
+ runAs("lemur");
+ assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED);
+ assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED);
+ assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED);
+ assertFalse(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED);
+
+ permissionService.setPermission(new SimplePermissionEntry(rootNodeRef, getPermission(PermissionService.READ), ROLE_AUTHENTICATED, AccessStatus.ALLOWED));
+ runAs("andy");
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED);
+ runAs("lemur");
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED);
+
+ permissionService.setPermission(new SimplePermissionEntry(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES), "andy", AccessStatus.DENIED));
+ runAs("andy");
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED);
+ runAs("lemur");
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED);
+
+ permissionService.setPermission(new SimplePermissionEntry(rootNodeRef, getPermission(PermissionService.READ_CHILDREN), "andy", AccessStatus.ALLOWED));
+ runAs("andy");
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED);
+ runAs("lemur");
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED);
+
+ permissionService.setPermission(new SimplePermissionEntry(rootNodeRef, getPermission(PermissionService.READ), "andy", AccessStatus.DENIED));
+ runAs("andy");
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED);
+ runAs("lemur");
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CHILDREN)) == AccessStatus.ALLOWED);
+ assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_CONTENT)) == AccessStatus.ALLOWED);
+ }
+ finally
+ {
+ permissionServiceImpl.setAnyDenyDenies(true);
+ }
+ }
+
public void testInheritPermissions()
{
runAs(AuthenticationUtil.getAdminUserName());
@@ -2770,7 +3136,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
assertEquals(0, permissionService.getAllSetPermissions(rootNodeRef).size());
}
-
+
public void xtestAclInsertionPerformanceShared()
{
NodeRef parent = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}one"), ContentModel.TYPE_FOLDER).getChildRef();
@@ -2781,10 +3147,10 @@ public class PermissionServiceTest extends AbstractPermissionTest
long start = System.nanoTime();
permissionService.setPermission(new SimplePermissionEntry(parent, getPermission(PermissionService.CONSUMER), "andy", AccessStatus.ALLOWED));
long end = System.nanoTime();
-
+
assertTrue("Time was "+(end - start)/1000000000.0f, end == start);
}
-
+
public void xtestAclInsertionPerformanceDefining()
{
NodeRef parent = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}one"), ContentModel.TYPE_FOLDER).getChildRef();
@@ -2904,7 +3270,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
// assertEquals(1, permissionService.getAllSetPermissionsForAuthority("andy").get(n10).size());
}
-
+
public void test_DefiningShared_AclUpdatePerformance()
{
runAs("admin");
@@ -2928,7 +3294,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
//assertTrue("Time was "+(end - start)/1000000000.0f, end == start);
}
-
+
public void test_DefiningDefining_AclUpdatePerformance()
{
runAs("admin");
@@ -2943,7 +3309,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
permissionService.setPermission(two, "andy", PermissionService.WRITE, true);
NodeRef test = nodeService.createNode(one, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}test"), ContentModel.TYPE_FOLDER).getChildRef();
permissionService.setPermission(test, "andy", PermissionService.CHANGE_PERMISSIONS, true);
-
+
// test has shared acl
@@ -2956,10 +3322,10 @@ public class PermissionServiceTest extends AbstractPermissionTest
long end = System.nanoTime();
//assertTrue("Time was "+(end - start)/1000000000.0f, end == start);
-
+
}
-
+
public void testAclInsertionPerformanceShared()
{
NodeRef parent = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}one"), ContentModel.TYPE_FOLDER).getChildRef();
@@ -2970,10 +3336,10 @@ public class PermissionServiceTest extends AbstractPermissionTest
long start = System.nanoTime();
permissionService.setPermission(new SimplePermissionEntry(parent, getPermission(PermissionService.CONSUMER), "andy", AccessStatus.ALLOWED));
long end = System.nanoTime();
-
+
//assertTrue("Time was "+(end - start)/1000000000.0f, end == start);
}
-
+
public void testAclInsertionPerformanceDefining()
{
NodeRef parent = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}one"), ContentModel.TYPE_FOLDER).getChildRef();
@@ -3006,7 +3372,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
//assertTrue("Time was "+(end - start)/1000000000.0f, end == start);
}
-
+
public void xtestFindNodesByPermission()
{
diff --git a/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryVoter.java b/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryVoter.java
index b59c3407c3..3397359f3e 100644
--- a/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryVoter.java
+++ b/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryVoter.java
@@ -57,6 +57,8 @@ public class ACLEntryVoter implements AccessDecisionVoter, InitializingBean
private static Log log = LogFactory.getLog(ACLEntryVoter.class);
private static final String ACL_NODE = "ACL_NODE";
+
+ private static final String ACL_PRI_CHILD_ASSOC_ON_CHILD = "ACL_PRI_CHILD_ASSOC_ON_CHILD";
private static final String ACL_PARENT = "ACL_PARENT";
@@ -205,6 +207,7 @@ public class ACLEntryVoter implements AccessDecisionVoter, InitializingBean
{
if ((attribute.getAttribute() != null)
&& (attribute.getAttribute().startsWith(ACL_NODE)
+ || attribute.getAttribute().startsWith(ACL_PRI_CHILD_ASSOC_ON_CHILD)
|| attribute.getAttribute().startsWith(ACL_PARENT)
|| attribute.getAttribute().equals(ACL_ALLOW)
|| attribute.getAttribute().startsWith(ACL_METHOD)
@@ -256,7 +259,7 @@ public class ACLEntryVoter implements AccessDecisionVoter, InitializingBean
MethodInvocation invocation = (MethodInvocation) object;
Method method = invocation.getMethod();
- Class[] params = method.getParameterTypes();
+ Class>[] params = method.getParameterTypes();
Boolean hasMethodEntry = null;
@@ -288,30 +291,95 @@ public class ACLEntryVoter implements AccessDecisionVoter, InitializingBean
hasMethodEntry = Boolean.TRUE;
}
}
- else if (cad.parameter >= invocation.getArguments().length)
+ else if (cad.typeString.equals(ACL_PRI_CHILD_ASSOC_ON_CHILD))
{
- continue;
+ if (cad.parameter.length == 2 && NodeRef.class.isAssignableFrom(params[cad.parameter[0]])
+ && NodeRef.class.isAssignableFrom(params[cad.parameter[1]]))
+ {
+ testNodeRef = getArgument(invocation, cad.parameter[1]);
+ if (testNodeRef != null)
+ {
+ if (nodeService.exists(testNodeRef))
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("\tPermission test on node " + nodeService.getPath(testNodeRef));
+ }
+ ChildAssociationRef primaryParent = nodeService.getPrimaryParent(testNodeRef);
+ NodeRef testParentNodeRef = getArgument(invocation, cad.parameter[0]);
+ if (primaryParent == null || testParentNodeRef == null
+ || !testParentNodeRef.equals(primaryParent.getParentRef()))
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("\tPermission test ignoring secondary parent association to "
+ + testParentNodeRef);
+ }
+ testNodeRef = null;
+ }
+ }
+ else if (log.isDebugEnabled())
+ {
+ log.debug("\tPermission test on non-existing node " + testNodeRef);
+ }
+ }
+ }
+ else if (cad.parameter.length == 1 && ChildAssociationRef.class.isAssignableFrom(params[cad.parameter[0]]))
+ {
+ ChildAssociationRef testParentRef = getArgument(invocation, cad.parameter[0]);
+ if (testParentRef != null)
+ {
+ if (testParentRef.isPrimary())
+ {
+ testNodeRef = testParentRef.getChildRef();
+ if (log.isDebugEnabled())
+ {
+ if (nodeService.exists(testNodeRef))
+ {
+ log.debug("\tPermission test on node " + nodeService.getPath(testNodeRef));
+ }
+ else
+ {
+ log.debug("\tPermission test on non-existing node " + testNodeRef);
+ }
+ }
+ }
+ else if (log.isDebugEnabled())
+ {
+ log.debug("\tPermission test ignoring secondary parent association to "
+ + testParentRef.getParentRef());
+ }
+ }
+ }
+ else
+ {
+ throw new ACLEntryVoterException("The specified parameter is not a NodeRef or ChildAssociationRef");
+ }
}
else if (cad.typeString.equals(ACL_NODE))
{
- if (StoreRef.class.isAssignableFrom(params[cad.parameter]))
+ if (cad.parameter.length != 1)
{
- if (invocation.getArguments()[cad.parameter] != null)
+ throw new ACLEntryVoterException("The specified parameter is not a NodeRef or ChildAssociationRef");
+ }
+ else if (StoreRef.class.isAssignableFrom(params[cad.parameter[0]]))
+ {
+ StoreRef storeRef = getArgument(invocation, cad.parameter[0]);
+ if (storeRef != null)
{
if (log.isDebugEnabled())
{
log.debug("\tPermission test against the store - using permissions on the root node");
}
- StoreRef storeRef = (StoreRef) invocation.getArguments()[cad.parameter];
if (nodeService.exists(storeRef))
{
testNodeRef = nodeService.getRootNode(storeRef);
}
}
}
- else if (NodeRef.class.isAssignableFrom(params[cad.parameter]))
+ else if (NodeRef.class.isAssignableFrom(params[cad.parameter[0]]))
{
- testNodeRef = (NodeRef) invocation.getArguments()[cad.parameter];
+ testNodeRef = getArgument(invocation, cad.parameter[0]);
if (log.isDebugEnabled())
{
if (nodeService.exists(testNodeRef))
@@ -322,14 +390,14 @@ public class ACLEntryVoter implements AccessDecisionVoter, InitializingBean
{
log.debug("\tPermission test on non-existing node " +testNodeRef);
}
-
}
}
- else if (ChildAssociationRef.class.isAssignableFrom(params[cad.parameter]))
+ else if (ChildAssociationRef.class.isAssignableFrom(params[cad.parameter[0]]))
{
- if (invocation.getArguments()[cad.parameter] != null)
+ ChildAssociationRef testChildRef = getArgument(invocation, cad.parameter[0]);
+ if (testChildRef != null)
{
- testNodeRef = ((ChildAssociationRef) invocation.getArguments()[cad.parameter]).getChildRef();
+ testNodeRef = testChildRef.getChildRef();
if (log.isDebugEnabled())
{
if (nodeService.exists(testNodeRef))
@@ -352,9 +420,13 @@ public class ACLEntryVoter implements AccessDecisionVoter, InitializingBean
{
// There is no point having parent permissions for store
// refs
- if (NodeRef.class.isAssignableFrom(params[cad.parameter]))
+ if (cad.parameter.length != 1)
{
- NodeRef child = (NodeRef) invocation.getArguments()[cad.parameter];
+ throw new ACLEntryVoterException("The specified parameter is not a NodeRef or ChildAssociationRef");
+ }
+ else if (NodeRef.class.isAssignableFrom(params[cad.parameter[0]]))
+ {
+ NodeRef child = getArgument(invocation, cad.parameter[0]);
if (child != null)
{
testNodeRef = nodeService.getPrimaryParent(child).getParentRef();
@@ -372,11 +444,12 @@ public class ACLEntryVoter implements AccessDecisionVoter, InitializingBean
}
}
}
- else if (ChildAssociationRef.class.isAssignableFrom(params[cad.parameter]))
+ else if (ChildAssociationRef.class.isAssignableFrom(params[cad.parameter[0]]))
{
- if (invocation.getArguments()[cad.parameter] != null)
+ ChildAssociationRef testParentRef = getArgument(invocation, cad.parameter[0]);
+ if (testParentRef != null)
{
- testNodeRef = ((ChildAssociationRef) invocation.getArguments()[cad.parameter]).getParentRef();
+ testNodeRef = testParentRef.getParentRef();
if (log.isDebugEnabled())
{
if (nodeService.exists(testNodeRef))
@@ -456,6 +529,13 @@ public class ACLEntryVoter implements AccessDecisionVoter, InitializingBean
}
}
+ @SuppressWarnings("unchecked")
+ private T getArgument(MethodInvocation invocation, int index)
+ {
+ Object[] args = invocation.getArguments();
+ return index > args.length ? null : (T)args[index];
+ }
+
private List extractSupportedDefinitions(ConfigAttributeDefinition config)
{
List definitions = new ArrayList(2);
@@ -480,7 +560,7 @@ public class ACLEntryVoter implements AccessDecisionVoter, InitializingBean
SimplePermissionReference required;
- int parameter;
+ int[] parameter;
String authority;
@@ -493,24 +573,36 @@ public class ACLEntryVoter implements AccessDecisionVoter, InitializingBean
}
typeString = st.nextToken();
- if (!(typeString.equals(ACL_NODE) || typeString.equals(ACL_PARENT) || typeString.equals(ACL_ALLOW) || typeString
- .equals(ACL_METHOD) || typeString.equals(ACL_DENY)))
+ if (!(typeString.equals(ACL_NODE) || typeString.equals(ACL_PRI_CHILD_ASSOC_ON_CHILD)
+ || typeString.equals(ACL_PARENT) || typeString.equals(ACL_ALLOW) || typeString.equals(ACL_METHOD) || typeString
+ .equals(ACL_DENY)))
{
throw new ACLEntryVoterException("Invalid type: must be ACL_NODE, ACL_PARENT or ACL_ALLOW");
}
- if (typeString.equals(ACL_NODE) || typeString.equals(ACL_PARENT))
+ if (typeString.equals(ACL_NODE) || typeString.equals(ACL_PRI_CHILD_ASSOC_ON_CHILD)
+ || typeString.equals(ACL_PARENT))
{
- if (st.countTokens() != 3)
+ int count = st.countTokens();
+ if (typeString.equals(ACL_PRI_CHILD_ASSOC_ON_CHILD))
{
- throw new ACLEntryVoterException("There must be four . separated tokens in each config attribute");
+ if (count != 3 && count != 4)
+ {
+ throw new ACLEntryVoterException("There must be three or four . separated tokens in each config attribute");
+ }
+ }
+ else if (count != 3)
+ {
+ throw new ACLEntryVoterException("There must be three . separated tokens in each config attribute");
+ }
+ // Handle a variable number of parameters
+ parameter = new int[count - 2];
+ for (int i=0; i();
+ Set sourceAspects = nodeDAO.getNodeAspects(nodeId);
+ for(QName aspectQName : sourceAspects)
+ {
+ AspectDefinition aspect = dictionaryService.getAspect(aspectQName);
+ if(aspect != null)
+ {
+ aspects.add(aspectQName);
+ }
+ }
}
nodeMetaData.setAspects(aspects);
@@ -593,9 +613,7 @@ public class SOLRTrackingComponentImpl implements SOLRTrackingComponent
{
paths.add(new Pair(catPair.getFirst().getBaseNamePath(tenantService), catPair.getSecond()));
}
-
-
-
+
nodeMetaData.setPaths(paths);
}
@@ -894,4 +912,23 @@ public class SOLRTrackingComponentImpl implements SOLRTrackingComponent
nodeDAO.setCheckNodeConsistency();
return nodeDAO.getMaxTxnIdByCommitTime(maxCommitTime);
}
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.solr.SOLRTrackingComponent#getMaxChangeSetCommitTime()
+ */
+ @Override
+ public Long getMaxChangeSetCommitTime()
+ {
+ return aclDAO.getMaxChangeSetCommitTime();
+ }
+
+ /* (non-Javadoc)
+ * @see org.alfresco.repo.solr.SOLRTrackingComponent#getMaxChangeSetId()
+ */
+ @Override
+ public Long getMaxChangeSetId()
+ {
+ long maxCommitTime = System.currentTimeMillis()+1L;
+ return aclDAO.getMaxChangeSetIdByCommitTime(maxCommitTime);
+ }
}
diff --git a/source/java/org/alfresco/repo/thumbnail/FailedThumbnailSourceAspect.java b/source/java/org/alfresco/repo/thumbnail/FailedThumbnailSourceAspect.java
index 4cc4b8df50..daab686b60 100644
--- a/source/java/org/alfresco/repo/thumbnail/FailedThumbnailSourceAspect.java
+++ b/source/java/org/alfresco/repo/thumbnail/FailedThumbnailSourceAspect.java
@@ -30,6 +30,8 @@ import org.alfresco.repo.policy.Behaviour;
import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.cmr.lock.LockService;
import org.alfresco.service.cmr.lock.LockStatus;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
@@ -136,12 +138,20 @@ public class FailedThumbnailSourceAspect implements NodeServicePolicies.OnDelete
}
@Override
- public void onContentUpdate(NodeRef nodeRef, boolean newContent)
+ public void onContentUpdate(final NodeRef nodeRef, boolean newContent)
{
- if (nodeService.exists(nodeRef) && lockService.getLockStatus(nodeRef) != LockStatus.LOCKED)
+ AuthenticationUtil.runAsSystem(new RunAsWork()
{
- deleteFailedThumbnailChildren(nodeRef);
- }
+ @Override
+ public Object doWork()
+ {
+ if (nodeService.exists(nodeRef) && lockService.getLockStatus(nodeRef) != LockStatus.LOCKED)
+ {
+ deleteFailedThumbnailChildren(nodeRef);
+ }
+ return null;
+ }
+ });
}
/**
diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java
index a964ae1a92..1b0ad92cc2 100644
--- a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java
+++ b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java
@@ -73,6 +73,8 @@ import org.alfresco.service.namespace.QName;
import org.alfresco.util.GUID;
import org.alfresco.util.collections.CollectionUtils;
import org.alfresco.util.collections.Function;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.hibernate.CacheMode;
import org.hibernate.Criteria;
import org.hibernate.FlushMode;
@@ -125,6 +127,8 @@ import org.springmodules.workflow.jbpm31.JbpmTemplate;
*/
public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
{
+ private static Log logger = LogFactory.getLog(JBPMEngine.class);
+
// Implementation dependencies
protected NodeService nodeService;
protected ServiceRegistry serviceRegistry;
@@ -442,7 +446,15 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
{
public WorkflowDefinition apply(ProcessDefinition value)
{
- return createWorkflowDefinition(value);
+ try
+ {
+ return createWorkflowDefinition(value);
+ }
+ catch (Exception ex)
+ {
+ logger.warn("Unable to load workflow definition: '" + value + "' due to exception.", ex);
+ return null;
+ }
}
});
}
@@ -453,7 +465,15 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
{
public WorkflowInstance apply(ProcessInstance value)
{
- return createWorkflowInstance(value);
+ try
+ {
+ return createWorkflowInstance(value);
+ }
+ catch (Exception ex)
+ {
+ logger.warn("Unable to load workflow instance: '" + value + "' due to exception.", ex);
+ return null;
+ }
}
});
}
@@ -893,7 +913,15 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
// retrieve workflow
GraphSession graphSession = context.getGraphSession();
ProcessInstance processInstance = getProcessInstanceIfExists(graphSession, workflowId);
- return createWorkflowInstance(processInstance);
+ try
+ {
+ return createWorkflowInstance(processInstance);
+ }
+ catch (Exception ex)
+ {
+ logger.warn("Unable to load workflow instance: '" + processInstance + "' due to exception.", ex);
+ return null;
+ }
}
});
}
@@ -1104,7 +1132,14 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
ProcessInstance processInstance = processInstances.get(workflowId);
// TODO: Determine if this is the most appropriate way to cancel workflow...
// It might be useful to record point at which it was cancelled etc
- workflowInstances.add(createWorkflowInstance(processInstance));
+ try
+ {
+ workflowInstances.add(createWorkflowInstance(processInstance));
+ }
+ catch(Exception ex)
+ {
+ logger.warn("Unable to load workflow instance: '" + processInstance + "' due to exception.", ex);
+ }
// delete the process instance
graphSession.deleteProcessInstance(processInstance, true, true);
@@ -1514,12 +1549,20 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
/// ------------------------
for(Object[] row : rows)
{
- WorkflowTask workflowTask = makeWorkflowTask(row, taskInstanceCache, variablesCache);
- if(workflowTask !=null )
+ try
{
- workflowTasks.add(workflowTask);
+ WorkflowTask workflowTask = makeWorkflowTask(row, taskInstanceCache, variablesCache);
+ if(workflowTask != null)
+ {
+ workflowTasks.add(workflowTask);
+ }
}
- }
+ catch (Exception ex)
+ {
+ logger.warn("Unable to load workflow instance: '" + row[0] + "' due to exception.", ex);
+ continue;
+ }
+ }
return workflowTasks;
}
@@ -1774,8 +1817,16 @@ public class JBPMEngine extends AlfrescoBpmEngine implements WorkflowEngine
workflowTasks = new ArrayList(filteredTasks.size());
for (TaskInstance task : filteredTasks)
{
- WorkflowTask workflowTask = createWorkflowTask(task);
- workflowTasks.add(workflowTask);
+ try
+ {
+ WorkflowTask workflowTask = createWorkflowTask(task);
+ workflowTasks.add(workflowTask);
+ }
+ catch (Exception ex)
+ {
+ logger.warn("Unable to load workflow task: '" + task + "' due to exception.", ex);
+ continue;
+ }
}
}
diff --git a/source/java/org/alfresco/repo/workflow/jscript/JscriptWorkflowInstance.java b/source/java/org/alfresco/repo/workflow/jscript/JscriptWorkflowInstance.java
index e95a9d70f4..574cfa36e4 100644
--- a/source/java/org/alfresco/repo/workflow/jscript/JscriptWorkflowInstance.java
+++ b/source/java/org/alfresco/repo/workflow/jscript/JscriptWorkflowInstance.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2010 Alfresco Software Limited.
+ * Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -185,9 +185,19 @@ public class JscriptWorkflowInstance implements Serializable
/**
* Delete workflow instance
+ * @deprecated as 'delete' is a JavaScript reserved word and so is unusable. Use {@link #remove()} instead.
*/
- public void delete()
- {
- serviceRegistry.getWorkflowService().deleteWorkflow(this.id);
- }
+ public void delete()
+ {
+ this.remove();
+ }
+
+ /**
+ * Deletes workflow instance.
+ * @since 3.4.9
+ */
+ public void remove()
+ {
+ serviceRegistry.getWorkflowService().deleteWorkflow(this.id);
+ }
}
diff --git a/source/java/org/alfresco/service/cmr/security/PersonService.java b/source/java/org/alfresco/service/cmr/security/PersonService.java
index 324b28dd57..b332c2da37 100644
--- a/source/java/org/alfresco/service/cmr/security/PersonService.java
+++ b/source/java/org/alfresco/service/cmr/security/PersonService.java
@@ -333,4 +333,13 @@ public interface PersonService
*/
@NotAuditable
public int countPeople();
+
+ /**
+ * Is the specified user, enabled
+ * @throws NoSuchPersonException
+ * if the user doesn't exist
+ * @return true = enabled.
+ */
+ @NotAuditable
+ public boolean isEnabled(final String userName);
}
diff --git a/source/java/org/alfresco/util/DynamicallySizedThreadPoolExecutor.java b/source/java/org/alfresco/util/DynamicallySizedThreadPoolExecutor.java
deleted file mode 100644
index 91a5a513c6..0000000000
--- a/source/java/org/alfresco/util/DynamicallySizedThreadPoolExecutor.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2005-2010 Alfresco Software Limited.
- *
- * This file is part of Alfresco
- *
- * Alfresco is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Alfresco 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Alfresco. If not, see .
- */
-package org.alfresco.util;
-
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.RejectedExecutionHandler;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.ReentrantLock;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * This is an instance of {@link java.util.concurrent.ThreadPoolExecutor} which
- * behaves how one would expect it to, even when faced with an unlimited
- * queue. Unlike the default {@link java.util.concurrent.ThreadPoolExecutor}, it
- * will add new Threads up to {@link #setMaximumPoolSize(int) maximumPoolSize}
- * when there is lots of pending work, rather than only when the queue is full
- * (which it often never will be, especially for unlimited queues)
- *
- * @author Nick Burch
- */
-public class DynamicallySizedThreadPoolExecutor extends ThreadPoolExecutor
-{
- private static Log logger = LogFactory.getLog(DynamicallySizedThreadPoolExecutor.class);
-
- private final ReentrantLock lock = new ReentrantLock();
- private int realCorePoolSize;
-
- public DynamicallySizedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
- BlockingQueue workQueue, RejectedExecutionHandler handler)
- {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
- this.realCorePoolSize = corePoolSize;
- }
-
- public DynamicallySizedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
- BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
- {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
- this.realCorePoolSize = corePoolSize;
- }
-
- public DynamicallySizedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
- BlockingQueue workQueue, ThreadFactory threadFactory)
- {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
- this.realCorePoolSize = corePoolSize;
- }
-
- public DynamicallySizedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
- BlockingQueue workQueue)
- {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
- this.realCorePoolSize = corePoolSize;
- }
-
- @Override
- public void setCorePoolSize(int corePoolSize)
- {
- this.realCorePoolSize = corePoolSize;
- super.setCorePoolSize(corePoolSize);
- }
-
- @Override
- public void execute(Runnable command)
- {
- // Do we want to add another thread?
- int threadCount = getPoolSize();
- if(logger.isDebugEnabled())
- {
- logger.debug("Current pool size is " + threadCount + ", real core=" + realCorePoolSize +
- ", current core=" + getCorePoolSize() + ", max=" + getMaximumPoolSize());
- }
-
- if(threadCount < getMaximumPoolSize())
- {
- // We're not yet at the full thread count
-
- // Does the queue size warrant adding one?
- // (If there are more than the maximum pool size of jobs pending,
- // it's time to add another thread)
- int queueSize = getQueue().size() + 1;// New job not yet added
- if(queueSize >= getMaximumPoolSize())
- {
- lock.lock();
- int currentCoreSize = getCorePoolSize();
- if(currentCoreSize < getMaximumPoolSize())
- {
- super.setCorePoolSize(currentCoreSize+1);
-
- if(logger.isInfoEnabled())
- {
- logger.info("Increased pool size to " + getCorePoolSize() + " from " +
- currentCoreSize + " due to queue size of " + queueSize);
- }
- }
- lock.unlock();
- }
- }
-
- // Now run the actual work
- super.execute(command);
- }
-
- @Override
- protected void afterExecute(Runnable r, Throwable t)
- {
- // If the queue is looking empty, allow the pool to
- // get rid of idle threads when it wants to
- int threadCount = getPoolSize();
- if(threadCount == getMaximumPoolSize() && threadCount > realCorePoolSize)
- {
- int queueSize = getQueue().size();
- int currentCoreSize = getCorePoolSize();
- if(queueSize < 2 && currentCoreSize > realCorePoolSize)
- {
- // Almost out of work, allow the pool to reduce threads when
- // required. Double checks the sizing inside a lock to avoid
- // race conditions taking us below the real core size.
- lock.lock();
- currentCoreSize = getCorePoolSize();
- if(currentCoreSize > realCorePoolSize)
- {
- super.setCorePoolSize(currentCoreSize-1);
-
- if(logger.isInfoEnabled())
- {
- logger.info("Decreased pool size to " + getCorePoolSize() + " from " +
- currentCoreSize + " (real core size is " + realCorePoolSize +
- ") due to queue size of " + queueSize);
- }
- }
- lock.unlock();
- }
- }
- }
-}
diff --git a/source/java/org/alfresco/util/TraceableThreadFactory.java b/source/java/org/alfresco/util/TraceableThreadFactory.java
deleted file mode 100644
index f0fde3372b..0000000000
--- a/source/java/org/alfresco/util/TraceableThreadFactory.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2005-2010 Alfresco Software Limited.
- *
- * This file is part of Alfresco
- *
- * Alfresco is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Alfresco 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Alfresco. If not, see .
- */
-package org.alfresco.util;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * A thread factory that spawns threads that are statically visible. Each factory uses a unique
- * thread group. All the groups that have been used can be fetched using
- * {@link #getActiveThreadGroups()}, allowing iteration of the the threads in the group.
- *
- * @since 2.1
- * @author Derek Hulley
- */
-public class TraceableThreadFactory implements ThreadFactory
-{
- private static final AtomicInteger factoryNumber = new AtomicInteger(1);
- private static List activeThreadGroups = Collections.synchronizedList(new ArrayList(1));
-
- /**
- * Get a list of thread groups registered by the factory.
- *
- * @return Returns a snapshot of thread groups
- */
- public static List getActiveThreadGroups()
- {
- return activeThreadGroups;
- }
-
- private final ThreadGroup group;
- private String namePrefix;
- private final AtomicInteger threadNumber;
- private boolean threadDaemon;
- private int threadPriority;
-
-
- public TraceableThreadFactory()
- {
- this.group = new ThreadGroup("TraceableThreadGroup-" + factoryNumber.getAndIncrement());
- TraceableThreadFactory.activeThreadGroups.add(this.group);
-
- this.namePrefix = "TraceableThread-" + factoryNumber.getAndIncrement() + "-thread-";
- this.threadNumber = new AtomicInteger(1);
-
- this.threadDaemon = true;
- this.threadPriority = Thread.NORM_PRIORITY;
- }
-
- /**
- * @param daemon true if all threads created must be daemon threads
- */
- public void setThreadDaemon(boolean daemon)
- {
- this.threadDaemon = daemon;
- }
-
- /**
- *
- * @param threadPriority the threads priority from 1 (lowest) to 10 (highest)
- */
- public void setThreadPriority(int threadPriority)
- {
- this.threadPriority = threadPriority;
- }
-
- public Thread newThread(Runnable r)
- {
- Thread thread = new Thread(
- group,
- r,
- namePrefix + threadNumber.getAndIncrement(),
- 0);
- thread.setDaemon(threadDaemon);
- thread.setPriority(threadPriority);
-
- return thread;
- }
-
- public void setNamePrefix(String namePrefix)
- {
- this.namePrefix = namePrefix;
- }
-
- public String getNamePrefix()
- {
- return this.namePrefix;
- }
-
-}