Passed
Push — release_2_1 ( 584353...81e271 )
by Tomasz
27:15
created

API::scrub()   D

Complexity

Conditions 19
Paths 42

Size

Total Lines 61
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 42
c 1
b 0
f 1
dl 0
loc 61
rs 4.5166
cc 19
nc 42
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 occurred 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
     * Dumps all configured information about IdPs in the federation
144
     */
145
    const ACTION_FEDERATION_LISTIDP = "DATADUMP-FED";
146
147
    /**
148
     * This action creates a new RADIUS profile (i.e. a classic profile for
149
     * institutions with their own RADIUS server, delivering one installer for
150
     * this profile).
151
     */
152
    const ACTION_NEWPROF_RADIUS = "NEWPROF-RADIUS";
153
154
    /**
155
     * This action creates a new Managed IdP profile (i.e. a profile where all
156
     * RADIUS is handled by our system and the administrator merely needs to
157
     * provision users via a web interface).
158
     */
159
    const ACTION_NEWPROF_SB = "NEWPROF-MANAGED";
160
161
    /**
162
     * This action creates a new end-user within a Managed IdP profile.
163
     */
164
    const ACTION_ENDUSER_NEW = "ENDUSER-NEW";
165
166
    /**
167
     * This action changes the end user expiry date
168
     */
169
    const ACTION_ENDUSER_CHANGEEXPIRY = "ENDUSER-CHANGEEXPIRY";
170
171
    /**
172
     * This action deactivates an existing end user in a Managed IdP profile.
173
     */
174
    const ACTION_ENDUSER_DEACTIVATE = "ENDUSER-DEACTIVATE";
175
176
    /**
177
     * This action lists all end users in a given Managed IdP profile.
178
     */
179
    const ACTION_ENDUSER_LIST = "ENDUSER-LIST";
180
181
    /**
182
     * This action identifies a user account from either his user ID, username
183
     * or any of their certificate CNs.
184
     */
185
    const ACTION_ENDUSER_IDENTIFY = "ENDUSER-IDENTIFY";
186
187
    /**
188
     * This action creates a new end-user voucher for eduroam credential
189
     * installation.
190
     */
191
    const ACTION_TOKEN_NEW = "TOKEN-NEW";
192
193
    /**
194
     * This action cancels a currently valid end-user voucher. Existing redeemed
195
     * credentials based on that voucher remain valid.
196
     */
197
    const ACTION_TOKEN_REVOKE = "TOKEN-REVOKE";
198
199
    /**
200
     * This action lists all vouchers for a given end-user.
201
     */
202
    const ACTION_TOKEN_LIST = "TOKEN-LIST";
203
204
    /**
205
     * This action lists all client certificate credentials issued to a given
206
     * end user.
207
     */
208
    const ACTION_CERT_LIST = "CERT-LIST";
209
210
    /**
211
     * This action revokes a specific client cert.
212
     */
213
    const ACTION_CERT_REVOKE = "CERT-REVOKE";
214
215
    /**
216
     * This action adds internal notes regarding this certificate. These notes
217
     * are included when retrieving certificate information with 
218
     * ACTION_CERT_LIST but are not actively used for anything.
219
     */
220
    const ACTION_CERT_ANNOTATE = "CERT-ANNOTATE";
221
    const AUXATTRIB_ADMINID = "ATTRIB-ADMINID";
222
    const AUXATTRIB_TARGETMAIL = "ATTRIB-TARGETMAIL";
223
    const AUXATTRIB_TARGETSMS = "ATTRIB-TARGETSMS";
224
    const AUXATTRIB_EXTERNALID = "ATTRIB-EXTERNALID";
225
    const AUXATTRIB_CAT_INST_ID = "ATTRIB-CAT-INSTID";
226
    const AUXATTRIB_CAT_PROFILE_ID = "ATTRIB-CAT-PROFILEID";
227
    const AUXATTRIB_PROFILE_REALM = 'ATTRIB-PROFILE-REALM';
228
    const AUXATTRIB_PROFILE_OUTERVALUE = 'ATTRIB-PROFILE-OUTERVALUE';
229
    const AUXATTRIB_PROFILE_TESTUSER = 'ATTRIB-PROFILE-TESTUSER';
