Struts, JAAS, Tomcat: getting acquainted (part 2)

25

In Struts, JAAS, Tomcat: getting acquainted (part 1) it was discussed how to employ JAAS authentication for custom and/or legacy realms under Tomcat. As authorization was left unaddressed, we herewith fullfil our promise to treat it later.

JAAS authorization

As Dan Moore already mentions in his own article titled “Using JAAS for Authorization and Authentication“, extending the Struts ActionServlet for enforcing security policies is far from ideal: only actions can be protected, you may become Struts-release dependent and even worse, the Struts ActionServlet may disappear in future releases alltogether.

That’s why we have choosen to use a servlet filter to implement JAAS-based authorization. The recipe is actually quite straightforward and can be summarized as follows:

  • Create a URL permission class (when dealing with web applications)
  • Create (i.e. configure + implement) an authorization filter
  • Create a policy file and make its location known to the JVM

The URL permission class

When dealing with web applications, we need to be able to grant or deny permissions, given a URL and subject (more precisely, a role that is retrieved via the Subject object) . This is explained in Section 3.1 of Dan Moore’s article, but basically boils down to the fact that their is no standard provision for URL-based resources.

For the time being, we took over the code of the URLPermission class (including the associated URLPermissionFactory) from the included example unaltered, although it should preferarably be generalized as is suggested by the author.

The authorization filter

We have implemented an AuthorizationFilter class. Note that when the subject has authenticated, his/her Subject should be present in the HTTP session, as was shown in part 1. The code is inspired on the example code from Dan Moore’s article (where he overwrites the process() method of the ActionServlet):

public void doFilter(ServletRequest req, ServletResponse response, FilterChain chain)
    throws IOException, ServletException
{
    HttpServletRequest request = (HttpServletRequest) req;
    HttpSession session = request.getSession();
    Subject subject = (Subject)
        session.getAttribute(Auth.SUBJECT_SESSION_KEY);

    String loginPage = request.getContextPath()+"/logon.do";
    String pageReq = request.getRequestURI();
    Permission permission =
        PermissionFactory.getInstance().getPermission(pageReq);

    if (subject == null &&
        (! request.getRequestURI().equals(loginPage)))
    {
        // subject cannot be found and person wants
        // to go to a page that is not the login page
        log.error("Unknown subject -> go to login page!");
        request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request, response);
    }
    else if (subject == null &&
         request.getRequestURI().equals(loginPage))
    {
        // subject cannot be found and person wants
        // to go to login page -> go ahead
        chain.doFilter(request, response);
    }
    else
    {
       if ( ! AuthorizationUtils.permitted(subject, permission) )
       {
           // subject is not permitted; redirect to error page
           log.error("Subject not permitted -> go to error page");
          request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request, response);
       }
       else
       {
           log.debug("Subject has permission -> continue...");
           chain.doFilter(request, response);
       }
    }
}

The code should speak for itself, only if the subject has authenticated successfully and according to the configured policy he/she has a role that permits the subject to visit this URL, the filter chain continues the normal request/response cycle, otherwise the user is either redirected to the login page (in case he/she did not authenticate yet) or an error page. Note that this error page should preferrably have been an authorization specific error page.

The actual authorization is done with the AuthorizationUtils class, which could again be incorporated from the example code unaltered.

In the web.xml deployment descriptor, we can decide when to invoke this authorization filter, e.g. only for *.do-based URLs, only for *.jsp-based files, for both, etc.

Example:

<filter>
    <filter-name>AuthorizationFilter</filter-name>
    <filter-class>nl.amis.hours.lifecycle.AuthorizationFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>AuthorizationFilter</filter-name>
    <url-pattern>*.do</url-pattern>
</filter-mapping>
</pre>

<h4>Configuring a policy</h4>

This is basically a (short) two-step process: make your policy configuration file known to the JVM and formulate the policy for your web application.

