Use closure to neatly encapsulate state.
patricknelson opened this issue · 2 comments
patricknelson commented
When creating a temporary admin account, we need to execute code in a neatly encapsulated manner since we have to maintain global state. I think we should completely remove (or at least deprecate and set a new major version) the use of loginAsAdmin()
method with a superior whileAdmin($closure)
method.
This is because a temp admin is created sometimes (if one isn't available) but never deleted and it's sloppy to do this OUTSIDE of this scope!
e.g.
static::whileAdmin(function() {
// ... do fancy admin-only stuff here, like...
$page->doPublish();
});
Proof of concept:
/**
* The intent with this method is to allow it to maintain it's own state while allowing you to execute your own
* arbitrary code within that state (i.e. while logged in as an administrator).
*
* @param callable $closure The closure (or class/method array) that you'd like to execute while logged in
* as an admin.
* @throws Exception|MigrationException
*/
protected static function whileAdmin(callable $closure) {
// Keeps track of the fact that a temporary admin was just created so we can delete it later.
$tempAdmin = false;
$admin = null;
if (!Member::currentUserID()) {
// See if a default admin is setup yet.
if (!Security::has_default_admin()) {
// Generate a randomized user/pass and use that as the default administrator just for this session.
$tempAdmin = true;
$user = substr(str_shuffle(sha1("u" . microtime())), 0, 20);
$pass = substr(str_shuffle(sha1("p" . microtime())), 0, 20);
Security::setDefaultAdmin($user, $pass);
}
$admin = Member::default_admin();
if (!$admin) throw new MigrationException("Cannot login: No default administrator found.");
Session::start();
Session::set("loggedInAs", $admin->ID);
}
// Call passed closure.
try {
call_user_func($closure);
} catch(Exception $e) {}
// Clean up.
Session::set("loggedInAs", null);
if ($tempAdmin && $admin) $admin->delete();
// Throw the exception if one occurred (in lieu of a "finally" block in older PHP versions).
if (isset($e)) throw $e;
}
patricknelson commented
PS: May need to also account for the possibility that a user may already be logged in somehow and do not want to upset that in the line above:
Session::set("loggedInAs", null);
patricknelson commented
Fixed in 4b10f12