org.openbravo.authentication.AuthenticationManager.java Source code

Java tutorial

Introduction

Here is the source code for org.openbravo.authentication.AuthenticationManager.java

Source

/*
 ************************************************************************************
 * Copyright (C) 2001-2013 Openbravo S.L.U.
 * Licensed under the Apache Software License version 2.0
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to  in writing,  software  distributed
 * under the License is distributed  on  an  "AS IS"  BASIS,  WITHOUT  WARRANTIES  OR
 * CONDITIONS OF ANY KIND, either  express  or  implied.  See  the  License  for  the
 * specific language governing permissions and limitations under the License.
 ************************************************************************************
 */

package org.openbravo.authentication;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.openbravo.authentication.basic.DefaultAuthenticationManager;
import org.openbravo.base.HttpBaseUtils;
import org.openbravo.base.VariablesBase;
import org.openbravo.base.exception.OBException;
import org.openbravo.base.secureApp.HttpSecureAppServlet;
import org.openbravo.base.secureApp.LoginUtils;
import org.openbravo.base.secureApp.VariablesSecureApp;
import org.openbravo.base.session.OBPropertiesProvider;
import org.openbravo.base.util.OBClassLoader;
import org.openbravo.dal.core.OBContext;
import org.openbravo.dal.service.OBDal;
import org.openbravo.database.ConnectionProvider;
import org.openbravo.erpCommon.obps.ActivationKey;
import org.openbravo.erpCommon.security.SessionLogin;
import org.openbravo.model.ad.access.Session;
import org.openbravo.service.db.DalConnectionProvider;
import org.openbravo.service.web.BaseWebServiceServlet;

/**
 * 
 * @author adrianromero
 * @author iperdomo
 */
public abstract class AuthenticationManager {

    private static final Logger log4j = Logger.getLogger(AuthenticationManager.class);
    private static final String DEFAULT_AUTH_CLASS = "org.openbravo.authentication.basic.DefaultAuthenticationManager";

    private static final String SUCCESS_SESSION_STANDARD = "S";
    private static final String SUCCESS_SESSION_WEB_SERVICE = "WS";
    private static final String REJECTED_SESSION_WEB_SERVICE = "WSR";
    private static final String SUCCESS_SESSION_CONNECTOR = "WSC";
    private static final String FAILED_SESSION = "F";

    protected ConnectionProvider conn = null;
    protected String defaultServletUrl = null;
    protected String localAdress = null;
    protected String username = "";

    /**
     * Returns an instance of AuthenticationManager subclass, based on the authentication.class
     * property in Openbravo.properties
     */
    public final static AuthenticationManager getAuthenticationManager(HttpServlet s) {
        AuthenticationManager authManager;
        String authClass = OBPropertiesProvider.getInstance().getOpenbravoProperties()
                .getProperty("authentication.class", DEFAULT_AUTH_CLASS);
        if (authClass == null || authClass.equals("")) {
            // If not defined, load default
            authClass = "org.openbravo.authentication.basic.DefaultAuthenticationManager";
        }
        try {
            authManager = (AuthenticationManager) OBClassLoader.getInstance().loadClass(authClass).newInstance();
            authManager.init(s);
        } catch (Exception e) {
            log4j.error(
                    "Defined authentication manager cannot be loaded. Verify the 'authentication.class' entry in Openbravo.properties");
            authManager = new DefaultAuthenticationManager(s);
            authManager.init(s);
        }
        return authManager;
    }

    public AuthenticationManager() {
    }

    public AuthenticationManager(HttpServlet s) throws AuthenticationException {
        init(s);
    }

    protected void bdErrorAjax(HttpServletResponse response, String strType, String strTitle, String strText)
            throws IOException {
        response.setContentType("text/xml; charset=UTF-8");
        PrintWriter out = response.getWriter();
        out.println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
        out.println("<xml-structure>\n");
        out.println("  <status>\n");
        out.println("    <type>" + strType + "</type>\n");
        out.println("    <title>" + strTitle + "</title>\n");
        out.println("    <description><![CDATA[" + strText + "]]></description>\n");
        out.println("  </status>\n");
        out.println("</xml-structure>\n");
        out.close();
    }

