com.twinsoft.convertigo.engine.admin.services.engine.Authenticate.java Source code

Java tutorial

Introduction

Here is the source code for com.twinsoft.convertigo.engine.admin.services.engine.Authenticate.java

Source

/*
 * Copyright (c) 2001-2011 Convertigo SA.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see<http://www.gnu.org/licenses/>.
 *
 * $URL$
 * $Author$
 * $Revision$
 * $Date$
 */

package com.twinsoft.convertigo.engine.admin.services.engine;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.codec.digest.DigestUtils;
import org.w3c.dom.Document;

import com.twinsoft.convertigo.engine.AuthenticatedSessionManager.Role;
import com.twinsoft.convertigo.engine.AuthenticatedSessionManager.SessionKey;
import com.twinsoft.convertigo.engine.Engine;
import com.twinsoft.convertigo.engine.EnginePropertiesManager;
import com.twinsoft.convertigo.engine.EnginePropertiesManager.PropertyName;
import com.twinsoft.convertigo.engine.admin.services.XmlService;
import com.twinsoft.convertigo.engine.admin.services.at.ServiceDefinition;
import com.twinsoft.convertigo.engine.admin.services.at.ServiceParameterDefinition;
import com.twinsoft.convertigo.engine.admin.util.ServiceUtils;
import com.twinsoft.convertigo.engine.enums.SessionAttribute;
import com.twinsoft.convertigo.engine.util.SimpleCipher;

@ServiceDefinition(name = "Authenticate", roles = { Role.ANONYMOUS }, parameters = {
        @ServiceParameterDefinition(name = "authType", description = "the authentication type required: login | logout"),
        @ServiceParameterDefinition(name = "authUserName", description = "the authentication user name"),
        @ServiceParameterDefinition(name = "authPassword", description = "the authentication user password (in clear text)") }, returnValue = "the authentication status")

public class Authenticate extends XmlService {

    /*
     * Defines the number of seconds the service will block account in case of
     * successive wrong authentication attempts.
     */
    private static final int AUTHENTICATION_DELAY = 300;

    /*
     * Defines the number max of wrong authentication attempts before
     * introducing a wait delay
     */
    private static final int MAX_NUMBER_OF_WRONG_AUTHENTICATION_ATTEMPTS = 5;

    /*
     * Defines the map of the number of currently wrong authentication attempts
     * with a given user ID
     */
    private static Map<String, AuthenticationAttempt> authenticationAttempts = new HashMap<String, AuthenticationAttempt>();

    private class AuthenticationAttempt {
        public int numberOfFailedRequests = 0;
        public long accountBlockedUntil = 0;
    }

