package org.apereo.cas.support.rest;
import org.apereo.cas.CentralAuthenticationService;
import org.apereo.cas.authentication.AuthenticationException;
import org.apereo.cas.authentication.AuthenticationManager;
import org.apereo.cas.authentication.AuthenticationResult;
import org.apereo.cas.authentication.AuthenticationTransaction;
import org.apereo.cas.authentication.CoreAuthenticationTestUtils;
import org.apereo.cas.authentication.DefaultAuthenticationSystemSupport;
import org.apereo.cas.authentication.DefaultAuthenticationTransactionManager;
import org.apereo.cas.authentication.DefaultPrincipalElectionStrategy;
import org.apereo.cas.authentication.principal.Service;
import org.apereo.cas.authentication.principal.WebApplicationServiceFactory;
import org.apereo.cas.ticket.InvalidTicketException;
import org.apereo.cas.ticket.ServiceTicket;
import org.apereo.cas.ticket.TicketGrantingTicket;
import org.apereo.cas.ticket.registry.TicketRegistrySupport;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import javax.security.auth.login.LoginException;
import java.util.HashMap;
import java.util.Map;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
/**
* Unit tests for {@link TicketsResource}.
*
* @author Dmitriy Kopylenko
* @since 4.0.0
*/
@RunWith(MockitoJUnitRunner.Silent.class)
public class TicketsResourceTests {
private static final String TICKETS_RESOURCE_URL = "/cas/v1/tickets";
private static final String USERNAME = "username";
private static final String OTHER_EXCEPTION = "Other exception";
private static final String SERVICE = "service";
private static final String TEST_VALUE = "test";
private static final String PASSWORD = "password";
@Mock
private CentralAuthenticationService casMock;
@Mock
private TicketRegistrySupport ticketSupport;
@InjectMocks
private TicketsResource ticketsResourceUnderTest;
private MockMvc mockMvc;
@Before
public void setUp() throws Exception {
final AuthenticationManager mgmr = mock(AuthenticationManager.class);
when(mgmr.authenticate(any(AuthenticationTransaction.class))).thenReturn(CoreAuthenticationTestUtils.getAuthentication());
when(ticketSupport.getAuthenticationFrom(anyString())).thenReturn(CoreAuthenticationTestUtils.getAuthentication());
this.ticketsResourceUnderTest = new TicketsResource(
new DefaultAuthenticationSystemSupport(new DefaultAuthenticationTransactionManager(mgmr),
new DefaultPrincipalElectionStrategy()), new DefaultCredentialFactory(),
ticketSupport, new WebApplicationServiceFactory(), casMock);
this.mockMvc = MockMvcBuilders.standaloneSetup(this.ticketsResourceUnderTest)
.defaultRequest(get("/")
.contextPath("/cas")
.contentType(MediaType.APPLICATION_FORM_URLENCODED))
.build();
}
@Test
public void normalCreationOfTGT() throws Throwable {
final String expectedReturnEntityBody = "<!DOCTYPE HTML PUBLIC \\\"-//IETF//DTD HTML 2.0//EN\\\">"
+ "<html><head><title>201 Created</title></head><body><h1>TGT Created</h1>"
+ "<form action=\"http://localhost/cas/v1/tickets/TGT-1\" "
+ "method=\"POST\">Service:<input type=\"text\" name=\"service\" value=\"\">"
+ "<br><input type=\"submit\" value=\"Submit\"></form></body></html>";
configureCasMockToCreateValidTGT();
this.mockMvc.perform(post(TICKETS_RESOURCE_URL)
.param(USERNAME, TEST_VALUE)
.param(PASSWORD, TEST_VALUE))
.andExpect(status().isCreated())
.andExpect(header().string("Location", "http://localhost/cas/v1/tickets/TGT-1"))
.andExpect(content().contentType(MediaType.TEXT_HTML))
.andExpect(content().string(expectedReturnEntityBody));
}
@Test
public void creationOfTGTWithAuthenticationException() throws Throwable {
configureCasMockTGTCreationToThrowAuthenticationException();
this.mockMvc.perform(post(TICKETS_RESOURCE_URL)
.param(USERNAME, TEST_VALUE)
.param(PASSWORD, TEST_VALUE))
.andExpect(status().isUnauthorized())
.andExpect(content().json("{\"authentication_exceptions\" : [ \"LoginException\" ]}"));
}
@Test
public void creationOfTGTWithUnexpectedRuntimeException() throws Throwable {
configureCasMockTGTCreationToThrow(new RuntimeException(OTHER_EXCEPTION));
this.mockMvc.perform(post(TICKETS_RESOURCE_URL)
.param(USERNAME, TEST_VALUE)
.param(PASSWORD, TEST_VALUE))
.andExpect(status().is5xxServerError())
.andExpect(content().string(OTHER_EXCEPTION));
}
@Test
public void creationOfTGTWithBadPayload() throws Throwable {
configureCasMockTGTCreationToThrow(new RuntimeException(OTHER_EXCEPTION));
this.mockMvc.perform(post(TICKETS_RESOURCE_URL)
.param("no_username_param", TEST_VALUE)
.param("no_password_param", TEST_VALUE))
.andExpect(status().is4xxClientError())
.andExpect(content().string("Invalid payload. 'username' and 'password' form fields are required."));
}
@Test
public void normalCreationOfST() throws Throwable {
configureCasMockToCreateValidST();
this.mockMvc.perform(post(TICKETS_RESOURCE_URL + "/TGT-1")
.param(SERVICE, CoreAuthenticationTestUtils.getService().getId()))
.andExpect(status().isOk())
.andExpect(content().contentType("text/plain;charset=ISO-8859-1"))
.andExpect(content().string("ST-1"));
}
@Test
public void creationOfSTWithInvalidTicketException() throws Throwable {
configureCasMockSTCreationToThrow(new InvalidTicketException("TGT-1"));
this.mockMvc.perform(post(TICKETS_RESOURCE_URL + "/TGT-1")
.param(SERVICE, CoreAuthenticationTestUtils.getService().getId()))
.andExpect(status().isNotFound())
.andExpect(content().string("TicketGrantingTicket could not be found"));
}
@Test
public void creationOfSTWithGeneralException() throws Throwable {
configureCasMockSTCreationToThrow(new RuntimeException(OTHER_EXCEPTION));
this.mockMvc.perform(post(TICKETS_RESOURCE_URL + "/TGT-1")
.param(SERVICE, CoreAuthenticationTestUtils.getService().getId()))
.andExpect(status().is5xxServerError())
.andExpect(content().string(OTHER_EXCEPTION));
}
@Test
public void deletionOfTGT() throws Throwable {
this.mockMvc.perform(delete(TICKETS_RESOURCE_URL + "/TGT-1"))
.andExpect(status().isOk());
}
private void configureCasMockToCreateValidTGT() throws Throwable {
final TicketGrantingTicket tgt = mock(TicketGrantingTicket.class);
when(tgt.getId()).thenReturn("TGT-1");
when(this.casMock.createTicketGrantingTicket(any(AuthenticationResult.class))).thenReturn(tgt);
}
private void configureCasMockTGTCreationToThrowAuthenticationException() throws Throwable {
final Map<String, Class<? extends Exception>> handlerErrors = new HashMap<>(1);
handlerErrors.put("TestCaseAuthenticationHander", LoginException.class);
when(this.casMock.createTicketGrantingTicket(any(AuthenticationResult.class)))
.thenThrow(new AuthenticationException(handlerErrors));
}
private void configureCasMockTGTCreationToThrow(final Throwable e) throws Throwable {
when(this.casMock.createTicketGrantingTicket(any(AuthenticationResult.class))).thenThrow(e);
}
private void configureCasMockSTCreationToThrow(final Throwable e) throws Throwable {
when(this.casMock.grantServiceTicket(anyString(), any(Service.class), any(AuthenticationResult.class))).thenThrow(e);
}
private void configureCasMockToCreateValidST() throws Throwable {
final ServiceTicket st = mock(ServiceTicket.class);
when(st.getId()).thenReturn("ST-1");
when(this.casMock.grantServiceTicket(anyString(), any(Service.class), any(AuthenticationResult.class))).thenReturn(st);
}
}