| | |
| | | * |
| | | * |
| | | * Copyright 2006-2010 Sun Microsystems, Inc. |
| | | * Portions Copyright 2011-2015 ForgeRock AS |
| | | * Portions Copyright 2011-2015 ForgeRock AS. |
| | | */ |
| | | package org.opends.dsml.protocol; |
| | | |
| | |
| | | private static final String ON_ERROR_EXIT = "exit"; |
| | | |
| | | private static JAXBContext jaxbContext; |
| | | private ObjectFactory objFactory; |
| | | private static Schema schema; |
| | | private MessageFactory messageFactory; |
| | | private MessageFactory messageFactorySOAP_1_1; |
| | | private MessageFactory messageFactorySOAP_1_2; |
| | | private String contentType; |
| | | |
| | | /** |
| | | * 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; |
| | | /** Prevent multiple logging when trying to set unavailable/unsupported parser features */ |
| | | private static AtomicBoolean logFeatureWarnings = new AtomicBoolean(false); |
| | | |
| | |
| | | private Boolean trustAll; |
| | | private Boolean useHTTPAuthzID; |
| | | private HashSet<String> exopStrings = new HashSet<String>(); |
| | | private org.opends.server.types.Control proxyAuthzControl; |
| | | |
| | | /** |
| | | * This method will be called by the Servlet Container when |
| | |
| | | } |
| | | } |
| | | |
| | | objFactory = new ObjectFactory(); |
| | | messageFactorySOAP_1_1 = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL); |
| | | messageFactorySOAP_1_2 = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL); |
| | | |
| | | this.contentHandler = new DSMLContentHandler(); |
| | | |
| | | DirectoryServer.bootstrapClient(); |
| | | } catch (Exception je) { |
| | | je.printStackTrace(); |
| | |
| | | |
| | | // Create response in the beginning as it might be used if the parsing |
| | | // fails. |
| | | ObjectFactory objFactory = new ObjectFactory(); |
| | | BatchResponse batchResponse = objFactory.createBatchResponse(); |
| | | List<JAXBElement<?>> batchResponses = batchResponse.getBatchResponses(); |
| | | |
| | | // Thi sis only used for building the response |
| | | Document doc = createSafeDocument(); |
| | | |
| | | MessageFactory messageFactory = null; |
| | | String messageContentType = null; |
| | | |
| | | if (useSSL || useStartTLS) |
| | | { |
| | | SSLConnectionFactory sslConnectionFactory = new SSLConnectionFactory(); |
| | |
| | | catch(SSLConnectionException e) |
| | | { |
| | | batchResponses.add( |
| | | createErrorResponse( |
| | | createErrorResponse(objFactory, |
| | | new LDAPException(LDAPResultCode.CLIENT_SIDE_CONNECT_ERROR, |
| | | LocalizableMessage.raw( |
| | | "Invalid SSL or TLS configuration to connect to LDAP server.")))); |
| | |
| | | String headerName = (String) en.nextElement(); |
| | | String headerVal = req.getHeader(headerName); |
| | | if (headerName.equalsIgnoreCase("content-type")) { |
| | | if (headerVal.startsWith(SOAPConstants.SOAP_1_1_CONTENT_TYPE)) { |
| | | messageFactory = messageFactorySOAP_1_1; |
| | | contentType = SOAPConstants.SOAP_1_1_CONTENT_TYPE; |
| | | } else if (headerVal.startsWith(SOAPConstants.SOAP_1_2_CONTENT_TYPE)) { |
| | | messageFactory = messageFactorySOAP_1_2; |
| | | contentType = SOAPConstants.SOAP_1_2_CONTENT_TYPE; |
| | | } else { |
| | | throw new ServletException("Content-Type does not match SOAP 1.1 or SOAP 1.2"); |
| | | try |
| | | { |
| | | if (headerVal.startsWith(SOAPConstants.SOAP_1_1_CONTENT_TYPE)) |
| | | { |
| | | messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL); |
| | | messageContentType = SOAPConstants.SOAP_1_1_CONTENT_TYPE; |
| | | } |
| | | else if (headerVal.startsWith(SOAPConstants.SOAP_1_2_CONTENT_TYPE)) |
| | | { |
| | | MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL); |
| | | messageContentType = SOAPConstants.SOAP_1_2_CONTENT_TYPE; |
| | | } |
| | | else { |
| | | throw new ServletException("Content-Type does not match SOAP 1.1 or SOAP 1.2"); |
| | | } |
| | | } |
| | | catch (SOAPException e) |
| | | { |
| | | throw new ServletException(e.getmessage()); |
| | | } |
| | | } else if (headerName.equalsIgnoreCase("authorization") && headerVal.startsWith("Basic ")) |
| | | { |
| | |
| | | } catch (ParseException ex) { |
| | | // user/DN:password parsing error |
| | | batchResponses.add( |
| | | createErrorResponse( |
| | | createErrorResponse(objFactory, |
| | | new LDAPException(LDAPResultCode.INVALID_CREDENTIALS, |
| | | LocalizableMessage.raw(ex.getMessage())))); |
| | | break; |
| | |
| | | else |
| | | { |
| | | batchResponses.add( |
| | | createErrorResponse( |
| | | createErrorResponse(objFactory, |
| | | new LDAPException(LDAPResultCode.INVALID_CREDENTIALS, |
| | | LocalizableMessage.raw("Invalid configured credentials.")))); |
| | | } |
| | |
| | | if (((!authenticationIsID && (bindDN == null)) || bindPassword == null) |
| | | && batchResponses.isEmpty()) { |
| | | batchResponses.add( |
| | | createErrorResponse( |
| | | createErrorResponse(objFactory, |
| | | new LDAPException(LDAPResultCode.INVALID_CREDENTIALS, |
| | | LocalizableMessage.raw("Unable to retrieve credentials.")))); |
| | | } |
| | |
| | | // SOAP was unable to parse XML successfully |
| | | batchResponses.add( |
| | | createXMLParsingErrorResponse(is, |
| | | objFactory, |
| | | batchResponse, |
| | | String.valueOf(ex.getCause()))); |
| | | } |
| | |
| | | } catch (JAXBException e) { |
| | | // schema validation failed |
| | | batchResponses.add(createXMLParsingErrorResponse(is, |
| | | objFactory, |
| | | batchResponse, |
| | | String.valueOf(e))); |
| | | } |
| | |
| | | } |
| | | // set requestID in response |
| | | batchResponse.setRequestID(batchRequest.getRequestID()); |
| | | org.opends.server.types.Control proxyAuthzControl = null; |
| | | |
| | | boolean connected = false; |
| | | |
| | |
| | | connected = true; |
| | | } catch (LDAPConnectionException e) { |
| | | // if connection failed, return appropriate error response |
| | | batchResponses.add(createErrorResponse(e)); |
| | | batchResponses.add(createErrorResponse(objFactory, e)); |
| | | } |
| | | } |
| | | if ( connected ) { |
| | | List<DsmlMessage> list = batchRequest.getBatchRequests(); |
| | | |
| | | for (DsmlMessage request : list) { |
| | | JAXBElement<?> result = performLDAPRequest(connection, request); |
| | | JAXBElement<?> result = performLDAPRequest(connection, objFactory, proxyAuthzControl, request); |
| | | if ( result != null ) { |
| | | batchResponses.add(result); |
| | | } |
| | |
| | | try { |
| | | Marshaller marshaller = jaxbContext.createMarshaller(); |
| | | marshaller.marshal(objFactory.createBatchResponse(batchResponse), doc); |
| | | sendResponse(doc, res); |
| | | sendResponse(doc, messageFactory, messageContentType, res); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | |
| | | * exception message and the type 'malformed request'. |
| | | * |
| | | * @param is the XML InputStream to parse |
| | | * @param objFactory the object factory |
| | | * @param batchResponse the JAXB object to fill in |
| | | * @param parserErrorMessage the parsing error message |
| | | * |
| | |
| | | */ |
| | | private JAXBElement<ErrorResponse> createXMLParsingErrorResponse( |
| | | InputStream is, |
| | | ObjectFactory objFactory, |
| | | BatchResponse batchResponse, |
| | | String parserErrorMessage) { |
| | | ErrorResponse errorResponse = objFactory.createErrorResponse(); |
| | | DSMLContentHandler contentHandler = new DSMLContentHandler(); |
| | | |
| | | try |
| | | { |
| | | // try alternative XML parsing using SAX to retrieve requestID value |
| | | final XMLReader xmlReader = createSafeXMLReader(); |
| | | |
| | | // clear previous match |
| | | this.contentHandler.requestID = null; |
| | | xmlReader.setContentHandler(this.contentHandler); |
| | | xmlReader.setContentHandler(contentHandler); |
| | | is.reset(); |
| | | |
| | | xmlReader.parse(new InputSource(is)); |
| | |
| | | if ( parserErrorMessage!= null ) { |
| | | errorResponse.setMessage(parserErrorMessage); |
| | | } |
| | | batchResponse.setRequestID(this.contentHandler.requestID); |
| | | batchResponse.setRequestID(contentHandler.requestID); |
| | | |
| | | errorResponse.setType(MALFORMED_REQUEST); |
| | | |
| | |
| | | * Returns an error response with attributes set according to the exception |
| | | * provided as argument. |
| | | * |
| | | * @param objFactory the object factory |
| | | * @param t the exception that occurred |
| | | * |
| | | * @return a JAXBElement that contains an ErrorResponse |
| | | */ |
| | | private JAXBElement<ErrorResponse> createErrorResponse(Throwable t) { |
| | | private JAXBElement<ErrorResponse> createErrorResponse(ObjectFactory objFactory, Throwable t) { |
| | | // potential exceptions are IOException, LDAPException, DecodeException |
| | | |
| | | ErrorResponse errorResponse = objFactory.createErrorResponse(); |
| | |
| | | * of error, an error response is returned. |
| | | * |
| | | * @param connection a connected connection |
| | | * @param objFactory the object factory |
| | | * @param proxyAuthzControl a proxy authz control, or null |
| | | * @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, |
| | | ObjectFactory objFactory, |
| | | org.opends.server.types.Control proxyAuthzControl, |
| | | DsmlMessage request) { |
| | | ArrayList<org.opends.server.types.Control> controls = |
| | | new ArrayList<org.opends.server.types.Control>(1); |
| | |
| | | return objFactory.createBatchResponseAuthResponse(ldapResult); |
| | | } |
| | | } catch (Throwable t) { |
| | | return createErrorResponse(t); |
| | | return createErrorResponse(objFactory, t); |
| | | } |
| | | // should never happen as the schema was validated |
| | | return null; |
| | |
| | | * or a correct DSML response. |
| | | * |
| | | * @param doc The document to include in the response. |
| | | * @param messageFactory The SOAP message factory. |
| | | * @param contentType The MIME content type to send appropriate for the MessageFactory |
| | | * @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) |
| | | private void sendResponse(Document doc, MessageFactory messageFactory, String contentType, HttpServletResponse res) |
| | | throws IOException, SOAPException { |
| | | |
| | | SOAPMessage reply = messageFactory.createMessage(); |
| | |
| | | * This class is used when an XML request is malformed to retrieve the |
| | | * requestID value using an event XML parser. |
| | | */ |
| | | private static class DSMLContentHandler extends DefaultHandler { |
| | | private class DSMLContentHandler extends DefaultHandler { |
| | | private String requestID; |
| | | /** |
| | | * This function fetches the requestID value of the batchRequest xml |