Files
wlan-cloud-ucentralgw/src/AP_WS_Process_connect.cpp
2024-06-04 12:55:54 -07:00

307 lines
10 KiB
C++

//
// Created by stephane bourque on 2022-07-26.
//
#include "AP_WS_Connection.h"
#include "AP_WS_Server.h"
#include "CentralConfig.h"
#include "Daemon.h"
#include "FindCountry.h"
#include "StorageService.h"
#include "CommandManager.h"
#include "framework/KafkaManager.h"
#include "framework/utils.h"
#include "firmware_revision_cache.h"
#include "UI_GW_WebSocketNotifications.h"
#include <GWKafkaEvents.h>
namespace OpenWifi {
[[maybe_unused]] static void SendKafkaFirmwareUpdate(const std::string &SerialNumber,
const std::string &OldFirmware,
const std::string &NewFirmware) {
if (KafkaManager()->Enabled()) {
Poco::JSON::Object EventDetails;
EventDetails.set("oldFirmware", OldFirmware);
EventDetails.set("newFirmware", NewFirmware);
Poco::JSON::Object Event;
Event.set("type", "device.firmware_change");
Event.set("timestamp", Utils::Now());
Event.set("payload", EventDetails);
KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber, Event);
}
}
[[maybe_unused]] static void SendKafkaDeviceNotProvisioned( const std::string &SerialNumber,
const std::string &Firmware,
const std::string &DeviceType,
const std::string &IP) {
if (KafkaManager()->Enabled()) {
Poco::JSON::Object EventDetails;
EventDetails.set("firmware", Firmware);
EventDetails.set("deviceType", DeviceType);
EventDetails.set("IP", IP);
Poco::JSON::Object Event;
Event.set("type", "device.not_provisioned");
Event.set("timestamp", Utils::Now());
Event.set("payload", EventDetails);
KafkaManager()->PostMessage(KafkaTopics::DEVICE_EVENT_QUEUE, SerialNumber, Event);
}
}
void AP_WS_Connection::Process_connect(Poco::JSON::Object::Ptr ParamsObj,
const std::string &Serial) {
if (ParamsObj->has(uCentralProtocol::UUID) && ParamsObj->has(uCentralProtocol::FIRMWARE) &&
ParamsObj->has(uCentralProtocol::CAPABILITIES)) {
uint64_t UUID = ParamsObj->get(uCentralProtocol::UUID);
auto Firmware = ParamsObj->get(uCentralProtocol::FIRMWARE).toString();
auto Capabilities = ParamsObj->getObject(uCentralProtocol::CAPABILITIES);
std::string DevicePassword;
if(ParamsObj->has("password")) {
DevicePassword = ParamsObj->get("password").toString();
}
SerialNumber_ = Serial;
SerialNumberInt_ = Utils::SerialNumberToInt(SerialNumber_);
CommandManager()->ClearQueue(SerialNumberInt_);
AP_WS_Server()->StartSession(State_.sessionId, SerialNumberInt_);
Config::Capabilities Caps(Capabilities);
Compatible_ = Caps.Compatible();
State_.UUID = UUID;
State_.Firmware = Firmware;
State_.PendingUUID = 0;
State_.Address = Utils::FormatIPv6(WS_->peerAddress().toString());
CId_ = SerialNumber_ + "@" + CId_;
auto Platform = Poco::toLower(Caps.Platform());
if(ParamsObj->has("reason")) {
State_.connectReason = ParamsObj->get("reason").toString();
}
auto IP = PeerAddress_.toString();
if (IP.substr(0, 7) == "::ffff:") {
IP = IP.substr(7);
}
bool RestrictedDevice = false;
if (Capabilities->has("restrictions")) {
RestrictedDevice = true;
Poco::JSON::Object::Ptr RestrictionObject = Capabilities->getObject("restrictions");
Restrictions_.from_json(RestrictionObject);
}
if (Capabilities->has("developer") && !Capabilities->isNull("developer")) {
Restrictions_.developer = Capabilities->getValue<bool>("developer");
}
if(Capabilities->has("secure-rtty")) {
RTTYMustBeSecure_ = Capabilities->getValue<bool>("secure-rtty");
}
State_.locale = FindCountryFromIP()->Get(IP);
GWObjects::Device DeviceInfo;
std::lock_guard DbSessionLock(DbSession_->Mutex());
auto DeviceExists = StorageService()->GetDevice(DbSession_->Session(), SerialNumber_, DeviceInfo);
if (Daemon()->AutoProvisioning() && !DeviceExists) {
// check the firmware version. if this is too old, we cannot let that device connect yet, we must
// force a firmware upgrade
GWObjects::DefaultFirmware MinimumFirmware;
if(FirmwareRevisionCache()->DeviceMustUpgrade(Compatible_, Firmware, MinimumFirmware)) {
Poco::JSON::Object UpgradeCommand, Params;
UpgradeCommand.set(uCentralProtocol::JSONRPC,uCentralProtocol::JSONRPC_VERSION);
UpgradeCommand.set(uCentralProtocol::METHOD,uCentralProtocol::UPGRADE);
Params.set(uCentralProtocol::SERIALNUMBER, SerialNumber_);
Params.set(uCentralProtocol::WHEN, 0);
Params.set(uCentralProtocol::URI, MinimumFirmware.uri);
Params.set(uCentralProtocol::KEEP_REDIRECTOR,1);
UpgradeCommand.set(uCentralProtocol::PARAMS, Params);
UpgradeCommand.set(uCentralProtocol::ID, 1);
std::ostringstream Command;
UpgradeCommand.stringify(Command);
if(Send(Command.str())) {
poco_information(
Logger(),
fmt::format(
"Forcing device {} to upgrade to {} before connection is allowed.",
SerialNumber_, MinimumFirmware.revision));
} else {
poco_error(
Logger(),
fmt::format(
"Could not force device {} to upgrade to {} before connection is allowed.",
SerialNumber_, MinimumFirmware.revision));
}
return;
} else {
StorageService()->CreateDefaultDevice( DbSession_->Session(),
SerialNumber_, Caps, Firmware, PeerAddress_,
State_.VerifiedCertificate == GWObjects::SIMULATED);
}
} else if (!Daemon()->AutoProvisioning() && !DeviceExists) {
SendKafkaDeviceNotProvisioned(SerialNumber_, Firmware, Compatible_, CId_);
poco_warning(Logger(),fmt::format("Device {} is a {} from {} and cannot be provisioned.",SerialNumber_,Compatible_, CId_));
return EndConnection();
} else if (DeviceExists) {
StorageService()->UpdateDeviceCapabilities(DbSession_->Session(), SerialNumber_, Caps);
int Updated{0};
if (!Firmware.empty()) {
if (Firmware != DeviceInfo.Firmware) {
DeviceFirmwareChangeKafkaEvent KEvent(SerialNumberInt_, Utils::Now(),
DeviceInfo.Firmware, Firmware);
DeviceInfo.Firmware = Firmware;
DeviceInfo.LastFWUpdate = Utils::Now();
++Updated;
GWWebSocketNotifications::SingleDeviceFirmwareChange_t Notification;
Notification.content.serialNumber = SerialNumber_;
Notification.content.newFirmware = Firmware;
GWWebSocketNotifications::DeviceFirmwareUpdated(Notification);
} else if (DeviceInfo.LastFWUpdate == 0) {
DeviceInfo.LastFWUpdate = Utils::Now();
++Updated;
}
}
if(ParamsObj->has("reason")) {
State_.connectReason = ParamsObj->get("reason").toString();
DeviceInfo.connectReason = State_.connectReason;
++Updated;
}
if(DeviceInfo.DevicePassword!=DevicePassword) {
DeviceInfo.DevicePassword = DevicePassword.empty() ? "openwifi" : DevicePassword ;
++Updated;
}
if (DeviceInfo.lastRecordedContact==0) {
DeviceInfo.lastRecordedContact = Utils::Now();
++Updated;
}
if (DeviceInfo.simulated && (State_.VerifiedCertificate!=GWObjects::SIMULATED)) {
DeviceInfo.simulated = false;
++Updated;
}
if (!DeviceInfo.simulated && (State_.VerifiedCertificate==GWObjects::SIMULATED)) {
DeviceInfo.simulated = true;
++Updated;
}
if (DeviceInfo.locale != State_.locale) {
DeviceInfo.locale = State_.locale;
++Updated;
}
if (Compatible_ != DeviceInfo.Compatible) {
DeviceInfo.Compatible = Compatible_;
++Updated;
}
if (Platform != DeviceInfo.DeviceType) {
DeviceInfo.DeviceType = Platform;
++Updated;
}
if (RestrictedDevice != DeviceInfo.restrictedDevice) {
DeviceInfo.restrictedDevice = RestrictedDevice;
++Updated;
}
if (Restrictions_ != DeviceInfo.restrictionDetails) {
DeviceInfo.restrictionDetails = Restrictions_;
++Updated;
}
if(DeviceInfo.certificateExpiryDate!=State_.certificateExpiryDate) {
DeviceInfo.certificateExpiryDate = State_.certificateExpiryDate;
++Updated;
}
if (Updated) {
StorageService()->UpdateDevice(DbSession_->Session(), DeviceInfo);
}
}
if(!Simulated_) {
uint64_t UpgradedUUID = 0;
if (LookForUpgrade(DbSession_->Session(), UUID, UpgradedUUID)) {
State_.UUID = UpgradedUUID;
}
}
State_.Compatible = Compatible_;
State_.Connected = true;
ConnectionCompletionTime_ =
std::chrono::high_resolution_clock::now() - ConnectionStart_;
State_.connectionCompletionTime = ConnectionCompletionTime_.count();
if (State_.VerifiedCertificate == GWObjects::VALID_CERTIFICATE) {
if ((Utils::SerialNumberMatch(CN_, SerialNumber_,
(int)AP_WS_Server()->MismatchDepth())) ||
AP_WS_Server()->IsSimSerialNumber(CN_)) {
State_.VerifiedCertificate = GWObjects::VERIFIED;
poco_information(Logger_,
fmt::format("CONNECT({}): Fully validated and authenticated "
"device. Session={} ConnectionCompletion Time={}",
CId_, State_.sessionId,
State_.connectionCompletionTime));
} else {
State_.VerifiedCertificate = GWObjects::MISMATCH_SERIAL;
if (AP_WS_Server()->AllowSerialNumberMismatch()) {
poco_information(
Logger_,
fmt::format("CONNECT({}): Serial number mismatch allowed. CN={} "
"Serial={} Session={} ConnectionCompletion Time={}",
CId_, CN_, SerialNumber_, State_.sessionId,
State_.connectionCompletionTime));
} else {
poco_information(
Logger_, fmt::format("CONNECT({}): Serial number mismatch disallowed. "
"Device rejected. CN={} Serial={} Session={}",
CId_, CN_, SerialNumber_, State_.sessionId));
return EndConnection();
}
}
} else {
poco_information(Logger_,
fmt::format("CONNECT({}): Simulator device. "
"Session={} ConnectionCompletion Time={}",
CId_, State_.sessionId,
State_.connectionCompletionTime));
}
GWWebSocketNotifications::SingleDevice_t Notification;
Notification.content.serialNumber = SerialNumber_;
GWWebSocketNotifications::DeviceConnected(Notification);
if (KafkaManager()->Enabled()) {
ParamsObj->set(uCentralProtocol::CONNECTIONIP, CId_);
ParamsObj->set("locale", State_.locale);
ParamsObj->set(uCentralProtocol::TIMESTAMP, Utils::Now());
ParamsObj->set(uCentralProtocol::UUID, uuid_);
KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber_, *ParamsObj);
}
} else {
poco_warning(
Logger_,
fmt::format("INVALID-PROTOCOL({}): Missing one of uuid, firmware, or capabilities",
CId_));
Errors_++;
}
}
} // namespace OpenWifi