Passed
Push — master ( 92b079...56770a )
by Tomasz
15:05
created

API::scrub()   D

Complexity

Conditions 19
Paths 22

Size

Total Lines 61
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

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

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
    /*
250
     * ACTIONS consists of a list of keywords, and associated REQuired and OPTional parameters
251
     * 
252
     */
253
    const ACTIONS = [
254
        // Inst-level actions.
255
        API::ACTION_NEWINST_BY_REF => [
256
            "REQ" => [API::AUXATTRIB_EXTERNALID,],
257
            "OPT" => [
258
                'general:geo_coordinates',
259
                'general:logo_file',
260
                'media:SSID',
261
                'media:wired',
262
                'media:remove_SSID',
263
                'media:consortium_OI',
264
                'media:force_proxy',
265
                'support:email',
266
                'support:info_file',
267
                'support:phone',
268
                'support:url'
269
            ],
270
            "FLAG" => [],
271
        ],
272
        API::ACTION_NEWINST => [
273
            "REQ" => [API::AUXATTRIB_INSTTYPE,], // "IdP", "SP" or "IdPSP"
274
            "OPT" => [
275
                'general:instname',
276
                'general:geo_coordinates',
277
                'general:logo_file',
278
                'media:SSID',
279
                'media:wired',
280
                'media:remove_SSID',
281
                'media:consortium_OI',
282
                'media:force_proxy',
283
                'support:email',
284
                'support:info_file',
285
                'support:phone',
286
                'support:url'
287
            ],
288
            "FLAG" => [],
289
            "RETVAL" => [
290
                API::AUXATTRIB_CAT_INST_ID, // New inst ID.
291
            ],            
292
        ],
293
        API::ACTION_DELINST => [
294
            "REQ" => [API::AUXATTRIB_CAT_INST_ID],
295
            "OPT" => [],
296
            "FLAG" => [],
297
            "RETVAL" => [],
298
        ],
299
        // Inst administrator management.
300
        API::ACTION_ADMIN_LIST => [
301
            "REQ" => [API::AUXATTRIB_CAT_INST_ID],
302
            "OPT" => [],
303
            "FLAG" => [],
304
            "RETVAL" => [
305
                ["ID", "MAIL", "LEVEL"] // Array with all admins of inst.
306
            ]
307
        ],
308
        API::ACTION_ADMIN_ADD => [
309
            "REQ" => [
310
                API::AUXATTRIB_ADMINID,
311
                API::AUXATTRIB_CAT_INST_ID
312
            ],
313
            "OPT" => [API::AUXATTRIB_TARGETMAIL],
314
            "FLAG" => [],
315
            "RETVAL" => [
316
                ["TOKEN URL",
317
                    "EMAIL SENT", // Dependent on TARGETMAIL input.
318
                    "EMAIL TRANSPORT SECURE"], // Dependent on TARGETMAIL input.
319
            ]
320
        ],
321
        API::ACTION_ADMIN_DEL => [
322
            "REQ" => [
323
                API::AUXATTRIB_ADMINID,
324
                API::AUXATTRIB_CAT_INST_ID
325
            ],
326
            "OPT" => [],
327
            "FLAG" => [],
328
            "RETVAL" => [],
329
        ],
330
        // Statistics.
331
        API::ACTION_STATISTICS_INST => [
332
            "REQ" => [API::AUXATTRIB_CAT_INST_ID],
333
            "OPT" => [],
334
            "FLAG" => [],
335
        ],
336
        API::ACTION_STATISTICS_FED => [
337
            "REQ" => [],
338
            "OPT" => [API::AUXATTRIB_DETAIL],
339
            "FLAG" => [],
340
            "RETVAL" => [
341
                ["device_id" => ["ADMIN", "SILVERBULLET", "USER"]] // Plus "TOTAL".
342
            ],
343
        ],
344
        API::ACTION_FEDERATION_LISTIDP => [
345
            "REQ" => [],
346
            "OPT" => [API::AUXATTRIB_CAT_INST_ID],
347
            "RETVAL" => [API::AUXATTRIB_CAT_INST_ID => "JSON_DATA"],
348
            "FLAG" => [API::FLAG_NOLOGO],
349
        ],
350
        // RADIUS profile actions.
351
        API::ACTION_NEWPROF_RADIUS => [
352
            "REQ" => [API::AUXATTRIB_CAT_INST_ID],
353
            "OPT" => [
354
                'eap:ca_file',
355
                'eap:server_name',
356
                'media:SSID',
357
                'media:wired',
358
                'media:remove_SSID',
359
                'media:consortium_OI',
360
                'media:force_proxy',
361
                'profile:name',
362
                'profile:customsuffix',
363
                'profile:description',
364
                'profile:production',
365
                'support:email',
366
                'support:info_file',
367
                'support:phone',
368
                'support:url',
369
                'device-specific:redirect',
370
                API::AUXATTRIB_PROFILE_INPUT_HINT,
371
                API::AUXATTRIB_PROFILE_INPUT_VERIFY,
372
                API::AUXATTRIB_PROFILE_OUTERVALUE,
373
                API::AUXATTRIB_PROFILE_REALM,
374
                API::AUXATTRIB_PROFILE_TESTUSER,
375
                API::AUXATTRIB_PROFILE_EAPTYPE,
376
            ],
377
            "FLAG" => [],
378
            "RETVAL" => API::AUXATTRIB_CAT_PROFILE_ID,
379
        ],
380
        // Silverbullet profile actions.
381
        API::ACTION_NEWPROF_SB => [
382
            "REQ" => [API::AUXATTRIB_CAT_INST_ID],
383
            "OPT" => [API::AUXATTRIB_SB_TOU],
384
            "FLAG" => [],
385
            "RETVAL" => API::AUXATTRIB_CAT_PROFILE_ID,
386
        ],
387
        API::ACTION_ENDUSER_NEW => [
388
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_USERNAME, API::AUXATTRIB_SB_EXPIRY],
389
            "OPT" => [],
390
            "FLAG" => [],
391
            "RETVAL" => [API::AUXATTRIB_SB_USERNAME, API::AUXATTRIB_SB_USERID],
392
        ],
393
        API::ACTION_ENDUSER_CHANGEEXPIRY => [
394
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_USERNAME, API::AUXATTRIB_SB_EXPIRY],
395
            "OPT" => [],
396
            "FLAG" => [],
397
            "RETVAL" => [],
398
        ],
399
        API::ACTION_ENDUSER_DEACTIVATE => [
400
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_USERID],
401
            "OPT" => [],
402
            "FLAG" => [],
403
            "RETVAL" => [],
404
        ],
405
        API::ACTION_ENDUSER_LIST => [
406
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID],
407
            "OPT" => [],
408
            "FLAG" => [],
409
            "RETVAL" => [
410
                [API::AUXATTRIB_SB_USERID => API::AUXATTRIB_SB_USERNAME],
411
            ],
412
        ],
413
        API::ACTION_ENDUSER_IDENTIFY => [
414
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID],
415
            "OPT" => [API::AUXATTRIB_SB_USERID, API::AUXATTRIB_SB_USERNAME, API::AUXATTRIB_SB_CERTSERIAL, API::AUXATTRIB_SB_CERTCN],
416
            "FLAG" => [],
417
            "RETVAL" => [API::AUXATTRIB_SB_USERNAME, API::AUXATTRIB_SB_USERID],
418
        ],
419
        API::ACTION_TOKEN_NEW => [
420
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_USERID],
421
            "OPT" => [API::AUXATTRIB_TOKEN_ACTIVATIONS, API::AUXATTRIB_TARGETMAIL, API::AUXATTRIB_TARGETSMS],
422
            "FLAG" => [],
423
            "RETVAL" => [
424
                API::AUXATTRIB_TOKENURL,
425
                API::AUXATTRIB_TOKEN,
426
                "EMAIL SENT", // Dependent on TARGETMAIL input.
427
                "EMAIL TRANSPORT SECURE", // Dependent on TARGETMAIL input.
428
                "SMS SENT", // Dependent on TARGETSMS input.
429
            ]
430
        ],
431
        API::ACTION_TOKEN_REVOKE => [
432
            "REQ" => [API::AUXATTRIB_TOKEN],
433
            "OPT" => [],
434
            "FLAG" => [],
435
            "RETVAL" => [],
436
        ],
437
        API::ACTION_TOKEN_LIST => [
438
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID],
439
            "OPT" => [API::AUXATTRIB_SB_USERID],
440
            "FLAG" => [],
441
            "RETVAL" => [
442
                [API::AUXATTRIB_SB_USERID => [API::AUXATTRIB_TOKEN, "STATUS"]],
443
            ]
444
        ],
445
        API::ACTION_CERT_LIST => [
446
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_USERID],
447
            "OPT" => [],
448
            "FLAG" => [],
449
            "RETVAL" => [
450
                [API::AUXATTRIB_SB_CERTSERIAL => ["ISSUED", "EXPIRY", "STATUS", "DEVICE", "CN"]]
451
            ]
452
        ],
453
        API::ACTION_CERT_REVOKE => [
454
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_CERTSERIAL],
455
            "OPT" => [],
456
            "FLAG" => [],
457
            "RETVAL" => [],
458
        ],
459
        API::ACTION_CERT_ANNOTATE => [
460
            "REQ" => [API::AUXATTRIB_CAT_PROFILE_ID, API::AUXATTRIB_SB_CERTSERIAL, API::AUXATTRIB_SB_CERTANNOTATION],
461
            "OPT" => [],
462
            "FLAG" => [],
463
            "RETVAL" => [],
464
        ]
465
    ];
466
467
    /**
468
     *
469
     * @var \web\lib\common\InputValidation
470
     */
471
    private $validator;
472
473
    /**
474
     * construct the API class
475
     */
476
    public function __construct() {
477
        $this->validator = new \web\lib\common\InputValidation();
478
        $this->loggerInstance = new \core\common\Logging();
479
    }
480
481
    /**
482
     * Only leave attributes in the request which are related to the ACTION.
483
     * Also sanitise by enforcing LANG attribute in multi-lang attributes.
484
     * 
485
     * @param array            $inputJson the incoming JSON request
486
     * @param \core\Federation $fedObject the federation the user is acting within
487
     * @return array the scrubbed attributes
488
     */
