Skip to content

Commit

Permalink
MapTiler HMAC SHA256 auth method
Browse files Browse the repository at this point in the history
  • Loading branch information
vcloarec authored and nyalldawson committed Jan 7, 2022
1 parent 6b6f5eb commit bfa96b0
Show file tree
Hide file tree
Showing 7 changed files with 536 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/auth/CMakeLists.txt
Expand Up @@ -18,6 +18,7 @@ add_subdirectory(identcert)
add_subdirectory(pkipaths)
add_subdirectory(pkipkcs12)
add_subdirectory(apiheader)
add_subdirectory(maptiler_hmacsha256)

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

set(AUTH_MAPTILER_HMACSHA256_HDRS
core/qgsauthmaptilerhmacsha256method.h
)

set(AUTH_MAPTILER_HMACSHA256_UIS_H "")

if (WITH_GUI)
set(AUTH_MAPTILER_HMACSHA256_SRCS ${AUTH_MAPTILER_HMACSHA256_SRCS}
gui/qgsauthmaptilerhmacsha256edit.cpp
)
set(AUTH_MAPTILER_HMACSHA256_HDRS ${AUTH_MAPTILER_HMACSHA256_HDRS}
gui/qgsauthmaptilerhmacsha256edit.h
)
set(AUTH_MAPTILER_HMACSHA256_UIS gui/qgsauthmaptilerhmacsha256edit.ui)
if (WITH_QT6)
QT6_WRAP_UI(AUTH_MAPTILER_HMACSHA256_UIS_H ${AUTH_MAPTILER_HMACSHA256_UIS})
else()
QT5_WRAP_UI(AUTH_MAPTILER_HMACSHA256_UIS_H ${AUTH_MAPTILER_HMACSHA256_UIS})
endif()
endif()


# static library
add_library(authmethod_maptilerhmacsha256_a STATIC ${AUTH_MAPTILER_HMACSHA256_SRCS} ${AUTH_MAPTILER_HMACSHA256_HDRS} ${AUTH_MAPTILER_HMACSHA256_UIS_H})

target_include_directories(authmethod_maptilerhmacsha256_a PUBLIC ${CMAKE_SOURCE_DIR}/src/auth/hmacsha256/core)

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

target_link_libraries(authmethod_maptilerhmacsha256_a qgis_core)

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

target_compile_definitions(authmethod_maptilerhmacsha256_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_maptilerhmacsha256_a ARCHIVE DESTINATION ${QGIS_PLUGIN_DIR})
else()
# dynamically loaded module
add_library(authmethod_maptilerhmacsha256 MODULE ${AUTH_MAPTILER_HMACSHA256_SRCS} ${AUTH_MAPTILER_HMACSHA256_HDRS} ${AUTH_MAPTILER_HMACSHA256_UIS_H})

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

target_link_libraries(authmethod_maptilerhmacsha256 qgis_core)

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

target_compile_definitions(authmethod_maptilerhmacsha256 PRIVATE "-DQT_NO_FOREACH")

