Java tutorial
/************************************************************************* * * * 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.upgrade; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Serializable; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.ejb.EJB; import javax.ejb.SessionContext; import javax.ejb.Stateless; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.cesecore.audit.enums.EventStatus; import org.cesecore.audit.enums.EventTypes; import org.cesecore.audit.enums.ModuleTypes; import org.cesecore.audit.enums.ServiceTypes; import org.cesecore.audit.log.SecurityEventsLoggerSessionLocal; import org.cesecore.authentication.tokens.AlwaysAllowLocalAuthenticationToken; import org.cesecore.authentication.tokens.AuthenticationToken; import org.cesecore.authentication.tokens.UsernamePrincipal; import org.cesecore.authentication.tokens.X509CertificateAuthenticationToken; import org.cesecore.authorization.AuthorizationDeniedException; import org.cesecore.authorization.cache.AccessTreeUpdateSessionLocal; import org.cesecore.authorization.control.AccessControlSessionLocal; import org.cesecore.authorization.control.StandardRules; import org.cesecore.authorization.rules.AccessRuleData; import org.cesecore.authorization.rules.AccessRuleExistsException; import org.cesecore.authorization.rules.AccessRuleManagementSessionLocal; import org.cesecore.authorization.rules.AccessRuleState; import org.cesecore.authorization.user.AccessUserAspectData; import org.cesecore.certificates.ca.CA; import org.cesecore.certificates.ca.CADoesntExistsException; import org.cesecore.certificates.ca.CAInfo; import org.cesecore.certificates.ca.CaSessionLocal; import org.cesecore.certificates.ca.InvalidAlgorithmException; import org.cesecore.certificates.ca.X509CA; import org.cesecore.certificates.ca.extendedservices.ExtendedCAServiceInfo; import org.cesecore.certificates.ca.extendedservices.ExtendedCAServiceTypes; import org.cesecore.certificates.certificateprofile.CertificatePolicy; import org.cesecore.certificates.certificateprofile.CertificateProfile; import org.cesecore.certificates.certificateprofile.CertificateProfileData; import org.cesecore.certificates.certificateprofile.CertificateProfileSessionLocal; import org.cesecore.configuration.GlobalConfigurationData; import org.cesecore.configuration.GlobalConfigurationSessionLocal; import org.cesecore.internal.InternalResources; import org.cesecore.jndi.JndiConstants; import org.cesecore.keys.token.CryptoToken; import org.cesecore.keys.token.CryptoTokenSessionLocal; import org.cesecore.roles.RoleData; import org.cesecore.roles.RoleNotFoundException; import org.cesecore.roles.access.RoleAccessSessionLocal; import org.cesecore.roles.management.RoleManagementSessionLocal; import org.cesecore.util.JBossUnmarshaller; import org.ejbca.config.GlobalConfiguration; import org.ejbca.core.ejb.EnterpriseEditionEjbBridgeSessionLocal; import org.ejbca.core.ejb.authorization.ComplexAccessControlSessionLocal; import org.ejbca.core.ejb.ca.publisher.PublisherSessionLocal; import org.ejbca.core.ejb.hardtoken.HardTokenData; import org.ejbca.core.ejb.hardtoken.HardTokenIssuerData; import org.ejbca.core.ejb.ra.raadmin.AdminPreferencesData; import org.ejbca.core.ejb.ra.raadmin.EndEntityProfileData; import org.ejbca.core.model.ca.caadmin.extendedcaservices.CmsCAService; import org.ejbca.core.model.ca.caadmin.extendedcaservices.HardTokenEncryptCAService; import org.ejbca.core.model.ca.caadmin.extendedcaservices.HardTokenEncryptCAServiceInfo; import org.ejbca.core.model.ca.caadmin.extendedcaservices.KeyRecoveryCAService; import org.ejbca.core.model.ca.caadmin.extendedcaservices.KeyRecoveryCAServiceInfo; import org.ejbca.core.model.ca.caadmin.extendedcaservices.XKMSCAService; import org.ejbca.core.model.ca.publisher.BasePublisher; import org.ejbca.core.model.ca.publisher.upgrade.BasePublisherConverter; import org.ejbca.util.JDBCUtil; import org.ejbca.util.SqlExecutor; /** * The upgrade session bean is used to upgrade the database between EJBCA * releases. * * @version $Id: UpgradeSessionBean.java 21141 2015-04-27 11:27:26Z mikekushner $ */ @Stateless(mappedName = JndiConstants.APP_JNDI_PREFIX + "UpgradeSessionRemote") @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public class UpgradeSessionBean implements UpgradeSessionLocal, UpgradeSessionRemote { private static final Logger log = Logger.getLogger(UpgradeSessionBean.class); /** Internal localization of logs and errors */ private static final InternalResources INTERNAL_RESOURCES = InternalResources.getInstance(); @PersistenceContext(unitName = "ejbca") private EntityManager entityManager; @Resource private SessionContext sessionContext; @EJB private AccessControlSessionLocal accessControlSession; @EJB private AccessRuleManagementSessionLocal accessRuleManagementSession; @EJB private AccessTreeUpdateSessionLocal accessTreeUpdateSession; @EJB private CaSessionLocal caSession; @EJB private CertificateProfileSessionLocal certProfileSession; @EJB private ComplexAccessControlSessionLocal complexAccessControlSession; @EJB private CryptoTokenSessionLocal cryptoTokenSession; @EJB private EnterpriseEditionEjbBridgeSessionLocal enterpriseEditionEjbBridgeSession; @EJB private GlobalConfigurationSessionLocal globalConfigurationSession; @EJB private PublisherSessionLocal publisherSession; @EJB private RoleAccessSessionLocal roleAccessSession; @EJB private RoleManagementSessionLocal roleMgmtSession; @EJB private SecurityEventsLoggerSessionLocal securityEventsLogger; private UpgradeSessionLocal upgradeSession; @PostConstruct public void ejbCreate() { upgradeSession = sessionContext.getBusinessObject(UpgradeSessionLocal.class); } @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED) @Override public boolean upgrade(String dbtype, String sOldVersion, boolean isPost) { try { log.debug("Upgrading from version=" + sOldVersion); final int oldVersion; { final String[] oldVersionArray = sOldVersion.split("\\."); // Split around the '.'-char oldVersion = Integer.parseInt(oldVersionArray[0]) * 100 + Integer.parseInt(oldVersionArray[1]); } if (isPost) { return postUpgrade(oldVersion, dbtype); } return upgrade(dbtype, oldVersion); } catch (RuntimeException e) { // We want to log in server.log so we can analyze the error log.error("Error thrown during upgrade: ", e); throw e; } finally { log.trace("<upgrade()"); } } private boolean postUpgrade(int oldVersion, String dbtype) { log.debug(">post-upgrade from version: " + oldVersion); if (oldVersion < 311) { log.error("Only upgrade from EJBCA 3.11.x is supported in EJBCA 4.0.x."); return false; } // Upgrade database change between EJBCA 3.11.x and EJBCA 4.0.x if needed if (oldVersion < 400) { if (!postMigrateDatabase400()) { return false; } } // Upgrade database change between EJBCA 4.0.x and EJBCA 5.0.x if needed, and previous post-upgrade succeeded if ((oldVersion < 500)) { if (!postMigrateDatabase500(dbtype)) { return false; } } if ((oldVersion < 632)) { if (!postMigrateDatabase632()) { return false; } } return true; } private boolean upgrade(String dbtype, int oldVersion) { log.debug(">upgrade from version: " + oldVersion + ", with dbtype: " + dbtype); if (oldVersion < 311) { log.error("Only upgrade from EJBCA 3.11.x is supported in EJBCA 4.0.x and higher."); return false; } // Upgrade between EJBCA 3.11.x and EJBCA 4.0.x to 5.0.x if (oldVersion <= 500) { if (!migrateDatabase500(dbtype)) { return false; } } if (oldVersion < 600) { log.error("(this is not an error) Nothing to upgrade at this point for EJBCA 6.2."); log.error("(this is not an error) The upgrade to 6.2 is performed when EJBCA is started."); } return true; } /** * Called from other migrate methods, don't call this directly, call from an * interface-method * */ private boolean migrateDatabase(String resource) { // Fetch the resource file with SQL to modify the database tables InputStream in = this.getClass().getResourceAsStream(resource); if (in == null) { log.error("Can not read resource for database '" + resource + "', this database probably does not need table definition changes."); // no error return true; } // Migrate database tables to new columns etc Connection con = null; log.info("Start migration of database."); try { InputStreamReader inreader = new InputStreamReader(in); con = JDBCUtil.getDBConnection(); SqlExecutor sqlex = new SqlExecutor(con, false); sqlex.runCommands(inreader); } catch (SQLException e) { log.error("SQL error during database migration: ", e); return false; } catch (IOException e) { log.error("IO error during database migration: ", e); return false; } finally { JDBCUtil.close(con); } log.info("Finished migration of database."); return true; } /** * (ECA-200:) In EJB 2.1 JBoss CMP used it's own serialization method for all Object/BLOB fields. * * This affects the following entity fields: * - CertificateProfileData.data * - HardTokenData.data * - HardTokenIssuerData.data * - LogConfigurationData.logConfiguration * - AdminPreferencesData.data * - EndEntityProfileData.data * - GlobalConfigurationData.data * * NOTE: You only need to run this if you upgrade a JBoss installation. */ private boolean postMigrateDatabase400() { log.error("(this is not an error) Starting post upgrade from EJBCA 3.11.x to EJBCA 4.0.x"); boolean ret = true; upgradeSession.postMigrateDatabase400SmallTables(); // Migrate small tables in a new transaction log.info(" Processing HardTokenData entities."); log.info(" - Building a list of entities."); final List<String> tokenSNs = HardTokenData.findAllTokenSN(entityManager); int position = 0; final int chunkSize = 1000; while (position < tokenSNs.size()) { log.info(" - Processing entity " + position + " to " + Math.min(position + chunkSize - 1, tokenSNs.size() - 1) + "."); // Migrate HardTokenData table in chunks, each running in a new transaction upgradeSession.postMigrateDatabase400HardTokenData(getSubSet(tokenSNs, position, chunkSize)); position += chunkSize; } log.error("(this is not an error) Finished post upgrade from EJBCA 3.11.x to EJBCA 4.0.x with result: " + ret); return ret; } /** @return a subset of the source list with index as its first item and index+count-1 as its last. */ private <T> List<T> getSubSet(final List<T> source, final int index, final int count) { List<T> ret = new ArrayList<T>(count); for (int i = 0; i < count; i++) { if (source.size() > (index + i)) { ret.add(source.get(index + i)); } } return ret; } @SuppressWarnings("rawtypes") @Override public void postMigrateDatabase400SmallTables() { // LogConfiguration removed for EJBCA 5.0, so no upgrade of that needed log.info(" Processing CertificateProfileData entities."); final List<CertificateProfileData> cpds = CertificateProfileData.findAll(entityManager); for (CertificateProfileData cpd : cpds) { // When the wrong class is given it can either return null, or throw an exception HashMap h = getDataUnsafe(cpd.getDataUnsafe()); cpd.setDataUnsafe(h); } log.info(" Processing HardTokenIssuerData entities."); final List<HardTokenIssuerData> htids = HardTokenIssuerData.findAll(entityManager); for (HardTokenIssuerData htid : htids) { HashMap h = getDataUnsafe(htid.getDataUnsafe()); htid.setDataUnsafe(h); } log.info(" Processing AdminPreferencesData entities."); final List<AdminPreferencesData> apds = AdminPreferencesData.findAll(entityManager); for (AdminPreferencesData apd : apds) { HashMap h = getDataUnsafe(apd.getDataUnsafe()); apd.setDataUnsafe(h); } log.info(" Processing EndEntityProfileData entities."); final List<EndEntityProfileData> eepds = EndEntityProfileData.findAll(entityManager); for (EndEntityProfileData eepd : eepds) { HashMap h = getDataUnsafe(eepd.getDataUnsafe()); eepd.setDataUnsafe(h); } log.info(" Processing GlobalConfigurationData entities."); GlobalConfigurationData gcd = globalConfigurationSession .findByConfigurationId(GlobalConfiguration.GLOBAL_CONFIGURATION_ID); HashMap h = getDataUnsafe(gcd.getDataUnsafe()); gcd.setDataUnsafe(h); } /** * @param cpd * @return */ @SuppressWarnings({ "unchecked", "rawtypes" }) private HashMap getDataUnsafe(Serializable s) { HashMap h = null; try { h = JBossUnmarshaller.extractObject(LinkedHashMap.class, s); if (h == null) { h = new LinkedHashMap(JBossUnmarshaller.extractObject(HashMap.class, s)); } } catch (ClassCastException e) { h = new LinkedHashMap(JBossUnmarshaller.extractObject(HashMap.class, s)); } return h; } @SuppressWarnings("rawtypes") @Override public void postMigrateDatabase400HardTokenData(List<String> subSet) { for (String tokenSN : subSet) { HardTokenData htd = HardTokenData.findByTokenSN(entityManager, tokenSN); if (htd != null) { HashMap h = getDataUnsafe(htd); htd.setDataUnsafe(h); } else { log.warn("Hard token was removed during processing. Ignoring token with serial number '" + tokenSN + "'."); } } } /** * In EJBCA 5.0 we have introduced a new authorization rule system. * The old "/super_administrator" rule is replaced by a rule to access "/" (StandardRules.ROLE_ROOT.resource()) with recursive=true. * therefore we must insert a new access rule in the database in all roles that have super_administrator access. * * We have also added a column to the table AdminEntityData: tokenType * * @param dbtype A string representation of the actual database. * */ @SuppressWarnings({ "unchecked", "deprecation" }) private boolean migrateDatabase500(String dbtype) { log.error("(this is not an error) Starting upgrade from ejbca 4.0.x to ejbca 5.0.x"); boolean ret = true; AuthenticationToken admin = new AlwaysAllowLocalAuthenticationToken( new UsernamePrincipal("UpgradeSessionBean.migrateDatabase500")); //Upgrade database migrateDatabase("/400_500/400_500-upgrade-" + dbtype + ".sql"); // fix CAs that don't have classpath for extended CA services Collection<Integer> caids = caSession.getAllCaIds(); for (Integer caid : caids) { try { CA ca = caSession.getCAForEdit(admin, caid); if (ca.getCAType() == CAInfo.CATYPE_X509) { Collection<Integer> extendedServiceTypes = ca.getExternalCAServiceTypes(); for (Integer type : extendedServiceTypes) { ExtendedCAServiceInfo info = ca.getExtendedCAServiceInfo(type); if (info == null) { @SuppressWarnings("rawtypes") HashMap data = ca.getExtendedCAServiceData(type); switch (type) { case ExtendedCAServiceTypes.TYPE_XKMSEXTENDEDSERVICE: data.put(ExtendedCAServiceInfo.IMPLEMENTATIONCLASS, XKMSCAService.class.getName()); ca.setExtendedCAServiceData(type, data); log.info("Updating extended CA service of type " + type + " with implementation class " + XKMSCAService.class.getName()); break; case ExtendedCAServiceTypes.TYPE_CMSEXTENDEDSERVICE: data.put(ExtendedCAServiceInfo.IMPLEMENTATIONCLASS, CmsCAService.class.getName()); ca.setExtendedCAServiceData(type, data); log.info("Updating extended CA service of type " + type + " with implementation class " + CmsCAService.class.getName()); break; case ExtendedCAServiceTypes.TYPE_HARDTOKENENCEXTENDEDSERVICE: data.put(ExtendedCAServiceInfo.IMPLEMENTATIONCLASS, HardTokenEncryptCAService.class.getName()); ca.setExtendedCAServiceData(type, data); log.info("Updating extended CA service of type " + type + " with implementation class " + HardTokenEncryptCAService.class.getName()); break; case ExtendedCAServiceTypes.TYPE_KEYRECOVERYEXTENDEDSERVICE: data.put(ExtendedCAServiceInfo.IMPLEMENTATIONCLASS, KeyRecoveryCAService.class.getName()); ca.setExtendedCAServiceData(type, data); log.info("Updating extended CA service of type " + type + " with implementation class " + KeyRecoveryCAService.class.getName()); break; default: break; } } else { // If we can't get info for the HardTokenEncrypt or KeyRecovery service it means they don't exist // as such in the database, but was hardcoded before. We need to create them from scratch switch (type) { case ExtendedCAServiceTypes.TYPE_HARDTOKENENCEXTENDEDSERVICE: HardTokenEncryptCAServiceInfo htinfo = new HardTokenEncryptCAServiceInfo( ExtendedCAServiceInfo.STATUS_ACTIVE); HardTokenEncryptCAService htservice = new HardTokenEncryptCAService(htinfo); log.info("Creating extended CA service of type " + type + " with implementation class " + HardTokenEncryptCAService.class.getName()); ca.setExtendedCAService(htservice); break; case ExtendedCAServiceTypes.TYPE_KEYRECOVERYEXTENDEDSERVICE: KeyRecoveryCAServiceInfo krinfo = new KeyRecoveryCAServiceInfo( ExtendedCAServiceInfo.STATUS_ACTIVE); KeyRecoveryCAService krservice = new KeyRecoveryCAService(krinfo); log.info("Creating extended CA service of type " + type + " with implementation class " + KeyRecoveryCAService.class.getName()); ca.setExtendedCAService(krservice); break; default: break; } } } // If key recovery and hard token encrypt service does not exist, we have to create them CAInfo cainfo = ca.getCAInfo(); Collection<ExtendedCAServiceInfo> extendedcaserviceinfos = new ArrayList<ExtendedCAServiceInfo>(); if (!extendedServiceTypes.contains(ExtendedCAServiceTypes.TYPE_HARDTOKENENCEXTENDEDSERVICE)) { log.info("Adding new extended CA service of type " + ExtendedCAServiceTypes.TYPE_HARDTOKENENCEXTENDEDSERVICE + " with implementation class " + HardTokenEncryptCAService.class.getName()); extendedcaserviceinfos .add(new HardTokenEncryptCAServiceInfo(ExtendedCAServiceInfo.STATUS_ACTIVE)); } if (!extendedServiceTypes.contains(ExtendedCAServiceTypes.TYPE_KEYRECOVERYEXTENDEDSERVICE)) { log.info("Adding new extended CA service of type " + ExtendedCAServiceTypes.TYPE_KEYRECOVERYEXTENDEDSERVICE + " with implementation class " + KeyRecoveryCAService.class.getName()); extendedcaserviceinfos .add(new KeyRecoveryCAServiceInfo(ExtendedCAServiceInfo.STATUS_ACTIVE)); } if (!extendedcaserviceinfos.isEmpty()) { cainfo.setExtendedCAServiceInfos(extendedcaserviceinfos); final CryptoToken cryptoToken = cryptoTokenSession .getCryptoToken(ca.getCAToken().getCryptoTokenId()); ca.updateCA(cryptoToken, cainfo); } // Finally store the upgraded CA caSession.editCA(admin, ca, true); } } catch (CADoesntExistsException e) { log.error("CA does not exist during upgrade: " + caid, e); } catch (AuthorizationDeniedException e) { log.error("Authorization denied to CA during upgrade: " + caid, e); } catch (InvalidAlgorithmException e) { log.error("Illegal Crypto Token algortihm during upgrade. CA Id: " + caid, e); } } /* * Upgrade super_administrator access rules to be a /* rule, so super_administrators can still do everything. * * Also, set token types to the standard X500 principal if otherwise null. Since token types is a new concept, * all existing aspects/admin entities must be of this type */ Collection<RoleData> roles = roleAccessSession.getAllRoles(); for (RoleData role : roles) { Collection<AccessUserAspectData> updatedUsers = new ArrayList<AccessUserAspectData>(); for (AccessUserAspectData userAspect : role.getAccessUsers().values()) { if (userAspect.getTokenType() == null) { userAspect.setTokenType(X509CertificateAuthenticationToken.TOKEN_TYPE); updatedUsers.add(userAspect); } } try { role = roleMgmtSession.addSubjectsToRole(admin, role, updatedUsers); } catch (RoleNotFoundException e) { log.error("Not possible to edit subjects for role: " + role.getRoleName(), e); } catch (AuthorizationDeniedException e) { log.error("Not possible to edit subjects for role: " + role.getRoleName(), e); } //The old "/super_administrator" rule is replaced by a rule to access "/" (StandardRules.ROLE_ROOT.resource()) with recursive=true. // therefore we must insert a new access rule in the database in all roles that have super_administrator access. final Map<Integer, AccessRuleData> rulemap = role.getAccessRules(); final Collection<AccessRuleData> rules = rulemap.values(); for (AccessRuleData rule : rules) { if (StringUtils.equals("/super_administrator", rule.getAccessRuleName()) && rule.getInternalState().equals(AccessRuleState.RULE_ACCEPT)) { // Now we add a new rule final AccessRuleData slashRule = new AccessRuleData(role.getRoleName(), StandardRules.ROLE_ROOT.resource(), AccessRuleState.RULE_ACCEPT, true); log.info("Replacing all rules of the role '" + role.getRoleName() + "' with the rule '" + slashRule + "' since the role contained the '" + StandardRules.ROLE_ROOT + "' rule."); final Collection<AccessRuleData> newrules = new ArrayList<AccessRuleData>(); newrules.add(slashRule); try { // if one of the rules was "super administrator" then all other rules of the role was disregarded in version<5. So now it should only be the '/' rule for the role. upgradeSession.replaceAccessRulesInRoleNoAuth(admin, role, newrules); } catch (RoleNotFoundException e) { log.error("Not possible to add new access rule to role: " + role.getRoleName(), e); } break; // no need to continue with this role } } } accessTreeUpdateSession.signalForAccessTreeUpdate(); accessControlSession.forceCacheExpire(); log.error("(this is not an error) Finished upgrade from ejbca 4.0.x to ejbca 5.0.x with result: " + ret); return ret; } @Deprecated @Override @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public RoleData replaceAccessRulesInRoleNoAuth(final AuthenticationToken authenticationToken, final RoleData role, final Collection<AccessRuleData> accessRules) throws RoleNotFoundException { RoleData result = roleAccessSession.findRole(role.getPrimaryKey()); if (result == null) { final String msg = INTERNAL_RESOURCES.getLocalizedMessage("authorization.errorrolenotexists", role.getRoleName()); throw new RoleNotFoundException(msg); } Map<Integer, AccessRuleData> rulesFromResult = result.getAccessRules(); Map<Integer, AccessRuleData> rulesToResult = new HashMap<Integer, AccessRuleData>(); //Lists for logging purposes. Collection<AccessRuleData> newRules = new ArrayList<AccessRuleData>(); Collection<AccessRuleData> changedRules = new ArrayList<AccessRuleData>(); for (AccessRuleData rule : accessRules) { if (AccessRuleData.generatePrimaryKey(role.getRoleName(), rule.getAccessRuleName()) != rule .getPrimaryKey()) { throw new Error( "Role " + role.getRoleName() + " did not match up with the role that created this rule."); } Integer ruleKey = rule.getPrimaryKey(); if (rulesFromResult.containsKey(ruleKey)) { AccessRuleData oldRule = rulesFromResult.get(ruleKey); if (!oldRule.equals(rule)) { changedRules.add(oldRule); } AccessRuleData newRule = accessRuleManagementSession.setState(rule, rule.getInternalState(), rule.getRecursive()); rulesFromResult.remove(ruleKey); rulesToResult.put(newRule.getPrimaryKey(), newRule); } else { try { newRules.add(accessRuleManagementSession.createRule(rule.getAccessRuleName(), result.getRoleName(), rule.getInternalState(), rule.getRecursive())); } catch (AccessRuleExistsException e) { throw new Error("Access rule exists, but wasn't found in persistence in previous call.", e); } rulesToResult.put(rule.getPrimaryKey(), rule); } } logAccessRulesAdded(authenticationToken, role.getRoleName(), newRules); logAccessRulesChanged(authenticationToken, role.getRoleName(), changedRules); //And for whatever remains: accessRuleManagementSession.remove(rulesFromResult.values()); result.setAccessRules(rulesToResult); result = entityManager.merge(result); logAccessRulesRemoved(authenticationToken, role.getRoleName(), rulesFromResult.values()); accessTreeUpdateSession.signalForAccessTreeUpdate(); accessControlSession.forceCacheExpire(); return result; } private void logAccessRulesAdded(AuthenticationToken authenticationToken, String rolename, Collection<AccessRuleData> addedRules) { if (addedRules.size() > 0) { StringBuilder addedRulesMsg = new StringBuilder(); for (AccessRuleData addedRule : addedRules) { addedRulesMsg.append("[" + addedRule.toString() + "]"); } final String msg = INTERNAL_RESOURCES.getLocalizedMessage("authorization.accessrulesadded", rolename, addedRulesMsg); Map<String, Object> details = new LinkedHashMap<String, Object>(); details.put("msg", msg); securityEventsLogger.log(EventTypes.ROLE_ACCESS_RULE_ADDITION, EventStatus.SUCCESS, ModuleTypes.ROLES, ServiceTypes.CORE, authenticationToken.toString(), null, null, null, details); } } private void logAccessRulesChanged(AuthenticationToken authenticationToken, String rolename, Collection<AccessRuleData> changedRules) { if (changedRules.size() > 0) { StringBuilder changedRulesMsg = new StringBuilder(); for (AccessRuleData changedRule : changedRules) { changedRulesMsg.append("[" + changedRule.toString() + "]"); } final String msg = INTERNAL_RESOURCES.getLocalizedMessage("authorization.accessruleschanged", rolename, changedRulesMsg); Map<String, Object> details = new LinkedHashMap<String, Object>(); details.put("msg", msg); securityEventsLogger.log(EventTypes.ROLE_ACCESS_RULE_CHANGE, EventStatus.SUCCESS, ModuleTypes.ROLES, ServiceTypes.CORE, authenticationToken.toString(), null, null, null, details); } } private void logAccessRulesRemoved(AuthenticationToken authenticationToken, String rolename, Collection<AccessRuleData> removedRules) { if (removedRules.size() > 0) { StringBuilder removedRulesMsg = new StringBuilder(); for (AccessRuleData removedRule : removedRules) { removedRulesMsg.append("[" + removedRule.getAccessRuleName() + "]"); } final String msg = INTERNAL_RESOURCES.getLocalizedMessage("authorization.accessrulesremoved", rolename, removedRulesMsg); Map<String, Object> details = new LinkedHashMap<String, Object>(); details.put("msg", msg); securityEventsLogger.log(EventTypes.ROLE_ACCESS_RULE_DELETION, EventStatus.SUCCESS, ModuleTypes.ROLES, ServiceTypes.CORE, authenticationToken.toString(), null, null, null, details); } } /** * EJBCA 6.3.1.1 moves the VA Publisher from Community to Enterprise, changing its baseclass in the process for Enterprise users. * This method will fail gracefully if user is not running Enterprise. It will also upgrade any placeholder publishers from 6.3.1.1 Community * if so required. * * @return true if the upgrade was successful */ private boolean postMigrateDatabase632() { if (!enterpriseEditionEjbBridgeSession.isRunningEnterprise()) { log.error("Upgrade procedure to 6.3.2 can only be run on EJBCA Enterprise."); return false; } log.error("(this is not an error) Starting post upgrade to 6.3.2"); //Find all publishers, make copies of them using the new publisher class. Map<Integer, BasePublisher> allPublishers = publisherSession.getAllPublishers(); Map<Integer, String> publisherNames = publisherSession.getPublisherIdToNameMap(); BasePublisherConverter publisherFactory; try { publisherFactory = (BasePublisherConverter) Class .forName("org.ejbca.va.publisher.EnterpriseValidationAuthorityPublisherFactoryImpl") .newInstance(); } catch (InstantiationException e) { //Shouldn't happen since we've already checked that we're running Enterprise throw new IllegalStateException(e); } catch (IllegalAccessException e) { //Shouldn't happen since we've already checked that we're running Enterprise throw new IllegalStateException(e); } catch (ClassNotFoundException e) { //Shouldn't happen since we've already checked that we're running Enterprise throw new IllegalStateException(e); } AuthenticationToken admin = new AlwaysAllowLocalAuthenticationToken( new UsernamePrincipal("UpgradeSessionBean.postMigrateDatabase631")); for (Integer publisherId : allPublishers.keySet()) { BasePublisher newPublisher = publisherFactory.createPublisher(allPublishers.get(publisherId)); if (newPublisher != null) { try { String publisherName = publisherNames.get(publisherId); log.info("Upgrading publisher: " + publisherName); publisherSession.changePublisher(admin, publisherName, newPublisher); } catch (AuthorizationDeniedException e) { throw new IllegalStateException("Always allow token was not given access to publishers.", e); } } } return true; } /** * In EJBCA 5.0 we have changed classname for CertificatePolicy. * In order to allow us to remove the legacy class in the future we want to upgrade all certificate profiles to use the new classname * * In order to be able to create new Roles we also need to remove the long deprecated database column caId, otherwise * we will get a database error during insert. Reading works fine though, so this is good for a post upgrade in order * to allow for 100% uptime upgrades. */ private boolean postMigrateDatabase500(String dbtype) { log.error("(this is not an error) Starting post upgrade from EJBCA 4.0.x to ejbca 5.0.x"); boolean ret = true; AuthenticationToken admin = new AlwaysAllowLocalAuthenticationToken( new UsernamePrincipal("UpgradeSessionBean.postMigrateDatabase500")); // post-upgrade "change CertificatePolicy from ejbca class to cesecore class in certificate profiles that have that defined. Map<Integer, String> map = certProfileSession.getCertificateProfileIdToNameMap(); Set<Integer> ids = map.keySet(); for (Integer id : ids) { CertificateProfile profile = certProfileSession.getCertificateProfile(id); final List<CertificatePolicy> policies = profile.getCertificatePolicies(); if ((policies != null) && (!policies.isEmpty())) { List<CertificatePolicy> newpolicies = getNewPolicies(policies); // Set the updated policies, replacing the old profile.setCertificatePolicies(newpolicies); try { final String profName = map.get(id); log.info("Upgrading CertificatePolicy of certificate profile '" + profName + "'. This profile can no longer be used with EJBCA 4.x."); certProfileSession.changeCertificateProfile(admin, profName, profile); } catch (AuthorizationDeniedException e) { log.error("Error upgrading certificate policy: ", e); } } } // post-upgrade "change CertificatePolicy from ejbca class to cesecore class in CAs profiles that have that defined? // fix CAs that don't have classpath for extended CA services Collection<Integer> caids = caSession.getAllCaIds(); for (Integer caid : caids) { try { CA ca = caSession.getCAForEdit(admin, caid); if (ca.getCAType() == CAInfo.CATYPE_X509) { try { X509CA x509ca = (X509CA) ca; final List<CertificatePolicy> policies = x509ca.getPolicies(); if ((policies != null) && (!policies.isEmpty())) { List<CertificatePolicy> newpolicies = getNewPolicies(policies); // Set the updated policies, replacing the old x509ca.setPolicies(newpolicies); // Finally store the upgraded CA log.info("Upgrading CertificatePolicy of CA '" + ca.getName() + "'. This CA can no longer be used with EJBCA 4.x."); caSession.editCA(admin, ca, true); } } catch (ClassCastException e) { log.error("CA is not of type X509CA: " + caid + ", " + ca.getClass().getName()); } } } catch (CADoesntExistsException e) { log.error("CA does not exist during upgrade: " + caid, e); } catch (AuthorizationDeniedException e) { log.error("Authorization denied to CA during upgrade: " + caid, e); } } boolean exists = upgradeSession.checkColumnExists500(); if (exists) { ret = migrateDatabase("/400_500/400_500-post-upgrade-" + dbtype + ".sql"); } // Creates a super admin role for Cli usage. post-upgrade to remove caId column must have been run in order // for this command to succeed. // In practice this means that when upgrading from EJBCA 4.0 you can not use the CLI in 5.0 before you // have finished migrating all your 4.0 nodes and run post-upgrade. complexAccessControlSession.createSuperAdministrator(); //Remove all old roles, should remove associated aspects and rules as well. removeOldRoles500(); log.error( "(this is not an error) Finished post upgrade from EJBCA 4.0.x to EJBCA 5.0.x with result: " + ret); return ret; } /** * This method removes the following now unused roles: * DEFAULT * Temporary Super Administrator Group * Public Web Users */ private void removeOldRoles500() { final String defaultRoleName = "DEFAULT"; final String tempSuperAdminRoleName = "Temporary Super Administrator Group"; final String publicWebRoleName = "Public Web Users"; final AuthenticationToken admin = new AlwaysAllowLocalAuthenticationToken( new UsernamePrincipal("UpgradeSessionBean.removeOldRoles")); try { RoleData defaultRole = roleAccessSession.findRole(defaultRoleName); if (defaultRole != null) { try { roleMgmtSession.remove(admin, defaultRole); } catch (RoleNotFoundException e) { //Ignore, can't happen } } RoleData tempSuperAdminRole = roleAccessSession.findRole(tempSuperAdminRoleName); if (tempSuperAdminRole != null) { try { roleMgmtSession.remove(admin, tempSuperAdminRole); } catch (RoleNotFoundException e) { //Ignore, can't happen } } RoleData publicWebRole = roleAccessSession.findRole(publicWebRoleName); if (publicWebRole != null) { try { roleMgmtSession.remove(admin, publicWebRole); } catch (RoleNotFoundException e) { //Ignore, can't happen } } } catch (AuthorizationDeniedException e) { throw new IllegalStateException( "AlwaysAllowLocalAuthenticationToken should not have been denied authorization"); } } private List<CertificatePolicy> getNewPolicies(final List<CertificatePolicy> policies) { final List<CertificatePolicy> newpolicies = new ArrayList<CertificatePolicy>(); for (final Iterator<?> it = policies.iterator(); it.hasNext();) { Object o = it.next(); try { final CertificatePolicy policy = (CertificatePolicy) o; // This was a new policy (org.cesecore), just add it newpolicies.add(policy); } catch (ClassCastException e) { // Here we stumbled upon an old certificate policy final org.ejbca.core.model.ca.certificateprofiles.CertificatePolicy policy = (org.ejbca.core.model.ca.certificateprofiles.CertificatePolicy) o; CertificatePolicy newpolicy = new CertificatePolicy(policy.getPolicyID(), policy.getQualifierId(), policy.getQualifier()); newpolicies.add(newpolicy); } } return newpolicies; } /** * Checks if the column cAId column exists in AdminGroupData * * @return true or false if the column exists or not */ public boolean checkColumnExists500() { // Try to find out if rowVersion exists and upgrade the PublisherQueueData in that case // This is needed since PublisherQueueData is a rather new table so it may have been created when the server started // and we are upgrading from a not so new version final Connection connection = JDBCUtil.getDBConnection(); boolean exists = false; try { final PreparedStatement stmt = connection .prepareStatement("select cAId from AdminGroupData where pk='0'"); stmt.executeQuery(); // If it did not throw an exception the column exists and we must run the post upgrade sql exists = true; log.info("cAId column exists in AdminGroupData"); } catch (SQLException e) { // Column did not exist log.info("cAId column does not exist in AdminGroupData"); log.error(e); } finally { try { connection.close(); } catch (SQLException e) { // do nothing } } return exists; } }