/* * Copyright (c) Members of the EGEE Collaboration. 2006-2010. * See http://www.eu-egee.org/partners/ for details on the copyright holders. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * $Id$ */ package org.glite.authz.pep.client; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.List; import org.glite.authz.common.model.Request; import org.glite.authz.common.model.Response; import org.glite.authz.common.model.Result; import org.glite.authz.common.util.Base64; import org.glite.authz.pep.client.config.PEPClientConfiguration; import org.glite.authz.pep.client.http.HttpClientBuilder; import org.glite.authz.pep.client.http.TLSProtocolSocketFactory; import org.glite.authz.pep.obligation.ObligationHandler; import org.glite.authz.pep.obligation.ObligationProcessingException; import org.glite.authz.pep.pip.PIPProcessingException; import org.glite.authz.pep.pip.PolicyInformationPoint; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.RequestEntity; import org.apache.commons.httpclient.methods.StringRequestEntity; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.caucho.hessian.io.HessianInput; import com.caucho.hessian.io.HessianOutput; /** * A PEP client to communicate with the Argus PEP Server and authorize request. * * It uses a multi-threaded http client to authorize the request. The http * client tries to keep alive connection whitin its pool of connections. * * @author Valery Tschopp <valery.tschopp@switch.ch> */ public class PEPClient { /** Class logger. */ private final Log log= LogFactory.getLog(PEPClient.class); /** Unmodifiable list of PIPs */ private List<PolicyInformationPoint> pips_= null; /** Unmodifiable list of ObligationHandlers */ private List<ObligationHandler> obligationHandlers_= null; /** Unmodifiable list of PEP daemon endpoints */ private List<String> pepdEndpoints_= null; /** HTTP client used to contact the PEP daemon. */ private HttpClient httpClient_= null; /** * Constructor. Creates a new PEP client based on the given configuration. * The PEP client uses a multi-threaded {@link HttpClient} with a pool of * connections. * * @param config * the client configuration used for this client * @throws PEPClientException */ public PEPClient(PEPClientConfiguration config) throws PEPClientException { HttpClientBuilder httpClientBuilder= new HttpClientBuilder(); httpClientBuilder.setConnectionTimeout(config.getConnectionTimeout()); httpClientBuilder.setMaxConnectionsPerHost(config.getMaxConnectionsPerHost()); httpClientBuilder.setMaxTotalConnections(config.getMaxTotalConnections()); // httpClientBuilder.setReceiveBufferSize(config.getReceiveBufferSize()); // httpClientBuilder.setSendBufferSize(config.getSendBufferSize()); if (config.getTrustManager() != null) { // if the key manager is null, it just means TLS client-auth isn't // enabled httpClientBuilder.setHttpsProtocolSocketFactory(new TLSProtocolSocketFactory(config.getKeyManager(), config.getTrustManager())); } httpClient_= httpClientBuilder.buildClient(); pepdEndpoints_= config.getPEPDaemonEndpoints(); if (pepdEndpoints_.isEmpty()) { throw new PEPClientException("Configuration doesn't contain any PEP Server endpoint URL"); } pips_= config.getPolicyInformationPoints(); obligationHandlers_= config.getObligationHandlers(); } /** * Authorizes the request with the PEP daemon and return the response * * @param request * the authorization request * @return the reponse * @throws PEPClientException * if a processing error occurs. */ public Response authorize(Request request) throws PEPClientException { Response response= null; Exception cause= null; try { runPolicyInformationPoints(request); } catch (PIPProcessingException e) { throw new PEPClientException("PIP processing failure", e); } for (String endpoint : pepdEndpoints_) { try { response= performRequest(endpoint, request); // success, exit loop break; } catch (PEPClientException e) { log.error("request failed for PEP Server " + endpoint, e); cause= e; } } if (response == null) { String error= "No PEP Server " + pepdEndpoints_ + " was able to process the request"; log.error(error); PEPClientException exception= new PEPClientException(error); if (cause != null) { exception.setStackTrace(cause.getStackTrace()); } throw exception; } try { runObligationHandlers(request, response); } catch (ObligationProcessingException e) { throw new PEPClientException("ObligationHandler processing failure", e); } return response; } /** * Calls out to the remote PEP and returns the response. * * @param pepUrl * the remote PEP to which to callout * @param authzRequest * the authorization request to send to the PEP daemon * @return the response to the request * @throws PEPClientException * thrown if there is a problem processing the request */ protected Response performRequest(String pepUrl, Request authzRequest) throws PEPClientException { String b64Message= null; try { ByteArrayOutputStream out= new ByteArrayOutputStream(); HessianOutput hout= new HessianOutput(out); hout.writeObject(authzRequest); hout.flush(); b64Message= Base64.encodeBytes(out.toByteArray()); } catch (IOException e) { log.error("Unable to serialize request object", e); throw new PEPClientException("Unable to serialize request object", e); } PostMethod postMethod= new PostMethod(pepUrl); try { RequestEntity requestEntity= new StringRequestEntity(b64Message, "application/octet-stream", "UTF-8"); postMethod.setRequestEntity(requestEntity); } catch (UnsupportedEncodingException e) { throw new PEPClientException(e); } Response response= null; try { httpClient_.executeMethod(postMethod); if (postMethod.getStatusCode() == HttpStatus.SC_OK) { try { InputStream is= new Base64.InputStream(postMethod.getResponseBodyAsStream()); HessianInput hin= new HessianInput(is); response= (Response) hin.readObject(Response.class); } catch (IOException e) { log.error("Unable to deserialize response object", e); throw new PEPClientException("Unable to deserialize response object", e); } } else { String error= postMethod.getStatusCode() + " status code response from the PEP Server " + pepUrl; log.error(error); throw new PEPClientException(error); } } catch (IOException e) { log.error("Unable to read response from PEP Server " + pepUrl, e); throw new PEPClientException("Unable to read response from PEP Server " + pepUrl, e); } finally { log.debug("release connection"); postMethod.releaseConnection(); } return response; } /** * Run the list of PIPs over the request. * * @param request * the request * @param pips * PIPs to run over the request * @throws PIPProcessingException * thrown if a PIP failed to populate the request */ protected void runPolicyInformationPoints(Request request) throws PIPProcessingException { boolean pipApplied; for (PolicyInformationPoint pip : pips_) { if (log.isDebugEnabled()) { log.debug("applying PIP " + pip.getId()); } pipApplied= pip.populateRequest(request); if (log.isErrorEnabled()) { log.debug("PIP " + pip.getId() + " applied: " + pipApplied); } } } /** * Runs the obligations handlers over the returned response. * * @param request * the authorization request made * @param response * the authorization response * * @throws ObligationProcessingException * thrown if there is a problem evaluating obligation handlers. */ protected void runObligationHandlers(Request request, Response response) throws ObligationProcessingException { if (response == null) return; boolean ohApplied; List<Result> results= response.getResults(); for (Result result : results) { for (ObligationHandler oh : obligationHandlers_) { if (log.isDebugEnabled()) { log.debug("applying OH " + oh.getObligationId()); } ohApplied= oh.evaluateObligation(request, result); if (log.isDebugEnabled()) { log.debug("OH " + oh.getObligationId() + " applied: " + ohApplied); } } } } }