2009-04-27 12:31:23 +02:00
< ? php
/**
2016-04-27 21:12:20 +02:00
* EGroupware admin - access - and session - log
2009-04-27 12:31:23 +02:00
*
* @ link http :// www . egroupware . org
* @ author Ralf Becker < RalfBecker - AT - outdoor - training . de >
* @ package admin
2019-12-14 12:09:22 +01:00
* @ copyright ( c ) 2009 - 19 by Ralf Becker < RalfBecker - AT - outdoor - training . de >
2009-04-27 12:31:23 +02:00
* @ license http :// opensource . org / licenses / gpl - license . php GPL - GNU General Public License
*/
2016-04-27 21:12:20 +02:00
use EGroupware\Api ;
use EGroupware\Api\Etemplate ;
2009-04-27 12:31:23 +02:00
/**
2011-04-13 16:11:09 +02:00
* Show EGroupware access - and session - log
2009-04-27 12:31:23 +02:00
*/
class admin_accesslog
{
/**
* Which methods of this class can be called as menuation
*
* @ var array
*/
public $public_functions = array (
'index' => true ,
2011-04-13 16:11:09 +02:00
'sessions' => true ,
2009-04-27 12:31:23 +02:00
);
/**
* Our storage object
*
2016-04-27 21:12:20 +02:00
* @ var Api\Storage\Base
2009-04-27 12:31:23 +02:00
*/
protected $so ;
/**
* Name of our table
*/
const TABLE = 'egw_access_log' ;
/**
* Name of app the table is registered
*/
const APP = 'phpgwapi' ;
/**
* Constructor
*
*/
function __construct ()
{
2016-04-27 21:12:20 +02:00
$this -> so = new Api\Storage\Base ( self :: APP , self :: TABLE , null , '' , true );
2014-10-09 09:45:59 +02:00
$this -> so -> timestamps = array ( 'li' , 'lo' , 'session_dla' , 'notification_heartbeat' );
2009-04-27 12:31:23 +02:00
}
/**
* query rows for the nextmatch widget
*
2014-10-09 09:45:59 +02:00
* @ param array $query with keys 'start' , 'search' , 'order' , 'sort' , 'col_filter' and
* 'session_list' true : all sessions , false : whole access - log , 'active' : only sessions with session - status active ( browser , no sync )
2009-04-27 12:31:23 +02:00
* @ param array & $rows returned rows / competitions
* @ param array & $readonlys eg . to disable buttons based on acl , not use here , maybe in a derived class
* @ return int total number of rows
*/
function get_rows ( $query , & $rows , & $readonlys )
{
2016-04-27 21:12:20 +02:00
$heartbeat_limit = Api\Session :: heartbeat_limit ();
2011-04-13 16:11:09 +02:00
if ( $query [ 'session_list' ]) // filter active sessions
{
$query [ 'col_filter' ][ 'lo' ] = null ; // not logged out
$query [ 'col_filter' ][ 0 ] = 'session_dla > ' . ( int )( time () - $GLOBALS [ 'egw_info' ][ 'server' ][ 'sessions_timeout' ]);
2020-10-06 11:59:19 +02:00
// for push via fallback (no native push) we use the heartbeat (constant polling of notification app)
if ( Api\Json\Push :: onlyFallback ())
{
$active_query = " notification_heartbeat > $heartbeat_limit " ;
}
else
{
// for native push we ask the push-server who is active
$online = ( array ) Api\Json\Push :: online ();
$active_query = $GLOBALS [ 'egw' ] -> db -> expression ( self :: TABLE , [ 'account_id' => $online ]);
}
2014-10-09 09:45:59 +02:00
switch (( string ) $query [ 'session_list' ])
{
case 'active' : // remove status != 'active', eg. CalDAV/eSync
2020-10-06 11:59:19 +02:00
$query [ 'col_filter' ][ 1 ] = $active_query ;
2014-10-09 09:45:59 +02:00
$query [ 'col_filter' ][ 3 ] = " session_php NOT LIKE '% %' " ; // remove blocked, bad login, etc
break ;
default :
2020-10-06 11:59:19 +02:00
$query [ 'col_filter' ][ 1 ] = " (notification_heartbeat IS NULL OR $active_query ) " ;
2014-10-09 09:45:59 +02:00
break ;
}
2014-08-19 19:15:50 +02:00
$query [ 'col_filter' ][ 2 ] = 'account_id>0' ;
2011-04-13 16:11:09 +02:00
}
2009-04-27 12:31:23 +02:00
$total = $this -> so -> get_rows ( $query , $rows , $readonlys );
2016-04-27 21:12:20 +02:00
$heartbeat_limit_user = Api\DateTime :: server2user ( $heartbeat_limit , 'ts' );
2011-04-13 16:11:09 +02:00
2009-04-27 12:31:23 +02:00
foreach ( $rows as & $row )
{
2014-11-10 17:49:21 +01:00
$row [ 'sessionstatus' ] = 'success' ;
2020-10-06 11:59:19 +02:00
if ( isset ( $online ) ?
// we still need to check notification_heartbeat to distinguish from non-interactive session like *DAV
isset ( $row [ 'notification_heartbeat' ]) && in_array ( $row [ 'account_id' ], $online ) :
$row [ 'notification_heartbeat' ] > $heartbeat_limit_user )
2011-04-13 16:11:09 +02:00
{
2014-11-10 17:49:21 +01:00
$row [ 'sessionstatus' ] = 'active' ;
2011-04-13 16:11:09 +02:00
}
if ( stripos ( $row [ 'session_php' ], 'blocked' ) !== false ||
stripos ( $row [ 'session_php' ], 'bad login' ) !== false ||
2014-10-09 09:45:59 +02:00
strpos ( $row [ 'session_php' ], ' ' ) !== false )
2009-10-06 10:28:31 +02:00
{
2011-04-13 16:11:09 +02:00
$row [ 'sessionstatus' ] = $row [ 'session_php' ];
2009-10-06 10:28:31 +02:00
}
if ( $row [ 'lo' ]) {
$row [ 'total' ] = ( $row [ 'lo' ] - $row [ 'li' ]) / 60 ;
2014-11-10 17:49:21 +01:00
$row [ 'sessionstatus' ] = 'logged out' ;
2009-10-06 10:28:31 +02:00
}
2011-04-13 16:11:09 +02:00
// eg. for bad login or password
2014-05-13 12:42:45 +02:00
if ( ! $row [ 'account_id' ]) $row [ 'alt_loginid' ] = ( $row [ 'loginid' ] ? $row [ 'loginid' ] : lang ( 'none' ));
2011-04-13 16:11:09 +02:00
// do not allow to kill or select own session
if ( $GLOBALS [ 'egw' ] -> session -> sessionid_access_log == $row [ 'sessionid' ] && $query [ 'session_list' ])
{
2014-01-20 12:27:23 +01:00
$row [ 'class' ] .= ' rowNoDelete ' ;
2011-04-13 16:11:09 +02:00
}
// do not allow to delete access log off active sessions
2014-11-10 17:49:21 +01:00
if ( ! $row [ 'lo' ] && $row [ 'session_dla' ] > time () - $GLOBALS [ 'egw_info' ][ 'server' ][ 'sessions_timeout' ] &&
in_array ( $row [ 'sessionstatus' ], array ( 'active' , 'success' )) && ! $query [ 'session_list' ])
2011-04-13 16:11:09 +02:00
{
2014-01-20 12:27:23 +01:00
$row [ 'class' ] .= ' rowNoDelete ' ;
2011-04-13 16:11:09 +02:00
}
2014-11-10 17:49:21 +01:00
$row [ 'sessionstatus' ] = lang ( $row [ 'sessionstatus' ]);
2011-04-13 16:11:09 +02:00
unset ( $row [ 'session_php' ]); // for security reasons, do NOT give real PHP sessionid to UI
2019-06-05 18:18:11 +02:00
$row [ 'os_browser' ] = Api\Header\UserAgent :: osBrowser ( $row [ 'user_agent' ]);
2011-04-13 16:11:09 +02:00
}
if ( $query [ 'session_list' ])
{
$rows [ 'no_total' ] = $rows [ 'no_lo' ] = true ;
2009-04-27 12:31:23 +02:00
}
2011-04-13 16:11:09 +02:00
$GLOBALS [ 'egw_info' ][ 'flags' ][ 'app_header' ] = lang ( 'Admin' ) . ' - ' .
( $query [ 'session_list' ] ? lang ( 'View sessions' ) : lang ( 'View Access Log' )) .
2016-04-27 21:12:20 +02:00
( $query [ 'col_filter' ][ 'account_id' ] ? ': ' . Api\Accounts :: username ( $query [ 'col_filter' ][ 'account_id' ]) : '' );
2009-04-27 12:31:23 +02:00
return $total ;
}
/**
2011-04-13 16:11:09 +02:00
* Display the access log or session list
2009-04-27 12:31:23 +02:00
*
2014-10-09 09:45:59 +02:00
* @ param array $content = null
* @ param string $msg = ''
* @ param boolean $sessions_list = false
2009-04-27 12:31:23 +02:00
*/
2011-04-13 16:11:09 +02:00
function index ( array $content = null , $msg = '' , $sessions_list = false )
2009-04-27 12:31:23 +02:00
{
2014-01-20 12:27:23 +01:00
2011-04-13 16:11:09 +02:00
if ( is_array ( $content )) $sessions_list = $content [ 'nm' ][ 'session_list' ];
// check if user has access to requested functionality
2015-08-04 11:55:29 +02:00
if ( $GLOBALS [ 'egw' ] -> acl -> check ( $sessions_list ? 'current_sessions' : 'access_log_acces' , 1 , 'admin' ))
2011-04-13 16:11:09 +02:00
{
$GLOBALS [ 'egw' ] -> redirect_link ( '/index.php' );
}
2009-04-27 12:31:23 +02:00
if ( ! isset ( $content ))
{
$content [ 'nm' ] = array (
'get_rows' => 'admin.admin_accesslog.get_rows' , // I method/callback to request the data for the rows eg. 'notes.bo.get_rows'
'no_filter' => True , // I disable the 1. filter
'no_filter2' => True , // I disable the 2. filter (params are the same as for filter)
'no_cat' => True , // I disable the cat-selectbox
'header_left' => false , // I template to show left of the range-value, left-aligned (optional)
'header_right' => false , // I template to show right of the range-value, right-aligned (optional)
'never_hide' => True , // I never hide the nextmatch-line if less then maxmatch entries
'lettersearch' => false , // I show a lettersearch
'start' => 0 , // IO position in list
'order' => 'li' , // IO name of the column to sort after (optional for the sortheaders)
'sort' => 'DESC' , // IO direction of the sort: 'ASC' or 'DESC'
2019-12-14 12:09:22 +01:00
'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
2009-04-27 12:31:23 +02:00
'csv_fields' => false , // I false=disable csv export, true or unset=enable it with auto-detected fieldnames,
//or array with name=>label or name=>array('label'=>label,'type'=>type) pairs (type is a eT widget-type)
2014-01-20 12:27:23 +01:00
'actions' => $this -> get_actions ( $sessions_list ),
2014-05-14 01:19:34 +02:00
'placeholder_actions' => false ,
2014-01-20 12:27:23 +01:00
'row_id' => 'sessionid' ,
2009-04-27 12:31:23 +02:00
);
if (( int ) $_GET [ 'account_id' ])
{
$content [ 'nm' ][ 'col_filter' ][ 'account_id' ] = ( int ) $_GET [ 'account_id' ];
}
2011-04-13 16:11:09 +02:00
if ( $sessions_list )
{
$content [ 'nm' ][ 'order' ] = 'session_dla' ;
$content [ 'nm' ][ 'options-selectcols' ] = array (
'lo' => false ,
'total' => false ,
);
}
$content [ 'nm' ][ 'session_list' ] = $sessions_list ;
2009-04-27 12:31:23 +02:00
}
2014-05-20 21:04:50 +02:00
//error_log(__METHOD__. ' accesslog =>' . array2string($content['nm']['selected']));
2014-01-20 12:27:23 +01:00
if ( $content [ 'nm' ][ 'action' ])
2009-04-27 12:31:23 +02:00
{
2014-05-20 21:04:50 +02:00
if ( $content [ 'nm' ][ 'select_all' ])
{
// get the whole selection
$query = array (
'search' => $content [ 'nm' ][ 'search' ],
'col_filter' => $content [ 'nm' ][ 'col_filter' ]
);
2014-01-20 12:27:23 +01:00
2014-05-20 21:04:50 +02:00
@ set_time_limit ( 0 ); // switch off the execution time limit, as it's for big selections to small
$query [ 'num_rows' ] = - 1 ; // all
2014-10-09 09:45:59 +02:00
$all = $readonlys = array ();
$this -> get_rows ( $query , $all , $readonlys );
2014-05-20 21:04:50 +02:00
$content [ 'nm' ][ 'selected' ] = array ();
foreach ( $all as $session )
{
$content [ 'nm' ][ 'selected' ][] = $session [ $content [ 'nm' ][ 'row_id' ]];
}
}
2014-01-20 12:27:23 +01:00
if ( ! count ( $content [ 'nm' ][ 'selected' ]) && ! $content [ 'nm' ][ 'select_all' ])
2011-04-13 16:11:09 +02:00
{
2014-01-20 12:27:23 +01:00
$msg = lang ( 'You need to select some entries first!' );
2011-04-13 16:11:09 +02:00
}
else
{
2014-10-09 09:45:59 +02:00
$success = $failed = $action = $action_msg = null ;
2014-01-20 12:27:23 +01:00
if ( $this -> action ( $content [ 'nm' ][ 'action' ], $content [ 'nm' ][ 'selected' ]
, $success , $failed , $action_msg , $msg ))
{ // In case of action success
switch ( $action_msg )
{
case 'deleted' :
$msg = lang ( '%1 log entries deleted.' , $success );
break ;
case 'killed' :
$msg = lang ( '%1 sessions killed' , $success );
}
}
elseif ( $failed ) // In case of action failiure
2011-04-13 16:11:09 +02:00
{
2014-01-20 12:27:23 +01:00
switch ( $action_msg )
{
case 'deleted' :
$msg = lang ( 'Error deleting log entry!' );
break ;
case 'killed' :
$msg = lang ( 'Permission denied!' );
}
2011-04-13 16:11:09 +02:00
}
2009-04-27 12:31:23 +02:00
}
}
2011-04-13 16:11:09 +02:00
$content [ 'msg' ] = $msg ;
2009-04-27 12:31:23 +02:00
$content [ 'percent' ] = 100.0 * $GLOBALS [ 'egw' ] -> db -> query (
'SELECT ((SELECT COUNT(*) FROM ' . self :: TABLE . ' WHERE lo != 0) / COUNT(*)) FROM ' . self :: TABLE ,
__LINE__ , __FILE__ ) -> fetchColumn ();
2016-04-27 21:12:20 +02:00
$tmpl = new Etemplate ( 'admin.accesslog' );
2014-10-09 09:45:59 +02:00
$tmpl -> exec ( 'admin.admin_accesslog.index' , $content , array (), $readonlys , array (
2009-04-27 12:31:23 +02:00
'nm' => $content [ 'nm' ],
));
}
2011-04-13 16:11:09 +02:00
2014-01-20 12:27:23 +01:00
/**
* Apply an action to multiple logs
*
* @ param type $action
* @ param type $checked
* @ param type $use_all
* @ param type $success
* @ param int $failed
* @ param type $action_msg
* @ return type number of failed
*/
2014-10-09 09:45:59 +02:00
function action ( $action , $checked , & $success , & $failed , & $action_msg )
2014-01-20 12:27:23 +01:00
{
$success = $failed = 0 ;
2014-05-20 21:04:50 +02:00
//error_log(__METHOD__.'selected:' . array2string($checked). 'action:' . $action);
2014-01-20 12:27:23 +01:00
switch ( $action )
{
case " delete " :
$action_msg = " deleted " ;
$del_msg = $this -> so -> delete ( array ( 'sessionid' => $checked ));
if ( $checked && $del_msg )
{
$success = $del_msg ;
}
else
{
$failed ++ ;
}
break ;
case " kill " :
$action_msg = " killed " ;
$sessionid = $checked ;
if (( $key = array_search ( $GLOBALS [ 'egw' ] -> session -> sessionid_access_log , $sessionid )))
{
unset ( $sessionid [ $key ]); // dont allow to kill own sessions
}
2015-08-04 11:55:29 +02:00
if ( $GLOBALS [ 'egw' ] -> acl -> check ( 'current_sessions' , 8 , 'admin' ))
2014-01-20 12:27:23 +01:00
{
$failed ++ ;
}
else
{
foreach (( array ) $sessionid as $id )
{
$GLOBALS [ 'egw' ] -> session -> destroy ( $id );
}
$success = count ( $sessionid );
}
break ;
}
return ! $failed ;
}
/**
* Get actions / context menu for index
*
* Changes here , require to log out , as $content [ 'nm' ] get stored in session !
*
* @ return array see nextmatch_widget :: egw_actions ()
*/
private static function get_actions ( $sessions_list )
{
2014-10-09 09:45:59 +02:00
$group = 0 ;
2014-01-20 12:27:23 +01:00
if ( $sessions_list )
{
// error_log(__METHOD__. $sessions_list);
$actions = array (
'kill' => array (
'caption' => 'Kill' ,
'confirm' => 'Kill this session' ,
'confirm_multiple' => 'Kill these sessions' ,
'group' => $group ,
2014-05-26 19:39:59 +02:00
'disableClass' => 'rowNoDelete' ,
2014-01-20 12:27:23 +01:00
),
);
}
else
{
$actions = array (
'delete' => array (
'caption' => 'Delete' ,
'confirm' => 'Delete this entry' ,
'confirm_multiple' => 'Delete these entries' ,
'group' => $group ,
'disableClass' => 'rowNoDelete' ,
),
);
}
2014-05-20 21:04:50 +02:00
// Automatic select all doesn't work with only 1 action
$actions [ 'select_all' ] = array (
'caption' => 'Select all' ,
//'checkbox' => true,
'hint' => 'Select all entries' ,
'enabled' => true ,
'shortcut' => array (
'keyCode' => 65 , // A
'ctrl' => true ,
2016-09-09 12:54:52 +02:00
'caption' => lang ( 'Ctrl' ) . '+A'
2014-05-20 21:04:50 +02:00
),
'group' => $group ++ ,
);
2014-01-20 12:27:23 +01:00
return $actions ;
}
2011-04-13 16:11:09 +02:00
/**
* Display session list
*
2014-10-09 09:45:59 +02:00
* @ param array $content = null
* @ param string $msg = ''
2011-04-13 16:11:09 +02:00
*/
function sessions ( array $content = null , $msg = '' )
{
2014-10-09 09:45:59 +02:00
return $this -> index ( $content , $msg , true );
2011-04-13 16:11:09 +02:00
}
2009-10-06 10:28:31 +02:00
}