Skip to content

Commit

Permalink
[FEATURE] Add new authentication method that supports a HTTP header
Browse files Browse the repository at this point in the history
  • Loading branch information
mo-tomcummins-roweit authored and nyalldawson committed Oct 22, 2021
1 parent deba02b commit 6b840a4
Show file tree
Hide file tree
Showing 7 changed files with 495 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/auth/CMakeLists.txt
Expand Up @@ -17,6 +17,7 @@ add_subdirectory(esritoken)
add_subdirectory(identcert)
add_subdirectory(pkipaths)
add_subdirectory(pkipkcs12)
add_subdirectory(apiheader)

if (WITH_OAUTH2_PLUGIN)
add_subdirectory(oauth2)
Expand Down
74 changes: 74 additions & 0 deletions src/auth/apiheader/CMakeLists.txt
@@ -0,0 +1,74 @@
set(AUTH_APIHEADER_SRCS
core/qgsauthapiheadermethod.cpp
)

set(AUTH_APIHEADER_HDRS
core/qgsauthapiheadermethod.h
)

set(AUTH_APIHEADER_UIS_H "")

if (WITH_GUI)
set(AUTH_APIHEADER_SRCS ${AUTH_APIHEADER_SRCS}
gui/qgsauthapiheaderedit.cpp
)
set(AUTH_APIHEADER_HDRS ${AUTH_APIHEADER_HDRS}
gui/qgsauthapiheaderedit.h
)
set(AUTH_APIHEADER_UIS gui/qgsauthapiheaderedit.ui)
if (WITH_QT6)
QT6_WRAP_UI(AUTH_APIHEADER_UIS_H ${AUTH_APIHEADER_UIS})
else()
QT5_WRAP_UI(AUTH_APIHEADER_UIS_H ${AUTH_APIHEADER_UIS})
endif()
endif()


# static library
add_library(authmethod_apiheader_a STATIC ${AUTH_APIHEADER_SRCS} ${AUTH_APIHEADER_HDRS} ${AUTH_APIHEADER_UIS_H})

target_include_directories(authmethod_apiheader_a PUBLIC ${CMAKE_SOURCE_DIR}/src/auth/apiheader/core)

# require c++17
target_compile_features(authmethod_apiheader_a PRIVATE cxx_std_17)

target_link_libraries(authmethod_apiheader_a qgis_core)

if (WITH_GUI)
target_include_directories(authmethod_apiheader_a PRIVATE
${CMAKE_SOURCE_DIR}/src/auth/apiheader/gui
${CMAKE_BINARY_DIR}/src/auth/apiheader
)
target_link_libraries (authmethod_apiheader_a qgis_gui)
endif()

target_compile_definitions(authmethod_apiheader_a PRIVATE "-DQT_NO_FOREACH")

if (FORCE_STATIC_LIBS)
# for (external) mobile apps to be able to pick up provider for linking
install (TARGETS authmethod_apiheader_a ARCHIVE DESTINATION ${QGIS_PLUGIN_DIR})
else()
# dynamically loaded module
add_library(authmethod_apiheader MODULE ${AUTH_APIHEADER_SRCS} ${AUTH_APIHEADER_HDRS} ${AUTH_APIHEADER_UIS_H})

# require c++17
target_compile_features(authmethod_apiheader PRIVATE cxx_std_17)

target_link_libraries(authmethod_apiheader qgis_core)

if (WITH_GUI)
target_include_directories(authmethod_apiheader PRIVATE
${CMAKE_SOURCE_DIR}/src/auth/apiheader/gui
${CMAKE_BINARY_DIR}/src/auth/apiheader
)
target_link_libraries (authmethod_apiheader qgis_gui)
add_dependencies(authmethod_apiheader ui)
endif()

target_compile_definitions(authmethod_apiheader PRIVATE "-DQT_NO_FOREACH")

