/** * Gets the ACL's for the module, will also expand them so the client side of the ACL's don't have to do as many checks. * * @param string $module The module we want to fetch the ACL for * @param object $userObject The user object for the ACL's we are retrieving. * @param object|bool $bean The SugarBean for getting specific ACL's for a module * @param bool $showYes Do not unset Yes Results * @return array Array of ACL's, first the action ACL's (access, create, edit, delete) then an array of the field level acl's */ public function getAclForModule($module, $userObject, $bean = false, $showYes = false) { $outputAcl = array('fields' => array()); $outputAcl['admin'] = $userObject->isAdminForModule($module) ? 'yes' : 'no'; $outputAcl['developer'] = $userObject->isDeveloperForModule($module) ? 'yes' : 'no'; if (!SugarACL::moduleSupportsACL($module)) { foreach (array('access', 'view', 'list', 'edit', 'delete', 'import', 'export', 'massupdate') as $action) { $outputAcl[$action] = 'yes'; } } else { $context = array('user' => $userObject); if ($bean instanceof SugarBean) { $context['bean'] = $bean; } // if the bean is not set, or a new bean.. set the owner override // this will allow fields marked Owner to pass through ok. if ($bean == false || empty($bean->id) || isset($bean->new_with_id) && $bean->new_with_id == true) { $context['owner_override'] = true; } $moduleAcls = SugarACL::getUserAccess($module, array(), $context); // Bug56391 - Use the SugarACL class to determine access to different actions within the module foreach (SugarACL::$all_access as $action => $bool) { $outputAcl[$action] = $moduleAcls[$action] == true || !isset($moduleAcls[$action]) ? 'yes' : 'no'; } // Only loop through the fields if we have a reason to, admins give full access on everything, no access gives no access to anything if ($outputAcl['access'] == 'yes') { // Currently create just uses the edit permission, but there is probably a need for a separate permission for create $outputAcl['create'] = $outputAcl['edit']; if ($bean === false) { $bean = BeanFactory::newBean($module); } // we cannot use ACLField::getAvailableFields because it limits the fieldset we return. We need all fields // for instance assigned_user_id is skipped in getAvailableFields, thus making the acl's look odd if Assigned User has ACL's // only assigned_user_name is returned which is a derived ["fake"] field. We really need assigned_user_id to return as well. if (empty($GLOBALS['dictionary'][$bean->object_name]['fields'])) { if (empty($bean->acl_fields)) { $fieldsAcl = array(); } else { $fieldsAcl = $bean->field_defs; } } else { $fieldsAcl = $GLOBALS['dictionary'][$bean->object_name]['fields']; if (isset($GLOBALS['dictionary'][$bean->object_name]['acl_fields']) && $GLOBALS['dictionary'][$bean->object_name] === false) { $fieldsAcl = array(); } } // get the field names SugarACL::listFilter($module, $fieldsAcl, $context, array('add_acl' => true)); $fieldsAcl = $this->getMetaDataHacks()->fixAcls($fieldsAcl); foreach ($fieldsAcl as $field => $fieldAcl) { switch ($fieldAcl['acl']) { case SugarACL::ACL_READ_WRITE: // Default, don't need to send anything down break; case SugarACL::ACL_READ_ONLY: $outputAcl['fields'][$field]['write'] = 'no'; $outputAcl['fields'][$field]['create'] = 'no'; break; case 2: $outputAcl['fields'][$field]['read'] = 'no'; break; case SugarACL::ACL_NO_ACCESS: default: $outputAcl['fields'][$field]['read'] = 'no'; $outputAcl['fields'][$field]['write'] = 'no'; $outputAcl['fields'][$field]['create'] = 'no'; break; } } } } // there are times when we need the yes results, for instance comparing access for a record if ($showYes === false) { // for brevity, filter out 'yes' fields since UI assumes 'yes' foreach ($outputAcl as $k => $v) { if ($v == 'yes') { unset($outputAcl[$k]); } } } $outputAcl['_hash'] = $this->hashChunk($outputAcl); return $outputAcl; }
/** * Get the beans ACL's to pass back any that differ * @param SugarBean $bean * @param array $fieldList * @return array */ public function getBeanAcl(SugarBean $bean, array $fieldList) { $acl = array('fields' => (object) array()); if (SugarACL::moduleSupportsACL($bean->module_dir)) { $mm = MetaDataManager::getManager($this->api->platform); $moduleAcl = $mm->getAclForModule($bean->module_dir, $this->api->user, false, true); $beanAcl = $mm->getAclForModule($bean->module_dir, $this->api->user, $bean, true); if ($beanAcl['_hash'] != $moduleAcl['_hash'] || !empty($fieldList)) { // diff the fields separately, they are usually empty anyway so we won't diff these often. $moduleAclFields = $moduleAcl['fields']; $beanAclFields = $beanAcl['fields']; // dont' need the fields here will append at the end unset($moduleAcl['fields']); unset($beanAcl['fields']); // don't need the hashes anymore unset($moduleAcl['_hash']); unset($beanAcl['_hash']); $acl = array_diff_assoc($beanAcl, $moduleAcl); $fieldAcls = array(); /** * Fields are different than module level acces * if fields is empty that means all access is granted * beanAclFields is empty and moduleAclFields is empty -> all access -> return empty * beanAclFields is empty and moduleAclFields is !empty -> all access -> return yes's * beanAclFields is !empty and moduleAclFields is empty -> beanAclFields access restrictions -> return beanAclFields * beanAclFields is !empty and moduleAclFields is !empty -> return all access = "Yes" from moduleAcl and unset any in beanAcl that is in ModuleAcl [don't dupe data] */ if (!empty($beanAclFields) && empty($moduleAclFields)) { $fieldAcls = $beanAclFields; } elseif (!empty($beanAclFields) && !empty($moduleAclFields)) { // we need the ones that are moduleAclFields but not in beanAclFields foreach ($moduleAclFields as $field => $aclActions) { foreach ($aclActions as $action => $access) { if (!isset($beanAclFields[$field][$action])) { $beanAclFields[$field][$action] = "yes"; } // if the bean action is set and it matches the access from module, we do not need to send it down if (isset($beanAclFields[$field][$action]) && $beanAclFields[$field][$action] == $access) { unset($beanAclFields[$field][$action]); } } } // cleanup BeanAclFields, we don't want to pass a field that doens't have actions foreach ($beanAclFields as $field => $actions) { if (empty($actions)) { unset($beanAclFields[$field]); } } $fieldAcls = $beanAclFields; } elseif (empty($beanAclFields) && !empty($moduleAclFields)) { // it is different because we now have access... foreach ($moduleAclFields as $field => $aclActions) { foreach ($aclActions as $action => $access) { $fieldAcls[$field][$action] = "yes"; } } } foreach ($fieldList as $fieldName) { if (empty($fieldAcls[$fieldName]) && isset($moduleAclFields[$fieldName])) { $fieldAcls[$fieldName] = $moduleAclFields[$fieldName]; } } $acl['fields'] = (object) $fieldAcls; } } return $acl; }
/** * Check if module supports ACLs * @api * @param string $module * @return bool */ public function moduleSupportsACL($module) { // FIXME: add support for non-bean ACLs if (!isset($GLOBALS['beanList'][$module])) { return false; } // Always use ACLs via SugarACL return SugarACL::moduleSupportsACL($module); }
private function formatRequestData(SugarBean $bean, $event, array $arguments) { $data = array(); $sfh = new SugarFieldHandler(); $arguments['bean'] = get_class($bean); if (isset($bean->id)) { $data['id'] = $bean->id; } if (!SugarACL::moduleSupportsACL($bean->webhook_target_module) || $bean->ACLAccess('detail')) { $fieldList = $bean->field_defs; $this->ACLFilterFieldList($fieldList, array('bean' => $bean)); $service = new RestService(); foreach ($fieldList as $fieldName => $properties) { $fieldType = !empty($properties['custom_type']) ? $properties['custom_type'] : $properties['type']; $field = $sfh->getSugarField($fieldType); if ('link' !== $fieldType && !empty($field) && (isset($bean->{$fieldName}) || 'relate' === $fieldType)) { $field->apiFormatField($data, $bean, array(), $fieldName, $properties, array(), $service); } } } $arguments['data'] = $data; $arguments['event'] = $event; return $arguments; }