/*
 * 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.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
//Add 20110705 小濵 Start ---
import java.util.Calendar;
//Add 20110705 小濵 End -----

import javax.servlet.ServletConfig;

import org.apache.cayenne.access.DataContext;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
import org.apache.cayenne.query.SelectQuery;
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.commons.field.ALStringField;
import com.aimluck.eip.common.ALBaseUser;
import com.aimluck.eip.common.ALEipConstants;
import com.aimluck.eip.orm.DatabaseOrmService;
import com.aimluck.eip.services.datasync.ALDataSyncFactoryService;
import com.aimluck.eip.util.ALCommonUtils;
//Add 20110705 小濵 Start ---
import com.aimluck.eip.cayenne.om.portlet.ImwMHanyo;
//Add 20110705 小濵 End -----

/**
 * ライセンス検証のためのクラスです。 <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);
    //Add 20110706 小濵 Start ---
    String old_password = JetspeedSecurity.convertPassword(password);
    //Add 20110706 小濵 Start ---    
    
    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");
      }
      //Add 20110630 小濵 Start ---
      JetspeedRunData data = getRunData();
      ALStringField new_passwd = new ALStringField();
      new_passwd.setFieldName("新しいパスワード");
      ALStringField new_passwd_confirm = new ALStringField();
      new_passwd_confirm.setFieldName("新しいパスワード(確認用)");
      new_passwd.setTrim(true);
      new_passwd_confirm.setTrim(true);

      // 新しいパスワード
      new_passwd.setNotNull(true);
      new_passwd.setCharacterType(ALStringField.TYPE_ALPHABET_NUMBER);
      new_passwd.limitMaxLength(16);

      // 新しいパスワード（確認用）
      new_passwd_confirm.setNotNull(true);
      new_passwd_confirm.setCharacterType(ALStringField.TYPE_ALPHABET_NUMBER);
      new_passwd_confirm.limitMaxLength(16);

      new_passwd.setValue(data.getParameters().getString("new_password", ""));
      new_passwd_confirm.setValue(data.getParameters().getString("new_password_confirm", ""));
      String change_password = data.getParameters().getString("change_password");
      
      ArrayList msgList = new ArrayList();
      if(change_password != null){
        new_passwd.validate(msgList);
        new_passwd_confirm.validate(msgList);
        if (!new_passwd.toString().equals(new_passwd_confirm.toString())) {
          msgList.add("確認用のパスワードと一致しません。");
        }else if(old_password.equals(new_passwd.toString())){
          msgList.add("同じパスワードには変更できません。別のパスワードを入力してください。");
        }
        if(msgList != null && msgList.size() > 0){
          String message = "";
          for(int i = 0;i < msgList.size();i++){
            if(i > 0){
              message += "<br>";
            }
            message += (String)msgList.get(i);
          }
          throw new FailedLoginException(message);
        }else{
          updatePassword(user,new_passwd.getValue());
        }
      }
      //Add 20110630 小濵 End -----
      //Add 20110628 小濵 Start ---
      if(change_password == null){
        ALBaseUser baseUser = (ALBaseUser) user;
        if(baseUser.checkPasswordEndDate() < 0){
          throw new FailedLoginException("このユーザーはパスワード有効期限を過ぎています。パスワードを変更してください。");
        }
      }
      //Add 20110628 小濵 End -----
    }

    // 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;
  }
  //Add 20110630 小濵 Start ---
  public boolean updatePassword(JetspeedUser user,String new_passwd){
    boolean res = true;
    try {
    ALBaseUser baseUser = (ALBaseUser) user;
    if (baseUser == null)
      return false;

    // 新しいパスワードをセットする
    JetspeedSecurity.forcePassword(baseUser, new_passwd);

    //パスワード有効期限をセットする
    Calendar cal = Calendar.getInstance();
    int passwordTimeLimit = getPasswordTimeLimit();
    cal.add(Calendar.DAY_OF_MONTH, passwordTimeLimit);
    baseUser.setPasswordEndDate(cal.getTime());
    
    // ユーザを更新する
    JetspeedSecurity.saveUser(user);

    // WebAPIとのDB同期
    if (!ALDataSyncFactoryService.getInstance().getDataSyncHandler()
        .updateUser(baseUser)) {
      return false;
    }

  } catch (Exception e) {
    logger.error("Exception", e);
    res = false;
  }
  return res;
  }
  
  public int getPasswordTimeLimit(){
    int passwordTimeLimit = 0;
    DataContext dataContext = DatabaseOrmService.getInstance()
    .getDataContext();
    SelectQuery query = new SelectQuery(ImwMHanyo.class);
    Expression exp1 = ExpressionFactory.matchExp(
        ImwMHanyo.PARENT_HANYO_ID_PROPERTY, 14);
    query.setQualifier(exp1);
    Expression exp2 = ExpressionFactory.matchExp(
        ImwMHanyo.DISABLED_PROPERTY, "F");
    query.andQualifier(exp2);
    
    List aList = dataContext.performQuery(query);
    if(aList == null || aList.size() == 0){
      return 0;
    }
    for (int i = 0; i < aList.size(); i++) {
      ImwMHanyo record = (ImwMHanyo) aList.get(i);
      passwordTimeLimit = Integer.parseInt(record.getValue());
    }
    return passwordTimeLimit;
  }
  //Add 20110630 小濵 End -----
}
