1: <?php
2: /**
3: * Copyright 1999-2014 Horde LLC (http://www.horde.org/)
4: *
5: * See the enclosed file COPYING for license information (GPL). If you
6: * did not receive this file, see http://www.horde.org/licenses/gpl.
7: *
8: * @category Horde
9: * @copyright 1999-2014 Horde LLC
10: * @license http://www.horde.org/licenses/gpl GPL
11: * @package IMP
12: */
13:
14: /**
15: * This class provides authentication for IMP.
16: *
17: * @author Chuck Hagenbuch <chuck@horde.org>
18: * @author Jon Parise <jon@horde.org>
19: * @author Michael Slusarz <slusarz@horde.org>
20: * @category Horde
21: * @copyright 1999-2014 Horde LLC
22: * @license http://www.horde.org/licenses/gpl GPL
23: * @package IMP
24: */
25: class IMP_Auth
26: {
27: /**
28: * Authenticate to the mail server.
29: *
30: * @param array $credentials An array of login credentials. If empty,
31: * attempts to login to the cached session.
32: * - password: (string) The user password.
33: * - server: (string) The server key to use (from backends.php).
34: * - userId: (string) The username.
35: *
36: * @throws Horde_Auth_Exception
37: */
38: public static function authenticate($credentials = array())
39: {
40: global $injector, $registry;
41:
42: // Do 'horde' authentication.
43: $imp_app = $registry->getApiInstance('imp', 'application');
44: if (!empty($imp_app->initParams['authentication']) &&
45: ($imp_app->initParams['authentication'] == 'horde')) {
46: if ($registry->getAuth()) {
47: return;
48: }
49: throw new Horde_Auth_Exception('', Horde_Auth::REASON_FAILED);
50: }
51:
52: if (!isset($credentials['server'])) {
53: $credentials['server'] = self::getAutoLoginServer();
54: }
55:
56: $imp_imap = $injector->getInstance('IMP_Factory_Imap')->create();
57:
58: // Check for valid IMAP Client object.
59: if (!$imp_imap->init) {
60: if (!isset($credentials['userId']) ||
61: !isset($credentials['password'])) {
62: throw new Horde_Auth_Exception('', Horde_Auth::REASON_BADLOGIN);
63: }
64:
65: try {
66: $imp_imap->createBaseImapObject($credentials['userId'], $credentials['password'], $credentials['server']);
67: } catch (IMP_Imap_Exception $e) {
68: self::_log(false, $imp_imap);
69: throw $e->authException();
70: }
71: }
72:
73: try {
74: $imp_imap->login();
75: } catch (IMP_Imap_Exception $e) {
76: self::_log(false, $imp_imap);
77: throw $e->authException();
78: }
79: }
80:
81: /**
82: * Perform transparent authentication.
83: *
84: * @param Horde_Auth_Application $auth_ob The authentication object.
85: *
86: * @return boolean True on successful transparent authentication.
87: */
88: public static function transparent($auth_ob)
89: {
90: $credentials = $auth_ob->getCredential('credentials');
91:
92: if (empty($credentials['transparent'])) {
93: /* Attempt hordeauth authentication. */
94: $credentials = self::_canAutoLogin();
95: if ($credentials === false) {
96: return false;
97: }
98: } else {
99: /* It is possible that preauthenticate() set the credentials.
100: * If so, use that information instead of hordeauth. */
101: $credentials['userId'] = $auth_ob->getCredential('userId');
102: }
103:
104: if (!isset($credentials['password']) ||
105: !strlen($credentials['password'])) {
106: return false;
107: }
108:
109: try {
110: self::authenticate($credentials);
111: } catch (Horde_Auth_Exception $e) {
112: return false;
113: }
114:
115: return true;
116: }
117:
118: /**
119: * Log login related message.
120: *
121: * @param boolean $status True on success, false on failure.
122: * @param IMP_Imap $imap_ob The IMP_Imap object to use.
123: */
124: protected static function _log($status, $imap_ob)
125: {
126: $msg = $status
127: ? 'Login success'
128: : 'FAILED LOGIN';
129: $user = $imap_ob->getParam('username');
130:
131: if (($auth_id = $GLOBALS['registry']->getAuth()) &&
132: ($user != $auth_id)) {
133: $user .= ' (Horde user ' . $auth_id . ')';
134: }
135:
136: Horde::log(
137: sprintf(
138: $msg . ' for %s (%s)%s to {%s}',
139: $user,
140: $_SERVER['REMOTE_ADDR'],
141: empty($_SERVER['HTTP_X_FORWARDED_FOR']) ? '' : ' (forwarded for [' . $_SERVER['HTTP_X_FORWARDED_FOR'] . '])',
142: $imap_ob->url
143: ),
144: $status ? 'NOTICE' : 'INFO'
145: );
146: }
147:
148: /**
149: * Returns the autologin server key.
150: *
151: * @return string The server key, or null if none available.
152: */
153: public static function getAutoLoginServer()
154: {
155: if (($servers = IMP_Imap::loadServerConfig()) === false) {
156: return null;
157: }
158:
159: $server_key = null;
160: foreach ($servers as $key => $val) {
161: if (is_null($server_key) && (substr($key, 0, 1) != '_')) {
162: $server_key = $key;
163: }
164:
165: /* Determines if the given mail server is the "preferred" mail
166: * server for this web server. This decision is based on the
167: * global 'SERVER_NAME' and 'HTTP_HOST' server variables and the
168: * contents of the 'preferred' field in the backend's config. */
169: if (($preferred = $val->preferred) &&
170: (in_array($_SERVER['SERVER_NAME'], $preferred) ||
171: in_array($_SERVER['HTTP_HOST'], $preferred))) {
172: return $key;
173: }
174: }
175:
176: return $server_key;
177: }
178:
179: /**
180: * Returns whether we can log in without a login screen for $server_key.
181: *
182: * @param string $server_key The server to check. Defaults to the
183: * autologin server.
184: * @param boolean $force If true, check $server_key even if there is
185: * more than one server available.
186: *
187: * @return array The credentials needed to login ('userId', 'password',
188: * 'server') or false if autologin not available.
189: */
190: protected static function _canAutoLogin($server_key = null, $force = false)
191: {
192: global $injector, $registry;
193:
194: if (($servers = $injector->getInstance('IMP_Factory_Imap')->create()->loadServerConfig()) === false) {
195: return false;
196: }
197:
198: if (is_null($server_key) || !$force) {
199: $auto_server = self::getAutoLoginServer();
200: if (is_null($server_key)) {
201: $server_key = $auto_server;
202: }
203: }
204:
205: if ((!empty($auto_server) || $force) &&
206: $registry->getAuth() &&
207: !empty($servers[$server_key]->hordeauth)) {
208: return array(
209: 'userId' => $registry->getAuth((strcasecmp($servers[$server_key]->hordeauth, 'full') === 0) ? null : 'bare'),
210: 'password' => $registry->getAuthCredential('password'),
211: 'server' => $server_key
212: );
213: }
214:
215: return false;
216: }
217:
218: /**
219: * Perform post-login tasks. Session creation requires the full IMP
220: * environment, which is not available until this callback.
221: *
222: * The following global IMP session variables are created by this method:
223: * - file_upload: (integer) If file uploads are allowed, the max size.
224: * - rteavail: (boolean) Is the HTML editor available?
225: *
226: * @throws Horde_Exception
227: */
228: public static function authenticateCallback()
229: {
230: global $browser, $injector, $session;
231:
232: $imp_imap = $injector->getInstance('IMP_Factory_Imap')->create();
233:
234: /* Perform post-login tasks for IMAP object. */
235: $imp_imap->doPostLoginTasks();
236:
237: /* Does the server allow file uploads? If yes, store the
238: * value, in bytes, of the maximum file size. */
239: $session->set('imp', 'file_upload', $browser->allowFileUploads());
240:
241: /* Is the HTML editor available? */
242: $session->set('imp', 'rteavail', $injector->getInstance('Horde_Editor')->supportedByBrowser());
243:
244: self::_log(true, $imp_imap);
245: }
246:
247: }
248: