From: Vincent Zanotti Date: Sat, 1 Jan 2011 23:06:07 +0000 (+0100) Subject: Adds a new PlHook subclass for token-authenticated requests. X-Git-Tag: core/1.1.2~16 X-Git-Url: http://git.polytechnique.org/?a=commitdiff_plain;h=a59a4446b3f96e25620e98d4aab14860dd2ee951;p=platal.git Adds a new PlHook subclass for token-authenticated requests. The new hook checks for either a valid token (in which case the authentication level is assumed to always match), or a valid session (in which case the auth level is checked against the session). Note that this finally allows token-based handlers to be permission checked. Signed-off-by: Vincent Zanotti --- diff --git a/classes/platal.php b/classes/platal.php index e5187e4..755cf67 100644 --- a/classes/platal.php +++ b/classes/platal.php @@ -38,18 +38,12 @@ abstract class PlHook $this->type = $type; } - public function needAuth() - { - return $this->auth > S::i('auth', AUTH_PUBLIC); - } - public function checkPerms() { - if (!$this->perms || $this->auth == AUTH_PUBLIC) { // No perms, no check - return true; - } - $s_perms = S::v('perms'); - return $s_perms->hasFlagCombination($this->perms); + // Don't check permissions if there are no permission requirement + // (either no requested group membership, or public auth is allowed). + return !$this->perms || $this->auth == AUTH_PUBLIC || + Platal::session()->checkPerms($this->perms); } public function hasType($type) @@ -62,7 +56,7 @@ abstract class PlHook public function call(PlPage &$page, array $args) { global $globals, $session, $platal; - if ($this->needAuth()) { + if (!$session->checkAuth($this->auth)) { if ($this->hasType(DO_AUTH)) { if (!$session->start($this->auth)) { $platal->force_login($page); @@ -81,14 +75,18 @@ abstract class PlHook } } +/** The standard plat/al hook, for interactive requests. + * It optionally does active authentication (DO_AUTH). The handler is invoked + * with the PlPage object, and with each of the remaining path components. + */ class PlStdHook extends PlHook { - private $hook; + private $callback; public function __construct($callback, $auth = AUTH_PUBLIC, $perms = 'user', $type = DO_AUTH) { parent::__construct($auth, $perms, $type); - $this->hook = $callback; + $this->callback = $callback; } protected function run(PlPage &$page, array $args) @@ -96,17 +94,76 @@ class PlStdHook extends PlHook global $session, $platal; $args[0] = $page; - $val = call_user_func_array($this->hook, $args); + $val = call_user_func_array($this->callback, $args); if ($val == PL_DO_AUTH) { if (!$session->start($session->loggedLevel())) { $platal->force_login($page); } - $val = call_user_func_array($this->hook, $args); + $val = call_user_func_array($this->callback, $args); } return $val; } } +/** A specialized hook for token-based requests. + * It is intended for purely passive requests (typically for serving CSV or RSS + * content outside the browser), and can fallback to regular session-based + * authentication when the token is not valid/available. + * + * Note that $auth is only applied for session-backed authentication; it is + * assumed that token-based auth is always enough for the hook (otherwise, just + * use PlStdHook above). + * + * Also, this hook requires that the first two unmatched path components are the + * user and token (for instance ////....). They will + * be popped before being passed to the handler, and replaced by the request's + * PlUser object. + */ +class PlTokenHook extends PlHook +{ + private $actualAuth; + private $callback; + + public function __construct($callback, $auth = AUTH_PUBLIC, $perms = 'user', $type = NO_AUTH) + { + // As mentioned above, $auth is only applied for session-based auth + // (as opposed to token-based). PlHook is initialized to AUTH_PUBLIC to + // avoid it refusing to approve requests; this is important as the user + // is not yet authenticated at that point (see below for the actual + // permissions check). + parent::__construct(AUTH_PUBLIC, $perms, $type); + $this->actualAuth = $auth; + $this->callback = $callback; + } + + protected function run(PlPage &$page, array $args) + { + // Retrieve the user, either from the session (less expensive, as it is + // already there), or from the in-path (user, token) pair. + if (S::logged() && Platal::session()->checkAuth($this->actualAuth)) { + $user = S::user(); + } else { + $user = Platal::session()->tokenAuth(@$args[1], @$args[2]); + } + + // Check the permissions, unless the handler is fully public. + if ($this->actualAuth > AUTH_PUBLIC) { + if (is_null($user) || !$user->checkPerms($this->perms)) { + return PL_FORBIDDEN; + } + } + + // Replace the first three remaining elements of the path with the + // PlPage and PlUser objects. + array_shift($args); + $args[0] = $page; + $args[1] = $user; + return call_user_func_array($this->callback, $args); + } +} + +/** A specialized plat/al hook for serving wiki pages. + */ class PlWikiHook extends PlHook { public function __construct($auth = AUTH_PUBLIC, $perms = 'user', $type = DO_AUTH) diff --git a/classes/plfeed.php b/classes/plfeed.php index d54199a..ce9ea08 100644 --- a/classes/plfeed.php +++ b/classes/plfeed.php @@ -95,15 +95,10 @@ abstract class PlFeed implements PlIterator return $this->iterator->last(); } - public function run(PlPage& $page, $login, $token, $require_auth = true, $type = 'rss2') + public function run(PlPage& $page, PlUser& $user, $require_auth = true, $type = 'rss2') { - $user = Platal::session()->tokenAuth($login, $token); - if (empty($user)) { - if ($require_auth) { - return PL_FORBIDDEN; - } else { - $user = null; - } + if (empty($user) && $require_auth) { + return PL_FORBIDDEN; } $page->assign('rss_hash', $token); diff --git a/classes/plmodule.php b/classes/plmodule.php index 55de69f..92071b8 100644 --- a/classes/plmodule.php +++ b/classes/plmodule.php @@ -34,24 +34,48 @@ abstract class PLModule abstract public function handlers(); /** Register a hook - * @param fun name of the handler (the exact name will be handler_$fun) - * @param auth authentification level of needed to run this handler + * @param fun name of the handler (the exact name will be handler_$fun); the + * handler will be invoked with the PlPage object, and the unmatched path + * components + * @param auth authentification level required to run this handler * @param perms permission required to run this handler * @param type additionnal flags * * Perms syntax is the following: - * perms = rights(,rights)* - * rights = right(:right)* + * perms = rights(,rights)* + * rights = right(:right)* * right is an atomic right permission (like 'admin', 'user', 'groupadmin', 'groupmember'...) * - * If type is set to NO_AUTH, the system will return 403 instead of asking auth data - * this is useful for Ajax handler - * If type is not set to NO_SKIN, the system will consider redirecting the user to https + * If type is set to NO_AUTH, the system will return 403 instead of asking + * auth data; this is useful for Ajax handler. If type is not set to + * NO_SKIN, the system will consider redirecting the user to https. */ public function make_hook($fun, $auth, $perms = 'user', $type = DO_AUTH) { - return new PlStdHook(array($this, 'handler_' . $fun), - $auth, $perms, $type); + return new PlStdHook(array($this, 'handler_' . $fun), $auth, $perms, $type); + } + + /** Register a token-authentified hook (rss, csv, ical, ...) + * @param fun name of the handler (the exact name will be handler_$fun); the + * handler will be invoked with the PlPage object, the PlUser of the + * request, and the unmatched path components + * @param auth authentification level required, when not token-authentified + * @param perms permission required to run this handler + * @param type additionnal flags + * + * See {@link make_hook} above for details on permissions and additional + * flags. Note that DO_AUTH has no effect here, as the request will always + * be passively identified. + * + * This hook requires that the first two unmatched path components form a + * valid (user, token) pair; if not, a session-based authentification will + * be attempted, in which case $auth will be honored. + * Note that because token-based authentication is weak, it should only be + * used for readonly handlers normally served in AUTH_COOKIE. + */ + public function make_token_hook($fun, $auth, $perms = 'user', $type = NO_HTTPS) + { + return new PlTokenHook(array($this, 'handler_' . $fun), $auth, $perms, $type); } /** Register a hook that points to a wiki page. diff --git a/include/platal.inc.php b/include/platal.inc.php index a638d0d..a174482 100644 --- a/include/platal.inc.php +++ b/include/platal.inc.php @@ -24,14 +24,24 @@ $TIME_BEGIN = microtime(true); require_once dirname(__FILE__) . '/version.inc.php'; require_once dirname(__FILE__) . '/misc.inc.php'; -define('PERMS_EXT', 'ext'); +// Common basic permission flags. define('PERMS_USER', 'user'); define('PERMS_ADMIN', 'admin'); -define('SKINNED', 0); -define('SIMPLE', 1); -define('NO_SKIN', 2); - +// Page style options, used when rendering pages. Options are exclusive. +define('SKINNED', 0); // Page is rendered with the normal skin. +define('SIMPLE', 1); // Page is rendered with a light skin (no leftnav). +define('NO_SKIN', 2); // Page content is passed as-is (use for csv, xml, ...). + +// Hook options bitmasks. Authentication options are mutually exclusive, but +// others (NO_HTTPS at the moment) are not. +// +// With PlStdHook, NO_AUTH indicates that no session will be started, and that +// the actual handler is responsible for doing authentication; DO_AUTH forces +// the engine to try to authenticate the user, including redirecting to the +// login page. Note that DO_AUTH is ignored if AUTH_PUBLIC is requested. +// +// Options NO_AUTH and DO_AUTH are ignored with PlTokenHook. define('NO_AUTH', 0); define('DO_AUTH', 1); define('NO_HTTPS', 2);