use of me.retrodaredevil.solarthing.packets.collection.StoredPacketGroup in project solarthing by wildmountainfarms.
the class AlterManagerAction method onUpdate.
@Override
protected void onUpdate() {
super.onUpdate();
// Supply queried solarthing_open packets to the security packet receiver, which may execute code to put commands requested to be scheduled in solarthing_alter
List<StoredPacketGroup> packets = openDatabaseCache.getAllCachedPackets();
securityPacketReceiver.receivePacketGroups(packets);
// Check packets in solarthing_alter and see if we need to send a command because of a scheduled command packet
Instant now = Instant.now();
List<VersionedPacket<StoredAlterPacket>> alterPackets = alterPacketsProvider.getPackets();
if (alterPackets == null) {
LOGGER.info("alterPackets is null. Maybe query failed? Maybe additional info in previous logs?");
} else {
for (VersionedPacket<StoredAlterPacket> versionedPacket : alterPackets) {
AlterPacket packet = versionedPacket.getPacket().getPacket();
if (packet instanceof ScheduledCommandPacket) {
ScheduledCommandPacket scheduledCommandPacket = (ScheduledCommandPacket) packet;
ScheduledCommandData data = scheduledCommandPacket.getData();
if (data.getScheduledTimeMillis() <= now.toEpochMilli()) {
if (now.toEpochMilli() - data.getScheduledTimeMillis() > Duration.ofMinutes(5).toMillis()) {
LOGGER.warn("Not going to send a command scheduled for more than 5 minutes ago! data: " + data);
} else {
doSendCommand(versionedPacket, scheduledCommandPacket);
}
}
} else if (packet instanceof FlagPacket) {
FlagPacket flagPacket = (FlagPacket) packet;
FlagData data = flagPacket.getFlagData();
ActivePeriod activePeriod = data.getActivePeriod();
if (activePeriod instanceof TimeRangeActivePeriod) {
// We only try to "manage" flags that use this type of ActivePeriod
TimeRangeActivePeriod period = (TimeRangeActivePeriod) activePeriod;
TimeRange timeRange = period.getTimeRange();
Instant endTime = timeRange.getEndTime();
if (endTime != null && endTime.compareTo(now) < 0) {
// If there is an end time, and it is in the past, then we should remove the flag
executorService.execute(() -> {
try {
database.getAlterDatabase().delete(versionedPacket);
} catch (SolarThingDatabaseException e) {
LOGGER.error("Could not delete a FlagPacket with an expired time", e);
// If we cannot delete it, no need to try again, it'll still be here next time around
}
});
}
}
}
}
}
}
use of me.retrodaredevil.solarthing.packets.collection.StoredPacketGroup in project solarthing by wildmountainfarms.
the class CouchDbMillisDatabase method query.
@Override
public List<StoredPacketGroup> query(MillisQuery query) throws SolarThingDatabaseException {
final ViewResponse response;
try {
response = database.queryView(SolarThingCouchDb.createMillisNullView(CouchDbQueryUtil.createMillisNullParams(query)));
} catch (CouchDbException e) {
throw ExceptionUtil.createFromCouchDbException(e);
}
List<ViewResponse.DocumentEntry> rows = response.getRows();
List<StoredPacketGroup> r = new ArrayList<>(rows.size());
for (ViewResponse.DocumentEntry row : rows) {
// When using includeDocs=true, we want to use the doc, not its value (which is null with millisNull)
JsonData jsonData = row.getDoc();
StoredPacketGroup storedPacketGroup = jsonDataToStoredPacketGroup(jsonData).getPacket();
r.add(storedPacketGroup);
}
return r;
}
use of me.retrodaredevil.solarthing.packets.collection.StoredPacketGroup in project solarthing by wildmountainfarms.
the class AlterManagerAction method isDocumentMadeByUs.
private boolean isDocumentMadeByUs(Instant now, ScheduledCommandData scheduledCommandData, StoredPacketGroup existingDocument) {
LargeIntegrityPacket largeIntegrityPacket = (LargeIntegrityPacket) existingDocument.getPackets().stream().filter(p -> p instanceof LargeIntegrityPacket).findAny().orElse(null);
if (largeIntegrityPacket == null) {
LOGGER.warn(SolarThingConstants.SUMMARY_MARKER, "The stored document did not have a LargeIntegrity packet. Someone must be trying to stop a scheduled command!");
return false;
}
String sender = largeIntegrityPacket.getSender();
if (!commandManager.getSender().equals(sender)) {
LOGGER.info(SolarThingConstants.SUMMARY_MARKER, "The sender of the large integrity packet we are inspecting is not us (" + commandManager.getSender() + "). It is " + sender + ". Might be a malicious actor, might be a bad setup.");
return false;
}
String encryptedHash = largeIntegrityPacket.getEncryptedHash();
String data;
try {
synchronized (CIPHER) {
data = Decrypt.decrypt(CIPHER, commandManager.getKeyPair().getPublic(), encryptedHash);
}
} catch (InvalidKeyException e) {
throw new RuntimeException("Should be a valid key!", e);
} catch (DecryptException e) {
LOGGER.warn(SolarThingConstants.SUMMARY_MARKER, "The document we are inspecting had a large integrity packet with the sender: " + sender + ", but that's us and we couldn't decrypt their payload. Likely a malicious actor", e);
return false;
}
final String[] split = data.split(",", 2);
LOGGER.debug("decrypted data: " + data);
if (split.length != 2) {
LOGGER.warn(SolarThingConstants.SUMMARY_MARKER, "split.length: " + split.length + " split: " + Arrays.asList(split));
return false;
}
String hexMillis = split[0];
// String message = split[1]; We don't care what the message is. We don't even care enough to check if it matches the payload's hash
long dateMillis;
try {
dateMillis = Long.parseLong(hexMillis, 16);
} catch (NumberFormatException e) {
LOGGER.error(SolarThingConstants.SUMMARY_MARKER, "Error parsing hex date millis", e);
return false;
}
if (dateMillis < scheduledCommandData.getScheduledTimeMillis()) {
LOGGER.warn(SolarThingConstants.SUMMARY_MARKER, "The dateMillis for this is less than the command's scheduled execution time! This must be a malicious actor!");
return false;
}
if (dateMillis > now.toEpochMilli()) {
LOGGER.warn(SolarThingConstants.SUMMARY_MARKER, "The dateMillis for this is greater than now! This should never ever happen.");
return false;
}
return true;
}
use of me.retrodaredevil.solarthing.packets.collection.StoredPacketGroup in project solarthing by wildmountainfarms.
the class CouchDbMillisDatabase method jsonDataToStoredPacketGroup.
private VersionedPacket<StoredPacketGroup> jsonDataToStoredPacketGroup(JsonData jsonData) throws SolarThingDatabaseException {
final JsonNode jsonNode;
try {
jsonNode = CouchDbJacksonUtil.getNodeFrom(jsonData);
} catch (JsonProcessingException e) {
throw new SolarThingDatabaseException("We couldn't parse some of the data into JSON. This should never happen", e);
}
if (!jsonNode.isObject()) {
throw new SolarThingDatabaseException("Something must be wrong with the packet millis view because we got this jsonNode: " + jsonNode);
}
ObjectNode objectNode = (ObjectNode) jsonNode;
final PacketGroup packetGroup;
try {
packetGroup = parser.parse(objectNode);
} catch (PacketParseException e) {
throw new SolarThingDatabaseException(e);
}
String documentId = objectNode.get("_id").asText();
String documentRevision = objectNode.get("_rev").asText();
StoredPacketGroup storedPacketGroup = PacketGroups.createStoredPacketGroup(packetGroup, new CouchDbStoredIdentifier(packetGroup.getDateMillis(), documentId, documentRevision));
return new VersionedPacket<>(storedPacketGroup, new RevisionUpdateToken(documentRevision));
}
use of me.retrodaredevil.solarthing.packets.collection.StoredPacketGroup in project solarthing by wildmountainfarms.
the class CommandUtil method getCommandRequesterHandlerList.
/**
* Gets packet handlers that will download requested commands
* @param databaseConfigs The list of database configs
* @param packetGroupReceiver Receives data that has been downloaded. Note that this may be called in a separate thread, so make sure it is thread safe
* @param options The options object
* @return A list of packet handlers that, when called, will possibly download commands and then forward those commands to {@code packetGroupReceiver}
*/
public static List<PacketHandler> getCommandRequesterHandlerList(List<DatabaseConfig> databaseConfigs, PacketGroupReceiver packetGroupReceiver, PacketHandlingOption options) {
// Handlers to request and get new commands to send (This may block the current thread). (This doesn't actually handle packets)
final List<PacketHandler> commandRequesterHandlerList = new ArrayList<>();
for (DatabaseConfig config : databaseConfigs) {
if (CouchDbDatabaseSettings.TYPE.equals(config.getType())) {
CouchDbDatabaseSettings settings = (CouchDbDatabaseSettings) config.getSettings();
CouchDbInstance instance = CouchDbUtil.createInstance(settings.getCouchProperties(), settings.getOkHttpProperties());
SolarThingDatabase database = CouchDbSolarThingDatabase.create(instance);
IndividualSettings individualSettings = config.getIndividualSettingsOrDefault(Constants.DATABASE_COMMAND_DOWNLOAD_ID, null);
FrequencySettings frequencySettings = individualSettings != null ? individualSettings.getFrequencySettings() : FrequencySettings.NORMAL_SETTINGS;
PacketHandler packetHandler = new PacketHandler() {
private final SecurityPacketReceiver securityPacketReceiver = new SecurityPacketReceiver(DatabaseDocumentKeyMap.createFromDatabase(database), packetGroupReceiver, new SecurityPacketReceiver.InstanceTargetPredicate(options.getSourceId(), options.getFragmentId()), Collections.singleton(CommandOpenPacket.class), System.currentTimeMillis(), options.getFragmentId(), options.getSourceId(), database.getEventDatabase());
@Override
public void handle(PacketCollection packetCollection) throws PacketHandleException {
final List<StoredPacketGroup> packetGroups;
try {
packetGroups = database.getOpenDatabase().query(new MillisQueryBuilder().startKey(System.currentTimeMillis() - 5 * 60 * 1000).build());
} catch (SolarThingDatabaseException e) {
throw new PacketHandleException(e);
}
securityPacketReceiver.receivePacketGroups(packetGroups);
}
};
commandRequesterHandlerList.add(new ThrottleFactorPacketHandler(new AsyncPacketHandlerWrapper(new PrintPacketHandleExceptionWrapper(packetHandler)), frequencySettings));
}
}
return commandRequesterHandlerList;
}
Aggregations