/*
* ====================
* 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.framework.common.objects;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import org.identityconnectors.common.CollectionUtil;
import org.identityconnectors.common.StringUtil;
import org.identityconnectors.common.security.GuardedString;
import org.identityconnectors.framework.api.operations.CreateApiOp;
import org.identityconnectors.framework.api.operations.SearchApiOp;
import org.identityconnectors.framework.common.FrameworkUtil;
/**
* Simplifies constructing instances of {@link Attribute}. A {@code Connector}
* developer does not need to implement the {@code Attribute} interface. The
* builder returns an instance of an implementation of {@link Attribute} that
* overrides the methods {@code equals()}, {@code hashcode()} and
* {@code toString()} to provide a uniform and robust class. This implementation
* is backed by an {@link ArrayList} that contains the values and preserves the
* order of those values (in case the order of values is significant to the
* target system or application).
*
* @author Will Droste
* @since 1.0
*/
public final class AttributeBuilder {
private final static String NAME_ERROR = "Name must not be blank!";
String name;
List<Object> value;
/**
* Creates a attribute with the specified name and a {@code null} value.
*
* @param name
* unique name of the attribute.
* @return instance of {@code Attribute} with a {@code null} value.
*/
public static Attribute build(final String name) {
AttributeBuilder bld = new AttributeBuilder();
bld.setName(name);
return bld.build();
}
/**
* Creates an {@code Attribute} with the name and the values provided.
*
* @param name
* unique name of the attribute.
* @param args
* variable number of arguments that are used as values for the
* attribute.
* @return instance of {@code Attribute} with the specified name and a value
* that includes the arguments provided.
*/
public static Attribute build(final String name, final Object... args) {
AttributeBuilder bld = new AttributeBuilder();
bld.setName(name);
bld.addValue(args);
return bld.build();
}
/**
* Creates an {@code Attribute} with the name and the values provided.
*
* @param name
* unique name of the attribute.
* @param obj
* a collection of objects that are used as values for the
* attribute.
* @return instance of {@code Attribute} with the specified name and a value
* that includes the arguments provided.
*/
public static Attribute build(final String name, final Collection<?> obj) {
// this method needs to be able to create the sub-classes
// Name, Uid, ObjectClass
AttributeBuilder bld = new AttributeBuilder();
bld.setName(name);
bld.addValue(obj);
return bld.build();
}
/**
* Get the name of the attribute that is being built.
*
* @return The name of the attribute.
*/
public String getName() {
return name;
}
/**
* Set the name of the attribute that is being built.
*
* @throws IllegalArgumentException
* if the name parameter is blank.
*/
public AttributeBuilder setName(final String name) {
if (StringUtil.isBlank(name)) {
throw new IllegalArgumentException(NAME_ERROR);
}
this.name = name;
return this;
}
/**
* Return any current value of the attribute that is being built.
*
* @return any current value of the attribute that is being built.
*/
public List<Object> getValue() {
return value == null ? null : CollectionUtil.asReadOnlyList(value);
}
/**
* Add each of the specified objects as a values for the attribute that is
* being built.
*
* @param objs
* the values to add
* @throws NullPointerException
* if any of the values is null.
*/
public AttributeBuilder addValue(final Object... objs) {
if (objs != null) {
addValuesInternal(Arrays.asList(objs));
}
return this;
}
/**
* Adds each object in the collection as a value for the attribute that is
* being built.
*
* @param obj
* the values to add
* @throws NullPointerException
* if any of the values is null.
*/
public AttributeBuilder addValue(final Collection<?> obj) {
addValuesInternal(obj);
return this;
}
/**
* @return a new attribute with the name and any values that have been
* provided to the builder.
* @throws IllegalArgumentException
* if no name has been provided.
*/
public Attribute build() {
if (StringUtil.isBlank(name)) {
throw new IllegalArgumentException(NAME_ERROR);
}
// check for subclasses and some operational attributes..
if (Uid.NAME.equals(name)) {
return new Uid(getSingleStringValue());
} else if (Name.NAME.equals(name)) {
return new Name(getSingleStringValue());
}
return new Attribute(name, value);
}
/**
* Confirm that the attribute that is being built has at most a single
* value.
*
* @throws IllegalArgumentException
* if the attribute contains more than a single value.
*/
private void checkSingleValue() {
if (value == null || value.size() != 1) {
throw new IllegalArgumentException("Must be a single value.");
}
}
/**
* @return the single string value of the attribute that is being built.
* @throws IllegalArgumentException
* if the attribute contains more than a single value, or if the
* value is not of type {@code String}.
*/
private String getSingleStringValue() {
checkSingleValue();
if (!(value.get(0) instanceof String)) {
throw new IllegalArgumentException("Attribute value must be an instance of String.");
}
return (String) value.get(0);
}
private void addValuesInternal(final Iterable<?> values) {
if (values != null) {
// make sure the list is ready to receive values.
if (value == null) {
value = new ArrayList<Object>();
}
// add each value checking to make sure its correct
for (Object v : values) {
FrameworkUtil.checkAttributeValue(name, v);
value.add(v);
}
}
}
// =======================================================================
// Operational Attributes
// =======================================================================
/**
* Builds an {@linkplain OperationalAttributes operational attribute} that
* represents the date and time that a password will expire on a target
* system or application.
*
* @param dateTime
* UTC time in milliseconds.
* @return an {@code Attribute} with the
* {@linkplain OperationalAttributes#PASSWORD_EXPIRATION_DATE_NAME
* pre-defined name for password expiration date}.
*/
public static Attribute buildPasswordExpirationDate(final Date dateTime) {
return buildPasswordExpirationDate(dateTime.getTime());
}
/**
* Builds an {@linkplain OperationalAttributes operational attribute} that
* represents the date/time that a password will expire on a target system
* or application.
*
* @param dateTime
* UTC time in milliseconds.
* @return an {@code Attribute} with the
* {@linkplain OperationalAttributes#PASSWORD_EXPIRATION_DATE_NAME
* pre-defined name for password expiration date}.
*/
public static Attribute buildPasswordExpirationDate(final long dateTime) {
return build(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME, dateTime);
}
/**
* Builds an {@linkplain OperationalAttributes operational attribute} that
* represents the password of an object on a target system or application.
*
* @param password
* the string that represents a password.
* @return an {@code Attribute} with the
* {@linkplain OperationalAttributes#PASSWORD_NAME predefined name
* for password}.
*/
public static Attribute buildPassword(final GuardedString password) {
return build(OperationalAttributes.PASSWORD_NAME, password);
}
/**
* Builds an {@linkplain OperationalAttributes operational attribute} that
* represents the password of an object on a target system or application.
* <p>
* The caller is responsible for clearing out the array of characters.
*
* @param password
* the characters that represent a password.
* @return an {@code Attribute} with the
* {@linkplain OperationalAttributes#PASSWORD_NAME predefined name
* for password}.
*/
public static Attribute buildPassword(final char[] password) {
return buildPassword(new GuardedString(password));
}
/**
* Builds an {@linkplain OperationalAttributes operational attribute} that
* represents the current password of an object on a target system or
* application.
* <p>
* Passing the current password indicates the account owner (and not an
* administrator) is changing the password. The use case is that an
* administrator password change may not keep history or validate against
* policy.
*
* @param password
* the string that represents a password.
* @return an {@code Attribute} with the
* {@linkplain OperationalAttributes#CURRENT_PASSWORD_NAME
* predefined name for current password}.
*/
public static Attribute buildCurrentPassword(final GuardedString password) {
return build(OperationalAttributes.CURRENT_PASSWORD_NAME, password);
}
/**
* Builds an {@linkplain OperationalAttributes operational attribute} that
* represents the current password of an object on a target system or
* application.
* <p>
* Passing the current password indicates the account owner (and not an
* administrator) is changing the password. The use case is that an
* administrator password change may not keep history or validate against
* policy.
* <p>
* The caller is responsible for clearing out the array of characters.
*
* @param password
* the characters that represent a password.
* @return an {@code Attribute} with the
* {@linkplain OperationalAttributes#CURRENT_PASSWORD_NAME
* predefined name for current password}.
*/
public static Attribute buildCurrentPassword(final char[] password) {
return buildCurrentPassword(new GuardedString(password));
}
/**
* Builds an {@linkplain OperationalAttributes operational attribute} that
* represents whether object is enabled on a target system or application.
* <ul>
* <li>Use this attribute with {@link CreateApiOp} or
* {@link org.identityconnectors.framework.api.operations.UpdateApiOp} to
* enable or disable an object.</li>
* <li>Read this attribute from
* {@link org.identityconnectors.framework.api.operations.GetApiOp} to
* determine whether an object currently is enabled or disabled.</li>
* <li>Use this attribute with {@link SearchApiOp} to select objects that
* are enabled or to select objects that are disabled.</li>
* </ul>
*
* @param value
* true indicates the object is enabled; otherwise false.
* @return an {@code Attribute} with the
* {@linkplain OperationalAttributes#ENABLE_NAME predefined name for
* enabled}.
*/
public static Attribute buildEnabled(final boolean value) {
return build(OperationalAttributes.ENABLE_NAME, value);
}
/**
* Builds an {@linkplain OperationalAttributes operational attribute} that
* represents the date and time to enable an object on a target system or
* application.
* <ul>
* <li>Use this attribute with {@link CreateApiOp} or
* {@link org.identityconnectors.framework.api.operations.UpdateApiOp} to
* set a date and time to enable an object.</li>
* <li>Read this attribute from
* {@link org.identityconnectors.framework.api.operations.GetApiOp} to
* determine when an object will be enabled.</li>
* <li>Use this attribute with {@link SearchApiOp} to select objects that
* are scheduled to be enabled at a certain date and time.</li>
* </ul>
*
* @param date
* The date and time to enable a particular object.
* @return an {@code Attribute} with the
* {@linkplain OperationalAttributes#ENABLE_DATE_NAME predefined
* name for enable date}.
*/
public static Attribute buildEnableDate(final Date date) {
return buildEnableDate(date.getTime());
}
/**
* Builds an {@linkplain OperationalAttributes operational attribute} that
* represents the date and time to enable an object on a target system or
* application. The date-and-time parameter is UTC in milliseconds.
* <ul>
* <li>Use this attribute with {@link CreateApiOp} or
* {@link org.identityconnectors.framework.api.operations.UpdateApiOp} to
* set a date and time to enable an object.</li>
* <li>Read this attribute from
* {@link org.identityconnectors.framework.api.operations.GetApiOp} to
* determine when an object will be enabled.</li>
* <li>Use this attribute with {@link SearchApiOp} to select objects that
* are scheduled to be enabled at a certain date and time.</li>
* </ul>
*
* @param date
* The date and time (UTC in milliseconds) to enable a particular
* object.
* @return an {@code Attribute} with the
* {@linkplain OperationalAttributes#ENABLE_DATE_NAME predefined
* name for enable date}.
*/
public static Attribute buildEnableDate(final long date) {
return build(OperationalAttributes.ENABLE_DATE_NAME, date);
}
/**
* Builds an {@linkplain OperationalAttributes operational attribute} that
* represents the date and time to disable an object on a target system or
* application.
* <ul>
* <li>Use this attribute with {@link CreateApiOp} or
* {@link org.identityconnectors.framework.api.operations.UpdateApiOp} to
* set a date and time to disable an object.</li>
* <li>Read this attribute from
* {@link org.identityconnectors.framework.api.operations.GetApiOp} to
* determine when an object will be disabled.</li>
* <li>Use this attribute with {@link SearchApiOp} to select objects that
* are scheduled to be disabled at a certain date and time.</li>
* </ul>
*
* @param date
* The date and time to disable a particular object.
* @return an {@code Attribute} with the
* {@linkplain OperationalAttributes#DISABLE_DATE_NAME predefined
* name for disable date}.
*/
public static Attribute buildDisableDate(final Date date) {
return buildDisableDate(date.getTime());
}
/**
* Builds an {@linkplain OperationalAttributes operational attribute} that
* represents the date and time to disable an object on a target system or
* application. The date-and-time parameter is UTC in milliseconds.
* <ul>
* <li>Use this attribute with {@link CreateApiOp} or
* {@link org.identityconnectors.framework.api.operations.UpdateApiOp} to
* set a date and time to disable an object.</li>
* <li>Read this attribute from
* {@link org.identityconnectors.framework.api.operations.GetApiOp} to
* determine when an object will be disabled.</li>
* <li>Use this attribute with {@link SearchApiOp} to select objects that
* are scheduled to be disabled at a certain date and time.</li>
* </ul>
*
* @param date
* The date and time (UTC in milliseconds) to disable a
* particular object.
* @return an {@code Attribute} with the
* {@linkplain OperationalAttributes#DISABLE_DATE_NAME predefined
* name for disable date}.
*/
public static Attribute buildDisableDate(final long date) {
return build(OperationalAttributes.DISABLE_DATE_NAME, date);
}
/**
* Builds an {@linkplain OperationalAttributes operational attribute} that
* represents whether an object is locked out on a target system or
* application.
* <ul>
* <li>Read this attribute from
* {@link org.identityconnectors.framework.api.operations.GetApiOp} to
* determine whether an object is currently locked out.</li>
* <li>Use this attribute with
* {@link org.identityconnectors.framework.api.operations.UpdateApiOp} to
* clear the lock-out status of an object (or to set the lock-out status of
* an object).</li>
* <li>Use this attribute with {@link SearchApiOp} to select objects that
* are currently locked out (or to select objects that are not currently
* locked out).</li>
* </ul>
*
* @param lock
* true if the object is locked out; otherwise false.
* @return an {@code Attribute} with the
* {@linkplain OperationalAttributes#LOCK_OUT_NAME predefined name
* for lockout state}.
*/
public static Attribute buildLockOut(final boolean lock) {
return build(OperationalAttributes.LOCK_OUT_NAME, lock);
}
/**
* Builds an {@linkplain OperationalAttributes operational attribute} that
* represents whether the password of an object is expired on a target
* system or application.
* <ul>
* <li>Read this attribute from
* {@link org.identityconnectors.framework.api.operations.GetApiOp} to
* determine whether the password of an object is currently expired.</li>
* <li>Use this attribute with
* {@link org.identityconnectors.framework.api.operations.UpdateApiOp} to
* expire the password of an object (or to clear the expired status of the
* password of an object).</li>
* <li>Use this attribute with {@link SearchApiOp} to select objects that
* have passwords that are currently expired (or to select objects that have
* passwords that are not currently expired).</li>
* </ul>
*
* @param value
* from the API true expires and from the SPI its shows its
* either expired or not.
* @return an {@code Attribute} with the
* {@linkplain OperationalAttributes#PASSWORD_EXPIRED_NAME
* predefined name for password expiration state}.
*/
public static Attribute buildPasswordExpired(final boolean value) {
return build(OperationalAttributes.PASSWORD_EXPIRED_NAME, value);
}
// =======================================================================
// Pre-defined Attributes
// =======================================================================
/**
* Builds an {@linkplain PredefinedAttributes pre-defined attribute} that
* represents the date and time of the most recent login for an object (such
* as an account) on a target system or application.
*
* @param date
* The date and time of the last login.
* @return an {@code Attribute} with the
* {@linkplain PredefinedAttributes#LAST_LOGIN_DATE_NAME predefined
* name for password expiration state}.
*/
public static Attribute buildLastLoginDate(final Date date) {
return buildLastLoginDate(date.getTime());
}
/**
* Builds an {@linkplain PredefinedAttributes pre-defined attribute} that
* represents the date and time of the most recent login for an object (such
* as an account) on a target system or application.
* <p>
* The time parameter is UTC in milliseconds.
*
* @param date
* The date and time (UTC in milliseconds) of the last login.
* @return an {@code Attribute} with the
* {@linkplain PredefinedAttributes#LAST_LOGIN_DATE_NAME predefined
* name for password expiration state}.
*/
public static Attribute buildLastLoginDate(final long date) {
return build(PredefinedAttributes.LAST_LOGIN_DATE_NAME, date);
}
/**
* Builds an {@linkplain PredefinedAttributes pre-defined attribute} that
* represents the date and time that the password was most recently changed
* for an object (such as an account) on a target system or application.
*
* @param date
* The date and time that the password was most recently changed.
* @return an {@code Attribute} with the
* {@linkplain PredefinedAttributes#LAST_PASSWORD_CHANGE_DATE_NAME
* predefined name for password expiration state}.
*/
public static Attribute buildLastPasswordChangeDate(final Date date) {
return buildLastPasswordChangeDate(date.getTime());
}
/**
* Builds an {@linkplain PredefinedAttributes pre-defined attribute} that
* represents the date and time that the password was most recently changed
* for an object (such as an account) on a target system or application.
* <p>
* The time parameter is UTC in milliseconds.
*
* @param date
* The date and time that the password was most recently changed.
* @return an {@code Attribute} with the
* {@linkplain PredefinedAttributes#LAST_PASSWORD_CHANGE_DATE_NAME
* predefined name for password expiration state}.
*/
public static Attribute buildLastPasswordChangeDate(final long date) {
return build(PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME, date);
}
/**
* Builds an {@linkplain PredefinedAttributes pre-defined attribute} that
* represents how often the password must be changed for an object (such as
* an account) on a target system or application.
* <p>
* The value for this attribute is expressed in milliseconds.
*
* @param value
* The number of milliseconds between the time that the password
* was most recently changed and the time when the password must
* be changed again.
* @return an {@code Attribute} with the
* {@linkplain PredefinedAttributes#PASSWORD_CHANGE_INTERVAL_NAME
* predefined name for password expiration state}.
*/
public static Attribute buildPasswordChangeInterval(final long value) {
return build(PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME, value);
}
}