489
    public function scrub($inputJson, $fedObject) {        
490
        $optionInstance = \core\Options::instance();
491
        $parameters = [];
492
        $allPossibleAttribs = array_merge(API::ACTIONS[$inputJson['ACTION']]['REQ'], API::ACTIONS[$inputJson['ACTION']]['OPT'],  API::ACTIONS[$inputJson['ACTION']]['FLAG']);
493
        // some actions don't need parameters. Don't get excited when there aren't any.
494
        if (!isset($inputJson['PARAMETERS'])) {
495
            return [];
496
        }
497
        foreach ($inputJson['PARAMETERS'] as $number => $oneIncomingParam) {
498
            // index has to be an integer
499
            if (!is_int($number)) {
500
                continue;
501
            }
502
            // do we actually have a value?
503
            if (!array_key_exists("VALUE", $oneIncomingParam)) {
504
                continue;
505
            }
506
            if (preg_match("/^ATTRIB-/", $oneIncomingParam['NAME'])) {// sanitise the AUX attr 
507
                switch ($oneIncomingParam['NAME']) {
508
                    case API::AUXATTRIB_CAT_INST_ID:
509
                        try {
510
                            $inst = $this->validator->existingIdP($oneIncomingParam['VALUE']);
511
                        } catch (Exception $e) {
512
                            continue 2;
513
                        }
514
                        if (strtoupper($inst->federation) != strtoupper($fedObject->tld)) {
515
                            // IdP in different fed, scrub it.
516
                            continue 2;
517
                        }
518
                        break;
519
                    case API::AUXATTRIB_TARGETMAIL:
520
                        if ($this->validator->email($oneIncomingParam['VALUE']) === FALSE) {
521
                            continue 2;
522
                        }
523
                        break;
524
                    case API::AUXATTRIB_ADMINID:
525
                        try {
526
                            $oneIncomingParam['VALUE'] = $this->validator->string($oneIncomingParam['VALUE']);
527
                        } catch (Exception $e) {
528
                            continue 2;
529
                        }
530
                        break;
531
                    default:
532
                        break;
533
                }   
534
            } elseif (preg_match("/^FLAG-/", $oneIncomingParam['NAME'])) {
535
                if ($oneIncomingParam['VALUE'] != "TRUE" && $oneIncomingParam['VALUE'] != "FALSE" ) {
536
                    continue;
537
                }
538
            } else {
539
            // is this multi-lingual, and not an AUX attrib? Then check for presence of LANG and CONTENT before considering to add                
540
                $optionProperties = $optionInstance->optionType($oneIncomingParam['NAME']);
541
                if ($optionProperties["flag"] == "ML" && !array_key_exists("LANG", $oneIncomingParam)) {
542
                    continue;
543
                }
544
            }
545
            if (in_array($oneIncomingParam['NAME'], $allPossibleAttribs)) {
546
                $parameters[$number] = $oneIncomingParam;
547
            }
548
        }
549
        return $parameters;
550
    }
551
552
    /**
553
     * extracts the first occurrence of a given parameter name from the set of inputs
554
     * 
555
     * @param array  $inputs   incoming set of arrays
556
     * @param string $expected attribute that is to be extracted
557
     * @return mixed the value, or FALSE if none was found
558
     */
559
    public function firstParameterInstance($inputs, $expected) {
560
        foreach ($inputs as $attrib) {
561
            if ($attrib['NAME'] == $expected) {
562
                return $attrib['VALUE'];
563
            }
564
        }
565
        return FALSE;
566
    }
567
568
    /**
569
     * we are coercing the submitted JSON-style parameters into the same format
570
     * we use for the HTML POST user-interactively.
571
     * That's ugly, hence the function name.
572
     * 
573
     * @param array $parameters the parameters as provided by JSON input
574
     * @return array
575
     * @throws Exception
576
     */
577
    public function uglify($parameters) {
578
        $coercedInline = [];
579
        $coercedFile = [];
580
        $optionObject = \core\Options::instance();
581
        $dir = \core\common\Entity::createTemporaryDirectory('test');
582
        foreach ($parameters as $number => $oneAttrib) {
583
            if (preg_match("/^ATTRIB-/", $oneAttrib['NAME'])) {
584
                continue;
585
            }
586
            $optionInfo = $optionObject->optionType($oneAttrib['NAME']);
587
            $basename = "S$number";
588
            $extension = "";
589
            switch ($optionInfo['type']) {
590
591
                case \core\Options::TYPECODE_COORDINATES:
592
                    $extension = \core\Options::TYPECODE_TEXT;
593
                    $coercedInline["option"][$basename] = $oneAttrib['NAME'] . "#";
594
                    $coercedInline["value"][$basename . "-" . $extension] = $oneAttrib['VALUE'];
595
                    break;
596
                case \core\Options::TYPECODE_TEXT:
597
                // Fall-through: they all get the same treatment.
598
                case \core\Options::TYPECODE_BOOLEAN:
599
                // Fall-through: they all get the same treatment.
600
                case \core\Options::TYPECODE_STRING:
601
                // Fall-through: they all get the same treatment.
602
                case \core\Options::TYPECODE_INTEGER:
603
                    $extension = $optionInfo['type'];
604
                    $coercedInline["option"][$basename] = $oneAttrib['NAME'] . "#";
605
                    $coercedInline["value"][$basename . "-" . $extension] = $oneAttrib['VALUE'];
606
                    if ($optionInfo['flag'] == "ML") {
607
                        $coercedInline["value"][$basename . "-lang"] = $oneAttrib['LANG'];
608
                    }
609
                    break;
610
                case \core\Options::TYPECODE_FILE:
611
                    // Binary data is expected in base64 encoding. This is true also for PEM files!
612
                    $extension = $optionInfo['type'];
613
                    $coercedInline["option"][$basename] = $oneAttrib['NAME'] . "#";
614
                    file_put_contents($dir['dir'] . "/" . $basename . "-" . $extension, base64_decode($oneAttrib['VALUE']));
615
                    $coercedFile["value"]['tmp_name'][$basename . "-" . $extension] = $dir['dir'] . "/" . $basename . "-" . $extension;
616
                    break;
617
                default:
618
                    throw new Exception("We don't seem to know this type code!");
619
            }
620
        }
621
        return ["POST" => $coercedInline, "FILES" => $coercedFile];
622
    }
