Passed
Push — master ( 69aa49...a72ce3 )
by Stefan
11:41
created

API::firstParameterInstance()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 3
nop 2
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * *****************************************************************************
5
 * Contributions to this work were made on behalf of the GÉANT project, a 
6
 * project that has received funding from the European Union’s Framework 
7
 * Programme 7 under Grant Agreements No. 238875 (GN3) and No. 605243 (GN3plus),
8
 * Horizon 2020 research and innovation programme under Grant Agreements No. 
9
 * 691567 (GN4-1) and No. 731122 (GN4-2).
10
 * On behalf of the aforementioned projects, GEANT Association is the sole owner
11
 * of the copyright in all material which was developed by a member of the GÉANT
12
 * project. GÉANT Vereniging (Association) is registered with the Chamber of 
13
 * Commerce in Amsterdam with registration number 40535155 and operates in the 
14
 * UK as a branch of GÉANT Vereniging.
15
 * 
16
 * Registered office: Hoekenrode 3, 1102BR Amsterdam, The Netherlands. 
17
 * UK branch address: City House, 126-130 Hills Road, Cambridge CB2 1PQ, UK
18
 *
19
 * License: see the web/copyright.inc.php file in the file structure or
20
 *          <base_url>/copyright.php after deploying the software
21
 * 
22
 * @package AdminAPI
23
 * @author Stefan Winter <[email protected]>
24
 * @license https://github.com/GEANT/CAT/blob/master/web/copyright.inc.php GEANT Standard Open Source Software Outward Licence
25
 * @link API
26
 */
27
28
namespace web\lib\admin;
29
30
use Exception;
31
32
/**
33
 * This class defines the various actions doable with the admin API, the
34
 * parameters and return values.
35
 * 
36
 */
