1 <?php
  2 /**
  3  * @package     Joomla.Platform
  4  * @subpackage  Authentication
  5  *
  6  * @copyright   Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
  7  * @license     GNU General Public License version 2 or later; see LICENSE
  8  */
  9 
 10 defined('JPATH_PLATFORM') or die;
 11 
 12 /**
 13  * Authentication class, provides an interface for the Joomla authentication system
 14  *
 15  * @since  11.1
 16  */
 17 class JAuthentication extends JObject
 18 {
 19     // Shared success status
 20     /**
 21      * This is the status code returned when the authentication is success (permit login)
 22      * @const  STATUS_SUCCESS successful response
 23      * @since  11.2
 24      */
 25     const STATUS_SUCCESS = 1;
 26 
 27     // These are for authentication purposes (username and password is valid)
 28     /**
 29      * Status to indicate cancellation of authentication (unused)
 30      * @const  STATUS_CANCEL cancelled request (unused)
 31      * @since  11.2
 32      */
 33     const STATUS_CANCEL = 2;
 34 
 35     /**
 36      * This is the status code returned when the authentication failed (prevent login if no success)
 37      * @const  STATUS_FAILURE failed request
 38      * @since  11.2
 39      */
 40     const STATUS_FAILURE = 4;
 41 
 42     // These are for authorisation purposes (can the user login)
 43     /**
 44      * This is the status code returned when the account has expired (prevent login)
 45      * @const  STATUS_EXPIRED an expired account (will prevent login)
 46      * @since  11.2
 47      */
 48     const STATUS_EXPIRED = 8;
 49 
 50     /**
 51      * This is the status code returned when the account has been denied (prevent login)
 52      * @const  STATUS_DENIED denied request (will prevent login)
 53      * @since  11.2
 54      */
 55     const STATUS_DENIED = 16;
 56 
 57     /**
 58      * This is the status code returned when the account doesn't exist (not an error)
 59      * @const  STATUS_UNKNOWN unknown account (won't permit or prevent login)
 60      * @since  11.2
 61      */
 62     const STATUS_UNKNOWN = 32;
 63 
 64     /**
 65      * An array of Observer objects to notify
 66      *
 67      * @var    array
 68      * @since  12.1
 69      */
 70     protected $observers = array();
 71 
 72     /**
 73      * The state of the observable object
 74      *
 75      * @var    mixed
 76      * @since  12.1
 77      */
 78     protected $state = null;
 79 
 80     /**
 81      * A multi dimensional array of [function][] = key for observers
 82      *
 83      * @var    array
 84      * @since  12.1
 85      */
 86     protected $methods = array();
 87 
 88     /**
 89      * @var    JAuthentication  JAuthentication instances container.
 90      * @since  11.3
 91      */
 92     protected static $instance;
 93 
 94     /**
 95      * Constructor
 96      *
 97      * @since   11.1
 98      */
 99     public function __construct()
100     {
101         $isLoaded = JPluginHelper::importPlugin('authentication');
102 
103         if (!$isLoaded)
104         {
105             JLog::add(JText::_('JLIB_USER_ERROR_AUTHENTICATION_LIBRARIES'), JLog::WARNING, 'jerror');
106         }
107     }
108 
109     /**
110      * Returns the global authentication object, only creating it
111      * if it doesn't already exist.
112      *
113      * @return  JAuthentication  The global JAuthentication object
114      *
115      * @since   11.1
116      */
117     public static function getInstance()
118     {
119         if (empty(self::$instance))
120         {
121             self::$instance = new JAuthentication;
122         }
123 
124         return self::$instance;
125     }
126 
127     /**
128      * Get the state of the JAuthentication object
129      *
130      * @return  mixed    The state of the object.
131      *
132      * @since   11.1
133      */
134     public function getState()
135     {
136         return $this->state;
137     }
138 
139     /**
140      * Attach an observer object
141      *
142      * @param   object  $observer  An observer object to attach
143      *
144      * @return  void
145      *
146      * @since   11.1
147      */
148     public function attach($observer)
149     {
150         if (is_array($observer))
151         {
152             if (!isset($observer['handler']) || !isset($observer['event']) || !is_callable($observer['handler']))
153             {
154                 return;
155             }
156 
157             // Make sure we haven't already attached this array as an observer
158             foreach ($this->observers as $check)
159             {
160                 if (is_array($check) && $check['event'] == $observer['event'] && $check['handler'] == $observer['handler'])
161                 {
162                     return;
163                 }
164             }
165 
166             $this->observers[] = $observer;
167             end($this->observers);
168             $methods = array($observer['event']);
169         }
170         else
171         {
172             if (!($observer instanceof JAuthentication))
173             {
174                 return;
175             }
176 
177             // Make sure we haven't already attached this object as an observer
178             $class = get_class($observer);
179 
180             foreach ($this->observers as $check)
181             {
182                 if ($check instanceof $class)
183                 {
184                     return;
185                 }
186             }
187 
188             $this->observers[] = $observer;
189             $methods = array_diff(get_class_methods($observer), get_class_methods('JPlugin'));
190         }
191 
192         $key = key($this->observers);
193 
194         foreach ($methods as $method)
195         {
196             $method = strtolower($method);
197 
198             if (!isset($this->methods[$method]))
199             {
200                 $this->methods[$method] = array();
201             }
202 
203             $this->methods[$method][] = $key;
204         }
205     }
206 
207     /**
208      * Detach an observer object
209      *
210      * @param   object  $observer  An observer object to detach.
211      *
212      * @return  boolean  True if the observer object was detached.
213      *
214      * @since   11.1
215      */
216     public function detach($observer)
217     {
218         $retval = false;
219 
220         $key = array_search($observer, $this->observers);
221 
222         if ($key !== false)
223         {
224             unset($this->observers[$key]);
225             $retval = true;
226 
227             foreach ($this->methods as &$method)
228             {
229                 $k = array_search($key, $method);
230 
231                 if ($k !== false)
232                 {
233                     unset($method[$k]);
234                 }
235             }
236         }
237 
238         return $retval;
239     }
240 
241     /**
242      * Finds out if a set of login credentials are valid by asking all observing
243      * objects to run their respective authentication routines.
244      *
245      * @param   array  $credentials  Array holding the user credentials.
246      * @param   array  $options      Array holding user options.
247      *
248      * @return  JAuthenticationResponse  Response object with status variable filled in for last plugin or first successful plugin.
249      *
250      * @see     JAuthenticationResponse
251      * @since   11.1
252      */
253     public function authenticate($credentials, $options = array())
254     {
255         // Get plugins
256         $plugins = JPluginHelper::getPlugin('authentication');
257 
258         // Create authentication response
259         $response = new JAuthenticationResponse;
260 
261         /*
262          * Loop through the plugins and check if the credentials can be used to authenticate
263          * the user
264          *
265          * Any errors raised in the plugin should be returned via the JAuthenticationResponse
266          * and handled appropriately.
267          */
268         foreach ($plugins as $plugin)
269         {
270             $className = 'plg' . $plugin->type . $plugin->name;
271 
272             if (class_exists($className))
273             {
274                 $plugin = new $className($this, (array) $plugin);
275             }
276             else
277             {
278                 // Bail here if the plugin can't be created
279                 JLog::add(JText::sprintf('JLIB_USER_ERROR_AUTHENTICATION_FAILED_LOAD_PLUGIN', $className), JLog::WARNING, 'jerror');
280                 continue;
281             }
282 
283             // Try to authenticate
284             $plugin->onUserAuthenticate($credentials, $options, $response);
285 
286             // If authentication is successful break out of the loop
287             if ($response->status === self::STATUS_SUCCESS)
288             {
289                 if (empty($response->type))
290                 {
291                     $response->type = isset($plugin->_name) ? $plugin->_name : $plugin->name;
292                 }
293 
294                 break;
295             }
296         }
297 
298         if (empty($response->username))
299         {
300             $response->username = $credentials['username'];
301         }
302 
303         if (empty($response->fullname))
304         {
305             $response->fullname = $credentials['username'];
306         }
307 
308         if (empty($response->password) && isset($credentials['password']))
309         {
310             $response->password = $credentials['password'];
311         }
312 
313         return $response;
314     }
315 
316     /**
317      * Authorises that a particular user should be able to login
318      *
319      * @param   JAuthenticationResponse  $response  response including username of the user to authorise
320      * @param   array                    $options   list of options
321      *
322      * @return  JAuthenticationResponse[]  Array of authentication response objects
323      *
324      * @since  11.2
325      */
326     public static function authorise($response, $options = array())
327     {
328         // Get plugins in case they haven't been imported already
329         JPluginHelper::importPlugin('user');
330 
331         JPluginHelper::importPlugin('authentication');
332         $dispatcher = JEventDispatcher::getInstance();
333         $results = $dispatcher->trigger('onUserAuthorisation', array($response, $options));
334 
335         return $results;
336     }
337 }
338