aboutsummaryrefslogtreecommitdiff
path: root/engine/classes/ElggPAM.php
blob: f07095fc131277bc26ba59d6a7e312f79448ddb4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<?php
/**
 * ElggPAM Pluggable Authentication Module
 *
 * @package    Elgg.Core
 * @subpackage Authentication
 */
class ElggPAM {
	/**
	 * @var string PAM policy type: user, api or plugin-defined policies
	 */
	protected $policy;

	/**
	 * @var array Failure mesages
	 */
	protected $messages;

	/**
	 * ElggPAM constructor
	 * 
	 * @param string $policy PAM policy type: user, api, or plugin-defined policies
	 */
	public function __construct($policy) {
		$this->policy = $policy;
		$this->messages = array('sufficient' => array(), 'required' => array());
	}

	/**
	 * Authenticate a set of credentials against a policy
	 * This function will process all registered PAM handlers or stop when the first
	 * handler fails. A handler fails by either returning false or throwing an
	 * exception. The advantage of throwing an exception is that it returns a message
	 * that can be passed to the user. The processing order of the handlers is
	 * determined by the order that they were registered.
	 *
	 * If $credentials are provided, the PAM handler should authenticate using the
	 * provided credentials. If not, then credentials should be prompted for or
	 * otherwise retrieved (eg from the HTTP header or $_SESSION).
	 *
	 * @param array $credentials Credentials array dependant on policy type
	 * @return bool
	 */
	public function authenticate($credentials = array()) {
		global $_PAM_HANDLERS;

		if (!isset($_PAM_HANDLERS[$this->policy]) ||
			!is_array($_PAM_HANDLERS[$this->policy])) {
			return false;
		}

		$authenticated = false;

		foreach ($_PAM_HANDLERS[$this->policy] as $k => $v) {
			$handler = $v->handler;
			if (!is_callable($handler)) {
				continue;
			}
			/* @var callable $handler */

			$importance = $v->importance;

			try {
				// Execute the handler
				// @todo don't assume $handler is a global function
				$result = call_user_func($handler, $credentials);
				if ($result) {
					$authenticated = true;
				} elseif ($result === false) {
					if ($importance == 'required') {
						$this->messages['required'][] = "$handler:failed";
						return false;
					} else {
						$this->messages['sufficient'][] = "$handler:failed";
					}
				}
			} catch (Exception $e) {
				if ($importance == 'required') {
					$this->messages['required'][] = $e->getMessage();
					return false;
				} else {
					$this->messages['sufficient'][] = $e->getMessage();
				}
			}
		}

		return $authenticated;
	}

	/**
	 * Get a failure message to display to user
	 * 
	 * @return string
	 */
	public function getFailureMessage() {
		$message = elgg_echo('auth:nopams');
		if (!empty($this->messages['required'])) {
			$message = $this->messages['required'][0];
		} elseif (!empty($this->messages['sufficient'])) {
			$message = $this->messages['sufficient'][0];
		}

		return elgg_trigger_plugin_hook('fail', 'auth', $this->messages, $message);
	}
}