Saturday, July 19, 2014

ADF Good (Best) Practice: Register bean in backingbeans cope

This blog is to highlight why we should register our beans in backing bean scope.

ADF supports following scopes: backingbean, request, view, pageFlow, session and application.

As a good practice we should register our beans in backingbean scope.

1. Why we should not use view, pageFlow, session, application: System needs to serialize any information in these scopes during fail over and also when user times out. Generally we bind UI components with a bean and these components are not serializable so if bean is registered in above scopes it will not get serialized and serialization will fail.


2. Why we should not use requestScope: Serialization is not a problem if bean is registered in request scope because system does not serialize any object that is present in request scope. That make sense also because anything that is in request scope should be lost in next request so what is the point to serialize that information. BUT still we should not register our beans in request scope. That is because if we use a task-flow on a page twice and task-flow has a request-scope bean, both instances of task-flow will start sharing same instance of bean. It can create a lot of confusion.


3. Why should we use backing-bean Scope: Backing bean scope is almost same a request scope in terms of life cyle so system need not to serialize any such bean. Also we get advantage that system create two instances of bean if we use task-flow twice on a page.


4. Is it good enough: Now question is "Can we meet all requirements by storing beans in backing-bean scope?". I would say most of the time YES, you can. Mostly we need to put certain INFORMATION on higher scopes (like pageFlow, session, application) and to store that we don't need to put bean in that scope. We can directly put INFORMATION (or value) in scope. For example we have a 4 page task-flow, which has employee search on first page and user can select an employee on first page. After selecting employee user can navigate to page 2, 3, 4. On page 4 we need to know selected employee record.
To achieve this one developer do following
   a. Bind employee table of first page on a page-flow bean
   b. Bind table with the bean.
   c. On page 4, get page-flow bean and then get table and then try to get selected employee row.

Other developer can follow this approach
   a. Bind employee table of first page on backingbean-scoped bean
   b. Bind table with that bean
   c. While navigating to page-2 get selected row from employee table and store its (employee-id or any other needed information) in page-flow scope. If you need complete employee information you can create employee-pojo (object) and set its attribute and then store employee object in page-flow
   d. Access employee information from page-flow scope.

[IF you are using bindings and on selection you have selection listener which makes employee row current, you can directly create binding of same vo on page-4. That could be even better]

We see mostly we need to information on different pages, so its better to store those information on needed scope rather than bean.


5. If you really want to store bean in higher scopes: If you really need bean in higher scopes atleast use ComponentReference.

http://myfaces.apache.org/trinidad/trinidad-api/apidocs/org/apache/myfaces/trinidad/util/ComponentReference.html



Disclaimer: Any views or opinions presented in this blog are solely those of the author and do not necessarily represent those of the company.

Wednesday, July 9, 2014

ADF: Programatic user authentication instead of j_security_check (Password retrieval )

When we create ADF project and enable security, system creates a login page for us. Which has following code.


<form method="POST" action="j_security_check">
      <table cellspacing="2" cellpadding="3" border="1">
        <tr>          <th>Username:</th>
          <td>            <input type="text" name="j_username"/>          </td>
        </tr>
        
        <tr>          <th>Password:</th>
          <td>            <input type="password" name="j_password"/>          </td>
        </tr>
      </table>
      <p>
        <input type="submit" name="submit" value="Submit"/>
      </p>
    </form>

When user clicks on submit button, login form get submitted. It calls j_security_check, which is provided by framework. It validates username/password against ldap configured in weblogic.

We do not get any point to know what was username/password. At times we may want to derive  username/password combination. A usecase could be integration with third party. Imagine you want to integrate with third party system which takes username/password. For example sharepoint webservices. I tried checking OPSS apis but seems like there is no way to retrieve password using them. OPSS apis not retrieving password makes sense also.

To solve our problem I can think of a solution to capture user password while he is trying to login and if authentication is fine store user password in sessionscope. Use it later when calling third party system.

I personally think there might be better ways to do it but I could not find any solution. In this blog I am just describing a way to authenticate user programmatically instead of j_security_check.

Here are the steps:

1. Change login.html to login.jspx: Instead of using normal login.html page lets create a jspx page for login.
Put following form entry in it.

