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:

ANS said...

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

Andrejus Baranovskis said...

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

B Vimalan said...

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

Andrejus Baranovskis said...

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

B Vimalan said...

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

Andrejus Baranovskis said...

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

B Vimalan said...

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

Andrejus Baranovskis said...

Hi,

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

Regards,
Andrejus

B Vimalan said...

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

B Vimalan said...

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

Andrejus Baranovskis said...

Hi Vimalan,

This sounds cool, thanks for update !

Regards,
Andrejus

B Vimalan said...

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

Andrejus Baranovskis said...

Hi Vilmalan,

Actually, I dont have such sample.

Regards,
Andrejus

B Vimalan said...

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

B Vimalan said...

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

Andrejus Baranovskis said...

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

lazyrobber said...

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

Andrejus Baranovskis said...

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

Anonymous said...

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

Andrejus Baranovskis said...

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

Anonymous said...

Ok, thanks

Anonymous said...

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

Andrejus Baranovskis said...

You see, JDeveloper rocks :)

Its because needed library was added automatically after restart.

Andrejus

Anonymous said...

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

Anupam Hyanki said...

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

Andrejus Baranovskis said...

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

Anupam Hyanki said...

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

Peter said...

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

Sameer Darbha said...

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

Anonymous said...

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 :)

Andrejus Baranovskis said...

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

Andrejus

Corneliu said...

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

Andrejus Baranovskis said...

Hi,

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

Andrejus

Corneliu said...

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

manish4u@gmail.com said...

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.

Andrejus Baranovskis said...

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

Gopal Krishna Aggarwal said...

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

Yahia said...

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

Andrejus Baranovskis said...

Standard ADF Security logout should work just fine.

Andrejus