package org.apereo.cas.web.view;
import com.google.common.base.Throwables;
import org.apereo.cas.CasProtocolConstants;
import org.apereo.cas.CasViewConstants;
import org.apereo.cas.authentication.CoreAuthenticationTestUtils;
import org.apereo.cas.authentication.ProtocolAttributeEncoder;
import org.apereo.cas.authentication.UsernamePasswordCredential;
import org.apereo.cas.authentication.support.DefaultCasProtocolAttributeEncoder;
import org.apereo.cas.services.ServicesManager;
import org.apereo.cas.util.EncodingUtils;
import org.apereo.cas.util.cipher.NoOpCipherExecutor;
import org.apereo.cas.util.crypto.PrivateKeyFactoryBean;
import org.apereo.cas.web.AbstractServiceValidateControllerTests;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.TestPropertySource;
import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.support.RequestContext;
import javax.crypto.Cipher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.PrivateKey;
import java.util.Map;
import static org.junit.Assert.*;
/**
* Unit tests for {@link Cas30ResponseView}.
*
* @author Misagh Moayyed
* @since 4.0.0
*/
@DirtiesContext
@TestPropertySource(properties = {"cas.clearpass.cacheCredential=true", "cas.clearpass.cipherEnabled=false"})
public class Cas30ResponseViewTests extends AbstractServiceValidateControllerTests {
private static final Logger LOGGER = LoggerFactory.getLogger(Cas30ResponseViewTests.class);
@Autowired
@Qualifier("servicesManager")
private ServicesManager servicesManager;
@Autowired
@Qualifier("cas3ServiceJsonView")
private View cas3ServiceJsonView;
@Autowired
@Qualifier("cas3SuccessView")
private View cas3SuccessView;
@Autowired
@Qualifier("cas3ServiceFailureView")
private View cas3ServiceFailureView;
@Before
public void setUp() {
this.serviceValidateController.setFailureView(cas3ServiceFailureView);
this.serviceValidateController.setSuccessView(cas3SuccessView);
this.serviceValidateController.setJsonView(cas3ServiceJsonView);
}
private Map<?, ?> renderView() throws Exception {
final ModelAndView modelAndView = this.getModelAndViewUponServiceValidationWithSecurePgtUrl();
final MockHttpServletRequest req = new MockHttpServletRequest(new MockServletContext());
req.setAttribute(RequestContext.WEB_APPLICATION_CONTEXT_ATTRIBUTE, new GenericWebApplicationContext(req.getServletContext()));
final ProtocolAttributeEncoder encoder = new DefaultCasProtocolAttributeEncoder(this.servicesManager, NoOpCipherExecutor.getInstance());
final View viewDelegated = new View() {
@Override
public String getContentType() {
return MediaType.TEXT_HTML_VALUE;
}
@Override
public void render(final Map<String, ?> map, final HttpServletRequest request, final HttpServletResponse response) throws Exception {
map.forEach(request::setAttribute);
}
};
final Cas30ResponseView view = new Cas30ResponseView(true, encoder, servicesManager,
"attribute", viewDelegated, true);
final MockHttpServletResponse resp = new MockHttpServletResponse();
view.render(modelAndView.getModel(), req, resp);
return (Map<?, ?>) req.getAttribute(CasProtocolConstants.VALIDATION_CAS_MODEL_ATTRIBUTE_NAME_ATTRIBUTES);
}
@Test
public void verifyViewAuthnAttributes() throws Exception {
final Map<?, ?> attributes = renderView();
assertTrue(attributes.containsKey(CasProtocolConstants.VALIDATION_CAS_MODEL_ATTRIBUTE_NAME_AUTHENTICATION_DATE));
assertTrue(attributes.containsKey(CasProtocolConstants.VALIDATION_CAS_MODEL_ATTRIBUTE_NAME_FROM_NEW_LOGIN));
assertTrue(attributes.containsKey(CasProtocolConstants.VALIDATION_REMEMBER_ME_ATTRIBUTE_NAME));
}
@Test
public void verifyPasswordAsAuthenticationAttributeCanDecrypt() throws Exception {
final Map<?, ?> attributes = renderView();
assertTrue(attributes.containsKey(CasViewConstants.MODEL_ATTRIBUTE_NAME_PRINCIPAL_CREDENTIAL));
final String encodedPsw = (String) attributes.get(CasViewConstants.MODEL_ATTRIBUTE_NAME_PRINCIPAL_CREDENTIAL);
final String password = decryptCredential(encodedPsw);
final UsernamePasswordCredential creds = CoreAuthenticationTestUtils.getCredentialsWithSameUsernameAndPassword();
assertEquals(password, creds.getPassword());
}
@Test
public void verifyProxyGrantingTicketAsAuthenticationAttributeCanDecrypt() throws Exception {
final Map<?, ?> attributes = renderView();
assertTrue(attributes.containsKey(CasViewConstants.MODEL_ATTRIBUTE_NAME_PROXY_GRANTING_TICKET));
final String encodedPgt = (String) attributes.get(CasViewConstants.MODEL_ATTRIBUTE_NAME_PROXY_GRANTING_TICKET);
final String pgt = decryptCredential(encodedPgt);
assertNotNull(pgt);
}
private String decryptCredential(final String cred) {
try {
final PrivateKeyFactoryBean factory = new PrivateKeyFactoryBean();
factory.setAlgorithm("RSA");
factory.setLocation(new ClassPathResource("RSA1024Private.p8"));
factory.setSingleton(false);
final PrivateKey privateKey = factory.getObject();
LOGGER.debug("Initializing cipher based on [{}]", privateKey.getAlgorithm());
final Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
LOGGER.debug("Decoding value [{}]", cred);
final byte[] cred64 = EncodingUtils.decodeBase64(cred);
LOGGER.debug("Initializing decrypt-mode via private key [{}]", privateKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
final byte[] cipherData = cipher.doFinal(cred64);
return new String(cipherData);
} catch (final Exception e) {
throw Throwables.propagate(e);
}
}
}