diff --git a/CMakeLists.txt b/CMakeLists.txt index 652a7a031..9ae70290f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -286,6 +286,9 @@ endif () if (WIN) add_compile_definitions(_HAS_AUTO_PTR_ETC=1) add_compile_definitions(_USE_MATH_DEFINES) + # On windows the compiler instead of using standard min & max functions expands them to + # min & max macros in every file that includes windows header file + add_compile_definitions(NOMINMAX) endif () if (IOS OR ANDROID) diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index e49bfd223..88bd56b4c 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -184,6 +184,10 @@ set(MM_HDRS mmstyle.h ) +if (NOT WIN32) + set(MM_SRCS ${MM_SRCS} static_plugins.cpp) +endif () + if (HAVE_BLUETOOTH) set(MM_SRCS ${MM_SRCS} position/providers/bluetoothpositionprovider.cpp) @@ -506,6 +510,21 @@ if (LNX) target_link_libraries(MerginMaps PUBLIC QGIS::Core) endif () +if (NOT WIN32) + # for every other platform except windows + find_library( + QCA_OSSL_PLUGIN_LIB + NAMES qca-ossl libqca-ossl + PATH_SUFFIXES lib/Qca/crypto + ) + + if (QCA_OSSL_PLUGIN_LIB) + message(STATUS "Found QCA OpenSSL Plugin: ${QCA_OSSL_PLUGIN_LIB}") + else () + message(WARNING "Could not find 'qca-ossl'. Authentication might fail.") + endif () +endif () + target_link_libraries( MerginMaps PUBLIC Qt6Keychain::Qt6Keychain @@ -515,6 +534,10 @@ target_link_libraries( Spatialite::Spatialite ) +if (NOT WIN32) + target_link_libraries(MerginMaps PUBLIC ${QCA_OSSL_PLUGIN_LIB}) +endif () + target_link_libraries( MerginMaps PUBLIC Spatialindex::Spatialindex diff --git a/app/activeproject.cpp b/app/activeproject.cpp index 8a91f5c52..6f1859b0c 100644 --- a/app/activeproject.cpp +++ b/app/activeproject.cpp @@ -17,6 +17,7 @@ #include "qgslayertreelayer.h" #include "qgslayertreegroup.h" #include "qgsmapthemecollection.h" +#include "qgsapplication.h" #include "activeproject.h" #include "coreutils.h" @@ -36,6 +37,7 @@ ActiveProject::ActiveProject( AppSettings &appSettings QObject( parent ) , mAppSettings( appSettings ) , mActiveLayer( activeLayer ) + , mAuthManager( QgsApplication::authManager() ) , mLocalProjectsManager( localProjectsManager ) , mProjectLoadingLog( "" ) { @@ -110,6 +112,9 @@ bool ActiveProject::forceLoad( const QString &filePath, bool force ) AndroidTrackingBroadcast::unregisterBroadcast(); #endif + // clear the authentication configurations from other projects before reloading + clearAllAuthenticationConfigs(); + // Just clear project if empty if ( filePath.isEmpty() ) { @@ -155,6 +160,17 @@ bool ActiveProject::forceLoad( const QString &filePath, bool force ) emit projectWillBeReloaded(); mActiveLayer.resetActiveLayer(); + // path to the authentication configuration file + const QDir projectDir = QFileInfo( filePath ).dir(); + const QFileInfo cfgFile( projectDir.filePath( CoreUtils::AUTH_CONFIG_FILENAME ) ); + if ( cfgFile.exists() && cfgFile.isFile() ) + { + // import the new configuration, if it exists. + const QString projectId = MerginProjectMetadata::fromCachedJson( CoreUtils::getProjectMetadataPath( projectDir.path() ) ).projectId; + const bool ok = mAuthManager->importAuthenticationConfigsFromXml( cfgFile.filePath(), projectId, true ); + CoreUtils::log( "Authentication database", QStringLiteral( "QGIS auth import of %1 configuration(s) : %2" ).arg( mAuthManager->configIds().count() ).arg( ok ? "successful" : "failed" ) ); + } + res = mQgsProject->read( filePath ); if ( !res ) { @@ -186,6 +202,7 @@ bool ActiveProject::forceLoad( const QString &filePath, bool force ) QString role = MerginProjectMetadata::fromCachedJson( CoreUtils::getProjectMetadataPath( mLocalProject.projectDir ) ).role; setProjectRole( role ); + updateMapTheme(); updateActiveLayer(); updateMapSettingsLayers(); @@ -495,6 +512,14 @@ void ActiveProject::updateActiveLayer() } } +void ActiveProject::clearAllAuthenticationConfigs() +{ + for ( const auto configuration : mAuthManager->configIds() ) + { + mAuthManager->removeAuthenticationConfig( configuration ); + } +} + bool ActiveProject::isProjectLoaded() const { return mQgsProject && !mQgsProject->fileName().isEmpty(); diff --git a/app/activeproject.h b/app/activeproject.h index e5a4e5149..92fee83b8 100644 --- a/app/activeproject.h +++ b/app/activeproject.h @@ -17,6 +17,7 @@ #include "appsettings.h" #include "activelayer.h" +#include "qgsauthmanager.h" #include "recordinglayersproxymodel.h" #include "localprojectsmanager.h" #include "autosynccontroller.h" @@ -212,11 +213,15 @@ class ActiveProject: public QObject //! Reloads layers in 'recording layers model' void updateRecordingLayers(); + // Clear all the authentication configurations from the database + void clearAllAuthenticationConfigs(); + QgsProject *mQgsProject = nullptr; LocalProject mLocalProject; AppSettings &mAppSettings; ActiveLayer &mActiveLayer; + QgsAuthManager *mAuthManager = nullptr; LocalProjectsManager &mLocalProjectsManager; InputMapSettings *mMapSettings = nullptr; std::unique_ptr mAutosyncController; diff --git a/app/main.cpp b/app/main.cpp index e5bda4ca6..45a76001f 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -27,6 +27,7 @@ #include "test/inputtests.h" #endif #include +#include "qgsauthmanager.h" #include #include "qgsconfig.h" #include "qgsproviderregistry.h" @@ -543,6 +544,14 @@ int main( int argc, char *argv[] ) LayerTreeFlatModelPixmapProvider *layerTreeFlatModelPixmapProvider( new LayerTreeFlatModelPixmapProvider ); LayerDetailLegendImageProvider *layerDetailLegendImageProvider( new LayerDetailLegendImageProvider ); + QgsAuthManager *authManager = QgsApplication::authManager(); + // remove existing authentication database when opening the app + authManager->removeAllAuthenticationConfigs(); + // set up the master password for authentication database retrieval + authManager->setPasswordHelperEnabled( false ); + authManager->setMasterPassword( QStringLiteral( "merginMaps" ), true ); + + // build position kit, save active provider to QSettings and load previously active provider PositionKit pk; QObject::connect( &pk, &PositionKit::positionProviderChanged, as, [as]( AbstractPositionProvider * provider ) diff --git a/app/static_plugins.cpp b/app/static_plugins.cpp new file mode 100644 index 000000000..12ac5f62d --- /dev/null +++ b/app/static_plugins.cpp @@ -0,0 +1,3 @@ +#include +// Required for QGIS authentication manager API on static builds (Linux/macOS/iOS/android) +Q_IMPORT_PLUGIN( opensslPlugin ) \ No newline at end of file diff --git a/app/test/testactiveproject.cpp b/app/test/testactiveproject.cpp index c3189f368..a93ca1614 100644 --- a/app/test/testactiveproject.cpp +++ b/app/test/testactiveproject.cpp @@ -10,6 +10,7 @@ #include "testactiveproject.h" #include "testutils.h" #include "inpututils.h" +#include "coreutils.h" #include "activeproject.h" #include @@ -36,8 +37,8 @@ void TestActiveProject::testProjectValidations() QString projectFilename = "bad_layer.qgz"; AppSettings as; - ActiveLayer al; - ActiveProject activeProject( as, al, mApi->localProjectsManager() ); + ActiveLayer activeLayer; + ActiveProject activeProject( as, activeLayer, mApi->localProjectsManager() ); QSignalSpy spyReportIssues( &activeProject, &ActiveProject::reportIssue ); QSignalSpy spyErrorsFound( &activeProject, &ActiveProject::loadingErrorFound ); @@ -61,8 +62,8 @@ void TestActiveProject::testProjectLoadFailure() InputUtils::cpDir( TestUtils::testDataDir() + "/load_failure", projectdir ); AppSettings as; - ActiveLayer al; - ActiveProject activeProject( as, al, mApi->localProjectsManager() ); + ActiveLayer activeLayer; + ActiveProject activeProject( as, activeLayer, mApi->localProjectsManager() ); mApi->localProjectsManager().addLocalProject( projectdir, projectname ); @@ -81,8 +82,8 @@ void TestActiveProject::testPositionTrackingFlag() // the position tracking availability is correctly set AppSettings as; - ActiveLayer al; - ActiveProject activeProject( as, al, mApi->localProjectsManager() ); + ActiveLayer activeLayer; + ActiveProject activeProject( as, activeLayer, mApi->localProjectsManager() ); // project "planes" - tracking not enabled QString projectDir = TestUtils::testDataDir() + "/planes/"; @@ -121,8 +122,8 @@ void TestActiveProject::testRecordingAllowed() QString projectFilename = "tracking-project.qgz"; AppSettings as; - ActiveLayer al; - ActiveProject activeProject( as, al, mApi->localProjectsManager() ); + ActiveLayer activeLayer; + ActiveProject activeProject( as, activeLayer, mApi->localProjectsManager() ); mApi->localProjectsManager().addLocalProject( projectDir, projectFilename ); QVERIFY( activeProject.load( projectDir + "/" + projectFilename ) ); @@ -169,8 +170,8 @@ void TestActiveProject::testRecordingAllowed() void TestActiveProject::testLoadingFlagFileExpiration() { AppSettings as; - ActiveLayer al; - ActiveProject activeProject( as, al, mApi->localProjectsManager() ); + ActiveLayer activeLayer; + ActiveProject activeProject( as, activeLayer, mApi->localProjectsManager() ); // project "planes" - tracking not enabled QString projectDir = TestUtils::testDataDir() + "/planes/"; @@ -191,3 +192,35 @@ void TestActiveProject::testLoadingFlagFileExpiration() QVERIFY( !flagFile.exists() ); } + +void TestActiveProject::testLoadingAuthFileFromConfiguration() +{ + AppSettings appSettings; + ActiveLayer activeLayer; + ActiveProject activeProject( appSettings, activeLayer, mApi->localProjectsManager() ); + QString projectDir = TestUtils::testDataDir() + QStringLiteral( "/project_auth_file/" ); + QString projectName = QStringLiteral( "auth-test.qgz" ); + QString authFile = QDir( projectDir ).filePath( CoreUtils::AUTH_CONFIG_FILENAME ); + + QgsAuthManager *authManager = QgsApplication::authManager(); + + mApi->localProjectsManager().addLocalProject( projectDir, projectName ); + activeProject.load( projectDir + projectName ); + + QSignalSpy spyLoadingStarted( &activeProject, &ActiveProject::loadingStarted ); + + // we expect the configuration import to fail + int count = authManager->configIds().count(); + QCOMPARE( count, 0 ); + + QFileInfo cfgFileInfo( authFile ); + if ( cfgFileInfo.exists() && cfgFileInfo.isFile() ) + { + // we still check that the configuration can be imported + bool ok = authManager->importAuthenticationConfigsFromXml( authFile, AUTH_CONFIG_PASSWORD, true ); + + QCOMPARE( ok, true ); + count = authManager->configIds().count(); + QCOMPARE( count, 1 ); + } +} diff --git a/app/test/testactiveproject.h b/app/test/testactiveproject.h index 428e81bef..d05744ca9 100644 --- a/app/test/testactiveproject.h +++ b/app/test/testactiveproject.h @@ -12,6 +12,10 @@ #include #include +#include +#include + +const QString AUTH_CONFIG_PASSWORD = QStringLiteral( "1234" ); class TestActiveProject : public QObject { @@ -29,6 +33,7 @@ class TestActiveProject : public QObject void testPositionTrackingFlag(); void testRecordingAllowed(); void testLoadingFlagFileExpiration(); + void testLoadingAuthFileFromConfiguration(); private: MerginApi *mApi; diff --git a/cmake/InstallStep.cmake b/cmake/InstallStep.cmake index b25df2627..375064e3e 100644 --- a/cmake/InstallStep.cmake +++ b/cmake/InstallStep.cmake @@ -77,7 +77,7 @@ if (WIN) ) install( DIRECTORY "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/Qca/crypto/" - DESTINATION . + DESTINATION "./Qca/crypto" FILES_MATCHING PATTERN "*.dll" ) diff --git a/core/coreutils.cpp b/core/coreutils.cpp index 976ebf180..a17b38aa8 100644 --- a/core/coreutils.cpp +++ b/core/coreutils.cpp @@ -29,6 +29,7 @@ const QString CoreUtils::QSETTINGS_APP_GROUP_NAME = QStringLiteral( "inputApp" ); const QString CoreUtils::LOG_TO_DEVNULL = QStringLiteral(); const QString CoreUtils::LOG_TO_STDOUT = QStringLiteral( "TO_STDOUT" ); +const QString CoreUtils::AUTH_CONFIG_FILENAME = QStringLiteral( "qgis_cfg.xml" ); QString CoreUtils::sLogFile = CoreUtils::LOG_TO_DEVNULL; int CoreUtils::CHECKSUM_CHUNK_SIZE = 65536; @@ -263,6 +264,11 @@ bool CoreUtils::hasProjectFileExtension( const QString filePath ) return filePath.contains( ".qgs", Qt::CaseInsensitive ) || filePath.contains( ".qgz", Qt::CaseInsensitive ); } +bool CoreUtils::isAuthConfigFile( const QString filePath ) +{ + return filePath == AUTH_CONFIG_FILENAME; +} + bool CoreUtils::isValidName( const QString &name ) { static QRegularExpression reForbiddenmNames( R"([@#$%^&*\(\)\{\}\[\]\\\/\|\+=<>~\?:;,`\'\"]|^[\s^\.].*$|^CON$|^PRN$|^AUX$|^NUL$|^COM\d$|^LPT\d|^support$|^helpdesk$|^merginmaps$|^lutraconsulting$|^mergin$|^lutra$|^input$|^sales$|^admin$)", QRegularExpression::CaseInsensitiveOption ); diff --git a/core/coreutils.h b/core/coreutils.h index 6740a6113..990fbca45 100644 --- a/core/coreutils.h +++ b/core/coreutils.h @@ -74,6 +74,7 @@ class CoreUtils */ static const QString LOG_TO_DEVNULL; static const QString LOG_TO_STDOUT; + static const QString AUTH_CONFIG_FILENAME; static void setLogFilename( const QString &value ); static QString logFilename(); @@ -88,6 +89,9 @@ class CoreUtils //! Checks whether file path has a QGIS project suffix (qgs or qgz) static bool hasProjectFileExtension( const QString filePath ); + // Checks whether file is the authentication configuration + static bool isAuthConfigFile( const QString filePath ); + /** * Check whether given project/user name is valid */ diff --git a/core/merginapi.cpp b/core/merginapi.cpp index b3f8caf6b..ab91e0611 100644 --- a/core/merginapi.cpp +++ b/core/merginapi.cpp @@ -373,6 +373,29 @@ bool MerginApi::projectFileHasBeenUpdated( const ProjectDiff &diff ) return false; } +bool MerginApi::authConfigurationHasChanged( const ProjectDiff &diff ) +{ + for ( QString filePath : diff.remoteAdded ) + { + if ( CoreUtils::isAuthConfigFile( filePath ) ) + return true; + } + + for ( QString filePath : diff.remoteUpdated ) + { + if ( CoreUtils::isAuthConfigFile( filePath ) ) + return true; + } + + for ( QString filePath : diff.remoteDeleted ) + { + if ( CoreUtils::isAuthConfigFile( filePath ) ) + return true; + } + + return false; +} + bool MerginApi::supportsSelectiveSync() const { return mSupportsSelectiveSync; @@ -3691,7 +3714,7 @@ void MerginApi::finishProjectSync( const QString &projectFullName, bool syncSucc ProjectDiff diff = transaction.diff; int newVersion = syncSuccessful ? transaction.version : -1; - if ( transaction.gpkgSchemaChanged || projectFileHasBeenUpdated( diff ) ) + if ( transaction.gpkgSchemaChanged || projectFileHasBeenUpdated( diff ) || authConfigurationHasChanged( diff ) ) { emit projectReloadNeededAfterSync( projectFullName ); } diff --git a/core/merginapi.h b/core/merginapi.h index bcd62322a..202e042c6 100644 --- a/core/merginapi.h +++ b/core/merginapi.h @@ -924,6 +924,8 @@ class MerginApi: public QObject bool projectFileHasBeenUpdated( const ProjectDiff &diff ); + bool authConfigurationHasChanged( const ProjectDiff &diff ); + //! Checks if retrieving the project role from the server was successful and //! if it differs from the current project role, emits a signal with new project role void reloadProjectRoleReplyFinished(); diff --git a/test/test_data/project_auth_file/Survey_points.gpkg b/test/test_data/project_auth_file/Survey_points.gpkg new file mode 100644 index 000000000..823bf9ced Binary files /dev/null and b/test/test_data/project_auth_file/Survey_points.gpkg differ diff --git a/test/test_data/project_auth_file/auth-test.qgz b/test/test_data/project_auth_file/auth-test.qgz new file mode 100644 index 000000000..25c66243e Binary files /dev/null and b/test/test_data/project_auth_file/auth-test.qgz differ diff --git a/test/test_data/project_auth_file/qgis_cfg.xml b/test/test_data/project_auth_file/qgis_cfg.xml new file mode 100644 index 000000000..3cbac7b8a --- /dev/null +++ b/test/test_data/project_auth_file/qgis_cfg.xml @@ -0,0 +1,2 @@ + +21ce1cf53ab351eb229b3ae83f7384b76578dd93d76e3515904213a79dddd46b0ed3e05f5f906fa681e7714af0d0d9dd551735a31bae5ea96333cc9ebe2debc003eebf44e42c1ec5cd9c7214d8a7399dedfd2f89819661615013ec79fb3bce642138f593be11c64f4f1374b274e1b5c887f229190521e7d0a340f97f1a27c78d70f1e3ac28d9b605d0ae07a9832c03ea2778c0629cbbc8e932d5b36b941f6d4eabe0b4d734d10ce41464064da7e2ee14204064a1ddfa7ca9427ad3aa4fd0cc467bab98b12235a867ddc6542935db835b diff --git a/vcpkg/ports/qca/portfile.cmake b/vcpkg/ports/qca/portfile.cmake index ab649bfb1..9e3ff7207 100644 --- a/vcpkg/ports/qca/portfile.cmake +++ b/vcpkg/ports/qca/portfile.cmake @@ -14,7 +14,7 @@ vcpkg_from_github( REF "v${VERSION}" SHA512 - 956d36058db61498c492fc9b5345b45ca75a5e8214fcbf358273dfacdd5980c1394394652536d9332df05f29fc912d0781338bcca403d98de1285bb8b1216402 + 21bbc483f78d8c6b99bf2a4375db6a1bcc8a1a16df01e2295dc6a5b43fa27ccbef39114ee33d456071f712a00aca0ab3bc1bf767df82333c2f98ea35f7d35b45 PATCHES 0001-fix-path-for-vcpkg.patch 0002-fix-build-error.patch diff --git a/vcpkg/ports/qca/vcpkg.json b/vcpkg/ports/qca/vcpkg.json index fb13e41ea..d543715ab 100644 --- a/vcpkg/ports/qca/vcpkg.json +++ b/vcpkg/ports/qca/vcpkg.json @@ -1,6 +1,6 @@ { "name": "qca", - "version": "2.3.9", + "version": "2.3.10", "description": "Qt Cryptographic Architecture (QCA).", "homepage": "https://userbase.kde.org/QCA", "dependencies": [