230
    const AUXATTRIB_PROFILE_INPUT_HINT = 'ATTRIB-PROFILE-HINTREALM';
231
    const AUXATTRIB_PROFILE_INPUT_VERIFY = 'ATTRIB-PROFILE-VERIFYREALM';
232
    const AUXATTRIB_PROFILE_EAPTYPE = "ATTRIB-PROFILE-EAPTYPE";
233
    const AUXATTRIB_SB_TOU = "ATTRIB-MANAGED-TOU";
234
    const AUXATTRIB_SB_USERNAME = "ATTRIB-MANAGED-USERNAME";
235
    const AUXATTRIB_SB_USERID = "ATTRIB-MANAGED-USERID";
236
    const AUXATTRIB_SB_CERTSERIAL = "ATTRIB-MANAGED-CERTSERIAL";
237
    const AUXATTRIB_SB_CERTCN = "ATTRIB-MANAGED-CERTCN";
238
    const AUXATTRIB_SB_CERTANNOTATION = "ATTRIB-MANAGED-CERTANNOTATION";
239
    const AUXATTRIB_SB_EXPIRY = "ATTRIB-MANAGED-EXPIRY"; /* MySQL timestamp format */
240
    const AUXATTRIB_TOKEN = "ATTRIB-TOKEN";
241
    const AUXATTRIB_TOKENURL = "ATTRIB-TOKENURL";
242
    const AUXATTRIB_TOKEN_ACTIVATIONS = "ATTRIB-TOKEN-ACTIVATIONS";
243
    const AUXATTRIB_INSTTYPE = "ATTRIB-INSTITUTION-TYPE";
244
    const AUXATTRIB_DETAIL = "ATTRIB-DETAIL";
245
    /**
246
     * This section defines allowed flags for actions
247
     */
248
    const FLAG_NOLOGO = "FLAG-NO-LOGO"; // skip logos in attribute listings
249
    const FLAG_ADD_STATS = "FLAG-ADD-STATS"; // add IdP statistice - only used in ACTION_FEDERATION_LISTIDP
250
    /*
251
     * ACTIONS consists of a list of keywords, and associated REQuired and OPTional parameters
252
     * 
253
     */
254
    const ACTIONS = [
255
        // Inst-level actions.
256
        API::ACTION_NEWINST_BY_REF => [
257
            "REQ" => [API::AUXATTRIB_EXTERNALID,],
258
            "OPT" => [
259
                'general:geo_coordinates',
260
                'general:logo_file',
261
                'media:SSID',
262
                'media:wired',
263
                'media:remove_SSID',
264
                'media:consortium_OI',
265
                'media:force_proxy',
266
                'support:email',
267
                'support:info_file',
268
                'support:phone',
269
                'support:url'
270
            ],
271
            "FLAG" => [],
272
        ],
273
        API::ACTION_NEWINST => [
274
            "REQ" => [API::AUXATTRIB_INSTTYPE,], // "IdP", "SP" or "IdPSP"
275
            "OPT" => [
276
                'general:instname',
277
                'general:geo_coordinates',
278
                'general:logo_file',
279
                'media:SSID',
280
                'media:wired',
281
                'media:remove_SSID',
282
                'media:consortium_OI',
283
                'media:force_proxy',
284
                'support:email',
285
                'support:info_file',
286
                'support:phone',
287
                'support:url'
288
            ],
289
            "FLAG" => [],
290
            "RETVAL" => [
291
                API::AUXATTRIB_CAT_INST_ID, // New inst ID.
292
            ],            
293
        ],
294
        API::ACTION_DELINST => [
295
            "REQ" => [API::AUXATTRIB_CAT_INST_ID],
296
            "OPT" => [],
297
            "FLAG" => [],
298
            "RETVAL" => [],
299
        ],
300
        // Inst administrator management.
301
        API::ACTION_ADMIN_LIST => [
302
            "REQ" => [API::AUXATTRIB_CAT_INST_ID],
303
            "OPT" => [],
304
            "FLAG" => [],
305
            "RETVAL" => [
306
                ["ID", "MAIL", "LEVEL"] // Array with all admins of inst.
307
            ]
308
        ],
309
        API::ACTION_ADMIN_ADD => [
310
            "REQ" => [
311
                API::AUXATTRIB_ADMINID,
312
                API::AUXATTRIB_CAT_INST_ID
313
            ],
314
            "OPT" => [API::AUXATTRIB_TARGETMAIL],
315
            "FLAG" => [],
316
            "RETVAL" => [
317
                ["TOKEN URL",
318
                    "EMAIL SENT", // Dependent on TARGETMAIL input.
319
                    "EMAIL TRANSPORT SECURE"], // Dependent on TARGETMAIL input.
320
            ]
321
        ],
322
        API::ACTION_ADMIN_DEL => [
323
            "REQ" => [
324
                API::AUXATTRIB_ADMINID,
325
                API::AUXATTRIB_CAT_INST_ID
326
            ],
327
            "OPT" => [],
328
            "FLAG" => [],
329
            "RETVAL" => [],
330
        ],
331
        // Statistics.
332
        API::ACTION_STATISTICS_INST => [
333
            "REQ" => [API::AUXATTRIB_CAT_INST_ID],
334
            "OPT" => [],
335
            "FLAG" => [],
336
        ],
337
        API::ACTION_STATISTICS_FED => [
338
            "REQ" => [],
339
            "OPT" => [API::AUXATTRIB_DETAIL],
340
            "FLAG" => [],
341
            "RETVAL" => [
342
                ["device_id" => ["ADMIN", "SILVERBULLET", "USER"]] // Plus "TOTAL".
343
            ],
344
        ],
345
        API::ACTION_FEDERATION_LISTIDP => [
346
            "REQ" => [],
347
            "OPT" => [API::AUXATTRIB_CAT_INST_ID, API::AUXATTRIB_DETAIL],
348
            "RETVAL" => [API::AUXATTRIB_CAT_INST_ID => "JSON_DATA"],
349
            "FLAG" => [API::FLAG_NOLOGO, API::FLAG_ADD_STATS],
350
        ],
351
        // RADIUS profile actions.
352
        API::ACTION_NEWPROF_RADIUS => [
353
            "REQ" => [API::AUXATTRIB_CAT_INST_ID],
354
            "OPT" => [
355
                'eap:ca_file',
356
                'eap:server_name',
357
                'media:SSID',
358
                'media:wired',
359
                'media:remove_SSID',
360
                'media:consortium_OI',
361
                'media:force_proxy',
362
                'profile:name',
363
                'profile:customsuffix',
364
                'profile:description',
365
                'profile:production',
366
                'support:email',
367
                'support:info_file',
368
                'support:phone',
369
                'support:url',
370
                'device-specific:redirect',
371
                API::AUXATTRIB_PROFILE_INPUT_HINT,
372
                API::AUXATTRIB_PROFILE_INPUT_VERIFY,
373
                API::AUXATTRIB_PROFILE_OUTERVALUE,
374
                API::AUXATTRIB_PROFILE_REALM,
375
                API::AUXATTRIB_PROFILE_TESTUSER,
376
                API::AUXATTRIB_PROFILE_EAPTYPE,
377
            ],
378
            "FLAG" => [],
379
            "RETVAL" => API::AUXATTRIB_CAT_PROFILE_ID,
380
        ],
381
        // Silverbullet profile actions.
382
        API::ACTION_NEWPROF_SB => [
383
            "REQ" => [API::AUXATTRIB_CAT_INST_ID],
384
            "OPT" => [API::AUXATTRIB_SB_TOU],
385
            "FLAG" => [],
386
            "RETVAL" => API::AUXATTRIB_CAT_PROFILE_ID,
387
        ],
388
        API::ACTION_ENDUSER_NEW => [
389
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_USERNAME, API::AUXATTRIB_SB_EXPIRY],
390
            "OPT" => [],
391
            "FLAG" => [],
392
            "RETVAL" => [API::AUXATTRIB_SB_USERNAME, API::AUXATTRIB_SB_USERID],
393
        ],
394
        API::ACTION_ENDUSER_CHANGEEXPIRY => [
395
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_USERNAME, API::AUXATTRIB_SB_EXPIRY],
396
            "OPT" => [],
397
            "FLAG" => [],
398
            "RETVAL" => [],
399
        ],
400
        API::ACTION_ENDUSER_DEACTIVATE => [
401
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_USERID],
402
            "OPT" => [],
403
            "FLAG" => [],
404
            "RETVAL" => [],
