/*
* ====================
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of the Common Development
* and Distribution License("CDDL") (the "License"). You may not use this file
* except in compliance with the License.
*
* You can obtain a copy of the License at
* http://opensource.org/licenses/cddl1.php
* See the License for the specific language governing permissions and limitations
* under the License.
*
* When distributing the Covered Code, include this CDDL Header Notice in each file
* and include the License file at http://opensource.org/licenses/cddl1.php.
* If applicable, add the following below this CDDL Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
* ====================
*/
package org.identityconnectors.vms;
import static org.identityconnectors.vms.VmsConstants.ATTR_ACCOUNT;
import static org.identityconnectors.vms.VmsConstants.ATTR_ASTLM;
import static org.identityconnectors.vms.VmsConstants.ATTR_BATCH;
import static org.identityconnectors.vms.VmsConstants.ATTR_BIOLM;
import static org.identityconnectors.vms.VmsConstants.ATTR_BYTLM;
import static org.identityconnectors.vms.VmsConstants.ATTR_CLI;
import static org.identityconnectors.vms.VmsConstants.ATTR_CLITABLES;
import static org.identityconnectors.vms.VmsConstants.ATTR_COPY_LOGIN_SCRIPT;
import static org.identityconnectors.vms.VmsConstants.ATTR_CPUTIME;
import static org.identityconnectors.vms.VmsConstants.ATTR_CREATE_DIRECTORY;
import static org.identityconnectors.vms.VmsConstants.ATTR_DEFAULT;
import static org.identityconnectors.vms.VmsConstants.ATTR_DEFPRIVILEGES;
import static org.identityconnectors.vms.VmsConstants.ATTR_DEVICE;
import static org.identityconnectors.vms.VmsConstants.ATTR_DIALUP;
import static org.identityconnectors.vms.VmsConstants.ATTR_DIOLM;
import static org.identityconnectors.vms.VmsConstants.ATTR_DIRECTORY;
import static org.identityconnectors.vms.VmsConstants.ATTR_ENQLM;
import static org.identityconnectors.vms.VmsConstants.ATTR_FILLM;
import static org.identityconnectors.vms.VmsConstants.ATTR_FLAGS;
import static org.identityconnectors.vms.VmsConstants.ATTR_GRANT_IDS;
import static org.identityconnectors.vms.VmsConstants.ATTR_JTQUOTA;
import static org.identityconnectors.vms.VmsConstants.ATTR_LGICMD;
import static org.identityconnectors.vms.VmsConstants.ATTR_LOCAL;
import static org.identityconnectors.vms.VmsConstants.ATTR_LOGIN_FAILS;
import static org.identityconnectors.vms.VmsConstants.ATTR_LOGIN_SCRIPT_SOURCE;
import static org.identityconnectors.vms.VmsConstants.ATTR_MAXACCTJOBS;
import static org.identityconnectors.vms.VmsConstants.ATTR_MAXDETACH;
import static org.identityconnectors.vms.VmsConstants.ATTR_MAXJOBS;
import static org.identityconnectors.vms.VmsConstants.ATTR_NETWORK;
import static org.identityconnectors.vms.VmsConstants.ATTR_OWNER;
import static org.identityconnectors.vms.VmsConstants.ATTR_PBYTLM;
import static org.identityconnectors.vms.VmsConstants.ATTR_PGFLQUOTA;
import static org.identityconnectors.vms.VmsConstants.ATTR_PRCLM;
import static org.identityconnectors.vms.VmsConstants.ATTR_PRIMEDAYS;
import static org.identityconnectors.vms.VmsConstants.ATTR_PRIORITY;
import static org.identityconnectors.vms.VmsConstants.ATTR_PRIVILEGES;
import static org.identityconnectors.vms.VmsConstants.ATTR_PWDEXPIRED;
import static org.identityconnectors.vms.VmsConstants.ATTR_PWDMINIMUM;
import static org.identityconnectors.vms.VmsConstants.ATTR_QUEPRIO;
import static org.identityconnectors.vms.VmsConstants.ATTR_REMOTE;
import static org.identityconnectors.vms.VmsConstants.ATTR_SHRFILLM;
import static org.identityconnectors.vms.VmsConstants.ATTR_TQELM;
import static org.identityconnectors.vms.VmsConstants.ATTR_UIC;
import static org.identityconnectors.vms.VmsConstants.ATTR_WSDEFAULT;
import static org.identityconnectors.vms.VmsConstants.ATTR_WSEXTENT;
import static org.identityconnectors.vms.VmsConstants.ATTR_WSQUOTA;
import static org.identityconnectors.vms.VmsConstants.FLAG_DISUSER;
import java.io.IOException;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.TreeSet;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.identityconnectors.common.CollectionUtil;
import org.identityconnectors.common.StringUtil;
import org.identityconnectors.common.logging.Log;
import org.identityconnectors.common.script.ScriptExecutor;
import org.identityconnectors.common.script.ScriptExecutorFactory;
import org.identityconnectors.common.security.GuardedString;
import org.identityconnectors.framework.common.exceptions.AlreadyExistsException;
import org.identityconnectors.framework.common.exceptions.ConnectorException;
import org.identityconnectors.framework.common.exceptions.UnknownUidException;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.AttributeBuilder;
import org.identityconnectors.framework.common.objects.AttributeInfo;
import org.identityconnectors.framework.common.objects.AttributeInfoBuilder;
import org.identityconnectors.framework.common.objects.AttributeInfoUtil;
import org.identityconnectors.framework.common.objects.AttributeUtil;
import org.identityconnectors.framework.common.objects.ConnectorObject;
import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder;
import org.identityconnectors.framework.common.objects.Name;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.ObjectClassInfo;
import org.identityconnectors.framework.common.objects.ObjectClassInfoBuilder;
import org.identityconnectors.framework.common.objects.OperationOptions;
import org.identityconnectors.framework.common.objects.OperationalAttributeInfos;
import org.identityconnectors.framework.common.objects.OperationalAttributes;
import org.identityconnectors.framework.common.objects.PredefinedAttributeInfos;
import org.identityconnectors.framework.common.objects.PredefinedAttributes;
import org.identityconnectors.framework.common.objects.ResultsHandler;
import org.identityconnectors.framework.common.objects.Schema;
import org.identityconnectors.framework.common.objects.SchemaBuilder;
import org.identityconnectors.framework.common.objects.ScriptContext;
import org.identityconnectors.framework.common.objects.Uid;
import org.identityconnectors.framework.common.objects.filter.EqualsFilter;
import org.identityconnectors.framework.common.objects.filter.FilterTranslator;
import org.identityconnectors.framework.spi.AttributeNormalizer;
import org.identityconnectors.framework.spi.Configuration;
import org.identityconnectors.framework.spi.ConnectorClass;
import org.identityconnectors.framework.spi.PoolableConnector;
import org.identityconnectors.framework.spi.operations.AuthenticateOp;
import org.identityconnectors.framework.spi.operations.CreateOp;
import org.identityconnectors.framework.spi.operations.DeleteOp;
import org.identityconnectors.framework.spi.operations.ResolveUsernameOp;
import org.identityconnectors.framework.spi.operations.SchemaOp;
import org.identityconnectors.framework.spi.operations.ScriptOnResourceOp;
import org.identityconnectors.framework.spi.operations.SearchOp;
import org.identityconnectors.framework.spi.operations.UpdateOp;
import org.identityconnectors.patternparser.MapTransform;
import org.identityconnectors.patternparser.Transform;
@ConnectorClass(displayNameKey = "VMSConnector", configurationClass = VmsConfiguration.class)
public class VmsConnector implements PoolableConnector, AuthenticateOp, CreateOp, DeleteOp,
SearchOp<String>, UpdateOp, SchemaOp, AttributeNormalizer, ScriptOnResourceOp,
ResolveUsernameOp {
private static final Log LOG = Log.getLog(VmsConnector.class);
private DateFormat vmsDateFormatWithSecs;
private DateFormat vmsDateFormatWithoutSecs;
private VmsConfiguration configuration;
private VmsConnection connection;
private Schema schema;
private Map<String, AttributeInfo> attributeInfoMap;
private Set<String> returnedByDefault;
private String changeOwnPasswordCommandScript;
private ScriptExecutor changeOwnPasswordCommandExecutor;
private String multipleAuthorizeCommandScript;
private ScriptExecutor multipleAuthorizeCommandExecutor;
private String dateCommandScript;
private ScriptExecutor dateCommandExecutor;
private final static Pattern ERROR_PATTERN = Pattern.compile("%\\w+-\\w-\\w+,[^\\n]*\\n");
private final static String VMS_DELTA_FORMAT =
"{0,number,##}-{1,number,##}:{2,number,##}:{3,number,##}.{4,number,00}";
private static final String SEPARATOR = "Username: ";
private static final String UAF_PROMPT = "UAF>";
private static final String UAF_PROMPT_CONTINUE = "_UAF>";
private static final Transform TRANSFORM = new MapTransform(VmsAuthorizeInfo.getInfo());
public static final int LONG_WAIT = 60000;
public static final int SHORT_WAIT = 60000;
private static final int SEGMENT_MAX = 250;
/* @formatter:off */
// VMS messages we search for
//
private static final String CLI_WARNING = "%CLI-W-"; // various CLI errors
private static final String USER_ADDED = "%UAF-I-ADDMSG,"; // user record successfully added
private static final String USER_EXISTS = "%UAF-E-UAEERR,"; // invalid user name, user name already exists
private static final String USER_REMOVED = "%UAF-I-REMMSG,"; // record removed from system authorization file
private static final String USER_RENAMED = "%UAF-I-RENMSG,"; // user record renamed
private static final String NO_MODS = "%UAF-I-NOMODS,"; // no modifications made
private static final String USER_UPDATED = "%UAF-I-MDFYMSG,"; // user record(s) updated
private static final String BAD_USER = "%UAF-W-BADUSR,"; // user name does not exist
private static final String BAD_SPEC = "%UAF-W-BADSPC,"; // no user matches specification
private static final String UAF_ERROR = "%UAF-E-"; // any UAF error message
private static final String DUP_IDENT = "-SYSTEM-F-DUPIDENT"; // duplicate identifier
private static final String DUPLNAM = "-SYSTEM-F-DUPLNAM"; // duplicate name
/* @formatter:on */
public VmsConnector() {
try {
changeOwnPasswordCommandScript =
VmsUtilities
.readFileFromClassPath("org/identityconnectors/vms/UserPasswordScript.txt");
multipleAuthorizeCommandScript =
VmsUtilities
.readFileFromClassPath("org/identityconnectors/vms/MultipleAuthorizeCommandScript.txt");
dateCommandScript =
VmsUtilities
.readFileFromClassPath("org/identityconnectors/vms/DateCommandScript.txt");
if (StringUtil.isEmpty(changeOwnPasswordCommandScript)) {
throw new ConnectorException("Internal error locating command scripts");
}
} catch (IOException ioe) {
throw ConnectorException.wrap(ioe);
}
}
/**
* Since the exclamation point is a comment delimiter in DCL (and AUTHORIZE
* input), we must quote any strings in which it appears. We also need to
* quote if any whitespace is contained
*
* @param unquoted
* @return
*/
protected String quoteForAuthorizeWhenNeeded(String unquoted) {
boolean quote = !Pattern.matches("(\\w)+", unquoted);
if (unquoted.length() == 0) {
quote = true;
}
if (!quote) {
return unquoted;
}
return quoteStringForAuthorize(unquoted);
}
protected String quoteForDCLWhenNeeded(String unquoted, boolean needsQuote) {
boolean quote = needsQuote || !Pattern.matches("(\\w)+", unquoted);
if (unquoted.length() == 0) {
quote = true;
}
if (!quote) {
return unquoted;
}
return quoteStringForDCL(unquoted);
}
/**
* VMS quoting rules:
* <ul>
* <li>replace a single quote with the sequence "+"'"+" <br>
* - end the string, append a single quote, append the remainder
* <li>replace a double quote with the sequence ""
* </ul>
* Then add double quotes around the entire string
*
* @param string
* @return
*/
private String quoteStringForDCL(String string) {
return "\"" + string.replaceAll("\"", "\"\"").replaceAll("'", "\"+\"'\"+\"") + "\"";
}
/**
* Authorize allows quoted strings with an embedded '"' indicated by '""'.
*
* @param string
* @return
*/
private String quoteStringForAuthorize(String string) {
return "\"" + string.replaceAll("\"", "\"\"") + "\"";
}
protected String asString(Object object) {
if (object instanceof GuardedString) {
GuardedString guarded = (GuardedString) object;
GuardedStringAccessor accessor = new GuardedStringAccessor();
guarded.access(accessor);
char[] result = accessor.getArray();
return new String(result);
} else if (object != null) {
return object.toString();
} else {
return null;
}
}
protected String listToVmsValueList(String name, List<Object> values) {
if (values.size() == 1) {
if (values.get(0) == null) {
return null;
} else {
String result = asString(values.get(0));
if (mayNeedQuotes(name)) {
result = quoteForAuthorizeWhenNeeded(result);
}
return result;
}
} else {
StringBuffer buffer = new StringBuffer();
char separator = '(';
for (Object value : values) {
buffer.append(separator);
String result = asString(value);
if (mayNeedQuotes(name)) {
result = quoteForAuthorizeWhenNeeded(result);
}
buffer.append(result);
separator = ',';
}
buffer.append(")");
return buffer.toString();
}
}
private boolean mayNeedQuotes(String name) {
return (name.equalsIgnoreCase(OperationalAttributes.PASSWORD_NAME)
|| name.equalsIgnoreCase(ATTR_OWNER) || name.equalsIgnoreCase(ATTR_ACCOUNT));
}
protected boolean isPresent(String base, String subString) {
if (base == null) {
return false;
} else {
return base.contains(subString);
}
}
/**
* Split the privileges into two command segments, so that they fit even in
* old VMS.
*
* @param prefix
* @param value
* @return
*/
private List<StringBuffer> privsCommand(String prefix, List<Object> value) {
List<StringBuffer> commandList = new LinkedList<StringBuffer>();
StringBuffer command = new StringBuffer();
commandList.add(command);
StringBuffer buffer = new StringBuffer();
buffer.append("MODIFY " + prefix + "=(");
String separator = "";
int i;
for (i = 0; i < value.size(); i++) {
String string = value.get(i).toString();
if (buffer.length() + (string.length() + 2) > 250) {
break;
}
buffer.append(separator + string);
separator = ",";
}
buffer.append("-");
command.append(buffer.toString());
command = new StringBuffer();
commandList.add(command);
buffer = new StringBuffer();
for (; i < value.size(); i++) {
String string = value.get(i).toString();
if (buffer.length() + (string.length() + 2) > 250) {
break;
}
buffer.append(separator + string);
}
buffer.append(")");
command.append(buffer.toString());
return commandList;
}
private List<StringBuffer> appendAttributes(boolean modify, String prefix,
Map<String, Attribute> attrMap) {
List<StringBuffer> commandList = new LinkedList<StringBuffer>();
StringBuffer command = new StringBuffer();
if (modify) {
command.append("MODIFY ");
} else {
command.append("ADD ");
}
command.append(prefix);
commandList.add(command);
// Administrative user doesn't need current password
//
Attribute currentPassword = attrMap.remove(OperationalAttributes.CURRENT_PASSWORD_NAME);
// Password expiration date is handled by the /PWDEXPIRED qualifier
//
Attribute expiration = attrMap.remove(OperationalAttributes.PASSWORD_EXPIRED_NAME);
if (expiration != null) {
VmsAttributeValidator.validate(OperationalAttributes.PASSWORD_EXPIRED_NAME, expiration
.getValue(), configuration);
if (AttributeUtil.getBooleanValue(expiration)) {
String value = "/" + ATTR_PWDEXPIRED;
command = appendToCommand(commandList, command, value);
} else {
String value = "/NO" + ATTR_PWDEXPIRED;
command = appendToCommand(commandList, command, value);
}
} else {
Attribute password = attrMap.get(OperationalAttributes.PASSWORD_NAME);
// If the password is being changed, VMS automatically pre-expires
// the password.
// If it wasn't already expired, we don't want to change that.
//
if (password != null && modify) {
// Get the user and find if currently pre-expired
//
List<StringBuffer> addCommand = new LinkedList<StringBuffer>();
StringBuffer buffer = new StringBuffer();
buffer.append("SHOW " + prefix);
addCommand.add(buffer);
Map<String, Object> variables = getVariablesForCommand();
fillInCommand(addCommand, variables);
String result = "";
try {
result = (String) multipleAuthorizeCommandExecutor.execute(variables);
if (!isPresent(result, "(pre-expired)")) {
String value = "/" + "NO" + ATTR_PWDEXPIRED;
command = appendToCommand(commandList, command, value);
}
} catch (Exception e) {
LOG.error(e, "error in create");
throw new ConnectorException(configuration
.getMessage(modify ? VmsMessages.ERROR_IN_MODIFY
: VmsMessages.ERROR_IN_CREATE), e);
}
if (isPresent(result, CLI_WARNING)) {
throw new ConnectorException(configuration.getMessage(
modify ? VmsMessages.ERROR_IN_MODIFY2 : VmsMessages.ERROR_IN_CREATE2,
result));
}
}
}
// Password change interval is handled by the /PWDLIFETIME qualifier
//
Attribute changeInterval =
attrMap.remove(PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME);
if (changeInterval != null) {
VmsAttributeValidator.validate(PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME,
changeInterval.getValue(), configuration);
long expirationTime = 0;
if (!CollectionUtil.isEmpty(changeInterval.getValue())) {
expirationTime = AttributeUtil.getLongValue(changeInterval).longValue();
}
if (expirationTime == 0) {
String value = "/NOPWDLIFETIME";
command = appendToCommand(commandList, command, value);
} else {
String deltaValue = remapToDelta(expirationTime);
String value = "/PWDLIFETIME=" + quoteForAuthorizeWhenNeeded(deltaValue);
command = appendToCommand(commandList, command, value);
}
}
// Disable Date is handled by the /EXPIRED qualifier
//
Attribute disableDate = attrMap.remove(OperationalAttributes.DISABLE_DATE_NAME);
if (disableDate != null) {
VmsAttributeValidator.validate(OperationalAttributes.DISABLE_DATE_NAME, disableDate
.getValue(), configuration);
long disableTime = 0;
if (!CollectionUtil.isEmpty(disableDate.getValue())) {
disableTime = AttributeUtil.getLongValue(disableDate).longValue();
}
if (disableTime == 0) {
String value = "/NOEXPIRED";
command = appendToCommand(commandList, command, value);
} else {
String deltaValue = vmsDateFormatWithoutSecs.format(new Date(disableTime));
String value = "/EXPIRED=" + quoteForAuthorizeWhenNeeded(deltaValue);
command = appendToCommand(commandList, command, value);
}
}
// Various Access modifiers (e.g., BATCH=(PRIMARY, 12-7)) are handled by
// NETWORK
// BATCH
// LOCAL
// DIALUP
// REMOTE
//
for (String accessorName : ACCESSORS) {
Attribute accessor = attrMap.remove(accessorName.toUpperCase());
if (accessor != null) {
VmsAttributeValidator.validate(accessorName, accessor.getValue(), configuration);
command =
appendToCommand(commandList, command,
appendAccessor(accessorName, accessor));
}
}
for (Attribute attribute : attrMap.values()) {
String name = attribute.getName();
List<Object> values = new LinkedList<Object>();
if (attribute.getValue() != null) {
values.addAll(attribute.getValue());
}
// Need to update values for list-valued attributes to specify
// negated values, as well as positive values
if (ATTR_FLAGS.equalsIgnoreCase(name)) {
updateValues(values, VmsAttributeValidator.FLAGS_LIST);
} else if (ATTR_PRIVILEGES.equalsIgnoreCase(name)) {
updateValues(values, VmsAttributeValidator.PRIVS_LIST);
} else if (ATTR_DEFPRIVILEGES.equalsIgnoreCase(name)) {
updateValues(values, VmsAttributeValidator.PRIVS_LIST);
} else if (ATTR_PRIMEDAYS.equalsIgnoreCase(name)) {
updateValues(values, VmsAttributeValidator.PRIMEDAYS_LIST);
}
// We treat empty lists as value needed to clear the attribute
// Dates are removed with NO prefix
// Deltas are removed with NO prefix
// Numbers get set to 0
// Strings get set to empty string
//
if (values.size() == 0) {
schema();
AttributeInfo info = attributeInfoMap.get(name);
if (info != null) {
if (OperationalAttributes.DISABLE_DATE_NAME.equals(name)
|| ATTR_CPUTIME.equals(name)
|| PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME.equals(name)) {
values.add(Boolean.FALSE);
} else if (info.getClass().isInstance(Number.class)
|| info.getClass().isInstance(short.class)
|| info.getClass().isInstance(int.class)
|| info.getClass().isInstance(float.class)
|| info.getClass().isInstance(double.class)
|| info.getClass().isInstance(long.class)) {
values.add(0);
} else if (info.getClass().isInstance(Boolean.class)) {
values.add(Boolean.FALSE);
} else {
values.add("");
}
} else {
values.add("");
}
}
if (!name.equals(Name.NAME) && isNeedsValidation(attribute)) {
VmsAttributeValidator.validate(name, values, configuration);
if (values.size() == 1 && values.get(0) instanceof Boolean) {
// We use boolean value to indicate negatable, valueless
// attributes, and to clear time-based attributes
//
String value = null;
if (((Boolean) values.get(0)).booleanValue()) {
value = "/" + remapName(attribute);
} else {
value = "/NO" + remapName(attribute);
}
command = appendToCommand(commandList, command, value);
} else {
if (isDateTimeAttribute(name)) {
remapToDateTime(values);
}
if (isDeltaAttribute(name)) {
remapToDelta(values);
}
String value = listToVmsValueList(attribute.getName(), values);
String first = "/" + remapName(attribute) + "=";
if (command.length() + first.length() + value.length() > SEGMENT_MAX) {
command = addNewCommandSegment(commandList, command);
}
command.append(first);
command.append(value);
}
}
}
return commandList;
}
/**
* When an accessor is specified, both the PRIMARY and SECONDARY values are
* either present or defaulted. The default is no values, thus
* ACCESS=(PRIMARY, 12) turns on PRIMARY access at 12, and turns OFF
* SECONDARY access for all 24 hours NOACCESS=(PRIMARY, 12) turns off
* PRIMARY access at 12, and turns ON SECONDARY access for all 24 hours
*
* @param accessorName
* @param accessor
* @return
*/
private String appendAccessor(String accessorName, Attribute accessor) {
if (accessor == null) {
return "";
}
List<Object> accessorValues = accessor.getValue();
// Separate out primary and secondary hours
//
String primary = (String) accessorValues.get(0);
String secondary = (String) accessorValues.get(1);
// If both noPrimary and noSecondary, we need to use the negative
// form, otherwise, the positive form
//
if (StringUtil.isBlank(primary)) {
if (StringUtil.isBlank(secondary)) {
return "/NO" + accessorName.toUpperCase();
} else {
return "/" + accessorName.toUpperCase() + "=(SECONDARY," + secondary + ")";
}
} else if (StringUtil.isBlank(secondary)) {
return "/" + accessorName.toUpperCase() + "=(PRIMARY," + primary;
} else {
return "/" + accessorName.toUpperCase() + "=(PRIMARY," + primary + ",SECONDARY,"
+ secondary + ")";
}
}
private StringBuffer appendToCommand(List<StringBuffer> commandList, StringBuffer command,
String value) {
if (command.length() + value.length() > SEGMENT_MAX) {
command = addNewCommandSegment(commandList, command);
}
command.append(value);
return command;
}
/**
* The values list is updated to hold negated values for every possibility
* that is not in the list.
*
* @param values
* @param possibilities
*/
private void updateValues(List<Object> values, Collection<String> possibilities) {
// Do case-insensitive value comparison
//
Collection<String> newValues = CollectionUtil.newCaseInsensitiveSet();
for (Object value : values) {
newValues.add(value.toString());
}
for (String possibility : possibilities) {
if (!newValues.contains(possibility)) {
values.add("NO" + possibility);
}
}
}
private StringBuffer addNewCommandSegment(List<StringBuffer> commandList, StringBuffer command) {
command.append("-");
command = new StringBuffer();
commandList.add(command);
return command;
}
private boolean isDateTimeAttribute(String attributeName) {
return OperationalAttributes.DISABLE_DATE_NAME.equals(attributeName);
}
private boolean isDeltaAttribute(String attributeName) {
if (PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME.equals(attributeName)) {
return true;
}
if (ATTR_CPUTIME.equals(attributeName)) {
return true;
}
return false;
}
private void remapToDateTime(List<Object> values) {
for (int i = 0; i < values.size(); i++) {
Object value = values.get(i);
values.set(i, vmsDateFormatWithoutSecs.format(new Date((Long) value)));
}
}
static void remapToDelta(List<Object> values) {
for (int i = 0; i < values.size(); i++) {
Object value = values.get(i);
value = remapToDelta((Long) value);
values.set(i, value);
}
}
private static final Pattern DELTA_PATTERN = Pattern
.compile("(?:(\\d+)\\s)?(\\d+):(\\d+)(?::(\\d+))?(?:.(\\d+))?");
private long remapFromDelta(String delta) {
if (delta == null) {
return 0;
}
Matcher matcher = DELTA_PATTERN.matcher(delta);
if (matcher.matches()) {
String daysS = matcher.group(1);
String hoursS = matcher.group(2);
String minutesS = matcher.group(3);
String secondsS = matcher.group(4);
String centisecondsS = matcher.group(5);
long days = daysS == null ? 0 : Long.parseLong(daysS);
long hours = Long.parseLong(hoursS);
long minutes = Long.parseLong(minutesS);
long seconds = secondsS == null ? 0 : Long.parseLong(secondsS);
long centiseconds = centisecondsS == null ? 0 : Long.parseLong(centisecondsS);
long result =
((((((((days * 24) + hours) * 60) + minutes) * 60) + seconds) * 100) + centiseconds) * 10;
return result;
}
return 0;
}
private static String remapToDelta(Long longValue) {
// Convert to hundredths of a second
longValue /= 10;
long centiseconds = longValue % 100;
// Convert to seconds
longValue /= 100;
long seconds = longValue % 60;
// Convert to minutes
longValue /= 60;
long minutes = longValue % 60;
// Convert to hours
longValue /= 60;
long hours = longValue % 24;
// Convert to days
longValue /= 24;
long days = longValue;
String value =
MessageFormat.format(VMS_DELTA_FORMAT, new Object[] { days, hours, minutes,
seconds, centiseconds });
return value;
}
private String remapName(Attribute attribute) {
if (attribute.is(OperationalAttributes.PASSWORD_NAME)) {
return "PASSWORD";
}
if (attribute.is(PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME)) {
return "PWDLIFETIME";
} else {
return attribute.getName();
}
}
private boolean isNeedsValidation(Attribute attribute) {
if (attribute.is(OperationalAttributes.CURRENT_PASSWORD_NAME)) {
return false;
}
if (attribute instanceof Uid) {
return false;
}
return true;
}
/**
* {@inheritDoc}
*/
public Uid create(ObjectClass objectClass, final Set<Attribute> originalAttrs,
final OperationOptions options) {
if (!objectClass.is(ObjectClass.ACCOUNT_NAME)) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.UNSUPPORTED_OBJECT_CLASS, objectClass.getObjectClassValue()));
}
Map<String, Attribute> attrMap =
new HashMap<String, Attribute>(AttributeUtil.toMap(originalAttrs));
// Extract action attributes
//
Attribute grantIds = attrMap.remove(ATTR_GRANT_IDS);
Attribute createDirectory = attrMap.remove(ATTR_CREATE_DIRECTORY);
Attribute copyLoginScript = attrMap.remove(ATTR_COPY_LOGIN_SCRIPT);
Attribute loginScriptSource = attrMap.remove(ATTR_LOGIN_SCRIPT_SOURCE);
Name name = (Name) attrMap.get(Name.NAME);
// If UIC contains wildcard, compute an appropriate value
//
Attribute uic = attrMap.get(ATTR_UIC);
if (uic == null || StringUtil.isBlank(AttributeUtil.getStringValue(uic))) {
throw new ConnectorException(configuration.getMessage(VmsMessages.NULL_ATTRIBUTE_VALUE,
ATTR_UIC));
}
VmsAttributeValidator.validate(ATTR_UIC, uic.getValue(), configuration);
String unusedUic = null;
boolean uniqueUicRequired = false;
String uicValue = AttributeUtil.getStringValue(uic);
if (uicValue.contains("*")) {
uniqueUicRequired = true;
unusedUic = getUnusedUicForGroup(uicValue);
attrMap.put(ATTR_UIC, AttributeBuilder.build(ATTR_UIC, unusedUic));
}
String accountId = name.getNameValue();
LOG.info("create(''{0}'')", accountId);
// Enable/disable is handled by FLAGS=([NO]DISUSER)
// (since VMS defaults to disabled, we enable if not explicitly
// specified)
//
Attribute enable = attrMap.get(OperationalAttributes.ENABLE_NAME);
if (enable == null) {
attrMap.put(OperationalAttributes.ENABLE_NAME, AttributeBuilder.build(
OperationalAttributes.ENABLE_NAME, Boolean.TRUE));
}
updateEnableAttribute(attrMap);
Attribute privileges =
configuration.getLongCommands() ? null : attrMap.remove(ATTR_PRIVILEGES);
Attribute defPrivileges =
configuration.getLongCommands() ? null : attrMap.remove(ATTR_DEFPRIVILEGES);
Attribute flags = configuration.getLongCommands() ? null : attrMap.remove(ATTR_FLAGS);
List<StringBuffer> addCommand = appendAttributes(false, accountId, attrMap);
String result = "";
List<List<StringBuffer>> commandList = new LinkedList<List<StringBuffer>>();
try {
commandList.add(addCommand);
Map<String, Object> variables = getVariablesForCommand();
fillInMultipleCommand(commandList, variables);
result = (String) multipleAuthorizeCommandExecutor.execute(variables);
} catch (Exception e) {
LOG.error(e, "error in create");
throw new ConnectorException(configuration.getMessage(VmsMessages.ERROR_IN_CREATE), e);
}
if (isPresent(result, CLI_WARNING)) {
throw new ConnectorException(configuration.getMessage(VmsMessages.ERROR_IN_CREATE2,
result));
}
if (isPresent(result, USER_ADDED)) {
LOG.info("user created");
} else if (isPresent(result, USER_EXISTS)) {
throw new AlreadyExistsException();
} else {
// If we can locate an error message, we use it
//
Matcher matcher = ERROR_PATTERN.matcher(result);
if (matcher.find()) {
result = matcher.group();
}
throw new ConnectorException(configuration.getMessage(VmsMessages.ERROR_IN_CREATE2,
result));
}
// We do the modifies separately, just in case the user already existed,
// and the create failed
//
result = "";
commandList = new LinkedList<List<StringBuffer>>();
try {
if (privileges != null) {
commandList.add(updatePrivileges(accountId, privileges));
}
if (defPrivileges != null) {
commandList.add(updateDefPrivileges(accountId, defPrivileges));
}
if (flags != null) {
commandList.add(updateFlags(accountId, flags));
}
if (grantIds != null) {
for (Object grantId : grantIds.getValue()) {
addGrantToCommandList(name.getNameValue(), commandList, grantId);
}
}
if (commandList.size() > 0) {
Map<String, Object> variables = getVariablesForCommand();
fillInMultipleCommand(commandList, variables);
result = (String) multipleAuthorizeCommandExecutor.execute(variables);
}
} catch (Exception e) {
LOG.error(e, "error in create");
throw new ConnectorException(configuration.getMessage(VmsMessages.ERROR_IN_CREATE), e);
}
// It's possible another connector (or someone else) got in and took the
// UIC.
// If so, we retry with the next UIC
//
if (uniqueUicRequired && (isPresent(result, DUP_IDENT) || isPresent(result, DUPLNAM))) {
do {
unusedUic = getNextUicForGroup(unusedUic);
tryAnotherUic(unusedUic, accountId);
attrMap.put(ATTR_UIC, AttributeBuilder.build(ATTR_UIC, unusedUic));
} while (!isUnique(unusedUic));
}
// Process action attributes
//
String createDirCommand = getCreateDirCommand(attrMap, createDirectory);
String copyLoginCommand = getCopyLoginCommand(attrMap, copyLoginScript, loginScriptSource);
if (createDirCommand != null) {
result = executeCommand(connection, createDirCommand);
Matcher matcher = ERROR_PATTERN.matcher(result);
if (matcher.find()) {
result = matcher.group();
throw new ConnectorException(configuration.getMessage(
VmsMessages.ERROR_IN_CREATE_DIRECTORY, result));
}
}
if (copyLoginCommand != null) {
result = executeCommand(connection, copyLoginCommand);
Matcher matcher = ERROR_PATTERN.matcher(result);
if (matcher.find()) {
result = matcher.group();
throw new ConnectorException(configuration.getMessage(
VmsMessages.ERROR_IN_COPY_LOGIN, result));
}
}
return new Uid(accountId);
}
private void addGrantToCommandList(String name, List<List<StringBuffer>> commandList,
Object grantId) {
StringBuffer buffer = new StringBuffer();
List<StringBuffer> grantCommand = new LinkedList<StringBuffer>();
grantCommand.add(buffer);
buffer.append("GRANT/IDENTIFIER " + grantId + " " + name);
commandList.add(grantCommand);
}
private void addRevokeToCommandList(String name, List<List<StringBuffer>> commandList,
Object grantId) {
StringBuffer buffer = new StringBuffer();
List<StringBuffer> grantCommand = new LinkedList<StringBuffer>();
grantCommand.add(buffer);
buffer.append("REVOKE/IDENTIFIER " + grantId + " " + name);
commandList.add(grantCommand);
}
private List<StringBuffer> updateListAttr(String accountId, Attribute attribute, String name,
Collection<String> valueSet) {
List<Object> values = null;
if (attribute.getValue() == null) {
values = new ArrayList<Object>();
} else {
values = new ArrayList<Object>(attribute.getValue());
}
updateValues(values, valueSet);
return privsCommand(accountId + "/" + name, values);
}
private List<StringBuffer> updatePrivileges(String accountId, Attribute privileges) {
return updateListAttr(accountId, privileges, ATTR_PRIVILEGES,
VmsAttributeValidator.PRIVS_LIST);
}
private List<StringBuffer> updateDefPrivileges(String accountId, Attribute defPrivileges) {
return updateListAttr(accountId, defPrivileges, ATTR_DEFPRIVILEGES,
VmsAttributeValidator.PRIVS_LIST);
}
private List<StringBuffer> updateFlags(String accountId, Attribute flags) {
return updateListAttr(accountId, flags, ATTR_FLAGS, VmsAttributeValidator.FLAGS_LIST);
}
private void updateEnableAttribute(Map<String, Attribute> attrMap) {
Attribute enable = attrMap.remove(OperationalAttributes.ENABLE_NAME);
if (enable != null) {
Boolean isEnable = AttributeUtil.getBooleanValue(enable);
Attribute flags = attrMap.remove(ATTR_FLAGS);
List<Object> flagsValue = new LinkedList<Object>();
if (flags != null) {
flagsValue = new LinkedList<Object>(flags.getValue());
}
if (isEnable) {
if (containsInsensitive(flagsValue, FLAG_DISUSER)) {
throw new IllegalArgumentException(configuration
.getMessage(VmsMessages.DISUSER_ERROR_1));
} else if (!containsInsensitive(flagsValue, "NO" + FLAG_DISUSER)) {
flagsValue.add("NO" + FLAG_DISUSER);
}
Attribute disableDate = attrMap.get(OperationalAttributes.DISABLE_DATE_NAME);
if (disableDate != null) {
// throw new
// IllegalArgumentException(configuration.getMessage(VmsMessages.DISABLE_AND_ENABLE));
} else {
attrMap.put(OperationalAttributes.DISABLE_DATE_NAME, AttributeBuilder.build(
OperationalAttributes.DISABLE_DATE_NAME, Long.valueOf(0)));
}
} else {
if (containsInsensitive(flagsValue, "NO" + FLAG_DISUSER)) {
throw new IllegalArgumentException(configuration
.getMessage(VmsMessages.DISUSER_ERROR_1));
} else if (!containsInsensitive(flagsValue, FLAG_DISUSER)) {
flagsValue.add(FLAG_DISUSER);
}
}
attrMap.put(ATTR_FLAGS, AttributeBuilder.build(ATTR_FLAGS, flagsValue));
}
}
private boolean containsInsensitive(List<Object> list, String string) {
for (Object object : list) {
if (string.equalsIgnoreCase(object.toString())) {
return true;
}
}
return false;
}
private void tryAnotherUic(String unusedUic, String accountId) {
Map<String, Attribute> uicAttrMap = new HashMap<String, Attribute>();
uicAttrMap.put(ATTR_UIC, AttributeBuilder.build(ATTR_UIC, unusedUic));
List<StringBuffer> uicCommand = appendAttributes(true, accountId, uicAttrMap);
Map<String, Object> uicVariables = getVariablesForCommand();
fillInCommand(uicCommand, uicVariables);
String result = "";
try {
result = (String) multipleAuthorizeCommandExecutor.execute(uicVariables);
} catch (Exception e) {
LOG.error(e, "error in tryAnotherUic");
throw new ConnectorException(configuration.getMessage(VmsMessages.ERROR_IN_MODIFY), e);
}
if (isPresent(result, CLI_WARNING)) {
throw new ConnectorException(configuration.getMessage(VmsMessages.ERROR_IN_MODIFY2,
result));
}
}
/**
* {@inheritDoc}
*/
public void delete(ObjectClass objectClass, Uid uid, OperationOptions options) {
if (!objectClass.is(ObjectClass.ACCOUNT_NAME)) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.UNSUPPORTED_OBJECT_CLASS, objectClass.getObjectClassValue()));
}
LOG.info("delete(''{0}'')", uid.getUidValue());
Map<String, Object> variables = getVariablesForCommand();
fillInCommand("REMOVE " + uid.getUidValue(), variables);
String result = "";
try {
result = (String) multipleAuthorizeCommandExecutor.execute(variables);
} catch (Exception e) {
LOG.error(e, "error in delete");
throw new ConnectorException(configuration.getMessage(VmsMessages.ERROR_IN_DELETE), e);
}
if (isPresent(result, CLI_WARNING)) {
throw new ConnectorException(configuration.getMessage(VmsMessages.ERROR_IN_DELETE2,
result));
}
if (isPresent(result, USER_REMOVED)) {
LOG.info("user deleted");
return;
} else if (isPresent(result, BAD_USER)) {
throw new UnknownUidException();
} else {
throw new ConnectorException(configuration.getMessage(VmsMessages.ERROR_IN_DELETE2,
result));
}
}
VmsConnection getConnection() {
return connection;
}
/**
* {@inheritDoc}
*/
public FilterTranslator<String> createFilterTranslator(ObjectClass oclass,
OperationOptions options) {
return new VmsFilterTranslator();
}
/**
* {@inheritDoc}
*/
public void executeQuery(ObjectClass objectClass, String query, ResultsHandler handler,
OperationOptions options) {
if (!objectClass.is(ObjectClass.ACCOUNT_NAME)) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.UNSUPPORTED_OBJECT_CLASS, objectClass.getObjectClassValue()));
}
Set<String> attributesToGet = null;
if (options != null && options.getAttributesToGet() != null) {
attributesToGet = CollectionUtil.newReadOnlySet(options.getAttributesToGet());
}
schema();
if (attributesToGet != null) {
for (String name : attributesToGet) {
if (!attributeInfoMap.containsKey(name)) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.NO_SUCH_ATTRIBUTE, name));
}
}
} else {
attributesToGet = returnedByDefault;
}
try {
if (query == null) {
query = "*";
}
LOG.info("executeQuery(''{0}'')", query);
List<String> filterStrings = new ArrayList<String>();
filterStrings.add(query);
filterUsers(handler, filterStrings, attributesToGet);
} catch (Exception e) {
LOG.error(e, "error in executeQuery");
throw new ConnectorException(configuration.getMessage(VmsMessages.ERROR_IN_SEARCH), e);
}
}
private void filterUsers(ResultsHandler handler, List<String> filters,
Set<String> searchAttributesToGet) throws Exception {
LinkedList<List<StringBuffer>> commandList = new LinkedList<List<StringBuffer>>();
for (String filter : filters) {
List<StringBuffer> commands = new LinkedList<StringBuffer>();
StringBuffer command = new StringBuffer();
command.append("SHOW " + filter);
commands.add(command);
commandList.add(commands);
}
Map<String, Object> variables = getVariablesForCommand();
fillInMultipleCommand(commandList, variables);
String users = (String) multipleAuthorizeCommandExecutor.execute(variables);
String[] userArray = users.split(SEPARATOR);
List<String> attributesToGet = null;
if (searchAttributesToGet != null) {
attributesToGet = CollectionUtil.newList(searchAttributesToGet);
}
for (int i = 1; i < userArray.length; i++) {
String user = SEPARATOR + userArray[i].replaceAll("\r\n", "\n");
// Now, we truncate from the UAF_PROMPT, if present
//
int index = user.indexOf(UAF_PROMPT);
if (index > -1) {
user = user.substring(0, index);
}
user += "\n";
try {
@SuppressWarnings("unchecked")
Map<String, Object> attributes = (Map<String, Object>) TRANSFORM.transform(user);
ConnectorObjectBuilder builder = new ConnectorObjectBuilder();
builder.setUid((String) attributes.get(Name.NAME));
String lastLogin =
(String) attributes.remove(PredefinedAttributes.LAST_LOGIN_DATE_NAME);
String cputime = (String) attributes.remove(ATTR_CPUTIME);
String lifetime =
(String) attributes
.remove(PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME);
// ENABLE is handled by checking to see if the DisUser Flag is
// present
//
@SuppressWarnings("unchecked")
List<String> flags = (List<String>) attributes.get(ATTR_FLAGS);
if (flags.size() > 0) {
if (includeInAttributes(OperationalAttributes.ENABLE_NAME, attributesToGet)) {
if (flags.contains(FLAG_DISUSER)) {
flags.remove(FLAG_DISUSER);
builder.addAttribute(OperationalAttributes.ENABLE_NAME, Boolean.FALSE);
} else {
builder.addAttribute(OperationalAttributes.ENABLE_NAME, Boolean.TRUE);
}
}
} else {
if (includeInAttributes(OperationalAttributes.ENABLE_NAME, attributesToGet)) {
builder.addAttribute(OperationalAttributes.ENABLE_NAME, Boolean.TRUE);
}
}
// DISABLE_DATE_NAME is handled by seeing the last password
// change plus
// the password lifetime is before the current time.
// If the password is pre-expired, we always set this to TRUE
//
if (includeInAttributes(OperationalAttributes.DISABLE_DATE_NAME, attributesToGet)) {
String expired =
(String) attributes.get(OperationalAttributes.DISABLE_DATE_NAME);
if (expired.contains("(none)")) {
builder.addAttribute(OperationalAttributes.DISABLE_DATE_NAME);
} else {
Date expiredDate = vmsDateFormatWithoutSecs.parse(expired);
// If the DISABLE_DATE is in the past, we report it as
// no value
//
if (expiredDate.before(new Date())) {
builder.addAttribute(OperationalAttributes.DISABLE_DATE_NAME);
} else {
builder.addAttribute(OperationalAttributes.DISABLE_DATE_NAME,
expiredDate.getTime());
}
}
}
// PASSWORD_EXPIRED is handled by seeing the last password
// change plus
// the password lifetime is before the current time.
// If the password is pre-expired, we always set this to TRUE
//
if (includeInAttributes(OperationalAttributes.PASSWORD_EXPIRED_NAME,
attributesToGet)) {
String lastChange =
(String) attributes
.get(PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME);
if (lastChange.contains("(pre-expired)")) {
builder.addAttribute(OperationalAttributes.PASSWORD_EXPIRED_NAME,
Boolean.TRUE);
} else {
Date expiredDate = getPasswordExpirationDate(lifetime, attributes);
Date vmsDate = getVmsDate();
builder.addAttribute(OperationalAttributes.PASSWORD_EXPIRED_NAME,
expiredDate.before(vmsDate));
}
}
// ATTR_DEVICE is parsed out from ATTR_DEFAULT
//
if (includeInAttributes(ATTR_DEVICE, attributesToGet)) {
String defaultString = (String) attributes.get(ATTR_DEFAULT);
if (StringUtil.isNotBlank(defaultString)) {
String[] defaultInfo = defaultString.split(":");
if (defaultInfo.length > 1) {
builder.addAttribute(ATTR_DEVICE, defaultInfo[0]);
} else {
builder.addAttribute(ATTR_DEVICE);
}
} else {
builder.addAttribute(ATTR_DEVICE);
}
}
// GRANT_IDS may not be in the parse. If not, we reget it.
//
if (includeInAttributes(ATTR_GRANT_IDS, attributesToGet)) {
List<String> grantIds = (List<String>) attributes.get(ATTR_GRANT_IDS);
if (grantIds == null) {
builder.addAttribute(ATTR_GRANT_IDS);
}
}
// ATTR_DIRECTORY is parsed out from ATTR_DEFAULT
//
if (includeInAttributes(ATTR_DIRECTORY, attributesToGet)) {
String defaultString = (String) attributes.get(ATTR_DEFAULT);
if (StringUtil.isNotBlank(defaultString)) {
String[] defaultInfo = defaultString.split(":");
if (defaultInfo.length > 1) {
builder.addAttribute(ATTR_DIRECTORY, defaultInfo[1]);
} else {
builder.addAttribute(ATTR_DIRECTORY, defaultInfo[0]);
}
} else {
builder.addAttribute(ATTR_DIRECTORY);
}
}
// Parse out the various access restrictions:
// NETWORK
// BATCH
// LOCAL
// DIALUP
// REMOTE
//
String accessRestrictions = (String) attributes.get("Access Restrictions");
parseAccessRestrictions(accessRestrictions, attributesToGet, builder);
// LAST_LOGIN
//
if (includeInAttributes(PredefinedAttributes.LAST_LOGIN_DATE_NAME, attributesToGet)) {
if (StringUtil.isNotBlank(lastLogin)) {
// Split it out into two separate dates
Pattern pattern =
Pattern.compile("(.*)\\(interactive\\),(.*)\\(non-interactive\\)");
Matcher matcher = pattern.matcher(lastLogin);
Object value = null;
if (matcher.matches()) {
String interactive = matcher.group(1).trim();
String noninteractive = matcher.group(2).trim();
if (interactive.equals("(none)")) {
if (noninteractive.equals("(none)")) {
// no date, so keep null
} else {
Date date =
vmsDateFormatWithoutSecs.parse(noninteractive.trim());
value = date.getTime();
}
} else {
if (noninteractive.equals("(none)")) {
Date date = vmsDateFormatWithoutSecs.parse(interactive.trim());
value = date.getTime();
} else {
Date interactiveDate =
vmsDateFormatWithoutSecs.parse(interactive.trim());
Date noninteractiveDate =
vmsDateFormatWithoutSecs.parse(noninteractive.trim());
if (interactiveDate.after(noninteractiveDate)) {
value = interactiveDate.getTime();
} else {
value = noninteractiveDate.getTime();
}
}
}
if (value != null) {
List<Object> values = new LinkedList<Object>();
values.add(value);
builder.addAttribute(PredefinedAttributes.LAST_LOGIN_DATE_NAME,
values);
} else {
builder.addAttribute(PredefinedAttributes.LAST_LOGIN_DATE_NAME);
}
}
}
}
// OperationalAttributes.DISABLE_DATE_NAME
//
if (includeInAttributes(OperationalAttributes.DISABLE_DATE_NAME, attributesToGet)) {
String expiration =
(String) attributes.remove(OperationalAttributes.DISABLE_DATE_NAME);
if (StringUtil.isNotBlank(expiration)) {
if (expiration.trim().equals("(none)")) {
builder.addAttribute(OperationalAttributes.DISABLE_DATE_NAME);
} else {
Date date = vmsDateFormatWithoutSecs.parse(expiration.trim());
List<Object> value = new LinkedList<Object>();
value.add(date.getTime());
builder.addAttribute(OperationalAttributes.DISABLE_DATE_NAME, value);
}
} else {
builder.addAttribute(OperationalAttributes.DISABLE_DATE_NAME);
}
}
// CPU
//
if (includeInAttributes(ATTR_CPUTIME, attributesToGet)) {
if (StringUtil.isNotBlank(cputime)) {
long cputimeLong = remapFromDelta(cputime);
builder.addAttribute(ATTR_CPUTIME, cputimeLong);
} else {
builder.addAttribute(ATTR_CPUTIME);
}
}
// PASSWORD_CHANGE_INTERVAL_NAME
//
if (includeInAttributes(PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME,
attributesToGet)) {
if (StringUtil.isNotBlank(lifetime)) {
long lifetimeLong = remapFromDelta(lifetime);
builder.addAttribute(PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME,
lifetimeLong);
} else {
builder.addAttribute(PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME);
}
}
// LAST_PASSWORD_CHANGE_DATE_NAME
// If the password is pre-expired, we return no value
//
String lastChange =
(String) attributes
.remove(PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME);
if (includeInAttributes(PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME,
attributesToGet)) {
if (lastChange.contains("(pre-expired)")) {
builder.addAttribute(PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME);
} else {
Date date = vmsDateFormatWithoutSecs.parse(lastChange.trim());
Object value = date.getTime();
List<Object> values = new LinkedList<Object>();
values.add(value);
builder.addAttribute(PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME,
values);
}
}
// PASSWORD_EXPIRATION_DATE is handled by seeing if there is an
// expiration
// date, and if so, converting it to milliseconds
//
// TODO: I believe we can't always compute this, since the case
// where the user has
// never logged in, but there is a password lifetime is not
// computable.
//
if (includeInAttributes(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME,
attributesToGet)) {
if (StringUtil.isNotBlank(lifetime)) {
if (lifetime.equalsIgnoreCase("(none)")) {
builder.addAttribute(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME);
} else if (lastChange.contains("(pre-expired)")) {
builder.addAttribute(
OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME, 0L);
} else {
Date expiredDate = getPasswordExpirationDate(lifetime, attributes);
builder.addAttribute(
OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME,
expiredDate.getTime());
}
} else {
builder.addAttribute(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME);
}
}
// All other attributes are handled normally
//
for (Map.Entry<String, Object> entry : attributes.entrySet()) {
Object value = entry.getValue();
String key = entry.getKey();
if (includeInAttributes(key, attributesToGet)) {
if (value instanceof Collection) {
builder.addAttribute(key, (Collection<?>) value);
} else if (value != null && StringUtil.isNotEmpty(value.toString())) {
builder.addAttribute(key, value);
} else {
builder.addAttribute(key);
}
}
}
ConnectorObject next = builder.build();
handler.handle(next);
} catch (Exception e) {
LOG.error(e, "error parsing users");
throw new ConnectorException(e);
}
}
}
private Date getVmsDate() {
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("CONNECTION", connection);
variables.put("SHELL_PROMPT", configuration.getLocalHostShellPrompt());
variables.put("SHORT_WAIT", SHORT_WAIT);
String result = "";
try {
result = (String) dateCommandExecutor.execute(variables);
result = result.replaceAll(configuration.getLocalHostShellPrompt(), "").trim();
Date date = vmsDateFormatWithSecs.parse(result);
return date;
} catch (Exception e) {
LOG.error(e, "error in getVmsDate");
throw new ConnectorException(configuration.getMessage(VmsMessages.ERROR_IN_GETDATE), e);
}
}
private Date getPasswordExpirationDate(String lifetime, Map<String, Object> attributes)
throws ParseException {
String lastChange =
(String) attributes.get(PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME);
// If the password is pre-expired, we say it expired at time 0
//
if (lastChange.contains("(pre-expired)")) {
return new Date(0);
}
// If there is no lifetime, there is no expiration date
//
if (lifetime == null) {
return null;
}
// Otherwise, password expires 'lifetime' after 'lastChangeDate'
//
long lastChangeDate = vmsDateFormatWithoutSecs.parse(lastChange).getTime();
long lifetimeLong = remapFromDelta(lifetime);
Date expiredDate = new Date(lastChangeDate + lifetimeLong);
return expiredDate;
}
private boolean includeInAttributes(String attribute, List<String> attributesToGet) {
if (attribute.equalsIgnoreCase(Name.NAME)) {
return true;
}
return attributesToGet.contains(attribute);
}
public Uid update(ObjectClass obj, Uid uid, Set<Attribute> attrs, OperationOptions options) {
return update(obj, AttributeUtil.addUid(attrs, uid), options);
}
/**
* {@inheritDoc}
*/
Uid update(ObjectClass objectClass, Set<Attribute> attributes, OperationOptions options) {
if (!objectClass.is(ObjectClass.ACCOUNT_NAME)) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.UNSUPPORTED_OBJECT_CLASS, objectClass.getObjectClassValue()));
}
Map<String, Attribute> attrMap =
new HashMap<String, Attribute>(AttributeUtil.toMap(attributes));
// Grants need to be handled separately
//
Attribute grantIds = attrMap.remove(ATTR_GRANT_IDS);
// Create-only attributes may not be specified
//
Attribute createDirectory = attrMap.remove(ATTR_CREATE_DIRECTORY);
Attribute copyLogin = attrMap.remove(ATTR_COPY_LOGIN_SCRIPT);
Attribute loginScriptSource = attrMap.remove(ATTR_LOGIN_SCRIPT_SOURCE);
if (isAttributeTrue(createDirectory)) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.UPDATE_ATTRIBUTE_VALUE, createDirectory.getName()));
}
if (isAttributeTrue(copyLogin)) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.UPDATE_ATTRIBUTE_VALUE, copyLogin.getName()));
}
// Operational Attributes are handled specially
//
Uid uid = (Uid) attrMap.remove(Uid.NAME);
Name name = (Name) attrMap.remove(Name.NAME);
Attribute currentPassword = attrMap.remove(OperationalAttributes.CURRENT_PASSWORD_NAME);
Attribute newPassword = attrMap.remove(OperationalAttributes.PASSWORD_NAME);
// If Password and CurrentPassword
// are specified, we change password via SET PASSWORD, rather than
// AUTHORIIZE
//
if (currentPassword != null && newPassword != null) {
LOG.info("update[changePassword](''{0}'')", uid.getUidValue());
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("SHELL_PROMPT", configuration.getLocalHostShellPrompt());
variables.put("SHORT_WAIT", SHORT_WAIT);
variables.put("USERNAME", uid.getUidValue());
GuardedString currentGS = AttributeUtil.getGuardedStringValue(currentPassword);
GuardedString newGS = AttributeUtil.getGuardedStringValue(newPassword);
GuardedStringAccessor accessor = new GuardedStringAccessor();
currentGS.access(accessor);
char[] currentArray = accessor.getArray();
newGS.access(accessor);
char[] newArray = accessor.getArray();
variables.put("CURRENT_PASSWORD", new String(currentArray));
variables.put("NEW_PASSWORD", new String(newArray));
variables.put("CONFIGURATION", configuration);
String result = "";
try {
result = (String) changeOwnPasswordCommandExecutor.execute(variables);
Arrays.fill(currentArray, 0, currentArray.length, ' ');
Arrays.fill(newArray, 0, newArray.length, ' ');
} catch (Exception e) {
Arrays.fill(currentArray, 0, currentArray.length, ' ');
Arrays.fill(newArray, 0, newArray.length, ' ');
LOG.error(e, "error in create");
throw new ConnectorException(configuration.getMessage(VmsMessages.ERROR_IN_MODIFY),
e);
}
if (result.indexOf("%SET-") > -1) {
String errortext = result.substring(result.indexOf("%SET-")).split("\n")[0];
throw new ConnectorException(configuration.getMessage(VmsMessages.ERROR_IN_MODIFY2,
errortext));
}
} else if (newPassword != null) {
// Put back the new password, so it can be changed administratively
//
attrMap.put(OperationalAttributes.PASSWORD_NAME, newPassword);
}
// If we have any remaining attributes, process them
//
if (attrMap.size() > 0) {
// If UIC contains wildcard, compute an appropriate value
//
boolean uniqueUicRequired = false;
String unusedUic = null;
Attribute uic = attrMap.get(ATTR_UIC);
if (uic != null && StringUtil.isBlank(AttributeUtil.getStringValue(uic))) {
String uicValue = AttributeUtil.getStringValue(uic);
if (uicValue.contains("*")) {
uniqueUicRequired = true;
unusedUic = getUnusedUicForGroup(uicValue);
attrMap.put(ATTR_UIC, AttributeBuilder.build(ATTR_UIC, unusedUic));
}
}
// Enable/disable is handled by FLAGS=([NO]DISUSER)
//
updateEnableAttribute(attrMap);
Attribute privileges =
configuration.getLongCommands() ? null : attrMap.remove(ATTR_PRIVILEGES);
Attribute defPrivileges =
configuration.getLongCommands() ? null : attrMap.remove(ATTR_DEFPRIVILEGES);
Attribute flags = configuration.getLongCommands() ? null : attrMap.remove(ATTR_FLAGS);
String accountId = uid.getUidValue();
List<StringBuffer> modifyCommand = appendAttributes(true, accountId, attrMap);
String result = "";
List<List<StringBuffer>> commandList = new LinkedList<List<StringBuffer>>();
try {
commandList.add(modifyCommand);
if (privileges != null) {
commandList.add(updatePrivileges(accountId, privileges));
}
if (defPrivileges != null) {
commandList.add(updateDefPrivileges(accountId, defPrivileges));
}
if (flags != null) {
commandList.add(updateFlags(accountId, flags));
}
Map<String, Object> variables = getVariablesForCommand();
fillInMultipleCommand(commandList, variables);
result = (String) multipleAuthorizeCommandExecutor.execute(variables);
} catch (Exception e) {
LOG.error(e, "error in create");
throw new ConnectorException(configuration.getMessage(VmsMessages.ERROR_IN_MODIFY),
e);
}
if (isPresent(result, CLI_WARNING)) {
throw new ConnectorException(configuration.getMessage(VmsMessages.ERROR_IN_MODIFY2,
result));
}
if (isPresent(result, USER_UPDATED) || isPresent(result, NO_MODS)) {
// OK, drop through and return uid
} else if (isPresent(result, BAD_SPEC)) {
throw new UnknownUidException();
} else {
throw new ConnectorException(configuration.getMessage(VmsMessages.ERROR_IN_MODIFY2,
result));
}
// It's possible another connector (or someone else) got in and took
// the UIC.
// If so, we retry with the next UIC
//
if (uniqueUicRequired && (isPresent(result, DUP_IDENT) || isPresent(result, DUPLNAM))) {
do {
unusedUic = getNextUicForGroup(unusedUic);
tryAnotherUic(unusedUic, uid.getUidValue());
} while (!isUnique(unusedUic));
}
}
// Now do grants
//
if (grantIds != null) {
setGrants(grantIds, uid.getUidValue());
}
// If name is different from Uid, we are performing a RENAME operation.
// Do this last, so that we don't lose the Uid change on error.
//
if (name != null && uid != null && !uid.getUidValue().equals(name.getNameValue())) {
Map<String, Object> variables = getVariablesForCommand();
fillInCommand("RENAME " + uid.getUidValue() + " " + name.getNameValue(), variables);
String result = "";
try {
result = (String) multipleAuthorizeCommandExecutor.execute(variables);
} catch (Exception e) {
LOG.error(e, "error in rename");
throw new ConnectorException(configuration.getMessage(VmsMessages.ERROR_IN_MODIFY),
e);
}
if (isPresent(result, CLI_WARNING)) {
throw new ConnectorException(configuration.getMessage(VmsMessages.ERROR_IN_CREATE2,
result));
}
if (isPresent(result, USER_RENAMED)) {
uid = new Uid(name.getNameValue());
} else if (isPresent(result, BAD_SPEC)) {
throw new UnknownUidException();
} else if (isPresent(result, USER_EXISTS)) {
throw new AlreadyExistsException();
} else {
throw new ConnectorException(configuration.getMessage(VmsMessages.ERROR_IN_MODIFY2,
result));
}
}
return uid;
}
private void setGrants(Attribute grantIds, String name) {
ConnectorObject object = getUser(name);
if (object != null) {
Attribute currentGrantsAttr = object.getAttributeByName(ATTR_GRANT_IDS);
List<Object> currentGrants =
currentGrantsAttr == null ? new LinkedList<Object>() : currentGrantsAttr
.getValue();
if (currentGrants != null) {
List<Object> grants =
grantIds.getValue() == null ? new LinkedList<Object>()
: new LinkedList<Object>(grantIds.getValue());
List<Object> revokes = new LinkedList<Object>(currentGrants);
revokes.removeAll(grants);
grants.removeAll(currentGrants);
if (grants.size() > 0 || revokes.size() > 0) {
LinkedList<List<StringBuffer>> commandList =
new LinkedList<List<StringBuffer>>();
for (Object grant : grants) {
addGrantToCommandList(name, commandList, (String) grant);
}
for (Object revoke : revokes) {
addRevokeToCommandList(name, commandList, (String) revoke);
}
Map<String, Object> variables = getVariablesForCommand();
fillInMultipleCommand(commandList, variables);
String result = "";
try {
result = (String) multipleAuthorizeCommandExecutor.execute(variables);
} catch (Exception e) {
LOG.error(e, "error in modify");
throw new ConnectorException(configuration
.getMessage(VmsMessages.ERROR_IN_MODIFY), e);
}
if (isPresent(result, CLI_WARNING)) {
throw new ConnectorException(configuration.getMessage(
VmsMessages.ERROR_IN_MODIFY2, result));
}
if (isPresent(result, UAF_ERROR)) {
throw new ConnectorException(configuration.getMessage(
VmsMessages.ERROR_IN_MODIFY2, result));
}
}
}
}
}
private ConnectorObject getUser(String name) {
LocalHandler handler = new LocalHandler();
executeQuery(ObjectClass.ACCOUNT, name, handler, null);
if (handler.iterator().hasNext()) {
return handler.iterator().next();
} else {
return null;
}
}
private void fillInCommand(String command, Map<String, Object> variables) {
List<StringBuffer> list = new LinkedList<StringBuffer>();
StringBuffer buffer = new StringBuffer();
buffer.append(command);
list.add(buffer);
fillInCommand(list, variables);
}
private void fillInCommand(List<StringBuffer> command, Map<String, Object> variables) {
List<List<StringBuffer>> commands = new LinkedList<List<StringBuffer>>();
commands.add(command);
fillInMultipleCommand(commands, variables);
}
private void fillInMultipleCommand(List<List<StringBuffer>> commands,
Map<String, Object> variables) {
List<Object> localCommandList = new LinkedList<Object>();
variables.put("COMMANDLISTS", localCommandList);
for (List<StringBuffer> localCommandOrig : commands) {
List<StringBuffer> localCommand = new LinkedList<StringBuffer>(localCommandOrig);
StringBuffer lastPart = localCommand.remove(localCommandOrig.size() - 1);
List<StringBuffer> firstContents = new LinkedList<StringBuffer>();
for (StringBuffer part : localCommand) {
firstContents.add(part);
}
List<Object> commandValue = new LinkedList<Object>();
commandValue.add(localCommand);
commandValue.add(lastPart);
localCommandList.add(commandValue);
}
}
/**
* {@inheritDoc}
*/
public Schema schema() {
if (schema != null) {
return schema;
}
final SchemaBuilder schemaBuilder = new SchemaBuilder(getClass());
Set<AttributeInfo> attributes = new HashSet<AttributeInfo>();
attributes.add(buildRequiredAttribute(Name.NAME, String.class));
// Required Attributes
//
attributes.add(buildRequiredAttribute(ATTR_UIC, String.class));
// Optional Attributes (have VMS default values)
//
attributes.add(AttributeInfoBuilder.build(ATTR_DEVICE, String.class));
attributes.add(AttributeInfoBuilder.build(ATTR_DIRECTORY, String.class));
attributes.add(AttributeInfoBuilder.build(ATTR_OWNER, String.class));
attributes.add(AttributeInfoBuilder.build(ATTR_ACCOUNT, String.class));
attributes.add(AttributeInfoBuilder.build(ATTR_CLI, String.class));
attributes.add(AttributeInfoBuilder.build(ATTR_CLITABLES, String.class));
attributes.add(AttributeInfoBuilder.build(ATTR_LGICMD, String.class));
attributes.add(AttributeInfoBuilder.build(ATTR_PWDMINIMUM, Integer.class));
attributes.add(AttributeInfoBuilder.build(ATTR_MAXJOBS, Integer.class));
attributes.add(AttributeInfoBuilder.build(ATTR_MAXACCTJOBS, Integer.class));
attributes.add(AttributeInfoBuilder.build(ATTR_SHRFILLM, Integer.class));
attributes.add(AttributeInfoBuilder.build(ATTR_PBYTLM, Integer.class));
attributes.add(AttributeInfoBuilder.build(ATTR_MAXDETACH, String.class));
attributes.add(AttributeInfoBuilder.build(ATTR_BIOLM, Integer.class));
attributes.add(AttributeInfoBuilder.build(ATTR_JTQUOTA, Integer.class));
attributes.add(AttributeInfoBuilder.build(ATTR_DIOLM, Integer.class));
attributes.add(AttributeInfoBuilder.build(ATTR_WSDEFAULT, Integer.class));
attributes.add(AttributeInfoBuilder.build(ATTR_PRIORITY, Integer.class));
attributes.add(AttributeInfoBuilder.build(ATTR_WSQUOTA, Integer.class));
attributes.add(AttributeInfoBuilder.build(ATTR_QUEPRIO, Integer.class));
attributes.add(AttributeInfoBuilder.build(ATTR_WSEXTENT, Integer.class));
attributes.add(AttributeInfoBuilder.build(ATTR_CPUTIME, Long.class));
attributes.add(AttributeInfoBuilder.build(ATTR_ENQLM, Integer.class));
attributes.add(AttributeInfoBuilder.build(ATTR_PGFLQUOTA, Integer.class));
attributes.add(AttributeInfoBuilder.build(ATTR_TQELM, Integer.class));
attributes.add(AttributeInfoBuilder.build(ATTR_ASTLM, Integer.class));
attributes.add(AttributeInfoBuilder.build(ATTR_BYTLM, Integer.class));
attributes.add(AttributeInfoBuilder.build(ATTR_FILLM, Integer.class));
attributes.add(AttributeInfoBuilder.build(ATTR_PRCLM, Integer.class));
// Multi-valued attributes
//
attributes.add(buildMultivaluedAttribute(ATTR_GRANT_IDS, String.class, false));
attributes.add(buildMultivaluedAttribute(ATTR_FLAGS, String.class, false));
attributes.add(buildMultivaluedAttribute(ATTR_PRIMEDAYS, String.class, false));
attributes.add(buildMultivaluedAttribute(ATTR_PRIVILEGES, String.class, false));
attributes.add(buildMultivaluedAttribute(ATTR_DEFPRIVILEGES, String.class, false));
attributes.add(buildMultivaluedAttribute(ATTR_NETWORK, String.class, false));
attributes.add(buildMultivaluedAttribute(ATTR_BATCH, String.class, false));
attributes.add(buildMultivaluedAttribute(ATTR_LOCAL, String.class, false));
attributes.add(buildMultivaluedAttribute(ATTR_DIALUP, String.class, false));
attributes.add(buildMultivaluedAttribute(ATTR_REMOTE, String.class, false));
// Write-only attributes
//
// attributes.add(buildWriteonlyAttribute(ATTR_ALGORITHM, String.class,
// false, false));
attributes.add(buildCreateonlyAttribute(ATTR_LOGIN_SCRIPT_SOURCE, String.class, false));
attributes.add(buildCreateonlyAttribute(ATTR_COPY_LOGIN_SCRIPT, Boolean.class, false));
attributes.add(buildCreateonlyAttribute(ATTR_CREATE_DIRECTORY, Boolean.class, false));
// Read-only attributes
//
attributes.add(buildReadonlyAttribute(ATTR_LOGIN_FAILS, Integer.class, false));
// Operational Attributes
//
attributes.add(OperationalAttributeInfos.PASSWORD);
boolean disableUserLogins = isDisableUserLogins(configuration);
if (!disableUserLogins) {
attributes.add(OperationalAttributeInfos.CURRENT_PASSWORD);
}
attributes.add(OperationalAttributeInfos.ENABLE);
attributes.add(OperationalAttributeInfos.PASSWORD_EXPIRED);
attributes.add(OperationalAttributeInfos.DISABLE_DATE);
// TODO: I believe we can't always compute this, since the case where
// the user has
// never logged in, but there is a password lifetime is not computable.
//
// attributes.add(buildReadonlyAttribute(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME,
// Long.class, false));
// Predefined Attributes
//
attributes.add(PredefinedAttributeInfos.LAST_LOGIN_DATE);
attributes.add(PredefinedAttributeInfos.LAST_PASSWORD_CHANGE_DATE);
attributes.add(PredefinedAttributeInfos.PASSWORD_CHANGE_INTERVAL);
ObjectClassInfoBuilder ociBuilder = new ObjectClassInfoBuilder();
ociBuilder.setType(ObjectClass.ACCOUNT_NAME);
ociBuilder.addAllAttributeInfo(attributes);
ObjectClassInfo objectClassInfo = ociBuilder.build();
schemaBuilder.defineObjectClass(objectClassInfo);
// Remove unsupported operations
//
if (disableUserLogins) {
schemaBuilder.removeSupportedObjectClass(AuthenticateOp.class, objectClassInfo);
}
schema = schemaBuilder.build();
attributeInfoMap = AttributeInfoUtil.toMap(attributes);
returnedByDefault = new TreeSet<String>();
for (AttributeInfo attribute : attributes) {
if (attribute.isReturnedByDefault()) {
returnedByDefault.add(attribute.getName());
}
}
return schema;
}
/**
* No access pattern, plus partial access patter.
*
* <pre>
* Primary 000000000011111111112222 Secondary 000000000011111111112222
* Day Hours 012345678901234567890123 Day Hours 012345678901234567890123
* Network: ----- No access ------ -----#-###--------------
* Batch: ----- No access ------ -----#-###--------------
* Local: ----- No access ------ -----#-###--------------
* Dialup: ----- No access ------ -----#-###--------------
* Remote: ----- No access ------ -----#-###--------------
*
* Full access pattern, plus No access pattern
* Primary 000000000011111111112222 Secondary 000000000011111111112222
* Day Hours 012345678901234567890123 Day Hours 012345678901234567890123
* Network: ##### Full access ###### ----- No access ------
* Batch: ##### Full access ###### ----- No access ------
* Local: ##### Full access ###### ----- No access ------
* Dialup: ##### Full access ###### ----- No access ------
* Remote: ##### Full access ###### ----- No access ------
* </pre>
*
* Full access to all pattern No access restrictions
*
*/
private void parseAccessRestrictions(String access, List<String> attributesToGet,
ConnectorObjectBuilder builder) {
if (access.trim().equals(FULL_ACCESS_FOR_ALL)) {
for (String accessor : ACCESSORS) {
if (includeInAttributes(accessor.toUpperCase(), attributesToGet)) {
builder.addAttribute(AttributeBuilder.build(accessor.toUpperCase(),
new Object[] { "0-23", "0-23" }));
}
}
} else {
for (String accessor : ACCESSORS) {
Pattern splitAccessPattern =
Pattern.compile(accessor + ":\\s+(.{24})\\s{12}(.{24})");
Matcher matcher = splitAccessPattern.matcher(access);
if (matcher.find()
&& (includeInAttributes(accessor.toUpperCase(), attributesToGet))) {
builder.addAttribute(AttributeBuilder.build(accessor.toUpperCase(),
convertAccessor(matcher.group(1), matcher.group(2))));
}
}
}
}
private List<String> convertAccessor(String primary, String secondary) {
List<String> result = new ArrayList<String>(2);
for (String value : new String[] { primary, secondary }) {
if (value.equals(FULL_ACCESS)) {
result.add("0-23");
} else if (value.equals(NO_ACCESS)) {
result.add("");
} else {
int lower = -1;
char previous = '-';
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < 24; i++) {
if (value.charAt(i) != previous) {
if (value.charAt(i) == '#') {
// Starting new string of hours
lower = i;
} else {
// ended previous string of hours
if (lower > -1) {
if ((i - 1) > lower) {
buffer.append("," + lower + "-" + (i - 1));
} else {
buffer.append("," + lower);
}
lower = -1;
}
}
}
previous = value.charAt(i);
}
if (lower > -1) {
if (lower < 23) {
buffer.append("," + lower + "-23");
} else {
buffer.append("," + lower);
}
}
result.add(buffer.toString().substring(1));
}
}
return result;
}
private static final String[] ACCESSORS = { "Network", "Batch", "Local", "Dialup", "Remote" };
private static final String FULL_ACCESS_FOR_ALL = "No access restrictions";
private static final String FULL_ACCESS = "##### Full access ######";
private static final String NO_ACCESS = "----- No access ------";
private AttributeInfo buildMultivaluedAttribute(String name, Class<?> clazz, boolean required) {
AttributeInfoBuilder builder = new AttributeInfoBuilder();
builder.setName(name);
builder.setType(clazz);
builder.setRequired(required);
builder.setMultiValued(true);
return builder.build();
}
private AttributeInfo buildRequiredAttribute(String name, Class<?> clazz) {
AttributeInfoBuilder builder = new AttributeInfoBuilder();
builder.setName(name);
builder.setType(clazz);
builder.setRequired(true);
builder.setMultiValued(false);
builder.setUpdateable(true);
builder.setCreateable(true);
return builder.build();
}
private AttributeInfo buildReadonlyAttribute(String name, Class<?> clazz, boolean required) {
AttributeInfoBuilder builder = new AttributeInfoBuilder();
builder.setName(name);
builder.setType(clazz);
builder.setRequired(required);
builder.setMultiValued(false);
builder.setCreateable(false);
builder.setUpdateable(false);
return builder.build();
}
private AttributeInfo buildCreateonlyAttribute(String name, Class<?> clazz, boolean required) {
AttributeInfoBuilder builder = new AttributeInfoBuilder();
builder.setName(name);
builder.setType(clazz);
builder.setRequired(required);
builder.setMultiValued(false);
builder.setUpdateable(false);
builder.setCreateable(true);
builder.setReadable(false);
builder.setReturnedByDefault(false);
return builder.build();
}
/**
* {@inheritDoc}
*/
public void dispose() {
if (connection != null) {
connection.dispose();
connection = null;
}
}
/**
* {@inheritDoc}
*/
public void checkAlive() {
connection.test();
}
/**
* {@inheritDoc}
*/
public Configuration getConfiguration() {
return configuration;
}
/**
* {@inheritDoc}
*/
public void init(Configuration cfg) {
configuration = (VmsConfiguration) cfg;
vmsDateFormatWithSecs =
new SimpleDateFormat(configuration.getVmsDateFormatWithSecs(), new Locale(
configuration.getVmsLocale()));
TimeZone timeZone = TimeZone.getTimeZone(configuration.getVmsTimeZone());
vmsDateFormatWithSecs.setTimeZone(timeZone);
vmsDateFormatWithoutSecs =
new SimpleDateFormat(configuration.getVmsDateFormatWithoutSecs(), new Locale(
configuration.getVmsLocale()));
vmsDateFormatWithoutSecs.setTimeZone(timeZone);
// Internal scripts are all in GROOVY for now
//
ScriptExecutorFactory scriptFactory = ScriptExecutorFactory.newInstance("GROOVY");
multipleAuthorizeCommandExecutor =
scriptFactory.newScriptExecutor(getClass().getClassLoader(),
multipleAuthorizeCommandScript, true);
changeOwnPasswordCommandExecutor =
scriptFactory.newScriptExecutor(getClass().getClassLoader(),
changeOwnPasswordCommandScript, true);
dateCommandExecutor =
scriptFactory.newScriptExecutor(getClass().getClassLoader(), dateCommandScript,
true);
try {
connection = new VmsConnection(configuration, VmsConnector.SHORT_WAIT);
} catch (Exception e) {
throw ConnectorException.wrap(e);
}
}
public Object runScriptOnResource(ScriptContext request, OperationOptions options) {
String user = options.getRunAsUser();
GuardedString password = options.getRunWithPassword();
if (user != null && password == null) {
throw new ConnectorException(configuration
.getMessage(VmsMessages.PASSWORD_REQUIRED_FOR_RUN_AS));
}
if (user != null && isDisableUserLogins(configuration)) {
throw new ConnectorException(configuration.getMessage(VmsMessages.RUN_AS_WHEN_DISABLED));
}
Map<String, Object> arguments = request.getScriptArguments();
String language = request.getScriptLanguage();
if (!"DCL".equalsIgnoreCase(language)) {
throw new ConnectorException(configuration.getMessage(
VmsMessages.UNSUPPORTED_SCRIPTING_LANGUAGE, language));
}
String script = request.getScriptText();
try {
if (user != null) {
VmsConfiguration configuration = new VmsConfiguration(this.configuration);
configuration.setUserName(user);
configuration.setPassword(password);
VmsConnection connection =
new VmsConnection(configuration, VmsConnector.SHORT_WAIT);
Object result = executeScript(connection, script, SHORT_WAIT, arguments);
connection.dispose();
return result;
} else {
return executeScript(connection, script, SHORT_WAIT, arguments);
}
} catch (Exception e) {
throw ConnectorException.wrap(e);
}
}
private boolean isDisableUserLogins(VmsConfiguration configuration) {
boolean disableUserLogins = false;
if (null != configuration && configuration.getDisableUserLogins() != null) {
disableUserLogins = configuration.getDisableUserLogins();
}
return disableUserLogins;
}
private boolean isSSH(VmsConfiguration configuration) {
boolean isSSH = false;
if (configuration.getSSH() != null) {
isSSH = configuration.getSSH();
}
return isSSH;
}
private boolean isAttributeTrue(Attribute attribute) {
if (attribute == null) {
return false;
}
return AttributeUtil.getBooleanValue(attribute);
}
protected String executeCommand(VmsConnection connection, String command) {
connection.resetStandardOutput();
try {
connection.send(command);
connection.waitFor(configuration.getLocalHostShellPrompt(), SHORT_WAIT);
} catch (Exception e) {
throw ConnectorException.wrap(e);
}
String output = connection.getStandardOutput();
int index = output.lastIndexOf(configuration.getLocalHostShellPrompt());
if (index != -1) {
output = output.substring(0, index);
}
String terminator = null;
if (isSSH(configuration)) {
terminator = "\r\n";
} else {
terminator = "\n\r";
}
// Strip trailing NULs (seen with SSH)
//
while (output.endsWith("\0")) {
output = output.substring(0, output.length() - 1);
}
// trim off starting or ending \n
//
if (output.startsWith(terminator)) {
output = output.substring(terminator.length());
}
if (output.endsWith(terminator)) {
output = output.substring(0, output.length() - terminator.length());
}
return output;
}
protected String[] executeScript(VmsConnection connection, String action, int timeout,
Map<String, Object> args) {
// create a temp file
//
String tmpfile = UUID.randomUUID().toString();
// Create an empty error file, so that we can send it back if there are
// no errors
//
executeCommand(connection, "SET DEFAULT SYS$LOGIN");
executeCommand(connection, "OPEN/WRITE OUTPUT_FILE " + tmpfile + ".ERROR");
executeCommand(connection, "CLOSE OUTPUT_FILE");
// create script and append actions
//
executeCommand(connection, "OPEN/WRITE OUTPUT_FILE " + tmpfile + ".COM");
executeCommand(connection, "WRITE OUTPUT_FILE \"$ DEFINE SYS$ERROR " + tmpfile + ".ERROR");
executeCommand(connection, "WRITE OUTPUT_FILE \"$ DEFINE SYS$OUTPUT " + tmpfile + ".OUTPUT");
setEnvironmentVariables(connection, args);
StringTokenizer st = new StringTokenizer(action, "\r\n\f");
if (st.hasMoreTokens()) {
do {
String token = st.nextToken();
if (!StringUtil.isBlank(token)) {
executeCommand(connection, "WRITE OUTPUT_FILE "
+ quoteForDCLWhenNeeded(token, true));
}
} while (st.hasMoreTokens());
}
executeCommand(connection, "CLOSE OUTPUT_FILE");
executeCommand(connection, "@" + tmpfile);
executeCommand(connection, "CMDSTATUS=$STATUS");
executeCommand(connection, "SET DEFAULT SYS$LOGIN");
executeCommand(connection, "DEAS SYS$ERROR");
executeCommand(connection, "DEAS SYS$OUTPUT");
// Capture the output
//
String status = executeCommand(connection, "WRITE SYS$OUTPUT CMDSTATUS");
String output = executeCommand(connection, "TYPE " + tmpfile + ".OUTPUT");
String error = executeCommand(connection, "TYPE " + tmpfile + ".ERROR");
executeCommand(connection, "DELETE " + tmpfile + ".OUTPUT;");
executeCommand(connection, "DELETE " + tmpfile + ".ERROR;*");
executeCommand(connection, "DELETE " + tmpfile + ".COM;");
return new String[] { status, output, error };
}
private void setEnvironmentVariables(VmsConnection connection, Map<String, Object> args) {
Set<Map.Entry<String, Object>> keyset = args.entrySet();
for (Map.Entry<String, Object> entry : keyset) {
String name = entry.getKey();
Object value = entry.getValue();
String dclAssignment = "$" + name + "=" + quoteForDCLWhenNeeded(value.toString(), true);
String line = "WRITE OUTPUT_FILE " + quoteForDCLWhenNeeded(dclAssignment, true);
if (line.length() < 255) {
executeCommand(connection, line);
}
}
}
public Attribute normalizeAttribute(ObjectClass oclass, Attribute attribute) {
// Because VMS reports NAME (and UID) in upper case, they will not match
// against a lower case name unless the values are forced to upper case.
//
if (attribute instanceof Name) {
Name name = (Name) attribute;
return new Name(name.getNameValue().toUpperCase());
}
if (attribute instanceof Uid) {
Uid uid = (Uid) attribute;
return new Uid(uid.getUidValue().toUpperCase());
}
return attribute;
}
public Uid authenticate(ObjectClass objectClass, String username, GuardedString password,
OperationOptions options) {
VmsConfiguration configuration = new VmsConfiguration(this.configuration);
configuration.setUserName(username);
configuration.setPassword(password);
try {
VmsConnection connection = new VmsConnection(configuration, VmsConnector.SHORT_WAIT);
connection.dispose();
} catch (Exception e) {
throw ConnectorException.wrap(e);
}
return new Uid(username);
}
public Uid resolveUsername(ObjectClass objectClass, String username, OperationOptions options) {
if (!objectClass.is(ObjectClass.ACCOUNT_NAME)) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.UNSUPPORTED_OBJECT_CLASS, objectClass.getObjectClassValue()));
}
LocalHandler handler = new LocalHandler();
List<String> query =
createFilterTranslator(objectClass, options).translate(
new EqualsFilter(AttributeBuilder.build(Name.NAME, username)));
executeQuery(ObjectClass.ACCOUNT, query.get(0), handler, options);
if (!handler.iterator().hasNext()) {
throw new UnknownUidException();
}
return handler.iterator().next().getUid();
}
private String getCreateDirCommand(Map<String, Attribute> attrMap, Attribute createDirectory) {
if (createDirectory == null || !AttributeUtil.getBooleanValue(createDirectory)) {
return null;
}
Attribute uicAttr = attrMap.get(ATTR_UIC);
Attribute deviceAttr = attrMap.get(ATTR_DEVICE);
Attribute directoryAttr = attrMap.get(ATTR_DIRECTORY);
if (uicAttr == null) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.MISSING_REQUIRED_ATTRIBUTE, ATTR_UIC));
}
if (deviceAttr == null) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.MISSING_REQUIRED_ATTRIBUTE, ATTR_DEVICE));
}
if (directoryAttr == null) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.MISSING_REQUIRED_ATTRIBUTE, ATTR_DIRECTORY));
}
String uic = AttributeUtil.getStringValue(uicAttr);
String device = AttributeUtil.getStringValue(deviceAttr);
String directory = AttributeUtil.getStringValue(directoryAttr);
if (StringUtil.isBlank(uic)) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.MISSING_ATTRIBUTE_VALUE, ATTR_UIC));
}
if (StringUtil.isBlank(device)) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.MISSING_ATTRIBUTE_VALUE, ATTR_DEVICE));
}
if (StringUtil.isBlank(directory)) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.MISSING_ATTRIBUTE_VALUE, ATTR_DIRECTORY));
}
StringBuffer cmd = new StringBuffer();
cmd.append("CREATE/DIRECTORY/PROTECTION=SYSTEM:RWED/OWNER_UIC=");
cmd.append(uic);
cmd.append(" ");
if (!device.endsWith(":")) {
device += ":";
}
cmd.append(device);
if (!directory.endsWith("]")) {
directory = "[" + directory + "]";
}
cmd.append(directory);
return cmd.toString();
}
private String getCopyLoginCommand(Map<String, Attribute> attrMap, Attribute copyLoginScript,
Attribute loginScriptSourceAttr) {
if (copyLoginScript == null || !AttributeUtil.getBooleanValue(copyLoginScript)) {
return null;
}
Attribute uicAttr = attrMap.get(ATTR_UIC);
Attribute deviceAttr = attrMap.get(ATTR_DEVICE);
Attribute directoryAttr = attrMap.get(ATTR_DIRECTORY);
if (uicAttr == null) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.MISSING_REQUIRED_ATTRIBUTE, ATTR_UIC));
}
if (deviceAttr == null) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.MISSING_REQUIRED_ATTRIBUTE, ATTR_DEVICE));
}
if (directoryAttr == null) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.MISSING_REQUIRED_ATTRIBUTE, ATTR_DIRECTORY));
}
if (loginScriptSourceAttr == null) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.MISSING_REQUIRED_ATTRIBUTE, ATTR_LOGIN_SCRIPT_SOURCE));
}
String uic = AttributeUtil.getStringValue(uicAttr);
String device = AttributeUtil.getStringValue(deviceAttr);
String directory = AttributeUtil.getStringValue(directoryAttr);
String loginScriptSource = AttributeUtil.getStringValue(loginScriptSourceAttr);
if (StringUtil.isBlank(uic)) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.MISSING_ATTRIBUTE_VALUE, ATTR_UIC));
}
if (StringUtil.isBlank(device)) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.MISSING_ATTRIBUTE_VALUE, ATTR_DEVICE));
}
if (StringUtil.isBlank(directory)) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.MISSING_ATTRIBUTE_VALUE, ATTR_DIRECTORY));
}
if (StringUtil.isBlank(loginScriptSource)) {
throw new IllegalArgumentException(configuration.getMessage(
VmsMessages.MISSING_ATTRIBUTE_VALUE, ATTR_LOGIN_SCRIPT_SOURCE));
}
StringBuffer cmd = new StringBuffer();
cmd.append("COPY ");
cmd.append(loginScriptSource);
cmd.append(" ");
if (!device.endsWith(":")) {
device += ":";
}
cmd.append(device);
if (!directory.endsWith("]")) {
directory = "[" + directory + "]";
}
cmd.append(directory);
return cmd.toString();
}
/**
* Find an unused member in the group.
*
* @param uicWithWildCard
* @return
*/
private String getUnusedUicForGroup(String uicWithWildCard) {
try {
Map<String, Object> variables = getVariablesForCommand();
fillInCommand("SHOW/BRIEF " + uicWithWildCard + "\n", variables);
String output = (String) multipleAuthorizeCommandExecutor.execute(variables);
Pattern uicPattern = Pattern.compile("\\[(\\d+),(\\d+)\\]");
Matcher matcher = uicPattern.matcher(output);
int offset = 0;
int max = 0;
String group = null;
while (matcher.find(offset)) {
group = matcher.group(1);
int value = Integer.valueOf(matcher.group(2), 8).intValue();
if (value > max) {
max = value;
}
offset = matcher.end(1);
}
return "[" + group + "," + Integer.toOctalString(max + 1) + "]";
} catch (Exception e) {
throw ConnectorException.wrap(e);
}
}
private String getNextUicForGroup(String uic) {
Pattern uicPattern = Pattern.compile("\\[(\\d+),(\\d+)\\]");
Matcher matcher = uicPattern.matcher(uic);
if (matcher.matches()) {
int value = Integer.valueOf(matcher.group(2), 8).intValue();
return "[" + matcher.group(1) + "," + Integer.toOctalString(value + 1) + "]";
} else {
return null;
}
}
/**
* Determine if the UIC is only assigned to a single user.
*
* @param uic
* @return
*/
private boolean isUnique(String uic) {
try {
Map<String, Object> variables = getVariablesForCommand();
fillInCommand("SHOW/BRIEF " + uic + "\n", variables);
String output = (String) multipleAuthorizeCommandExecutor.execute(variables);
return output.indexOf(uic) == output.lastIndexOf(uic);
} catch (Exception e) {
throw ConnectorException.wrap(e);
}
}
public static class LocalHandler implements ResultsHandler, Iterable<ConnectorObject> {
private List<ConnectorObject> objects = new LinkedList<ConnectorObject>();
public boolean handle(ConnectorObject object) {
objects.add(object);
return true;
}
public Iterator<ConnectorObject> iterator() {
return objects.iterator();
}
}
private Map<String, Object> getVariablesForCommand() {
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("SHELL_PROMPT", configuration.getLocalHostShellPrompt());
variables.put("SHORT_WAIT", SHORT_WAIT);
variables.put("LONG_WAIT", LONG_WAIT);
variables.put("UAF_PROMPT", UAF_PROMPT);
variables.put("UAF_PROMPT_CONTINUE", UAF_PROMPT_CONTINUE);
variables.put("CONNECTION", connection);
return variables;
}
}