1: <?php
2: /**
3: * ElggPAM Pluggable Authentication Module
4: *
5: * @package Elgg.Core
6: * @subpackage Authentication
7: */
8: class ElggPAM {
9: /**
10: * @var string PAM policy type: user, api or plugin-defined policies
11: */
12: protected $policy;
13:
14: /**
15: * @var array Failure mesages
16: */
17: protected $messages;
18:
19: /**
20: * ElggPAM constructor
21: *
22: * @param string $policy PAM policy type: user, api, or plugin-defined policies
23: */
24: public function __construct($policy) {
25: $this->policy = $policy;
26: $this->messages = array('sufficient' => array(), 'required' => array());
27: }
28:
29: /**
30: * Authenticate a set of credentials against a policy
31: * This function will process all registered PAM handlers or stop when the first
32: * handler fails. A handler fails by either returning false or throwing an
33: * exception. The advantage of throwing an exception is that it returns a message
34: * that can be passed to the user. The processing order of the handlers is
35: * determined by the order that they were registered.
36: *
37: * If $credentials are provided, the PAM handler should authenticate using the
38: * provided credentials. If not, then credentials should be prompted for or
39: * otherwise retrieved (eg from the HTTP header or $_SESSION).
40: *
41: * @param array $credentials Credentials array dependant on policy type
42: * @return bool
43: */
44: public function authenticate($credentials = array()) {
45: global $_PAM_HANDLERS;
46:
47: if (!isset($_PAM_HANDLERS[$this->policy]) ||
48: !is_array($_PAM_HANDLERS[$this->policy])) {
49: return false;
50: }
51:
52: $authenticated = false;
53:
54: foreach ($_PAM_HANDLERS[$this->policy] as $k => $v) {
55: $handler = $v->handler;
56: if (!is_callable($handler)) {
57: continue;
58: }
59: /* @var callable $handler */
60:
61: $importance = $v->importance;
62:
63: try {
64: // Execute the handler
65: // @todo don't assume $handler is a global function
66: $result = call_user_func($handler, $credentials);
67: if ($result) {
68: $authenticated = true;
69: } elseif ($result === false) {
70: if ($importance == 'required') {
71: $this->messages['required'][] = "$handler:failed";
72: return false;
73: } else {
74: $this->messages['sufficient'][] = "$handler:failed";
75: }
76: }
77: } catch (Exception $e) {
78: if ($importance == 'required') {
79: $this->messages['required'][] = $e->getMessage();
80: return false;
81: } else {
82: $this->messages['sufficient'][] = $e->getMessage();
83: }
84: }
85: }
86:
87: return $authenticated;
88: }
89:
90: /**
91: * Get a failure message to display to user
92: *
93: * @return string
94: */
95: public function getFailureMessage() {
96: $message = elgg_echo('auth:nopams');
97: if (!empty($this->messages['required'])) {
98: $message = $this->messages['required'][0];
99: } elseif (!empty($this->messages['sufficient'])) {
100: $message = $this->messages['sufficient'][0];
101: }
102:
103: return elgg_trigger_plugin_hook('fail', 'auth', $this->messages, $message);
104: }
105: }
106: