Sunday, January 17, 2010

Using JAAS auhtorization to protect business objects

You will frequently a scenario in your application where different roles should be permitted access to different business objects.  
Consider an application where different types of users can save different types of content e.g. Audio users can save audio content; Text users can save text content only and similarly. Admin can save whatever it wants. You can apply these constraints in JAAS.


grant principal weblogic.security.principal.WLSGroupImpl "audioUser" 
{
 permission com.test.security.ResourcePermission "com.test.entity.audio.*", "create,read,update,delete";
}; 

grant principal weblogic.security.principal.WLSGroupImpl "textUser" 
{
 permission com.test.security.ResourcePermission "com.test.entity.text.*", "create,read,update,delete";
}; 

grant principal weblogic.security.principal.WLSGroupImpl "admin" 
{
 permission com.test.security.ResourcePermission "*", "create,read,update,delete";
}; 



Note:
1- I put all business objects permitted to audioUser in package com.test.entity.audio.* and similarly for textUser. I achieved these benefits using BasicPermission
2- I have used weblogic.security.principal.WLSGroupImpl for my role principals because i was using Weblogic authentication.
3- You can specify above constraints in java.policy. If you are using Weblogic you can also specify it in weblogic.policy but it depends on how you are staring weblogic server either:
-Djava.security.policy==%WL_HOME%\server\lib\weblogic.policy
OR
-Djava.security.policy=%WL_HOME%\server\lib\weblogic.policy
== enforces only weblogic.policy to be used, whereas = appends weblogic.policy to other policies specified in java.security.
4- To use JAAS authorization you need not enable Security Manager. And i would recommend not to do so as enabling it will also make weblogic code and other applications to pass through security checks and you may get all sorts of exceptions.

Usually code applies authorization checks like below:

if(System.getSecurityManager() != null){
 System.getSecurityManager().checkPermission(...);
}

So disabling security manager also disables authorization checks. Instead of above i would apply authorization directly using AccessController.

To authorize update operation you would:









AccessController.checkPermission(weblogic.security.Security.getCurrentSubject(), new ResourcePermission(this.getClass()
.getCanonicalName(), EnumSet.of(Action.update)));


Or better yet put above code of checking permission in Subject.doAs() so as to avoid protection domain checks even if security manager is enabled.




Now the code, you require just two classes to implement this. You do not need to place below classes in JAVA_HOME/lib/ext, you can put these in your project. This is the magic of UnresolvedPermission.










package com.test.security;


import java.security.BasicPermission;
import java.security.Permission;
import java.util.EnumSet;


public class ResourcePermission extends BasicPermission {
private EnumSet<Action> actionSet;


public ResourcePermission(String name, String actions) {
super(name == null ? "" : name);
actionSet = Action.getActionSet(actions);
}


public ResourcePermission(String name, EnumSet<Action> actionSet) {
super(name == null ? "" : name);
this.actionSet = actionSet;
}


@Override
public boolean equals(Object obj) {
if (obj != null && obj instanceof ResourcePermission) {
ResourcePermission other = (ResourcePermission) obj;
return super.equals(obj) && actionSet.equals(other.getActionSet());
}
return false;
}


@Override
public String getActions() {
return Action.toString(actionSet);
}


public EnumSet<Action> getActionSet() {
return actionSet;
}


@Override
public int hashCode() {
return actionSet.hashCode() + getName().hashCode();
}


@Override
public boolean implies(Permission permission) {
if (permission != null && permission instanceof ResourcePermission) {
ResourcePermission other = (ResourcePermission) permission;
return super.implies(permission)
&& actionSet.containsAll(other.getActionSet());
}
return false;
}
}


///////////////////////////////////////////////////////////////////////////////////////


package com.test.security;


import java.util.EnumSet;


public enum Action {
create, update, delete, read;


public static EnumSet<Action> getActionSet(String serializedActions) {
EnumSet<Action> actionSet = EnumSet.noneOf(Action.class);
if (serializedActions != null) {
String[] serializedActionsSet = serializedActions.split(",");
for (String serializedActionItem : serializedActionsSet) {
actionSet.add(Action.valueOf(serializedActionItem));
}
}
return actionSet;
}


public static String toString(EnumSet<Action> actionSet) {
StringBuilder actions = new StringBuilder();
for (Action action : actionSet) {
actions.append(action.toString() + ",");
}
if (actions.lastIndexOf(",") == actions.length() - 1) {
actions.replace(actions.length() - 1, actions.length() - 1, "");
}
return actions.toString();
}


}


Understanding Weblogic class loading



Yes! You need to understand class loading. I was also of the view that why do I need to go into the details of weblogic class loading, can’t I just develop and deploy j2ee applications on weblogic. And then you encounter following kind of problems
1-      Log4j is not working as expected, although you have configured it correctly.
2-      You get ClassCastException although you have included the library in your application.

I will explain the problems and solutions later, first class loading.

Class loading is a task of loading java classes and is performed by class loaders.  Class loaders are arranged in inheritance hierarchy this is called Delegation. Each class loader first requests super class loader to load requested class and if super fails it tries to load itself. In this way class loaders do not load classes already loaded by parent class loaders.

J2SE defines following levels of class loaders.  Please refer to figure 1.

1- 1-      Bootstrap class loader: is native code and its responsibility is to load JAVA_HOME/lib and JAVA_HOME/lib/ext.
2-  2-     System class loader: loads classes in CLASSPATH.

Weblogic replaces system class loader with its own version.
1-      Weblogic system class loader, loads weblogic classes.
2-      For each enterprise application (EAR) deployed on weblogic there are two instances of class loaders one for EJB and other for Web app as its child. For an EJB application only EJB Class loader is used and similarly for Web app. EJB Class loader is parent of Web class loader as typically web classes use EJBs. Note that there are separate instances of these class loaders for each deployed weblogic application, referred in docs as sibling class loaders. So, that classes loaded by one weblogic application are not visible to others. This enables weblogic applications to use different versions of single class / library.

If you create an enterprise application project, EJB Class loader loads classes/libraries of both EJB project and EAR project. Whereas Web class loader loads classes/libraries of web project only.  Suppose you have an enterprise project with following structure.

Enterprise Project
                Ear-Content
APP-INF
lib
ep_x1.jar
ep_x2.jar
Web Project
                Web-Content
Web-INF
lib
wp_x1.jar
ep_x1.jar
ej_x1.jar
ejbModule

Considering jars only, EJB Class loader will load ep_x1.jar, ep_x2.jar and ej_x1.jar. Web class loader will only load wp_x1.jar. And when it tries to load ep_x1.jar it will find out that its classes are already loaded by its parent class loader so it reuses.

Now let us add log4j support in your project. In order to avoid repetition you should add log4j.jar in Enterprise project’s APP-INF/lib folder and reference it from both Web and EJB projects. Do the same for all of your common libraries. What about log4j.properties? Where should we place the file? As you may know log4j automatically loads log4j.properties from CLASSPATH on startup (static loading). And in our current configuration log4j is loaded by EJB Class loader so we should place single properties file in EJB project.

EJB Project
log4j.properties
ejbModule


What if log4j is being used in weblogic e.g. someone placed it in {$DOMAIN_HOME}/lib folder? Since log4j is already loaded by weblogic system class loader, your project’s log4j.properties will not be statically loaded. Solution is to reload log4.jar but how? Here comes weblogic Filter class loader. In your enterprise project’s weblogic-application.xml add below lines

<wls:prefer-application-packages>
    <wls:package-name>org.apache.log4j.*</wls:package-name> 
</wls:prefer-application-packages>

Filter Class loader throws ClassNotFound exception on each of the classes configured as above, thus allowing child class loaders to themselves load classes. And this will solve our problem as EJB Class loader will load log4j.jar irrelevant of whether it is already loaded by parent class loaders and therefore allowing log4j to read our supplied log4j.properties.









Thursday, December 10, 2009

Eclipselink Oracle Lob



You always need special care to read/write lobs not only with JPA but also with JDBC.


In case of JPA you can have persistent lob fields e.g.


 @Lob
@Basic(fetch = FetchType.LAZY)
@Column(nullable = false)
private byte[] content;



Lazy fetch type is usually good, so that not to read lob unless it is required. 



If your entity is detached (almost always in j2ee/web) you have to query for the lob field

SELECT o.content FROM Message o where o.id  = ?
Query.getSingleResult()



In case of Oracle, correct way to read/write lobs varies with db version and driver version. Lobs of size less than 4K are handled normally but for larger sizes it can be an issue. In case of EclipseLink, you just need to set following properties in persistence.xml to read/write lobs > 4K. Behind the screen EclipseLink will first insert empty blob field and then update it to actual value.


<property name="eclipselink.target-database" value=" org.eclipse.persistence.platform.database.oracle.OraclePlatform "/>

<property name="eclipselink.target-server" value="WebLogic_10" />


Assuming you are using Weblogic 10g or higher, for other values refer to Eclipselink docs.


You can verify size of lob fields by following SQL query
select DBMS_LOB.GETLENGTH(content) from message where id = 3

Tuesday, December 1, 2009

SQL Sequence Cache - Missing ids

