Skip to content

Commit

Permalink
Extended Auth module so that it notifies the Evse also in case a toke…
Browse files Browse the repository at this point in the history
…n 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 <[email protected]>
  • Loading branch information
Pietfried committed May 24, 2024
1 parent 4cc1739 commit f39d68b
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 7 deletions.
13 changes: 12 additions & 1 deletion modules/Auth/lib/AuthHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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 {
Expand Down
42 changes: 36 additions & 6 deletions modules/Auth/tests/auth_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class AuthTest : public ::testing::Test {
protected:
std::unique_ptr<AuthHandler> auth_handler;
std::unique_ptr<FakeAuthReceiver> auth_receiver;
testing::MockFunction<bool(json message)> send_callback_mock;
StrictMock<MockFunction<void(const ProvidedIdToken& token, TokenValidationStatus status)>>
mock_publish_token_validation_status_callback;

Expand All @@ -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) {
Expand Down Expand Up @@ -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();

ASSERT_TRUE(result1 == TokenHandlingResult::ACCEPTED);
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(result1 == TokenHandlingResult::ACCEPTED);
ASSERT_TRUE(result2 == TokenHandlingResult::REJECTED);
ASSERT_TRUE(this->auth_receiver->get_authorization(0));
ASSERT_FALSE(this->auth_receiver->get_authorization(1));
}

Expand Down Expand Up @@ -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) {

Expand Down

0 comments on commit f39d68b

Please sign in to comment.