//
// Copyright 2010 Cinch Logic Pty Ltd.
//
// http://www.chililog.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package org.chililog.server.pubsub.jsonhttp;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang.StringUtils;
import org.chililog.server.common.AppProperties;
import org.chililog.server.common.ChiliLogException;
import org.chililog.server.common.JsonTranslator;
import org.chililog.server.common.Log4JLogger;
import org.chililog.server.data.MongoConnection;
import org.chililog.server.data.RepositoryConfigBO;
import org.chililog.server.data.RepositoryConfigController;
import org.chililog.server.data.UserBO;
import org.chililog.server.data.UserController;
import org.chililog.server.engine.RepositoryEntryMqMessage;
import org.chililog.server.pubsub.MqProducerSessionPool;
import org.chililog.server.pubsub.Strings;
import org.chililog.server.pubsub.MqProducerSessionPool.Pooled;
import org.chililog.server.workbench.workers.AuthenticationTokenAO;
import org.hornetq.api.core.Message;
import org.hornetq.api.core.SimpleString;
import org.hornetq.api.core.client.ClientMessage;
import com.mongodb.DB;
/**
* Worker to process publication requests
*/
public class PublicationWorker {
private static Log4JLogger _logger = Log4JLogger.getLogger(PublicationWorker.class);
private MqProducerSessionPool _sessionPool;
/**
* Cache for authenticated users so we don't hit the database for every publication request. Timeout is set as per
* "mq.security_invalidation_interval" in app.properties.
*/
private static final ConcurrentHashMap<String, Date> _authenticationCache = new ConcurrentHashMap<String, Date>();
/**
* Constructor
*
* @param sessionPool
* MQ Session Pool to use to get producer for writing to an address
*/
public PublicationWorker(MqProducerSessionPool sessionPool) {
_sessionPool = sessionPool;
}
/**
* Process a publishing request
*
* @param request
* Publishing request in JSON format
* @param response
* Publishing response in JSON format
* @return true if successful; false if error
*/
public boolean process(String request, StringBuilder response) {
Pooled p = null;
String messageId = null;
try {
if (StringUtils.isBlank(request)) {
throw new IllegalArgumentException("Request content is blank.");
}
// Parse JSON
PublicationRequestAO requestAO = JsonTranslator.getInstance().fromJson(request, PublicationRequestAO.class);
messageId = requestAO.getMessageID();
// Authenticate
authenticate(requestAO);
// Publish
SimpleString repoAddress = SimpleString.toSimpleString(RepositoryConfigBO.buildPubSubAddress(requestAO
.getRepositoryName()));
p = _sessionPool.getPooled();
for (LogEntryAO logEntry : requestAO.getLogEntries()) {
ClientMessage message = p.session.createMessage(Message.TEXT_TYPE, false);
message.putStringProperty(RepositoryEntryMqMessage.TIMESTAMP, logEntry.getTimestamp());
message.putStringProperty(RepositoryEntryMqMessage.SOURCE, logEntry.getSource());
message.putStringProperty(RepositoryEntryMqMessage.HOST, logEntry.getHost());
message.putStringProperty(RepositoryEntryMqMessage.SEVERITY, logEntry.getSeverity());
if (!StringUtils.isBlank(logEntry.getFields())) {
message.putStringProperty(RepositoryEntryMqMessage.FIELDS, logEntry.getFields());
}
message.getBodyBuffer().writeNullableSimpleString(SimpleString.toSimpleString(logEntry.getMessage()));
p.producer.send(repoAddress, message);
}
_sessionPool.returnPooled(p);
// Prepare response
PublicationResponseAO responseAO = new PublicationResponseAO(messageId);
JsonTranslator.getInstance().toJson(responseAO, response);
// Finish
return true;
} catch (Exception ex) {
if (p != null) {
try {
_sessionPool.addPooled();
p.session.close();
} catch (Exception ex2) {
_logger.error(ex2, "Error closing pooled connection");
}
}
_logger.error(ex, "Error processing message: %s", request);
PublicationResponseAO responseAO = new PublicationResponseAO(messageId, ex);
JsonTranslator.getInstance().toJson(responseAO, response);
return false;
}
}
/**
* Authenticate request
*
* @param publicationAO
* @throws ChiliLogException
*/
public void authenticate(PublicationRequestAO publicationAO) throws ChiliLogException {
String repoName = publicationAO.getRepositoryName();
// Check cache
String key = String.format("%s_%s_%s", repoName, publicationAO.getUsername(), publicationAO.getPassword());
Date expiry = _authenticationCache.get(key);
if (expiry != null && expiry.after(new Date())) {
// Validate
return;
}
// Check db
DB db = MongoConnection.getInstance().getConnection();
// Make user repository exists
RepositoryConfigController.getInstance().getByName(db, repoName);
// Make sure user exists and password is valid
UserBO user = UserController.getInstance().getByUsername(db, publicationAO.getUsername());
boolean passwordOK = false;
if (publicationAO.getPassword().startsWith("token:")) {
// Password is a token so we need to check the token
// Must have come from the workbench
String jsonToken = publicationAO.getPassword().substring(6);
AuthenticationTokenAO token = AuthenticationTokenAO.fromString(jsonToken);
passwordOK = token.getUserID().equals(user.getDocumentID().toString());
} else {
// Make sure user exists and password is valid
passwordOK = user.validatePassword(publicationAO.getPassword());
}
if (!passwordOK) {
throw new ChiliLogException(Strings.PUBLISHER_AUTHENTICATION_ERROR);
}
// Make sure the user can publish to the repository
String administratorRole = UserBO.createRepositoryAdministratorRoleName(repoName);
String workbenchRole = UserBO.createRepositoryWorkbenchRoleName(repoName);
String publicationRole = UserBO.createRepositoryPublisherRoleName(repoName);
if (!user.hasRole(administratorRole) && !user.hasRole(publicationRole) &&
!user.hasRole(workbenchRole) && !user.isSystemAdministrator()) {
throw new ChiliLogException(Strings.PUBLISHER_AUTHENTICATION_ERROR);
}
// Cache details
GregorianCalendar newExpiry = new GregorianCalendar();
newExpiry.add(Calendar.MILLISECOND, AppProperties.getInstance().getMqSecurityInvalidationInterval());
_authenticationCache.put(key, newExpiry.getTime());
}
}