- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
236 lines
11 KiB
C++
236 lines
11 KiB
C++
// SPDX-License-Identifier: Apache-2.0
|
|
// Copyright Pionix GmbH and Contributors to EVerest
|
|
#include <everest/helpers/helpers.hpp>
|
|
|
|
#include <utility>
|
|
|
|
#include "Auth.hpp"
|
|
#include <everest/logging.hpp>
|
|
|
|
namespace module {
|
|
|
|
void Auth::init() {
|
|
invoke_init(*p_main);
|
|
invoke_init(*p_reservation);
|
|
|
|
this->auth_handler = std::make_unique<AuthHandler>(
|
|
string_to_selection_algorithm(this->config.selection_algorithm), this->config.connection_timeout,
|
|
this->config.plug_in_timeout_enabled, this->config.prioritize_authorization_over_stopping_transaction,
|
|
this->config.ignore_connector_faults, this->info.id,
|
|
(!this->r_kvs.empty() ? this->r_kvs.at(0).get() : nullptr));
|
|
|
|
for (const auto& token_provider : this->r_token_provider) {
|
|
token_provider->subscribe_provided_token([this](ProvidedIdToken provided_token) {
|
|
{
|
|
auto state = this->event_state.handle();
|
|
if (!state->started) {
|
|
EVLOG_warning << "Auth not fully initialized. Discarding provided token";
|
|
return;
|
|
}
|
|
}
|
|
std::thread t([this, provided_token]() { this->auth_handler->on_token(provided_token); });
|
|
t.detach();
|
|
});
|
|
}
|
|
for (const auto& token_validator : this->r_token_validator) {
|
|
token_validator->subscribe_validate_result_update([this](ValidationResultUpdate validation_result_update) {
|
|
{
|
|
auto state = this->event_state.handle();
|
|
if (!state->started) {
|
|
EVLOG_warning << "Auth not fully initialized. Discarding validation result update";
|
|
return;
|
|
}
|
|
}
|
|
this->auth_handler->handle_token_validation_result_update(validation_result_update);
|
|
});
|
|
}
|
|
|
|
// Subscribe to session events and errors in init() so we don't miss any events
|
|
// Events received before ready() are queued.
|
|
int32_t evse_index = 0;
|
|
for (const auto& evse_manager : this->r_evse_manager) {
|
|
const int32_t evse_idx = evse_index;
|
|
|
|
evse_manager->subscribe_session_event([this, evse_idx](SessionEvent session_event) {
|
|
{
|
|
auto state = this->event_state.handle();
|
|
if (!state->started) {
|
|
EVLOG_debug << "Auth not fully initialized, but received a session event on evse_index: "
|
|
<< evse_idx << " that will be queued up: " << session_event.event;
|
|
state->event_queue.emplace(evse_idx, session_event);
|
|
return;
|
|
}
|
|
}
|
|
this->auth_handler->handle_session_event(this->auth_handler->get_evse_id_by_index(evse_idx), session_event);
|
|
});
|
|
|
|
evse_manager->subscribe_error(
|
|
"evse_manager/Inoperative",
|
|
[this, evse_idx](const Everest::error::Error& error) {
|
|
{
|
|
auto state = this->event_state.handle();
|
|
if (!state->started) {
|
|
EVLOG_debug << "Auth not fully initialized, queuing permanent fault raised for evse_index: "
|
|
<< evse_idx;
|
|
state->event_queue.emplace(evse_idx, PermanentFaultRaised{1});
|
|
return;
|
|
}
|
|
}
|
|
this->auth_handler->handle_permanent_fault_raised(this->auth_handler->get_evse_id_by_index(evse_idx),
|
|
1);
|
|
},
|
|
[this, evse_idx](const Everest::error::Error& error) {
|
|
{
|
|
auto state = this->event_state.handle();
|
|
if (!state->started) {
|
|
EVLOG_debug << "Auth not fully initialized, queuing permanent fault cleared for evse_index: "
|
|
<< evse_idx;
|
|
state->event_queue.emplace(evse_idx, PermanentFaultCleared{1});
|
|
return;
|
|
}
|
|
}
|
|
this->auth_handler->handle_permanent_fault_cleared(this->auth_handler->get_evse_id_by_index(evse_idx),
|
|
1);
|
|
});
|
|
|
|
evse_index++;
|
|
}
|
|
}
|
|
|
|
void Auth::ready() {
|
|
invoke_ready(*p_main);
|
|
invoke_ready(*p_reservation);
|
|
|
|
int32_t evse_index = 0;
|
|
for (const auto& evse_manager : this->r_evse_manager) {
|
|
const int32_t evse_id = evse_manager->call_get_evse().id;
|
|
std::vector<Connector> connectors;
|
|
for (const auto& connector : evse_manager->call_get_evse().connectors) {
|
|
connectors.push_back(
|
|
Connector(connector.id, connector.type.value_or(types::evse_manager::ConnectorTypeEnum::Unknown)));
|
|
}
|
|
|
|
this->auth_handler->init_evse(evse_id, evse_index, connectors);
|
|
|
|
evse_index++;
|
|
}
|
|
|
|
this->auth_handler->register_publish_token_validation_status_callback(
|
|
[this](const ProvidedIdToken& token, TokenValidationStatus status,
|
|
const std::vector<MessageContent>& tariff_messages) {
|
|
this->p_main->publish_token_validation_status({token, status, tariff_messages});
|
|
});
|
|
|
|
this->auth_handler->register_notify_evse_callback(
|
|
[this](const int evse_index, const ProvidedIdToken& provided_token, const ValidationResult& validation_result) {
|
|
this->r_evse_manager.at(evse_index)->call_authorize_response(provided_token, validation_result);
|
|
});
|
|
this->auth_handler->register_withdraw_authorization_callback(
|
|
[this](const int32_t evse_index) { this->r_evse_manager.at(evse_index)->call_withdraw_authorization(); });
|
|
this->auth_handler->register_validate_token_callback([this](const ProvidedIdToken& provided_token) {
|
|
std::vector<ValidationResult> validation_results;
|
|
for (const auto& token_validator : this->r_token_validator) {
|
|
try {
|
|
const auto result = token_validator->call_validate_token(provided_token);
|
|
validation_results.push_back(result);
|
|
// TODO: This is very broad catch, make it more narrow when the everest-framework error handling will be
|
|
// established
|
|
} catch (const std::exception& e) {
|
|
EVLOG_warning << "Exception during validating token: " << e.what();
|
|
ValidationResult validation_result;
|
|
validation_result.authorization_status = AuthorizationStatus::Unknown;
|
|
validation_results.push_back(validation_result);
|
|
}
|
|
}
|
|
return validation_results;
|
|
});
|
|
this->auth_handler->register_stop_transaction_callback(
|
|
[this](const int32_t evse_index, const StopTransactionRequest& request) {
|
|
this->r_evse_manager.at(evse_index)->call_stop_transaction(request);
|
|
});
|
|
this->auth_handler->register_reserved_callback(
|
|
[this](const std::optional<int32_t> evse_id, const int32_t& reservation_id) {
|
|
// Only call the evse manager to store the reservation if it is done for a specific evse.
|
|
if (evse_id.has_value()) {
|
|
EVLOG_info << "Call reserved callback for evse id " << evse_id.value();
|
|
|
|
if (!this->r_evse_manager.at(evse_id.value() - 1)->call_reserve(reservation_id)) {
|
|
EVLOG_warning << "EVSE manager does not allow placing a reservation for evse id " << evse_id.value()
|
|
<< ": cancelling reservation.";
|
|
this->auth_handler->handle_cancel_reservation(reservation_id);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ReservationUpdateStatus status;
|
|
status.reservation_id = reservation_id;
|
|
status.reservation_status = Reservation_status::Placed;
|
|
this->p_reservation->publish_reservation_update(status);
|
|
return true;
|
|
});
|
|
this->auth_handler->register_reservation_cancelled_callback(
|
|
[this](const std::optional<int32_t> evse_id, const int32_t reservation_id, const ReservationEndReason reason,
|
|
const bool send_reservation_update) {
|
|
// Only call the evse manager to cancel the reservation if it was for a specific evse
|
|
if (evse_id.has_value() && evse_id.value() > 0) {
|
|
EVLOG_debug << "Call evse manager to cancel the reservation with evse id " << evse_id.value();
|
|
this->r_evse_manager.at(evse_id.value() - 1)->call_cancel_reservation();
|
|
}
|
|
|
|
if (send_reservation_update) {
|
|
ReservationUpdateStatus status;
|
|
status.reservation_id = reservation_id;
|
|
if (reason == ReservationEndReason::Expired) {
|
|
status.reservation_status = Reservation_status::Expired;
|
|
} else if (reason == ReservationEndReason::Cancelled) {
|
|
status.reservation_status = Reservation_status::Removed;
|
|
} else {
|
|
// On reservation used: do not publish a reservation update!!
|
|
return;
|
|
}
|
|
this->p_reservation->publish_reservation_update(status);
|
|
}
|
|
});
|
|
|
|
// Process any events that were queued during init before we were ready
|
|
{
|
|
auto state = this->event_state.handle();
|
|
while (!state->event_queue.empty()) {
|
|
auto queued_event = state->event_queue.front();
|
|
state->event_queue.pop();
|
|
const int32_t evse_id = this->auth_handler->get_evse_id_by_index(queued_event.evse_index);
|
|
if (std::holds_alternative<SessionEvent>(queued_event.data)) {
|
|
const auto& session_event = std::get<SessionEvent>(queued_event.data);
|
|
EVLOG_debug << "Processing queued session event for evse_id: " << evse_id
|
|
<< ", event: " << session_event.event;
|
|
this->auth_handler->handle_session_event(evse_id, session_event);
|
|
} else if (std::holds_alternative<PermanentFaultRaised>(queued_event.data)) {
|
|
const auto& fault = std::get<PermanentFaultRaised>(queued_event.data);
|
|
EVLOG_debug << "Processing queued permanent fault raised for evse_id: " << evse_id;
|
|
this->auth_handler->handle_permanent_fault_raised(evse_id, fault.connector_id);
|
|
} else if (std::holds_alternative<PermanentFaultCleared>(queued_event.data)) {
|
|
const auto& fault = std::get<PermanentFaultCleared>(queued_event.data);
|
|
EVLOG_debug << "Processing queued permanent fault cleared for evse_id: " << evse_id;
|
|
this->auth_handler->handle_permanent_fault_cleared(evse_id, fault.connector_id);
|
|
}
|
|
}
|
|
state->started = true;
|
|
}
|
|
|
|
this->auth_handler->initialize();
|
|
}
|
|
|
|
void Auth::set_connection_timeout(int& connection_timeout) {
|
|
this->auth_handler->set_connection_timeout(connection_timeout);
|
|
}
|
|
|
|
void Auth::set_master_pass_group_id(const std::string& master_pass_group_id) {
|
|
this->auth_handler->set_master_pass_group_id(master_pass_group_id);
|
|
}
|
|
|
|
WithdrawAuthorizationResult Auth::handle_withdraw_authorization(const WithdrawAuthorizationRequest& request) {
|
|
return this->auth_handler->handle_withdraw_authorization(request);
|
|
}
|
|
|
|
} // namespace module
|