/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.talend.esb.security.oidc; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import org.apache.cxf.helpers.IOUtils; import org.apache.cxf.jaxrs.client.WebClient; import javax.ws.rs.core.Response; public class OidcClientUtils { public static final String OIDC_TOKEN_ENDPOINT_LOCATION = "org.talend.esb.job.oidc.token.endpoint"; public static final String OIDC_VALIDATION_ENDPOINT_LOCATION = "org.talend.esb.job.oidc.validation.endpoint"; public static final String OIDC_PUBLIC_CLIENT_ID = "org.talend.esb.job.oidc.public.client.id"; public static final String OIDC_SCOPE = "org.talend.esb.job.oidc.scope"; private static final String DEFAULT_OIDC_SCOPE = "openid"; private static final String DEFAULT_PUBLIC_CLIENT_ID = "aFSloIZSXHRQtA"; private static Map<String, String> oidcProperties = new HashMap<String, String>(); public OidcClientUtils(Map<String, String> oidcProperties) { OidcClientUtils.oidcProperties = oidcProperties; } public static String getTokenEndpointLocation() { if (System.getProperty(OIDC_TOKEN_ENDPOINT_LOCATION) != null) { return (String) System.getProperty(OIDC_TOKEN_ENDPOINT_LOCATION); } else { return oidcProperties.get(OIDC_TOKEN_ENDPOINT_LOCATION); } } public static String getValidationEndpointLocation() { if (System.getProperty(OIDC_VALIDATION_ENDPOINT_LOCATION) != null) { return (String) System .getProperty(OIDC_VALIDATION_ENDPOINT_LOCATION); } else { return oidcProperties.get(OIDC_VALIDATION_ENDPOINT_LOCATION); } } public static String getPublicClientID() { if (System.getProperty(OIDC_PUBLIC_CLIENT_ID) != null) { return (String) System.getProperty(OIDC_PUBLIC_CLIENT_ID); } else { if (null == oidcProperties.get(OIDC_PUBLIC_CLIENT_ID)) { return DEFAULT_PUBLIC_CLIENT_ID; } return oidcProperties.get(OIDC_PUBLIC_CLIENT_ID); } } public static String getScope() { if (System.getProperty(OIDC_SCOPE) != null) { return (String) System.getProperty(OIDC_SCOPE); } else { if (null == oidcProperties.get(OIDC_SCOPE)) { return DEFAULT_OIDC_SCOPE; } return oidcProperties.get(OIDC_SCOPE); } } public static Map<String, String> getOidcSettings() { Map<String, String> settings = new HashMap<String, String>(); settings.put(OIDC_TOKEN_ENDPOINT_LOCATION, getTokenEndpointLocation()); settings.put(OIDC_VALIDATION_ENDPOINT_LOCATION, getValidationEndpointLocation()); settings.put(OIDC_PUBLIC_CLIENT_ID, getPublicClientID()); settings.put(OIDC_SCOPE, getScope()); return settings; } public static Map<String, String> parseJson(InputStream is) throws IOException { String str = IOUtils.readStringFromStream(is).trim(); if (str.length() == 0) { return Collections.emptyMap(); } if (!str.startsWith("{") || !str.endsWith("}")) { throw new IOException("JSON Sequence is broken: " + str); } Map<String, String> map = new LinkedHashMap<String, String>(); str = str.substring(1, str.length() - 1).trim(); String[] jsonPairs = str.split(","); for (int i = 0; i < jsonPairs.length; i++) { String pair = jsonPairs[i].trim(); if (pair.length() == 0) { continue; } int index = pair.indexOf(":"); String key = pair.substring(0, index).trim(); if (key.startsWith("\"") && key.endsWith("\"")) { key = key.substring(1, key.length() - 1); } String value = pair.substring(index + 1).trim(); if (value.startsWith("\"") && value.endsWith("\"")) { value = value.substring(1, value.length() - 1); } map.put(key, value); } return map; } /** * * Obtain OIDC access token from token endpoint. Intended to be used by OIDC clients. * * @param oidcUsername * @param oidcPassword * @return Authentication bearer HTTP header value * @throws java.lang.Exception * */ public static String oidcClientBearer(String oidcUsername, String oidcPassword) throws java.lang.Exception { return oidcClientBearer(oidcUsername, oidcPassword, getOidcSettings()); } /** * * Obtain OIDC access token from token endpoint. Intended to be used by OIDC clients. * * @param oidcUsername * @param oidcPassword * @return Authentication bearer HTTP header value * @throws java.lang.Exception */ public static String oidcClientBearer(String oidcUsername, String oidcPassword, Map<String, String> settings) throws java.lang.Exception { if (oidcUsername == null || oidcUsername.isEmpty()) { throw new IllegalArgumentException("OIDC username is a required parameter"); } if (oidcPassword == null) { throw new IllegalArgumentException("OIDC password is a required parameter"); } String tokenEndpoint = settings.get(OIDC_TOKEN_ENDPOINT_LOCATION); if (tokenEndpoint == null || tokenEndpoint.isEmpty()) { throw new Exception("Token endpoint setting is null or empty"); } String clientId = settings.get(OIDC_PUBLIC_CLIENT_ID); if (clientId == null || clientId.isEmpty()) { throw new Exception("OIDC client ID setting is null or empty"); } String scope = settings.get(OIDC_SCOPE); if (scope == null || scope.isEmpty()) { scope = "openid"; } WebClient webClient = WebClient.create(tokenEndpoint, java.util.Collections.singletonList(new org.apache.cxf.jaxrs.provider.json.JSONProvider())).type("application/x-www-form-urlencoded"); Response response = webClient.post("grant_type=password&scope=" + scope +"&username=" + oidcUsername + "&password=" + oidcPassword + "&client_id=" + clientId); java.util.Map<String, String> responseMap; try { responseMap = parseJson((java.io.InputStream) response.getEntity()); } catch (Exception ex) { throw new Exception("Can not parse response from OIDC Access Token service: ", ex); } if (response.getStatus() != 200) { if (responseMap.get("error") != null) { throw new Exception("OIDC Access Token request failed: " + responseMap.get("error")); } else { throw new Exception("OIDC token endpoint replied with HTTTP " + response.getStatus() + " on token request"); } } if (!"Bearer".equals(responseMap.get("token_type"))) { throw new Exception("Token returned from OIDC Access Token service is not of Bearer type"); } return "Bearer " + responseMap.get("access_token"); } }