diff --git a/config/alfresco/extension/bootstrap/split-person-cleanup-context.xml.sample b/config/alfresco/extension/bootstrap/split-person-cleanup-context.xml.sample new file mode 100644 index 0000000000..ee45de9163 --- /dev/null +++ b/config/alfresco/extension/bootstrap/split-person-cleanup-context.xml.sample @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/java/org/alfresco/filesys/netbios/NetBIOSName.java b/source/java/org/alfresco/filesys/netbios/NetBIOSName.java index 097a029f30..3a8cadb630 100644 --- a/source/java/org/alfresco/filesys/netbios/NetBIOSName.java +++ b/source/java/org/alfresco/filesys/netbios/NetBIOSName.java @@ -85,6 +85,10 @@ public class NetBIOSName public static final String SMBServer = "*SMBSERVER"; public static final String SMBServer2 = "*SMBSERV"; + // Adapter status request name + + public static final String AdapterStatusName = "*"; + // Default time to live for name registrations public static final int DefaultTTL = 28800; // 8 hours diff --git a/source/java/org/alfresco/filesys/netbios/NetBIOSPacket.java b/source/java/org/alfresco/filesys/netbios/NetBIOSPacket.java index d21dc96c51..6a1a380e09 100644 --- a/source/java/org/alfresco/filesys/netbios/NetBIOSPacket.java +++ b/source/java/org/alfresco/filesys/netbios/NetBIOSPacket.java @@ -114,14 +114,23 @@ public class NetBIOSPacket public static final int ACT_ERR = 0x06; public static final int CFT_ERR = 0x07; - // Name flags - - public static final int NAME_PERM = 0x02; - public static final int NAME_ACTIVE = 0x04; - public static final int NAME_CONFLICT = 0x08; - public static final int NAME_DEREG = 0x10; - public static final int NAME_GROUP = 0x80; - + // Name flags + + public static final int NAME_PERM = 0x0200; + public static final int NAME_ACTIVE = 0x0400; + public static final int NAME_CONFLICT = 0x0800; + public static final int NAME_DEREG = 0x1000; + public static final int NAME_GROUP = 0x8000; + + public static final int NAME_TYPE_BNODE = 0x0000; + public static final int NAME_TYPE_PNODE = 0x2000; + public static final int NAME_TYPE_MNODE = 0x4000; + public static final int NAME_TYPE_RESVD = 0x6000; + + // Adapter status name in encoded format + + private static final String AdapterStatusNBName = "CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + // NetBIOS packet buffer private byte[] m_nbbuf; @@ -1252,4 +1261,75 @@ public class NetBIOSPacket return pos; } + + /** + * Build an adapter status response + * + * @param nameList NetBIOSNameList + * @param int nodeType + * @return int + */ + public final int buildAdapterStatusResponse(NetBIOSNameList nameList, int nodeType) { + + // Fill in the header + + setOpcode(NetBIOSPacket.RESP_QUERY); + setFlags(NetBIOSPacket.FLG_RECURSDES + NetBIOSPacket.FLG_AUTHANSWER); + + setQuestionCount(0); + setAnswerCount(1); + setAdditionalCount(0); + setNameServiceCount(0); + + // Pack the encoded adapter status name into the NetBIOS packet + + int pos = NB_DATA; + m_nbbuf [ pos++] = ( byte) NAME_LEN; + + pos = DataPacker.putString( AdapterStatusNBName, m_nbbuf, pos, true); + + // Set the name type and class + + DataPacker.putShort (( short) 0x21, m_nbbuf, pos); + pos += 2; + + DataPacker.putShort (( short) 0x01, m_nbbuf, pos); + pos += 2; + + pos = setTTL(pos, 10000); + pos = setResourceDataLength(pos, (nameList.numberOfNames() * 18) + 42); + + // Pack the names + + m_nbbuf[pos++] = (byte) nameList.numberOfNames(); + + for ( int i = 0; i < nameList.numberOfNames(); i++) { + + // Get the current name + + NetBIOSName nbName = nameList.getName( i); + + // Pack the NetBIOS name and flags + + System.arraycopy( nbName.getNetBIOSName(), 0, m_nbbuf, pos, NetBIOSName.NameLength); + pos += NetBIOSName.NameLength; + + int flags = nodeType + NAME_ACTIVE; + if ( nbName.isGroupName()) + flags += NAME_GROUP; + + DataPacker.putShort(( short) flags, m_nbbuf, pos); + pos += 2; + } + + // Zero out the statistics, MAC address area + + for ( int i = 0; i < 42; i++) + m_nbbuf[pos++] = (byte) 0; + + // Set the packet length, and return the length + + setLength(pos); + return getLength(); + } } \ No newline at end of file diff --git a/source/java/org/alfresco/filesys/netbios/server/NetBIOSNameServer.java b/source/java/org/alfresco/filesys/netbios/server/NetBIOSNameServer.java index ddbc0c5a46..f555db4162 100644 --- a/source/java/org/alfresco/filesys/netbios/server/NetBIOSNameServer.java +++ b/source/java/org/alfresco/filesys/netbios/server/NetBIOSNameServer.java @@ -35,6 +35,7 @@ import java.util.Hashtable; import java.util.Vector; import org.alfresco.filesys.netbios.NetBIOSName; +import org.alfresco.filesys.netbios.NetBIOSNameList; import org.alfresco.filesys.netbios.NetBIOSPacket; import org.alfresco.filesys.netbios.NetworkSettings; import org.alfresco.filesys.netbios.RFCNetBIOSProtocol; @@ -1105,10 +1106,20 @@ public class NetBIOSNameServer extends NetworkServer implements Runnable char nameType = searchName.charAt(15); int len = 0; - while (len <= 14 && searchName.charAt(len) != ' ') - len++; + while (len <= 14 && searchName.charAt(len) != ' ' && searchName.charAt(len) != 0) + len++; searchName = searchName.substring(0, len); + // Check if this is an adapter status request + + if ( searchName.equals( NetBIOSName.AdapterStatusName)) { + + // Process the adapter status request + + processAdapterStatus( pkt, fromAddr, fromPort); + return; + } + // Debug if (logger.isDebugEnabled()) @@ -1399,6 +1410,50 @@ public class NetBIOSNameServer extends NetworkServer implements Runnable } /** + * Process an adapter status request + * @param pkt NetBIOSPacket + * @param fromAddr InetAddress + * @param fromPort int + */ + protected final void processAdapterStatus(NetBIOSPacket pkt, InetAddress fromAddr, int fromPort) { + + // Debug + + if (logger.isDebugEnabled()) + logger.debug("%% Adapter status request"); + + // Build the local name list + + NetBIOSNameList nameList = new NetBIOSNameList(); + for ( int i = 0; i < m_localNames.size(); i++) + nameList.addName( m_localNames.get( i)); + + // Build the name query response + + int pktLen = pkt.buildAdapterStatusResponse( nameList, hasPrimaryWINSServer() ? NetBIOSPacket.NAME_TYPE_PNODE : NetBIOSPacket.NAME_TYPE_BNODE); + + // Debug + + if (logger.isDebugEnabled()) { + logger.debug("%% NetBIOS Reply to " + fromAddr.getHostAddress() + " :-"); + pkt.DumpPacket(false); + } + + // Send the reply packet + + try { + + // Send the name query reply + + sendPacket(pkt, pktLen, fromAddr, fromPort); + } + catch (java.io.IOException ex) { + if ( logger.isErrorEnabled()) + logger.error(ex); + } + } + + /** * Remove a local add name listener from the NetBIOS name server. * * @param l AddNameListener diff --git a/source/java/org/alfresco/repo/admin/patch/impl/VersionHistoryPerformancePatch.java b/source/java/org/alfresco/repo/admin/patch/impl/VersionHistoryPerformancePatch.java index de674a4ecd..b323b22514 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/VersionHistoryPerformancePatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/VersionHistoryPerformancePatch.java @@ -42,14 +42,17 @@ public class VersionHistoryPerformancePatch extends AbstractPatch // Get the id String versionedNodeId = (String)this.nodeService.getProperty(nodeRef, VersionModel.PROP_QNAME_VERSIONED_NODE_ID); - // Set the cm:name - this.nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, versionedNodeId); + if (versionedNodeId != null) + { + // Set the cm:name + this.nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, versionedNodeId); - // Move the node - this.nodeService.moveNode( nodeRef, - rootNodeRef, - VersionModel.CHILD_QNAME_VERSION_HISTORIES, - QName.createQName(VersionModel.NAMESPACE_URI, versionedNodeId)); + // Move the node + this.nodeService.moveNode( nodeRef, + rootNodeRef, + VersionModel.CHILD_QNAME_VERSION_HISTORIES, + QName.createQName(VersionModel.NAMESPACE_URI, versionedNodeId)); + } updateCount++; } diff --git a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPInitialDirContextFactoryImpl.java b/source/java/org/alfresco/repo/security/authentication/ldap/LDAPInitialDirContextFactoryImpl.java index 8d74ed1208..c831cfd457 100644 --- a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPInitialDirContextFactoryImpl.java +++ b/source/java/org/alfresco/repo/security/authentication/ldap/LDAPInitialDirContextFactoryImpl.java @@ -28,6 +28,7 @@ import java.util.Collections; import java.util.Hashtable; import java.util.Map; +import javax.naming.AuthenticationNotSupportedException; import javax.naming.Context; import javax.naming.NamingException; import javax.naming.directory.Attribute; @@ -228,6 +229,10 @@ public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFa catch (javax.naming.AuthenticationException ax) { + } + catch(AuthenticationNotSupportedException e) + { + } catch (NamingException nx) { @@ -254,6 +259,10 @@ public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFa { logger.info("LDAP server does not fall back to anonymous bind for a string uid and password at " + env.get(Context.PROVIDER_URL)); } + catch(AuthenticationNotSupportedException e) + { + logger.info("LDAP server does not fall back to anonymous bind for a string uid and password at " + env.get(Context.PROVIDER_URL)); + } catch (NamingException nx) { logger.info("LDAP server does not support simple string user ids and invalid credentials at "+ env.get(Context.PROVIDER_URL)); @@ -279,6 +288,10 @@ public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFa { logger.info("LDAP server does not fall back to anonymous bind for a simple dn and password at " + env.get(Context.PROVIDER_URL)); } + catch(AuthenticationNotSupportedException e) + { + logger.info("LDAP server does not fall back to anonymous bind for a simple dn and password at " + env.get(Context.PROVIDER_URL)); + } catch (NamingException nx) { logger.info("LDAP server does not support simple DN and invalid password at "+ env.get(Context.PROVIDER_URL)); @@ -309,9 +322,13 @@ public class LDAPInitialDirContextFactoryImpl implements LDAPInitialDirContextFa { logger.info("LDAP server does not fall back to anonymous bind for known principal and invalid credentials at " + env.get(Context.PROVIDER_URL)); } + catch(AuthenticationNotSupportedException e) + { + logger.info("LDAP server does not support the required authentication mechanism"); + } catch (NamingException nx) { - // already donw + // already done } } } diff --git a/source/java/org/alfresco/repo/security/person/SplitPersonCleanupBootstrapBean.java b/source/java/org/alfresco/repo/security/person/SplitPersonCleanupBootstrapBean.java new file mode 100644 index 0000000000..43efa922b9 --- /dev/null +++ b/source/java/org/alfresco/repo/security/person/SplitPersonCleanupBootstrapBean.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.security.person; + +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.transaction.TransactionUtil; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.AbstractLifecycleBean; +import org.alfresco.util.GUID; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.doomdark.uuid.UUID; +import org.springframework.context.ApplicationEvent; + +/** + * Remove all duplicate users that have previously been split and had guids added to the uid. This been should be wired up into a custom bootstrap process + * + * @author Andy Hind + */ +public class SplitPersonCleanupBootstrapBean extends AbstractLifecycleBean +{ + protected final static Log log = LogFactory.getLog(SplitPersonCleanupBootstrapBean.class); + + private NodeService nodeService; + + private PersonService personService; + + private TransactionService transactionService; + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + @Override + protected void onBootstrap(ApplicationEvent event) + { + // reindex + log.info("Removing person entries with GUIDS at the end of the uid ..."); + int count = removePeopleWithGUIDBasedIds(); + log.info("... removed " + count); + } + + /** + * Can have uid+GUID or uid + "(" + GUID + ")" + * + * @return + */ + private int removePeopleWithGUIDBasedIds() + { + Integer count = TransactionUtil.executeInUserTransaction(transactionService, + new TransactionUtil.TransactionWork() + { + + public Integer doWork() throws Exception + { + int count = 0; + // A GUID should be 36 chars + + Set people = personService.getAllPeople(); + for (NodeRef person : people) + { + String uid = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty( + person, ContentModel.PROP_USERNAME)); + if (isUIDWithGUID(uid)) + { + // Delete via the person service to get the correct tidy up + personService.deletePerson(uid); + if (log.isDebugEnabled()) + { + log.debug("... removed person with uid " + uid); + } + log.info("... removed person with uid " + uid); + count++; + } + } + return count; + } + + }); + return count.intValue(); + + } + + private boolean isUIDWithGUID(String uid) + { + if (uid.length() > 36) + { + // uid + GUID + // Check the last 36 chars are a valid guid + String guidString = uid.substring(uid.length() - 36); + try + { + UUID id = new UUID(guidString); + // We have a valid guid. + return true; + } + catch (NumberFormatException e) + { + // Not a valid GUID + } + } + + if (uid.length() > 38) + { + // UID + "(" + GUID + ")" + String guidString = uid.substring(uid.length() - 38); + if (guidString.startsWith("(") && guidString.endsWith(")")) + { + guidString = guidString.substring(1, 37); + try + { + UUID id = new UUID(guidString); + // We have a valid guid. + return true; + } + catch (NumberFormatException e) + { + // Not a valid GUID + } + } + + } + + return false; + } + + @Override + protected void onShutdown(ApplicationEvent event) + { + // Nothing to do + } + + public static void main(String[] args) + { + SplitPersonCleanupBootstrapBean tester = new SplitPersonCleanupBootstrapBean(); + String[] test = new String[] { "andy", "andy" + GUID.generate(), "andy(" + GUID.generate() + ")", + GUID.generate() + "banana", "andy" + GUID.generate() + "banana", + "adbadbaddbadbadbadbabdbadbadbabdabdbbadbadbabdbadbadbadb" + + }; + + for (String uid : test) + { + System.out.println(uid + " ... is a uid with guid = " + tester.isUIDWithGUID(uid)); + } + } +}