Liferay LDAP Export to Active Directory disabled user bug.

Liferay 6.1 LDAP tool has a bug where the Liferay user is exported into Active Directory but is disabled by default. After much deliberation I found a workaround where we can write a hook to export users instead of using the out of the box tool. The hook uses a Model Listener on the User Model to look for changes to the user object i.e. the custom code is triggered when anything about the User object changes. The custom code uses the Java JNDI interface to create and or update users in Active Directory.

	@Override
	public void onAfterCreate(User user) throws ModelListenerException {
		LDAPUtil util = new LDAPUtil();	

				try {
					log.info("Attempting to create user: Screen Name: "
							+ user.getScreenName() + " :First Name: "
							+ user.getFirstName() + " : LastName: "
							+ user.getLastName() + " Email:"
							+ user.getEmailAddress()+ " OpenID: "
					+ user.getOpenId()+ " JobTitle: "
					+ user.getJobTitle());
					util.createAdUser(user.getScreenName(),
							user.getPasswordUnencrypted(), user.getFirstName(),
							user.getLastName(), user.getEmailAddress(),
							user.getOpenId(), user.getJobTitle());

				} catch (Exception e) {
					e.printStackTrace();
					throw new ModelListenerException(e);
				}
	}

 	public void createAdUser(String screenName, String password, String firstName, String lastName, String emailAddress, String openID, String jobTitle) {
		try {

			// Create the initial directory context
			LdapContext ctx = getLdapContext();
			String userDn = getUserDN(screenName);

			// Create attributes to be associated with the new user
			Attributes attrs = new BasicAttributes(true);

			// These are the mandatory attributes for a user object
			// Note that Win2K3 will automagically create a random
			// samAccountName if it is not present. (Win2K does not)
			List<String> objectClasses =  Arrays.asList(PropsUtil.get("your.object.classes").split(","));
			for (String objectClass: objectClasses){
				attrs.put("objectClass", objectClass);
			}
			//attrs.put("samAccountName", screenName);
			
			attrs.put("cn", screenName);
			attrs.put("mail", emailAddress);
			// These are some optional (but useful) attributes
			attrs.put("givenName",firstName);
			attrs.put("sn", lastName);
			attrs.put("displayName", screenName);
			if (openID != null && openID.trim().length() > 0){
				log.info("AD User: Setting emplyeeID to: "+openID);
				attrs.put("employeeID", openID);
			}
			if (jobTitle != null && jobTitle.trim().length() > 0){
				log.info("AD User: Setting emplyeeNumber to: "+openID);
				attrs.put("employeeNumber", jobTitle);
			}
			//attrs.put("userPrincipalName", username);

			// Note that the user object must be created before the password
			// can be set. Therefore as the user is created with no
			// password, userAccountControl must be set to the following
			// otherwise the Win2K3 password filter will return error 53
			// unwilling to perform.
			attrs.put("userAccountControl", Integer.toString(UF_NORMAL_ACCOUNT + UF_PASSWD_NOTREQD + UF_PASSWORD_EXPIRED + UF_ACCOUNTDISABLE));
			log.info("Creating AD user: " + userDn);
			// Create the context
			Context result = ctx.createSubcontext(userDn, attrs);
			log.info("Created account: " + userDn);

			// Set password is a ldap modfy operation
			// and we'll update the userAccountControl
			// enabling the account and forcing the password to never expire
			if (password != null) {
				ModificationItem[] mods = new ModificationItem[2];

				//Replace the "unicdodePwd" attribute with a new value
				//Password must be both Unicode and a quoted string
				String newQuotedPassword = "\"" + password + "\"";
				byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");

				mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("unicodePwd", newUnicodePassword));
				mods[1] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("userAccountControl", Integer.toString(UF_NORMAL_ACCOUNT + UF_DONT_EXPIRE_PASSWD)));

				// Perform the update
				ctx.modifyAttributes(userDn, mods);
				log.info("Successfully set password and updated userAccountControl");
			}

			ctx.close();

		} catch (NamingException e) {
			log.info("NamingException while creating AD user " + screenName);
			throw new RuntimeException(e);
		} catch (IOException e) {
			log.info("IOException while creating AD user " + screenName);
			throw new RuntimeException(e);
		}
	}