623
624
    /**
625
     * Returns a JSON construct detailing the error that happened
626
     * 
627
     * @param int    $code        error code to return
628
     * @param string $description textual description to return
629
     * @return string
630
     */
631
    public function returnError($code, $description) {
632
        echo json_encode(["result" => "ERROR", "details" => ["errorcode" => $code, "description" => $description]], JSON_PRETTY_PRINT);
633
    }
634
635
    /**
636
     * Returns a JSON construct with details of the successful API call
637
     * 
638
     * @param array $details details to return with the SUCCESS
639
     * @return string
640
     */
641
    public function returnSuccess($details) {
642
        $output = json_encode(["result" => "SUCCESS", "details" => $details], JSON_PRETTY_PRINT);
643
        if ($output === FALSE) {
644
            $this->returnError(API::ERROR_INTERNAL_ERROR, "Unable to JSON encode return data: ". json_last_error(). " - ". json_last_error_msg());
645
        }
646
        else {
647
            echo $output;
648
        }
649
    }
650
651
    /**
652
     * Checks if the profile is a valid SB profile belonging to the federation,
653
     * and fulfills all the prerequisites for being manipulated over API
654
     * 
655
     * @param \core\Federation $fed federation identifier
656
     * @param integer          $id  profile identifier
657
     * @return boolean|array
658
     */
659
    public function commonSbProfileChecks($fed, $id) {
660
        $validator = new \web\lib\common\InputValidation();
661
        $adminApi = new \web\lib\admin\API();
662
        try {
663
            $profile = $validator->existingProfile($id);
664
        } catch (Exception $e) {
665
            $adminApi->returnError(self::ERROR_INVALID_PARAMETER, "Profile identifier does not exist!");
666
            return FALSE;
667
        }
668
        if (!$profile instanceof \core\ProfileSilverbullet) {
669
            $adminApi->returnError(self::ERROR_INVALID_PARAMETER, sprintf("Profile identifier is not %s!", \core\ProfileSilverbullet::PRODUCTNAME));
670
            return FALSE;
671
        }
672
        $idp = new \core\IdP($profile->institution);
673
        if (strtoupper($idp->federation) != strtoupper($fed->tld)) {
674
            $adminApi->returnError(self::ERROR_INVALID_PARAMETER, "Profile is not in the federation for this APIKEY!");
675
            return FALSE;
676
        }
677
        if (count($profile->getAttributes("hiddenprofile:tou_accepted")) < 1) {
678
            $adminApi->returnError(self::ERROR_NO_TOU, "The terms of use have not yet been accepted for this profile!");
679
            return FALSE;
680
        }
681
        return [$idp, $profile];
682
    }
683
    
684
    public $loggerInstance;
685
686
}
687