37
class API {
38
39
    /**
40
     * This error is returned if the API is globally disabled on a deployment.
41
     */
42
    const ERROR_API_DISABLED = 1;
43
44
    /**
45
     * This error is returned if the API request did not contain an API key.
46
     */
47
    const ERROR_NO_APIKEY = 2;
48
49
    /**
50
     * This error is returned if the API request contained an unknown API key.
51
     */
52
    const ERROR_INVALID_APIKEY = 3;
53
54
    /**
55
     * An action was requested, but one if its required parameters was missing.
56
     */
57
    const ERROR_MISSING_PARAMETER = 4;
58
59
    /**
60
     * An action was requested, but one if its required parameters was
61
     *  malformed.
62
     */
63
    const ERROR_INVALID_PARAMETER = 5;
64
65
    /**
66
     * The API request did not contain a requested action.
67
     */
68
    const ERROR_NO_ACTION = 6;
69
70
    /**
71
     * The action that was requested is not recognised.
72
     */
73
    const ERROR_INVALID_ACTION = 7;
74
75
    /**
76
     * The API request as a whole did not parse correctly.
77
     */
78
    const ERROR_MALFORMED_REQUEST = 8;
79
80
    /**
81
     * An internal error occured and the requested action could not be
82
     *  performed.
83
     */
84
    const ERROR_INTERNAL_ERROR = 9;
85
86
    /**
87
     * An action for a Silverbullet profile was requested, but the profile admin
88
     * has not accepted the Terms of Use for Silverbullet yet.
89
     * 
90
     * Note: Silverbullet is currently marketed as "eduroam Managed IdP" on the
91
     * eduroam deployment of the code base.
92
     */
93
    const ERROR_NO_TOU = 10;
94
95
    /**
96
     * This action creates a new institution. The institution is identified by
97
     * a reference to the external DB.
98
     */
99
    const ACTION_NEWINST_BY_REF = "NEWINST-BY-REF";
100
101
    /**
102
     * This action creates a new institution. The institution is created in the
103
     * system, all institution properties are set by including optional
104
     * parameters.
105
     */
106
    const ACTION_NEWINST = "NEWINST";
107
108
    /**
109
     * This action deletes an existing institution.
110
     */
111
    const ACTION_DELINST = "DELINST";
112
113
    /**
114
     * This action lists all administrators of an institution.
115
     */
116
    const ACTION_ADMIN_LIST = "ADMIN-LIST";
117
118
    /**
119
     * This action creates a new invitation token for administering an existing
120
     * institution. The invitation can be sent directly via mail, or the sign-up
121
     * token can be returned in the API response for the caller to hand it out.
122
     */
123
    const ACTION_ADMIN_ADD = "ADMIN-ADD";
124
125
    /**
126
     * This action de-authorises an existing administrator from administering an
127
     * institution. The institution is not deleted. If the administrator is also
128
     * managing other institutions, that is not changed.
129
     */
130
    const ACTION_ADMIN_DEL = "ADMIN-DEL";
131
132
    /**
133
     * This action retrieves download statistics for a given institution.
134
     */
135
    const ACTION_STATISTICS_INST = "STATISTICS-INST";
136
137
    /**
138
     * This action retrieves cumulated statistics for the entire federation.
139
     */
140
    const ACTION_STATISTICS_FED = "STATISTICS-FED";
141
142
    /**
143
     * This action creates a new RADIUS profile (i.e. a classic profile for
144
     * institutions with their own RADIUS server, delivering one installer for
145
     * this profile).
146
     */
147
    const ACTION_NEWPROF_RADIUS = "NEWPROF-RADIUS";
148
149
    /**
150
     * This action creates a new Managed IdP profile (i.e. a profile where all
151
     * RADIUS is handled by our system and the administrator merely needs to
152
     * provision users via a web interface).
153
     */
154
    const ACTION_NEWPROF_SB = "NEWPROF-MANAGED";
155
156
    /**
157
     * This action creates a new end-user within a Managed IdP profile.
158
     */
159
    const ACTION_ENDUSER_NEW = "ENDUSER-NEW";
160
161
    /**
162
     * This action deactivates an existing end user in a Managed IdP profile.
163
     */
164
    const ACTION_ENDUSER_DEACTIVATE = "ENDUSER-DEACTIVATE";
165
166
    /**
167
     * This action lists all end users in a given Managed IdP profile.
168
     */
169
    const ACTION_ENDUSER_LIST = "ENDUSER-LIST";
170
171
    /**
172
     * This action identifies a user account from either his user ID, username
173
     * or any of their certificate CNs.
174
     */
175
    const ACTION_ENDUSER_IDENTIFY = "ENDUSER-IDENTIFY";
176
177
    /**
178
     * This action creates a new end-user voucher for eduroam credential
179
     * installation.
180
     */
181
    const ACTION_TOKEN_NEW = "TOKEN-NEW";
182
183
    /**
184
     * This action cancels a currently valid end-user voucher. Existing redeemed
185
     * credentials based on that voucher remain valid.
186
     */
187
    const ACTION_TOKEN_REVOKE = "TOKEN-REVOKE";
188
189
    /**
190
     * This action lists all vouchers for a given end-user.
191
     */
192
    const ACTION_TOKEN_LIST = "TOKEN-LIST";
193
194
    /**
195
     * This action lists all client certificate credentials issued to a given
196
     * end user.
197
     */
198
    const ACTION_CERT_LIST = "CERT-LIST";
199
200
    /**
201
     * This action revokes a specific client cert.
202
     */
203
    const ACTION_CERT_REVOKE = "CERT-REVOKE";
204
205
    /**
206
     * This action adds internal notes regarding this certificate. These notes
207
     * are included when retrieving certificate information with 
208
     * ACTION_CERT_LIST but are not actively used for anything.
209
     */
210
    const ACTION_CERT_ANNOTATE = "CERT-ANNOTATE";
211
    const AUXATTRIB_ADMINID = "ATTRIB-ADMINID";
212
    const AUXATTRIB_TARGETMAIL = "ATTRIB-TARGETMAIL";
213
    const AUXATTRIB_TARGETSMS = "ATTRIB-TARGETSMS";
214
    const AUXATTRIB_EXTERNALID = "ATTRIB-EXTERNALID";
215
    const AUXATTRIB_CAT_INST_ID = "ATTRIB-CAT-INSTID";
216
    const AUXATTRIB_CAT_PROFILE_ID = "ATTRIB-CAT-PROFILEID";
217
    const AUXATTRIB_PROFILE_REALM = 'ATTRIB-PROFILE-REALM';
218
    const AUXATTRIB_PROFILE_OUTERVALUE = 'ATTRIB-PROFILE-OUTERVALUE';
219
    const AUXATTRIB_PROFILE_TESTUSER = 'ATTRIB-PROFILE-TESTUSER';
220
    const AUXATTRIB_PROFILE_INPUT_HINT = 'ATTRIB-PROFILE-HINTREALM';
221
    const AUXATTRIB_PROFILE_INPUT_VERIFY = 'ATTRIB-PROFILE-VERIFYREALM';
222
    const AUXATTRIB_PROFILE_EAPTYPE = "ATTRIB-PROFILE-EAPTYPE";
223
    const AUXATTRIB_SB_TOU = "ATTRIB-MANAGED-TOU";
224
    const AUXATTRIB_SB_USERNAME = "ATTRIB-MANAGED-USERNAME";
225
    const AUXATTRIB_SB_USERID = "ATTRIB-MANAGED-USERID";
226
    const AUXATTRIB_SB_CERTSERIAL = "ATTRIB-MANAGED-CERTSERIAL";
227
    const AUXATTRIB_SB_CERTANNOTATION = "ATTRIB-MANAGED-CERTANNOTATION";
228
    const AUXATTRIB_SB_EXPIRY = "ATTRIB-MANAGED-EXPIRY"; /* MySQL timestamp format */
229
    const AUXATTRIB_TOKEN = "ATTRIB-TOKEN";
230
    const AUXATTRIB_TOKENURL = "ATTRIB-TOKENURL";
231
    const AUXATTRIB_TOKEN_ACTIVATIONS = "ATTRIB-TOKEN-ACTIVATIONS";
232
    const AUXATTRIB_INSTTYPE = "ATTRIB-INSTITUTION-TYPE";
233
234
    /*
235
     * ACTIONS consists of a list of keywords, and associated REQuired and OPTional parameters
236
     * 
237
     */
238
    const ACTIONS = [
239
        // Inst-level actions.
240
        API::ACTION_NEWINST_BY_REF => [
241
            "REQ" => [API::AUXATTRIB_EXTERNALID,],
242
            "OPT" => [
243
                'general:geo_coordinates',
244
                'general:logo_file',
245
                'media:SSID',
246
                'media:SSID_with_legacy',
247
                'media:wired',
248
                'media:remove_SSID',
249
                'media:consortium_OI',
250
                'media:force_proxy',
251
                'support:email',
252
                'support:info_file',
253
                'support:phone',
254
                'support:url'
255
            ],
256
        ],
257
        API::ACTION_NEWINST => [
258
            "REQ" => [API::AUXATTRIB_INSTTYPE,], // "IdP", "SP" or "IdPSP"
259
            "OPT" => [
260
                'general:instname',
261
                'general:geo_coordinates',
262
                'general:logo_file',
263
                'media:SSID',
264
                'media:SSID_with_legacy',
265
                'media:wired',
266
                'media:remove_SSID',
267
                'media:consortium_OI',
268
                'media:force_proxy',
269
                'support:email',
270
                'support:info_file',
271
                'support:phone',
272
                'support:url'
273
            ],
274
            "RETVAL" => [
275
                API::AUXATTRIB_CAT_INST_ID, // New inst ID.
276
            ],
277
        ],
278
        API::ACTION_DELINST => [
279
            "REQ" => [API::AUXATTRIB_CAT_INST_ID],
280
            "OPT" => [],
281
            "RETVAL" => [],
282
        ],
283
        // Inst administrator management.
284
        API::ACTION_ADMIN_LIST => [
285
            "REQ" => [API::AUXATTRIB_CAT_INST_ID],
286
            "OPT" => [
287
            ],
288
            "RETVAL" => [
289
                ["ID", "MAIL", "LEVEL"] // Array with all admins of inst.
290
            ]
291
        ],
292
        API::ACTION_ADMIN_ADD => [
293
            "REQ" => [
294
                API::AUXATTRIB_ADMINID,
295
                API::AUXATTRIB_CAT_INST_ID
296
            ],
297
            "OPT" => [API::AUXATTRIB_TARGETMAIL],
298
            "RETVAL" => [
299
                ["TOKEN URL",
300
                    "EMAIL SENT", // Dependent on TARGETMAIL input.
301
                    "EMAIL TRANSPORT SECURE"], // Dependent on TARGETMAIL input.
302
            ]
303
        ],
304
        API::ACTION_ADMIN_DEL => [
305
            "REQ" => [
306
                API::AUXATTRIB_ADMINID,
307
                API::AUXATTRIB_CAT_INST_ID
308
            ],
309
            "OPT" => [],
310
            "RETVAL" => [],
311
        ],
312
        // Statistics.
313
        API::ACTION_STATISTICS_INST => [
314
            "REQ" => [API::AUXATTRIB_CAT_INST_ID],
315
            "OPT" => []
316
        ],
317
        API::ACTION_STATISTICS_FED => [
318
            "REQ" => [],
319
            "OPT" => [],
320
            "RETVAL" => [
321
                ["device_id" => ["ADMIN", "SILVERBULLET", "USER"]] // Plus "TOTAL".
322
            ],
323
        ],
324
        // RADIUS profile actions.
325
        API::ACTION_NEWPROF_RADIUS => [
326
            "REQ" => [API::AUXATTRIB_CAT_INST_ID],
327
            "OPT" => [
328
                'eap:ca_file',
329
                'eap:server_name',
330
                'media:SSID',
331
                'media:SSID_with_legacy',
332
                'media:wired',
333
                'media:remove_SSID',
334
                'media:consortium_OI',
335
                'media:force_proxy',
336
                'profile:name',
337
                'profile:customsuffix',
338
                'profile:description',
339
                'profile:production',
340
                'support:email',
341
                'support:info_file',
342
                'support:phone',
343
                'support:url',
344
                API::AUXATTRIB_PROFILE_INPUT_HINT,
345
                API::AUXATTRIB_PROFILE_INPUT_VERIFY,
346
                API::AUXATTRIB_PROFILE_OUTERVALUE,
347
                API::AUXATTRIB_PROFILE_REALM,
348
                API::AUXATTRIB_PROFILE_TESTUSER,
349
                API::AUXATTRIB_PROFILE_EAPTYPE,
350
            ],
351
            "RETVAL" => API::AUXATTRIB_CAT_PROFILE_ID,
352
        ],
353
        // Silverbullet profile actions.
354
        API::ACTION_NEWPROF_SB => [
355
            "REQ" => [API::AUXATTRIB_CAT_INST_ID],
356
            "OPT" => [API::AUXATTRIB_SB_TOU],
357
            "RETVAL" => API::AUXATTRIB_CAT_PROFILE_ID,
358
        ],
359
        API::ACTION_ENDUSER_NEW => [
360
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_USERNAME, API::AUXATTRIB_SB_EXPIRY],
361
            "OPT" => [],
362
            "RETVAL" => [API::AUXATTRIB_SB_USERNAME, API::AUXATTRIB_SB_USERID],
363
        ],
364
        API::ACTION_ENDUSER_DEACTIVATE => [
365
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_USERID],
366
            "OPT" => [],
367
            "RETVAL" => [],
368
        ],
