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