Rewrite login/logout process

This commit is contained in:
Dmitriy Simushev 2014-05-29 11:38:21 +00:00
parent 6a5b962f01
commit 335cdc6055
18 changed files with 402 additions and 213 deletions

View File

@ -372,7 +372,7 @@ function check_status()
if (!check_admin($link)) {
$page['nextstep'] = getlocal("installed.login_link");
$page['nextnotice'] = getlocal2("installed.notice", array(MIBEW_WEB_ROOT . "/install/"));
$page['nextstepurl'] = MIBEW_WEB_ROOT . "/operator/login.php?login=admin";
$page['nextstepurl'] = MIBEW_WEB_ROOT . "/operator/login?login=admin";
}
$page['show_small_login'] = true;

View File

@ -18,8 +18,10 @@
namespace Mibew;
use Mibew\AccessControl\Check\CheckResolver;
use Mibew\Authentication\AuthenticationManager;
use Mibew\Controller\ControllerResolver;
use Mibew\EventDispatcher;
use Mibew\Http\CookieFactory;
use Mibew\Http\Exception\AccessDeniedException as AccessDeniedHttpException;
use Mibew\Http\Exception\HttpException;
use Mibew\Http\Exception\MethodNotAllowedException as MethodNotAllowedHttpException;
@ -60,6 +62,11 @@ class Application
*/
protected $accessCheckResolver = null;
/**
* @var AuthenticationManager|null
*/
protected $authenticationManager = null;
/**
* Class constructor.
*/
@ -69,6 +76,7 @@ class Application
$this->router = new Router(new RouteCollectionLoader($this->fileLocator));
$this->controllerResolver = new ControllerResolver($this->router);
$this->accessCheckResolver = new CheckResolver();
$this->authenticationManager = new AuthenticationManager();
}
/**
@ -84,13 +92,20 @@ class Application
$context->fromRequest($request);
$this->router->setContext($context);
// Actualize cookie factory in the authentication manager.
$cookie_factory = CookieFactory::fromRequest($request);
$this->authenticationManager->setCookieFactory($cookie_factory);
try {
// Try to match a route, check if the client can access it and add
// extra data to the request.
try {
$parameters = $this->router->matchRequest($request);
$request->attributes->add($parameters);
$request->attributes->set('_operator', $this->extractOperator($request));
$request->attributes->set(
'_operator',
$this->authenticationManager->extractOperator($request)
);
// Check if the user can access the page
$access_check = $this->accessCheckResolver->getCheck($request);
@ -112,63 +127,30 @@ class Application
$controller = $this->controllerResolver->getController($request);
$response = call_user_func($controller, $request);
} catch (AccessDeniedHttpException $e) {
return $this->buildAccessDeniedResponse($request);
$response = $this->buildAccessDeniedResponse($request);
} catch (HttpException $e) {
// Build response based on status code which is stored in exception
// instance.
$http_status = $e->getStatusCode();
$content = Response::$statusTexts[$http_status];
return new Response($content, $http_status);
$response = new Response($content, $http_status);
} catch (\Exception $e) {
return new Response('Internal Server Error', 500);
$response = new Response('Internal Server Error', 500);
}
if ($response instanceof Response) {
return $response;
} else {
if (!($response instanceof Response)) {
// Convert all content returned by a controller's action to Response
// instance.
return new Response((string)$response);
}
$response = new Response((string)$response);
}
/**
* Extracts operator's data from the passed in request object.
*
* @param Request $request A request to extract operator from.
* @return array|bool Associative array with operator's data or boolean
* false if there is no operator related with the request.
*
* @todo Remove this method when Object Oriented wrapper for an operator
* will be created.
*/
protected function extractOperator(Request $request)
{
// Try to get operator from session.
if (isset($_SESSION[SESSION_PREFIX . "operator"])) {
return $_SESSION[SESSION_PREFIX . "operator"];
}
// Get modified operator from the request and attach authentication info
// to the response to distinguish him in the next requests.
$operator = $request->attributes->get('_operator');
$this->authenticationManager->attachOperator($response, $operator);
// Check if operator had used "remember me" feature.
if ($request->cookies->has(REMEMBER_OPERATOR_COOKIE_NAME)) {
$cookie_value = $request->cookies->get(REMEMBER_OPERATOR_COOKIE_NAME);
list($login, $pwd) = preg_split('/\x0/', base64_decode($cookie_value), 2);
$op = operator_by_login($login);
$can_login = $op
&& isset($pwd)
&& isset($op['vcpassword'])
&& calculate_password_hash($op['vclogin'], $op['vcpassword']) == $pwd
&& !operator_is_disabled($op);
if ($can_login) {
$_SESSION[SESSION_PREFIX . "operator"] = $op;
return $op;
}
}
// Operator's data cannot be extracted from the request.
return false;
return $response;
}
/**
@ -207,7 +189,7 @@ class Application
// Operator is not logged in. Redirect him to the login page.
$_SESSION['backpath'] = $request->getUri();
$response = new RedirectResponse($request->getUriForPath('/operator/login.php'));
$response = new RedirectResponse($this->router->generate('login'));
return $response;
}

View File

@ -0,0 +1,160 @@
<?php
/*
* Copyright 2005-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Mibew\Authentication;
use Mibew\Http\CookieFactory;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* Controls operator's authentication.
*/
class AuthenticationManager
{
/**
* @var CookieFactory|null
*/
protected $cookieFactory = null;
/**
* Extracts operator's data from the passed in request object.
*
* @param Request $request A request to extract operator from.
* @return array|bool Associative array with operator's data or boolean
* false if there is no operator related with the request.
*/
public function extractOperator(Request $request)
{
// Try to get operator from session.
if (isset($_SESSION[SESSION_PREFIX . 'operator'])) {
return $_SESSION[SESSION_PREFIX . 'operator'];
}
// Check if operator had used "remember me" feature.
if ($request->cookies->has(REMEMBER_OPERATOR_COOKIE_NAME)) {
$cookie_value = $request->cookies->get(REMEMBER_OPERATOR_COOKIE_NAME);
list($login, $pwd) = preg_split('/\x0/', base64_decode($cookie_value), 2);
$op = operator_by_login($login);
$can_login = $op
&& isset($pwd)
&& isset($op['vcpassword'])
&& calculate_password_hash($op['vclogin'], $op['vcpassword']) == $pwd
&& !operator_is_disabled($op);
if ($can_login) {
$_SESSION[SESSION_PREFIX . 'operator'] = $op;
return $op;
}
}
// Operator's data cannot be extracted from the request.
return false;
}
/**
* Attaches operator's token to the response, thus is can be used to extract
* operator in the next request.
*
* @param Response $response The response object which will be sent to the
* client.
* @param array $operator Operator's data.
* @return Response Updated response.
*/
public function attachOperator(Response $response, $operator)
{
if ($operator) {
// Calculate password hashes for operator in the request and for the
// operator in session. If the hashes are different then operator's
// password or login was changed.
$password_hash = calculate_password_hash(
$operator['vclogin'],
$operator['vcpassword']
);
if (isset($_SESSION[SESSION_PREFIX . 'operator'])) {
$old_operator = $_SESSION[SESSION_PREFIX . 'operator'];
$old_password_hash = calculate_password_hash(
$old_operator['vclogin'],
$old_operator['vcpassword']
);
$credentials_changed = $password_hash != $old_password_hash;
} else {
$credentials_changed = false;
}
// Check if we need to remember the operator
if (isset($operator['remember_me'])) {
$remember = $operator['remember_me'];
unset($operator['remember_me']);
} else {
$remember = false;
}
// Update operator in the session
$_SESSION[SESSION_PREFIX . 'operator'] = $operator;
// Set or update remember me cookie if needed
if ($remember || $credentials_changed) {
$remember_cookie = $this->getCookieFactory()->createCookie(
REMEMBER_OPERATOR_COOKIE_NAME,
base64_encode($operator['vclogin'] . "\x0" . $password_hash),
time() + 60 * 60 * 24 * 1000,
true
);
$response->headers->setCookie($remember_cookie);
}
} else {
// Clean up session data
unset($_SESSION[SESSION_PREFIX . 'operator']);
unset($_SESSION['backpath']);
// Clear remember cookie
$cookie_factory = $this->getCookieFactory();
$response->headers->clearCookie(
REMEMBER_OPERATOR_COOKIE_NAME,
$cookie_factory->getPath(),
$cookie_factory->getDomain()
);
}
}
/**
* Updates instance of cookie factory related with the manager.
*
* @param CookieFactory $factory An instance of CookieFactory.
*/
public function setCookieFactory(CookieFactory $factory)
{
$this->cookieFactory = $factory;
}
/**
* Returns an instance of cookie factory related with the manager.
*
* @return CookieFactory
*/
public function getCookieFactory()
{
if (is_null($this->cookieFactory)) {
$this->cookieFactory = new CookieFactory();
}
return $this->cookieFactory;
}
}

