mirror of https://github.com/MISP/MISP
Merge pull request #9473 from JakubOnderka/logging
chg: [internal] Do not log in audit log last_api_accesspull/9477/head
commit
edd6d3f157
|
@ -79,7 +79,7 @@ class UserLoginProfilesController extends AppController
|
|||
'fields' => ['UserLoginProfile.*']
|
||||
));
|
||||
if (empty($profile)) {
|
||||
throw new NotFoundException(__('Invalid UserLoginProfile'));
|
||||
throw new NotFoundException(__('Invalid user login profile'));
|
||||
}
|
||||
if ($this->UserLoginProfile->delete($id)) {
|
||||
$this->loadModel('Log');
|
||||
|
@ -87,13 +87,13 @@ class UserLoginProfilesController extends AppController
|
|||
$this->Log->createLogEntry($this->Auth->user(), 'delete', 'UserLoginProfile', $id, $fieldsDescrStr, json_encode($profile));
|
||||
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('UserLoginProfile', 'admin_delete', $id, $this->response->type(), 'UserLoginProfile deleted.');
|
||||
return $this->RestResponse->saveSuccessResponse('UserLoginProfile', 'admin_delete', $id, $this->response->type(), 'User login profile deleted.');
|
||||
} else {
|
||||
$this->Flash->success(__('UserLoginProfile deleted'));
|
||||
$this->redirect(array('admin'=> false, 'controller' => 'userLoginProfiles', 'action' => 'index', $profile['UserLoginProfile']['user_id']));
|
||||
}
|
||||
}
|
||||
$this->Flash->error(__('UserLoginProfile was not deleted'));
|
||||
$this->Flash->error(__('User login profile was not deleted'));
|
||||
$this->redirect(array('admin'=> false, 'controller' => 'userLoginProfiles', 'action' => 'index', $profile['UserLoginProfile']['user_id']));
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ class UserLoginProfilesController extends AppController
|
|||
{
|
||||
if ($this->request->is('post')) {
|
||||
$userLoginProfile = $this->__setTrust($logId, 'malicious');
|
||||
$this->Flash->info(__('You marked a login suspicious. You must change your password NOW !'));
|
||||
$this->Flash->info(__('You marked a login suspicious. You must change your password NOW!'));
|
||||
$this->loadModel('Log');
|
||||
$details = 'User reported suspicious login for log ID: '. $logId;
|
||||
// raise an alert (the SIEM component should ensure (org)admins are informed)
|
||||
|
@ -123,9 +123,9 @@ class UserLoginProfilesController extends AppController
|
|||
'recursive' => -1
|
||||
));
|
||||
unset($user['User']['password']);
|
||||
$this->UserLoginProfile->email_report_malicious($user, $userLoginProfile);
|
||||
$this->UserLoginProfile->emailReportMalicious($user, $userLoginProfile);
|
||||
// change account info to force password change, redirect to new password page.
|
||||
$this->User->id = $this->Auth->user('id');
|
||||
$this->User->id = $this->Auth->user('id');
|
||||
$this->User->saveField('change_pw', 1);
|
||||
$this->redirect(array('controller' => 'users', 'action' => 'change_pw'));
|
||||
return;
|
||||
|
@ -153,14 +153,13 @@ class UserLoginProfilesController extends AppController
|
|||
$data['hash'] = $this->UserLoginProfile->hash($data);
|
||||
|
||||
// add the userLoginProfile trust status if it not already there, based on the hash
|
||||
$result = $this->UserLoginProfile->find('count', array(
|
||||
'conditions' => array('UserLoginProfile.hash' => $data['hash'])
|
||||
));
|
||||
if ($result == 0) {
|
||||
$exists = $this->UserLoginProfile->hasAny([
|
||||
'UserLoginProfile.hash' => $data['hash']
|
||||
]);
|
||||
if (!$exists) {
|
||||
// no row yet, save it.
|
||||
$this->UserLoginProfile->save($data);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1332,34 +1332,37 @@ class UsersController extends AppController
|
|||
|
||||
private function _postlogin()
|
||||
{
|
||||
$this->User->extralog($this->Auth->user(), "login");
|
||||
$authUser = $this->Auth->user();
|
||||
$this->User->extralog($authUser, "login");
|
||||
|
||||
$this->User->Behaviors->disable('SysLogLogable.SysLogLogable');
|
||||
$this->User->id = $this->Auth->user('id');
|
||||
$user = $this->User->find('first', array(
|
||||
'conditions' => array(
|
||||
'User.id' => $this->Auth->user('id')
|
||||
'User.id' => $authUser['id'],
|
||||
),
|
||||
'fields' => ['User.id', 'User.current_login', 'User.last_login'],
|
||||
'recursive' => -1
|
||||
));
|
||||
unset($user['User']['password']);
|
||||
// update login timestamp and welcome user
|
||||
$this->User->updateLoginTimes($user['User']);
|
||||
$lastUserLogin = $user['User']['last_login'];
|
||||
$this->User->Behaviors->enable('SysLogLogable.SysLogLogable');
|
||||
|
||||
$lastUserLogin = $user['User']['last_login'];
|
||||
if ($lastUserLogin) {
|
||||
$readableDatetime = (new DateTime())->setTimestamp($lastUserLogin)->format('D, d M y H:i:s O'); // RFC822
|
||||
$this->Flash->info(__('Welcome! Last login was on %s', $readableDatetime));
|
||||
}
|
||||
|
||||
if (Configure::read('Security.alert_on_suspicious_logins')) {
|
||||
try {
|
||||
// there are reasons to believe there is evil happening, suspicious. Inform user and (org)admins.
|
||||
$suspiciousness_reason = $this->User->UserLoginProfile->_isSuspicious();
|
||||
if ($suspiciousness_reason) {
|
||||
$suspiciousnessReason = $this->User->UserLoginProfile->_isSuspicious();
|
||||
if ($suspiciousnessReason) {
|
||||
// raise an alert (the SIEM component should ensure (org)admins are informed)
|
||||
$this->loadModel('Log');
|
||||
$this->Log->createLogEntry($this->Auth->user(), 'auth_alert', 'User', $this->Auth->user('id'), 'Suspicious login.', $suspiciousness_reason);
|
||||
$this->Log->createLogEntry($authUser, 'auth_alert', 'User', $authUser['id'], 'Suspicious login.', $suspiciousnessReason);
|
||||
// Line below commented out to NOT inform user/org admin of the suspicious login.
|
||||
// The reason is that we want to prevent other user actions cause trouble.
|
||||
// The reason is that we want to prevent other user actions cause trouble.
|
||||
// However this also means we're sitting on data that could be used to detect new evil logins.
|
||||
// As we're generating alerts, the sysadmin should be keeping an eye on these
|
||||
// $this->User->UserLoginProfile->email_suspicious($user, $suspiciousness_reason);
|
||||
|
@ -1367,11 +1370,12 @@ class UsersController extends AppController
|
|||
// verify UserLoginProfile trust status and perform informative actions
|
||||
if (!$this->User->UserLoginProfile->_isTrusted()) {
|
||||
// send email to inform the user
|
||||
$this->User->UserLoginProfile->email_newlogin($user);
|
||||
$this->User->UserLoginProfile->emailNewLogin($authUser);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// At first login after code update and before DB schema update we might end up with problems.
|
||||
// Just catch it cleanly here to prevent problems.
|
||||
$this->log($e->getMessage(), LOG_WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3065,7 +3069,7 @@ class UsersController extends AppController
|
|||
* @return array
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
private function __adminFetchConditions($id, $edit = True)
|
||||
private function __adminFetchConditions($id, $edit = true)
|
||||
{
|
||||
if (empty($id)) {
|
||||
throw new NotFoundException(__('Invalid user'));
|
||||
|
@ -3076,7 +3080,7 @@ class UsersController extends AppController
|
|||
if (!$user['Role']['perm_site_admin']) {
|
||||
$conditions['User.org_id'] = $user['org_id']; // org admin
|
||||
if ($edit) {
|
||||
$conditions['Role.perm_site_admin'] = False;
|
||||
$conditions['Role.perm_site_admin'] = false;
|
||||
}
|
||||
}
|
||||
return $conditions;
|
||||
|
@ -3133,69 +3137,65 @@ class UsersController extends AppController
|
|||
$this->render('/genericTemplates/confirm');
|
||||
}
|
||||
|
||||
public function view_login_history($user_id = null) {
|
||||
if ($user_id && $this->_isAdmin()) { // org and site admins
|
||||
$user = $this->User->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => $this->__adminFetchConditions($user_id),
|
||||
'contain' => [
|
||||
'UserSetting',
|
||||
'Role',
|
||||
'Organisation'
|
||||
]
|
||||
));
|
||||
if (empty($user)) {
|
||||
public function view_login_history($userId = null)
|
||||
{
|
||||
if ($userId && $this->_isAdmin()) { // org and site admins
|
||||
$userExists = $this->User->hasAny($this->__adminFetchConditions($userId));
|
||||
if (!$userExists) {
|
||||
throw new NotFoundException(__('Invalid user'));
|
||||
}
|
||||
} else {
|
||||
$user_id = $this->Auth->user('id');
|
||||
$userId = $this->Auth->user('id');
|
||||
}
|
||||
$this->loadModel('UserLoginProfile');
|
||||
|
||||
$this->loadModel('Log');
|
||||
$logs = $this->Log->find('all', array(
|
||||
'conditions' => array(
|
||||
'Log.user_id' => $user_id,
|
||||
'OR' => array ('Log.action' => array('login', 'login_fail', 'auth', 'auth_fail'))
|
||||
'Log.user_id' => $userId,
|
||||
'OR' => array('Log.action' => array('login', 'login_fail', 'auth', 'auth_fail'))
|
||||
),
|
||||
'fields' => array('Log.action', 'Log.created', 'Log.ip', 'Log.change', 'Log.id'),
|
||||
'order' => array('Log.created DESC'),
|
||||
'order' => array('Log.id DESC'),
|
||||
'limit' => 100 // relatively high limit, as we'll be grouping data afterwards.
|
||||
));
|
||||
$lst = array();
|
||||
|
||||
$profiles = [];
|
||||
$prevProfile = null;
|
||||
$prevCreatedLast = null;
|
||||
$prevCreatedFirst = null;
|
||||
$prevLogEntry = null;
|
||||
$prevActions = array();
|
||||
|
||||
$actions_translator = [
|
||||
$actionsTranslator = [
|
||||
'auth_fail' => 'API:failed',
|
||||
'auth' => 'API:login',
|
||||
'login' => 'web:login',
|
||||
'login_fail' => 'web:failed'
|
||||
];
|
||||
|
||||
$max_rows = 6; // limit to a few rows, to prevent cluttering the interface.
|
||||
$maxRows = 6; // limit to a few rows, to prevent cluttering the interface.
|
||||
// We didn't filter the data at SQL query too much, nor by age, as we want to show "enough" data, even if old
|
||||
$rows = 0;
|
||||
// group authentications by type of loginprofile, to make the list shorter
|
||||
foreach ($logs as $logEntry) {
|
||||
$loginProfile = $this->UserLoginProfile->_fromLog($logEntry['Log']);
|
||||
if (!$loginProfile) continue; // skip if empty log
|
||||
$loginProfile = $this->User->UserLoginProfile->_fromLog($logEntry['Log']);
|
||||
if (!$loginProfile) {
|
||||
continue; // skip if empty log
|
||||
}
|
||||
$loginProfile['ip'] = $logEntry['Log']['ip'] ?? null; // transitional workaround
|
||||
if ($this->UserLoginProfile->_isSimilar($loginProfile, $prevProfile)) {
|
||||
if ($this->User->UserLoginProfile->_isSimilar($loginProfile, $prevProfile)) {
|
||||
// continue find as same type of login
|
||||
$prevCreatedFirst = $logEntry['Log']['created'];
|
||||
$prevActions[] = $actions_translator[$logEntry['Log']['action']] ?? $logEntry['Log']['action'];
|
||||
$prevActions[] = $actionsTranslator[$logEntry['Log']['action']];
|
||||
} else {
|
||||
// add as new entry
|
||||
if (null != $prevProfile) {
|
||||
if (null !== $prevProfile) {
|
||||
$actionsString = ''; // count actions
|
||||
foreach(array_count_values($prevActions) as $action => $cnt) {
|
||||
foreach (array_count_values($prevActions) as $action => $cnt) {
|
||||
$actionsString .= $action . ' (' . $cnt . "x) ";
|
||||
}
|
||||
$lst[] = array(
|
||||
'status' => $this->UserLoginProfile->_getTrustStatus($prevProfile, $user_id),
|
||||
$profiles[] = [
|
||||
'status' => $this->User->UserLoginProfile->_getTrustStatus($prevProfile, $userId),
|
||||
'platform' => $prevProfile['ua_platform'],
|
||||
'browser' => $prevProfile['ua_browser'],
|
||||
'region' => $prevProfile['geoip'],
|
||||
|
@ -3204,40 +3204,47 @@ class UsersController extends AppController
|
|||
'last_seen' => $prevCreatedLast,
|
||||
'first_seen' => $prevCreatedFirst,
|
||||
'actions' => $actionsString,
|
||||
'actions_button' => ('unknown' == $this->UserLoginProfile->_getTrustStatus($prevProfile, $user_id)) ? true : false,
|
||||
'id' => $prevLogEntry);
|
||||
'actions_button' => ('unknown' == $this->User->UserLoginProfile->_getTrustStatus($prevProfile, $userId)) ? true : false,
|
||||
'id' => $prevLogEntry
|
||||
];
|
||||
}
|
||||
// build new entry
|
||||
$prevProfile = $loginProfile;
|
||||
$prevCreatedFirst = $prevCreatedLast = $logEntry['Log']['created'];
|
||||
$prevActions[] = $actions_translator[$logEntry['Log']['action']] ?? $logEntry['Log']['action'];
|
||||
$prevActions[] = $actionsTranslator[$logEntry['Log']['action']];
|
||||
$prevLogEntry = $logEntry['Log']['id'];
|
||||
$rows += 1;
|
||||
if ($rows == $max_rows) break;
|
||||
$rows++;
|
||||
if ($rows === $maxRows) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// add last entry
|
||||
$actionsString = ''; // count actions
|
||||
foreach(array_count_values($prevActions) as $action => $cnt) {
|
||||
$actionsString .= $action . ' (' . $cnt . "x) ";
|
||||
if (null !== $prevProfile) {
|
||||
$actionsString = ''; // count actions
|
||||
foreach (array_count_values($prevActions) as $action => $cnt) {
|
||||
$actionsString .= $action . ' (' . $cnt . "x) ";
|
||||
}
|
||||
$profiles[] = array(
|
||||
'status' => $this->User->UserLoginProfile->_getTrustStatus($prevProfile, $userId),
|
||||
'platform' => $prevProfile['ua_platform'],
|
||||
'browser' => $prevProfile['ua_browser'],
|
||||
'region' => $prevProfile['geoip'],
|
||||
'ip' => $prevProfile['ip'],
|
||||
'accept_lang' => $prevProfile['accept_lang'],
|
||||
'last_seen' => $prevCreatedLast,
|
||||
'first_seen' => $prevCreatedFirst,
|
||||
'actions' => $actionsString,
|
||||
'actions_button' => ('unknown' == $this->User->UserLoginProfile->_getTrustStatus($prevProfile, $userId)) ? true : false,
|
||||
'id' => $prevLogEntry
|
||||
);
|
||||
}
|
||||
$lst[] = array(
|
||||
'status' => $this->UserLoginProfile->_getTrustStatus($prevProfile, $user_id),
|
||||
'platform' => $prevProfile['ua_platform'],
|
||||
'browser' => $prevProfile['ua_browser'],
|
||||
'region' => $prevProfile['geoip'],
|
||||
'ip' => $prevProfile['ip'],
|
||||
'accept_lang' => $prevProfile['accept_lang'],
|
||||
'last_seen' => $prevCreatedLast,
|
||||
'first_seen' => $prevCreatedFirst,
|
||||
'actions' => $actionsString,
|
||||
'actions_button' => ('unknown' == $this->UserLoginProfile->_getTrustStatus($prevProfile, $user_id)) ? true : false,
|
||||
'id' => $prevLogEntry);
|
||||
$this->set('data', $lst);
|
||||
$this->set('user_id', $user_id);
|
||||
$this->set('data', $profiles);
|
||||
$this->set('user_id', $userId);
|
||||
}
|
||||
|
||||
public function logout401() {
|
||||
public function logout401()
|
||||
{
|
||||
# You should read the documentation in docs/CONFIG.ApacheSecureAuth.md
|
||||
# before using this endpoint. It is not useful without webserver config
|
||||
# changes.
|
||||
|
@ -3262,16 +3269,9 @@ class UsersController extends AppController
|
|||
if (empty($this->request->data['User']['email'])) {
|
||||
throw new MethodNotAllowedException(__('No email provided, cannot generate password reset message.'));
|
||||
}
|
||||
$user = [
|
||||
'id' => 0,
|
||||
'email' => 'SYSTEM',
|
||||
'Organisation' => [
|
||||
'name' => 'SYSTEM'
|
||||
]
|
||||
];
|
||||
$this->loadModel('Log');
|
||||
$this->Log->createLogEntry($user, 'forgot', 'User', 0, 'Password reset requested for: ' . $this->request->data['User']['email']);
|
||||
$this->User->forgotRouter($this->request->data['User']['email'], $this->_remoteIp());
|
||||
$this->Log->createLogEntry('SYSTEM', 'forgot', 'User', 0, 'Password reset requested for: ' . $this->request->data['User']['email']);
|
||||
$this->User->forgotRouter($this->request->data['User']['email'], $this->User->_remoteIp());
|
||||
$message = __('Password reset request submitted. If a valid user is found, you should receive an e-mail with a temporary reset link momentarily. Please be advised that this link is only valid for 10 minutes.');
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('User', 'forgot', false, $this->response->type(), $message);
|
||||
|
@ -3308,5 +3308,4 @@ class UsersController extends AppController
|
|||
return $this->__pw_change(['User' => $user], 'password_reset', $abortPost, $token, true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -68,6 +68,13 @@ class SecurityAudit
|
|||
];
|
||||
}
|
||||
|
||||
if (!Configure::read('Security.alert_on_suspicious_logins')) {
|
||||
$output['Login'][] = [
|
||||
'warning',
|
||||
__('Warning about suspicious logins is disabled. You can enable alert by setting `Security.alert_on_suspicious_logins` to `true`.'),
|
||||
];
|
||||
}
|
||||
|
||||
if (empty(Configure::read('Security.disable_browser_cache'))) {
|
||||
$output['Browser'][] = [
|
||||
'warning',
|
||||
|
|
|
@ -25,6 +25,7 @@ class AuditLogBehavior extends ModelBehavior
|
|||
'date_modified' => true, // User
|
||||
'current_login' => true, // User
|
||||
'last_login' => true, // User
|
||||
'last_api_access' => true, // User
|
||||
'newsread' => true, // User
|
||||
'unique_ips' => true, // User
|
||||
'proposal_email_lock' => true, // Event
|
||||
|
|
|
@ -3,16 +3,30 @@ App::uses('AppModel', 'Model');
|
|||
|
||||
class Log extends AppModel
|
||||
{
|
||||
const WARNING_ACTIONS = array(
|
||||
const WARNING_ACTIONS = [
|
||||
'warning',
|
||||
'change_pw',
|
||||
'login_fail',
|
||||
'version_warning',
|
||||
'auth_fail'
|
||||
);
|
||||
const ERROR_ACTIONS = array(
|
||||
];
|
||||
|
||||
const ERROR_ACTIONS = [
|
||||
'error'
|
||||
);
|
||||
];
|
||||
|
||||
const AUTH_ACTIONS = [
|
||||
'auth',
|
||||
'auth_fail',
|
||||
'auth_alert',
|
||||
'change_pw',
|
||||
'login',
|
||||
'login_fail',
|
||||
'logout',
|
||||
'password_reset',
|
||||
'forgot',
|
||||
];
|
||||
|
||||
public $validate = array(
|
||||
'action' => array(
|
||||
'rule' => array(
|
||||
|
|
|
@ -1032,18 +1032,18 @@ class User extends AppModel
|
|||
}
|
||||
|
||||
/**
|
||||
* @param int $org_id
|
||||
* @param int $orgId
|
||||
* @param int|false $excludeUserId
|
||||
* @return array
|
||||
* @return array User ID => Email
|
||||
*/
|
||||
public function getOrgAdminsForOrg($org_id, $excludeUserId = false)
|
||||
public function getOrgAdminsForOrg($orgId, $excludeUserId = false)
|
||||
{
|
||||
$adminRoles = $this->Role->find('column', array(
|
||||
'conditions' => array('perm_admin' => 1),
|
||||
'fields' => array('Role.id')
|
||||
));
|
||||
$conditions = array(
|
||||
'User.org_id' => $org_id,
|
||||
'User.org_id' => $orgId,
|
||||
'User.disabled' => 0,
|
||||
'User.role_id' => $adminRoles
|
||||
);
|
||||
|
@ -1059,7 +1059,12 @@ class User extends AppModel
|
|||
));
|
||||
}
|
||||
|
||||
public function getSiteAdmins($excludeUserId = false) {
|
||||
/**
|
||||
* @param int|false $excludeUserId
|
||||
* @return array User ID => Email
|
||||
*/
|
||||
public function getSiteAdmins($excludeUserId = false)
|
||||
{
|
||||
$adminRoles = $this->Role->find('column', array(
|
||||
'conditions' => array('perm_site_admin' => 1),
|
||||
'fields' => array('Role.id')
|
||||
|
|
|
@ -14,7 +14,6 @@ class UserLoginProfile extends AppModel
|
|||
'userKey' => 'user_id',
|
||||
'change' => 'full'
|
||||
),
|
||||
'Containable'
|
||||
);
|
||||
|
||||
public $validate = [
|
||||
|
@ -61,7 +60,7 @@ class UserLoginProfile extends AppModel
|
|||
return true;
|
||||
}
|
||||
|
||||
public function hash($data)
|
||||
public function hash(array $data)
|
||||
{
|
||||
unset($data['hash']);
|
||||
unset($data['created_at']);
|
||||
|
@ -126,15 +125,24 @@ class UserLoginProfile extends AppModel
|
|||
return $this->userProfile;
|
||||
}
|
||||
|
||||
public function _fromLog($logEntry)
|
||||
/**
|
||||
* @param array $logEntry
|
||||
* @return array|false|string[]
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function _fromLog(array $logEntry)
|
||||
{
|
||||
if (!$logEntry['change']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = ["user_agent" => "", "ip" => "", "accept_lang" => "", "geoip" => "", "ua_pattern" => "", "ua_platform" => "", "ua_browser" => ""];
|
||||
$data = array_merge($data, JsonTool::decode($logEntry['change']) ?? []);
|
||||
$data['ip'] = $logEntry['ip'];
|
||||
$data['timestamp'] = $logEntry['created'];
|
||||
$data = array_merge($data, JsonTool::decode($logEntry['change']));
|
||||
if ($data['user_agent'] === "") {
|
||||
return false;
|
||||
}
|
||||
$data['ip'] = $logEntry['ip'];
|
||||
$data['timestamp'] = $logEntry['created'];
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
@ -174,6 +182,11 @@ class UserLoginProfile extends AppModel
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $userProfileToCheck
|
||||
* @param int $userId
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function _getTrustStatus(array $userProfileToCheck, $userId = null)
|
||||
{
|
||||
if (!$userId) {
|
||||
|
@ -183,7 +196,7 @@ class UserLoginProfile extends AppModel
|
|||
if (!isset($this->knownUserProfiles[$userId])) {
|
||||
$this->knownUserProfiles[$userId] = $this->find('all', [
|
||||
'conditions' => ['UserLoginProfile.user_id' => $userId],
|
||||
'recursive' => 0
|
||||
'recursive' => -1,
|
||||
]);
|
||||
}
|
||||
// perform check on all entries, and stop when check OK
|
||||
|
@ -215,15 +228,12 @@ class UserLoginProfile extends AppModel
|
|||
if (strpos($this->_getTrustStatus($this->_getUserProfile()), 'malicious') !== false) {
|
||||
return __('A user reported a similar login profile as malicious.');
|
||||
}
|
||||
|
||||
// same IP as previous malicious user
|
||||
$maliciousWithSameIP = $this->find('first', [
|
||||
'conditions' => [
|
||||
'UserLoginProfile.ip' => $this->_getUserProfile()['ip'],
|
||||
'UserLoginProfile.status' => 'malicious'
|
||||
],
|
||||
'recursive' => 0,
|
||||
'fields' => array('UserLoginProfile.*')]
|
||||
);
|
||||
$maliciousWithSameIP = $this->hasAny([
|
||||
'UserLoginProfile.ip' => $this->_getUserProfile()['ip'],
|
||||
'UserLoginProfile.status' => 'malicious'
|
||||
]);
|
||||
if ($maliciousWithSameIP) {
|
||||
return __('The source IP was reported as as malicious by a user.');
|
||||
}
|
||||
|
@ -234,7 +244,7 @@ class UserLoginProfile extends AppModel
|
|||
return false;
|
||||
}
|
||||
|
||||
public function email_newlogin($user)
|
||||
public function emailNewLogin(array $user)
|
||||
{
|
||||
if (!Configure::read('MISP.disable_emailing')) {
|
||||
$date_time = date('c');
|
||||
|
@ -249,7 +259,7 @@ class UserLoginProfile extends AppModel
|
|||
}
|
||||
}
|
||||
|
||||
public function email_report_malicious($user, $userLoginProfile)
|
||||
public function emailReportMalicious(array $user, array $userLoginProfile)
|
||||
{
|
||||
// inform the org admin
|
||||
$date_time = $userLoginProfile['timestamp']; // LATER not ideal as timestamp is string without timezone info
|
||||
|
@ -259,19 +269,22 @@ class UserLoginProfile extends AppModel
|
|||
$body->set('baseurl', Configure::read('MISP.baseurl'));
|
||||
$body->set('misp_org', Configure::read('MISP.org'));
|
||||
$body->set('date_time', $date_time);
|
||||
$org_admins = $this->User->getOrgAdminsForOrg($user['User']['org_id']);
|
||||
$admins = $this->User->getSiteAdmins();
|
||||
$all_admins = array_unique(array_merge($org_admins, $admins));
|
||||
foreach ($all_admins as $admin_email) {
|
||||
|
||||
$orgAdmins = array_keys($this->User->getOrgAdminsForOrg($user['User']['org_id']));
|
||||
$admins = array_keys($this->User->getSiteAdmins());
|
||||
$allAdmins = array_unique(array_merge($orgAdmins, $admins));
|
||||
|
||||
$subject = __("[%s MISP] Suspicious login reported.", Configure::read('MISP.org'));
|
||||
foreach ($allAdmins as $adminUserId) {
|
||||
$admin = $this->User->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => ['User.email' => $admin_email]
|
||||
'conditions' => ['User.id' => $adminUserId]
|
||||
));
|
||||
$this->User->sendEmail($admin, $body, false, "[" . Configure::read('MISP.org') . " MISP] Suspicious login reported.");
|
||||
$this->User->sendEmail($admin, $body, false, $subject);
|
||||
}
|
||||
}
|
||||
|
||||
public function email_suspicious($user, $suspiciousness_reason)
|
||||
public function email_suspicious(array $user, $suspiciousness_reason)
|
||||
{
|
||||
if (!Configure::read('MISP.disable_emailing')) {
|
||||
$date_time = date('c');
|
||||
|
@ -295,11 +308,11 @@ class UserLoginProfile extends AppModel
|
|||
$body->set('date_time', $date_time);
|
||||
$body->set('suspiciousness_reason', $suspiciousness_reason);
|
||||
|
||||
$org_admins = $this->User->getOrgAdminsForOrg($user['User']['org_id']);
|
||||
foreach ($org_admins as $org_admin_email) {
|
||||
$orgAdmins = array_keys($this->User->getOrgAdminsForOrg($user['User']['org_id']));
|
||||
foreach ($orgAdmins as $orgAdminID) {
|
||||
$org_admin = $this->User->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => ['User.email' => $org_admin_email]
|
||||
'conditions' => ['User.id' => $orgAdminID]
|
||||
));
|
||||
$this->User->sendEmail($org_admin, $body, false, "[" . Configure::read('MISP.org') . " MISP] Suspicious login detected.");
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ class EcsLog implements CakeLogInterface
|
|||
'message' => $message,
|
||||
];
|
||||
|
||||
if (in_array($action, ['auth', 'auth_fail', 'auth_alert', 'change_pw', 'login', 'login_fail', 'logout', 'password_reset'], true)) {
|
||||
if (in_array($action, Log::AUTH_ACTIONS, true)) {
|
||||
$message['event']['category'] = 'authentication';
|
||||
|
||||
if (in_array($action, ['auth_fail', 'login_fail'], true)) {
|
||||
|
@ -338,8 +338,8 @@ class EcsLog implements CakeLogInterface
|
|||
];
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
} else if (session_status() === PHP_SESSION_ACTIVE) {
|
||||
// include session data just when session is active to avoid unnecessary session starting
|
||||
App::uses('AuthComponent', 'Controller/Component');
|
||||
$authUser = AuthComponent::user();
|
||||
if (!empty($authUser)) {
|
||||
|
|
|
@ -25,7 +25,7 @@ if (!function_exists('str_contains')) {
|
|||
}
|
||||
}
|
||||
|
||||
foreach($data as $entry ){
|
||||
foreach ($data as $entry) {
|
||||
$platform = h(strtolower($entry['platform']));
|
||||
if (str_contains($platform, 'win')) $platform = 'windows';
|
||||
if (str_contains($platform, 'macosx')) $platform = 'apple';
|
||||
|
@ -34,7 +34,7 @@ foreach($data as $entry ){
|
|||
if (str_contains($entry['status'], 'malicious')) $bgcolor = '#ffdddd';
|
||||
elseif (str_contains($entry['status'], 'trusted')) $bgcolor = '#ddffdd';
|
||||
?>
|
||||
<div class="device-overview"style="background-color:<?= $bgcolor ?>;">
|
||||
<div class="device-overview" style="background-color:<?= $bgcolor ?>;">
|
||||
<div>
|
||||
<span class="device-icon fab fa-<?= h(strtolower($platform))?>" style="font-size:30px;"></span>
|
||||
<span class="device-icon fab fa-<?= h(strtolower($entry['browser']))?>" style="font-size:30px;"></span>
|
||||
|
@ -79,5 +79,4 @@ echo '</div>';
|
|||
|
||||
if (!$this->request->is('ajax')) {
|
||||
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'globalActions', 'menuItem' => 'view'));
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue