From d6ea726b706cfc0e3958d19f6909f126d21c1dd2 Mon Sep 17 00:00:00 2001 From: pietfried Date: Fri, 24 May 2024 22:03:56 +0200 Subject: [PATCH] Extended Auth module so that it notifies the Evse also in case a token was rejected. This is especially required to allow the ISO15118 state machine to escape the authorize loop when a Plug&Charge authorization was rejected Signed-off-by: pietfried --- modules/Auth/lib/AuthHandler.cpp | 13 +++++++++- modules/Auth/tests/auth_tests.cpp | 42 ++++++++++++++++++++++++++----- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/modules/Auth/lib/AuthHandler.cpp b/modules/Auth/lib/AuthHandler.cpp index f76eb1aad5..f0c404e433 100644 --- a/modules/Auth/lib/AuthHandler.cpp +++ b/modules/Auth/lib/AuthHandler.cpp @@ -189,12 +189,13 @@ TokenHandlingResult AuthHandler::handle_token(const ProvidedIdToken& provided_to return TokenHandlingResult::NO_CONNECTOR_AVAILABLE; } + types::authorization::ValidationResult validation_result = {types::authorization::AuthorizationStatus::Unknown}; if (!validation_results.empty()) { bool authorized = false; int i = 0; // iterate over validation results while (i < validation_results.size() && !authorized && !referenced_connectors.empty()) { - auto validation_result = validation_results.at(i); + validation_result = validation_results.at(i); if (validation_result.authorization_status == AuthorizationStatus::Accepted) { if (this->equals_master_pass_group_id(validation_result.parent_id_token)) { @@ -253,6 +254,16 @@ TokenHandlingResult AuthHandler::handle_token(const ProvidedIdToken& provided_to return TokenHandlingResult::ACCEPTED; } else { EVLOG_debug << "id_token could not be validated by any validator"; + // in case the validation was not successful, we need to notify the evse and transmit the validation result. + // This is especially required for Plug&Charge with ISO15118 in order to allow the ISO15118 state machine to + // escape the Authorize loop. We do this for all connectors that were referenced + if (provided_token.connectors.has_value()) { + const auto connectors = provided_token.connectors.value(); + std::for_each(connectors.begin(), connectors.end(), + [this, provided_token, validation_result](int32_t connector) { + this->notify_evse(connector, provided_token, validation_result); + }); + } return TokenHandlingResult::REJECTED; } } else { diff --git a/modules/Auth/tests/auth_tests.cpp b/modules/Auth/tests/auth_tests.cpp index dabb46bd67..97a4a0ee8f 100644 --- a/modules/Auth/tests/auth_tests.cpp +++ b/modules/Auth/tests/auth_tests.cpp @@ -76,6 +76,7 @@ class AuthTest : public ::testing::Test { protected: std::unique_ptr auth_handler; std::unique_ptr auth_receiver; + testing::MockFunction send_callback_mock; StrictMock> mock_publish_token_validation_status_callback; @@ -93,6 +94,8 @@ class AuthTest : public ::testing::Test { << provided_token.id_token.value; if (validation_result.authorization_status == AuthorizationStatus::Accepted) { this->auth_receiver->authorize(evse_index); + } else { + this->auth_receiver->deauthorize(evse_index); } }); this->auth_handler->register_withdraw_authorization_callback([this](int32_t evse_index) { @@ -449,18 +452,18 @@ TEST_F(AuthTest, test_two_plugins_with_invalid_rfid) { Call(Field(&ProvidedIdToken::id_token, provided_token_1.id_token), TokenValidationStatus::Accepted)); EXPECT_CALL(mock_publish_token_validation_status_callback, Call(Field(&ProvidedIdToken::id_token, provided_token_2.id_token), TokenValidationStatus::Rejected)); - - std::thread t3([this, provided_token_1, &result1]() { result1 = this->auth_handler->on_token(provided_token_1); }); - std::thread t4([this, provided_token_2, &result2]() { result2 = this->auth_handler->on_token(provided_token_2); }); - t1.join(); t2.join(); + std::thread t3([this, provided_token_1, &result1]() { result1 = this->auth_handler->on_token(provided_token_1); }); t3.join(); - t4.join(); ASSERT_TRUE(result1 == TokenHandlingResult::ACCEPTED); - ASSERT_TRUE(result2 == TokenHandlingResult::REJECTED); ASSERT_TRUE(this->auth_receiver->get_authorization(0)); + + std::thread t4([this, provided_token_2, &result2]() { result2 = this->auth_handler->on_token(provided_token_2); }); + t4.join(); + + ASSERT_TRUE(result2 == TokenHandlingResult::REJECTED); ASSERT_FALSE(this->auth_receiver->get_authorization(1)); } @@ -1099,6 +1102,33 @@ TEST_F(AuthTest, test_plug_and_charge) { ASSERT_FALSE(this->auth_receiver->get_authorization(1)); } +/// \brief Test PlugAndCharge +TEST_F(AuthTest, test_plug_and_charge_rejected) { + + // put authorization on evse so that we can check later if it was removed + this->auth_receiver->authorize(0); + ASSERT_TRUE(this->auth_receiver->get_authorization(0)); + + const SessionEvent session_event = get_session_started_event(types::evse_manager::StartSessionReason::EVConnected); + this->auth_handler->handle_session_event(1, session_event); + + ProvidedIdToken provided_token; + provided_token.id_token = {INVALID_TOKEN, types::authorization::IdTokenType::eMAID}; + provided_token.authorization_type = types::authorization::AuthorizationType::PlugAndCharge; + provided_token.certificate.emplace("TestCertificate"); + provided_token.connectors = {1, 2}; + + EXPECT_CALL(mock_publish_token_validation_status_callback, + Call(Field(&ProvidedIdToken::id_token, provided_token.id_token), TokenValidationStatus::Processing)); + EXPECT_CALL(mock_publish_token_validation_status_callback, + Call(Field(&ProvidedIdToken::id_token, provided_token.id_token), TokenValidationStatus::Rejected)); + + const auto result = this->auth_handler->on_token(provided_token); + ASSERT_TRUE(result == TokenHandlingResult::REJECTED); + ASSERT_FALSE(this->auth_receiver->get_authorization(0)); + ASSERT_FALSE(this->auth_receiver->get_authorization(1)); +} + /// \brief Test empty intersection of referenced connectors in provided token and in validation result TEST_F(AuthTest, test_empty_intersection) {