diff --git a/python/PyQt6/core/auto_generated/qgsmaplayer.sip.in b/python/PyQt6/core/auto_generated/qgsmaplayer.sip.in index 73545c5cd377..f185105e38a3 100644 --- a/python/PyQt6/core/auto_generated/qgsmaplayer.sip.in +++ b/python/PyQt6/core/auto_generated/qgsmaplayer.sip.in @@ -567,13 +567,14 @@ or other problem. Child classes set this flag when initialized. :return: ``True`` if the layer is valid and can be accessed %End + QString publicSource( bool hidePassword = false ) const; %Docstring Gets a version of the internal layer definition that has sensitive bits removed (for example, the password). This function should be used when displaying the source name for general viewing. -:param hidePassword: False, if the password should be removed or replaced by an arbitrary string, since QGIS 3.34 +:param hidePassword: ``True`` to replace the value of credentials with 'xxxxxxxx', ``False`` to completely remove credentials (key and value). Since QGIS 3.34 .. seealso:: :py:func:`source` %End diff --git a/python/core/auto_generated/qgsmaplayer.sip.in b/python/core/auto_generated/qgsmaplayer.sip.in index cfa3b903874f..41b536975391 100644 --- a/python/core/auto_generated/qgsmaplayer.sip.in +++ b/python/core/auto_generated/qgsmaplayer.sip.in @@ -567,13 +567,14 @@ or other problem. Child classes set this flag when initialized. :return: ``True`` if the layer is valid and can be accessed %End + QString publicSource( bool hidePassword = false ) const; %Docstring Gets a version of the internal layer definition that has sensitive bits removed (for example, the password). This function should be used when displaying the source name for general viewing. -:param hidePassword: False, if the password should be removed or replaced by an arbitrary string, since QGIS 3.34 +:param hidePassword: ``True`` to replace the value of credentials with 'xxxxxxxx', ``False`` to completely remove credentials (key and value). Since QGIS 3.34 .. seealso:: :py:func:`source` %End diff --git a/src/core/qgsmaplayer.cpp b/src/core/qgsmaplayer.cpp index e1cfb6040556..d4b31630664d 100644 --- a/src/core/qgsmaplayer.cpp +++ b/src/core/qgsmaplayer.cpp @@ -473,13 +473,37 @@ QString QgsMapLayer::metadataUrlFormat() const } } -QString QgsMapLayer::publicSource( bool hidePassword ) const +QString QgsMapLayer::publicSource( bool redactCredentials ) const { QGIS_PROTECT_QOBJECT_THREAD_ACCESS + QString safeName = mDataSource; + + if ( providerType() == QLatin1String( "gdal" ) ) + { + QVariantMap components = QgsProviderRegistry::instance()->decodeUri( providerType(), safeName ); + QVariantMap credentialOptions = components.value( QStringLiteral( "credentialOptions" ) ).toMap(); + if ( !credentialOptions.empty() ) + { + if ( redactCredentials ) + { + for ( auto it = credentialOptions.begin(); it != credentialOptions.end(); ++it ) + { + it.value() = QStringLiteral( "XXXXXXXX" ); + } + components.insert( QStringLiteral( "credentialOptions" ), credentialOptions ); + } + else + { + components.remove( QStringLiteral( "credentialOptions" ) ); + } + } + safeName = QgsProviderRegistry::instance()->encodeUri( providerType(), components ); + } + // Redo this every time we're asked for it, as we don't know if // dataSource has changed. - QString safeName = QgsDataSourceUri::removePassword( mDataSource, hidePassword ); + safeName = QgsDataSourceUri::removePassword( safeName, redactCredentials ); return safeName; } @@ -3232,12 +3256,14 @@ QString QgsMapLayer::generalHtmlMetadata() const // name metadata += QStringLiteral( "" ) + tr( "Name" ) + QStringLiteral( "" ) + name() + QStringLiteral( "\n" ); + const QString lPublicSource = publicSource(); + QString path; bool isLocalPath = false; if ( dataProvider() ) { // local path - QVariantMap uriComponents = QgsProviderRegistry::instance()->decodeUri( dataProvider()->name(), publicSource() ); + QVariantMap uriComponents = QgsProviderRegistry::instance()->decodeUri( dataProvider()->name(), lPublicSource ); if ( uriComponents.contains( QStringLiteral( "path" ) ) ) { path = uriComponents[QStringLiteral( "path" )].toString(); @@ -3283,8 +3309,8 @@ QString QgsMapLayer::generalHtmlMetadata() const } // data source - if ( publicSource() != path || !isLocalPath ) - metadata += QStringLiteral( "" ) + tr( "Source" ) + QStringLiteral( "%1" ).arg( publicSource() != path ? publicSource() : path ) + QStringLiteral( "\n" ); + if ( lPublicSource != path || !isLocalPath ) + metadata += QStringLiteral( "" ) + tr( "Source" ) + QStringLiteral( "%1" ).arg( lPublicSource != path ? lPublicSource : path ) + QStringLiteral( "\n" ); // provider if ( dataProvider() ) diff --git a/src/core/qgsmaplayer.h b/src/core/qgsmaplayer.h index 19303cb6e212..861b2ffc5c0b 100644 --- a/src/core/qgsmaplayer.h +++ b/src/core/qgsmaplayer.h @@ -573,12 +573,15 @@ class CORE_EXPORT QgsMapLayer : public QObject */ bool isValid() const; + // TODO QGIS 4.0: consider changing bool hidePassword to an enumeration: HIDE_CREDENTIALS / REDACT_CREDENTIALS + // to avoid the ambiguity of the double negation (hide = false) + /** * Gets a version of the internal layer definition that has sensitive - * bits removed (for example, the password). This function should - * be used when displaying the source name for general viewing. - * \param hidePassword False, if the password should be removed or replaced by an arbitrary string, since QGIS 3.34 - * \see source() + * bits removed (for example, the password). This function should + * be used when displaying the source name for general viewing. + * \param hidePassword TRUE to replace the value of credentials with 'xxxxxxxx', FALSE to completely remove credentials (key and value). Since QGIS 3.34 + * \see source() */ QString publicSource( bool hidePassword = false ) const; diff --git a/tests/src/core/testqgsmaplayer.cpp b/tests/src/core/testqgsmaplayer.cpp index 47e6f2d5a6e9..cad52331d752 100644 --- a/tests/src/core/testqgsmaplayer.cpp +++ b/tests/src/core/testqgsmaplayer.cpp @@ -23,6 +23,7 @@ //qgis includes... #include +#include #include #include #include @@ -73,6 +74,8 @@ class TestQgsMapLayer : public QObject void readCustomProperties(); + void publicSourceOnGdalWithCredentials(); + private: QgsVectorLayer *mpLayer = nullptr; }; @@ -510,5 +513,15 @@ void TestQgsMapLayer::readCustomProperties() QCOMPARE( spy.at( 1 ).at( 0 ), "my_property_two" ); } +void TestQgsMapLayer::publicSourceOnGdalWithCredentials() +{ + QgsRasterLayer rl( + QStringLiteral( "test.tif|option:AN=OPTION|credential:SOMEKEY=AAAAA|credential:ANOTHER=BBB" ), QString(), QStringLiteral( "gdal" ) + ); + QCOMPARE( rl.publicSource( true ), QStringLiteral( "test.tif|option:AN=OPTION|credential:ANOTHER=XXXXXXXX|credential:SOMEKEY=XXXXXXXX" ) ); + QCOMPARE( rl.publicSource( false ), QStringLiteral( "test.tif|option:AN=OPTION" ) ); + QCOMPARE( rl.publicSource(), QStringLiteral( "test.tif|option:AN=OPTION" ) ); +} + QGSTEST_MAIN( TestQgsMapLayer ) #include "testqgsmaplayer.moc"