parent
df4ce2cfe9
commit
a00f874c96
@ -0,0 +1,34 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
require_once __DIR__.'/functions.inc.php'; |
||||
|
||||
/** @var array $uData */ |
||||
if ($uData['auth_source'] === 'oauth2') { |
||||
$plugin = OAuth2::create(); |
||||
|
||||
if ('true' !== $plugin->get(OAuth2::SETTING_ENABLE)) { |
||||
api_not_allowed(true); |
||||
} |
||||
|
||||
$oauth2IdField = new ExtraFieldValue('user'); |
||||
$oauth2IdValue = $oauth2IdField->get_values_by_handler_and_field_variable( |
||||
$uData['user_id'], |
||||
OAuth2::EXTRA_FIELD_OAUTH2_ID |
||||
); |
||||
|
||||
if (empty($oauth2IdValue) || empty($oauth2IdValue['value'])) { |
||||
api_not_allowed(true); |
||||
} |
||||
|
||||
$provider = $plugin->getProvider(); |
||||
|
||||
$authUrl = $provider->getAuthorizationUrl(); |
||||
|
||||
ChamiloSession::write('oauth2state', $provider->getState()); |
||||
|
||||
// Redirect to OAuth2 login. |
||||
header('Location: '.$authUrl); |
||||
// Avoid execution from here in local.inc.php script. |
||||
exit; |
||||
} |
||||
@ -0,0 +1,22 @@ |
||||
# The OAuth2 Plugin |
||||
Allows authentication with a generic OAuth2 provider. |
||||
|
||||
This plugin adds an extra field to users : |
||||
- `oauth2_id`, to store each users' OAuth2 identifier. |
||||
|
||||
> This plugin uses the [`league/oauth2-client`](https://oauth2-client.thephpleague.com/) package. |
||||
|
||||
### To configure the OAuth2 server |
||||
The OAuth2 server administrator must give you an OAuth2 client identifier and secret and enter this callback URL : |
||||
`https://{CHAMILO_URL}/plugin/oauth2/src/callback.php`. |
||||
|
||||
### To configure this plugin |
||||
* Enable it |
||||
* Fill in the setting parameters (read the help messages) |
||||
* assign a region. Preferably `login_bottom`. |
||||
|
||||
Also, you can configure the external login to work with the classic Chamilo form login. |
||||
Adding this line in `configuration.php` file. |
||||
```php |
||||
$extAuthSource["oauth2"]["login"] = $_configuration['root_sys']."main/auth/external_login/login.oauth2.php"; |
||||
``` |
||||
@ -0,0 +1,31 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
/** |
||||
* @author Sébastien Ducoulombier <seb@ldd.fr> |
||||
* inspired by AzureActiveDirectory plugin from Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com> |
||||
* |
||||
* @package chamilo.plugin.oauth2 |
||||
*/ |
||||
|
||||
/** @var OAuth2 $oAuth2Plugin */ |
||||
$oAuth2Plugin = OAuth2::create(); |
||||
|
||||
if ($oAuth2Plugin->get(OAuth2::SETTING_ENABLE) === 'true') { |
||||
$_template['block_title'] = $oAuth2Plugin->get(OAuth2::SETTING_BLOCK_NAME); |
||||
|
||||
$_template['signin_url'] = $oAuth2Plugin->getSignInURL(); |
||||
|
||||
$managementLoginEnabled = 'true' === $oAuth2Plugin->get(OAuth2::SETTING_MANAGEMENT_LOGIN_ENABLE); |
||||
|
||||
$_template['management_login_enabled'] = $managementLoginEnabled; |
||||
|
||||
if ($managementLoginEnabled) { |
||||
$managementLoginName = $oAuth2Plugin->get(OAuth2::SETTING_MANAGEMENT_LOGIN_NAME); |
||||
|
||||
if (empty($managementLoginName)) { |
||||
$managementLoginName = $oAuth2Plugin->get_lang('ManagementLogin'); |
||||
} |
||||
|
||||
$_template['management_login_name'] = $managementLoginName; |
||||
} |
||||
} |
||||
@ -0,0 +1,8 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
if (!api_is_platform_admin()) { |
||||
die('You must have admin permissions to install plugins'); |
||||
} |
||||
|
||||
OAuth2::create()->install(); |
||||
@ -0,0 +1,69 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
/** |
||||
* Strings to english L10n. |
||||
* |
||||
* @author Sébastien Ducoulombier <seb@ldd.fr> |
||||
* inspired by Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com> |
||||
* |
||||
* @package chamilo.plugin.oauth2 |
||||
*/ |
||||
$strings['plugin_title'] = 'OAuth2'; |
||||
$strings['plugin_comment'] = 'Allow authentication with an <em>OAuth2</em> server'; |
||||
|
||||
$strings['enable'] = 'Enable'; |
||||
|
||||
$strings['client_id'] = 'Client ID'; |
||||
$strings['client_id_help'] = '<strong>The <em>OAuth2</em> client identifier</strong> |
||||
the <em>OAuth2</em> server administrator assigned to this Chamilo instance. |
||||
<br/>Required.'; |
||||
|
||||
$strings['client_secret'] = 'Client Secret'; |
||||
$strings['client_secret_help'] = '<strong>The secret code</strong> associated to the <em>OAuth2</em> client identifier. |
||||
<br/>Required.'; |
||||
|
||||
$strings['authorize_url'] = 'Authorize URL'; |
||||
$strings['authorize_url_help'] = 'The <em>OAuth2</em> server URL to request authorization. |
||||
<br/>Required.'; |
||||
|
||||
$strings['access_token_url'] = 'Access Token URL'; |
||||
$strings['access_token_url_help'] = 'The <em>OAuth2</em> server URL to request an access token. |
||||
<br/>Required.'; |
||||
|
||||
$strings['access_token_method'] = 'Access Token HTTP Method'; |
||||
$strings['access_token_method_help'] = 'Default value: POST'; |
||||
|
||||
$strings['resource_owner_details_url'] = 'Resource Owner Details URL'; |
||||
$strings['resource_owner_details_url_help'] = 'The <em>OAuth2</em> server URL |
||||
returning the identified user information as a <em>JSON</em> array. |
||||
Required.'; |
||||
|
||||
$strings['response_error'] = 'Response error key'; |
||||
$strings['response_error_help'] = 'Default is <code>error</code>'; |
||||
|
||||
$strings['response_code'] = 'Response code key'; |
||||
$strings['response_code_help'] = 'By default, an error code retrieval is not attempted'; |
||||
|
||||
$strings['response_resource_owner_id'] = 'Response Resource Owner Id key'; |
||||
$strings['response_resource_owner_id_help'] = 'The array key to the user\'s <em>OAuth2</em> identifier value. |
||||
<br/>Default value: <code>id</code>. |
||||
<br/>If the identifier is in a subentry of the returned <em>JSON</em> array, |
||||
<br/>then please enter successive path keys separated by dots. For example, |
||||
<br/><code>data.0.id</code> |
||||
<br/>means the identifier is to be found at |
||||
<code>$jsonArray["data"][0]["id"]</code>'; |
||||
|
||||
$strings['block_name'] = 'Block name'; |
||||
$strings['block_name_help'] = 'The title shown above the <em>OAuth2</em> Login button'; |
||||
|
||||
$strings['management_login_enable'] = 'Management login'; |
||||
$strings['management_login_enable_help'] = 'Disable the Chamilo login and enable an alternative login page for users. |
||||
<br> |
||||
You will need copy file <code>/plugin/oauth2/layout/login_form.tpl</code> |
||||
to directory <code>/main/template/overrides/layout/</code>.'; |
||||
$strings['management_login_name'] = 'Name for the management login'; |
||||
$strings['management_login_name_help'] = 'Default value is "Management Login".'; |
||||
|
||||
$strings['OAuth2Id'] = 'OAuth2 identifier'; |
||||
$strings['ManagementLogin'] = 'Management Login'; |
||||
$strings['InvalidId'] = 'Login failed - the OAuth2 identifier was not recognized as an existing Chamilo user\'s.'; |
||||
@ -0,0 +1,44 @@ |
||||
{% if _u.logged == 0 %} |
||||
{% if login_form %} |
||||
<div id="login-block" class="panel panel-default"> |
||||
<div class="panel-body"> |
||||
{{ login_language_form }} |
||||
{% if plugin_login_top is not null %} |
||||
<div id="plugin_login_top"> |
||||
{{ plugin_login_top }} |
||||
</div> |
||||
{% endif %} |
||||
|
||||
{{ login_failed }} |
||||
|
||||
{% set oauth2_plugin_enabled = 'oauth2'|api_get_plugin_setting('enable') %} |
||||
{% set oauth2_plugin_manage_login = 'oauth2'|api_get_plugin_setting('manage_login_enable') %} |
||||
|
||||
{% if 'false' == oauth2_plugin_enabled or 'false' == oauth2_plugin_manage_login %} |
||||
{{ login_form }} |
||||
|
||||
{% if "allow_lostpassword" | api_get_setting == 'true' or "allow_registration"|api_get_setting == 'true' %} |
||||
<ul class="nav nav-pills nav-stacked"> |
||||
{% if "allow_registration"|api_get_setting != 'false' %} |
||||
<li><a href="{{ _p.web_main }}auth/inscription.php"> {{ 'SignUp'|get_lang }} </a></li> |
||||
{% endif %} |
||||
|
||||
{% if "allow_lostpassword"|api_get_setting == 'true' %} |
||||
<li> |
||||
<a href="{{ _p.web_main }}auth/lostPassword.php">{{ 'LostPassword'|get_lang }}</a> |
||||
</li> |
||||
{% endif %} |
||||
</ul> |
||||
{% endif %} |
||||
|
||||
{% endif %} |
||||
|
||||
{% if plugin_login_bottom is not null %} |
||||
<div id="plugin_login_bottom"> |
||||
{{ plugin_login_bottom }} |
||||
</div> |
||||
{% endif %} |
||||
</div> |
||||
</div> |
||||
{% endif %} |
||||
{% endif %} |
||||
@ -0,0 +1,35 @@ |
||||
<?php |
||||
/* For license terms, see /license.txt */ |
||||
|
||||
require __DIR__.'/../../main/inc/global.inc.php'; |
||||
|
||||
$plugin = OAuth2::create(); |
||||
|
||||
$pluginEnabled = $plugin->get(OAuth2::SETTING_ENABLE); |
||||
$managementLoginEnabled = $plugin->get(OAuth2::SETTING_MANAGEMENT_LOGIN_ENABLE); |
||||
|
||||
if ('true' !== $pluginEnabled || 'true' !== $managementLoginEnabled) { |
||||
header('Location: '.api_get_path(WEB_PATH)); |
||||
|
||||
exit; |
||||
} |
||||
|
||||
$userId = api_get_user_id(); |
||||
|
||||
if (!($userId) || api_is_anonymous($userId)) { |
||||
$managementLoginName = $plugin->get(OAuth2::SETTING_MANAGEMENT_LOGIN_NAME); |
||||
|
||||
if (empty($managementLoginName)) { |
||||
$managementLoginName = $plugin->get_lang('ManagementLogin'); |
||||
} |
||||
|
||||
$template = new Template($managementLoginName); |
||||
// Only display if the user isn't logged in. |
||||
$template->assign('login_language_form', api_display_language_form(true, true)); |
||||
$template->assign('login_form', $template->displayLoginForm()); |
||||
|
||||
$content = $template->fetch('oauth2/view/login.tpl'); |
||||
|
||||
$template->assign('content', $content); |
||||
$template->display_one_col_template(); |
||||
} |
||||
@ -0,0 +1,11 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
/** |
||||
* @author Sébastien Ducoulombier <seb@ldd.fr>, inspired by Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com> |
||||
* |
||||
* @package chamilo.plugin.oauth2 |
||||
*/ |
||||
/** @var OAuth2 $plugin_info */ |
||||
$plugin_info = OAuth2::create()->get_info(); |
||||
|
||||
$plugin_info['templates'] = ['view/block.tpl']; |
||||
@ -0,0 +1,204 @@ |
||||
<?php |
||||
/* For license terms, see /license.txt */ |
||||
|
||||
use League\OAuth2\Client\Provider\Exception\IdentityProviderException; |
||||
use \League\OAuth2\Client\Provider\GenericProvider; |
||||
use League\OAuth2\Client\Token\AccessToken; |
||||
use League\OAuth2\Client\Tool\ArrayAccessorTrait; |
||||
|
||||
/** |
||||
* OAuth2 plugin class. |
||||
* |
||||
* @author Sébastien Ducoulombier <seb@ldd.fr> |
||||
* inspired by AzureActiveDirectory plugin class from Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com> |
||||
* |
||||
* @package chamilo.plugin.oauth2 |
||||
*/ |
||||
class OAuth2 extends Plugin |
||||
{ |
||||
use ArrayAccessorTrait; |
||||
|
||||
const SETTING_ENABLE = 'enable'; |
||||
|
||||
const SETTING_CLIENT_ID = 'client_id'; |
||||
const SETTING_CLIENT_SECRET = 'client_secret'; |
||||
|
||||
const SETTING_AUTHORIZE_URL = 'authorize_url'; |
||||
# const SETTING_SCOPES = 'scopes'; |
||||
# const SETTING_SCOPE_SEPARATOR = 'scope_separator'; |
||||
|
||||
const SETTING_ACCESS_TOKEN_URL = 'access_token_url'; |
||||
const SETTING_ACCESS_TOKEN_METHOD = 'access_token_method'; |
||||
# const SETTING_ACCESS_TOKEN_RESOURCE_OWNER_ID = 'access_token_resource_owner_id'; |
||||
|
||||
const SETTING_RESOURCE_OWNER_DETAILS_URL = 'resource_owner_details_url'; |
||||
|
||||
const SETTING_RESPONSE_ERROR = 'response_error'; |
||||
const SETTING_RESPONSE_CODE = 'response_code'; |
||||
const SETTING_RESPONSE_RESOURCE_OWNER_ID = 'response_resource_owner_id'; |
||||
|
||||
const SETTING_BLOCK_NAME = 'block_name'; |
||||
|
||||
const SETTING_MANAGEMENT_LOGIN_ENABLE = 'management_login_enable'; |
||||
const SETTING_MANAGEMENT_LOGIN_NAME = 'management_login_name'; |
||||
|
||||
const EXTRA_FIELD_OAUTH2_ID = 'oauth2_id'; |
||||
|
||||
protected function __construct() |
||||
{ |
||||
parent::__construct( |
||||
'0.1', |
||||
'Sébastien Ducoulombier', |
||||
[ |
||||
self::SETTING_ENABLE => 'boolean', |
||||
|
||||
self::SETTING_CLIENT_ID => 'text', |
||||
self::SETTING_CLIENT_SECRET => 'text', |
||||
|
||||
self::SETTING_AUTHORIZE_URL => 'text', |
||||
# self::SETTING_SCOPES => 'text', |
||||
# self::SETTING_SCOPE_SEPARATOR => 'text', |
||||
|
||||
self::SETTING_ACCESS_TOKEN_URL => 'text', |
||||
self::SETTING_ACCESS_TOKEN_METHOD => [ |
||||
'type' => 'select', |
||||
'options' => [ |
||||
GenericProvider::METHOD_POST => 'POST', |
||||
GenericProvider::METHOD_GET => 'GET', |
||||
] |
||||
], |
||||
# self::SETTING_ACCESS_TOKEN_RESOURCE_OWNER_ID => 'text', |
||||
|
||||
self::SETTING_RESOURCE_OWNER_DETAILS_URL => 'text', |
||||
|
||||
self::SETTING_RESPONSE_ERROR => 'text', |
||||
self::SETTING_RESPONSE_CODE => 'text', |
||||
self::SETTING_RESPONSE_RESOURCE_OWNER_ID => 'text', |
||||
|
||||
self::SETTING_BLOCK_NAME => 'text', |
||||
|
||||
self::SETTING_MANAGEMENT_LOGIN_ENABLE => 'boolean', |
||||
self::SETTING_MANAGEMENT_LOGIN_NAME => 'text', |
||||
] |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Instance the plugin. |
||||
* |
||||
* @staticvar null $result |
||||
* |
||||
* @return $this |
||||
*/ |
||||
public static function create() |
||||
{ |
||||
static $result = null; |
||||
|
||||
return $result ? $result : $result = new self(); |
||||
} |
||||
|
||||
/** |
||||
* @return string |
||||
*/ |
||||
public function get_name() |
||||
{ |
||||
return 'oauth2'; |
||||
} |
||||
|
||||
/** |
||||
* @return GenericProvider |
||||
*/ |
||||
public function getProvider() |
||||
{ |
||||
return new GenericProvider( |
||||
[ |
||||
'clientId' => $this->get(self::SETTING_CLIENT_ID), |
||||
'clientSecret' => $this->get(self::SETTING_CLIENT_SECRET), |
||||
'redirectUri' => api_get_path(WEB_PLUGIN_PATH).'oauth2/src/callback.php', |
||||
|
||||
'urlAuthorize' => $this->get(self::SETTING_AUTHORIZE_URL), |
||||
# 'scopes' => $this->get(self::SETTING_SCOPES) or null, |
||||
# 'scopeSeparator' => $this->get(self::SETTING_SCOPE_SEPARATOR) ?: ',', |
||||
|
||||
'urlAccessToken' => $this->get(self::SETTING_ACCESS_TOKEN_URL), |
||||
'accessTokenMethod' => $this->get(self::SETTING_ACCESS_TOKEN_METHOD) ?: GenericProvider::METHOD_POST, |
||||
#'accessTokenResourceOwnerId' => $this->get(self::SETTING_ACCESS_TOKEN_RESOURCE_OWNER_ID) |
||||
# ?: GenericProvider::ACCESS_TOKEN_RESOURCE_OWNER_ID, |
||||
|
||||
'urlResourceOwnerDetails' => $this->get(self::SETTING_RESOURCE_OWNER_DETAILS_URL), |
||||
|
||||
'responseError' => $this->get(self::SETTING_RESPONSE_ERROR) ?: 'error', |
||||
'responseCode' => $this->get(self::SETTING_RESPONSE_CODE) ?: null, |
||||
'responseResourceOwnerId' => $this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_ID) ?: 'id', |
||||
] |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* @return array user information, as returned by api_get_user_info(userId) |
||||
* @throws IdentityProviderException |
||||
* @var AccessToken $accessToken |
||||
* @var GenericProvider $provider |
||||
*/ |
||||
public function getUserInfo($provider, $accessToken) |
||||
{ |
||||
$url = $provider->getResourceOwnerDetailsUrl($accessToken); |
||||
$request = $provider->getAuthenticatedRequest($provider::METHOD_GET, $url, $accessToken); |
||||
$response = $provider->getParsedResponse($request); |
||||
if (false === is_array($response)) { |
||||
throw new UnexpectedValueException( |
||||
get_lang('invalid_json_received_from_provider') |
||||
); |
||||
} |
||||
$resourceOwnerId = $this->getValueByKey( |
||||
$response, |
||||
$this->get(self::SETTING_RESPONSE_RESOURCE_OWNER_ID) |
||||
); |
||||
if (empty($resourceOwnerId)) { |
||||
throw new RuntimeException( |
||||
get_lang('wrong_response_resource_owner_id') |
||||
); |
||||
} |
||||
$extraFieldValue = new ExtraFieldValue('user'); |
||||
$result = $extraFieldValue->get_item_id_from_field_variable_and_field_value( |
||||
OAuth2::EXTRA_FIELD_OAUTH2_ID, |
||||
$resourceOwnerId |
||||
); |
||||
if (false === $result) { |
||||
throw new RuntimeException( |
||||
get_lang('no_user_has_this_oauth_code') |
||||
); |
||||
} |
||||
if (is_array($result) and array_key_exists('item_id', $result)) { |
||||
$userId = $result['item_id']; |
||||
} else { |
||||
$userId = $result; |
||||
} |
||||
$userInfo = api_get_user_info($userId); |
||||
if (empty($userInfo)) { |
||||
throw new LogicException( |
||||
get_lang('internal_error_cannot_get_user_info') |
||||
); |
||||
} |
||||
|
||||
return $userInfo; |
||||
} |
||||
|
||||
public function getSignInURL() |
||||
{ |
||||
return api_get_path(WEB_PLUGIN_PATH).$this->get_name().'/src/callback.php'; |
||||
} |
||||
|
||||
/** |
||||
* Create extra fields for user when installing. |
||||
*/ |
||||
public function install() |
||||
{ |
||||
UserManager::create_extra_field( |
||||
self::EXTRA_FIELD_OAUTH2_ID, |
||||
ExtraField::FIELD_TYPE_TEXT, |
||||
$this->get_lang('OAuth2Id'), |
||||
'' |
||||
); |
||||
} |
||||
} |
||||
@ -0,0 +1,53 @@ |
||||
<?php |
||||
/* For license terms, see /license.txt */ |
||||
|
||||
require __DIR__.'/../../../main/inc/global.inc.php'; |
||||
|
||||
$plugin = OAuth2::create(); |
||||
|
||||
$provider = $plugin->getProvider(); |
||||
|
||||
// If we don't have an authorization code then get one |
||||
if (!array_key_exists('code', $_GET)) { |
||||
// Fetch the authorization URL from the provider; this returns the |
||||
// urlAuthorize option and generates and applies any necessary parameters |
||||
// (e.g. state). |
||||
$authorizationUrl = $provider->getAuthorizationUrl(); |
||||
|
||||
// Get the state generated for you and store it to the session. |
||||
ChamiloSession::write('oauth2state', $provider->getState()); |
||||
|
||||
// Redirect the user to the authorization URL. |
||||
header('Location: '.$authorizationUrl); |
||||
exit; |
||||
} |
||||
|
||||
// Check given state against previously stored one to mitigate CSRF attack |
||||
if (!array_key_exists('state', $_GET) || ($_GET['state'] !== ChamiloSession::read('oauth2state'))) { |
||||
ChamiloSession::erase('oauth2state'); |
||||
exit('Invalid state'); |
||||
} |
||||
|
||||
try { |
||||
// Try to get an access token using the authorization code grant. |
||||
$accessToken = $provider->getAccessToken('authorization_code', [ |
||||
'code' => $_GET['code'] |
||||
]); |
||||
$userInfo = $plugin->getUserInfo($provider, $accessToken); |
||||
if ($userInfo['active'] != '1') { |
||||
throw new Exception(get_lang('AccountInactive')); |
||||
} |
||||
} catch (Exception $exception) { |
||||
$message = Display::return_message($exception->getMessage(), 'error'); |
||||
Display::addFlash($message); |
||||
header('Location: '.api_get_path(WEB_PATH)); |
||||
exit; |
||||
} |
||||
|
||||
$_user['user_id'] = $userInfo['user_id']; |
||||
$_user['uidReset'] = true; |
||||
|
||||
ChamiloSession::write('_user', $_user); |
||||
ChamiloSession::write('_user_auth_source', 'oauth2'); |
||||
|
||||
Redirect::session_request_uri(true, $userInfo['user_id']); |
||||
@ -0,0 +1,18 @@ |
||||
{% if not _u.logged %} |
||||
<div id="oauth2-login"> |
||||
{% if not oauth2.block_title is empty %} |
||||
<h4>{{ oauth2.block_title }}</h4> |
||||
{% endif %} |
||||
|
||||
{% if not oauth2.signin_url is empty %} |
||||
<a href="{{ oauth2.signin_url }}" class="btn btn-default">{{ 'SignIn'|get_lang }}</a> |
||||
{% endif %} |
||||
|
||||
{% if oauth2.management_login_enabled %} |
||||
<hr> |
||||
<a href="{{ _p.web_plugin ~ 'oauth2/login.php' }}"> |
||||
{{ oauth2.management_login_name }} |
||||
</a> |
||||
{% endif %} |
||||
</div> |
||||
{% endif %} |
||||
@ -0,0 +1,19 @@ |
||||
<div class="row"> |
||||
<div class="col-sm-4 col-sm-offset-4"> |
||||
{{ login_language_form }} |
||||
|
||||
{{ login_form }} |
||||
|
||||
{% if "allow_lostpassword"|api_get_setting == 'true' or "allow_registration"|api_get_setting == 'true' %} |
||||
<ul class="nav nav-pills nav-stacked"> |
||||
{% if "allow_registration"|api_get_setting != 'false' %} |
||||
<li><a href="{{ _p.web_main }}auth/inscription.php">{{ 'SignUp'|get_lang }}</a></li> |
||||
{% endif %} |
||||
|
||||
{% if "allow_lostpassword"|api_get_setting == 'true' %} |
||||
<li><a href="{{ _p.web_main }}auth/lostPassword.php">{{ 'LostPassword'|get_lang }}</a></li> |
||||
{% endif %} |
||||
</ul> |
||||
{% endif %} |
||||
</div> |
||||
</div> |
||||
Loading…
Reference in new issue