install (TARGETS authmethod_maptilerhmacsha256
RUNTIME DESTINATION ${QGIS_PLUGIN_DIR}
LIBRARY DESTINATION ${QGIS_PLUGIN_DIR}
)
endif()
188 changes: 188 additions & 0 deletions src/auth/maptiler_hmacsha256/core/qgsauthmaptilerhmacsha256method.cpp
@@ -0,0 +1,188 @@
/***************************************************************************
qgsauthmaptilerhmacsha256method.cpp
--------------------------
begin : January 2022
copyright : (C) 2022 by Vincent Cloarec
author : Vincent Cloarec
email : vcloarec 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 "qgsauthmaptilerhmacsha256method.h"

#include <QMessageAuthenticationCode>
#include <QUrlQuery>

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

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


const QString QgsAuthMapTilerHmacSha256Method::AUTH_METHOD_KEY = QStringLiteral( "MapTilerHmacSha256" );
const QString QgsAuthMapTilerHmacSha256Method::AUTH_METHOD_DESCRIPTION = QStringLiteral( "MapTiler HMAC-SHA256" );
const QString QgsAuthMapTilerHmacSha256Method::AUTH_METHOD_DISPLAY_DESCRIPTION = tr( "MapTiler HMAC SHA256-Signature" );

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


QgsAuthMapTilerHmacSha256Method::QgsAuthMapTilerHmacSha256Method()
{
setVersion( 1 );
setExpansions( QgsAuthMethod::NetworkRequest );
setDataProviders( QStringList()
<< QStringLiteral( "wms" )
<< QStringLiteral( "vectortile" ) );

}

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

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

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

bool QgsAuthMapTilerHmacSha256Method::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 token = mconfig.config( QStringLiteral( "token" ) );
const QStringList splitToken = token.split( '_' );

if ( splitToken.count() != 2 )
{
QgsDebugMsg( QStringLiteral( "Update request config FAILED for authcfg: %1: config invalid" ).arg( authcfg ) );
return false;
}

const QString key = splitToken.at( 0 );
const QString secret = splitToken.at( 1 );

QUrl url = request.url();
QUrlQuery query( url.query() );
query.removeQueryItem( QStringLiteral( "key" ) );

QList<QPair<QString, QString> > queryItems = query.queryItems();

queryItems.append( {QStringLiteral( "key" ), key} );

query.setQueryItems( queryItems );
url.setQuery( query );

QString signature = calculateSignature( secret, url.url() );
request.setUrl( QString( url.url() + QStringLiteral( "&signature=" ) + signature ) );

return true;
}

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

void QgsAuthMapTilerHmacSha256Method::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 QgsAuthMapTilerHmacSha256Method::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 QgsAuthMapTilerHmacSha256Method::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 QgsAuthMapTilerHmacSha256Method::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 ) );
}
}

QByteArray QgsAuthMapTilerHmacSha256Method::calculateSignature( const QString &token, const QString &keyedUrl )
{
QByteArray decodedToken = QByteArray::fromHex( token.toStdString().c_str() );

QMessageAuthenticationCode authCode( QCryptographicHash::Sha256, decodedToken );
authCode.addData( keyedUrl.toUtf8() );

return authCode.result().toBase64( QByteArray::Base64UrlEncoding );
}

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

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


#ifndef HAVE_STATIC_PROVIDERS
QGISEXTERN QgsAuthMethodMetadata *authMethodMetadataFactory()
{
return new QgsAuthMapTilerHmacSha256MethodMetadata();
}
#endif
@@ -0,0 +1,80 @@
/***************************************************************************
qgsauthmaptilerhmacsha256method.h
---------------------
begin : January 2022
copyright : (C) 2022 by Vincent Cloarec
author : Vincent Cloarec
email : vcloarec 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 QGSAUTHHMACSHA256METHOD_H
#define QGSAUTHHMACSHA256METHOD_H

#include <QObject>
#include <QMutex>

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


class QgsAuthMapTilerHmacSha256Method : 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 QgsAuthMapTilerHmacSha256Method();

// 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 );

QByteArray calculateSignature( const QString &token, const QString &keyedUrl );

static QMap<QString, QgsAuthMethodConfig> sAuthConfigCache;

};


class QgsAuthMapTilerHmacSha256MethodMetadata : public QgsAuthMethodMetadata
{
public:
QgsAuthMapTilerHmacSha256MethodMetadata()
: QgsAuthMethodMetadata( QgsAuthMapTilerHmacSha256Method::AUTH_METHOD_KEY, QgsAuthMapTilerHmacSha256Method::AUTH_METHOD_DESCRIPTION )
{}
QgsAuthMapTilerHmacSha256Method *createAuthMethod() const override {return new QgsAuthMapTilerHmacSha256Method;}
};

#endif // QGSAUTHHMACSHA256METHOD_H

0 comments on commit bfa96b0

Please sign in to comment.