aboutsummaryrefslogtreecommitdiffhomepage
path: root/main/app/sprinkles/account/src/Authorize
diff options
context:
space:
mode:
authorMarvin Borner2018-06-08 20:03:25 +0200
committerMarvin Borner2018-06-08 20:03:25 +0200
commit92b7dd3335a6572debeacfb5faa82c63a5e67888 (patch)
tree7ebbca22595d542ec5e2912a24a0400ac8f6b113 /main/app/sprinkles/account/src/Authorize
parent22a1bb27f94ea33042b0bdd35bef1a5cfa96cc0d (diff)
Some minor fixes
Diffstat (limited to 'main/app/sprinkles/account/src/Authorize')
-rw-r--r--main/app/sprinkles/account/src/Authorize/AccessConditionExpression.php276
-rw-r--r--main/app/sprinkles/account/src/Authorize/AuthorizationException.php48
-rw-r--r--main/app/sprinkles/account/src/Authorize/AuthorizationManager.php306
-rw-r--r--main/app/sprinkles/account/src/Authorize/ParserNodeFunctionEvaluator.php378
4 files changed, 504 insertions, 504 deletions
diff --git a/main/app/sprinkles/account/src/Authorize/AccessConditionExpression.php b/main/app/sprinkles/account/src/Authorize/AccessConditionExpression.php
index e36f4f4..8a8225e 100644
--- a/main/app/sprinkles/account/src/Authorize/AccessConditionExpression.php
+++ b/main/app/sprinkles/account/src/Authorize/AccessConditionExpression.php
@@ -1,138 +1,138 @@
-<?php
-/**
- * UserFrosting (http://www.userfrosting.com)
- *
- * @link https://github.com/userfrosting/UserFrosting
- * @license https://github.com/userfrosting/UserFrosting/blob/master/licenses/UserFrosting.md (MIT License)
- */
-
-namespace UserFrosting\Sprinkle\Account\Authorize;
-
-use Monolog\Logger;
-use PhpParser\Lexer\Emulative as EmulativeLexer;
-use PhpParser\Node;
-use PhpParser\NodeTraverser;
-use PhpParser\Parser as Parser;
-use PhpParser\PrettyPrinter\Standard as StandardPrettyPrinter;
-use PhpParser\Error as PhpParserException;
-use Psr\Http\Message\ServerRequestInterface as Request;
-use UserFrosting\Sprinkle\Account\Database\Models\User;
-
-/**
- * AccessConditionExpression class
- *
- * This class models the evaluation of an authorization condition expression, as associated with permissions.
- * A condition is built as a boolean expression composed of AccessCondition method calls.
- *
- * @author Alex Weissman (https://alexanderweissman.com)
- */
-class AccessConditionExpression
-{
- /**
- * @var User A user object, which for convenience can be referenced as 'self' in access conditions.
- */
- protected $user;
-
- /**
- * @var ParserNodeFunctionEvaluator The node visitor, which evaluates access condition callbacks used in a permission condition.
- */
- protected $nodeVisitor;
-
- /**
- * @var \PhpParser\Parser The PhpParser object to use (initialized in the ctor)
- */
- protected $parser;
-
- /**
- * @var NodeTraverser The NodeTraverser object to use (initialized in the ctor)
- */
- protected $traverser;
-
- /**
- * @var StandardPrettyPrinter The PrettyPrinter object to use (initialized in the ctor)
- */
- protected $prettyPrinter;
-
- /**
- * @var Logger
- */
- protected $logger;
-
- /**
- * @var bool Set to true if you want debugging information printed to the auth log.
- */
- protected $debug;
-
- /**
- * Create a new AccessConditionExpression object.
- *
- * @param User $user A user object, which for convenience can be referenced as 'self' in access conditions.
- * @param Logger $logger A Monolog logger, used to dump debugging info for authorization evaluations.
- * @param bool $debug Set to true if you want debugging information printed to the auth log.
- */
- public function __construct(ParserNodeFunctionEvaluator $nodeVisitor, User $user, Logger $logger, $debug = FALSE) {
- $this->nodeVisitor = $nodeVisitor;
- $this->user = $user;
- $this->parser = new Parser(new EmulativeLexer);
- $this->traverser = new NodeTraverser;
- $this->traverser->addVisitor($nodeVisitor);
- $this->prettyPrinter = new StandardPrettyPrinter;
- $this->logger = $logger;
- $this->debug = $debug;
- }
-
- /**
- * Evaluates a condition expression, based on the given parameters.
- *
- * The special parameter `self` is an array of the current user's data.
- * This get included automatically, and so does not need to be passed in.
- * @param string $condition a boolean expression composed of calls to AccessCondition functions.
- * @param array[mixed] $params the parameters to be used when evaluating the expression.
- * @return bool true if the condition is passed for the given parameters, otherwise returns false.
- */
- public function evaluateCondition($condition, $params) {
- // Set the reserved `self` parameters.
- // This replaces any values of `self` specified in the arguments, thus preventing them from being overridden in malicious user input.
- // (For example, from an unfiltered request body).
- $params['self'] = $this->user->export();
-
- $this->nodeVisitor->setParams($params);
-
- $code = "<?php $condition;";
-
- if ($this->debug) {
- $this->logger->debug("Evaluating access condition '$condition' with parameters:", $params);
- }
-
- // Traverse the parse tree, and execute any callbacks found using the supplied parameters.
- // Replace the function node with the return value of the callback.
- try {
- // parse
- $stmts = $this->parser->parse($code);
-
- // traverse
- $stmts = $this->traverser->traverse($stmts);
-
- // Evaluate boolean statement. It is safe to use eval() here, because our expression has been reduced entirely to a boolean expression.
- $expr = $this->prettyPrinter->prettyPrintExpr($stmts[0]);
- $expr_eval = "return " . $expr . ";\n";
- $result = eval($expr_eval);
-
- if ($this->debug) {
- $this->logger->debug("Expression '$expr' evaluates to " . ($result == TRUE ? "true" : "false"));
- }
-
- return $result;
- } catch (PhpParserException $e) {
- if ($this->debug) {
- $this->logger->debug("Error parsing access condition '$condition':" . $e->getMessage());
- }
- return FALSE; // Access fails if the access condition can't be parsed.
- } catch (AuthorizationException $e) {
- if ($this->debug) {
- $this->logger->debug("Error parsing access condition '$condition':" . $e->getMessage());
- }
- return FALSE;
- }
- }
-}
+<?php
+/**
+ * UserFrosting (http://www.userfrosting.com)
+ *
+ * @link https://github.com/userfrosting/UserFrosting
+ * @license https://github.com/userfrosting/UserFrosting/blob/master/licenses/UserFrosting.md (MIT License)
+ */
+
+namespace UserFrosting\Sprinkle\Account\Authorize;
+
+use Monolog\Logger;
+use PhpParser\Lexer\Emulative as EmulativeLexer;
+use PhpParser\Node;
+use PhpParser\NodeTraverser;
+use PhpParser\Parser as Parser;
+use PhpParser\PrettyPrinter\Standard as StandardPrettyPrinter;
+use PhpParser\Error as PhpParserException;
+use Psr\Http\Message\ServerRequestInterface as Request;
+use UserFrosting\Sprinkle\Account\Database\Models\User;
+
+/**
+ * AccessConditionExpression class
+ *
+ * This class models the evaluation of an authorization condition expression, as associated with permissions.
+ * A condition is built as a boolean expression composed of AccessCondition method calls.
+ *
+ * @author Alex Weissman (https://alexanderweissman.com)
+ */
+class AccessConditionExpression
+{
+ /**
+ * @var User A user object, which for convenience can be referenced as 'self' in access conditions.
+ */
+ protected $user;
+
+ /**
+ * @var ParserNodeFunctionEvaluator The node visitor, which evaluates access condition callbacks used in a permission condition.
+ */
+ protected $nodeVisitor;
+
+ /**
+ * @var \PhpParser\Parser The PhpParser object to use (initialized in the ctor)
+ */
+ protected $parser;
+
+ /**
+ * @var NodeTraverser The NodeTraverser object to use (initialized in the ctor)
+ */
+ protected $traverser;
+
+ /**
+ * @var StandardPrettyPrinter The PrettyPrinter object to use (initialized in the ctor)
+ */
+ protected $prettyPrinter;
+
+ /**
+ * @var Logger
+ */
+ protected $logger;
+
+ /**
+ * @var bool Set to true if you want debugging information printed to the auth log.
+ */
+ protected $debug;
+
+ /**
+ * Create a new AccessConditionExpression object.
+ *
+ * @param User $user A user object, which for convenience can be referenced as 'self' in access conditions.
+ * @param Logger $logger A Monolog logger, used to dump debugging info for authorization evaluations.
+ * @param bool $debug Set to true if you want debugging information printed to the auth log.
+ */
+ public function __construct(ParserNodeFunctionEvaluator $nodeVisitor, User $user, Logger $logger, $debug = FALSE) {
+ $this->nodeVisitor = $nodeVisitor;
+ $this->user = $user;
+ $this->parser = new Parser(new EmulativeLexer);
+ $this->traverser = new NodeTraverser;
+ $this->traverser->addVisitor($nodeVisitor);
+ $this->prettyPrinter = new StandardPrettyPrinter;
+ $this->logger = $logger;
+ $this->debug = $debug;
+ }
+
+ /**
+ * Evaluates a condition expression, based on the given parameters.
+ *
+ * The special parameter `self` is an array of the current user's data.
+ * This get included automatically, and so does not need to be passed in.
+ * @param string $condition a boolean expression composed of calls to AccessCondition functions.
+ * @param array[mixed] $params the parameters to be used when evaluating the expression.
+ * @return bool true if the condition is passed for the given parameters, otherwise returns false.
+ */
+ public function evaluateCondition($condition, $params) {
+ // Set the reserved `self` parameters.
+ // This replaces any values of `self` specified in the arguments, thus preventing them from being overridden in malicious user input.
+ // (For example, from an unfiltered request body).
+ $params['self'] = $this->user->export();
+
+ $this->nodeVisitor->setParams($params);
+
+ $code = "<?php $condition;";
+
+ if ($this->debug) {
+ $this->logger->debug("Evaluating access condition '$condition' with parameters:", $params);
+ }
+
+ // Traverse the parse tree, and execute any callbacks found using the supplied parameters.
+ // Replace the function node with the return value of the callback.
+ try {
+ // parse
+ $stmts = $this->parser->parse($code);
+
+ // traverse
+ $stmts = $this->traverser->traverse($stmts);
+
+ // Evaluate boolean statement. It is safe to use eval() here, because our expression has been reduced entirely to a boolean expression.
+ $expr = $this->prettyPrinter->prettyPrintExpr($stmts[0]);
+ $expr_eval = "return " . $expr . ";\n";
+ $result = eval($expr_eval);
+
+ if ($this->debug) {
+ $this->logger->debug("Expression '$expr' evaluates to " . ($result == TRUE ? "true" : "false"));
+ }
+
+ return $result;
+ } catch (PhpParserException $e) {
+ if ($this->debug) {
+ $this->logger->debug("Error parsing access condition '$condition':" . $e->getMessage());
+ }
+ return FALSE; // Access fails if the access condition can't be parsed.
+ } catch (AuthorizationException $e) {
+ if ($this->debug) {
+ $this->logger->debug("Error parsing access condition '$condition':" . $e->getMessage());
+ }
+ return FALSE;
+ }
+ }
+}
diff --git a/main/app/sprinkles/account/src/Authorize/AuthorizationException.php b/main/app/sprinkles/account/src/Authorize/AuthorizationException.php
index 33f3d35..f93e847 100644
--- a/main/app/sprinkles/account/src/Authorize/AuthorizationException.php
+++ b/main/app/sprinkles/account/src/Authorize/AuthorizationException.php
@@ -1,24 +1,24 @@
-<?php
-/**
- * UserFrosting (http://www.userfrosting.com)
- *
- * @link https://github.com/userfrosting/UserFrosting
- * @license https://github.com/userfrosting/UserFrosting/blob/master/licenses/UserFrosting.md (MIT License)
- */
-
-namespace UserFrosting\Sprinkle\Account\Authorize;
-
-use UserFrosting\Support\Exception\ForbiddenException;
-
-/**
- * AuthorizationException class
- *
- * Exception for AccessConditionExpression.
- *
- * @author Alex Weissman (https://alexanderweissman.com)
- * @see http://www.userfrosting.com/components/#authorization
- */
-class AuthorizationException extends ForbiddenException
-{
-
-}
+<?php
+/**
+ * UserFrosting (http://www.userfrosting.com)
+ *
+ * @link https://github.com/userfrosting/UserFrosting
+ * @license https://github.com/userfrosting/UserFrosting/blob/master/licenses/UserFrosting.md (MIT License)
+ */
+
+namespace UserFrosting\Sprinkle\Account\Authorize;
+
+use UserFrosting\Support\Exception\ForbiddenException;
+
+/**
+ * AuthorizationException class
+ *
+ * Exception for AccessConditionExpression.
+ *
+ * @author Alex Weissman (https://alexanderweissman.com)
+ * @see http://www.userfrosting.com/components/#authorization
+ */
+class AuthorizationException extends ForbiddenException
+{
+
+}
diff --git a/main/app/sprinkles/account/src/Authorize/AuthorizationManager.php b/main/app/sprinkles/account/src/Authorize/AuthorizationManager.php
index f9fb196..487881f 100644
--- a/main/app/sprinkles/account/src/Authorize/AuthorizationManager.php
+++ b/main/app/sprinkles/account/src/Authorize/AuthorizationManager.php
@@ -1,153 +1,153 @@
-<?php
-/**
- * UserFrosting (http://www.userfrosting.com)
- *
- * @link https://github.com/userfrosting/UserFrosting
- * @license https://github.com/userfrosting/UserFrosting/blob/master/licenses/UserFrosting.md (MIT License)
- */
-
-namespace UserFrosting\Sprinkle\Account\Authorize;
-
-use Interop\Container\ContainerInterface;
-use UserFrosting\Sprinkle\Account\Database\Models\User;
-
-/**
- * AuthorizationManager class.
- *
- * Manages a collection of access condition callbacks, and uses them to perform access control checks on user objects.
- * @author Alex Weissman (https://alexanderweissman.com)
- */
-class AuthorizationManager
-{
- /**
- * @var ContainerInterface The global container object, which holds all your services.
- */
- protected $ci;
-
- /**
- * @var array[callable] An array of callbacks that accept some parameters and evaluate to true or false.
- */
- protected $callbacks = [];
-
- /**
- * Create a new AuthorizationManager object.
- *
- * @param ContainerInterface $ci The global container object, which holds all your services.
- */
- public function __construct(ContainerInterface $ci, array $callbacks = []) {
- $this->ci = $ci;
- $this->callbacks = $callbacks;
- }
-
- /**
- * Register an authorization callback, which can then be used in permission conditions.
- *
- * To add additional callbacks, simply extend the `authorizer` service in your Sprinkle's service provider.
- * @param string $name
- * @param callable $callback
- */
- public function addCallback($name, $callback) {
- $this->callbacks[$name] = $callback;
- return $this;
- }
-
- /**
- * Get all authorization callbacks.
- *
- * @return callable[]
- */
- public function getCallbacks() {
- return $this->callbacks;
- }
-
- /**
- * Checks whether or not a user has access on a particular permission slug.
- *
- * Determine if this user has access to the given $slug under the given $params.
- *
- * @param UserFrosting\Sprinkle\Account\Database\Models\User $user
- * @param string $slug The permission slug to check for access.
- * @param array $params [optional] An array of field names => values, specifying any additional data to provide the authorization module
- * when determining whether or not this user has access.
- * @return boolean True if the user has access, false otherwise.
- */
- public function checkAccess(User $user, $slug, array $params = []) {
- $debug = $this->ci->config['debug.auth'];
-
- if ($debug) {
- $trace = array_slice(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3), 1);
- $this->ci->authLogger->debug("Authorization check requested at: ", $trace);
- $this->ci->authLogger->debug("Checking authorization for user {$user->id} ('{$user->user_name}') on permission '$slug'...");
- }
-
- if ($this->ci->authenticator->guest()) {
- if ($debug) {
- $this->ci->authLogger->debug("User is not logged in. Access denied.");
- }
- return FALSE;
- }
-
- // The master (root) account has access to everything.
- // Need to use loose comparison for now, because some DBs return `id` as a string.
-
- if ($user->id == $this->ci->config['reserved_user_ids.master']) {
- if ($debug) {
- $this->ci->authLogger->debug("User is the master (root) user. Access granted.");
- }
- return TRUE;
- }
-
- // Find all permissions that apply to this user (via roles), and check if any evaluate to true.
- $permissions = $user->getCachedPermissions();
-
- if (empty($permissions) || !isset($permissions[$slug])) {
- if ($debug) {
- $this->ci->authLogger->debug("No matching permissions found. Access denied.");
- }
- return FALSE;
- }
-
- $permissions = $permissions[$slug];
-
- if ($debug) {
- $this->ci->authLogger->debug("Found matching permissions: \n" . print_r($this->getPermissionsArrayDebugInfo($permissions), TRUE));
- }
-
- $nodeVisitor = new ParserNodeFunctionEvaluator($this->callbacks, $this->ci->authLogger, $debug);
- $ace = new AccessConditionExpression($nodeVisitor, $user, $this->ci->authLogger, $debug);
-
- foreach ($permissions as $permission) {
- $pass = $ace->evaluateCondition($permission->conditions, $params);
- if ($pass) {
- if ($debug) {
- $this->ci->authLogger->debug("User passed conditions '{$permission->conditions}' . Access granted.");
- }
- return TRUE;
- }
- }
-
- if ($debug) {
- $this->ci->authLogger->debug("User failed to pass any of the matched permissions. Access denied.");
- }
-
- return FALSE;
- }
-
- /**
- * Remove extraneous information from the permission to reduce verbosity.
- *
- * @param array
- * @return array
- */
- protected function getPermissionsArrayDebugInfo($permissions) {
- $permissionsInfo = [];
- foreach ($permissions as $permission) {
- $permissionData = array_only($permission->toArray(), ['id', 'slug', 'name', 'conditions', 'description']);
- // Remove this until we can find an efficient way to only load these once during debugging
- //$permissionData['roles_via'] = $permission->roles_via->pluck('id')->all();
- $permissionsInfo[] = $permissionData;
- }
-
- return $permissionsInfo;
- }
-}
+<?php
+/**
+ * UserFrosting (http://www.userfrosting.com)
+ *
+ * @link https://github.com/userfrosting/UserFrosting
+ * @license https://github.com/userfrosting/UserFrosting/blob/master/licenses/UserFrosting.md (MIT License)
+ */
+
+namespace UserFrosting\Sprinkle\Account\Authorize;
+
+use Interop\Container\ContainerInterface;
+use UserFrosting\Sprinkle\Account\Database\Models\User;
+
+/**
+ * AuthorizationManager class.
+ *
+ * Manages a collection of access condition callbacks, and uses them to perform access control checks on user objects.
+ * @author Alex Weissman (https://alexanderweissman.com)
+ */
+class AuthorizationManager
+{
+ /**
+ * @var ContainerInterface The global container object, which holds all your services.
+ */
+ protected $ci;
+
+ /**
+ * @var array[callable] An array of callbacks that accept some parameters and evaluate to true or false.
+ */
+ protected $callbacks = [];
+
+ /**
+ * Create a new AuthorizationManager object.
+ *
+ * @param ContainerInterface $ci The global container object, which holds all your services.
+ */
+ public function __construct(ContainerInterface $ci, array $callbacks = []) {
+ $this->ci = $ci;
+ $this->callbacks = $callbacks;
+ }
+
+ /**
+ * Register an authorization callback, which can then be used in permission conditions.
+ *
+ * To add additional callbacks, simply extend the `authorizer` service in your Sprinkle's service provider.
+ * @param string $name
+ * @param callable $callback
+ */
+ public function addCallback($name, $callback) {
+ $this->callbacks[$name] = $callback;
+ return $this;
+ }
+
+ /**
+ * Get all authorization callbacks.
+ *
+ * @return callable[]
+ */
+ public function getCallbacks() {
+ return $this->callbacks;
+ }
+
+ /**
+ * Checks whether or not a user has access on a particular permission slug.
+ *
+ * Determine if this user has access to the given $slug under the given $params.
+ *
+ * @param UserFrosting\Sprinkle\Account\Database\Models\User $user
+ * @param string $slug The permission slug to check for access.
+ * @param array $params [optional] An array of field names => values, specifying any additional data to provide the authorization module
+ * when determining whether or not this user has access.
+ * @return boolean True if the user has access, false otherwise.
+ */
+ public function checkAccess(User $user, $slug, array $params = []) {
+ $debug = $this->ci->config['debug.auth'];
+
+ if ($debug) {
+ $trace = array_slice(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3), 1);
+ $this->ci->authLogger->debug("Authorization check requested at: ", $trace);
+ $this->ci->authLogger->debug("Checking authorization for user {$user->id} ('{$user->user_name}') on permission '$slug'...");
+ }
+
+ if ($this->ci->authenticator->guest()) {
+ if ($debug) {
+ $this->ci->authLogger->debug("User is not logged in. Access denied.");
+ }
+ return FALSE;
+ }
+
+ // The master (root) account has access to everything.
+ // Need to use loose comparison for now, because some DBs return `id` as a string.
+
+ if ($user->id == $this->ci->config['reserved_user_ids.master']) {
+ if ($debug) {
+ $this->ci->authLogger->debug("User is the master (root) user. Access granted.");
+ }
+ return TRUE;
+ }
+
+ // Find all permissions that apply to this user (via roles), and check if any evaluate to true.
+ $permissions = $user->getCachedPermissions();
+
+ if (empty($permissions) || !isset($permissions[$slug])) {
+ if ($debug) {
+ $this->ci->authLogger->debug("No matching permissions found. Access denied.");
+ }
+ return FALSE;
+ }
+
+ $permissions = $permissions[$slug];
+
+ if ($debug) {
+ $this->ci->authLogger->debug("Found matching permissions: \n" . print_r($this->getPermissionsArrayDebugInfo($permissions), TRUE));
+ }
+
+ $nodeVisitor = new ParserNodeFunctionEvaluator($this->callbacks, $this->ci->authLogger, $debug);
+ $ace = new AccessConditionExpression($nodeVisitor, $user, $this->ci->authLogger, $debug);
+
+ foreach ($permissions as $permission) {
+ $pass = $ace->evaluateCondition($permission->conditions, $params);
+ if ($pass) {
+ if ($debug) {
+ $this->ci->authLogger->debug("User passed conditions '{$permission->conditions}' . Access granted.");
+ }
+ return TRUE;
+ }
+ }
+
+ if ($debug) {
+ $this->ci->authLogger->debug("User failed to pass any of the matched permissions. Access denied.");
+ }
+
+ return FALSE;
+ }
+
+ /**
+ * Remove extraneous information from the permission to reduce verbosity.
+ *
+ * @param array
+ * @return array
+ */
+ protected function getPermissionsArrayDebugInfo($permissions) {
+ $permissionsInfo = [];
+ foreach ($permissions as $permission) {
+ $permissionData = array_only($permission->toArray(), ['id', 'slug', 'name', 'conditions', 'description']);
+ // Remove this until we can find an efficient way to only load these once during debugging
+ //$permissionData['roles_via'] = $permission->roles_via->pluck('id')->all();
+ $permissionsInfo[] = $permissionData;
+ }
+
+ return $permissionsInfo;
+ }
+}
diff --git a/main/app/sprinkles/account/src/Authorize/ParserNodeFunctionEvaluator.php b/main/app/sprinkles/account/src/Authorize/ParserNodeFunctionEvaluator.php
index e0db07d..af26d9a 100644
--- a/main/app/sprinkles/account/src/Authorize/ParserNodeFunctionEvaluator.php
+++ b/main/app/sprinkles/account/src/Authorize/ParserNodeFunctionEvaluator.php
@@ -1,189 +1,189 @@
-<?php
-/**
- * UserFrosting (http://www.userfrosting.com)
- *
- * @link https://github.com/userfrosting/UserFrosting
- * @license https://github.com/userfrosting/UserFrosting/blob/master/licenses/UserFrosting.md (MIT License)
- */
-
-namespace UserFrosting\Sprinkle\Account\Authorize;
-
-use Monolog\Logger;
-use PhpParser\Node;
-use PhpParser\NodeVisitorAbstract;
-use PhpParser\PrettyPrinter\Standard as StandardPrettyPrinter;
-
-/**
- * ParserNodeFunctionEvaluator class
- *
- * This class parses access control condition expressions.
- *
- * @author Alex Weissman (https://alexanderweissman.com)
- * @see http://www.userfrosting.com/components/#authorization
- */
-class ParserNodeFunctionEvaluator extends NodeVisitorAbstract
-{
-
- /**
- * @var array[callable] An array of callback functions to be used when evaluating a condition expression.
- */
- protected $callbacks;
- /**
- * @var \PhpParser\PrettyPrinter\Standard The PrettyPrinter object to use (initialized in the ctor)
- */
- protected $prettyPrinter;
- /**
- * @var array The parameters to be used when evaluating the methods in the condition expression, as an array.
- */
- protected $params = [];
-
- /**
- * @var Logger
- */
- protected $logger;
-
- /**
- * @var bool Set to true if you want debugging information printed to the auth log.
- */
- protected $debug;
-
- /**
- * Create a new ParserNodeFunctionEvaluator object.
- *
- * @param array $params The parameters to be used when evaluating the methods in the condition expression, as an array.
- * @param Logger $logger A Monolog logger, used to dump debugging info for authorization evaluations.
- * @param bool $debug Set to true if you want debugging information printed to the auth log.
- */
- public function __construct($callbacks, $logger, $debug = FALSE) {
- $this->callbacks = $callbacks;
- $this->prettyPrinter = new StandardPrettyPrinter;
- $this->logger = $logger;
- $this->debug = $debug;
- $this->params = [];
- }
-
- public function leaveNode(Node $node) {
- // Look for function calls
- if ($node instanceof \PhpParser\Node\Expr\FuncCall) {
- $eval = new \PhpParser\Node\Scalar\LNumber;
-
- // Get the method name
- $callbackName = $node->name->toString();
- // Get the method arguments
- $argNodes = $node->args;
-
- $args = [];
- $argsInfo = [];
- foreach ($argNodes as $arg) {
- $argString = $this->prettyPrinter->prettyPrintExpr($arg->value);
-
- // Debugger info
- $currentArgInfo = [
- 'expression' => $argString
- ];
- // Resolve parameter placeholders ('variable' names (either single-word or array-dot identifiers))
- if (($arg->value instanceof \PhpParser\Node\Expr\BinaryOp\Concat) || ($arg->value instanceof \PhpParser\Node\Expr\ConstFetch)) {
- $value = $this->resolveParamPath($argString);
- $currentArgInfo['type'] = "parameter";
- $currentArgInfo['resolved_value'] = $value;
- // Resolve arrays
- } else if ($arg->value instanceof \PhpParser\Node\Expr\Array_) {
- $value = $this->resolveArray($arg);
- $currentArgInfo['type'] = "array";
- $currentArgInfo['resolved_value'] = print_r($value, TRUE);
- // Resolve strings
- } else if ($arg->value instanceof \PhpParser\Node\Scalar\String_) {
- $value = $arg->value->value;
- $currentArgInfo['type'] = "string";
- $currentArgInfo['resolved_value'] = $value;
- // Resolve numbers
- } else if ($arg->value instanceof \PhpParser\Node\Scalar\DNumber) {
- $value = $arg->value->value;
- $currentArgInfo['type'] = "float";
- $currentArgInfo['resolved_value'] = $value;
- } else if ($arg->value instanceof \PhpParser\Node\Scalar\LNumber) {
- $value = $arg->value->value;
- $currentArgInfo['type'] = "integer";
- $currentArgInfo['resolved_value'] = $value;
- // Anything else is simply interpreted as its literal string value
- } else {
- $value = $argString;
- $currentArgInfo['type'] = "unknown";
- $currentArgInfo['resolved_value'] = $value;
- }
-
- $args[] = $value;
- $argsInfo[] = $currentArgInfo;
- }
-
- if ($this->debug) {
- if (count($args)) {
- $this->logger->debug("Evaluating callback '$callbackName' on: ", $argsInfo);
- } else {
- $this->logger->debug("Evaluating callback '$callbackName'...");
- }
- }
-
- // Call the specified access condition callback with the specified arguments.
- if (isset($this->callbacks[$callbackName]) && is_callable($this->callbacks[$callbackName])) {
- $result = call_user_func_array($this->callbacks[$callbackName], $args);
- } else {
- throw new AuthorizationException("Authorization failed: Access condition method '$callbackName' does not exist.");
- }
-
- if ($this->debug) {
- $this->logger->debug("Result: " . ($result ? "1" : "0"));
- }
-
- return new \PhpParser\Node\Scalar\LNumber($result ? "1" : "0");
- }
- }
-
- public function setParams($params) {
- $this->params = $params;
- }
-
- /**
- * Resolve an array expression in a condition expression into an actual array.
- *
- * @param string $arg the array, represented as a string.
- * @return array[mixed] the array, as a plain ol' PHP array.
- */
- private function resolveArray($arg) {
- $arr = [];
- $items = (array)$arg->value->items;
- foreach ($items as $item) {
- if ($item->key) {
- $arr[$item->key] = $item->value->value;
- } else {
- $arr[] = $item->value->value;
- }
- }
- return $arr;
- }
-
- /**
- * Resolve a parameter path (e.g. "user.id", "post", etc) into its value.
- *
- * @param string $path the name of the parameter to resolve, based on the parameters set in this object.
- * @throws Exception the path could not be resolved. Path is malformed or key does not exist.
- * @return mixed the value of the specified parameter.
- */
- private function resolveParamPath($path) {
- $pathTokens = explode(".", $path);
- $value = $this->params;
- foreach ($pathTokens as $token) {
- $token = trim($token);
- if (is_array($value) && isset($value[$token])) {
- $value = $value[$token];
- continue;
- } else if (is_object($value) && isset($value->$token)) {
- $value = $value->$token;
- continue;
- } else {
- throw new AuthorizationException("Cannot resolve the path \"$path\". Error at token \"$token\".");
- }
- }
- return $value;
- }
-}
+<?php
+/**
+ * UserFrosting (http://www.userfrosting.com)
+ *
+ * @link https://github.com/userfrosting/UserFrosting
+ * @license https://github.com/userfrosting/UserFrosting/blob/master/licenses/UserFrosting.md (MIT License)
+ */
+
+namespace UserFrosting\Sprinkle\Account\Authorize;
+
+use Monolog\Logger;
+use PhpParser\Node;
+use PhpParser\NodeVisitorAbstract;
+use PhpParser\PrettyPrinter\Standard as StandardPrettyPrinter;
+
+/**
+ * ParserNodeFunctionEvaluator class
+ *
+ * This class parses access control condition expressions.
+ *
+ * @author Alex Weissman (https://alexanderweissman.com)
+ * @see http://www.userfrosting.com/components/#authorization
+ */
+class ParserNodeFunctionEvaluator extends NodeVisitorAbstract
+{
+
+ /**
+ * @var array[callable] An array of callback functions to be used when evaluating a condition expression.
+ */
+ protected $callbacks;
+ /**
+ * @var \PhpParser\PrettyPrinter\Standard The PrettyPrinter object to use (initialized in the ctor)
+ */
+ protected $prettyPrinter;
+ /**
+ * @var array The parameters to be used when evaluating the methods in the condition expression, as an array.
+ */
+ protected $params = [];
+
+ /**
+ * @var Logger
+ */
+ protected $logger;
+
+ /**
+ * @var bool Set to true if you want debugging information printed to the auth log.
+ */
+ protected $debug;
+
+ /**
+ * Create a new ParserNodeFunctionEvaluator object.
+ *
+ * @param array $params The parameters to be used when evaluating the methods in the condition expression, as an array.
+ * @param Logger $logger A Monolog logger, used to dump debugging info for authorization evaluations.
+ * @param bool $debug Set to true if you want debugging information printed to the auth log.
+ */
+ public function __construct($callbacks, $logger, $debug = FALSE) {
+ $this->callbacks = $callbacks;
+ $this->prettyPrinter = new StandardPrettyPrinter;
+ $this->logger = $logger;
+ $this->debug = $debug;
+ $this->params = [];
+ }
+
+ public function leaveNode(Node $node) {
+ // Look for function calls
+ if ($node instanceof \PhpParser\Node\Expr\FuncCall) {
+ $eval = new \PhpParser\Node\Scalar\LNumber;
+
+ // Get the method name
+ $callbackName = $node->name->toString();
+ // Get the method arguments
+ $argNodes = $node->args;
+
+ $args = [];
+ $argsInfo = [];
+ foreach ($argNodes as $arg) {
+ $argString = $this->prettyPrinter->prettyPrintExpr($arg->value);
+
+ // Debugger info
+ $currentArgInfo = [
+ 'expression' => $argString
+ ];
+ // Resolve parameter placeholders ('variable' names (either single-word or array-dot identifiers))
+ if (($arg->value instanceof \PhpParser\Node\Expr\BinaryOp\Concat) || ($arg->value instanceof \PhpParser\Node\Expr\ConstFetch)) {
+ $value = $this->resolveParamPath($argString);
+ $currentArgInfo['type'] = "parameter";
+ $currentArgInfo['resolved_value'] = $value;
+ // Resolve arrays
+ } else if ($arg->value instanceof \PhpParser\Node\Expr\Array_) {
+ $value = $this->resolveArray($arg);
+ $currentArgInfo['type'] = "array";
+ $currentArgInfo['resolved_value'] = print_r($value, TRUE);
+ // Resolve strings
+ } else if ($arg->value instanceof \PhpParser\Node\Scalar\String_) {
+ $value = $arg->value->value;
+ $currentArgInfo['type'] = "string";
+ $currentArgInfo['resolved_value'] = $value;
+ // Resolve numbers
+ } else if ($arg->value instanceof \PhpParser\Node\Scalar\DNumber) {
+ $value = $arg->value->value;
+ $currentArgInfo['type'] = "float";
+ $currentArgInfo['resolved_value'] = $value;
+ } else if ($arg->value instanceof \PhpParser\Node\Scalar\LNumber) {
+ $value = $arg->value->value;
+ $currentArgInfo['type'] = "integer";
+ $currentArgInfo['resolved_value'] = $value;
+ // Anything else is simply interpreted as its literal string value
+ } else {
+ $value = $argString;
+ $currentArgInfo['type'] = "unknown";
+ $currentArgInfo['resolved_value'] = $value;
+ }
+
+ $args[] = $value;
+ $argsInfo[] = $currentArgInfo;
+ }
+
+ if ($this->debug) {
+ if (count($args)) {
+ $this->logger->debug("Evaluating callback '$callbackName' on: ", $argsInfo);
+ } else {
+ $this->logger->debug("Evaluating callback '$callbackName'...");
+ }
+ }
+
+ // Call the specified access condition callback with the specified arguments.
+ if (isset($this->callbacks[$callbackName]) && is_callable($this->callbacks[$callbackName])) {
+ $result = call_user_func_array($this->callbacks[$callbackName], $args);
+ } else {
+ throw new AuthorizationException("Authorization failed: Access condition method '$callbackName' does not exist.");
+ }
+
+ if ($this->debug) {
+ $this->logger->debug("Result: " . ($result ? "1" : "0"));
+ }
+
+ return new \PhpParser\Node\Scalar\LNumber($result ? "1" : "0");
+ }
+ }
+
+ public function setParams($params) {
+ $this->params = $params;
+ }
+
+ /**
+ * Resolve an array expression in a condition expression into an actual array.
+ *
+ * @param string $arg the array, represented as a string.
+ * @return array[mixed] the array, as a plain ol' PHP array.
+ */
+ private function resolveArray($arg) {
+ $arr = [];
+ $items = (array)$arg->value->items;
+ foreach ($items as $item) {
+ if ($item->key) {
+ $arr[$item->key] = $item->value->value;
+ } else {
+ $arr[] = $item->value->value;
+ }
+ }
+ return $arr;
+ }
+
+ /**
+ * Resolve a parameter path (e.g. "user.id", "post", etc) into its value.
+ *
+ * @param string $path the name of the parameter to resolve, based on the parameters set in this object.
+ * @throws Exception the path could not be resolved. Path is malformed or key does not exist.
+ * @return mixed the value of the specified parameter.
+ */
+ private function resolveParamPath($path) {
+ $pathTokens = explode(".", $path);
+ $value = $this->params;
+ foreach ($pathTokens as $token) {
+ $token = trim($token);
+ if (is_array($value) && isset($value[$token])) {
+ $value = $value[$token];
+ continue;
+ } else if (is_object($value) && isset($value->$token)) {
+ $value = $value->$token;
+ continue;
+ } else {
+ throw new AuthorizationException("Cannot resolve the path \"$path\". Error at token \"$token\".");
+ }
+ }
+ return $value;
+ }
+}