-
-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[gdal] Allow storing credential key/value credential options in uris #57801
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -27,6 +27,7 @@ | |||||||||||||
#include "qgsgdalproviderbase.h" | ||||||||||||||
#include "qgsgdalutils.h" | ||||||||||||||
#include "qgssettings.h" | ||||||||||||||
#include "qgsmessagelog.h" | ||||||||||||||
|
||||||||||||||
#include <mutex> | ||||||||||||||
#include <QRegularExpression> | ||||||||||||||
|
@@ -290,6 +291,32 @@ GDALDatasetH QgsGdalProviderBase::gdalOpen( const QString &uri, unsigned int nOp | |||||||||||||
option.toUtf8().constData() ); | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
const QString vsiPrefix = parts.value( QStringLiteral( "vsiPrefix" ) ).toString(); | ||||||||||||||
const QString vsiSuffix = parts.value( QStringLiteral( "vsiSuffix" ) ).toString(); | ||||||||||||||
|
||||||||||||||
const QVariantMap credentialOptions = parts.value( QStringLiteral( "credentialOptions" ) ).toMap(); | ||||||||||||||
parts.remove( QStringLiteral( "credentialOptions" ) ); | ||||||||||||||
if ( !credentialOptions.isEmpty() && !vsiPrefix.isEmpty() ) | ||||||||||||||
{ | ||||||||||||||
const thread_local QRegularExpression bucketRx( QStringLiteral( "^(.*?)/" ) ); | ||||||||||||||
const QRegularExpressionMatch bucketMatch = bucketRx.match( parts.value( QStringLiteral( "path" ) ).toString() ); | ||||||||||||||
if ( bucketMatch.hasMatch() ) | ||||||||||||||
{ | ||||||||||||||
const QString bucket = vsiPrefix + bucketMatch.captured( 1 ); | ||||||||||||||
for ( auto it = credentialOptions.constBegin(); it != credentialOptions.constEnd(); ++it ) | ||||||||||||||
{ | ||||||||||||||
#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 6, 0) | ||||||||||||||
VSISetPathSpecificOption( bucket.toLocal8Bit().constData(), it.key().toLocal8Bit().constData(), it.value().toString().toLocal8Bit().constData() ); | ||||||||||||||
#elif GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 5, 0) | ||||||||||||||
VSISetCredential( bucket.toLocal8Bit().constData(), it.key().toLocal8Bit().constData(), it.value().toString().toLocal8Bit().constData() ); | ||||||||||||||
Comment on lines
+309
to
+311
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
#else | ||||||||||||||
( void )bucket; | ||||||||||||||
QgsMessageLog::logMessage( QObject::tr( "Cannot use VSI credential options on GDAL versions earlier than 3.5" ), QStringLiteral( "GDAL" ), Qgis::MessageLevel::Critical ); | ||||||||||||||
#endif | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
const bool modify_OGR_GPKG_FOREIGN_KEY_CHECK = !CPLGetConfigOption( "OGR_GPKG_FOREIGN_KEY_CHECK", nullptr ); | ||||||||||||||
if ( modify_OGR_GPKG_FOREIGN_KEY_CHECK ) | ||||||||||||||
{ | ||||||||||||||
|
@@ -301,8 +328,6 @@ GDALDatasetH QgsGdalProviderBase::gdalOpen( const QString &uri, unsigned int nOp | |||||||||||||
|
||||||||||||||
if ( !hDS ) | ||||||||||||||
{ | ||||||||||||||
const QString vsiPrefix = parts.value( QStringLiteral( "vsiPrefix" ) ).toString(); | ||||||||||||||
const QString vsiSuffix = parts.value( QStringLiteral( "vsiSuffix" ) ).toString(); | ||||||||||||||
if ( vsiSuffix.isEmpty() && QgsGdalUtils::isVsiArchivePrefix( vsiPrefix ) ) | ||||||||||||||
{ | ||||||||||||||
// in the case that a direct path to a vsi supported archive was specified BUT | ||||||||||||||
|
@@ -391,6 +416,7 @@ QVariantMap QgsGdalProviderBase::decodeGdalUri( const QString &uri ) | |||||||||||||
QString layerName; | ||||||||||||||
QString authcfg; | ||||||||||||||
QStringList openOptions; | ||||||||||||||
QVariantMap credentialOptions; | ||||||||||||||
|
||||||||||||||
const thread_local QRegularExpression authcfgRegex( " authcfg='([^']+)'" ); | ||||||||||||||
QRegularExpressionMatch match; | ||||||||||||||
|
@@ -451,13 +477,39 @@ QVariantMap QgsGdalProviderBase::decodeGdalUri( const QString &uri ) | |||||||||||||
break; | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
const thread_local QRegularExpression credentialOptionRegex( QStringLiteral( "\\|credential:([^|]*)" ) ); | ||||||||||||||
const thread_local QRegularExpression credentialOptionKeyValueRegex( QStringLiteral( "(.*?)=(.*)" ) ); | ||||||||||||||
while ( true ) | ||||||||||||||
{ | ||||||||||||||
const QRegularExpressionMatch match = credentialOptionRegex.match( path ); | ||||||||||||||
if ( match.hasMatch() ) | ||||||||||||||
{ | ||||||||||||||
const QRegularExpressionMatch keyValueMatch = credentialOptionKeyValueRegex.match( match.captured( 1 ) ); | ||||||||||||||
if ( keyValueMatch.hasMatch() ) | ||||||||||||||
{ | ||||||||||||||
credentialOptions.insert( keyValueMatch.captured( 1 ), keyValueMatch.captured( 2 ) ); | ||||||||||||||
} | ||||||||||||||
else | ||||||||||||||
{ | ||||||||||||||
credentialOptions.insert( match.captured( 1 ), QString() ); | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not completely sure if we need to handle a credential that is a key without value. I can't think of a case where it is needed currently. Unless this is meant to remove a path specific option. But in that case VSISetPathSpecificOption() should be called with a nullptr value and not an empty string There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I'll remove this support. (I wasn't sure if in future there'd be valueless options added in gdal) |
||||||||||||||
} | ||||||||||||||
path = path.remove( match.capturedStart( 0 ), match.capturedLength( 0 ) ); | ||||||||||||||
} | ||||||||||||||
else | ||||||||||||||
{ | ||||||||||||||
break; | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
QVariantMap uriComponents; | ||||||||||||||
uriComponents.insert( QStringLiteral( "path" ), path ); | ||||||||||||||
uriComponents.insert( QStringLiteral( "layerName" ), layerName ); | ||||||||||||||
if ( !openOptions.isEmpty() ) | ||||||||||||||
uriComponents.insert( QStringLiteral( "openOptions" ), openOptions ); | ||||||||||||||
if ( !credentialOptions.isEmpty() ) | ||||||||||||||
uriComponents.insert( QStringLiteral( "credentialOptions" ), credentialOptions ); | ||||||||||||||
if ( !vsiPrefix.isEmpty() ) | ||||||||||||||
uriComponents.insert( QStringLiteral( "vsiPrefix" ), vsiPrefix ); | ||||||||||||||
if ( !vsiSuffix.isEmpty() ) | ||||||||||||||
|
@@ -494,6 +546,19 @@ QString QgsGdalProviderBase::encodeGdalUri( const QVariantMap &parts ) | |||||||||||||
uri += openOption; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
const QVariantMap credentialOptions = parts.value( QStringLiteral( "credentialOptions" ) ).toMap(); | ||||||||||||||
for ( auto it = credentialOptions.constBegin(); it != credentialOptions.constEnd(); ++it ) | ||||||||||||||
{ | ||||||||||||||
if ( !it.value().toString().isEmpty() ) | ||||||||||||||
{ | ||||||||||||||
uri += QStringLiteral( "|credential:%1=%2" ).arg( it.key(), it.value().toString() ); | ||||||||||||||
} | ||||||||||||||
else | ||||||||||||||
{ | ||||||||||||||
uri += QStringLiteral( "|credential:%1" ).arg( it.key() ); | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
if ( !authcfg.isEmpty() ) | ||||||||||||||
uri += QStringLiteral( " authcfg='%1'" ).arg( authcfg ); | ||||||||||||||
|
||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rouault does this approach look valid to you?