View File

@ -0,0 +1,132 @@
<?php
/*
* Copyright 2005-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Mibew\Controller;
use Mibew\EventDispatcher;
use Symfony\Component\HttpFoundation\Request;
/**
* Contains acctions related with operator login process.
*/
class LoginController extends AbstractController
{
/**
* Builds a page with login form.
*
* @param Request $request Incoming request.
* @return string Rendered page content.
*/
public function showFormAction(Request $request)
{
// Check if the operator already logged in
if ($request->attributes->get('_operator')) {
// Redirect the operator to home page.
// TODO: Use a route for URI generation.
return $this->redirect($request->getUriForPath('/operator'));
}
$page = array(
'formisRemember' => true,
'version' => MIBEW_VERSION,
// Use errors list stored in the request. We need to do so to have
// an ability to pass the request from the "submitForm" action.
'errors' => $request->attributes->get('errors', array()),
);
// Try to get login from the request.
if ($request->request->has('login')) {
$page['formlogin'] = $request->request->get('login');
} elseif ($request->query->has('login')) {
$login = $request->query->get('login');
if (preg_match("/^(\w{1,15})$/", $login)) {
$page['formlogin'] = $login;
}
}
$page['localeLinks'] = get_locale_links();
$page['title'] = getlocal('page_login.title');
$page['headertitle'] = getlocal('app.title');
$page['show_small_login'] = false;
$page['fixedwrap'] = true;
return $this->render('login', $page);
}
/**
* Processes submitting of the form which is generated in
* {@link \Mibew\Controller\LoginController::showFormAction()} method.
*
* Triggers 'operatorLogin' event after operator logged in and pass to it an
* associative array with following items:
* - 'operator': array of the logged in operator info;
* - 'remember': boolean, indicates if system should remember operator.
*
* @param Request $request Incoming request.
* @return string Rendered page content.
*/
public function submitFormAction(Request $request)
{
$login = $request->request->get('login');
$password = $request->request->get('password');
$remember = $request->request->get('isRemember') == 'on';
$errors = array();
$operator = operator_by_login($login);
$operator_can_login = $operator
&& isset($operator['vcpassword'])
&& check_password_hash($operator['vclogin'], $password, $operator['vcpassword'])
&& !operator_is_disabled($operator);
if ($operator_can_login) {
if ($remember) {
$operator['remember_me'] = true;
}
// Update operator in the request. Doing so we tell the
// Authentication manager that operator should be associated with
// the session.
$request->attributes->set('_operator', $operator);
// Redirect the current operator to the needed page.
$target = isset($_SESSION['backpath'])
? $_SESSION['backpath']
: $request->getUriForPath('/operator');
// Trigger login event
$args = array(
'operator' => $operator,
'remember' => $remember,
);
$dispatcher = EventDispatcher::getInstance();
$dispatcher->triggerEvent('operatorLogin', $args);
return $this->redirect($target);
} else {
if (operator_is_disabled($operator)) {
$errors[] = getlocal('page_login.operator.disabled');
} else {
$errors[] = getlocal("page_login.error");
}
}
// Rebuild login form
$request->attributes->set('errors', $errors);
return $this->showFormAction($request);
}
}

View File

@ -0,0 +1,49 @@
<?php
/*
* Copyright 2005-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Mibew\Controller;
use Mibew\EventDispatcher;
use Symfony\Component\HttpFoundation\Request;
/**
* Contains acctions related with operator logout process.
*/
class LogoutController extends AbstractController
{
/**
* Logs operator out.
*
* Triggers 'operatorLogout' event after operator logged out.
*
* @param Request $request Incoming request.
* @return Response Prepared response object.
*/
public function logoutAction(Request $request)
{
// Detach operator's object from the request. This should tells
// authentication manager that operator session should be closed.
$request->attributes->remove('_operator');
// Trigger logout event
$dispatcher = EventDispatcher::getInstance();
$dispatcher->triggerEvent('operatorLogout');
// Redirect the current operator to the login page.
return $this->redirect($this->generateUrl('login'));
}
}

View File

@ -140,12 +140,10 @@ class AvatarController extends AbstractController
// Update path to avatar in the database
update_operator_avatar($op['operatorid'], $avatar);
// Operator's data are cached in the session thus we need to update them
// Operator's data are cached in the request thus we need to update them
// manually.
if ($avatar && $operator['operatorid'] == $op_id) {
$operator['vcavatar'] = $avatar;
$_SESSION[SESSION_PREFIX . 'operator'] = $operator;
$request->attributes->set('_operator', $operator);
}

