/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2010 Sun Microsystems, Inc.
*/
package org.opends.sdk.controls;
import static com.sun.opends.sdk.messages.Messages.*;
import static com.sun.opends.sdk.util.StaticUtils.getExceptionMessage;
import java.io.IOException;
import java.util.*;
import org.opends.sdk.*;
import org.opends.sdk.asn1.ASN1;
import org.opends.sdk.asn1.ASN1Reader;
import org.opends.sdk.asn1.ASN1Writer;
import org.opends.sdk.schema.AttributeType;
import org.opends.sdk.schema.Schema;
import org.opends.sdk.schema.UnknownSchemaElementException;
import com.sun.opends.sdk.util.Validator;
/**
* A partial implementation of the get effective rights request control as
* defined in draft-ietf-ldapext-acl-model. The main differences are:
*
* - The response control is not supported. Instead the OpenDS implementation
* creates attributes containing effective rights information with the entry
* being returned.
*
- The attribute type names are dynamically created.
*
- The set of attributes for which effective rights information is to be
* requested can be included in the control.
*
* The get effective rights request control value has the following BER
* encoding:
*
*
* GetRightsControl ::= SEQUENCE {
* authzId authzId -- Only the "dn:DN" form is supported.
* attributes SEQUENCE OF AttributeType
* }
*
*
* @see draft-ietf-ldapext-acl-model
* - Access Control Model for LDAPv3
**/
public final class GetEffectiveRightsRequestControl implements Control
{
/**
* The OID for the get effective rights request control.
*/
public static final String OID = "1.3.6.1.4.1.42.2.27.9.5.2";
/**
* A decoder which can be used for decoding the get effective rights request
* control.
*/
public static final ControlDecoder DECODER =
new ControlDecoder()
{
public GetEffectiveRightsRequestControl decodeControl(
final Control control, final DecodeOptions options)
throws DecodeException
{
Validator.ensureNotNull(control);
if (control instanceof GetEffectiveRightsRequestControl)
{
return (GetEffectiveRightsRequestControl) control;
}
if (!control.getOID().equals(OID))
{
final LocalizableMessage message = ERR_GETEFFECTIVERIGHTS_CONTROL_BAD_OID
.get(control.getOID(), OID);
throw DecodeException.error(message);
}
DN authorizationDN = null;
List attributes = Collections.emptyList();
if (control.hasValue())
{
final ASN1Reader reader = ASN1.getReader(control.getValue());
try
{
reader.readStartSequence();
final String authzIDString = reader.readOctetStringAsString();
final String lowerAuthzIDString = authzIDString.toLowerCase();
Schema schema;
// Make sure authzId starts with "dn:" and is a valid DN.
if (lowerAuthzIDString.startsWith("dn:"))
{
final String authorizationDNString = authzIDString.substring(3);
schema = options.getSchemaResolver().resolveSchema(
authorizationDNString);
try
{
authorizationDN = DN.valueOf(authorizationDNString, schema);
}
catch (final LocalizedIllegalArgumentException e)
{
final LocalizableMessage message = ERR_GETEFFECTIVERIGHTS_INVALID_AUTHZIDDN
.get(getExceptionMessage(e));
throw DecodeException.error(message, e);
}
}
else
{
final LocalizableMessage message = INFO_GETEFFECTIVERIGHTS_INVALID_AUTHZID
.get(lowerAuthzIDString);
throw DecodeException.error(message);
}
// There is an sequence containing an attribute list, try to
// decode it.
if (reader.hasNextElement())
{
attributes = new LinkedList();
reader.readStartSequence();
while (reader.hasNextElement())
{
// Decode as an attribute type.
final String attributeName = reader.readOctetStringAsString();
AttributeType attributeType;
try
{
// FIXME: we're using the schema associated with the authzid
// which is not really correct. We should really use the schema
// associated with the entry.
attributeType = schema.getAttributeType(attributeName);
}
catch (final UnknownSchemaElementException e)
{
final LocalizableMessage message = ERR_GETEFFECTIVERIGHTS_UNKNOWN_ATTRIBUTE
.get(attributeName);
throw DecodeException.error(message, e);
}
attributes.add(attributeType);
}
reader.readEndSequence();
attributes = Collections.unmodifiableList(attributes);
}
reader.readEndSequence();
}
catch (final IOException e)
{
final LocalizableMessage message = INFO_GETEFFECTIVERIGHTS_DECODE_ERROR
.get(e.getMessage());
throw DecodeException.error(message);
}
}
return new GetEffectiveRightsRequestControl(control.isCritical(),
authorizationDN, attributes);
}
public String getOID()
{
return OID;
}
};
/**
* Creates a new get effective rights request control with the provided
* criticality, optional authorization name and attribute list.
*
* @param isCritical
* {@code true} if it is unacceptable to perform the operation
* without applying the semantics of this control, or {@code false}
* if it can be ignored.
* @param authorizationName
* The distinguished name of the user for which effective rights are
* to be returned, or {@code null} if the client's authentication ID
* is to be used.
* @param attributes
* The list of attributes for which effective rights are to be
* returned, which may be empty indicating that no attribute rights
* are to be returned.
* @return The new control.
* @throws NullPointerException
* If {@code attributes} was {@code null}.
*/
public static GetEffectiveRightsRequestControl newControl(
final boolean isCritical, final DN authorizationName,
final Collection attributes) throws NullPointerException
{
Validator.ensureNotNull(attributes);
final Collection copyOfAttributes = Collections
.unmodifiableList(new ArrayList(attributes));
return new GetEffectiveRightsRequestControl(isCritical, authorizationName,
copyOfAttributes);
}
/**
* Creates a new get effective rights request control with the provided
* criticality, optional authorization name and attribute list. The
* authorization name and attributes, if provided, will be decoded using the
* default schema.
*
* @param isCritical
* {@code true} if it is unacceptable to perform the operation
* without applying the semantics of this control, or {@code false}
* if it can be ignored.
* @param authorizationName
* The distinguished name of the user for which effective rights are
* to be returned, or {@code null} if the client's authentication ID
* is to be used.
* @param attributes
* The list of attributes for which effective rights are to be
* returned, which may be empty indicating that no attribute rights
* are to be returned.
* @return The new control.
* @throws UnknownSchemaElementException
* If the default schema is a strict schema and one or more of the
* requested attribute types were not recognized.
* @throws LocalizedIllegalArgumentException
* If {@code authorizationName} is not a valid LDAP string
* representation of a DN.
* @throws NullPointerException
* If {@code attributes} was {@code null}.
*/
public static GetEffectiveRightsRequestControl newControl(
final boolean isCritical, final String authorizationName,
final String... attributes) throws UnknownSchemaElementException,
LocalizedIllegalArgumentException, NullPointerException
{
Validator.ensureNotNull((Object) attributes);
final DN dn = authorizationName == null ? null : DN
.valueOf(authorizationName);
List copyOfAttributes;
if (attributes != null && attributes.length > 0)
{
copyOfAttributes = new ArrayList(attributes.length);
for (final String attribute : attributes)
{
copyOfAttributes.add(Schema.getDefaultSchema().getAttributeType(
attribute));
}
copyOfAttributes = Collections.unmodifiableList(copyOfAttributes);
}
else
{
copyOfAttributes = Collections.emptyList();
}
return new GetEffectiveRightsRequestControl(isCritical, dn,
copyOfAttributes);
}
// The DN representing the authzId (may be null meaning use the client's DN).
private final DN authorizationName;
// The unmodifiable list of attributes to be queried (may be empty).
private final Collection attributes;
private final boolean isCritical;
private GetEffectiveRightsRequestControl(final boolean isCritical,
final DN authorizationName, final Collection attributes)
{
this.isCritical = isCritical;
this.authorizationName = authorizationName;
this.attributes = attributes;
}
/**
* Returns an unmodifiable list of attributes for which effective rights are
* to be returned, which may be empty indicating that no attribute rights are
* to be returned.
*
* @return The unmodifiable list of attributes for which effective rights are
* to be returned.
*/
public Collection getAttributes()
{
return attributes;
}
/**
* Returns the distinguished name of the user for which effective rights are
* to be returned, or {@code null} if the client's authentication ID is to be
* used.
*
* @return The distinguished name of the user for which effective rights are
* to be returned.
*/
public DN getAuthorizationName()
{
return authorizationName;
}
/**
* {@inheritDoc}
*/
public String getOID()
{
return OID;
}
/**
* {@inheritDoc}
*/
public ByteString getValue()
{
final ByteStringBuilder buffer = new ByteStringBuilder();
final ASN1Writer writer = ASN1.getWriter(buffer);
try
{
writer.writeStartSequence();
if (authorizationName != null)
{
writer.writeOctetString("dn:" + authorizationName);
}
if (!attributes.isEmpty())
{
writer.writeStartSequence();
for (final AttributeType attribute : attributes)
{
writer.writeOctetString(attribute.getNameOrOID());
}
writer.writeEndSequence();
}
writer.writeEndSequence();
return buffer.toByteString();
}
catch (final IOException ioe)
{
// This should never happen unless there is a bug somewhere.
throw new RuntimeException(ioe);
}
}
/**
* {@inheritDoc}
*/
public boolean hasValue()
{
return authorizationName != null || !attributes.isEmpty();
}
/**
* {@inheritDoc}
*/
public boolean isCritical()
{
return isCritical;
}
/**
* {@inheritDoc}
*/
@Override
public String toString()
{
final StringBuilder builder = new StringBuilder();
builder.append("GetEffectiveRightsRequestControl(oid=");
builder.append(getOID());
builder.append(", criticality=");
builder.append(isCritical());
builder.append(", authorizationDN=\"");
builder.append(authorizationName);
builder.append("\"");
builder.append(", attributes=(");
builder.append(attributes);
builder.append("))");
return builder.toString();
}
}