Carefully decide your sql sequence caching strategy especially in case of multiple connections accessing the table. We often use db as a storage for Producer Consumer scenario, like below


App1 ---(inserting)---> TableA <---(querying)--- App2 


TableA has a before-insert trigger which creates primary key using the sequence. 


Now for consumer application i.e. App2, I was using a query 
select * from TableA where id > :lastReadId
where :lastReadId is obviously the id which App2 accessed on last query. 


Run both apps and you'll see a problem of missing ids, i.e. some ids are missed during insertion and consequently data is not consumed completely. A peek will show that this problem arises when we use cached sequence, so change it no-cache and it'll work fine.





Monday, September 7, 2009

Polymorphism (remove ifs/switches)

When?
1- 1Behavior changes with state
Example:
switch(operation){
                case “register”
                                register();
                case “unregister”:
                                unregister();
                case “update”
                                update();
}
Instead create a hierarchy with operation as a base class and register, unregister and update etc as subclasses. Now the instantiation of appropriate subclass lies on dependency injector or factory method
2- 2- Same check throughout the code
Example:
if(!isProduction){
                logger.write(“something”)
}
Instead create a logger hierarchy with Logger as abstract class and NullLogger that does nothing.
So you do:
logger.write(“something”)
In this case the conditional checking required to instantiate a correct logger goes in logger factory or dependency injection.
Logger logger = LogFactory.getLogger();
getLogger(){
      if(isProduction) return new NullLogger();
else return new FileLogger();
}             
Benefits?
1-      1- Must for libraries so that others can extend and add their own behaviors
2-      2- Ease of testability, lesser code coverage tests.
3-      3Readability

Monday, August 31, 2009

Daemon Applications in J2EE


Prior to ejb 3.0 spec there was no such concept of container provided timer services. But now an ejb (stateless + mdb) can schedule itself. Moreover container provided timer services are reliable (can survive container crash) and serializable (we can save timer handle for later reference e.g. for cancellation).
But still we cannot right daemon ejbs! It could have been possible if timer services were not transaction aware so we could create timer in PostConstruct callback. But since timer services are transaction aware and PostConstruct callback is not, so we cannot do this. How to get away with it? Startup servlet is the solution, it will call our dameon ejb on first instance and from there we can schedule for regular intervals.
Other point that must be kept in mind while using ejb timer services is that they are not real time as per specs i.e. we cannot expect container to expire timer in millis. Programming model does not restrict it as interval unit is in millis but container does not provide any guarantees for real time. So, for this you are bound to use schedulers e.g. Quartz. I have seen two ways, to integrate quartz in j2ee
1-      Startup servlet approach as mentioned above. http://www.theserverside.com/news/thread.tss?thread_id=24896
2-       A resource adapter with Message beans as endpoints see JBoss for an implementation. This is really cool way of achieving this. http://www.jboss.org/ejb3/docs/tutorial/jca/inflow/quartz/quartz.html

Sunday, August 30, 2009

JAAS LDAP OC4J

We need a good LDAP tool; the best I found was LDAPSoft LDAP Admin Tool. It is limited trial version. It automatically connects to my network’s LDAP server if connected to LAN and gives a very good view active directory contents.
For website orion-web.xml
For ejb orion-ejb.xml
For application orion-application.xml
Orion-xxx.xml is OC4J propriety file and is not dictated by J2EE specs.
We need to create orion-application.xml using the information given by LDAPSoft.
<?xml version = '1.0' encoding = 'windows-1252'?>
<orion-application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:noNamespaceSchemaLocation="http://xmlns.oracle.com/oracleas/schema/orion-application-10_0.xsd">
 <!--jazn element specifies that I am using custom LDAP provider i.e. Active directory for JAAS. Other options for JAAS are db, file based, Oracle Internet Directory (oracle implementation of LDAP)-->
 <jazn provider="XML">
  <property name="custom.ldap.provider" value="true"></property>
 </jazn>
<!—Role that I will be using in the application. I gave it an app friendly name i.e. RAUser but in the end this app friendly role name must be mapped to a LDAP group whom authenticated users will be member. So, In order to define roles in the application I have to follow a three step process.
1- Define app friendly role name in web.xml under <security-role>
2- Specify authorization access of the role in web.xml under
<security-constraint>….<auth-constraint>. This step is optional.
3- Define mapping of app friendly roles names to backend (db, file based, LDAP) security provider. The first two steps are defined by J2EE specs but this step is specific is container. So, for OC4J I define it in orion-application.xml under <security-role-mapping>
   -->
 <security-role-mapping name="RAUser">
  <group name="VAS IT"></group>
 </security-role-mapping>
