Sunday, June 28, 2015

ADF Tip: PanelGroupLayout Avoid inner Divs [Any component to create single div]

Problem Description: At times I want only a single div in my html. PanelGroupLayout(vertical) can be used to create a div but then if I put any component in it, all will be surrounded by additional div. I don't want that. But there is no problem with PanelGroupLayout(Vertical). As name suggest vertical layout so it needs to surround inner components with a div. So the problem is to get an ADF component, which only generates a div thats it. No impact on inner component. I do not think there is any other simple component available in ADF, which can only generate a div. I would have been helpful but seems like there is none. All are complex components and even simplest panelgrouplayout add additional complexity.


Solution: I could not get a perfect solution for this but below approach helps me.

Surround inner components of PanelGroupLayout-Vertical with a af:group.
af:group does not generate any html.


Thats it.

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

    Thursday, June 4, 2015

    ADF: Smarter RichTextEditor (Default Styling)

    Problem Description: 


    ADF RichTextEditor (RTE) provides ability to style your content. Most of the time any content created using RTE needs to be displayed in some other page. We can definitely use OutputText (escape=false) to show exactly same content with same styling. But if you take customer's point of view, most of the time they know their content well but they don't know how to style it. What they want is content should get styled according to rest of the website automatically. For example let way I have a red box in my UI, which shows announcements. HR has edit button which launches a new popup with RTE , which allows him to create announcement text. As shown below




    Now if we see base page is in red background and we want white text on it. If I open RTE by default it provides me white background and black text. Even if I style it I can make its background-color Red but I can't set text-color to be white (Atleast I could not do that). Similarly there could be other things for example any <h1> tag should leave 20px margin at top and 10px at bottom. And it should be a default behavior because that is how rest of website behaves. We can't expect customer to go in source mode and put style="margin-top: 20px; margin-bottom: 10px". He should just be adding <h1> tag thats it.
    Basically what I am saying that there should be a mechanism to put default style for our html tags (background-color, text-color, ul, li, ol, h1-h6, table th etc.). Same style should be used on RTE and base page (where content actually needs to appear)


    Why RTE is not showing styles of base page

    If we try to see styles and components of RTE using chrome inspector (F12), we find that RTE has created an iframe with head/body. All our cotent is apearing below <body> tag. There is no css mentioned in head section, so all tags that we create here are taking styles directly from browser defaults. Because of that text-color is always black. Even you may find that few components are looking different in different browser based on browser specific settings. Definitely base page on which we are going to show content is having our application styles so they are different.




    Possible solution

    What we need is somehow all my tags should use same style on RTE and base page. Idea is to create a CSS, which will be applied on RTE and base page content. To apply a CSS on RTE we need to add css entry in head section of iframe-html.

    To achieve this I will be using javascript to get handle of iframe-head section and set a <link> tag to my custom css.
    Before that let us write our requirements
    1. RTE should look in red background
    2. Text-color automatically should become white
    3. If I put h1 tag it should automatically leave 20px at top and 10px at bottom
    4. RTE width should be adjusted as the width of base page section.


    Steps to be followed:


    1. Create a new CSS for your ADF application: 

    First thing we want to create a new CSS for our ADF application. We need to add trinidad-skins.xml file and modify entry of trinidad-config.xml file.
    You can go over blog http://sanjeev-technology.blogspot.in/2015/04/adf-do-you-know-oracle-provided-skins.html to see steps on how to create new css for your ADF application.

    My projects trinidad-skins.xml entry is
    <?xml version="1.0" encoding="ISO-8859-1"?>
    <!-- Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved. -->
    <skins xmlns="http://myfaces.apache.org/trinidad/skin">
    <skin>
        <id>sanstyle-v1.desktop</id>
        <family>sanStyle-v1</family>
        <extends>fusionFx-v1.desktop</extends>
        <render-kit-id>org.apache.myfaces.trinidad.desktop</render-kit-id>
        <style-sheet-name>/css/RichTextEditor.css</style-sheet-name>
      </skin>
      </skins>


    trinidad-config.xml enry is
    <trinidad-config xmlns="http://myfaces.apache.org/trinidad/config">
      <skin-family>sanStyle-v1</skin-family>
    </trinidad-config>

    I also have /css/RichTextEditor.css file, which will have my styling.

    2. Add Javascript on proper event to set css for iframe-head section:

    To do this we need to capture iframe component. I wanted to run javascript on iframe load event but seems like iframe is construted locally without any url so load even is not firing. (Just my guess..) Because of this I just did a workaround to run my script after 10 milisecond and get handle of iframe object. It gives a flickering effect but OK for me. May be you want to investigate more on it and get correct time to run your javascript
    Now my javascript code in /js/RichTextEditor.js file is
    function styleRTE(evt){
         myVar=setTimeout(function(){setRTEStyle()},10)
    }
        
        function setRTEStyle() {
               $('iframe').contents().find("head")
                  .append($("<link rel=\"stylesheet\" type=\"text/css\" href=\"../css/RichTextEditor.css\">"));   
               $('iframe').contents().find("body").addClass("myRichTextEditor");

        }


    Note: I am using jquery, you can write plain javascript also.
    Note: I am using 'iframe' selector, it means all iframe will have following styles. You can use other jquery selector based on your requirement.

    Here are the description what I am doing in this javascript
    a. function sytleRTE runs setRTEStyle after 10 milliseconds
    b. function setRTEStyle finds 'iframe' objects and append <link rel="stylesheet" type="text/css" href="../css/RichTextEditor.css"> in head section.
    c. function setRTEStyle also tries to set style for body tag as myRichTextEditor.

    Now I need to call styleRTE on proper event of ADF. In my case I am calling it on popup launch event but if your RTE is on base page directly, you may want to call it on pageload event.

    To call it on popup launch event I have following code


    3. Add style in your css file: 

    I have added following styles in my css file (RichTextEditor.css)

    4. Lets see RTE now in chrome inspector (F12):



    5. Now lets set our base page

       I am using outputtext with escape=false to show base page content. This will make sure that I get complete content as it is on my ADF page but to make sure that any content in this also have styles from RichTextEditor.css I need to surround my outputtext with a panelgroup and then set style myRichTextEditor on that panel group.

    Now let see it in chrome inspector


    Similarly you can add default styling for other tags in RichTextEditor.css as
    .myRichTextEditor h2 {

    }

    .myRichTextEditor ul {

    }

    .myRichTextEditor ul li {


    }


    With that I have achieved following
    a. Set a default style for each tags in RTE
    b. Same style is used to show tags on base page. 


    NOTE: Step 1 where we configure trinidad-skin.xml and trinidad-config.xml can be optional. Over only aim is to add RichTextEditor.css on our base page. We can achieve that using <af:resource type="css" source="/css/RichTextEditor.css"/> also. But we need to add it on all jspx pages and who knows if you want to have style only to be done on one single instance of RTE you can try this approach.