Files
ResInsight/ApplicationLibCode/Application/Tools/Cloud/RiaCloudConnector.cpp

292 lines
11 KiB
C++
Raw Normal View History

/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2024 Equinor ASA
//
// ResInsight 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 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RiaCloudConnector.h"
#include "RiaConnectorTools.h"
#include "RiaLogging.h"
#include "RiaOsduDefines.h"
#include "RiaOsduOAuthHttpServerReplyHandler.h"
#include "RiaSumoDefines.h"
#include <QAbstractOAuth>
2024-07-13 11:47:32 +02:00
#include <QByteArray>
2024-07-12 18:51:22 +02:00
#include <QDateTime>
#include <QDesktopServices>
#include <QEventLoop>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QOAuth2AuthorizationCodeFlow>
#include <QOAuthHttpServerReplyHandler>
#include <QObject>
#include <QString>
#include <QTimer>
#include <QUrl>
#include <QUrlQuery>
#include <QtCore>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaCloudConnector::RiaCloudConnector( QObject* parent,
const QString& server,
const QString& authority,
const QString& scopes,
const QString& clientId,
unsigned int port )
: QObject( parent )
, m_server( server )
, m_authority( authority )
, m_scopes( scopes )
, m_clientId( clientId )
{
m_authCodeFlow = new QOAuth2AuthorizationCodeFlow( this );
m_networkAccessManager = new QNetworkAccessManager( this );
m_authCodeFlow->setNetworkAccessManager( m_networkAccessManager );
RiaLogging::debug( "SSL BUILD VERSION: " + QSslSocket::sslLibraryBuildVersionString() );
RiaLogging::debug( "SSL VERSION STRING: " + QSslSocket::sslLibraryVersionString() );
RiaLogging::debug( "Cloud config:" );
RiaLogging::debug( " server: '" + server + "'" );
RiaLogging::debug( " authority: '" + authority + "'" );
RiaLogging::debug( " scopes: '" + scopes + "'" );
RiaLogging::debug( " client id: '" + clientId + "'" );
connect( m_authCodeFlow,
&QOAuth2AuthorizationCodeFlow::authorizeWithBrowser,
[]( QUrl url )
{
RiaLogging::info( "Authorize with url: " + url.toString() );
QUrlQuery query( url );
url.setQuery( query );
QDesktopServices::openUrl( url );
} );
QString authUrl = constructAuthUrl( m_authority );
m_authCodeFlow->setAuthorizationUrl( QUrl( authUrl ) );
QString tokenUrl = constructTokenUrl( m_authority );
m_authCodeFlow->setAccessTokenUrl( QUrl( tokenUrl ) );
// App key
m_authCodeFlow->setClientIdentifier( m_clientId );
m_authCodeFlow->setScope( m_scopes );
auto replyHandler = new RiaOsduOAuthHttpServerReplyHandler( port, this );
m_authCodeFlow->setReplyHandler( replyHandler );
RiaLogging::debug( "Server callback: " + replyHandler->callback() );
connect( m_authCodeFlow, SIGNAL( granted() ), this, SLOT( accessGranted() ) );
connect( m_authCodeFlow,
SIGNAL( error( const QString&, const QString&, const QUrl& ) ),
this,
SLOT( errorReceived( const QString&, const QString&, const QUrl& ) ) );
connect( m_authCodeFlow,
SIGNAL( authorizationCallbackReceived( const QVariantMap& ) ),
this,
SLOT( authorizationCallbackReceived( const QVariantMap& ) ) );
2024-07-12 18:51:22 +02:00
connect( m_authCodeFlow,
&QOAuth2AuthorizationCodeFlow::tokenChanged,
this,
[&]( const QString& token )
{
RiaLogging::debug( "Access token changed." );
exportTokenToFile();
} );
connect( m_authCodeFlow,
&QOAuth2AuthorizationCodeFlow::refreshTokenChanged,
this,
[&]( const QString& refreshToken )
{
RiaLogging::debug( "Refresh token changed." );
exportTokenToFile();
} );
connect( m_authCodeFlow,
&QOAuth2AuthorizationCodeFlow::expirationAtChanged,
this,
[&]( const QDateTime& expiration )
{
RiaLogging::debug( QString( "Access token expiration changed: %1" ).arg( expiration.toString() ) );
exportTokenToFile();
} );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RiaCloudConnector::~RiaCloudConnector()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaCloudConnector::accessGranted()
{
2024-07-12 18:51:22 +02:00
QString currentToken = m_authCodeFlow->token();
emit tokenReady( currentToken );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaCloudConnector::errorReceived( const QString& error, const QString& errorDescription, const QUrl& uri )
{
RiaLogging::debug( "Cloud Error Received: " + error + ". Description: " + errorDescription );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaCloudConnector::authorizationCallbackReceived( const QVariantMap& data )
{
RiaLogging::debug( "Authorization callback received:" );
for ( const auto& [key, value] : data.toStdMap() )
{
RiaLogging::debug( " Key: " + key + " Value: " + value.toString() );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaCloudConnector::requestFailed( const QAbstractOAuth::Error error )
{
RiaLogging::error( "Request failed: " );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaCloudConnector::requestToken()
{
2024-07-12 18:51:22 +02:00
if ( token().isEmpty() )
{
2024-07-12 18:51:22 +02:00
RiaLogging::debug( "No valid access token found." );
if ( !m_authCodeFlow->refreshToken().isEmpty() )
{
RiaLogging::info( "Refreshing access token with refresh token." );
m_authCodeFlow->refreshAccessToken();
}
else
{
RiaLogging::info( "Requesting token." );
m_authCodeFlow->grant();
}
}
else
{
RiaLogging::debug( "Has token: skipping token request." );
emit accessGranted();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RiaCloudConnector::token() const
{
2024-07-12 18:51:22 +02:00
QString currentToken = m_authCodeFlow->token();
QDateTime expiration = m_authCodeFlow->expirationAt();
if ( !currentToken.isEmpty() && expiration.isValid() && expiration > QDateTime::currentDateTime() )
{
return currentToken;
}
else
{
return QString();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaCloudConnector::exportTokenToFile()
{
QString tokenDataJson = RiaConnectorTools::tokenDataAsJson( m_authCodeFlow );
RiaConnectorTools::writeTokenData( m_tokenDataFilePath, tokenDataJson );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaCloudConnector::importTokenFromFile()
{
auto tokenDataJson = RiaConnectorTools::readTokenData( m_tokenDataFilePath );
if ( !tokenDataJson.isEmpty() )
{
RiaConnectorTools::initializeTokenDataFromJson( m_authCodeFlow, tokenDataJson );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaCloudConnector::setTokenDataFilePath( const QString& filePath )
{
m_tokenDataFilePath = filePath;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RiaCloudConnector::constructAuthUrl( const QString& authority )
{
return authority + "/oauth2/v2.0/authorize";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RiaCloudConnector::constructTokenUrl( const QString& authority )
{
return authority + "/oauth2/v2.0/token";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RiaCloudConnector::requestTokenBlocking()
{
2024-07-12 18:51:22 +02:00
QString currentToken = token();
if ( !currentToken.isEmpty() ) return currentToken;
QTimer timer;
timer.setSingleShot( true );
QEventLoop loop;
connect( this, SIGNAL( tokenReady( const QString& ) ), &loop, SLOT( quit() ) );
connect( &timer, SIGNAL( timeout() ), &loop, SLOT( quit() ) );
requestToken();
timer.start( RiaSumoDefines::requestTimeoutMillis() );
loop.exec();
2024-07-12 18:51:22 +02:00
return m_authCodeFlow->token();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RiaCloudConnector::server() const
{
return m_server;
}