Merge pull request #639 from ctrlaltdavid/enhancement/domain-login-items

Don't display domain log-in items unless relevant
This commit is contained in:
kasenvr 2020-09-02 18:51:14 -04:00 committed by GitHub
commit bbc5044451
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 126 additions and 96 deletions

View file

@ -1352,10 +1352,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
connect(accountManager.data(), &AccountManager::usernameChanged, this, &Application::updateWindowTitle); connect(accountManager.data(), &AccountManager::usernameChanged, this, &Application::updateWindowTitle);
auto domainAccountManager = DependencyManager::get<DomainAccountManager>(); auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
connect(domainAccountManager.data(), &DomainAccountManager::authRequired, dialogsManager.data(), connect(domainAccountManager.data(), &DomainAccountManager::authRequired,
&DialogsManager::showDomainLoginDialog); dialogsManager.data(), &DialogsManager::showDomainLoginDialog);
connect(domainAccountManager.data(), &DomainAccountManager::loginComplete, this, connect(domainAccountManager.data(), &DomainAccountManager::authRequired, this, &Application::updateWindowTitle);
&Application::updateWindowTitle); connect(domainAccountManager.data(), &DomainAccountManager::loginComplete, this, &Application::updateWindowTitle);
// ####### TODO: Connect any other signals from domainAccountManager. // ####### TODO: Connect any other signals from domainAccountManager.
// use our MyAvatar position and quat for address manager path // use our MyAvatar position and quat for address manager path
@ -7086,8 +7086,9 @@ void Application::updateWindowTitle() const {
auto domainAccountManager = DependencyManager::get<DomainAccountManager>(); auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
auto isInErrorState = nodeList->getDomainHandler().isInErrorState(); auto isInErrorState = nodeList->getDomainHandler().isInErrorState();
bool isMetaverseLoggedIn = accountManager->isLoggedIn(); bool isMetaverseLoggedIn = accountManager->isLoggedIn();
bool hasDomainLogIn = domainAccountManager->hasLogIn();
bool isDomainLoggedIn = domainAccountManager->isLoggedIn(); bool isDomainLoggedIn = domainAccountManager->isLoggedIn();
QString authedDomain = domainAccountManager->getAuthedDomain(); QString authedDomainName = domainAccountManager->getAuthedDomainName();
QString buildVersion = " - Vircadia - " QString buildVersion = " - Vircadia - "
+ (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build")) + (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build"))
@ -7117,20 +7118,23 @@ void Application::updateWindowTitle() const {
QString metaverseDetails; QString metaverseDetails;
if (isMetaverseLoggedIn) { if (isMetaverseLoggedIn) {
metaverseDetails = "Metaverse: Logged in as " + metaverseUsername; metaverseDetails = " (Metaverse: Logged in as " + metaverseUsername + ")";
} else { } else {
metaverseDetails = "Metaverse: Not Logged In"; metaverseDetails = " (Metaverse: Not Logged In)";
} }
QString domainDetails; QString domainDetails;
if (currentPlaceName == authedDomain && isDomainLoggedIn) { if (hasDomainLogIn) {
domainDetails = "Domain: Logged in as " + domainUsername; if (currentPlaceName == authedDomainName && isDomainLoggedIn) {
domainDetails = " (Domain: Logged in as " + domainUsername + ")";
} else {
domainDetails = " (Domain: Not Logged In)";
}
} else { } else {
domainDetails = "Domain: Not Logged In"; domainDetails = "";
} }
QString title = QString() + currentPlaceName + connectionStatus + " (" + metaverseDetails + ") (" + domainDetails + ")" QString title = currentPlaceName + connectionStatus + metaverseDetails + domainDetails + buildVersion;
+ buildVersion;
#ifndef WIN32 #ifndef WIN32
// crashes with vs2013/win32 // crashes with vs2013/win32

View file

@ -43,6 +43,7 @@
#include "avatar/AvatarManager.h" #include "avatar/AvatarManager.h"
#include "avatar/AvatarPackager.h" #include "avatar/AvatarPackager.h"
#include "AvatarBookmarks.h" #include "AvatarBookmarks.h"
#include "DomainAccountManager.h"
#include "MainWindow.h" #include "MainWindow.h"
#include "render/DrawStatus.h" #include "render/DrawStatus.h"
#include "scripting/MenuScriptingInterface.h" #include "scripting/MenuScriptingInterface.h"
@ -73,6 +74,7 @@ const char* EXCLUSION_GROUP_KEY = "exclusionGroup";
Menu::Menu() { Menu::Menu() {
auto dialogsManager = DependencyManager::get<DialogsManager>(); auto dialogsManager = DependencyManager::get<DialogsManager>();
auto accountManager = DependencyManager::get<AccountManager>(); auto accountManager = DependencyManager::get<AccountManager>();
auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
// File/Application menu ---------------------------------- // File/Application menu ----------------------------------
MenuWrapper* fileMenu = addMenu("File"); MenuWrapper* fileMenu = addMenu("File");
@ -89,11 +91,15 @@ Menu::Menu() {
} }
auto domainLogin = addActionToQMenuAndActionHash(fileMenu, "Domain: Log In"); auto domainLogin = addActionToQMenuAndActionHash(fileMenu, "Domain: Log In");
domainLogin->setVisible(false);
connect(domainLogin, &QAction::triggered, [] { connect(domainLogin, &QAction::triggered, [] {
auto dialogsManager = DependencyManager::get<DialogsManager>(); auto dialogsManager = DependencyManager::get<DialogsManager>();
dialogsManager->setDomainLoginState(); dialogsManager->setDomainLoginState();
dialogsManager->showDomainLoginDialog(); dialogsManager->showDomainLoginDialog();
}); });
connect(domainAccountManager.data(), &DomainAccountManager::hasLogInChanged, [domainLogin](bool hasLogIn) {
domainLogin->setVisible(hasLogIn);
});
// File > Quit // File > Quit
addActionToQMenuAndActionHash(fileMenu, MenuOption::Quit, Qt::CTRL | Qt::Key_Q, qApp, SLOT(quit()), QAction::QuitRole); addActionToQMenuAndActionHash(fileMenu, MenuOption::Quit, Qt::CTRL | Qt::Key_Q, qApp, SLOT(quit()), QAction::QuitRole);

View file

@ -29,50 +29,56 @@
const bool VERBOSE_HTTP_REQUEST_DEBUGGING = false; const bool VERBOSE_HTTP_REQUEST_DEBUGGING = false;
// ####### TODO: Enable and use these? DomainAccountManager::DomainAccountManager() {
// ####### TODO: Add storing domain URL and check against it when retrieving values?
// ####### TODO: Add storing _authURL and check against it when retrieving values?
/*
Setting::Handle<QString> domainAccessToken {"private/domainAccessToken", "" };
Setting::Handle<QString> domainAccessRefreshToken {"private/domainAccessToken", "" };
Setting::Handle<int> domainAccessTokenExpiresIn {"private/domainAccessTokenExpiresIn", -1 };
Setting::Handle<QString> domainAccessTokenType {"private/domainAccessTokenType", "" };
*/
DomainAccountManager::DomainAccountManager() :
_authURL(),
_username(),
_access_token(),
_refresh_token(),
_domain_name()
{
connect(this, &DomainAccountManager::loginComplete, this, &DomainAccountManager::sendInterfaceAccessTokenToServer); connect(this, &DomainAccountManager::loginComplete, this, &DomainAccountManager::sendInterfaceAccessTokenToServer);
} }
void DomainAccountManager::setAuthURL(const QUrl& authURL) { void DomainAccountManager::setDomainURL(const QUrl& domainURL) {
if (_authURL != authURL) { if (domainURL == _currentAuth.domainURL) {
_authURL = authURL; return;
qCDebug(networking) << "AccountManager URL for authenticated requests has been changed to" << qPrintable(_authURL.toString());
_access_token = "";
_refresh_token = "";
// ####### TODO: Restore and refresh OAuth2 tokens if have them for this domain.
// ####### TODO: Handle "keep me logged in".
} }
qCDebug(networking) << "DomainAccountManager domain URL has been changed to" << qPrintable(domainURL.toString());
// Restore OAuth2 authorization if have it for this domain.
if (_knownAuths.contains(domainURL)) {
_currentAuth = _knownAuths.value(domainURL);
} else {
_currentAuth = DomainAccountDetails();
_currentAuth.domainURL = domainURL;
}
emit hasLogInChanged(hasLogIn());
}
void DomainAccountManager::setAuthURL(const QUrl& authURL) {
if (authURL == _currentAuth.authURL) {
return;
}
_currentAuth.authURL = authURL;
qCDebug(networking) << "DomainAccountManager URL for authenticated requests has been changed to"
<< qPrintable(_currentAuth.authURL.toString());
_currentAuth.accessToken = "";
_currentAuth.refreshToken = "";
emit hasLogInChanged(hasLogIn());
}
bool DomainAccountManager::hasLogIn() {
return !_currentAuth.authURL.isEmpty();
} }
bool DomainAccountManager::isLoggedIn() { bool DomainAccountManager::isLoggedIn() {
return !_authURL.isEmpty() && hasValidAccessToken(); return !_currentAuth.authURL.isEmpty() && hasValidAccessToken();
} }
void DomainAccountManager::requestAccessToken(const QString& username, const QString& password) { void DomainAccountManager::requestAccessToken(const QString& username, const QString& password) {
_username = username; _currentAuth.username = username;
_access_token = ""; _currentAuth.accessToken = "";
_refresh_token = ""; _currentAuth.refreshToken = "";
QNetworkRequest request; QNetworkRequest request;
@ -86,9 +92,9 @@ void DomainAccountManager::requestAccessToken(const QString& username, const QSt
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("client_id=" + _clientID); formData.append("client_id=" + _currentAuth.clientID);
request.setUrl(_authURL); request.setUrl(_currentAuth.authURL);
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
@ -111,10 +117,16 @@ void DomainAccountManager::requestAccessTokenFinished() {
if (rootObject.contains("access_token")) { if (rootObject.contains("access_token")) {
// Success. // Success.
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
_domain_name = nodeList->getDomainHandler().getHostname(); _currentAuth.authedDomainName = nodeList->getDomainHandler().getHostname();
QUrl rootURL = requestReply->url(); QUrl rootURL = requestReply->url();
rootURL.setPath(""); rootURL.setPath("");
setTokensFromJSON(rootObject, rootURL); setTokensFromJSON(rootObject, rootURL);
// Remember domain login for the current Interface session.
_knownAuths.insert(_currentAuth.domainURL, _currentAuth);
// ####### TODO: Handle "keep me logged in".
emit loginComplete(); emit loginComplete();
} else { } else {
// Failure. // Failure.
@ -137,22 +149,19 @@ void DomainAccountManager::sendInterfaceAccessTokenToServer() {
bool DomainAccountManager::accessTokenIsExpired() { bool DomainAccountManager::accessTokenIsExpired() {
// ####### TODO: accessTokenIsExpired() // ####### TODO: accessTokenIsExpired()
return true; return true;
/*
return domainAccessTokenExpiresIn.get() != -1 && domainAccessTokenExpiresIn.get() <= QDateTime::currentMSecsSinceEpoch();
*/
} }
bool DomainAccountManager::hasValidAccessToken() { bool DomainAccountManager::hasValidAccessToken() {
// ###### TODO: wire this up to actually retrieve a token (based on session or storage) and confirm that it is in fact valid and relevant to the current domain. // ###### TODO: wire this up to actually retrieve a token (based on session or storage) and confirm that it is in fact valid and relevant to the current domain.
// QString currentDomainAccessToken = domainAccessToken.get(); // QString currentDomainAccessToken = domainAccessToken.get();
QString currentDomainAccessToken = _access_token; QString currentDomainAccessToken = _currentAuth.accessToken;
// if (currentDomainAccessToken.isEmpty() || accessTokenIsExpired()) { // if (currentDomainAccessToken.isEmpty() || accessTokenIsExpired()) {
if (currentDomainAccessToken.isEmpty()) { if (currentDomainAccessToken.isEmpty()) {
if (VERBOSE_HTTP_REQUEST_DEBUGGING) { if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
qCDebug(networking) << "An access token is required for requests to" qCDebug(networking) << "An access token is required for requests to"
<< qPrintable(_authURL.toString()); << qPrintable(_currentAuth.authURL.toString());
} }
return false; return false;
@ -168,17 +177,8 @@ bool DomainAccountManager::hasValidAccessToken() {
} }
void DomainAccountManager::setTokensFromJSON(const QJsonObject& jsonObject, const QUrl& url) { void DomainAccountManager::setTokensFromJSON(const QJsonObject& jsonObject, const QUrl& url) {
_access_token = jsonObject["access_token"].toString(); _currentAuth.accessToken = jsonObject["access_token"].toString();
_refresh_token = jsonObject["refresh_token"].toString(); _currentAuth.refreshToken = jsonObject["refresh_token"].toString();
// ####### TODO: Enable and use these?
// ####### TODO: Protect these per AccountManager?
// ######: TODO: clientID needed?
// qCDebug(networking) << "Storing a domain account with access-token for" << qPrintable(url.toString());
// domainAccessToken.set(jsonObject["access_token"].toString());
// domainAccessRefreshToken.set(jsonObject["refresh_token"].toString());
// domainAccessTokenExpiresIn.set(QDateTime::currentMSecsSinceEpoch() + (jsonObject["expires_in"].toDouble() * 1000));
// domainAccessTokenType.set(jsonObject["token_type"].toString());
} }
bool DomainAccountManager::checkAndSignalForAccessToken() { bool DomainAccountManager::checkAndSignalForAccessToken() {
@ -193,7 +193,7 @@ bool DomainAccountManager::checkAndSignalForAccessToken() {
// Emit a signal so somebody can call back to us and request an access token given a user name and password. // Emit a signal so somebody can call back to us and request an access token given a user name and password.
// Dialog can be hidden immediately after showing if we've just teleported to the domain, unless the signal is delayed. // Dialog can be hidden immediately after showing if we've just teleported to the domain, unless the signal is delayed.
auto domain = _authURL.host(); auto domain = _currentAuth.authURL.host();
QTimer::singleShot(500, this, [this, domain] { QTimer::singleShot(500, this, [this, domain] {
emit this->authRequired(domain); emit this->authRequired(domain);
}); });

View file

@ -18,49 +18,56 @@
#include <DependencyManager.h> #include <DependencyManager.h>
struct DomainAccountDetails {
QUrl domainURL;
QUrl authURL;
QString clientID;
QString username;
QString accessToken;
QString refreshToken;
QString authedDomainName;
};
class DomainAccountManager : public QObject, public Dependency { class DomainAccountManager : public QObject, public Dependency {
Q_OBJECT Q_OBJECT
public: public:
DomainAccountManager(); DomainAccountManager();
void setDomainURL(const QUrl& domainURL);
void setAuthURL(const QUrl& authURL); void setAuthURL(const QUrl& authURL);
void setClientID(const QString& clientID) { _clientID = clientID; } void setClientID(const QString& clientID) { _currentAuth.clientID = clientID; }
QString getUsername() { return _username; } const QString& getUsername() { return _currentAuth.username; }
QString getAccessToken() { return _access_token; } const QString& getAccessToken() { return _currentAuth.accessToken; }
QString getRefreshToken() { return _refresh_token; } const QString& getRefreshToken() { return _currentAuth.refreshToken; }
QString getAuthedDomain() { return _domain_name; } const QString& getAuthedDomainName() { return _currentAuth.authedDomainName; }
bool hasLogIn();
bool isLoggedIn(); bool isLoggedIn();
Q_INVOKABLE bool checkAndSignalForAccessToken(); Q_INVOKABLE bool checkAndSignalForAccessToken();
public slots: public slots:
void requestAccessToken(const QString& username, const QString& password); void requestAccessToken(const QString& username, const QString& password);
void requestAccessTokenFinished(); void requestAccessTokenFinished();
signals: signals:
void hasLogInChanged(bool hasLogIn);
void authRequired(const QString& domain); void authRequired(const QString& domain);
void loginComplete(); void loginComplete();
void loginFailed(); void loginFailed();
void logoutComplete(); void logoutComplete();
void newTokens(); void newTokens();
private slots:
private: private:
bool hasValidAccessToken(); bool hasValidAccessToken();
bool accessTokenIsExpired(); bool accessTokenIsExpired();
void setTokensFromJSON(const QJsonObject&, const QUrl& url); void setTokensFromJSON(const QJsonObject&, const QUrl& url);
void sendInterfaceAccessTokenToServer(); void sendInterfaceAccessTokenToServer();
QUrl _authURL; DomainAccountDetails _currentAuth;
QString _clientID; QHash<QUrl, DomainAccountDetails> _knownAuths; // <domainURL, DomainAccountDetails>
QString _username; // ####### TODO: Store elsewhere?
QString _access_token; // ####... ""
QString _refresh_token; // ####... ""
QString _domain_name; // ####... ""
}; };
#endif // hifi_DomainAccountManager_h #endif // hifi_DomainAccountManager_h

View file

@ -241,6 +241,8 @@ void DomainHandler::setURLAndID(QUrl domainURL, QUuid domainID) {
} }
} }
DependencyManager::get<DomainAccountManager>()->setDomainURL(_domainURL);
emit domainURLChanged(_domainURL); emit domainURLChanged(_domainURL);
if (_sockAddr.getPort() != domainPort) { if (_sockAddr.getPort() != domainPort) {
@ -527,6 +529,12 @@ bool DomainHandler::reasonSuggestsDomainLogin(ConnectionRefusedReason reasonCode
} }
void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer<ReceivedMessage> message) { void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer<ReceivedMessage> message) {
// Ignore any residual packets from previous domain.
if (!message->getSenderSockAddr().getAddress().isEqual(_sockAddr.getAddress())) {
return;
}
// we're hearing from this domain-server, don't need to refresh API info // we're hearing from this domain-server, don't need to refresh API info
_apiRefreshTimer.stop(); _apiRefreshTimer.stop();
@ -584,18 +592,28 @@ void DomainHandler::processDomainServerConnectionDeniedPacket(QSharedPointer<Rec
accountManager->generateNewUserKeypair(); accountManager->generateNewUserKeypair();
_connectionDenialsSinceKeypairRegen = 0; _connectionDenialsSinceKeypairRegen = 0;
} }
// Server with domain login will prompt for domain login, not metaverse, so reset domain values if asked for metaverse.
auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
domainAccountManager->setAuthURL(QUrl());
domainAccountManager->setClientID(QString());
} else if (reasonSuggestsDomainLogin(reasonCode)) { } else if (reasonSuggestsDomainLogin(reasonCode)) {
qCWarning(networking) << "Make sure you are logged in to the domain."; qCWarning(networking) << "Make sure you are logged in to the domain.";
auto accountManager = DependencyManager::get<DomainAccountManager>(); auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
if (!extraInfo.isEmpty()) { if (!extraInfo.isEmpty()) {
auto extraInfoComponents = extraInfo.split("|"); auto extraInfoComponents = extraInfo.split("|");
accountManager->setAuthURL(extraInfoComponents.value(0)); domainAccountManager->setAuthURL(extraInfoComponents.value(0));
accountManager->setClientID(extraInfoComponents.value(1)); domainAccountManager->setClientID(extraInfoComponents.value(1));
} else {
// Shouldn't occur, but just in case.
domainAccountManager->setAuthURL(QUrl());
domainAccountManager->setClientID(QString());
} }
if (!_hasCheckedForDomainAccessToken) { if (!_hasCheckedForDomainAccessToken) {
accountManager->checkAndSignalForAccessToken(); domainAccountManager->checkAndSignalForAccessToken();
_hasCheckedForDomainAccessToken = true; _hasCheckedForDomainAccessToken = true;
} }

View file

@ -476,33 +476,28 @@ void NodeList::sendDomainServerCheckIn() {
packetStream << _ownerType.load() << publicSockAddr << localSockAddr << _nodeTypesOfInterest.toList(); packetStream << _ownerType.load() << publicSockAddr << localSockAddr << _nodeTypesOfInterest.toList();
packetStream << DependencyManager::get<AddressManager>()->getPlaceName(); packetStream << DependencyManager::get<AddressManager>()->getPlaceName();
// ####### TODO: Also send if need to send new domainLogin data?
if (!domainIsConnected) { if (!domainIsConnected) {
// Metaverse account.
DataServerAccountInfo& accountInfo = accountManager->getAccountInfo(); DataServerAccountInfo& accountInfo = accountManager->getAccountInfo();
packetStream << accountInfo.getUsername(); packetStream << accountInfo.getUsername();
// if this is a connect request, and we can present a username signature, send it along // if this is a connect request, and we can present a username signature, send it along
if (requiresUsernameSignature && accountManager->getAccountInfo().hasPrivateKey()) { if (requiresUsernameSignature && accountManager->getAccountInfo().hasPrivateKey()) {
const QByteArray& usernameSignature = accountManager->getAccountInfo().getUsernameSignature(connectionToken); const QByteArray& usernameSignature = accountManager->getAccountInfo().getUsernameSignature(connectionToken);
packetStream << usernameSignature; packetStream << usernameSignature;
} else { } else {
// ####### TODO: Only append if are going to send domain username?
packetStream << QString(""); // Placeholder in case have domain username. packetStream << QString(""); // Placeholder in case have domain username.
} }
} else {
// ####### TODO: Only append if are going to send domainUsername?
packetStream << QString("") << QString(""); // Placeholders in case have domain username.
}
// Send domain domain login data from Interface to domain server. // Domain account.
if (_hasDomainAccountManager) { if (_hasDomainAccountManager) {
auto domainAccountManager = DependencyManager::get<DomainAccountManager>(); auto domainAccountManager = DependencyManager::get<DomainAccountManager>();
if (!domainAccountManager->getUsername().isEmpty()) { if (!domainAccountManager->getUsername().isEmpty() && !domainAccountManager->getAccessToken().isEmpty()) {
packetStream << domainAccountManager->getUsername(); packetStream << domainAccountManager->getUsername();
if (!domainAccountManager->getAccessToken().isEmpty()) {
packetStream << (domainAccountManager->getAccessToken() + ":" + domainAccountManager->getRefreshToken()); packetStream << (domainAccountManager->getAccessToken() + ":" + domainAccountManager->getRefreshToken());
} }
} }
} }
flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendDSCheckIn); flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendDSCheckIn);