3

I am trying to write a unit test for a guest user account. The code under test checks guest by calling this method, which in Unit Test returns null for the guest account.

/**
 * Determines if the user is a guest account.
 *
 * @return True if the account is guest account, false otherwise.
 */
public boolean isGuest() {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    if (auth != null) {
        if (auth instanceof AnonymousAuthenticationToken) {
            return true;
        } else {
            return false;
        }
    } else {
        return false;
    }
}

In the server Tomcat container, the anonymous user is okay it returns an instance of AnonymousAuthenticationToken. Because the container environment & unit test environment both share the same security configuration class, assume the security config is probably correct.

The test code below also works with the MockUser so I also think the security test configuration is probably okay:

@Test
@WithMockUser(username="Test.Customer.1@mailinator.com", roles = {"ADMIN"})
public void testCheckoutPage() throws Exception{
    logger.entry();
    String targetView = OrderViews.convertViewReference(getPageDirectory(), OrderViews.CHECKOUT_LOGIN_PAGE, false);
    String targetUrl = "/checkout";

    Order order = OrderBuilder.buildSampleGuestOrder(OrderStatus.NEW, 5);
    prepareMocks(order);
    Map<String, Object> sessionAttrs = new HashMap<>();
    sessionAttrs.put(OrderConstants.OPEN_ORDER_ID_ATTRIBUTE, order.getId());

    this.mockMvc.perform(get(targetUrl).sessionAttrs(sessionAttrs))
            .andExpect(status().isOk())
            .andExpect(view().name(targetView))
            .andExpect(model().attribute("order", order))
            .andExpect(model().attributeExists("loginForm"));


    this.mockMvc.perform(MockMvcRequestBuilders.post(targetUrl))
            .andExpect(status().isMethodNotAllowed());

    logger.exit();
}

Does anyone have an idea how to simulate the anonymous authentication token in Unit Test?

Richard G
  • 5,243
  • 11
  • 53
  • 95

2 Answers2

5

In Spring Security 4.1 (not yet GA) we are introducing support for @WithAnonymousUser.

The @WithAnonymousUser support is built using @WithSecurityContext. This means you could easily add the support to your codebase in 4.0.x until 4.1.x is released. To get it to work you would need to copy the following classes to your test source folders:

package org.springframework.security.test.context.support;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.context.SecurityContext;

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@WithSecurityContext(factory = WithAnonymousUserSecurityContextFactory.class)
public @interface WithAnonymousUser {}
package org.springframework.security.test.context.support;

import java.util.List;

import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;

final class WithAnonymousUserSecurityContextFactory implements
        WithSecurityContextFactory<WithAnonymousUser> {

    public SecurityContext createSecurityContext(WithAnonymousUser withUser) {
        List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS");
        Authentication authentication = new AnonymousAuthenticationToken("key", "anonymous", authorities);
        SecurityContext context = SecurityContextHolder.createEmptyContext();
        context.setAuthentication(authentication);
        return context;
    }
}

Then you can use the following to run as an anonymous user:

@Test
@WithAnonymousUser
public void testAnonymous() throws Exception {
    // ...
}

NOTE: It is important to note that just as you needed to do for @WithMockUser you need to ensure you setup MockMvc with apply(springSecurity()) as outlined in the reference.

Rob Winch
  • 21,440
  • 2
  • 59
  • 76
1

Set the authentication before running test

@Before
public void setupAuthentication(){
    SecurityContextHolder.getContext().setAuthentication(new AnonymousAuthenticationToken("GUEST","USERNAME", AuthorityUtils
            .createAuthorityList("ROLE_ONE", "ROLE_TWO")));
}
KSTN
  • 2,002
  • 14
  • 18