405
        ],
406
        API::ACTION_ENDUSER_LIST => [
407
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID],
408
            "OPT" => [],
409
            "FLAG" => [],
410
            "RETVAL" => [
411
                [API::AUXATTRIB_SB_USERID => API::AUXATTRIB_SB_USERNAME],
412
            ],
413
        ],
414
        API::ACTION_ENDUSER_IDENTIFY => [
415
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID],
416
            "OPT" => [API::AUXATTRIB_SB_USERID, API::AUXATTRIB_SB_USERNAME, API::AUXATTRIB_SB_CERTSERIAL, API::AUXATTRIB_SB_CERTCN],
417
            "FLAG" => [],
418
            "RETVAL" => [API::AUXATTRIB_SB_USERNAME, API::AUXATTRIB_SB_USERID],
419
        ],
420
        API::ACTION_TOKEN_NEW => [
421
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_USERID],
422
            "OPT" => [API::AUXATTRIB_TOKEN_ACTIVATIONS, API::AUXATTRIB_TARGETMAIL, API::AUXATTRIB_TARGETSMS],
423
            "FLAG" => [],
424
            "RETVAL" => [
425
                API::AUXATTRIB_TOKENURL,
426
                API::AUXATTRIB_TOKEN,
427
                "EMAIL SENT", // Dependent on TARGETMAIL input.
428
                "EMAIL TRANSPORT SECURE", // Dependent on TARGETMAIL input.
429
                "SMS SENT", // Dependent on TARGETSMS input.
430
            ]
431
        ],
432
        API::ACTION_TOKEN_REVOKE => [
433
            "REQ" => [API::AUXATTRIB_TOKEN],
434
            "OPT" => [],
435
            "FLAG" => [],
436
            "RETVAL" => [],
437
        ],
438
        API::ACTION_TOKEN_LIST => [
439
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID],
440
            "OPT" => [API::AUXATTRIB_SB_USERID],
441
            "FLAG" => [],
442
            "RETVAL" => [
443
                [API::AUXATTRIB_SB_USERID => [API::AUXATTRIB_TOKEN, "STATUS"]],
444
            ]
445
        ],
446
        API::ACTION_CERT_LIST => [
447
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_USERID],
448
            "OPT" => [],
449
            "FLAG" => [],
450
            "RETVAL" => [
451
                [API::AUXATTRIB_SB_CERTSERIAL => ["ISSUED", "EXPIRY", "STATUS", "DEVICE", "CN"]]
452
            ]
453
        ],
454
        API::ACTION_CERT_REVOKE => [
455
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_CERTSERIAL],
456
            "OPT" => [],
457
            "FLAG" => [],
458
            "RETVAL" => [],
459
        ],
460
        API::ACTION_CERT_ANNOTATE => [
461
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_CERTSERIAL, API::AUXATTRIB_SB_CERTANNOTATION],
462
            "OPT" => [],
463
            "FLAG" => [],
464
            "RETVAL" => [],
465
        ]
466
    ];
467
468
    /**
469
     *
470
     * @var \web\lib\common\InputValidation
471
     */
472
    private $validator;
473
474
    /**
475
     * construct the API class
476
     */
477
    public function __construct() {
478
        $this->validator = new \web\lib\common\InputValidation();
479
        $this->loggerInstance = new \core\common\Logging();
480
    }
481
482
    /**
483
     * Only leave attributes in the request which are related to the ACTION.
484
     * Also sanitise by enforcing LANG attribute in multi-lang attributes.
485
     * 
486
     * @param array            $inputJson the incoming JSON request
487
     * @param \core\Federation $fedObject the federation the user is acting within
488
     * @return array the scrubbed attributes
489
     */
