/*
 * Aipo is a groupware program developed by Aimluck,Inc.
 * Copyright (C) 2004-2008 Aimluck,Inc.
 * http://aipostyle.com/
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU 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/>.
 */
package com.aimluck.eip.services.security;

import java.util.Date;
import java.util.GregorianCalendar;

import javax.servlet.ServletConfig;

import org.apache.jetspeed.om.security.JetspeedUser;
import org.apache.jetspeed.om.security.UserNamePrincipal;
import org.apache.jetspeed.services.JetspeedSecurity;
import org.apache.jetspeed.services.JetspeedUserManagement;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
import org.apache.jetspeed.services.rundata.JetspeedRunData;
import org.apache.jetspeed.services.rundata.JetspeedRunDataService;
import org.apache.jetspeed.services.security.CredentialExpiredException;
import org.apache.jetspeed.services.security.FailedLoginException;
import org.apache.jetspeed.services.security.JetspeedSecurityCache;
import org.apache.jetspeed.services.security.JetspeedSecurityException;
import org.apache.jetspeed.services.security.JetspeedSecurityService;
import org.apache.jetspeed.services.security.LoginException;
import org.apache.jetspeed.services.security.PortalAuthentication;
import org.apache.jetspeed.services.security.UnknownUserException;
import org.apache.turbine.services.InitializationException;
import org.apache.turbine.services.TurbineBaseService;
import org.apache.turbine.services.TurbineServices;
import org.apache.turbine.services.resources.ResourceService;
import org.apache.turbine.services.rundata.RunDataService;

import com.aimluck.eip.common.ALBaseUser;
import com.aimluck.eip.common.ALEipConstants;
import com.aimluck.eip.util.ALCommonUtils;

/**
 * ライセンス検証のためのクラスです。 <br />
 * 
 */
