Sunday, June 28, 2015

Weblogic J2EE security using web.xml and weblogic.xml (Quick scenarios)

In a J2EE project we can use web.xml and weblogic.xml to secure application.
To secure a J2EE application you need to perform following tasks

1. Create a login page
2. Secure resources (urls) by application roles
3. Map application roles with LDAP groups



In this blog I will be creating a J2EE application in jdeveloper and secure it. 
Let say I have a bare minimal initial application with only web.xml as shown below.



[NOTE: JDEV creates JSF/EJB related artefacts by default when you create a J2EE project but I have deleted them and removed their libraries]




First thing we need to do is create a login page. In J2EE we can have 3 kinds of login configuration.

a. Use Http Basic login: In this case browser provides in built login challenge (username/password). This is good for quick testing but may not be suitable for production. To enable this kind of login, you just need to add following lines in your code.
<login-config>
  <auth-method>BASIC</auth-method>
</login-config>


b. Use SSO: If Single Sign On is configured we can use below entry in webl.xml.
<login-config>
  <auth-method>CLIENT-CERT</auth-method>
</login-config>

c. Use Custom Login page: If you want to have your own login page you can configure web.xml as
<login-config>
  <auth-method>FORM</auth-method>
  <form-login-config>
    <form-login-page>/pages/Login.html</form-login-page>
    <form-error-page>/pages/Error.html</form-error-page>
  </form-login-config>
</login-config>

We will create a custom login page. 

Login.html (created under public_html/pages folder) looks like
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=windows-1252"></meta>
    <title>Login</title>
  </head>
  <body>
  <form method="POST" action="j_security_check">
  User Name: <input type="text" name="j_username"></input>
  Password: <input type="password" name="j_password"></input>
  <Input type="Submit" value="Login"></input>
  </form>
  </body>
</html>


Effectively you just need to call j_security_check with j_username and j_password value.
Error page could have below code
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=windows-1252"></meta>
    <title>Login</title>
  </head>
  <body>
  <p style="color: red; font-weight: bold">Error: Invalid username or password </p>
  <form method="POST" action="j_security_check">
  User Name: <input type="text" name="j_username"></input>
  Password: <input type="password" name="j_password"></input>
  <Input type="Submit" value="Login"></input>
  </form>
  </body>

</html>

With that our login module is set. Now lets secure our resources (pages)



Now before setting our security in web.xml I would like to comment on application role and enterprise role (group).
There are two kinds of roles, one is Application role and other is Enterprise role (aka group). In our J2EE app we should define application role and use them. For example if few pages are build for HR, we can define an application role as 'HRAppRole'. In our coding we should only refer this role. We should secure all our pages using this role only.

User/Password and their actual roles are maintained in LDAP directories. These roles are called Enterprise role or group. We should not refer these roles (group) in our application. Instead we should map Application role and Enterprise role (group) in weblogic.xml file.

Advantage of this approach is that code is not dependent of LDAP roles. If tomorrow we need to change LDAP group name, impact on code will be minimal. We just need to change mapping. We can also deploy same code against different LDAP. With deployment plan we can even change mapping of weblogic.xml at deployment time.



Let say we have application role as 'HRAppRole' and enterprise role as 'HR'. We need to have following entries for these roles
  1. Define HRAppRole in web.xml
            <security-role>
               <role-name>HRAppRole</role-name>
           </security-role>


        2. Define mapping of HRAppRole and HR in weblogic.xml

    <security-role-assignment>
         <role-name>HRAppRole</role-name>
         <principal-name>HR</principal-name>
     </security-role-assignment>

We have another scenario of accessing a page by all valid users. For that also we need to define an application role 'valid-users' and map it with group 'users' as
web.xml:
          <security-role>
               <role-name>valid-users</role-name>
           </security-role>

weblogic.xml:
        <security-role-assignment> 
            <role-name>valid-users</role-name>  
            <principal-name>users</principal-name>  
        </security-role-assignment>  
      
