Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "EclipseLink/Examples/JPA/Oracle/Proxy"

< EclipseLink‎ | Examples‎ | JPA‎ | Oracle
(Full (Read and Write) Access Control. VPD. Isolated cache case.)
 
(10 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
<div style="border:1px solid #999999;background-color:#ffffff;align:center">
 
<div style="border:1px solid #999999;background-color:#ffffff;align:center">
<table border="0" cellpadding="5" align="center"><tr><td width="20">[[image:Catnicon.gif]]</td><td>This page is under construction.<p>The code samples provided here require validation in various architectures before being finalized.<br>--[[User:Douglas.clarke.oracle.com|Doug]] 21:22, 29 March 2008 (EDT)</td><td width="20">[[image:Catnicon.gif]]</td></tr></table>
+
<table border="0" cellpadding="5" align="center"><tr><td width="20">[[image:Catnicon.gif]]</td><td>This example is currently under development see [http://bugs.eclipse.org/224964 Bug 224964]</td></tr></table>
 
</div>
 
</div>
  
Line 15: Line 15:
 
=== Requirements ===
 
=== Requirements ===
  
* Access to OracleConnection (typically from OracleDataSource)
+
* Access to OracleConnection (typically from OracleDataSource) using Oracle jdbc driver version 10.1.0.2 or later.
  
* Event listener to open and close the proxy session. This can be done with regular or exclusive connections
+
=== Write Access Control. Auditing ===
 +
Each change of the database could be attributed to the database user who did it (auditing).
 +
Eclipselink application maintains the shared cache.
 +
* EntityManager uses proxy user "john" for writes and reads inside transaction. Note that reads performed outside of transaction are done through the main (non proxied) connection.
 +
<source lang=java>
 +
Map emProperties = new HashMap();
 +
emProperties.put("eclipselink.oracle.proxy-type", OracleConnection.PROXYTYPE_USER_NAME);
 +
emProperties.put(OracleConnection.PROXY_USER_NAME, "john");
 +
EntityManager em = emf.createEntityManager(emProperties);
 +
// or in case of injected EntityManager
 +
((org.eclipse.persistence.internal.jpa.EntityManagerImpl)em.getDelegate()).setProperties(emProperties);
 +
</source>
  
== Configuration ==
+
=== Full (Read and Write) Access Control. VPD. ===
 +
==== Isolated cache case. ====
 +
The entities defined to use isolated (not shared) cache will be both written and read through the same "exclusive" connection.
 +
* Pass to createEntityManagerFactory method a property(ies) indicating that particular entity(ies) uses isolated cache.
 +
<source lang=java>
 +
// Entity named Employee uses isolated cache and will be both read and written through exclusive connection.
 +
"eclipselink.cache.shared.Employee"  -> "false"
 +
</source>
 +
* Or alternatively pass to createEntityManagerFactory method a property indicating that all entities use isolated cache.
 +
<source lang=java>
 +
// All entities use isolated cache and will be both read and written through exclusive connection.
 +
"eclipselink.cache.shared.default"  -> "false"
 +
</source>
 +
* Pass to either createEntityManagerFactory or createEntityManager a property indicating that isolated entities should be read through exclusive connection.
 +
<source lang=java>
 +
  "eclipselink.jdbc.exclusive-connection.mode" -> "Isolated"
 +
</source>
  
* Configure SessionEventListerner
+
==== Shared cache case. ====
 +
All entities will be both written and read through the same "exclusive" connection.
 +
Note that (unless the entities are isolated) all the read entities end up in the same shared cache that might compromise security.
 +
* Pass to either createEntityManagerFactory or createEntityManager a property indicating that isolated entities should be read through exclusive connection.
 +
<source lang=java>
 +
  "eclipselink.jdbc.exclusive-connection.mode" -> "Always"
 +
</source>
  
== Opening the Proxy Session ==
+
=== Defining Proxy Properties on EntityManagerFactory. ===
 +
Proxy properties may be also used by EntityManagerFactory. In that case all connections use them - unless overridden in EntityManager.
 +
<source lang=java>
 +
Map factoryProperties = new HashMap();
 +
factoryProperties.put("eclipselink.oracle.proxy-type", OracleConnection.PROXYTYPE_USER_NAME);
 +
factoriesProperties.put(OracleConnection.PROXY_USER_NAME, "sarah");
 +
EntityManagerFactory emf = Persistence.createEntityManagerFactory(factoryProperties);
  
 +
// em1 doesn't specify its own proxy properties - uses proxy user "sarah" specified by the factory.
 +
EntityManager em1 = emf.createEntityManager();
  
=== SessionEVentListener.postAcquireExclusiveConnection(SessionEvent event) ===
+
// em2 uses its own proxy properties - proxy user "john", doesn't matter whether factory has proxy properties or not.
 +
EntityManager em2 = emf.createEntityManager(emProperties);
  
<pre>
+
// em3 doesn't use any proxy connection - cancels proxy properties defined in the factory.
    @Override
+
Map cancelProperties = new HashMap();
    public void postAcquireExclusiveConnection(SessionEvent event) {
+
cancelProperties.put("eclipselink.oracle.proxy-type", "");
        Session session = event.getSession();
+
EntityManager em3 = emf.createEntityManager(cancelProperties);
        if (!session.isClientSession()) {
+
</source>
            return;
+
        }
+
        ClientSession clientSession = (ClientSession)session;
+
        Accessor accessor = (Accessor)event.getResult();
+
        accessor.incrementCallCount(clientSession);
+
 
+
        openProxySession(accessor.getConnection(),
+
                        ((ExclusiveIsolatedClientSession)event.getSession()).getConnectionPolicy().getProperties());
+
    }
+
</pre>
+
 
+
<pre>
+
    /**
+
    * In case "proxytype" property is specified connects using proxy connection,
+
    * otherwise calls its superclass.
+
    */
+
    private void openProxySession(Connection conn,
+
                                  Map properties) throws DatabaseException,
+
                                                        ValidationException {
+
        Integer proxytype =
+
            (Integer)ConversionManager.getDefaultManager().convertObject(properties.get(OracleOCIConnectionPool.PROXYTYPE),
+
                                                                        Integer.class);
+
 
+
        if (proxytype != null) {
+
            try {
+
                Properties props = new Properties();
+
                props.putAll(properties);
+
                props.remove(OracleOCIConnectionPool.PROXYTYPE);
+
                ((OracleConnection)conn).openProxySession(proxytype.intValue(),
+
                                                          props);
+
            } catch (SQLException exception) {
+
                throw DatabaseException.sqlException(exception);
+
            } catch (ClassCastException classCastException) {
+
                throw ValidationException.oracleJDBC10_1_0_2ProxyConnectorRequiresOracleConnection();
+
            } catch (NoSuchMethodError noSuchMethodError) {
+
                throw ValidationException.oracleJDBC10_1_0_2ProxyConnectorRequiresOracleConnectionVersion();
+
            }
+
        }
+
    }
+
</pre>
+
 
+
=== SessionEventListener.preReleaseExclusiveConnection(SessionEvent event) ===
+
 
+
<pre>
+
    @Override
+
    public void preReleaseExclusiveConnection(SessionEvent event) {
+
        Session session = event.getSession();
+
        if (!session.isClientSession()) {
+
            return;
+
        }
+
        ClientSession clientSession = (ClientSession)session;
+
        oracle.toplink.internal.databaseaccess.Accessor accessor =
+
            clientSession.getWriteConnection();
+
        if (accessor != null) {
+
            oracle.jdbc.OracleConnection oracleConn =
+
                (oracle.jdbc.OracleConnection)accessor.getConnection();
+
            if (oracleConn != null && oracleConn.isProxySession()) {
+
                try {
+
                    oracleConn.close(oracle.jdbc.OracleConnection.PROXY_SESSION);
+
                } catch (java.sql.SQLException sqlEx) {
+
                    throw new RuntimeException(sqlEx);
+
                }
+
            }
+
            accessor.decrementCallCount();
+
        }
+
    }
+
</pre>
+
 
+
== Passing in the User Credentials ==
+
 
+
Passing in the credentials required for opening the proxy session involves passing the Properties to the event listener.
+
 
+
 
+
== Extras ==
+
 
+
The following are not require but are useful utilities when working with proxy authentication.
+
 
+
=== Configure OracleDataSource using Customizer ===
+
 
+
In some environments the creation of an OracleDataSource may be required. the following code uses a SessionCustomizer to replace the configured internal connection pool with an OracleDataSource using the provided configuration values.
+
 
+
<pre>
+
            // create a data source using the supplied connection string
+
            OracleDataSource ods;
+
            try {
+
                ods = new OracleDataSource();
+
            } catch (SQLException ex) {
+
                throw new RuntimeException(ex);
+
            }
+
            ods.setURL(session.getLogin().getConnectionString());
+
            ods.setUser(session.getLogin().getUserName());
+
            ods.setPassword("tiger");
+
 
+
            // substitute connector with the new one using the created data source
+
            session.getLogin().setConnector(new JNDIConnector(ods));
+
 
+
            // make sure to have external connection pooling flag set
+
            session.getLogin().setUsesExternalConnectionPooling(true);
+
            // This line is required to ensure that the connections being proxied are not shared
+
            ((ServerSession)session).getDefaultConnectionPolicy().setShouldUseExclusiveConnection(true);
+
</pre>
+

Latest revision as of 16:53, 9 July 2008

Catnicon.gifThis example is currently under development see Bug 224964

How to use EclipseLink JPA with Oracle Proxy Authentication

The Oracle database offers proxy authentication enabling the application to leverage a shared data source connected to the database by a single common/default user and then when used within the application the connection can be 'proxied' to be a different user. This offers the benefit of the database having knowledge of the specific user for the purposes of auditing or secure data access.

In this how-to the focus is on the usage of proxy authentication in conjunction with EclipseLink's JPA.

Overview

Requirements

  • Access to OracleConnection (typically from OracleDataSource) using Oracle jdbc driver version 10.1.0.2 or later.

Write Access Control. Auditing

Each change of the database could be attributed to the database user who did it (auditing). Eclipselink application maintains the shared cache.

  • EntityManager uses proxy user "john" for writes and reads inside transaction. Note that reads performed outside of transaction are done through the main (non proxied) connection.
Map emProperties = new HashMap();
emProperties.put("eclipselink.oracle.proxy-type", OracleConnection.PROXYTYPE_USER_NAME);
emProperties.put(OracleConnection.PROXY_USER_NAME, "john");
EntityManager em = emf.createEntityManager(emProperties);
// or in case of injected EntityManager
((org.eclipse.persistence.internal.jpa.EntityManagerImpl)em.getDelegate()).setProperties(emProperties);

Full (Read and Write) Access Control. VPD.

Isolated cache case.

The entities defined to use isolated (not shared) cache will be both written and read through the same "exclusive" connection.

  • Pass to createEntityManagerFactory method a property(ies) indicating that particular entity(ies) uses isolated cache.
// Entity named Employee uses isolated cache and will be both read and written through exclusive connection.
"eclipselink.cache.shared.Employee"  -> "false"
  • Or alternatively pass to createEntityManagerFactory method a property indicating that all entities use isolated cache.
// All entities use isolated cache and will be both read and written through exclusive connection.
"eclipselink.cache.shared.default"  -> "false"
  • Pass to either createEntityManagerFactory or createEntityManager a property indicating that isolated entities should be read through exclusive connection.
 
  "eclipselink.jdbc.exclusive-connection.mode" -> "Isolated"

Shared cache case.

All entities will be both written and read through the same "exclusive" connection. Note that (unless the entities are isolated) all the read entities end up in the same shared cache that might compromise security.

  • Pass to either createEntityManagerFactory or createEntityManager a property indicating that isolated entities should be read through exclusive connection.
 
  "eclipselink.jdbc.exclusive-connection.mode" -> "Always"

Defining Proxy Properties on EntityManagerFactory.

Proxy properties may be also used by EntityManagerFactory. In that case all connections use them - unless overridden in EntityManager.

Map factoryProperties = new HashMap();
factoryProperties.put("eclipselink.oracle.proxy-type", OracleConnection.PROXYTYPE_USER_NAME);
factoriesProperties.put(OracleConnection.PROXY_USER_NAME, "sarah");
EntityManagerFactory emf = Persistence.createEntityManagerFactory(factoryProperties);
 
// em1 doesn't specify its own proxy properties - uses proxy user "sarah" specified by the factory.
EntityManager em1 = emf.createEntityManager();
 
// em2 uses its own proxy properties - proxy user "john", doesn't matter whether factory has proxy properties or not.
EntityManager em2 = emf.createEntityManager(emProperties);
 
// em3 doesn't use any proxy connection - cancels proxy properties defined in the factory.
Map cancelProperties = new HashMap();
cancelProperties.put("eclipselink.oracle.proxy-type", "");
EntityManager em3 = emf.createEntityManager(cancelProperties);

Back to the top