369
        API::ACTION_ENDUSER_LIST => [
370
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID],
371
            "OPT" => [],
372
            "RETVAL" => [
373
                [API::AUXATTRIB_SB_USERID => API::AUXATTRIB_SB_USERNAME],
374
            ],
375
        ],
376
        API::ACTION_ENDUSER_IDENTIFY => [
377
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID],
378
            "OPT" => [API::AUXATTRIB_SB_USERID, API::AUXATTRIB_SB_USERNAME, API::AUXATTRIB_SB_CERTSERIAL],
379
            "RETVAL" => [
380
                [API::AUXATTRIB_SB_USERID => API::AUXATTRIB_SB_USERNAME],
381
            ],
382
        ],
383
        API::ACTION_TOKEN_NEW => [
384
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_USERID],
385
            "OPT" => [API::AUXATTRIB_TOKEN_ACTIVATIONS, API::AUXATTRIB_TARGETMAIL, API::AUXATTRIB_TARGETSMS],
386
            "RETVAL" => [
387
                API::AUXATTRIB_TOKENURL,
388
                API::AUXATTRIB_TOKEN,
389
                "EMAIL SENT", // Dependent on TARGETMAIL input.
390
                "EMAIL TRANSPORT SECURE", // Dependent on TARGETMAIL input.
391
                "SMS SENT", // Dependent on TARGETSMS input.
392
            ]