<af:panelFormLayout id="pfl2" styleClass="form_layout">
                        <f:facet name="footer"/>
                        <af:inputText label="#{portalBundle.USER_NAME}:" id="it1"
                                      value="#{viewScope.username}"
                                      required="true" maximumLength="50"
                                      partialTriggers="cb1"/>
                        <af:inputText label="#{portalBundle.PASSWORD}:" id="it2"
                                      value="#{viewScope.password}"
                                      secret="true" required="true"
                                      maximumLength="50" partialTriggers="cb1"
                                      shortDesc="Enter Password"/>
                        <af:commandButton id="cb1"
                                          action="#{backingBeanScope.LoginPageBean.doLogin}"
                                          text="#{portalBundle.LOGIN}"
                                          styleClass="login"
                                          partialSubmit="true"/>
                      </af:panelFormLayout>


In above code commandButton-cb1 does a partial submit and calls doLogin method of LoginPageBean. Username and password are stored in viewscope.

LoginPageBean code should look like



import com.kbace.hrc.common.util.diagnostic.DiagnosticUtil;


import java.io.IOException;

import java.util.Map;

import javax.faces.application.FacesMessage;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;

import javax.security.auth.Subject;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import oracle.adf.share.ADFContext;
import oracle.adf.share.logging.ADFLogger;
import oracle.adf.share.security.SecurityContext;
import oracle.adf.view.rich.component.rich.RichPopup;
import oracle.adf.view.rich.component.rich.output.RichOutputText;

import weblogic.security.URLCallbackHandler;
import weblogic.security.services.Authentication;

import weblogic.servlet.security.ServletAuthentication;


public class LoginPageBean {
    private static ADFLogger _logger =
        ADFLogger.createADFLogger(LoginPageBean.class);
    private RichOutputText pageInit;
    private RichPopup loginErrorPopup;
    private RichPopup userRoleErrorPopup;

    public LoginPageBean() {
    }
    private String _username, _password;


       public String doLogin() {
        String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
        _username = (String)ADFUtil.evaluateEL("#{viewScope.username}");
        _password = (String)ADFUtil.evaluateEL("#{viewScope.password}");
        String un = _username;
        byte[] pw = _password.getBytes();
        FacesContext ctx = FacesContext.getCurrentInstance();
        HttpServletRequest request = (HttpServletRequest)ctx.getExternalContext().getRequest();
        String loginStatus = "login_failed";
        try {
            Subject subject =   Authentication.login(new URLCallbackHandler(un, pw));
            weblogic.servlet.security.ServletAuthentication.runAs(subject,     request);
            ServletAuthentication.generateNewSessionID(request);

            loginStatus = "success";
             String redirecturl = "/adfAuthentication?success_url=/index.html";
            Map session = (Map)ADFUtil.evaluateEL("#{sessionScope}");
            session.put("userPassword", _password);
            session.put("username", _username);

                try {
                   FacesContext.getCurrentInstance().getExternalContext().redirect(FacesContext.getCurrentInstance().getExternalContext().getRequestContextPath() + redirecturl);
               
                } catch (Exception nm) {
                    nm.printStackTrace();
                }
          
        } catch (FailedLoginException fle) {
            ADFUtil.getRequestFlowScope().put("pShowAuthError", "Y");
            RichPopup.PopupHints pph=new RichPopup.PopupHints();
            this.getLoginErrorPopup().show(pph);
            ADFUtil.setEL("#{viewScope.password}", null);

        } catch (LoginException le) {
            FacesMessage msg =
                new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error Logging In",
                                 "Error Logging In");
            ctx.addMessage(null, msg);

        }
        return null;
    }



}




Above method doLogin tries following things
1. Get username/password from viewscope
2. Authenticate username/password using weblogic security apis. 
3. If valid username/password then start a new session.
4. Store username/password in sessionscope
5. Redirect to index.html if successful login.
6. Show error popup if authentication fails.
   

Also we need to add following libraries in our project


a.       com.bea.core.weblogic.security.auth_1.1.0.0.jar: $MW_HOME\modules
b.      com.bea.core.weblogic.security.identity_1.1.2.1.jar: $MW_HOME\modules
c.       ws.api.jar: $MW_HOME\wlserver_10.3\server\lib



I am not very sure if its best way of doing it. I would definitely love to know better ways of doing this but for time being I just know this way.


Disclaimer: Any views or opinions presented in this blog are solely those of the author and do not necessarily represent those of the company.