/* * Copyright 2014 JBoss Inc * * 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 io.apiman.manager.api.gateway.rest; import io.apiman.common.util.AesEncrypter; import io.apiman.common.util.ApimanStrLookup; import io.apiman.common.util.crypt.CurrentDataEncrypter; import io.apiman.common.util.crypt.DataEncryptionContext; import io.apiman.gateway.engine.beans.Api; import io.apiman.gateway.engine.beans.ApiEndpoint; import io.apiman.gateway.engine.beans.Client; import io.apiman.gateway.engine.beans.SystemStatus; import io.apiman.gateway.engine.beans.exceptions.PublishingException; import io.apiman.gateway.engine.beans.exceptions.RegistrationException; import io.apiman.manager.api.beans.gateways.GatewayBean; import io.apiman.manager.api.beans.gateways.RestGatewayConfigBean; import io.apiman.manager.api.gateway.GatewayAuthenticationException; import io.apiman.manager.api.gateway.IGatewayLink; import io.apiman.manager.api.gateway.i18n.Messages; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.text.StrLookup; import org.apache.commons.lang3.text.StrSubstitutor; import org.apache.http.HttpException; import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.protocol.HttpContext; import org.apache.http.ssl.SSLContextBuilder; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; /** * An implementation of a Gateway Link that uses the Gateway's simple REST * API to publish APIs. * * @author eric.wittmann@redhat.com */ public class RestGatewayLink implements IGatewayLink { private static StrLookup LOOKUP = new ApimanStrLookup(); private static StrSubstitutor PROPERTY_SUBSTITUTOR = new StrSubstitutor(LOOKUP); static { PROPERTY_SUBSTITUTOR.setValueDelimiter(':'); } private static final ObjectMapper mapper = new ObjectMapper(); private static SSLConnectionSocketFactory sslConnectionFactory; static { try { SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, new TrustStrategy() { @Override public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }); sslConnectionFactory = new SSLConnectionSocketFactory(builder.build(), NoopHostnameVerifier.INSTANCE); } catch (Exception e) { throw new RuntimeException(e); } } @SuppressWarnings("unused") private GatewayBean gateway; private CloseableHttpClient httpClient; private GatewayClient gatewayClient; private RestGatewayConfigBean config; /** * Constructor. * @param gateway the gateway */ public RestGatewayLink(final GatewayBean gateway) { try { this.gateway = gateway; String cfg = gateway.getConfiguration(); cfg = CurrentDataEncrypter.instance.decrypt(cfg, new DataEncryptionContext()); cfg = PROPERTY_SUBSTITUTOR.replace(cfg); setConfig((RestGatewayConfigBean) mapper.reader(RestGatewayConfigBean.class).readValue(cfg)); getConfig().setPassword(AesEncrypter.decrypt(getConfig().getPassword())); httpClient = HttpClientBuilder.create() .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) .setSSLSocketFactory(sslConnectionFactory) .addInterceptorFirst(new HttpRequestInterceptor() { @Override public void process(HttpRequest request, HttpContext context) throws HttpException, IOException { configureBasicAuth(request); } }).build(); } catch (JsonProcessingException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } } /** * @see io.apiman.manager.api.gateway.IGatewayLink#close() */ @Override public void close() { try { httpClient.close(); } catch (IOException e) { // TODO log the error? } } /** * Checks that the gateway is up. */ private boolean isGatewayUp() throws GatewayAuthenticationException { SystemStatus status = getClient().getStatus(); return status.isUp(); } /** * @see io.apiman.manager.api.gateway.IGatewayLink#getStatus() */ @Override public SystemStatus getStatus() throws GatewayAuthenticationException { return getClient().getStatus(); } /** * @see io.apiman.manager.api.gateway.IGatewayLink#getApiEndpoint(java.lang.String, java.lang.String, java.lang.String) */ @Override public ApiEndpoint getApiEndpoint(String organizationId, String apiId, String version) throws GatewayAuthenticationException { return getClient().getApiEndpoint(organizationId, apiId, version); } /** * @see io.apiman.manager.api.gateway.IGatewayLink#publishApi(io.apiman.gateway.engine.beans.Api) */ @Override public void publishApi(Api api) throws PublishingException, GatewayAuthenticationException { if (!isGatewayUp()) { throw new PublishingException(Messages.i18n.format("RestGatewayLink.GatewayNotRunning")); //$NON-NLS-1$ } getClient().publish(api); } /** * @see io.apiman.manager.api.gateway.IGatewayLink#retireApi(io.apiman.gateway.engine.beans.Api) */ @Override public void retireApi(Api api) throws PublishingException, GatewayAuthenticationException { if (!isGatewayUp()) { throw new PublishingException(Messages.i18n.format("RestGatewayLink.GatewayNotRunning")); //$NON-NLS-1$ } getClient().retire(api.getOrganizationId(), api.getApiId(), api.getVersion()); } /** * @see io.apiman.manager.api.gateway.IGatewayLink#registerClient(io.apiman.gateway.engine.beans.Client) */ @Override public void registerClient(Client client) throws RegistrationException, GatewayAuthenticationException { if (!isGatewayUp()) { throw new RegistrationException(Messages.i18n.format("RestGatewayLink.GatewayNotRunning")); //$NON-NLS-1$ } getClient().register(client); } /** * @see io.apiman.manager.api.gateway.IGatewayLink#unregisterClient(io.apiman.gateway.engine.beans.Client) */ @Override public void unregisterClient(Client client) throws RegistrationException, GatewayAuthenticationException { if (!isGatewayUp()) { throw new RegistrationException(Messages.i18n.format("RestGatewayLink.GatewayNotRunning")); //$NON-NLS-1$ } getClient().unregister(client.getOrganizationId(), client.getClientId(), client.getVersion()); } /** * Configures BASIC authentication for the request. * @param request */ protected void configureBasicAuth(HttpRequest request) { try { String username = getConfig().getUsername(); String password = getConfig().getPassword(); String up = username + ":" + password; //$NON-NLS-1$ String base64 = new String(Base64.encodeBase64(up.getBytes("UTF-8"))); //$NON-NLS-1$ String authHeader = "Basic " + base64; //$NON-NLS-1$ request.setHeader("Authorization", authHeader); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * @return the gateway client */ protected GatewayClient getClient() { if (gatewayClient == null) { gatewayClient = createClient(); } return gatewayClient; } /** * @return a newly created rest gateway client */ private GatewayClient createClient() { String gatewayEndpoint = getConfig().getEndpoint(); return new GatewayClient(gatewayEndpoint, httpClient); } /** * @return the config */ public RestGatewayConfigBean getConfig() { return config; } /** * @param config the config to set */ public void setConfig(RestGatewayConfigBean config) { this.config = config; } }