Update to work with WordPress plugin

This commit is contained in:
David Rowe 2020-08-04 17:12:31 +12:00
parent 7da91d9557
commit bc56eb5ac7
5 changed files with 36 additions and 46 deletions

View file

@ -77,13 +77,6 @@
"type": "checkbox", "type": "checkbox",
"advanced": true "advanced": true
}, },
{
"name": "domain_access_token",
"label": "Domain API Access Token",
"help": "This is the access token that your domain-server will use to verify users and their roles. This token must grant access to that permission set on your REST API server.",
"advanced": true,
"backup": false
},
{ {
"name": "oauth2_url_path", "name": "oauth2_url_path",
"label": "Authentication URL", "label": "Authentication URL",
@ -95,6 +88,13 @@
"label": "WordPress API URL Base", "label": "WordPress API URL Base",
"help": "The URL base that the domain server will use to make WordPress API calls. Typically \"https://oursite.com/wp-json/\". However, if using non-pretty permalinks or otherwise get a 404 error then try \"https://oursite.com/?rest_route=/\".", "help": "The URL base that the domain server will use to make WordPress API calls. Typically \"https://oursite.com/wp-json/\". However, if using non-pretty permalinks or otherwise get a 404 error then try \"https://oursite.com/?rest_route=/\".",
"advanced": true "advanced": true
},
{
"name": "plugin_client_id",
"label": "WordPress Plugin Client ID",
"help": "This is the client ID from the WordPress plugin configuration.",
"advanced": true,
"backup": false
} }
] ]
}, },

View file

@ -453,6 +453,7 @@ SharedNodePointer DomainGatekeeper::processAssignmentConnectRequest(const NodeCo
const QString AUTHENTICATION_ENAABLED = "authentication.enable_oauth2"; const QString AUTHENTICATION_ENAABLED = "authentication.enable_oauth2";
const QString AUTHENTICATION_OAUTH2_URL_PATH = "authentication.oauth2_url_path"; const QString AUTHENTICATION_OAUTH2_URL_PATH = "authentication.oauth2_url_path";
const QString AUTHENTICATION_WORDPRESS_URL_BASE = "authentication.wordpress_url_base"; const QString AUTHENTICATION_WORDPRESS_URL_BASE = "authentication.wordpress_url_base";
const QString AUTHENTICATION_PLUGIN_CLIENT_ID = "authentication.plugin_client_id";
const QString MAXIMUM_USER_CAPACITY = "security.maximum_user_capacity"; const QString MAXIMUM_USER_CAPACITY = "security.maximum_user_capacity";
const QString MAXIMUM_USER_CAPACITY_REDIRECT_LOCATION = "security.maximum_user_capacity_redirect_location"; const QString MAXIMUM_USER_CAPACITY_REDIRECT_LOCATION = "security.maximum_user_capacity_redirect_location";
@ -553,8 +554,15 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect
if (domainAuthURLVariant.canConvert<QString>()) { if (domainAuthURLVariant.canConvert<QString>()) {
domainAuthURL = domainAuthURLVariant.toString(); domainAuthURL = domainAuthURLVariant.toString();
} }
QString domainAuthClientID;
auto domainAuthClientIDVariant = _server->_settingsManager.valueForKeyPath(AUTHENTICATION_PLUGIN_CLIENT_ID);
if (domainAuthClientIDVariant.canConvert<QString>()) {
domainAuthClientID = domainAuthClientIDVariant.toString();
}
sendConnectionDeniedPacket("You lack the required domain permissions to connect to this domain.", sendConnectionDeniedPacket("You lack the required domain permissions to connect to this domain.",
nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::NotAuthorizedDomain, domainAuthURL); nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::NotAuthorizedDomain,
domainAuthURL + "|" + domainAuthClientID);
} else { } else {
sendConnectionDeniedPacket("You lack the required metaverse permissions to connect to this domain.", sendConnectionDeniedPacket("You lack the required metaverse permissions to connect to this domain.",
nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::NotAuthorizedMetaverse); nodeConnection.senderSockAddr, DomainHandler::ConnectionRefusedReason::NotAuthorizedMetaverse);
@ -1242,11 +1250,9 @@ void DomainGatekeeper::requestDomainUser(const QString& username, const QString&
} }
// Get data pertaining to "me", the user who generated the access token. // Get data pertaining to "me", the user who generated the access token.
// ####### TODO: Confirm API route and data w.r.t. OAuth2 plugin's capabilities. [plugin] const QString WORDPRESS_USER_ROUTE = "wp/v2/users/me";
const QString WORDPRESS_USER_ROUTE = "wp/v2/users/me?context=edit&_fields=id,username,roles"; const QString WORDPRESS_USER_QUERY = "_fields=username,roles";
QUrl domainUserURL = apiBase + WORDPRESS_USER_ROUTE; QUrl domainUserURL = apiBase + WORDPRESS_USER_ROUTE + (apiBase.contains("?") ? "&" : "?") + WORDPRESS_USER_QUERY;
// ####### TODO: Append a random key to check in response? [security]
QNetworkRequest request; QNetworkRequest request;
@ -1275,34 +1281,24 @@ void DomainGatekeeper::requestDomainUserFinished() {
auto httpStatus = requestReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); auto httpStatus = requestReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (200 <= httpStatus && httpStatus < 300) { if (200 <= httpStatus && httpStatus < 300) {
// Success.
// ####### TODO: Verify Expected response. [plugin]
/*
{
id: 2,
username : 'apiuser',
roles : ['subscriber'] ,
}
*/
QString username = rootObject["username"].toString().toLower(); QString username = rootObject["username"].toString().toLower();
if (_inFlightDomainUserIdentityRequests.contains(username)) { if (_inFlightDomainUserIdentityRequests.contains(username)) {
// Success! Verified user. // Success! Verified user.
_verifiedDomainUserIdentities.insert(username, _inFlightDomainUserIdentityRequests.value(username)); _verifiedDomainUserIdentities.insert(username, _inFlightDomainUserIdentityRequests.value(username));
_inFlightDomainUserIdentityRequests.remove(username); _inFlightDomainUserIdentityRequests.remove(username);
// ####### TODO: Handle roles.
} else { } else {
// Failure. // Failure.
qDebug() << "Unexpected username in response for user details -" << username; qDebug() << "Unexpected username in response for user details -" << username;
} }
// ####### TODO: Handle roles if available. [plugin]
} else { } else {
// Failure. // Failure.
// ####### TODO: Error fields to report. [plugin]
qDebug() << "Error in response for user details -" << httpStatus << requestReply->error() qDebug() << "Error in response for user details -" << httpStatus << requestReply->error()
<< "-" << rootObject["error"].toString(); << "-" << rootObject["error"].toString() << rootObject["error_description"].toString();
_inFlightDomainUserIdentityRequests.clear(); _inFlightDomainUserIdentityRequests.clear();
} }

View file

@ -26,8 +26,6 @@
// FIXME: Generalize to other OAuth2 sources for domain login. // FIXME: Generalize to other OAuth2 sources for domain login.
const bool VERBOSE_HTTP_REQUEST_DEBUGGING = false; const bool VERBOSE_HTTP_REQUEST_DEBUGGING = false;
const QString DOMAIN_ACCOUNT_MANAGER_REQUESTED_SCOPE = "foo bar"; // ####### TODO: WordPress plugin's required scope. [plugin]
// ####### TODO: Should scope be configured in domain server settings? [plugin]
// ####### TODO: Add storing domain URL and check against it when retrieving values? // ####### TODO: Add storing domain URL and check against it when retrieving values?
// ####### TODO: Add storing _authURL and check against it when retrieving values? // ####### TODO: Add storing _authURL and check against it when retrieving values?
@ -71,15 +69,15 @@ void DomainAccountManager::requestAccessToken(const QString& username, const QSt
request.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT); request.setHeader(QNetworkRequest::UserAgentHeader, NetworkingConstants::VIRCADIA_USER_AGENT);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
// ####### TODO: WordPress plugin's authorization requirements. [plugin]
request.setRawHeader(QByteArray("Authorization"), QByteArray("Basic b2F1dGgtY2xpZW50LTE6b2F1dGgtY2xpZW50LXNlY3JldC0x"));
// miniOrange WordPress API Authentication plugin:
// - Requires "client_id" parameter.
// - Ignores "state" parameter.
QByteArray formData; QByteArray formData;
formData.append("grant_type=password&"); formData.append("grant_type=password&");
formData.append("username=" + QUrl::toPercentEncoding(username) + "&"); formData.append("username=" + QUrl::toPercentEncoding(username) + "&");
formData.append("password=" + QUrl::toPercentEncoding(password) + "&"); formData.append("password=" + QUrl::toPercentEncoding(password) + "&");
formData.append("scope=" + DOMAIN_ACCOUNT_MANAGER_REQUESTED_SCOPE); formData.append("client_id=" + _clientID);
// ####### TODO: Include state? [plugin]
request.setUrl(_authURL); request.setUrl(_authURL);
@ -100,20 +98,13 @@ void DomainAccountManager::requestAccessTokenFinished() {
auto httpStatus = requestReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); auto httpStatus = requestReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (200 <= httpStatus && httpStatus < 300) { if (200 <= httpStatus && httpStatus < 300) {
// ####### TODO: Process response state? [plugin] // miniOrange plugin provides no scope.
// ####### TODO: Process response scope? [plugin]
// ####### TODO: Which method are the tokens provided in? [plugin]
if (rootObject.contains("access_token")) { if (rootObject.contains("access_token")) {
// Success. // Success.
QUrl rootURL = requestReply->url(); QUrl rootURL = requestReply->url();
rootURL.setPath(""); rootURL.setPath("");
setTokensFromJSON(rootObject, rootURL); setTokensFromJSON(rootObject, rootURL);
emit loginComplete(); emit loginComplete();
} else { } else {
// Failure. // Failure.
qCDebug(networking) << "Received a response for password grant that is missing one or more expected values."; qCDebug(networking) << "Received a response for password grant that is missing one or more expected values.";
@ -122,10 +113,8 @@ void DomainAccountManager::requestAccessTokenFinished() {
} else { } else {
// Failure. // Failure.
// ####### TODO: Error object fields to report. [plugin]
qCDebug(networking) << "Error in response for password grant -" << httpStatus << requestReply->error() qCDebug(networking) << "Error in response for password grant -" << httpStatus << requestReply->error()
<< "_" << rootObject["error"].toString(); << "-" << rootObject["error"].toString() << rootObject["error_description"].toString();
emit loginFailed(); emit loginFailed();
} }
} }
@ -173,6 +162,7 @@ void DomainAccountManager::setTokensFromJSON(const QJsonObject& jsonObject, cons
// ####### TODO: Enable and use these. // ####### TODO: Enable and use these.
// ####### TODO: Protect these per AccountManager? // ####### TODO: Protect these per AccountManager?
// ######: TODO: clientID needed?
/* /*
qCDebug(networking) << "Storing a domain account with access-token for" << qPrintable(url.toString()); qCDebug(networking) << "Storing a domain account with access-token for" << qPrintable(url.toString());
domainAccessToken.set(jsonObject["access_token"].toString()); domainAccessToken.set(jsonObject["access_token"].toString());

View file

@ -24,6 +24,7 @@ public:
DomainAccountManager(); DomainAccountManager();
void setAuthURL(const QUrl& authURL); void setAuthURL(const QUrl& authURL);
void setClientID(const QString& clientID) { _clientID = clientID; }
QString getUsername() { return _username; } QString getUsername() { return _username; }
QString getAccessToken() { return _access_token; } QString getAccessToken() { return _access_token; }
@ -51,6 +52,7 @@ private:
void sendInterfaceAccessTokenToServer(); void sendInterfaceAccessTokenToServer();
QUrl _authURL; QUrl _authURL;
QString _clientID;
QString _username; // ####### TODO: Store elsewhere? QString _username; // ####### TODO: Store elsewhere?
QString _access_token; // ####... "" QString _access_token; // ####... ""
QString _refresh_token; // ####... "" QString _refresh_token; // ####... ""

View file

@ -589,7 +589,9 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer<Rec
auto accountManager = DependencyManager::get<DomainAccountManager>(); auto accountManager = DependencyManager::get<DomainAccountManager>();
if (!extraInfo.isEmpty()) { if (!extraInfo.isEmpty()) {
accountManager->setAuthURL(extraInfo); auto extraInfoComponents = extraInfo.split("|");
accountManager->setAuthURL(extraInfoComponents.value(0));
accountManager->setClientID(extraInfoComponents.value(1));
} }
if (!_hasCheckedForDomainAccessToken) { if (!_hasCheckedForDomainAccessToken) {