From 81bb3a65b12cb9bec02327facf4ef5e0647893e4 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Mon, 20 Jul 2020 12:28:35 +0800 Subject: [PATCH] MDL-68974 admin: Prevent login as outside of the desired context --- lib/accesslib.php | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/lib/accesslib.php b/lib/accesslib.php index ab6f09eff00..46652ef487e 100644 --- a/lib/accesslib.php +++ b/lib/accesslib.php @@ -521,6 +521,16 @@ function has_capability($capability, context $context, $user = null, $doanything } } + if (!empty($USER->loginascontext)) { + // The current user is logged in as another user and can assume their identity at or below the `loginascontext` + // defined in the USER session. + // The user may not assume their identity at any other location. + if (!$USER->loginascontext->is_parent_of($context, true)) { + // The context being checked is not the specified context, or one of its children. + return false; + } + } + // Find out if user is admin - it is not possible to override the doanything in any way // and it is not possible to switch to admin role either. if ($doanything) { @@ -5590,6 +5600,30 @@ abstract class context extends stdClass implements IteratorAggregate { return $result; } + /** + * Determine if the current context is a parent of the possible child. + * + * @param context $possiblechild + * @param bool $includeself Whether to check the current context + * @return bool + */ + public function is_parent_of(context $possiblechild, bool $includeself): bool { + // A simple substring check is used on the context path. + // The possible child's path is used as a haystack, with the current context as the needle. + // The path is prefixed with '+' to ensure that the parent always starts at the top. + // It is suffixed with '+' to ensure that parents are not included. + // The needle always suffixes with a '/' to ensure that the contextid uses a complete match (i.e. 142/ instead of 14). + // The haystack is suffixed with '/+' if $includeself is true to allow the current context to match. + // The haystack is suffixed with '+' if $includeself is false to prevent the current context from matching. + $haystacksuffix = $includeself ? '/+' : '+'; + + $strpos = strpos( + "+{$possiblechild->path}{$haystacksuffix}", + "+{$this->path}/" + ); + return $strpos === 0; + } + /** * Returns parent contexts of this context in reversed order, i.e. parent first, * then grand parent, etc. @@ -5614,6 +5648,30 @@ abstract class context extends stdClass implements IteratorAggregate { return $result; } + /** + * Determine if the current context is a child of the possible parent. + * + * @param context $possibleparent + * @param bool $includeself Whether to check the current context + * @return bool + */ + public function is_child_of(context $possibleparent, bool $includeself): bool { + // A simple substring check is used on the context path. + // The current context is used as a haystack, with the possible parent as the needle. + // The path is prefixed with '+' to ensure that the parent always starts at the top. + // It is suffixed with '+' to ensure that children are not included. + // The needle always suffixes with a '/' to ensure that the contextid uses a complete match (i.e. 142/ instead of 14). + // The haystack is suffixed with '/+' if $includeself is true to allow the current context to match. + // The haystack is suffixed with '+' if $includeself is false to prevent the current context from matching. + $haystacksuffix = $includeself ? '/+' : '+'; + + $strpos = strpos( + "+{$this->path}{$haystacksuffix}", + "+{$possibleparent->path}/" + ); + return $strpos === 0; + } + /** * Returns parent context ids of this context in reversed order, i.e. parent first, * then grand parent, etc.