diff options
| author | Petr Mrázek <peterix@gmail.com> | 2021-07-22 20:15:20 +0200 |
|---|---|---|
| committer | Petr Mrázek <peterix@gmail.com> | 2021-07-25 19:50:44 +0200 |
| commit | dd133680858351e3e07690e286882327a4f42ba5 (patch) | |
| tree | 42ac11eb0d6cc58d3785c57f8571046cafe4b769 /libraries/katabasis | |
| parent | 2568752af57258a33f27f4c2d0b8fc486fb3d100 (diff) | |
| download | PrismLauncher-dd133680858351e3e07690e286882327a4f42ba5.tar.gz PrismLauncher-dd133680858351e3e07690e286882327a4f42ba5.tar.bz2 PrismLauncher-dd133680858351e3e07690e286882327a4f42ba5.zip | |
NOISSUE bulk addition of code from Katabasis
Diffstat (limited to 'libraries/katabasis')
20 files changed, 2225 insertions, 0 deletions
diff --git a/libraries/katabasis/.gitignore b/libraries/katabasis/.gitignore new file mode 100644 index 00000000..35e189c5 --- /dev/null +++ b/libraries/katabasis/.gitignore @@ -0,0 +1,2 @@ +build/ +*.kdev4 diff --git a/libraries/katabasis/CMakeLists.txt b/libraries/katabasis/CMakeLists.txt new file mode 100644 index 00000000..7bbe1a8b --- /dev/null +++ b/libraries/katabasis/CMakeLists.txt @@ -0,0 +1,60 @@ +cmake_minimum_required(VERSION 3.20) + +string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD) +if(IS_IN_SOURCE_BUILD) + message(FATAL_ERROR "You are building Katabasis in-source. Please separate the build tree from the source tree.") +endif() + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + if(CMAKE_HOST_SYSTEM_VERSION MATCHES ".*[Mm]icrosoft.*" OR + CMAKE_HOST_SYSTEM_VERSION MATCHES ".*WSL.*" + ) + message(FATAL_ERROR "Building Katabasis is not supported in Linux-on-Windows distributions. Use a real Linux machine, not a fraudulent one.") + endif() +endif() + +project(Katabasis) +enable_testing() + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_CXX_STANDARD_REQUIRED true) +set(CMAKE_C_STANDARD_REQUIRED true) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_C_STANDARD 11) + +find_package(Qt5 COMPONENTS Core Network REQUIRED) + +set( katabasis_PRIVATE + src/OAuth2.cpp + + src/JsonResponse.cpp + src/JsonResponse.h + src/PollServer.cpp + src/Reply.cpp + src/ReplyServer.cpp + src/Requestor.cpp +) + +set( katabasis_PUBLIC + include/katabasis/OAuth2.h + + include/katabasis/Globals.h + include/katabasis/PollServer.h + include/katabasis/Reply.h + include/katabasis/ReplyServer.h + + include/katabasis/Requestor.h + include/katabasis/RequestParameter.h +) + +add_library( Katabasis STATIC ${katabasis_PRIVATE} ${katabasis_PUBLIC} ) +target_link_libraries(Katabasis Qt5::Core Qt5::Network) + +# needed for statically linked Katabasis in shared libs on x86_64 +set_target_properties(Katabasis + PROPERTIES POSITION_INDEPENDENT_CODE TRUE +) + +target_include_directories(Katabasis PUBLIC include PRIVATE src include/katabasis) diff --git a/libraries/katabasis/LICENSE b/libraries/katabasis/LICENSE new file mode 100644 index 00000000..9ac8d42f --- /dev/null +++ b/libraries/katabasis/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2012, Akos Polster +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libraries/katabasis/README.md b/libraries/katabasis/README.md new file mode 100644 index 00000000..a4dc0994 --- /dev/null +++ b/libraries/katabasis/README.md @@ -0,0 +1,36 @@ +# Katabasis - MS-flavoerd OAuth for Qt, derived from the O2 library + +This library's sole purpose is to make interacting with MSA and various MSA and XBox authenticated services less painful. + +It may be possible to backport some of the changes to O2 in the future, but for the sake of going fast, all compatibility concerns have been ignored. + +[You can find the original library's git repository here.](https://github.com/pipacs/o2) + +Notes to contributors: + + * Please follow the coding style of the existing source, where reasonable + * Code contributions are released under Simplified BSD License, as specified in LICENSE. Do not contribute if this license does not suit your code + * If you are interested in working on this, come to the MultiMC Discord server and talk first + +## Installation + +Clone the Github repository, integrate the it into your CMake build system. + +The library is static only, dynamic linking and system-wide installation are out of scope and undesirable. + +## Usage + +At this stage, don't, unless you want to help with the library itself. + +This is an experimental fork of the O2 library and is undergoing a big design/architecture shift in order to support different features: + +* Multiple accounts +* Multi-stage authentication/authorization schemes +* Tighter control over token chains and their storage +* Talking to complex APIs and individually authorized microservices +* Token lifetime management, 'offline mode' and resilience in face of network failures +* Token and claims/entitlements validation +* Caching of some API results +* XBox magic +* Mojang magic +* Generally, magic that you would spend weeks on researching while getting confused by contradictory/incomplete documentation (if any is available) diff --git a/libraries/katabasis/acknowledgements.md b/libraries/katabasis/acknowledgements.md new file mode 100644 index 00000000..c1c8a3d4 --- /dev/null +++ b/libraries/katabasis/acknowledgements.md @@ -0,0 +1,110 @@ +# O2 library by Akos Polster and contributors + +[The origin of this fork.](https://github.com/pipacs/o2) + +> Copyright (c) 2012, Akos Polster +> All rights reserved. +> +> Redistribution and use in source and binary forms, with or without +> modification, are permitted provided that the following conditions are met: +> +> * Redistributions of source code must retain the above copyright notice, this +> list of conditions and the following disclaimer. +> +> * Redistributions in binary form must reproduce the above copyright notice, +> this list of conditions and the following disclaimer in the documentation +> and/or other materials provided with the distribution. +> +> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +> AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +> IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +> DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +> FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +> DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +> SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +> CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +> OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +> OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +# SimpleCrypt by Andre Somers + +Cryptographic methods for Qt. + +> Copyright (c) 2011, Andre Somers +> All rights reserved. +> +> Redistribution and use in source and binary forms, with or without +> modification, are permitted provided that the following conditions are met: +> +> * Redistributions of source code must retain the above copyright +> notice, this list of conditions and the following disclaimer. +> * Redistributions in binary form must reproduce the above copyright +> notice, this list of conditions and the following disclaimer in the +> documentation and/or other materials provided with the distribution. +> * Neither the name of the Rathenau Instituut, Andre Somers nor the +> names of its contributors may be used to endorse or promote products +> derived from this software without specific prior written permission. +> +> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +> ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +> WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +> DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY +> DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +> (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +> LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +> ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +> (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +> SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Mandeep Sandhu <mandeepsandhu.chd@gmail.com> + +Configurable settings storage, Twitter XAuth specialization, new demos, cleanups. + +> "Hi Akos, +> +> I'm writing this mail to confirm that my contributions to the O2 library, available here https://github.com/pipacs/o2, can be freely distributed according to the project's license (as shown in the LICENSE file). +> +> Regards, +> -mandeep" + +# Sergey Gavrushkin <https://github.com/ncux> + +FreshBooks specialization + +# Theofilos Intzoglou <https://github.com/parapente> + +Hubic specialization + +# Dimitar + +SurveyMonkey specialization + +# David Brooks <https://github.com/dbrnz> + +CMake related fixes and improvements. + +# Lukas Vogel <https://github.com/lukedirtwalker> + +Spotify support + +# Alan Garny <https://github.com/agarny> + +Windows DLL build support + +# MartinMikita <https://github.com/MartinMikita> + +Bug fixes + +# Larry Shaffer <https://github.com/dakcarto> + +Versioning, shared lib, install target and header support + +# Gilmanov Ildar <https://github.com/gilmanov-ildar> + +Bug fixes, support for ```qml``` module + +# Fabian Vogt <https://github.com/Vogtinator> + +Bug fixes, support for building without Qt keywords enabled + diff --git a/libraries/katabasis/include/katabasis/Bits.h b/libraries/katabasis/include/katabasis/Bits.h new file mode 100644 index 00000000..3fd2f530 --- /dev/null +++ b/libraries/katabasis/include/katabasis/Bits.h @@ -0,0 +1,33 @@ +#pragma once + +#include <QString> +#include <QDateTime> +#include <QMap> +#include <QVariantMap> + +namespace Katabasis { +enum class Activity { + Idle, + LoggingIn, + LoggingOut, + Refreshing +}; + +enum class Validity { + None, + Assumed, + Certain +}; + +struct Token { + QDateTime issueInstant; + QDateTime notAfter; + QString token; + QString refresh_token; + QVariantMap extra; + + Validity validity = Validity::None; + bool persistent = true; +}; + +} diff --git a/libraries/katabasis/include/katabasis/Globals.h b/libraries/katabasis/include/katabasis/Globals.h new file mode 100644 index 00000000..512745d3 --- /dev/null +++ b/libraries/katabasis/include/katabasis/Globals.h @@ -0,0 +1,59 @@ +#pragma once + +namespace Katabasis { + +// Common constants +const char ENCRYPTION_KEY[] = "12345678"; +const char MIME_TYPE_XFORM[] = "application/x-www-form-urlencoded"; +const char MIME_TYPE_JSON[] = "application/json"; + +// OAuth 1/1.1 Request Parameters +const char OAUTH_CALLBACK[] = "oauth_callback"; +const char OAUTH_CONSUMER_KEY[] = "oauth_consumer_key"; +const char OAUTH_NONCE[] = "oauth_nonce"; +const char OAUTH_SIGNATURE[] = "oauth_signature"; +const char OAUTH_SIGNATURE_METHOD[] = "oauth_signature_method"; +const char OAUTH_TIMESTAMP[] = "oauth_timestamp"; +const char OAUTH_VERSION[] = "oauth_version"; +// OAuth 1/1.1 Response Parameters +const char OAUTH_TOKEN[] = "oauth_token"; +const char OAUTH_TOKEN_SECRET[] = "oauth_token_secret"; +const char OAUTH_CALLBACK_CONFIRMED[] = "oauth_callback_confirmed"; +const char OAUTH_VERFIER[] = "oauth_verifier"; + +// OAuth 2 Request Parameters +const char OAUTH2_RESPONSE_TYPE[] = "response_type"; +const char OAUTH2_CLIENT_ID[] = "client_id"; +const char OAUTH2_CLIENT_SECRET[] = "client_secret"; +const char OAUTH2_USERNAME[] = "username"; +const char OAUTH2_PASSWORD[] = "password"; +const char OAUTH2_REDIRECT_URI[] = "redirect_uri"; +const char OAUTH2_SCOPE[] = "scope"; +const char OAUTH2_GRANT_TYPE_CODE[] = "code"; +const char OAUTH2_GRANT_TYPE_TOKEN[] = "token"; +const char OAUTH2_GRANT_TYPE_PASSWORD[] = "password"; +const char OAUTH2_GRANT_TYPE_DEVICE[] = "urn:ietf:params:oauth:grant-type:device_code"; +const char OAUTH2_GRANT_TYPE[] = "grant_type"; +const char OAUTH2_API_KEY[] = "api_key"; +const char OAUTH2_STATE[] = "state"; +const char OAUTH2_CODE[] = "code"; + +// OAuth 2 Response Parameters +const char OAUTH2_ACCESS_TOKEN[] = "access_token"; +const char OAUTH2_REFRESH_TOKEN[] = "refresh_token"; +const char OAUTH2_EXPIRES_IN[] = "expires_in"; +const char OAUTH2_DEVICE_CODE[] = "device_code"; +const char OAUTH2_USER_CODE[] = "user_code"; +const char OAUTH2_VERIFICATION_URI[] = "verification_uri"; +const char OAUTH2_VERIFICATION_URL[] = "verification_url"; // Google sign-in +const char OAUTH2_VERIFICATION_URI_COMPLETE[] = "verification_uri_complete"; +const char OAUTH2_INTERVAL[] = "interval"; + +// Parameter values +const char AUTHORIZATION_CODE[] = "authorization_code"; + +// Standard HTTP headers +const char HTTP_HTTP_HEADER[] = "HTTP"; +const char HTTP_AUTHORIZATION_HEADER[] = "Authorization"; + +} diff --git a/libraries/katabasis/include/katabasis/OAuth2.h b/libraries/katabasis/include/katabasis/OAuth2.h new file mode 100644 index 00000000..4361691c --- /dev/null +++ b/libraries/katabasis/include/katabasis/OAuth2.h @@ -0,0 +1,233 @@ +#pragma once + +#include <QNetworkAccessManager> +#include <QNetworkRequest> +#include <QNetworkReply> +#include <QPair> + +#include "Reply.h" +#include "RequestParameter.h" +#include "Bits.h" + +namespace Katabasis { + +class ReplyServer; +class PollServer; + + +/* + * FIXME: this is not as simple as it should be. it squishes 4 different grant flows into one big ball of mud + * This serves no practical purpose and simply makes the code less readable / maintainable. + * + * Therefore: Split this into the 4 different OAuth2 flows that people can use as authentication steps. Write tests/examples for all of them. + */ + +/// Simple OAuth2 authenticator. +class OAuth2: public QObject +{ + Q_OBJECT +public: + Q_ENUMS(GrantFlow) + +public: + + struct Options { + QString userAgent = QStringLiteral("Katabasis/1.0"); + QString redirectionUrl = QStringLiteral("http://localhost:%1"); + QString responseType = QStringLiteral("code"); + QString scope; + QString clientIdentifier; + QString clientSecret; + QUrl authorizationUrl; + QUrl accessTokenUrl; + QVector<quint16> listenerPorts = { 0 }; + }; + + /// Authorization flow types. + enum GrantFlow { + GrantFlowAuthorizationCode, ///< @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.1 + GrantFlowImplicit, ///< @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.2 + GrantFlowResourceOwnerPasswordCredentials, + GrantFlowDevice ///< @see https://tools.ietf.org/html/rfc8628#section-1 + }; + + /// Authorization flow. + GrantFlow grantFlow(); + void setGrantFlow(GrantFlow value); + +public: + /// Are we authenticated? + bool linked(); + + /// Authentication token. + QString token(); + + /// Provider-specific extra tokens, available after a successful authentication + QVariantMap extraTokens(); + + /// Page content on local host after successful oauth. + /// Provide it in case you do not want to close the browser, but display something + QByteArray replyContent() const; + void setReplyContent(const QByteArray &value); + +public: + + // TODO: remove + /// Resource owner username. + /// instances with the same (username, password) share the same "linked" and "token" properties. + QString username(); + void setUsername(const QString &value); + + // TODO: remove + /// Resource owner password. + /// instances with the same (username, password) share the same "linked" and "token" properties. + QString password(); + void setPassword(const QString &value); + + // TODO: remove + /// API key. + QString apiKey(); + void setApiKey(const QString &value); + + // TODO: remove + /// Allow ignoring SSL errors? + /// E.g. SurveyMonkey fails on Mac due to SSL error. Ignoring the error circumvents the problem + bool ignoreSslErrors(); + void setIgnoreSslErrors(bool ignoreSslErrors); + + // TODO: put in `Options` + /// User-defined extra parameters to append to request URL + QVariantMap extraRequestParams(); + void setExtraRequestParams(const QVariantMap &value); + + // TODO: split up the class into multiple, each implementing one OAuth2 flow + /// Grant type (if non-standard) + QString grantType(); + void setGrantType(const QString &value); + +public: + /// Constructor. + /// @param parent Parent object. + explicit OAuth2(Options & opts, Token & token, QObject *parent = 0, QNetworkAccessManager *manager = 0); + + /// Get refresh token. + QString refreshToken(); + + /// Get token expiration time + QDateTime expires(); + +public slots: + /// Authenticate. + virtual void link(); + + /// De-authenticate. + virtual void unlink(); + + /// Refresh token. + bool refresh(); + + /// Handle situation where reply server has opted to close its connection + void serverHasClosed(bool paramsfound = false); + +signals: + /// Emitted when a token refresh has been completed or failed. + void refreshFinished(QNetworkReply::NetworkError error); + + /// Emitted when client needs to open a web browser window, with the given URL. + void openBrowser(const QUrl &url); + + /// Emitted when client can close the browser window. + void closeBrowser(); + + /// Emitted when client needs to show a verification uri and user code + void showVerificationUriAndCode(const QUrl &uri, const QString &code); + + /// Emitted when authentication/deauthentication succeeded. + void linkingSucceeded(); + + /// Emitted when authentication/deauthentication failed. + void linkingFailed(); + + void activityChanged(Activity activity); + +public slots: + /// Handle verification response. + virtual void onVerificationReceived(QMap<QString, QString>); + +protected slots: + /// Handle completion of a token request. + virtual void onTokenReplyFinished(); + + /// Handle failure of a token request. + virtual void onTokenReplyError(QNetworkReply::NetworkError error); + + /// Handle completion of a refresh request. + virtual void onRefreshFinished(); + + /// Handle failure of a refresh request. + virtual void onRefreshError(QNetworkReply::NetworkError error); + + /// Handle completion of a Device Authorization Request + virtual void onDeviceAuthReplyFinished(); + +protected: + /// Build HTTP request body. + QByteArray buildRequestBody(const QMap<QString, QString> ¶meters); + + /// Set refresh token. + void setRefreshToken(const QString &v); + + /// Set token expiration time. + void setExpires(QDateTime v); + + /// Start polling authorization server + void startPollServer(const QVariantMap ¶ms); + + /// Set authentication token. + void setToken(const QString &v); + + /// Set the linked state + void setLinked(bool v); + + /// Set extra tokens found in OAuth response + void setExtraTokens(QVariantMap extraTokens); + + /// Set local reply server + void setReplyServer(ReplyServer *server); + + ReplyServer * replyServer() const; + + /// Set local poll server + void setPollServer(PollServer *server); + + PollServer * pollServer() const; + + void updateActivity(Activity activity); + +protected: + QString username_; + QString password_; + + Options options_; + + QVariantMap extraReqParams_; + QString apiKey_; + QNetworkAccessManager *manager_ = nullptr; + ReplyList timedReplies_; + GrantFlow grantFlow_; + QString grantType_; + +protected: + QString redirectUri_; + Token &token_; + + // this should be part of the reply server impl + QByteArray replyContent_; + +private: + ReplyServer *replyServer_ = nullptr; + PollServer *pollServer_ = nullptr; + Activity activity_ = Activity::Idle; +}; + +} diff --git a/libraries/katabasis/include/katabasis/PollServer.h b/libraries/katabasis/include/katabasis/PollServer.h new file mode 100644 index 00000000..77103867 --- /dev/null +++ b/libraries/katabasis/include/katabasis/PollServer.h @@ -0,0 +1,48 @@ +#pragma once + +#include <QByteArray> +#include <QMap> +#include <QNetworkRequest> +#include <QObject> +#include <QString> +#include <QTimer> + +class QNetworkAccessManager; + +namespace Katabasis { + +/// Poll an authorization server for token +class PollServer : public QObject +{ + Q_OBJECT + +public: + explicit PollServer(QNetworkAccessManager * manager, const QNetworkRequest &request, const QByteArray & payload, int expiresIn, QObject *parent = 0); + + /// Seconds to wait between polling requests + Q_PROPERTY(int interval READ interval WRITE setInterval) + int interval() const; + void setInterval(int interval); + +signals: + void verificationReceived(QMap<QString, QString>); + void serverClosed(bool); // whether it has found parameters + +public slots: + void startPolling(); + +protected slots: + void onPollTimeout(); + void onExpiration(); + void onReplyFinished(); + +protected: + QNetworkAccessManager *manager_; + const QNetworkRequest request_; + const QByteArray payload_; + const int expiresIn_; + QTimer expirationTimer; + QTimer pollTimer; +}; + +} diff --git a/libraries/katabasis/include/katabasis/Reply.h b/libraries/katabasis/include/katabasis/Reply.h new file mode 100644 index 00000000..3af1d49f --- /dev/null +++ b/libraries/katabasis/include/katabasis/Reply.h @@ -0,0 +1,60 @@ +#pragma once + +#include <QList> +#include <QTimer> +#include <QNetworkRequest> +#include <QNetworkReply> +#include <QNetworkAccessManager> +#include <QByteArray> + +namespace Katabasis { + +/// A network request/reply pair that can time out. +class Reply: public QTimer { + Q_OBJECT + +public: + Reply(QNetworkReply *reply, int timeOut = 60 * 1000, QObject *parent = 0); + +signals: + void error(QNetworkReply::NetworkError); + +public slots: + /// When time out occurs, the QNetworkReply's error() signal is triggered. + void onTimeOut(); + +public: + QNetworkReply *reply; +}; + +/// List of O2Replies. +class ReplyList { +public: + ReplyList() { ignoreSslErrors_ = false; } + + /// Destructor. + /// Deletes all O2Reply instances in the list. + virtual ~ReplyList(); + + /// Create a new O2Reply from a QNetworkReply, and add it to this list. + void add(QNetworkReply *reply); + + /// Add an O2Reply to the list, while taking ownership of it. + void add(Reply *reply); + + /// Remove item from the list that corresponds to a QNetworkReply. + void remove(QNetworkReply *reply); + + /// Find an O2Reply in the list, corresponding to a QNetworkReply. + /// @return Matching O2Reply or NULL. + Reply *find(QNetworkReply *reply); + + bool ignoreSslErrors(); + void setIgnoreSslErrors(bool ignoreSslErrors); + +protected: + QList<Reply *> replies_; + bool ignoreSslErrors_; +}; + +} diff --git a/libraries/katabasis/include/katabasis/ReplyServer.h b/libraries/katabasis/include/katabasis/ReplyServer.h new file mode 100644 index 00000000..bf47df69 --- /dev/null +++ b/libraries/katabasis/include/katabasis/ReplyServer.h @@ -0,0 +1,53 @@ +#pragma once + +#include <QTcpServer> +#include <QMap> +#include <QByteArray> +#include <QString> + +namespace Katabasis { + +/// HTTP server to process authentication response. +class ReplyServer: public QTcpServer { + Q_OBJECT + +public: + explicit ReplyServer(QObject *parent = 0); + + /// Page content on local host after successful oauth - in case you do not want to close the browser, but display something + Q_PROPERTY(QByteArray replyContent READ replyContent WRITE setReplyContent) + QByteArray replyContent(); + void setReplyContent(const QByteArray &value); + + /// Seconds to keep listening *after* first response for a callback with token content + Q_PROPERTY(int timeout READ timeout WRITE setTimeout) + int timeout(); + void setTimeout(int timeout); + + /// Maximum number of callback tries to accept, in case some don't have token content (favicons, etc.) + Q_PROPERTY(int callbackTries READ callbackTries WRITE setCallbackTries) + int callbackTries(); + void setCallbackTries(int maxtries); + + QString uniqueState(); + void setUniqueState(const QString &state); + +signals: + void verificationReceived(QMap<QString, QString>); + void serverClosed(bool); // whether it has found parameters + +public slots: + void onIncomingConnection(); + void onBytesReady(); + QMap<QString, QString> parseQueryParams(QByteArray *data); + void closeServer(QTcpSocket *socket = 0, bool hasparameters = false); + +protected: + QByteArray replyContent_; + int timeout_; + int maxtries_; + int tries_; + QString uniqueState_; +}; + +} diff --git a/libraries/katabasis/include/katabasis/RequestParameter.h b/libraries/katabasis/include/katabasis/RequestParameter.h new file mode 100644 index 00000000..ca36934a --- /dev/null +++ b/libraries/katabasis/include/katabasis/RequestParameter.h @@ -0,0 +1,15 @@ +#pragma once + +namespace Katabasis { + +/// Request parameter (name-value pair) participating in authentication. +struct RequestParameter { + RequestParameter(const QByteArray &n, const QByteArray &v): name(n), value(v) {} + bool operator <(const RequestParameter &other) const { + return (name == other.name)? (value < other.value): (name < other.name); + } + QByteArray name; + QByteArray value; +}; + +} diff --git a/libraries/katabasis/include/katabasis/Requestor.h b/libraries/katabasis/include/katabasis/Requestor.h new file mode 100644 index 00000000..61437f76 --- /dev/null +++ b/libraries/katabasis/include/katabasis/Requestor.h @@ -0,0 +1,116 @@ +#pragma once +#include <QObject> +#include <QNetworkRequest> +#include <QNetworkReply> +#include <QNetworkAccessManager> +#include <QUrl> +#include <QByteArray> +#include <QHttpMultiPart> + +#include "Reply.h" + +namespace Katabasis { + +class OAuth2; + +/// Makes authenticated requests. +class Requestor: public QObject { + Q_OBJECT + +public: + explicit Requestor(QNetworkAccessManager *manager, OAuth2 *authenticator, QObject *parent = 0); + ~Requestor(); + + + /// Some services require the access token to be sent as a Authentication HTTP header + /// and refuse requests with the access token in the query. + /// This function allows to use or ignore the access token in the query. + /// The default value of `true` means that the query will contain the access token. + /// By setting the value to false, the query will not contain the access token. + /// See: + /// https://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-16#section-4.3 + /// https://tools.ietf.org/html/rfc6750#section-2.3 + + void setAddAccessTokenInQuery(bool value); + + /// Some services require the access token to be sent as a Authentication HTTP header. + /// This is the case for Twitch and Mixer. + /// When the access token expires and is refreshed, O2Requestor::retry() needs to update the Authentication HTTP header. + /// In order to do so, O2Requestor needs to know the format of the Authentication HTTP header. + void setAccessTokenInAuthenticationHTTPHeaderFormat(const QString &value); + +public slots: + /// Make a GET request. + /// @return Request ID or -1 if there are too many requests in the queue. + int get(const QNetworkRequest &req, int timeout = 60*1000); + + /// Make a POST request. + /// @return Request ID or -1 if there are too many requests in the queue. + int post(const QNetworkRequest &req, const QByteArray &data, int timeout = 60*1000); + int post(const QNetworkRequest &req, QHttpMultiPart* data, int timeout = 60*1000); + + /// Make a PUT request. + /// @return Request ID or -1 if there are too many requests in the queue. + int put(const QNetworkRequest &req, const QByteArray &data, int timeout = 60*1000); + int put(const QNetworkRequest &req, QHttpMultiPart* data, int timeout = 60*1000); + + /// Make a HEAD request. + /// @return Request ID or -1 if there are too many requests in the queue. + int head(const QNetworkRequest &req, int timeout = 60*1000); + + /// Make a custom request. + / |