View File

@ -113,13 +113,12 @@ class PermissionsController extends AbstractController
}
}
// Update operator's permissions in the database and in cached session
// Update operator's permissions in the database and in cached request
// data if it is needed.
update_operator_permissions($op['operatorid'], $new_permissions);
if ($operator['operatorid'] == $op_id) {
$operator['iperm'] = $new_permissions;
$_SESSION[SESSION_PREFIX . 'operator'] = $operator;
$request->attributes->set('_operator', $operator);
}

View File

@ -206,7 +206,7 @@ class ProfileController extends AbstractController
// Update existing operator
update_operator($op_id, $login, $email, $password, $local_name, $common_name, $code);
// Operator data are cached in the session, thus we need to manually
// Operator data are cached in the request, thus we need to manually
// update them.
if (!empty($password) && $op_id == $operator['operatorid']) {
// Check if the admin has set his password for the first time.
@ -214,7 +214,6 @@ class ProfileController extends AbstractController
// Update operator's password.
$operator['vcpassword'] = calculate_password_hash($login, $password);
$_SESSION[SESSION_PREFIX . 'operator'] = $operator;
$request->attributes->set('_operator', $operator);
// Redirect the admin to the home page if needed.

View File

@ -572,7 +572,7 @@ function check_login($redirect = true)
// Redirect operator if need
if ($redirect) {
$_SESSION['backpath'] = $requested;
header("Location: " . MIBEW_WEB_ROOT . "/operator/login.php");
header("Location: " . MIBEW_WEB_ROOT . "/operator/login");
exit;
} else {
return null;
@ -603,63 +603,6 @@ function get_logged_in()
: false;
}
/**
* Log in operator
*
* Triggers 'operatorLogin' event after operator logged in and pass to it an
* associative array with following items:
* - 'operator': array of the logged in operator info;
* - 'remember': boolean, indicates if system should remember operator.
*
* @param array $operator Operators info
* @param boolean $remember Indicates if system should remember operator
* @param boolean $https Indicates if cookie should be flagged as a secure one
*/
function login_operator($operator, $remember, $https = false)
{
$_SESSION[SESSION_PREFIX . "operator"] = $operator;
if ($remember) {
$password_hash = calculate_password_hash($operator['vclogin'], $operator['vcpassword']);
setcookie(
REMEMBER_OPERATOR_COOKIE_NAME,
base64_encode($operator['vclogin'] . "\x0" . $password_hash),
time() + 60 * 60 * 24 * 1000,
MIBEW_WEB_ROOT . "/",
null,
$https,
true
);
} elseif (isset($_COOKIE[REMEMBER_OPERATOR_COOKIE_NAME])) {
setcookie(REMEMBER_OPERATOR_COOKIE_NAME, '', time() - 3600, MIBEW_WEB_ROOT . "/");
}
// Trigger login event
$args = array(
'operator' => $operator,
'remember' => $remember,
);
$dispatcher = EventDispatcher::getInstance();
$dispatcher->triggerEvent('operatorLogin', $args);
}
/**
* Log out current operator
*
* Triggers 'operatorLogout' event after operator logged out.
*/
function logout_operator()
{
unset($_SESSION[SESSION_PREFIX . "operator"]);
unset($_SESSION['backpath']);
if (isset($_COOKIE[REMEMBER_OPERATOR_COOKIE_NAME])) {
setcookie(REMEMBER_OPERATOR_COOKIE_NAME, '', time() - 3600, MIBEW_WEB_ROOT . "/");
}
// Trigger logout event
$dispatcher = EventDispatcher::getInstance();
$dispatcher->triggerEvent('operatorLogout');
}
function setup_redirect_links($threadid, $operator, $token)
{
$result = array();

View File

@ -216,6 +216,26 @@ invite:
_controller: Mibew\Controller\InvitationController::inviteAction
_access_check: Mibew\AccessControl\Check\LoggedInCheck
## Log in
login:
path: /operator/login
defaults:
_controller: Mibew\Controller\LoginController::showFormAction
methods: [GET]
login_submit:
path: /operator/login
defaults:
_controller: Mibew\Controller\LoginController::submitFormAction
methods: [POST]
## Log out
logout:
path: /operator/logout
defaults:
_controller: Mibew\Controller\LogoutController::logoutAction
_access_check: Mibew\AccessControl\Check\LoggedInCheck
## Operators
operator_add:
path: /operator/operator/add

View File

@ -1,71 +0,0 @@
<?php
/*
* Copyright 2005-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Import namespaces and classes of the core
use Mibew\Style\PageStyle;
// Initialize libraries
require_once(dirname(dirname(__FILE__)) . '/libs/init.php');
$page = array(
'formisRemember' => true,
'version' => MIBEW_VERSION,
'errors' => array(),
);
if (isset($_POST['login']) && isset($_POST['password'])) {
$login = get_param('login');
$password = get_param('password');
$remember = isset($_POST['isRemember']) && $_POST['isRemember'] == "on";
$operator = operator_by_login($login);
$operator_can_login = $operator
&& isset($operator['vcpassword'])
&& check_password_hash($operator['vclogin'], $password, $operator['vcpassword'])
&& !operator_is_disabled($operator);
if ($operator_can_login) {
$target = $password == ''
? MIBEW_WEB_ROOT . "/operator/operator/" . intval($operator['operatorid']) . '/edit'
: (isset($_SESSION['backpath']) ? $_SESSION['backpath'] : MIBEW_WEB_ROOT . "/operator/index.php");
login_operator($operator, $remember, is_secure_request());
header("Location: $target");
exit;
} else {
if (operator_is_disabled($operator)) {
$page['errors'][] = getlocal('page_login.operator.disabled');
} else {
$page['errors'][] = getlocal("page_login.error");
}
$page['formlogin'] = $login;
}
} elseif (isset($_GET['login'])) {
$login = get_get_param('login');
if (preg_match("/^(\w{1,15})$/", $login)) {
$page['formlogin'] = $login;
}
}
$page['localeLinks'] = get_locale_links();
$page['title'] = getlocal("page_login.title");
$page['headertitle'] = getlocal("app.title");
$page['show_small_login'] = false;
$page['fixedwrap'] = true;
$page_style = new PageStyle(PageStyle::getCurrentStyle());
$page_style->render('login', $page);

View File

@ -1,22 +0,0 @@
<?php
/*
* Copyright 2005-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
require_once(dirname(dirname(__FILE__)) . '/libs/init.php');
logout_operator();
header("Location: " . MIBEW_WEB_ROOT . "/operator/login.php");

View File

@ -26,7 +26,7 @@
{{else}}
{{#if show_small_login}}
<div id="loginsmallpane">
<form name="smallLogin" method="post" action="{{mibewRoot}}/operator/login.php">
<form name="smallLogin" method="post" action="{{mibewRoot}}/operator/login">
{{l10n "page_login.login"}}
<input type="text" name="login" size="8" class="formauth"/>
<input type="password" name="password" size="8" class="formauth" autocomplete="off"/>

View File

@ -43,7 +43,7 @@
<li>
<h2>{{l10n "right.other"}}</h2>
<ul class="submenu">
<li><a href="{{mibewRoot}}/operator/logout.php">{{l10n "topMenu.logoff"}}</a></li>
<li><a href="{{mibewRoot}}/operator/logout">{{l10n "topMenu.logoff"}}</a></li>
</ul>
</li>
{{/if}}

View File

@ -161,7 +161,7 @@
<div class="dashitem">
<div class="dashitem-content">
<img src="{{stylePath}}/images/dash/exit.gif" alt=""/>
<a href="{{mibewRoot}}/operator/logout.php">
<a href="{{mibewRoot}}/operator/logout">
{{l10n "topMenu.logoff"}}
</a>
{{l10n "content.logoff"}}

View File

@ -8,7 +8,7 @@
<p>{{l10n "app.descr"}}</p>
</div>
<form name="loginForm" method="post" action="{{mibewRoot}}/operator/login.php">
<form name="loginForm" method="post" action="{{mibewRoot}}/operator/login">
<div id="loginpane">
<div class="header">

View File

@ -14,7 +14,7 @@
{{l10n "restore.sent"}}
<br/>
<br/>
<a href="login.php">{{l10n "restore.back_to_login"}}</a>
<a href="{{mibewRoot}}/operator/login">{{l10n "restore.back_to_login"}}</a>
</div>
</div>
{{else}}
@ -60,7 +60,7 @@
</table>
<div class="links">
<a href="login.php">{{l10n "restore.back_to_login"}}</a>
<a href="{{mibewRoot}}/operator/login">{{l10n "restore.back_to_login"}}</a>
</div>
</div>

View File

@ -14,7 +14,7 @@
{{l10n "resetpwd.changed"}}
<br/>
<br/>
<a href="{{mibewRoot}}/operator/login.php?login={{loginname}}">{{l10n "resetpwd.login"}}</a>
<a href="{{mibewRoot}}/operator/login?login={{loginname}}">{{l10n "resetpwd.login"}}</a>
</div>
</div>
{{else}}
@ -75,11 +75,11 @@
</table>
<div class="links">
<a href="{{mibewRoot}}/operator/login.php">{{l10n "restore.back_to_login"}}</a>
<a href="{{mibewRoot}}/operator/login">{{l10n "restore.back_to_login"}}</a>
</div>
</div>
{{else}}
<a href="{{mibewRoot}}/operator/login.php">{{l10n "restore.back_to_login"}}</a>
<a href="{{mibewRoot}}/operator/login">{{l10n "restore.back_to_login"}}</a>
{{/if}}
</div>