mirror of https://github.com/MISP/MISP
new: [internal] Show auth key usage in key view page
parent
d7c027fe91
commit
ee8a495d89
|
@ -236,26 +236,11 @@ class AppController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
$userMonitoringEnabled = false;
|
||||
if ($this->Auth->user()) {
|
||||
$user = $this->Auth->user();
|
||||
Configure::write('CurrentUserId', $user['id']);
|
||||
$this->User->setMonitoring($user);
|
||||
if (Configure::read('MISP.log_user_ips')) {
|
||||
$redis = $this->{$this->modelClass}->setupRedis();
|
||||
if ($redis) {
|
||||
$remoteAddress = trim($_SERVER['REMOTE_ADDR']);
|
||||
$redis->set('misp:ip_user:' . $remoteAddress, $user['id']);
|
||||
$redis->expire('misp:ip_user:' . $remoteAddress, 60 * 60 * 24 * 30); // 30 days
|
||||
$redis->sadd('misp:user_ip:' . $user['id'], $remoteAddress);
|
||||
|
||||
// Allow to log key usage
|
||||
if (isset($user['authkey_id']) && Configure::read('MISP.log_user_ips_auth')) {
|
||||
$key = "misp:authkey:{$user['authkey_id']}";
|
||||
$hashKey = date("Y-m-d") . ":$remoteAddress";
|
||||
$redis->hIncrBy($key, $hashKey, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
$userMonitoringEnabled = $this->__accessMonitor($user);
|
||||
// update script
|
||||
if ($user['Role']['perm_site_admin'] || (Configure::read('MISP.live') && !$this->_isRest())) {
|
||||
$this->{$this->modelClass}->runUpdates();
|
||||
|
@ -325,7 +310,7 @@ class AppController extends Controller
|
|||
|
||||
if (
|
||||
Configure::read('MISP.log_paranoid') ||
|
||||
!empty(Configure::read('Security.monitored'))
|
||||
$userMonitoringEnabled
|
||||
) {
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
$this->Log->create();
|
||||
|
@ -337,7 +322,7 @@ class AppController extends Controller
|
|||
) &&
|
||||
(
|
||||
!empty(Configure::read('MISP.log_paranoid_include_post_body')) ||
|
||||
!empty(Configure::read('Security.monitored'))
|
||||
$userMonitoringEnabled
|
||||
)
|
||||
) {
|
||||
$payload = $this->request->input();
|
||||
|
@ -635,6 +620,53 @@ class AppController extends Controller
|
|||
return in_array($this->action, $actionsToCheck[$controller], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* User access monitoring
|
||||
* @param array $user
|
||||
* @return bool True is user monitoring is enabled
|
||||
*/
|
||||
private function __accessMonitor(array $user)
|
||||
{
|
||||
$userMonitoringEnabled = Configure::read('Security.user_monitoring_enabled');
|
||||
$logUserIps = Configure::read('MISP.log_user_ips');
|
||||
|
||||
if (!$userMonitoringEnabled && !$logUserIps) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$redis = $this->User->setupRedis();
|
||||
if (!$redis) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($logUserIps) {
|
||||
$remoteAddress = trim($_SERVER['REMOTE_ADDR']);
|
||||
|
||||
$pipe = $redis->multi(Redis::PIPELINE);
|
||||
// keep for 30 days
|
||||
$pipe->setex('misp:ip_user:' . $remoteAddress, 60 * 60 * 24 * 30, $user['id']);
|
||||
$pipe->sadd('misp:user_ip:' . $user['id'], $remoteAddress);
|
||||
|
||||
// Log key usage if enabled
|
||||
if (isset($user['authkey_id']) && Configure::read('MISP.log_user_ips_auth')) {
|
||||
// Use request time if defined
|
||||
$time = isset($_SERVER['REQUEST_TIME']) ? $_SERVER['REQUEST_TIME'] : time();
|
||||
$hashKey = date("Y-m-d", $time) . ":$remoteAddress";
|
||||
$pipe->hIncrBy("misp:authkey_usage:{$user['authkey_id']}", $hashKey, 1);
|
||||
// delete after one year of inactivity
|
||||
$pipe->expire("misp:authkey_usage:{$user['authkey_id']}", 3600 * 24 * 365);
|
||||
$pipe->set("misp:authkey_last_usage:{$user['authkey_id']}", $time);
|
||||
}
|
||||
$pipe->exec();
|
||||
}
|
||||
|
||||
// Return true for the current user if monitoring of this user is enabled in Redis.
|
||||
if ($userMonitoringEnabled && $redis->sismember('misp:monitored_users', $user['id'])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function __rateLimitCheck()
|
||||
{
|
||||
$info = array();
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property AuthKey $AuthKey
|
||||
*/
|
||||
class AuthKeysController extends AppController
|
||||
{
|
||||
public $components = array(
|
||||
|
@ -26,10 +29,15 @@ class AuthKeysController extends AppController
|
|||
$this->CRUD->index([
|
||||
'filters' => ['User.username', 'authkey', 'comment', 'User.id'],
|
||||
'quickFilters' => ['authkey', 'comment'],
|
||||
'contain' => ['User'],
|
||||
'contain' => ['User.id', 'User.email'],
|
||||
'conditions' => $conditions,
|
||||
'afterFind' => function (array $authKeys) {
|
||||
$keyUsageEnabled = Configure::read('MISP.log_user_ips') && Configure::read('MISP.log_user_ips_auth');
|
||||
foreach ($authKeys as &$authKey) {
|
||||
if ($keyUsageEnabled) {
|
||||
$lastUsed = $this->AuthKey->getKeyUsage($authKey['AuthKey']['id'])[1];
|
||||
$key['AuthKey']['last_used'] = $lastUsed ? $lastUsed->format('c') : null;
|
||||
}
|
||||
unset($authKey['AuthKey']['authkey']);
|
||||
}
|
||||
return $authKeys;
|
||||
|
@ -101,6 +109,13 @@ class AuthKeysController extends AppController
|
|||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
|
||||
if (Configure::read('MISP.log_user_ips') && Configure::read('MISP.log_user_ips_auth')) {
|
||||
list($keyUsage, $lastUsed) = $this->AuthKey->getKeyUsage($id);
|
||||
$this->set('keyUsage', $keyUsage);
|
||||
$this->set('lastUsed', $lastUsed);
|
||||
}
|
||||
|
||||
$this->set('menuData', [
|
||||
'menuList' => $this->_isSiteAdmin() ? 'admin' : 'globalActions',
|
||||
'menuItem' => 'authKeyView',
|
||||
|
|
|
@ -109,6 +109,34 @@ class AuthKey extends AppModel
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getKeyUsage($id)
|
||||
{
|
||||
$redis = $this->setupRedisWithException();
|
||||
$data = $redis->hGetAll("misp:authkey_usage:$id");
|
||||
|
||||
$output = [];
|
||||
foreach ($data as $key => $count) {
|
||||
list($date, $ip) = explode(':', $key);
|
||||
if (isset($output[$date])) {
|
||||
$output[$date] += $count;
|
||||
} else {
|
||||
$output[$date] = $count;
|
||||
}
|
||||
}
|
||||
// Data from redis are not sorted
|
||||
ksort($output);
|
||||
|
||||
$lastUsage = $redis->get("misp:authkey_last_usage:$id");
|
||||
$lastUsage = $lastUsage === false ? null : new DateTime("@$lastUsage");
|
||||
|
||||
return [$output, $lastUsage];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AbstractPasswordHasher
|
||||
*/
|
||||
|
|
|
@ -1294,24 +1294,6 @@ class User extends AppModel
|
|||
return $data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the monitoring flag in Configure for the current user
|
||||
* Reads the state from redis
|
||||
*/
|
||||
public function setMonitoring($user)
|
||||
{
|
||||
if (
|
||||
!empty(Configure::read('Security.user_monitoring_enabled'))
|
||||
) {
|
||||
$redis = $this->setupRedis();
|
||||
if (!empty($redis->sismember('misp:monitored_users', $user['id']))) {
|
||||
Configure::write('Security.monitored', 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Configure::write('Security.monitored', 0);
|
||||
}
|
||||
|
||||
public function registerUser($added_by, $registration, $org_id, $role_id) {
|
||||
$user = array(
|
||||
'email' => $registration['data']['email'],
|
||||
|
|
|
@ -66,6 +66,14 @@
|
|||
'description' => empty($ajax) ? __('A list of API keys bound to a user.') : false,
|
||||
'pull' => 'right',
|
||||
'actions' => [
|
||||
[
|
||||
'url' => $baseurl . '/auth_keys/view',
|
||||
'url_params_data_paths' => array(
|
||||
'AuthKey.id'
|
||||
),
|
||||
'icon' => 'eye',
|
||||
'dbclickAction' => true
|
||||
],
|
||||
[
|
||||
'onclick' => sprintf(
|
||||
'openGenericModal(\'%s/authKeys/delete/[onclick_params_data_path]\');',
|
||||
|
|
|
@ -1,4 +1,17 @@
|
|||
<?php
|
||||
$keyUsageCsv = null;
|
||||
if (isset($keyUsage)) {
|
||||
$todayString = date('Y-m-d');
|
||||
$today = strtotime($todayString);
|
||||
$startDate = key($keyUsage); // oldest date for sparkline
|
||||
$startDate = strtotime($startDate) - (3600 * 24 * 3);
|
||||
$keyUsageCsv = 'Date,Close\n';
|
||||
for ($date = $startDate; $date <= $today; $date += (3600 * 24)) {
|
||||
$dateAsString = date('Y-m-d', $date);
|
||||
$keyUsageCsv .= $dateAsString . ',' . (isset($keyUsage[$dateAsString]) ? $keyUsage[$dateAsString] : 0) . '\n';
|
||||
}
|
||||
}
|
||||
|
||||
echo $this->element(
|
||||
'genericElements/SingleViews/single_view',
|
||||
[
|
||||
|
@ -38,9 +51,21 @@ echo $this->element(
|
|||
[
|
||||
'key' => __('Comment'),
|
||||
'path' => 'AuthKey.comment'
|
||||
],
|
||||
[
|
||||
'key' => __('Key usage'),
|
||||
'type' => 'sparkline',
|
||||
'path' => 'AuthKey.id',
|
||||
'csv' => [
|
||||
'data' => $keyUsageCsv,
|
||||
],
|
||||
'requirement' => isset($keyUsage),
|
||||
],
|
||||
[
|
||||
'key' => __('Last used'),
|
||||
'raw' => $lastUsed ? $lastUsed->format('Y-m-d H:i:s') : __('Not used yet'),
|
||||
'requirement' => isset($keyUsage),
|
||||
]
|
||||
],
|
||||
'children' => [
|
||||
]
|
||||
]
|
||||
);
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
$elementId = Hash::extract($data, $field['path'])[0];
|
||||
if (!empty($field['csv_data_path'])) {
|
||||
$csv = Hash::extract($data, $field['csv_data_path']);
|
||||
if (!empty($csv)) {
|
||||
$csv = $csv[0];
|
||||
}
|
||||
} else {
|
||||
$csv = $field['csv']['data'];
|
||||
}
|
||||
if (!empty($csv)) {
|
||||
$scope = empty($field['csv']['scope']) ? '' : $field['csv']['scope'];
|
||||
echo $this->element('sparkline', array('scope' => $scope, 'id' => $elementId, 'csv' => $csv));
|
||||
}
|
|
@ -30,6 +30,10 @@
|
|||
$listElements = '';
|
||||
if (!empty($fields)) {
|
||||
foreach ($fields as $field) {
|
||||
if (isset($field['requirement']) && !$field['requirement']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($field['type'])) {
|
||||
$field['type'] = 'generic';
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue