package com.intrbiz.bergamot.command.handler;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.sql.Timestamp;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
import com.intrbiz.Util;
import com.intrbiz.accounting.Accounting;
import com.intrbiz.bergamot.accounting.model.SignAgentAccountingEvent;
import com.intrbiz.bergamot.command.CommandProcessor;
import com.intrbiz.bergamot.config.model.BergamotCfg;
import com.intrbiz.bergamot.config.model.HostCfg;
import com.intrbiz.bergamot.config.validator.ValidatedBergamotConfiguration;
import com.intrbiz.bergamot.crypto.util.PEMUtil;
import com.intrbiz.bergamot.crypto.util.SerialNum;
import com.intrbiz.bergamot.data.BergamotDB;
import com.intrbiz.bergamot.importer.BergamotConfigImporter;
import com.intrbiz.bergamot.importer.BergamotImportReport;
import com.intrbiz.bergamot.model.AgentRegistration;
import com.intrbiz.bergamot.model.Config;
import com.intrbiz.bergamot.model.ConfigChange;
import com.intrbiz.bergamot.model.Site;
import com.intrbiz.bergamot.model.message.agent.manager.AgentManagerRequest;
import com.intrbiz.bergamot.model.message.agent.manager.AgentManagerResponse;
import com.intrbiz.bergamot.model.message.agent.manager.request.SignAgent;
import com.intrbiz.bergamot.model.message.agent.manager.response.SignedAgent;
import com.intrbiz.bergamot.model.message.command.CommandResponse;
import com.intrbiz.bergamot.model.message.command.GeneralCommandError;
import com.intrbiz.bergamot.model.message.command.RegisterBergamotAgent;
import com.intrbiz.bergamot.model.message.command.RegisteredBergamotAgent;
import com.intrbiz.bergamot.queue.BergamotAgentManagerQueue;
import com.intrbiz.queue.RPCClient;
import com.intrbiz.queue.name.RoutingKey;
public class RegisterBergamotAgentHandler implements BergamotCommandHandler<RegisterBergamotAgent>
{
private Logger logger = Logger.getLogger(RegisterBergamotAgentHandler.class);
private BergamotAgentManagerQueue queue;
private RPCClient<AgentManagerRequest, AgentManagerResponse, RoutingKey> client;
private Accounting accounting = Accounting.create(RegisterBergamotAgentHandler.class);
public void init(CommandProcessor processor)
{
this.queue = BergamotAgentManagerQueue.open();
this.client = this.queue.createBergamotAgentManagerRPCClient();
}
@Override
public CommandResponse process(RegisterBergamotAgent request)
{
try
{
logger.info("Registering agent: " + request.getCommonName() + " (" + request.getAgentId() + ")");
// lookup the details provided
try (BergamotDB db = BergamotDB.connect())
{
// lookup the template
Site site = db.getSite(Site.getSiteId(request.getAgentId()));
if (site == null) throw new RuntimeException("No such site");
Config template = db.getConfig(request.getTemplateId());
if (template == null) throw new RuntimeException("No such template");
HostCfg hostTemplate = (HostCfg) template.getConfiguration();
// assert that the template is a Host template
if (! (template.getConfiguration() instanceof HostCfg)) throw new RuntimeException("The template must be a host template");
// call to the agent manager to sign the certificate
AgentManagerResponse response = this.client.publish(new SignAgent(site.getId(), request.getAgentId(), request.getCommonName(), request.getPublicKey())).get(10, TimeUnit.SECONDS);
if (response instanceof SignedAgent)
{
Certificate crt = PEMUtil.loadCertificate(((SignedAgent) response).getCertificatePEM());
logger.info("Successfully signed agent certificate: " + crt);
// account
this.accounting.account(new SignAgentAccountingEvent(site.getId(), request.getAgentId(), request.getCommonName(), ((X509Certificate) crt).getSerialNumber().toString(), null));
// store the certificate
db.setAgentRegistration(new AgentRegistration(site.getId(), request.getAgentId(), request.getCommonName(), SerialNum.fromBigInt(((X509Certificate) crt).getSerialNumber()).toString()));
// create a host based on the template
this.applyConfigurationChange(db, request, site, hostTemplate);
// got a certificate and registered the host
RegisteredBergamotAgent resp = new RegisteredBergamotAgent();
resp.setAgentId(request.getAgentId());
resp.setCertificate(((SignedAgent) response).getCertificatePEM());
resp.setCommonName(request.getCommonName());
return resp;
}
else
{
return new GeneralCommandError("Failed to sign agent certificate");
}
}
}
catch (Exception e)
{
return new GeneralCommandError(e.getMessage());
}
}
private void applyConfigurationChange(BergamotDB db, RegisterBergamotAgent request, Site site, HostCfg template)
{
// create the configuration change
BergamotCfg changeCfg = new BergamotCfg();
changeCfg.setSite(site.getName());
changeCfg.setSummary("Auto register host: " + request.getCommonName());
// the host we are adding
HostCfg host = new HostCfg();
host.setAgentId(request.getAgentId());
host.setName(request.getCommonName());
if (! Util.isEmpty(template.getSummary()))
{
host.setSummary(request.getCommonName() + " (" + template.getSummary() + ")");
}
else
{
host.setSummary(request.getCommonName());
}
host.getInheritedTemplates().add(template.getName());
changeCfg.addObject(host);
// log the config change
ConfigChange change = new ConfigChange(site.getId(), null, changeCfg);
db.setConfigChange(change);
// apply it
ValidatedBergamotConfiguration validatedConfiguration = changeCfg.validate(db.getObjectLocator(site.getId()));
if (validatedConfiguration.getReport().isValid())
{
// good we have a valid configuration, import it
BergamotConfigImporter importer = new BergamotConfigImporter(validatedConfiguration).online(true);
BergamotImportReport importReport = importer.importConfiguration();
if (importReport.isSuccessful())
{
// whoop, we successfully registered the host
change.setApplied(true);
change.setAppliedAt(new Timestamp(System.currentTimeMillis()));
db.setConfigChange(change);
}
else
{
logger.error("Failed to auto register host, the configuration import failed");
// keep the config change, it can be manually applied later
// TODO: raise a notifcation in this instance
}
}
else
{
logger.error("Failed to auto register host, the resultant configuration is not valid");
// keep the config change, it can be manually applied later
// TODO: raise a notifcation in this instance
}
}
@SuppressWarnings("unchecked")
@Override
public Class<? extends RegisterBergamotAgent>[] handles()
{
return new Class[] { RegisterBergamotAgent.class };
}
}