Skip to content

Commit

Permalink
Merge pull request #60417 from qgis/backport-60402-to-release-3_40
Browse files Browse the repository at this point in the history
[Backport release-3_40] Move provider-specific credential redaction logic to QgsProviderMetadata
  • Loading branch information
alexbruy authored Feb 5, 2025
2 parents b962090 + 2df47de commit 5243170
Show file tree
Hide file tree
Showing 12 changed files with 136 additions and 26 deletions.
16 changes: 16 additions & 0 deletions python/PyQt6/core/auto_additions/qgis.py
Original file line number Diff line number Diff line change
Expand Up @@ -2549,6 +2549,22 @@
# --
Qgis.FileFilterType.baseClass = Qgis
# monkey patching scoped based enum
Qgis.UriCleaningFlag.RemoveCredentials.__doc__ = "Completely remove credentials (eg passwords) from the URI. This flag is not compatible with the RedactCredentials flag."
Qgis.UriCleaningFlag.RedactCredentials.__doc__ = "Replace the value of credentials (eg passwords) with 'xxxxxxxx'. This flag is not compatible with the RemoveCredentials flag."
Qgis.UriCleaningFlag.__doc__ = """Flags for cleaning layer URIs.

.. versionadded:: 3.42

* ``RemoveCredentials``: Completely remove credentials (eg passwords) from the URI. This flag is not compatible with the RedactCredentials flag.
* ``RedactCredentials``: Replace the value of credentials (eg passwords) with 'xxxxxxxx'. This flag is not compatible with the RemoveCredentials flag.

"""
# --
Qgis.UriCleaningFlag.baseClass = Qgis
Qgis.UriCleaningFlags = lambda flags=0: Qgis.UriCleaningFlag(flags)
Qgis.UriCleaningFlags.baseClass = Qgis
UriCleaningFlags = Qgis # dirty hack since SIP seems to introduce the flags in module
# monkey patching scoped based enum
Qgis.SublayerQueryFlag.FastScan.__doc__ = "Indicates that the provider must scan for sublayers using the fastest possible approach -- e.g. by first checking that a uri has an extension which is known to be readable by the provider"
Qgis.SublayerQueryFlag.ResolveGeometryType.__doc__ = "Attempt to resolve the geometry type for vector sublayers"
Qgis.SublayerQueryFlag.CountFeatures.__doc__ = "Count features in vector sublayers"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,13 @@ If a provider does not work with paths, unmodified URI will be returned.
.. seealso:: :py:func:`absoluteToRelativeUri`

.. versionadded:: 3.30
%End

virtual QString cleanUri( const QString &uri, Qgis::UriCleaningFlags flags = Qgis::UriCleaningFlag::RemoveCredentials ) const;
%Docstring
Cleans a layer ``uri``, e.g. to remove or hide sensitive information from the URI.

.. versionadded:: 3.42
%End

virtual QList< QgsDataItemProvider * > dataItemProviders() const /Factory/;
Expand Down
11 changes: 11 additions & 0 deletions python/PyQt6/core/auto_generated/qgis.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,15 @@ The development version
TiledScene,
};

enum class UriCleaningFlag /BaseType=IntFlag/
{
RemoveCredentials,
RedactCredentials,
};

typedef QFlags<Qgis::UriCleaningFlag> UriCleaningFlags;