We tell the JVM to locate our policy file by adding the following entry to the <code>$JAVA_HOME/jre/lib/security/java.security</code> file:

<pre>
auth.policy.provider=com.sun.security.auth.PolicyFile
auth.policy.url.1=file:${java.home}/lib/security/AmisHours.policy

Note that this may be configured on the command line as well.

Finally, we set up a draft AmisHours.policy policy file:

grant  Principal nl.amis.hours.auth.AmisHoursPrincipal "ONTWIKKELAAR"
{
   permission nl.amis.hours.auth.URLPermission "/AmisHours/sheet.do";
};

grant Principal * *
{
   permission nl.amis.hours.auth.URLPermission "/AmisHours/logoff.do";
   permission nl.amis.hours.auth.URLPermission "/AmisHours/logon.do";
};

Subjects who are in the role of “ONTWIKKELAAR” (Dutch for developer) can now view their time sheets, whereas e.g. project managers cannot. This doesn’t make any sense, of course, but was merely meant to test if the policy file had the intended effect, which in fact it did :-)

Concluding remarks/thoughts

After having coded the authentication with JAAS, I got some doubts on the benefits of using JAAS over e.g. coding the authentication logic completely yourself, including the communication with the legacy realm (private communication).

But now that authorization is in place, I’m very charmed by the fact that we can now change the policy without having to change anything in the code nor in the deployment descriptor.

Considering the fact that many application servers offer JAAS-based authentication & authorization capabilities by efault (JBoss) and given the fact that JAAS is the standard API for Java security, I am much more confident that JAAS can be a sound choice when providing security for both Java stand-alone and web-based applications.

Share.

About Author