490
    public function scrub($inputJson, $fedObject) {        
491
        $optionInstance = \core\Options::instance();
492
        $parameters = [];
493
        $allPossibleAttribs = array_merge(API::ACTIONS[$inputJson['ACTION']]['REQ'], API::ACTIONS[$inputJson['ACTION']]['OPT'],  API::ACTIONS[$inputJson['ACTION']]['FLAG']);
494
        // some actions don't need parameters. Don't get excited when there aren't any.
495
        if (!isset($inputJson['PARAMETERS'])) {
496
            $inputJson['PARAMETERS'] = [];
497
        }
498
        foreach ($inputJson['PARAMETERS'] as $number => $oneIncomingParam) {
499
            // index has to be an integer
500
            if (!is_int($number)) {
501
                continue;
502
            }
503
            // do we actually have a value?
504
            if (!array_key_exists("VALUE", $oneIncomingParam)) {
505
                continue;
506
            }
507
            if (preg_match("/^ATTRIB-/", $oneIncomingParam['NAME'])) {// sanitise the AUX attr 
508
                switch ($oneIncomingParam['NAME']) {
509
                    case API::AUXATTRIB_CAT_INST_ID:
510
                        try {
511
                            $inst = $this->validator->existingIdP($oneIncomingParam['VALUE']);
512
                        } catch (Exception $e) {
513
                            continue 2;
514
                        }
515
                        if (strtoupper($inst->federation) != strtoupper($fedObject->tld)) {
516
                            // IdP in different fed, scrub it.
517
                            continue 2;
518
                        }
519
                        break;
520
                    case API::AUXATTRIB_TARGETMAIL:
521
                        if ($this->validator->email($oneIncomingParam['VALUE']) === FALSE) {
522
                            continue 2;
523
                        }
524
                        break;
525
                    case API::AUXATTRIB_ADMINID:
526
                        try {
527
                            $oneIncomingParam['VALUE'] = $this->validator->string($oneIncomingParam['VALUE']);
528
                        } catch (Exception $e) {
529
                            continue 2;
530
                        }
531
                        break;
532
                    default:
533
                        break;
534
                }   
535
            } elseif (preg_match("/^FLAG-/", $oneIncomingParam['NAME'])) {
536
                if ($oneIncomingParam['VALUE'] != "TRUE" && $oneIncomingParam['VALUE'] != "FALSE" ) {
537
                    continue;
538
                }
539
            } else {
540
            // is this multi-lingual, and not an AUX attrib? Then check for presence of LANG and CONTENT before considering to add                
541
                $optionProperties = $optionInstance->optionType($oneIncomingParam['NAME']);
542
                if ($optionProperties["flag"] == "ML" && !array_key_exists("LANG", $oneIncomingParam)) {
543
                    continue;
544
                }
545
            }
546
            if (in_array($oneIncomingParam['NAME'], $allPossibleAttribs)) {
547
                $parameters[$number] = $oneIncomingParam;
548
            }
549
        }
550
        return $parameters;
551
    }
552
553
    /**
554
     * extracts the first occurrence of a given parameter name from the set of inputs
555
     * 
556
     * @param array  $inputs   incoming set of arrays
557
     * @param string $expected attribute that is to be extracted
558
     * @return mixed the value, or FALSE if none was found
559
     */
560
    public function firstParameterInstance($inputs, $expected) {
561
        foreach ($inputs as $attrib) {
562
            if ($attrib['NAME'] == $expected) {
563
                return $attrib['VALUE'];
564
            }
565
        }
566
        return FALSE;
567
    }
568
569
    /**
570
     * we are coercing the submitted JSON-style parameters into the same format
571
     * we use for the HTML POST user-interactively.
572
     * That's ugly, hence the function name.
573
     * 
574
     * @param array $parameters the parameters as provided by JSON input
575
     * @return array
576
     * @throws Exception
577
     */
