/** * @ApiDoc( * section="ACL Management", * description="Saves the given rules" * ) * * @Rest\RequestParam(name="targetId", requirements=".+", strict=true, description="Target id") * @Rest\RequestParam(name="targetType", requirements=".+", strict=true, description="Target type") * @Rest\RequestParam(name="rules", strict=false, description="ACL rules array") * * @Rest\Post("/user/acl") * * @param int $targetId * @param int $targetType * @param array $rules * * @return bool */ public function saveAcl($targetId, $targetType, $rules = null) { $targetId += 0; $targetType += 0; AclQuery::create()->filterByTargetId($targetId)->filterByTargetType($targetType)->delete(); if (0 < count($rules)) { $i = 1; if (is_array($rules)) { foreach ($rules as $rule) { $ruleObject = new Acl(); $ruleObject->setPrio($i); $ruleObject->setTargetType($targetType); $ruleObject->setTargetId($targetId); $ruleObject->setTargetId($targetId); $ruleObject->setObject(Objects::normalizeObjectKey(@$rule['object'])); $ruleObject->setSub(filter_var(@$rule['sub'], FILTER_VALIDATE_BOOLEAN)); $ruleObject->setAccess(filter_var(@$rule['access'], FILTER_VALIDATE_BOOLEAN)); $ruleObject->setFields(@$rule['fields']); $ruleObject->setConstraintType(@$rule['constraintType']); $ruleObject->setConstraintCode(@$rule['constraintCode']); $ruleObject->setMode(@$rule['mode'] + 0); $ruleObject->save(); $i++; } } } $this->cacher->invalidateCache('core/acl'); return true; }
/** * Important call directly after the creation of this class. * * @param string $objectKey * @param Object $definition */ public function configure($objectKey, $definition) { $this->objectKey = \Jarves\Objects::normalizeObjectKey($objectKey); $this->definition = $definition; foreach ($this->definition->getFields() as $field) { if ($field->isPrimaryKey()) { $this->primaryKeys[] = $field->getId(); } } }
/** * Returns php class name. * * @param string $objectName * * @return string */ public function getPhpName($objectName = null) { $clazz = Objects::normalizeObjectKey($objectName ?: $this->getObjectKey()); $clazz = $this->objects->getNamespace($clazz) . '\\Model\\' . ucfirst($this->objects->getName($clazz)); return $clazz; }
/** * @param ACLRequest $aclRequest * * @return bool */ public function check(ACLRequest $aclRequest) { $objectKey = Objects::normalizeObjectKey($aclRequest->getObjectKey()); $targetType = $aclRequest->getTargetType(); $targetId = $aclRequest->getTargetId(); $pk = $aclRequest->getPrimaryKey(); $field = $aclRequest->getField(); $pk = $this->objects->normalizePkString($objectKey, $pk); if (ACL::TARGET_TYPE_USER === $targetType && null === $targetId) { //0 means guest $targetId = $this->pageStack->getUser() ? $this->pageStack->getUser()->getId() : 0; } $user = $this->pageStack->getUser(); if ($user) { $groupIds = $user->getGroupIds(); if (false !== strpos(',' . $groupIds . ',', ',1,')) { //user is in the admin group, so he has always access. return true; } } if (ACL::TARGET_TYPE_USER === $targetType && 1 === $targetId) { //user admin has always access return true; } if (ACL::TARGET_TYPE_GROUP === $targetType && 1 === $targetId) { //group admin has always access return true; } if (0 === $targetId) { //guests do always have no access return false; } if (ACL::TARGET_TYPE_GROUP === $targetType && !$targetId) { throw new \InvalidArgumentException('For type TARGET_TYPE_GROUP a targetId is required.'); } $cacheKey = null; if ($pk && $this->getCaching()) { $pkString = $this->objects->getObjectUrlId($objectKey, $pk); $cacheKey = md5($targetType . '.' . $targetId . '.' . $objectKey . '/' . $pkString . '/' . json_encode($field)); $cached = $this->cacher->getDistributedCache('core/acl/' . $cacheKey); if (null !== $cached) { return $cached; } } $rules = self::getRules($objectKey, $aclRequest->getMode(), $targetType, $targetId); if (count($rules) === 0) { //no rules found, so we have no access return false; } $access = null; $currentObjectPk = $pk; $definition = $this->objects->getDefinition($objectKey); $not_found = true; //starts directly as if we were in the parent checking. $parent_acl = $aclRequest->isAsParent(); $fCount = null; $fKey = null; $fValue = null; $fIsArray = is_array($field); if ($fIsArray) { $fCount = count($field); $fKey = key($field); $fValue = current($field); if (is_int($fKey)) { $fKey = $fValue; $fValue = null; } } $depth = 0; $match = false; $originObjectItemPk = $currentObjectPk; while ($not_found) { $currentObjectPkString = null; if ($currentObjectPk) { $currentObjectPkString = $this->objects->getObjectUrlId($objectKey, $currentObjectPk); } $depth++; if ($depth > 50) { $not_found = false; break; } foreach ($rules as $aclRule) { if ($parent_acl && !$aclRule['sub']) { //as soon we enter the parent_acl mode we only take acl rules into consideration //that are also valid for children (sub=true) continue; } $match = false; /* * CUSTOM CONSTRAINT */ if ($aclRule['constraint_type'] === ACL::CONSTRAINT_CONDITION) { $objectItem = null; if ($originObjectItemPk === $currentObjectPk && null !== $aclRequest->getPrimaryObjectItem()) { $objectItem = $aclRequest->getPrimaryObjectItem(); } else { if ($originObjectItemPk) { $objectItem = $this->objects->get($objectKey, $currentObjectPk); } } if ($objectItem && $this->conditionOperator->satisfy($aclRule['constraint_code'], $objectItem, $objectKey)) { $match = true; } /* * EXACT */ } else { if ($aclRule['constraint_type'] === ACL::CONSTRAINT_EXACT) { if ($currentObjectPk && $aclRule['constraint_code'] === $currentObjectPkString) { $match = true; } /** * ALL */ } else { $match = true; } } if (!$match && $aclRule['sub'] && $currentObjectPk) { // we need to check if a parent matches this $acl as we have sub=true $parentItem = $this->objects->normalizePkString($objectKey, $currentObjectPk); $parentCondition = Condition::create($aclRule['constraint_code']); $parentOptions['fields'] = $this->conditionOperator->extractFields($parentCondition); while ($parentItem = $this->objects->getParent($objectKey, $this->objects->getObjectPk($objectKey, $parentItem), $parentOptions)) { if ($aclRule['constraint_type'] === ACL::CONSTRAINT_CONDITION && $this->conditionOperator->satisfy($parentCondition, $parentItem)) { $match = true; break; } else { if ($aclRule['constraint_type'] === ACL::CONSTRAINT_EXACT && $aclRule['constraint_code'] === $this->objects->getObjectUrlId($objectKey, $parentItem)) { $match = true; break; } } } } if ($match) { //match, check all $field $field2Key = $field; if ($field) { if ($fIsArray && $fCount === 1) { if (is_string($fKey) && is_array($aclRule['fields'][$fKey])) { //this field has limits if (($field2Acl = $aclRule['fields'][$fKey]) !== null) { if (is_array($field2Acl[0])) { //complex field rule, $field2Acl = ([{access: no, condition: [['id', '>', 2], ..]}, {}, ..]) foreach ($field2Acl as $fRule) { $satisfy = false; if (($f = $definition->getField($fKey)) && $f->getType() === 'object') { $uri = $f->getObject() . '/' . $fValue; $uriObject = $this->objects->getFromUrl($uri); $satisfy = $this->conditionOperator->satisfy($fRule['condition'], $uriObject); } else { if (null !== $fValue) { $satisfy = $this->conditionOperator->satisfy($fRule['condition'], $field); } } if ($satisfy) { return $fRule['access'] === 1 ? true : false; } } //if no field rules fits, we consider the whole rule if ($aclRule['access'] !== 2) { return $aclRule['access'] === 1 ? true : false; } } else { //simple field rule $field2Acl = ({"value1": yes, "value2": no} if ($field2Acl[$fKey] !== null) { return $field2Acl[$fKey] === 1 ? true : false; } else { //current($field) is not exactly defined in $field2Acl, so we set $access to $acl['access'] // //if access = 2 then wo do not know it, cause 2 means 'inherited', so maybe //a other rule has more detailed rule if ($aclRule['access'] !== 2) { $access = $aclRule['access'] === 1 ? true : false; break; } } } } } else { //this field has only true or false $field2Key = $fKey; } } if (!is_array($field2Key)) { if ($aclRule['fields'] && ($field2Acl = $aclRule['fields'][$field2Key]) !== null && !is_array($aclRule['fields'][$field2Key])) { $access = $field2Acl === 1 ? true : false; break; } else { //$field is not exactly defined, so we set $access to $acl['access'] //and maybe a rule with the same code has the field defined // if access = 2 then this rule is only for exactly define fields if ($aclRule['access'] !== 2) { $access = $aclRule['access'] === 1 ? true : false; break; } } } } else { $access = $aclRule['access'] === 1 ? true : false; break; } } } //foreach if (null === $access && $definition->isNested() && $pk) { //$access has not defined yet (no rule matched yet). Check if nested and $pk is given //load its root and check again if (null === ($currentObjectPk = $this->objects->getParentPk($objectKey, $currentObjectPk))) { $access = $aclRequest->isRootHasAccess() ? true : $access; break; } $parent_acl = true; } else { break; } } $access = (bool) $access; if ($pk && $this->getCaching()) { $this->cacher->setDistributedCache('core/acl/' . $cacheKey, $access); } return $access; }
/** * @param string $objectKey * @return Object * * @throws BundleNotFoundException */ public function getObject($objectKey) { $objectKey = Objects::normalizeObjectKey($objectKey); if (false === strpos($objectKey, '/')) { return null; } list($bundleName, $objectName) = explode('/', $objectKey); if ('bundle' !== substr($bundleName, -6)) { $bundleName .= 'bundle'; } if (!($config = $this->getConfig($bundleName))) { throw new BundleNotFoundException(sprintf('Bundle `%s` not found. [%s]', $bundleName, $objectKey)); } return $config->getObject($objectName); }
/** * Adds a item. * * @param string $objectKey * @param array $values * @param mixed $targetPk Full PK as array or as primary key string (url). * @param string $position If nested set. `first` (child), `last` (last child), `prev` (sibling), `next` (sibling) * @param bool $targetObjectKey If this object key differs from $objectKey then we'll use $pk as `scope`. Also * it is then only possible to have position `first` and `last`. * @param array $options * * @return mixed * * @throws \Jarves\Exceptions\InvalidArgumentException */ public function add($objectKey, $values, $targetPk = null, $position = 'first', $targetObjectKey = null, array $options = array()) { $targetPk = $this->normalizePk($objectKey, $targetPk); $objectKey = Objects::normalizeObjectKey($objectKey); if ($targetObjectKey) { $targetObjectKey = Objects::normalizeObjectKey($targetObjectKey); } $repository = $this->getRepository($objectKey, $options); return $repository->add($values, $targetPk, $position, $targetObjectKey); }
/** * @return string */ public function getObject() { return Objects::normalizeObjectKey($this->object); }
/** * When ObjectCrudController is without custom sub class used, then we need to get the object information * from the route, defined in Jarves\Router\RestApiLoader * * @return string */ protected function detectObjectKeyFromRoute() { $request = $this->requestStack->getCurrentRequest(); return $request ? Objects::normalizeObjectKey($request->attributes->get('_jarves_object')) : ''; }