Skip to content
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

[oauth2] Allow for extra OAuth2 token(s) to be added into the headers #60668

Merged
merged 2 commits into from
Mar 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/auth/oauth2/core/qgsauthoauth2config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ QgsAuthOAuth2Config::QgsAuthOAuth2Config( QObject *parent )
connect( this, &QgsAuthOAuth2Config::requestTimeoutChanged, this, &QgsAuthOAuth2Config::configChanged );
connect( this, &QgsAuthOAuth2Config::queryPairsChanged, this, &QgsAuthOAuth2Config::configChanged );
connect( this, &QgsAuthOAuth2Config::customHeaderChanged, this, &QgsAuthOAuth2Config::configChanged );
connect( this, &QgsAuthOAuth2Config::extraTokensChanged, this, &QgsAuthOAuth2Config::configChanged );

// always recheck validity on any change
// this, in turn, may emit validityChanged( bool )
Expand Down Expand Up @@ -230,6 +231,15 @@ void QgsAuthOAuth2Config::setCustomHeader( const QString &header )
emit customHeaderChanged( mCustomHeader );
}

void QgsAuthOAuth2Config::setExtraTokens( const QVariantMap &tokens )
{
if ( mExtraTokens == tokens )
return;

mExtraTokens = tokens;
emit extraTokensChanged( mExtraTokens );
}

