mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 19:24:32 +02:00
don't allow a username and password for domain auth, require access token
This commit is contained in:
parent
31080c41df
commit
3969c8bfe0
7 changed files with 123 additions and 86 deletions
|
@ -64,7 +64,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
|
||||
qRegisterMetaType<DomainServerWebSessionData>("DomainServerWebSessionData");
|
||||
qRegisterMetaTypeStreamOperators<DomainServerWebSessionData>("DomainServerWebSessionData");
|
||||
|
||||
|
||||
if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth() && optionallySetupAssignmentPayment()) {
|
||||
// we either read a certificate and private key or were not passed one
|
||||
// and completed login or did not need to
|
||||
|
@ -75,7 +75,7 @@ DomainServer::DomainServer(int argc, char* argv[]) :
|
|||
loadExistingSessionsFromSettings();
|
||||
|
||||
// check if we have the flag that enables dynamic IP
|
||||
setupDynamicIPAddressUpdating();
|
||||
setupDynamicSocketUpdating();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,6 +151,17 @@ bool DomainServer::optionallySetupOAuth() {
|
|||
|
||||
const QVariantMap& settingsMap = _settingsManager.getSettingsMap();
|
||||
_oauthProviderURL = QUrl(settingsMap.value(OAUTH_PROVIDER_URL_OPTION).toString());
|
||||
|
||||
// if we don't have an oauth provider URL then we default to the default node auth url
|
||||
if (_oauthProviderURL.isEmpty()) {
|
||||
_oauthProviderURL = DEFAULT_NODE_AUTH_URL;
|
||||
}
|
||||
|
||||
// setup our account manager with that _oauthProviderURL
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
accountManager.disableSettingsFilePersistence();
|
||||
accountManager.setAuthURL(_oauthProviderURL);
|
||||
|
||||
_oauthClientID = settingsMap.value(OAUTH_CLIENT_ID_OPTION).toString();
|
||||
_oauthClientSecret = QProcessEnvironment::systemEnvironment().value(OAUTH_CLIENT_SECRET_ENV);
|
||||
_hostname = settingsMap.value(REDIRECT_HOSTNAME_OPTION).toString();
|
||||
|
@ -225,41 +236,37 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) {
|
|||
addStaticAssignmentsToQueue();
|
||||
}
|
||||
|
||||
const QString HIFI_USERNAME_ENV_KEY = "DOMAIN_SERVER_USERNAME";
|
||||
const QString HIFI_PASSWORD_ENV_KEY = "DOMAIN_SERVER_PASSWORD";
|
||||
|
||||
bool DomainServer::hasOAuthProviderAndAuthInformation() {
|
||||
bool DomainServer::didSetupAccountManagerWithAccessToken() {
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
|
||||
if (accountManager.hasValidAccessToken()) {
|
||||
// we already gave the account manager a valid access token
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_oauthProviderURL.isEmpty()) {
|
||||
// check for an access-token in our settings, can optionally be overidden by env value
|
||||
const QString ACCESS_TOKEN_KEY_PATH = "metaverse.access_token";
|
||||
const QString ENV_ACCESS_TOKEN_KEY = "DOMAIN_SERVER_ACCESS_TOKEN";
|
||||
|
||||
static bool hasAttemptedAuthWithOAuthProvider = false;
|
||||
QString accessToken = QProcessEnvironment::systemEnvironment().value(ENV_ACCESS_TOKEN_KEY);
|
||||
|
||||
if (!hasAttemptedAuthWithOAuthProvider) {
|
||||
AccountManager& accountManager = AccountManager::getInstance();
|
||||
accountManager.setAuthURL(_oauthProviderURL);
|
||||
if (accessToken.isEmpty()) {
|
||||
const QVariant* accessTokenVariant = valueForKeyPath(_settingsManager.getSettingsMap(), ACCESS_TOKEN_KEY_PATH);
|
||||
|
||||
if (!accountManager.hasValidAccessToken()) {
|
||||
// we don't have a valid access token so we need to get one
|
||||
// check if we have a username and password set via env
|
||||
QString username = QProcessEnvironment::systemEnvironment().value(HIFI_USERNAME_ENV_KEY);
|
||||
QString password = QProcessEnvironment::systemEnvironment().value(HIFI_PASSWORD_ENV_KEY);
|
||||
|
||||
if (!username.isEmpty() && !password.isEmpty()) {
|
||||
|
||||
accountManager.requestAccessToken(username, password);
|
||||
|
||||
// connect to loginFailed signal from AccountManager so we can quit if that is the case
|
||||
connect(&accountManager, &AccountManager::loginFailed, this, &DomainServer::loginFailed);
|
||||
} else {
|
||||
qDebug() << "Missing access-token or username and password combination. domain-server will now quit.";
|
||||
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
|
||||
return false;
|
||||
}
|
||||
if (accessTokenVariant->canConvert(QMetaType::QString)) {
|
||||
accessToken = accessTokenVariant->toString();
|
||||
} else {
|
||||
qDebug() << "A domain-server feature that requires authentication is enabled but no access token is present."
|
||||
<< "Set an access token via the web interface, in your user or master config"
|
||||
<< "at keypath metaverse.access_token or in your ENV at key DOMAIN_SERVER_ACCESS_TOKEN";
|
||||
return false;
|
||||
}
|
||||
|
||||
hasAttemptedAuthWithOAuthProvider = true;
|
||||
}
|
||||
|
||||
// give this access token to the AccountManager
|
||||
accountManager.setAccessTokenForCurrentAuthURL(accessToken);
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
@ -277,7 +284,7 @@ bool DomainServer::optionallySetupAssignmentPayment() {
|
|||
|
||||
if (settingsMap.contains(PAY_FOR_ASSIGNMENTS_OPTION) &&
|
||||
settingsMap.value(PAY_FOR_ASSIGNMENTS_OPTION).toBool() &&
|
||||
hasOAuthProviderAndAuthInformation()) {
|
||||
didSetupAccountManagerWithAccessToken()) {
|
||||
|
||||
qDebug() << "Assignments will be paid for via" << qPrintable(_oauthProviderURL.toString());
|
||||
|
||||
|
@ -299,20 +306,20 @@ bool DomainServer::optionallySetupAssignmentPayment() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void DomainServer::setupDynamicIPAddressUpdating() {
|
||||
const QString ENABLE_DYNAMIC_IP_UPDATING_OPTION = "update-ip";
|
||||
void DomainServer::setupDynamicSocketUpdating() {
|
||||
const QString ENABLE_DYNAMIC_SOCKET_UPDATING_KEY_PATH = "metaverse.update_sockets";
|
||||
|
||||
const QVariantMap& settingsMap = _settingsManager.getSettingsMap();
|
||||
const QVariant* updateSocketValue = valueForKeyPath(_settingsManager.getSettingsMap(),
|
||||
ENABLE_DYNAMIC_SOCKET_UPDATING_KEY_PATH);
|
||||
|
||||
if (settingsMap.contains(ENABLE_DYNAMIC_IP_UPDATING_OPTION) &&
|
||||
settingsMap.value(ENABLE_DYNAMIC_IP_UPDATING_OPTION).toBool() &&
|
||||
hasOAuthProviderAndAuthInformation()) {
|
||||
if (updateSocketValue && updateSocketValue->canConvert(QMetaType::Bool) && updateSocketValue->toBool()
|
||||
&& didSetupAccountManagerWithAccessToken()) {
|
||||
|
||||
LimitedNodeList* nodeList = LimitedNodeList::getInstance();
|
||||
const QUuid& domainID = nodeList->getSessionUUID();
|
||||
|
||||
if (!domainID.isNull()) {
|
||||
qDebug() << "domain-server IP address will be updated for domain with ID"
|
||||
qDebug() << "domain-server socket will be updated for domain with ID"
|
||||
<< uuidStringWithoutCurlyBraces(domainID) << "via" << _oauthProviderURL.toString();
|
||||
|
||||
const int STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS = 30 * 1000;
|
||||
|
|
|
@ -68,9 +68,9 @@ private:
|
|||
void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid());
|
||||
bool optionallySetupOAuth();
|
||||
bool optionallyReadX509KeyAndCertificate();
|
||||
bool hasOAuthProviderAndAuthInformation();
|
||||
bool didSetupAccountManagerWithAccessToken();
|
||||
bool optionallySetupAssignmentPayment();
|
||||
void setupDynamicIPAddressUpdating();
|
||||
void setupDynamicSocketUpdating();
|
||||
|
||||
void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);
|
||||
|
||||
|
|
|
@ -52,7 +52,8 @@ JSONCallbackParameters::JSONCallbackParameters(QObject* jsonCallbackReceiver, co
|
|||
AccountManager::AccountManager() :
|
||||
_authURL(),
|
||||
_pendingCallbackMap(),
|
||||
_accountInfo()
|
||||
_accountInfo(),
|
||||
_shouldPersistToSettingsFile(false)
|
||||
{
|
||||
qRegisterMetaType<OAuthAccessToken>("OAuthAccessToken");
|
||||
qRegisterMetaTypeStreamOperators<OAuthAccessToken>("OAuthAccessToken");
|
||||
|
@ -76,14 +77,18 @@ void AccountManager::logout() {
|
|||
|
||||
emit balanceChanged(0);
|
||||
connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged);
|
||||
|
||||
QSettings settings;
|
||||
settings.beginGroup(ACCOUNTS_GROUP);
|
||||
|
||||
QString keyURLString(_authURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE));
|
||||
settings.remove(keyURLString);
|
||||
|
||||
qDebug() << "Removed account info for" << _authURL << "from in-memory accounts and .ini file";
|
||||
|
||||
if (_shouldPersistToSettingsFile) {
|
||||
QSettings settings;
|
||||
settings.beginGroup(ACCOUNTS_GROUP);
|
||||
|
||||
QString keyURLString(_authURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE));
|
||||
settings.remove(keyURLString);
|
||||
|
||||
qDebug() << "Removed account info for" << _authURL << "from in-memory accounts and .ini file";
|
||||
} else {
|
||||
qDebug() << "Cleared data server account info in account manager.";
|
||||
}
|
||||
|
||||
emit logoutComplete();
|
||||
// the username has changed to blank
|
||||
|
@ -109,28 +114,29 @@ void AccountManager::setAuthURL(const QUrl& authURL) {
|
|||
if (_authURL != authURL) {
|
||||
_authURL = authURL;
|
||||
|
||||
qDebug() << "URL for node authentication has been changed to" << qPrintable(_authURL.toString());
|
||||
qDebug() << "Re-setting authentication flow.";
|
||||
|
||||
// check if there are existing access tokens to load from settings
|
||||
QSettings settings;
|
||||
settings.beginGroup(ACCOUNTS_GROUP);
|
||||
|
||||
foreach(const QString& key, settings.allKeys()) {
|
||||
// take a key copy to perform the double slash replacement
|
||||
QString keyCopy(key);
|
||||
QUrl keyURL(keyCopy.replace("slashslash", "//"));
|
||||
|
||||
if (keyURL == _authURL) {
|
||||
// pull out the stored access token and store it in memory
|
||||
_accountInfo = settings.value(key).value<DataServerAccountInfo>();
|
||||
qDebug() << "Found a data-server access token for" << qPrintable(keyURL.toString());
|
||||
|
||||
// profile info isn't guaranteed to be saved too
|
||||
if (_accountInfo.hasProfile()) {
|
||||
emit profileChanged();
|
||||
} else {
|
||||
requestProfile();
|
||||
qDebug() << "AccountManager URL for authenticated requests has been changed to" << qPrintable(_authURL.toString());
|
||||
|
||||
if (_shouldPersistToSettingsFile) {
|
||||
// check if there are existing access tokens to load from settings
|
||||
QSettings settings;
|
||||
settings.beginGroup(ACCOUNTS_GROUP);
|
||||
|
||||
foreach(const QString& key, settings.allKeys()) {
|
||||
// take a key copy to perform the double slash replacement
|
||||
QString keyCopy(key);
|
||||
QUrl keyURL(keyCopy.replace("slashslash", "//"));
|
||||
|
||||
if (keyURL == _authURL) {
|
||||
// pull out the stored access token and store it in memory
|
||||
_accountInfo = settings.value(key).value<DataServerAccountInfo>();
|
||||
qDebug() << "Found a data-server access token for" << qPrintable(keyURL.toString());
|
||||
|
||||
// profile info isn't guaranteed to be saved too
|
||||
if (_accountInfo.hasProfile()) {
|
||||
emit profileChanged();
|
||||
} else {
|
||||
requestProfile();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -307,8 +313,9 @@ void AccountManager::passErrorToCallback(QNetworkReply* requestReply) {
|
|||
}
|
||||
|
||||
bool AccountManager::hasValidAccessToken() {
|
||||
|
||||
|
||||
if (_accountInfo.getAccessToken().token.isEmpty() || _accountInfo.getAccessToken().isExpired()) {
|
||||
|
||||
if (VERBOSE_HTTP_REQUEST_DEBUGGING) {
|
||||
qDebug() << "An access token is required for requests to" << qPrintable(_authURL.toString());
|
||||
}
|
||||
|
@ -330,6 +337,19 @@ bool AccountManager::checkAndSignalForAccessToken() {
|
|||
return hasToken;
|
||||
}
|
||||
|
||||
void AccountManager::setAccessTokenForCurrentAuthURL(const QString& accessToken) {
|
||||
// clear our current DataServerAccountInfo
|
||||
_accountInfo = DataServerAccountInfo();
|
||||
|
||||
// start the new account info with a new OAuthAccessToken
|
||||
OAuthAccessToken newOAuthToken;
|
||||
newOAuthToken.token = accessToken;
|
||||
|
||||
qDebug() << "Setting new account manager access token to" << accessToken;
|
||||
|
||||
_accountInfo.setAccessToken(newOAuthToken);
|
||||
}
|
||||
|
||||
void AccountManager::requestAccessToken(const QString& login, const QString& password) {
|
||||
|
||||
NetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
|
@ -380,12 +400,14 @@ void AccountManager::requestAccessTokenFinished() {
|
|||
_accountInfo.setAccessTokenFromJSON(rootObject);
|
||||
|
||||
emit loginComplete(rootURL);
|
||||
|
||||
// store this access token into the local settings
|
||||
QSettings localSettings;
|
||||
localSettings.beginGroup(ACCOUNTS_GROUP);
|
||||
localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE),
|
||||
QVariant::fromValue(_accountInfo));
|
||||
|
||||
if (_shouldPersistToSettingsFile) {
|
||||
// store this access token into the local settings
|
||||
QSettings localSettings;
|
||||
localSettings.beginGroup(ACCOUNTS_GROUP);
|
||||
localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE),
|
||||
QVariant::fromValue(_accountInfo));
|
||||
}
|
||||
|
||||
requestProfile();
|
||||
}
|
||||
|
@ -429,13 +451,16 @@ void AccountManager::requestProfileFinished() {
|
|||
// the username has changed to whatever came back
|
||||
emit usernameChanged(_accountInfo.getUsername());
|
||||
|
||||
// store the whole profile into the local settings
|
||||
QUrl rootURL = profileReply->url();
|
||||
rootURL.setPath("");
|
||||
QSettings localSettings;
|
||||
localSettings.beginGroup(ACCOUNTS_GROUP);
|
||||
localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE),
|
||||
QVariant::fromValue(_accountInfo));
|
||||
if (_shouldPersistToSettingsFile) {
|
||||
// store the whole profile into the local settings
|
||||
QUrl rootURL = profileReply->url();
|
||||
rootURL.setPath("");
|
||||
QSettings localSettings;
|
||||
localSettings.beginGroup(ACCOUNTS_GROUP);
|
||||
localSettings.setValue(rootURL.toString().replace("//", DOUBLE_SLASH_SUBSTITUTE),
|
||||
QVariant::fromValue(_accountInfo));
|
||||
}
|
||||
|
||||
} else {
|
||||
// TODO: error handling
|
||||
qDebug() << "Error in response for profile";
|
||||
|
|
|
@ -59,10 +59,13 @@ public:
|
|||
const QUrl& getAuthURL() const { return _authURL; }
|
||||
void setAuthURL(const QUrl& authURL);
|
||||
bool hasAuthEndpoint() { return !_authURL.isEmpty(); }
|
||||
|
||||
void disableSettingsFilePersistence() { _shouldPersistToSettingsFile = false; }
|
||||
|
||||
bool isLoggedIn() { return !_authURL.isEmpty() && hasValidAccessToken(); }
|
||||
bool hasValidAccessToken();
|
||||
Q_INVOKABLE bool checkAndSignalForAccessToken();
|
||||
void setAccessTokenForCurrentAuthURL(const QString& accessToken);
|
||||
|
||||
void requestAccessToken(const QString& login, const QString& password);
|
||||
void requestProfile();
|
||||
|
@ -107,6 +110,7 @@ private:
|
|||
QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap;
|
||||
|
||||
DataServerAccountInfo _accountInfo;
|
||||
bool _shouldPersistToSettingsFile;
|
||||
};
|
||||
|
||||
#endif // hifi_AccountManager_h
|
||||
|
|
|
@ -27,6 +27,7 @@ public:
|
|||
DataServerAccountInfo& operator=(const DataServerAccountInfo& otherInfo);
|
||||
|
||||
const OAuthAccessToken& getAccessToken() const { return _accessToken; }
|
||||
void setAccessToken(const OAuthAccessToken& accessToken) { _accessToken = accessToken; }
|
||||
void setAccessTokenFromJSON(const QJsonObject& jsonObject);
|
||||
|
||||
const QString& getUsername() const { return _username; }
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
OAuthAccessToken::OAuthAccessToken() :
|
||||
token(),
|
||||
refreshToken(),
|
||||
expiryTimestamp(0),
|
||||
expiryTimestamp(-1),
|
||||
tokenType()
|
||||
{
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
|
||||
QByteArray authorizationHeaderValue() const { return QString("Bearer %1").arg(token).toUtf8(); }
|
||||
|
||||
bool isExpired() const { return expiryTimestamp <= QDateTime::currentMSecsSinceEpoch(); }
|
||||
bool isExpired() const { return expiryTimestamp != -1 && expiryTimestamp <= QDateTime::currentMSecsSinceEpoch(); }
|
||||
|
||||
QString token;
|
||||
QString refreshToken;
|
||||
|
|
Loading…
Reference in a new issue