/*
* Copyright 2015 Evgeny Dolganov (evgenij.dolganov@gmail.com).
*
* 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.
*/
package och.chat.service;
import static och.api.model.PropKey.*;
import static och.api.model.chat.account.PrivilegeType.*;
import static och.api.model.client.ClientInfo.*;
import static och.api.model.user.SecurityContext.*;
import static och.chat.service.SecurityService.*;
import static och.comp.chats.common.StoreOps.*;
import static och.util.ConcurrentUtil.*;
import static och.util.StringUtil.*;
import static och.util.Util.*;
import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Future;
import javax.servlet.ServletRequestEvent;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import och.api.exception.InvalidInputException;
import och.api.exception.ValidationException;
import och.api.exception.chat.ChatAccountBlockedException;
import och.api.exception.chat.ChatAccountPausedException;
import och.api.exception.chat.NoAvailableOperatorException;
import och.api.exception.chat.NoChatAccountException;
import och.api.exception.client.AccountNotAddedToSessionException;
import och.api.exception.user.AccessDeniedException;
import och.api.exception.web.MaxSessionsCountByIpException;
import och.api.model.chat.ChatLog;
import och.api.model.chat.ChatLogHist;
import och.api.model.chat.ChatOperator;
import och.api.model.chat.ChatUser;
import och.api.model.chat.Feedback;
import och.api.model.chat.account.PrivilegeType;
import och.api.model.chat.config.Key;
import och.api.model.client.ClientInfo;
import och.api.model.client.ClientSession;
import och.api.model.user.User;
import och.api.model.web.ReqInfo;
import och.api.remote.chats.InitUserTokenReq;
import och.api.remote.chats.RemoveUserSessionReq;
import och.api.remote.chats.UpdateUserSessionsReq;
import och.comp.chats.ChatsAccService;
import och.comp.mail.MailService;
import och.comp.mail.stub.SenderStub;
import och.service.props.impl.MapProps;
import och.util.StringUtil;
import och.util.concurrent.AsyncListener;
import org.junit.Before;
import org.junit.Test;
import test.BaseTest;
import web.MockHttpServletRequest;
import web.MockServletContext;
public class ChatsAppTest extends BaseTest implements AsyncListener {
public static final String TEMPLATES_PATH = "./server-chat/web/WEB-INF/templates";
long reloadAccountsDelay = 50;
MockServletContext servletContext = new MockServletContext();
MapProps props = new MapProps();
ChatsApp app;
ChatsService chats;
SecurityService security;
File accountsDir;
String ip1 = "1.1.1.1";
String ip2 = "2.2.2.2";
String userAgent1 = "userAgent1";
String userAgent2 = "userAgent2";
String session1 = "1";
String session2 = "2";
String session3 = "3";
ClientSession client1 = new ClientSession(session1, ip1, userAgent1);
ClientSession client2 = new ClientSession(session2, ip1, userAgent2);
ClientSession client3 = new ClientSession(session3, ip2, userAgent1);
ChatOperator operator1 = new ChatOperator(1, "name 1");
ChatOperator operator2 = new ChatOperator(2, "name 2");
String text1 = "привет";
String text2 = "\n123\t";
String text3 = "text3";
CopyOnWriteArrayList<Future<?>> futures = new CopyOnWriteArrayList<>();
SenderStub mailSender = new SenderStub();
@Before
public void before() throws Exception {
accountsDir = new File(TEST_DIR, "accounts");
props.putVal(chats_rootDir, accountsDir.getPath());
props.putVal(chats_reloadDelay, reloadAccountsDelay);
props.putVal(templates_path, TEMPLATES_PATH);
props.putVal(mail_storeToDisc, true);
props.putVal(mail_storeDir, TEST_PATH+"/mails");
ChatsAccService accs = new ChatsAccService(new File(props.getStrVal(chats_rootDir)), props, this);
MailService mail = new MailService(mailSender, props, this);
app = ChatsApp.create(props, servletContext, accs, mail);
chats = app.chats;
security = app.security;
}
@Override
public void onFutureEvent(Future<?> future) {
futures.add(future);
}
@Test
public void test_all() throws Exception{
//sec
test_sec_create_clientSession_with_exists_httpSession();
test_sec_create_max_sessions_for_ip();
test_sec_create_user_session();
test_sec_updateUserSessions();
test_sec_logout();
//chat
test_chat_sec();
test_chat_create_remove_update_operators_on_userSession_change();
pushToSecurityContext_SYSTEM_USER();
try {
test_chat_start_with_no_account();
test_chat_start_wtih_no_active_operators();
test_chat_auto_reload_accounts();
test_chat_close_chats_on_close_session();
test_chat_add_comment_to_no_account();
test_chat_init_again();
test_chat_getChatLog();
test_chat_maxClientMsgSizeAndClientAdd();
test_chat_addFeedback();
test_chat_updateUserContacts();
test_chat_restoreOldChatsAfterCrash();
test_chat_blocked();
test_chat_paused();
test_chat_clientRefHist();
}finally {
popUserFromSecurityContext();
}
}
private void test_chat_clientRefHist() throws Exception {
String accId = "test_clientRefHist";
chats.createAcc(accId);
chats.putOperator(accId, operator1);
chats.setActiveOperator(accId, operator1.id);
ClientSession clientSession = createClientSession(accId);
//create chat
try {
ReqInfo.putInfoToThreadLocal(new ReqInfo(null, null, null, "some1", null));
chats.addComment(accId, clientSession, "1");
chats.addComment(accId, clientSession, "2");
ReqInfo.putInfoToThreadLocal(new ReqInfo(null, null, null, "some2", null));
chats.addComment(accId, clientSession, "3");
chats.addComment(accId, clientSession, "4");
} finally {
ReqInfo.removeInfoFromThreadLocal();
}
//create hist
chats.closeChats(clientSession);
lastFrom(futures).get();
//check hist
List<ChatLogHist> history = chats.getHistory(accId, new Date());
assertEquals(1, history.size());
Map<Integer, String> clientRefs = history.get(0).clientRefs;
assertEquals(2, clientRefs.size());
assertEquals("some1", clientRefs.get(0));
assertEquals("some2", clientRefs.get(2));
}
public void test_chat_restoreOldChatsAfterCrash() throws Exception {
String accId1 = "restoreOldChatsAfterCrash";
chats.createAcc(accId1);
chats.putOperator(accId1, operator1);
chats.setActiveOperator(accId1, operator1.id);
HttpSession session = null;
ClientSession clientSession = null;
String chatId = null;
//create chat
{
MockHttpServletRequest req = mockReq();
chats.checkChatAndInitClientSession(req, mockResp(), accId1);
session = req.getSession(false);
clientSession = (ClientSession)session.getAttribute(CLIENT_INFO);
assertEquals(list(accId1), clientSession.getAccIds());
chatId = chats.addComment(accId1, clientSession, "123").id;
chats.addOperatorToChat(accId1, chatId, operator1.id, 0);
chats.addComment(accId1, chatId, operator1.id, "345");
ChatLog chatLog = chats.getChatLog(accId1, clientSession);
assertEquals(2, chatLog.users.size());
assertEquals(2, chatLog.messages.size());
}
//new app
ChatsAccService accs = new ChatsAccService(new File(props.getStrVal(chats_rootDir)), props, this);
MailService mail = new MailService(mailSender, props, this);
ChatsApp app2 = ChatsApp.create(props, servletContext, accs, mail);
ChatsService chats2 = app2.chats;
//restore chat by id
{
MockHttpServletRequest req = mockReq();
chats2.checkChatAndInitClientSession(req, mockResp(), accId1, chatId);
HttpSession session2 = req.getSession(false);
ClientSession clientSession2 = (ClientSession)session2.getAttribute(CLIENT_INFO);
assertEquals(list(accId1), clientSession2.getAccIds());
assertFalse(session.getId().equals(session2.getId()));
assertFalse(clientSession.sessionId.equals(clientSession2.sessionId));
assertNull(chats2.getChatLog(accId1, clientSession));
ChatLog chatLog = chats2.getChatLog(accId1, clientSession2);
assertEquals(chatId, chatLog.id);
assertEquals(2, chatLog.users.size());
assertEquals(2, chatLog.messages.size());
//check all models
assertEquals(1, chats2.getAllActiveChatLogs(accId1).size());
assertEquals(1, chats2.getActiveChatLogs(accId1, operator1.id).size());
}
//no second restore for other session
{
MockHttpServletRequest req = mockReq();
chats2.checkChatAndInitClientSession(req, mockResp(), accId1, chatId);
ClientSession newClientSession = (ClientSession)req.getSession(false).getAttribute(CLIENT_INFO);
assertNull(chats2.getChatLog(accId1, newClientSession));
}
//wrong ip - no restore
{
MockHttpServletRequest req = new MockHttpServletRequest();
req.remoteAddr = "some";
chats2.checkChatAndInitClientSession(req, mockResp(), accId1, chatId);
ClientSession newClientSession = (ClientSession)req.getSession(false).getAttribute(CLIENT_INFO);
assertNull(chats2.getChatLog(accId1, newClientSession));
}
//wrong agent - no restore
{
MockHttpServletRequest req = new MockHttpServletRequest();
req.setUserAgent("1234");
chats2.checkChatAndInitClientSession(req, mockResp(), accId1, chatId);
ClientSession newClientSession = (ClientSession)req.getSession(false).getAttribute(CLIENT_INFO);
assertNull(chats2.getChatLog(accId1, newClientSession));
}
//close all active chats - no more restore
chats.closeChats(clientSession);
getAndClearAllFutures(futures);
{
MockHttpServletRequest req = mockReq();
chats2.checkChatAndInitClientSession(req, mockResp(), accId1, chatId);
ClientSession newClientSession = (ClientSession)req.getSession(false).getAttribute(CLIENT_INFO);
assertNull(chats2.getChatLog(accId1, newClientSession));
}
}
private void test_chat_updateUserContacts() throws Exception {
String accId1 = "updateUserContacts-1";
String accId2 = "updateUserContacts-2";
String accId3 = "updateUserContacts-3";
int userId = 1;
int userId2 = 2;
String initEmail = "e1";
String newEmail = "e2";
chats.createAcc(accId1);
chats.createAcc(accId2);
chats.createAcc(accId3);
chats.putOperator(accId1, new ChatOperator(userId, null, initEmail));
chats.putOperator(accId2, new ChatOperator(userId, null, initEmail));
chats.putOperator(accId3, new ChatOperator(userId2, null, initEmail));
assertEquals(initEmail, chats.getOperator(accId1, userId).email);
assertEquals(initEmail, chats.getOperator(accId2, userId).email);
assertEquals(initEmail, chats.getOperator(accId3, userId2).email);
//update
chats.updateUserContact(userId, newEmail);
getAndClearAllFutures(futures);
assertEquals(newEmail, chats.getOperator(accId1, userId).email);
assertEquals(newEmail, chats.getOperator(accId2, userId).email);
assertEquals(initEmail, chats.getOperator(accId3, userId2).email);
}
private void test_chat_addFeedback() throws Exception {
String accId = randomUUID();
String maxStr = randomStr(props.getIntVal(chats_maxMsgSize));
chats.createAcc(accId);
chats.putOperator(accId, new ChatOperator(1, "some", "op1@mail.com"));
chats.putOperator(accId, new ChatOperator(2, "some", "op2@mail.com"));
//wrong text
try {
chats.addFeedback(accId, randomClientInfo(), maxStr + "1");
}catch(ValidationException e){
//ok
}
int feedbacksCount = 0;
//valid text
{
mailSender.tasks.clear();
ClientInfo client = randomClientInfo();
client.email = "b@b.bb";
client.name = "some name";
String text = "my text\n\n\n123";
chats.addFeedback(accId, client, text);
feedbacksCount++;
getAndClearAllFutures(futures);
List<Feedback> feedbacks = chats.getFeedbacks(accId, new Date());
assertEquals(feedbacksCount, feedbacks.size());
//check mails
assertEquals(2, mailSender.tasks.size());
{
String mailText = mailSender.tasks.get(0).msg.text;
assertTrue(mailText.contains(text));
assertTrue(mailText.contains(client.email));
assertTrue(mailText.contains(client.name));
}
}
//dissable notifications
{
mailSender.tasks.clear();
chats.putAccConfig(accId, Key.feedback_notifyOpsByEmail, "false");
ClientInfo client = randomClientInfo();
client.email = "b@b.bb";
client.name = "some name";
String text = "my text\n\n\n123";
chats.addFeedback(accId, client, text);
feedbacksCount++;
getAndClearAllFutures(futures);
List<Feedback> feedbacks = chats.getFeedbacks(accId, new Date());
assertEquals(feedbacksCount, feedbacks.size());
//check mails
assertEquals(0, mailSender.tasks.size());
chats.putAccConfig(accId, Key.feedback_notifyOpsByEmail, "true");
chats.addFeedback(accId, client, text);
feedbacksCount++;
getAndClearAllFutures(futures);
assertEquals(2, mailSender.tasks.size());
}
//set acc name
{
mailSender.tasks.clear();
String accName = "renamed-acc";
chats.putAccConfig(accId, Key.name, accName);
ClientInfo client = randomClientInfo();
client.email = "b@b.bb";
client.name = "some name";
String text = "my text\n\n\n123";
chats.addFeedback(accId, client, text);
feedbacksCount++;
getAndClearAllFutures(futures);
List<Feedback> feedbacks = chats.getFeedbacks(accId, new Date());
assertEquals(feedbacksCount, feedbacks.size());
//check mails
assertEquals(2, mailSender.tasks.size());
{
String mailText = mailSender.tasks.get(0).msg.text;
assertTrue(mailText.contains(text));
assertTrue(mailText.contains(client.email));
assertTrue(mailText.contains(client.name));
}
}
//empty fields
{
mailSender.tasks.clear();
ClientInfo client = randomClientInfo();
client.email = "some";
client.name = null;
String text = "123";
chats.addFeedback(accId, client, text);
feedbacksCount++;
getAndClearAllFutures(futures);
List<Feedback> feedbacks = chats.getFeedbacks(accId, new Date());
assertEquals(feedbacksCount, feedbacks.size());
//check mails
assertEquals(2, mailSender.tasks.size());
}
//html escape
{
mailSender.tasks.clear();
String accName = "<b>renamed-acc</b>";
chats.putAccConfig(accId, Key.name, accName);
ClientInfo client = randomClientInfo();
client.email = "<b>@b.bb";
client.name = "<b>123</b>";
String text = "<b>my text\n\n\n123</b>";
chats.addFeedback(accId, client, text);
feedbacksCount++;
getAndClearAllFutures(futures);
List<Feedback> feedbacks = chats.getFeedbacks(accId, new Date());
assertEquals(feedbacksCount, feedbacks.size());
//check mails
assertEquals(2, mailSender.tasks.size());
{
String mailText = mailSender.tasks.get(0).msg.text;
assertTrue( ! mailText.contains(text));
assertTrue( ! mailText.contains(client.email));
assertTrue( ! mailText.contains(client.name));
assertTrue( ! mailText.contains(accId));
assertTrue( ! mailText.contains(accName));
}
}
}
private void test_chat_maxClientMsgSizeAndClientAdd() throws Exception {
String accountId = randomSimpleId();
chats.createAcc(accountId);
chats.putOperator(accountId, operator1);
chats.setActiveOperator(accountId, operator1.id);
ClientSession clientSession = createClientSession(accountId);
//add
String invalidText = StringUtil.randomStr(chats_maxMsgSize.intDefVal()+1);
try {
chats.addComment(accountId, clientSession, invalidText);
fail_exception_expected();
}catch(ValidationException e){
//ok
}
ChatLog chatLog = chats.getChatLog(accountId, clientSession);
assertNull(chatLog);
}
private void test_chat_paused() throws Exception {
long userId1 = 100;
long userId2 = 200;
String accId = "test_chat_paused";
chats.createAcc(accId);
assertFalse(chats.isAccPaused(accId));
chats.setAccPaused(accId, true);
assertTrue(chats.isAccPaused(accId));
//можем добавлять оператора в акк и делать его активным
chats.putOperator(accId, new ChatOperator(userId1));
chats.setActiveOperator(accId, userId1);
chats.setAccPaused(accId, false);
assertFalse(chats.isAccPaused(accId));
//создаем клиентский чат
String chatId;
ClientSession clientSession;
{
clientSession = createClientSession(accId);
chats.addComment(accId, clientSession, "some");
ChatLog chatLog = chats.getChatLog(accId, clientSession);
chatId = chatLog.id;
assertEquals(1, chatLog.messages.size());
}
//добавляем коммент при активном чате
{
chats.addOperatorToChat(accId, chatId, userId1, 0);
chats.addComment(accId, chatId, userId1, "some2");
assertEquals(2, chats.getChatLogById(accId, chatId).messages.size());
}
chats.setAccPaused(accId, true);
//не можем добавить коммент при запаузенном чате
try {
chats.addComment(accId, chatId, userId1, "some");
fail_exception_expected();
}catch(ChatAccountPausedException e){
//ok
}
//не можем добавлять новых юзеров к чату
{
chats.putOperator(accId, new ChatOperator(userId2));
chats.setActiveOperator(accId, userId2);
try {
chats.addOperatorToChat(accId, chatId, userId2, 0);
fail_exception_expected();
}catch(ChatAccountPausedException e){
//ok
}
}
//не можем добавлять юзерские комменты
{
try {
chats.addComment(accId, clientSession, "some");
}catch(ChatAccountPausedException e){
//ok
}
}
//можем добавлять фидбеки
{
chats.addFeedback(accId, randomClientInfo(), "some");
}
//проверяем что старт нового инстанса содержит нужные нам данные
{
ChatsService chats2 = ChatsApp.create(props, servletContext).chats;
assertTrue(chats2.isAccPaused(accId));
}
chats.setAccPaused(accId, false);
//как только чат разпаузился -- можем снова работать операторами
{
chats.addOperatorToChat(accId, chatId, userId2, 1);
chats.addComment(accId, chatId, userId1, "some");
chats.addComment(accId, chatId, userId2, "some");
assertEquals(4, chats.getChatLogById(accId, chatId).messages.size());
}
}
private void test_chat_blocked() throws Exception {
long userId1 = 100;
long userId2 = 200;
String accId = "test_chat_blocked";
chats.createAcc(accId);
assertFalse(chats.isAccBlocked(accId));
chats.setAccBlocked(accId, true);
assertTrue(chats.isAccBlocked(accId));
//можем добавлять оператора в акк и делать его активным
chats.putOperator(accId, new ChatOperator(userId1));
chats.setActiveOperator(accId, userId1);
chats.setAccBlocked(accId, false);
assertFalse(chats.isAccBlocked(accId));
//создаем клиентский чат
String chatId;
ClientSession clientSession;
{
clientSession = createClientSession(accId);
chats.addComment(accId, clientSession, "some");
ChatLog chatLog = chats.getChatLog(accId, clientSession);
chatId = chatLog.id;
assertEquals(1, chatLog.messages.size());
}
//добавляем коммент при активном чате
{
chats.addOperatorToChat(accId, chatId, userId1, 0);
chats.addComment(accId, chatId, userId1, "some2");
assertEquals(2, chats.getChatLogById(accId, chatId).messages.size());
}
chats.setAccBlocked(accId, true);
//не можем добавить коммент при блокированном чате
try {
chats.addComment(accId, chatId, userId1, "some");
fail_exception_expected();
}catch(ChatAccountBlockedException e){
//ok
}
//не можем добавлять новых юзеров к чату
{
chats.putOperator(accId, new ChatOperator(userId2));
chats.setActiveOperator(accId, userId2);
try {
chats.addOperatorToChat(accId, chatId, userId2, 0);
fail_exception_expected();
}catch(ChatAccountBlockedException e){
//ok
}
}
//не можем добавлять юзерские комменты к блокированному чату
{
try {
chats.addComment(accId, clientSession, "some");
}catch(ChatAccountBlockedException e){
//ok
}
}
//не можем добавлять фидбеки
{
try {
chats.addFeedback(accId, randomClientInfo(), "some");
}catch(ChatAccountBlockedException e){
//ok
}
}
//проверяем что старт нового инстанса содержит нужные нам данные
{
ChatsService chats2 = ChatsApp.create(props, servletContext).chats;
assertTrue(chats2.isAccBlocked(accId));
}
chats.setAccBlocked(accId, false);
//как только чат разблокировался -- можем снова работать операторами
{
chats.addOperatorToChat(accId, chatId, userId2, 1);
chats.addComment(accId, chatId, userId1, "some");
chats.addComment(accId, chatId, userId2, "some");
assertEquals(4, chats.getChatLogById(accId, chatId).messages.size());
}
}
private void test_chat_create_remove_update_operators_on_userSession_change() throws Exception {
String accId1 = "acc1-"+randomSimpleId();
String accId2 = "acc2-"+randomSimpleId();
long userId = 100;
pushToSecurityContext_SYSTEM_USER();
try {
chats.createAcc(accId1);
chats.createAcc(accId2);
chats.putOperator(accId1, new ChatOperator(userId));
chats.putOperator(accId2, new ChatOperator(userId));
assertFalse(chats.isActiveOperator(accId1, userId));
}finally {
popUserFromSecurityContext();
}
String token = "some";
MockHttpServletRequest req = mockReq();
String userIp = req.getRemoteAddr();
String userUserAgent = "someAgent";
req.setUserAgent(userUserAgent);
//init session
Map<String, Set<PrivilegeType>> privsByAcc = new HashMap<>();
privsByAcc.put(accId1, set(CHAT_OPERATOR));
security.initUserToken(new InitUserTokenReq(token, userId, userIp, userUserAgent, privsByAcc));
security.initUserSession(req, token);
HttpSession session = req.getSession(false);
assertNotNull(session);
security.sessionCreated(new HttpSessionEvent(session));
pushToSecurityContext_SYSTEM_USER();
try {
assertFalse(chats.isActiveOperator(accId1, userId));
assertFalse(chats.isActiveOperator(accId2, userId));
}finally {
popUserFromSecurityContext();
}
//save session to holder
app.sessionsHolder.sessionCreated(new HttpSessionEvent(session));
//update session
privsByAcc.remove(accId1);
privsByAcc.put(accId2, set(CHAT_OPERATOR));
security.updateUserSessions(new UpdateUserSessionsReq(userId, privsByAcc));
pushToSecurityContext_SYSTEM_USER();
try {
assertTrue(chats.isOperator(accId1, userId));
assertTrue(chats.isOperator(accId2, userId));
assertFalse(chats.isActiveOperator(accId1, userId));
assertFalse(chats.isActiveOperator(accId2, userId));
}finally {
popUserFromSecurityContext();
}
//обновление активности оператора для одного акка
pushToSecurityContext_SYSTEM_USER();
try {
chats.setActiveOperator(accId1, userId);
chats.setActiveOperator(accId2, userId);
assertTrue(chats.isActiveOperator(accId1, userId));
assertTrue(chats.isActiveOperator(accId2, userId));
//удаляем привилегию только в одной сессии
Map<String, Set<PrivilegeType>> newPrivs = new HashMap<>();
newPrivs.put(accId1, set(CHAT_OPERATOR));
newPrivs.put(accId2, set(CHAT_OPERATOR));
newPrivs.put("some-other-acc", set(CHAT_OPERATOR));
security.updateUserSessions(new UpdateUserSessionsReq(userId, privsByAcc));
assertTrue(chats.isActiveOperator(accId1, userId));
assertTrue(chats.isActiveOperator(accId2, userId));
}finally {
popUserFromSecurityContext();
}
//remove session
security.sessionDestroyed(new HttpSessionEvent(session));
pushToSecurityContext_SYSTEM_USER();
try {
assertFalse(chats.isActiveOperator(accId1, userId));
assertFalse(chats.isActiveOperator(accId2, userId));
}finally {
popUserFromSecurityContext();
}
}
private void test_sec_logout() throws Exception {
pushToSecurityContext_SYSTEM_USER();
try {
long user1 = 1;
long user2 = 2;
String accId1 = "test_sec_logout-1";
String accId2 = "test_sec_logout-2";
//create accs
{
app.chats.createAcc(accId1);
app.chats.createAcc(accId2);
}
//register users
{
app.chats.putOperator(accId1, new ChatOperator(user1));
app.chats.putOperator(accId2, new ChatOperator(user2));
assertFalse(app.chats.isActiveOperator(accId1, user1));
assertFalse(app.chats.isActiveOperator(accId2, user2));
}
//login user
MockHttpServletRequest req = mockReq();
String token1 = "valid1";
{
String userIp = req.getRemoteAddr();
String userUserAgent = "someAgent";
req.setUserAgent(userUserAgent);
Map<String, Set<PrivilegeType>> initPrivs = map(accId1, set(PrivilegeType.CHAT_OPERATOR));
security.initUserToken(new InitUserTokenReq(token1, user1, userIp, userUserAgent, initPrivs));
security.initUserSession(req, token1);
app.sessionsHolder.sessionCreated(new HttpSessionEvent(req.getSession(false)));
//юзер стал активным каскадно после секьюрити сервиса - отключили эту фишку
//assertTrue(app.chats.isActiveOperator(accId1, user1));
assertFalse(app.chats.isActiveOperator(accId1, user1));
//делаем активным юзера через явный вызов апи чатов
assertFalse(app.chats.isActiveOperator(accId2, user2));
app.chats.setActiveOperator(accId2, user2);
assertTrue(app.chats.isActiveOperator(accId2, user2));
}
//logout user
{
security.removeUserSession(new RemoveUserSessionReq(token1));
assertFalse(app.chats.isActiveOperator(accId1, user1));
assertTrue(app.chats.isActiveOperator(accId2, user2));
}
}finally {
popUserFromSecurityContext();
}
}
private void test_sec_updateUserSessions() throws Exception {
MockHttpServletRequest req = mockReq();
String userIp = req.getRemoteAddr();
String userUserAgent = "someAgent";
req.setUserAgent(userUserAgent);
String token = "valid";
long userId = 99;
//init session
Map<String, Set<PrivilegeType>> initPrivs = new HashMap<>();
initPrivs.put("test", new HashSet<PrivilegeType>());
security.initUserToken(new InitUserTokenReq(token, userId, userIp, userUserAgent, initPrivs));
assertEquals(userId, security.initUserSession(req, token));
User user1 = security.getUserFromSession(req);
Map<String, Set<PrivilegeType>> privs1 = user1.getParam(PRIVILEGES_PARAM);
assertNotNull(privs1.get("test"));
//add session to holder
app.sessionsHolder.sessionCreated(new HttpSessionEvent(req.getSession(false)));
//update session
Map<String, Set<PrivilegeType>> newPrivs = new HashMap<>();
newPrivs.put("test2", new HashSet<PrivilegeType>());
security.updateUserSessions(new UpdateUserSessionsReq(userId, newPrivs));
//check updated session
User user2 = security.getUserFromSession(req);
Map<String, Set<PrivilegeType>> privs2 = user2.getParam(PRIVILEGES_PARAM);
assertNotSame(privs1, privs2);
assertNull(privs2.get("test"));
assertNotNull(privs2.get("test2"));
//remove privs
security.updateUserSessions(new UpdateUserSessionsReq(userId, null));
User user3 = security.getUserFromSession(req);
Map<String, Set<PrivilegeType>> privs3 = user3.getParam(PRIVILEGES_PARAM);
assertNotNull(privs3);
assertTrue(privs3.size() == 0);
}
private void test_chat_sec() throws Exception {
String accId = randomSimpleId();
//create
{
try {
chats.createAcc(accId);
fail_exception_expected();
}catch (AccessDeniedException e) {
//ok
}
pushToSecurityContext_SYSTEM_USER();
try {
chats.createAcc(accId);
} finally {
popUserFromSecurityContext();
}
}
//get acc info
{
try {
chats.getAllActiveChatLogs(accId);
fail_exception_expected();
}catch (AccessDeniedException e) {
//ok
}
//by admin
pushToSecurityContext_SYSTEM_USER();
try {
chats.getAllActiveChatLogs(accId);
} finally {
popUserFromSecurityContext();
}
//by acc user
Map<String, Set<PrivilegeType>> privs = new HashMap<>();
privs.put(accId, set(CHAT_OPERATOR));
User user = new User(operator1.id);
user.putParam(SecurityService.PRIVILEGES_PARAM, privs);
pushToSecurityContext(user);
try {
chats.getAllActiveChatLogs(accId);
}finally {
popUserFromSecurityContext();
}
}
}
private void test_sec_create_user_session() throws Exception {
MockHttpServletRequest req = mockReq();
String userIp = req.getRemoteAddr();
String userUserAgent = "someAgent";
req.setUserAgent(userUserAgent);
String token1 = "valid1";
String token2 = "valid2";
String token3 = "valid3";
int userId1 = 99;
int userId2 = 999;
//no token in req
try {
security.initUserSession(req, null);
fail_exception_expected();
}catch (InvalidInputException e) {
assertExceptionWithText(e, "reqToken is null");
}
//bad token
try {
security.initUserSession(req, "unknown");
fail_exception_expected();
}catch (InvalidInputException e) {
assertExceptionWithText(e, "no data by token");
}
//bad client ip
security.initUserToken(new InitUserTokenReq("bad-ip", userId1, "some-bad-ip", userUserAgent));
try {
security.initUserSession(req, "bad-ip");
fail_exception_expected();
}catch (AccessDeniedException e) {
assertExceptionWithText(e,"invalid client ip");
}
//bad user agent
security.initUserToken(new InitUserTokenReq("bad-user-agent", userId1, userIp, "other-user-agent"));
try {
security.initUserSession(req, "bad-user-agent");
fail_exception_expected();
}catch (AccessDeniedException e) {
assertExceptionWithText(e, "invalid userAgent");
}
//token was deleted
props.putVal(users_waitChatSessionTokenLivetime, 1);
security.initUserToken(new InitUserTokenReq("token-deleted", userId1, userIp, userUserAgent));
Thread.sleep(10);
try {
security.initUserSession(req, "token-deleted");
fail_exception_expected();
}catch (InvalidInputException e) {
assertExceptionWithText(e, "no data by token");
}
//valid
props.removeVal(users_waitChatSessionTokenLivetime);
//with privs
Map<String, Set<PrivilegeType>> initPrivs = new HashMap<>();
initPrivs.put("test", new HashSet<PrivilegeType>());
security.initUserToken(new InitUserTokenReq(token1, userId1, userIp, userUserAgent, initPrivs));
security.initUserToken(new InitUserTokenReq(token2, userId2, userIp, userUserAgent));
security.initUserToken(new InitUserTokenReq(token3, userId1, userIp, userUserAgent));
assertEquals(userId1, security.initUserSession(req, token1));
//again
assertEquals(userId1, security.initUserSession(req, token1));
//other token with same user id
assertEquals(userId1, security.initUserSession(req, token3));
//token with wrong user id
try {
security.initUserSession(req, token2);
fail_exception_expected();
}catch (InvalidInputException e) {
assertExceptionWithText(e, "already has a session for other user");
}
//check session
{
User user = security.getUserFromSession(req);
assertEquals(userId1, user.id);
Map<String, Set<PrivilegeType>> privs = user.getParam(PRIVILEGES_PARAM);
assertNotNull(privs.get("test"));
}
//add session to holder
app.sessionsHolder.sessionCreated(new HttpSessionEvent(req.getSession(false)));
//remove session
security.removeUserSession(new RemoveUserSessionReq(token1));
assertNull(security.getUserFromSession(req));
}
private void test_chat_getChatLog(){
String accountId = randomSimpleId();
chats.createAcc(accountId);
chats.putOperator(accountId, operator1);
chats.setActiveOperator(accountId, operator1.id);
ClientSession clientSession = createClientSession(accountId);
//before add
assertNull(chats.getChatLog(accountId, clientSession));
//add
String text = "test";
chats.addComment(accountId, clientSession, text);
ChatLog chatLog = chats.getChatLog(accountId, clientSession);
assertNotNull(chatLog);
assertEquals(1, chatLog.messages.size());
assertEquals(text, chatLog.messages.get(0).text);
assertEquals(0, chatLog.operators.size());
chats.addOperatorToChat(accountId, chatLog.id, operator1.id, 0);
chatLog = chats.getChatLog(accountId, clientSession);
{
List<ChatUser> users = chatLog.users;
assertEquals(2, users.size());
String operator = chatLog.operators.get(users.get(1).operatorId);
assertEquals(operator1.name, operator);
}
}
private void test_chat_init_again() {
String accountId1 = randomSimpleId();
String accountId2 = randomSimpleId();
MockHttpServletRequest req = mockReq();
chats.createAcc(accountId1);
chats.createAcc(accountId2);
chats.putOperator(accountId1, operator1);
chats.putOperator(accountId2, operator1);
chats.setActiveOperator(accountId1, operator1.id);
chats.setActiveOperator(accountId2, operator1.id);
//double
chats.checkChatAndInitClientSession(req, mockResp(), accountId1);
chats.checkChatAndInitClientSession(req, mockResp(), accountId1);
HttpSession session = req.getSession(false);
ClientSession clientSession = (ClientSession)session.getAttribute(CLIENT_INFO);
assertEquals(1, clientSession.getAccIds().size());
assertEquals(list(accountId1), clientSession.getAccIds());
//double
chats.checkChatAndInitClientSession(req, mockResp(), accountId2);
chats.checkChatAndInitClientSession(req, mockResp(), accountId2);
assertEquals(2, clientSession.getAccIds().size());
assertEquals(list(accountId1, accountId2), clientSession.getAccIds());
}
private void test_chat_add_comment_to_no_account() {
String accountId = randomSimpleId();
chats.createAcc(accountId);
chats.putOperator(accountId, operator1);
chats.setActiveOperator(accountId, operator1.id);
ClientSession clientSession = createClientSession(accountId);
chats.removeAcc(accountId);
try {
chats.addComment(accountId, clientSession, "123");
fail_exception_expected();
}catch (NoChatAccountException e) {
//ok
}
}
private void test_chat_close_chats_on_close_session() throws Exception {
String accId1 = randomSimpleId();
String accId2 = randomSimpleId();
String accId3 = randomSimpleId();
MockHttpServletRequest req = mockReq();
req.remoteAddr = ip2;
chats.createAcc(accId1);
chats.createAcc(accId2);
chats.createAcc(accId3);
chats.putOperator(accId1, operator1);
chats.putOperator(accId2, operator1);
chats.putOperator(accId3, operator1);
chats.setActiveOperator(accId1, operator1.id);
chats.setActiveOperator(accId2, operator1.id);
chats.setActiveOperator(accId3, operator1.id);
chats.checkChatAndInitClientSession(req, mockResp(), accId1);
chats.checkChatAndInitClientSession(req, mockResp(), accId2);
HttpSession session = req.getSession(false);
ClientSession clientSession = (ClientSession)session.getAttribute(CLIENT_INFO);
assertNotNull(clientSession);
assertEquals(2, clientSession.getAccIds().size());
assertEquals(list(accId1, accId2), clientSession.getAccIds());
chats.addComment(accId1, clientSession, "123");
chats.addComment(accId2, clientSession, "345");
try {
chats.addComment(accId3, clientSession, "345");
fail_exception_expected();
}catch (AccountNotAddedToSessionException e) {
//ok
}
//close by event
app.security.sessionDestroyed(new HttpSessionEvent(session));
try {
chats.addComment(accId1, clientSession, "123");
fail_exception_expected();
}catch (AccountNotAddedToSessionException e) {
//ok
}
try {
chats.addComment(accId2, clientSession, "345");
fail_exception_expected();
}catch (AccountNotAddedToSessionException e) {
//ok
}
//close by method
chats.checkChatAndInitClientSession(req, mockResp(), accId1);
chats.addComment(accId1, clientSession, "123");
chats.closeChats(clientSession);
try {
chats.addComment(accId1, clientSession, "123");
fail_exception_expected();
}catch (AccountNotAddedToSessionException e) {
//ok
}
}
private void test_chat_auto_reload_accounts() throws Exception {
String accountId = randomSimpleId();
try {
chats.checkChatAndInitClientSession(mockReq(), mockResp(), accountId);
fail_exception_expected();
}catch (NoChatAccountException e) {
//ok
}
File account2Dir = new File(accountsDir, getAccountDirName(accountId));
assertFalse(account2Dir.exists());
assertTrue(account2Dir.mkdir());
Thread.sleep(reloadAccountsDelay*2);
chats.putOperator(accountId, operator1);
chats.setActiveOperator(accountId, operator1.id);
chats.checkChatAndInitClientSession(mockReq(), mockResp(), accountId);
}
private void test_chat_start_wtih_no_active_operators(){
String accountId = randomSimpleId();
//no operators
chats.createAcc(accountId);
try {
chats.checkChatAndInitClientSession(mockReq(), mockResp(), accountId);
fail_exception_expected();
}catch (NoAvailableOperatorException e) {/*ok*/}
//no operators again
chats.setActiveOperator(accountId, operator1.id);
try {
chats.checkChatAndInitClientSession(mockReq(), mockResp(), accountId);
fail_exception_expected();
}catch (NoAvailableOperatorException e) {/*ok*/}
//no active operators
chats.putOperator(accountId, operator1);
try {
chats.checkChatAndInitClientSession(mockReq(), mockResp(), accountId);
fail_exception_expected();
}catch (NoAvailableOperatorException e) {/*ok*/}
//1 active operator
chats.setActiveOperator(accountId, operator1.id);
chats.checkChatAndInitClientSession(mockReq(), mockResp(), accountId);
//no active operators
chats.removeActiveOperator(accountId, operator1.id);
try {
chats.checkChatAndInitClientSession(mockReq(), mockResp(), accountId);
fail_exception_expected();
}catch (NoAvailableOperatorException e) {/*ok*/}
chats.setActiveOperator(accountId, operator1.id);
chats.checkChatAndInitClientSession(mockReq(), mockResp(), accountId);
//no operators
chats.removeOperator(accountId, operator1.id);
try {
chats.checkChatAndInitClientSession(mockReq(), mockResp(), accountId);
fail_exception_expected();
}catch (NoAvailableOperatorException e) {/*ok*/}
}
private void test_chat_start_with_no_account(){
String accountId = randomSimpleId();
try {
chats.checkChatAndInitClientSession(mockReq(), mockResp(), accountId);
fail_exception_expected();
}catch (NoChatAccountException e) {
//ok
}
}
private void test_sec_create_clientSession_with_exists_httpSession(){
MockHttpServletRequest req = mockReq();
//first call
{
security.initClientSessionForAcc(req, "some");
ClientSession clientSession = (ClientSession)req.getSession().getAttribute(CLIENT_INFO);
assertNotNull(clientSession);
assertTrue(clientSession.containsAccId("some"));
}
//second call
{
security.initClientSessionForAcc(req, "some");
security.initClientSessionForAcc(req, "some2");
ClientSession clientSession = (ClientSession)req.getSession().getAttribute(CLIENT_INFO);
assertTrue(clientSession.containsAccId("some"));
assertTrue(clientSession.containsAccId("some2"));
}
//remove session obj
{
req.getSession().removeAttribute(CLIENT_INFO);
security.initClientSessionForAcc(req, "some");
ClientSession clientSession = (ClientSession)req.getSession().getAttribute(CLIENT_INFO);
assertNotNull(clientSession);
assertTrue(clientSession.containsAccId("some"));
assertFalse(clientSession.containsAccId("some2"));
}
}
private void test_sec_create_max_sessions_for_ip() {
//default max count
Integer maxCount = security.getMaxSessionsCountForChatClient(ip1);
assertEquals(chats_maxSessionsByIP.intDefVal(), maxCount);
MockHttpServletRequest lastReq = null;
for (int i = 0; i < maxCount; i++) {
lastReq = mockReq();
lastReq.remoteAddr = ip1;
app.sessionsCounter.requestInitialized(new ServletRequestEvent(servletContext, lastReq));
security.initClientSessionForAcc(lastReq, null);
app.sessionsCounter.sessionCreated(new HttpSessionEvent(lastReq.getSession(false)));
}
//try more then max
try {
MockHttpServletRequest req = mockReq();
req.remoteAddr = ip1;
app.sessionsCounter.requestInitialized(new ServletRequestEvent(servletContext, req));
security.initClientSessionForAcc(req, null);
fail_exception_expected();
}catch (MaxSessionsCountByIpException e) {
//ok
}
//try other
{
MockHttpServletRequest req = mockReq();
req.remoteAddr = ip2;
app.sessionsCounter.requestInitialized(new ServletRequestEvent(servletContext, req));
security.initClientSessionForAcc(req, null);
}
//destroy one
{
app.sessionsCounter.sessionDestroyed(new HttpSessionEvent(lastReq.getSession(false)));
MockHttpServletRequest req = mockReq();
req.remoteAddr = ip1;
app.sessionsCounter.requestInitialized(new ServletRequestEvent(servletContext, req));
security.initClientSessionForAcc(req, null);
}
//change max count
int newMaxForAll = chats_maxSessionsByIP.intDefVal() + 10;
int newMaxForIp1 = newMaxForAll + 10;
props.putVal(chats_maxSessionsByIP, newMaxForAll);
assertEquals(newMaxForAll, security.getMaxSessionsCountForChatClient(ip1));
assertEquals(newMaxForAll, security.getMaxSessionsCountForChatClient(ip2));
props.putVal(chats_maxSessionsByIP+"_"+ip1, newMaxForIp1);
assertEquals(newMaxForIp1, security.getMaxSessionsCountForChatClient(ip1));
assertEquals(newMaxForAll, security.getMaxSessionsCountForChatClient(ip2));
//with new max ip
app.sessionsCounter.clearSessionsCount(ip1);
for (int i = 0; i < newMaxForIp1; i++) {
lastReq = mockReq();
lastReq.remoteAddr = ip1;
app.sessionsCounter.requestInitialized(new ServletRequestEvent(servletContext, lastReq));
security.initClientSessionForAcc(lastReq, null);
app.sessionsCounter.sessionCreated(new HttpSessionEvent(lastReq.getSession(false)));
}
//try more then max
try {
MockHttpServletRequest req = mockReq();
req.remoteAddr = ip1;
app.sessionsCounter.requestInitialized(new ServletRequestEvent(servletContext, req));
security.initClientSessionForAcc(req, null);
fail_exception_expected();
}catch (MaxSessionsCountByIpException e) {
//ok
}
}
private ClientSession createClientSession(String accId){
MockHttpServletRequest req = mockReq();
chats.checkChatAndInitClientSession(req, mockResp(), accId);
HttpSession session = req.getSession(false);
return (ClientSession)session.getAttribute(CLIENT_INFO);
}
}