    public void init(HttpServlet s) throws AuthenticationException {
        if (s instanceof ConnectionProvider) {
            conn = (ConnectionProvider) s;
        } else {
            conn = new DalConnectionProvider();
        }
        defaultServletUrl = s.getServletConfig().getServletContext().getInitParameter("ServletSinIdentificar");
    }

    /**
     * Used in the service method of the {@link HttpSecureAppServlet} to know if the request is
     * authenticated or not. This method calls the <b>doAuthenticate</b> that makes the actual checks
     * and could be easily extended by sub-classes. Returns the user id if the user is already logged
     * in or null if is not authenticated.
     * 
     * @param request
     *          HTTP request object to handle parameters and session attributes
     * @param response
     *          HTTP response object to handle possible redirects
     * @return the value of AD_User_ID if the user is already authenticated or <b>null</b> if not
     */
    public final String authenticate(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException, ServletException, IOException {

        if (localAdress == null) {
            localAdress = HttpBaseUtils.getLocalAddress(request);
        }

        final String userId = doAuthenticate(request, response);

        final VariablesSecureApp vars = new VariablesSecureApp(request, false);
        if (StringUtils.isEmpty(vars.getSessionValue("#AD_SESSION_ID"))) {
            setDBSession(request, userId, SUCCESS_SESSION_STANDARD, true);
        }

        // if we in 'forceLogin' state, there is no need to process any other code
        if (userId == null && "Y".equals(request.getSession().getAttribute("forceLogin"))) {
            return null;
        }

        // A restricted resource can define a custom login URL
        // It just need to set an the attribute loginURL in the request
        final String customLoginURL = (String) request.getAttribute("loginURL");

        final String loginURL = localAdress
                + (customLoginURL == null || "".equals(customLoginURL) ? defaultServletUrl : customLoginURL);

        if (userId == null && !response.isCommitted()) {
            response.sendRedirect(loginURL);
            return null;
        }

        return userId;
    }

    /**
     * Authentication for web services and external services. All authenticated requests not using the
     * standard UI *MUST* use this authentication or
     * {@link AuthenticationManager#webServiceAuthenticate(String, String)}.
     * 
     * @param request
     *          HTTP request object to handle parameters and session attributes
     * @return the value of AD_User_ID if the user is already authenticated or <b>null</b> if not
     * @throws AuthenticationException
     *           in case of an authentication error different than incorrect user/password (which just
     *           returns null)
     */
    public final String webServiceAuthenticate(HttpServletRequest request) throws AuthenticationException {
        final String userId = doWebServiceAuthenticate(request);

        final String dbSessionId = setDBSession(request, userId, SUCCESS_SESSION_WEB_SERVICE, false);

        return webServicePostAuthenticate(userId, dbSessionId);
    }

    /**
     * Authentication for web services and external services. All authenticated requests not using the
     * standard UI *MUST* use this authentication or
     * {@link AuthenticationManager#webServiceAuthenticate(HttpServletRequest)}. This one is intended
     * for authentications for non standard REST web services (such as SOAP).
     * 
     * @param user
     *          User name to authenticate
     * @param password
     *          Password to validate user
     * @return the value of AD_User_ID if the user is already authenticated or <b>null</b> if not
     * @throws AuthenticationException
     *           in case of an authentication error different than incorrect user/password (which just
     *           returns null)
     */
    public final String webServiceAuthenticate(String user, String password) throws AuthenticationException {
        username = user;
        final String userId = doWebServiceAuthenticate(user, password);
        final String dbSessionId = setDBSession(null, userId, SUCCESS_SESSION_WEB_SERVICE, false);
        return webServicePostAuthenticate(userId, dbSessionId);
    }

    private String webServicePostAuthenticate(String userId, String dbSessionId) throws AuthenticationException {
        if (userId == null) {
            return null;
        }

        switch (ActivationKey.getInstance(true).checkNewWSCall(true)) {
        case NO_RESTRICTION:
            return userId;
        case EXCEEDED_WARN_WS_CALLS:
            log4j.warn("Number of webservice calls exceeded today.");
            return userId;
        case EXCEEDED_MAX_WS_CALLS:
            updateDBSession(dbSessionId, false, REJECTED_SESSION_WEB_SERVICE);
            log4j.warn("Cannot use WS, exceeded number of calls");
            throw new AuthenticationException("Exceeded maximum number of allowed calls to web services.");
        case EXPIRED:
            updateDBSession(dbSessionId, false, REJECTED_SESSION_WEB_SERVICE);
            log4j.warn("Cannot use WS, license expired");
            throw new AuthenticationException("Exceeded maximum number of allowed calls to web services.");
        case EXPIRED_MODULES:
            updateDBSession(dbSessionId, false, REJECTED_SESSION_WEB_SERVICE);
            log4j.warn("Cannot use WS, expired modules");
            throw new AuthenticationException("There are expired modules");
        }

        return null;
    }

    /**
     * Authentication for approved connectors. Only authorized connectors are allowed to use this
     * authentication.
     * 
     * @param request
     *          HTTP request object to handle parameters and session attributes
     * @return the value of AD_User_ID if the user is already authenticated or <b>null</b> if not
     * @throws AuthenticationException
     *           in case of an authentication error different than incorrect user/password (which just
     *           returns null)
     */
    public final String connectorAuthenticate(HttpServletRequest request) throws AuthenticationException {
        final String userId = doWebServiceAuthenticate(request);

        setDBSession(request, userId, SUCCESS_SESSION_CONNECTOR, false);

        return userId;
    }

    /**
     * Authentication for approved connectors. Only authorized connectors are allowed to use this
     * authentication.
     * 
     * @param user
     *          User name to authenticate
     * @param password
     *          Password to validate user
     * @return the value of AD_User_ID if the user is already authenticated or <b>null</b> if not
     * @throws AuthenticationException
     *           in case of an authentication error different than incorrect user/password (which just
     *           returns null)
     */
    public final String connectorAuthenticate(String user, String password) throws AuthenticationException {
        final String userId = doWebServiceAuthenticate(user, password);

        setDBSession(null, userId, SUCCESS_SESSION_CONNECTOR, false);

        return userId;
    }

    private String setDBSession(HttpServletRequest request, String userId, String successSessionType,
            boolean setSession) {
        final VariablesSecureApp vars = new VariablesSecureApp(request, false);
        String dbSessionId = vars.getSessionValue("#AD_SESSION_ID");
        if (StringUtils.isEmpty(dbSessionId)) {
            dbSessionId = createDBSession(request, username, userId, successSessionType);
            if (setSession) {
                vars.setSessionValue("#AD_SESSION_ID", dbSessionId);
                if (userId != null) {
                    HttpSession session = request.getSession(false);
                    if (session != null) {
                        session.setAttribute("#Authenticated_user", userId);
                    }
                }
            }
        }
        return dbSessionId;
    }

    /**
     * Clears all session attributes and calls the <b>doLogout</b> method
     */
    public final void logout(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        VariablesBase vars = new VariablesBase(request);
        vars.clearSession(true);

        doLogout(request, response);
    }

    /**
     * Called from the <b>authenticate</b> method makes the necessary processing to check if the
     * request is authenticated or not. The simplest way to check is if the #Authenticated_user
     * session attribute is present and return it.
     * 
     * @param request
     *          HTTP request object, used for handling parameters and session attributes
     * @param response
     * @return <ul>
     *         <li>The user id (AD_User_ID) if the request is already authenticated or the
     *         authentication process succeeded</li>
     *         <li><b>null</b> if the request is not authenticated or authentication process failed
     *         (e.g. wrong password)</li>
     *         </ul>
     * @see DefaultAuthenticationManager
     */
    protected abstract String doAuthenticate(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException, ServletException, IOException;

    /**
     * Authentication used by web services and connectors. This authentication can be overriden by
     * subclasses. By default it looks for user and password parameters in the request, if they are
     * not present, Basic authentication is performed
     * 
     * @param request
     *          HTTP request object, used for handling parameters and session attributes
     * @return <ul>
     *         <li>The user id (AD_User_ID) if the request is already authenticated or the
     *         authentication process succeeded</li>
     *         <li><b>null</b> if the request is not authenticated or authentication process failed
     *         (e.g. wrong password)</li>
     *         </ul>
     */
    protected String doWebServiceAuthenticate(HttpServletRequest request) {
        final String login = request.getParameter(BaseWebServiceServlet.LOGIN_PARAM);
        final String password = request.getParameter(BaseWebServiceServlet.PASSWORD_PARAM);
        String userId = null;
        if (login != null && password != null) {
            username = login;
            userId = LoginUtils.getValidUserId(new DalConnectionProvider(false), login, password);
        } else { // use basic authentication
            userId = doBasicAuthentication(request);
        }

        return userId;
    }

    protected String doWebServiceAuthenticate(String user, String password) {
        return LoginUtils.getValidUserId(new DalConnectionProvider(false), user, password);
    }

    /**
     * Method called from the <b>logout</b> method after clearing all session attributes. The usual
     * process is to redirect the user to the login page
     * 
     * @param request
     *          HTTP request object
     * @param response
     *          HTTP response object
     */
    protected abstract void doLogout(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException;

    protected final String createDBSession(HttpServletRequest req, String strUser, String strUserAuth) {
        return createDBSession(req, strUser, strUserAuth, "S");
    }

    protected final String createDBSession(HttpServletRequest req, String strUser, String strUserAuth,
            String successSessionType) {
        try {
            String usr = strUserAuth == null ? "0" : strUserAuth;

            final SessionLogin sl = new SessionLogin(req, "0", "0", usr);

            if (strUserAuth == null) {
                sl.setStatus(FAILED_SESSION);
            } else {
                sl.setStatus(successSessionType);
            }

            sl.setUserName(strUser);
            if (req != null) {
                sl.setServerUrl(HttpBaseUtils.getLocalAddress(req));
            }
            sl.save();
            return sl.getSessionID();
        } catch (Exception e) {
            log4j.error("Error creating DB session", e);
            return null;
        }
    }

    protected final void updateDBSession(String sessionId, boolean sessionActive, String status) {
        try {
            OBContext.setAdminMode();
            Session session = OBDal.getInstance().get(Session.class, sessionId);
            session.setSessionActive(sessionActive);
            session.setLoginStatus(status);
            OBDal.getInstance().flush();
        } catch (Exception e) {
            log4j.error("Error updating session in DB", e);
        } finally {
            OBContext.restorePreviousMode();
        }

    }

    private String doBasicAuthentication(HttpServletRequest request) {
        try {
            final String auth = request.getHeader("Authorization");
            if (auth == null) {
                return null;
            }
            if (!auth.toUpperCase().startsWith("BASIC ")) {
                return null; // only BASIC supported
            }

            // user and password come after BASIC
            final String userpassEncoded = auth.substring(6);

            // Decode it, using any base 64 decoder
            final String decodedUserPass = new String(Base64.decodeBase64(userpassEncoded.getBytes()));
            final int index = decodedUserPass.indexOf(":");
            if (index == -1) {
                return null;
            }
            final String login = decodedUserPass.substring(0, index);
            final String password = decodedUserPass.substring(index + 1);
            String userId = LoginUtils.getValidUserId(new DalConnectionProvider(), login, password);
            username = login;
            return userId;
        } catch (final Exception e) {
            throw new OBException(e);
        }
    }

}