25 Comments

  1. Hi,
    I am also facing the same problem while reading the ApplicationResources.properties.

    It is working fine in Windows Environment,But it is not working on Linux Environment.Please help me out from this.

    Thanks in Advance,
    Narasimha

  2. hi all,
    the jGuard project (http://jguard.sourceforge.net) has published a new release(0.65).
    some of the new features in this release:
    - dynamically manage roles and permissions through a webapp
    - configuration is easier
    - logging system has been added
    - new database implementations has been added (DB2, MS SQL Server)

    jGuard provides an easy JAAS integration in j2ee environment.
    enjoy!

    Charles(jGuard team).

  3. For Zeger, please see above my reply in 19 regarding the article I found for per-field permission.

  4. Zeger Hendrikse on

    Sorry to all, but due to a mistake in my e-mail address in my profile, I wasn’t kept up to date on the comments of my own post. Quite a lot now, I must say :-)

    As far as JGuard is concerned, if I would start another project, I would definitely take the effort to use it. At that time, I was motivated to learn JAAS, but now that I’m acquainted on a baisc level, it is indeed better to use an existing solution (like JGuard), than to reinvent the wheel.

    To John, comment 21: As you may have concluded, this was a study project for me, so I was relatively new on the subject. Consequently, I’m afraid I wouldn’t know the answer to your question on per-field permissions.

  5. Thanks for your tips. But Hibernate has a reputation of slower performance that blcks us away. For instance level security seems to me it still in record row level, which means users may do either modifying or viewing on all fields of the whole row record. What I looked is for whole record row, every one can view its fields, but some user can edit some fields(not all fields) within that record row, admin user can edit all fields. Am I right?

  6. Comment from Thijs: Security: Declarative permissions using JAAS and Interceptors http://www.hibernate.org/140.html might also be usefull,
    They describe an approach for declarative security using objectids in a database.

    id | permission | action | classname | principal | oid
    ----+---------------------------+--------+-----------+-----------+-----
    1 | HibernateClassPermission | * | * | bob |
    2 | HibernateObjectPermission | load | User | alice | 47

    Thanks for your link, might also come in handy.
    I currently use a security filter plus ideas from the instance-level security article, but adapted to a database and with nested groups.

  7. I got a good one in http://www-106.ibm.com/developerworks/library/wa-appsec “web app security using Structs,servlet filters, and custom taglibs”(02 Sep 2004 Swaminathan Radhakrishnan) which introduced page accessing level security and attribute-level security which are exactly what I am looking for.

    This is the only one I found to have talked about the attribute/field level security sofar. Most of the articles are talking page accessing level even like the first link “Instance-level”, because many authors did not create the enterprise application with field/attribute level security. But the above one I just found looks very good
    which I mean he kept application performance in mind.

  8. Thanks a lot, Thijs. I will study it and if get some insight, I will report back here.

  9. All these authorization is related to URL accessing. But I have a question of authorization that user access and view url of some page containing serveral fields in a record row, but the security was configurably set up to allow that user to update few fields, not all fields on that record row. E.G., for employee record row:
    employeeFirstName EmployeeLatName, EmployeePhoneNumber, EmploeeSalary, EmployeeDepartmentNo

    The regular user can only edit his first/last names, phone number, but cannot edit salary, deptno. The admin user can edit all the fields. Also, the fields which allow the regular users to edit are configurable on a form by the admin user. So how to design these kind of security. Our current web application was designed by Oracle forms/reports 6i, every thing works fine, but we decided to overhaul it into J2EE +Struts web application. Any guru shed some light please. Thanks

  10. Hi Karthic – I am having the same problem (java.security.AccessControlException: access denied).
    I made the changes you had suggested, but I am still getting this error.
    I know my policy file is being read. I am using oc4j (Oracle App Srv 9.0.4) to server up the
    app, is there anything that needs to be configured differently? If you have any ideas please send them along.Mike
    needs to be setup differently with

  11. Hi Mei,
    I had the same problem, and i solved it following these point:

    1) Dan moore has instructions( readme) written pertaining to UNIX/LINUX environment and when i configured it for Windows i got this problem ..it requires some change

    2) place the com dir inside web-inf/classes {instead of copying it under WEB-INF as mentioned in readme}

    3) replace the LogonAction.class inside classes\org\apache\struts\webapp\example with the one by Dan… ( don’t copy the complete dir structure)

    4) in tagish.login change

    com.tagish.auth.FileLogin required debug=true pwdFile=”/usr/local/java/jre/lib/security/passwd” {this is for linux)

    to windows path Ex :

    com.tagish.auth.FileLogin required debug=true pwdFile=”C:/j2sdk1.4.2_01/jre/lib/security/passwd”

    that’s it and this works cool ..in case u need any help [ keyan_z@yahoo.com ]

    reg Karthic

  12. Hi Zeger,
    Don’t know if you are still watch this post. When I excecute my code
    if ( ! AuthorizationUtils.permitted(subject, permission) )
    {
    …..
    }
    else
    {
    ….
    }

    I called method AuthorizationUtils.permitted(subject, permission),
    in this method, but I always get the exception in code “sm.checkpermission(p);” Always deny the access.

    I have specified my policy file in java.security file correctly, and I know the policy file has been reached,
    but don’t is there any possible reason cause this problem.

    Could you send me an email at yyq99@yahoo.com

    Thanks!

    Mei

  13. Zeger Hendrikse on

    Dear Mei,

    Recently I had the same problem as you mentioned: it was just a case of not having included the ApplicationResources.properties in my WAR file (in the WEB-INF/classes dir) …

  14. Zeger Hendrikse on

    Unfortunately, I reworked the example right away to use servlet filters instead of extending the action servlet. Consequently, I did not encounter this problem myself, nor could I think of anything right now that might cause it.

  15. Thanks for reply. This still doesn’t work. My question is if the problem is , why it works when I
    use org.apache.struts.action.ActionServlet. The problem only happens when I choose to use com.xor.auth.ActionServlet?
    Do I need to make some changes in com.xor.auth.ActionServlet?

  16. Zeger Hendrikse on

    Your “problem” isn’t related to JAAS, but to a Struts configuration issue. Struts cannot find the message resource bundle.

    Try to add something like


    <init-param>
    <param-name>application</param-name>
    <param-value>ApplicationResources</param-value>
    </init-param>

    to the init param section of the ActionServlet, where you strip the .properties part from the name of your message resource bundle.

    Hope this helps!

  17. Zeger Hendrikse,
    This article realy helps me to under stand JAAS a lot. Since I am still very new to JAVA security,
    I still have the problem to run Dan Moore’s Example. When I change the “org.apache.struts.action.ActionServlet ”
    to “com.xor.auth.ActionServlet” in web.xml. I cannot run the index.jsp page, I got the error like this:
    Cannot find message resources under key org.apache.struts.action.MESSAGE

    How can I fix it? I have checked the struts-config.xml file, it looks exaclty like it should be according to Dan Moore’s instruction


    <servlet>
    <servlet -name>action</servlet>
    <servlet -class>
    // org.apache.struts.action.ActionServlet
    com.xor.auth.ActionServlet
    </servlet>
    <init -param>
    <param -name>config</param>
    <param -value>/WEB-INF/struts-config.xml, /WEB-INF/struts-config-registration.xml</param>
    </init>
    <load -on-startup>1</load>
    </servlet>

    Thanks!

  18. hi all,
    by reading this day your comments, it seems you are in the same way before i start the jGuard project.
    ” I did stumble upon jGuard, but the 15 pages configuration scared me off at that time.”
    => it’s right that the actual jGuard documentation is regrouped in 1 BIG page.
    one item on the top of the jGuard’s TODO list, is to write a quick startup guide, without the optional features. the complete documentation should be divided into separate sections too.
    this item will be done near the upcoming 0.63 release.

    so, my main comment(objective?) about your JAAS article, is that you should evaluate jGuard deeper, to not reinvent ….not the wheel, but jGuard (not so useful…. ;p ).
    i hope jGuard will fullfills your requirements. your comments about this project are welcomed too!
    and if your motivated, you can join the project.

    sincerly yours,

    Charles (jGuard team).

  19. Zeger Hendrikse on

    Of course, you don’t want your plain passwords to be sent over the wire.

    Configuring the SSL connector for Tomcat (see e.g. Tomcat SSL Howto), you can simply add something like the following in your web.xml deployment descriptor:


    <security-constraint>
    <web-resource-collection>
    <web-resource-name>AmisHours</web-resource-name>
    <description>
    (SSL) security constraint for
    resources in the AmisHours application
    </description>
    <url-pattern>/logon.jsp</url-pattern>
    <url-pattern>*.do</url-pattern>
    <http-method>POST</http-method>
    <http-method>GET</http-method>
    </web-resource-collection>
    <user-data-constraint>
    <description>SSL required</description>
    <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
    </security-constraint>

  20. Zeger Hendrikse on

    Leon,

    Indeed, in the way it was described here the locations of both the login configuration file (AmisHours.login) and the policy file (AmisHours.policy) were centrally managed (in java.security).

    The alternative is to provision these locations using command line parameters to the JVM.

  21. Zeger Hendrikse on

    No, I didn’t know about the Security Filter project. Thanks for the reference, I’ll check it out as well.

    I did stumble upon jGuard, but the 15 pages configuration scared me off at that time. However, now that I’m much more acquainted with Java security, I would be much more willing to look at such alternatives.

    Part of the intention of our project is to gather and disseminate knowledge, which was an additional motivation to start with plain JAAS.

    At this stage it will be interesting indeed to compare “our solution” to the ones offered by Security Filter and jGuard.

  22. Leon van Tegelen on

    Good stuff! I like the idea of a true Authorization component. One (small) worry I have is (re)deployment. Configuration of the security componen
    is de-coupled from the application source. On deployment (or for example jre upgrade) is there a way to check whether security is setup correctly

    Any ideas on how portable this approach is towards other app servers (for example OC4J). As JAAS is a standard I would expect it should be