393
        ],
394
        API::ACTION_TOKEN_REVOKE => [
395
            "REQ" => [API::AUXATTRIB_TOKEN],
396
            "OPT" => [],
397
            "RETVAL" => [],
398
        ],
399
        API::ACTION_TOKEN_LIST => [
400
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID],
401
            "OPT" => [API::AUXATTRIB_SB_USERID],
402
            "RETVAL" => [
403
                [API::AUXATTRIB_SB_USERID => [API::AUXATTRIB_TOKEN, "STATUS"]],
404
            ]
405
        ],
406
        API::ACTION_CERT_LIST => [
407
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_USERID],
408
            "OPT" => [],
409
            "RETVAL" => [
410
                [API::AUXATTRIB_SB_CERTSERIAL => ["ISSUED", "EXPIRY", "STATUS", "DEVICE", "CN"]]
411
            ]
412
        ],
413
        API::ACTION_CERT_REVOKE => [
414
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_CERTSERIAL],
415
            "OPT" => [],
416
            "RETVAL" => [],
417
        ],
418
        API::ACTION_CERT_ANNOTATE => [
419
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_CERTSERIAL, API::AUXATTRIB_SB_CERTANNOTATION],
420
            "OPT" => [],
421
            "RETVAL" => [],
422
        ]
