/* * Policy.java * * Created on 2008-07-08 07:57 */ package com.systemtier.jacc; import com.sun.enterprise.security.auth.login.PasswordCredential; import com.sun.enterprise.security.provider.PolicyWrapper; import java.io.IOException; import java.security.AccessController; import java.security.Permission; import java.security.ProtectionDomain; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Hashtable; import java.util.Properties; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.naming.Context; import javax.naming.InitialContext; import javax.security.auth.Subject; import javax.sql.DataSource; /** * Represents application server security policy. Only permissions instances of * com.systemtier.jacc.BasicPermission are checked here. Checking of other permissions are passed to * standard GlassFish policy implementation. * * @author Aleksandras Novikovas * @author System Tier * @version 1.0 */ public class Policy extends PolicyWrapper { /** * Holds full class name. Mostly used for logging. */ private static final String CLASS_NAME = Policy.class.getName (); /** * The logger object. */ private static final Logger log = Logger.getLogger (CLASS_NAME); /** * Holds JDBC resource name to access security database. */ public static String JDBC_RESOURCE_NAME = "jdbc/authPool"; /** * Query used to test BasicPermission for the user. */ // PostgreSql public static String PERMISSION_TEST_QUERY = "select exists (select 1 from sec.active_user_list au join sec.group_user gu on gu.user_name = au.user_name join sec.group_permission gp on gu.group_name = gp.group_name where au.user_name = ? and gp.permission_name = ?)"; // MS SQL // public static String PERMISSION_TEST_QUERY = "select case when exists (select 1 from sec.active_user_list au join sec.group_user gu on gu.user_name = au.user_name join sec.group_permission gp on gu.group_name = gp.group_name where au.user_name = ? and gp.permission_name = ?) then 1 else 0 end"; /** * Holds cache for tested permissions. */ private Hashtable> cache; /** * Creates a new instance of Policy. */ public Policy () { super (); cache = new Hashtable> (); Properties settings = new Properties (); try { if (log.isLoggable (Level.FINEST)) { log.finest ("Reading settings file."); } settings.load (getClass ().getClassLoader ().getResourceAsStream ("com/systemtier/jacc/settings.properties")); p = settings.getProperty ("JDBC_RESOURCE_NAME"); if (p != null) { JDBC_RESOURCE_NAME = p; if (log.isLoggable (Level.FINEST)) { log.finest ("JDBC_RESOURCE_NAME=" + JDBC_RESOURCE_NAME); } } p = settings.getProperty ("PERMISSION_TEST_QUERY"); if (p != null) { PERMISSION_TEST_QUERY = p; if (log.isLoggable (Level.FINEST)) { log.finest ("PERMISSION_TEST_QUERY=" + PERMISSION_TEST_QUERY); } } } catch (IOException ex) { if (log.isLoggable (Level.WARNING)) { log.log (Level.WARNING, "Settings for properties can not be openned. Using defaults."); } } if (log.isLoggable (Level.INFO)) { log.log (Level.INFO, CLASS_NAME + " instantiated."); } } @Override public void refresh () { if (log.isLoggable (Level.FINER)) { log.entering (CLASS_NAME, "refresh"); } cache.clear (); super.refresh (); if (log.isLoggable (Level.FINER)) { log.exiting (CLASS_NAME, "refresh"); } } /** * Evaluates the global policy for the permissions granted to the ProtectionDomain and tests whether * the permission is granted.

* Only com.systemtier.jacc.BasicPermission instances are checked in this method. Checking of other * permissions are passed to standard GlassFish policy implementation.

* Checking is done by querying database.

* * @param domain ProtectionDomain to test. * @param permission Permission object to be tested for implication. * * @return boolean true if permission is a proper subset of a permission granted to this * ProtectionDomain. */ @Override public boolean implies (ProtectionDomain domain, Permission permission) { if (permission instanceof BasicPermission) { if (log.isLoggable (Level.FINER)) { log.entering (CLASS_NAME, "implies", new Object [] {permission});//domain, permission } if (log.isLoggable (Level.FINEST)) { log.finest ("Permission is instanceof BasicPermission."); } String userName = getCurrentUserName (); if (userName != null) { if (log.isLoggable (Level.FINEST)) { log.finest ("Retrieving permission test result from cache."); } // Checking cache before doing actual test of permission Hashtable cachedSubjectPermissions = cache.get (userName); if (cachedSubjectPermissions == null) { // Create permissions cache for subject. cachedSubjectPermissions = new Hashtable (); cache.put (userName, cachedSubjectPermissions); } // Retrieving cached permission test. Boolean cachedPermissionResult = cachedSubjectPermissions.get (permission); if (cachedPermissionResult == null) { Context ctx = null; DataSource ds = null; Connection con = null; PreparedStatement ps = null; ResultSet rs = null; try { if (log.isLoggable (Level.FINEST)) { log.finest ("Retrieving permission test result from database."); } ctx = new InitialContext (); ds = (DataSource) ctx.lookup (JDBC_RESOURCE_NAME); con = ds.getConnection (); ps = con.prepareStatement (PERMISSION_TEST_QUERY); ps.setString (1, userName); ps.setString (2, permission.getName ()); if (log.isLoggable (Level.FINEST)) { log.finest ("Executing database request to test permission."); } rs = ps.executeQuery (); boolean authorized = false; if (rs.next ()) authorized = rs.getBoolean (1); if (log.isLoggable (Level.FINEST)) { log.finest ("Caching permission test result."); } cachedSubjectPermissions.put (permission, new Boolean (authorized)); if (log.isLoggable (Level.FINER)) { log.exiting (CLASS_NAME, "implies", authorized?Boolean.TRUE:Boolean.FALSE); } return authorized; } catch (Exception ex) { log.log (Level.SEVERE, "Permission can not be tested.", ex); } finally { if (rs != null) { try { rs.close (); } catch (Exception ex) {} } if (ps != null) { try { ps.close (); } catch (Exception ex) {} } if (con != null) { try { con.close (); } catch (Exception ex) {} } if (ctx != null) { try { ctx.close (); } catch (Exception ex) {} } } } else { if (log.isLoggable (Level.FINEST)) { log.finest ("Permission test result found in cache."); } if (log.isLoggable (Level.FINER)) { log.exiting (CLASS_NAME, "implies", cachedPermissionResult); } return cachedPermissionResult.booleanValue (); } } if (log.isLoggable (Level.FINER)) { log.exiting (CLASS_NAME, "implies", Boolean.FALSE); } return false; } else { return super.implies (domain, permission); } } /** * Returns current subject's user name. Subject is taken from current calling context. * User name is extracted from first found private PasswordCredential. * Returns null if subject can not be retrieved or it does not contain private * PasswordCredential or user name in PasswordCredential is not set. * * @return String current user's name or null. */ public static String getCurrentUserName () { if (log.isLoggable (Level.FINER)) { log.entering (CLASS_NAME, "getCurrentUserName"); } String userName = null; Subject subject = Subject.getSubject (AccessController.getContext ()); if (subject != null) { if (log.isLoggable (Level.FINEST)) { log.finest ("Got subject: " + subject.toString ()); } Set privateCredentials = subject.getPrivateCredentials (Object.class); for (Object credential : privateCredentials) { if (credential instanceof PasswordCredential) { if (log.isLoggable (Level.FINEST)) { log.finest ("Got instance of PasswordCredential: " + credential.toString ()); } userName = ((PasswordCredential) credential).getUser (); break; } } } else { if (log.isLoggable (Level.FINEST)) { log.finest ("Subject not found in current context."); } } if ("".equals (userName)) userName = null; if (log.isLoggable (Level.FINER)) { log.exiting (CLASS_NAME, "getCurrentUserName", userName); } return userName; } }