    @Override
    protected void getServiceResult(HttpServletRequest request, Document document) throws Exception {
        boolean logIn = "login".equals(ServiceUtils.getRequiredParameter(request, "authType"));

        HttpSession httpSession = request.getSession();

        // Login
        if (logIn) {

            String authToken = request.getParameter("authToken");

            String user = null;
            String password = null;

            if (authToken != null) {
                try {
                    Class.forName("com.twinsoft.convertigo.eclipse.actions.AdministrationAction")
                            .getMethod("checkAuthToken", String.class).invoke(null, authToken);
                    user = EnginePropertiesManager.getProperty(PropertyName.ADMIN_USERNAME);
                } catch (Throwable t) {
                    authToken = null;
                }
            }

            if (authToken == null) {
                user = ServiceUtils.getRequiredParameter(request, "authUserName");
                password = ServiceUtils.getRequiredParameter(request, "authPassword");
            }

            httpSession.setAttribute(SessionKey.ADMIN_USER.toString(), user);
            Engine.logAdmin.info("User '" + user + "' is trying to login");

            // Check authentication attempts
            AuthenticationAttempt authenticationAttempt = Authenticate.authenticationAttempts.get(user);

            if (authenticationAttempt != null && authenticationAttempt.accountBlockedUntil != 0) {
                long now = System.currentTimeMillis();
                if (now > authenticationAttempt.accountBlockedUntil) {
                    // Unblock the account
                    authenticationAttempt.accountBlockedUntil = 0;
                    authenticationAttempt.numberOfFailedRequests = 0;
                } else {
                    // Continue blocking the account
                    Engine.logAdmin.warn("Detected possible brute force attack: user '" + user
                            + "' has failed to login too many times; authentication request is blocked.");
                    Engine.authenticatedSessionManager.removeAuthenticatedSession(httpSession);

                    long secondsRemaining = (authenticationAttempt.accountBlockedUntil - now) / 1000;

                    ServiceUtils.addMessage(document, document.getDocumentElement(),
                            "The '" + user + "' account is blocked during " + secondsRemaining
                                    + " seconds, due to too many failed authentication attempts.\n\n"
                                    + "Please wait and retry it later.",
                            "error", false);

                    return;
                }
            }

            Role[] roles = null;

            // Legacy authentication
            if (authToken != null || (EnginePropertiesManager.getProperty(PropertyName.ADMIN_USERNAME).equals(user)
                    && EnginePropertiesManager.checkProperty(PropertyName.ADMIN_PASSWORD, password))) {
                roles = new Role[] { Role.WEB_ADMIN, Role.TEST_PLATFORM, Role.AUTHENTICATED };
            } else if (EnginePropertiesManager.getProperty(PropertyName.TEST_PLATFORM_USERNAME).equals(user)
                    && EnginePropertiesManager.checkProperty(PropertyName.TEST_PLATFORM_PASSWORD, password)) {
                roles = new Role[] { Role.TEST_PLATFORM, Role.AUTHENTICATED };
            } else if (Engine.authenticatedSessionManager.hasUser(user)
                    && Engine.authenticatedSessionManager.getPassword(user).equals(DigestUtils.md5Hex(password))) {
                Set<Role> set = Engine.authenticatedSessionManager.getRoles(user);
                roles = new Role[set.size() + 1];
                set.toArray(roles);
                roles[roles.length - 1] = Role.AUTHENTICATED;
            }
            // Trial authentication
            else {
                File hackTrial = new File(Engine.CONFIGURATION_PATH + "/hackTrial.txt");
                if (hackTrial.exists()) {
                    try {
                        BufferedReader br = new BufferedReader(new FileReader(hackTrial));
                        String line = br.readLine();
                        br.close();
                        if (!"ok, you can deploy !!".equals(line)) {
                            Engine.logAdmin.error("Trial authentication failure: wrong internal data!");
                        } else if (user.matches(".+@.+\\.[a-z]+") && user.equals(SimpleCipher.decode(password))) {
                            roles = new Role[] { Role.TRIAL };
                            httpSession.setAttribute("trial_user", true);
                        } else {
                            Engine.logAdmin.error("Trial authentication failure: wrong username/password");
                        }
                    } catch (Exception e) {
                        Engine.logAdmin.error("Trial authentication failure: wrong internal data!", e);
                    }
                }
            }

            if (roles == null) {
                if (authenticationAttempt == null) {
                    //The AuthenticationAttempt object does not exist; we must create a new one
                    authenticationAttempt = new AuthenticationAttempt();
                    Authenticate.authenticationAttempts.put(user, authenticationAttempt);
                }

                Engine.logAdmin.error("Invalid password or user name '" + user + "' (attempt #"
                        + authenticationAttempt.numberOfFailedRequests + ")");

                authenticationAttempt.numberOfFailedRequests++;

                Engine.authenticatedSessionManager.removeAuthenticatedSession(httpSession);

                if (authenticationAttempt.numberOfFailedRequests > Authenticate.MAX_NUMBER_OF_WRONG_AUTHENTICATION_ATTEMPTS) {
                    Engine.logAdmin.warn("Detected possible brute force attack: user '" + user
                            + "' has failed to login too many times; authentication request is blocked.");

                    if (authenticationAttempt.accountBlockedUntil == 0) {
                        authenticationAttempt.accountBlockedUntil = System.currentTimeMillis()
                                + Authenticate.AUTHENTICATION_DELAY * 1000;
                    }

                    long now = System.currentTimeMillis();
                    long secondsRemaining = (authenticationAttempt.accountBlockedUntil - now) / 1000;

                    ServiceUtils.addMessage(document, document.getDocumentElement(),
                            "The '" + user + "' account is blocked during " + secondsRemaining
                                    + " seconds, due to too many failed authentication attempts.\n\n"
                                    + "Please wait and retry it later.",
                            "error", false);
                } else {
                    ServiceUtils.addMessage(document, document.getDocumentElement(),
                            "Invalid authentication!\n\nPlease verify your user ID and/or your password.", "error",
                            false);
                }
            } else {
                Authenticate.authenticationAttempts.remove(user);

                Engine.authenticatedSessionManager.addAuthenticatedSession(httpSession, roles);

                ServiceUtils.addMessage(document, document.getDocumentElement(), "", "success");
                ServiceUtils.addMessage(document, document.getDocumentElement(),
                        "" + httpSession.getAttribute(SessionKey.ADMIN_USER.toString()), "user", false);
                ServiceUtils.addRoleNodes(document.getDocumentElement(), roles);

                SessionAttribute.authenticatedUser.set(httpSession, "c8o:admin");

                Engine.logAdmin.info("User '" + user + "' has been successfully authenticated");
            }
        }
        // Logout
        else {
            Engine.authenticatedSessionManager.removeAuthenticatedSession(httpSession);
            SessionAttribute.authenticatedUser.remove(httpSession);
            ServiceUtils.addMessage(document, document.getDocumentElement(), "", "success");
        }
    }
}