423
    ];
424
425
    /**
426
     *
427
     * @var \web\lib\common\InputValidation
428
     */
429
    private $validator;
430
431
    /**
432
     * construct the API class
433
     */
434
    public function __construct() {
435
        $this->validator = new \web\lib\common\InputValidation();
436
    }
437
438
    /**
439
     * Only leave attributes in the request which are related to the ACTION.
440
     * Also sanitise by enforcing LANG attribute in multi-lang attributes.
441
     * 
442
     * @param array            $inputJson the incoming JSON request
443
     * @param \core\Federation $fedObject the federation the user is acting within
444
     * @return array the scrubbed attributes
445
     */
446
    public function scrub($inputJson, $fedObject) {
447
        $optionInstance = \core\Options::instance();
448
        $parameters = [];
449
        $allPossibleAttribs = array_merge(API::ACTIONS[$inputJson['ACTION']]['REQ'], API::ACTIONS[$inputJson['ACTION']]['OPT']);
450
        // some actions don't need parameters. Don't get excited when there aren't any.
451
        if (!isset($inputJson['PARAMETERS'])) {
452
            $inputJson['PARAMETERS'] = [];
453
        }
454
        foreach ($inputJson['PARAMETERS'] as $number => $oneIncomingParam) {
455
            // index has to be an integer
456
            if (!is_int($number)) {
457
                continue;
458
            }
459
            // do we actually have a value?
460
            if (!array_key_exists("VALUE", $oneIncomingParam)) {
461
                continue;
462
            }
463
            // is this multi-lingual, and not an AUX attrib? Then check for presence of LANG and CONTENT before considering to add
464
            if (!preg_match("/^ATTRIB-/", $oneIncomingParam['NAME'])) {
465
                $optionProperties = $optionInstance->optionType($oneIncomingParam['NAME']);
466
                if ($optionProperties["flag"] == "ML" && !array_key_exists("LANG", $oneIncomingParam)) {
467
                    continue;
468
                }
469
            } else { // sanitise the AUX attr 
470
                switch ($oneIncomingParam['NAME']) {
471
                    case API::AUXATTRIB_CAT_INST_ID:
472
                        try {
473
                            $inst = $this->validator->existingIdP($oneIncomingParam['VALUE']);
474
                        } catch (Exception $e) {
475
                            continue 2;
476
                        }
477
                        if (strtoupper($inst->federation) != strtoupper($fedObject->tld)) {
478
                            // IdP in different fed, scrub it.
479
                            continue 2;
480
                        }
481
                        break;
482
                    case API::AUXATTRIB_TARGETMAIL:
483
                        if ($this->validator->email($oneIncomingParam['VALUE']) === FALSE) {
484
                            continue 2;
485
                        }
486
                        break;
487
                    case API::AUXATTRIB_ADMINID:
488
                        try {
489
                            $oneIncomingParam['VALUE'] = $this->validator->string($oneIncomingParam['VALUE']);
490
                        } catch (Exception $e) {
491
                            continue 2;
492
                        }
493
                        break;
494
                    default:
495
                        break;
496
                }
497
            }
498
            if (in_array($oneIncomingParam['NAME'], $allPossibleAttribs)) {
499
                $parameters[$number] = $oneIncomingParam;
500
            }
501
        }
502
        return $parameters;
503
    }
