Last active
January 25, 2018 17:46
-
-
Save amusarra/2c8f764a96b0eadd9f8ced3cae00c6db to your computer and use it in GitHub Desktop.
Liferay Portal Security Audit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package it.dontesta.labs.liferay.portal.security.audit.message.processor.dummy; | |
import com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil; | |
import com.liferay.portal.kernel.audit.AuditMessage; | |
import com.liferay.portal.kernel.log.Log; | |
import com.liferay.portal.kernel.log.LogFactoryUtil; | |
import com.liferay.portal.security.audit.AuditMessageProcessor; | |
import it.dontesta.labs.liferay.portal.security.audit.message.processor.dummy.configuration.DummyAuditMessageProcessorConfiguration; | |
import org.osgi.service.component.annotations.Activate; | |
import org.osgi.service.component.annotations.Component; | |
import org.osgi.service.component.annotations.Modified; | |
import java.util.Map; | |
/** | |
* @author Antonio Musarra | |
*/ | |
@Component( | |
configurationPid = "it.dontesta.labs.liferay.portal.security.audit.message.processor.dummy.configuration.DummyAuditMessageProcessorConfiguration", | |
immediate = true, | |
property = "eventTypes=*", | |
service = AuditMessageProcessor.class | |
) | |
public class DummyAuditMessageProcessor implements AuditMessageProcessor { | |
@Override | |
public void process(AuditMessage auditMessage) { | |
try { | |
doProcess(auditMessage); | |
} | |
catch (Exception e) { | |
_log.fatal("Unable to process audit message " + auditMessage, e); | |
} | |
} | |
@Activate | |
@Modified | |
protected void activate(Map<String, Object> properties) { | |
_dummyAuditMessageProcessorConfiguration = | |
ConfigurableUtil.createConfigurable( | |
DummyAuditMessageProcessorConfiguration.class, properties); | |
} | |
protected void doProcess(AuditMessage auditMessage) throws Exception { | |
if (_dummyAuditMessageProcessorConfiguration.enabled()) { | |
if(_log.isInfoEnabled()) { | |
_log.info("Dummy processor processing this Audit Message => " | |
+ auditMessage.toJSONObject()); | |
} | |
} | |
} | |
private static final Log _log = LogFactoryUtil.getLog( | |
DummyAuditMessageProcessor.class); | |
private volatile DummyAuditMessageProcessorConfiguration | |
_dummyAuditMessageProcessorConfiguration; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package it.dontesta.labs.liferay.portal.security.audit.event.security.authentication; | |
import com.liferay.portal.kernel.audit.AuditException; | |
import com.liferay.portal.kernel.audit.AuditMessage; | |
import com.liferay.portal.kernel.audit.AuditRouter; | |
import com.liferay.portal.kernel.json.JSONFactory; | |
import com.liferay.portal.kernel.json.JSONObject; | |
import com.liferay.portal.kernel.log.Log; | |
import com.liferay.portal.kernel.log.LogFactoryUtil; | |
import com.liferay.portal.kernel.model.User; | |
import com.liferay.portal.kernel.security.auth.AuthFailure; | |
import com.liferay.portal.kernel.service.UserLocalService; | |
import java.util.Map; | |
import it.dontesta.labs.liferay.portal.security.audit.router.constants.EventTypes; | |
import org.osgi.service.component.annotations.Component; | |
import org.osgi.service.component.annotations.Reference; | |
/** | |
* @author Antonio Musarra | |
*/ | |
@Component( | |
immediate = true, | |
property = {"key=auth.failure"}, | |
service = AuthFailure.class | |
) | |
public class LoginFailure implements AuthFailure { | |
@Override | |
public void onFailureByEmailAddress( | |
long companyId, String emailAddress, Map<String, String[]> headerMap, | |
Map<String, String[]> parameterMap) { | |
try { | |
User user = _userLocalService.getUserByEmailAddress( | |
companyId, emailAddress); | |
AuditMessage auditMessage = buildAuditMessage( | |
user, headerMap, "Failed to authenticate by email address"); | |
_auditRouter.route(auditMessage); | |
} | |
catch (AuditException ae) { | |
if (_log.isWarnEnabled()) { | |
_log.warn("Unable to route audit message", ae); | |
} | |
} | |
catch (Exception e) { | |
if (_log.isWarnEnabled()) { | |
_log.warn("Unable to route audit message", e); | |
} | |
} | |
} | |
@Override | |
public void onFailureByScreenName( | |
long companyId, String screenName, Map<String, String[]> headerMap, | |
Map<String, String[]> parameterMap) { | |
try { | |
User user = _userLocalService.getUserByScreenName( | |
companyId, screenName); | |
AuditMessage auditMessage = buildAuditMessage( | |
user, headerMap, "Failed to authenticate by screen name"); | |
_auditRouter.route(auditMessage); | |
} | |
catch (AuditException ae) { | |
if (_log.isWarnEnabled()) { | |
_log.warn("Unable to route audit message", ae); | |
} | |
} | |
catch (Exception e) { | |
if (_log.isWarnEnabled()) { | |
_log.warn("Unable to route audit message", e); | |
} | |
} | |
} | |
@Override | |
public void onFailureByUserId( | |
long companyId, long userId, Map<String, String[]> headerMap, | |
Map<String, String[]> parameterMap) { | |
try { | |
User user = _userLocalService.getUserById(companyId, userId); | |
AuditMessage auditMessage = buildAuditMessage( | |
user, headerMap, "Failed to authenticate by user ID"); | |
_auditRouter.route(auditMessage); | |
} | |
catch (AuditException ae) { | |
if (_log.isWarnEnabled()) { | |
_log.warn("Unable to route audit message", ae); | |
} | |
} | |
catch (Exception e) { | |
if (_log.isWarnEnabled()) { | |
_log.warn("Unable to route audit message", e); | |
} | |
} | |
} | |
protected AuditMessage buildAuditMessage( | |
User user, Map<String, String[]> headerMap, String reason) { | |
JSONObject additionalInfoJSONObject = _jsonFactory.createJSONObject(); | |
additionalInfoJSONObject.put( | |
"headers", _jsonFactory.serialize(headerMap)); | |
additionalInfoJSONObject.put("reason", reason); | |
AuditMessage auditMessage = new AuditMessage( | |
EventTypes.LOGIN_FAILURE, user.getCompanyId(), user.getUserId(), | |
user.getFullName(), User.class.getName(), | |
String.valueOf(user.getPrimaryKey()), null, | |
additionalInfoJSONObject); | |
return auditMessage; | |
} | |
private static final Log _log = LogFactoryUtil.getLog(LoginFailure.class); | |
@Reference | |
private AuditRouter _auditRouter; | |
@Reference | |
private JSONFactory _jsonFactory; | |
@Reference | |
private UserLocalService _userLocalService; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package it.dontesta.labs.liferay.portal.security.audit.message.processor; | |
import com.liferay.mail.kernel.model.MailMessage; | |
import com.liferay.mail.kernel.service.MailServiceUtil; | |
import com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil; | |
import com.liferay.portal.kernel.audit.AuditMessage; | |
import com.liferay.portal.kernel.log.Log; | |
import com.liferay.portal.kernel.log.LogFactoryUtil; | |
import com.liferay.portal.security.audit.AuditMessageProcessor; | |
import it.dontesta.labs.liferay.portal.security.audit.message.processor.configuration.DummyAuditMessageProcessorConfiguration; | |
import it.dontesta.labs.liferay.portal.security.audit.message.processor.configuration.LoginFailureAuditMessageProcessorConfiguration; | |
import it.dontesta.labs.liferay.portal.security.audit.router.constants.EventTypes; | |
import org.osgi.service.component.annotations.Activate; | |
import org.osgi.service.component.annotations.Component; | |
import org.osgi.service.component.annotations.Modified; | |
import javax.mail.internet.InternetAddress; | |
import java.util.Map; | |
/** | |
* @author Antonio Musarra | |
*/ | |
@Component( | |
configurationPid = "it.dontesta.labs.liferay.portal.security.audit.message.processor.configuration.LoginFailureAuditMessageProcessorConfiguration", | |
immediate = true, | |
property = "eventTypes=" + EventTypes.LOGIN_FAILURE, | |
service = AuditMessageProcessor.class | |
) | |
public class LoginFailureAuditMessageProcessor implements AuditMessageProcessor { | |
@Override | |
public void process(AuditMessage auditMessage) { | |
try { | |
doProcess(auditMessage); | |
} | |
catch (Exception e) { | |
_log.fatal("Unable to process audit message " + auditMessage, e); | |
} | |
} | |
@Activate | |
@Modified | |
protected void activate(Map<String, Object> properties) { | |
_loginFailureAuditMessageProcessorConfiguration = | |
ConfigurableUtil.createConfigurable( | |
LoginFailureAuditMessageProcessorConfiguration.class, properties); | |
} | |
protected void doProcess(AuditMessage auditMessage) throws Exception { | |
if (_loginFailureAuditMessageProcessorConfiguration.enabled()) { | |
if(_log.isDebugEnabled()) { | |
_log.debug("Login Failure processor processing this Audit Message => " | |
+ auditMessage.toJSONObject()); | |
} | |
InternetAddress fromAddress = null; | |
InternetAddress toAddress = null; | |
try { | |
fromAddress = new InternetAddress( | |
_loginFailureAuditMessageProcessorConfiguration.from()); | |
toAddress = new InternetAddress( | |
_loginFailureAuditMessageProcessorConfiguration.reportTo()); | |
MailMessage mailMessage = new MailMessage(); | |
mailMessage.setTo(toAddress); | |
mailMessage.setFrom(fromAddress); | |
mailMessage.setSubject( | |
_loginFailureAuditMessageProcessorConfiguration.emailSubject()); | |
mailMessage.setBody(auditMessage.toJSONObject().toString()); | |
MailServiceUtil.sendEmail(mailMessage); | |
if(_log.isInfoEnabled()) { | |
_log.info("Send audit email to " | |
+ _loginFailureAuditMessageProcessorConfiguration.reportTo()); | |
} | |
} catch (Exception e) { | |
if(_log.isWarnEnabled()) { | |
_log.warn("Send email failed.", e); | |
} | |
} | |
} | |
} | |
private static final Log _log = LogFactoryUtil.getLog( | |
LoginFailureAuditMessageProcessor.class); | |
private volatile LoginFailureAuditMessageProcessorConfiguration | |
_loginFailureAuditMessageProcessorConfiguration; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package it.dontesta.labs.liferay.portal.security.audit.router; | |
import com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil; | |
import com.liferay.portal.kernel.audit.AuditException; | |
import com.liferay.portal.kernel.audit.AuditMessage; | |
import com.liferay.portal.kernel.audit.AuditRouter; | |
import com.liferay.portal.kernel.log.Log; | |
import com.liferay.portal.kernel.log.LogFactoryUtil; | |
import com.liferay.portal.kernel.messaging.DestinationNames; | |
import com.liferay.portal.kernel.messaging.MessageBus; | |
import com.liferay.portal.kernel.messaging.proxy.ProxyMessageListener; | |
import com.liferay.portal.kernel.util.HashMapDictionary; | |
import com.liferay.portal.kernel.util.StringPool; | |
import com.liferay.portal.kernel.util.StringUtil; | |
import com.liferay.portal.kernel.util.Validator; | |
import com.liferay.portal.security.audit.AuditMessageProcessor; | |
import com.liferay.portal.security.audit.configuration.AuditConfiguration; | |
import it.dontesta.labs.liferay.portal.security.audit.router.constants.AuditConstants; | |
import org.osgi.framework.BundleContext; | |
import org.osgi.framework.ServiceRegistration; | |
import org.osgi.service.component.annotations.Activate; | |
import org.osgi.service.component.annotations.Component; | |
import org.osgi.service.component.annotations.ConfigurationPolicy; | |
import org.osgi.service.component.annotations.Deactivate; | |
import org.osgi.service.component.annotations.Modified; | |
import org.osgi.service.component.annotations.Reference; | |
import org.osgi.service.component.annotations.ReferenceCardinality; | |
import org.osgi.service.component.annotations.ReferencePolicy; | |
import org.osgi.service.component.annotations.ReferencePolicyOption; | |
import java.util.Dictionary; | |
import java.util.HashSet; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Set; | |
import java.util.concurrent.ConcurrentHashMap; | |
import java.util.concurrent.CopyOnWriteArrayList; | |
/** | |
* @author Antonio Musarra | |
*/ | |
@Component( | |
configurationPid = "com.liferay.portal.security.audit.configuration.AuditConfiguration", | |
configurationPolicy = ConfigurationPolicy.OPTIONAL, | |
immediate = true, | |
service = StandardAuditRouter.class | |
) | |
public class StandardAuditRouter implements AuditRouter { | |
@Override | |
public boolean isDeployed() { | |
int auditMessageProcessorsCount = _auditMessageProcessors.size(); | |
return (auditMessageProcessorsCount > 0) || | |
!_globalAuditMessageProcessors.isEmpty(); | |
} | |
@Override | |
public void route(AuditMessage auditMessage) throws AuditException { | |
if (!getAuditEnabled()) { | |
if (_log.isWarnEnabled()) { | |
_log.warn("Liferay Portal Security Audit disabled, " | |
+ "not processing this message: => " | |
+ auditMessage.toJSONObject() | |
); | |
} | |
return; | |
} | |
if (!isDeployed()) { | |
if (_log.isWarnEnabled()) { | |
_log.warn("No Audit Message Processor installed."); | |
} | |
return; | |
} | |
for (AuditMessageProcessor globalAuditMessageProcessor : | |
_globalAuditMessageProcessors) { | |
globalAuditMessageProcessor.process(auditMessage); | |
} | |
String eventType = auditMessage.getEventType(); | |
Set<AuditMessageProcessor> auditMessageProcessors = | |
_auditMessageProcessors.get(eventType); | |
if (auditMessageProcessors != null) { | |
for (AuditMessageProcessor auditMessageProcessor : | |
auditMessageProcessors) { | |
auditMessageProcessor.process(auditMessage); | |
} | |
} | |
} | |
@Activate | |
@Modified | |
protected void activate(BundleContext bundleContext, | |
Map<String, Object> properties) { | |
_auditConfiguration = | |
ConfigurableUtil.createConfigurable(AuditConfiguration.class, | |
properties); | |
if (_log.isInfoEnabled()) { | |
_log.info("Liferay Portal Security Audit enabled : " | |
+ getAuditEnabled()); | |
} | |
ProxyMessageListener proxyMessageListener = new ProxyMessageListener(); | |
proxyMessageListener.setManager(this); | |
proxyMessageListener.setMessageBus(_messageBus); | |
Dictionary<String, Object> proxyMessageListenerProperties | |
= new HashMapDictionary<>(); | |
proxyMessageListenerProperties.put("destination.name", DestinationNames.AUDIT); | |
_serviceRegistration = bundleContext.registerService( | |
ProxyMessageListener.class, | |
proxyMessageListener, | |
proxyMessageListenerProperties); | |
} | |
@Deactivate | |
protected void deactivate() { | |
if (_serviceRegistration != null) { | |
_serviceRegistration.unregister(); | |
} | |
} | |
/** | |
* Get eventType from OSGi properties | |
* | |
* @param properties OSGi properties | |
* @return String array of the eventType | |
*/ | |
private String[] getEventTypes( | |
Map<String, Object> properties) { | |
String eventTypes = (String)properties.get(AuditConstants.EVENT_TYPES); | |
if (Validator.isNull(eventTypes)) { | |
throw new IllegalArgumentException( | |
"The property \"" + AuditConstants.EVENT_TYPES + "\" is null"); | |
} | |
return StringUtil.split(eventTypes); | |
} | |
@Reference ( | |
cardinality = ReferenceCardinality.MULTIPLE, | |
policy = ReferencePolicy.DYNAMIC, | |
policyOption = ReferencePolicyOption.GREEDY, | |
unbind = "unsetAuditMessageProcessor" | |
) | |
protected void setAuditMessageProcessor( | |
AuditMessageProcessor auditMessageProcessor, | |
Map<String, Object> properties) { | |
String[] eventTypes = getEventTypes(properties); | |
if ((eventTypes.length == 1) && eventTypes[0].equals(StringPool.STAR)) { | |
_globalAuditMessageProcessors.add(auditMessageProcessor); | |
return; | |
} | |
for (String eventType : eventTypes) { | |
Set<AuditMessageProcessor> auditMessageProcessorsSet = | |
_auditMessageProcessors.get(eventType); | |
if (auditMessageProcessorsSet == null) { | |
auditMessageProcessorsSet = new HashSet<>(); | |
_auditMessageProcessors.put( | |
eventType, auditMessageProcessorsSet); | |
} | |
auditMessageProcessorsSet.add(auditMessageProcessor); | |
} | |
} | |
protected void unsetAuditMessageProcessor( | |
AuditMessageProcessor auditMessageProcessor, | |
Map<String, Object> properties) { | |
String[] eventTypes = getEventTypes(properties); | |
if ((eventTypes.length == 1) && eventTypes[0].equals(StringPool.STAR)) { | |
_globalAuditMessageProcessors.remove(auditMessageProcessor); | |
return; | |
} | |
for (String eventType : eventTypes) { | |
Set<AuditMessageProcessor> auditMessageProcessorsSet = | |
_auditMessageProcessors.get(eventType); | |
if (auditMessageProcessorsSet == null) { | |
continue; | |
} | |
auditMessageProcessorsSet.remove(auditMessageProcessor); | |
} | |
} | |
private boolean getAuditEnabled() { | |
return _auditConfiguration.enabled(); | |
} | |
private static final Log _log = LogFactoryUtil.getLog( | |
StandardAuditRouter.class); | |
private final Map<String, Set<AuditMessageProcessor>> | |
_auditMessageProcessors = new ConcurrentHashMap<>(); | |
private final List<AuditMessageProcessor> _globalAuditMessageProcessors = | |
new CopyOnWriteArrayList<>(); | |
private volatile AuditConfiguration _auditConfiguration; | |
private ServiceRegistration<ProxyMessageListener> _serviceRegistration; | |
@Reference | |
private MessageBus _messageBus; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment