PageRenderTime 41ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/src/core/testqgsauthmanager.cpp

http://github.com/qgis/Quantum-GIS
C++ | 473 lines | 334 code | 81 blank | 58 comment | 18 complexity | ddf1d6844dea4fa0c4a6977d566f48ec MD5 | raw file
Possible License(s): LGPL-2.0, GPL-3.0, GPL-2.0, CC-BY-SA-3.0, MIT, 0BSD, BSD-3-Clause
  1. /***************************************************************************
  2. testqgsauthmanager.cpp
  3. ----------------------
  4. Date : October 2015
  5. Copyright : (C) 2015 by Boundless Spatial, Inc. USA
  6. Author : Larry Shaffer
  7. Email : lshaffer at boundlessgeo dot com
  8. ***************************************************************************
  9. * *
  10. * This program is free software; you can redistribute it and/or modify *
  11. * it under the terms of the GNU General Public License as published by *
  12. * the Free Software Foundation; either version 2 of the License, or *
  13. * (at your option) any later version. *
  14. * *
  15. ***************************************************************************/
  16. #include "qgstest.h"
  17. #include <QtTest/QSignalSpy>
  18. #include <QObject>
  19. #include <QDesktopServices>
  20. #include <QDir>
  21. #include <QFile>
  22. #include <QString>
  23. #include <QStringList>
  24. #include <QTextStream>
  25. #include "qgsapplication.h"
  26. #include "qgsauthmanager.h"
  27. #include "qgsauthconfig.h"
  28. #include "qgssettings.h"
  29. /**
  30. * \ingroup UnitTests
  31. * Unit tests for QgsAuthManager
  32. */
  33. class TestQgsAuthManager: public QObject
  34. {
  35. Q_OBJECT
  36. public:
  37. TestQgsAuthManager();
  38. public slots:
  39. void doSync();
  40. private slots:
  41. void initTestCase();
  42. void cleanupTestCase();
  43. void init();
  44. void cleanup();
  45. void testMasterPassword();
  46. void testAuthConfigs();
  47. void testAuthMethods();
  48. void testPasswordHelper();
  49. private:
  50. void cleanupTempDir();
  51. QList<QgsAuthMethodConfig> registerAuthConfigs();
  52. void reportRow( const QString &msg );
  53. void reportHeader( const QString &msg );
  54. QString mPkiData;
  55. QString mTempDir;
  56. const char *mPass = nullptr;
  57. QString mReport;
  58. };
  59. TestQgsAuthManager::TestQgsAuthManager()
  60. : mPkiData( QStringLiteral( TEST_DATA_DIR ) + "/auth_system/certs_keys" )
  61. , mTempDir( QDir::tempPath() + "/auth" )
  62. , mPass( "pass" )
  63. {
  64. }
  65. void TestQgsAuthManager::initTestCase()
  66. {
  67. cleanupTempDir();
  68. mReport += QLatin1String( "<h1>QgsAuthManager Tests</h1>\n" );
  69. // make QGIS_AUTH_DB_DIR_PATH temp dir for qgis-auth.db and master password file
  70. QDir tmpDir = QDir::temp();
  71. QVERIFY2( tmpDir.mkpath( mTempDir ), "Couldn't make temp directory" );
  72. qputenv( "QGIS_AUTH_DB_DIR_PATH", mTempDir.toLatin1() );
  73. // init app and auth manager
  74. QgsApplication::init();
  75. QgsApplication::initQgis();
  76. QVERIFY2( !QgsApplication::authManager()->isDisabled(),
  77. "Authentication system is DISABLED" );
  78. QString mySettings = QgsApplication::showSettings();
  79. mySettings = mySettings.replace( '\n', QLatin1String( "<br />\n" ) );
  80. mReport += "<p>" + mySettings + "</p>\n";
  81. // verify QGIS_AUTH_DB_DIR_PATH (temp auth db path) worked
  82. QString db1( QFileInfo( QgsApplication::authManager()->authenticationDatabasePath() ).canonicalFilePath() );
  83. QString db2( QFileInfo( mTempDir + "/qgis-auth.db" ).canonicalFilePath() );
  84. QVERIFY2( db1 == db2, "Auth db temp path does not match db path of manager" );
  85. // verify master pass can be set manually
  86. // (this also creates a fresh password hash in the new temp database)
  87. QVERIFY2( QgsApplication::authManager()->setMasterPassword( mPass, true ),
  88. "Master password could not be set" );
  89. QVERIFY2( QgsApplication::authManager()->masterPasswordIsSet(),
  90. "Auth master password not set from passed string" );
  91. // create QGIS_AUTH_PASSWORD_FILE file
  92. QString passfilepath = mTempDir + "/passfile";
  93. QFile passfile( passfilepath );
  94. if ( passfile.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
  95. {
  96. QTextStream fout( &passfile );
  97. fout << QString( mPass ) << "\r\n";
  98. passfile.close();
  99. qputenv( "QGIS_AUTH_PASSWORD_FILE", passfilepath.toLatin1() );
  100. }
  101. // qDebug( "QGIS_AUTH_PASSWORD_FILE=%s", qgetenv( "QGIS_AUTH_PASSWORD_FILE" ).constData() );
  102. // re-init app and auth manager
  103. QgsApplication::quit();
  104. // QTest::qSleep( 3000 );
  105. QgsApplication::init();
  106. QgsApplication::initQgis();
  107. QVERIFY2( !QgsApplication::authManager()->isDisabled(),
  108. "Authentication system is DISABLED" );
  109. // verify QGIS_AUTH_PASSWORD_FILE worked, when compared against hash in db
  110. QVERIFY2( QgsApplication::authManager()->masterPasswordIsSet(),
  111. "Auth master password not set from QGIS_AUTH_PASSWORD_FILE" );
  112. // all tests should now have a valid qgis-auth.db and stored/set master password
  113. }
  114. void TestQgsAuthManager::cleanup()
  115. {
  116. // Restore password_helper_insecure_fallback value
  117. QgsSettings settings;
  118. settings.setValue( QStringLiteral( "password_helper_insecure_fallback" ), false, QgsSettings::Section::Auth );
  119. }
  120. void TestQgsAuthManager::cleanupTempDir()
  121. {
  122. QDir tmpDir = QDir( mTempDir );
  123. if ( tmpDir.exists() )
  124. {
  125. Q_FOREACH ( const QString &tf, tmpDir.entryList( QDir::NoDotAndDotDot | QDir::Files ) )
  126. {
  127. QVERIFY2( tmpDir.remove( mTempDir + '/' + tf ), qPrintable( "Could not remove " + mTempDir + '/' + tf ) );
  128. }
  129. QVERIFY2( tmpDir.rmdir( mTempDir ), qPrintable( "Could not remove directory " + mTempDir ) );
  130. }
  131. }
  132. void TestQgsAuthManager::cleanupTestCase()
  133. {
  134. QgsApplication::exitQgis();
  135. cleanupTempDir();
  136. QString myReportFile = QDir::tempPath() + "/qgistest.html";
  137. QFile myFile( myReportFile );
  138. if ( myFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
  139. {
  140. QTextStream myQTextStream( &myFile );
  141. myQTextStream << mReport;
  142. myFile.close();
  143. // QDesktopServices::openUrl( "file:///" + myReportFile );
  144. }
  145. }
  146. void TestQgsAuthManager::init()
  147. {
  148. mReport += "<h2>" + QString( QTest::currentTestFunction() ) + "</h2>\n";
  149. }
  150. void TestQgsAuthManager::reportRow( const QString &msg )
  151. {
  152. mReport += msg + "<br>\n";
  153. }
  154. void TestQgsAuthManager::reportHeader( const QString &msg )
  155. {
  156. mReport += "<h3>" + msg + "</h3>\n";
  157. }
  158. void TestQgsAuthManager::testMasterPassword()
  159. {
  160. // password is already stored/set in initTestCase()
  161. // NOTE: leave it in the same state when done with this test
  162. QgsAuthManager *authm = QgsApplication::authManager();
  163. QVERIFY( authm->masterPasswordIsSet() );
  164. QVERIFY( authm->masterPasswordHashInDatabase() );
  165. QVERIFY( authm->masterPasswordSame( mPass ) );
  166. QVERIFY( !authm->masterPasswordSame( "wrongpass" ) );
  167. QVERIFY( authm->setMasterPassword() );
  168. QVERIFY( authm->verifyMasterPassword() );
  169. QVERIFY( authm->verifyMasterPassword( mPass ) );
  170. QVERIFY( !authm->verifyMasterPassword( "wrongpass" ) );
  171. QList<QVariant> spyargs;
  172. QSignalSpy spy( authm, SIGNAL( masterPasswordVerified( bool ) ) );
  173. QVERIFY( authm->setMasterPassword( true ) );
  174. QCOMPARE( spy.count(), 1 );
  175. spyargs = spy.takeFirst();
  176. QVERIFY( spyargs.at( 0 ).type() == QVariant::Bool );
  177. QVERIFY( spyargs.at( 0 ).toBool() );
  178. authm->clearMasterPassword();
  179. QVERIFY( !authm->masterPasswordIsSet() );
  180. QVERIFY( !authm->setMasterPassword( "wrongpass", true ) );
  181. QVERIFY( !authm->masterPasswordIsSet() );
  182. QCOMPARE( spy.count(), 1 );
  183. spyargs = spy.takeFirst();
  184. QVERIFY( spyargs.at( 0 ).type() == QVariant::Bool );
  185. QVERIFY( !spyargs.at( 0 ).toBool() );
  186. authm->clearMasterPassword();
  187. QVERIFY( !authm->masterPasswordIsSet() );
  188. QVERIFY( authm->setMasterPassword( mPass, true ) );
  189. QVERIFY( authm->masterPasswordIsSet() );
  190. QCOMPARE( spy.count(), 1 );
  191. spyargs = spy.takeFirst();
  192. QVERIFY( spyargs.at( 0 ).type() == QVariant::Bool );
  193. QVERIFY( spyargs.at( 0 ).toBool() );
  194. }
  195. void TestQgsAuthManager::testAuthConfigs()
  196. {
  197. QList<QgsAuthMethodConfig> configs( registerAuthConfigs() );
  198. QVERIFY( !configs.isEmpty() );
  199. QgsAuthManager *authm = QgsApplication::authManager();
  200. // test storing/loading/updating
  201. Q_FOREACH ( QgsAuthMethodConfig config, configs )
  202. {
  203. QVERIFY( config.isValid() );
  204. QVERIFY( authm->storeAuthenticationConfig( config ) );
  205. // config should now have a valid, unique ID
  206. QString configid( config.id() );
  207. QVERIFY( !configid.isEmpty() );
  208. QVERIFY( !authm->configIdUnique( configid ) ); // uniqueness registered, so can't be overridden
  209. QVERIFY( authm->configIds().contains( configid ) );
  210. // config -> method map should have been updated and return the right method key
  211. QCOMPARE( authm->configAuthMethodKey( configid ), config.method() );
  212. // loading into new base config should return same as original stored
  213. QgsAuthMethodConfig config2;
  214. QVERIFY( authm->loadAuthenticationConfig( configid, config2, false ) );
  215. QVERIFY( config2.isValid() );
  216. QVERIFY( config2.configMap().isEmpty() ); // has no sensitive data
  217. QVERIFY( authm->loadAuthenticationConfig( configid, config2, true ) );
  218. QVERIFY( config2.isValid() );
  219. QVERIFY( !config2.configMap().isEmpty() ); // has sensitive data
  220. QVERIFY( config == config2 );
  221. // values haven't been changed, but db update should take place and values should roundtrip
  222. QVERIFY( authm->updateAuthenticationConfig( config2 ) );
  223. QVERIFY( authm->loadAuthenticationConfig( configid, config2, true ) );
  224. QVERIFY( config2.isValid() );
  225. QVERIFY( !config2.configMap().isEmpty() ); // has sensitive data
  226. QVERIFY( config == config2 );
  227. // changed config should update then correctly roundtrip
  228. Q_FOREACH ( const QString &key, config2.configMap().keys() )
  229. {
  230. config2.setConfig( key, config2.configMap().value( key ) + "changed" );
  231. }
  232. config2.setName( config2.name() + "changed" );
  233. config2.setUri( config2.uri() + "changed" );
  234. QVERIFY( authm->updateAuthenticationConfig( config2 ) );
  235. QgsAuthMethodConfig config3;
  236. QVERIFY( authm->loadAuthenticationConfig( configid, config3, true ) );
  237. QVERIFY( config3.isValid() );
  238. QVERIFY( !config3.configMap().isEmpty() );
  239. QVERIFY( config != config3 );
  240. QVERIFY( config2 == config3 );
  241. // config can be deleted
  242. QVERIFY( authm->removeAuthenticationConfig( configid ) );
  243. QVERIFY( !authm->configIds().contains( configid ) );
  244. QVERIFY( authm->configIds().isEmpty() );
  245. // config with custom id can be stored
  246. QString customid( authm->uniqueConfigId() );
  247. config2.setId( customid );
  248. QVERIFY( authm->storeAuthenticationConfig( config2 ) );
  249. QCOMPARE( config2.id(), customid );
  250. QVERIFY( config2 != config3 ); // id should be different
  251. // custom configid can be deleted
  252. QVERIFY( authm->removeAuthenticationConfig( customid ) );
  253. QVERIFY( !authm->configIds().contains( customid ) );
  254. QVERIFY( authm->configIds().isEmpty() );
  255. }
  256. // verify cleanup of test configs
  257. QVERIFY( authm->configIds().isEmpty() );
  258. QVERIFY( authm->availableAuthMethodConfigs().isEmpty() );
  259. // test bulk operations
  260. configs.clear();
  261. configs = registerAuthConfigs();
  262. QVERIFY( !configs.isEmpty() );
  263. // test storing, then retrieving configid -> config map
  264. QgsAuthMethodConfigsMap idcfgmap;
  265. Q_FOREACH ( QgsAuthMethodConfig config, configs )
  266. {
  267. QVERIFY( authm->storeAuthenticationConfig( config ) );
  268. idcfgmap.insert( config.id(), config );
  269. }
  270. QgsAuthMethodConfigsMap authmap( authm->availableAuthMethodConfigs() );
  271. QCOMPARE( authmap.size(), 3 );
  272. QgsAuthMethodConfigsMap::iterator it = authmap.begin();
  273. for ( it = authmap.begin(); it != authmap.end(); ++it )
  274. {
  275. QString cfgid = it.key();
  276. if ( !idcfgmap.contains( cfgid ) )
  277. continue;
  278. QgsAuthMethodConfig cfg = it.value();
  279. QgsAuthMethodConfig origcfg = idcfgmap.take( cfgid );
  280. QCOMPARE( origcfg.id(), cfg.id() );
  281. QCOMPARE( origcfg.name(), cfg.name() );
  282. QCOMPARE( origcfg.method(), cfg.method() );
  283. QCOMPARE( origcfg.uri(), cfg.uri() );
  284. }
  285. QCOMPARE( idcfgmap.size(), 0 );
  286. QVERIFY( authm->removeAllAuthenticationConfigs() );
  287. }
  288. void TestQgsAuthManager::testAuthMethods()
  289. {
  290. QList<QgsAuthMethodConfig> configs( registerAuthConfigs() );
  291. QVERIFY( !configs.isEmpty() );
  292. QgsAuthManager *authm = QgsApplication::authManager();
  293. Q_FOREACH ( QgsAuthMethodConfig config, configs )
  294. {
  295. QVERIFY( config.isValid() );
  296. QVERIFY( authm->storeAuthenticationConfig( config ) );
  297. // config should now have a valid, unique ID
  298. // (see testAuthConfigs for further config testing)
  299. QString configid( config.id() );
  300. // correct method, loaded from core auth method plugin registry, should be returned
  301. QgsAuthMethod *authmethod = authm->configAuthMethod( configid );
  302. QVERIFY( authmethod );
  303. QCOMPARE( authmethod->key(), config.method() );
  304. }
  305. QVERIFY( authm->removeAllAuthenticationConfigs() );
  306. }
  307. QList<QgsAuthMethodConfig> TestQgsAuthManager::registerAuthConfigs()
  308. {
  309. QList<QgsAuthMethodConfig> configs;
  310. // Basic
  311. QgsAuthMethodConfig b_config;
  312. b_config.setName( QStringLiteral( "Basic" ) );
  313. b_config.setMethod( QStringLiteral( "Basic" ) );
  314. b_config.setUri( QStringLiteral( "http://example.com" ) );
  315. b_config.setConfig( QStringLiteral( "username" ), QStringLiteral( "username" ) );
  316. b_config.setConfig( QStringLiteral( "password" ), QStringLiteral( "password" ) );
  317. b_config.setConfig( QStringLiteral( "realm" ), QStringLiteral( "Realm" ) );
  318. if ( !b_config.isValid() )
  319. {
  320. return configs;
  321. }
  322. // PKI-Paths
  323. QgsAuthMethodConfig p_config;
  324. p_config.setName( QStringLiteral( "PKI-Paths" ) );
  325. p_config.setMethod( QStringLiteral( "PKI-Paths" ) );
  326. p_config.setUri( QStringLiteral( "http://example.com" ) );
  327. p_config.setConfig( QStringLiteral( "certpath" ), mPkiData + "/gerardus_cert.pem" );
  328. p_config.setConfig( QStringLiteral( "keypath" ), mPkiData + "gerardus_key_w-pass.pem" );
  329. if ( !p_config.isValid() )
  330. {
  331. return configs;
  332. }
  333. // PKI-PKCS#12
  334. QgsAuthMethodConfig k_config;
  335. k_config.setName( QStringLiteral( "PKI-PKCS#12" ) );
  336. k_config.setMethod( QStringLiteral( "PKI-PKCS#12" ) );
  337. k_config.setUri( QStringLiteral( "http://example.com" ) );
  338. k_config.setConfig( QStringLiteral( "bundlepath" ), mPkiData + "/gerardus.p12" );
  339. k_config.setConfig( QStringLiteral( "bundlepass" ), QStringLiteral( "password" ) );
  340. if ( !k_config.isValid() )
  341. {
  342. return configs;
  343. }
  344. // do this last, so we are assured to have all core configs
  345. configs << b_config << p_config << k_config;
  346. return configs;
  347. }
  348. void TestQgsAuthManager::doSync()
  349. {
  350. QgsAuthManager *authm = QgsApplication::authManager();
  351. QVERIFY( authm->passwordHelperSync() );
  352. }
  353. void TestQgsAuthManager::testPasswordHelper()
  354. {
  355. QgsAuthManager *authm = QgsApplication::authManager();
  356. authm->clearMasterPassword();
  357. QgsSettings settings;
  358. settings.setValue( QStringLiteral( "password_helper_insecure_fallback" ), true, QgsSettings::Section::Auth );
  359. // Test enable/disable
  360. // It should be enabled by default
  361. QVERIFY( authm->passwordHelperEnabled() );
  362. authm->setPasswordHelperEnabled( false );
  363. QVERIFY( ! authm->passwordHelperEnabled() );
  364. authm->setPasswordHelperEnabled( true );
  365. QVERIFY( authm->passwordHelperEnabled() );
  366. // Sync with wallet
  367. QVERIFY( authm->setMasterPassword( mPass, true ) );
  368. QVERIFY( authm->masterPasswordIsSet() );
  369. QObject::connect( authm, &QgsAuthManager::passwordHelperSuccess,
  370. QApplication::instance(), &QCoreApplication::quit );
  371. QObject::connect( authm, &QgsAuthManager::passwordHelperFailure,
  372. QApplication::instance(), &QCoreApplication::quit );
  373. QMetaObject::invokeMethod( this, "doSync", Qt::QueuedConnection );
  374. qApp->exec();
  375. authm->clearMasterPassword();
  376. QVERIFY( authm->setMasterPassword() );
  377. QVERIFY( authm->masterPasswordIsSet() );
  378. // Delete from wallet
  379. authm->clearMasterPassword();
  380. QVERIFY( authm->passwordHelperDelete() );
  381. QVERIFY( ! authm->setMasterPassword() );
  382. QVERIFY( ! authm->masterPasswordIsSet() );
  383. // Re-sync
  384. QVERIFY( authm->setMasterPassword( mPass, true ) );
  385. QMetaObject::invokeMethod( this, "doSync", Qt::QueuedConnection );
  386. qApp->exec();
  387. authm->clearMasterPassword();
  388. QVERIFY( authm->setMasterPassword() );
  389. QVERIFY( authm->masterPasswordIsSet() );
  390. }
  391. QGSTEST_MAIN( TestQgsAuthManager )
  392. #include "testqgsauthmanager.moc"