public class ALEipAuthentication extends TurbineBaseService implements
    PortalAuthentication {

  private static final JetspeedLogger logger = JetspeedLogFactoryService
      .getLogger(ALEipAuthentication.class.getName());

  private JetspeedRunDataService runDataService = null;

  private final static String CONFIG_ANONYMOUS_USER = "user.anonymous";

  String anonymousUser = "anon";

  private final static String CACHING_ENABLE = "caching.enable";

  private boolean cachingEnable = true;

  private final static String CONFIG_PASSWORD_EXPIRATION_PERIOD = "password.expiration.period";

  private int expirationPeriod = 0;

  /**
   * 
   * @param username
   * @param cellular_uid
   * @return
   * @throws UnknownUserException
   * @throws JetspeedSecurityException
   */
  public JetspeedUser loginCellularUid(String username, String cellular_uid)
      throws UnknownUserException, JetspeedSecurityException {
    JetspeedUser user = JetspeedUserManagement.getUser(new UserNamePrincipal(
        username));
    ALBaseUser baseUser = (ALBaseUser) user;
    if (cellular_uid == null || cellular_uid.length() == 0
        || !cellular_uid.equals(baseUser.getCelluarUId())) {
      logger.error("Invalid cellular uid for user: " + username);
      throw new UnknownUserException(
          "[ALEipAuthentication] Credential authentication failure");
    }

    return user;
  }

  /**
   * 
   */
  public JetspeedUser login(String username, String password)
      throws LoginException {

    if (username.equals(this.anonymousUser)) {
      throw new LoginException("Anonymous user cannot login");
    }

    JetspeedUser user = null;

    username = JetspeedSecurity.convertUserName(username);
    password = JetspeedSecurity.convertPassword(password);

    if (password.startsWith(ALEipConstants.KEY_CELLULAR_UID)) {
      // 携帯電話の固有 ID でログイン認証する．
      String cellularUid = password.substring(ALEipConstants.KEY_CELLULAR_UID
          .length(), password.length());
      try {
        user = loginCellularUid(username, cellularUid);
      } catch (UnknownUserException e) {
        logger.warn("Unknown user attempted access: " + username, e);
        throw new FailedLoginException(e.toString());
      } catch (JetspeedSecurityException e) {
        logger.warn("User denied authentication: " + username, e);
        throw new LoginException(e.toString());
      }
    } else {
      try {
        user = JetspeedUserManagement.getUser(new UserNamePrincipal(username));
        password = JetspeedSecurity.encryptPassword(password);
      } catch (UnknownUserException e) {
        logger.warn("Unknown user attempted access: " + username, e);
        throw new FailedLoginException(e.toString());
      } catch (JetspeedSecurityException e) {
        logger.warn("User denied authentication: " + username, e);
        throw new LoginException(e.toString());
      }

      if (!user.getPassword().equals(password)) {
        logger.error("Invalid password for user: " + username);
        throw new FailedLoginException("Credential authentication failure");
      }
    }

    // Check for password expiration
    if (this.expirationPeriod > 0) {
      Date passwordLastChangedDate = user.getPasswordChanged();
      Date passwordExpireDate = null;
      if (passwordLastChangedDate != null) {
        GregorianCalendar gcal = (GregorianCalendar) GregorianCalendar
            .getInstance();
        gcal.setTime(passwordLastChangedDate);
        gcal.add(GregorianCalendar.DATE, this.expirationPeriod);
        passwordExpireDate = gcal.getTime();
        if (logger.isDebugEnabled()) {
          logger.debug("TurbineAuthentication: password last changed = "
              + passwordLastChangedDate.toString() + ", password expires = "
              + passwordExpireDate.toString());
        }
      }

      if (passwordExpireDate == null
          || (new Date().getTime() > passwordExpireDate.getTime())) {
        throw new CredentialExpiredException("Password expired");
      }

    }

    // IPA#70075625
    // Sesion Fixation 対策
    JetspeedRunData rundata = getRunData();
    if (rundata != null) {
      // Session ID を再発行する
      rundata.getSession().invalidate();
      rundata.setSession(rundata.getRequest().getSession(true));
    }
    //

    user.setHasLoggedIn(Boolean.valueOf(true));

    try {
      user.updateLastLogin();
      putUserIntoContext(user);
      if (cachingEnable) {
        JetspeedSecurityCache.load(username);
      }
    } catch (Exception e) {
      logger.error("Failed to update last login ", e);
      putUserIntoContext(JetspeedSecurity.getAnonymousUser());
      throw new LoginException("Failed to update last login ", e);
    }

    // for security
    rundata.getUser().setTemp(ALEipConstants.SECURE_ID,
        ALCommonUtils.getSecureRandomBase64());

    return user;
  }

  /**
   * 
   */
  public JetspeedUser getAnonymousUser() throws LoginException {
    JetspeedUser user = null;
    try {
      user = JetspeedUserManagement
          .getUser(new UserNamePrincipal(anonymousUser));
      user.setHasLoggedIn(Boolean.valueOf(false));
      putUserIntoContext(user);
      if (cachingEnable) {
        JetspeedSecurityCache.load(user.getUserName());
      }
    } catch (JetspeedSecurityException e) {
      logger.error("Failed to get anonymous user: ", e);
      throw new LoginException("Failed to get anonymous user: ", e);
    }
    return user;
  }

  /**
   * 
   */
  public void logout() throws LoginException {
    try {
      // if (cachingEnable)
      // {
      // JetspeedSecurityCache.unload(getUserFromContext().getUserName());
      // }
      getAnonymousUser();
    } catch (Exception e) {
      logger.error("Exception logging user out ", e);
      throw new LoginException("Exception logging user out ", e);
    }
  }

  /**
   * 
   */
  public synchronized void init(ServletConfig conf)
      throws InitializationException {
    if (getInit()) {
      return;
    }

    super.init(conf);

    ResourceService serviceConf = ((TurbineServices) TurbineServices
        .getInstance()).getResources(JetspeedSecurityService.SERVICE_NAME);

    anonymousUser = serviceConf.getString(CONFIG_ANONYMOUS_USER, anonymousUser);
    cachingEnable = serviceConf.getBoolean(CACHING_ENABLE, cachingEnable);
    expirationPeriod = serviceConf.getInt(
        this.CONFIG_PASSWORD_EXPIRATION_PERIOD, 0);

    this.runDataService = (JetspeedRunDataService) TurbineServices
        .getInstance().getService(RunDataService.SERVICE_NAME);

    setInit(true);
  }

  protected JetspeedRunData getRunData() {
    JetspeedRunData rundata = null;
    if (this.runDataService != null) {
      rundata = this.runDataService.getCurrentRunData();
    }
    return rundata;
  }

  protected JetspeedUser getUserFromContext() {
    JetspeedRunData rundata = getRunData();
    JetspeedUser user = null;
    if (rundata != null) {
      user = (JetspeedUser) rundata.getUser();
    }
    return user;
  }

  protected JetspeedRunData putUserIntoContext(JetspeedUser user) {
    JetspeedRunData rundata = getRunData();
    if (rundata != null) {

      rundata.setUser(user);
      rundata.save();
    }
    return rundata;
  }
}
