001package com.identityworksllc.iiq.common; 002 003import org.apache.commons.logging.Log; 004import org.apache.commons.logging.LogFactory; 005import sailpoint.Version; 006import sailpoint.api.SailPointContext; 007import sailpoint.server.Environment; 008import sailpoint.tools.GeneralException; 009import sailpoint.tools.Util; 010 011import java.lang.reflect.InvocationTargetException; 012import java.lang.reflect.Method; 013import java.util.Optional; 014import java.util.concurrent.atomic.AtomicReference; 015import java.util.concurrent.locks.Lock; 016import java.util.concurrent.locks.ReentrantLock; 017 018/** 019 * A singleton utility for retrieving AccessHistory data, even in versions 020 * of IIQ less than 8.4. This is useful for creating multi-version plugins. 021 */ 022@SuppressWarnings("JavaReflectionMemberAccess") 023public class AccessHistory { 024 025 /** 026 * The Versioned singleton instance 027 */ 028 private static AccessHistory INSTANCE; 029 030 /** 031 * A lock used to prevent more than one class from constructing an instance of 032 * this class at the same time. 033 */ 034 private static final Lock lock = new ReentrantLock(); 035 036 /** 037 * The logger 038 */ 039 private static final Log log = LogFactory.getLog(AccessHistory.class); 040 041 /** 042 * Gets an instance of the utility class with version-specific implementation 043 * classes already configured. 044 * 045 * @return An instance of {@link AccessHistory} 046 * @throws GeneralException on failures 047 */ 048 public static AccessHistory get() throws GeneralException { 049 if (INSTANCE == null) { 050 try { 051 lock.lockInterruptibly(); 052 try { 053 if (INSTANCE == null) { 054 INSTANCE = new AccessHistory(); 055 } 056 } finally { 057 lock.unlock(); 058 } 059 } catch(Exception e) { 060 throw new GeneralException(e); 061 } 062 } 063 return INSTANCE; 064 } 065 066 067 /** 068 * The cached flag indicating that access history is available 069 */ 070 private final AtomicReference<Boolean> accessHistoryEnabled; 071 /** 072 * The cached Method to get the context 073 */ 074 private final AtomicReference<Method> cachedAccessHistoryContextGetMethod; 075 /** 076 * The cached Method to get the environment 077 */ 078 private final AtomicReference<Method> cachedAccessHistoryEnvGetMethod; 079 080 private AccessHistory() { 081 accessHistoryEnabled = new AtomicReference<>(); 082 cachedAccessHistoryEnvGetMethod = new AtomicReference<>(); 083 cachedAccessHistoryContextGetMethod = new AtomicReference<>(); 084 } 085 086 087 /** 088 * Returns the SailPointContext associated with Access History 089 * @return The access history context, or an empty optional if not available 090 */ 091 public Optional<SailPointContext> getAccessHistoryContext() throws GeneralException { 092 String version = Version.getVersion(); 093 boolean is84 = Util.isNotNullOrEmpty(version) && (version.compareTo("8.4") >= 0); 094 if (is84 && this.isAccessHistoryEnabled()) { 095 try { 096 if (cachedAccessHistoryContextGetMethod.get() == null) { 097 Class<?> ahu = Class.forName("sailpoint.accesshistory.AccessHistoryUtil"); 098 Method method = ahu.getMethod("getAccessHistoryContext"); 099 cachedAccessHistoryContextGetMethod.set(method); 100 } 101 SailPointContext ahContext = (SailPointContext) cachedAccessHistoryContextGetMethod.get().invoke(null); 102 103 return Optional.of(ahContext); 104 } catch (NoSuchMethodException | SecurityException | IllegalAccessException e) { 105 log.debug("Caught an exception constructing the 8.4 access history feature", e); 106 } catch(ClassNotFoundException e) { 107 throw new GeneralException("This environment appears to be 8.4, but AccessHistoryUtil was not found", e); 108 } catch (InvocationTargetException e) { 109 throw new GeneralException("Exception thrown while constructing the AH Environment", e); 110 } 111 } else { 112 log.debug("Access history is not available and/or enabled (version = " + version + ")"); 113 } 114 115 return Optional.empty(); 116 } 117 118 /** 119 * Returns the Environment associated with Access History 120 * @return The access history environment, or an empty optional if not available 121 */ 122 public Optional<Environment> getAccessHistoryEnvironment() throws GeneralException { 123 String version = Version.getVersion(); 124 boolean is84 = Util.isNotNullOrEmpty(version) && (version.compareTo("8.4") >= 0); 125 if (is84 && this.isAccessHistoryEnabled()) { 126 try { 127 if (cachedAccessHistoryEnvGetMethod.get() == null) { 128 Method method = Environment.class.getMethod("getEnvironmentAccessHistory"); 129 cachedAccessHistoryEnvGetMethod.set(method); 130 } 131 Environment ahEnvironment = (Environment) cachedAccessHistoryEnvGetMethod.get().invoke(null); 132 133 return Optional.of(ahEnvironment); 134 } catch (NoSuchMethodException | SecurityException | IllegalAccessException e) { 135 log.debug("Caught an exception constructing the 8.4 access history feature", e); 136 } catch (InvocationTargetException e) { 137 throw new GeneralException("Exception thrown while constructing the AH Environment", e); 138 } 139 } else { 140 log.debug("Access history is not available and/or enabled (version = " + version + ")"); 141 } 142 143 return Optional.empty(); 144 } 145 146 /** 147 * Returns true if access history is enabled. The result will be cached 148 * so that reflection is not used for every call of this method. 149 * 150 * @return True, if this is 8.4 and access history is enabled 151 */ 152 public boolean isAccessHistoryEnabled() { 153 if (accessHistoryEnabled.get() != null) { 154 return accessHistoryEnabled.get(); 155 } 156 String version = Version.getVersion(); 157 boolean is84 = Util.isNotNullOrEmpty(version) && (version.compareTo("8.4") >= 0); 158 if (is84) { 159 try { 160 boolean enabled = (boolean) Version.class.getMethod("isAccessHistoryEnabled").invoke(null); 161 accessHistoryEnabled.set(enabled); 162 return enabled; 163 } catch(Exception e) { 164 log.debug("Caught an exception checking whether access history is enabled", e); 165 } 166 } 167 168 accessHistoryEnabled.set(false); 169 return false; 170 } 171 172 173}