// SPDX-License-Identifier: Apache-2.0 // Copyright Pionix GmbH and Contributors to EVerest #include #include #include "Auth.hpp" #include namespace module { void Auth::init() { invoke_init(*p_main); invoke_init(*p_reservation); this->auth_handler = std::make_unique( 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 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& 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 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 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 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(queued_event.data)) { const auto& session_event = std::get(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(queued_event.data)) { const auto& fault = std::get(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(queued_event.data)) { const auto& fault = std::get(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