Subversion Repositories oidplus

Rev

Rev 277 | Rev 289 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
115 daniel-mar 1
<?php
2
 
3
/*
4
 * OIDplus 2.0
5
 * Copyright 2019 Daniel Marschall, ViaThinkSoft
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
 
20
class OIDplusLogger {
21
 
188 daniel-mar 22
        private static function split_maskcodes($maskcodes) {
288 daniel-mar 23
                // This function splits a mask code containing multiple components
24
                // (delimited by '+' or '/') in single components
25
                // It takes care that '+' and '/' inside brackets won't be used to split the codes
26
                // Also, brackets can be escaped.
27
                // The severity block (optional, must be standing in front of a component)
28
                // is handled too. Inside the severity block, you may only use '/' to split components.
29
                // The severity block will be implicitly repeated from the previous components if a component
30
                // does not feature one.
31
                //
32
                // "[S]AAA(BBB)+CCC(DDD)"   ==> array(
33
                //                                 array(array("S"),"AAA(BBB)"),
34
                //                                 array(array("S"),"CCC(DDD)")
35
                //                              )
36
                // "[S]AAA(B+BB)+CCC(DDD)"  ==> array(
37
                //                                 array(array("S"),"AAA(B+BB)"),
38
                //                                 array(array("S"),"CCC(DDD)")
39
                //                              )
40
                // "[S]AAA(B\)BB)+CCC(DDD)" ==> array(
41
                //                                 array(array("S"),"AAA(B\)BB)"),
42
                //                                 array(array("S"),"CCC(DDD)")
43
                //                              )
44
 
188 daniel-mar 45
                $out = array();
288 daniel-mar 46
                $sevs = array(); // Note: The severity block will repeat for the next components if not changed explicitly
47
 
188 daniel-mar 48
                $code = '';
288 daniel-mar 49
                $sev = '';
188 daniel-mar 50
                $bracket_level = 0;
288 daniel-mar 51
                $is_escaping = false;
52
                $inside_severity_block = false;
188 daniel-mar 53
                for ($i=0; $i<strlen($maskcodes); $i++) {
54
                        $char = $maskcodes[$i];
288 daniel-mar 55
 
56
                        if ($inside_severity_block) {
57
                                // Severity block (optional)
58
                                // e.g.  [?WARN/!OK] ==> $sevs = array("?WARN", "!OK")
59
                                if ($char == '\\') {
60
                                        if ($is_escaping) {
61
                                                $is_escaping = false;
62
                                                $sev .= $char;
63
                                        } else {
64
                                                $is_escaping = true;
65
                                        }
66
                                }
67
                                else if ($char == '[') {
68
                                        if ($is_escaping) {
69
                                                $is_escaping = false;
70
                                        } else {
71
                                                $bracket_level++;
72
                                        }
73
                                        $sev .= $char;
74
                                }
75
                                else if ($char == ']') {
76
                                        if ($is_escaping) {
77
                                                $is_escaping = false;
78
                                                $sev .= $char;
79
                                        } else {
80
                                                $bracket_level--;
81
                                                if ($bracket_level < 0) return false;
82
                                                if ($bracket_level == 0) {
83
                                                        $inside_severity_block = false;
84
                                                        if ($sev != '') $sevs[] = $sev;
85
                                                        $sev = '';
86
                                                } else {
87
                                                        $sev .= $char;
88
                                                }
89
                                        }
90
                                }
91
                                else if ((($char == '/')) && ($bracket_level == 1)) {
92
                                        if ($is_escaping) {
93
                                                $is_escaping = false;
94
                                                $sev .= $char;
95
                                        } else {
96
                                                if ($sev != '') $sevs[] = $sev;
97
                                                $sev = '';
98
                                        }
99
                                } else {
100
                                        if ($is_escaping) {
101
                                                // This would actually be an error, because we cannot escape this
102
                                                $is_escaping = false;
103
                                                $sev .= '\\' . $char;
104
                                        } else {
105
                                                $sev .= $char;
106
                                        }
107
                                }
188 daniel-mar 108
                        } else {
288 daniel-mar 109
                                // Normal data (after the severity block)
110
                                if (($char == '[') && ($code == '')) {
111
                                        $inside_severity_block = true;
112
                                        $bracket_level++;
113
                                        $sevs = array();
114
                                }
115
                                else if ($char == '\\') {
116
                                        if ($is_escaping) {
117
                                                $is_escaping = false;
118
                                                $code .= $char;
119
                                        } else {
120
                                                $is_escaping = true;
121
                                        }
122
                                }
123
                                else if ($char == '(') {
124
                                        if ($is_escaping) {
125
                                                $is_escaping = false;
126
                                        } else {
127
                                                $bracket_level++;
128
                                        }
129
                                        $code .= $char;
130
                                }
131
                                else if ($char == ')') {
132
                                        if ($is_escaping) {
133
                                                $is_escaping = false;
134
                                        } else {
135
                                                $bracket_level--;
136
                                                if ($bracket_level < 0) return false;
137
                                        }
138
                                        $code .= $char;
139
                                }
140
                                else if ((($char == '+') || ($char == '/')) && ($bracket_level == 0)) {
141
                                        if ($is_escaping) {
142
                                                $is_escaping = false;
143
                                                $code .= $char;
144
                                        } else {
145
                                                if ($code != '') $out[] = array($sevs,$code);
146
                                                $code = '';
147
                                        }
148
                                } else {
149
                                        if ($is_escaping) {
150
                                                // This would actually be an error, because we cannot escape this
151
                                                $is_escaping = false;
152
                                                $code .= '\\' . $char;
153
                                        } else {
154
                                                $code .= $char;
155
                                        }
156
                                }
188 daniel-mar 157
                        }
158
                }
288 daniel-mar 159
                if ($code != '') $out[] = array($sevs,$code);
160
                if ($inside_severity_block) return false;
188 daniel-mar 161
 
162
                return $out;
163
        }
164
 
263 daniel-mar 165
        public static function log($maskcodes, $event) {
288 daniel-mar 166
                // What is a mask code?
167
                // A mask code gives information about the log event:
168
                // 1. The severity (info, warning, error)
169
                // 2. In which logbook(s) the event shall be placed
170
                // Example:
171
                // The event would be:
172
                // "Person 'X' moves from house 'A' to house 'B'"
173
                // This event would affect the person X and the two houses,
174
                // so, instead of logging into 3 logbooks separately,
175
                // you would create a mask code that tells the system
176
                // to put the message into the logbooks of person X,
177
                // house A and house B.
115 daniel-mar 178
 
179
                $users = array();
180
                $objects = array();
181
 
288 daniel-mar 182
                // A mask code with multiple components is split into single codes
183
                // using '+' or '/', e.g. "OID(x)+RA(x)" would be split to "OID(x)" and "RA(x)"
184
                // which would result in the message being placed in the logbook of OID x,
185
                // and the logbook of the RA owning OID x.
188 daniel-mar 186
                $maskcodes_ary = self::split_maskcodes($maskcodes);
187
                if ($maskcodes_ary === false) {
288 daniel-mar 188
                        throw new OIDplusException("Invalid maskcode '$maskcodes' (failed to split)");
188 daniel-mar 189
                }
288 daniel-mar 190
                foreach ($maskcodes_ary as list($sevs,$maskcode)) {
191
                        // At the beginning of each mask code, you can define a severity.
192
                        // If you have a mask code with multiple components, you don't have to place the
193
                        // severity for each component. You can just leave it at the beginning.
194
                        // e.g. "[WARN]OID(x)+RA(x)" is equal to "[WARN]OID(x)+[WARN]RA(x)"
195
                        // You can also put different severities for the components:
196
                        // e.g. "[INFO]OID(x)+[WARN]RA(x)" would be a info for the OID, but a warning for the RA.
197
                        // If you want to make the severity dependent on wheather the user is logged in or not,
198
                        // prepend "?" or "!" and use '/' as delimiter
199
                        // Example: "[?WARN/!OK]RA(x)" means: If RA is not logged in, it is a warning; if it is logged in, it is an success
200
                        $severity = 0; // default severity = none
201
                        foreach ($sevs as $sev) {
202
                                switch (strtoupper($sev)) {
203
                                        // [OK]   = Success
204
                                        //          Numeric value: 1
205
                                        //          Rule of thumb: YOU have done something and it was successful
206
                                        case '?OK':
207
                                                $severity_online = 1;
208
                                                break;
209
                                        case '!OK':
210
                                        case  'OK':
211
                                                $severity = 1;
212
                                                break;
213
                                        // [INFO] = Informational
214
                                        //          Numeric value: 2
215
                                        //          Rule of thumb: Someone else has done something (that affects you) and it was successful
216
                                        case '?INFO':
217
                                                $severity_online = 2;
218
                                                break;
219
                                        case '!INFO':
220
                                        case  'INFO':
221
                                                $severity = 2;
222
                                                break;
223
                                        // [WARN] = Warning
224
                                        //          Numeric value: 3
225
                                        //          Rule of thumb: Something happened (probably someone did something) and it affects you
226
                                        case '?WARN':
227
                                                $severity_online = 3;
228
                                                break;
229
                                        case '!WARN':
230
                                        case  'WARN':
231
                                                $severity = 3;
232
                                                break;
233
                                        // [ERR]  = Error
234
                                        //          Numeric value: 4
235
                                        //          Rule of thumb: Something failed (probably someone did something) and it affects you
236
                                        case '?ERR':
237
                                                $severity_online = 4;
238
                                                break;
239
                                        case '!ERR':
240
                                        case  'ERR':
241
                                                $severity = 4;
242
                                                break;
243
                                        // [CRIT] = Critical
244
                                        //          Numeric value: 5
245
                                        //          Rule of thumb: Something happened (probably someone did something) which is not an error,
246
                                        //          but some critical situation (e.g. hardware failure), and it affects you
247
                                        case '?CRIT':
248
                                                $severity_online = 5;
249
                                                break;
250
                                        case '!CRIT':
251
                                        case  'CRIT':
252
                                                $severity = 5;
253
                                                break;
254
                                        default:
255
                                                throw new OIDplusException("Invalid maskcode '$maskcodes' (Unknown severity '$sev')");
256
                                }
257
                        }
258
 
115 daniel-mar 259
                        // OID(x)       Save log entry into the logbook of: Object "x"
260
                        if (preg_match('@^OID\((.+)\)$@ismU', $maskcode, $m)) {
261
                                $object_id = $m[1];
288 daniel-mar 262
                                $objects[] = array($severity, $object_id);
250 daniel-mar 263
                                if ($object_id == '') throw new OIDplusException("OID logger mask requires OID");
115 daniel-mar 264
                        }
265
 
288 daniel-mar 266
                        // SUPOID(x)    Save log entry into the logbook of: Parent of object "x"
267
                        else if (preg_match('@^SUPOID\((.+)\)$@ismU', $maskcode, $m)) {
268
                                $object_id         = $m[1];
269
                                if ($object_id == '') throw new OIDplusException("SUPOID logger mask requires OID");
270
                                $obj = OIDplusObject::parse($object_id);
271
                                if ($obj) {
272
                                        $parent = $obj->getParent()->nodeId();
273
                                        $objects[] = array($severity, $parent);
274
                                } else {
275
                                        throw new OIDplusException("SUPOID logger mask: Invalid object '$object_id'");
276
                                }
277
                        }
278
 
115 daniel-mar 279
                        // OIDRA(x)?    Save log entry into the logbook of: Logged in RA of object "x"
288 daniel-mar 280
                        // Remove or replace "?" by "!" if the entity does not need to be logged in
115 daniel-mar 281
                        else if (preg_match('@^OIDRA\((.+)\)([\?\!])$@ismU', $maskcode, $m)) {
282
                                $object_id         = $m[1];
116 daniel-mar 283
                                $ra_need_login     = $m[2] == '?';
250 daniel-mar 284
                                if ($object_id == '') throw new OIDplusException("OIDRA logger mask requires OID");
115 daniel-mar 285
                                $obj = OIDplusObject::parse($object_id);
116 daniel-mar 286
                                if ($obj) {
287
                                        if ($ra_need_login) {
288
                                                foreach (OIDplus::authUtils()->loggedInRaList() as $ra) {
288 daniel-mar 289
                                                        if ($obj->userHasWriteRights($ra)) $users[] = array($severity_online, $ra->raEmail());
116 daniel-mar 290
                                                }
291
                                        } else {
288 daniel-mar 292
                                                // $users[] = array($severity, $obj->getRa()->raEmail());
116 daniel-mar 293
                                                foreach (OIDplusRA::getAllRAs() as $ra) {
288 daniel-mar 294
                                                        if ($obj->userHasWriteRights($ra)) $users[] = array($severity, $ra->raEmail());
116 daniel-mar 295
                                                }
115 daniel-mar 296
                                        }
288 daniel-mar 297
                                } else {
298
                                        throw new OIDplusException("OIDRA logger mask: Invalid object '$object_id'");
115 daniel-mar 299
                                }
300
                        }
301
 
302
                        // SUPOIDRA(x)? Save log entry into the logbook of: Logged in RA that owns the superior object of "x"
288 daniel-mar 303
                        // Remove or replace "?" by "!" if the entity does not need to be logged in
115 daniel-mar 304
                        else if (preg_match('@^SUPOIDRA\((.+)\)([\?\!])$@ismU', $maskcode, $m)) {
305
                                $object_id         = $m[1];
116 daniel-mar 306
                                $ra_need_login     = $m[2] == '?';
250 daniel-mar 307
                                if ($object_id == '') throw new OIDplusException("SUPOIDRA logger mask requires OID");
115 daniel-mar 308
                                $obj = OIDplusObject::parse($object_id);
116 daniel-mar 309
                                if ($obj) {
310
                                        if ($ra_need_login) {
311
                                                foreach (OIDplus::authUtils()->loggedInRaList() as $ra) {
288 daniel-mar 312
                                                        if ($obj->userHasParentalWriteRights($ra)) $users[] = array($severity_online, $ra->raEmail());
116 daniel-mar 313
                                                }
314
                                        } else {
288 daniel-mar 315
                                                // $users[] = array($severity, $obj->getParent()->getRa()->raEmail());
116 daniel-mar 316
                                                foreach (OIDplusRA::getAllRAs() as $ra) {
288 daniel-mar 317
                                                        if ($obj->userHasParentalWriteRights($ra)) $users[] = array($severity, $ra->raEmail());
116 daniel-mar 318
                                                }
115 daniel-mar 319
                                        }
288 daniel-mar 320
                                } else {
321
                                        throw new OIDplusException("SUPOIDRA logger mask: Invalid object '$object_id'");
115 daniel-mar 322
                                }
323
                        }
324
 
325
                        // RA(x)?       Save log entry into the logbook of: Logged in RA "x"
288 daniel-mar 326
                        // Remove or replace "?" by "!" if the entity does not need to be logged in
327
                        else if (preg_match('@^RA\((.*)\)([\?\!])$@ismU', $maskcode, $m)) {
115 daniel-mar 328
                                $ra_email          = $m[1];
116 daniel-mar 329
                                $ra_need_login     = $m[2] == '?';
288 daniel-mar 330
                                if (!empty($ra_email)) {
331
                                        if ($ra_need_login && OIDplus::authUtils()->isRaLoggedIn($ra_email)) {
332
                                                $users[] = array($severity_online, $ra_email);
333
                                        } else if (!$ra_need_login) {
334
                                                $users[] = array($severity, $ra_email);
335
                                        }
115 daniel-mar 336
                                }
337
                        }
338
 
339
                        // A?   Save log entry into the logbook of: A logged in admin
288 daniel-mar 340
                        // Remove or replace "?" by "!" if the entity does not need to be logged in
115 daniel-mar 341
                        else if (preg_match('@^A([\?\!])$@ismU', $maskcode, $m)) {
116 daniel-mar 342
                                $admin_need_login = $m[1] == '?';
115 daniel-mar 343
                                if ($admin_need_login && OIDplus::authUtils()->isAdminLoggedIn()) {
288 daniel-mar 344
                                        $users[] = array($severity_online, 'admin');
115 daniel-mar 345
                                } else if (!$admin_need_login) {
288 daniel-mar 346
                                        $users[] = array($severity, 'admin');
115 daniel-mar 347
                                }
348
                        }
349
 
350
                        // Unexpected
351
                        else {
288 daniel-mar 352
                                throw new OIDplusException("Unexpected logger component '$maskcode' in mask code '$maskcodes'");
115 daniel-mar 353
                        }
116 daniel-mar 354
                }
115 daniel-mar 355
 
117 daniel-mar 356
                // Now write the log message
357
 
150 daniel-mar 358
                $addr = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null;
261 daniel-mar 359
                OIDplus::db()->query("insert into ###log (addr, unix_ts, event) values (?, ?, ?)", array($addr, time(), $event));
117 daniel-mar 360
                $log_id = OIDplus::db()->insert_id();
150 daniel-mar 361
                if ($log_id === false) {
261 daniel-mar 362
                        $res = OIDplus::db()->query("select max(id) as last_id from ###log");
251 daniel-mar 363
                        if ($res->num_rows() == 0) throw new OIDplusException("Could not log event");
236 daniel-mar 364
                        $row = $res->fetch_array();
150 daniel-mar 365
                        $log_id = $row['last_id'];
288 daniel-mar 366
                        if ($log_id == 0) throw new OIDplusException("Could not log event");
150 daniel-mar 367
                }
117 daniel-mar 368
 
288 daniel-mar 369
                $object_dupe_check = array();
370
                foreach ($objects as list($severity, $object)) {
371
                        if (in_array($object, $object_dupe_check)) continue;
372
                        $object_dupe_check[] = $object;
373
                        OIDplus::db()->query("insert into ###log_object (log_id, severity, object) values (?, ?, ?)", array($log_id, $severity, $object));
117 daniel-mar 374
                }
375
 
288 daniel-mar 376
                $user_dupe_check = array();
377
                foreach ($users as list($severity, $username)) {
378
                        if (in_array($username, $user_dupe_check)) continue;
379
                        $user_dupe_check[] = $username;
380
                        OIDplus::db()->query("insert into ###log_user (log_id, severity, username) values (?, ?, ?)", array($log_id, $severity, $username));
117 daniel-mar 381
                }
382
 
115 daniel-mar 383
        }
384
}