Java tutorial
/* ************************************************************************* * The contents of this file are subject to the Openbravo Public License * Version 1.1 (the "License"), being the Mozilla Public License * Version 1.1 with a permitted attribution clause; you may not use this * file except in compliance with the License. You may obtain a copy of * the License at http://www.openbravo.com/legal/license.html * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * The Original Code is Openbravo ERP. * The Initial Developer of the Original Code is Openbravo SLU * All portions are Copyright (C) 2009-2013 Openbravo SLU * All Rights Reserved. * Contributor(s): ______________________________________. ************************************************************************ */ package org.openbravo.erpCommon.obps; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectInputStream; import java.math.BigInteger; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import java.security.KeyFactory; import java.security.PublicKey; import java.security.Signature; import java.security.spec.X509EncodedKeySpec; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.UUID; import java.util.zip.CRC32; import javax.crypto.Cipher; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Appender; import org.apache.log4j.Logger; import org.apache.log4j.PatternLayout; import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; import org.hibernate.Query; import org.hibernate.criterion.Order; import org.hibernate.criterion.Restrictions; import org.openbravo.base.exception.OBException; import org.openbravo.base.secureApp.VariablesSecureApp; import org.openbravo.base.session.OBPropertiesProvider; import org.openbravo.dal.core.DalContextListener; import org.openbravo.dal.core.OBContext; import org.openbravo.dal.service.OBCriteria; import org.openbravo.dal.service.OBDal; import org.openbravo.database.ConnectionProvider; import org.openbravo.erpCommon.obps.DisabledModules.Artifacts; import org.openbravo.erpCommon.utility.HttpsUtils; import org.openbravo.erpCommon.utility.OBError; import org.openbravo.erpCommon.utility.SystemInfo; import org.openbravo.erpCommon.utility.Utility; import org.openbravo.model.ad.access.Session; import org.openbravo.model.ad.module.Module; import org.openbravo.model.ad.system.HeartbeatLog; import org.openbravo.model.ad.system.SystemInformation; import org.openbravo.model.ad.ui.Tab; import org.openbravo.scheduling.ProcessBundle; import org.openbravo.service.db.DalConnectionProvider; public class ActivationKey { private final static String OB_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCPwCM5RfisLvWhujHajnLEjEpLC7DOXLySuJmHBqcQ8AQ63yZjlcv3JMkHMsPqvoHF3s2ztxRcxBRLc9C2T3uXQg0PTH5IAxsV4tv05S+tNXMIajwTeYh1LCoQyeidiid7FwuhtQNQST9/FqffK1oVFBnWUfgZKLMO2ZSHoEAORwIDAQAB"; private final static String OB_PUBLIC_KEY2 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCeivfuzeE+hdv7mXEyOWTpGglsT1J+UHcp9RrHydgLgccPdQ5EjqtKVSc/jzzJV5g+9XaSxz9pK5TuzzdN4fJHPCnuO0EiwWI2dxS/t1Boo+gGageGZyFRMhMsULU4902gzmw1qugEskUSKONJcR65H06HYRn2fTgVbGvEhFMASwIDAQAB"; private final static String ON_DEMAND_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCfHx5q0Bs45Eg2x1V6ASx86ZWNh8jniPprH5xonuJ5ATVSDQ/UGsz1d0v/3WkWXaj98OwUPJt5/iSe7l5DAZ7I2C22y3CQx8pNiBfi4FK+HtRM4pOhK5YQXV2vNV5hTPgsjrOrjdPXZ+SQbDqUIGSNhwBVmrczNO9THDN+eQCSQIDAQAB";; private static final String HEARTBEAT_URL = "https://butler.openbravo.com:443/heartbeat-server/heartbeat"; private boolean isActive = false; private boolean hasActivationKey = false; private Date lastUpdateTimestamp; private String errorMessage = ""; private String messageType = "Error"; private Properties instanceProperties; private static final Logger log = Logger.getLogger(ActivationKey.class); private String strPublicKey; private static boolean opsLog = false; private static String opsLogId; private Long pendingTime; private boolean hasExpired = false; private boolean subscriptionConvertedProperty = false; private boolean subscriptionActuallyConverted = false; private LicenseClass licenseClass; private LicenseType licenseType; private List<String> tier1Artifacts; private List<String> tier2Artifacts; private List<String> goldenExcludedArtifacts; private Date lastRefreshTime; private boolean trial = false; private boolean golden = false; private Date startDate; private Date endDate; private boolean limitedWsAccess = true; private boolean limitNamedUsers = false; private boolean outOfPlatform = false; private Long maxUsers; private boolean notActiveYet = false; private boolean inconsistentInstance = false; private long maxWsCalls; private long wsDayCounter; private Date initWsCountTime; private List<Date> exceededInLastDays; private static final Logger log4j = Logger.getLogger(ActivationKey.class); private static final String TIER_1_PREMIUM_FEATURE = "T1P"; private static final String TIER_2_PREMIUM_FEATURE = "T2P"; private static final String GOLDEN_EXCLUDED = "GOLDENEXCLUDED"; private static final SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd"); /** * Number of minutes since last license refresh to wait before doing it again */ private static final int REFRESH_MIN_TIME = 60; public enum LicenseRestriction { NO_RESTRICTION, OPS_INSTANCE_NOT_ACTIVE, NUMBER_OF_SOFT_USERS_REACHED, NUMBER_OF_CONCURRENT_USERS_REACHED, MODULE_EXPIRED, NOT_MATCHED_INSTANCE, HB_NOT_ACTIVE, EXPIRED_GOLDEN, CONCURRENT_NAMED_USER, ON_DEMAND_OFF_PLATFORM } public enum CommercialModuleStatus { NO_SUBSCRIBED, ACTIVE, EXPIRED, NO_ACTIVE_YET, CONVERTED_SUBSCRIPTION, DISABLED } public enum FeatureRestriction { NO_RESTRICTION(""), DISABLED_MODULE_RESTRICTION("FeatureInDisabledModule"), TIER1_RESTRICTION( "FEATURE_OBPS_ONLY"), TIER2_RESTRICTION( "FEATURE_OBPS_ONLY"), UNKNOWN_RESTRICTION(""), GOLDEN_RESTRICTION("RESTRICTED_TO_GOLDEN"); private String msg; private FeatureRestriction(String msg) { this.msg = msg; } @Override public String toString() { return msg; } } public enum WSRestriction { NO_RESTRICTION, EXCEEDED_MAX_WS_CALLS, EXCEEDED_WARN_WS_CALLS, EXPIRED, EXPIRED_MODULES; } public enum LicenseClass { COMMUNITY("C"), BASIC("B"), STD("STD"); private String code; private LicenseClass(String code) { this.code = code; } public String getCode() { return code; } } public enum LicenseType { CONCURRENT_USERS("USR"), ON_DEMAND("DMD"); private String code; private LicenseType(String code) { this.code = code; } public String getCode() { return code; } } public enum SubscriptionStatus { COMMUNITY("COM"), ACTIVE("ACT"), CANCEL("CAN"), EXPIRED("EXP"), NO_ACTIVE_YET("NAY"); private String code; private SubscriptionStatus(String code) { this.code = code; } /** * Returns the name of the current status in the given language. */ public String getStatusName(String language) { return Utility.getListValueName("OBPSLicenseStatus", code, language); } } private static final int MILLSECS_PER_DAY = 24 * 60 * 60 * 1000; private static final int PING_TIMEOUT_SECS = 120; private static final Long EXPIRATION_BASIC_DAYS = 30L; private static final Long EXPIRATION_PROF_DAYS = 30L; private final static int WS_DAYS_EXCEEDING_ALLOWED = 5; private final static long WS_DAYS_EXCEEDING_ALLOWED_PERIOD = 30L; private final static long WS_MS_EXCEEDING_ALLOWED_PERIOD = MILLSECS_PER_DAY * WS_DAYS_EXCEEDING_ALLOWED_PERIOD; private static final Long OUT_OF_PLATFORM_DEMAND_MAX_USERS = 2L; private static final String ON_DEMAND_PLATFORM_CHECK_URL = "http://localhost:20290/checkOnDemand?qry="; /** * Session types that are considered for user concurrency */ private static final List<String> ACTIVE_SESSION_TYPES = new ArrayList<String>() { { add("S"); add("CUR"); add("SUR"); } }; private static ActivationKey instance = new ActivationKey(); /** * @see ActivationKey#getInstance(boolean) * */ public static synchronized ActivationKey getInstance() { return getInstance(false); } /** * Obtains the ActivationKey instance. Instances should be get in this way, rather than creating a * new one. * * If refreshIfNeeded parameter is true, license is tried to be refreshed if it is needed to. * * @param refreshIfNeeded * refresh license if needed to * */ public static synchronized ActivationKey getInstance(boolean refreshIfNeeded) { if (refreshIfNeeded) { instance.refreshIfNeeded(); } if (instance.startDate != null) { // check dates in case there is a license with dates instance.checkDates(); } return instance; } public static synchronized void setInstance(ActivationKey ak) { instance = ak; ak.setRefreshTime(new Date()); } private void setRefreshTime(Date time) { lastRefreshTime = time; } /** * Reloads ActivationKey instance from information in DB. */ public static synchronized ActivationKey reload() { ActivationKey ak = getInstance(); org.openbravo.model.ad.system.System sys = OBDal.getInstance() .get(org.openbravo.model.ad.system.System.class, "0"); ak.loadInfo(sys.getActivationKey()); ak.loadRestrictions(); ak.lastUpdateTimestamp = sys.getUpdated(); return ak; } /** * ActivationKey constructor, this should not be used. ActivationKey should be treated as * Singleton, so the {@link ActivationKey#getInstance()} method should be used instead. * <p/> * This constructor is public to maintain backwards compatibility. * * @deprecated */ public ActivationKey() { OBContext.setAdminMode(); try { org.openbravo.model.ad.system.System sys = OBDal.getInstance() .get(org.openbravo.model.ad.system.System.class, "0"); strPublicKey = sys.getInstanceKey(); lastUpdateTimestamp = sys.getUpdated(); String activationKey = sys.getActivationKey(); loadInfo(activationKey); loadRestrictions(); } finally { OBContext.restorePreviousMode(); } } public ActivationKey(String publicKey, String activationKey) { strPublicKey = publicKey; loadInfo(activationKey); loadRestrictions(); } private void loadInfo(String activationKey) { // Reset isActive = false; hasActivationKey = false; errorMessage = ""; messageType = "Error"; instanceProperties = null; hasExpired = false; subscriptionConvertedProperty = false; subscriptionActuallyConverted = false; tier1Artifacts = null; tier2Artifacts = null; goldenExcludedArtifacts = null; trial = false; golden = false; licenseClass = LicenseClass.COMMUNITY; licenseType = null; startDate = null; endDate = null; pendingTime = null; limitedWsAccess = false; limitNamedUsers = false; outOfPlatform = false; maxUsers = null; if (strPublicKey == null || activationKey == null || strPublicKey.equals("") || activationKey.equals("")) { hasActivationKey = false; setLogger(); return; } PublicKey pk = getPublicKey(strPublicKey); if (pk == null) { hasActivationKey = true; errorMessage = "@NotAValidKey@"; setLogger(); return; } hasActivationKey = true; try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); boolean signed = decrypt(activationKey.getBytes(), pk, bos, OB_PUBLIC_KEY); if (!signed) { // Basic license is only supported from 2.50mp21, they are signed with second key. So in // case first key does not work, try to use the second one. bos = new ByteArrayOutputStream(); signed = decrypt(activationKey.getBytes(), pk, bos, OB_PUBLIC_KEY2); } if (signed) { byte[] props = bos.toByteArray(); ByteArrayInputStream isProps = new ByteArrayInputStream(props); InputStreamReader reader = new InputStreamReader(isProps, "UTF-8"); instanceProperties = new Properties(); instanceProperties.load(reader); reader.close(); } else { isActive = false; errorMessage = "@NotSigned@"; setLogger(); return; } String sysId = getProperty("sysId"); String dbId = getProperty("dbId"); String macId = getProperty("macId"); SystemInfo.loadId(new DalConnectionProvider(false)); if ((sysId != null && !sysId.isEmpty() && !sysId.equals(SystemInfo.getSystemIdentifier())) || (dbId != null && !dbId.isEmpty() && !dbId.equals(SystemInfo.getDBIdentifier())) || (macId != null && !macId.isEmpty() && !macId.equals(SystemInfo.getMacAddress()))) { isActive = false; inconsistentInstance = true; errorMessage = "@IncorrectLicenseInstance@"; setLogger(); return; } } catch (Exception e) { isActive = false; errorMessage = "@NotAValidKey@"; e.printStackTrace(); setLogger(); return; } // Get license class, old Activation Keys do not have this info, so treat them as Standard // Edition instances String pLicenseClass = getProperty("licenseedition"); if (pLicenseClass == null || pLicenseClass.isEmpty() || pLicenseClass.equals("STD")) { licenseClass = LicenseClass.STD; } else if (pLicenseClass.equals("B")) { licenseClass = LicenseClass.BASIC; } else { log4j.warn("Unknown license class:" + pLicenseClass + ". Using Basic!."); licenseClass = LicenseClass.BASIC; } String pLicenseType = getProperty("lincensetype"); if ("DMD".equals(pLicenseType)) { licenseType = LicenseType.ON_DEMAND; } else if ("USR".equals(pLicenseType)) { licenseType = LicenseType.CONCURRENT_USERS; } else { log4j.warn("Unknown license type:" + pLicenseType + ". Using Concurrent Users!."); licenseType = LicenseType.CONCURRENT_USERS; } if (licenseType == LicenseType.ON_DEMAND) { if (!checkInOnDemandPlatform()) { outOfPlatform = true; String limitusers = getProperty("limitusers"); maxUsers = StringUtils.isEmpty(limitusers) ? 0L : new Long(limitusers); if (maxUsers == 0L) { maxUsers = OUT_OF_PLATFORM_DEMAND_MAX_USERS; } log.warn("On Demand license ouf of platform limiting to " + maxUsers + " concurrent users"); } else { maxUsers = 0L; } } if (licenseType == LicenseType.CONCURRENT_USERS) { String limitusers = getProperty("limitusers"); maxUsers = StringUtils.isEmpty(limitusers) ? 0L : new Long(limitusers); } // Check for dates to know if the instance is active subscriptionConvertedProperty = "true".equals(getProperty("subscriptionConverted")); trial = "true".equals(getProperty("trial")); golden = "true".equals(getProperty("golden")); String strUnlimitedWsAccess = getProperty("unlimitedWsAccess"); if (StringUtils.isEmpty(strUnlimitedWsAccess)) { // old license, setting defaults if (trial || golden) { limitedWsAccess = true; maxWsCalls = 500L; instanceProperties.put("wsPacks", "1"); instanceProperties.put("wsUnitsPerUnit", "500"); initializeWsCounter(); } else { limitedWsAccess = false; } } else { limitedWsAccess = "false".equals(getProperty("unlimitedWsAccess")); if (limitedWsAccess) { String packs = getProperty("wsPacks"); String unitsPack = getProperty("wsUnitsPerUnit"); if (StringUtils.isEmpty(packs) || StringUtils.isEmpty(unitsPack)) { log.warn("Couldn't determine ws call limitation, setting unlimited."); limitedWsAccess = false; } else { try { Integer nPacks = Integer.parseInt(packs); Integer nUnitsPack = Integer.parseInt(unitsPack); maxWsCalls = nPacks * nUnitsPack; log.debug("Maximum ws calls: " + maxWsCalls); initializeWsCounter(); } catch (Exception e) { log.error("Error setting ws call limitation, setting unlimited.", e); limitedWsAccess = false; } } } } try { startDate = sDateFormat.parse(getProperty("startdate")); if (getProperty("enddate") != null) { endDate = sDateFormat.parse(getProperty("enddate")); } } catch (Exception e) { errorMessage = "@ErrorReadingDates@"; isActive = false; log.error(e.getMessage(), e); setLogger(); return; } checkDates(); } private boolean checkInOnDemandPlatform() { InputStream is = null; BufferedReader in = null; try { String qry = UUID.randomUUID().toString(); URL url = new URL(ON_DEMAND_PLATFORM_CHECK_URL + qry); URLConnection conn = url.openConnection(); is = conn.getInputStream(); in = new BufferedReader(new InputStreamReader(is)); String l = in.readLine(); PublicKey pk = getPublicKey(ON_DEMAND_PUBLIC_KEY); ByteArrayOutputStream bos = new ByteArrayOutputStream(); decrypt(l.getBytes(), pk, bos, ON_DEMAND_PUBLIC_KEY); String s = new String(bos.toByteArray()); if (qry.equals(s)) { return true; } return false; } catch (Exception e) { log.error("Error verifying on On Demand platform.", e); return false; } finally { if (is != null) { try { is.close(); } catch (IOException e) { log.error("Error verifying on On Demand platform.", e); } } if (in != null) { try { in.close(); } catch (IOException e) { log.error("Error verifying on On Demand platform.", e); } } } } private void checkDates() { // Check for dates to know if the instance is active Date now = new Date(); if (startDate == null || now.before(startDate)) { isActive = false; notActiveYet = true; String dateFormat = OBPropertiesProvider.getInstance().getOpenbravoProperties() .getProperty("dateFormat.java"); SimpleDateFormat outputFormat = new SimpleDateFormat(dateFormat); errorMessage = "@OPSNotActiveTill@ " + outputFormat.format(startDate); messageType = "Warning"; setLogger(); return; } if (endDate != null) { pendingTime = ((endDate.getTime() - now.getTime()) / MILLSECS_PER_DAY) + 1; if (pendingTime <= 0) { if (subscriptionConvertedProperty) { // A bought out instance is actually converted when the license has expired. subscriptionActuallyConverted = true; } else { isActive = false; hasExpired = true; String dateFormat = OBPropertiesProvider.getInstance().getOpenbravoProperties() .getProperty("dateFormat.java"); SimpleDateFormat outputFormat = new SimpleDateFormat(dateFormat); errorMessage = "@OPSActivationExpired@ " + outputFormat.format(endDate); setLogger(); return; } } } isActive = true; setLogger(); } private boolean decrypt(byte[] bytes, PublicKey pk, ByteArrayOutputStream bos, String strOBPublicKey) throws Exception { PublicKey obPk = getPublicKey(strOBPublicKey); // get OB public key to check signature Signature signer = Signature.getInstance("MD5withRSA"); signer.initVerify(obPk); Cipher cipher = Cipher.getInstance("RSA"); ByteArrayInputStream bis = new ByteArrayInputStream( org.apache.commons.codec.binary.Base64.decodeBase64(bytes)); // Encryptation only accepts 128B size, it must be chuncked final byte[] buf = new byte[128]; final byte[] signature = new byte[128]; // read the signature if (!(bis.read(signature) > 0)) { return false; } // decrypt while ((bis.read(buf)) > 0) { cipher.init(Cipher.DECRYPT_MODE, pk); bos.write(cipher.doFinal(buf)); } // verify signature signer.update(bos.toByteArray()); boolean signed = signer.verify(signature); log.debug("signature length:" + buf.length); log.debug("singature:" + (new BigInteger(signature).toString(16).toUpperCase())); log.debug("signed:" + signed); if (!signed) { isActive = false; errorMessage = "@NotSigned@"; setLogger(); return false; } return true; } /** * Loads information about the restricted artifacts due to license (Premium and Advance features). */ @SuppressWarnings("unchecked") private void loadRestrictions() { DisabledModules.reload(); tier1Artifacts = new ArrayList<String>(); tier2Artifacts = new ArrayList<String>(); goldenExcludedArtifacts = new ArrayList<String>(); if (isActive() && licenseType == LicenseType.ON_DEMAND) { limitNamedUsers = true; } else { limitNamedUsers = OBPropertiesProvider.getInstance().getBooleanProperty("login.limit.user.session"); } if (isActive() && licenseClass == LicenseClass.STD && !golden) { // Don't read restrictions for Standard instances return; } try { // read restriction file from context directory String restrictionsFilePath = null; if (DalContextListener.getServletContext() != null) { // Taking restrictions from Tomcat context restrictionsFilePath = DalContextListener.getServletContext() .getRealPath("src-loc/design/org/openbravo/erpCommon/obps/licenseRestrictions"); } else { // Not in Tomcat context, taking restrictions from sources restrictionsFilePath = OBPropertiesProvider.getInstance().getOpenbravoProperties() .getProperty("source.path") + "/src/org/openbravo/erpCommon/obps/licenseRestrictions"; } File restrictionsFile = new File(restrictionsFilePath); log4j.debug("Restrictions file: " + restrictionsFile.getAbsolutePath()); FileInputStream fis = new FileInputStream(restrictionsFile); byte fileContent[] = new byte[(int) restrictionsFile.length()]; fis.read(fileContent); ByteArrayOutputStream bos = new ByteArrayOutputStream(); decrypt(fileContent, getPublicKey(OB_PUBLIC_KEY), bos, OB_PUBLIC_KEY); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); HashMap<String, ArrayList<String>> m1 = (HashMap<String, ArrayList<String>>) ois.readObject(); ois.close(); if (!isActive()) { // community instance, restrict both tiers tier1Artifacts.addAll(m1.get(TIER_1_PREMIUM_FEATURE)); tier2Artifacts.addAll(m1.get(TIER_2_PREMIUM_FEATURE)); } else if (licenseClass == LicenseClass.BASIC) { // basic, restrict tier 2 tier2Artifacts.addAll(m1.get(TIER_2_PREMIUM_FEATURE)); } if (isGolden()) { goldenExcludedArtifacts.addAll(m1.get(GOLDEN_EXCLUDED)); } } catch (Exception e) { log4j.error("Error reading license restriction file", e); tier1Artifacts = null; tier2Artifacts = null; goldenExcludedArtifacts = null; } } public LicenseClass getLicenseClass() { return licenseClass == null ? LicenseClass.COMMUNITY : licenseClass; } @SuppressWarnings({ "static-access", "unchecked" }) private void setLogger() { if (isActive() && !opsLog) { // add instance id to logger Enumeration<Appender> appenders = log.getRootLogger().getAllAppenders(); while (appenders.hasMoreElements()) { Appender appender = appenders.nextElement(); if (appender.getLayout() instanceof PatternLayout) { PatternLayout l = (PatternLayout) appender.getLayout(); opsLogId = getOpsLogId() + " "; String conversionPattern = l.getConversionPattern(); // do not set checksum in case it is already set if (conversionPattern == null || !conversionPattern.startsWith(opsLogId)) { l.setConversionPattern(opsLogId + conversionPattern); } } } opsLog = true; } if (!isActive() && opsLog) { // remove instance id from logger Enumeration<Appender> appenders = log.getRootLogger().getAllAppenders(); while (appenders.hasMoreElements()) { Appender appender = appenders.nextElement(); if (appender.getLayout() instanceof PatternLayout) { PatternLayout l = (PatternLayout) appender.getLayout(); String pattern = l.getConversionPattern(); if (pattern.startsWith(opsLogId)) { l.setConversionPattern(l.getConversionPattern().substring(opsLogId.length())); } } } opsLog = false; } } public String getOpsLogId() { CRC32 crc = new CRC32(); crc.update(getPublicKey().getBytes()); return Long.toHexString(crc.getValue()); } private PublicKey getPublicKey(String strPublickey) { try { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); byte[] rawPublicKey = org.apache.commons.codec.binary.Base64.decodeBase64(strPublickey.getBytes()); X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(rawPublicKey); return keyFactory.generatePublic(publicKeySpec); } catch (Exception e) { log.error(e.getMessage(), e); return null; } } public String getPublicKey() { return strPublicKey; } /** * Returns true when the instance currently is OBPS active, this is when it has a valid activation * key. */ public boolean isActive() { return isActive; } /** * Returns true when the instance currently is OBPS active. It is similar as * {@link ActivationKey#isActive}, but it is static and is initialized whenever the ActivationKey * class is instantiated. */ public static boolean isActiveInstance() { return opsLog; } /** * Returns true when the instance has a activation key and the activation file has been loaded. It * doesn't verify is still valid. */ public boolean isOPSInstance() { return instanceProperties != null; } /** * Returns true in case the instance has activation key though it might not be valid or activated */ public boolean hasActivationKey() { return hasActivationKey; } public String getErrorMessage() { return errorMessage; } public String getMessageType() { return messageType; } /** * Deprecated, use instead {@link ActivationKey#checkOPSLimitations(String)} * */ @Deprecated public LicenseRestriction checkOPSLimitations() { return checkOPSLimitations(""); } /** * Checks the current activation key * * @param currentSession * Current session, not to be taken into account * * @return {@link LicenseRestriction} with the status of the restrictions */ public LicenseRestriction checkOPSLimitations(String currentSession) { LicenseRestriction result = LicenseRestriction.NO_RESTRICTION; if (!isOPSInstance()) { return LicenseRestriction.NO_RESTRICTION; } if (inconsistentInstance) { return LicenseRestriction.NOT_MATCHED_INSTANCE; } if (trial && !isHeartbeatActive()) { return LicenseRestriction.HB_NOT_ACTIVE; } if (!isActive && golden) { return LicenseRestriction.EXPIRED_GOLDEN; } if (!isActive) { return LicenseRestriction.OPS_INSTANCE_NOT_ACTIVE; } Long softUsers = null; if (getProperty("limituserswarn") != null) { softUsers = new Long(getProperty("limituserswarn")); } // maxUsers==0 is unlimited concurrent users if (maxUsers != 0) { OBContext.setAdminMode(); int activeSessions = 0; try { activeSessions = getActiveSessions(currentSession); log4j.debug("Active sessions: " + activeSessions); if (activeSessions >= maxUsers || (softUsers != null && activeSessions >= softUsers)) { // Before raising concurrent users error, clean the session with ping timeout and try it // again if (deactivateTimeOutSessions(currentSession)) { activeSessions = getActiveSessions(currentSession); log4j.debug("Active sessions after timeout cleanup: " + activeSessions); } } } catch (Exception e) { log4j.error("Error checking sessions", e); } finally { OBContext.restorePreviousMode(); } if (activeSessions >= maxUsers) { return LicenseRestriction.NUMBER_OF_CONCURRENT_USERS_REACHED; } if (softUsers != null && activeSessions >= softUsers) { result = LicenseRestriction.NUMBER_OF_SOFT_USERS_REACHED; } } if (getExpiredInstalledModules().size() > 0) { result = LicenseRestriction.MODULE_EXPIRED; } if (licenseType == LicenseType.ON_DEMAND && outOfPlatform && !"true".equals(getProperty("limited.on.demand"))) { result = LicenseRestriction.ON_DEMAND_OFF_PLATFORM; } return result; } public LicenseRestriction checkOPSLimitations(String currentSession, String username, boolean forceNamedUserLogin) { if (forceNamedUserLogin) { // Forcing log in even there are other sessions for same user: disabling the other sessions disableOtherSessionsOfUser(currentSession, username); } LicenseRestriction result = checkOPSLimitations(currentSession); boolean checkNamedUserLimitation = StringUtils.isNotEmpty(username) && limitNamedUsers; if ((result != LicenseRestriction.NO_RESTRICTION && result != LicenseRestriction.NUMBER_OF_CONCURRENT_USERS_REACHED) || !checkNamedUserLimitation) { return result; } if (!forceNamedUserLogin) { // Checking named users concurrency if no restriction OBContext.setAdminMode(); int activeSessions = 0; try { activeSessions = getActiveSessionsForNamedUser(currentSession, username); log4j.debug("Active sessions for " + username + " user: " + activeSessions); if (activeSessions > 0) { // Before raising concurrent users error, clean the session with ping timeout and try it // again if (deactivateTimeOutSessions(currentSession)) { activeSessions = getActiveSessionsForNamedUser(currentSession, username); log4j.debug("Active sessions after timeout cleanup: " + activeSessions); } } } catch (Exception e) { log4j.error("Error checking sessions", e); } finally { OBContext.restorePreviousMode(); } if (activeSessions > 0) { result = LicenseRestriction.CONCURRENT_NAMED_USER; } } return result; } /** * Checks if heartbeat is active and a beat has been sent during last days. */ public boolean isHeartbeatActive() { OBContext.setAdminMode(); try { Boolean active = OBDal.getInstance().get(SystemInformation.class, "0").isEnableHeartbeat(); if (active == null || !active) { return false; } OBCriteria<HeartbeatLog> hbLog = OBDal.getInstance().createCriteria(HeartbeatLog.class); Calendar lastDays = Calendar.getInstance(); lastDays.add(Calendar.DAY_OF_MONTH, -9); hbLog.add(Restrictions.ge(HeartbeatLog.PROPERTY_CREATIONDATE, new Date(lastDays.getTimeInMillis()))); return hbLog.count() > 0; } finally { OBContext.restorePreviousMode(); } } /** * Looks for all active sessions that have not had activity during last * {@link ActivationKey#PING_TIMEOUT_SECS} seconds and deactivates them. Activity is tracked by * the requests the browser sends to look for alerts (see * {@link org.openbravo.erpCommon.utility.VerticalMenu}). * <p/> * PING_TIMEOUT_SECS is hardcoded to 120s, pings are hardcoded in front-end to 50s. */ private boolean deactivateTimeOutSessions(String currentSessionId) { // Last valid ping time is current time substract timeout seconds Calendar cal = Calendar.getInstance(); cal.add(Calendar.SECOND, (-1) * PING_TIMEOUT_SECS); Date lastValidPingTime = new Date(cal.getTimeInMillis()); OBCriteria<Session> obCriteria = OBDal.getInstance().createCriteria(Session.class); // sesion_active='Y' and (lastPing is null or lastPing<lastValidPing) obCriteria.add(Restrictions.and(Restrictions.eq(Session.PROPERTY_SESSIONACTIVE, true), Restrictions.or(Restrictions.isNull(Session.PROPERTY_LASTPING), Restrictions.lt(Session.PROPERTY_LASTPING, lastValidPingTime)))); obCriteria.add(Restrictions.in(Session.PROPERTY_LOGINSTATUS, ACTIVE_SESSION_TYPES)); obCriteria.add(Restrictions.ne(Session.PROPERTY_ID, currentSessionId)); boolean sessionDeactivated = false; for (Session expiredSession : obCriteria.list()) { expiredSession.setSessionActive(false); sessionDeactivated = true; log4j.info("Deactivated session: " + expiredSession.getId() + " beacuse of ping time out. Last ping: " + expiredSession.getLastPing() + ". Last valid ping time: " + lastValidPingTime); } if (sessionDeactivated) { OBDal.getInstance().flush(); } else { log4j.debug("No ping timeout sessions"); } return sessionDeactivated; } /** * Returns the number of current active sessions */ private int getActiveSessions(String currentSession) { OBCriteria<Session> obCriteria = OBDal.getInstance().createCriteria(Session.class); obCriteria.add(Restrictions.eq(Session.PROPERTY_SESSIONACTIVE, true)); obCriteria.add(Restrictions.in(Session.PROPERTY_LOGINSTATUS, ACTIVE_SESSION_TYPES)); if (currentSession != null && !currentSession.equals("")) { obCriteria.add(Restrictions.ne(Session.PROPERTY_ID, currentSession)); } return obCriteria.count(); } private int getActiveSessionsForNamedUser(String currentSession, String username) { OBCriteria<Session> obCriteria = OBDal.getInstance().createCriteria(Session.class); obCriteria.add(Restrictions.eq(Session.PROPERTY_SESSIONACTIVE, true)); obCriteria.add(Restrictions.eq(Session.PROPERTY_USERNAME, username)); obCriteria.add(Restrictions.in(Session.PROPERTY_LOGINSTATUS, ACTIVE_SESSION_TYPES)); if (currentSession != null && !currentSession.equals("")) { obCriteria.add(Restrictions.ne(Session.PROPERTY_ID, currentSession)); } return obCriteria.count(); } private void disableOtherSessionsOfUser(String currentSession, String username) { OBContext.setAdminMode(); try { OBCriteria<Session> obCriteria = OBDal.getInstance().createCriteria(Session.class); obCriteria.add(Restrictions.eq(Session.PROPERTY_SESSIONACTIVE, true)); obCriteria.add(Restrictions.eq(Session.PROPERTY_USERNAME, username)); if (currentSession != null && !currentSession.equals("")) { obCriteria.add(Restrictions.ne(Session.PROPERTY_ID, currentSession)); } for (Session session : obCriteria.list()) { session.setSessionActive(false); session.setLoginStatus("CNU"); log.info("Disabled session " + session.getId() + " for user " + username + " becaue of concurrent sessions with same user"); } OBDal.getInstance().flush(); } catch (Exception e) { log.error("Error deactivating concurrent named users sessions", e); } finally { OBContext.restorePreviousMode(); } } public String toString(ConnectionProvider conn, String lang) { String dateFormat = OBPropertiesProvider.getInstance().getOpenbravoProperties() .getProperty("dateFormat.java"); SimpleDateFormat outputFormat = new SimpleDateFormat(dateFormat); StringBuilder sb = new StringBuilder(); if (instanceProperties != null) { sb.append("<tr><td>").append(Utility.messageBD(conn, "OPSCustomer", lang)).append("</td><td>") .append(getProperty("customer")).append("</td></tr>"); sb.append("<tr><td>").append(Utility.messageBD(conn, "OPSLicenseEdition", lang)).append("</td><td>") .append(Utility.getListValueName("OBPSLicenseEdition", licenseClass.getCode(), lang)) .append("</td></tr>"); sb.append("<tr><td>").append(Utility.messageBD(conn, "OPSLicenseType", lang)).append("</td><td>") .append(Utility.getListValueName("OPSLicenseType", getProperty("lincensetype"), lang)); if (trial) { sb.append(" (" + Utility.messageBD(conn, "OPSTrialLicense", lang) + ")"); } sb.append("</td></tr>"); if (startDate != null) { sb.append("<tr><td>").append(Utility.messageBD(conn, "OPSStartDate", lang)).append("</td><td>") .append(outputFormat.format(startDate)).append("</td></tr>"); } if (endDate != null) { sb.append("<tr><td>").append(Utility.messageBD(conn, "OPSEndDate", lang)).append("</td><td>") .append((getProperty("enddate") == null ? Utility.messageBD(conn, "OPSNoEndDate", lang) : outputFormat.format(endDate))) .append("</td></tr>"); } if (licenseType != LicenseType.ON_DEMAND || outOfPlatform) { sb.append("<tr><td>").append(Utility.messageBD(conn, "OPSConcurrentUsers", lang)) .append("</td><td>") .append((maxUsers == null || maxUsers == 0L) ? Utility.messageBD(conn, "OPSUnlimitedUsers", lang) : maxUsers) .append("</td></tr>"); if (getProperty("limituserswarn") != null) { sb.append("<tr><td>").append(Utility.messageBD(conn, "OPSConcurrentUsersWarn", lang)) .append("</td><td>").append(getProperty("limituserswarn")).append("</td></tr>"); } } sb.append("<tr><td>").append(Utility.messageBD(conn, "OPSInstanceNo", lang)).append("</td><td>") .append(getProperty("instanceno")).append("\n"); sb.append("<tr><td>").append(Utility.messageBD(conn, "OPSInstancePurpose", lang)).append("</td><td>") .append(Utility.getListValueName("InstancePurpose", getProperty("purpose"), lang)) .append("</td></tr>"); sb.append("<tr><td>").append(Utility.messageBD(conn, "OPSWSLimitation", lang)).append("</td><td>"); sb.append(getWSExplanation(conn, lang)); sb.append("</td></tr>"); } else { sb.append(Utility.messageBD(conn, "OPSNonActiveInstance", lang)); } return sb.toString(); } public String getPurpose(String lang) { return Utility.getListValueName("InstancePurpose", getProperty("purpose"), lang); } public String getLicenseExplanation(ConnectionProvider conn, String lang) { if (licenseType == LicenseType.CONCURRENT_USERS) { return getProperty("limitusers") + " " + Utility.messageBD(conn, "OPSConcurrentUsers", lang); } else { return Utility.getListValueName("OPSLicenseType", getProperty("lincensetype"), lang); } } /** * Returns a message explaining WS call limitations */ public String getWSExplanation(ConnectionProvider conn, String lang) { if (!limitedWsAccess) { return Utility.messageBD(conn, "OPSWSUnlimited", lang); } else { String packs = getProperty("wsPacks"); String unitsPack = getProperty("wsUnitsPerUnit"); return Utility.messageBD(conn, "OPWSLimited", lang).replace("@packs@", packs).replace("@unitsPerPack@", unitsPack); } } public boolean hasExpirationDate() { return isOPSInstance() && (getProperty("enddate") != null); } public String getProperty(String propName) { return instanceProperties.getProperty(propName); } public Long getPendingDays() { return pendingTime; } public boolean isSubscriptionConverted() { return subscriptionConvertedProperty; } public boolean hasExpired() { return hasExpired; } public boolean isNotActiveYet() { return notActiveYet; } /** * Obtains a List of all the modules that are installed in the instance which license has expired. * * @return List of the expired modules */ public ArrayList<Module> getExpiredInstalledModules() { OBContext.setAdminMode(); try { ArrayList<Module> result = new ArrayList<Module>(); HashMap<String, CommercialModuleStatus> subscribedModules = getSubscribedModules(); Iterator<String> iterator = subscribedModules.keySet().iterator(); while (iterator.hasNext()) { String moduleId = iterator.next(); if (subscribedModules.get(moduleId) == CommercialModuleStatus.EXPIRED) { Module module = OBDal.getInstance().get(Module.class, moduleId); if (module != null && module.getStatus().equals("A")) { result.add(module); } } } return result; } finally { OBContext.restorePreviousMode(); } } /** * Obtains a list for modules ID the instance is subscribed to and their statuses * * @return HashMap<String, CommercialModuleStatus> containing the subscribed modules */ public HashMap<String, CommercialModuleStatus> getSubscribedModules() { return getSubscribedModules(true); } /** * Same as {@link ActivationKey#getSubscribedModules()} with the includeDisabled parameter. When * this parameter is true, disabled modules are returned with the DISABLED status, other way they * are returned with the status they would have if they were not disabled. */ private HashMap<String, CommercialModuleStatus> getSubscribedModules(boolean includeDisabled) { HashMap<String, CommercialModuleStatus> moduleList = new HashMap<String, CommercialModuleStatus>(); if (instanceProperties == null) { return moduleList; } SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd"); String allModules = getProperty("modules"); if (allModules == null || allModules.equals("")) return moduleList; String modulesInfo[] = allModules.split(","); Date now = new Date(); for (String moduleInfo : modulesInfo) { String moduleData[] = moduleInfo.split("\\|"); Date validFrom = null; Date validTo = null; try { validFrom = sd.parse(moduleData[1]); if (moduleData.length > 2) { validTo = sd.parse(moduleData[2]); } if (includeDisabled && !DisabledModules.isEnabled(Artifacts.MODULE, moduleData[0])) { moduleList.put(moduleData[0], CommercialModuleStatus.DISABLED); } else if (subscriptionActuallyConverted) { moduleList.put(moduleData[0], CommercialModuleStatus.CONVERTED_SUBSCRIPTION); } else if (validFrom.before(now) && (validTo == null || validTo.after(now))) { moduleList.put(moduleData[0], CommercialModuleStatus.ACTIVE); } else if (validFrom.after(now)) { moduleList.put(moduleData[0], CommercialModuleStatus.NO_ACTIVE_YET); } else if (validTo != null && validTo.before(now)) { if (subscriptionConvertedProperty) { moduleList.put(moduleData[0], CommercialModuleStatus.CONVERTED_SUBSCRIPTION); } else { moduleList.put(moduleData[0], CommercialModuleStatus.EXPIRED); } } } catch (Exception e) { log.error("Error reading module's dates module:" + moduleData[0], e); } } return moduleList; } /** * Checks whether a disabled module can be enabled again. A commercial module cannot be enabled in * case its license has expired or the instance is not commercial. * * @param module * @return true in case the module can be enabled */ public boolean isModuleEnableable(Module module) { if (!module.isCommercial()) { return true; } if (!isActive()) { return false; } HashMap<String, CommercialModuleStatus> moduleList = getSubscribedModules(false); if (!moduleList.containsKey(module.getId())) { return false; } CommercialModuleStatus status = moduleList.get(module.getId()); return status == CommercialModuleStatus.ACTIVE || status == CommercialModuleStatus.CONVERTED_SUBSCRIPTION; } /** * Returns the status for the commercial module passed as parameter. Note that module tier is not * checked here, this should be correctly handled in the license itself. * * @param moduleId * @return the status for the commercial module passed as parameter */ public CommercialModuleStatus isModuleSubscribed(String moduleId) { return isModuleSubscribed(moduleId, true); } private CommercialModuleStatus isModuleSubscribed(String moduleId, boolean refreshIfNeeded) { HashMap<String, CommercialModuleStatus> moduleList = getSubscribedModules(); if (!moduleList.containsKey(moduleId)) { log4j.debug("Module " + moduleId + " is not in the list of subscribed modules"); if (!refreshIfNeeded) { return CommercialModuleStatus.NO_SUBSCRIBED; } boolean refreshed = refreshLicense(REFRESH_MIN_TIME); if (refreshed) { return ActivationKey.instance.isModuleSubscribed(moduleId); } else { return CommercialModuleStatus.NO_SUBSCRIBED; } } return moduleList.get(moduleId); } /** * Refreshes license online in case of: * <ul> * <li>It expired * <li>Maximum number of WS calls has been reached during last 30 days * <li>Maximum number of concurrent users has been reached * </ul> */ private void refreshIfNeeded() { if (hasActivationKey && !subscriptionConvertedProperty && !trial && (hasExpired || checkNewWSCall(false) != WSRestriction.NO_RESTRICTION || checkOPSLimitations(null) == LicenseRestriction.NUMBER_OF_CONCURRENT_USERS_REACHED)) { refreshLicense(24 * 60); } else { if (licenseType == LicenseType.ON_DEMAND && outOfPlatform) { outOfPlatform = !checkInOnDemandPlatform(); if (outOfPlatform) { log.warn("Still working out of On Demand platform"); } else { log.info("Now working on On Demand platform"); maxUsers = 0L; } } OBContext.setAdminMode(); try { // Reload from DB if it was modified from outside if (lastUpdateTimestamp == null || !lastUpdateTimestamp.equals( OBDal.getInstance().get(org.openbravo.model.ad.system.System.class, "0").getUpdated())) { instance = ActivationKey.reload(); } } catch (Exception e) { log.error("Error checking if activation key should be refreshed", e); } finally { OBContext.restorePreviousMode(); } } } private synchronized boolean refreshLicense(int minutesToRefresh) { Date timeToRefresh = null; if (lastRefreshTime != null) { Calendar calendar = Calendar.getInstance(); calendar.setTime(lastRefreshTime); calendar.add(Calendar.MINUTE, minutesToRefresh); timeToRefresh = calendar.getTime(); } if (timeToRefresh == null || new Date().after(timeToRefresh)) { log4j.debug("Trying to refresh license, last refresh " + (lastRefreshTime == null ? "never" : lastRefreshTime.toString())); Map<String, Object> params = new HashMap<String, Object>(); params.put("publicKey", strPublicKey); params.put("purpose", getProperty("purpose")); params.put("instanceNo", getProperty("instanceno")); params.put("activate", true); ProcessBundle pb = new ProcessBundle(null, new VariablesSecureApp("0", "0", "0")); pb.setParams(params); boolean refreshed = false; OBContext.setAdminMode(); try { new ActiveInstanceProcess().execute(pb); OBError msg = (OBError) pb.getResult(); refreshed = msg.getType().equals("Success"); if (refreshed) { OBDal.getInstance().flush(); log4j.debug("Instance refreshed"); } else { log4j.info("Problem refreshing instance " + msg.getMessage()); } } catch (Exception e) { log4j.error("Error refreshing instance", e); refreshed = false; } finally { OBContext.restorePreviousMode(); } if (!refreshed) { // Even license couldn't be refreshed, set lastRefreshTime not to try to refresh in the // following period of time lastRefreshTime = new Date(); } return refreshed; } else { log4j.debug("Not refreshing, last refresh was " + lastRefreshTime.toString() + ". Next time to refresh " + timeToRefresh.toString()); return false; } } /** * Checks whether there is access to an artifact because of license restrictions (checking core * advance and premium features). * * @param type * Type of artifact (Window, Report, Process...) * @param id * Id of the Artifact * @return true in case it has access, false if not */ public FeatureRestriction hasLicenseAccess(String type, String id) { String actualType = type; if (actualType == null || actualType.isEmpty() || id == null || id.isEmpty()) { return FeatureRestriction.NO_RESTRICTION; } log4j.debug("Type:" + actualType + " id:" + id); if (tier1Artifacts == null || tier2Artifacts == null || goldenExcludedArtifacts == null) { log4j.error("No restrictions set, do not allow access"); throw new OBException(Utility.messageBD(new DalConnectionProvider(false), "NoRestrictionsFile", OBContext.getOBContext().getLanguage().getLanguage())); } String artifactId = id; if ("W".equals(actualType)) { // Access is granted to window, but permissions is checked for tabs OBContext.setAdminMode(); try { Tab tab = OBDal.getInstance().get(Tab.class, id); if (tab == null) { log4j.error("Could't find tab " + id + " to check access. Access not allowed"); return FeatureRestriction.UNKNOWN_RESTRICTION; } artifactId = tab.getWindow().getId(); // For windows check whether the window's module is disabled, and later whether the tab is // disabled if (!DisabledModules.isEnabled(Artifacts.MODULE, tab.getWindow().getModule().getId())) { return FeatureRestriction.DISABLED_MODULE_RESTRICTION; } } finally { OBContext.restorePreviousMode(); } } else if ("MW".equals(actualType)) { // Menu window, it receives window instead of tab actualType = "W"; } else if ("R".equals(actualType)) { actualType = "P"; } // Check disabled modules restrictions Artifacts artifactType; if ("MW".equals(actualType)) { artifactType = Artifacts.WINDOW; } else if ("W".equals(actualType)) { artifactType = Artifacts.TAB; } else if ("X".equals(actualType)) { artifactType = Artifacts.FORM; } else { artifactType = Artifacts.PROCESS; } // Use id instead of artifactId to keep tabs' ids if (!DisabledModules.isEnabled(artifactType, id)) { return FeatureRestriction.DISABLED_MODULE_RESTRICTION; } // Check core premium features restrictions if (licenseClass == LicenseClass.STD && !golden) { return FeatureRestriction.NO_RESTRICTION; } if (tier1Artifacts.contains(actualType + artifactId)) { return FeatureRestriction.TIER1_RESTRICTION; } if (tier2Artifacts.contains(actualType + artifactId)) { return FeatureRestriction.TIER2_RESTRICTION; } if (goldenExcludedArtifacts.contains(actualType + artifactId)) { return FeatureRestriction.GOLDEN_RESTRICTION; } if ("W".equals(actualType)) { // For windows, check also tab restrictions return hasLicencesTabAccess(id); } return FeatureRestriction.NO_RESTRICTION; } public FeatureRestriction hasLicencesTabAccess(String tabId) { if (tier1Artifacts.contains("T" + tabId)) { return FeatureRestriction.TIER1_RESTRICTION; } if (tier2Artifacts.contains("T" + tabId)) { return FeatureRestriction.TIER2_RESTRICTION; } return FeatureRestriction.NO_RESTRICTION; } /** * Verifies all the commercial installed modules are allowed to the instance. * * @return List of non allowed modules */ public String verifyInstalledModules() { return verifyInstalledModules(true); } String verifyInstalledModules(boolean refreshIfneeded) { String rt = ""; OBContext.setAdminMode(); try { OBCriteria<Module> mods = OBDal.getInstance().createCriteria(Module.class); mods.add(Restrictions.eq(Module.PROPERTY_COMMERCIAL, true)); mods.add(Restrictions.eq(Module.PROPERTY_ENABLED, true)); // Allow development of commercial modules which are not in the license. mods.add(Restrictions.eq(Module.PROPERTY_INDEVELOPMENT, false)); mods.addOrder(Order.asc(Module.PROPERTY_NAME)); for (Module mod : mods.list()) { if (isModuleSubscribed(mod.getId(), refreshIfneeded) == CommercialModuleStatus.NO_SUBSCRIBED) { rt += (rt.isEmpty() ? "" : ", ") + mod.getName(); } } } finally { OBContext.restorePreviousMode(); } return rt; } /** * Returns current subscription status */ public SubscriptionStatus getSubscriptionStatus() { if (!isOPSInstance() || inconsistentInstance) { return SubscriptionStatus.COMMUNITY; } else if (isSubscriptionConverted()) { return SubscriptionStatus.CANCEL; } else if (hasExpired()) { return SubscriptionStatus.EXPIRED; } else if (isNotActiveYet()) { return SubscriptionStatus.NO_ACTIVE_YET; } else { return SubscriptionStatus.ACTIVE; } } public boolean isTrial() { return trial; } public boolean isGolden() { return golden; } /** * Returns a JSONObject with a message warning about near expiration or already expired instance. * */ public JSONObject getExpirationMessage(String lang) { JSONObject result = new JSONObject(); try { if (outOfPlatform) { result.put("type", "Error"); result.put("text", Utility.messageBD(new DalConnectionProvider(false), "OBPS_ON_DEMAND_OFF_PLATFORM_LOGIN", lang, false)); return result; } // Community or professional without expiration if (pendingTime == null || subscriptionActuallyConverted) { return result; } if (!hasExpired) { String msg; Long daysToExpireMsg = getProperty("daysWarn") == null ? null : Long.parseLong(getProperty("daysWarn")); if (golden) { msg = "OBPS_TO_EXPIRE_GOLDEN"; if (daysToExpireMsg == null) { daysToExpireMsg = 999L; // show always } } else if (trial) { msg = "OBPS_TO_EXPIRE_TRIAL"; if (daysToExpireMsg == null) { daysToExpireMsg = 999L; // show always } } else if (licenseClass == LicenseClass.BASIC) { msg = "OBPS_TO_EXPIRE_BASIC"; if (daysToExpireMsg == null) { daysToExpireMsg = EXPIRATION_BASIC_DAYS; } } else { msg = "OBPS_TO_EXPIRE_PROF"; if (daysToExpireMsg == null) { daysToExpireMsg = EXPIRATION_PROF_DAYS; } } if (pendingTime <= daysToExpireMsg) { result.put("type", "Error"); result.put("text", Utility.messageBD(new DalConnectionProvider(false), msg, lang, false) .replace("@days@", pendingTime.toString())); } } else { String msg; if (golden) { msg = "OBPS_EXPIRED_GOLDEN"; result.put("disableLogin", true); } else if (trial) { msg = "OBPS_EXPIRED_TRIAL"; } else if (licenseClass == LicenseClass.BASIC) { msg = "OBPS_EXPIRED_BASIC"; } else { msg = "OBPS_EXPIRED_PROF"; } result.put("type", "Error"); result.put("text", Utility.messageBD(new DalConnectionProvider(false), msg, lang, false)); } } catch (JSONException e) { log4j.error("Error calculating expiration message", e); } return result; } /** * This method checks web service can be called. If <code>updateCounter</code> parameter is * <code>true</code> number of daily calls is increased by one. * * @param updateCounter * daily calls should be updated */ public synchronized WSRestriction checkNewWSCall(boolean updateCounter) { if (hasExpired) { return WSRestriction.EXPIRED; } if (getExpiredInstalledModules().size() > 0) { return WSRestriction.EXPIRED_MODULES; } if (!limitedWsAccess) { return WSRestriction.NO_RESTRICTION; } Date today = getDayAt0(new Date()); if (initWsCountTime == null || today.getTime() != initWsCountTime.getTime()) { initializeWsDayCounter(); } long checkCalls = maxWsCalls; if (updateCounter) { wsDayCounter += 1; // Adding 1 to maxWsCalls because session is already saved in DB checkCalls += 1; } if (wsDayCounter > checkCalls) { // clean up old days while (!exceededInLastDays.isEmpty() && exceededInLastDays.get(0).getTime() < today.getTime() - WS_MS_EXCEEDING_ALLOWED_PERIOD) { Date removed = exceededInLastDays.remove(0); log.info("Removed date from exceeded days " + removed); } if (!exceededInLastDays.contains(today)) { exceededInLastDays.add(today); // Adding a new failing day, send a new beat to butler Runnable sendBeatProcess = new Runnable() { @Override public void run() { try { String content = "beatType=CWSR"; content += "&systemIdentifier=" + URLEncoder.encode(SystemInfo.getSystemIdentifier(), "utf-8"); content += "&dbIdentifier=" + URLEncoder.encode(SystemInfo.getDBIdentifier(), "utf-8"); content += "&macId=" + URLEncoder.encode(SystemInfo.getMacAddress(), "utf-8"); content += "&obpsId=" + URLEncoder.encode(SystemInfo.getOBPSInstance(), "utf-8"); content += "&instanceNo=" + URLEncoder.encode(SystemInfo.getOBPSIntanceNumber(), "utf-8"); URL url = new URL(HEARTBEAT_URL); HttpsUtils.sendSecure(url, content); log.info("Sending CWSR beat"); } catch (Exception e) { log.error("Error connecting server", e); } } }; Thread sendBeat = new Thread(sendBeatProcess); sendBeat.start(); } if (exceededInLastDays.size() > WS_DAYS_EXCEEDING_ALLOWED) { return WSRestriction.EXCEEDED_MAX_WS_CALLS; } else { return WSRestriction.EXCEEDED_WARN_WS_CALLS; } } return WSRestriction.NO_RESTRICTION; } private Date getDayAt0(Date date) { try { return sDateFormat.parse(sDateFormat.format(date)); } catch (ParseException e) { log.error("Error getting day " + date + " at 0:00", e); return new Date(); } } private void initializeWsDayCounter() { initWsCountTime = getDayAt0(new Date()); OBContext.setAdminMode(); try { OBCriteria<Session> qLogins = OBDal.getInstance().createCriteria(Session.class); qLogins.add(Restrictions.eq(Session.PROPERTY_LOGINSTATUS, "WS")); qLogins.add(Restrictions.ge(Session.PROPERTY_CREATIONDATE, initWsCountTime)); wsDayCounter = qLogins.count(); log.info("Initialized ws count to " + wsDayCounter + " from " + initWsCountTime); } finally { OBContext.restorePreviousMode(); } } private void initializeWsCounter() { StringBuilder hql = new StringBuilder(); hql.append("select min(creationDate)\n"); hql.append(" from ADSession\n"); hql.append(" where loginStatus = 'WS'\n"); hql.append(" and creationDate > :firstDay\n"); hql.append(" group by day(creationDate), month(creationDate), year(creationDate)\n"); hql.append("having count(*) > :maxWsPerDay\n"); hql.append(" order by 1\n"); Query qExceededDays = OBDal.getInstance().getSession().createQuery(hql.toString()); qExceededDays.setParameter("firstDay", new Date(getDayAt0(new Date()).getTime() - WS_MS_EXCEEDING_ALLOWED_PERIOD)); qExceededDays.setParameter("maxWsPerDay", maxWsCalls); exceededInLastDays = new ArrayList<Date>(); for (Object d : qExceededDays.list()) { Date day = getDayAt0((Date) d); exceededInLastDays.add(day); log.info("Addind exceeded ws calls day " + day); } initializeWsDayCounter(); } /** * Returns the number of days during last 30 days exceeding the maximum allowed number of calls */ public int getWsCallsExceededDays() { if (exceededInLastDays == null) { return 0; } return exceededInLastDays.size(); } /** * Returns the number of days that can exceed the maximum number of ws calls taking into account * the ones that exceeded it during last 30 days. */ public int getExtraWsExceededDaysAllowed() { return WS_DAYS_EXCEEDING_ALLOWED - getWsCallsExceededDays(); } /** * Returns the number of days pending till the end of ws calls verification period. */ public int getNumberOfDaysLeftInPeriod() { if (exceededInLastDays == null || exceededInLastDays.size() == 0) { return (int) WS_DAYS_EXCEEDING_ALLOWED_PERIOD; } Date today = getDayAt0(new Date()); Date firstDayOfPeriod = exceededInLastDays.get(0); long lastDayOfPeriod; if (today.getTime() + (getExtraWsExceededDaysAllowed() * MILLSECS_PER_DAY) < firstDayOfPeriod.getTime() + WS_MS_EXCEEDING_ALLOWED_PERIOD) { lastDayOfPeriod = firstDayOfPeriod.getTime() + WS_MS_EXCEEDING_ALLOWED_PERIOD; } else { lastDayOfPeriod = today.getTime() + WS_MS_EXCEEDING_ALLOWED_PERIOD; } new Date(lastDayOfPeriod); long pendingMs = lastDayOfPeriod - today.getTime() - (exceededInLastDays.size() * MILLSECS_PER_DAY); return (int) (pendingMs / MILLSECS_PER_DAY); } public boolean isOffPlatform() { return outOfPlatform; } }