void QgsAuthOAuth2Config::setRequestTimeout( int value )
{
const int preval( mRequestTimeout );
Expand Down Expand Up @@ -269,6 +279,7 @@ void QgsAuthOAuth2Config::setToDefaults()
setPersistToken( false );
setAccessMethod( QgsAuthOAuth2Config::AccessMethod::Header );
setCustomHeader( QString() );
setExtraTokens( QVariantMap() );
setRequestTimeout( 30 ); // in seconds
setQueryPairs( QVariantMap() );
}
Expand Down Expand Up @@ -381,6 +392,9 @@ bool QgsAuthOAuth2Config::loadConfigTxt(

if ( variantMap.contains( QStringLiteral( "customHeader" ) ) )
setCustomHeader( variantMap.value( QStringLiteral( "customHeader" ) ).toString() );
if ( variantMap.contains( QStringLiteral( "extraTokens" ) ) )
setExtraTokens( variantMap.value( QStringLiteral( "extraTokens" ) ).toMap() );

if ( variantMap.contains( QStringLiteral( "requestTimeout" ) ) )
setRequestTimeout( variantMap.value( QStringLiteral( "requestTimeout" ) ).toInt() );
if ( variantMap.contains( QStringLiteral( "requestUrl" ) ) )
Expand Down Expand Up @@ -428,6 +442,7 @@ QByteArray QgsAuthOAuth2Config::saveConfigTxt(
variant.insert( "clientSecret", clientSecret() );
variant.insert( "configType", static_cast<int>( configType() ) );
variant.insert( "customHeader", customHeader() );
variant.insert( "extraTokens", extraTokens() );
variant.insert( "description", description() );
variant.insert( "grantFlow", static_cast<int>( grantFlow() ) );
variant.insert( "id", id() );
Expand Down Expand Up @@ -480,6 +495,7 @@ QVariantMap QgsAuthOAuth2Config::mappedProperties() const
vmap.insert( QStringLiteral( "refreshTokenUrl" ), this->refreshTokenUrl() );
vmap.insert( QStringLiteral( "accessMethod" ), static_cast<int>( this->accessMethod() ) );
vmap.insert( QStringLiteral( "customHeader" ), this->customHeader() );
vmap.insert( QStringLiteral( "extraTokens" ), this->extraTokens() );
vmap.insert( QStringLiteral( "requestTimeout" ), this->requestTimeout() );
vmap.insert( QStringLiteral( "requestUrl" ), this->requestUrl() );
vmap.insert( QStringLiteral( "scope" ), this->scope() );
Expand Down
28 changes: 28 additions & 0 deletions src/auth/oauth2/core/qgsauthoauth2config.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class QgsAuthOAuth2Config : public QObject
Q_PROPERTY( int requestTimeout READ requestTimeout WRITE setRequestTimeout NOTIFY requestTimeoutChanged )
Q_PROPERTY( QVariantMap queryPairs READ queryPairs WRITE setQueryPairs NOTIFY queryPairsChanged )
Q_PROPERTY( QString customHeader READ customHeader WRITE setCustomHeader NOTIFY customHeaderChanged )
Q_PROPERTY( QVariantMap extraTokens READ extraTokens WRITE setExtraTokens NOTIFY extraTokensChanged )

//! Construct a QgsAuthOAuth2Config instance
explicit QgsAuthOAuth2Config( QObject *parent = nullptr );
Expand Down Expand Up @@ -165,6 +166,16 @@ class QgsAuthOAuth2Config : public QObject
*/
QString customHeader() const { return mCustomHeader; }

/**
* Returns the extra tokens that will be added into the header for header access methods.
*
* The map key represents the response field to take the token from, and the associated value the header
* name to be used for subsequent requests.
*
* \since QGIS 3.44
*/
QVariantMap extraTokens() const { return mExtraTokens; }

//! Request timeout
int requestTimeout() const { return mRequestTimeout; }

Expand Down Expand Up @@ -316,6 +327,16 @@ class QgsAuthOAuth2Config : public QObject
*/
void setCustomHeader( const QString &header );

/**
* Sets the extra \a tokens that will be added into the header for header access methods.
*
* The map key represents the response field to take the token from, and the associated value the header
* name to be used for subsequent requests.
*
* \since QGIS 3.44
*/
void setExtraTokens( const QVariantMap &tokens );

//! Set request timeout to \a value
void setRequestTimeout( int value );
//! Set query pairs to \a pairs
Expand Down Expand Up @@ -378,6 +399,12 @@ class QgsAuthOAuth2Config : public QObject
*/
void customHeaderChanged( const QString & );

/**
* Emitted when the extra tokens header list has changed
* \since QGIS 3.44
*/
void extraTokensChanged( const QVariantMap & );

//! Emitted when configuration request timeout has changed
void requestTimeoutChanged( int );
//! Emitted when configuration query pair has changed
Expand Down Expand Up @@ -407,6 +434,7 @@ class QgsAuthOAuth2Config : public QObject
bool mPersistToken = false;
AccessMethod mAccessMethod = AccessMethod::Header;
QString mCustomHeader;
QVariantMap mExtraTokens;
int mRequestTimeout = 30; // in seconds
QVariantMap mQueryPairs;
bool mValid = false;
Expand Down
15 changes: 15 additions & 0 deletions src/auth/oauth2/core/qgsauthoauth2method.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,21 @@ bool QgsAuthOAuth2Method::updateNetworkRequest( QNetworkRequest &request, const
{
const QString header = o2->oauth2config()->customHeader().isEmpty() ? QString( O2_HTTP_AUTHORIZATION_HEADER ) : o2->oauth2config()->customHeader();
request.setRawHeader( header.toLatin1(), QStringLiteral( "Bearer %1" ).arg( o2->token() ).toLatin1() );

const QVariantMap extraTokens = o2->oauth2config()->extraTokens();
if ( !extraTokens.isEmpty() )
{
const QVariantMap receivedExtraTokens = o2->extraTokens();
const QStringList extraTokenNames = extraTokens.keys();
for ( const QString &extraTokenName : extraTokenNames )
{
if ( receivedExtraTokens.contains( extraTokenName ) )
{
request.setRawHeader( extraTokens[extraTokenName].toString().replace( '_', '-' ).toLatin1(), receivedExtraTokens[extraTokenName].toString().toLatin1() );
}
}
}

#ifdef QGISDEBUG
msg = QStringLiteral( "Updated request HEADER with access token for authcfg: %1" ).arg( authcfg );
QgsDebugMsgLevel( msg, 2 );
Expand Down
90 changes: 87 additions & 3 deletions src/auth/oauth2/gui/qgsauthoauth2edit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,11 @@ void QgsAuthOAuth2Edit::setupConnections()
connect( spnbxRequestTimeout, static_cast<void ( QSpinBox::* )( int )>( &QSpinBox::valueChanged ), mOAuthConfigCustom.get(), &QgsAuthOAuth2Config::setRequestTimeout );

connect( mTokenHeaderLineEdit, &QLineEdit::textChanged, mOAuthConfigCustom.get(), &QgsAuthOAuth2Config::setCustomHeader );
connect( mExtraTokensTable, &QTableWidget::cellChanged, mOAuthConfigCustom.get(), [=]( int, int ) {
mOAuthConfigCustom->setExtraTokens( extraTokens() );
} );
connect( mAddExtraTokenButton, &QToolButton::clicked, this, &QgsAuthOAuth2Edit::addExtraToken );
connect( mRemoveExtraTokenButton, &QToolButton::clicked, this, &QgsAuthOAuth2Edit::removeExtraToken );

connect( mOAuthConfigCustom.get(), &QgsAuthOAuth2Config::validityChanged, this, &QgsAuthOAuth2Edit::configValidityChanged );

Expand Down Expand Up @@ -403,6 +408,8 @@ void QgsAuthOAuth2Edit::loadFromOAuthConfig( const QgsAuthOAuth2Config *config )
leApiKey->setText( config->apiKey() );
mTokenHeaderLineEdit->setText( config->customHeader() );

populateExtraTokens( config->extraTokens() );

// advanced
chkbxTokenPersist->setChecked( config->persistToken() );
cmbbxAccessMethod->setCurrentIndex( static_cast<int>( config->accessMethod() ) );
Expand Down Expand Up @@ -873,11 +880,19 @@ void QgsAuthOAuth2Edit::updateConfigAccessMethod( int indx )
case QgsAuthOAuth2Config::AccessMethod::Header:
mTokenHeaderLineEdit->setVisible( true );
mTokenHeaderLabel->setVisible( true );
mExtraTokensHeaderLabel->setVisible( true );
mExtraTokensTable->setVisible( true );
mAddExtraTokenButton->setVisible( true );
mAddExtraTokenButton->setVisible( true );
break;
case QgsAuthOAuth2Config::AccessMethod::Form:
case QgsAuthOAuth2Config::AccessMethod::Query:
mTokenHeaderLineEdit->setVisible( false );
mTokenHeaderLabel->setVisible( false );
mExtraTokensHeaderLabel->setVisible( false );
mExtraTokensTable->setVisible( false );
mAddExtraTokenButton->setVisible( false );
mAddExtraTokenButton->setVisible( false );
break;
}
}
Expand Down Expand Up @@ -915,7 +930,6 @@ void QgsAuthOAuth2Edit::populateQueryPairs( const QVariantMap &querypairs, bool
}
}


void QgsAuthOAuth2Edit::queryTableSelectionChanged()
{
const bool hassel = tblwdgQueryPairs->selectedItems().count() > 0;
Expand All @@ -941,7 +955,6 @@ QVariantMap QgsAuthOAuth2Edit::queryPairs() const
return querypairs;
}


void QgsAuthOAuth2Edit::addQueryPair()
{
addQueryPairRow( QString(), QString() );
Expand All @@ -956,7 +969,6 @@ void QgsAuthOAuth2Edit::removeQueryPair()
tblwdgQueryPairs->removeRow( tblwdgQueryPairs->currentRow() );
}


void QgsAuthOAuth2Edit::clearQueryPairs()
{
for ( int i = tblwdgQueryPairs->rowCount(); i > 0; --i )
Expand All @@ -965,6 +977,78 @@ void QgsAuthOAuth2Edit::clearQueryPairs()
}
}