504
505
    /**
506
     * extracts the first occurence of a given parameter name from the set of inputs
507
     * 
508
     * @param array  $inputs   incoming set of arrays
509
     * @param string $expected attribute that is to be extracted
510
     * @return mixed the value, or FALSE if none was found
511
     */
512
    public function firstParameterInstance($inputs, $expected) {
513
        foreach ($inputs as $attrib) {
514
            if ($attrib['NAME'] == $expected) {
515
                return $attrib['VALUE'];
516
            }
517
        }
518
        return FALSE;
519
    }
520
521
    /**
522
     * we are coercing the submitted JSON-style parameters into the same format
523
     * we use for the HTML POST user-interactively.
524
     * That's ugly, hence the function name.
525
     * 
526
     * @param array $parameters the parameters as provided by JSON input
527
     * @return array
528
     * @throws Exception
529
     */
530
    public function uglify($parameters) {
531
        $coercedInline = [];
532
        $coercedFile = [];
533
        $optionObject = \core\Options::instance();
534
        $dir = \core\common\Entity::createTemporaryDirectory('test');
535
        foreach ($parameters as $number => $oneAttrib) {
536
            if (preg_match("/^ATTRIB-/", $oneAttrib['NAME'])) {
537
                continue;
538
            }
539
            $optionInfo = $optionObject->optionType($oneAttrib['NAME']);
540
            $basename = "S$number";
541
            $extension = "";
542
            switch ($optionInfo['type']) {
543
544
                case \core\Options::TYPECODE_COORDINATES:
545
                    $extension = \core\Options::TYPECODE_TEXT;
546
                    $coercedInline["option"][$basename] = $oneAttrib['NAME'] . "#";
547
                    $coercedInline["value"][$basename . "-" . $extension] = $oneAttrib['VALUE'];
548
                    break;
549
                case \core\Options::TYPECODE_TEXT:
550
                // Fall-through: they all get the same treatment.
551
                case \core\Options::TYPECODE_BOOLEAN:
552
                // Fall-through: they all get the same treatment.
553
                case \core\Options::TYPECODE_STRING:
554
                // Fall-through: they all get the same treatment.
555
                case \core\Options::TYPECODE_INTEGER:
556
                    $extension = $optionInfo['type'];
557
                    $coercedInline["option"][$basename] = $oneAttrib['NAME'] . "#";
558
                    $coercedInline["value"][$basename . "-" . $extension] = $oneAttrib['VALUE'];
559
                    if ($optionInfo['flag'] == "ML") {
560
                        $coercedInline["value"][$basename . "-lang"] = $oneAttrib['LANG'];
561
                    }
562
                    break;
563
                case \core\Options::TYPECODE_FILE:
564
                    // Binary data is expected in base64 encoding. This is true also for PEM files!
565
                    $extension = $optionInfo['type'];
566
                    $coercedInline["option"][$basename] = $oneAttrib['NAME'] . "#";
567
                    file_put_contents($dir['dir'] . "/" . $basename . "-" . $extension, base64_decode($oneAttrib['VALUE']));
568
                    $coercedFile["value"]['tmp_name'][$basename . "-" . $extension] = $dir['dir'] . "/" . $basename . "-" . $extension;
569
                    break;
570
                default:
571
                    throw new Exception("We don't seem to know this type code!");
572
            }
573
        }
574
        return ["POST" => $coercedInline, "FILES" => $coercedFile];
575
    }
