diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 75cb11dab7..32b02382da 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -156,10 +156,8 @@ void DomainGatekeeper::processConnectRequestPacket(QSharedPointer_settingsManager.getAllKnownGroupNames().contains(group)) { - userPerms |= _server->_settingsManager.getPermissionsForGroup(group, QUuid()); -//#ifdef WANT_DEBUG - qDebug() << "| user-permissions: domain user " << verifiedDomainUserName << "is in group:" << group << "so:" << userPerms; -//#endif - + if (!verifiedDomainUserName.isEmpty()) { + auto userGroups = _domainGroupMemberships[verifiedDomainUserName]; + foreach (QString userGroup, userGroups) { + // Domain groups may be specified as comma- and/or space-separated lists of group names. + // For example, "silver gold, platinum". + auto domainGroups = _server->_settingsManager.getDomainGroupNames() + .filter(QRegularExpression("^(.*[\\s,])?" + userGroup + "([\\s,].*)?$", + QRegularExpression::CaseInsensitiveOption)); + foreach(QString domainGroup, domainGroups) { + userPerms |= _server->_settingsManager.getPermissionsForGroup(domainGroup, QUuid()); // No rank for domain groups. +#ifdef WANT_DEBUG + qDebug() << "| user-permissions: domain user " << verifiedDomainUserName << "is in group:" << domainGroup + << "so:" << userPerms; +#endif } } - userPerms.setVerifiedDomainUserName(verifiedDomainUserName); - userPerms.setVerifiedDomainUserGroups(verifiedDomainUserGroups); } if (verifiedUsername.isEmpty()) { @@ -289,6 +291,26 @@ NodePermissions DomainGatekeeper::setPermissionsForUser(bool isLocalUser, QStrin userPerms.setVerifiedUserName(verifiedUsername); } + // If this user is a known member of an domain group that is blacklisted, remove the implied permissions. + if (!verifiedDomainUserName.isEmpty()) { + auto userGroups = _domainGroupMemberships[verifiedDomainUserName]; + foreach(QString userGroup, userGroups) { + // Domain groups may be specified as comma- and/or space-separated lists of group names. + // For example, "silver gold, platinum". + auto domainGroups = _server->_settingsManager.getDomainBlacklistGroupNames() + .filter(QRegularExpression("^(.*[\\s,])?" + userGroup + "([\\s,].*)?$", + QRegularExpression::CaseInsensitiveOption)); + foreach(QString domainGroup, domainGroups) { + userPerms &= ~_server->_settingsManager.getForbiddensForGroup(domainGroup, QUuid()); +#ifdef WANT_DEBUG + qDebug() << "| user-permissions: domain user is in blacklist group:" << domainGroup << "so:" << userPerms; +#endif + } + } + + userPerms.setVerifiedDomainUserName(verifiedDomainUserName); + } + #ifdef WANT_DEBUG qDebug() << "| user-permissions: final:" << userPerms; #endif @@ -309,7 +331,6 @@ void DomainGatekeeper::updateNodePermissions() { // authentication and verifiedUsername is only set once they user's key has been confirmed. QString verifiedUsername = node->getPermissions().getVerifiedUserName(); QString verifiedDomainUserName = node->getPermissions().getVerifiedDomainUserName(); - QStringList verifiedDomainUserGroups = node->getPermissions().getVerifiedDomainUserGroups(); NodePermissions userPerms(NodePermissionsKey(verifiedUsername, 0)); if (node->getPermissions().isAssignment) { @@ -345,8 +366,7 @@ void DomainGatekeeper::updateNodePermissions() { } userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, verifiedDomainUserName, - verifiedDomainUserGroups, connectingAddr.getAddress(), - hardwareAddress, machineFingerprint); + connectingAddr.getAddress(), hardwareAddress, machineFingerprint); } node->setPermissions(userPerms); @@ -486,31 +506,28 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect // ####### TODO: OAuth2 corollary of metaverse code, above. + getDomainGroupMemberships(domainUsernameSignature); // Optimistically get started on group memberships. +#ifdef WANT_DEBUG + qDebug() << "stalling login because we have no domain username-signature:" << domainUsername; +#endif return SharedNodePointer(); } else if (verifyDomainUserSignature(domainUsername, domainUsernameSignature, nodeConnection.senderSockAddr)) { // User's domain identity is confirmed. - - // ####### TODO: Get user's domain group memberships (WordPress roles) from domain. - // This may already be provided at the same time as the "verify" call to the domain API. - // If it isn't, need to initiate getting them then handle their receipt along the lines of the - // metaverse code, above. - verifiedDomainUserGroups = QString("test-group").toLower().split(" "); - + getDomainGroupMemberships(domainUsername); verifiedDomainUsername = domainUsername.toLower(); - } else { // User's identity didn't check out. // ####### TODO: OAuth2 corollary of metaverse code, above. #ifdef WANT_DEBUG - qDebug() << "stalling login because signature verification failed:" << username; + qDebug() << "stalling login because domain signature verification failed:" << domainUsername; #endif return SharedNodePointer(); } } - userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, verifiedDomainUsername, verifiedDomainUserGroups, + userPerms = setPermissionsForUser(isLocalUser, verifiedUsername, verifiedDomainUsername, nodeConnection.senderSockAddr.getAddress(), nodeConnection.hardwareAddress, nodeConnection.machineFingerprint); @@ -1004,7 +1021,6 @@ void DomainGatekeeper::getGroupMemberships(const QString& username) { AccountManagerAuth::Required, QNetworkAccessManager::PostOperation, callbackParams, QJsonDocument(json).toJson()); - } QString extractUsernameFromGroupMembershipsReply(QNetworkReply* requestReply) { @@ -1059,6 +1075,21 @@ void DomainGatekeeper::getIsGroupMemberErrorCallback(QNetworkReply* requestReply _inFlightGroupMembershipsRequests.remove(extractUsernameFromGroupMembershipsReply(requestReply)); } + +void DomainGatekeeper::getDomainGroupMemberships(const QString& domainUserName) { + + // ####### TODO: Get user's domain group memberships (WordPress roles) from domain. + // This may be able to be provided at the same time as the "authenticate user" call to the domain API, in which case + // a copy of some of the following code can be made there. However, this code is still needed for refreshing groups. + + // ####### TODO: Check how often this method and the WordPress API is called. + + QStringList wordpressGroupsForUser; + wordpressGroupsForUser << "silVER" << "gold" << "coal"; + _domainGroupMemberships[domainUserName] = wordpressGroupsForUser; +} + + void DomainGatekeeper::getDomainOwnerFriendsList() { JSONCallbackParameters callbackParams; callbackParams.callbackReceiver = this; @@ -1107,6 +1138,7 @@ void DomainGatekeeper::getDomainOwnerFriendsListErrorCallback(QNetworkReply* req qDebug() << "getDomainOwnerFriendsList api call failed:" << requestReply->error(); } +// ####### TODO: Domain equivalent or addition void DomainGatekeeper::refreshGroupsCache() { // if agents are connected to this domain, refresh our cached information about groups and memberships in such. getDomainOwnerFriendsList(); diff --git a/domain-server/src/DomainGatekeeper.h b/domain-server/src/DomainGatekeeper.h index 172c63028c..99bd875457 100644 --- a/domain-server/src/DomainGatekeeper.h +++ b/domain-server/src/DomainGatekeeper.h @@ -128,14 +128,17 @@ private: QSet _inFlightGroupMembershipsRequests; // keep track of which we've already asked for NodePermissions setPermissionsForUser(bool isLocalUser, QString verifiedUsername, QString verifiedDomainUsername, - QStringList verifiedDomainUserGroups, const QHostAddress& senderAddress, - const QString& hardwareAddress, const QUuid& machineFingerprint); + const QHostAddress& senderAddress, const QString& hardwareAddress, + const QUuid& machineFingerprint); void getGroupMemberships(const QString& username); // void getIsGroupMember(const QString& username, const QUuid groupID); void getDomainOwnerFriendsList(); + // Login and groups for domain, separate from metaverse. bool domainHasLogin(); + void getDomainGroupMemberships(const QString& domainUserName); + QHash _domainGroupMemberships; // // Local ID management. void initLocalIDManagement(); diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 73d78a5c70..30ca15a51e 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -2185,6 +2185,24 @@ QList DomainServerSettingsManager::getBlacklistGroupIDs() { return result.toList(); } +QStringList DomainServerSettingsManager::getDomainGroupNames() { + // Names as configured in domain server; not necessarily mnetaverse groups. + QSet result; + foreach(NodePermissionsKey groupKey, _groupPermissions.keys()) { + result += _groupPermissions[groupKey]->getID(); + } + return result.toList(); +} + +QStringList DomainServerSettingsManager::getDomainBlacklistGroupNames() { + // Names as configured in domain server; not necessarily mnetaverse groups. + QSet result; + foreach (NodePermissionsKey groupKey, _groupForbiddens.keys()) { + result += _groupForbiddens[groupKey]->getID(); + } + return result.toList(); +} + void DomainServerSettingsManager::debugDumpGroupsState() { qDebug() << "--------- GROUPS ---------"; diff --git a/domain-server/src/DomainServerSettingsManager.h b/domain-server/src/DomainServerSettingsManager.h index e28b9f6cd1..8c18c22b32 100644 --- a/domain-server/src/DomainServerSettingsManager.h +++ b/domain-server/src/DomainServerSettingsManager.h @@ -105,6 +105,9 @@ public: QList getGroupIDs(); QList getBlacklistGroupIDs(); + QStringList getDomainGroupNames(); + QStringList getDomainBlacklistGroupNames(); + // these are used to locally cache the result of calling "api/v1/groups/.../is_member/..." on metaverse's api void clearGroupMemberships(const QString& name) { _groupMembership[name.toLower()].clear(); } void recordGroupMembership(const QString& name, const QUuid groupID, QUuid rankID); diff --git a/libraries/networking/src/NodePermissions.h b/libraries/networking/src/NodePermissions.h index 6658b7ea12..82c008feef 100644 --- a/libraries/networking/src/NodePermissions.h +++ b/libraries/networking/src/NodePermissions.h @@ -54,8 +54,6 @@ public: void setVerifiedDomainUserName(QString userName) { _verifiedDomainUserName = userName.toLower(); } const QString& getVerifiedDomainUserName() const { return _verifiedDomainUserName; } - void setVerifiedDomainUserGroups(QStringList userGroups) { _verifiedDomainUserGroups = userGroups; } - const QStringList& getVerifiedDomainUserGroups() const { return _verifiedDomainUserGroups; } void setGroupID(QUuid groupID) { _groupID = groupID; if (!groupID.isNull()) { _groupIDSet = true; }} QUuid getGroupID() const { return _groupID; } @@ -106,7 +104,6 @@ protected: QUuid _rankID { QUuid() }; // 0 unless this is for a group QString _verifiedUserName; QString _verifiedDomainUserName; - QStringList _verifiedDomainUserGroups; bool _groupIDSet { false }; QUuid _groupID;