enum class SublayerQueryFlag /BaseType=IntFlag/
{
FastScan,
Expand Down Expand Up @@ -3313,6 +3322,8 @@ QFlags<Qgis::SnappingType> operator|(Qgis::SnappingType f1, QFlags<Qgis::Snappin

QFlags<Qgis::SqlLayerDefinitionCapability> operator|(Qgis::SqlLayerDefinitionCapability f1, QFlags<Qgis::SqlLayerDefinitionCapability> f2);

QFlags<Qgis::UriCleaningFlag> operator|(Qgis::UriCleaningFlag f1, QFlags<Qgis::UriCleaningFlag> f2);

QFlags<Qgis::SublayerFlag> operator|(Qgis::SublayerFlag f1, QFlags<Qgis::SublayerFlag> f2);

QFlags<Qgis::SublayerQueryFlag> operator|(Qgis::SublayerQueryFlag f1, QFlags<Qgis::SublayerQueryFlag> f2);
Expand Down
15 changes: 15 additions & 0 deletions python/core/auto_additions/qgis.py
Original file line number Diff line number Diff line change
Expand Up @@ -2521,6 +2521,21 @@
# --
Qgis.FileFilterType.baseClass = Qgis
# monkey patching scoped based enum
Qgis.UriCleaningFlag.RemoveCredentials.__doc__ = "Completely remove credentials (eg passwords) from the URI. This flag is not compatible with the RedactCredentials flag."
Qgis.UriCleaningFlag.RedactCredentials.__doc__ = "Replace the value of credentials (eg passwords) with 'xxxxxxxx'. This flag is not compatible with the RemoveCredentials flag."
Qgis.UriCleaningFlag.__doc__ = """Flags for cleaning layer URIs.

.. versionadded:: 3.42

* ``RemoveCredentials``: Completely remove credentials (eg passwords) from the URI. This flag is not compatible with the RedactCredentials flag.
* ``RedactCredentials``: Replace the value of credentials (eg passwords) with 'xxxxxxxx'. This flag is not compatible with the RemoveCredentials flag.

"""
# --
Qgis.UriCleaningFlag.baseClass = Qgis
Qgis.UriCleaningFlags.baseClass = Qgis
UriCleaningFlags = Qgis # dirty hack since SIP seems to introduce the flags in module
# monkey patching scoped based enum
Qgis.SublayerQueryFlag.FastScan.__doc__ = "Indicates that the provider must scan for sublayers using the fastest possible approach -- e.g. by first checking that a uri has an extension which is known to be readable by the provider"
Qgis.SublayerQueryFlag.ResolveGeometryType.__doc__ = "Attempt to resolve the geometry type for vector sublayers"
Qgis.SublayerQueryFlag.CountFeatures.__doc__ = "Count features in vector sublayers"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,13 @@ If a provider does not work with paths, unmodified URI will be returned.
.. seealso:: :py:func:`absoluteToRelativeUri`

.. versionadded:: 3.30
%End

virtual QString cleanUri( const QString &uri, Qgis::UriCleaningFlags flags = Qgis::UriCleaningFlag::RemoveCredentials ) const;
%Docstring
Cleans a layer ``uri``, e.g. to remove or hide sensitive information from the URI.

.. versionadded:: 3.42
%End

virtual QList< QgsDataItemProvider * > dataItemProviders() const /Factory/;
Expand Down
11 changes: 11 additions & 0 deletions python/core/auto_generated/qgis.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,15 @@ The development version
TiledScene,
};

enum class UriCleaningFlag
{
RemoveCredentials,
RedactCredentials,
};

typedef QFlags<Qgis::UriCleaningFlag> UriCleaningFlags;


enum class SublayerQueryFlag
{
FastScan,
Expand Down Expand Up @@ -3313,6 +3322,8 @@ QFlags<Qgis::SnappingType> operator|(Qgis::SnappingType f1, QFlags<Qgis::Snappin

QFlags<Qgis::SqlLayerDefinitionCapability> operator|(Qgis::SqlLayerDefinitionCapability f1, QFlags<Qgis::SqlLayerDefinitionCapability> f2);

QFlags<Qgis::UriCleaningFlag> operator|(Qgis::UriCleaningFlag f1, QFlags<Qgis::UriCleaningFlag> f2);

QFlags<Qgis::SublayerFlag> operator|(Qgis::SublayerFlag f1, QFlags<Qgis::SublayerFlag> f2);

QFlags<Qgis::SublayerQueryFlag> operator|(Qgis::SublayerQueryFlag f1, QFlags<Qgis::SublayerQueryFlag> f2);
Expand Down
23 changes: 23 additions & 0 deletions src/core/providers/gdal/qgsgdalprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2720,6 +2720,29 @@ QString QgsGdalProviderMetadata::relativeToAbsoluteUri( const QString &uri, cons
return context.pathResolver().readPath( src );
}

QString QgsGdalProviderMetadata::cleanUri( const QString &uri, Qgis::UriCleaningFlags flags ) const
{
QVariantMap components = decodeUri( uri );
QVariantMap credentialOptions = components.value( QStringLiteral( "credentialOptions" ) ).toMap();
if ( !credentialOptions.empty() )
{
if ( flags.testFlag( Qgis::UriCleaningFlag::RedactCredentials ) )
{
for ( auto it = credentialOptions.begin(); it != credentialOptions.end(); ++it )
{
it.value() = QStringLiteral( "XXXXXXXX" );
}
components.insert( QStringLiteral( "credentialOptions" ), credentialOptions );
}
else if ( flags.testFlag( Qgis::UriCleaningFlag::RemoveCredentials ) )
{
components.remove( QStringLiteral( "credentialOptions" ) );
}
}

return encodeUri( components );
}

bool QgsGdalProviderMetadata::uriIsBlocklisted( const QString &uri ) const
{
const QVariantMap parts = decodeUri( uri );
Expand Down
1 change: 1 addition & 0 deletions src/core/providers/gdal/qgsgdalprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ class QgsGdalProviderMetadata final: public QgsProviderMetadata
QString encodeUri( const QVariantMap &parts ) const override;
QString absoluteToRelativeUri( const QString &uri, const QgsReadWriteContext &context ) const override;
QString relativeToAbsoluteUri( const QString &uri, const QgsReadWriteContext &context ) const override;
QString cleanUri( const QString &uri, Qgis::UriCleaningFlags flags = Qgis::UriCleaningFlag::RemoveCredentials ) const override;
bool uriIsBlocklisted( const QString &uri ) const override;
QgsGdalProvider *createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags = Qgis::DataProviderReadFlags() ) override;
QgsGdalProvider *createRasterDataProvider(
Expand Down
9 changes: 9 additions & 0 deletions src/core/providers/qgsprovidermetadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,15 @@ QString QgsProviderMetadata::relativeToAbsoluteUri( const QString &uri, const Qg
return context.pathResolver().readPath( uri );
}

QString QgsProviderMetadata::cleanUri( const QString &uri, Qgis::UriCleaningFlags flags ) const
{
if ( flags.testFlag( Qgis::UriCleaningFlag::RemoveCredentials ) )
return QgsDataSourceUri::removePassword( uri );
else if ( flags.testFlag( Qgis::UriCleaningFlag::RedactCredentials ) )
return QgsDataSourceUri::removePassword( uri, true );
return uri;
}

Qgis::VectorExportResult QgsProviderMetadata::createEmptyLayer( const QString &, const QgsFields &,
Qgis::WkbType, const QgsCoordinateReferenceSystem &,
bool, QMap<int, int> &,
Expand Down
7 changes: 7 additions & 0 deletions src/core/providers/qgsprovidermetadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,13 @@ class CORE_EXPORT QgsProviderMetadata : public QObject
*/
virtual QString relativeToAbsoluteUri( const QString &uri, const QgsReadWriteContext &context ) const;

/**
* Cleans a layer \a uri, e.g. to remove or hide sensitive information from the URI.
*
* \since QGIS 3.42
*/
virtual QString cleanUri( const QString &uri, Qgis::UriCleaningFlags flags = Qgis::UriCleaningFlag::RemoveCredentials ) const;

/**
* Returns data item providers. Caller is responsible for ownership of the item providers
* \see QgsProviderGuiMetadata::dataItemGuiProviders()
Expand Down
21 changes: 21 additions & 0 deletions src/core/qgis.h
Original file line number Diff line number Diff line change
Expand Up @@ -1293,6 +1293,26 @@ class CORE_EXPORT Qgis
};
Q_ENUM( FileFilterType )

/**
* Flags for cleaning layer URIs.
*
* \since QGIS 3.42
*/
enum class UriCleaningFlag : int SIP_ENUM_BASETYPE( IntFlag )
{
RemoveCredentials = 1 << 0, //!< Completely remove credentials (eg passwords) from the URI. This flag is not compatible with the RedactCredentials flag.
RedactCredentials = 1 << 1, //!< Replace the value of credentials (eg passwords) with 'xxxxxxxx'. This flag is not compatible with the RemoveCredentials flag.
};
Q_ENUM( UriCleaningFlag )

/**
* Flags for cleaning layer URIs.
*
* \since QGIS 3.42
*/
Q_DECLARE_FLAGS( UriCleaningFlags, UriCleaningFlag )
Q_FLAG( UriCleaningFlags )

/**
* Flags which control how data providers will scan for sublayers in a dataset.
*
Expand Down Expand Up @@ -5721,6 +5741,7 @@ Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::SelectionFlags )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::SettingsTreeNodeOptions )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::SnappingTypes )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::SqlLayerDefinitionCapabilities )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::UriCleaningFlags )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::SublayerFlags )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::SublayerQueryFlags )
Q_DECLARE_OPERATORS_FOR_FLAGS( Qgis::FeatureRendererFlags )
Expand Down
34 changes: 8 additions & 26 deletions src/core/qgsmaplayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -477,34 +477,16 @@ 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.
safeName = QgsDataSourceUri::removePassword( safeName, redactCredentials );
return safeName;
if ( const QgsProviderMetadata *metadata = QgsProviderRegistry::instance()->providerMetadata( providerType() ) )
{
return metadata->cleanUri( mDataSource, redactCredentials ? Qgis::UriCleaningFlag::RedactCredentials : Qgis::UriCleaningFlag::RemoveCredentials );
}
else
{
return QgsDataSourceUri::removePassword( mDataSource, redactCredentials );
}
}

QString QgsMapLayer::source() const
Expand Down

0 comments on commit 5243170

Please sign in to comment.