Passed
Push — master ( 209205...a3611a )
by Stefan
03:23
created

API::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 2
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
1
<?php
2
3
/*
4
 * ******************************************************************************
5
 * Copyright 2011-2018 DANTE Ltd. and GÉANT on behalf of the GN3, GN3+, GN4-1 
6
 * and GN4-2 consortia
7
 *
8
 * License: see the web/copyright.php file in the file structure
9
 * ******************************************************************************
10
 */
11
12
namespace web\lib\admin;
13
14
use Exception;
15
16
require_once(dirname(dirname(dirname(dirname(__FILE__)))) . "/config/_config.php");
17
18
class API {
19
20
    const ERROR_API_DISABLED = 1;
21
    const ERROR_NO_APIKEY = 2;
22
    const ERROR_INVALID_APIKEY = 3;
23
    const ERROR_MISSING_PARAMETER = 4;
24
    const ERROR_INVALID_PARAMETER = 5;
25
    const ERROR_NO_ACTION = 6;
26
    const ERROR_INVALID_ACTION = 7;
27
    const ERROR_MALFORMED_REQUEST = 8;
28
    const ACTION_NEWINST_BY_REF = "NEWINST-BY-REF";
29
    const ACTION_NEWINST = "NEWINST";
30
    const ACTION_DELINST = "DELINST";
31
    const ACTION_ADMIN_LIST = "ADMIN-LIST";
32
    const ACTION_ADMIN_ADD = "ADMIN-ADD";
33
    const ACTION_ADMIN_DEL = "ADMIN-DEL";
34
    const ACTION_STATISTICS_INST = "STATISTICS-INST";
35
    const ACTION_NEWPROF_RADIUS = "NEWPROF-RADIUS";
36
    const ACTION_NEWPROF_SB = "NEWPROF-SB";
37
    const ACTION_ENDUSER_NEW = "ENDUSER-NEW";
38
    const ACTION_ENDUSER_DEACTIVATE = "ENDUSER-DEACTIVATE";
39
    const ACTION_ENDUSER_LIST = "ENDUSER-LIST";
40
    const ACTION_TOKEN_NEW = "TOKEN-NEW";
41
    const ACTION_TOKEN_REVOKE = "TOKEN-REVOKE";
42
    const ACTION_TOKEN_LIST = "TOKEN-LIST";
43
    const ACTION_CERT_LIST = "CERT-LIST";
44
    const ACTION_CERT_REVOKE = "CERT-REVOKE";
45
    const AUXATTRIB_ADMINID = "ATTRIB-ADMINID";
46
    const AUXATTRIB_ADMINEMAIL = "ATTRIB-ADMINEMAIL";
47
    const AUXATTRIB_EXTERNALID = "ATTRIB-EXTERNALID";
48
    const AUXATTRIB_CAT_INST_ID = "ATTRIB-CAT-INSTID";
49
50
    /*
51
     * ACTIONS consists of a list of keywords, and associated REQuired and OPTional parameters
52
     * 
53
     */
54
    const ACTIONS = [
55
        # inst-level actions
56
        API::ACTION_NEWINST_BY_REF => [
57
            "REQ" => [API::AUXATTRIB_EXTERNALID,],
58
            "OPT" => ['general:geo_coordinates', 'general:logo_file', 'media:SSID', 'media:SSID_with_legacy', 'media:wired', 'media:remove_SSID', 'media:consortium_OI', 'media:force_proxy', 'support:email', 'support:info_file', 'support:phone', 'support:url'],
59
        ],
60
        API::ACTION_NEWINST => [
61
            "REQ" => [],
62
            "OPT" => ['general:instname', 'general:geo_coordinates', 'general:logo_file', 'media:SSID', 'media:SSID_with_legacy', 'media:wired', 'media:remove_SSID', 'media:consortium_OI', 'media:force_proxy', 'support:email', 'support:info_file', 'support:phone', 'support:url'],
63
        ],
64
        API::ACTION_DELINST => [
65
            "REQ" => [API::AUXATTRIB_CAT_INST_ID],
66
            "OPT" => []
67
        ],
68
        API::ACTION_ADMIN_LIST => [
69
            "REQ" => [API::AUXATTRIB_CAT_INST_ID],
70
            "OPT" => []
71
        ],
72
        API::ACTION_ADMIN_ADD => [
73
            "REQ" => [API::AUXATTRIB_ADMINID, API::AUXATTRIB_CAT_INST_ID],
74
            "OPT" => [API::AUXATTRIB_ADMINEMAIL]
75
        ],
76
        API::ACTION_ADMIN_DEL => [
77
            "REQ" => [],
78
            "OPT" => []
79
        ],
80
        API::ACTION_STATISTICS_INST => [
81
            "REQ" => [],
82
            "OPT" => []
83
        ],
84
        # RADIUS profile actions
85
        API::ACTION_NEWPROF_RADIUS => [
86
            "REQ" => [],
87
            "OPT" => []
88
        ],
89
        # Silverbullet profile actions
90
        API::ACTION_NEWPROF_SB => [
91
            "REQ" => [],
92
            "OPT" => []
93
        ],
94
        API::ACTION_ENDUSER_NEW => [
95
            "REQ" => [],
96
            "OPT" => []
97
        ],
98
        API::ACTION_ENDUSER_DEACTIVATE => [
99
            "REQ" => [],
100
            "OPT" => []
101
        ],
102
        API::ACTION_ENDUSER_LIST => [
103
            "REQ" => [],
104
            "OPT" => []
105
        ],
106
        API::ACTION_TOKEN_NEW => [
107
            "REQ" => [],
108
            "OPT" => []
109
        ],
110
        API::ACTION_TOKEN_REVOKE => [
111
            "REQ" => [],
112
            "OPT" => []
113
        ],
114
        API::ACTION_TOKEN_LIST => [
115
            "REQ" => [],
116
            "OPT" => []
117
        ],
118
        API::ACTION_CERT_LIST => [
119
            "REQ" => [],
120
            "OPT" => []
121
        ],
122
        API::ACTION_CERT_REVOKE => [
123
            "REQ" => [],
124
            "OPT" => []
125
        ],
126
    ];
127
128
    /**
129
     *
130
     * @var \web\lib\common\InputValidation
131
     */
132
    private $validator;
133
134
    public function __construct() {
135
        $this->validator = new \web\lib\common\InputValidation();
136
    }
137
138
    /**
139
     * Only leave attributes in the request which are related to the ACTION.
140
     * Also sanitise by enforcing LANG attribute in multi-lang attributes.
141
     * 
142
     * @param array $inputJson the incoming JSON request
143
     * @return array the scrubbed attributes
144
     */
145
    public function scrub($inputJson) {
146
        $optionInstance = \core\Options::instance();
147
        $parameters = [];
148
        $allPossibleAttribs = array_merge(API::ACTIONS[$inputJson['ACTION']]['REQ'], API::ACTIONS[$inputJson['ACTION']]['OPT']);
149
        foreach ($inputJson['PARAMETERS'] as $number => $oneIncomingParam) {
150
            // index has to be an integer
151
            if (!is_int($number)) {
152
                continue;
153
            }
154
            // do we actually have a value?
155
            if (!array_key_exists("VALUE", $oneIncomingParam)) {
156
                continue;
157
            }
158
            // is this multi-lingual, and not an AUX attrib? Then check for presence of LANG and CONTENT before considering to add
159
            if (!preg_match("/^ATTRIB-/", $oneIncomingParam['NAME'])) {
160
                $optionProperties = $optionInstance->optionType($oneIncomingParam['NAME']);
161
                if ($optionProperties["flag"] == "ML" && !array_key_exists("LANG", $oneIncomingParam)) {
162
                    continue;
163
                }
164
            } else { // sanitise the AUX attr 
165
                switch ($oneIncomingParam['NAME']) {
166
                    case API::AUXATTRIB_CAT_INST_ID:
167
                        try {
168
                            $inst = $this->validator->IdP($oneIncomingParam['VALUE']);
0 ignored issues
show
Unused Code introduced by
The assignment to $inst is dead and can be removed.
Loading history...
169
                        } catch (Exception $e) {
170
                            continue;
171
                        }
172
                        break;
173
                    case API::AUXATTRIB_ADMINEMAIL:
174
                        if ($this->validator->email($oneIncomingParam['VALUE']) === FALSE) {
175
                            continue;
176
                        }
177
                        break;
178
                    case API::AUXATTRIB_ADMINID:
179
                        try {
180
                            $oneIncomingParam['VALUE'] = $this->validator->string($oneIncomingParam['VALUE']);
181
                        } catch (Exception $e) {
182
                            continue;
183
                        }
184
                        break;
185
                    default:
186
                        continue;
187
                }
188
            }
189
            if (in_array($oneIncomingParam['NAME'], $allPossibleAttribs)) {
190
                $parameters[$number] = $oneIncomingParam;
191
            }
192
        }
193
        return $parameters;
194
    }
195
196
    public function firstParameterInstance($inputs, $expected) {
197
        foreach ($inputs as $attrib) {
198
            if ($attrib['NAME'] == $expected) {
199
                return $attrib['VALUE'];
200
            }
201
        }
202
        return FALSE;
203
    }
204
    /**
205
     * we are coercing the submitted JSON-style parameters into the same format
206
     * we use for the HTML POST user-interactively.
207
     * That's ugly, hence the function name.
208
     * 
209
     * @param array $parameters
210
     */
211
    public function uglify($parameters) {
212
        $coercedInline = [];
213
        $coercedFile = [];
214
        $optionObject = \core\Options::instance();
215
        $cat = new \core\CAT();
216
        $dir = $cat->createTemporaryDirectory('test');
217
        foreach ($parameters as $number => $oneAttrib) {
218
            $optionInfo = $optionObject->optionType($oneAttrib['NAME']);
219
            $basename = "S$number";
220
            $extension = "";
221
            switch ($optionInfo['type']) {
222
223
                case \core\Options::TYPECODE_COORDINATES:
224
                    $extension = \core\Options::TYPECODE_TEXT;
225
                    $coercedInline["option"][$basename] = $oneAttrib['NAME'] . "#";
226
                    $coercedInline["value"][$basename . "-" . $extension] = $oneAttrib['VALUE'];
227
                    break;
228
                case \core\Options::TYPECODE_TEXT:
229
                // fall-through: they all get the same treatment
230
                case \core\Options::TYPECODE_BOOLEAN:
231
                // fall-through: they all get the same treatment
232
                case \core\Options::TYPECODE_STRING:
233
                // fall-through: they all get the same treatment
234
                case \core\Options::TYPECODE_INTEGER:
235
                    $extension = $optionInfo['type'];
236
                    $coercedInline["option"][$basename] = $oneAttrib['NAME'] . "#";
237
                    $coercedInline["value"][$basename . "-" . $extension] = $oneAttrib['VALUE'];
238
                    if ($optionInfo['flag'] == "ML") {
239
                        $coercedInline["value"][$basename . "-lang"] = $oneAttrib['LANG'];
240
                    }
241
                    break;
242
                case \core\Options::TYPECODE_FILE:
243
                    // binary data is expected in base64 encoding. This is true
244
                    // also for PEM files!
245
                    $extension = $optionInfo['type'];
246
                    $coercedInline["option"][$basename] = $oneAttrib['NAME'] . "#";
247
                    file_put_contents($dir['dir'] . "/" . $basename . "-" . $extension, base64_decode($oneAttrib['VALUE']));
248
                    $coercedFile["value"]['tmp_name'][$basename . "-" . $extension] = $dir['dir'] . "/" . $basename . "-" . $extension;
249
                    break;
250
                default:
251
                    throw new Exception("We don't seem to know this type code!");
252
            }
253
        }
254
        return ["POST" => $coercedInline, "FILES" => $coercedFile];
255
    }
256
257
}
258