@@ -2289,10 +2289,10 @@ private function __checkIfPulledEventExistsAndAddOrUpdate($event, $eventId, &$su
if (!$existingEvent) {
// add data for newly imported events
$result = $eventModel->_add($event, true, $user, $server['Server']['org_id'], $passAlong, true, $jobId);
if ($result === true) {
if ($result) {
$successes[] = $eventId;
} else {
$fails[$eventId] = __('Failed (partially?) because of errors: ') . $result;
$fails[$eventId] = __('Failed (partially?) because of validation errors: ') . json_encode($eventModel->validationErrors, true);
}
} else {
if (!$existingEvent['Event']['locked'] && !$server['Server']['internal']) {
@@ -2316,6 +2316,7 @@ private function __pullEvent($eventId, &$successes, &$fails, $eventModel, $serve
$eventId,
$server
);
;
if (!empty($event)) {
if ($this->__checkIfEventIsBlockedBeforePull($event)) {
return false;
@@ -2328,7 +2329,7 @@ private function __pullEvent($eventId, &$successes, &$fails, $eventModel, $serve
}
} else {
// error
$fails[$eventId] = __('failed downloading the event') . ': ' . json_encode($event);
$fails[$eventId] = __('failed downloading the event');
}
return true;
}
@@ -5187,4 +5188,65 @@ public function updateJSON()
}
return $results;
}

public function resetRemoteAuthKey($id)
{
$server = $this->find('first', array(
'recursive' => -1,
'conditions' => array('Server.id' => $id)
));
if (empty($server)) {
return __('Invalid server');
}
$HttpSocket = $this->setupHttpSocket($server);
$request = $this->setupSyncRequest($server);
$uri = $server['Server']['url'] . '/users/resetauthkey/me';
try {
$response = $HttpSocket->post($uri, '{}', $request);
} catch (Exception $e) {
$this->Log = ClassRegistry::init('Log');
$this->Log->create();
$message = 'Could not reset the remote authentication key.';
$this->Log->save(array(
'org' => 'SYSTEM',
'model' => 'Server',
'model_id' => $id,
'email' => 'SYSTEM',
'action' => 'error',
'user_id' => 0,
'title' => 'Error: ' . $message,
));
return $message;
}
if ($response->isOk()) {
try {
$response = json_decode($response->body, true);
} catch (Exception $e) {
$this->Log = ClassRegistry::init('Log');
$this->Log->create();
$message = 'Invalid response received from the remote instance.';
$this->Log->save(array(
'org' => 'SYSTEM',
'model' => 'Server',
'model_id' => $id,
'email' => 'SYSTEM',
'action' => 'error',
'user_id' => 0,
'title' => 'Error: ' . $message,
));
return $message;
}
if (!empty($response['message'])) {
$authkey = $response['message'];
}
if (substr($authkey, 0, 17) === 'Authkey updated: ') {
$authkey = substr($authkey, 17, 57);
}
$server['Server']['authkey'] = $authkey;
$this->save($server);
return true;
} else {
return __('Could not reset the remote authentication key.');
}
}
}
@@ -1253,4 +1253,174 @@ public function createInitialUser($org_id)
$this->save($admin);
return $authKey;
}

public function resetAllSyncAuthKeysRouter($user, $jobId = false)
{
if (Configure::read('MISP.background_jobs')) {
$job = ClassRegistry::init('Job');
$job->create();
$eventModel = ClassRegistry::init('Event');
$data = array(
'worker' => $eventModel->__getPrioWorkerIfPossible(),
'job_type' => __('reset_all_sync_api_keys'),
'job_input' => __('Reseting all API keys'),
'status' => 0,
'retries' => 0,
'org_id' => $user['org_id'],
'org' => $user['Organisation']['name'],
'message' => 'Issuing new API keys to all sync users.',
);
$job->save($data);
$jobId = $job->id;
$process_id = CakeResque::enqueue(
'prio',
'AdminShell',
array('resetSyncAuthkeys', $user['id'], $jobId),
true
);
$job->saveField('process_id', $process_id);
return true;
} else {
return $this->resetAllSyncAuthKeys($user);
}
}

public function resetAllSyncAuthKeys($user, $jobId = false)
{
$affected_users = $this->find('all', array(
'recursive' => -1,
'contain' => array('Role'),
'conditions' => array(
'OR' => array(
'Role.perm_sync' => 1,
'Role.perm_admin' => 1
),
'Role.perm_site_admin' => 0
)
));
$results = array('success' => 0, 'fails' => 0);
$user_count = count($affected_users);
if ($jobId) {
$job = ClassRegistry::init('Job');
$existingJob = $job->find('first', array(
'conditions' => array('Job.id' => $jobId),
'recursive' => -1
));
if (empty($existingJob)) {
$jobId = false;
}
}
foreach ($affected_users as $k => $affected_user) {
try {
$reset_result = $this->resetauthkey($user, $affected_user['User']['id'], true);
if ($reset_result) {
$results['success'] += 1;
} else {
$results['fails'] += 1;
}
} catch (Exception $e) {
$results['fails'] += 1;
}
if ($jobId) {
if ($k % 100 == 0) {
$job->id = $jobId;
$job->saveField('progress', 100 * (($k + 1) / count($user_count)));
$job->saveField('message', __('Reset in progress - %s/%s.', $k, $user_count));
}
}
}
if ($jobId) {
$message = __('%s authkeys reset, %s could not be reset', $results['success'], $results['fails']);
$job->saveField('progress', 100);
$job->saveField('message', $message);
$job->saveField('status', 4);
}
return $results;
}

public function resetauthkey($user, $id, $alert = false)
{
$this->id = $id;
if (!$id || !$this->exists($id)) {
return false;
}
$updatedUser = $this->read();
$oldKey = $this->data['User']['authkey'];
if (empty($user['Role']['perm_site_admin']) && !($user['Role']['perm_admin'] && $user['org_id'] == $updatedUser['User']['org_id']) && ($user['id'] != $id)) {
return false;
}
$newkey = $this->generateAuthKey();
$this->saveField('authkey', $newkey);
$this->extralog(
$user,
'reset_auth_key',
sprintf(
__('Authentication key for user %s (%s) updated.'),
$updatedUser['User']['id'],
$updatedUser['User']['email']
),
$fieldsResult = 'authkey(' . $oldKey . ') => (' . $newkey . ')',
$updatedUser
);
if ($alert) {
$baseurl = Configure::read('MISP.external_baseurl');
if (empty($baseurl)) {
$baseurl = Configure::read('MISP.baseurl');
}
$body = __(
"Dear user,\n\nan API key reset has been triggered by an administrator for your user account on %s.\n\nYour new API key is: %s\n\nPlease update your server's sync setup to reflect this change.\n\nWe apologise for the inconvenience.",
$baseurl,
$newkey
);
$bodyNoEnc = __(
"Dear user,\n\nan API key reset has been triggered by an administrator for your user account on %s.\n\nYour new API key can be retrieved by logging in using this sync user's account.\n\nPlease update your server's sync setup to reflect this change.\n\nWe apologise for the inconvenience.",
$baseurl,
$newkey
);
$this->sendEmail(
$updatedUser,
$body,
$bodyNoEnc,
__('API key reset by administrator')
);
}
return $newkey;
}

public function extralog($user, $action = null, $description = null, $fieldsResult = null, $modifiedUser = null)
{
// new data
$model = 'User';
$modelId = $user['id'];
if (!empty($modifiedUser)) {
$modelId = $modifiedUser['User']['id'];
}
if ($action == 'login') {
$description = "User (" . $user['id'] . "): " . $user['email'];
} elseif ($action == 'logout') {
$description = "User (" . $user['id'] . "): " . $user['email'];
} elseif ($action == 'edit') {
$description = "User (" . $modifiedUser['User']['id'] . "): " . $modifiedUser['User']['email'];
} elseif ($action == 'change_pw') {
$description = "User (" . $modifiedUser['User']['id'] . "): " . $modifiedUser['User']['email'];
$fieldsResult = "Password changed.";
}

// query
$this->Log = ClassRegistry::init('Log');
$this->Log->create();
$this->Log->save(array(
'org' => $user['Organisation']['name'],
'model' => $model,
'model_id' => $modelId,
'email' => $user['email'],
'action' => $action,
'title' => $description,
'change' => isset($fieldsResult) ? $fieldsResult : ''));

// write to syslogd as well
App::import('Lib', 'SysLog.SysLog');
$syslog = new SysLog();
$syslog->write('notice', $description . ' -- ' . $action . (empty($fieldResult) ? '' : '-- ' . $fieldResult));
}
}
@@ -21,6 +21,7 @@
<th><?php echo $this->Paginator->sort('id');?></th>
<th><?php echo $this->Paginator->sort('name');?></th>
<th><?php echo __('Connection test');?></th>
<th><?php echo __('Reset API key');?></th>
<th><?php echo $this->Paginator->sort('internal');?></th>
<th><?php echo $this->Paginator->sort('push');?></th>
<th><?php echo $this->Paginator->sort('pull');?></th>
@@ -70,6 +71,20 @@ foreach ($servers as $server):
?>
</td>
<td id="connection_test_<?php echo $server['Server']['id'];?>"><span role="button" tabindex="0" aria-label="<?php echo __('Test the connection to the remote instance');?>" title="<?php echo __('Test the connection to the remote instance');?>" class="btn btn-primary" style="line-height:10px; padding: 4px 4px;" onClick="testConnection('<?php echo $server['Server']['id'];?>');"><?php echo __('Run');?></span></td>
<td id="reset_api_key_<?php echo $server['Server']['id'];?>">
<?php
echo $this->Form->postLink(
__('Reset'),
$baseurl . '/servers/resetRemoteAuthKey/' . $server['Server']['id'],
array(
'style' => 'line-height:10px; padding: 4px 4px;',
'title' => __('Remotely reset API key'),
'aria-label' => __('Remotely reset API key'),
'class' => 'btn btn-primary'
)
);
?>
</td>

<td><span class="<?php echo ($server['Server']['internal']? 'icon-ok' : 'icon-remove'); ?>" role="img" aria-label="<?php echo ($server['Server']['internal']? __('Yes') : __('No')); ?>" title="<?php echo ($server['Server']['internal']? __('Internal instance that ignores distribution level degradation *WARNING: Only use this setting if you have several internal instances and the sync link is to an internal extension of the current MISP community*') : __('Normal sync link to an external MISP instance. Distribution degradation will follow the normal rules.')); ?>"></span></td>
<td><span class="<?php echo ($server['Server']['push']? 'icon-ok' : 'icon-remove'); ?>" role="img" aria-label="<?php echo ($server['Server']['push']? __('Yes') : __('No')); ?>"></span><span class="short <?php if (!$server['Server']['push'] || empty($ruleDescription['push'])) echo "hidden"; ?>" data-toggle="popover" title="Distribution List" data-content="<?php echo $ruleDescription['push']; ?>"> (<?php echo __('Rules');?>)</span></td>
@@ -1,5 +1,25 @@
<div class="users index">
<h2><?php echo __('Users');?></h2>
<?php
if ($isSiteAdmin) {
echo sprintf(
'<span>%s</span>',
__(
'Click %s to reset the API keys of all sync and org admin users in one shot. This will also automatically inform them of their new API keys.',
$this->Form->postLink(
__('here'),
$baseurl . '/users/resetAllSyncAuthKeys',
array(
'title' => __('Reset all sync user API keys'),
'aria-label' => __('Reset all sync user API keys'),
'class' => 'bold'
),
__('Are you sure you wish to reset the API keys of all users with sync privileges?')
)
)
);
}
?>
<div class="pagination">
<ul>
<?php