Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * Represent the url for each method and the encoding of the parameters and response.
19
 *
20
 * @package    core_badges
21
 * @copyright  2012 onwards Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 * @author     Yuliya Bozhko <yuliya.bozhko@totaralms.com>
24
 */
25
 
26
namespace core_badges;
27
 
28
defined('MOODLE_INTERNAL') || die();
29
 
30
global $CFG;
31
require_once($CFG->libdir . '/filelib.php');
32
 
33
use context_system;
34
use core_badges\external\assertion_exporter;
35
use core_badges\external\collection_exporter;
36
use core_badges\external\issuer_exporter;
37
use core_badges\external\badgeclass_exporter;
38
use curl;
39
 
40
/**
41
 * Represent a single method for the remote api.
42
 *
43
 * @package    core_badges
44
 * @copyright  2012 onwards Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
45
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
46
 */
47
class backpack_api_mapping {
48
 
49
    /** @var string The action of this method. */
50
    public $action;
51
 
52
    /** @var string The base url of this backpack. */
53
    private $url;
54
 
55
    /** @var array List of parameters for this method. */
56
    public $params;
57
 
58
    /** @var string Name of a class to export parameters for this method. */
59
    public $requestexporter;
60
 
61
    /** @var string Name of a class to export response for this method. */
62
    public $responseexporter;
63
 
64
    /** @var boolean This method returns an array of responses. */
65
    public $multiple;
66
 
67
    /** @var string get or post methods. */
68
    public $method;
69
 
70
    /** @var boolean json decode the response. */
71
    public $json;
72
 
73
    /** @var boolean Authentication is required for this request. */
74
    public $authrequired;
75
 
76
    /** @var boolean Differentiate the function that can be called on a user backpack or a site backpack. */
77
    private $isuserbackpack;
78
 
79
    /** @var string Error string from authentication request. */
80
    private static $authenticationerror = '';
81
 
82
    /** @var mixed List of parameters for this method. */
83
    protected $postparams;
84
 
85
    /** @var int OpenBadges version 1 or 2. */
86
    protected $backpackapiversion;
87
 
88
    /**
89
     * Create a mapping.
90
     *
91
     * @param string $action The action of this method.
92
     * @param string $url The base url of this backpack.
93
     * @param mixed $postparams List of parameters for this method.
94
     * @param string $requestexporter Name of a class to export parameters for this method.
95
     * @param string $responseexporter Name of a class to export response for this method.
96
     * @param boolean $multiple This method returns an array of responses.
97
     * @param string $method get or post methods.
98
     * @param boolean $json json decode the response.
99
     * @param boolean $authrequired Authentication is required for this request.
100
     * @param boolean $isuserbackpack user backpack or a site backpack.
101
     * @param integer $backpackapiversion OpenBadges version 1 or 2.
102
     */
103
    public function __construct($action, $url, $postparams, $requestexporter, $responseexporter,
104
                                $multiple, $method, $json, $authrequired, $isuserbackpack, $backpackapiversion) {
105
        $this->action = $action;
106
        $this->url = $url;
107
        $this->postparams = $postparams;
108
        $this->requestexporter = $requestexporter;
109
        $this->responseexporter = $responseexporter;
110
        $this->multiple = $multiple;
111
        $this->method = $method;
112
        $this->json = $json;
113
        $this->authrequired = $authrequired;
114
        $this->isuserbackpack = $isuserbackpack;
115
        $this->backpackapiversion = $backpackapiversion;
116
    }
117
 
118
    /**
119
     * Get the unique key for the token.
120
     *
121
     * @param string $type The type of token.
122
     * @return string
123
     */
124
    private function get_token_key($type) {
125
        $prefix = 'badges_';
126
        if ($this->isuserbackpack) {
127
            $prefix .= 'user_backpack_';
128
        } else {
129
            $prefix .= 'site_backpack_';
130
        }
131
        $prefix .= $type . '_token';
132
        return $prefix;
133
    }
134
 
135
    /**
136
     * Remember the error message in a static variable.
137
     *
138
     * @param string $msg The message.
139
     */
140
    public static function set_authentication_error($msg) {
141
        self::$authenticationerror = $msg;
142
    }
143
 
144
    /**
145
     * Get the last authentication error in this request.
146
     *
147
     * @return string
148
     */
149
    public static function get_authentication_error() {
150
        return self::$authenticationerror;
151
    }
152
 
153
    /**
154
     * Does the action match this mapping?
155
     *
156
     * @param string $action The action.
157
     * @return boolean
158
     */
159
    public function is_match($action) {
160
        return $this->action == $action;
161
    }
162
 
163
    /**
164
     * Parse the method url and insert parameters.
165
     *
166
     * @param string $apiurl The raw apiurl.
167
     * @param string $param1 The first parameter.
168
     * @param string $param2 The second parameter.
169
     * @return string
170
     */
171
    private function get_url($apiurl, $param1, $param2) {
172
        $urlscheme = parse_url($apiurl, PHP_URL_SCHEME);
173
        $urlhost = parse_url($apiurl, PHP_URL_HOST);
174
 
175
        $url = $this->url;
176
        $url = str_replace('[SCHEME]', $urlscheme, $url);
177
        $url = str_replace('[HOST]', $urlhost, $url);
178
        $url = str_replace('[URL]', $apiurl, $url);
179
        $url = str_replace('[PARAM1]', $param1 ?? '', $url);
180
        $url = str_replace('[PARAM2]', $param2 ?? '', $url);
181
 
182
        return $url;
183
    }
184
 
185
    /**
186
     * Parse the post parameters and insert replacements.
187
     *
188
     * @param string $email The api username.
189
     * @param string $password The api password.
190
     * @param string $param The parameter.
191
     * @return mixed
192
     */
193
    private function get_post_params($email, $password, $param) {
194
        global $PAGE;
195
 
196
        if ($this->method == 'get') {
197
            return '';
198
        }
199
 
200
        $request = $this->postparams;
201
        if ($request === '[PARAM]') {
202
            $value = $param;
203
            foreach ($value as $key => $keyvalue) {
204
                if (gettype($value[$key]) == 'array') {
205
                    $newkey = 'related_' . $key;
206
                    $value[$newkey] = $value[$key];
207
                    unset($value[$key]);
208
                }
209
            }
210
        } else if (is_array($request)) {
211
            foreach ($request as $key => $value) {
212
                if ($value == '[EMAIL]') {
213
                    $value = $email;
214
                    $request[$key] = $value;
215
                } else if ($value == '[PASSWORD]') {
216
                    $value = $password;
217
                    $request[$key] = $value;
218
                } else if ($value == '[PARAM]') {
219
                    $request[$key] = is_array($param) ? $param[0] : $param;
220
                }
221
            }
222
        }
223
        $context = context_system::instance();
224
        $exporter = $this->requestexporter;
225
        $output = $PAGE->get_renderer('core', 'badges');
226
        if (!empty($exporter)) {
227
            $exporterinstance = new $exporter($value, ['context' => $context]);
228
            $request = $exporterinstance->export($output);
229
        }
230
        if ($this->json) {
231
            return json_encode($request);
232
        }
233
        return $request;
234
    }
235
 
236
    /**
237
     * Read the response from a V1 user request and save the userID.
238
     *
239
     * @param string $response The request response.
240
     * @param integer $backpackid The backpack id.
241
     * @return mixed
242
     */
243
    private function convert_email_response($response, $backpackid) {
244
        global $SESSION;
245
 
246
        if (isset($response->status) && $response->status == 'okay') {
247
 
248
            // Remember the tokens.
249
            $useridkey = $this->get_token_key(BADGE_USER_ID_TOKEN);
250
            $backpackidkey = $this->get_token_key(BADGE_BACKPACK_ID_TOKEN);
251
 
252
            $SESSION->$useridkey = $response->userId;
253
            $SESSION->$backpackidkey = $backpackid;
254
            return $response->userId;
255
        }
256
        if (!empty($response->error)) {
257
            self::set_authentication_error($response->error);
258
        }
259
        return false;
260
    }
261
 
262
    /**
263
     * Get the user id from a previous user request.
264
     *
265
     * @return integer
266
     */
267
    private function get_auth_user_id() {
268
        global $USER;
269
 
270
        if ($this->isuserbackpack) {
271
            return $USER->id;
272
        } else {
273
            // The access tokens for the system backpack are shared.
274
            return -1;
275
        }
276
    }
277
 
278
    /**
279
     * Parse the response from an openbadges 2 login.
280
     *
281
     * @param string $response The request response data.
282
     * @param integer $backpackid The id of the backpack.
283
     * @return mixed
284
     */
285
    private function oauth_token_response($response, $backpackid) {
286
        global $SESSION;
287
 
288
        if (isset($response->access_token) && isset($response->refresh_token)) {
289
            // Remember the tokens.
290
            $accesskey = $this->get_token_key(BADGE_ACCESS_TOKEN);
291
            $refreshkey = $this->get_token_key(BADGE_REFRESH_TOKEN);
292
            $expireskey = $this->get_token_key(BADGE_EXPIRES_TOKEN);
293
            $useridkey = $this->get_token_key(BADGE_USER_ID_TOKEN);
294
            $backpackidkey = $this->get_token_key(BADGE_BACKPACK_ID_TOKEN);
295
            if (isset($response->expires_in)) {
296
                $timeout = $response->expires_in;
297
            } else {
298
                $timeout = 15 * 60; // 15 minute timeout if none set.
299
            }
300
            $expires = $timeout + time();
301
 
302
            $SESSION->$expireskey = $expires;
303
            $SESSION->$useridkey = $this->get_auth_user_id();
304
            $SESSION->$accesskey = $response->access_token;
305
            $SESSION->$refreshkey = $response->refresh_token;
306
            $SESSION->$backpackidkey = $backpackid;
307
            return -1;
308
        } else if (isset($response->error_description)) {
309
            self::set_authentication_error($response->error_description);
310
        }
311
        return $response;
312
    }
313
 
314
    /**
315
     * Standard options used for all curl requests.
316
     *
317
     * @return array
318
     */
319
    private function get_curl_options() {
320
        return array(
321
            'FRESH_CONNECT'     => true,
322
            'RETURNTRANSFER'    => true,
323
            'FOLLOWLOCATION'    => true,
324
            'FORBID_REUSE'      => true,
325
            'HEADER'            => 0,
326
            'CONNECTTIMEOUT'    => 3,
327
            'CONNECTTIMEOUT'    => 3,
328
            // Follow redirects with the same type of request when sent 301, or 302 redirects.
329
            'CURLOPT_POSTREDIR' => 3,
330
        );
331
    }
332
 
333
    /**
334
     * Make an api request and parse the response.
335
     *
336
     * @param string $apiurl Raw request url.
337
     * @param string $urlparam1 Parameter for the request.
338
     * @param string $urlparam2 Parameter for the request.
339
     * @param string $email User email for authentication.
340
     * @param string $password for authentication.
341
     * @param mixed $postparam Raw data for the post body.
342
     * @param string $backpackid the id of the backpack to use.
343
     * @return mixed
344
     */
345
    public function request($apiurl, $urlparam1, $urlparam2, $email, $password, $postparam, $backpackid) {
346
        global $SESSION, $PAGE;
347
 
348
        $curl = new curl();
349
 
350
        $url = $this->get_url($apiurl, $urlparam1, $urlparam2);
351
 
352
        if ($this->authrequired) {
353
            $accesskey = $this->get_token_key(BADGE_ACCESS_TOKEN);
354
            if (isset($SESSION->$accesskey)) {
355
                $token = $SESSION->$accesskey;
356
                $curl->setHeader('Authorization: Bearer ' . $token);
357
            }
358
        }
359
        if ($this->json) {
360
            $curl->setHeader(array('Content-type: application/json'));
361
        }
362
        $curl->setHeader(array('Accept: application/json', 'Expect:'));
363
        $options = $this->get_curl_options();
364
 
365
        $post = $this->get_post_params($email, $password, $postparam);
366
 
367
        if ($this->method == 'get') {
368
            $response = $curl->get($url, $post, $options);
369
        } else if ($this->method == 'post') {
370
            $response = $curl->post($url, $post, $options);
371
        } else if ($this->method == 'put') {
372
            $response = $curl->put($url, $post, $options);
373
        }
374
        $response = json_decode($response);
375
        if (isset($response->result)) {
376
            $response = $response->result;
377
        }
378
        $context = context_system::instance();
379
        $exporter = $this->responseexporter;
380
        if (class_exists($exporter)) {
381
            $output = $PAGE->get_renderer('core', 'badges');
382
            if (!$this->multiple) {
383
                if (count($response)) {
384
                    $response = $response[0];
385
                }
386
                if (empty($response)) {
387
                    return null;
388
                }
389
                $apidata = $exporter::map_external_data($response, $this->backpackapiversion);
390
                $exporterinstance = new $exporter($apidata, ['context' => $context]);
391
                $data = $exporterinstance->export($output);
392
                return $data;
393
            } else {
394
                $multiple = [];
395
                if (empty($response)) {
396
                    return $multiple;
397
                }
398
                foreach ($response as $data) {
399
                    $apidata = $exporter::map_external_data($data, $this->backpackapiversion);
400
                    $exporterinstance = new $exporter($apidata, ['context' => $context]);
401
                    $multiple[] = $exporterinstance->export($output);
402
                }
403
                return $multiple;
404
            }
405
        } else if (method_exists($this, $exporter)) {
406
            return $this->$exporter($response, $backpackid);
407
        }
408
        return $response;
409
    }
410
}