/*************************************************************************
* *
* EJBCA Community: The OpenSource Certificate Authority *
* *
* This software is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or any later version. *
* *
* See terms of license at gnu.org. *
* *
*************************************************************************/
package org.ejbca.core.ejb.approval;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.security.KeyPair;
import java.security.Principal;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger;
import org.cesecore.authentication.tokens.AuthenticationSubject;
import org.cesecore.authentication.tokens.AuthenticationToken;
import org.cesecore.authentication.tokens.UsernamePrincipal;
import org.cesecore.authentication.tokens.X509CertificateAuthenticationTokenMetaData;
import org.cesecore.authorization.control.StandardRules;
import org.cesecore.authorization.user.AccessMatchType;
import org.cesecore.authorization.user.matchvalues.X500PrincipalAccessMatchValue;
import org.cesecore.certificates.certificate.CertificateStoreSessionRemote;
import org.cesecore.certificates.certificate.InternalCertificateStoreSessionRemote;
import org.cesecore.certificates.certificateprofile.CertificateProfileConstants;
import org.cesecore.certificates.endentity.EndEntityConstants;
import org.cesecore.certificates.endentity.EndEntityInformation;
import org.cesecore.certificates.endentity.EndEntityType;
import org.cesecore.certificates.endentity.EndEntityTypes;
import org.cesecore.certificates.util.AlgorithmConstants;
import org.cesecore.keys.util.KeyTools;
import org.cesecore.mock.authentication.SimpleAuthenticationProviderSessionRemote;
import org.cesecore.mock.authentication.tokens.TestAlwaysAllowLocalAuthenticationToken;
import org.cesecore.roles.Role;
import org.cesecore.roles.management.RoleSessionRemote;
import org.cesecore.roles.member.RoleMember;
import org.cesecore.roles.member.RoleMemberSessionRemote;
import org.cesecore.util.CertTools;
import org.cesecore.util.CryptoProviderTools;
import org.cesecore.util.EJBTools;
import org.cesecore.util.EjbRemoteHelper;
import org.cesecore.util.FileTools;
import org.ejbca.config.EjbcaConfiguration;
import org.ejbca.core.ejb.authentication.cli.CliAuthenticationProviderSessionRemote;
import org.ejbca.core.ejb.authentication.cli.CliAuthenticationToken;
import org.ejbca.core.ejb.ca.CaTestCase;
import org.ejbca.core.ejb.ra.EndEntityManagementSessionRemote;
import org.ejbca.core.model.SecConst;
import org.ejbca.core.model.approval.AdminAlreadyApprovedRequestException;
import org.ejbca.core.model.approval.Approval;
import org.ejbca.core.model.approval.ApprovalDataVO;
import org.ejbca.core.model.approval.ApprovalException;
import org.ejbca.core.model.approval.ApprovalRequestExpiredException;
import org.ejbca.core.model.approval.approvalrequests.AddEndEntityApprovalRequest;
import org.ejbca.core.model.approval.approvalrequests.ViewHardTokenDataApprovalRequest;
import org.ejbca.core.model.approval.profile.AccumulativeApprovalProfile;
import org.ejbca.core.model.authorization.AccessRulesConstants;
import org.ejbca.core.protocol.ws.BatchCreateTool;
import org.ejbca.util.query.ApprovalMatch;
import org.ejbca.util.query.BasicMatch;
import org.ejbca.util.query.Query;
import org.ejbca.util.query.TimeMatch;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* Test of approvals.
*
* Note/TODO:
* A lot of tests in this class is written in such a way that they are sensitive to timing on a highly loaded test
* server. This needs to rewritten in a more robust way at a future point in time to avoid false negatives.
* The EXPIRATION_PERIOD constant can be adjusted depending on test server load/performance.
*
* @version $Id: ApprovalSessionTest.java 32020 2019-03-28 15:44:35Z undulf $
*/
public class ApprovalSessionTest extends CaTestCase {
private static final Logger log = Logger.getLogger(ApprovalSessionTest.class);
private static final AuthenticationToken intadmin = new TestAlwaysAllowLocalAuthenticationToken(ApprovalSessionTest.class.getSimpleName());
private static final String P12_FOLDER_NAME = "p12";
private static final String roleName = "ApprovalTest";
/**
* Expiration period for approval requests and admin approvals, in milliseconds, to use in this test.
* A less performant or more highly loaded test environment may need a higher value.
*/
private static final long EXPIRATION_PERIOD = 4000;
private static final long EXPIRATION_PERIOD_LONG = 80000;
private static final long EXPIRATION_SLEEP = EXPIRATION_PERIOD + 100;
private static String adminusername1 = "createTestCAWithEndEntity";
private static String adminusername2 = adminusername1 + "2";
private static String adminusername3 = adminusername1 + "3";
private static String reqadminusername = "req" + adminusername1;
private static X509Certificate reqadmincert = null;
private static X509Certificate admincert1 = null;
private static X509Certificate admincert2 = null;
private static X509Certificate admincert3 = null;
private static X509Certificate externalcert = null;
private static AuthenticationToken reqadmin = null;
private static AuthenticationToken admin1 = null;
private static AuthenticationToken admin2 = null;
private static AuthenticationToken admin3 = null;
private static AuthenticationToken externaladmin = null;
private static AccumulativeApprovalProfile approvalProfile = null;
private static AccumulativeApprovalProfile approvalProfileLongExpirationPeriod = null;
private static List fileHandles = new ArrayList<>();
private final SimpleAuthenticationProviderSessionRemote simpleAuthenticationProvider = EjbRemoteHelper.INSTANCE.getRemoteSession(
SimpleAuthenticationProviderSessionRemote.class, EjbRemoteHelper.MODULE_TEST);
private final ApprovalProfileSessionRemote approvalProfileSession = EjbRemoteHelper.INSTANCE.getRemoteSession(
ApprovalProfileSessionRemote.class, EjbRemoteHelper.MODULE_TEST);
private Role role;
private int caid = getTestCAId();
private long originalValidity = 0L;
private long originalValidityLongExpirationPeriod = 0L;
// Identifiers of temporary instances to be removed after a test
private List removeApprovalIds = new ArrayList<>();
private DummyApprovalRequest nonExecutableRequest;
private DummyApprovalRequest nonExecutableRequestLongExpirationPeriod;
private String removeUserName = null;
private static KeyPair externalAdminRsaKey;
private ApprovalSessionRemote approvalSessionRemote = EjbRemoteHelper.INSTANCE.getRemoteSession(ApprovalSessionRemote.class);
private ApprovalSessionProxyRemote approvalSessionProxyRemote = EjbRemoteHelper.INSTANCE.getRemoteSession(ApprovalSessionProxyRemote.class,
EjbRemoteHelper.MODULE_TEST);
private ApprovalExecutionSessionRemote approvalExecutionSessionRemote = EjbRemoteHelper.INSTANCE
.getRemoteSession(ApprovalExecutionSessionRemote.class);
private CertificateStoreSessionRemote certificateStoreSession = EjbRemoteHelper.INSTANCE.getRemoteSession(CertificateStoreSessionRemote.class);
private EndEntityManagementSessionRemote endEntityManagementSession = EjbRemoteHelper.INSTANCE
.getRemoteSession(EndEntityManagementSessionRemote.class);
private RoleSessionRemote roleSession = EjbRemoteHelper.INSTANCE.getRemoteSession(RoleSessionRemote.class);
private RoleMemberSessionRemote roleMemberSession = EjbRemoteHelper.INSTANCE.getRemoteSession(RoleMemberSessionRemote.class);
@BeforeClass
public static void beforeClass() throws Exception {
CryptoProviderTools.installBCProviderIfNotAvailable();
createTestCA();
approvalProfile = new AccumulativeApprovalProfile("AccumulativeApprovalProfile");
approvalProfile.setNumberOfApprovalsRequired(2);
approvalProfile.setMaxExtensionTime(0);
ApprovalProfileSessionRemote approvalProfileSession = EjbRemoteHelper.INSTANCE.getRemoteSession(ApprovalProfileSessionRemote.class);
int approvalProfileId = approvalProfileSession.addApprovalProfile(intadmin, approvalProfile);
approvalProfile.setProfileId(approvalProfileId);
approvalProfileLongExpirationPeriod = new AccumulativeApprovalProfile("ApprovalProfileLongExpirationPeriod");
approvalProfileLongExpirationPeriod.setNumberOfApprovalsRequired(2);
approvalProfileLongExpirationPeriod.setMaxExtensionTime(0);
int longExpirationApprovalProfileId = approvalProfileSession.addApprovalProfile(intadmin, approvalProfileLongExpirationPeriod);
approvalProfileLongExpirationPeriod.setProfileId(longExpirationApprovalProfileId);
externalAdminRsaKey = KeyTools.genKeys("1024", AlgorithmConstants.KEYALGORITHM_RSA);
}
@AfterClass
public static void afterClass() throws Exception {
removeTestCA();
InternalCertificateStoreSessionRemote internalCertificateStoreSession = EjbRemoteHelper.INSTANCE
.getRemoteSession(InternalCertificateStoreSessionRemote.class, EjbRemoteHelper.MODULE_TEST);
internalCertificateStoreSession.removeCertificate(admincert1);
internalCertificateStoreSession.removeCertificate(admincert2);
internalCertificateStoreSession.removeCertificate(admincert3);
internalCertificateStoreSession.removeCertificate(externalcert);
internalCertificateStoreSession.removeCertificate(reqadmincert);
ApprovalProfileSessionRemote approvalProfileSession = EjbRemoteHelper.INSTANCE.getRemoteSession(ApprovalProfileSessionRemote.class);
if ((approvalProfile.getProfileId() != null) && (approvalProfileSession.getApprovalProfile(approvalProfile.getProfileId()) != null)) {
approvalProfileSession.removeApprovalProfile(intadmin, approvalProfile);
}
if ((approvalProfileLongExpirationPeriod.getProfileId() != null) && (approvalProfileSession.getApprovalProfile(approvalProfileLongExpirationPeriod.getProfileId()) != null)) {
approvalProfileSession.removeApprovalProfile(intadmin, approvalProfileLongExpirationPeriod);
}
for (File file : fileHandles) {
FileTools.delete(file);
}
}
@Before
public void createTestCAWithEndEntity() throws Exception {
EndEntityInformation userdata = new EndEntityInformation(adminusername1, "CN=" + adminusername1, caid, null, null, new EndEntityType(
EndEntityTypes.ENDUSER), EndEntityConstants.EMPTY_END_ENTITY_PROFILE, CertificateProfileConstants.CERTPROFILE_FIXED_ENDUSER,
SecConst.TOKEN_SOFT_P12, 0, null);
userdata.setPassword("foo123");
endEntityManagementSession.addUser(intadmin, userdata, true);
EndEntityInformation userdata2 = new EndEntityInformation(adminusername2, "CN=" + adminusername2, caid, null, null, new EndEntityType(
EndEntityTypes.ENDUSER), EndEntityConstants.EMPTY_END_ENTITY_PROFILE, CertificateProfileConstants.CERTPROFILE_FIXED_ENDUSER,
SecConst.TOKEN_SOFT_P12, 0, null);
userdata2.setPassword("foo123");
endEntityManagementSession.addUser(intadmin, userdata2, true);
EndEntityInformation userdata3 = new EndEntityInformation(adminusername3, "CN=" + adminusername3, caid, null, null, new EndEntityType(
EndEntityTypes.ENDUSER), EndEntityConstants.EMPTY_END_ENTITY_PROFILE, CertificateProfileConstants.CERTPROFILE_FIXED_ENDUSER,
SecConst.TOKEN_SOFT_P12, 0, null);
userdata3.setPassword("foo123");
endEntityManagementSession.addUser(intadmin, userdata3, true);
EndEntityInformation reqUserData = new EndEntityInformation(reqadminusername, "CN=" + reqadminusername, caid, null, null,
new EndEntityType(EndEntityTypes.ENDUSER), EndEntityConstants.EMPTY_END_ENTITY_PROFILE,
CertificateProfileConstants.CERTPROFILE_FIXED_ENDUSER, SecConst.TOKEN_SOFT_P12, 0, null);
reqUserData.setPassword("foo123");
endEntityManagementSession.addUser(intadmin, reqUserData, true);
externalcert = CertTools.genSelfCert("CN=externalCert,C=SE", 30, null, externalAdminRsaKey.getPrivate(), externalAdminRsaKey.getPublic(),
AlgorithmConstants.SIGALG_SHA1_WITH_RSA, false);
externaladmin = simpleAuthenticationProvider.authenticate(makeAuthenticationSubject(externalcert));
fileHandles.addAll(BatchCreateTool.createAllNew(intadmin, new File(P12_FOLDER_NAME)));
final Role oldRole = roleSession.getRole(intadmin, null, roleName);
if (oldRole != null) {
roleSession.deleteRoleIdempotent(intadmin, oldRole.getRoleId());
}
role = roleSession.persistRole(intadmin, new Role(null, roleName, Arrays.asList(
AccessRulesConstants.REGULAR_APPROVEENDENTITY,
AccessRulesConstants.ENDENTITYPROFILEBASE,
StandardRules.CAACCESSBASE.resource()
), null));
roleMemberSession.persist(intadmin, new RoleMember(X509CertificateAuthenticationTokenMetaData.TOKEN_TYPE,
caid, X500PrincipalAccessMatchValue.WITH_COMMONNAME.getNumericValue(),
AccessMatchType.TYPE_EQUALCASE.getNumericValue(), adminusername1, role.getRoleId(), null));
roleMemberSession.persist(intadmin, new RoleMember(X509CertificateAuthenticationTokenMetaData.TOKEN_TYPE,
caid, X500PrincipalAccessMatchValue.WITH_COMMONNAME.getNumericValue(),
AccessMatchType.TYPE_EQUALCASE.getNumericValue(), adminusername2, role.getRoleId(), null));
roleMemberSession.persist(intadmin, new RoleMember(X509CertificateAuthenticationTokenMetaData.TOKEN_TYPE,
caid, X500PrincipalAccessMatchValue.WITH_COMMONNAME.getNumericValue(),
AccessMatchType.TYPE_EQUALCASE.getNumericValue(), adminusername3, role.getRoleId(), null));
roleMemberSession.persist(intadmin, new RoleMember(X509CertificateAuthenticationTokenMetaData.TOKEN_TYPE,
caid, X500PrincipalAccessMatchValue.WITH_COMMONNAME.getNumericValue(),
AccessMatchType.TYPE_EQUALCASE.getNumericValue(), reqadminusername, role.getRoleId(), null));
roleMemberSession.persist(intadmin, new RoleMember(X509CertificateAuthenticationTokenMetaData.TOKEN_TYPE,
"CN=externalCert,C=SE".hashCode(), X500PrincipalAccessMatchValue.WITH_SERIALNUMBER.getNumericValue(),
AccessMatchType.TYPE_EQUALCASE.getNumericValue(), CertTools.getSerialNumberAsString(externalcert), role.getRoleId(), null));
admincert1 = (X509Certificate) EJBTools.unwrapCertCollection(certificateStoreSession.findCertificatesByUsername(adminusername1)).iterator().next();
admincert2 = (X509Certificate) EJBTools.unwrapCertCollection(certificateStoreSession.findCertificatesByUsername(adminusername2)).iterator().next();
admincert3 = (X509Certificate) EJBTools.unwrapCertCollection(certificateStoreSession.findCertificatesByUsername(adminusername3)).iterator().next();
reqadmincert = (X509Certificate) EJBTools.unwrapCertCollection(certificateStoreSession.findCertificatesByUsername(reqadminusername)).iterator().next();
admin1 = simpleAuthenticationProvider.authenticate(makeAuthenticationSubject(admincert1));
admin2 = simpleAuthenticationProvider.authenticate(makeAuthenticationSubject(admincert2));
admin3 = simpleAuthenticationProvider.authenticate(makeAuthenticationSubject(admincert3));
reqadmin = simpleAuthenticationProvider.authenticate(makeAuthenticationSubject(reqadmincert));
// TODO: before is had both a cert and username input?
originalValidity = approvalProfile.getRequestExpirationPeriod();
approvalProfile.setApprovalExpirationPeriod(EXPIRATION_PERIOD);
approvalProfile.setRequestExpirationPeriod(EXPIRATION_PERIOD);
approvalProfileSession.changeApprovalProfile(intadmin, approvalProfile);
nonExecutableRequest = new DummyApprovalRequest(reqadmin, null, caid, EndEntityConstants.EMPTY_END_ENTITY_PROFILE, false, approvalProfile);
removeApprovalIds = new ArrayList<>();
removeApprovalIds.add(nonExecutableRequest.generateApprovalId());
originalValidityLongExpirationPeriod = approvalProfileLongExpirationPeriod.getRequestExpirationPeriod();
approvalProfileLongExpirationPeriod.setApprovalExpirationPeriod(EXPIRATION_PERIOD_LONG);
approvalProfileLongExpirationPeriod.setRequestExpirationPeriod(EXPIRATION_PERIOD_LONG);
approvalProfileSession.changeApprovalProfile(intadmin, approvalProfileLongExpirationPeriod);
nonExecutableRequestLongExpirationPeriod = new DummyApprovalRequest(reqadmin, null, caid, EndEntityConstants.EMPTY_END_ENTITY_PROFILE, false, approvalProfileLongExpirationPeriod);
removeApprovalIds.add(nonExecutableRequestLongExpirationPeriod.generateApprovalId());
}
@Override
@After
public void tearDown() throws Exception {
// Reset profile
approvalProfile.setNumberOfApprovalsRequired(2);
approvalProfile.setMaxExtensionTime(0);
approvalProfile.setApprovalExpirationPeriod(originalValidity);
approvalProfile.setRequestExpirationPeriod(originalValidity);
approvalProfileSession.changeApprovalProfile(intadmin, approvalProfile);
approvalProfileLongExpirationPeriod.setNumberOfApprovalsRequired(2);
approvalProfileLongExpirationPeriod.setMaxExtensionTime(0);
approvalProfileLongExpirationPeriod.setApprovalExpirationPeriod(originalValidityLongExpirationPeriod);
approvalProfileLongExpirationPeriod.setRequestExpirationPeriod(originalValidityLongExpirationPeriod);
approvalProfileSession.changeApprovalProfile(intadmin, approvalProfileLongExpirationPeriod);
//
for (int removeApprovalId : removeApprovalIds) {
Collection approvals = approvalSessionRemote.findApprovalDataVO(removeApprovalId);
if (approvals != null && !approvals.isEmpty()) {
for (ApprovalDataVO approvalDataVO : approvals) {
approvalSessionRemote.removeApprovalRequest(intadmin, approvalDataVO.getId());
}
}
}
for (final String username : Arrays.asList(adminusername1, adminusername2, adminusername3, reqadminusername)) {
try {
endEntityManagementSession.deleteUser(intadmin, username);
} catch (Exception e) {
// NOPMD: ignore
}
}
if (role != null) {
roleSession.deleteRoleIdempotent(intadmin, role.getRoleId());
}
if (removeUserName != null) {
endEntityManagementSession.deleteUser(intadmin, removeUserName);
}
}
@Test
public void testAddApprovalRequest() throws Exception {
log.trace(">testAddApprovalRequest");
int approvalId = removeApprovalIds.get(0);
Certificate cert = nonExecutableRequest.getRequestAdminCert();
assertEquals(CertTools.getIssuerDN(reqadmincert), CertTools.getIssuerDN(cert));
// Test that the approval request does not exist.
Collection result = approvalSessionRemote.findApprovalDataVO(approvalId);
assertEquals(0, result.size());
approvalSessionRemote.addApprovalRequest(admin1, nonExecutableRequest);
// Test that the approvalRequest exists now
result = approvalSessionRemote.findApprovalDataVO(approvalId);
assertEquals(1, result.size());
ApprovalDataVO next = result.iterator().next();
assertEquals("Status was expired and not waiting.", ApprovalDataVO.STATUS_WAITINGFORAPPROVAL, next.getStatus());
assertEquals(caid, next.getCAId());
assertEquals(EndEntityConstants.EMPTY_END_ENTITY_PROFILE, next.getEndEntityProfileId());
assertEquals(CertTools.getIssuerDN(reqadmincert), next.getReqadmincertissuerdn());
assertEquals(CertTools.getSerialNumberAsString(reqadmincert), next.getReqadmincertsn());
assertEquals(approvalId, next.getApprovalId());
assertEquals(nonExecutableRequest.getApprovalType(), next.getApprovalType());
assertEquals(0, next.getApprovals().size());
assertFalse(next.getApprovalRequest().isExecutable());
assertEquals(2, next.getRemainingApprovals());
Thread.sleep(EXPIRATION_SLEEP);
// Test that the request expires as it should
result = approvalSessionRemote.findApprovalDataVO(approvalId);
assertEquals("Request should not expire", 1, result.size());
next = result.iterator().next();
assertEquals("Status was not expired.", ApprovalDataVO.STATUS_EXPIRED, next.getStatus());
// Then after one of them have expired
result = approvalSessionRemote.findApprovalDataVO(approvalId);
ApprovalDataVO expired = result.iterator().next();
approvalSessionRemote.addApprovalRequest(admin1, nonExecutableRequest);
approvalSessionRemote.removeApprovalRequest(admin1, expired.getId());
result = approvalSessionRemote.findApprovalDataVO(approvalId);
assertEquals("Should contain the approval request after removal of expired", 1, result.size());
// Test approvalId generation with a "real" approval request with a requestAdmin
approvalProfile.setNumberOfApprovalsRequired(1);
ViewHardTokenDataApprovalRequest ar = new ViewHardTokenDataApprovalRequest("APPROVALREQTESTTOKENUSER1",
"CN=APPROVALREQTESTTOKENUSER1", "12345678", true, reqadmin, null, 1, 0, 0, approvalProfile);
log.debug("Adding approval with approvalID (hash): " + ar.generateApprovalId());
approvalSessionRemote.addApprovalRequest(admin1, ar);
result = approvalSessionRemote.findApprovalDataVO(ar.generateApprovalId());
assertEquals("Should contain the approval request", 1, result.size());
log.trace("testApprove");
int approvalId = removeApprovalIds.get(0);
approvalSessionRemote.addApprovalRequest(admin1, nonExecutableRequest);
final Approval approval1 = createApproval("ap1test");
final Approval approval2 = createApproval("ap2test");
approvalExecutionSessionRemote.approve(admin1, approvalId, approval1);
Collection result = approvalSessionRemote.findApprovalDataVO(approvalId);
assertEquals("Wrong number of approval requests was returned.", 1, result.size());
ApprovalDataVO next = result.iterator().next();
assertEquals("Status was not set to 'Waiting for Approval'", ApprovalDataVO.STATUS_WAITINGFORAPPROVAL, next.getStatus());
assertEquals("Wrong number of remaining approvals", 1, next.getRemainingApprovals());
approvalExecutionSessionRemote.approve(admin2, approvalId, approval2);
result = approvalSessionRemote.findApprovalDataVO(approvalId);
assertEquals(1, result.size());
next = result.iterator().next();
assertEquals("Status = " + next.getStatus(), ApprovalDataVO.STATUS_APPROVED, next.getStatus());
assertEquals(0, next.getRemainingApprovals());
// Test that the approval expires as it should
Thread.sleep(EXPIRATION_SLEEP);
result = approvalSessionRemote.findApprovalDataVO(approvalId);
assertEquals(1, result.size());
next = result.iterator().next();
assertEquals("Status was not expired.", ApprovalDataVO.STATUS_EXPIRED, next.getStatus());
approvalSessionRemote.removeApprovalRequest(admin1, next.getId());
// Test using an executable Dummy, different behaviour
final DummyApprovalRequest executableRequest = new DummyApprovalRequest(reqadmin, null, caid, EndEntityConstants.EMPTY_END_ENTITY_PROFILE, true, approvalProfile);
int executableApprovalId = executableRequest.generateApprovalId();
removeApprovalIds.add(executableApprovalId);
approvalSessionRemote.addApprovalRequest(admin1, executableRequest);
approvalExecutionSessionRemote.approve(admin1, approvalId, approval1);
approvalExecutionSessionRemote.approve(admin2, approvalId, approval2);
result = approvalSessionRemote.findApprovalDataVO(executableApprovalId);
assertEquals(1, result.size());
next = result.iterator().next();
assertEquals("Status = " + next.getStatus(), ApprovalDataVO.STATUS_EXECUTED, next.getStatus());
// Make sure that the approval still have status executed after expiration
Thread.sleep(EXPIRATION_SLEEP);
result = approvalSessionRemote.findApprovalDataVO(executableApprovalId);
assertEquals(1, result.size());
next = result.iterator().next();
assertEquals("Status = " + next.getStatus(), ApprovalDataVO.STATUS_EXECUTED, next.getStatus());
approvalSessionRemote.removeApprovalRequest(admin1, next.getId());
log.trace("testApproveFromCli");
final AuthenticationToken cliReqAuthToken = getCliAdmin();
final String username = "ApprovalEndEntityUsername";
removeUserName = username;
final EndEntityInformation userdata = new EndEntityInformation(username, "C=SE, O=AnaTom, CN=" + username, caid, null, null,
EndEntityConstants.STATUS_NEW, new EndEntityType(EndEntityTypes.ENDUSER), EndEntityConstants.EMPTY_END_ENTITY_PROFILE,
CertificateProfileConstants.CERTPROFILE_FIXED_ENDUSER, new Date(), new Date(), SecConst.TOKEN_SOFT_P12, 0, null);
userdata.setPassword("foo123");
approvalProfileLongExpirationPeriod.setNumberOfApprovalsRequired(1);
final AddEndEntityApprovalRequest eeApprovalRequest = new AddEndEntityApprovalRequest(userdata, false, cliReqAuthToken, null, caid,
EndEntityConstants.EMPTY_END_ENTITY_PROFILE, approvalProfileLongExpirationPeriod);
int approvalId = eeApprovalRequest.generateApprovalId();
removeApprovalIds.add(approvalId);
approvalSessionRemote.addApprovalRequest(cliReqAuthToken, eeApprovalRequest);
// Use the authentication token
endEntityManagementSession.addUser(intadmin, userdata, false);
endEntityManagementSession.changeUser(cliReqAuthToken, userdata, false);
approvalExecutionSessionRemote.approve(intadmin, approvalId, createApprovalLongExpirationPeriod("ap1test"));
final int actualStatus = approvalSessionRemote.isApproved(approvalId);
assertEquals(ApprovalDataVO.STATUS_APPROVED, actualStatus);
log.trace("testReject()");
int approvalId = removeApprovalIds.get(0);
approvalSessionRemote.addApprovalRequest(reqadmin, nonExecutableRequest);
final Approval approval1 = createApproval("ap1test");
approvalExecutionSessionRemote.approve(admin1, approvalId, approval1);
Collection result = approvalSessionRemote.findApprovalDataVO(approvalId);
ApprovalDataVO next = result.iterator().next();
assertEquals("Status = " + next.getStatus(), ApprovalDataVO.STATUS_WAITINGFORAPPROVAL, next.getStatus());
assertEquals(1, next.getRemainingApprovals());
Approval rejection = createApproval("rejectiontest");
approvalExecutionSessionRemote.reject(admin2, approvalId, rejection);
result = approvalSessionRemote.findApprovalDataVO(approvalId);
next = result.iterator().next();
assertEquals("Status = " + next.getStatus(), ApprovalDataVO.STATUS_REJECTED, next.getStatus());
assertEquals("No approvals expected to be required.", 0, next.getRemainingApprovals());
approvalSessionRemote.removeApprovalRequest(admin1, next.getId());
nonExecutableRequest = new DummyApprovalRequest(reqadmin, null, caid, EndEntityConstants.EMPTY_END_ENTITY_PROFILE, false, approvalProfile);
approvalSessionRemote.addApprovalRequest(reqadmin, nonExecutableRequest);
rejection = createApproval("rejectiontest2");
approvalExecutionSessionRemote.reject(admin1, approvalId, rejection);
result = approvalSessionRemote.findApprovalDataVO(approvalId);
next = result.iterator().next();
assertEquals("Status = " + next.getStatus(), ApprovalDataVO.STATUS_REJECTED, next.getStatus());
assertEquals("No approvals expected to be required.", 0, next.getRemainingApprovals());
// Try to approve a rejected request
try {
approvalExecutionSessionRemote.approve(admin2, approvalId, approval1);
fail("It shouldn't be possible to approve a rejected request");
} catch (ApprovalException e) {
log.info("ApprovalException: " + e.getErrorCode() + ". " + e.getMessage());
}
// Test that the approval expires as it should
Thread.sleep(EXPIRATION_SLEEP);
result = approvalSessionRemote.findApprovalDataVO(approvalId);
assertEquals(1, result.size());
next = result.iterator().next();
assertEquals("Status = " + next.getStatus(), ApprovalDataVO.STATUS_EXPIRED, next.getStatus());
// Try to reject an expired request
try {
approvalExecutionSessionRemote.reject(admin2, approvalId, rejection);
fail("It shouln't be possible to reject and expired request");
} catch (ApprovalException e) {
log.debug("Caught expected exception: " + e.getMessage());
}
approvalSessionRemote.removeApprovalRequest(admin1, next.getId());
log.trace("testIsApproved");
int approvalId = removeApprovalIds.get(1);
approvalSessionRemote.addApprovalRequest(reqadmin, nonExecutableRequestLongExpirationPeriod);
int status = approvalSessionRemote.isApproved(approvalId);
assertEquals(2, status);
approvalExecutionSessionRemote.approve(admin1, approvalId, createApprovalLongExpirationPeriod("ap1test"));
status = approvalSessionRemote.isApproved(approvalId);
assertEquals(1, status);
approvalExecutionSessionRemote.approve(admin2, approvalId, createApprovalLongExpirationPeriod("ap2test"));
status = approvalSessionRemote.isApproved(approvalId);
assertEquals(ApprovalDataVO.STATUS_APPROVED, status);
log.trace("testExtendApprovalRequest");
int approvalId = removeApprovalIds.get(0);
int requestId = approvalSessionRemote.addApprovalRequest(admin1, nonExecutableRequest);
Thread.sleep(EXPIRATION_SLEEP);
// Should be in expired state now
ApprovalDataVO result = approvalSessionRemote.findNonExpiredApprovalRequest(approvalId);
assertNull(result);
// Try to extend without having enabled request extension in the profile. Should fail
try {
approvalSessionProxyRemote.extendApprovalRequestNoAuth(admin1, requestId, 1000);
fail("Should not be able to extend request when disabled in profile");
} catch (Exception e) {
// NOPMD expected
}
// Enable approval extension
approvalProfile.setMaxExtensionTime(3000);
approvalProfileSession.changeApprovalProfile(intadmin, approvalProfile);
// Extend the validity of the request
approvalSessionProxyRemote.extendApprovalRequestNoAuth(admin1, requestId, 2000);
// Should have been unexpired, so findNonExpiredApprovalRequest should return it.
// And the approvalId (the request hash) should not change by a change of expiry date.
log.debug("Trying to find approval with ApprovalId " + approvalId);
result = approvalSessionRemote.findNonExpiredApprovalRequest(approvalId);
assertNotNull(result);
assertEquals(ApprovalDataVO.STATUS_WAITINGFORAPPROVAL, result.getStatus()); // should not change at all during the test, just a safety check
log.trace("testFindNonExpiredApprovalRequest");
int approvalId = removeApprovalIds.get(0);
approvalSessionRemote.addApprovalRequest(admin1, nonExecutableRequest);
Thread.sleep(EXPIRATION_SLEEP);
// Then after one of them have expired
approvalSessionRemote.addApprovalRequest(admin1, nonExecutableRequest);
ApprovalDataVO result = approvalSessionRemote.findNonExpiredApprovalRequest(approvalId);
assertNotNull("Approval should not be found, because it should have expired", result);
assertEquals(ApprovalDataVO.STATUS_WAITINGFORAPPROVAL, result.getStatus());
log.trace("testQuery");
// Add a few requests
final DummyApprovalRequest req1 = new DummyApprovalRequest(reqadmin, null, caid, EndEntityConstants.EMPTY_END_ENTITY_PROFILE, false, approvalProfileLongExpirationPeriod);
final int req1ApprovalId = req1.generateApprovalId();
final DummyApprovalRequest req2 = new DummyApprovalRequest(admin1, null, caid, EndEntityConstants.EMPTY_END_ENTITY_PROFILE, false, approvalProfileLongExpirationPeriod);
final int req2ApprovalId = req2.generateApprovalId();
final DummyApprovalRequest req3 = new DummyApprovalRequest(admin2, null, 3, 2, false, approvalProfileLongExpirationPeriod);
final int req3ApprovalId = req3.generateApprovalId();
removeApprovalIds.add(req1ApprovalId);
removeApprovalIds.add(req2ApprovalId);
removeApprovalIds.add(req3ApprovalId);
approvalSessionRemote.addApprovalRequest(admin1, req1);
approvalSessionRemote.addApprovalRequest(admin1, req2);
approvalSessionRemote.addApprovalRequest(admin1, req3);
// Make some queries
Query q1 = new Query(Query.TYPE_APPROVALQUERY);
q1.add(ApprovalMatch.MATCH_WITH_APPROVALTYPE, BasicMatch.MATCH_TYPE_EQUALS, "" + req1.getApprovalType());
List result = approvalSessionProxyRemote.query(q1, 0, 3, "cAId=" + caid,
"(endEntityProfileId=" + EndEntityConstants.EMPTY_END_ENTITY_PROFILE + ")");
assertTrue("Result size " + result.size(), result.size() >= 2 && result.size() <= 3);
result = approvalSessionProxyRemote.query(q1, 1, 3, "cAId=" + caid, "(endEntityProfileId=" + EndEntityConstants.EMPTY_END_ENTITY_PROFILE + ")");
assertTrue("Result size " + result.size(), result.size() >= 1 && result.size() <= 3);
result = approvalSessionProxyRemote.query(q1, 0, 1, "cAId=" + caid, "(endEntityProfileId=" + EndEntityConstants.EMPTY_END_ENTITY_PROFILE + ")");
assertEquals("Result size " + result.size(), 1, result.size());
Query q2 = new Query(Query.TYPE_APPROVALQUERY);
q2.add(ApprovalMatch.MATCH_WITH_STATUS, BasicMatch.MATCH_TYPE_EQUALS, "" + ApprovalDataVO.STATUS_WAITINGFORAPPROVAL, Query.CONNECTOR_AND);
q2.add(ApprovalMatch.MATCH_WITH_REQUESTADMINCERTSERIALNUMBER, BasicMatch.MATCH_TYPE_EQUALS, reqadmincert.getSerialNumber().toString(16));
result = approvalSessionProxyRemote.query(q1, 1, 3, "cAId=" + caid, "(endEntityProfileId=" + EndEntityConstants.EMPTY_END_ENTITY_PROFILE + ")");
assertTrue("Result size " + result.size(), result.size() >= 1 && result.size() <= 3);
log.trace("testExpiredQuery");
approvalProfile.setRequestExpirationPeriod(0);
approvalProfileSession.changeApprovalProfile(intadmin, approvalProfile);
// Add a few requests
final DummyApprovalRequest expiredRequest = new DummyApprovalRequest(admin3, null, caid, EndEntityConstants.EMPTY_END_ENTITY_PROFILE, false, approvalProfile);
removeApprovalIds.add(expiredRequest.generateApprovalId());
approvalSessionRemote.addApprovalRequest(admin1, expiredRequest);
Query expiredQuery = new Query(Query.TYPE_APPROVALQUERY);
expiredQuery.add(ApprovalMatch.MATCH_WITH_APPROVALTYPE, BasicMatch.MATCH_TYPE_EQUALS, "" + expiredRequest.getApprovalType(), Query.CONNECTOR_AND);
expiredQuery.add(TimeMatch.MATCH_WITH_EXPIRETIME, null, new Date());
List result = approvalSessionProxyRemote.query(
expiredQuery,
0,
3,
"cAId=" + caid,
"(endEntityProfileId=" + EndEntityConstants.EMPTY_END_ENTITY_PROFILE + ")");
assertTrue("At least one expired query was not returned.", result.size() > 0);
log.trace("testApprovalsWithExternalAdmins()");
int approvalId = removeApprovalIds.get(1);
approvalSessionRemote.addApprovalRequest(admin1, nonExecutableRequestLongExpirationPeriod);
approvalExecutionSessionRemote.approve(admin1, approvalId, createApprovalLongExpirationPeriod("ap1test"));
Collection result = approvalSessionRemote.findApprovalDataVO(approvalId);
assertEquals(1, result.size());
ApprovalDataVO next = result.iterator().next();
assertEquals("Status = " + next.getStatus(), ApprovalDataVO.STATUS_WAITINGFORAPPROVAL, next.getStatus());
assertEquals(1, next.getRemainingApprovals());
approvalExecutionSessionRemote.approve(externaladmin, approvalId, createApprovalLongExpirationPeriod("ap2test"));
result = approvalSessionRemote.findApprovalDataVO(approvalId);
assertEquals(1, result.size());
next = result.iterator().next();
assertEquals("Status = " + next.getStatus(), ApprovalDataVO.STATUS_APPROVED, next.getStatus());
assertEquals(0, next.getRemainingApprovals());
log.trace(" principals = new HashSet<>(Collections.singletonList((Principal) certificate.getSubjectX500Principal()));
Set credentials = new HashSet<>(Collections.singletonList(certificate));
return new AuthenticationSubject(principals, credentials);
}
private AuthenticationToken getCliAdmin() {
final String username = EjbcaConfiguration.getCliDefaultUser();
final String password = EjbcaConfiguration.getCliDefaultPassword();
final Set principals = new HashSet<>();
principals.add(new UsernamePrincipal(username));
final AuthenticationSubject subject = new AuthenticationSubject(principals, null);
final CliAuthenticationToken authenticationToken = (CliAuthenticationToken) EjbRemoteHelper.INSTANCE.getRemoteSession(
CliAuthenticationProviderSessionRemote.class).authenticate(subject);
authenticationToken.setSha1HashFromCleartextPassword(password);
return authenticationToken;
}
private Approval createApproval(final String approvalComment) {
return new Approval(approvalComment, AccumulativeApprovalProfile.FIXED_STEP_ID, getPartitionId());
}
private Approval createApprovalLongExpirationPeriod(final String approvalComment) {
return new Approval(approvalComment, AccumulativeApprovalProfile.FIXED_STEP_ID, getLongExpirationPeriodApprovalPartitionId());
}
}