1 /*! 2 * \brief Unit tests for \ref ServerMessageHandlerImpl 3 * 4 * \copyright Copyright (c) 2017-2021 Governikus GmbH & Co. KG, Germany 5 */ 6 7 #include "ServerMessageHandler.h" 8 9 #include "AppSettings.h" 10 #include "LogHandler.h" 11 #include "messages/IfdConnect.h" 12 #include "messages/IfdConnectResponse.h" 13 #include "messages/IfdDisconnect.h" 14 #include "messages/IfdDisconnectResponse.h" 15 #include "messages/IfdError.h" 16 #include "messages/IfdEstablishContext.h" 17 #include "messages/IfdEstablishContextResponse.h" 18 #include "messages/IfdEstablishPaceChannel.h" 19 #include "messages/IfdEstablishPaceChannelResponse.h" 20 #include "messages/IfdModifyPinResponse.h" 21 #include "messages/IfdStatus.h" 22 #include "messages/IfdTransmit.h" 23 #include "messages/IfdTransmitResponse.h" 24 25 #include "MockCardConnectionWorker.h" 26 #include "MockDataChannel.h" 27 #include "MockReaderManagerPlugIn.h" 28 #include "TestFileHelper.h" 29 30 #include <QSignalSpy> 31 #include <QtTest> 32 33 Q_IMPORT_PLUGIN(MockReaderManagerPlugIn) 34 35 using namespace governikus; 36 37 Q_DECLARE_METATYPE(StatusCode) 38 Q_DECLARE_METATYPE(ECardApiResult::Minor) 39 40 class MockRemoteDispatcherServer 41 : public RemoteDispatcherServer 42 { 43 Q_OBJECT 44 QSharedPointer<const RemoteMessage> mMessage; 45 46 public: MockRemoteDispatcherServer(const QSharedPointer<DataChannel> & pDataChannel)47 explicit MockRemoteDispatcherServer(const QSharedPointer<DataChannel>& pDataChannel) 48 : RemoteDispatcherServer(pDataChannel) 49 { 50 } 51 52 send(const QSharedPointer<const RemoteMessage> & pMessage)53 Q_INVOKABLE void send(const QSharedPointer<const RemoteMessage>& pMessage) override 54 { 55 RemoteDispatcherServer::send(pMessage); 56 mMessage = pMessage; 57 } 58 59 getMessage()60 QSharedPointer<const RemoteMessage> getMessage() 61 { 62 return mMessage; 63 } 64 65 66 }; 67 68 class test_ServerMessageHandler 69 : public QObject 70 { 71 Q_OBJECT 72 73 private: 74 QSharedPointer<MockDataChannel> mDataChannel; 75 QPointer<MockRemoteDispatcherServer> mRemoteDispatcher; 76 77 removeReaderAndConsumeMessages(const QString & pReaderName)78 void removeReaderAndConsumeMessages(const QString& pReaderName) 79 { 80 QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); 81 MockReaderManagerPlugIn::getInstance().removeReader(pReaderName); 82 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 83 } 84 85 ensureContext(QString & pContextHandle)86 void ensureContext(QString& pContextHandle) 87 { 88 QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); 89 90 const QByteArray establishContextMsg("{\n" 91 " \"msg\": \"IFDEstablishContext\",\n" 92 " \"Protocol\": \"IFDInterface_WebSocket_v2\",\n" 93 " \"UDName\": \"MAC-MINI\"\n" 94 "}"); 95 96 mDataChannel->onReceived(establishContextMsg); 97 98 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 99 const QList<QVariant>& establishContextResponseArguments = sendSpy.last(); 100 101 const QVariant establishContextResponseVariant = establishContextResponseArguments.at(0); 102 QVERIFY(establishContextResponseVariant.canConvert<QByteArray>()); 103 const IfdEstablishContextResponse establishContextResponse(RemoteMessage::parseByteArray(establishContextResponseVariant.toByteArray())); 104 QVERIFY(!establishContextResponse.isIncomplete()); 105 QCOMPARE(establishContextResponse.getType(), RemoteCardMessageType::IFDEstablishContextResponse); 106 107 pContextHandle = establishContextResponse.getContextHandle(); 108 QVERIFY(!pContextHandle.isEmpty()); 109 } 110 111 private Q_SLOTS: initTestCase()112 void initTestCase() 113 { 114 Env::getSingleton<LogHandler>()->init(); 115 const auto readerManager = Env::getSingleton<ReaderManager>(); 116 readerManager->init(); 117 readerManager->isScanRunning(); // just to wait until initialization finished 118 119 Env::setCreator<RemoteDispatcherServer*>(std::function<RemoteDispatcherServer* (const QSharedPointer<DataChannel>& pDataChannel)>([this](const QSharedPointer<DataChannel>){ 120 mRemoteDispatcher = new MockRemoteDispatcherServer(mDataChannel); 121 return mRemoteDispatcher.data(); 122 })); 123 } 124 125 cleanupTestCase()126 void cleanupTestCase() 127 { 128 Env::getSingleton<ReaderManager>()->shutdown(); 129 } 130 131 init()132 void init() 133 { 134 mDataChannel.reset(new MockDataChannel()); 135 } 136 137 cleanup()138 void cleanup() 139 { 140 Env::getSingleton<LogHandler>()->resetBacklog(); 141 } 142 143 checkLogOnInvalidContext()144 void checkLogOnInvalidContext() 145 { 146 QSignalSpy logSpy(Env::getSingleton<LogHandler>()->getEventHandler(), &LogEventHandler::fireLog); 147 ServerMessageHandlerImpl serverMessageHandler(mDataChannel); 148 IfdConnectResponse unexpectedMsg(QStringLiteral("RemoteReader")); 149 150 mDataChannel->onReceived(unexpectedMsg.toByteArray(IfdVersion::Version::latest, QStringLiteral("invalidConextHandle"))); 151 QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Invalid context handle received"))); 152 } 153 154 checkLogOnUnexpectedMessageWithContext()155 void checkLogOnUnexpectedMessageWithContext() 156 { 157 QSignalSpy logSpy(Env::getSingleton<LogHandler>()->getEventHandler(), &LogEventHandler::fireLog); 158 QSignalSpy spyContextHandle(mDataChannel.data(), &MockDataChannel::fireSend); 159 ServerMessageHandlerImpl serverMessageHandler(mDataChannel); 160 161 IfdEstablishContext establishContext(IfdVersion::Version::v2, DeviceInfo::getName()); 162 mDataChannel->onReceived(establishContext.toByteArray(IfdVersion::Version::v2, QString())); 163 164 const QJsonDocument& doc = QJsonDocument::fromJson(spyContextHandle.at(0).at(0).toByteArray()); 165 const QString& contextHandle = doc.object().value(QLatin1String("ContextHandle")).toString(); 166 167 IfdConnectResponse unexpectedMsg(QStringLiteral("RemoteReader")); 168 mDataChannel->onReceived(unexpectedMsg.toByteArray(IfdVersion::Version::v2, contextHandle)); 169 QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Received an unexpected message of type: IFDConnectResponse"))); 170 } 171 172 checkLogOnInvalidMessage()173 void checkLogOnInvalidMessage() 174 { 175 QSignalSpy logSpy(Env::getSingleton<LogHandler>()->getEventHandler(), &LogEventHandler::fireLog); 176 ServerMessageHandlerImpl serverMessageHandler(mDataChannel); 177 178 mDataChannel->onReceived("{\n" 179 " \"ContextHandle\": \"TestContext\",\n" 180 " \"msg\": \"RANDOM_STUFF\"\n" 181 "}\n"); 182 QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Invalid messageType received"))); 183 } 184 185 testUnexpectedMessagesCauseAnIfdErrorMessage()186 void testUnexpectedMessagesCauseAnIfdErrorMessage() 187 { 188 ServerMessageHandlerImpl serverMessageHandler(mDataChannel); 189 QString contextHandle; 190 ensureContext(contextHandle); 191 192 QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); 193 194 // We have a context handle: send unexpected messages and verify that an error message is sent back. 195 sendSpy.clear(); 196 197 ReaderInfo info(QStringLiteral("NFC Reader")); 198 info.setMaxApduLength(500); 199 info.setConnected(true); 200 info.setBasicReader(true); 201 Env::getSingleton<AppSettings>()->getRemoteServiceSettings().setPinPadMode(false); 202 const IfdStatus status(info); 203 204 const QByteArrayList serverMessages({ 205 status.toByteArray(IfdVersion::Version::v2, contextHandle), 206 IfdConnectResponse("NFC Reader").toByteArray(IfdVersion::Version::v2, contextHandle), 207 IfdDisconnectResponse("NFC Reader").toByteArray(IfdVersion::Version::v2, contextHandle), 208 IfdTransmitResponse("NFC Reader", "9000").toByteArray(IfdVersion::Version::v2, contextHandle), 209 IfdEstablishPaceChannelResponse("My little Reader", EstablishPaceChannelOutput()).toByteArray(IfdVersion::Version::v2, contextHandle) 210 }); 211 for (const auto& serverMessage : serverMessages) 212 { 213 mDataChannel->onReceived(serverMessage); 214 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 215 216 const QList<QVariant>& errorMessageArguments = sendSpy.last(); 217 const QVariant errorMessageVariant = errorMessageArguments.at(0); 218 QVERIFY(errorMessageVariant.canConvert<QByteArray>()); 219 220 const IfdError errorMessage(RemoteMessage::parseByteArray(errorMessageVariant.toByteArray())); 221 QVERIFY(!errorMessage.isIncomplete()); 222 QCOMPARE(errorMessage.getType(), RemoteCardMessageType::IFDError); 223 QCOMPARE(errorMessage.getContextHandle(), contextHandle); 224 QCOMPARE(errorMessage.getSlotHandle(), QString()); 225 QVERIFY(errorMessage.resultHasError()); 226 QCOMPARE(errorMessage.getResultMinor(), ECardApiResult::Minor::AL_Unkown_API_Function); 227 228 sendSpy.clear(); 229 } 230 } 231 232 ifdConnectForUnconnectedReaderSendsIFDL_UnknownSlot()233 void ifdConnectForUnconnectedReaderSendsIFDL_UnknownSlot() 234 { 235 ServerMessageHandlerImpl serverMessageHandler(mDataChannel); 236 QString contextHandle; 237 ensureContext(contextHandle); 238 239 QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); 240 const QByteArray ifdConnectMsg = IfdConnect(QStringLiteral("test-reader"), true).toByteArray(IfdVersion::Version::latest, contextHandle); 241 mDataChannel->onReceived(ifdConnectMsg); 242 243 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 244 245 const QList<QVariant>& connectResponseArguments = sendSpy.last(); 246 const QVariant connectResponseVariant = connectResponseArguments.at(0); 247 QVERIFY(connectResponseVariant.canConvert<QByteArray>()); 248 249 const IfdConnectResponse connectResponse(RemoteMessage::parseByteArray(connectResponseVariant.toByteArray())); 250 QVERIFY(!connectResponse.isIncomplete()); 251 QCOMPARE(connectResponse.getType(), RemoteCardMessageType::IFDConnectResponse); 252 QVERIFY(!connectResponse.getContextHandle().isEmpty()); 253 QCOMPARE(connectResponse.getSlotHandle(), QStringLiteral("test-reader")); 254 QVERIFY(connectResponse.resultHasError()); 255 QCOMPARE(connectResponse.getResultMinor(), ECardApiResult::Minor::IFDL_UnknownSlot); 256 } 257 258 ifdConnectForReaderWithoutCardSendsAL_Unknown_Error()259 void ifdConnectForReaderWithoutCardSendsAL_Unknown_Error() 260 { 261 ServerMessageHandlerImpl serverMessageHandler(mDataChannel); 262 QString contextHandle; 263 ensureContext(contextHandle); 264 265 QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); 266 MockReaderManagerPlugIn::getInstance().addReader("test-reader"); 267 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 268 sendSpy.clear(); 269 270 const QByteArray ifdConnectMsg = IfdConnect(QStringLiteral("test-reader"), true).toByteArray(IfdVersion::Version::latest, contextHandle); 271 mDataChannel->onReceived(ifdConnectMsg); 272 273 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 274 275 const QList<QVariant>& connectResponseArguments = sendSpy.last(); 276 const QVariant connectResponseVariant = connectResponseArguments.at(0); 277 QVERIFY(connectResponseVariant.canConvert<QByteArray>()); 278 279 const IfdConnectResponse connectResponse(RemoteMessage::parseByteArray(connectResponseVariant.toByteArray())); 280 QVERIFY(!connectResponse.isIncomplete()); 281 QCOMPARE(connectResponse.getType(), RemoteCardMessageType::IFDConnectResponse); 282 QVERIFY(!connectResponse.getContextHandle().isEmpty()); 283 QCOMPARE(connectResponse.getSlotHandle(), QStringLiteral("test-reader")); 284 QVERIFY(connectResponse.resultHasError()); 285 QCOMPARE(connectResponse.getResultMinor(), ECardApiResult::Minor::AL_Unknown_Error); 286 287 removeReaderAndConsumeMessages(QStringLiteral("test-reader")); 288 } 289 290 ifdConnectForReaderWithConnectedCardSendsIFDL_IFD_SharingViolation()291 void ifdConnectForReaderWithConnectedCardSendsIFDL_IFD_SharingViolation() 292 { 293 QSignalSpy logSpy(Env::getSingleton<LogHandler>()->getEventHandler(), &LogEventHandler::fireLog); 294 ServerMessageHandlerImpl serverMessageHandler(mDataChannel); 295 QString contextHandle; 296 ensureContext(contextHandle); 297 298 QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); 299 300 MockReader* reader = MockReaderManagerPlugIn::getInstance().addReader("test-reader"); 301 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 302 reader->setCard(MockCardConfig()); 303 QTRY_COMPARE(sendSpy.count(), 2); // clazy:exclude=qstring-allocations 304 305 const CardInfo cardInfo(CardType::EID_CARD, QSharedPointer<const EFCardAccess>(), 3, true); 306 ReaderInfo info = reader->getReaderInfo(); 307 info.setCardInfo(cardInfo); 308 reader->setReaderInfo(info); 309 QTRY_COMPARE(sendSpy.count(), 3); // clazy:exclude=qstring-allocations 310 sendSpy.clear(); 311 312 const QByteArray ifdConnectMsg = IfdConnect(QStringLiteral("test-reader"), true).toByteArray(IfdVersion::Version::latest, contextHandle); 313 mDataChannel->onReceived(ifdConnectMsg); 314 315 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 316 317 const QList<QVariant>& connectResponse1Arguments = sendSpy.last(); 318 const QVariant connectResponse1Variant = connectResponse1Arguments.at(0); 319 QVERIFY(connectResponse1Variant.canConvert<QByteArray>()); 320 321 const IfdConnectResponse connectResponse1(RemoteMessage::parseByteArray(connectResponse1Variant.toByteArray())); 322 QVERIFY(!connectResponse1.isIncomplete()); 323 QCOMPARE(connectResponse1.getType(), RemoteCardMessageType::IFDConnectResponse); 324 QVERIFY(!connectResponse1.getContextHandle().isEmpty()); 325 QVERIFY(!connectResponse1.getSlotHandle().isEmpty()); 326 327 QVERIFY(!connectResponse1.resultHasError()); 328 QCOMPARE(connectResponse1.getResultMinor(), ECardApiResult::Minor::null); 329 330 sendSpy.clear(); 331 332 // Card connected, try to connect a second time and get an error. 333 mDataChannel->onReceived(ifdConnectMsg); 334 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 335 336 const QList<QVariant>& connectResponse2Arguments = sendSpy.last(); 337 const QVariant connectResponse2Variant = connectResponse2Arguments.at(0); 338 QVERIFY(connectResponse2Variant.canConvert<QByteArray>()); 339 340 const IfdConnectResponse connectResponse2(RemoteMessage::parseByteArray(connectResponse2Variant.toByteArray())); 341 QVERIFY(!connectResponse2.isIncomplete()); 342 QCOMPARE(connectResponse2.getType(), RemoteCardMessageType::IFDConnectResponse); 343 QVERIFY(!connectResponse2.getContextHandle().isEmpty()); 344 QCOMPARE(connectResponse2.getSlotHandle(), QStringLiteral("test-reader")); 345 QVERIFY(connectResponse2.resultHasError()); 346 QCOMPARE(connectResponse2.getResultMinor(), ECardApiResult::Minor::IFDL_IFD_SharingViolation); 347 348 QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Card is already connected \"test-reader\""))); 349 350 removeReaderAndConsumeMessages(QStringLiteral("test-reader")); 351 } 352 353 ifdDisconnectForReaderWithConnectedCardSendsCorrectResponse()354 void ifdDisconnectForReaderWithConnectedCardSendsCorrectResponse() 355 { 356 ServerMessageHandlerImpl serverMessageHandler(mDataChannel); 357 QString contextHandle; 358 ensureContext(contextHandle); 359 360 QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); 361 362 MockReader* reader = MockReaderManagerPlugIn::getInstance().addReader("test-reader"); 363 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 364 reader->setCard(MockCardConfig()); 365 QTRY_COMPARE(sendSpy.count(), 2); // clazy:exclude=qstring-allocations 366 const CardInfo cardInfo(CardType::EID_CARD, QSharedPointer<const EFCardAccess>(), 3, true); 367 ReaderInfo info = reader->getReaderInfo(); 368 info.setCardInfo(cardInfo); 369 reader->setReaderInfo(info); 370 QTRY_COMPARE(sendSpy.count(), 3); // clazy:exclude=qstring-allocations 371 sendSpy.clear(); 372 373 const QByteArray ifdConnectMsg = IfdConnect(QStringLiteral("test-reader"), true).toByteArray(IfdVersion::Version::latest, contextHandle); 374 mDataChannel->onReceived(ifdConnectMsg); 375 376 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 377 378 const QList<QVariant>& connectResponseArguments = sendSpy.last(); 379 const QVariant connectResponseVariant = connectResponseArguments.at(0); 380 QVERIFY(connectResponseVariant.canConvert<QByteArray>()); 381 382 const IfdConnectResponse connectResponse(RemoteMessage::parseByteArray(connectResponseVariant.toByteArray())); 383 QVERIFY(!connectResponse.isIncomplete()); 384 QCOMPARE(connectResponse.getType(), RemoteCardMessageType::IFDConnectResponse); 385 QCOMPARE(connectResponse.getContextHandle(), contextHandle); 386 QVERIFY(!connectResponse.getSlotHandle().isEmpty()); 387 388 QVERIFY(!connectResponse.resultHasError()); 389 QCOMPARE(connectResponse.getResultMinor(), ECardApiResult::Minor::null); 390 391 sendSpy.clear(); 392 393 // Card connected, try to disconnect. 394 const QByteArray ifdDisconnectMsg = IfdDisconnect(connectResponse.getSlotHandle()).toByteArray(IfdVersion::Version::latest, contextHandle); 395 mDataChannel->onReceived(ifdDisconnectMsg); 396 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 397 398 const QList<QVariant>& disconnectResponseArguments = sendSpy.last(); 399 const QVariant disconnectResponseVariant = disconnectResponseArguments.at(0); 400 QVERIFY(disconnectResponseVariant.canConvert<QByteArray>()); 401 402 const IfdDisconnectResponse disconnectResponse(RemoteMessage::parseByteArray(disconnectResponseVariant.toByteArray())); 403 QVERIFY(!disconnectResponse.isIncomplete()); 404 QCOMPARE(disconnectResponse.getType(), RemoteCardMessageType::IFDDisconnectResponse); 405 QCOMPARE(disconnectResponse.getContextHandle(), contextHandle); 406 QCOMPARE(disconnectResponse.getSlotHandle(), connectResponse.getSlotHandle()); 407 QVERIFY(!disconnectResponse.resultHasError()); 408 QCOMPARE(disconnectResponse.getResultMinor(), ECardApiResult::Minor::null); 409 410 removeReaderAndConsumeMessages(QStringLiteral("test-reader")); 411 } 412 413 ifdDisconnectForReaderWithWrongReaderNameSendsIFDL_InvalidSlotHandle()414 void ifdDisconnectForReaderWithWrongReaderNameSendsIFDL_InvalidSlotHandle() 415 { 416 QSignalSpy logSpy(Env::getSingleton<LogHandler>()->getEventHandler(), &LogEventHandler::fireLog); 417 ServerMessageHandlerImpl serverMessageHandler(mDataChannel); 418 QString contextHandle; 419 ensureContext(contextHandle); 420 421 QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); 422 423 MockReader* reader = MockReaderManagerPlugIn::getInstance().addReader("test-reader"); 424 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 425 reader->setCard(MockCardConfig()); 426 QTRY_COMPARE(sendSpy.count(), 2); // clazy:exclude=qstring-allocations 427 const CardInfo cardInfo(CardType::EID_CARD, QSharedPointer<const EFCardAccess>(), 3, true); 428 ReaderInfo info = reader->getReaderInfo(); 429 info.setCardInfo(cardInfo); 430 reader->setReaderInfo(info); 431 QTRY_COMPARE(sendSpy.count(), 3); // clazy:exclude=qstring-allocations 432 sendSpy.clear(); 433 434 const QByteArray ifdConnectMsg = IfdConnect(QStringLiteral("test-reader"), true).toByteArray(IfdVersion::Version::latest, contextHandle); 435 mDataChannel->onReceived(ifdConnectMsg); 436 437 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 438 439 const QList<QVariant>& connectResponseArguments = sendSpy.last(); 440 const QVariant connectResponseVariant = connectResponseArguments.at(0); 441 QVERIFY(connectResponseVariant.canConvert<QByteArray>()); 442 443 const IfdConnectResponse connectResponse(RemoteMessage::parseByteArray(connectResponseVariant.toByteArray())); 444 QVERIFY(!connectResponse.isIncomplete()); 445 QCOMPARE(connectResponse.getType(), RemoteCardMessageType::IFDConnectResponse); 446 QCOMPARE(connectResponse.getContextHandle(), contextHandle); 447 QVERIFY(!connectResponse.getSlotHandle().isEmpty()); 448 449 QVERIFY(!connectResponse.resultHasError()); 450 QCOMPARE(connectResponse.getResultMinor(), ECardApiResult::Minor::null); 451 452 sendSpy.clear(); 453 454 // Card connected, try to disconnect from wrong reader. 455 const QByteArray ifdDisconnectMsg = IfdDisconnect(QStringLiteral("wrong-reader")).toByteArray(IfdVersion::Version::latest, contextHandle); 456 mDataChannel->onReceived(ifdDisconnectMsg); 457 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 458 459 const QList<QVariant>& disconnectResponseArguments = sendSpy.last(); 460 const QVariant disconnectResponseVariant = disconnectResponseArguments.at(0); 461 QVERIFY(disconnectResponseVariant.canConvert<QByteArray>()); 462 463 const IfdDisconnectResponse disconnectResponse(RemoteMessage::parseByteArray(disconnectResponseVariant.toByteArray())); 464 QVERIFY(!disconnectResponse.isIncomplete()); 465 QCOMPARE(disconnectResponse.getType(), RemoteCardMessageType::IFDDisconnectResponse); 466 QCOMPARE(connectResponse.getContextHandle(), contextHandle); 467 QCOMPARE(disconnectResponse.getSlotHandle(), QStringLiteral("wrong-reader")); 468 QVERIFY(disconnectResponse.resultHasError()); 469 QCOMPARE(disconnectResponse.getResultMinor(), ECardApiResult::Minor::IFDL_InvalidSlotHandle); 470 471 QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Card is not connected \"wrong-reader\""))); 472 473 removeReaderAndConsumeMessages(QStringLiteral("test-reader")); 474 } 475 476 ifdTransmitWithWrongReaderNameSendsIFDL_InvalidSlotHandle()477 void ifdTransmitWithWrongReaderNameSendsIFDL_InvalidSlotHandle() 478 { 479 QSignalSpy logSpy(Env::getSingleton<LogHandler>()->getEventHandler(), &LogEventHandler::fireLog); 480 ServerMessageHandlerImpl serverMessageHandler(mDataChannel); 481 QString contextHandle; 482 ensureContext(contextHandle); 483 484 QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); 485 486 MockReader* reader = MockReaderManagerPlugIn::getInstance().addReader("test-reader"); 487 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 488 reader->setCard(MockCardConfig()); 489 QTRY_COMPARE(sendSpy.count(), 2); // clazy:exclude=qstring-allocations 490 const CardInfo cardInfo(CardType::EID_CARD, QSharedPointer<const EFCardAccess>(), 3, true); 491 ReaderInfo info = reader->getReaderInfo(); 492 info.setCardInfo(cardInfo); 493 reader->setReaderInfo(info); 494 QTRY_COMPARE(sendSpy.count(), 3); // clazy:exclude=qstring-allocations 495 sendSpy.clear(); 496 497 const QByteArray ifdConnectMsg = IfdConnect(QStringLiteral("test-reader"), true).toByteArray(IfdVersion::Version::latest, contextHandle); 498 mDataChannel->onReceived(ifdConnectMsg); 499 500 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 501 502 const QList<QVariant>& connectResponseArguments = sendSpy.last(); 503 const QVariant connectResponseVariant = connectResponseArguments.at(0); 504 QVERIFY(connectResponseVariant.canConvert<QByteArray>()); 505 506 const IfdConnectResponse connectResponse(RemoteMessage::parseByteArray(connectResponseVariant.toByteArray())); 507 QVERIFY(!connectResponse.isIncomplete()); 508 QCOMPARE(connectResponse.getType(), RemoteCardMessageType::IFDConnectResponse); 509 QCOMPARE(connectResponse.getContextHandle(), contextHandle); 510 QVERIFY(!connectResponse.getSlotHandle().isEmpty()); 511 512 QVERIFY(!connectResponse.resultHasError()); 513 QCOMPARE(connectResponse.getResultMinor(), ECardApiResult::Minor::null); 514 515 sendSpy.clear(); 516 517 // Card connected, try to transmit to wrong reader. 518 const QByteArray ifdTransmitMsg = IfdTransmit(QStringLiteral("wrong-reader"), QByteArray()).toByteArray(IfdVersion::Version::latest, contextHandle); 519 mDataChannel->onReceived(ifdTransmitMsg); 520 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 521 522 const QList<QVariant>& transmitResponseArguments = sendSpy.last(); 523 const QVariant transmitResponseVariant = transmitResponseArguments.at(0); 524 QVERIFY(transmitResponseVariant.canConvert<QByteArray>()); 525 526 const IfdTransmitResponse transmitResponse(RemoteMessage::parseByteArray(transmitResponseVariant.toByteArray())); 527 QVERIFY(!transmitResponse.isIncomplete()); 528 QCOMPARE(transmitResponse.getType(), RemoteCardMessageType::IFDTransmitResponse); 529 QCOMPARE(transmitResponse.getContextHandle(), contextHandle); 530 QCOMPARE(transmitResponse.getSlotHandle(), QStringLiteral("wrong-reader")); 531 QVERIFY(transmitResponse.resultHasError()); 532 QCOMPARE(transmitResponse.getResultMinor(), ECardApiResult::Minor::IFDL_InvalidSlotHandle); 533 534 QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Card is not connected \"wrong-reader\""))); 535 536 removeReaderAndConsumeMessages(QStringLiteral("test-reader")); 537 } 538 539 ifdEstablishPACEChannelWithWrongReaderNameSendsIFDL_InvalidSlotHandle()540 void ifdEstablishPACEChannelWithWrongReaderNameSendsIFDL_InvalidSlotHandle() 541 { 542 QSignalSpy logSpy(Env::getSingleton<LogHandler>()->getEventHandler(), &LogEventHandler::fireLog); 543 ServerMessageHandlerImpl serverMessageHandler(mDataChannel); 544 QString contextHandle; 545 ensureContext(contextHandle); 546 547 QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); 548 549 MockReader* reader = MockReaderManagerPlugIn::getInstance().addReader("test-reader"); 550 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 551 reader->setCard(MockCardConfig()); 552 QTRY_COMPARE(sendSpy.count(), 2); // clazy:exclude=qstring-allocations 553 const CardInfo cardInfo(CardType::EID_CARD, QSharedPointer<const EFCardAccess>(), 3, true); 554 ReaderInfo info = reader->getReaderInfo(); 555 info.setCardInfo(cardInfo); 556 reader->setReaderInfo(info); 557 QTRY_COMPARE(sendSpy.count(), 3); // clazy:exclude=qstring-allocations 558 sendSpy.clear(); 559 560 const QByteArray ifdConnectMsg = IfdConnect(QStringLiteral("test-reader"), true).toByteArray(IfdVersion::Version::latest, contextHandle); 561 mDataChannel->onReceived(ifdConnectMsg); 562 563 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 564 565 const QList<QVariant>& connectResponseArguments = sendSpy.last(); 566 const QVariant connectResponseVariant = connectResponseArguments.at(0); 567 QVERIFY(connectResponseVariant.canConvert<QByteArray>()); 568 569 const IfdConnectResponse connectResponse(RemoteMessage::parseByteArray(connectResponseVariant.toByteArray())); 570 QVERIFY(!connectResponse.isIncomplete()); 571 QCOMPARE(connectResponse.getType(), RemoteCardMessageType::IFDConnectResponse); 572 QCOMPARE(connectResponse.getContextHandle(), contextHandle); 573 QVERIFY(!connectResponse.getSlotHandle().isEmpty()); 574 575 QVERIFY(!connectResponse.resultHasError()); 576 QCOMPARE(connectResponse.getResultMinor(), ECardApiResult::Minor::null); 577 578 sendSpy.clear(); 579 580 // Card connected, try to establish PACE with the wrong reader. 581 EstablishPaceChannel establishPaceChannel(PacePasswordId::PACE_PIN); 582 const QByteArray ifdEstablishPACEChannelMsg = IfdEstablishPaceChannel(QStringLiteral("wrong-reader"), establishPaceChannel, 6).toByteArray(IfdVersion::Version::latest, contextHandle); 583 mDataChannel->onReceived(ifdEstablishPACEChannelMsg); 584 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 585 586 const QList<QVariant>& ifdEstablishPACEChannelResponseArguments = sendSpy.last(); 587 const QVariant ifdEstablishPACEChannelResponseVariant = ifdEstablishPACEChannelResponseArguments.at(0); 588 QVERIFY(ifdEstablishPACEChannelResponseVariant.canConvert<QByteArray>()); 589 590 const IfdEstablishPaceChannelResponse ifdEstablishPACEChannelResponse(RemoteMessage::parseByteArray(ifdEstablishPACEChannelResponseVariant.toByteArray())); 591 QVERIFY(!ifdEstablishPACEChannelResponse.isIncomplete()); 592 QCOMPARE(ifdEstablishPACEChannelResponse.getType(), RemoteCardMessageType::IFDEstablishPACEChannelResponse); 593 QCOMPARE(ifdEstablishPACEChannelResponse.getContextHandle(), contextHandle); 594 QCOMPARE(ifdEstablishPACEChannelResponse.getSlotHandle(), QStringLiteral("wrong-reader")); 595 QVERIFY(ifdEstablishPACEChannelResponse.resultHasError()); 596 QCOMPARE(ifdEstablishPACEChannelResponse.getResultMinor(), ECardApiResult::Minor::IFDL_InvalidSlotHandle); 597 598 QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Card is not connected \"wrong-reader\""))); 599 600 removeReaderAndConsumeMessages(QStringLiteral("test-reader")); 601 } 602 603 ifdEstablishPACEChannelWithBasicReaderNameSendsAL_Unknown_Error()604 void ifdEstablishPACEChannelWithBasicReaderNameSendsAL_Unknown_Error() 605 { 606 const bool pinpadModeToSave = Env::getSingleton<AppSettings>()->getRemoteServiceSettings().getPinPadMode(); 607 Env::getSingleton<AppSettings>()->getRemoteServiceSettings().setPinPadMode(false); 608 609 QSignalSpy logSpy(Env::getSingleton<LogHandler>()->getEventHandler(), &LogEventHandler::fireLog); 610 ServerMessageHandlerImpl serverMessageHandler(mDataChannel); 611 QString contextHandle; 612 ensureContext(contextHandle); 613 614 QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); 615 616 MockReader* reader = MockReaderManagerPlugIn::getInstance().addReader("test-reader"); 617 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 618 reader->setCard(MockCardConfig()); 619 QTRY_COMPARE(sendSpy.count(), 2); // clazy:exclude=qstring-allocations 620 const CardInfo cardInfo(CardType::EID_CARD, QSharedPointer<const EFCardAccess>(), 3, true); 621 ReaderInfo info = reader->getReaderInfo(); 622 info.setCardInfo(cardInfo); 623 reader->setReaderInfo(info); 624 QTRY_COMPARE(sendSpy.count(), 3); // clazy:exclude=qstring-allocations 625 sendSpy.clear(); 626 627 const QByteArray ifdConnectMsg = IfdConnect(QStringLiteral("test-reader"), true).toByteArray(IfdVersion::Version::latest, contextHandle); 628 mDataChannel->onReceived(ifdConnectMsg); 629 630 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 631 632 const QList<QVariant>& connectResponseArguments = sendSpy.last(); 633 const QVariant connectResponseVariant = connectResponseArguments.at(0); 634 QVERIFY(connectResponseVariant.canConvert<QByteArray>()); 635 636 const IfdConnectResponse connectResponse(RemoteMessage::parseByteArray(connectResponseVariant.toByteArray())); 637 QVERIFY(!connectResponse.isIncomplete()); 638 QCOMPARE(connectResponse.getType(), RemoteCardMessageType::IFDConnectResponse); 639 QCOMPARE(connectResponse.getContextHandle(), contextHandle); 640 QVERIFY(!connectResponse.getSlotHandle().isEmpty()); 641 642 QVERIFY(!connectResponse.resultHasError()); 643 QCOMPARE(connectResponse.getResultMinor(), ECardApiResult::Minor::null); 644 645 sendSpy.clear(); 646 647 // Card connected, try to establish PACE with basic reader while not in pinpad mode. 648 const QByteArray ifdEstablishPACEChannelMsg = IfdEstablishPaceChannel(connectResponse.getSlotHandle(), EstablishPaceChannel(), 6).toByteArray(IfdVersion::Version::latest, contextHandle); 649 mDataChannel->onReceived(ifdEstablishPACEChannelMsg); 650 QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations 651 652 const QList<QVariant>& ifdEstablishPACEChannelResponseArguments = sendSpy.last(); 653 const QVariant ifdEstablishPACEChannelResponseVariant = ifdEstablishPACEChannelResponseArguments.at(0); 654 QVERIFY(ifdEstablishPACEChannelResponseVariant.canConvert<QByteArray>()); 655 656 const IfdEstablishPaceChannelResponse ifdEstablishPACEChannelResponse(RemoteMessage::parseByteArray(ifdEstablishPACEChannelResponseVariant.toByteArray())); 657 QVERIFY(!ifdEstablishPACEChannelResponse.isIncomplete()); 658 QCOMPARE(ifdEstablishPACEChannelResponse.getType(), RemoteCardMessageType::IFDEstablishPACEChannelResponse); 659 QCOMPARE(ifdEstablishPACEChannelResponse.getContextHandle(), contextHandle); 660 QCOMPARE(ifdEstablishPACEChannelResponse.getSlotHandle(), connectResponse.getSlotHandle()); 661 QVERIFY(ifdEstablishPACEChannelResponse.resultHasError()); 662 QCOMPARE(ifdEstablishPACEChannelResponse.getResultMinor(), ECardApiResult::Minor::AL_Unknown_Error); 663 664 QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("EstablishPaceChannel is only available in pin pad mode."))); 665 666 removeReaderAndConsumeMessages(QStringLiteral("test-reader")); 667 Env::getSingleton<AppSettings>()->getRemoteServiceSettings().setPinPadMode(pinpadModeToSave); 668 } 669 670 test_handleIfdModifyPin()671 void test_handleIfdModifyPin() 672 { 673 QSignalSpy logSpy(Env::getSingleton<LogHandler>()->getEventHandler(), &LogEventHandler::fireLog); 674 const QByteArray message("{\n" 675 " \"ContextHandle\": \"TestContext\",\n" 676 " \"InputData\": \"abcd1234\",\n" 677 " \"SlotHandle\": \"SlotHandle\",\n" 678 " \"msg\": \"IFDModifyPIN\"\n" 679 "}\n"); 680 681 const QJsonObject& obj = QJsonDocument::fromJson(message).object(); 682 ServerMessageHandlerImpl serverMsgHandler(mDataChannel); 683 QSignalSpy spyModifyPin(&serverMsgHandler, &ServerMessageHandler::fireModifyPin); 684 QString contextHandle; 685 ensureContext(contextHandle); 686 687 QVERIFY(!TestFileHelper::containsLog(logSpy, QLatin1String("ModifyPin is only available in pin pad mode."))); 688 Env::getSingleton<AppSettings>()->getRemoteServiceSettings().setPinPadMode(false); 689 Q_EMIT mRemoteDispatcher->fireReceived(RemoteCardMessageType::IFDModifyPIN, obj, QString()); 690 QCOMPARE(mRemoteDispatcher->getMessage()->getType(), RemoteCardMessageType::IFDModifyPINResponse); 691 const auto& msg1 = mRemoteDispatcher->getMessage().staticCast<const IfdModifyPinResponse>(); 692 QCOMPARE(msg1->getResultMinor(), ECardApiResult::Minor::AL_Unknown_Error); 693 QCOMPARE(msg1->getSlotHandle(), "SlotHandle"); 694 QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("ModifyPin is only available in pin pad mode."))); 695 696 QVERIFY(!TestFileHelper::containsLog(logSpy, QLatin1String("Card is not connected"))); 697 Env::getSingleton<AppSettings>()->getRemoteServiceSettings().setPinPadMode(true); 698 Q_EMIT mRemoteDispatcher->fireReceived(RemoteCardMessageType::IFDModifyPIN, obj, QString()); 699 const auto& msg2 = mRemoteDispatcher->getMessage().staticCast<const IfdModifyPinResponse>(); 700 QCOMPARE(msg2->getResultMinor(), ECardApiResult::Minor::IFDL_InvalidSlotHandle); 701 QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Card is not connected"))); 702 } 703 704 test_SendModifyPinResponse_data()705 void test_SendModifyPinResponse_data() 706 { 707 QTest::addColumn<StatusCode>("statusCode"); 708 QTest::addColumn<ECardApiResult::Minor>("minor"); 709 710 QTest::newRow("success") << StatusCode::SUCCESS << ECardApiResult::Minor::null; 711 QTest::newRow("empty") << StatusCode::EMPTY << ECardApiResult::Minor::IFDL_Terminal_NoCard; 712 QTest::newRow("inputTimeout") << StatusCode::INPUT_TIMEOUT << ECardApiResult::Minor::IFDL_Timeout_Error; 713 QTest::newRow("inputCancelled") << StatusCode::INPUT_CANCELLED << ECardApiResult::Minor::IFDL_CancellationByUser; 714 QTest::newRow("passwordsDiffer") << StatusCode::PASSWORDS_DIFFER << ECardApiResult::Minor::IFDL_IO_RepeatedDataMismatch; 715 QTest::newRow("passwordOutOfRange") << StatusCode::PASSWORD_OUTOF_RANGE << ECardApiResult::Minor::IFDL_IO_UnknownPINFormat; 716 QTest::newRow("default") << StatusCode::INVALID << ECardApiResult::Minor::AL_Unknown_Error; 717 } 718 719 test_SendModifyPinResponse()720 void test_SendModifyPinResponse() 721 { 722 QFETCH(StatusCode, statusCode); 723 QFETCH(ECardApiResult::Minor, minor); 724 725 ServerMessageHandlerImpl serverMsgHandler(mDataChannel); 726 const QString slotHandle("Slot Handle"); 727 const ResponseApdu apdu(statusCode); 728 729 QString contextHandle; 730 ensureContext(contextHandle); 731 serverMsgHandler.sendModifyPinResponse(slotHandle, apdu); 732 QCOMPARE(mRemoteDispatcher->getMessage()->getType(), RemoteCardMessageType::IFDModifyPINResponse); 733 const auto& msg = mRemoteDispatcher->getMessage().staticCast<const IfdModifyPinResponse>(); 734 QCOMPARE(msg->getResultMinor(), minor); 735 QCOMPARE(msg->getSlotHandle(), slotHandle); 736 } 737 738 test_EstablishPaceChannelResponse_data()739 void test_EstablishPaceChannelResponse_data() 740 { 741 QTest::addColumn<CardReturnCode>("returnCode"); 742 QTest::addColumn<ECardApiResult::Minor>("minor"); 743 744 QTest::newRow("unknown") << CardReturnCode::UNKNOWN << ECardApiResult::Minor::AL_Unknown_Error; 745 QTest::newRow("unknown") << CardReturnCode::CARD_NOT_FOUND << ECardApiResult::Minor::IFDL_Terminal_NoCard; 746 QTest::newRow("default") << CardReturnCode::OK << ECardApiResult::Minor::null; 747 } 748 749 test_EstablishPaceChannelResponse()750 void test_EstablishPaceChannelResponse() 751 { 752 QFETCH(CardReturnCode, returnCode); 753 QFETCH(ECardApiResult::Minor, minor); 754 755 const QString slotHandle("Slot Handle"); 756 EstablishPaceChannelOutput output; 757 output.setPaceReturnCode(returnCode); 758 759 ServerMessageHandlerImpl serverMsgHandler(mDataChannel); 760 QString contextHandle; 761 ensureContext(contextHandle); 762 763 serverMsgHandler.sendEstablishPaceChannelResponse(slotHandle, output); 764 QCOMPARE(mRemoteDispatcher->getMessage()->getType(), RemoteCardMessageType::IFDEstablishPACEChannelResponse); 765 const auto& msg = mRemoteDispatcher->getMessage().staticCast<const IfdEstablishPaceChannelResponse>(); 766 QCOMPARE(msg->getResultMinor(), minor); 767 QCOMPARE(msg->getSlotHandle(), slotHandle); 768 } 769 770 771 }; 772 773 774 QTEST_GUILESS_MAIN(test_ServerMessageHandler) 775 #include "test_ServerMessageHandler.moc" 776