All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.wavemaker.runtime.data.sqlserver.SqlServerUserImpersonatingDataSourceProxy Maven / Gradle / Ivy

There is a newer version: 11.9.2.ee
Show newest version
/**
 * Copyright (C) 2020 WaveMaker, 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 com.wavemaker.runtime.data.sqlserver; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import javax.sql.DataSource; import org.springframework.jdbc.datasource.ConnectionProxy; import org.springframework.jdbc.datasource.DelegatingDataSource; import org.springframework.jdbc.support.JdbcUtils; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** * DataSource proxy implementation that will allow impersonation of the currently authenticated user on MS SQLServer * when executing JDBC statements. * *

* Impersonation of the current user is achieved by executing a SQLServer-specific * EXECUTE AS USER='{username}' statement, where {username} is the username for the currently authenticated * user as provided by the {@link SecurityContext}. For this to work successfully, the user must be a valid * ActiveDirectory LDAP user. Optionally, an Active Directory Domain name may be configured that will be prepended to * the username. If the {@link #setActiveDirectoryDomain(String) activeDirectoryDomain} property is set with a non-empty * value, the statement executed to prepare the connection will be in the form of * EXECUTE AS USER='{activeDirectoryDomain}\{username}'. * *

* The EXECUTE AS statement will only be run when a connection is first requested at the start of a * Spring-managed transaction. When the transaction is completed and the connection is closed, (or released back to a * pool) a compensating REVERT statement will be executed that will return the connection to its original * state. * * @author Jeremy Grelle */ public class SqlServerUserImpersonatingDataSourceProxy extends DelegatingDataSource { private String activeDirectoryDomain = ""; public SqlServerUserImpersonatingDataSourceProxy() { super(); } public SqlServerUserImpersonatingDataSourceProxy(DataSource targetDataSource) { super(targetDataSource); } @Override public Connection getConnection() throws SQLException { DataSource ds = getTargetDataSource(); Assert.state(ds != null, "'targetDataSource' is required"); return getAuditingConnectionProxy(getTargetDataSource().getConnection()); } /** * Sets an Active Directory Domain name to be used as a prefix to the username when running an * EXECUTE AS statement to prepare a connection. * * @param activeDirectoryDomain the Active Directory Domain name */ public void setActiveDirectoryDomain(String activeDirectoryDomain) { this.activeDirectoryDomain = activeDirectoryDomain; } @Override public void setTargetDataSource(DataSource targetDataSource) { super.setTargetDataSource(targetDataSource); } private Connection getAuditingConnectionProxy(Connection connection) throws SQLException { return (Connection) Proxy.newProxyInstance(ConnectionProxy.class.getClassLoader(), new Class[]{ConnectionProxy.class}, new AuditingInvocationHandler(connection)); } private class AuditingInvocationHandler implements InvocationHandler { private final Connection target; public AuditingInvocationHandler(Connection targetConnection) throws SQLException { this.target = targetConnection; Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null) { StringBuilder query = new StringBuilder(); query.append("EXECUTE AS USER='"); if (StringUtils.hasText(SqlServerUserImpersonatingDataSourceProxy.this.activeDirectoryDomain)) { query.append(SqlServerUserImpersonatingDataSourceProxy.this.activeDirectoryDomain + "\\"); } query.append(auth.getName() + "'"); executeStatement(query.toString()); } } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // Invocation on ConnectionProxy interface coming in... if (method.getName().equals("equals")) { // Only consider equal when proxies are identical. return proxy == args[0]; } else if (method.getName().equals("hashCode")) { // Use hashCode of PersistenceManager proxy. return System.identityHashCode(proxy); } else if (method.getName().equals("unwrap")) { if (((Class) args[0]).isInstance(proxy)) { return proxy; } } else if (method.getName().equals("isWrapperFor")) { if (((Class) args[0]).isInstance(proxy)) { return true; } } else if (method.getName().equals("close")) { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null) { executeStatement("REVERT"); } } else if (method.getName().equals("getTargetConnection")) { // Handle getTargetConnection method: return underlying Connection. return this.target; } // Invoke method on target Connection. try { return method.invoke(this.target, args); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } } private void executeStatement(String sql) throws SQLException { Statement statement = null; try { statement = this.target.createStatement(); statement.execute(sql); } finally { JdbcUtils.closeStatement(statement); } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy