package com.atlassian.confluence.rpc;

import com.atlassian.confluence.ConfluenceTestCase;
import com.atlassian.confluence.DummyCache;
import com.atlassian.confluence.setup.settings.SettingsManager;
import com.atlassian.confluence.setup.settings.Settings;
import com.atlassian.confluence.security.PermissionManager;
import com.atlassian.confluence.security.Permission;
import com.atlassian.confluence.user.UserAccessor;
import com.atlassian.confluence.rpc.auth.TokenAuthenticationManager;
import com.atlassian.user.impl.cache.CacheManager;
import com.atlassian.user.impl.cache.Cache;
import com.atlassian.user.impl.DefaultUser;
import com.atlassian.user.User;
import com.mockobjects.dynamic.Mock;
import com.mockobjects.dynamic.C;

/**
 * Created by IntelliJ IDEA.
 * User: user
 * Date: Feb 22, 2006
 * Time: 3:19:52 PM
 * To change this template use File | Settings | File Templates.
 */
public class TestTokenAuthenticationManager extends ConfluenceTestCase {
    TokenAuthenticationManager tokenManager;
    Mock mockUserAccessor;
    Mock mockPermissionManager;
    Mock mockSettingsManager;
    Mock mockCacheManager;

    private static final String LOGIN_USERNAME = "bob";
    private static final String LOGIN_PASSWORD = "foo";

    protected void setUp() throws Exception {
        super.setUp();

        mockUserAccessor = new Mock(UserAccessor.class);
        mockPermissionManager = new Mock(PermissionManager.class);
        mockSettingsManager = new Mock(SettingsManager.class);
        mockCacheManager = new Mock(CacheManager.class);

        tokenManager = new TokenAuthenticationManager();
        tokenManager.setUserAccessor((UserAccessor) mockUserAccessor.proxy());
        tokenManager.setPermissionManager((PermissionManager) mockPermissionManager.proxy());
        tokenManager.setSettingsManager((SettingsManager) mockSettingsManager.proxy());
        tokenManager.setCacheManager((CacheManager) mockCacheManager.proxy());
    }

    protected void tearDown() throws Exception {
        mockUserAccessor = null;
        mockPermissionManager = null;
        mockSettingsManager = null;
        mockCacheManager = null;

        tokenManager = null;

        super.tearDown();
    }

    private void verifyMocks() {
        mockUserAccessor.verify();
        mockPermissionManager.verify();
        mockSettingsManager.verify();
        mockCacheManager.verify();
    }

    private DefaultUser createDefaultUser() {
        DefaultUser user = new DefaultUser();
        user.setName(LOGIN_USERNAME);
        user.setPassword(LOGIN_PASSWORD);

        return user;
    }

    public void testLogin() throws Exception {
        Cache cache = makeCache(TokenAuthenticationManager.TOKEN_CACHE);
        User user =  createDefaultUser();

        // Expectations
        // Grab the user
        mockUserAccessor.expectAndReturn("getUser", C.args(C.eq(LOGIN_USERNAME)), user);

        // Authentication
        mockUserAccessor.expectAndReturn("authenticate", C.args(C.eq(LOGIN_USERNAME), C.eq(LOGIN_PASSWORD)), true);

        // Grab the cache
        mockGetCacheManager(TokenAuthenticationManager.TOKEN_CACHE, cache);

        // Login
        String token = tokenManager.login(LOGIN_USERNAME, LOGIN_PASSWORD);

        // Make sure it's valid
        assertNotNull(token);
        assertTrue(token.length() > 0);

        // We expect the token to have been put in the cache
        assertNotNull(cache.get(token));
        assertTrue(cache.get(token).equals(user));

        verifyMocks();
    }

    public void testLoginWithNullUser() throws Exception {

        DefaultUser user = null;

        mockUserAccessor.expectAndReturn("getUser", C.args(C.eq(LOGIN_USERNAME)), user);

        try {
            tokenManager.login(LOGIN_USERNAME, LOGIN_PASSWORD);

            fail("Expected token manager to throw exception");
        }
        catch (RemoteException e) {
            // Expected
        }

        verifyMocks();
    }

    public void testLoginWithWrongPassword() throws Exception {
        // Expectations
        // Grab the user
        mockUserAccessor.expectAndReturn("getUser", C.args(C.eq(LOGIN_USERNAME)), createDefaultUser());

        // Authentication
        mockUserAccessor.expectAndReturn("authenticate", C.args(C.eq(LOGIN_USERNAME), C.eq(LOGIN_PASSWORD)), false);

        try {
            tokenManager.login(LOGIN_USERNAME, LOGIN_PASSWORD);

            fail("Expected token manager to throw exception for incorrect password");
        }
        catch (RemoteException e) {
            // Expected
        }

        verifyMocks();
    }

    public void testLogout() throws Exception {
        Cache cache = makeCache(TokenAuthenticationManager.TOKEN_CACHE);
        String token = mockLogin(createDefaultUser(), true, cache);

        // Expecteations
        // We expect the cache to be grabbed
        mockGetCacheManager(TokenAuthenticationManager.TOKEN_CACHE, cache);

        assertTrue(tokenManager.logout(token));

        // Make sure the token no longer exists
        assertNull(cache.get(token));

        verifyMocks();
    }

    public void testLogoutWithInvalidToken() throws Exception {
        Cache cache = makeCache(TokenAuthenticationManager.TOKEN_CACHE);
        String token = mockLogin(createDefaultUser(), true, cache);

        // Expectations
        // We expect the cache to be grabbed
        mockGetCacheManager(TokenAuthenticationManager.TOKEN_CACHE, cache);

        assertFalse(tokenManager.logout(token + 1));

        // Make sure the token still longer exists
        assertNotNull(cache.get(token));

        verifyMocks();
    }

    public void testMakeUserFromToken() throws Exception {
        Cache cache = makeCache(TokenAuthenticationManager.TOKEN_CACHE);
        DefaultUser user = createDefaultUser();

        String token = mockLogin(user, true, cache);

        // Expectations
        // We expect the cache to be grabbed
        mockGetCacheManager(TokenAuthenticationManager.TOKEN_CACHE, cache);

        User retrievedUser = tokenManager.makeNonAnonymousUserFromToken(token);

        assertEquals(user, retrievedUser);

        verifyMocks();
    }

    public void testLogoutInvalidatesToken() throws Exception
    {
        Cache cache = makeCache(TokenAuthenticationManager.TOKEN_CACHE);
        String token = mockLogin(createDefaultUser(), true, cache);

        // Expectations
        // Grab the cache
        mockGetCacheManager(TokenAuthenticationManager.TOKEN_CACHE, cache);

        // When the user is retrieved, the cache is accessed
        mockGetCacheManager(TokenAuthenticationManager.TOKEN_CACHE, cache);

        tokenManager.logout(token);

        try {
            tokenManager.makeNonAnonymousUserFromToken(token);
            fail("Expected token manager to throw InvalidSessionException when attempting to reuse an old token");
        }
        catch (InvalidSessionException e) {
            // Expected behaviour
        }

        verifyMocks();
    }

    public void testMakeAnonymousUser() throws NotPermittedException {
        // Expectations
        Settings globalSettings = new Settings();
        globalSettings.setAllowRemoteApiAnonymous(true);

        mockSettingsManager.expectAndReturn("getGlobalSettings", globalSettings);

        User user = tokenManager.makeAnonymousUser();

        // User should be null, since they're anonymous
        assertNull(user);

        verifyMocks();
    }

    public void testMakeAnonymousUserWithAnonymousAccessDisabled() throws NotPermittedException {
        // Expectations
        Settings globalSettings = new Settings();
        globalSettings.setAllowRemoteApiAnonymous(false);

        mockSettingsManager.expectAndReturn("getGlobalSettings", globalSettings);

        try
        {
            tokenManager.makeAnonymousUser();

            fail("Expected the call to fail, since anonymous access to the remote api is disabled.");
        }
        catch (NotPermittedException e)
        {
            // Expected
        }

        verifyMocks();
    }

    public void testHasUseConfluencePermission()
    {
        DefaultUser user = createDefaultUser();

        // Expectations
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.VIEW), C.eq(PermissionManager.TARGET_APPLICATION)), true);

        assertTrue(tokenManager.hasUseConfluencePermission(user));

        verifyMocks();
    }

    public void testHasUseConfluencePermissionWithDisabledUser()
    {
        DefaultUser user = createDefaultUser();

        // Expectations
        mockPermissionManager.expectAndReturn("hasPermission", C.args(C.eq(user), C.eq(Permission.VIEW), C.eq(PermissionManager.TARGET_APPLICATION)), false);

        assertFalse(tokenManager.hasUseConfluencePermission(user));

        verifyMocks();
    }

    private String mockLogin(DefaultUser user, boolean authenticated, Cache cache) throws Exception {
        // Expectations
        // Grab the user
        mockUserAccessor.expectAndReturn("getUser", C.args(C.eq(user.getName())), user);

        // Authentication
        mockUserAccessor.expectAndReturn("authenticate", C.args(C.eq(user.getName()), C.eq(user.getPassword())), authenticated);

        // Grab the cache
        mockGetCacheManager(TokenAuthenticationManager.TOKEN_CACHE, cache);

        // Login
        String token = tokenManager.login(user.getName(), user.getPassword());

        // Make sure it's valid
        assertNotNull(token);
        assertTrue(token.length() > 0);

        return token;
    }

    private void mockGetCacheManager(String key, Cache returnValue)
    {
        mockCacheManager.expectAndReturn("getCache", C.args(C.eq(key)), returnValue);
    }

    private Cache makeCache(String name)
    {
        return new DummyCache(name);
    }

}
