org.picketlink.identity.federation.api.wstrust.WSTrustClient Maven / Gradle / Ivy
Show all versions of picketlink-federation
/*
* JBoss, Home of Professional Open Source
*
* Copyright 2013 Red Hat, Inc. and/or its affiliates.
*
* 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.
*/
package org.picketlink.identity.federation.api.wstrust;
import org.picketlink.common.PicketLinkLogger;
import org.picketlink.common.PicketLinkLoggerFactory;
import org.picketlink.common.exceptions.ParsingException;
import org.picketlink.common.exceptions.fed.WSTrustException;
import org.picketlink.identity.federation.core.wstrust.STSClient;
import org.picketlink.identity.federation.core.wstrust.STSClientConfig;
import org.picketlink.identity.federation.core.wstrust.STSClientConfig.Builder;
import org.picketlink.identity.federation.core.wstrust.STSClientPool;
import org.picketlink.identity.federation.core.wstrust.STSClientFactory;
import org.picketlink.identity.federation.core.wstrust.WSTrustUtil;
import org.picketlink.identity.federation.core.wstrust.wrappers.RequestSecurityToken;
import org.w3c.dom.Element;
import java.net.URI;
import java.security.Principal;
/**
* WS-Trust Client
*
* @author [email protected]
* @since Aug 29, 2009
*/
public class WSTrustClient {
private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
/**
* The array of STSClient instances that this class delegates to.
*/
private final STSClient[] clients;
public static class SecurityInfo {
private final String username;
private final String passwd;
public SecurityInfo(String name, char[] pass) {
username = name;
passwd = new String(pass);
}
public SecurityInfo(String name, String pass) {
username = name;
passwd = pass;
}
}
public WSTrustClient(String serviceName, String port, String endpointURI, SecurityInfo secInfo) throws ParsingException {
this(serviceName, port, new String[]{endpointURI}, secInfo);
}
public WSTrustClient(String serviceName, String port, String[] endpointURIs, SecurityInfo secInfo) throws ParsingException {
// basic input validation.
if (serviceName == null || port == null || endpointURIs == null || secInfo == null)
throw logger.nullArgumentError("The service name, port, endpoint URIs and security info parameters cannot be null");
if (endpointURIs.length == 0)
throw logger.nullArgumentError("At least one endpoint URI must be provided");
// create an STSClient for each endpointURI.
this.clients = new STSClient[endpointURIs.length];
Builder builder = new STSClientConfig.Builder();
builder.serviceName(serviceName).portName(port).username(secInfo.username).password(secInfo.passwd);
STSClientPool pool = STSClientFactory.getInstance();
int index = 0;
for (String endpointURI : endpointURIs) {
builder.endpointAddress(endpointURI);
STSClientConfig config = builder.build();
pool.createPool(config);
this.clients[index++] = pool.getClient(config);
}
}
/**
* This method will send a RequestSecurityToken with a RequestType of issue and the passed-in tokenType identifies
* the type
* of token to be issued by the STS.
*
* @param tokenType - The type of token to be issued.
*
* @return Element - The Security Token element. Will be of the tokenType specified.
*
* @throws WSTrustException
*/
public Element issueToken(String tokenType) throws WSTrustException {
if (tokenType == null)
throw logger.nullArgumentError("The token type");
RequestSecurityToken request = new RequestSecurityToken();
request.setTokenType(URI.create(tokenType));
return this.issueInternal(request, 0);
}
/**
* This method will send a RequestSecurityToken with a RequestType of issue and the passed-in endpointURI identifies
* the
* ultimate recipient of the token.
*
* @param endpointURI - The ultimate recipient of the token. This will be set at the AppliesTo for the
* RequestSecurityToken
* which is an optional element so it may be null.
*
* @return Element - The Security Token element. Will be of the tokenType configured for the endpointURI.
*
* @throws WSTrustException
*/
public Element issueTokenForEndpoint(String endpointURI) throws WSTrustException {
if (endpointURI == null)
throw logger.nullArgumentError("The endpoint URI");
RequestSecurityToken request = new RequestSecurityToken();
request.setAppliesTo(WSTrustUtil.createAppliesTo(endpointURI));
return this.issueInternal(request, 0);
}
/**
* Issues a Security Token from the STS. This methods has the option of specifying both or one of
* endpointURI/tokenType but
* at least one must specified.
*
* @param endpointURI - The ultimate recipient of the token. This will be set at the AppliesTo for the
* RequestSecurityToken
* which is an optional element so it may be null.
* @param tokenType - The type of security token to be issued.
*
* @return Element - The Security Token Element issued.
*
* @throws WSTrustException
*/
public Element issueToken(String endpointURI, String tokenType) throws WSTrustException {
if (endpointURI == null && tokenType == null)
throw logger.nullArgumentError("Either the token type or endpoint URI must be specified");
RequestSecurityToken request = new RequestSecurityToken();
if (tokenType != null)
request.setTokenType(URI.create(tokenType));
if (endpointURI != null)
request.setAppliesTo(WSTrustUtil.createAppliesTo(endpointURI));
return this.issueInternal(request, 0);
}
/**
*
* Issues a security token on behalf of the specified principal.
*
*
* @param endpointURI - The ultimate recipient of the token. This will be set at the AppliesTo for the
* RequestSecurityToken
* which is an optional element so it may be null.
* @param tokenType - The type of security token to be issued.
* @param principal - The {@code Principal} on behalf of whom the token is to be issued.
*
* @return an {@code Element} representing the issued security token.
*
* @throws WSTrustException if a processing error occurs while issuing the security token.
*/
public Element issueTokenOnBehalfOf(String endpointURI, String tokenType, Principal principal) throws WSTrustException {
if (endpointURI == null && tokenType == null)
throw logger.nullArgumentError("Either the token type or endpoint URI must be specified");
if (principal == null)
throw logger.nullArgumentError("The on-behalf-of principal");
RequestSecurityToken request = new RequestSecurityToken();
if (tokenType != null)
request.setTokenType(URI.create(tokenType));
if (endpointURI != null)
request.setAppliesTo(WSTrustUtil.createAppliesTo(endpointURI));
request.setOnBehalfOf(WSTrustUtil.createOnBehalfOfWithUsername(principal.getName(), "ID"));
return this.issueInternal(request, 0);
}
/**
*
* Issues a security token using the specified {@code RequestSecurityToken} object.
*
*
* @param request an instance of {@code RequestSecurityToken} that contains the WS-Trust request information.
*
* @return an {@code Element} representing the issued security token.
*
* @throws IllegalArgumentException if the specified request is null.
* @throws WSTrustException if a processing error occurs while issuing the token.
*/
public Element issueToken(RequestSecurityToken request) throws WSTrustException {
if (request == null)
throw logger.nullArgumentError("request");
return this.issueInternal(request, 0);
}
/**
* This method will send a RequestSecurityToken with a RequestType of renew and the passed-in tokenType identifies
* the type
* of token to be renewed by the STS.
*
* @param tokenType - The type of token to be renewed.
* @param token - The security token to be renewed.
*
* @return Element - The Security Token element. Will be of the tokenType specified.
*/
public Element renewToken(String tokenType, Element token) throws WSTrustException {
return this.renewInternal(tokenType, token, 0);
}
/**
* This method will send a RequestSecurityToken with a RequestType of validated by the STS.
*
* @param token - The security token to be validated.
*
* @return true - If the security token was sucessfully valiated.
*/
public boolean validateToken(Element token) throws WSTrustException {
return this.validateInternal(token, 0);
}
/**
*
* This method sends a WS-Trust cancel message to the STS in order to cancel (revoke) the specified security token.
*
*
* @param token the security token to be canceled.
*
* @return {@code true} if the token was successfully canceled; {@code false} otherwise.
*
* @throws WSTrustException if an error occurs while canceling the security token.
*/
public boolean cancelToken(Element token) throws WSTrustException {
return this.cancelInternal(token, 0);
}
/**
*
* This method issues a token using the specified request and has failover support when more than one endpoint URI
* has been
* provided in the constructor. If a {@code ConnectException} occurs when sending the WS-Trust request to one
* endpoint, the
* code makes a new attempt using the next URI until the request reaches an STS instance or all URIs have been
* tried.
*
*
* @param request a {@code RequestSecurityToken} instance that contains the WS-Trust request information.
* @param clientIndex an {@code int} that indicates which of the {@code STSClient} instances should be used to
* perform the
* request.
*
* @return an {@code Element} representing the security token that has been issued.
*
* @throws WSTrustException if a WS-Trust exception is thrown by the STS.
*/
private Element issueInternal(RequestSecurityToken request, int clientIndex) throws WSTrustException {
STSClient client = this.clients[clientIndex];
try {
return client.issueToken(request);
} catch (RuntimeException e) {
// if this was a connection refused exception and we still have clients to try, call the next client.
if (this.isCausedByConnectException(e) && clientIndex < this.clients.length - 1) {
return this.issueInternal(request, ++clientIndex);
}
throw e;
}
}
/**
*
* This method renews the specified token and has failover support when more than one endpoint URI has been provided
* in the
* constructor. If a {@code ConnectException} occurs when sending the WS-Trust request to one endpoint, the code
* makes a new
* attempt using the next URI until the request reaches an STS instance or all URIs have been tried.
*
*
* @param tokenType the type of the token being renewed.
* @param token an {@code Element} representing the security token being renewed.
* @param clientIndex an {@code int} that indicates which of the {@code STSClient} instances should be used to
* perform the
* request.
*
* @return an {@code Element} representing the security token that has been renewed.
*
* @throws WSTrustException if a WS-Trust exception is thrown by the STS.
*/
private Element renewInternal(String tokenType, Element token, int clientIndex) throws WSTrustException {
STSClient client = this.clients[clientIndex];
try {
return client.renewToken(tokenType, token);
} catch (RuntimeException e) {
// if this was a connection refused exception and we still have clients to try, call the next client.
if (this.isCausedByConnectException(e) && clientIndex < this.clients.length - 1) {
return this.renewInternal(tokenType, token, ++clientIndex);
}
throw e;
}
}
/**
*
* This method validates the specified token and has failover support when more than one endpoint URI has been
* provided in
* the constructor. If a {@code ConnectException} occurs when sending the WS-Trust request to one endpoint, the code
* makes a
* new attempt using the next URI until the request reaches an STS instance or all URIs have been tried.
*
*
* @param token an {@code Element} representing the security token being validated.
* @param clientIndex an {@code int} that indicates which of the {@code STSClient} instances should be used to
* perform the
* request.
*
* @return {@code true} if the token was considered valid; {@code false} otherwise.
*
* @throws WSTrustException if a WS-Trust exception is thrown by the STS.
*/
private boolean validateInternal(Element token, int clientIndex) throws WSTrustException {
STSClient client = this.clients[clientIndex];
try {
return client.validateToken(token);
} catch (RuntimeException e) {
// if this was a connection refused exception and we still have clients to try, call the next client.
if (this.isCausedByConnectException(e) && clientIndex < this.clients.length - 1) {
return this.validateInternal(token, ++clientIndex);
}
throw e;
}
}
/**
*
* This method cancels the specified token and has failover support when more than one endpoint URI has been
* provided in the
* constructor. If a {@code ConnectException} occurs when sending the WS-Trust request to one endpoint, the code
* makes a new
* attempt using the next URI until the request reaches an STS instance or all URIs have been tried.
*
*
* @param token an {@code Element} representing the security token being canceled.
* @param clientIndex an {@code int} that indicates which of the {@code STSClient} instances should be used to
* perform the
* request.
*
* @return {@code true} if the token was canceled; {@code false} otherwise.
*
* @throws WSTrustException if a WS-Trust exception is thrown by the STS.
*/
private boolean cancelInternal(Element token, int clientIndex) throws WSTrustException {
STSClient client = this.clients[clientIndex];
try {
return client.cancelToken(token);
} catch (RuntimeException e) {
// if this was a connection refused exception and we still have clients to try, call the next client.
if (this.isCausedByConnectException(e) && clientIndex < this.clients.length - 1) {
return this.cancelInternal(token, ++clientIndex);
}
throw e;
}
}
/**
*
* Checks if the root of the specified {@code Throwable} is an instance of {@code java.net.ConnectException}.
*
*
* @param throwable the {@code Throwable} that will be inspected.
*
* @return {@code true} if the root cause is a {@code java.net.ConnectException}; {@code false} otherwise.
*/
private boolean isCausedByConnectException(Throwable throwable) {
// iterate through the causes until we reach the root cause.
while (throwable.getCause() != null)
throwable = throwable.getCause();
// check if the root throwable is a ConnectException.
if (throwable instanceof java.net.ConnectException && throwable.getMessage().equals("Connection refused"))
return true;
return false;
}
/**
* This method returns all allocated clients back to the STSClientPool when pooling is enabled.
*/
public void close() {
STSClientPool pool = STSClientFactory.getInstance();
for (STSClient client: this.clients) {
pool.returnClient(client);
}
}
}