/*
 * Copyright 2017-2018 the original author(https://github.com/wj596)
 * 
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * 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.
 * </p>
 */
package org.jsets.shiro.filter;

import java.util.Date;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionException;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.subject.Subject;
import org.jsets.shiro.cache.CacheDelegator;
import org.jsets.shiro.config.ShiroProperties;
import org.jsets.shiro.listener.AuthListenerManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Strings;

/**
 * 保持账号唯一用户登陆
 * 
 * author wangjie (https://github.com/wj596)
 * @date 2016年6月31日
 * 
 */
public class KeepOneUserFilter extends JsetsAccessControlFilter {

	private static final Logger LOGGER = LoggerFactory.getLogger(KeepOneUserFilter.class);
	
	private final ShiroProperties properties;
	private final CacheDelegator cacheDelegator;
	private final SessionManager sessionManager;
	private final AuthListenerManager authListenerManager;

	public KeepOneUserFilter(ShiroProperties properties,CacheDelegator cacheDelegator
					,SessionManager sessionManager,AuthListenerManager authListenerManager) {
		this.properties = properties;
		this.cacheDelegator = cacheDelegator;
		this.sessionManager = sessionManager;
		this.authListenerManager = authListenerManager;
	}

	@Override
	protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
		if(!this.properties.isKeepOneEnabled()) return true;
		return false;
	}

	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
		Subject subject = getSubject(request, response);
		if (!subject.isAuthenticated() && !subject.isRemembered()) {
			return this.respondLogin(request, response);
		}
		String account = (String) subject.getPrincipal();
		String loginedSessionId = this.cacheDelegator.getKeepUser(account);
		Session loginedSession = null;
		Session currentSession = subject.getSession();
		String currentSessionId = (String) currentSession.getId();
		
		if(currentSessionId.equals(loginedSessionId)) {
			return true;
		} else if (Strings.isNullOrEmpty(loginedSessionId)){
			this.cacheDelegator.putKeepUser(account, currentSessionId);
        	return true;
		} else if (null==currentSession.getAttribute(ShiroProperties.ATTRIBUTE_SESSION_KICKOUT)) {
			this.cacheDelegator.putKeepUser(account, currentSessionId);
			try{
				loginedSession = this.sessionManager.getSession(new DefaultSessionKey(loginedSessionId));
				if(null != loginedSession){
					loginedSession.setAttribute(ShiroProperties.ATTRIBUTE_SESSION_KICKOUT,Boolean.TRUE);
				}
			} catch(SessionException e){
				LOGGER.warn(e.getMessage());
			}
		}
        if (null!=currentSession.getAttribute(ShiroProperties.ATTRIBUTE_SESSION_KICKOUT)) {
        	subject.logout();
        	String loginedHost = "";
        	Date loginedTime = null;
			if(null != loginedSession){
				loginedHost = loginedSession.getHost();
				loginedTime = loginedSession.getStartTimestamp();
			}
			this.authListenerManager.onKeepOneKickout(request, account, loginedHost, loginedTime);
			return this.respondRedirect(request, response,this.properties.getKickoutUrl());
        }

		return true;
	}
}