/* * 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.gateway.engine.policies.auth; import io.apiman.gateway.engine.async.AsyncResultImpl; import io.apiman.gateway.engine.async.IAsyncResult; import io.apiman.gateway.engine.async.IAsyncResultHandler; import io.apiman.gateway.engine.beans.ApiRequest; import io.apiman.gateway.engine.components.IJdbcComponent; import io.apiman.gateway.engine.components.jdbc.IJdbcClient; import io.apiman.gateway.engine.components.jdbc.IJdbcConnection; import io.apiman.gateway.engine.components.jdbc.IJdbcResultSet; import io.apiman.gateway.engine.components.jdbc.JdbcOptionsBean; import io.apiman.gateway.engine.policies.AuthorizationPolicy; import io.apiman.gateway.engine.policies.config.basicauth.JDBCIdentitySource; import io.apiman.gateway.engine.policies.config.basicauth.JDBCType; import io.apiman.gateway.engine.policy.IPolicyContext; import java.util.HashSet; import java.util.Set; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import org.apache.commons.codec.digest.DigestUtils; /** * An identity validator that uses the static information in the config * to validate the user. * * @author eric.wittmann@redhat.com */ public class JDBCIdentityValidator implements IIdentityValidator<JDBCIdentitySource> { /** * Constructor. */ public JDBCIdentityValidator() { } /** * @see io.apiman.gateway.engine.policies.auth.IIdentityValidator#validate(String, String, ApiRequest, IPolicyContext, Object, IAsyncResultHandler) */ @Override public void validate(final String username, final String password, final ApiRequest request, final IPolicyContext context, final JDBCIdentitySource config, final IAsyncResultHandler<Boolean> handler) { String sqlPwd = password; switch (config.getHashAlgorithm()) { case MD5: sqlPwd = DigestUtils.md5Hex(password); break; case SHA1: sqlPwd = DigestUtils.sha1Hex(password); break; case SHA256: sqlPwd = DigestUtils.sha256Hex(password); break; case SHA384: sqlPwd = DigestUtils.sha384Hex(password); break; case SHA512: sqlPwd = DigestUtils.sha512Hex(password); break; case None: default: break; } final String query = config.getQuery(); final String queryUsername = username; final String queryPassword = sqlPwd; IJdbcClient client; try { client = createClient(context, config); } catch (Throwable e) { handler.handle(AsyncResultImpl.create(e, Boolean.class)); return; } client.connect(new IAsyncResultHandler<IJdbcConnection>() { @Override public void handle(IAsyncResult<IJdbcConnection> result) { if (result.isError()) { handler.handle(AsyncResultImpl.create(result.getError(), Boolean.class)); } else { validate(result.getResult(), query, queryUsername, queryPassword, context, config, handler); } } }); } /** * Creates the appropriate jdbc client. * @param context * @param config */ private IJdbcClient createClient(IPolicyContext context, JDBCIdentitySource config) throws Throwable { IJdbcComponent jdbcComponent = context.getComponent(IJdbcComponent.class); if (config.getType() == JDBCType.datasource || config.getType() == null) { DataSource ds = lookupDatasource(config); return jdbcComponent.create(ds); } if (config.getType() == JDBCType.url) { JdbcOptionsBean options = new JdbcOptionsBean(); options.setJdbcUrl(config.getJdbcUrl()); options.setUsername(config.getUsername()); options.setPassword(config.getPassword()); options.setAutoCommit(true); return jdbcComponent.createStandalone(options); } throw new Exception("Unknown JDBC options."); //$NON-NLS-1$ } /** * @param connection * @param query * @param username * @param context * @param password * @param config * @param handler */ protected void validate(final IJdbcConnection connection, final String query, final String username, final String password, final IPolicyContext context, final JDBCIdentitySource config, final IAsyncResultHandler<Boolean> handler) { IAsyncResultHandler<IJdbcResultSet> queryHandler = new IAsyncResultHandler<IJdbcResultSet>() { @Override public void handle(IAsyncResult<IJdbcResultSet> result) { if (result.isError()) { closeQuietly(connection); handler.handle(AsyncResultImpl.create(result.getError(), Boolean.class)); } else { boolean validated = false; IJdbcResultSet resultSet = result.getResult(); if (resultSet.next()) { validated = true; } resultSet.close(); if (validated && config.isExtractRoles()) { extractRoles(connection, username, context, config, handler); } else { closeQuietly(connection); handler.handle(AsyncResultImpl.create(validated)); } } } }; connection.query(queryHandler, query, username, password); } /** * @param connection * @param username * @param context * @param config * @param handler */ protected void extractRoles(final IJdbcConnection connection, final String username, final IPolicyContext context, final JDBCIdentitySource config, final IAsyncResultHandler<Boolean> handler) { String roleQuery = config.getRoleQuery(); IAsyncResultHandler<IJdbcResultSet> roleHandler = new IAsyncResultHandler<IJdbcResultSet>() { @Override public void handle(IAsyncResult<IJdbcResultSet> result) { if (result.isError()) { closeQuietly(connection); handler.handle(AsyncResultImpl.create(result.getError(), Boolean.class)); } else { Set<String> extractedRoles = new HashSet<>(); IJdbcResultSet resultSet = result.getResult(); while (resultSet.next()) { String roleName = resultSet.getString(1); extractedRoles.add(roleName); } context.setAttribute(AuthorizationPolicy.AUTHENTICATED_USER_ROLES, extractedRoles); closeQuietly(connection); handler.handle(AsyncResultImpl.create(true)); } } }; connection.query(roleHandler, roleQuery, username); } /** * @param connection */ protected void closeQuietly(IJdbcConnection connection) { try { connection.close(); } catch (Exception e) { // TODO log this error } } /** * Lookup the datasource from JNDI. * @param config */ private DataSource lookupDatasource(JDBCIdentitySource config) { DataSource ds; try { InitialContext ctx = new InitialContext(); ds = lookupDS(ctx, config.getDatasourcePath()); if (ds == null) { ds = lookupDS(ctx, "java:comp/env/" + config.getDatasourcePath()); //$NON-NLS-1$ } } catch (Exception e) { throw new RuntimeException(e); } if (ds == null) { throw new RuntimeException("Datasource not found: " + config.getDatasourcePath()); //$NON-NLS-1$ } return ds; } /** * Lookup the datasource from JNDI. * @param ctx * @param path */ private DataSource lookupDS(InitialContext ctx, String path) { try { return (DataSource) ctx.lookup(path); } catch (NamingException e) { return null; } } }