With that knowledge let us try to secure our pages.




    Next step is to create few pages and set security for them. Effectively there could be following possible scenarios.
    • Pages, which are accessible by everyone. No login should be needed for such pages. Let us have a PublicPage.html in this category.
    • Pages, which are accessible by valid users. Such pages needs login but user need not to have any specific role to access such pages. Only valid username/password is good enough. Let us have AllValidUserPage.html for this category.
    <security-constraint>
          <web-resource-collection>
            <web-resource-name>AllValidUserPage</web-resource-name>
            <url-pattern>pages/AllValidUserPage.html</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
         </web-resource-collection>
         <!-- you can repeat web-resource-collection tag -->
         <auth-constraint>
            <role-name>valid-users</role-name>
         </auth-constraint>
      </security-constraint>
    • Pages, which are accessible by users with specific role. For example only HR can access such pages. Let us have a RoleSpecificPage.html for this category.

      <security-constraint>
         <web-resource-collection>
             <web-resource-name>RoleSpecificPage</web-resource-name>
             <url-pattern>pages/RoleSpecificPage.html</url-pattern>
             <http-method>GET</http-method>
             <http-method>POST</http-method>
          </web-resource-collection>
          <auth-constraint>
              <role-name>HRAppRole</role-name>
          </auth-constraint>
       </security-constraint>
    • Page, can be accessed by anybody but few components (like button etc) should only be visible if user has a specific role
    For this usecase you need to have application role defined in web.xml and corresponding mapping in weblogic.xml only. You don't need <security-constraint> tag in web.xml. You need to secure component by using request.isUserInRole("HRAppRole") expression. For example if I have jsp I can put below lines to hide a button
    <% if( request.isUserInRole("HRAppRole")) { %>
    <a href="#" onclick="ShowEditPopup()"> Link only for HR</a>
    <% } %>
                          
    • Pages, which can not be accessed by anybody. Let us have RestrictedPage.html for this category


        <security-constraint>
          <display-name>excluded</display-name>
          <web-resource-collection>
              <web-resource-name>Restricted Pages</web-resource-name>
              <url-pattern>pages/RestrictedPage.html</url-pattern>
          </web-resource-collection>  
       <auth-constraint /> <!--blank auth-constraint -->
    </security-constraint>


    Code wise I do not have anything special in above mentioned html files. Just hard coded text to describe that page.


    For all above scenarios, effectively my web.xml looks like
    <?xml version = '1.0' encoding = 'windows-1252'?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
             version="2.5" xmlns="http://java.sun.com/xml/ns/javaee">
      <security-constraint>
        <web-resource-collection>
          <web-resource-name>AllValidUserPage</web-resource-name>
          <url-pattern>pages/AllValidUserPage.html</url-pattern>
          <http-method>GET</http-method>
          <http-method>POST</http-method>
        </web-resource-collection>
        <!-- you can repeat web-resource-collection tag -->
        <auth-constraint>
          <role-name>valid-users</role-name>
        </auth-constraint>
      </security-constraint>
      <security-constraint>
        <web-resource-collection>
          <web-resource-name>RoleSpecificPage</web-resource-name>
          <url-pattern>pages/RoleSpecificPage.html</url-pattern>
          <http-method>GET</http-method>
          <http-method>POST</http-method>
        </web-resource-collection>
        <auth-constraint>
          <role-name>HRAppRole</role-name>
        </auth-constraint>
      </security-constraint>
      <security-constraint>
        <display-name>excluded</display-name>
        <web-resource-collection>
          <web-resource-name>Restricted Pages</web-resource-name>
          <url-pattern>pages/RestrictedPage.html</url-pattern>
        </web-resource-collection>
        <auth-constraint/>
      </security-constraint>
      <security-role>
        <role-name>HRAppRole</role-name>
      </security-role>
      <security-role>
        <role-name>valid-users</role-name>
      </security-role>
      <login-config>
        <auth-method>FORM</auth-method>
        <form-login-config>
          <form-login-page>/pages/Login.html</form-login-page>
          <form-error-page>/pages/Error.html</form-error-page>
        </form-login-config>
      </login-config>
    </web-app>

     weblogic.xml looks like
    <?xml version = '1.0' encoding = 'windows-1252'?>
    <weblogic-web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                      xsi:schemaLocation="http://www.bea.com/ns/weblogic/weblogic-web-app http://www.bea.com/ns/weblogic/weblogic-web-app/1.0/weblogic-web-app.xsd"
                      xmlns="http://www.bea.com/ns/weblogic/weblogic-web-app">
         <security-role-assignment>
              <role-name>HRAppRole</role-name>
              <principal-name>HR</principal-name>
         </security-role-assignment>
         <security-role-assignment>
              <role-name>valid-users</role-name>
              <principal-name>users</principal-name>
         </security-role-assignment>
    </weblogic-web-app>

    NOT TIME FOR TESTING:

    Before that we need to create few test users and assign them groups in LDAP. We will use embedded ldap of weblogic. Follow these steps 

    1. Start weblogic server and login into console.
    2. Navigate to Security Realm > myrealm > Users and Groups
    3. Select Group tab and click New to create new group. Create a group with name HR

    4. Create two test users, userA with HR access and userB with no group assigned.


    Now type in following urls

    URL Result
    http://127.0.0.1:7101//pages/PublicPage.html NO login screen appears and user can directly view it.
    http://127.0.0.1:7101//pages/AllValidUserPage.html Login screen appears and both userA/userB can view it. Wrong username/password takes us to error page.
    http://127.0.0.1:7101//pages/RoleSpecificPage.html Login screen appears and but only userA can view it. Wrong username/password takes us to error page.
    http://127.0.0.1:7101//pages/RestrictedPage.html Login screen appears and neither userA nor userB can access it. Wrong username/password takes us to error page.

    Thats it. Key points that we got are
    a. How to setup Login pages (web.xml and Login.html)
    b. What is application role and enterprise group
    c. How to defined an application role in web.xml
    d. How to map it with enterprise group in weblogic.xml
    e. A specific group 'users' is assigned by default to all valid users.
    f. How to secure a page
           which needs to be accessed by all valid users
           which needs to be accessed by only users of certain group
           which can not be accessed by anybody