Before you begin

Start the Database and Infrastructure processes.

Configure the Database to use OID

  1. Start -> Oracle - ora102db -> Configuration and Migration Tools -> Net Configuration Assistant
  2. Check Directory Usage Configuration
  3. Click Next
  4. As Directory Type, select Oracle Internet Directory (already selected)
  5. Click Next
  6. Directory Location:
  7. Click Next
  8. As Oracle Context, select cn=OracleContext,dc=disney,dc=com (not cn=OracleContext)
  9. Click Next
  10. Click Next
  11. Click Finish

Register the Database with OID

  1. Go to Enterprise Security Manager
  2. Expand Realms
  3. Expand us
  4. Expand Users, by Search Base
  5. If dbcreator is not listed, then add it (User ID: dbcreator, Email Address: joker@gotham.com, Password: welcome1
  6. Create test users batman (Email Address batman@gotham.com) and robin (Email Address robin@gotham.com), both with password oracle3
  7. Click Groups from the submenu
  8. Select Oracle Database Registration Administrators (already selected) and click Edit
  9. Click Add User
  10. Type dbcreator and press Enter, then click Select
  11. Click Submit
  12. Click OK

  13. Start -> Oracle - ora102db -> Configuration and Migration Tools -> Database Configuration Assistant
  14. Click Next
  15. Select Configure Database Options and click Next
  16. Select your database (ORCL) and click Next
  17. Select Yes, register the database
  18. Click Next
  19. Select Dedicated Server Mode (already selected)
  20. Click Finish
  21. Click OK
  22. When asked "Do you want to perform another operation?", click No

Create demo objects

  1. Start SQL*Plus and log in as SYS:
    sqlplus sys@orcl as sysdba
    

  2. Create an application schema called MYAPP:
    create user myapp identified by oracle1
    default tablespace users
    temporary tablespace temp
    quota unlimited on users;
    
    grant create session, create table to myapp;
    

  3. Connect to MYAPP and create a table called MYDATA:
    connect myapp/oracle1@orcl
    
    create table mydata (id number, name varchar2(20));
    
    insert into mydata values (1, 'first');
    insert into mydata values (2, 'second');
    
    commit;
    

  4. Connect as SYS and create "read only" and "read/write" roles for accessing the application data:
    connect sys@orcl as sysdba
    
    create role myapp_ro identified globally;
    
    grant select on myapp.mydata to myapp_ro;
    
    create role myapp_rw identified globally;
    
    grant insert, select, update, delete on myapp.mydata to myapp_rw;
    

  5. Finally, create an application user called MYAPP_USER with a synonym pointing to MYDATA:
    create user myapp_user identified globally as '';
    grant create session to myapp_user;
    
    create synonym myapp_user.mydata for myapp.mydata;
    

Match all users to a global database schema

  1. Go to Enterprise Security Manager
  2. Expand Realms
  3. Expand us
  4. Expand Enterprise Domains
  5. Select OracleDefaultDomain
  6. Click the Database Schema Mapping tab
  7. Click Add
  8. Navigate to cn=Users,dc=disney,dc=com by expanding com, then disney, then Users
  9. Check Subtree Level (already checked)
  10. As Schema, enter myapp_user
  11. Click OK
  12. Click Apply

Match users to appropriate global database roles

  1. Go to Enterprise Security Manager

  2. Select Operations from the menu
  3. Select Create Enterprise Role
  4. As Role Name, enter myapp_ro

  5. Similarly, create an Enterprise Role called myapp_rw

  6. Navigate to the newly created role (Realms -> us -> Enterprise Domains -> OracleDefaultDomain -> Enterprise Roles -> myapp_rw)
  7. Click the Database Global Roles tab
  8. Click Add
  9. Select your database (e.g. orcl)
  10. Click OK
  11. Click the name of the Global Database Role (e.g. MYAPP_RW) to match the Enterprise Role (e.g. myapp_rw)
  12. Click OK
  13. Click Apply

  14. Repeat the above steps for the myapp_ro role

  15. Connect the myapp_ro role to user robin, and the myapp_rw role to user batman

Test

  1. Start SQL*Plus and log in as ROBIN and verify that you can only read the data, but not update it:
    sqlplus robin/oracle3@orcl
    
    SQL> show user
    USER is MYAPP_USER
    
    SQL> select sys_context('userenv', 'session_user') from dual;
    
    SYS_CONTEXT('USERENV','SESSION_USER')
    -------------------------------------
    MYAPP_USER
    
    SQL> select sys_context('userenv', 'external_name') from dual;
    
    SYS_CONTEXT('USERENV','EXTERNAL_NAME')
    --------------------------------------
    cn=robin,cn=users,dc=disney,dc=com
    
    SQL> select * from mydata;
    
    SQL> update mydata set name = 'FIRST' where id = 1;
    

  2. Connect as BATMAN and verify that you can both read and update the data:
    connect batman/oracle3@orcl
    
    select * from mydata;
    
    update mydata set name = 'FIRST' where id = 1;
    

Proxy Authentication

Now let's see if we can combine Enterprise User Security with Proxy Authentication in order to achieve accountability while still being able to use a connection pool in our web application. First, we'll create a simple Java class, and then we'll create an HTTP servlet to be deployed in OC4J.
  1. Create user MYAPP_POOL with minimum privileges in the database:
    connect sys@orcl as sysdba
    
    create user myapp_pool identified by oracle4;
    
    grant create session to myapp_pool;
    

  2. Allow MYAPP_USER to connect through the proxy user MYAPP_POOL:
    alter user myapp_user grant connect through myapp_pool authenticated using distinguished name;
    

  3. Start JDeveloper

  4. Create a new Empty Project called myapp

  5. Include your database's JDBC library (e.g. D:\ora102db\jdbc\lib\ojdbc14.jar) in the project libraries

  6. Create a class called dbhelper:
    package myapp;
    
    import java.sql.*;
    import oracle.jdbc.pool.OracleOCIConnectionPool;
    
    public class dbhelper {
    
      public static String doQuery(Connection conn, String query) {
    
        if (conn == null) {
          return "No connection! ";
        }
    
        StringBuffer sb = new StringBuffer();
    
        try {
    
          Statement stmt = conn.createStatement();
          ResultSet rs = stmt.executeQuery(query);
    
          for (int t_count = 0; ; t_count++ ) {
            if (rs.next()) {
              sb.append (
                rs.getString(1)
              + " : "
              + rs.getString(2)
              + "\n"
              );
            }
            else {
              break;
            }
          }
    
          rs.close();
          stmt.close();
        }
        catch (Exception e) {
          sb.append("Query: SQL Exception: " + e.toString());
        }
    
        return sb.toString();
      }
    
      public static void showQueryResult (Connection conn, String query) {
        System.out.print(doQuery(conn, query));
      }
    
      public static String getSessionInfoDetail (Connection conn, String prompt, String key) {
        return doQuery (
          conn
        , "select '" + prompt + "', sys_context('userenv', '" + key + "') from dual"
        );
      }
    
      public static String getSessionInfo (Connection conn) {
        return getSessionInfoDetail(conn, "Session User", "session_user")
        + getSessionInfoDetail(conn, "Proxy User", "proxy_user")
        + getSessionInfoDetail(conn, "External Name", "external_name")
        + getSessionInfoDetail(conn, "Enterprise Identity", "enterprise_identity")
        + getSessionInfoDetail(conn, "Session ID", "sessionid")
        + getSessionInfoDetail(conn, "SID", "sid")
        ;
      }
    
      public static void showSessionInfo (Connection conn) {
        System.out.print(getSessionInfo(conn));
      }
    
      public static void closeConnection (Connection conn) {
        if (conn != null) {
          try {
            conn.close();
          } catch (Exception ignored) {}
        }
      }
    
      public static void closeConnectionPool (OracleOCIConnectionPool cp) {
        if (cp != null) {
          try {
            cp.close();
          } catch (Exception ignored) {}
        }
      }
    }
    

  7. Create a class called proxyTest:
    package myapp;
    
    import java.sql.*;
    import java.util.Properties;
    import oracle.jdbc.pool.OracleOCIConnectionPool;
    
    public class proxyTest
    {
      public static void main(String[] args)
      {
        OracleOCIConnectionPool cp = null;
        Connection conn = null;
    
        try {
          cp = new OracleOCIConnectionPool();
          cp.setURL (
            "jdbc:oracle:oci:@"
          + "(DESCRIPTION="
          + "(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))"
          + "(CONNECT_DATA=(SERVICE_NAME=orcl.disney.com))"
          + ")"
          );
          cp.setUser("myapp_pool");
          cp.setPassword("oracle4");
          Properties prop = new Properties();
          prop.put(OracleOCIConnectionPool.CONNPOOL_MIN_LIMIT, "3");
          prop.put(OracleOCIConnectionPool.CONNPOOL_MAX_LIMIT, "20");
          prop.put(OracleOCIConnectionPool.CONNPOOL_INCREMENT, "1");
          cp.setPoolConfig(prop);
          Properties userProp = new Properties();
          userProp.put (
            OracleOCIConnectionPool.PROXY_DISTINGUISHED_NAME
          , "cn=batman,cn=Users,dc=disney,dc=com"
          );
    
          conn = cp.getProxyConnection(OracleOCIConnectionPool.PROXYTYPE_DISTINGUISHED_NAME, userProp);
        }
        catch (Exception e) {
          System.out.println("Connection error: " + e);
        }
    
        dbhelper.showQueryResult(conn, "select id, name from mydata order by 1");
    
        dbhelper.showSessionInfo(conn);
    
        dbhelper.closeConnection(conn);
        dbhelper.closeConnectionPool(cp);
      }
    }
    

  8. Run the proxyTest class by pressing F11 – the result should be something like this:
    1 : first
    2 : second
    Session User : MYAPP_USER
    Proxy User : MYAPP_POOL
    External Name : cn=batman,cn=Users,dc=disney,dc=com
    Enterprise Identity : cn=batman,cn=Users,dc=disney,dc=com
    Session ID : 1459
    Database SID : 366
    

  9. Install a standalone OC4J – See "Installing Oracle Application Server 10g Release 3 (10.1.3) on Windows XP".

  10. Create a Managed Data Source called MyappDS that uses a Connection Pool called MyappCP that connects to the database as MYAPP_POOL

  11. Add a Deployment Profile for WAR called myapp to your JDeveloper project, with J2EE Web Context Root set to /myapp

  12. Create a servlet class called myservlet with servlet mapping /myservlet:
    package myapp;
    
    import java.sql.*;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    import java.util.Properties;
    
    import javax.naming.*;
    
    import javax.servlet.*;
    import javax.servlet.http.*;
    
    import javax.sql.DataSource;
    import oracle.jdbc.*;
    
    public class myservlet extends HttpServlet {
      private static final String CONTENT_TYPE = "text/html; charset=windows-1252";
    
      public void init(ServletConfig config) throws ServletException {
        super.init(config);
      }
    
      public void doGet(HttpServletRequest request, 
                        HttpServletResponse response) throws ServletException, IOException {
        response.setContentType(CONTENT_TYPE);
        PrintWriter out = response.getWriter();
    
        out.println("<html><head><title>myservlet</title></head><body>");
    
        DataSource ds = null;
        OracleConnection conn = null;
    
        try {
          InitialContext ic = new InitialContext();
          ds = (DataSource)ic.lookup("jdbc/MyappDS");
          conn = (OracleConnection)ds.getConnection();
          Properties prop = new Properties();
          prop.put (
            OracleConnection.PROXY_DISTINGUISHED_NAME
          , "cn=batman,cn=Users,dc=disney,dc=com"
          );
          conn.openProxySession(OracleConnection.PROXYTYPE_DISTINGUISHED_NAME, prop);
        }
        catch (Exception e) {
          out.println(e.toString());
        }
    
        out.print (
          "<pre>"
        + dbhelper.doQuery(conn, "select id, name from mydata order by 1")
        + dbhelper.getSessionInfo(conn)
        + "</pre>"
        );
    
        try {
          conn.close(OracleConnection.PROXY_SESSION);
        }
        catch (Exception e) {
          out.println(e.toString());
        }
    
        dbhelper.closeConnection(conn);
    
        out.println("</body></html>");
        out.close();
      }
    }
    

  13. Right-click on the Deployment Profile (myapp.deploy) and select Deploy to EAR file (or use the wizard to create a connection and deploy directly to OC4J)

  14. Deploy myapp.ear to your OC4J installation

  15. Go to http://localhost:8888/myapp/myservlet – the output should be similar to that of the proxyTest class (see above)

Auditing

  1. Enable database auditing, setting the audit_trail paramter to DB_EXTENDED
  2. Enable auditing of queries on the MYDATA table in the MYAPP schema:
    connect sys as sysdba
    audit select on myapp.mydata by access;
    
  3. Query the MYDATA table, either by logging in as batman using SQL*Plus, or by running the servlet described above
  4. Query the audit trail:
    connect sys as sysdba
    
    select
    	sessionid
    ,	entryid
    ,	ntimestamp#
    ,	comment$text
    ,	sqltext
    ,	returncode
    from
    	aud$
    where
    	userid = 'MYAPP_USER'
    order by
    	1, 2
    /
    
  5. If you later would like to disable auditing of queries on the MYDATA table in the MYAPP schema, then use this command:
    connect sys as sysdba
    noaudit select on myapp.mydata;