- The tutorial was a little ambiguous when it came to editing
auth.yml
(which I couldn't find). In the end I added an entry toconfig/default/container/services_auth.yml
. If I were to turn this into an extension, would this block of YAML just go inconfig/services.yml
, as in the "acme" skeleton? - I added my language text to
phpBB/language/en/acp/board.php
, because thats where the builtin authenticators put their language text. Looking at the skeleton it looks like I should just move these tolanguage/en/common.php
. I don't see any references tocommon.php
anywhere else in the skeleton so is this file arbitrarily named? If i put my language text inlanguage/en/foobarbaz.php
will it get pulled in automatically? - Do
auth_provider_xxx.html
templates get pulled in from extensions? The acme extension appears to have it's own code for the admin control panel, whereas I think I need my auth provider settings to show up within the auth selection dialog. Can I just save the file atadm/style/auth_provider_remote_user.html
within my extension tree?
Here is the complete code of my auth provider and changes, for reference.
Code: Select all
diff --git a/phpBB/adm/style/auth_provider_remote_user.html b/phpBB/adm/style/auth_provider_remote_user.html
new file mode 100644
index 000000000..85535a2b1
--- /dev/null
+++ b/phpBB/adm/style/auth_provider_remote_user.html
@@ -0,0 +1,11 @@
+<fieldset id="auth_remote_user_settings">
+ <legend>{L_REMOTE_USER}</legend>
+ <dl>
+ <dt><label for="remote_user_varname">
+ {L_REMOTE_USER_VARNAME}{L_COLON}</label><br />
+ <span>{L_REMOTE_USER_VARNAME_EXPLAIN}</span></dt>
+ <dd><input type="text" id="remote_user_varname" size="40"
+ name="config[remote_user_varname]" value="{AUTH_REMOTE_USER_VARNAME}" />
+ </dd>
+ </dl>
+</fieldset>
diff --git a/phpBB/config/default/container/services_auth.yml b/phpBB/config/default/container/services_auth.yml
index ed8dc90a7..797c19cad 100644
--- a/phpBB/config/default/container/services_auth.yml
+++ b/phpBB/config/default/container/services_auth.yml
@@ -108,3 +108,18 @@ services:
- '@request'
tags:
- { name: auth.provider.oauth.service }
+
+ # NOTE(josh): this is what is added
+ auth.provider.remote_user:
+ class: phpbb\auth\provider\remote_user
+ arguments:
+ - '@dbal.conn'
+ - '@config'
+ - '@passwords.manager'
+ - '@request'
+ - '@user'
+ - '@service_container'
+ - '%core.root_path%'
+ - '%core.php_ext%'
+ tags:
+ - { name: auth.provider }
diff --git a/phpBB/language/en/acp/board.php b/phpBB/language/en/acp/board.php
index 4887a73ca..20252db2d 100644
--- a/phpBB/language/en/acp/board.php
+++ b/phpBB/language/en/acp/board.php
@@ -458,7 +458,13 @@ $lang = array_merge($lang, array(
'LDAP_USER' => 'LDAP user <var>dn</var>',
'LDAP_USER_EXPLAIN' => 'Leave blank to use anonymous binding. If filled in phpBB uses the specified distinguished name on login attempts to find the correct user, e.g. <samp>uid=Username,ou=MyUnit,o=MyCompany,c=US</samp>. Required for Active Directory Servers.',
'LDAP_USER_FILTER' => 'LDAP user filter',
- 'LDAP_USER_FILTER_EXPLAIN' => 'Optionally you can further limit the searched objects with additional filters. For example <samp>objectClass=posixGroup</samp> would result in the use of <samp>(&(uid=$username)(objectClass=posixGroup))</samp>',
+ 'LDAP_USER_FILTER_EXPLAIN' => 'Optionally you can further limit the searched objects with additional filters. For example <samp>objectClass=posixGroup</samp> would result in the use of <samp>(&(uid=$username)(objectClass=posixGroup))</samp>',
+
+ 'REMOTE_USER' => 'Remote User',
+ 'REMOTE_USER_VARNAME' => 'Remote User CGI-Var',
+ 'REMOTE_USER_VARNAME_EXPLAIN' => 'CGI variable in which the server will place the username (if authenticated through Remote User Token)',
+ 'REMOTE_USER_SETUP_BEFORE_USE' => 'You have to setup remote user authentication before you switch phpBB to this authentication method. Make sure your reverse proxy is passing in the right variable.',
+ 'REMOTE_USER_INVALID_USERNAME' => 'You have to setup remote user authentication before you switch phpBB to this authentication method. The rerverse proxy is providing a username (%s) other than your current one (%s).',
));
// Server Settings
diff --git a/phpBB/phpbb/auth/provider/remote_user.php b/phpBB/phpbb/auth/provider/remote_user.php
new file mode 100755
index 000000000..7aaf1d9b1
--- /dev/null
+++ b/phpBB/phpbb/auth/provider/remote_user.php
@@ -0,0 +1,431 @@
+<?php
+/**
+ *
+ * This file is part of the phpBB Forum Software package.
+ *
+ * @copyright (c) phpBB Limited <https://www.phpbb.com>
+ * @license GNU General Public License, version 2 (GPL-2.0)
+ *
+ * For full copyright and license information, please see
+ * the docs/CREDITS.txt file.
+ *
+ */
+
+namespace phpbb\auth\provider;
+
+/**
+ * Database authentication provider for phpBB3
+ * This is for authentication via the integrated user table
+ */
+class remote_user extends \phpbb\auth\provider\base {
+ /**
+ * phpBB passwords manager
+ *
+ * @var \phpbb\passwords\manager
+ */
+ protected $passwords_manager;
+
+ /**
+ * DI container
+ *
+ * @var \Symfony\Component\DependencyInjection\ContainerInterface
+ */
+ protected $phpbb_container;
+
+ /**
+ * Database Authentication Constructor
+ *
+ * @param \phpbb\db\driver\driver_interface $db
+ * @param \phpbb\config\config $config
+ * @param \phpbb\passwords\manager $passwords_manager
+ * @param \phpbb\request\request $request
+ * @param \phpbb\user $user
+ * @param \Symfony\Component\DependencyInjection\ContainerInterface
+ * $phpbb_container DI container
+ * @param string $phpbb_root_path
+ * @param string $php_ext
+ */
+ public function __construct(\phpbb\db\driver\driver_interface $db,
+ \phpbb\config\config $config,
+ \phpbb\passwords\manager $passwords_manager,
+ \phpbb\request\request $request,
+ \phpbb\user $user,
+ \Symfony\Component\DependencyInjection\ContainerInterface $phpbb_container,
+ $phpbb_root_path, $php_ext) {
+ $this->db = $db;
+ $this->config = $config;
+ $this->passwords_manager = $passwords_manager;
+ $this->request = $request;
+ $this->user = $user;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->php_ext = $php_ext;
+ $this->phpbb_container = $phpbb_container;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function init() {
+ if (empty($config['remote_user_varname'])) {
+ $this->config['remote_user_varname'] = "REMOTE_USER";
+ }
+ $varname = $this->config['remote_user_varname'];
+
+ if (!$this->request->is_set($varname,
+ \phpbb\request\request_interface::SERVER)) {
+ return $this->user->lang['REMOTE_USER_SETUP_BEFORE_USE'];
+ }
+
+ $remoteuser = htmlspecialchars_decode(
+ $this->request->server($varname));
+ if ($this->user->data['username'] !== $remoteuser) {
+ return sprintf($this->user->lang['REMOTE_USER_INVALID_USERNAME'],
+ $remoteuser, $this->user->data['username']);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function login($username, $password) {
+ // Auth plugins get the password untrimmed.
+ // For compatibility we trim() here.
+ $password = trim($password);
+
+ if (!$username) {
+ return array(
+ 'status' => LOGIN_ERROR_USERNAME,
+ 'error_msg' => 'LOGIN_ERROR_USERNAME',
+ 'user_row' => array('user_id' => ANONYMOUS),
+ );
+ }
+ $username_clean = utf8_clean_string($username);
+
+ // If password is empyt, check for RUT, otherwise disallow
+ $remoteauth_skip_pass = False;
+ if (!$password) {
+ $err = array(
+ 'status' => LOGIN_ERROR_PASSWORD,
+ 'error_msg' => 'NO_PASSWORD_SUPPLIED',
+ 'user_row' => array('user_id' => ANONYMOUS),
+ );
+
+ // If no password was supplied but we have a remote user token
+ // supplied by the reverse proxy, then we approve the login. This allows
+ // remote user token to provide admin auth.
+ if (empty($config['remote_user_varname'])) {
+ $this->config['remote_user_varname'] = "REMOTE_USER";
+ }
+ $varname = $this->config['remote_user_varname'];
+
+ if (!$this->request->is_set($varname,
+ \phpbb\request\request_interface::SERVER)) {
+ return $err;
+ }
+
+ $remote_user = htmlspecialchars_decode($this->request->server($varname));
+ if ($remote_user != $username_clean) {
+ return $err;
+ }
+
+ $remoteauth_skip_pass = True;
+ }
+
+ $sql = 'SELECT *
+ FROM ' . USERS_TABLE . "
+ WHERE username_clean = '" . $this->db->sql_escape($username_clean) . "'";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ // Successful login... set user_login_attempts to zero...
+ if ($remoteauth_skip_pass) {
+ return array(
+ 'status' => LOGIN_SUCCESS,
+ 'error_msg' => false,
+ 'user_row' => $row,
+ );
+ }
+
+ if (
+ ($this->user->ip && !$this->config['ip_login_limit_use_forwarded']) ||
+ ($this->user->forwarded_for &&
+ $this->config['ip_login_limit_use_forwarded'])) {
+ $sql = 'SELECT COUNT(*) AS attempts
+ FROM ' . LOGIN_ATTEMPT_TABLE . '
+ WHERE attempt_time > '
+ . (time() - (int) $this->config['ip_login_limit_time']);
+ if ($this->config['ip_login_limit_use_forwarded']) {
+ $sql .= " AND attempt_forwarded_for = '"
+ . $this->db->sql_escape($this->user->forwarded_for) . "'";
+ } else {
+ $sql .= " AND attempt_ip = '"
+ . $this->db->sql_escape($this->user->ip) . "' ";
+ }
+
+ $result = $this->db->sql_query($sql);
+ $attempts = (int) $this->db->sql_fetchfield('attempts');
+ $this->db->sql_freeresult($result);
+
+ $attempt_data = array(
+ 'attempt_ip' => $this->user->ip,
+ 'attempt_browser' => trim(substr($this->user->browser, 0, 149)),
+ 'attempt_forwarded_for' => $this->user->forwarded_for,
+ 'attempt_time' => time(),
+ 'user_id' => ($row) ? (int) $row['user_id'] : 0,
+ 'username' => $username,
+ 'username_clean' => $username_clean,
+ );
+ $sql = 'INSERT INTO ' . LOGIN_ATTEMPT_TABLE
+ . $this->db->sql_build_array('INSERT', $attempt_data);
+ $this->db->sql_query($sql);
+ } else {
+ $attempts = 0;
+ }
+
+ if (!$row) {
+ if ($this->config['ip_login_limit_max'] &&
+ $attempts >= $this->config['ip_login_limit_max']) {
+ return array(
+ 'status' => LOGIN_ERROR_ATTEMPTS,
+ 'error_msg' => 'LOGIN_ERROR_ATTEMPTS',
+ 'user_row' => array('user_id' => ANONYMOUS),
+ );
+ }
+
+ return array(
+ 'status' => LOGIN_ERROR_USERNAME,
+ 'error_msg' => 'LOGIN_ERROR_USERNAME',
+ 'user_row' => array('user_id' => ANONYMOUS),
+ );
+ }
+
+ $show_captcha = ($this->config['max_login_attempts'] &&
+ $row['user_login_attempts'] >= $this->config['max_login_attempts']) ||
+ ($this->config['ip_login_limit_max'] &&
+ $attempts >= $this->config['ip_login_limit_max']);
+
+ // If there are too many login attempts, we need to check for a confirm
+ // image. Every auth module is able to define what to do by itself...
+ if ($show_captcha) {
+ /* @var $captcha_factory \phpbb\captcha\factory */
+ $captcha_factory = $this->phpbb_container->get('captcha.factory');
+ $captcha =
+ $captcha_factory->get_instance($this->config['captcha_plugin']);
+ $captcha->init(CONFIRM_LOGIN);
+ $vc_response = $captcha->validate($row);
+ if ($vc_response) {
+ return array(
+ 'status' => LOGIN_ERROR_ATTEMPTS,
+ 'error_msg' => 'LOGIN_ERROR_ATTEMPTS',
+ 'user_row' => $row,
+ );
+ } else {
+ $captcha->reset();
+ }
+
+ }
+
+ // Check password ...
+ if ($this->passwords_manager->check(
+ $password, $row['user_password'], $row)) {
+ // Check for old password hash...
+ if ($this->passwords_manager->convert_flag ||
+ strlen($row['user_password']) == 32) {
+ $hash = $this->passwords_manager->hash($password);
+
+ // Update the password in the users table to the new format
+ $sql = 'UPDATE ' . USERS_TABLE . "
+ SET user_password = '" . $this->db->sql_escape($hash) . "'
+ WHERE user_id = {$row['user_id']}";
+ $this->db->sql_query($sql);
+
+ $row['user_password'] = $hash;
+ }
+
+ $sql = 'DELETE FROM ' . LOGIN_ATTEMPT_TABLE . '
+ WHERE user_id = ' . $row['user_id'];
+ $this->db->sql_query($sql);
+
+ if ($row['user_login_attempts'] != 0) {
+ // Successful, reset login attempts (the user passed all stages)
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET user_login_attempts = 0
+ WHERE user_id = ' . $row['user_id'];
+ $this->db->sql_query($sql);
+ }
+
+ // User inactive...
+ if ($row['user_type'] == USER_INACTIVE ||
+ $row['user_type'] == USER_IGNORE) {
+ return array(
+ 'status' => LOGIN_ERROR_ACTIVE,
+ 'error_msg' => 'ACTIVE_ERROR',
+ 'user_row' => $row,
+ );
+ }
+
+ // Successful login... set user_login_attempts to zero...
+ return array(
+ 'status' => LOGIN_SUCCESS,
+ 'error_msg' => false,
+ 'user_row' => $row,
+ );
+ }
+
+ // Password incorrect - increase login attempts
+ $sql = 'UPDATE ' . USERS_TABLE . '
+ SET user_login_attempts = user_login_attempts + 1
+ WHERE user_id = ' . (int) $row['user_id'] . '
+ AND user_login_attempts < ' . LOGIN_ATTEMPTS_MAX;
+ $this->db->sql_query($sql);
+
+ // Give status about wrong password...
+ return array(
+ 'status' => ($show_captcha) ? LOGIN_ERROR_ATTEMPTS : LOGIN_ERROR_PASSWORD,
+ 'error_msg' => 'LOGIN_ERROR_PASSWORD',
+ 'user_row' => $row,
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function acp() {
+ // These are fields required in the config table
+ return array(
+ 'remote_user_varname',
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get_acp_template($new_config) {
+ return array(
+ 'TEMPLATE_FILE' => 'auth_provider_remote_user.html',
+ 'TEMPLATE_VARS' => array(
+ 'AUTH_REMOTE_USER_VARNAME' => $new_config['remote_user_varname'],
+ ),
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function autologin() {
+ if (empty($config['remote_user_varname'])) {
+ $this->config['remote_user_varname'] = "REMOTE_USER";
+ }
+ $varname = $this->config['remote_user_varname'];
+ if (!$this->request->is_set($varname,
+ \phpbb\request\request_interface::SERVER)) {
+ return array();
+ }
+
+ $remote_user = htmlspecialchars_decode($this->request->server($varname));
+
+ if (!empty($remote_user)) {
+ set_var($remote_user, $remote_user, 'string', true);
+
+ $sql = 'SELECT *
+ FROM ' . USERS_TABLE . "
+ WHERE username = '" . $this->db->sql_escape($remote_user) . "'";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if ($row) {
+ if ($row['user_type'] == USER_INACTIVE ||
+ $row['user_type'] == USER_IGNORE) {
+ return array();
+ } else {
+ return $row;
+ }
+ }
+
+ if (!function_exists('user_add')) {
+ include $this->phpbb_root_path
+ . 'includes/functions_user.'
+ . $this->php_ext;
+ }
+
+ // create the user if she does not exist yet
+ user_add($this->user_row($remote_user));
+
+ $sql = 'SELECT *
+ FROM ' . USERS_TABLE . "
+ WHERE username_clean = '"
+ . $this->db->sql_escape(utf8_clean_string($remote_user)) . "'";
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if ($row) {
+ return $row;
+ }
+ }
+
+ return array();
+ }
+
+ /**
+ * This function generates an array which can be passed to the user_add
+ * function in order to create a user
+ *
+ * @param string $username The username of the new user.
+ * @return array Contains data that can be passed directly to
+ * the user_add function.
+ */
+ private function user_row($username) {
+ // first retrieve default group id
+ $sql = 'SELECT group_id
+ FROM ' . GROUPS_TABLE . "
+ WHERE group_name = '" . $this->db->sql_escape('REGISTERED') . "'
+ AND group_type = " . GROUP_SPECIAL;
+ $result = $this->db->sql_query($sql);
+ $row = $this->db->sql_fetchrow($result);
+ $this->db->sql_freeresult($result);
+
+ if (!$row) {
+ trigger_error('NO_GROUP');
+ }
+
+ // generate user account data
+ return array(
+ 'username' => $username,
+ 'user_password' => substr(md5(rand()), 0, 7),
+ 'user_email' => '',
+ 'group_id' => (int) $row['group_id'],
+ 'user_type' => USER_NORMAL,
+ 'user_ip' => $this->user->ip,
+ 'user_new' => ($this->config['new_member_post_limit']) ? 1 : 0,
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validate_session($user) {
+ if (empty($config['remote_user_varname'])) {
+ $this->config['remote_user_varname'] = "REMOTE_USER";
+ }
+ $varname = $this->config['remote_user_varname'];
+
+ // Check if remote user token is set and handle this case
+ if ($this->request->is_set($varname,
+ \phpbb\request\request_interface::SERVER)) {
+ $php_auth_user = $this->request->server($varname);
+ return ($php_auth_user === $user['username']) ? true : false;
+ }
+
+ // remote user token is not set. A valid session is now determined by the
+ // user type (anonymous/bot or not)
+ if ($user['user_type'] == USER_IGNORE) {
+ return true;
+ }
+
+ return false;
+ }
+}