install (TARGETS authmethod_apiheader
RUNTIME DESTINATION ${QGIS_PLUGIN_DIR}
LIBRARY DESTINATION ${QGIS_PLUGIN_DIR}
)
endif()
157 changes: 157 additions & 0 deletions src/auth/apiheader/core/qgsauthapiheadermethod.cpp
@@ -0,0 +1,157 @@
/***************************************************************************
qgsauthapiheadermethod.cpp
--------------------------
begin : October 2021
copyright : (C) 2021 by Nyall Dawson
author : Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsauthapiheadermethod.h"

#include "qgsauthmanager.h"
#include "qgslogger.h"
#include "qgsapplication.h"

#ifdef HAVE_GUI
#include "qgsauthapiheaderedit.h"
#endif

#include <QNetworkProxy>
#include <QMutexLocker>
#include <QUuid>

const QString QgsAuthApiHeaderMethod::AUTH_METHOD_KEY = QStringLiteral( "APIHeader" );
const QString QgsAuthApiHeaderMethod::AUTH_METHOD_DESCRIPTION = QStringLiteral( "API Header" );
const QString QgsAuthApiHeaderMethod::AUTH_METHOD_DISPLAY_DESCRIPTION = tr( "API Header" );

QMap<QString, QgsAuthMethodConfig> QgsAuthApiHeaderMethod::sAuthConfigCache = QMap<QString, QgsAuthMethodConfig>();


QgsAuthApiHeaderMethod::QgsAuthApiHeaderMethod()
{
setVersion( 2 );
setExpansions( QgsAuthMethod::NetworkRequest );
setDataProviders( QStringList()
<< QStringLiteral( "wms" ) );
}

QString QgsAuthApiHeaderMethod::key() const
{
return AUTH_METHOD_KEY;
}

QString QgsAuthApiHeaderMethod::description() const
{
return AUTH_METHOD_DESCRIPTION;
}

QString QgsAuthApiHeaderMethod::displayDescription() const
{
return AUTH_METHOD_DISPLAY_DESCRIPTION;
}

bool QgsAuthApiHeaderMethod::updateNetworkRequest( QNetworkRequest &request, const QString &authcfg,
const QString &dataprovider )
{
Q_UNUSED( dataprovider )
const QgsAuthMethodConfig mconfig = getMethodConfig( authcfg );
if ( !mconfig.isValid() )
{
QgsDebugMsg( QStringLiteral( "Update request config FAILED for authcfg: %1: config invalid" ).arg( authcfg ) );
return false;
}

const QString headerKey = mconfig.config( QStringLiteral( "headerKey" ) );
const QString headerValue = mconfig.config( QStringLiteral( "headerValue" ) );

if ( !headerKey.isEmpty() && !headerValue.isEmpty() )
{
request.setRawHeader( QStringLiteral( "%1" ).arg( headerKey ).toLocal8Bit(), QStringLiteral( "%1" ).arg( headerValue ).toLocal8Bit() );
}
return true;
}

void QgsAuthApiHeaderMethod::clearCachedConfig( const QString &authcfg )
{
removeMethodConfig( authcfg );
}

void QgsAuthApiHeaderMethod::updateMethodConfig( QgsAuthMethodConfig &mconfig )
{
if ( mconfig.hasConfig( QStringLiteral( "oldconfigstyle" ) ) )
{
QgsDebugMsg( QStringLiteral( "Updating old style auth method config" ) );
}

// NOTE: add updates as method version() increases due to config storage changes
}

QgsAuthMethodConfig QgsAuthApiHeaderMethod::getMethodConfig( const QString &authcfg, bool fullconfig )
{
const QMutexLocker locker( &mMutex );
QgsAuthMethodConfig mconfig;

// check if it is cached
if ( sAuthConfigCache.contains( authcfg ) )
{
mconfig = sAuthConfigCache.value( authcfg );
QgsDebugMsg( QStringLiteral( "Retrieved config for authcfg: %1" ).arg( authcfg ) );
return mconfig;
}

// else build basic bundle
if ( !QgsApplication::authManager()->loadAuthenticationConfig( authcfg, mconfig, fullconfig ) )
{
QgsDebugMsg( QStringLiteral( "Retrieve config FAILED for authcfg: %1" ).arg( authcfg ) );
return QgsAuthMethodConfig();
}

// cache bundle
putMethodConfig( authcfg, mconfig );

return mconfig;
}

void QgsAuthApiHeaderMethod::putMethodConfig( const QString &authcfg, const QgsAuthMethodConfig &mconfig )
{
const QMutexLocker locker( &mMutex );
QgsDebugMsg( QStringLiteral( "Putting token config for authcfg: %1" ).arg( authcfg ) );
sAuthConfigCache.insert( authcfg, mconfig );
}

void QgsAuthApiHeaderMethod::removeMethodConfig( const QString &authcfg )
{
const QMutexLocker locker( &mMutex );
if ( sAuthConfigCache.contains( authcfg ) )
{
sAuthConfigCache.remove( authcfg );
QgsDebugMsg( QStringLiteral( "Removed token config for authcfg: %1" ).arg( authcfg ) );
}
}

#ifdef HAVE_GUI
QWidget *QgsAuthApiHeaderMethod::editWidget( QWidget *parent ) const
{
return new QgsAuthApiHeaderEdit( parent );
}
#endif

//////////////////////////////////////////////
// Plugin externals
//////////////////////////////////////////////


#ifndef HAVE_STATIC_PROVIDERS
QGISEXTERN QgsAuthMethodMetadata *authMethodMetadataFactory()
{
return new QgsAuthApiHeaderMethodMetadata();
}
#endif
78 changes: 78 additions & 0 deletions src/auth/apiheader/core/qgsauthapiheadermethod.h
@@ -0,0 +1,78 @@
/***************************************************************************
qgsauthapiheadermethod.h
---------------------
begin : October 2021
copyright : (C) 2021 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSAUTHAPIHEADERMETHOD_H
#define QGSAUTHAPIHEADERMETHOD_H

#include <QObject>
#include <QMutex>

#include "qgsauthconfig.h"
#include "qgsauthmethod.h"
#include "qgsauthmethodmetadata.h"


class QgsAuthApiHeaderMethod : public QgsAuthMethod
{
Q_OBJECT

public:

static const QString AUTH_METHOD_KEY;
static const QString AUTH_METHOD_DESCRIPTION;
static const QString AUTH_METHOD_DISPLAY_DESCRIPTION;

explicit QgsAuthApiHeaderMethod();

// QgsAuthMethod interface
QString key() const override;

QString description() const override;

QString displayDescription() const override;

bool updateNetworkRequest( QNetworkRequest &request, const QString &authcfg,
const QString &dataprovider = QString() ) override;

void clearCachedConfig( const QString &authcfg ) override;
void updateMethodConfig( QgsAuthMethodConfig &mconfig ) override;

#ifdef HAVE_GUI
QWidget *editWidget( QWidget *parent )const override;
#endif

private:
QgsAuthMethodConfig getMethodConfig( const QString &authcfg, bool fullconfig = true );

void putMethodConfig( const QString &authcfg, const QgsAuthMethodConfig &mconfig );

void removeMethodConfig( const QString &authcfg );

static QMap<QString, QgsAuthMethodConfig> sAuthConfigCache;

};


class QgsAuthApiHeaderMethodMetadata : public QgsAuthMethodMetadata
{
public:
QgsAuthApiHeaderMethodMetadata()
: QgsAuthMethodMetadata( QgsAuthApiHeaderMethod::AUTH_METHOD_KEY, QgsAuthApiHeaderMethod::AUTH_METHOD_DESCRIPTION )
{}
QgsAuthApiHeaderMethod *createAuthMethod() const override {return new QgsAuthApiHeaderMethod;}
//QStringList supportedDataProviders() const override;
};

#endif // QGSAUTHAPIHEADERMETHOD_H
74 changes: 74 additions & 0 deletions src/auth/apiheader/gui/qgsauthapiheaderedit.cpp
@@ -0,0 +1,74 @@
/***************************************************************************
qgsauthapiheaderedit.cpp
------------------------
begin : October 2021
copyright : (C) 2021 by Nyall Dawson
author : Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsauthapiheaderedit.h"
#include "ui_qgsauthapiheaderedit.h"


QgsAuthApiHeaderEdit::QgsAuthApiHeaderEdit( QWidget *parent )
: QgsAuthMethodEdit( parent )
{
setupUi( this );
connect( headerKeyEdit, &QLineEdit::textChanged, this, &QgsAuthApiHeaderEdit::textChanged );
}

bool QgsAuthApiHeaderEdit::validateConfig()
{
const bool curvalid = !headerKeyEdit->text().isEmpty();
if ( mValid != curvalid )
{
mValid = curvalid;
emit validityChanged( curvalid );
}
return curvalid;
}

QgsStringMap QgsAuthApiHeaderEdit::configMap() const
{
QgsStringMap config;
config.insert( QStringLiteral( "headerKey" ), headerKeyEdit->text() );
config.insert( QStringLiteral( "headerValue" ), headerValueEdit->toPlainText() );

return config;
}

void QgsAuthApiHeaderEdit::loadConfig( const QgsStringMap &configmap )
{
clearConfig();

mConfigMap = configmap;
headerKeyEdit->setText( configmap.value( QStringLiteral( "headerKey" ) ) );
headerValueEdit->setPlainText( configmap.value( QStringLiteral( "headerValue" ) ) );

validateConfig();
}

void QgsAuthApiHeaderEdit::resetConfig()
{
loadConfig( mConfigMap );
}

void QgsAuthApiHeaderEdit::clearConfig()
{
headerKeyEdit->clear();
headerValueEdit->clear();
}

void QgsAuthApiHeaderEdit::textChanged( const QString &txt )
{
Q_UNUSED( txt )
validateConfig();
}

0 comments on commit 6b840a4

Please sign in to comment.