<!—- This section defines LDAP connectivity details. The content of this section are copied to system-jazn-data.xml after you deploy your application. All the settings under this section can be mentioned at deployment time using OracleAS Control but I took them to orion-application.xml so that I can reuse this file across projects. For this first time I deployed my application from OAS control but afterwards I copied the below section from system-jazn-data.xml-->
 <jazn-loginconfig>
  <application>
<!—application name that I will use when I deploy the application in OC4J.-->
   <name>RA-ldap</name>
   <login-modules>
    <login-module>
     <class>oracle.security.jazn.login.module.LDAPLoginModule</class>
     <control-flag>required</control-flag>
     <options>
      <option>
       <name>oracle.security.jaas.ldap.connect.pool.prefsize</name>
       <value>10</value>
      </option>
      <option>
       <name>oracle.security.jaas.ldap.connect.pool.initsize</name>
       <value>2</value>
      </option>
      <option>
       <name>oracle.security.jaas.ldap.connect.pool.timeout</name>
       <value>300000</value>
      </option>
      <option>
 <!--ObjectClass of user, for Active directory standard is user. I can verify this from LDAP tool by viewing any user properties-->      <name>oracle.security.jaas.ldap.user.object.class</name>
       <value>user</value>
      </option>
      <option>
       <name>oracle.security.jaas.ldap.provider.connect.pool</name>
       <value>true</value>
      </option>
      <option>
 <!--User credentials that I will use to connect to Active Directory server, for the time being I used my own. I can search and read Active Directory using my own credentials. But you should create credentials specifically for this app and set password expiry to never -->  <name>oracle.security.jaas.ldap.provider.credential</name>
       <value>{903}iezH/eKV6X1BkCbc/j+aKS+K</value>
      </option>
      <option>
       <name>oracle.security.jaas.ldap.provider.type</name>
       <value>Active Directory</value>
      </option>
      <option>
       <name>oracle.security.jaas.ldap.connect.pool.maxsize</name>
       <value>25</value>
      </option>
      <option>
<!--Active directory server info -->
       <name>oracle.security.jaas.ldap.provider.url</name>
       <value>ldap://172.18.70.33:389</value>
      </option>
      <option>
<!--Whether to search for role (IT) directly under oracle.security.jaas.ldap.role.searchbase or in the whole subtree that lies under it which is obviously expensive-->
       <name>oracle.security.jaas.ldap.role.searchscope</name>
       <value>onelevel</value>
      </option>
      <option>
<!--Whether to search for user directly under oracle.security.jaas.ldap.user.searchbase or search whole subtree-->
       <name>oracle.security.jaas.ldap.user.searchscope</name>
       <value>onelevel</value>
      </option>
      <option>
<!-- Search base for role (IT)-->
       <name>oracle.security.jaas.ldap.role.searchbase</name>
       <value>OU=Groups,OU=RASoft,DC=WT,DC=WI,DC=Pri</value>
      </option>
      <option>
<!-- Search base for user-->
       <name>oracle.security.jaas.ldap.user.searchbase</name>
       <value>OU=Executives,OU=IT,OU=Central,OU=RASoft,DC=WT,DC=WI,DC=Pri</value>
      </option>
      <option>
<!—Which LDAP attribute contains group name? It is cn by default for active diretcory-->       <name>oracle.security.jaas.ldap.role.name.attribute</name>
       <value>cn</value>
      </option>
      <option>
<!--ObjectClass of group/role which is group by default for Active Directory-->       <name>oracle.security.jaas.ldap.role.object.class</name>
       <value>group</value>
      </option>
      <option>
<!--Which LDAP attribute contains user name? It is sAMAccountName by default for active diretcory--> <name>oracle.security.jaas.ldap.user.name.attribute</name>
       <value>sAMAccountName</value>
      </option>
      <option>
<!—User credentials for connecting to active directory server-->
       <name>oracle.security.jaas.ldap.provider.user</name>
       <value>wt\struser</value>
      </option>
      <option>
<!--Whether user are direct member of role/group (IT)-->       <name>oracle.security.jaas.ldap.membership.searchscope</name>
       <value>direct</value>
      </option>
      <option>
<!--Which group/role attribute specifies membership? For Active Directory it is by default member-->      
       <name>oracle.security.jaas.ldap.member.attribute</name>
       <value>member</value>
      </option>
      <option>
       <name>oracle.security.jaas.ldap.lm.cache_enabled</name>
       <value>true</value>
      </option>
     </options>
    </login-module>
   </login-modules>
  </application>
 </jazn-loginconfig>