578
    public function uglify($parameters) {
579
        $coercedInline = [];
580
        $coercedFile = [];
581
        $optionObject = \core\Options::instance();
582
        $dir = \core\common\Entity::createTemporaryDirectory('test');
583
        foreach ($parameters as $number => $oneAttrib) {
584
            if (preg_match("/^ATTRIB-/", $oneAttrib['NAME'])) {
585
                continue;
586
            }
587
            $optionInfo = $optionObject->optionType($oneAttrib['NAME']);
588
            $basename = "S$number";
589
            $extension = "";
590
            switch ($optionInfo['type']) {
591
592
                case \core\Options::TYPECODE_COORDINATES:
593
                    $extension = \core\Options::TYPECODE_TEXT;
594
                    $coercedInline["option"][$basename] = $oneAttrib['NAME'] . "#";
595
                    $coercedInline["value"][$basename . "-" . $extension] = $oneAttrib['VALUE'];
596
                    break;
597
                case \core\Options::TYPECODE_TEXT:
598
                // Fall-through: they all get the same treatment.
599
                case \core\Options::TYPECODE_BOOLEAN:
600
                // Fall-through: they all get the same treatment.
601
                case \core\Options::TYPECODE_STRING:
602
                // Fall-through: they all get the same treatment.
603
                case \core\Options::TYPECODE_INTEGER:
604
                    $extension = $optionInfo['type'];
605
                    $coercedInline["option"][$basename] = $oneAttrib['NAME'] . "#";
606
                    $coercedInline["value"][$basename . "-" . $extension] = $oneAttrib['VALUE'];
607
                    if ($optionInfo['flag'] == "ML") {
608
                        $coercedInline["value"][$basename . "-lang"] = $oneAttrib['LANG'];
609
                    }
610
                    break;
611
                case \core\Options::TYPECODE_FILE:
612
                    // Binary data is expected in base64 encoding. This is true also for PEM files!
613
                    $extension = $optionInfo['type'];
614
                    $coercedInline["option"][$basename] = $oneAttrib['NAME'] . "#";
615
                    file_put_contents($dir['dir'] . "/" . $basename . "-" . $extension, base64_decode($oneAttrib['VALUE']));
616
                    $coercedFile["value"]['tmp_name'][$basename . "-" . $extension] = $dir['dir'] . "/" . $basename . "-" . $extension;
617
                    break;
618
                default:
619
                    throw new Exception("We don't seem to know this type code!");
620
            }
621
        }
622
        return ["POST" => $coercedInline, "FILES" => $coercedFile];
623
    }
624
625
    /**
626
     * Returns a JSON construct detailing the error that happened
627
     * 
628
     * @param int    $code        error code to return
629
     * @param string $description textual description to return
630
     * @return string
631
     */
632
    public function returnError($code, $description) {
633
        echo json_encode(["result" => "ERROR", "details" => ["errorcode" => $code, "description" => $description]], JSON_PRETTY_PRINT);
634
    }
635
636
    /**
637
     * Returns a JSON construct with details of the successful API call
638
     * 
639
     * @param array $details details to return with the SUCCESS
640
     * @return string
641
     */
642
    public function returnSuccess($details) {
643
        $output = json_encode(["result" => "SUCCESS", "details" => $details], JSON_PRETTY_PRINT);
644
        if ($output === FALSE) {
645
            $this->returnError(API::ERROR_INTERNAL_ERROR, "Unable to JSON encode return data: ". json_last_error(). " - ". json_last_error_msg());
646
        }
647
        else {
648
            echo $output;
649
        }
650
    }
651
652
    /**
653
     * Checks if the profile is a valid SB profile belonging to the federation,
654
     * and fulfills all the prerequisites for being manipulated over API
655
     * 
656
     * @param \core\Federation $fed federation identifier
657
     * @param integer          $id  profile identifier
658
     * @return boolean|array
659
     */
660
    public function commonSbProfileChecks($fed, $id) {
661
        $validator = new \web\lib\common\InputValidation();
662
        $adminApi = new \web\lib\admin\API();
663
        try {
664
            $profile = $validator->existingProfile($id);
665
        } catch (Exception $e) {
666
            $adminApi->returnError(self::ERROR_INVALID_PARAMETER, "Profile identifier does not exist!");
667
            return FALSE;
668
        }
669
        if (!$profile instanceof \core\ProfileSilverbullet) {
670
            $adminApi->returnError(self::ERROR_INVALID_PARAMETER, sprintf("Profile identifier is not %s!", \core\ProfileSilverbullet::PRODUCTNAME));
671
            return FALSE;
672
        }
673
        $idp = new \core\IdP($profile->institution);
674
        if (strtoupper($idp->federation) != strtoupper($fed->tld)) {
675
            $adminApi->returnError(self::ERROR_INVALID_PARAMETER, "Profile is not in the federation for this APIKEY!");
676
            return FALSE;
677
        }
678
        if (count($profile->getAttributes("hiddenprofile:tou_accepted")) < 1) {
679
            $adminApi->returnError(self::ERROR_NO_TOU, "The terms of use have not yet been accepted for this profile!");
680
            return FALSE;
681
        }
682
        return [$idp, $profile];
683
    }
684
    
685
    public $loggerInstance;
686
687
}
688