576
577
    /**
578
     * Returns a JSON construct detailing the error that happened
579
     * 
580
     * @param int    $code        error code to return
581
     * @param string $description textual description to return
582
     * @return string
583
     */
584
    public function returnError($code, $description) {
585
        echo json_encode(["result" => "ERROR", "details" => ["errorcode" => $code, "description" => $description]], JSON_PRETTY_PRINT);
586
    }
587
588
    /**
589
     * Returns a JSON construct with details of the successful API call
590
     * 
591
     * @param array $details details to return with the SUCCESS
592
     * @return string
593
     */
594
    public function returnSuccess($details) {
595
        echo json_encode(["result" => "SUCCESS", "details" => $details], JSON_PRETTY_PRINT);
596
    }
597
598
    /**
599
     * Checks if the profile is a valid SB profile belonging to the federation,
600
     * and fulfills all the prerequisites for being manipulated over API
601
     * 
602
     * @param \core\Federation $fed federation identifier
603
     * @param integer          $id  profile identifier
604
     * @return boolean|array
605
     */
606
    public function commonSbProfileChecks($fed, $id) {
607
        $validator = new \web\lib\common\InputValidation();
608
        $adminApi = new \web\lib\admin\API();
609
        try {
610
            $profile = $validator->existingProfile($id);
611
        } catch (Exception $e) {
612
            $adminApi->returnError(web\lib\admin\API::ERROR_INVALID_PARAMETER, "Profile identifier does not exist!");
0 ignored issues
show
Bug introduced by
The type web\lib\admin\web\lib\admin\API was not found. Did you mean web\lib\admin\API? If so, make sure to prefix the type with \.
Loading history...
613
            return FALSE;
614
        }
615
        if (!$profile instanceof core\ProfileSilverbullet) {
0 ignored issues
show
Bug introduced by
The type web\lib\admin\core\ProfileSilverbullet was not found. Did you mean core\ProfileSilverbullet? If so, make sure to prefix the type with \.
Loading history...
616
            $adminApi->returnError(web\lib\admin\API::ERROR_INVALID_PARAMETER, sprintf("Profile identifier is not %s!", \core\ProfileSilverbullet::PRODUCTNAME));
617
            return FALSE;
618
        }
619
        $idp = new \core\IdP($profile->institution);
620
        if (strtoupper($idp->federation) != strtoupper($fed->tld)) {
621
            $adminApi->returnError(web\lib\admin\API::ERROR_INVALID_PARAMETER, "Profile is not in the federation for this APIKEY!");
622
            return FALSE;
623
        }
624
        if (count($profile->getAttributes("hiddenprofile:tou_accepted")) < 1) {
625
            $adminApi->returnError(web\lib\admin\API::ERROR_NO_TOU, "The terms of use have not yet been accepted for this profile!");
626
            return FALSE;
627
        }
628
        return [$idp, $profile];
629
    }
630
631
}
632