1<?php
2
3namespace OAuth\Plugin;
4
5use OAuth\Common\Consumer\Credentials;
6use OAuth\Common\Http\Exception\TokenResponseException;
7use OAuth\Common\Storage\Exception\TokenNotFoundException;
8use OAuth\ServiceFactory;
9use OAuth\OAuth2\Token\StdOAuth2Token;
10
11/**
12 * Class AbstractAdapter
13 *
14 * For each service that shall be used for logging into DokuWiki a subclass of this abstract
15 * class has to be created. It defines how to talk to the Service's API to retrieve user
16 * information
17 *
18 * @package OAuth\Plugin
19 */
20abstract class AbstractAdapter {
21
22    /** @var \OAuth\Common\Service\AbstractService|\OAuth\OAuth2\Service\AbstractService|\OAuth\OAuth2\Service\AbstractService */
23    public $oAuth = null;
24    /** @var \helper_plugin_evesso */
25    protected $hlp = null;
26    /** @var \OAuth\Plugin\oAuthStorage */
27    protected $storage = null;
28
29    /**
30     * Constructor
31     *
32     * @param $url
33     */
34    public function __construct() {
35        $this->hlp = plugin_load('helper', 'evesso');
36
37        $credentials = new Credentials(
38            $this->hlp->getKey(),
39            $this->hlp->getSecret(),
40            $this->hlp->getRedirectURI()
41        );
42
43        $this->storage = new oAuthStorage();
44
45        $serviceFactory = new ServiceFactory();
46        $serviceFactory->setHttpClient(new oAuthHTTPClient());
47        $this->oAuth = $serviceFactory->createService(
48            $this->getServiceName(),
49            $credentials,
50            $this->storage,
51            $this->getScope()
52        );
53    }
54
55    /**
56     * Check if the initialization worked
57     *
58     * @return bool
59     */
60    public function isInitialized() {
61        if(is_null($this->oAuth)) {
62            return false;
63        }
64        return true;
65    }
66
67    /**
68     * Redirects to the service for requesting access
69     *
70     * This is the first step of oAuth authentication
71     *
72     * This implementation tries to abstract away differences between oAuth1 and oAuth2,
73     * but might need to be overwritten for specific services
74     */
75    public function login() {
76        if(is_a($this->oAuth, 'OAuth\OAuth2\Service\AbstractService')) { /* oAuth2 handling */
77
78            $url = $this->oAuth->getAuthorizationUri();
79        } else { /* oAuth1 handling */
80
81            // extra request needed for oauth1 to request a request token :-)
82            $token = $this->oAuth->requestRequestToken();
83
84            $url = $this->oAuth->getAuthorizationUri(array('oauth_token' => $token->getRequestToken()));
85        }
86
87        send_redirect($url);
88    }
89
90    /**
91     * Clear storage token for user
92     *
93     */
94    public function logout() {
95        if ($this->isInitialized()) {
96            $this->oAuth->getStorage()->clearToken($this->oAuth->service());
97            $this->oAuth->getStorage()->clearAuthorizationState($this->oAuth->service());
98        }
99    }
100
101    /**
102     * Check access_token
103     *
104     * Update as needed
105     *
106     * @return bool true if access_token is valid. false otherwise
107     */
108    public function checkToken() {
109        global $INPUT;
110
111        if ($INPUT->get->has('code')) {
112            if (!$this->requestAccessToken()) { //Request access token (Second step of oAuth authentication)
113                return false;
114            }
115        } else {
116            //Check if access token is still valid, if not, refresh the access_token
117            if (!$this->checkAccessToken() && !$this->refreshAccessToken()) {
118                return false;
119            }
120        }
121        return true;
122    }
123
124    /**
125     * Request access token
126     *
127     * Second step of oAuth authentication
128     *
129     * @global type $INPUT
130     * @global \OAuth\Plugin\type $conf
131     * @return boolean true if successful. false otherwise
132     */
133    private function requestAccessToken() {
134        global $INPUT, $conf;
135
136        try {
137            $this->oAuth->requestAccessToken($INPUT->get->str('code'), $INPUT->get->str('state', null));
138            return true;
139        } catch (TokenResponseException $e) {
140            msg($e->getMessage(), -1);
141            if ($conf['allowdebug']) {
142                msg('<pre>' . hsc($e->getTraceAsString()) . '</pre>', -1);
143            }
144            return false;
145        } finally {
146            $this->oAuth->getStorage()->clearAuthorizationState($this->oAuth->service());
147        }
148    }
149
150    /**
151     * Check if access token is still valid
152     *
153     * @global type $conf
154     * @return boolean true if access_token is vaild. false otherwise
155     */
156    private function checkAccessToken() {
157        global $conf;
158        try {
159            if ($this->oAuth->getStorage()->hasAccessToken($this->oAuth->service()) && $this->oAuth->getStorage()->retrieveAccessToken($this->oAuth->service())->getEndOfLife() - 90 > time()) {
160                return true; // access_token is still vaild - already validated
161            }
162        } catch (TokenNotFoundException $e) {
163            msg($e->getMessage(), -1);
164            if ($conf['allowdebug']) {
165                msg('<pre>' . hsc($e->getTraceAsString()) . '</pre>', -1);
166            }
167        }
168        return false; // oAuth storage have no token
169    }
170
171    /**
172     * Refresh access_token (from refresh_token)
173     *
174     * @global \OAuth\Plugin\type $conf
175     * @return boolean true if successful. false otherwise
176     */
177    private function refreshAccessToken() {
178        global $conf;
179        try {
180            if ($this->oAuth->getStorage()->hasAccessToken($this->oAuth->service())) {
181                $this->oAuth->refreshAccessToken($this->oAuth->getStorage()->retrieveAccessToken($this->oAuth->service()));
182                return true;
183            }
184        } catch (TokenNotFoundException | TokenResponseException $e) {
185            msg($e->getMessage(), -1);
186            if ($conf['allowdebug']) {
187                msg('<pre>' . hsc($e->getTraceAsString()) . '</pre>', -1);
188            }
189        }
190        return false;
191    }
192
193    /**
194     * Return the name of the oAuth service class to use
195     *
196     * This should match with one of the files in
197     * phpoauth/src/oAuth/oAuth[12]/Service/*
198     *
199     * By default extracts the name from the class name
200     *
201     * @return string
202     */
203    public function getServiceName() {
204        return $this->getAdapterName();
205    }
206
207    /**
208     * Return the name of this Adapter
209     *
210     * It specifies which configuration setting should be used
211     *
212     * @return string
213     */
214    public function getAdapterName() {
215        $name = preg_replace('/Adapter$/', '', get_called_class());
216        $name = str_replace('OAuth\\Plugin\\', '', $name);
217        return $name;
218    }
219
220    /**
221     * Return the scope to request
222     *
223     * This should return the minimal scope needed for accessing the user's data
224     *
225     * @return array
226     */
227    public function getScope() {
228        return array();
229    }
230
231    /**
232     * Retrieve the user's data
233     *
234     * The array needs to contain at least 'email', 'name', 'user', 'grps'
235     *
236     * @return array
237     */
238    abstract public function getUser();
239}
240