/*
* 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 2006-2010 Sun Microsystems, Inc.
* Portions Copyright 2011-2013 ForgeRock AS
*/
package org.opends.dsml.protocol;
import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI;
import static org.opends.server.protocols.ldap.LDAPResultCode.
CLIENT_SIDE_CONNECT_ERROR;
import static org.opends.server.util.ServerConstants.SASL_MECHANISM_PLAIN;
import static org.opends.messages.CoreMessages.
INFO_RESULT_CLIENT_SIDE_ENCODING_ERROR;
import static org.opends.messages.CoreMessages.INFO_RESULT_AUTHORIZATION_DENIED;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.text.ParseException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.StringTokenizer;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.soap.*;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.opends.messages.Message;
import org.opends.server.controls.ProxiedAuthV2Control;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.asn1.ASN1Exception;
import org.opends.server.protocols.ldap.LDAPConstants;
import org.opends.server.protocols.ldap.LDAPFilter;
import org.opends.server.protocols.ldap.LDAPMessage;
import org.opends.server.protocols.ldap.LDAPResultCode;
import org.opends.server.protocols.ldap.SearchRequestProtocolOp;
import org.opends.server.schema.SchemaConstants;
import org.opends.server.tools.LDAPConnection;
import org.opends.server.tools.LDAPConnectionException;
import org.opends.server.tools.LDAPConnectionOptions;
import org.opends.server.tools.SSLConnectionException;
import org.opends.server.tools.SSLConnectionFactory;
import org.opends.server.types.ByteString;
import org.opends.server.types.DereferencePolicy;
import org.opends.server.types.LDAPException;
import org.opends.server.types.SearchScope;
import org.opends.server.util.Base64;
import org.w3c.dom.Document;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
/**
* This class provides the entry point for the DSML request.
* It parses the SOAP request, calls the appropriate class
* which performs the LDAP operation, and returns the response
* as a DSML response.
*/
public class DSMLServlet extends HttpServlet {
private static final String PKG_NAME = "org.opends.dsml.protocol";
private static final String PORT = "ldap.port";
private static final String HOST = "ldap.host";
private static final String USERDN = "ldap.userdn";
private static final String USERPWD = "ldap.userpassword";
private static final String USESSL = "ldap.usessl";
private static final String USESTARTTLS = "ldap.usestarttls";
private static final String TRUSTSTOREPATH = "ldap.truststore.path";
private static final String TRUSTSTOREPASSWORD = "ldap.truststore.password";
private static final String TRUSTALLCERTS = "ldap.trustall";
private static final String USEHTTPAUTHZID = "ldap.authzidtypeisid";
private static final String EXOPSTRINGPREFIX = "ldap.exop.string.";
private static final long serialVersionUID = -3748022009593442973L;
private static final AtomicInteger nextMessageID = new AtomicInteger(1);
// definitions of return error messages
private static final String MALFORMED_REQUEST = "malformedRequest";
private static final String NOT_ATTEMPTED = "notAttempted";
private static final String AUTHENTICATION_FAILED = "authenticationFailed";
private static final String COULD_NOT_CONNECT = "couldNotConnect";
private static final String GATEWAY_INTERNAL_ERROR = "gatewayInternalError";
private static final String UNRESOLVABLE_URI = "unresolvableURI";
// definitions of onError values
private static final String ON_ERROR_RESUME = "resume";
private static final String ON_ERROR_EXIT = "exit";
private static JAXBContext jaxbContext;
private ObjectFactory objFactory;
private MessageFactory messageFactory;
private DocumentBuilder db;
private static Schema schema;
// this extends the default handler of SAX parser. It helps to retrieve the
// requestID value when the xml request is malformed and thus unparsable
// using SOAP or JAXB.
private DSMLContentHandler contentHandler;
private String hostName;
private Integer port;
private String userDN;
private String userPassword;
private Boolean useSSL;
private Boolean useStartTLS;
private String trustStorePathValue;
private String trustStorePasswordValue;
private Boolean trustAll;
private Boolean useHTTPAuthzID;
private HashSet<String> exopStrings = new HashSet<String>();
private org.opends.server.types.Control proxyAuthzControl = null;
/**
* This method will be called by the Servlet Container when
* this servlet is being placed into service.
*
* @param config - the <CODE>ServletConfig</CODE> object that
* contains configuration information for this servlet.
* @throws ServletException If an error occurs during processing.
*/
@Override
public void init(ServletConfig config) throws ServletException {
try {
hostName = config.getServletContext().getInitParameter(HOST);
port = new Integer(config.getServletContext().getInitParameter(PORT));
userDN = config.getServletContext().getInitParameter(USERDN);
userPassword = config.getServletContext().getInitParameter(USERPWD);
useSSL = Boolean.valueOf(
config.getServletContext().getInitParameter(USESSL));
useStartTLS = Boolean.valueOf(
config.getServletContext().getInitParameter(USESTARTTLS));
trustStorePathValue =
config.getServletContext().getInitParameter(TRUSTSTOREPATH);
trustStorePasswordValue =
config.getServletContext().getInitParameter(TRUSTSTOREPASSWORD);
trustAll = Boolean.valueOf(
config.getServletContext().getInitParameter(TRUSTALLCERTS));
useHTTPAuthzID = Boolean.valueOf(
config.getServletContext().getInitParameter(USEHTTPAUTHZID));
/*
* Find all the param-names matching the pattern:
* ldap.exop.string.1.2.3.4.5
* and if the value's true then mark that OID (1.2.3.4.5) as one returning
* a string value.
*/
Enumeration names = config.getServletContext().getInitParameterNames();
while (names.hasMoreElements())
{
String name = (String) names.nextElement();
if (name.startsWith(EXOPSTRINGPREFIX) &&
Boolean.valueOf(config.getServletContext().getInitParameter(name)))
{
exopStrings.add(name.substring(EXOPSTRINGPREFIX.length()));
}
}
// allow the use of anyURI values in adds and modifies
System.setProperty("mapAnyUriToUri", "true");
if(jaxbContext==null)
jaxbContext = JAXBContext.newInstance(PKG_NAME,
this.getClass().getClassLoader());
// assign the DSMLv2 schema for validation
if(schema==null)
{
URL url = getClass().getResource("/resources/DSMLv2.xsd");
if ( url != null ) {
SchemaFactory sf = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI);
schema = sf.newSchema(url);
}
}
objFactory = new ObjectFactory();
messageFactory = MessageFactory.newInstance();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
db = dbf.newDocumentBuilder();
this.contentHandler = new DSMLContentHandler();
DirectoryServer.bootstrapClient();
} catch (Exception je) {
je.printStackTrace();
throw new ServletException(je.getMessage());
}
}
/**
* Check if using the proxy authz control will work, by using it to read
* the Root DSE.
*
* @param connection The authenticated LDAP connection used to check.
* @param authorizationID The authorization ID, in the format
* "u:<userid>" or "dn:<DN>".
* @return a configured proxy authz control.
* @throws LDAPConnectionException If an error occurs during the check.
*
*/
private org.opends.server.types.Control checkAuthzControl(
LDAPConnection connection, String authorizationID)
throws LDAPConnectionException
{
LinkedHashSet<String>attributes = new LinkedHashSet<String>(1);
attributes.add(SchemaConstants.NO_ATTRIBUTES);
ArrayList<org.opends.server.types.Control> controls =
new ArrayList<org.opends.server.types.Control>(1);
org.opends.server.types.Control proxyAuthzControl =
new ProxiedAuthV2Control(true, ByteString.valueOf(authorizationID));
controls.add(proxyAuthzControl);
try
{
SearchRequestProtocolOp protocolOp = new SearchRequestProtocolOp(
ByteString.wrap(new byte[]{}), SearchScope.BASE_OBJECT,
DereferencePolicy.NEVER_DEREF_ALIASES,
0, 0,
true, LDAPFilter.decode("(objectClass=*)"), attributes);
byte opType;
LDAPMessage msg =
new LDAPMessage(DSMLServlet.nextMessageID(), protocolOp, controls);
connection.getLDAPWriter().writeMessage(msg);
do {
LDAPMessage responseMessage = connection.getLDAPReader().
readMessage();
opType = responseMessage.getProtocolOpType();
switch (opType)
{
case LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE:
switch (responseMessage.getSearchResultDoneProtocolOp().
getResultCode())
{
default:
Message m = INFO_RESULT_AUTHORIZATION_DENIED.get();
throw new LDAPConnectionException(m, CLIENT_SIDE_CONNECT_ERROR,
null);
case LDAPResultCode.SUCCESS:
return proxyAuthzControl;
}
}
} while (opType != LDAPConstants.OP_TYPE_SEARCH_RESULT_DONE);
}
catch (LDAPException le)
{
Message m = INFO_RESULT_CLIENT_SIDE_ENCODING_ERROR.get();
throw new LDAPConnectionException(m, CLIENT_SIDE_CONNECT_ERROR, null, le);
}
catch (ASN1Exception ae)
{
Message m = INFO_RESULT_CLIENT_SIDE_ENCODING_ERROR.get();
throw new LDAPConnectionException(m, CLIENT_SIDE_CONNECT_ERROR, null,
ae);
}
catch (IOException ie)
{
Message m = INFO_RESULT_CLIENT_SIDE_ENCODING_ERROR.get();
throw new LDAPConnectionException(m, CLIENT_SIDE_CONNECT_ERROR, null, ie);
}
throw new LDAPConnectionException(Message.EMPTY);
}
/**
* The HTTP POST operation. This servlet expects a SOAP message
* with a DSML request payload.
*
* @param req Information about the request received from the client.
* @param res Information about the response to send to the client.
* @throws ServletException If an error occurs during servlet processing.
* @throws IOException If an error occurs while interacting with the client.
*/
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
LDAPConnectionOptions connOptions = new LDAPConnectionOptions();
connOptions.setUseSSL(useSSL);
connOptions.setStartTLS(useStartTLS);
LDAPConnection connection = null;
BatchRequest batchRequest = null;
// Keep the Servlet input stream buffered in case the SOAP unmarshalling
// fails, the SAX parsing will be able to retrieve the requestID even if
// the XML is malmformed by resetting the input stream.
BufferedInputStream is = new BufferedInputStream(req.getInputStream(),
65536);
if ( is.markSupported() ) {
is.mark(65536);
}
// Create response in the beginning as it might be used if the parsing
// failes.
BatchResponse batchResponse = objFactory.createBatchResponse();
List<JAXBElement<?>> batchResponses = batchResponse.getBatchResponses();
Document doc = db.newDocument();
if (useSSL || useStartTLS)
{
SSLConnectionFactory sslConnectionFactory = new SSLConnectionFactory();
try
{
sslConnectionFactory.init(trustAll, null, null, null,
trustStorePathValue, trustStorePasswordValue);
}
catch(SSLConnectionException e)
{
batchResponses.add(
createErrorResponse(
new LDAPException(LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR,
Message.raw(
"Invalid SSL or TLS configuration to connect to LDAP server."))));
}
connOptions.setSSLConnectionFactory(sslConnectionFactory);
}
SOAPBody soapBody = null;
MimeHeaders mimeHeaders = new MimeHeaders();
Enumeration en = req.getHeaderNames();
String bindDN = null;
String bindPassword = null;
boolean authenticationInHeader = false;
boolean authenticationIsID = false;
while (en.hasMoreElements()) {
String headerName = (String) en.nextElement();
String headerVal = req.getHeader(headerName);
if (headerName.equalsIgnoreCase("authorization")) {
if (headerVal.startsWith("Basic ")) {
authenticationInHeader = true;
String authorization = headerVal.substring(6).trim();
try {
String unencoded = new String(Base64.decode(authorization));
int colon = unencoded.indexOf(':');
if (colon > 0) {
if (useHTTPAuthzID)
{
connOptions.setSASLMechanism("mech=" + SASL_MECHANISM_PLAIN);
connOptions.addSASLProperty(
"authid=u:" + unencoded.substring(0, colon).trim());
authenticationIsID = true;
}
else
{
bindDN = unencoded.substring(0, colon).trim();
}
bindPassword = unencoded.substring(colon + 1);
}
} catch (ParseException ex) {
// user/DN:password parsing error
batchResponses.add(
createErrorResponse(
new LDAPException(LDAPResultCode.INVALID_CREDENTIALS,
Message.raw(ex.getMessage()))));
break;
}
}
}
StringTokenizer tk = new StringTokenizer(headerVal, ",");
while (tk.hasMoreTokens()) {
mimeHeaders.addHeader(headerName, tk.nextToken().trim());
}
}
if ( ! authenticationInHeader ) {
// if no authentication, set default user from web.xml
if (userDN != null)
{
bindDN = userDN;
if (userPassword != null)
{
bindPassword = userPassword;
}
else
{
batchResponses.add(
createErrorResponse(
new LDAPException(LDAPResultCode.INVALID_CREDENTIALS,
Message.raw("Invalid configured credentials."))));
}
}
else
{
bindDN = "";
bindPassword = "";
}
} else {
// otherwise if DN or password is null, send back an error
if (((!authenticationIsID && (bindDN == null)) || bindPassword == null)
&& batchResponses.isEmpty()) {
batchResponses.add(
createErrorResponse(
new LDAPException(LDAPResultCode.INVALID_CREDENTIALS,
Message.raw("Unable to retrieve credentials."))));
}
}
// if an error already occured, the list is not empty
if ( batchResponses.isEmpty() ) {
try {
SOAPMessage message = messageFactory.createMessage(mimeHeaders, is);
soapBody = message.getSOAPBody();
} catch (SOAPException ex) {
// SOAP was unable to parse XML successfully
batchResponses.add(
createXMLParsingErrorResponse(is,
batchResponse,
String.valueOf(ex.getCause())));
}
}
if ( soapBody != null ) {
Iterator it = soapBody.getChildElements();
while (it.hasNext()) {
Object obj = it.next();
if (!(obj instanceof SOAPElement)) {
continue;
}
SOAPElement se = (SOAPElement) obj;
JAXBElement<BatchRequest> batchRequestElement = null;
try {
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.setSchema(schema);
batchRequestElement = unmarshaller.unmarshal(se, BatchRequest.class);
} catch (JAXBException e) {
// schema validation failed
batchResponses.add(createXMLParsingErrorResponse(is,
batchResponse,
String.valueOf(e)));
}
if ( batchRequestElement != null ) {
boolean authzInBind = false;
boolean authzInControl = false;
batchRequest = batchRequestElement.getValue();
/*
* Process optional authRequest (i.e. use authz)
*/
if (batchRequest.authRequest != null) {
if (authenticationIsID == true) {
// If we are using SASL, then use the bind authz.
connOptions.addSASLProperty("authzid=" +
batchRequest.authRequest.getPrincipal());
authzInBind = true;
} else {
// If we are using simple then we have to do some work after
// the bind.
authzInControl = true;
}
}
// set requestID in response
batchResponse.setRequestID(batchRequest.getRequestID());
boolean connected = false;
if ( connection == null ) {
connection = new LDAPConnection(hostName, port, connOptions);
try {
connection.connectToHost(bindDN, bindPassword);
if (authzInControl)
{
proxyAuthzControl = checkAuthzControl(connection,
batchRequest.authRequest.getPrincipal());
}
if (authzInBind || authzInControl)
{
LDAPResult authResponse = objFactory.createLDAPResult();
ResultCode code = ResultCodeFactory.create(objFactory,
LDAPResultCode.SUCCESS);
authResponse.setResultCode(code);
batchResponses.add(
objFactory.createBatchResponseAuthResponse(authResponse));
}
connected = true;
} catch (LDAPConnectionException e) {
// if connection failed, return appropriate error response
batchResponses.add(createErrorResponse(e));
}
}
if ( connected ) {
List<DsmlMessage> list = batchRequest.getBatchRequests();
for (DsmlMessage request : list) {
JAXBElement<?> result = performLDAPRequest(connection, request);
if ( result != null ) {
batchResponses.add(result);
}
// evaluate response to check if an error occured
Object o = result.getValue();
if ( o instanceof ErrorResponse ) {
if ( ON_ERROR_EXIT.equals(batchRequest.getOnError()) ) {
break;
}
} else if ( o instanceof LDAPResult ) {
int code = ((LDAPResult)o).getResultCode().getCode();
if ( code != LDAPResultCode.SUCCESS
&& code != LDAPResultCode.REFERRAL
&& code != LDAPResultCode.COMPARE_TRUE
&& code != LDAPResultCode.COMPARE_FALSE ) {
if ( ON_ERROR_EXIT.equals(batchRequest.getOnError()) ) {
break;
}
}
}
}
}
// close connection to LDAP server
if ( connection != null ) {
connection.close(nextMessageID);
}
}
}
}
try {
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.marshal(objFactory.createBatchResponse(batchResponse), doc);
sendResponse(doc, res);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Returns an error response after a parsing error. The response has the
* requestID of the batch request, the error response message of the parsing
* exception message and the type 'malformed request'.
*
* @param is the xml InputStream to parse
* @param batchResponse the JAXB object to fill in
* @param parserErrorMessage the parsing error message
*
* @return a JAXBElement that contains an ErrorResponse
*/
private JAXBElement<ErrorResponse> createXMLParsingErrorResponse(
InputStream is,
BatchResponse batchResponse,
String parserErrorMessage) {
ErrorResponse errorResponse = objFactory.createErrorResponse();
try {
// try alternative XML parsing using SAX to retrieve requestID value
XMLReader xmlReader = XMLReaderFactory.createXMLReader();
// clear previous match
this.contentHandler.requestID = null;
xmlReader.setContentHandler(this.contentHandler);
is.reset();
xmlReader.parse(new InputSource(is));
} catch (Throwable e) {
// document is unparsable so will jump here
}
if ( parserErrorMessage!= null ) {
errorResponse.setMessage(parserErrorMessage);
}
batchResponse.setRequestID(this.contentHandler.requestID);
errorResponse.setType(MALFORMED_REQUEST);
return objFactory.createBatchResponseErrorResponse(errorResponse);
}
/**
* Returns an error response with attributes set according to the exception
* provided as argument.
*
* @param t the exception that occured
*
* @return a JAXBElement that contains an ErrorResponse
*/
private JAXBElement<ErrorResponse> createErrorResponse(Throwable t) {
// potential exceptions are IOException, LDAPException, ASN1Exception
ErrorResponse errorResponse = objFactory.createErrorResponse();
errorResponse.setMessage(String.valueOf(t));
if ( t instanceof LDAPException ) {
switch(((LDAPException)t).getResultCode()) {
case LDAPResultCode.AUTHORIZATION_DENIED:
case LDAPResultCode.INAPPROPRIATE_AUTHENTICATION:
case LDAPResultCode.INVALID_CREDENTIALS:
case LDAPResultCode.STRONG_AUTH_REQUIRED:
errorResponse.setType(AUTHENTICATION_FAILED);
break;
case LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR:
errorResponse.setType(COULD_NOT_CONNECT);
break;
case LDAPResultCode.UNWILLING_TO_PERFORM:
errorResponse.setType(NOT_ATTEMPTED);
break;
default:
errorResponse.setType(MALFORMED_REQUEST);
break;
}
} else if ( t instanceof LDAPConnectionException ) {
errorResponse.setType(COULD_NOT_CONNECT);
} else if ( t instanceof IOException ) {
errorResponse.setType(UNRESOLVABLE_URI);
} else {
errorResponse.setType(GATEWAY_INTERNAL_ERROR);
}
return objFactory.createBatchResponseErrorResponse(errorResponse);
}
/**
* Performs the LDAP operation and sends back the result (if any). In case
* of error, an error response is returned.
*
* @param connection a connected connection
* @param request the JAXB request to perform
*
* @return null for an abandon request, the expect result for all other
* requests or an error in case of unexpected behaviour.
*/
private JAXBElement<?> performLDAPRequest(LDAPConnection connection,
DsmlMessage request) {
ArrayList<org.opends.server.types.Control> controls =
new ArrayList<org.opends.server.types.Control>(1);
if (proxyAuthzControl != null)
{
controls.add(proxyAuthzControl);
}
try {
if (request instanceof SearchRequest) {
// Process the search request.
SearchRequest sr = (SearchRequest) request;
DSMLSearchOperation ds = new DSMLSearchOperation(connection);
SearchResponse searchResponse = ds.doSearch(objFactory, sr, controls);
return objFactory.createBatchResponseSearchResponse(searchResponse);
} else if (request instanceof AddRequest) {
// Process the add request.
AddRequest ar = (AddRequest) request;
DSMLAddOperation addOp = new DSMLAddOperation(connection);
LDAPResult addResponse = addOp.doOperation(objFactory, ar, controls);
return objFactory.createBatchResponseAddResponse(addResponse);
} else if (request instanceof AbandonRequest) {
// Process the abandon request.
AbandonRequest ar = (AbandonRequest) request;
DSMLAbandonOperation ao = new DSMLAbandonOperation(connection);
LDAPResult abandonResponse = ao.doOperation(objFactory, ar, controls);
return null;
} else if (request instanceof ExtendedRequest) {
// Process the extended request.
ExtendedRequest er = (ExtendedRequest) request;
DSMLExtendedOperation eo = new DSMLExtendedOperation(connection,
exopStrings);
ExtendedResponse extendedResponse = eo.doOperation(objFactory, er,
controls);
return objFactory.createBatchResponseExtendedResponse(extendedResponse);
} else if (request instanceof DelRequest) {
// Process the delete request.
DelRequest dr = (DelRequest) request;
DSMLDeleteOperation delOp = new DSMLDeleteOperation(connection);
LDAPResult delResponse = delOp.doOperation(objFactory, dr, controls);
return objFactory.createBatchResponseDelResponse(delResponse);
} else if (request instanceof CompareRequest) {
// Process the compare request.
CompareRequest cr = (CompareRequest) request;
DSMLCompareOperation compareOp =
new DSMLCompareOperation(connection);
LDAPResult compareResponse = compareOp.doOperation(objFactory, cr,
controls);
return objFactory.createBatchResponseCompareResponse(compareResponse);
} else if (request instanceof ModifyDNRequest) {
// Process the Modify DN request.
ModifyDNRequest mr = (ModifyDNRequest) request;
DSMLModifyDNOperation moddnOp =
new DSMLModifyDNOperation(connection);
LDAPResult moddnResponse = moddnOp.doOperation(objFactory, mr,
controls);
return objFactory.createBatchResponseModDNResponse(moddnResponse);
} else if (request instanceof ModifyRequest) {
// Process the Modify request.
ModifyRequest modr = (ModifyRequest) request;
DSMLModifyOperation modOp = new DSMLModifyOperation(connection);
LDAPResult modResponse = modOp.doOperation(objFactory, modr, controls);
return objFactory.createBatchResponseModifyResponse(modResponse);
} else if (request instanceof AuthRequest) {
// Process the Auth request.
// Only returns an BatchReponse with an AuthResponse containing the
// LDAP result code AUTH_METHOD_NOT_SUPPORTED
ResultCode resultCode = objFactory.createResultCode();
resultCode.setCode(LDAPResultCode.AUTH_METHOD_NOT_SUPPORTED);
LDAPResult ldapResult = objFactory.createLDAPResult();
ldapResult.setResultCode(resultCode);
return objFactory.createBatchResponseAuthResponse(ldapResult);
}
} catch (Throwable t) {
return createErrorResponse(t);
}
// should never happen as the schema was validated
return null;
}
/**
* Send a response back to the client. This could be either a SOAP fault
* or a correct DSML response.
*
* @param doc The document to include in the response.
* @param res Information about the HTTP response to the client.
*
* @throws IOException If an error occurs while interacting with the client.
* @throws SOAPException If an encoding or decoding error occurs.
*/
private void sendResponse(Document doc, HttpServletResponse res)
throws IOException, SOAPException {
SOAPMessage reply = messageFactory.createMessage();
SOAPHeader header = reply.getSOAPHeader();
header.detachNode();
SOAPBody replyBody = reply.getSOAPBody();
res.setHeader("Content-Type", "text/xml");
SOAPElement bodyElement = replyBody.addDocument(doc);
reply.saveChanges();
OutputStream os = res.getOutputStream();
reply.writeTo(os);
os.flush();
}
/**
* Retrieves a message ID that may be used for the next LDAP message sent to
* the Directory Server.
*
* @return A message ID that may be used for the next LDAP message sent to
* the Directory Server.
*/
public static int nextMessageID() {
int nextID = nextMessageID.getAndIncrement();
if (nextID == Integer.MAX_VALUE) {
nextMessageID.set(1);
}
return nextID;
}
/**
* This class is used when a xml request is malformed to retrieve the
* requestID value using an event xml parser.
*/
private static class DSMLContentHandler extends DefaultHandler {
private String requestID;
/*
* This function fetches the requestID value of the batchRequest xml
* element and call the default implementation (super).
*/
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
if ( requestID==null && localName.equals("batchRequest") ) {
requestID = attributes.getValue("requestID");
}
super.startElement(uri, localName, qName, attributes);
}
}
}