Sleds/cppcore/cppcoreobjects/permissions.cpp

1177 lines
39 KiB
C++
Raw Normal View History

2025-03-13 21:28:38 +00:00
// Copyright (c) 2013, IOnU Security, Inc.
/**
* The Permissions class...
*/
#include "permissions.h"
#include "cloudguardurn.h"
#include "../../cppjson/jsonobject.h"
#include "../../libeye/eyeutils.h"
#include "../../libeye/eyeinterface.h"
#ifdef WIN32
#include <io.h>
#else
#include <unistd.h>
#endif
#include <sstream>
#include <algorithm>
#include <list>
#include <functional>
#include <locale>
using namespace sequencelogic;
namespace
{
const int SL_USER_ID_LENGTH = SL_URN_PMO_LEN;
const std::string PERMISSIONS_ALL = PERMISSION_ALL;
const char TOPIC_KEY[] = "topic";
const char KEYS_KEY[] = "keys";
bool checkUserValue(const std::string &value);
std::string getSignaturePath(const std::string &digest);
// will behave as if it is a StorageNode
// Add the new object to the array.
void createPermittedItem(const std::string &metaType, const std::string &urn, JSONArray &itemArray);
// van null -> only groups; van non-null -> only users
void addReferences(const JSONArray &permArray, std::set<CloudGuardURN> &list, const CloudGuardURN &van = CloudGuardURN());
// Legacy code. This used to check for "nested" groups.
bool checkGroupTopology(const std::string &type, const std::string &u) { return true; }
bool checkPermissionKey(const std::string &perm) { return (PERMISSIONS_ALL.find(perm) != std::string::npos); }
int SplitString(const std::string &strToSplit, const char ch, std::vector<std::string> &splitStrings);
#ifdef WIN32
bool canReadFile (const std::string &fileName) { return (_access_s(fileName.c_str(), 04) == 0); }
bool canWriteFile(const std::string &fileName) { return (_access_s(fileName.c_str(), 02) == 0); }
#else
bool canReadFile (const std::string &fileName) { return (access(fileName.c_str(), R_OK) == 0); }
bool canWriteFile(const std::string &fileName) { return (access(fileName.c_str(), W_OK) == 0); }
#endif
};
const char* Permissions::PERMISSION_KEYS[] = {
PERMISSION_READ,
PERMISSION_WRITE,
PERMISSION_ADMIN,
PERMISSION_DOORWAY,
PERMISSION_STREAM,
PERMISSION_SHOW_SHREDDED
};
const int Permissions::NUM_PERMISSION_KEYS = sizeof(PERMISSION_KEYS) / sizeof(PERMISSION_KEYS[0]);
const char Permissions::SIGNATURE_KEY[] = "signature";
/**
* Construct with default permissions.
*/
Permissions::Permissions() : _pData(new JSONObject())
{
for (int i = 0; i < NUM_PERMISSION_KEYS; ++i)
{
std::string keyName = PERMISSION_KEYS[i];
_pData->setJSONArray(keyName.c_str());
keyName += PERMISSION_ASSIGNMENT_SUFFIX;
_pData->setJSONArray(keyName.c_str());
}
mergeDefaultPermissions();
}
Permissions::Permissions(const PermissionsPtr pOrigObj) : _pData(new JSONObject(*pOrigObj->_pData))
{
setAuthority (pOrigObj->getAuthority());
//mergeDefaultPermissions();
}
Permissions::Permissions(const std::string &permStr) : _pData(new JSONObject (permStr))
{
//mergeDefaultPermissions();
}
Permissions::Permissions(const Permissions &origObj) : _pData(new JSONObject(*origObj._pData))
{
setAuthority(origObj.getAuthority());
//mergeDefaultPermissions();
}
Permissions::Permissions(const JSONObject &origObj) : _pData(new JSONObject(origObj))
{
//mergeDefaultPermissions();
}
Permissions& Permissions::operator=(const Permissions &rhs)
{
_pData.reset(new JSONObject(*rhs._pData));
setAuthority(rhs.getAuthority());
return *this;
}
/**
* Comparison.
*/
bool Permissions::operator==(const Permissions &rhs)
{
return ((*_pData == *rhs._pData) && (_authority == rhs._authority));
}
PermissionsPtr Permissions::createFromParent(const JSONObject &parent)
{
PermissionsPtr pRetVal (new Permissions());
if (parent.search(PERMISSION_ROOT_KEY) != NULL)
pRetVal.reset (new Permissions (parent.getJSONObject(PERMISSION_ROOT_KEY).toString()));
pRetVal->set(std::string(PERMISSION_SHOW_SHREDDED) + PERMISSION_ASSIGNMENT_SUFFIX, PERMISSION_VALUE_OWNER);
pRetVal->mergeDefaultPermissions();
return pRetVal;
}
PermissionsPtr Permissions::createFromDeviceFile(const std::string &fileName, const CloudGuardURN &ownerAuthority)
{
std::shared_ptr <Permissions> pRetVal (new Permissions ());
pRetVal->setOwner(ownerAuthority.getPMOID());
pRetVal->setAuthority(ownerAuthority.getPMOID());
if(canReadFile(fileName))
{
pRetVal->setWithAssign(PERMISSION_READ, PERMISSION_VALUE_OWNER);
pRetVal->setWithAssign(PERMISSION_STREAM, PERMISSION_VALUE_OWNER);
}
if (canWriteFile(fileName))
pRetVal->setWithAssign(PERMISSION_WRITE, PERMISSION_VALUE_OWNER);
pRetVal->set(std::string(PERMISSION_SHOW_SHREDDED) + PERMISSION_ASSIGNMENT_SUFFIX, PERMISSION_VALUE_OWNER);
pRetVal->mergeDefaultPermissions();
return pRetVal;
}
/**
* Construct from a flattened non-json string.
* @param owner
* @param authority
* @param string read=something,else | write=something ...
* @return
*/
PermissionsPtr Permissions::createFromFlatString(const std::string &owner, const std::string &string, const std::string &authority /*= ""*/)
{
PermissionsPtr pRetVal (new Permissions ());
pRetVal->setAuthority(authority);
pRetVal->setOwner(owner);
std::vector<std::string> flatStringToks;
::SplitString(string, '|', flatStringToks);
for (unsigned int i = 0; i < flatStringToks.size(); ++i)
{
std::vector<std::string> whatParts;
if (::SplitString(flatStringToks[i], '=', whatParts) > 0)
{
std::string keyName = whatParts[0];
if (keyName == PERMISSION_KEY_OWNER)
{
if (checkUserValue(whatParts[1]))
pRetVal->_pData->setJSONValue(keyName.c_str(), whatParts[1].c_str());
}
else
{
if (checkPermissionKey(keyName))
{
pRetVal->_pData->setJSONArray(keyName);
JSONArray &subArrayVal = pRetVal->_pData->getJSONArray(keyName);
std::vector<std::string> whoParts;
::SplitString(whatParts[1], ',', whoParts);
for (unsigned int j = 0; j < whoParts.size(); ++j)
{
if (checkUserValue(whoParts[j]))
subArrayVal.addElement(whoParts[j]);
}
}
}
}
}
pRetVal->mergeDefaultPermissions();
return pRetVal;
}
/**
* Incoming userPermissions is of form:
* <pre>
* {
* "id": "me",
* "role": "owner",
* "selfLink": "https:\/\/www.googleapis.com\/drive\/v2\/files\/0ByK--JTHkV6AZzB5LXlpR2RnMU0\/permissions\/me",
* "type": "user",
* "etag": "\"Q0cVodxX8sh4vfxZTlOyWcmmc0k\/m7evUIvideYSbzR6BMwMo0JTKWU\"",
* "kind": "drive#permission"
* },
* </pre>
* @param userPermissions
* @param ownerAuthority - if google perms indicate ownership of file then this will be assigned to our
* owner and authority; if we are NOT the owner, then those fields are NOT set
* @return
* @see https://developers.google.com/drive/v2/reference/permissions#resource
*/
PermissionsPtr Permissions::createFromGoogleUserPermissions(const JSONObjectPtr pUserPermissions, const CloudGuardURN &ownerAuthority)
{
PermissionsPtr pRetVal (new Permissions ());
if (!pUserPermissions->empty() && (pUserPermissions->getJSONString("kind") == "drive#permission"))
{
std::string role = pUserPermissions->getJSONString("role");
std::string id = pUserPermissions->getJSONString("id");
if ((role == "owner") && (id == "me"))
{
pRetVal->setAuthority(ownerAuthority.getPMOID());
pRetVal->setOwner(ownerAuthority.getPMOID());
pRetVal->setWithAssign(PERMISSION_ADMIN, PERMISSION_VALUE_OWNER);
pRetVal->setWithAssign(PERMISSION_READ, PERMISSION_VALUE_OWNER);
pRetVal->setWithAssign(PERMISSION_WRITE, PERMISSION_VALUE_OWNER);
pRetVal->set(std::string(PERMISSION_SHOW_SHREDDED) + PERMISSION_ASSIGNMENT_SUFFIX, PERMISSION_VALUE_OWNER);
pRetVal->set(PERMISSION_STREAM, PERMISSION_VALUE_OWNER);
}
else if (role == "reader")
{
pRetVal->set(PERMISSION_READ, ownerAuthority.getPMOID());
pRetVal->set(PERMISSION_STREAM, ownerAuthority.getPMOID());
pRetVal->setWithAssign(PERMISSION_SHOW_SHREDDED, PERMISSION_VALUE_OWNER);
}
else if (role == "writer")
{
pRetVal->set(PERMISSION_READ, ownerAuthority.getPMOID());
pRetVal->set(PERMISSION_WRITE, ownerAuthority.getPMOID());
pRetVal->set(PERMISSION_STREAM, ownerAuthority.getPMOID());
pRetVal->set(std::string(PERMISSION_SHOW_SHREDDED) + PERMISSION_ASSIGNMENT_SUFFIX, PERMISSION_VALUE_OWNER);
}
}
pRetVal->mergeDefaultPermissions();
return pRetVal;
}
/**
* Creates a Permission object that references ONLY a single office. Typically this office
* will be the client office.
* @param pmoUrn
* @return
*/
PermissionsPtr Permissions::createFromOffice(const CloudGuardURN &pmoUrn)
{
std::vector<CloudGuardURN> tmpUrnVec;
tmpUrnVec.push_back(pmoUrn);
return createFromUrnList(tmpUrnVec);
}
/**
* Creates a Permission object that references a team
* @param teamUrn
* @return
*/
PermissionsPtr Permissions::createFromTeam(const CloudGuardURN &teamUrn)
{
// Verify this is a team URN...
if ((teamUrn.getType() == CloudGuardURN::URN_TYPE::PMO_DEVICE_DOCUMENT) && (teamUrn.getDeviceID() == GROUP_DOC_DEVICE_ID))
{
std::vector<CloudGuardURN> tmpUrnVec;
tmpUrnVec.push_back(teamUrn);
return createFromUrnList(tmpUrnVec);
}
else
return PermissionsPtr();
}
/**
* Given two or more URNs, create Permissions object that allows them to chat.
* At least one URN must NOT be a group.
* @param urns - at least two, with at least one being a non-group
* @return
*/
PermissionsPtr Permissions::createFromUrnList(std::vector<CloudGuardURN> &urns)
{
PermissionsPtr pRetVal (new Permissions());
// Find the first urn that is not a group urn, and set that as the owner.
CloudGuardURN ownerURN;
for (unsigned int i = 0; (ownerURN.getType() == CloudGuardURN::URN_TYPE::INVALID) && (i < urns.size()); ++i)
if (!urns[i].isGroupURN())
ownerURN = urns[i];
pRetVal->setOwner(ownerURN.getPMOID());
pRetVal->setAuthority(ownerURN.getPMOID());
// We want the same Permissions toOrderedString, with the same owner/auth, no matter
// what the order of URNs are, e.g. A,B should produce same digest as B,A
// So just sort the array.
std::sort(urns.begin(), urns.end());
for (unsigned int i = 0; i < urns.size(); ++i)
{
CloudGuardURN &urn = urns[i];
std::string pmo = urn.getPMOID();
if (urn.isGroupURN())
pmo = urn.getUrn();
pRetVal->set(std::string(PERMISSION_ADMIN) + PERMISSION_ASSIGNMENT_SUFFIX, pmo);
pRetVal->set(std::string(PERMISSION_READ) + PERMISSION_ASSIGNMENT_SUFFIX, pmo);
pRetVal->set(std::string(PERMISSION_WRITE) + PERMISSION_ASSIGNMENT_SUFFIX, pmo);
pRetVal->set(std::string(PERMISSION_SHOW_SHREDDED) + PERMISSION_ASSIGNMENT_SUFFIX, pmo);
pRetVal->set(PERMISSION_ADMIN, pmo);
pRetVal->set(PERMISSION_READ, pmo);
pRetVal->set(PERMISSION_WRITE, pmo);
// Currently, we are not allowing groups and individual users.
//if (urn.isGroupURN())
// break;
}
pRetVal->mergeDefaultPermissions();
return pRetVal;
}
void Permissions::mergeDefaultPermissions()
{
// 1.2.8.7 removed required perms
/*
char *pPolicy = ionu_policy_check(PN_REQUIRED_PERMS);
if (pPolicy != NULL)
{
Permissions defPerms(pPolicy);
mergePermissions(this, defPerms, false);
delete [] pPolicy;
}
*/
}
/**
* Guarantee consistent ordering of hash keys...
* This will not include owner key and value, and any "owner" value will be replaced by
* the actual owner ID in the output.
* @return
*/
std::string Permissions::toOrderedString() const
{
std::stringstream streamBuf;
streamBuf << "{";
std::string comma;
for (int i = 0; i < 2; ++i)
{
std::string suffix = (i == 0) ? "" : PERMISSION_ASSIGNMENT_SUFFIX;
for (int j = 0; j < NUM_PERMISSION_KEYS; ++j)
{
std::stringstream tmpBuf;
const JSONArray &perms = _pData->getJSONArray(std::string(PERMISSION_KEYS[j]) + suffix);
std::list<std::string> tmpList;
for (int k = 0; k < perms.getnumelements(); ++k)
{
std::string permVal = perms[k].getstring();
if (permVal != "")
tmpList.push_back(permVal);
}
tmpList.sort();
bool bHasOwner = false;
for (std::list<std::string>::const_iterator citer = tmpList.begin();
citer != tmpList.end(); ++citer)
{
std::string permVal = *citer;
if (permVal == getOwner())
bHasOwner = true;
if (permVal == PERMISSION_VALUE_OWNER)
{
if (bHasOwner)
// we already emitted the UUID for owner so don't do it again
// note that normal "assign" logic prevents all other duplicate values
continue;
else
permVal = getOwner();
}
if (citer != tmpList.begin())
tmpBuf << ",";
tmpBuf << "\"" << permVal << "\"";
}
streamBuf << comma << "\"" << PERMISSION_KEYS[j] << suffix << "\":[" << tmpBuf.str() << "]";
comma = ",";
}
}
// Append "topic"...
if (HasMember(TOPIC_KEY) && (_pData->getJSONString(TOPIC_KEY) != ""))
streamBuf << ",\"" << TOPIC_KEY << "\":\"" << _pData->getJSONString(TOPIC_KEY) << "\"";
streamBuf << "}";
return streamBuf.str();
}
/**
* For UX display of short rights string.
* @param user
* @return
*/
std::string Permissions::toRightsString(const std::string &user) const
{
std::stringstream streamBuf;
streamBuf << rightsString(PERMISSION_READ, user);
streamBuf << rightsString(PERMISSION_WRITE, user);
streamBuf << rightsString(PERMISSION_DOORWAY, user);
streamBuf << rightsString(PERMISSION_STREAM, user);
streamBuf << rightsString(PERMISSION_ADMIN, user);
return streamBuf.str();
}
std::string Permissions::rightsString(const std::string &perm, const std::string &user) const
{
bool r = can(*this, perm, user);
bool ra = can(*this, perm+PERMISSION_ASSIGNMENT_SUFFIX, user);
// r+w+d+a+ or r+w d -; e.g. {right|-}[+ ]...
return (r ? perm.substr(0, 1) : "-") +
(ra ? "+" : " ");
}
std::string Permissions::getPermittedRightsSignature()
{
std::string str = toOrderedString();
//LibIOnU ionu = new LibIOnU();
//String dig = ionu.md5(str);
std::string dig = DigestMessage(MESSAGE_DIGEST_MD5, reinterpret_cast<unsigned const char *>(str.c_str()), str.length());
dig = dig.substr(0, 12);
return dig;
}
std::string Permissions::getSignature(const CloudGuardURN &van) const
{
return getUserAndGroupSig(van);
}
std::string Permissions::getUserAndGroupSig(const CloudGuardURN &van) const
{
std::string refs = getOrderedReferences(van);
std::string digest = DigestMessage(MESSAGE_DIGEST_MD5, reinterpret_cast<unsigned const char *>(refs.c_str()), refs.length());
digest = digest.substr(0, 12);
return digest;
}
std::string Permissions::getPermittedRightsSig() const
{
std::string str = toOrderedString();
std::string digest = DigestMessage(MESSAGE_DIGEST_MD5, reinterpret_cast<unsigned const char *>(str.c_str()), str.length());
digest = digest.substr(0, 12);
return digest;
}
std::string Permissions::getOrderedReferences(const CloudGuardURN &van) const
{
CloudGuardURNSet allRefs;
getAllReferences(van, allRefs);
std::stringstream allRefsStr;
for (CloudGuardURNSetIter iter = allRefs.begin(); iter != allRefs.end(); ++iter)
{
if (iter != allRefs.begin())
allRefsStr << "|";
allRefsStr << iter->getUrn();
}
return allRefsStr.str();
}
std::string Permissions::getSignaturePath(const CloudGuardURN &van) const
{
std::string dig = getSignature(van);
return ::getSignaturePath(dig);
}
void Permissions::getPermittedItems(const CloudGuardURN &van, JSONArray &permittedItems) const
{
permittedItems.clear();
createPermittedItem(PERMISSION_VALUE_OWNER, CloudGuardURN(van.getVANID(), getOwner()).getUrn(), permittedItems);
//createPermittedItem(PERMISSION_VALUE_ALL, "", permittedItems);
std::set<CloudGuardURN> urnSet;
getReferencedGroups(urnSet);
for (std::set<CloudGuardURN>::const_iterator citer = urnSet.begin();
citer != urnSet.end();
++citer)
{
createPermittedItem("group", citer->getUrn(), permittedItems);
}
getReferencedUsers(van, urnSet);
for (std::set<CloudGuardURN>::const_iterator citer = urnSet.begin();
citer != urnSet.end();
++citer)
{
createPermittedItem("user", citer->getUrn(), permittedItems);
}
}
/**
* Gets a set of referenced user values. This does NOT reference 'mbrs' data but inspects
* individual rights.
* @param van tell us what van we are operating within
* @param Return set, possibly empty list of group document URN's
*/
void Permissions::getReferencedUsers(const CloudGuardURN &van, std::set<CloudGuardURN> &referencedUsers) const
{
referencedUsers.clear();
for (int i = 0; i < NUM_PERMISSION_KEYS; ++i)
{
addReferences(_pData->getJSONArray(PERMISSION_KEYS[i]), referencedUsers, van);
addReferences(_pData->getJSONArray(std::string(PERMISSION_KEYS[i])+PERMISSION_ASSIGNMENT_SUFFIX), referencedUsers, van);
}
}
/**
* Gets a set of referenced group values. This does NOT reference 'mbrs' data but inspects
* individual rights.
* @param Return set, possibly empty list of group document URN's
*/
void Permissions::getReferencedGroups(std::set<CloudGuardURN> &referencedGroups) const
{
referencedGroups.clear();
for (int i = 0; i < NUM_PERMISSION_KEYS; ++i)
{
addReferences(_pData->getJSONArray(PERMISSION_KEYS[i]), referencedGroups);
addReferences(_pData->getJSONArray(std::string(PERMISSION_KEYS[i])+PERMISSION_ASSIGNMENT_SUFFIX), referencedGroups);
}
}
/**
* Get all references, both users and groups (you need to check the group device ID to see what's
* a group and what is a normal user).
* @param van
*/
void Permissions::getAllReferences(const CloudGuardURN &van, std::set<CloudGuardURN> &allReferences) const
{
allReferences.clear();
getReferencedUsers(van, allReferences);
std::set<CloudGuardURN> tmpRefs;
getReferencedGroups(tmpRefs);
std::copy(tmpRefs.begin(), tmpRefs.end(), std::inserter(allReferences, allReferences.begin()));
}
/**
* Same results as getAllReferences but in JSONArray format with each element being a string.
* @param van
* @return
*/
JSONArrayPtr Permissions::getAllReferencesAsStringArray (const CloudGuardURN &vanUrn) const
{
std::set<CloudGuardURN> allRefs;
getAllReferences(vanUrn, allRefs);
JSONArrayPtr pRetVal (new JSONArray());
for (std::set<CloudGuardURN>::iterator iter = allRefs.begin(); iter != allRefs.end(); ++iter)
pRetVal->addElement(iter->getUrn());
return pRetVal;
}
/**
* Updates members array by adding all unique UUID values referenced by this object and
* any referenced groups. Membership simply means that you are named in a *some* permission.
* It's useful for indexing and having a list of everyone who can interact with a document.
*
* @param van - tell us van we are operating within
* @param groupMap - null (no group resolution) or map of group doc URN to UUID values.
* @param Return set of members (PMO urn's) - internal 'mbrs' array is also set on this object
*/
void Permissions::updateMembers(const CloudGuardURN &van, const CloudGuardURNToURNSetMap &groupMap, CloudGuardURNSet &curMembers)
{
curMembers.clear();
for (int i = 0; i < NUM_PERMISSION_KEYS; ++i)
{
collectMembers(van, groupMap, _pData->getJSONArray(PERMISSION_KEYS[i]), curMembers);
collectMembers(van, groupMap, _pData->getJSONArray(std::string(PERMISSION_KEYS[i])+PERMISSION_ASSIGNMENT_SUFFIX), curMembers);
}
_pData->setJSONArray(PERMISSION_MEMBERS_KEY);
JSONArray &newMembers = _pData->getJSONArray(PERMISSION_MEMBERS_KEY);
for (CloudGuardURNSetIter urnIter = curMembers.begin(); urnIter != curMembers.end(); ++urnIter)
newMembers.addElement(urnIter->getUrn());
}
void Permissions::setOwner(const std::string &owner)
{
if (checkUserValue(owner))
_pData->setJSONValue(PERMISSION_KEY_OWNER, owner.c_str());
}
std::string Permissions::getOwner() const
{
return _pData->getJSONString(PERMISSION_KEY_OWNER);
}
std::string Permissions::getName() const
{
return _pData->getJSONString(PERMISSION_NAME_KEY);
}
void Permissions::setName(const std::string &name)
{
_pData->setJSONValue(PERMISSION_NAME_KEY, name.c_str());
}
std::string Permissions::getType() const
{
return _pData->getJSONString(PERMISSION_TYPE_KEY, "file");
}
bool Permissions::canRead(const std::string &user) const
{
return can(*this, PERMISSION_READ, user);
}
bool Permissions::canWrite(const std::string &user) const
{
return can(*this, PERMISSION_WRITE, user);
}
bool Permissions::isAdmin(const std::string &user) const
{
return can(*this, PERMISSION_ADMIN, user);
}
bool Permissions::canDoorway(const std::string &user) const
{
return can(*this, PERMISSION_DOORWAY, user);
}
bool Permissions::canStream(const std::string &user) const
{
return can(*this, PERMISSION_STREAM, user);
}
bool Permissions::canHide(const std::string &user) const
{
return can(*this, PERMISSION_SHOW_SHREDDED, user);
}
bool Permissions::assignRead(const std::string &user)
{
return assign(_pData.get(), PERMISSION_READ, _authority, user);
}
bool Permissions::assignRead(const std::vector<std::string> &users)
{
return assign(_pData.get(), PERMISSION_READ, _authority, users);
}
bool Permissions::assignWrite(const std::string &user)
{
return assign(_pData.get(), PERMISSION_WRITE, _authority, user);
}
bool Permissions::assignWrite(const std::vector<std::string> &users)
{
return assign(_pData.get(), PERMISSION_WRITE, _authority, users);
}
bool Permissions::assignAdmin(const std::string &user)
{
return assign(_pData.get(), PERMISSION_ADMIN, _authority, user);
}
bool Permissions::assignAdmin(const std::vector<std::string> &users)
{
return assign(_pData.get(), PERMISSION_ADMIN, _authority, users);
}
bool Permissions::assignDoorway(const std::string &user)
{
return assign(_pData.get(), PERMISSION_DOORWAY, _authority, user);
}
bool Permissions::assignDoorway(const std::vector<std::string> &users)
{
return assign(_pData.get(), PERMISSION_DOORWAY, _authority, users);
}
bool Permissions::assignStream(const std::string &user)
{
return assign(_pData.get(), PERMISSION_STREAM, _authority, user);
}
bool Permissions::assignStream(const std::vector<std::string> &users)
{
return assign(_pData.get(), PERMISSION_STREAM, _authority, users);
}
bool Permissions::assignHide(const std::string &user)
{
return assign(_pData.get(), PERMISSION_SHOW_SHREDDED, _authority, user);
}
bool Permissions::assignHide(const std::vector<std::string> &users)
{
return assign(_pData.get(), PERMISSION_SHOW_SHREDDED, _authority, users);
}
/**
* Merge, or copy, perms into this permissions object.
*/
void Permissions::simpleMerge(const Permissions &perms)
{
mergePermissions(this, perms, false);
}
PermissionsPtr Permissions::merge(const Permissions &permissions)
{
PermissionsPtr pRetVal(new Permissions(*this));
mergePermissions(pRetVal.get(), permissions, true);
return pRetVal;
}
void Permissions::mergePermissions(Permissions *pDest, const Permissions &src, bool bAssign)
{
for (int i = 0; i < 2; ++i)
{
std::string suffix = (i == 0) ? "" : PERMISSION_ASSIGNMENT_SUFFIX;
for (int j = 0; j < NUM_PERMISSION_KEYS; ++j)
{
const JSONArray &oldArray = src._pData->getJSONArray(std::string(PERMISSION_KEYS[j]) + suffix);
if (oldArray.empty())
continue;
for (int i = 0; i < oldArray.getnumelements(); ++i)
{
std::string oldVal = oldArray[i].getstring();
if (bAssign)
assign(*pDest, PERMISSION_KEYS[j], pDest->_authority, oldVal);
else
pDest->set(PERMISSION_KEYS[j], oldVal);
}
}
}
}
/**
* Set a value, and it's 'assign' counterpart.
*/
void Permissions::setWithAssign(const std::string &perm, const std::string &user)
{
set(perm, user);
set(perm+PERMISSION_ASSIGNMENT_SUFFIX, user);
}
void Permissions::removeWithAssign(const std::string &perm, const std::string &user)
{
remove(perm, user);
remove(perm+PERMISSION_ASSIGNMENT_SUFFIX, user);
}
/**
* Set the indicated permission, ignoring any access/assignment rights checking.
* @param perm
* @param user - no validation is performed on this value
*/
void Permissions::set(const std::string &perm, const std::string &user)
{
if (checkPermissionKey(perm) &&
checkUserValue(user) &&
checkGroupTopology(getType(), user))
{
_pData->setJSONArray(perm);
JSONArray &permVal = _pData->getJSONArray(perm);
// It is an array.
bool bFoundVal = false;
for (int i = 0; i < permVal.getnumelements(); ++i)
{
std::string tmpVal = permVal[i].getstring();
bFoundVal = (user == tmpVal);
}
if (!bFoundVal)
permVal.addElement(user);
}
}
/**
* Remove the indicated permission, ignoring any access/assignment rights checking.
* @param perm
* @param user
*/
void Permissions::remove(const std::string &perm, const std::string &user)
{
if (checkPermissionKey(perm) && checkUserValue(user))
{
if (_pData->search(perm.c_str()) != NULL)
{
JSONArray &permArray = _pData->getJSONArray(perm);
permArray.removeElement(user);
}
}
mergeDefaultPermissions();
}
void Permissions::setType(const std::string &type)
{
if (type == "")
_pData->erase(PERMISSION_TYPE_KEY);
else if (std::string(PERMISSION_TYPE_VALUES).find(type) != std::string::npos)
_pData->setJSONValue(PERMISSION_TYPE_KEY, type.c_str());
}
void Permissions::collectMembers(const CloudGuardURN &van, const CloudGuardURNToURNSetMap &groupMap, const JSONArray &permArray, CloudGuardURNSet &mbrs)
{
if (permArray.empty())
return;
for (int i = 0; i < permArray.getnumelements(); ++i)
{
std::string memberStr = permArray[i].getstring();
if (memberStr != "")
{
if (memberStr.find(CloudGuardURN::NAMESPACE) == 0)
{
if (!groupMap.empty())
{
//memberStr = memberStr.substr(sizeof(PERMISSION_GROUP_PREFIX)-1);
CloudGuardURN groupKey (memberStr);
CloudGuardURNToURNSetMapCIter mapIter = groupMap.find(groupKey);
if (mapIter == groupMap.end())
{
// Aparrently, this is bad. According to the comments in the Java code:
// "bad programmer, didn't call getReferencedGroups and then iterate DB and resolve each"
//std::stringstream msg;
//msg << "In Permissions::collectMembers: expect: " << groupKey.getUrn() << " /" << groupKey.getUrn() << std::endl;
//for (mapIter = groupMap.begin(); mapIter != groupMap.end(); ++mapIter)
// msg << " has: " << mapIter->first.getUrn() << " /" << mapIter->first.getUrn() << std::endl;
//IONUWebService::logDebug("%s", msg.str().c_str());
}
else
std::copy(mapIter->second.begin(), mapIter->second.end(), std::inserter(mbrs, mbrs.begin()));
}
}
else if (memberStr == PERMISSION_VALUE_OWNER)
mbrs.insert(CloudGuardURN(van.getVANID(), getOwner()));
//else if (memberStr == Permissions::PERMISSION_VALUE_ALL)
// mbrs.insert(CloudGuardURN(van.getVANID(), CloudGuardURN::PMO_ALL_ID));
else
// uuid
mbrs.insert(CloudGuardURN(van.getVANID(), memberStr));
}
}
}
//////////// static: can be used on Permissions or raw JSONObject
/**
* Does the user have the indicated permission?
* @param obj
* @param perm either a perm (read) or assignment (readAssign)
* @param user
* @return
*/
bool Permissions::can(const Permissions &obj, const std::string &perm, const std::string &user, std::string *pWhyReturn /*= NULL*/)
{
std::string tmpUser = user;
bool bRemove = ((tmpUser != "") && (tmpUser[0] == '-'));
if ((tmpUser != "") && ((tmpUser[0] == '+') || (tmpUser[0] == '-')))
tmpUser.erase(tmpUser.begin());
return can(obj._pData.get(), perm, tmpUser, bRemove, pWhyReturn);
}
bool Permissions::can(const JSONObject *pObj, const std::string &perm, const std::string &user, bool bRemove, std::string *pWhyReturn /*= NULL*/)
{
bool bRetVal = (checkPermissionKey(perm) && checkUserValue(user));
if ((pObj != NULL) && bRetVal)
{
bRetVal = false;
if (pObj->search(perm.c_str()) == NULL)
const_cast<JSONObject *>(pObj)->setJSONArray(perm);
const JSONArray &permArray = pObj->getJSONArray(perm);
std::string owner = pObj->getJSONString(PERMISSION_KEY_OWNER);
std::string why = "not permitted";
if (permArray.empty() && (perm.find(PERMISSION_ASSIGNMENT_SUFFIX) != std::string::npos))
{
// Perms array must not exist. Older files will not have the 'hideAssign', and possibly
// others in the future.
// For now, create the missing '...Assign' perm based on the write permissions.
const JSONArray &writeArray = pObj->getJSONArray(PERMISSION_WRITE);
const_cast<JSONArray &>(permArray) = writeArray;
}
if (!bRetVal)
{
Permissions tmpPerms;
tmpPerms.mergeDefaultPermissions();
for (int i = 0; i < permArray.getnumelements(); ++i)
{
std::string permMember = permArray[i].getstring();
if (bRemove &&
tmpPerms.HasMember(perm) &&
(tmpPerms._pData->getJSONArray(perm).getnumelements() > 0) &&
(tmpPerms._pData->getJSONArray(perm).hasString(permMember)))
{
bRetVal = false;
why = "cannot remove default permission";
break;
}
else if ((permMember == PERMISSION_VALUE_OWNER) && (owner == user))
{
bRetVal = true;
why = "is owner";
}
else if (permMember == user)
{
bRetVal = true;
why = "is named user";
}
}
}
if (pWhyReturn != NULL)
*pWhyReturn += why;
}
return bRetVal;
}
bool Permissions::canAssign(const JSONObject *pObj, const std::string &perm, const std::string &user)
{
return can(pObj, perm+std::string(PERMISSION_ASSIGNMENT_SUFFIX), user, false);
}
/**
* Assign 'perm' to 'user', but only if 'auth' has assign* priv.
* @param obj - Permissions object
* @param perm -
* @param auth
* @param user - no validation is performed on this value
* @return true if assignment successful; false if it was not
*/
bool Permissions::assign(JSONObject *pObj, const std::string &perm, const std::string &auth, const std::string &user)
{
if (pObj == NULL)
return false;
if (!checkPermissionKey(perm))
return false;
std::string trimUser = user;
if ((trimUser[0] == '-') || (trimUser[0] == '+'))
trimUser.erase(0, 1);
if (!checkUserValue(trimUser))
return false;
if (!checkGroupTopology(pObj->getJSONString("type", "file"), trimUser))
return false;
// check *Assign unless we are trying to assign/update a *Assign field in which
// we check adminAssign
std::string asn;
if ((perm.find(PERMISSION_ASSIGNMENT_SUFFIX) == std::string::npos) && (perm != PERMISSION_ADMIN))
// read/write/doorway/stream/hide => *Assign required
asn = perm + PERMISSION_ASSIGNMENT_SUFFIX;
else if (perm == PERMISSION_ADMIN)
// admin => admin
asn = PERMISSION_ADMIN;
else if (perm == (std::string(PERMISSION_ADMIN) + PERMISSION_ASSIGNMENT_SUFFIX))
// adminAssign => adminAssign
asn = perm;
else
// readAssign/writeAssign/doorwayAssign => admin
asn = PERMISSION_ADMIN;
bool bRetVal = can(Permissions(*pObj), asn, auth);
if (bRetVal)
{
pObj->setJSONArray(perm);
JSONArray &permArray = pObj->getJSONArray(perm);
if (user.find('-') == 0)
permArray.removeElement(trimUser);
else
permArray.addElement(trimUser);
}
else if (can(Permissions(*pObj), perm, trimUser))
bRetVal = true; // since the perm already exists, do not fail if authority cannot assign it
return bRetVal;
}
bool Permissions::assign(JSONObject *pObj, const std::string &perm, const std::string &auth, const std::vector<std::string> &users)
{
bool bRetVal = true;
for (unsigned int i = 0; bRetVal && (i < users.size()); ++i)
bRetVal &= assign(pObj, perm, auth, users[i]);
return bRetVal;
}
bool Permissions::HasMember(const std::string &memberName) const
{
return (_pData->search(memberName.c_str()) != NULL);
}
std::string Permissions::toString() const
{
return _pData->toString();
}
JSONArray Permissions::getUserArray(const std::string &arrayName) const
{
return _pData->getJSONArray(arrayName);
}
bool Permissions::LoadFromFile(const std::string &fileName)
{
return _pData->LoadFromFile(fileName);
}
bool Permissions::SaveToFile(const std::string &fileName) const
{
return _pData->SaveToFile(fileName);
}
void Permissions::setKeys(const JSONArray &keys)
{
_pData->setJSONValue(KEYS_KEY, keys);
}
bool Permissions::hasKeys() const
{
return (_pData->search(KEYS_KEY) != NULL);
}
void Permissions::clearKeys()
{
_pData->erase(KEYS_KEY);
}
bool Permissions::isEmpty() const
{
bool bRetVal = _pData->empty();
if (!bRetVal)
{
// Check that the arrays are empty...
bRetVal = true;
for (int i = 0; bRetVal && (i < NUM_PERMISSION_KEYS); ++i)
{
std::string keyName = PERMISSION_KEYS[i];
const JSONArray &permArray = _pData->getJSONArray(keyName);
bRetVal &= (permArray.getnumelements() == 0);
keyName += PERMISSION_ASSIGNMENT_SUFFIX;
const JSONArray &permAssignArray = _pData->getJSONArray(keyName);
bRetVal &= (permAssignArray.getnumelements() == 0);
}
}
return bRetVal;
}
/**
* Get a list of users, based on a permission key.
*/
void Permissions::getUsers(const CloudGuardURN &van, const std::string &key, std::vector<CloudGuardURN> &users) const
{
users.clear();
const JSONArray &keyArray = _pData->getJSONArray(key);
for (int i = 0; i < keyArray.getnumelements(); ++i)
{
std::string userStr = keyArray[i].getstring();
if (userStr.find(CloudGuardURN::NAMESPACE) == 0)
users.push_back(CloudGuardURN(userStr));
else
users.push_back(CloudGuardURN(van.getVANID(), userStr, "", ""));
}
}
namespace
{
bool checkUserValue(const std::string &value)
{
std::string upperCaseValue = value;
if (upperCaseValue.find(CloudGuardURN::NAMESPACE) == 0)
std::transform(upperCaseValue.begin()+::strlen(CloudGuardURN::NAMESPACE), upperCaseValue.end(),
upperCaseValue.begin()+::strlen(CloudGuardURN::NAMESPACE), std::bind2nd(std::ptr_fun(std::toupper<std::string::value_type>), std::locale()));
else
std::transform(upperCaseValue.begin(), upperCaseValue.end(), upperCaseValue.begin(), std::bind2nd(
std::ptr_fun(std::toupper<std::string::value_type>), std::locale()));
bool bRetVal = (value == PERMISSION_VALUE_OWNER);
if (!bRetVal)
{
bRetVal = true;
size_t i = 0;
bool bPermissionGroup = (upperCaseValue.find(CloudGuardURN::NAMESPACE) == 0);
if (bPermissionGroup)
i = ::strlen(CloudGuardURN::NAMESPACE) - 1;
for (; bRetVal && (i < upperCaseValue.length()); ++i)
{
char tmpChar = upperCaseValue[i];
bRetVal = (std::isdigit(tmpChar, std::locale()) ||
(('A' <= tmpChar) && (tmpChar <= 'F'))) ||
(bPermissionGroup && (tmpChar == ':'));
}
if (bRetVal)
// Check lengths...
bRetVal = ((upperCaseValue.length() == SL_USER_ID_LENGTH) ||
(upperCaseValue.length() == static_cast<unsigned int>(CloudGuardURN::NSS_LENGTH))) ||
(bPermissionGroup && (upperCaseValue.length() == CloudGuardURN::NSS_LENGTH + ::strlen(CloudGuardURN::NAMESPACE)));
}
return bRetVal;
}
std::string getSignaturePath(const std::string &digest)
{
std::stringstream streamBuf;
if (digest.length() >= 12)
{
for (int i = 0; i < 12; i += 3)
{
if (i > 0)
streamBuf << "/";
streamBuf << digest.substr(i, 3);
}
streamBuf << digest.substr(12);
}
return streamBuf.str();
}
// will behave as if it is a StorageNode
void createPermittedItem(const std::string &metaType, const std::string &urn, JSONArray &itemArray)
{
JSONObject node;
node.setJSONValue( "name", ""); // caller must resolve
node.setJSONValue("path", urn.c_str());
node.setJSONValue("metatype", metaType.c_str());
node.setJSONValue("type", "SNT_ACTION"); // avoid pulling in StorageNodeType import
node.setJSONValue("status", "informational"); // ... StorageNodeStatus
itemArray.addElement(node);
}
// van null -> only groups; van non-null -> only users
void addReferences(const JSONArray &permArray, std::set<CloudGuardURN> &list, const CloudGuardURN &van /*= CloudGuardURN()*/)
{
if (permArray.empty())
return;
for (int i = 0; i < permArray.getnumelements(); ++i)
{
std::string objStr = permArray[i].getstring();
if ((van.getType() == CloudGuardURN::INVALID) && (objStr.find(CloudGuardURN::NAMESPACE) == 0))
list.insert(objStr);
else if ( (objStr != "") && (van.getType() != CloudGuardURN::INVALID) &&
(objStr.find(CloudGuardURN::NAMESPACE) != 0) &&
(objStr != PERMISSION_VALUE_OWNER) )/* &&
(objStr != Permissions::PERMISSION_VALUE_ALL) )*/
{
CloudGuardURN urn (van.getVANID(), objStr);
list.insert(urn);
}
}
}
int SplitString(const std::string &strToSplit, const char ch, std::vector<std::string> &splitStrings)
{
int nRetVal = 0;
splitStrings.clear();
std::string::size_type nStart = 0;
while (strToSplit.find(ch, nStart) != std::string::npos)
{
splitStrings.push_back(strToSplit.substr(nStart, strToSplit.find(ch, nStart) - nStart));
++nRetVal;
nStart = strToSplit.find(ch, nStart)+1;
}
// Check for a last arg...
if (nStart <= strToSplit.length())
{
splitStrings.push_back(strToSplit.substr(nStart));
++nRetVal;
}
return nRetVal;
}
}