To validate the integration with OES we need a sample ADF Application that is rich enough to allow us to test securing the various ADF elements. To achieve this we can add some items including bounded task flows to the application developed in this tutorial. A sample JDeveloper 11.1.1.6 project is available here. It depends on the Fusion Order Demo (FOD) database schema which is easily created using the FOD build scripts.In the deployment we have chosen to enable only ADF Authentication as we will delegate Authorization, mostly, to OES.The welcome page of the application with all the links exposed looks as follows:
The Welcome, Browse Products, Browse Stock and System Administration links go to pages while the Supplier Registration and Update Stock are bounded task flows. The Login link goes to a basic login page and once logged in a link is presented that goes to a logout page. Only the Browse Products and Browse Stock pages are really connected to the database--the other pages and task flows do not really perform any operations on the database.
Required Security Policies
We make use of a set of test users and roles as decscribed on the welcome page of the application. In order to exercise the different authorization possibilities we would like to enforce the following sample policies:
Anonymous users can see the Login, Welcome and Supplier Registration links. They can also see the Welcome page, the Login page and follow the Supplier Registration task flow. They can see the icon adjacent to the Login link indicating whether they have logged in or not.
Authenticated users can see the Browse Product page.
Only staff granted the right can see the Browse Product page cost price value returned from the database and then only if the value is below a configurable limit.
Suppliers and staff can see the Browse Stock links and pages. Customers cannot.
Suppliers can see the Update Stock link but only those with the update permission are allowed to follow the task flow that it launches. We could hide the link but leave it exposed here so we can easily demonstrate the method call activity protecting the task flow.
Only staff granted the right can see the System Administration link and the System Administration page it accesses.
Implementing the required policies
In order to secure the application we will make use of the following techniques:
EL Expressions and Java backing beans: JSF has the notion of EL expressions to reference data from backing Java classes. We use these to control the presentation of links on the navigation page which respect the security contraints. So a user will not see links that he is not allowed to click on into. These Java backing beans can call on to OES for an authorization decision. Important Note: naturally we would configure the WLS domain where our ADF application is running as an OES WLS SM, which would allow us to efficiently query OES over the PEP API. However versioning conflicts between OES 11.1.1.5 and ADF 11.1.1.6 mean that this is not possible. Nevertheless, we can make use of the OES RESTful gateway technique from this posting in order to call into OES.
You can easily create and manage backing beans in Jdeveloper as follows:
Custom ADF Phase Listener: ADF extends the JSF page lifecycle flow and allows one to hook into the flow to intercept page rendering. We use this to put a check prior to rendering any protected pages, again calling on to OES via the backing bean. Phase listeners are configured in the adf-settings.xml file. See the MyPageListener.java class in the project. Here, for example, is the code we use in the listener to check for allowed access to the sysadmin page, navigating back to the welcome page if authorization is not granted:
if (page != null && (page.equals("/system.jspx") || page.equals("/system"))){ System.out.println("MyPageListener: Checking Authorization for /system"); if (getValue("#{oesBackingBean.UIAccessSysAdmin}").toString().equals("false") ){ System.out.println("MyPageListener: Forcing navigation away from system" + "to welcome"); NavigationHandler nh = fc.getApplication().getNavigationHandler(); nh.handleNavigation(fc, null, "welcome"); } else { System.out.println("MyPageListener: access allowed"); } }
Method call activity: our app makes use of bounded task flows to implement the sequence of pages that update the stock or allow suppliers to self register. ADF takes care of ensuring that a bounded task flow can be entered by only one page. So a way to protect all those pages is to make a call to OES in the first activity and then either exit the task flow or continue depending on the authorization decision. The method call returns a String which contains the name of the transition to effect. This is where we configure the method call activity in JDeveloper:
We implement each of the policies using the above techniques as follows:
Policies 1 and 2: as these policies concern the coarse grained notions of controlling access to anonymous and authenticated users we can make use of the container’s security constraints which can be defined in the web.xml file. The allPages constraint is added automatically when we configure Authentication for the ADF application. We have added the “anonymousss” constraint to allow access to the the required pages, task flows and icons:
<security-constraint> <web-resource-collection> <web-resource-name>anonymousss</web-resource-name> <url-pattern>/faces/welcome</url-pattern> <url-pattern>/afr/*</url-pattern> <url-pattern>/adf/*</url-pattern> <url-pattern>/key.png</url-pattern> <url-pattern>/faces/supplier-reg-btf/*</url-pattern> <url-pattern>/faces/supplier_register_complete</url-pattern> </web-resource-collection> </security-constraint>
Policy 3: we can place an EL expression on the element representing the cost price on the products.jspx page: #{oesBackingBean.dataAccessCostPrice}. This EL Expression references a method in a Java backing bean that will call on to OES for an authorization decision. In OES we model the authorization requirement by requiring the view permission on the resource /MyADFApp/data/costprice and granting it only to the staff application role. We recover any obligations to determine the limit.
Policy 4: is implemented by putting an EL expression on the Browse Stock link #{oesBackingBean.UIAccessBrowseStock} which checks for the view permission on the /MyADFApp/ui/stock resource. The stock.jspx page is protected by checking for the same permission in a custom phase listener—if the required permission is not satisfied then we force navigation back to the welcome page.
Policy 5: the Update Stock link is protected with the same EL expression as the Browse Link: #{oesBackingBean.UIAccessBrowseStock}. However the Update Stock link launches a bounded task flow and to protect it the first activity in the flow is a method call activity which will execute an EL expression #{oesBackingBean.isUIAccessSupplierUpdateTransition} to check for the update permission on the /MyADFApp/ui/stock resource and either transition to the next step in the flow or terminate the flow with an authorization error.
Policy 6: the System Administration link is protected with an EL Expression #{oesBackingBean.UIAccessSysAdmin} that checks for view access on the /MyADF/ui/sysadmin resource. The system page is protected in the same way at the stock page—the custom phase listener checks for the same permission that protects the link and if not satisfied we navigate back to the welcome page.
Testing the Application
To test the application:
deploy the OES11g Admin to a WLS domain
deploy the OES gateway in a another domain configured to be a WLS SM. You must ensure that the jps-config.xml file therein is configured to allow access to the identity store, otherwise the gateway will not b eable to resolve the principals for the requested users. To do this ensure that the following elements appear in the jps-config.xml file:
<serviceProvider type="IDENTITY_STORE" name="idstore.ldap.provider" class="oracle.security.jps.internal.idstore.ldap.LdapIdentityStoreProvider"> <description>LDAP-based IdentityStore Provider</description> </serviceProvider>
<serviceInstance name="idstore.ldap" provider="idstore.ldap.provider"> <property name="idstore.config.provider" value="oracle.security.jps.wls.internal.idstore.WlsLdapIdStoreConfigProvider"/> <property name="CONNECTION_POOL_CLASS" value="oracle.security.idm.providers.stdldap.JNDIPool"/></serviceInstance>
<serviceInstanceRef ref="idstore.ldap"/>
download the sample application and change the URL to the gateway in the MyADFApp OESBackingBean code to point to the OES
Gateway and deploy the application to an 11.1.1.6 WLS domain that has been extended with the ADF JRF files. You will need to configure the FOD database connection to point your database which contains the FOD schema.
populate the OES Admin and OES Gateway WLS LDAP stores with the sample set of users and groups. If you have configured the WLS domains to point to the same LDAP then it would only have to be done once. To help with this there is a directory called ldap_scripts in the sample project with ldif files for the test users and groups.
start the OES Admin console and configure the required OES authorization policies for the MyADFApp application and push them to the WLS SM containing the OES Gateway.
Login to the MyADFApp as each of the users described on the login page to test that the security policy is correct.
You will see informative logging from the OES Gateway and the ADF application to their respective WLS consoles.
Congratulations, you may now login to the OES Admin console and change policies that will control the behaviour of your ADF application--change the limit value in the obligation for the cost price for example, or define Role Mapping policies to determine staff access to the system administration page based on user profile attributes.
ADF Development Notes
Some notes on ADF development which are probably typical gotchas:
May need this on WLS startup in order to allow us to overwrite credentials for the database, the signal here is that there is an error trying to access the data base: -Djps.app.credential.overwrite.allowed=true
Best to call Bounded Task flows via a CommandLink (as opposed to a go link) as you cannot seem to start them again from a go link, even having completed the task flow correctly with a return activity.
Once a bounded task flow (BTF) is initated it must complete correctly via a return activity—attempting to click on any other link whilst in the context of a BTF has no effect. See here for example:
When using the ADF Authentication only security approach it seems to be awkward to allow anonymous access to the welcome and registration pages. We can achieve anonymous access using the web.xml security constraint shown above (where no auth-constraint is specified) however it is not clear what needs to be listed in there….for example the /afr/* and /adf/* are in there by trial and error as sometimes the welcome page will not render if we omit those items. I was not able to use the default allPages constraint with for example the anonymous-role or the everyone WLS group in order to be able to allow anonymous access to pages.
The ADF security best practice advises placing all pages under the public_html/WEB-INF folder as then ADF will not allow any direct access to the .jspx pages but will only allow acces via a link of the form /faces/welcome rather than /faces/welcome.jspx. This seems like a very good practice to follow as having multiple entry points to data is a source of confusion in a web application (particulary from a security point of view).
In Authentication+Authorization mode only pages with a Page definition file are protected. In order to add an emty one right click on the page and choose Go to Page Definition. This will create an empty page definition and now the page will require explicit permission to be seen.
It is advisable to give a unique context root via the weblogic.xml for the application, as otherwise the application will clash with any other application with the same context root and it will not deploy