</orion-application>
Now I need to focus on web.xml where I will set appropriate configs to trigger form based authentication. Below is the related excerpt from web.xml.
<!--
    ISSUE:
    If welcom-file-list is enabled partial requests
    enter into redirect loop. e.g.
    http://localhost:8888/RA-ldap/
    or
    http://localhost:8888/RA-ldap/faces
    results in redirect to
    http://localhost:8888/RA-ldap/
    and continues forever.
    -->
<!--Note no slash(/) in start as is the case with other paths specified in web.xml because this is not a path but file name instead-->
    <!--
    <welcome-file-list>         
        <welcome-file>faces/verifyNic.jsp</welcome-file>
    </welcome-file-list>-->
    <security-constraint>
        <display-name>SecurityConstraint</display-name>
        <web-resource-collection>
            <web-resource-name>SecurePages</web-resource-name>
            <description></description>
            <url-pattern>/faces/verifyNic.jsp</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
            <http-method>HEAD</http-method>
            <http-method>PUT</http-method>
            <http-method>OPTIONS</http-method>
            <http-method>TRACE</http-method>
            <http-method>DELETE</http-method>
        </web-resource-collection>
        <auth-constraint>
            <description></description>
            <role-name>RAUser</role-name>
        </auth-constraint>
    </security-constraint>
    <login-config>
        <auth-method>FORM</auth-method>
        <form-login-config>
            <form-login-page>/faces/login.jsp</form-login-page>
            <form-error-page>/faces/login.jsp?login=Incorrect user name / password</form-error-page>
        </form-login-config>
    </login-config>
    <security-role>
     <role-name>RAUser</role-name>
    </security-role>
FORM BASED AUTHENTICATION
In form based auth the login page as specified by <form-login-page> should post two params j_username, j_password to j_security_check servlet. This is what J2EE specs define. The sequence of steps is:
1-    Users requests a protected resource such as in this case verifyNic.jsp
2-    AS determines whether user is already logged in by checking if its session exists on server.
3-    If not logged in AS redirects to <form-login-page>
4-    Login page gets user name and password and post it to j_security_check which authenticates and authorize user and redirects user to url requested in step 1.
To make redirection in step 4 work in OC4J I need to start the server with following java option:
-Doc4j.redirect=true
For stand alone I do it by starting sever like this
>java -Doc4j.redirect=true -jar
$ORACLE_STANDALONE_HOME\j2ee\home\oc4j.jar
And for OAS I need to put this parameter in server properties which can be accessed from OAS control or opmn.xml.
In OAS control click on instance then Administration and then server properties and add it under java options section.
But what if user comes direct to login page or user logs out and tries to login again? To which page will AS redirect user? OC4J sends user to no where and keeps j_security_check in url which displays resource not found (404) error in browser. Some app servers allow another parameter j_url or j_uri to j_security_check so that server can redirect, after user is authenticated, to the given url but this is not the case with OC4J. I was unable to use welcome-file-list feature of web.xml as it enters into redirect loop (for details see above web.xml excerpt).
Another issue with JSF is that I cannot define the form’s action target because it follows a post-back model. And if I use a simple JSP page for login, I am deprived of all JSF input validations and then I have to do it all myself.
So the solution to above two problems is login proxy page. Below is JSF navigation diagram of the project.

The action of login.jsp is
        FacesContext context = FacesContext.getCurrentInstance();
        Map request = context.getExternalContext().getRequestMap();
        request.put("username", txtUser.getValue());
        request.put("password", txtPassword.getValue());
        return "proxy";
The jsp source of proxy.jsp is
<body onload="document.forms[0].submit();document.forms[1].submit();">
 
  <form action="verifyNic.jsp" method="POST"></form>
  <form action="j_security_check" method="POST">
      <input type="hidden" name="j_username" value="${requestScope.username}"/>
      <input type="hidden" name="j_password" value="${requestScope.password}"/>
  </form>
Explanation of proxy.jsp:
First html form tells OC4J that after authentication succeeds redirect me to verifyNic.jsp.
Second form submits user supplied credentials on previous page to j_security_check.
The order of form submits is important.
Logout
        FacesContext context = FacesContext.getCurrentInstance();
        HttpSession session = (HttpSession)context.getExternalContext().getSession(false);
        session.invalidate();
Get logged in user name in EJB
    @Resource
    private SessionContext ctx;
……
ctx.getCallerPrincipal().getName()
Login error
<h:outputLabel value="#{param.login}" …
Displays whatever is in query string parameter login. E.g. for login.jsp?login=incorrect it will display incorrect.
Value attribute uses JSF Unified Expression Language (EL) and implicit object param.