add a list of blacklist groups which remove permissions rather than grant them

This commit is contained in:
Seth Alves 2016-06-24 14:03:43 -07:00
parent cc1b6f0cb2
commit ead6e476a9
6 changed files with 237 additions and 12 deletions

View file

@ -534,6 +534,77 @@
}
],
"columns": [
{
"name": "permissions_id",
"label": "Group Name"
},
{
"name": "group_id",
"label": "Group ID",
"readonly": true
},
{
"name": "id_can_connect",
"label": "Connect",
"type": "checkbox",
"editable": true,
"default": true
},
{
"name": "id_can_adjust_locks",
"label": "Lock / Unlock",
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_rez",
"label": "Rez",
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_rez_tmp",
"label": "Rez Temporary",
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_write_to_asset_server",
"label": "Write Assets",
"type": "checkbox",
"editable": true,
"default": false
},
{
"name": "id_can_connect_past_max_capacity",
"label": "Ignore Max Capacity",
"type": "checkbox",
"editable": true,
"default": false
}
]
},
{
"name": "group_forbiddens",
"type": "table",
"caption": "Permissions denied to Users in Groups",
"can_add_new_rows": true,
"groups": [
{
"label": "Group",
"span": 2
},
{
"label": "Permissions <a data-toggle='tooltip' data-html='true' title='<p><strong>Domain-Wide User Permissions</strong></p><ul><li><strong>Connect</strong><br />Sets whether users in specific groups can connect to the domain.</li><li><strong>Lock / Unlock</strong><br />Sets whether users in specific groups can change the &ldquo;locked&rdquo; property of an entity (either from on to off or off to on).</li><li><strong>Rez</strong><br />Sets whether users in specific groups can create new entities.</li><li><strong>Rez Temporary</strong><br />Sets whether users in specific groups can create new entities with a finite lifetime.</li><li><strong>Write Assets</strong><br />Sets whether users in specific groups can make changes to the domain&rsquo;s asset-server assets.</li><li><strong>Ignore Max Capacity</strong><br />Sets whether user in specific groups can connect even if the domain has reached or exceeded its maximum allowed agents.</li></ul><p>Permissions granted to a specific user will be a union of the permissions granted to the groups they are in. Group permissions are only granted if the user doesn&rsquo;t have their own row in the previous section.</p>'>?</a>",
"span": 6
}
],
"columns": [
{
"name": "permissions_id",

View file

@ -149,15 +149,21 @@ NodePermissions DomainGatekeeper::applyPermissionsForUser(bool isLocalUser,
}
// if this user is a known member of a group, give them the implied permissions
foreach (QUuid groupID, _server->_settingsManager.getKnownGroupIDs()) {
if (groupID.isNull()) {
continue;
}
foreach (QUuid groupID, _server->_settingsManager.getGroupIDs()) {
if (_server->_settingsManager.isGroupMember(verifiedUsername, groupID)) {
userPerms |= _server->_settingsManager.getPermissionsForGroup(groupID);
qDebug() << "user-permissions: user is in group:" << groupID << "so:" << userPerms;
}
}
// if this user is a known member of a blacklist group, remove the implied permissions
foreach (QUuid groupID, _server->_settingsManager.getBlacklistGroupIDs()) {
if (_server->_settingsManager.isGroupMember(verifiedUsername, groupID)) {
userPerms &= ~_server->_settingsManager.getForbiddensForGroup(groupID);
qDebug() << "user-permissions: user is in blacklist group:" << groupID << "so:" << userPerms;
}
}
}
}
@ -689,7 +695,7 @@ void DomainGatekeeper::processICEPingReplyPacket(QSharedPointer<ReceivedMessage>
void DomainGatekeeper::getGroupMemberships(const QString& username) {
// loop through the groups mentioned on the settings page and ask if this user is in each. The replies
// will be received asynchronously and permissions will be updated as the answers come in.
QList<QUuid> groupIDs = _server->_settingsManager.getKnownGroupIDs();
QList<QUuid> groupIDs = _server->_settingsManager.getGroupIDs() + _server->_settingsManager.getBlacklistGroupIDs();
foreach (QUuid groupID, groupIDs) {
if (groupID.isNull()) {
continue;

View file

@ -328,6 +328,9 @@ void DomainServerSettingsManager::packPermissions() {
// save settings for groups
packPermissionsForMap("permissions", _groupPermissions, GROUP_PERMISSIONS_KEYPATH);
// save settings for blacklist groups
packPermissionsForMap("permissions", _groupForbiddens, GROUP_FORBIDDENS_KEYPATH);
persistToFile();
_configMap.loadMasterAndUserConfig(_argumentList);
}
@ -338,6 +341,7 @@ void DomainServerSettingsManager::unpackPermissions() {
_standardAgentPermissions.clear();
_agentPermissions.clear();
_groupPermissions.clear();
_groupForbiddens.clear();
bool foundLocalhost = false;
bool foundAnonymous = false;
@ -363,6 +367,12 @@ void DomainServerSettingsManager::unpackPermissions() {
groupPermissions = valueForKeyPath(_configMap.getUserConfig(), GROUP_PERMISSIONS_KEYPATH, true);
(*groupPermissions) = QVariantList();
}
QVariant* groupForbiddens = valueForKeyPath(_configMap.getUserConfig(), GROUP_FORBIDDENS_KEYPATH);
if (!groupForbiddens || !groupForbiddens->canConvert(QMetaType::QVariantList)) {
qDebug() << "failed to extract group forbiddens from settings.";
groupForbiddens = valueForKeyPath(_configMap.getUserConfig(), GROUP_FORBIDDENS_KEYPATH, true);
(*groupForbiddens) = QVariantList();
}
QList<QVariant> standardPermissionsList = standardPermissions->toList();
foreach (QVariant permsHash, standardPermissionsList) {
@ -411,6 +421,23 @@ void DomainServerSettingsManager::unpackPermissions() {
}
}
QList<QVariant> groupForbiddensList = groupForbiddens->toList();
foreach (QVariant permsHash, groupForbiddensList) {
NodePermissionsPointer perms { new NodePermissions(permsHash.toMap()) };
QString id = perms->getID();
if (_groupForbiddens.contains(id)) {
qDebug() << "duplicate name in group forbiddens table: " << id;
_groupForbiddens[id] |= perms;
needPack = true;
} else {
_groupForbiddens[id] = perms;
}
if (perms->isGroup()) {
// the group-id was cached. hook-up the id in the id->group hash
_groupByID[perms->getGroupID()] = _groupForbiddens[id];
}
}
// if any of the standard names are missing, add them
if (!foundLocalhost) {
NodePermissionsPointer perms { new NodePermissions(NodePermissions::standardNameLocalhost) };
@ -445,7 +472,8 @@ void DomainServerSettingsManager::unpackPermissions() {
#ifdef WANT_DEBUG
qDebug() << "--------------- permissions ---------------------";
QList<QHash<QString, NodePermissionsPointer>> permissionsSets;
permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get() << _groupPermissions.get();
permissionsSets << _standardAgentPermissions.get() << _agentPermissions.get()
<< _groupPermissions.get() << _groupForbiddens.get();
foreach (auto permissionSet, permissionsSets) {
QHashIterator<QString, NodePermissionsPointer> i(permissionSet);
while (i.hasNext()) {
@ -499,6 +527,27 @@ NodePermissions DomainServerSettingsManager::getPermissionsForGroup(const QUuid&
}
NodePermissions DomainServerSettingsManager::getForbiddensForGroup(const QString& groupname) const {
if (_groupForbiddens.contains(groupname)) {
return *(_groupForbiddens[groupname].get());
}
NodePermissions nullForbiddens;
nullForbiddens.setAll(false);
return nullForbiddens;
}
NodePermissions DomainServerSettingsManager::getForbiddensForGroup(const QUuid& groupID) const {
if (!_groupByID.contains(groupID)) {
NodePermissions nullForbiddens;
nullForbiddens.setAll(false);
return nullForbiddens;
}
QString groupName = _groupByID[groupID]->getID();
return getForbiddensForGroup(groupName);
}
QVariant DomainServerSettingsManager::valueOrDefaultValueForKeyPath(const QString& keyPath) {
const QVariant* foundValue = valueForKeyPath(_configMap.getMergedConfig(), keyPath);
@ -920,10 +969,22 @@ void DomainServerSettingsManager::persistToFile() {
}
void DomainServerSettingsManager::requestMissingGroupIDs() {
QHashIterator<QString, NodePermissionsPointer> i(_groupPermissions.get());
while (i.hasNext()) {
i.next();
NodePermissionsPointer perms = i.value();
QHashIterator<QString, NodePermissionsPointer> i_permissions(_groupPermissions.get());
while (i_permissions.hasNext()) {
i_permissions.next();
NodePermissionsPointer perms = i_permissions.value();
if (!perms->getGroupID().isNull()) {
// we already know this group's ID
continue;
}
// make a call to metaverse api to turn the group name into a group ID
getGroupID(perms->getID());
}
QHashIterator<QString, NodePermissionsPointer> i_forbiddens(_groupForbiddens.get());
while (i_forbiddens.hasNext()) {
i_forbiddens.next();
NodePermissionsPointer perms = i_forbiddens.value();
if (!perms->getGroupID().isNull()) {
// we already know this group's ID
continue;
@ -964,10 +1025,21 @@ void DomainServerSettingsManager::getGroupIDJSONCallback(QNetworkReply& requestR
QString groupName = jsonObject["group_name"].toString();
QUuid groupID = QUuid(jsonObject["group_id"].toString());
bool found = false;
if (_groupPermissions.contains(groupName)) {
qDebug() << "ID for group:" << groupName << "is" << groupID;
_groupPermissions[groupName]->setGroupID(groupID);
_groupByID[groupID] = _groupPermissions[groupName];
found = true;
}
if (_groupForbiddens.contains(groupName)) {
qDebug() << "ID for group:" << groupName << "is" << groupID;
_groupForbiddens[groupName]->setGroupID(groupID);
_groupByID[groupID] = _groupForbiddens[groupName];
found = true;
}
if (found) {
packPermissions();
} else {
qDebug() << "DomainServerSettingsManager::getGroupIDJSONCallback got response for unknown group:" << groupName;
@ -988,3 +1060,23 @@ void DomainServerSettingsManager::recordGroupMembership(const QString& name, con
bool DomainServerSettingsManager::isGroupMember(const QString& name, const QUuid& groupID) {
return _groupMembership[name][groupID];
}
QList<QUuid> DomainServerSettingsManager::getGroupIDs() {
QList<QUuid> result;
foreach (QString groupName, _groupPermissions.keys()) {
if (_groupPermissions[groupName]->isGroup()) {
result << _groupPermissions[groupName]->getGroupID();
}
}
return result;
}
QList<QUuid> DomainServerSettingsManager::getBlacklistGroupIDs() {
QList<QUuid> result;
foreach (QString groupName, _groupForbiddens.keys()) {
if (_groupForbiddens[groupName]->isGroup()) {
result << _groupForbiddens[groupName]->getGroupID();
}
}
return result;
}

View file

@ -28,6 +28,7 @@ const QString SETTINGS_PATH_JSON = SETTINGS_PATH + ".json";
const QString AGENT_STANDARD_PERMISSIONS_KEYPATH = "security.standard_permissions";
const QString AGENT_PERMISSIONS_KEYPATH = "security.permissions";
const QString GROUP_PERMISSIONS_KEYPATH = "security.group_permissions";
const QString GROUP_FORBIDDENS_KEYPATH = "security.group_forbiddens";
class DomainServerSettingsManager : public QObject {
Q_OBJECT
@ -55,7 +56,14 @@ public:
bool havePermissionsForGroup(const QString& groupname) const { return _groupPermissions.contains(groupname); }
NodePermissions getPermissionsForGroup(const QString& groupname) const;
NodePermissions getPermissionsForGroup(const QUuid& groupID) const;
QList<QUuid> getKnownGroupIDs() { return _groupByID.keys(); }
// these remove permissions from users in certain groups
bool haveForbiddensForGroup(const QString& groupname) const { return _groupForbiddens.contains(groupname); }
NodePermissions getForbiddensForGroup(const QString& groupname) const;
NodePermissions getForbiddensForGroup(const QUuid& groupID) const;
QList<QUuid> getGroupIDs();
QList<QUuid> getBlacklistGroupIDs();
// these are used to locally cache the result of calling "api/v1/groups/.../is_member/..." on metaverse's api
void recordGroupMembership(const QString& name, const QUuid groupID, bool isMember);
@ -100,7 +108,8 @@ private:
NodePermissionsMap _standardAgentPermissions; // anonymous, logged-in, localhost
NodePermissionsMap _agentPermissions; // specific account-names
NodePermissionsMap _groupPermissions; // permissions granted by membershipt to specific groups
NodePermissionsMap _groupPermissions; // permissions granted by membership to specific groups
NodePermissionsMap _groupForbiddens; // permissions denied due to membership in a specific group
QHash<QUuid, NodePermissionsPointer> _groupByID; // similar to _groupPermissions but key is group-id rather than name
// keep track of answers to api queries about which users are in which groups

View file

@ -85,6 +85,48 @@ NodePermissionsPointer& operator|=(NodePermissionsPointer& lhs, const NodePermis
return lhs;
}
NodePermissions& NodePermissions::operator&=(const NodePermissions& rhs) {
this->canConnectToDomain &= rhs.canConnectToDomain;
this->canAdjustLocks &= rhs.canAdjustLocks;
this->canRezPermanentEntities &= rhs.canRezPermanentEntities;
this->canRezTemporaryEntities &= rhs.canRezTemporaryEntities;
this->canWriteToAssetServer &= rhs.canWriteToAssetServer;
this->canConnectPastMaxCapacity &= rhs.canConnectPastMaxCapacity;
return *this;
}
NodePermissions& NodePermissions::operator&=(const NodePermissionsPointer& rhs) {
if (rhs) {
*this &= *rhs.get();
}
return *this;
}
NodePermissionsPointer& operator&=(NodePermissionsPointer& lhs, const NodePermissionsPointer& rhs) {
if (lhs && rhs) {
*lhs.get() &= rhs;
}
return lhs;
}
NodePermissions NodePermissions::operator~() {
NodePermissions result = *this;
result.canConnectToDomain = !result.canConnectToDomain;
result.canAdjustLocks = !result.canAdjustLocks;
result.canRezPermanentEntities = !result.canRezPermanentEntities;
result.canRezTemporaryEntities = !result.canRezTemporaryEntities;
result.canWriteToAssetServer = !result.canWriteToAssetServer;
result.canConnectPastMaxCapacity = !result.canConnectPastMaxCapacity;
return result;
}
NodePermissionsPointer operator~(NodePermissionsPointer& lhs) {
if (lhs) {
NodePermissionsPointer result { new NodePermissions };
(*result.get()) = ~(*lhs.get());
return result;
}
return lhs;
}
QDataStream& operator<<(QDataStream& out, const NodePermissions& perms) {
out << perms.canConnectToDomain;
out << perms.canAdjustLocks;

View file

@ -60,6 +60,9 @@ public:
NodePermissions& operator|=(const NodePermissions& rhs);
NodePermissions& operator|=(const NodePermissionsPointer& rhs);
NodePermissions& operator&=(const NodePermissions& rhs);
NodePermissions& operator&=(const NodePermissionsPointer& rhs);
NodePermissions operator~();
friend QDataStream& operator<<(QDataStream& out, const NodePermissions& perms);
friend QDataStream& operator>>(QDataStream& in, NodePermissions& perms);
@ -93,5 +96,7 @@ const NodePermissions DEFAULT_AGENT_PERMISSIONS;
QDebug operator<<(QDebug debug, const NodePermissions& perms);
QDebug operator<<(QDebug debug, const NodePermissionsPointer& perms);
NodePermissionsPointer& operator|=(NodePermissionsPointer& lhs, const NodePermissionsPointer& rhs);
NodePermissionsPointer& operator&=(NodePermissionsPointer& lhs, const NodePermissionsPointer& rhs);
NodePermissionsPointer operator~(NodePermissionsPointer& lhs);
#endif // hifi_NodePermissions_h