package com.company.silentlogin.service;

import org.springframework.test.AbstractDependencyInjectionSpringContextTests;
import org.apache.commons.codec.binary.Base64;
import org.easymock.MockControl;

import java.security.MessageDigest;
import java.util.*;

import com.company.security.service.SecurityService;
import com.company.security.User;
import com.company.menu.service.MenuElementService;
import com.company.silentlogin.SilentLoginProfile;

/**
 * Unit tests for the SilentLoginServiceImpl class.
 */
public class SilentLoginServiceImplTest extends AbstractDependencyInjectionSpringContextTests
{
   private SilentLoginServiceImpl ta;
   private MessageDigest digester;
   private MockControl mockSecurityServiceControl;
   private SecurityService securityService;
   private MockControl mockMenuElementServiceControl;
   private MenuElementService menuElementService;

   /**
    * @see org.springframework.test.AbstractDependencyInjectionSpringContextTests#getConfigLocations()
    */
   protected String[] getConfigLocations()
   {
      return new String[] { "com/nokia/support/silentlogin/dao/cache/test-context.xml" };
   }

   /**
    * @see org.springframework.test.AbstractDependencyInjectionSpringContextTests#onSetUp()
    */
   protected void onSetUp() throws Exception
   {
      mockMenuElementServiceControl = MockControl.createControl(MenuElementService.class);
      menuElementService = (MenuElementService) mockMenuElementServiceControl.getMock();

      mockSecurityServiceControl = MockControl.createControl(SecurityService.class);
      securityService = (SecurityService) mockSecurityServiceControl.getMock();

      ta = (SilentLoginServiceImpl) getContext(contextKey()).getBean("silentLoginService");
      ta.setSecurityService(securityService);
      ta.setMenuElementService(menuElementService);
      ta.setDigestAlgorithm("SHA-1");
      digester = MessageDigest.getInstance("SHA-1");
   }

   public void testOneTimeUse() throws Exception
   {
      String ticket = ta.register("APP", "EISENHAU");
      assertTrue(ta.authenticate(createDigestedKey("APP", ticket), "EISENHAU"));
      assertFalse(ta.authenticate(createDigestedKey("APP", ticket), "EISENHAU"));
   }

   public void testNonMatchingKey() throws Exception
   {
      String ticket = ta.register("APP", "EISENHAU");
      assertFalse(ta.authenticate(createDigestedKey("XXX", ticket), "EISENHAU"));
   }

   public void testNonMatchingUsername() throws Exception
   {
      String ticket = ta.register("APP", "EISENHAU");
      assertFalse(ta.authenticate(createDigestedKey("APP", ticket), "XXXXX"));
      assertFalse(ta.authenticate(createDigestedKey("APP", ticket), "EISENHAU"));
   }

   public void testMultipleTicketsForSameUsername() throws Exception
   {
      String ticket1 = ta.register("APP", "EISENHAU");
      String ticket2 = ta.register("APP", "EISENHAU");
      String ticket3 = ta.register("APP", "EISENHAU");
      assertTrue(ta.authenticate(createDigestedKey("APP", ticket1), "EISENHAU"));
      assertTrue(ta.authenticate(createDigestedKey("APP", ticket2), "EISENHAU"));
      assertTrue(ta.authenticate(createDigestedKey("APP", ticket3), "EISENHAU"));
   }

   public void testGeneratePayloadWithEmptyRequest() throws Exception
   {      
      assertTrue(ta.generatePayload(Locale.US, "EISENHAU", new HashSet()).isEmpty());
   }

   public void testGeneratePayloadForMenus() throws Exception
   {
      List menuElements = new ArrayList();
      menuElements.add("test");
      Set payloadTokens = new HashSet();
      payloadTokens.add("menus");
      menuElementService.getElementsForUser(Locale.US, "EISENHAU");
      mockMenuElementServiceControl.setReturnValue(menuElements);
      mockMenuElementServiceControl.replay();
      Map payload = ta.generatePayload(Locale.US, "EISENHAU", payloadTokens);
      assertTrue(payload.containsKey("menus"));
      List returnedMenuElements = (List) payload.get("menus");
      assertFalse(returnedMenuElements.isEmpty());
      assertEquals("test", returnedMenuElements.get(0));
      mockMenuElementServiceControl.verify();
   }

   public void testGeneratePayloadForRoles() throws Exception
   {
      List roles = new ArrayList();
      roles.add("ROLE_KMSS_VIEWER");
      Set payloadTokens = new HashSet();
      payloadTokens.add("roles");
      securityService.getAuthorizations(User.createUser("EISENHAU"));
      mockSecurityServiceControl.setReturnValue(roles);
      mockSecurityServiceControl.replay();
      Map payload = ta.generatePayload(Locale.US, "EISENHAU", payloadTokens);
      assertTrue(payload.containsKey("roles"));
      List returnedRoles = (List) payload.get("roles");
      assertFalse(returnedRoles.isEmpty());
      assertEquals("ROLE_KMSS_VIEWER", returnedRoles.get(0));
      mockSecurityServiceControl.verify();
   }

   public void testGeneratePayloadForProfile() throws Exception
   {
      User user = User.createUser("EISENHAU");
      Set payloadTokens = new HashSet();
      payloadTokens.add("profile");
      securityService.getUser(user);
      mockSecurityServiceControl.setReturnValue(user);
      mockSecurityServiceControl.replay();
      Map payload = ta.generatePayload(Locale.US, "EISENHAU", payloadTokens);
      assertTrue(payload.containsKey("profile"));
      SilentLoginProfile returnedProfile = (SilentLoginProfile) payload.get("profile");
      assertEquals("EISENHAU", returnedProfile.getUsername());
      mockSecurityServiceControl.verify();
   }

   /**
    * Creates a digest key from the application key and a ticket.
    *
    * @param appKey the application key.
    * @param ticket the one-time use ticket.
    * @return the String form of the digest key.
    * @throws Exception never thrown.
    */
   private String createDigestedKey(String appKey, String ticket) throws Exception
   {
      String combined = appKey + ticket;
      return new String(Base64.encodeBase64(digester.digest(combined.getBytes())));
   }
}
