1 | <?php |
||||
2 | /** |
||||
3 | * EGroupware admin - access- and session-log |
||||
4 | * |
||||
5 | * @link http://www.egroupware.org |
||||
6 | * @author Ralf Becker <RalfBecker-AT-outdoor-training.de> |
||||
7 | * @package admin |
||||
8 | * @copyright (c) 2009-19 by Ralf Becker <RalfBecker-AT-outdoor-training.de> |
||||
9 | * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License |
||||
10 | */ |
||||
11 | |||||
12 | use EGroupware\Api; |
||||
13 | use EGroupware\Api\Etemplate; |
||||
14 | |||||
15 | /** |
||||
16 | * Show EGroupware access- and session-log |
||||
17 | */ |
||||
18 | class admin_accesslog |
||||
19 | { |
||||
20 | /** |
||||
21 | * Which methods of this class can be called as menuation |
||||
22 | * |
||||
23 | * @var array |
||||
24 | */ |
||||
25 | public $public_functions = array( |
||||
26 | 'index' => true, |
||||
27 | 'sessions' => true, |
||||
28 | ); |
||||
29 | |||||
30 | /** |
||||
31 | * Our storage object |
||||
32 | * |
||||
33 | * @var Api\Storage\Base |
||||
34 | */ |
||||
35 | protected $so; |
||||
36 | |||||
37 | /** |
||||
38 | * Name of our table |
||||
39 | */ |
||||
40 | const TABLE = 'egw_access_log'; |
||||
41 | /** |
||||
42 | * Name of app the table is registered |
||||
43 | */ |
||||
44 | const APP = 'phpgwapi'; |
||||
45 | |||||
46 | /** |
||||
47 | * Constructor |
||||
48 | * |
||||
49 | */ |
||||
50 | function __construct() |
||||
51 | { |
||||
52 | $this->so = new Api\Storage\Base(self::APP,self::TABLE,null,'',true); |
||||
53 | $this->so->timestamps = array('li', 'lo', 'session_dla', 'notification_heartbeat'); |
||||
54 | } |
||||
55 | |||||
56 | /** |
||||
57 | * query rows for the nextmatch widget |
||||
58 | * |
||||
59 | * @param array $query with keys 'start', 'search', 'order', 'sort', 'col_filter' and |
||||
60 | * 'session_list' true: all sessions, false: whole access-log, 'active': only sessions with session-status active (browser, no sync) |
||||
61 | * @param array &$rows returned rows/competitions |
||||
62 | * @param array &$readonlys eg. to disable buttons based on acl, not use here, maybe in a derived class |
||||
63 | * @return int total number of rows |
||||
64 | */ |
||||
65 | function get_rows($query,&$rows,&$readonlys) |
||||
66 | { |
||||
67 | $heartbeat_limit = Api\Session::heartbeat_limit(); |
||||
68 | |||||
69 | if ($query['session_list']) // filter active sessions |
||||
70 | { |
||||
71 | $query['col_filter']['lo'] = null; // not logged out |
||||
72 | $query['col_filter'][0] = 'session_dla > '.(int)(time() - $GLOBALS['egw_info']['server']['sessions_timeout']); |
||||
73 | switch((string)$query['session_list']) |
||||
74 | { |
||||
75 | case 'active': // remove status != 'active', eg. CalDAV/eSync |
||||
76 | $query['col_filter'][1] = "notification_heartbeat > $heartbeat_limit"; |
||||
77 | $query['col_filter'][3] = "session_php NOT LIKE '% %'"; // remove blocked, bad login, etc |
||||
78 | break; |
||||
79 | default: |
||||
80 | $query['col_filter'][1] = "(notification_heartbeat IS NULL OR notification_heartbeat > $heartbeat_limit)"; |
||||
81 | break; |
||||
82 | } |
||||
83 | $query['col_filter'][2] = 'account_id>0'; |
||||
84 | } |
||||
85 | $total = $this->so->get_rows($query,$rows,$readonlys); |
||||
86 | |||||
87 | $heartbeat_limit_user = Api\DateTime::server2user($heartbeat_limit, 'ts'); |
||||
88 | |||||
89 | foreach($rows as &$row) |
||||
90 | { |
||||
91 | $row['sessionstatus'] = 'success'; |
||||
92 | if ($row['notification_heartbeat'] > $heartbeat_limit_user) |
||||
93 | { |
||||
94 | $row['sessionstatus'] = 'active'; |
||||
95 | } |
||||
96 | if (stripos($row['session_php'],'blocked') !== false || |
||||
97 | stripos($row['session_php'],'bad login') !== false || |
||||
98 | strpos($row['session_php'],' ') !== false) |
||||
99 | { |
||||
100 | $row['sessionstatus'] = $row['session_php']; |
||||
101 | } |
||||
102 | if ($row['lo']) { |
||||
103 | $row['total'] = ($row['lo'] - $row['li']) / 60; |
||||
104 | $row['sessionstatus'] = 'logged out'; |
||||
105 | } |
||||
106 | // eg. for bad login or password |
||||
107 | if (!$row['account_id']) $row['alt_loginid'] = ($row['loginid']?$row['loginid']:lang('none')); |
||||
108 | |||||
109 | // do not allow to kill or select own session |
||||
110 | if ($GLOBALS['egw']->session->sessionid_access_log == $row['sessionid'] && $query['session_list']) |
||||
111 | { |
||||
112 | $row['class'] .= ' rowNoDelete '; |
||||
113 | } |
||||
114 | // do not allow to delete access log off active sessions |
||||
115 | if (!$row['lo'] && $row['session_dla'] > time()-$GLOBALS['egw_info']['server']['sessions_timeout'] && |
||||
116 | in_array($row['sessionstatus'], array('active', 'success')) && !$query['session_list']) |
||||
117 | { |
||||
118 | $row['class'] .= ' rowNoDelete '; |
||||
119 | } |
||||
120 | $row['sessionstatus'] = lang($row['sessionstatus']); |
||||
121 | unset($row['session_php']); // for security reasons, do NOT give real PHP sessionid to UI |
||||
122 | |||||
123 | $row['os_browser'] = Api\Header\UserAgent::osBrowser($row['user_agent']); |
||||
124 | } |
||||
125 | if ($query['session_list']) |
||||
126 | { |
||||
127 | $rows['no_total'] = $rows['no_lo'] = true; |
||||
128 | } |
||||
129 | $GLOBALS['egw_info']['flags']['app_header'] = lang('Admin').' - '. |
||||
130 | ($query['session_list'] ? lang('View sessions') : lang('View Access Log')). |
||||
131 | ($query['col_filter']['account_id'] ? ': '.Api\Accounts::username($query['col_filter']['account_id']) : ''); |
||||
132 | |||||
133 | return $total; |
||||
134 | } |
||||
135 | |||||
136 | /** |
||||
137 | * Display the access log or session list |
||||
138 | * |
||||
139 | * @param array $content =null |
||||
140 | * @param string $msg ='' |
||||
141 | * @param boolean $sessions_list =false |
||||
142 | */ |
||||
143 | function index(array $content=null, $msg='', $sessions_list=false) |
||||
144 | { |
||||
145 | |||||
146 | if (is_array($content)) $sessions_list = $content['nm']['session_list']; |
||||
147 | |||||
148 | // check if user has access to requested functionality |
||||
149 | if ($GLOBALS['egw']->acl->check($sessions_list ? 'current_sessions' : 'access_log_acces',1,'admin')) |
||||
150 | { |
||||
151 | $GLOBALS['egw']->redirect_link('/index.php'); |
||||
152 | } |
||||
153 | |||||
154 | if(!isset($content)) |
||||
155 | { |
||||
156 | $content['nm'] = array( |
||||
157 | 'get_rows' => 'admin.admin_accesslog.get_rows', // I method/callback to request the data for the rows eg. 'notes.bo.get_rows' |
||||
158 | 'no_filter' => True, // I disable the 1. filter |
||||
159 | 'no_filter2' => True, // I disable the 2. filter (params are the same as for filter) |
||||
160 | 'no_cat' => True, // I disable the cat-selectbox |
||||
161 | 'header_left' => false, // I template to show left of the range-value, left-aligned (optional) |
||||
162 | 'header_right' => false, // I template to show right of the range-value, right-aligned (optional) |
||||
163 | 'never_hide' => True, // I never hide the nextmatch-line if less then maxmatch entries |
||||
164 | 'lettersearch' => false, // I show a lettersearch |
||||
165 | 'start' => 0, // IO position in list |
||||
166 | 'order' => 'li', // IO name of the column to sort after (optional for the sortheaders) |
||||
167 | 'sort' => 'DESC', // IO direction of the sort: 'ASC' or 'DESC' |
||||
168 | 'default_cols' => '!session_action', // I columns to use if there's no user or default pref (! as first char uses all but the named columns), default all columns |
||||
169 | 'csv_fields' => false, // I false=disable csv export, true or unset=enable it with auto-detected fieldnames, |
||||
170 | //or array with name=>label or name=>array('label'=>label,'type'=>type) pairs (type is a eT widget-type) |
||||
171 | 'actions' => $this->get_actions($sessions_list), |
||||
172 | 'placeholder_actions' => false, |
||||
173 | 'row_id' => 'sessionid', |
||||
174 | ); |
||||
175 | if ((int)$_GET['account_id']) |
||||
176 | { |
||||
177 | $content['nm']['col_filter']['account_id'] = (int)$_GET['account_id']; |
||||
178 | } |
||||
179 | if ($sessions_list) |
||||
180 | { |
||||
181 | $content['nm']['order'] = 'session_dla'; |
||||
182 | $content['nm']['options-selectcols'] = array( |
||||
183 | 'lo' => false, |
||||
184 | 'total' => false, |
||||
185 | ); |
||||
186 | } |
||||
187 | $content['nm']['session_list'] = $sessions_list; |
||||
188 | } |
||||
189 | //error_log(__METHOD__. ' accesslog =>' . array2string($content['nm']['selected'])); |
||||
190 | if ($content['nm']['action']) |
||||
191 | { |
||||
192 | if ($content['nm']['select_all']) |
||||
193 | { |
||||
194 | // get the whole selection |
||||
195 | $query = array( |
||||
196 | 'search' => $content['nm']['search'], |
||||
197 | 'col_filter' => $content['nm']['col_filter'] |
||||
198 | ); |
||||
199 | |||||
200 | @set_time_limit(0); // switch off the execution time limit, as it's for big selections to small |
||||
0 ignored issues
–
show
|
|||||
201 | $query['num_rows'] = -1; // all |
||||
202 | $all = $readonlys = array(); |
||||
203 | $this->get_rows($query,$all,$readonlys); |
||||
204 | $content['nm']['selected'] = array(); |
||||
205 | foreach($all as $session) |
||||
206 | { |
||||
207 | $content['nm']['selected'][] = $session[$content['nm']['row_id']]; |
||||
208 | } |
||||
209 | } |
||||
210 | if (!count($content['nm']['selected']) && !$content['nm']['select_all']) |
||||
211 | { |
||||
212 | $msg = lang('You need to select some entries first!'); |
||||
213 | } |
||||
214 | else |
||||
215 | { |
||||
216 | $success = $failed = $action = $action_msg = null; |
||||
0 ignored issues
–
show
|
|||||
217 | if ($this->action($content['nm']['action'],$content['nm']['selected'] |
||||
218 | ,$success,$failed,$action_msg,$msg)) |
||||
0 ignored issues
–
show
The call to
admin_accesslog::action() has too many arguments starting with $msg .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||
219 | { // In case of action success |
||||
220 | switch ($action_msg) |
||||
221 | { |
||||
222 | case'deleted': |
||||
223 | $msg = lang('%1 log entries deleted.',$success); |
||||
0 ignored issues
–
show
The call to
lang() has too many arguments starting with $success .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||
224 | break; |
||||
225 | case'killed': |
||||
226 | $msg = lang('%1 sessions killed',$success); |
||||
227 | } |
||||
228 | } |
||||
229 | elseif($failed) // In case of action failiure |
||||
230 | { |
||||
231 | switch ($action_msg) |
||||
232 | { |
||||
233 | case'deleted': |
||||
234 | $msg = lang('Error deleting log entry!'); |
||||
235 | break; |
||||
236 | case'killed': |
||||
237 | $msg = lang('Permission denied!'); |
||||
238 | } |
||||
239 | } |
||||
240 | } |
||||
241 | } |
||||
242 | |||||
243 | $content['msg'] = $msg; |
||||
244 | $content['percent'] = 100.0 * $GLOBALS['egw']->db->query( |
||||
245 | 'SELECT ((SELECT COUNT(*) FROM '.self::TABLE.' WHERE lo != 0) / COUNT(*)) FROM '.self::TABLE, |
||||
246 | __LINE__,__FILE__)->fetchColumn(); |
||||
247 | |||||
248 | $tmpl = new Etemplate('admin.accesslog'); |
||||
249 | $tmpl->exec('admin.admin_accesslog.index', $content, array(), $readonlys, array( |
||||
250 | 'nm' => $content['nm'], |
||||
251 | )); |
||||
252 | } |
||||
253 | |||||
254 | /** |
||||
255 | * Apply an action to multiple logs |
||||
256 | * |
||||
257 | * @param type $action |
||||
258 | * @param type $checked |
||||
259 | * @param type $use_all |
||||
260 | * @param type $success |
||||
261 | * @param int $failed |
||||
262 | * @param type $action_msg |
||||
263 | * @return type number of failed |
||||
264 | */ |
||||
265 | function action($action,$checked,&$success,&$failed,&$action_msg) |
||||
266 | { |
||||
267 | $success = $failed = 0; |
||||
268 | //error_log(__METHOD__.'selected:' . array2string($checked). 'action:' . $action); |
||||
269 | switch ($action) |
||||
270 | { |
||||
271 | case "delete": |
||||
272 | $action_msg = "deleted"; |
||||
273 | $del_msg= $this->so->delete(array('sessionid' => $checked)); |
||||
274 | if ($checked && $del_msg) |
||||
275 | { |
||||
276 | $success = $del_msg; |
||||
277 | } |
||||
278 | else |
||||
279 | { |
||||
280 | $failed ++; |
||||
281 | } |
||||
282 | break; |
||||
283 | case "kill": |
||||
284 | $action_msg = "killed"; |
||||
285 | $sessionid = $checked; |
||||
286 | if (($key = array_search($GLOBALS['egw']->session->sessionid_access_log, $sessionid))) |
||||
287 | { |
||||
288 | unset($sessionid[$key]); // dont allow to kill own sessions |
||||
289 | } |
||||
290 | if ($GLOBALS['egw']->acl->check('current_sessions',8,'admin')) |
||||
291 | { |
||||
292 | $failed ++; |
||||
293 | } |
||||
294 | else |
||||
295 | { |
||||
296 | foreach((array)$sessionid as $id) |
||||
297 | { |
||||
298 | $GLOBALS['egw']->session->destroy($id); |
||||
299 | } |
||||
300 | $success= count($sessionid); |
||||
301 | } |
||||
302 | break; |
||||
303 | } |
||||
304 | return !$failed; |
||||
305 | } |
||||
306 | |||||
307 | /** |
||||
308 | * Get actions / context menu for index |
||||
309 | * |
||||
310 | * Changes here, require to log out, as $content['nm'] get stored in session! |
||||
311 | * |
||||
312 | * @return array see nextmatch_widget::egw_actions() |
||||
313 | */ |
||||
314 | private static function get_actions($sessions_list) |
||||
315 | { |
||||
316 | $group = 0; |
||||
317 | if ($sessions_list) |
||||
318 | { |
||||
319 | // error_log(__METHOD__. $sessions_list); |
||||
320 | $actions= array( |
||||
321 | 'kill' => array( |
||||
322 | 'caption' => 'Kill', |
||||
323 | 'confirm' => 'Kill this session', |
||||
324 | 'confirm_multiple' => 'Kill these sessions', |
||||
325 | 'group' => $group, |
||||
326 | 'disableClass' => 'rowNoDelete', |
||||
327 | ), |
||||
328 | ); |
||||
329 | |||||
330 | } |
||||
331 | else |
||||
332 | { |
||||
333 | $actions= array( |
||||
334 | 'delete' => array( |
||||
335 | 'caption' => 'Delete', |
||||
336 | 'confirm' => 'Delete this entry', |
||||
337 | 'confirm_multiple' => 'Delete these entries', |
||||
338 | 'group' => $group, |
||||
339 | 'disableClass' => 'rowNoDelete', |
||||
340 | ), |
||||
341 | ); |
||||
342 | } |
||||
343 | // Automatic select all doesn't work with only 1 action |
||||
344 | $actions['select_all'] = array( |
||||
345 | 'caption' => 'Select all', |
||||
346 | //'checkbox' => true, |
||||
347 | 'hint' => 'Select all entries', |
||||
348 | 'enabled' => true, |
||||
349 | 'shortcut' => array( |
||||
350 | 'keyCode' => 65, // A |
||||
351 | 'ctrl' => true, |
||||
352 | 'caption' => lang('Ctrl').'+A' |
||||
353 | ), |
||||
354 | 'group' => $group++, |
||||
355 | ); |
||||
356 | return $actions; |
||||
357 | } |
||||
358 | |||||
359 | /** |
||||
360 | * Display session list |
||||
361 | * |
||||
362 | * @param array $content =null |
||||
363 | * @param string $msg ='' |
||||
364 | */ |
||||
365 | function sessions(array $content=null, $msg='') |
||||
366 | { |
||||
367 | return $this->index($content, $msg, true); |
||||
0 ignored issues
–
show
Are you sure the usage of
$this->index($content, $msg, true) targeting admin_accesslog::index() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||
368 | } |
||||
369 | } |
||||
370 |
If you suppress an error, we recommend checking for the error condition explicitly: