1<?php
2/**
3 * Copyright 1999-2017 Horde LLC (http://www.horde.org/)
4 *
5 * See the enclosed file COPYING for license information (LGPL). If you did
6 * not receive this file, see http://www.horde.org/licenses/lgpl21.
7 *
8 * @author   Chuck Hagenbuch <chuck@horde.org>
9 * @category Horde
10 * @license  http://www.horde.org/licenses/lgpl21 LGPL-2.1
11 * @package  Auth
12 */
13
14/**
15 * The Horde_Auth_Http class transparently logs users in to Horde using
16 * already present HTTP authentication headers.
17 *
18 * @author    Chuck Hagenbuch <chuck@horde.org>
19 * @category  Horde
20 * @copyright 1999-2017 Horde LLC
21 * @license   http://www.horde.org/licenses/lgpl21 LGPL-2.1
22 * @package   Auth
23 */
24class Horde_Auth_Http extends Horde_Auth_Base
25{
26    /**
27     * An array of capabilities, so that the driver can report which
28     * operations it supports and which it doesn't.
29     *
30     * @var array
31     */
32    protected $_capabilities = array(
33        'transparent' => true
34    );
35
36    /**
37     * Array of usernames and hashed passwords.
38     *
39     * @var array
40     */
41    protected $_users = array();
42
43    /**
44     * Constructor.
45     *
46     * @param array $params  Optional parameters:
47     * <pre>
48     * 'encryption' - (string) Kind of passwords in the .htpasswd file.
49     *                Either 'crypt-des' (standard crypted htpasswd entries)
50     *                [DEFAULT] or 'aprmd5'. This information is used if
51     *                you want to directly authenticate users with this
52     *                driver, instead of relying on transparent auth.
53     * 'htpasswd_file' - (string) TODO
54     * </pre>
55     */
56    public function __construct(array $params = array())
57    {
58        $params = array_merge(array(
59            'encryption' => 'crypt-des'
60        ), $params);
61
62        parent::__construct($params);
63
64        if (!empty($this->_params['htpasswd_file'])) {
65            $users = file($this->_params['htpasswd_file']);
66            if (is_array($users)) {
67                // Enable the list users capability.
68                $this->_capabilities['list'] = true;
69
70                foreach ($users as $line) {
71                    list($user, $pass) = explode(':', $line, 2);
72                    $this->_users[trim($user)] = trim($pass);
73                }
74            }
75        }
76    }
77
78    /**
79     * Find out if a set of login credentials are valid. Only supports
80     * htpasswd files with DES passwords right now.
81     *
82     * @param string $userId       The userId to check.
83     * @param array  $credentials  An array of login credentials. For IMAP,
84     *                             this must contain a password entry.
85     *
86     * @throws Horde_Auth_Exception
87     */
88    protected function _authenticate($userId, $credentials)
89    {
90        if (empty($credentials['password']) ||
91            empty($this->_users[$userId])) {
92            throw new Horde_Auth_Exception('', Horde_Auth::REASON_BADLOGIN);
93        }
94
95        $hash = Horde_Auth::getCryptedPassword($credentials['password'], $this->_users[$userId], $this->_params['encryption'], !empty($this->_params['show_encryption']));
96
97        if ($hash != $this->_users[$userId]) {
98            throw new Horde_Auth_Exception('', Horde_Auth::REASON_BADLOGIN);
99        }
100    }
101
102    /**
103     * Lists all users in the system.
104     *
105     * @param boolean $sort  Sort the users?
106     *
107     * @return array  The array of userIds.
108     */
109    public function listUsers($sort = false)
110    {
111        // this driver sorts by default
112        $users = array_keys($this->_users);
113        return $this->_sort($users, $sort);
114    }
115
116    /**
117     * Automatic authentication: Find out if the client has HTTP
118     * authentication info present.
119     *
120     * @return boolean  Whether or not the client is allowed.
121     */
122    public function transparent()
123    {
124        if (empty($_SERVER['PHP_AUTH_USER']) ||
125            empty($_SERVER['PHP_AUTH_PW'])) {
126            return false;
127        }
128
129        $this->_credentials['userId'] = $_SERVER['PHP_AUTH_USER'];
130        $this->_credentials['credentials'] = array(
131            'password' => Horde_Util::dispelMagicQuotes($_SERVER['PHP_AUTH_PW'])
132        );
133
134        return true;
135    }
136
137}
138