QVariantMap QgsAuthOAuth2Edit::extraTokens() const
{
QVariantMap extraTokens;
for ( int i = 0; i < mExtraTokensTable->rowCount(); ++i )
{
if ( mExtraTokensTable->item( i, 0 )->text().isEmpty() || mExtraTokensTable->item( i, 1 )->text().isEmpty() )
{
continue;
}
extraTokens.insert( mExtraTokensTable->item( i, 1 )->text(), QVariant( mExtraTokensTable->item( i, 0 )->text() ) );
}
return extraTokens;
}

void QgsAuthOAuth2Edit::addExtraToken()
{
mExtraTokensTable->blockSignals( true );
addExtraTokenRow( QString(), QString() );
mExtraTokensTable->blockSignals( false );

mExtraTokensTable->setFocus();
mExtraTokensTable->setCurrentCell( mExtraTokensTable->rowCount() - 1, 0 );
mExtraTokensTable->edit( mExtraTokensTable->currentIndex() );
}

void QgsAuthOAuth2Edit::removeExtraToken()
{
mExtraTokensTable->removeRow( mExtraTokensTable->currentRow() );
}

void QgsAuthOAuth2Edit::clearExtraTokens()
{
for ( int i = mExtraTokensTable->rowCount(); i > 0; --i )
{
mExtraTokensTable->removeRow( i - 1 );
}
}

void QgsAuthOAuth2Edit::addExtraTokenRow( const QString &key, const QString &val )
{
const int rowCnt = mExtraTokensTable->rowCount();
mExtraTokensTable->insertRow( rowCnt );

const Qt::ItemFlags itmFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable
| Qt::ItemIsEditable | Qt::ItemIsDropEnabled;

QTableWidgetItem *keyItm = new QTableWidgetItem( key );
keyItm->setFlags( itmFlags );
mExtraTokensTable->setItem( rowCnt, 0, keyItm );

QTableWidgetItem *valItm = new QTableWidgetItem( val );
keyItm->setFlags( itmFlags );
mExtraTokensTable->setItem( rowCnt, 1, valItm );
}

void QgsAuthOAuth2Edit::populateExtraTokens( const QVariantMap &tokens, bool append )
{
mExtraTokensTable->blockSignals( true );
if ( !append )
{
clearExtraTokens();
}

QVariantMap::const_iterator i = tokens.constBegin();
while ( i != tokens.constEnd() )
{
addExtraTokenRow( i.value().toString(), i.key() );
++i;
}
mExtraTokensTable->blockSignals( false );
}

void QgsAuthOAuth2Edit::parseSoftwareStatement( const QString &path )
{
QFile file( path );
Expand Down
11 changes: 11 additions & 0 deletions src/auth/oauth2/gui/qgsauthoauth2edit.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ class QgsAuthOAuth2Edit : public QgsAuthMethodEdit, private Ui::QgsAuthOAuth2Edi

void populateQueryPairs( const QVariantMap &querypairs, bool append = false );

void addExtraToken();

void removeExtraToken();

void clearExtraTokens();

void populateExtraTokens( const QVariantMap &tokens, bool append = false );

void queryTableSelectionChanged();

void updateConfigQueryPairs();
Expand Down Expand Up @@ -139,6 +147,9 @@ class QgsAuthOAuth2Edit : public QgsAuthMethodEdit, private Ui::QgsAuthOAuth2Edi
void addQueryPairRow( const QString &key, const QString &val );
QVariantMap queryPairs() const;

void addExtraTokenRow( const QString &key, const QString &val );
QVariantMap extraTokens() const;

int customTab() const { return 0; }
int definedTab() const { return 1; }
int statementTab() const { return 2; }
Expand Down
Loading