Thursday, October 25, 2007

Security in Oracle ADF and Automatic Page Loading

In enterprise applications, automatic page loading based on user role is used quite often. Automatic page load is done during user login phase. This means that when user with role A enters into system, page X will be opened for him. And in the same way, when user with role B enters into system, page Y will be opened. Information about which page to open is acquired from security container, but how to open dynamically one or another page - here is the question.

Solution I have used is to put some empty intermediate page between login module and application pages. After user is authenticated in login module, he is transfered to intermediate page. However, this intermediate page does not require any input from the user, implemented logic allows to check connected user role and based on it automatically open suitable page in application.

You can download developed sample application - OnPageLoadAuthorization.zip. Sample is based on Employees entity from standard HR schema. Two JSPX pages are implemented, first is opened when connected user have read-only access rights and second is designed for editable case. This means that two roles (clerks and managers) are defined in web.xml - one allows only read-only access and second editable. Clerks role is mapped to access clerks.jspx and managers role to access managers.jspx page. On both pages the same data from Employees entity is available.

It's time to describe how actually automatic redirection is done based on role. All logic is centered in index.jspx, so I will describe it. This page is used for automatic redirect, it is achieved with onPageLoad() function in backing bean. onPageLoad() function is defined as empty function in a class that implements oracle.adf.controller.v2.lifecycle.PagePhaseListener. In this class standard beforePhase(PagePhaseEvent event) function is implemented, provided functionality allows to call onPageLoad() during page loading process. Class code:


So, backing bean of index.jspx page extends this class and provides code for onPageLoad() function by overriding it:


In this function actual logic is implemented - depending on role, redirection is done. One important thing, do not forget to include ControllerClass definition into index.jspx page page-definition file. You should bind it to a name of backing bean for the same page:


How it works? Let's provide in login form a user with read-only access - alex (welcome):


Read-only table with Employees data is opened:


And if we are providing a user - john (welcome) who can edit data, another page is opened:



When running sample application, don't forget to add adf-faces-impl.jar and jsf-impl.jar to application's WEB-INF\lib directory.

39 comments:

  1. Hai Andrejus

    I have created an onPageLoad()
    function in the backing bean for my page as u told in this blog.The page is loading through the action of a af:commandMenuItem.My page contains two af:inputText component with valueChangeListener
    functions.My requirement is to
    avoid executing the onPageLoad() function while the valueChangeListener event fired.
    Please tell me how to limit the execution of the onPageLoad() only
    when the commandMenuItem action happens.
    Thanks
    Ans

    ReplyDelete
  2. Hi Ans,

    onPageLoad() is invoked when page is loaded, I'm not sure if your use case will work. But, you can try to store some flag value in managed bean and access this value in onPageLoad(). So, you will know when to execute code in onPageLoad() and when not to execute.

    Regards,
    Andrejus

    ReplyDelete
  3. Hi Andre,

    Based on your example, i implemented onPageLoad functionality. But onPageLoad() method is not at called when the page is loaded. First i am loading parent page from that page i am loading child window. While loading child window i need to execute a store procedure which will return some list, that list i want to show it in selecOneChoice component in child window.

    I am using EJB3.0, Toplink and ADF

    Thanks & Regards
    Vimalan Balan

    ReplyDelete
  4. Hi Vilmalan,

    There should be no difference if you are using EJB 3.0 and TopLink. Since described functionality is related only to JSF.

    Regards,
    Andrejus

    ReplyDelete
  5. Hi Andre,

    Ok, but onPageLoad method is not at called when the child page opens. What i have to change. I posted in the forum. Kindly look into and tell where i committed the mistake

    http://forums.oracle.com/forums/thread.jspa?threadID=636166&tstart=0

    Thanks & Regards
    Vimalan Balan

    ReplyDelete
  6. Vilmalan,

    If this dont work with dialog windows for you, its possible I think just to put PL/SQL invocation code in Backing bean of parent page. Put it into Action Method for command button available in parent page and store values returned from PL/SQL in some Managed bean.

    You will be able to access values stored in Managed bean from child page.

    Regards,
    Andrejus

    ReplyDelete
  7. Hi Andre,

    I am not clear. I am going to try what you have said. But one question why the method is not calling. Please specify the reason.

    Sorry if i asked anything wrongly.

    Thanks for your reply and once i got the solution i will reply to you.

    Thanks & Regards
    Vimalan Balan

    ReplyDelete
  8. Hi,

    I'm not sure why it is not called. Since I haven't used it in such way like you describe.

    Regards,
    Andrejus

    ReplyDelete
  9. Hi Andre,

    Thanks for your solution and based on your solution i fixed it and populate it in the selectOneChoice component.

    One thing, i want to fix the onPageLoad problem. Since it will be required for me in some other case in my development. Hope you understand the problem which i described about onPageLoad. Kindly tell how can i resolve it.

    Thanks & Regards
    Vimalan Balan

    ReplyDelete
  10. Hi Andre,

    I done a silly mistake.

    i.e. In Controller class, i declared like this "#{backing_PODetailInput}

    and i corrected it by removing the double-quotes and now the method is called.


    Thanks & Regards
    Vimalan Balan

    ReplyDelete
  11. Hi Vimalan,

    This sounds cool, thanks for update !

    Regards,
    Andrejus

    ReplyDelete
  12. Hi Andre,

    One help needed regarding in resource bundle.

    Actually i am using resource bundle for displaying labels in the jspx, since i developing the page for multilingual that's why i am using bundle in the label attribute.

    When i throw the validation error its displaying like this

    com.sun.faces.el.ValueBindingImpl@106cf24 - Value required.

    How can i fix the code in order display like this

    PO Number - Value required.

    Kindly provide the syntax and changes to be done or sample code.

    Thanks & Regards
    Vimalan Balan

    ReplyDelete
  13. Hi Vilmalan,

    Actually, I dont have such sample.

    Regards,
    Andrejus

    ReplyDelete
  14. Hi Andre,

    For the resource bundle issue, in work around basis i done the fix. Kindly verify it and give your value able feedback.

    I posted the fix in the below forum
    http://forums.oracle.com/forums/thread.jspa?threadID=636957&tstart=0

    Thanks & Regards
    Vimalan Balan

    ReplyDelete
  15. Hi Andre,

    Regarding resource bundle issue.

    Actually, i assigned resource bundle value in setLabel method of CoreInputText component in onPageLoad method. when i run the application its throws null pointer exception because component is not initialized. So i set like this

    CoreInputText poNumber = new CoreInputText();
    this.poNumber.setLabel("resource.poNUmber");

    Its working properly. But after page loading and entered some value in the poNumber. Its throw null pointer exception.

    If i remove the CoreInputText poNumber = new CoreInputText();
    then i am able to get the values from the input text.

    What i have to do for to fix this problem. Since if we use resource bundle in lable attribute means label is not displaying in the error message.

    How to fix this issue. Need to your help.

    Thanks & Regards
    Vimalan Balan

    ReplyDelete
  16. Hi,

    You can put your code in setter method and add If statement to check for Null value. Just exactly like I'm doing in this sample: CRUD implementation with JDeveloper 11g. Look for setJobIdId(...) method.

    Regards,
    Andrejus

    ReplyDelete
  17. Hi

    I'm using your onPageLoad() to redirect profile.jspx to login.jspx if the users haven't logged in. But after login and coming back to profile.jspx, all tables show No rows yet.

    If the users go the right way: login.jspx first, then profile.jpsx, the tables show data correctly.

    I'm working on ADF Faces, JDev 10g

    Any ideas are appreciated

    Hien

    ReplyDelete
  18. Hi,

    If user haven't logged in, it should be handled by Security Container. Ideally you shouldn't use onPageLoad() method in this case.

    Regards,
    Andrejus

    ReplyDelete
  19. Hi,

    I copied your entire source code from
    OnPageLoadBackingBeanBase to my managed bean that I gave the same name, but I get following error: Import oracle.adf.controller.faces.context.FacesPageLifecycleContext not found.
    I work in JDeveloper 11g TP3.
    What should I do?

    Thanks in advance

    ReplyDelete
  20. Hi,

    I have not tested this with 11g. You can ask on OTN Forum, may be someone already was working with on page load functionality in 11g.

    Regards,
    Andrejus

    ReplyDelete
  21. Hi,

    I just tried it again and it works fine now. Meanwhile I closed JDeveloper several times and it works now. Interesting :) Just so you know

    Thanks

    ReplyDelete
  22. You see, JDeveloper rocks :)

    Its because needed library was added automatically after restart.

    Andrejus

    ReplyDelete
  23. Hi,

    I have a following problem onFormLoad() doesn't invoke. I'm making two pages: page1.jspx and page2.jspx. On page1.jspx load I want automatic redirection to page2.jspx. But onFormLoad() doesn't trigger.

    I followed your blog, but I'm doing it without authorization.
    What am I doing wrong? Is this even the right example for my problem? Or is there some other way. One day :) I want to develop dynamic redirection just like in your example but without authorization.

    Thanks a lot

    ReplyDelete
  24. Hi Andrejus,
    What changes dop i have to make if I would want the index page to render first and then redirect to some other page automatically? It might be useful in providing a processing page.

    ~Anupam

    ReplyDelete
  25. Hi Anupam,

    Its actually same technique as described. Just you need to add processing page with the same logic as index page currently have.

    Regards,
    Andrejus

    ReplyDelete
  26. Actually the page redirect is called while the page is in process of loading. So before the index page will render itself, the next page (where its redirected)will be shown. BTW I follwed your example while I naviagte index page via some other page it throws some exception: ValidatorId is null.
    However it works fine if I run the index page staright away.
    ~Anupam

    ReplyDelete
  27. Hi i used this aproach in my app and was almost succefull but when it executes the code for redirection it doesnt redirect it stays on the same page.

    I have a pop-up for login and have code to make the login after that i redirect it to a page like index.jsp and after executing pageload it stays on the same page (index)

    Any ideas ? Thanks

    ReplyDelete
  28. Hi Andrejus

    What will happen if you run the pages clerks.jspx and manager.jspx ?
    Will they behave in the same way ?
    My requirement is to check if user has permission to see the page or not.
    Depending on the result, I have to either show the page or show an error page.
    Will this approach work in that case ?
    Or is there any other simple approach for this requirement ?

    Thanks and Regards
    Sameer

    ReplyDelete
  29. i used this example for my adf 11g application..a set the page controller for the page as said in this example but it didn't work..after i read 11g developers guide i set it as ControllerClass="fms.view.backing.MyPageController" and it's working fine :)

    ReplyDelete
  30. Yes, things in 11g may change. Thanks for update ;)

    Andrejus

    ReplyDelete
  31. Hi,
    Should the afterPhase method load for fragments page also?
    If not how can I call a backing method when a fragment get loaded.
    Regards Corneliu

    ReplyDelete
  32. Hi,

    You should use Task Flow Method Call activity, as Default Activity inside Region Task Flow.

    Andrejus

    ReplyDelete
  33. Hi,

    First thanks for your help and quick answers.
    If I use the methodCall, I am not able to get the Iterator like this:
    DCBindingContainer bc = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
    DCIteratorBinding iter = bc.findIteratorBinding("ZonesView1Iterator");
    DCBindingContainer is null

    What should I do in order to call a method that has the above code inside and not get a NUllPointer.

    Regards Corneliu

    ReplyDelete
  34. Hi Andrejus

    In the method beforePhase, a call is made to getBindingContainer(), just before calling onPageLoad(). And then its set back to null.

    Can you please explain why we are doing that.

    Thanks,
    Manish.

    ReplyDelete
  35. Hi,

    If I correctly remember, just to reset ADF bindings. But this is old code from ADF 10g, you should test it on ADF 11g - may be setting to null is not needed anymore.

    Andrejus

    ReplyDelete
  36. Hi Andrejus,

    The blog is very nice and I have implemented the solution in one of my project.

    This solution is working fine iff we are sure about the page after login. e.g.(intermediate.jspx) and we configure ControllerClass in that jspx.

    But I am facing a issue when user bookmarks other page of the application and gets login page. After login he gets navigated to bookmarked page instead of intermediate page.

    Is there any way that we can configure in adf security that after login, it will always go to defined page but not to the page user tried to access application?

    I tried below code in web.xml


    /faces/intermediate.jspx

    ReplyDelete
  37. This all V. Good - Thanks
    But Can I know the way to Log out
    from the 2 pages to the Login html page and then relogin again ...

    Iam using Oracle fusion developer guide way to logout but No way

    the simple logout way works only within normal login without On Load Page

    please Help

    ReplyDelete
  38. Standard ADF Security logout should work just fine.

    Andrejus

    ReplyDelete