Merge branch '2.4' of github.com:MISP/MISP into feature-report-from-event

pull/6590/head
mokaddem 2020-11-16 10:46:07 +01:00
commit 51066e786c
No known key found for this signature in database
GPG Key ID: 164C473F627A06FA
66 changed files with 2669 additions and 943 deletions

View File

@ -92,8 +92,8 @@ install:
- sudo chmod -R 770 `pwd`/.gnupg
# Get authkey
- sudo usermod -a -G www-data $USER
- sudo -E su $USER -c 'app/Console/cake userInit -q | sudo tee ./key.txt'
- sudo -E su $USER -c 'app/Console/cake Admin runUpdates'
- sudo -E su $USER -c 'app/Console/cake userInit -q | sudo tee ./key.txt'
- sudo -E su $USER -c 'app/Console/cake Admin setSetting "Session.autoRegenerate" 0'
- sudo -E su $USER -c 'app/Console/cake Admin setSetting "Session.timeout" 600'
- sudo -E su $USER -c 'app/Console/cake Admin setSetting "Session.cookieTimeout" 3600'

View File

@ -8,9 +8,27 @@ CREATE TABLE IF NOT EXISTS `admin_settings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`setting` varchar(255) COLLATE utf8_bin NOT NULL,
`value` text COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
INDEX `setting` (`setting`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `allowedlist` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE IF NOT EXISTS `attachment_scans` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` varchar(40) COLLATE utf8_bin NOT NULL,
`attribute_id` int(11) NOT NULL,
`infected` tinyint(1) NOT NULL,
`malware_name` varchar(191) NULL,
`timestamp` int(11) NOT NULL,
PRIMARY KEY (`id`),
INDEX `index` (`type`, `attribute_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- --------------------------------------------------------
--
@ -34,6 +52,8 @@ CREATE TABLE IF NOT EXISTS `attributes` (
`comment` text COLLATE utf8_bin,
`deleted` tinyint(1) NOT NULL DEFAULT 0,
`disable_correlation` tinyint(1) NOT NULL DEFAULT 0,
`first_seen` BIGINT(20) NULL DEFAULT NULL,
`last_seen` BIGINT(20) NULL DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `event_id` (`event_id`),
INDEX `object_id` (`object_id`),
@ -43,6 +63,8 @@ CREATE TABLE IF NOT EXISTS `attributes` (
INDEX `type` (`type`),
INDEX `category` (`category`),
INDEX `sharing_group_id` (`sharing_group_id`),
INDEX `first_seen` (`first_seen`),
INDEX `last_seen` (`last_seen`),
UNIQUE INDEX `uuid` (`uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
@ -64,6 +86,24 @@ CREATE TABLE IF NOT EXISTS `attribute_tags` (
INDEX `tag_id` (`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `auth_keys` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`uuid` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL,
`authkey` varchar(72) CHARACTER SET ascii DEFAULT NULL,
`authkey_start` varchar(4) CHARACTER SET ascii DEFAULT NULL,
`authkey_end` varchar(4) CHARACTER SET ascii DEFAULT NULL,
`created` int(10) unsigned NOT NULL,
`expiration` int(10) unsigned NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`comment` text COLLATE utf8mb4_unicode_ci,
PRIMARY KEY (`id`),
KEY `authkey_start` (`authkey_start`),
KEY `authkey_end` (`authkey_end`),
KEY `created` (`created`),
KEY `expiration` (`expiration`),
KEY `user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
--
@ -111,16 +151,32 @@ CREATE TABLE IF NOT EXISTS `correlations` (
`date` date NOT NULL,
`info` text COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`),
INDEX `value` (`value`(255)),
INDEX `event_id` (`event_id`),
INDEX `1_event_id` (`1_event_id`),
INDEX `attribute_id` (`attribute_id`),
INDEX `1_attribute_id` (`1_attribute_id`),
INDEX `org_id` (`org_id`),
INDEX `sharing_group_id` (`sharing_group_id`),
INDEX `a_sharing_group_id` (`a_sharing_group_id`)
INDEX `1_attribute_id` (`1_attribute_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE IF NOT EXISTS dashboards (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uuid` varchar(40) COLLATE utf8_bin NOT NULL,
`name` varchar(191) NOT NULL,
`description` text,
`default` tinyint(1) NOT NULL DEFAULT 0,
`selectable` tinyint(1) NOT NULL DEFAULT 0,
`user_id` int(11) NOT NULL DEFAULT 0,
`restrict_to_org_id` int(11) NOT NULL DEFAULT 0,
`restrict_to_role_id` int(11) NOT NULL DEFAULT 0,
`restrict_to_permission_flag` varchar(191) NOT NULL DEFAULT '',
`value` text,
`timestamp` int(11) NOT NULL,
PRIMARY KEY (id),
INDEX `name` (`name`),
INDEX `uuid` (`uuid`),
INDEX `user_id` (`user_id`),
INDEX `restrict_to_org_id` (`restrict_to_org_id`),
INDEX `restrict_to_permission_flag` (`restrict_to_permission_flag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS decaying_models (
`id` int(11) NOT NULL AUTO_INCREMENT,
@ -175,6 +231,21 @@ CREATE TABLE IF NOT EXISTS event_graph (
INDEX `timestamp` (`timestamp`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS event_reports (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uuid` varchar(40) COLLATE utf8_bin NOT NULL ,
`event_id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
`content` text,
`distribution` tinyint(4) NOT NULL DEFAULT 0,
`sharing_group_id` int(11),
`timestamp` int(11) NOT NULL,
`deleted` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (id),
CONSTRAINT u_uuid UNIQUE (uuid),
INDEX `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- --------------------------------------------------------
--
@ -217,7 +288,7 @@ CREATE TABLE IF NOT EXISTS `events` (
-- Table structure for `event_blacklists`
--
CREATE TABLE IF NOT EXISTS `event_blacklists` (
CREATE TABLE IF NOT EXISTS `event_blocklists` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`event_uuid` varchar(40) COLLATE utf8_bin NOT NULL,
`created` datetime NOT NULL,
@ -326,8 +397,10 @@ CREATE TABLE IF NOT EXISTS `feeds` (
`headers` TEXT COLLATE utf8_bin,
`caching_enabled` tinyint(1) NOT NULL DEFAULT 0,
`force_to_ids` tinyint(1) NOT NULL DEFAULT 0,
`orgc_id` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
INDEX `input_source` (`input_source`)
INDEX `input_source` (`input_source`),
INDEX `orgc_id` (`orgc_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- -------------------------------------------------------
@ -436,6 +509,28 @@ CREATE TABLE IF NOT EXISTS `galaxy_reference` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE IF NOT EXISTS inbox (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uuid` varchar(40) COLLATE utf8_bin NOT NULL,
`title` varchar(191) NOT NULL,
`type` varchar(191) NOT NULL,
`ip` varchar(191) NOT NULL,
`user_agent` text,
`user_agent_sha256` varchar(64) NOT NULL,
`comment` text,
`deleted` tinyint(1) NOT NULL DEFAULT 0,
`timestamp` int(11) NOT NULL,
`store_as_file` tinyint(1) NOT NULL DEFAULT 0,
`data` longtext,
PRIMARY KEY (id),
INDEX `title` (`title`),
INDEX `type` (`type`),
INDEX `uuid` (`uuid`),
INDEX `user_agent_sha256` (`user_agent_sha256`),
INDEX `ip` (`ip`),
INDEX `timestamp` (`timestamp`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- --------------------------------------------------------
--
@ -544,7 +639,7 @@ KEY `type` (`type`)
-- Table structure for `org_blacklists`
--
CREATE TABLE IF NOT EXISTS `org_blacklists` (
CREATE TABLE IF NOT EXISTS `org_blocklists` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`org_uuid` varchar(40) COLLATE utf8_bin NOT NULL,
`created` datetime NOT NULL,
@ -573,6 +668,8 @@ CREATE TABLE IF NOT EXISTS `objects` (
`sharing_group_id` int(11),
`comment` text COLLATE utf8_bin NOT NULL,
`deleted` tinyint(1) NOT NULL DEFAULT 0,
`first_seen` BIGINT(20) NULL DEFAULT NULL,
`last_seen` BIGINT(20) NULL DEFAULT NULL,
PRIMARY KEY (id),
INDEX `name` (`name`),
INDEX `template_uuid` (`template_uuid`),
@ -582,7 +679,9 @@ CREATE TABLE IF NOT EXISTS `objects` (
INDEX `uuid` (`uuid`),
INDEX `timestamp` (`timestamp`),
INDEX `distribution` (`distribution`),
INDEX `sharing_group_id` (`sharing_group_id`)
INDEX `sharing_group_id` (`sharing_group_id`),
INDEX `first_seen` (`first_seen`),
INDEX `last_seen` (`last_seen`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
@ -671,7 +770,7 @@ CREATE TABLE IF NOT EXISTS `object_template_elements` (
`sane_default` text COLLATE utf8_bin,
`values_list` text COLLATE utf8_bin,
`description` text COLLATE utf8_bin,
`disable_correlation` tinyint(1) NOT NULL DEFAULT 0,
`disable_correlation` tinyint(1),
`multiple` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (id),
INDEX `object_relation` (`object_relation`),
@ -804,6 +903,8 @@ CREATE TABLE IF NOT EXISTS `roles` (
`perm_publish_zmq` tinyint(1) NOT NULL DEFAULT 0,
`perm_publish_kafka` tinyint(1) NOT NULL DEFAULT 0,
`perm_decaying` tinyint(1) NOT NULL DEFAULT 0,
`enforce_rate_limit` tinyint(1) NOT NULL DEFAULT 0,
`rate_limit_count` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
@ -868,6 +969,8 @@ CREATE TABLE IF NOT EXISTS `shadow_attributes` (
`timestamp` int(11) NOT NULL DEFAULT 0,
`proposal_to_delete` BOOLEAN NOT NULL DEFAULT 0,
`disable_correlation` tinyint(1) NOT NULL DEFAULT 0,
`first_seen` BIGINT(20) NULL DEFAULT NULL,
`last_seen` BIGINT(20) NULL DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `event_id` (`event_id`),
INDEX `event_uuid` (`event_uuid`),
@ -877,7 +980,9 @@ CREATE TABLE IF NOT EXISTS `shadow_attributes` (
INDEX `value1` (`value1`(255)),
INDEX `value2` (`value2`(255)),
INDEX `type` (`type`),
INDEX `category` (`category`)
INDEX `category` (`category`),
INDEX `first_seen` (`first_seen`),
INDEX `last_seen` (`last_seen`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-- --------------------------------------------------------
@ -968,6 +1073,35 @@ CREATE TABLE IF NOT EXISTS `sharing_groups` (
INDEX `organisation_uuid` (`organisation_uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE IF NOT EXISTS sightingdb_orgs (
`id` int(11) NOT NULL AUTO_INCREMENT,
`sightingdb_id` int(11) NOT NULL,
`org_id` int(11) NOT NULL,
PRIMARY KEY (id),
INDEX `sightingdb_id` (`sightingdb_id`),
INDEX `org_id` (`org_id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS sightingdbs (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`description` text,
`owner` varchar(255) DEFAULT '',
`host` varchar(255) DEFAULT 'http://localhost',
`port` int(11) DEFAULT 9999,
`timestamp` int(11) NOT NULL DEFAULT 0,
`enabled` tinyint(1) NOT NULL DEFAULT 0,
`skip_proxy` tinyint(1) NOT NULL DEFAULT 0,
`ssl_skip_verification` tinyint(1) NOT NULL DEFAULT 0,
`namespace` varchar(255) DEFAULT '',
PRIMARY KEY (id),
INDEX `name` (`name`),
INDEX `owner` (`owner`),
INDEX `host` (`host`),
INDEX `port` (`port`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- --------------------------------------------------------
--
@ -1305,7 +1439,7 @@ CREATE TABLE IF NOT EXISTS `warninglists` (
`description` text COLLATE utf8_bin NOT NULL,
`version` int(11) NOT NULL DEFAULT '1',
`enabled` tinyint(1) NOT NULL DEFAULT 0,
`warninglist_entry_count` int(11) unsigned DEFAULT NULL,
`warninglist_entry_count` int(11) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
@ -1338,24 +1472,12 @@ CREATE TABLE IF NOT EXISTS `warninglist_types` (
-- --------------------------------------------------------
--
-- Table structure for table `whitelist`
--
CREATE TABLE IF NOT EXISTS `whitelist` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
-- --------------------------------------------------------
--
-- Default values for initial installation
--
INSERT IGNORE INTO `admin_settings` (`id`, `setting`, `value`) VALUES
(1, 'db_version', '40');
(1, 'db_version', '61');
INSERT IGNORE INTO `feeds` (`id`, `provider`, `name`, `url`, `distribution`, `default`, `enabled`) VALUES
(1, 'CIRCL', 'CIRCL OSINT Feed', 'https://www.circl.lu/doc/misp/feed-osint', 3, 1, 0),
@ -1550,3 +1672,5 @@ INSERT IGNORE INTO `template_element_texts` (`id`, `name`, `template_element_id`
INSERT IGNORE INTO `org_blacklists` (`org_uuid`, `created`, `org_name`, `comment`) VALUES
('58d38339-7b24-4386-b4b4-4c0f950d210f', NOW(), 'Setec Astrononomy', 'default example'),
('58d38326-eda8-443a-9fa8-4e12950d210f', NOW(), 'Acme Finance', 'default example');
INSERT IGNORE INTO `admin_settings` (`setting`, `value`) VALUES ('fix_login', NOW());

View File

@ -60,6 +60,7 @@ class AppController extends Controller
public $sql_dump = false;
private $isRest = null;
public $restResponsePayload = null;
// Used for _isAutomation(), a check that returns true if the controller & action combo matches an action that is a non-xml and non-json automation method
// This is used to allow authentication via headers for methods not covered by _isRest() - as that only checks for JSON and XML formats
@ -102,18 +103,11 @@ class AppController extends Controller
'RateLimit',
'IndexFilter',
'Deprecation',
'RestSearch'
'RestSearch',
'CRUD'
//,'DebugKit.Toolbar'
);
private function __isApiFunction($controller, $action)
{
if (isset($this->automationArray[$controller]) && in_array($action, $this->automationArray[$controller])) {
return true;
}
return false;
}
public function beforeFilter()
{
$this->Auth->loginRedirect = Configure::read('MISP.baseurl') . '/users/routeafterlogin';
@ -695,24 +689,7 @@ class AppController extends Controller
protected function _isRest()
{
// This method is surprisingly slow and called many times for one request, so it make sense to cache the result.
if ($this->isRest !== null) {
return $this->isRest;
}
$api = $this->__isApiFunction($this->request->params['controller'], $this->request->params['action']);
if (isset($this->RequestHandler) && ($api || $this->RequestHandler->isXml() || $this->_isJson() || $this->_isCsv())) {
if ($this->_isJson()) {
if (!empty($this->request->input()) && empty($this->request->input('json_decode'))) {
throw new MethodNotAllowedException('Invalid JSON input. Make sure that the JSON input is a correctly formatted JSON string. This request has been blocked to avoid an unfiltered request.');
}
}
$this->isRest = true;
return true;
} else {
$this->isRest = false;
return false;
}
return $this->IndexFilter->isRest();
}
protected function _isAutomation()
@ -882,8 +859,14 @@ class AppController extends Controller
public function checkAuthUser($authkey)
{
$this->loadModel('User');
$user = $this->User->getAuthUserByAuthkey($authkey);
if (Configure::read('Security.advanced_authkeys')) {
$this->loadModel('AuthKey');
$user = $this->AuthKey->getAuthUserByAuthKey($authkey);
} else {
$this->loadModel('User');
$user = $this->User->getAuthUserByAuthKey($authkey);
}
if (empty($user)) {
return false;
}
@ -1275,7 +1258,7 @@ class AppController extends Controller
if ($returnFormat === 'download') {
$returnFormat = 'json';
}
if ($returnFormat === 'stix' && $this->_isJson()) {
if ($returnFormat === 'stix' && $this->IndexFilter->isJson()) {
$returnFormat = 'stix-json';
}
$elementCounter = 0;

View File

@ -0,0 +1,105 @@
<?php
App::uses('AppController', 'Controller');
class AuthKeysController extends AppController
{
public $components = array(
'Security',
'CRUD',
'RequestHandler'
);
public $paginate = array(
'limit' => 60,
'order' => array(
'AuthKey.name' => 'ASC'
)
);
public function index($id = false)
{
$conditions = [];
if (!$this->_isAdmin()) {
$conditions['AND'][] = ['AuthKey.user_id' => $this->Auth->user('id')];
} else if (!$this->_isSiteAdmin()) {
$userIds = $this->AuthKey->User->find('list', [
'conditions' => ['User.org_id' => $this->Auth->user('org_id')],
'fields' => ['User.id', 'User.id']
]);
$conditions['AND'][] = ['AuthKey.user_id' => array_values($userIds)];
}
if ($id !== false) {
$this->set('user_id', $id);
$conditions['AND'][] = ['AuthKey.user_id' => $id];
}
$this->CRUD->index([
'filters' => ['User.username', 'authkey', 'comment', 'User.id'],
'quickFilters' => ['authkey', 'comment'],
'contain' => ['User'],
'exclude_fields' => ['authkey'],
'conditions' => $conditions
]);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->set('metaGroup', $this->_isAdmin ? 'admin' : 'globalActions');
$this->set('metaAction', 'authkeys_index');
}
public function delete($id)
{
$params = [];
if (!$this->_isAdmin()) {
$params['conditions'] = ['user_id' => $this->Auth->user('id')];
}
$this->CRUD->delete($id, $params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
}
public function edit($id)
{
$this->set('metaGroup', 'admin');
$this->set('metaAction', 'authkeys_edit');
}
public function add($user_id = false)
{
$this->set('menuData', array('menuList' => $this->_isSiteAdmin() ? 'admin' : 'globalActions', 'menuItem' => 'authKeyAdd'));
$params = [
'displayOnSuccess' => 'authkey_display',
'saveModelVariable' => ['authkey_raw']
];
$selectConditions = [];
if (!$this->_isSiteAdmin()) {
$selectConditions['AND'][] = ['User.id' => $this->Auth->user('id')];
$params['override'] = ['user_id' => $this->Auth->user('id')];
} else if ($user_id) {
$selectConditions['AND'][] = ['User.id' => $user_id];
$params['override'] = ['user_id' => $user_id];
}
$this->CRUD->add($params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->loadModel('User');
$dropdownData = [
'user' => $this->User->find('list', [
'sort' => ['username' => 'asc'],
'conditions' => $selectConditions
])
];
$this->set(compact('dropdownData'));
}
public function view($id = false)
{
$this->set('menuData', array('menuList' => $this->_isSiteAdmin() ? 'admin' : 'globalActions', 'menuItem' => 'authKeyView'));
$this->CRUD->view($id, ['contain' => ['User.id', 'User.email']]);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
}
}

View File

@ -72,6 +72,13 @@ class ACLComponent extends Component
'view' => array('*'),
'viewPicture' => array('*'),
),
'authKeys' => [
'add' => ['perm_auth'],
'delete' => ['perm_auth'],
'edit' => ['perm_auth'],
'index' => ['perm_auth'],
'view' => ['perm_auth']
],
'dashboards' => array(
'getForm' => array('*'),
'index' => array('*'),
@ -601,7 +608,7 @@ class ACLComponent extends Component
),
'users' => array(
'acceptRegistrations' => array('perm_site_admin'),
'admin_add' => array('perm_admin'),
'admin_add' => ['AND' => ['perm_admin', 'add_user_enabled']],
'admin_delete' => array('perm_admin'),
'admin_edit' => array('perm_admin'),
'admin_email' => array('perm_admin'),
@ -611,19 +618,19 @@ class ACLComponent extends Component
'admin_quickEmail' => array('perm_admin'),
'admin_view' => array('perm_admin'),
'attributehistogram' => array('*'),
'change_pw' => array('*'),
'change_pw' => ['AND' => ['self_management_enabled', 'password_change_enabled']],
'checkAndCorrectPgps' => array(),
'checkIfLoggedIn' => array('*'),
'dashboard' => array('*'),
'delete' => array('perm_admin'),
'discardRegistrations' => array('perm_site_admin'),
'downloadTerms' => array('*'),
'edit' => array('*'),
'edit' => array('self_management_enabled'),
'email_otp' => array('*'),
'searchGpgKey' => array('*'),
'fetchGpgKey' => array('*'),
'histogram' => array('*'),
'initiatePasswordReset' => array('perm_admin'),
'initiatePasswordReset' => ['AND' => ['perm_admin', 'password_change_enabled']],
'login' => array('*'),
'logout' => array('*'),
'register' => array('*'),
@ -636,6 +643,7 @@ class ACLComponent extends Component
'tagStatisticsGraph' => array('*'),
'terms' => array('*'),
'updateLoginTime' => array('*'),
'updateToAdvancedAuthKeys' => array(),
'verifyCertificate' => array(),
'verifyGPG' => array(),
'view' => array('*'),
@ -672,6 +680,36 @@ class ACLComponent extends Component
)
);
private $dynamicChecks = [];
public function __construct(ComponentCollection $collection, $settings = array())
{
parent::__construct($collection, $settings);
$this->dynamicChecks['host_org_user'] = function (array $user) {
$hostOrgId = Configure::read('MISP.host_org_id');
return (int)$user['org_id'] === (int)$hostOrgId;
};
$this->dynamicChecks['self_management_enabled'] = function (array $user) {
if (Configure::read('MISP.disableUserSelfManagement') && !$user['Role']['perm_admin']) {
throw new MethodNotAllowedException('User self-management has been disabled on this instance.');
}
return true;
};
$this->dynamicChecks['password_change_enabled'] = function (array $user) {
if (Configure::read('MISP.disable_user_password_change')) {
throw new MethodNotAllowedException('User password change has been disabled on this instance.');
}
return true;
};
$this->dynamicChecks['add_user_enabled'] = function (array $user) {
if (Configure::read('MISP.disable_user_add')) {
throw new MethodNotAllowedException('Adding users has been disabled on this instance.');
}
return true;
};
}
private function __checkLoggedActions($user, $controller, $action)
{
$loggedActions = array(
@ -689,7 +727,6 @@ class ACLComponent extends Component
foreach ($loggedActions as $k => $v) {
$loggedActions[$k] = array_change_key_case($v);
}
$message = '';
if (!empty($loggedActions[$controller])) {
if (!empty($loggedActions[$controller][$action])) {
$message = $loggedActions[$controller][$action]['message'];
@ -744,6 +781,24 @@ class ACLComponent extends Component
}
}
/**
* @param array $user
* @param string $controller
* @param string $action
* @return bool
*/
public function canUserAccess($user, $controller, $action)
{
try {
$this->checkAccess($user, $controller, $action, false);
} catch (NotFoundException $e) {
throw new RuntimeException("Invalid controller '$controller' specified.", 0, $e);
} catch (MethodNotAllowedException $e) {
return false;
}
return true;
}
/**
* The check works like this:
* - If the user is a site admin, return true
@ -755,38 +810,38 @@ class ACLComponent extends Component
* @param array|null $user
* @param string $controller
* @param string $action
* @param bool $soft If true, instead of exception, HTTP error code is retuned as int.
* @return bool|int
* @param bool $checkLoggedActions
* @return true
* @throws NotFoundException
* @throws MethodNotAllowedException
* @throws InternalErrorException
*/
public function checkAccess($user, $controller, $action, $soft = false)
public function checkAccess($user, $controller, $action, $checkLoggedActions = true)
{
$controller = lcfirst(Inflector::camelize($controller));
$action = strtolower($action);
$host_org_id = Configure::read('MISP.host_org_id');
$aclList = $this->__aclList;
foreach ($aclList as $k => $v) {
$aclList[$k] = array_change_key_case($v);
}
if (!$soft) {
if ($checkLoggedActions) {
$this->__checkLoggedActions($user, $controller, $action);
}
if ($user && $user['Role']['perm_site_admin']) {
return true;
}
$aclList = $this->__aclList;
foreach ($aclList as $k => $v) {
$aclList[$k] = array_change_key_case($v);
}
if (!isset($aclList[$controller])) {
return $this->__error(404, 'Invalid controller.', $soft);
$this->__error(404);
}
if (isset($aclList[$controller][$action]) && !empty($aclList[$controller][$action])) {
if (in_array('*', $aclList[$controller][$action])) {
$rules = $aclList[$controller][$action];
if (in_array('*', $rules)) {
return true;
}
if (isset($aclList[$controller][$action]['OR'])) {
foreach ($aclList[$controller][$action]['OR'] as $permission) {
if ($permission === 'host_org_user') {
if ((int)$user['org_id'] === (int)$host_org_id) {
if (isset($rules['OR'])) {
foreach ($rules['OR'] as $permission) {
if (isset($this->dynamicChecks[$permission])) {
if ($this->dynamicChecks[$permission]($user)) {
return true;
}
} else {
@ -795,11 +850,11 @@ class ACLComponent extends Component
}
}
}
} elseif (isset($aclList[$controller][$action]['AND'])) {
} elseif (isset($rules['AND'])) {
$allConditionsMet = true;
foreach ($aclList[$controller][$action]['AND'] as $permission) {
if ($permission === 'host_org_user') {
if ((int)$user['org_id'] !== (int)$host_org_id) {
foreach ($rules['AND'] as $permission) {
if (isset($this->dynamicChecks[$permission])) {
if (!$this->dynamicChecks[$permission]($user)) {
$allConditionsMet = false;
}
} else {
@ -811,27 +866,30 @@ class ACLComponent extends Component
if ($allConditionsMet) {
return true;
}
} elseif ($aclList[$controller][$action][0] !== 'host_org_user' && $user['Role'][$aclList[$controller][$action][0]]) {
return true;
} elseif ($aclList[$controller][$action][0] === 'host_org_user' && (int)$user['org_id'] === (int)$host_org_id) {
} elseif (isset($this->dynamicChecks[$rules[0]])) {
if ($this->dynamicChecks[$rules[0]]($user)) {
return true;
}
} elseif ($user['Role'][$rules[0]]) {
return true;
}
}
return $this->__error(403, 'You do not have permission to use this functionality.', $soft);
$this->__error(403);
}
private function __error($code, $message, $soft = false)
/**
* @param int $code
* @throws InternalErrorException|MethodNotAllowedException|NotFoundException
*/
private function __error($code)
{
if ($soft) {
return $code;
}
switch ($code) {
case 404:
throw new NotFoundException($message);
throw new NotFoundException('Invalid controller.');
case 403:
throw new MethodNotAllowedException($message);
throw new MethodNotAllowedException('You do not have permission to use this functionality.');
default:
throw new InternalErrorException('Unknown error: ' . $message);
throw new InternalErrorException('Unknown error');
}
}
@ -903,39 +961,18 @@ class ACLComponent extends Component
return $results;
}
private function __checkRoleAccess($role)
private function __checkRoleAccess(array $role)
{
$result = array();
$fakeUser = ['Role' => $role, 'org_id' => Configure::read('MISP.host_org_id')];
foreach ($this->__aclList as $controller => $actions) {
$controllerNames = Inflector::variable($controller) == Inflector::underscore($controller) ? array(Inflector::variable($controller)) : array(Inflector::variable($controller), Inflector::underscore($controller));
$controllerNames = Inflector::variable($controller) === Inflector::underscore($controller) ?
array(Inflector::variable($controller)) :
array(Inflector::variable($controller), Inflector::underscore($controller));
foreach ($controllerNames as $controllerName) {
foreach ($actions as $action => $permissions) {
if ($role['perm_site_admin']) {
$result[] = DS . $controllerName . DS . $action;
} elseif (in_array('*', $permissions)) {
$result[] = DS . $controllerName . DS . $action . DS . '*';
} elseif (isset($permissions['OR'])) {
$access = false;
foreach ($permissions['OR'] as $permission) {
if ($role[$permission]) {
$access = true;
}
}
if ($access) {
$result[] = DS . $controllerName . DS . $action . DS . '*';
}
} elseif (isset($permissions['AND'])) {
$access = true;
foreach ($permissions['AND'] as $permission) {
if ($role[$permission]) {
$access = false;
}
}
if ($access) {
$result[] = DS . $controllerName . DS . $action . DS . '*';
}
} elseif (isset($permissions[0]) && $role[$permissions[0]]) {
$result[] = DS . $controllerName . DS . $action . DS . '*';
if ($this->canUserAccess($fakeUser, $controllerName, $action)) {
$result[] = "/$controllerName/$action";
}
}
}

View File

@ -0,0 +1,300 @@
<?php
class CRUDComponent extends Component
{
public $Controller = null;
public function initialize(Controller $controller, $settings=array()) {
$this->Controller = $controller;
}
private function prepareResponse()
{
if ($this->Controller->request->is('ajax')) {
$this->Controller->set('ajax', true);
}
}
public function index($options)
{
$this->prepareResponse();
if (!empty($options['quickFilters'])) {
if (empty($options['filters'])) {
$options['filters'] = [];
}
$options['filters'][] = 'quickFilter';
}
$params = $this->Controller->IndexFilter->harvestParameters(empty($options['filters']) ? [] : $options['filters']);
$query = [];
$query = $this->setFilters($params, $query);
$query = $this->setQuickFilters($params, $query, empty($options['quickFilters']) ? [] : $options['quickFilters']);
if (!empty($options['contain'])) {
$query['contain'] = $options['contain'];
}
if (!empty($options['conditions'])) {
$query['conditions']['AND'][] = $options['conditions'];
}
if ($this->Controller->IndexFilter->isRest()) {
if (!empty($this->Controller->paginate['fields'])) {
$query['fields'] = $this->Controller->paginate['fields'];
}
$data = $this->Controller->{$this->Controller->defaultModel}->find('all', $query);
if (isset($options['afterFind'])) {
$data = $options['afterFind']($data);
}
$this->Controller->restResponsePayload = $this->Controller->RestResponse->viewData($data, 'json');
} else {
$this->Controller->paginate = $query;
$data = $this->Controller->paginate();
if (isset($options['afterFind'])) {
$data = $options['afterFind']($data);
}
$this->Controller->set('data', $data);
}
}
public function add(array $params = [])
{
$modelName = $this->Controller->defaultModel;
$data = [];
if ($this->Controller->request->is('post')) {
$input = $this->Controller->request->data;
if (empty($input[$modelName])) {
$input = [$modelName => $input];
}
if (!empty($params['override'])) {
foreach ($params['override'] as $field => $value) {
$input[$modelName][$field] = $value;
}
}
if (isset($input[$modelName]['id'])) {
}
unset($input[$modelName]['id']);
if (!empty($params['fields'])) {
$data = [];
foreach ($params['fields'] as $field) {
$data[$field] = $input[$modelName][$field];
}
} else {
$data = $input;
}
if ($this->Controller->{$modelName}->save($data)) {
$data = $this->Controller->{$modelName}->find('first', [
'recursive' => -1,
'conditions' => [
'id' => $this->Controller->{$modelName}->id
]
]);
if (!empty($params['saveModelVariable'])) {
foreach ($params['saveModelVariable'] as $var) {
if (isset($this->Controller->{$modelName}->$var)) {
$data[$modelName][$var] = $this->Controller->{$modelName}->$var;
}
}
}
$message = __('%s added.', $modelName);
if ($this->Controller->IndexFilter->isRest()) {
$this->Controller->restResponsePayload = $this->Controller->RestResponse->viewData($data, 'json');
} else {
$this->Controller->Flash->success($message);
if (!empty($params['displayOnSuccess'])) {
$this->Controller->set('entity', $data);
$this->Controller->set('referer', $this->Controller->referer());
$this->Controller->render($params['displayOnSuccess']);
return;
}
$this->Controller->redirect(['action' => 'index']);
}
} else {
$message = __('%s could not be added.', $modelName);
if ($this->Controller->IndexFilter->isRest()) {
} else {
$this->Controller->Flash->error($message);
}
}
}
$this->Controller->set('entity', $data);
}
public function edit(int $id, array $params = []): void
{
$modelName = $this->Controller->defaultModel;
if (empty($id)) {
throw new NotFoundException(__('Invalid %s.', $modelname));
}
$data = $this->Controller->{$modelName}->find('first',
isset($params['get']) ? $params['get'] : [
'recursive' => -1,
'conditions' => [
'id' => $id
]
]);
if ($this->Controller->request->is('post') || $this->Controller->request->is('put')) {
$input = $this->Controller->request->data;
if (empty($input[$modelName])) {
$input = [$modelName => $input];
}
if (!empty($params['override'])) {
foreach ($params['override'] as $field => $value) {
$input[$field] = $value;
}
}
if (!empty($params['fields'])) {
foreach ($params['fields'] as $field) {
$data[$field] = $input[$modelName][$field];
}
} else {
foreach ($input as $field => $fieldData) {
$data[$field] = $fieldData;
}
}
if ($this->Controller->{$modelName}->save($data)) {
$message = __('%s updated.', $modelName);
if ($this->Controller->IndexFilter->isRest()) {
$this->Controller->restResponsePayload = $this->Controller->RestResponse->viewData($data, 'json');
} else {
$this->Controller->Flash->success($message);
$this->Controller->redirect(['action' => 'index']);
}
} else {
if ($this->Controller->IndexFilter->isRest()) {
}
}
}
$this->Controller->set('entity', $data);
}
public function view(int $id, array $params = []): void
{
if (empty($id)) {
throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias));
}
$modelName = $this->Controller->defaultModel;
$data = $this->Controller->{$modelName}->find('first', [
'recursive' => -1,
'conditions' => array($modelName . '.id' => $id),
'contain' => empty($params['contain']) ? [] : $params['contain']
]);
if ($this->Controller->IndexFilter->isRest()) {
$this->Controller->restResponsePayload = $this->Controller->RestResponse->viewData($data, 'json');
} else {
$this->Controller->set('data', $data);
}
}
public function delete(int $id, array $params = []): void
{
$this->prepareResponse();
$modelName = $this->Controller->defaultModel;
if (empty($id)) {
throw new NotFoundException(__('Invalid %s.', $modelname));
}
$conditions = [];
$conditions['AND'][] = ['id' => $id];
if (!empty($params['conditions'])) {
$conditions['AND'][] = $params['conditions'];
}
$data = $this->Controller->{$modelName}->find('first', [
'recursive' => -1,
'conditions' => $conditions
]);
if (empty($data)) {
throw new NotFoundException(__('Invalid %s.', $modelname));
}
if ($this->Controller->request->is('post') || $this->Controller->request->is('delete')) {
if (!empty($params['modelFunction'])) {
$result = $this->Controller->$modelName->{$params['modelFunction']}($id);
} else {
$result = $this->Controller->{$modelName}->delete($id);
}
if ($result) {
$message = __('%s deleted.', $modelName);
if ($this->Controller->IndexFilter->isRest()) {
$data = $this->Controller->{$modelName}->find('first', [
'recursive' => -1,
'conditions' => array('id' => $id)
]);
$this->Controller->restResponsePayload = $this->Controller->RestResponse->saveSuccessResponse($modelname, 'delete', $id, 'json', $message);
} else {
$this->Controller->Flash->success($message);
$this->Controller->redirect($this->Controller->referer());
}
}
}
$this->Controller->set('id', $data[$modelName]['id']);
$this->Controller->set('data', $data);
$this->Controller->layout = 'ajax';
$this->Controller->render('/genericTemplates/delete');
}
protected function setQuickFilters($params, $query, $quickFilterFields)
{
$queryConditions = [];
if (!empty($params['quickFilter']) && !empty($quickFilterFields)) {
foreach ($quickFilterFields as $filterField) {
$queryConditions[$filterField] = $params['quickFilter'];
}
$query['conditions']['OR'][] = $queryConditions;
}
return $query;
}
protected function setFilters($params, $query)
{
$params = $this->massageFilters($params);
$conditions = array();
if (!empty($params['simpleFilters'])) {
foreach ($params['simpleFilters'] as $filter => $filterValue) {
if ($filter === 'quickFilter') {
continue;
}
if (strlen(trim($filterValue, '%')) === strlen($filterValue)) {
$query['conditions']['AND'][] = [$filter => $filterValue];
} else {
$query['conditions']['AND'][] = [$filter . ' LIKE' => $filterValue];
}
}
}
/* Currently not implemented
if (!empty($params['relatedFilters'])) {
foreach ($params['relatedFilters'] as $filter => $filterValue) {
$filterParts = explode('.', $filter);
$query->matching($filterParts[0], function(\Cake\ORM\Query $q) use ($filterValue, $filter) {
if (strlen(trim($filterValue, '%')) === strlen($filterValue)) {
return $q->where([$filter => $filterValue]);
} else {
return $q->like([$filter => $filterValue]);
}
});
}
}
*/
return $query;
}
protected function massageFilters(array $params): array
{
$massagedFilters = [
'simpleFilters' => [],
'relatedFilters' => []
];
if (!empty($params)) {
foreach ($params as $param => $paramValue) {
if (strpos($param, '.') !== false) {
$param = explode('.', $param);
if ($param[0] === $this->Controller->{$this->Controller->defaultModel}) {
$massagedFilters['simpleFilters'][implode('.', $param)] = $paramValue;
} else {
$massagedFilters['relatedFilters'][implode('.', $param)] = $paramValue;
}
} else {
$massagedFilters['simpleFilters'][$param] = $paramValue;
}
}
}
return $massagedFilters;
}
}

View File

@ -6,29 +6,30 @@
class IndexFilterComponent extends Component
{
private $__Controller = false;
public $Controller = false;
public $isRest = null;
public function startup(Controller $controller) {
$this->__Controller = $controller;
public function initialize(Controller $controller) {
$this->Controller = $controller;
}
// generic function to standardise on the collection of parameters. Accepts posted request objects, url params, named url params
public function harvestParameters($paramArray, &$exception = array())
{
$data = array();
if (!empty($this->__Controller->request->is('post'))) {
if (empty($this->__Controller->request->data)) {
$exception = $this->__Controller->RestResponse->throwException(
if (!empty($this->Controller->request->is('post'))) {
if (empty($this->Controller->request->data)) {
$exception = $this->Controller->RestResponse->throwException(
400,
__('Either specify the search terms in the url, or POST a json with the filter parameters.'),
'/' . $this->__Controller->request->params['controller'] . '/' . $this->__Controller->action
'/' . $this->Controller->request->params['controller'] . '/' . $this->Controller->action
);
return false;
} else {
if (isset($this->__Controller->request->data['request'])) {
$data = $this->__Controller->request->data['request'];
if (isset($this->Controller->request->data['request'])) {
$data = $this->Controller->request->data['request'];
} else {
$data = $this->__Controller->request->data;
$data = $this->Controller->request->data;
}
}
}
@ -41,8 +42,8 @@ class IndexFilterComponent extends Component
$data[$p] = $options['ordered_url_params'][$p];
$data[$p] = str_replace(';', ':', $data[$p]);
}
if (isset($this->__Controller->params['named'][$p])) {
$data[$p] = str_replace(';', ':', $this->__Controller->params['named'][$p]);
if (isset($this->Controller->params['named'][$p])) {
$data[$p] = str_replace(';', ':', $this->Controller->params['named'][$p]);
}
}
}
@ -73,8 +74,55 @@ class IndexFilterComponent extends Component
}
}
}
$this->__Controller->set('passedArgs', json_encode($this->__Controller->passedArgs, true));
$this->Controller->set('passedArgs', json_encode($this->Controller->passedArgs, true));
return $data;
}
public function isRest()
{
// This method is surprisingly slow and called many times for one request, so it make sense to cache the result.
if ($this->isRest !== null) {
return $this->isRest;
}
$api = $this->isApiFunction($this->Controller->request->params['controller'], $this->Controller->request->params['action']);
if (isset($this->Controller->RequestHandler) && ($api || $this->Controller->RequestHandler->isXml() || $this->isJson() || $this->isCsv())) {
if ($this->isJson()) {
if (!empty($this->Controller->request->input()) && empty($this->Controller->request->input('json_decode'))) {
throw new MethodNotAllowedException('Invalid JSON input. Make sure that the JSON input is a correctly formatted JSON string. This request has been blocked to avoid an unfiltered request.');
}
}
$this->isRest = true;
return true;
} else {
$this->isRest = false;
return false;
}
}
public function isJson($data=false)
{
if ($data) {
return (json_decode($data) != null) ? true : false;
}
return $this->Controller->request->header('Accept') === 'application/json' || $this->Controller->RequestHandler->prefers() === 'json';
}
public function isCsv()
{
}
public function isXml()
{
}
public function isApiFunction($controller, $action)
{
if (isset($this->Controller->automationArray[$controller]) && in_array($action, $this->Controller->automationArray[$controller])) {
return true;
}
return false;
}
}

View File

@ -310,7 +310,7 @@ class RestResponseComponent extends Component
foreach ($this->__scopedFieldsConstraint as $controller => $actions) {
$controller = Inflector::tableize($controller);
foreach ($actions as $action => $data) {
if ($this->ACL->checkAccess($user, $controller, $action, true) === true) {
if ($this->ACL->canUserAccess($user, $controller, $action)) {
$admin_routing = '';
if (substr($action, 0, 6) === 'admin_') {
$action = substr($action, 6);
@ -331,7 +331,7 @@ class RestResponseComponent extends Component
foreach ($this->__descriptions as $controller => $actions) {
$controller = Inflector::tableize($controller);
foreach ($actions as $action => $data) {
if ($this->ACL->checkAccess($user, $controller, $action, true) === true) {
if ($this->ACL->canUserAccess($user, $controller, $action)) {
$admin_routing = '';
if (substr($action, 0, 6) === 'admin_') {
$action = substr($action, 6);
@ -373,7 +373,11 @@ class RestResponseComponent extends Component
return $result;
}
// use a relative path to check if the current api has a description
/**
* Use a relative path to check if the current api has a description
* @param string $relative_path
* @return array
*/
public function getApiInfo($relative_path)
{
$this->__setup();
@ -383,7 +387,7 @@ class RestResponseComponent extends Component
if (count($relative_path) >= 2) {
if ($relative_path[0] == 'admin') {
if (count($relative_path) < 3) {
return '[]';
return [];
}
$admin = true;
$relative_path = array_slice($relative_path, 1);
@ -393,16 +397,10 @@ class RestResponseComponent extends Component
$relative_path[1] = 'admin_' . $relative_path[1];
}
if (isset($this->__descriptions[$relative_path[0]][$relative_path[1]])) {
$temp = $this->__descriptions[$relative_path[0]][$relative_path[1]];
} else {
$temp = array();
return $this->__descriptions[$relative_path[0]][$relative_path[1]];
}
if (empty($temp)) {
return '[]';
}
return json_encode(array('api_info' => $temp), JSON_PRETTY_PRINT);
}
return '[]';
return [];
}
public function saveFailResponse($controller, $action, $id = false, $validationErrors, $format = false, $data = null)

View File

@ -26,53 +26,31 @@ class RolesController extends AppController
)
);
public function view($id = null)
public function view($id=false)
{
$this->Role->id = $id;
if (!$this->Role->exists()) {
throw new NotFoundException(__('Invalid role'));
}
if ($this->_isRest()) {
return $this->RestResponse->viewData($this->Role->read(null, $id), $this->response->type());
} else {
$this->set('premissionLevelName', $this->Role->premissionLevelName);
$this->set('role', $this->Role->read(null, $id));
$this->set('id', $id);
$this->set('menuData', ['menuList' => 'globalActions', 'menuItem' => 'roles']);
$this->CRUD->view($id);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->set('permissionLevelName', $this->Role->premissionLevelName);
$this->set('permFlags', $this->Role->permFlags);
}
public function admin_add()
{
if (!$this->_isSiteAdmin()) {
$this->redirect(array('controller' => 'roles', 'action' => 'index', 'admin' => false));
}
if ($this->request->is('post')) {
$this->Role->create();
if ($this->Role->save($this->request->data)) {
if ($this->_isRest()) {
$role = $this->Role->find('first', array(
'recursive' => -1,
'conditions' => array('Role.id' => $this->Role->id)
));
return $this->RestResponse->viewData($role, $this->response->type());
} else {
$this->Flash->success(__('The Role has been saved'));
$this->redirect(array('action' => 'index'));
}
} else {
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Role', 'admin_add', false, $this->Role->validationErrors, $this->response->type());
} else {
if (!($this->Session->check('Message.flash'))) {
$this->Role->Session->setFlash(__('The Role could not be saved. Please, try again.'));
}
}
}
} elseif ($this->_isRest()) {
return $this->RestResponse->describe('Roles', 'admin_add', false, $this->response->type());
$this->set('menuData', array('menuList' => 'admin', 'menuItem' => 'addRole'));
$params = [];
$selectConditions = [];
$this->CRUD->add($params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->set('permFlags', $this->Role->permFlags);
$this->set('options', $this->options);
$dropdownData = [
'options' => $this->options
];
$this->set(compact('dropdownData'));
}
public function admin_edit($id = null)
@ -121,48 +99,34 @@ class RolesController extends AppController
$this->set('id', $id);
}
public function admin_index()
public function admin_index($id = false)
{
if (!$this->_isSiteAdmin()) {
$this->redirect(array('controller' => 'roles', 'action' => 'index', 'admin' => false));
}
$this->recursive = 0;
if ($this->_isRest()) {
$roles = $this->Role->find('all', array(
'recursive' => -1
));
return $this->RestResponse->viewData($roles, $this->response->type());
} else {
$this->set('list', $this->paginate());
$this->set('permFlags', $this->Role->permFlags);
$this->loadModel('AdminSetting');
$this->set('default_role_id', $this->AdminSetting->getSetting('default_role'));
$this->set('options', $this->options);
$params = [
'filters' => ['name'],
'quickFilters' => ['name'],
'afterFind' => function($elements) {
$this->loadModel('AdminSetting');
$default_setting = $this->AdminSetting->getSetting('default_role');
foreach ($elements as &$role) {
$role['Role']['default'] = ($role['Role']['id'] == $default_setting) ? true : false;
}
return $elements;
}
];
//$this->paginate['fields'] = ['id', 'name'];
$this->CRUD->index($params);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
$this->set('permFlags', $this->Role->permFlags);
$this->set('menuData', array('menuList' => 'globalActions', 'menuItem' => 'roles'));
}
public function admin_delete($id = null)
{
if (!$this->request->is('post') && !$this->request->is('put') && !$this->request->is('delete')) {
throw new MethodNotAllowedException();
}
$this->Role->id = $id;
if (!$this->Role->exists()) {
throw new NotFoundException(__('Invalid Role'));
}
if ($this->Role->delete()) {
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Roles', 'admin_delete', $id, $this->response->type());
} else {
$this->Flash->success(__('Role deleted'));
$this->redirect(array('action' => 'index'));
}
}
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Roles', 'admin_delete', $id, $this->Role->validationErrors, $this->response->type());
} else {
$this->Flash->error(__('Role could not be deleted'));
$this->redirect(array('action' => 'index'));
$this->CRUD->delete($id);
if ($this->IndexFilter->isRest()) {
return $this->restResponsePayload;
}
}
@ -185,29 +149,37 @@ class RolesController extends AppController
public function admin_set_default($role_id = false)
{
$this->Role->id = $role_id;
if ((!is_numeric($role_id) && $role_id !== false) || !$this->Role->exists()) {
$message = 'Invalid Role.';
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Roles', 'admin_set_default', $role_id, $message, $this->response->type());
} else {
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => $message)), 'status'=>200, 'type' => 'json'));
if ($this->request->is('post')) {
$this->Role->id = $role_id;
if ((!is_numeric($role_id) && $role_id !== false) || !$this->Role->exists()) {
$message = 'Invalid Role.';
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Roles', 'admin_set_default', $role_id, $message, $this->response->type());
} else {
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => $message)), 'status'=>200, 'type' => 'json'));
}
}
}
$this->loadModel('AdminSetting');
$result = $this->AdminSetting->changeSetting('default_role', $role_id);
if ($result === true) {
$message = $role_id ? __('Default role set.') : __('Default role unset.');
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Roles', 'admin_set_default', $role_id, $this->response->type(), $message);
$this->loadModel('AdminSetting');
$result = $this->AdminSetting->changeSetting('default_role', $role_id);
if ($result === true) {
$message = $role_id ? __('Default role set.') : __('Default role unset.');
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('Roles', 'admin_set_default', $role_id, $this->response->type(), $message);
} else {
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => $message)), 'status'=>200, 'type' => 'json'));
}
} else {
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => $message)), 'status'=>200, 'type' => 'json'));
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Roles', 'admin_set_default', $role_id, $result, $this->response->type());
} else {
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => $result)), 'status'=>200, 'type' => 'json'));
}
}
} else {
if ($this->_isRest()) {
return $this->RestResponse->saveFailResponse('Roles', 'admin_set_default', $role_id, $result, $this->response->type());
return $this->RestResponse->saveFailResponse('Role', 'admin_set_default', false, __('This endpoint expects a POST request.'), $this->response->type());
} else {
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => $result)), 'status'=>200, 'type' => 'json'));
$this->layout = false;
}
}
}

View File

@ -1667,7 +1667,7 @@ class ServersController extends AppController
throw new MethodNotAllowedException('You don\'t have permission to do that.');
}
$server = $this->Server->find('first', ['Server.id' => $id]);
$server = $this->Server->find('first', ['conditions' => ['Server.id' => $id]]);
if (!$server) {
throw new NotFoundException(__('Invalid server'));
}
@ -1964,19 +1964,23 @@ class ServersController extends AppController
}
$curl = '';
$python = '';
$result = $this->__doRestQuery($request, $curl, $python);
$this->set('curl', $curl);
$this->set('python', $python);
if (!$result) {
$this->Flash->error('Something went wrong. Make sure you set the http method, body (when sending POST requests) and URL correctly.');
} else {
$this->set('data', $result);
try {
$result = $this->__doRestQuery($request, $curl, $python);
$this->set('curl', $curl);
$this->set('python', $python);
if (!$result) {
$this->Flash->error('Something went wrong. Make sure you set the http method, body (when sending POST requests) and URL correctly.');
} else {
$this->set('data', $result);
}
} catch (Exception $e) {
$this->Flash->error(__('Something went wrong. %s', $e->getMessage()));
}
}
$header =
'Authorization: ' . $this->Auth->user('authkey') . PHP_EOL .
'Accept: application/json' . PHP_EOL .
'Content-Type: application/json';
$header = sprintf(
"Authorization: %s \nAccept: application/json\nContent-type: application/json",
empty(Configure::read('Security.advanced_authkeys')) ? $this->Auth->user('authkey') : __('YOUR_API_KEY')
);
$this->set('header', $header);
$this->set('allValidApis', $allValidApis);
// formating for optgroup
@ -1988,17 +1992,33 @@ class ServersController extends AppController
$this->set('allValidApisFieldsContraint', $allValidApisFieldsContraint);
}
private function __doRestQuery($request, &$curl = false, &$python = false)
/**
* @param array $request
* @param string $curl
* @param string $python
* @return array|false
*/
private function __doRestQuery(array $request, &$curl = false, &$python = false)
{
App::uses('SyncTool', 'Tools');
$params = array();
$this->loadModel('RestClientHistory');
$this->RestClientHistory->create();
$date = new DateTime();
$logHeaders = $request['header'];
if (!empty(Configure::read('Security.advanced_authkeys'))) {
$logHeaders = explode("\n", $request['header']);
foreach ($logHeaders as $k => $header) {
if (strpos($header, 'Authorization') !== false) {
$logHeaders[$k] = 'Authorization: ' . __('YOUR_API_KEY');
}
}
$logHeaders = implode("\n", $logHeaders);
}
$rest_history_item = array(
'org_id' => $this->Auth->user('org_id'),
'user_id' => $this->Auth->user('id'),
'headers' => $request['header'],
'headers' => $logHeaders,
'body' => empty($request['body']) ? '' : $request['body'],
'url' => $request['url'],
'http_method' => $request['method'],
@ -2018,7 +2038,7 @@ class ServersController extends AppController
$url = $request['url'];
}
} else {
throw new InvalidArgumentException('Url not set.');
throw new InvalidArgumentException('URL not set.');
}
if (!empty($request['skip_ssl_validation'])) {
$params['ssl_verify_peer'] = false;
@ -2081,7 +2101,7 @@ class ServersController extends AppController
return false;
}
$view_data['duration'] = microtime(true) - $start;
$view_data['duration'] = round($view_data['duration'] * 1000, 2) . 'ms';
$view_data['duration'] = round($view_data['duration'] * 1000, 2) . ' ms';
$view_data['url'] = $url;
$view_data['code'] = $response->code;
$view_data['headers'] = $response->headers;
@ -2160,7 +2180,7 @@ misp.direct_call(relative_path, body)
$curl = sprintf(
'curl \%s -d \'%s\' \%s -H "Authorization: %s" \%s -H "Accept: %s" \%s -H "Content-type: %s" \%s -X POST %s',
PHP_EOL,
json_encode(json_decode($request['body']), true),
json_encode(json_decode($request['body'])),
PHP_EOL,
$request['header']['Authorization'],
PHP_EOL,
@ -2179,9 +2199,11 @@ misp.direct_call(relative_path, body)
$relative_path = $this->request->data['url'];
$result = $this->RestResponse->getApiInfo($relative_path);
if ($this->_isRest()) {
return $this->RestResponse->viewData($result, $this->response->type(), false, true);
if (!empty($result)) {
$result['api_info'] = $result;
}
return $this->RestResponse->viewData($result, $this->response->type());
} else {
$result = json_decode($result, true);
if (empty($result)) {
return $this->RestResponse->viewData('&nbsp;', $this->response->type());
}

View File

@ -71,6 +71,9 @@ class UsersController extends AppController
if (empty($user)) {
throw new NotFoundException(__('Invalid user'));
}
if (!empty(Configure::read('Security.advanced_authkeys'))) {
unset($user['User']['authkey']);
}
if (!empty($user['User']['gpgkey'])) {
$pgpDetails = $this->User->verifySingleGPG($user);
$user['User']['pgp_status'] = isset($pgpDetails[2]) ? $pgpDetails[2] : 'OK';
@ -87,12 +90,16 @@ class UsersController extends AppController
return $this->RestResponse->viewData($this->__massageUserObject($user), $this->response->type());
} else {
$this->set('user', $user);
$this->set('admin_view', false);
}
}
private function __massageUserObject($user)
{
unset($user['User']['server_id']);
if (!empty(Configure::read('Security.advanced_authkeys'))) {
unset($user['User']['authkey']);
}
$user['User']['password'] = '*****';
$objectsToInclude = array('User', 'Role', 'UserSetting', 'Organisation');
foreach ($objectsToInclude as $objectToInclude) {
@ -124,9 +131,6 @@ class UsersController extends AppController
public function edit()
{
if (!$this->_isAdmin() && Configure::read('MISP.disableUserSelfManagement')) {
throw new MethodNotAllowedException('User self-management has been disabled on this instance.');
}
$currentUser = $this->User->find('first', array(
'conditions' => array('User.id' => $this->Auth->user('id')),
'recursive' => -1
@ -178,8 +182,11 @@ class UsersController extends AppController
}
if (!$abortPost) {
// What fields should be saved (allowed to be saved)
$fieldList = array('email', 'autoalert', 'gpgkey', 'certif_public', 'nids_sid', 'contactalert', 'disabled');
if (!empty($this->request->data['User']['password'])) {
$fieldList = array('autoalert', 'gpgkey', 'certif_public', 'nids_sid', 'contactalert', 'disabled');
if ($this->__canChangeLogin()) {
$fieldList[] = 'email';
}
if ($this->__canChangePassword() && !empty($this->request->data['User']['password'])) {
$fieldList[] = 'password';
$fieldList[] = 'confirm_password';
}
@ -236,15 +243,14 @@ class UsersController extends AppController
$this->set('complexity', !empty(Configure::read('Security.password_policy_complexity')) ? Configure::read('Security.password_policy_complexity') : $this->Server->serverSettings['Security']['password_policy_complexity']['value']);
$this->set('length', !empty(Configure::read('Security.password_policy_length')) ? Configure::read('Security.password_policy_length') : $this->Server->serverSettings['Security']['password_policy_length']['value']);
$roles = $this->User->Role->find('list');
$this->set(compact('roles'));
$this->set('roles', $roles);
$this->set('id', $id);
$this->set('canChangePassword', $this->__canChangePassword());
$this->set('canChangeLogin', $this->__canChangeLogin());
}
public function change_pw()
{
if (!$this->_isAdmin() && Configure::read('MISP.disableUserSelfManagement')) {
throw new MethodNotAllowedException('User self-management has been disabled on this instance.');
}
$id = $this->Auth->user('id');
$user = $this->User->find('first', array(
'conditions' => array('User.id' => $id),
@ -327,14 +333,11 @@ class UsersController extends AppController
$this->User->set('password', '');
$this->request->data = $this->User->data;
$roles = $this->User->Role->find('list');
$this->set(compact('roles'));
$this->set('roles', $roles);
}
public function admin_index()
{
if (!$this->_isAdmin()) {
throw new NotFoundException(__('Invalid user or not authorised.'));
}
$this->User->virtualFields['org_ci'] = 'UPPER(Organisation.name)';
$urlParams = "";
$passedArgsArray = array();
@ -490,9 +493,6 @@ class UsersController extends AppController
public function admin_filterUserIndex()
{
if (!$this->_isAdmin() && !$this->_isSiteAdmin()) {
throw new MethodNotAllowedException();
}
$passedArgsArray = array();
$booleanFields = array('autoalert', 'contactalert', 'termsaccepted');
$textFields = array('role', 'email', 'authkey');
@ -563,14 +563,25 @@ class UsersController extends AppController
public function admin_view($id = null)
{
$contain = [
'UserSetting',
'Role',
'Organisation'
];
if (!empty(Configure::read('Security.advanced_authkeys'))) {
$contain['AuthKey'] = [
'conditions' => [
'OR' => [
'AuthKey.expiration' => 0,
'AuthKey.expiration <' => time()
]
]
];
}
$user = $this->User->find('first', array(
'recursive' => -1,
'conditions' => array('User.id' => $id),
'contain' => array(
'UserSetting',
'Role',
'Organisation'
)
'contain' => $contain
));
if (empty($user)) {
throw new NotFoundException(__('Invalid user'));
@ -584,6 +595,9 @@ class UsersController extends AppController
if (empty($this->Auth->user('Role')['perm_site_admin']) && !(empty($user['Role']['perm_site_admin']))) {
$user['User']['authkey'] = __('Redacted');
}
if (!empty(Configure::read('Security.advanced_authkeys'))) {
unset($user['User']['authkey']);
}
$this->set('user', $user);
if (!$this->_isSiteAdmin() && !($this->_isAdmin() && $this->Auth->user('org_id') == $user['User']['org_id'])) {
throw new MethodNotAllowedException();
@ -605,14 +619,13 @@ class UsersController extends AppController
$user2 = $this->User->find('first', array('conditions' => array('User.id' => $user['User']['invited_by']), 'recursive' => -1));
$this->set('id', $id);
$this->set('user2', $user2);
$this->set('admin_view', true);
$this->render('view');
}
}
public function admin_add()
{
if (!$this->_isAdmin()) {
throw new Exception('Administrators only.');
}
$params = null;
if (!$this->_isSiteAdmin()) {
$params = array('conditions' => array('perm_site_admin !=' => 1, 'perm_sync !=' => 1, 'perm_regexp_access !=' => 1));
@ -660,7 +673,7 @@ class UsersController extends AppController
'disabled' => 0,
'newsread' => 0,
'change_pw' => 1,
'authkey' => $this->User->generateAuthKey(),
'authkey' => (new RandomTool())->random_str(true, 40),
'termsaccepted' => 0,
'org_id' => $this->Auth->user('org_id')
);
@ -756,12 +769,19 @@ class UsersController extends AppController
$notification_message .= ' ' . __('User notification of new credentials could not be send.');
}
}
if (!empty(Configure::read('Security.advanced_authkeys'))) {
$this->loadModel('AuthKey');
$newKey = $this->AuthKey->createnewkey($this->User->id);
}
if ($this->_isRest()) {
$user = $this->User->find('first', array(
'conditions' => array('User.id' => $this->User->id),
'recursive' => -1
));
$user['User']['password'] = '******';
if (!empty(Configure::read('Security.advanced_authkeys'))) {
$user['User']['authkey'] = $newKey;
}
return $this->RestResponse->viewData($user, $this->response->type());
} else {
$this->Flash->success(__('The user has been saved.') . $notification_message);
@ -878,7 +898,6 @@ class UsersController extends AppController
}
}
}
$fail = false;
if (!$this->_isSiteAdmin() && !$abortPost) {
$organisation = $this->User->Organisation->find('first', array(
'conditions' => array('Organisation.id' => $userToEdit['User']['org_id']),
@ -913,6 +932,13 @@ class UsersController extends AppController
if (!$this->_isSiteAdmin()) {
$blockedFields[] = 'org_id';
}
if (!$this->__canChangeLogin()) {
$blockedFields[] = 'email';
}
if (!$this->__canChangePassword()) {
$blockedFields[] = 'enable_password';
$blockedFields[] = 'change_pw';
}
foreach (array_keys($this->request->data['User']) as $field) {
if (in_array($field, $blockedFields)) {
continue;
@ -994,6 +1020,9 @@ class UsersController extends AppController
$this->User->extralog($this->Auth->user(), "edit", "user", $fieldsResult, $user);
if ($this->_isRest()) {
$user['User']['password'] = '******';
if (!empty(Configure::read('Security.advanced_authkeys'))) {
unset($user['User']['authkey']);
}
return $this->RestResponse->viewData($user, $this->response->type());
} else {
$this->Flash->success(__('The user has been saved'));
@ -1048,6 +1077,8 @@ class UsersController extends AppController
$this->set('id', $id);
$this->set(compact('roles'));
$this->set(compact('syncRoles'));
$this->set('canChangeLogin', $this->__canChangeLogin());
$this->set('canChangePassword', $this->__canChangePassword());
}
public function admin_delete($id = null)
@ -2664,4 +2695,55 @@ class UsersController extends AppController
}
}
}
public function updateToAdvancedAuthKeys()
{
if (!$this->request->is('post')) {
throw new MethodNotAllowedException(__('This endpoint can only be triggered via POST requests.'));
}
$users = $this->User->find('all', [
'recursive' => -1,
'contain' => ['AuthKey'],
'fields' => ['id', 'authkey']
]);
$updated = 0;
foreach ($users as $user) {
if (!empty($user['AuthKey'])) {
$currentKeyStart = substr($user['User']['authkey'], 0, 4);
$currentKeyEnd = substr($user['User']['authkey'], -4);
foreach ($user['AuthKey'] as $authkey) {
if ($authkey['authkey_start'] === $currentKeyStart && $authkey['authkey_end'] === $currentKeyEnd) {
continue 2;
}
}
}
$this->User->AuthKey->create();
$this->User->AuthKey->save([
'authkey' => $user['User']['authkey'],
'expiration' => 0,
'user_id' => $user['User']['id']
]);
$updated += 1;
}
$message = __('The upgrade process is complete, %s authkey(s) generated.', $updated);
if ($this->_isRest()) {
return $this->RestResponse->saveSuccessResponse('User', 'acceptRegistrations', false, $this->response->type(), $message);
} else {
$this->Flash->success($message);
$this->redirect($this->referer());
}
}
private function __canChangePassword()
{
return $this->ACL->canUserAccess($this->Auth->user(), 'users', 'change_pw');
}
private function __canChangeLogin()
{
if ($this->_isSiteAdmin()) {
return true;
}
return !Configure::read('MISP.disable_user_login_change');
}
}

View File

@ -86,7 +86,7 @@ class AppModel extends Model
39 => false, 40 => false, 41 => false, 42 => false, 43 => false, 44 => false,
45 => false, 46 => false, 47 => false, 48 => false, 49 => false, 50 => false,
51 => false, 52 => false, 53 => false, 54 => false, 55 => false, 56 => false,
57 => false, 58 => false, 59 => false, 60 => false,
57 => false, 58 => false, 59 => false, 60 => false, 61 => false
);
public $advanced_updates_description = array(
@ -1448,6 +1448,25 @@ class AppModel extends Model
INDEX `index` (`type`, `attribute_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
break;
case 61:
$sqlArray[] = "CREATE TABLE IF NOT EXISTS `auth_keys` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`uuid` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL,
`authkey` varchar(72) CHARACTER SET ascii DEFAULT NULL,
`authkey_start` varchar(4) CHARACTER SET ascii DEFAULT NULL,
`authkey_end` varchar(4) CHARACTER SET ascii DEFAULT NULL,
`created` int(10) unsigned NOT NULL,
`expiration` int(10) unsigned NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`comment` text COLLATE utf8mb4_unicode_ci,
PRIMARY KEY (`id`),
KEY `authkey_start` (`authkey_start`),
KEY `authkey_end` (`authkey_end`),
KEY `created` (`created`),
KEY `expiration` (`expiration`),
KEY `user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
break;
case 'fixNonEmptySharingGroupID':
$sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
$sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';

View File

@ -3348,23 +3348,8 @@ class Attribute extends AppModel
$params['contain']['AttributeTag']['Tag']['conditions']['exportable'] = 1;
}
if (!empty($options['includeContext'])) {
$params['contain']['Event'] = array(
'fields' => array(
'id','orgc_id','org_id','date','threat_level_id','info','published','uuid','analysis','timestamp','distribution','publish_timestamp','sharing_group_id','extends_uuid'
),
'EventTag' => array(
'Tag' => array(
'fields' => array(
'Tag.id', 'Tag.name', 'Tag.colour', 'Tag.numerical_value'
)
)
),
'Orgc' => array(
'fields' => array(
'Orgc.id', 'Orgc.uuid', 'Orgc.name'
)
)
);
// include just event id for conditions, rest event data will be fetched later
$params['contain']['Event']['fields'] = ['id'];
}
if (isset($options['contain'])) {
// We may use a string instead of an array to ask for everything
@ -3503,6 +3488,19 @@ class Attribute extends AppModel
}
}
$results = $this->find('all', $params);
if (!empty($options['includeContext']) && !empty($results)) {
$eventIds = [];
foreach ($results as $result) {
$eventIds[$result['Attribute']['event_id']] = true; // deduplicate
}
$eventsById = $this->__fetchEventsForAttributeContext($user, array_keys($eventIds), !empty($options['includeAllTags']));
foreach ($results as &$result) {
$result['Event'] = $eventsById[$result['Attribute']['event_id']];
}
unset($eventsById, $result); // unset result is important, because it is reference
}
foreach ($results as $k => $result) {
if (!empty($result['AttributeTag'])) {
$tagCulled = false;
@ -3518,14 +3516,6 @@ class Attribute extends AppModel
$results[$k]['AttributeTag'] = array_values($results[$k]['AttributeTag']);
}
}
if (isset($result['Event']['EventTag'])) {
$results[$k]['Event']['Tag'] = array();
foreach ($result['Event']['EventTag'] as $et) {
$et['Tag']['local'] = $et['local'];
$results[$k]['Event']['Tag'][] = $et['Tag'];
}
unset($results[$k]['Event']['EventTag']);
}
if (!empty($options['includeSightings'])) {
$temp = $result['Attribute'];
$temp['Event'] = $result['Event'];
@ -3608,6 +3598,50 @@ class Attribute extends AppModel
return $attributes;
}
/**
* @param array $user
* @param array $eventIds
* @param bool $includeAllTags
* @return array
* @throws Exception
*/
private function __fetchEventsForAttributeContext(array $user, array $eventIds, $includeAllTags)
{
if (empty($eventIds)) {
return [];
}
$events = $this->Event->fetchEvent($user, [
'eventid' => $eventIds,
'metadata' => true,
'sgReferenceOnly' => true,
'includeEventCorrelations' => false,
'includeAllTags' => $includeAllTags,
]);
$eventFields = ['id', 'orgc_id', 'org_id', 'date', 'threat_level_id', 'info', 'published', 'uuid', 'analysis', 'timestamp', 'distribution', 'publish_timestamp', 'sharing_group_id', 'extends_uuid'];
$tagFields = ['id', 'name', 'colour', 'numerical_value'];
$eventsById = [];
// Reformat to required format
foreach ($events as $event) {
$newEvent = [];
foreach ($eventFields as $eventField) {
$newEvent[$eventField] = $event['Event'][$eventField];
}
$tags = [];
foreach ($event['EventTag'] as $et) {
$tag = ['local' => $et['local']];
foreach ($tagFields as $tagField) {
$tag[$tagField] = $et['Tag'][$tagField];
}
$tags[] = $tag;
}
$newEvent['Tag'] = $tags;
$newEvent['Orgc'] = $event['Orgc'];
$eventsById[$newEvent['id']] = $newEvent;
}
return $eventsById;
}
private function __attachEventTagsToAttributes($eventTags, &$results, $key, $options)
{
if (!isset($eventTags[$results[$key]['Event']['id']])) {
@ -4645,10 +4679,10 @@ class Attribute extends AppModel
$tmpfile->write($exportTool->header($exportToolParams));
$loop = false;
if (empty($params['limit'])) {
$memory_in_mb = $this->convert_to_memory_limit_to_mb(ini_get('memory_limit'));
$memoryInMb = $this->convert_to_memory_limit_to_mb(ini_get('memory_limit'));
$default_attribute_memory_coefficient = Configure::check('MISP.default_attribute_memory_coefficient') ? Configure::read('MISP.default_attribute_memory_coefficient') : 80;
$memory_scaling_factor = isset($exportTool->memory_scaling_factor) ? $exportTool->memory_scaling_factor : $default_attribute_memory_coefficient;
$params['limit'] = $memory_in_mb * $memory_scaling_factor;
$memoryScalingFactor = isset($exportTool->memory_scaling_factor) ? $exportTool->memory_scaling_factor : $default_attribute_memory_coefficient;
$params['limit'] = $memoryInMb * $memoryScalingFactor;
$loop = true;
$params['page'] = 1;
}
@ -4664,9 +4698,9 @@ class Attribute extends AppModel
* @param array $params
* @param bool $loop If true, data are fetched in loop to keep memory usage low
* @param TmpFileTool $tmpfile
* @param $exportTool
* @param object $exportTool
* @param array $exportToolParams
* @return int
* @return int Number of attributes
* @throws Exception
*/
private function __iteratedFetch(array $user, array $params, $loop, TmpFileTool $tmpfile, $exportTool, array $exportToolParams)

119
app/Model/AuthKey.php Normal file
View File

@ -0,0 +1,119 @@
<?php
App::uses('AppModel', 'Model');
App::uses('RandomTool', 'Tools');
/**
* @property User $User
*/
class AuthKey extends AppModel
{
public $recursive = -1;
public $actsAs = array(
'SysLogLogable.SysLogLogable' => array(
'userModel' => 'User',
'userKey' => 'user_id',
'change' => 'full'),
'Containable',
);
public $belongsTo = array(
'User'
);
public $authkey_raw = false;
// massage the data before we send it off for validation before saving anything
public function beforeValidate($options = array())
{
//parent::beforeValidate();
if (empty($this->data['AuthKey']['id'])) {
if (empty($this->data['AuthKey']['uuid'])) {
$this->data['AuthKey']['uuid'] = CakeText::uuid();
}
// add a timestamp if it is not set
if (empty($this->data['AuthKey']['timestamp'])) {
$this->data['AuthKey']['timestamp'] = time();
}
if (empty($this->data['AuthKey']['authkey'])) {
$authkey = (new RandomTool())->random_str(true, 40);
} else {
$authkey = $this->data['AuthKey']['authkey'];
}
$passwordHasher = $this->getHasher();
$this->data['AuthKey']['authkey'] = $passwordHasher->hash($authkey);
$this->data['AuthKey']['authkey_start'] = substr($authkey, 0, 4);
$this->data['AuthKey']['authkey_end'] = substr($authkey, -4);
$this->data['AuthKey']['authkey_raw'] = $authkey;
$this->authkey_raw = $authkey;
if (empty($this->data['AuthKey']['expiration'])) {
$this->data['AuthKey']['expiration'] = 0;
} else {
$this->data['AuthKey']['expiration'] = strtotime($this->data['AuthKey']['expiration']);
}
}
return true;
}
public function getAuthUserByAuthKey($authkey)
{
$start = substr($authkey, 0, 4);
$end = substr($authkey, -4);
$existing_authkeys = $this->find('all', [
'recursive' => -1,
'fields' => ['authkey', 'user_id'],
'conditions' => [
'OR' => [
'expiration >' => time(),
'expiration' => 0
],
'authkey_start' => $start,
'authkey_end' => $end,
]
]);
$passwordHasher = $this->getHasher();
foreach ($existing_authkeys as $existing_authkey) {
if ($passwordHasher->check($authkey, $existing_authkey['AuthKey']['authkey'])) {
return $this->User->getAuthUser($existing_authkey['AuthKey']['user_id']);
}
}
return false;
}
public function resetauthkey($id)
{
$existing_authkeys = $this->find('all', [
'recursive' => -1,
'conditions' => [
'user_id' => $id
]
]);
foreach ($existing_authkeys as $key) {
$key['AuthKey']['expiration'] = time();
$this->save($key);
}
return $this->createnewkey($id);
}
public function createnewkey($id)
{
$newKey = [
'authkey' => (new RandomTool())->random_str(true, 40),
'user_id' => $id
];
$this->create();
if ($this->save($newKey)) {
return $newKey['authkey'];
} else {
return false;
}
}
/**
* @return AbstractPasswordHasher
*/
private function getHasher()
{
return new BlowfishPasswordHasher();
}
}

View File

@ -2252,7 +2252,7 @@ class Event extends AppModel
return array();
}
$sharingGroupData = $this->__cacheSharingGroupData($user, $useCache);
$sharingGroupData = $options['sgReferenceOnly'] ? [] : $this->__cacheSharingGroupData($user, $useCache);
// Initialize classes that will be necessary during event fetching
if ((empty($options['metadata']) && empty($options['noSightings'])) && !isset($this->Sighting)) {

View File

@ -385,31 +385,31 @@ class Organisation extends AppModel
return (empty($org)) ? false : $org[$this->alias];
}
/**
* @param array $event
* @param array $fields
* @return array
*/
public function attachOrgsToEvent($event, $fields)
{
if (empty($this->__orgCache[$event['Event']['orgc_id']])) {
$temp = $this->find('first', array(
'conditions' => array('id' => $event['Event']['orgc_id']),
$toFetch = [];
if (!isset($this->__orgCache[$event['Event']['orgc_id']])) {
$toFetch[] = $event['Event']['orgc_id'];
}
if (!isset($this->__orgCache[$event['Event']['org_id']]) && $event['Event']['org_id'] != $event['Event']['orgc_id']) {
$toFetch[] = $event['Event']['org_id'];
}
if (!empty($toFetch)) {
$orgs = $this->find('all', array(
'conditions' => array('id' => $toFetch),
'recursive' => -1,
'fields' => $fields
));
if (!empty($temp)) {
$temp = $temp[$this->alias];
foreach ($orgs as $org) {
$this->__orgCache[$org[$this->alias]['id']] = $org[$this->alias];
}
$this->__orgCache[$event['Event']['orgc_id']] = $temp;
}
$event['Orgc'] = $this->__orgCache[$event['Event']['orgc_id']];
if (empty($this->__orgCache[$event['Event']['org_id']])) {
$temp = $this->find('first', array(
'conditions' => array('id' => $event['Event']['org_id']),
'recursive' => -1,
'fields' => $fields
));
if (!empty($temp)) {
$temp = $temp[$this->alias];
}
$this->__orgCache[$event['Event']['org_id']] = $temp;
}
$event['Org'] = $this->__orgCache[$event['Event']['org_id']];
return $event;
}

View File

@ -918,8 +918,34 @@ class Server extends AppModel
'test' => 'testBool',
'type' => 'boolean',
'null' => false,
),
'disable_user_login_change' => array(
'level' => self::SETTING_RECOMMENDED,
'description' => __('When enabled only Site admins can change user email. This should be enabled if you manage user logins by external system.'),
'value' => false,
'errorMessage' => '',
'test' => 'testBool',
'type' => 'boolean',
'null' => false,
),
'disable_user_password_change' => array(
'level' => self::SETTING_RECOMMENDED,
'description' => __('When enabled only Site admins can change user password. This should be enabled if you manage user passwords by external system.'),
'value' => false,
'errorMessage' => '',
'test' => 'testBool',
'type' => 'boolean',
'null' => false,
),
'disable_user_add' => array(
'level' => self::SETTING_RECOMMENDED,
'description' => __('When enabled, Org Admins could not add new users. This should be enabled if you manage users by external system.'),
'value' => false,
'errorMessage' => '',
'test' => 'testBool',
'type' => 'boolean',
'null' => false,
),
'block_event_alert' => array(
'level' => 1,
'description' => __('Enable this setting to start blocking alert e-mails for events with a certain tag. Define the tag in MISP.block_event_alert_tag.'),
@ -1309,6 +1335,14 @@ class Server extends AppModel
'editable' => false,
'redacted' => true
),
'advanced_authkeys' => array(
'level' => 0,
'description' => __('Advanced authkeys will allow each user to create and manage a set of authkeys for themselves, each with individual expirations and comments. API keys are stored in a hashed state and can no longer be recovered from MISP. Users will be prompted to note down their key when creating a new authkey. You can generate a new set of API keys for all users on demand in the diagnostics page, or by triggering %s.', sprintf('<a href="%s/servers/serverSettings/diagnostics#advanced_authkey_update">%s</a>', $this->baseurl, __('the advanced upgrade'))),
'value' => false,
'errorMessage' => '',
'test' => 'testBool',
'type' => 'boolean',
),
'rest_client_enable_arbitrary_urls' => array(
'level' => 0,
'description' => __('Enable this setting if you wish for users to be able to query any arbitrary URL via the rest client. Keep in mind that queries are executed by the MISP server, so internal IPs in your MISP\'s network may be reachable.'),
@ -1324,7 +1358,7 @@ class Server extends AppModel
'value' => false,
'errorMessage' => '',
'test' => null,
'type' => 'string',
'type' => 'string'
),
'syslog' => array(
'level' => 0,

View File

@ -208,7 +208,8 @@ class User extends AppModel
'counterQuery' => ''
),
'Post',
'UserSetting'
'UserSetting',
'AuthKey'
);
public $actsAs = array(
@ -1119,23 +1120,28 @@ class User extends AppModel
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 (empty(Configure::read('Security.advanced_authkeys'))) {
$oldKey = $this->data['User']['authkey'];
$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
);
} else {
$this->AuthKey = ClassRegistry::init('AuthKey');
$newkey = $this->AuthKey->resetauthkey($id);
}
if ($alert) {
$baseurl = Configure::read('MISP.external_baseurl');
if (empty($baseurl)) {

33
app/View/AuthKeys/add.ctp Normal file
View File

@ -0,0 +1,33 @@
<?php
echo $this->element('genericElements/Form/genericForm', [
'data' => [
'description' => __('Authkeys are used for API access. A user can have more than one authkey, so if you would like to use separate keys per tool that queries MISP, add additional keys. Use the comment field to make identifying your keys easier.'),
'fields' => [
[
'field' => 'user_id',
'label' => __('User'),
'options' => $dropdownData['user'],
'type' => 'dropdown'
],
[
'field' => 'comment',
'class' => 'span6'
],
[
'field' => 'expiration',
'label' => 'Expiration',
'class' => 'datepicker span6',
'placeholder' => "YYYY-MM-DD",
'type' => 'text'
]
],
'submit' => [
'action' => $this->request->params['action'],
'ajaxSubmit' => 'submitGenericFormInPlace();'
]
]
]);
if (!$ajax) {
echo $this->element('/genericElements/SideMenu/side_menu', $menuData);
}

View File

@ -0,0 +1,28 @@
<?php
if ($ajax) {
?>
<div id="genericModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="genericModalLabel" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="genericModalLabel"><?= __('Authkey created'); ?></h3>
</div>
<div class="modal-body modal-body-long">
<p><?= __('Please make sure that you note down the authkey below, this is the only time the authkey is shown in plain text, so make sure you save it. If you lose the key, simply remove the entry and generate a new one.'); ?></p>
<p><?=__('MISP will use the first and the last 4 digit for identification purposes.')?></p>
<p><?= sprintf('%s: <span class="bold">%s</span>', __('Authkey'), h($entity['AuthKey']['authkey_raw'])) ?></p>
</div>
<div class="modal-footer">
<a href="<?= $referer ?>" class="btn btn-primary"><?= __('I have noted down my key, take me back now') ?></a>
</div>
</div>
<?php
} else {
?>
<h4><?= __('Authkey created'); ?></h4>
<p><?= __('Please make sure that you note down the authkey below, this is the only time the authkey is shown in plain text, so make sure you save it. If you lose the key, simply remove the entry and generate a new one.'); ?></p>
<p><?=__('MISP will use the first and the last 4 digit for identification purposes.')?></p>
<p><?= sprintf('%s: <span class="bold">%s</span>', __('Authkey'), h($entity['AuthKey']['authkey_raw'])) ?></p>
<a href="<?= $referer ?>" class="btn btn-primary"><?= __('I have noted down my key, take me back now') ?></a>
<?php
}
?>

104
app/View/AuthKeys/index.ctp Normal file
View File

@ -0,0 +1,104 @@
<?php
echo sprintf('<div %s>', empty($ajax) ? 'class="index"' : '');
echo $this->element('genericElements/IndexTable/index_table', [
'data' => [
'data' => $data,
'top_bar' => [
'pull' => 'right',
'children' => [
[
'type' => 'simple',
'children' => [
'data' => [
'type' => 'simple',
'text' => __('Add authentication key'),
'class' => 'btn btn-primary',
'onClick' => 'openGenericModal',
'onClickParams' => [
sprintf(
'%s/auth_keys/add%s',
$baseurl,
empty($user_id) ? '' : ('/' . $user_id)
)
]
]
]
],
[
'type' => 'search',
'button' => __('Filter'),
'placeholder' => __('Enter value to search'),
'data' => '',
'searchKey' => 'value'
]
]
],
'fields' => [
[
'name' => '#',
'sort' => 'AuthKey.id',
'data_path' => 'AuthKey.id',
],
[
'name' => __('User'),
'sort' => 'User.email',
'data_path' => 'User.email',
],
[
'name' => __('Auth key'),
'sort' => 'AuthKey.authkey_start',
'element' => 'authkey',
'data_path' => 'AuthKey',
'privacy' => 1
],
[
'name' => __('Expiration'),
'sort' => 'AuthKey.expiration',
'data_path' => 'AuthKey.expiration',
'element' => 'expiration'
],
[
'name' => __('Disabled'),
'sort' => 'AuthKey.disabled',
'data_path' => 'AuthKey.disabled',
'element' => 'boolean'
],
[
'name' => __('Comment'),
'sort' => 'AuthKey.comment',
'data_path' => 'AuthKey.comment',
],
],
'title' => empty($ajax) ? __('Authentication key Index') : false,
'description' => empty($ajax) ? __('A list of API keys bound to a user.') : false,
'pull' => 'right',
'actions' => [
[
'onclick' => sprintf(
'openGenericModal(\'%s/authKeys/delete/[onclick_params_data_path]\');',
$baseurl
),
'onclick_params_data_path' => 'AuthKey.id',
'icon' => 'trash'
]
]
]
]);
echo '</div>';
if (empty($ajax)) {
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => $metaGroup, 'menuItem' => $this->action));
}
?>
<script type="text/javascript">
var passedArgsArray = <?php echo $passedArgs; ?>;
$(document).ready(function() {
$('#quickFilterButton').click(function() {
runIndexQuickFilter();
});
$('#quickFilterField').on('keypress', function (e) {
if(e.which === 13) {
runIndexQuickFilter();
}
});
});
</script>

View File

@ -0,0 +1,46 @@
<?php
echo $this->element(
'genericElements/SingleViews/single_view',
[
'title' => 'Authkey View',
'data' => $data,
'fields' => [
[
'key' => __('ID'),
'path' => 'AuthKey.id'
],
[
'key' => __('uuid'),
'path' => 'AuthKey.uuid'
],
[
'key' => __('Authkey'),
'path' => 'AuthKey',
'type' => 'authkey'
],
[
'key' => __('Created'),
'path' => 'AuthKey.created',
'type' => 'datetime'
],
[
'key' => __('Expiration'),
'path' => 'AuthKey.expiration',
'type' => 'date'
],
[
'key' => __('User'),
'path' => 'User.id',
'pathName' => 'User.email',
'model' => 'users',
'type' => 'model'
],
[
'key' => __('Comment'),
'path' => 'AuthKey.comment'
]
],
'children' => [
]
]
);

View File

@ -7,12 +7,12 @@
<th><?php echo $this->Paginator->sort('authkey');?></th>
<th><?php echo $this->Paginator->sort('autoalert');?></th>
<th><?php echo $this->Paginator->sort('contactalert');?></th>
<th><?php echo $this->Paginator->sort('gpgkey');?></th>
<th><?php echo $this->Paginator->sort('gpgkey', __('PGP key'));?></th>
<?php if (Configure::read('SMIME.enabled')): ?>
<th><?php echo $this->Paginator->sort('certif_public', 'SMIME');?></th>
<th><?php echo $this->Paginator->sort('certif_public', 'S/MIME');?></th>
<?php endif; ?>
<th><?php echo $this->Paginator->sort('nids_sid');?></th>
<th><?php echo $this->Paginator->sort('termsaccepted');?></th>
<th><?php echo $this->Paginator->sort('nids_sid', __('NIDS SID'));?></th>
<th><?php echo $this->Paginator->sort('termsaccepted', __('Terms accepted'));?></th>
<th><?php echo $this->Paginator->sort('current_login', __('Last login'));?></th>
<th><?php echo $this->Paginator->sort('date_created', __('Created'));?></th>
<?php
@ -40,7 +40,7 @@
<td ondblclick="document.location ='<?php echo $this->Html->url(array('admin' => true, 'action' => 'view', $user['User']['id']), true);?>';">
<?php echo h($user['User']['email']); ?>&nbsp;
</td>
<td ondblclick="document.location ='<?php echo $this->Html->url(array('admin' => true, 'action' => 'view', $user['User']['id']), true);?>';" class="<?php echo $user['Role']['perm_auth'] ? 'bold' : 'grey'; ?>">
<td class="bold<?= $user['Role']['perm_auth'] ? '' : ' grey'; ?>">
<span class="privacy-value quickSelect" data-hidden-value="<?= h($user['User']['authkey']) ?>">****************************************</span>&nbsp;<i class="privacy-toggle fas fa-eye useCursorPointer" title="<?= __('Reveal hidden value') ?>"></i>
</td>
<td class="short" ondblclick="document.location ='<?php echo $this->Html->url(array('admin' => true, 'action' => 'view', $user['User']['id']), true);?>';">
@ -64,10 +64,10 @@
<?php echo ($user['User']['termsaccepted'] == 1) ? __("Yes") : __("No"); ?>
</td>
<td class="short" ondblclick="document.location ='<?php echo $this->Html->url(array('admin' => true, 'action' => 'view', $user['User']['id']), true);?>';" title="<?php echo !$user['User']['current_login'] ? __('N/A') : h(date("Y-m-d H:i:s",$user['User']['current_login']));?>">
<?php echo !$user['User']['current_login'] ? __('N/A') : h(date("Y-m-d",$user['User']['current_login'])); ?>&nbsp;
<?php echo !$user['User']['current_login'] ? __('N/A') : h(date("Y-m-d", $user['User']['current_login'])); ?>&nbsp;
</td>
<td class="short" ondblclick="document.location ='<?php echo $this->Html->url(array('admin' => true, 'action' => 'view', $user['User']['id']), true);?>';" title="<?php echo !$user['User']['current_login'] ? 'N/A' : h(date("Y-m-d H:i:s",$user['User']['current_login']));?>">
<?php echo !$user['User']['date_created'] ? __('N/A') : h(date("Y-m-d",$user['User']['date_created'])); ?>&nbsp;
<?php echo !$user['User']['date_created'] ? __('N/A') : h(date("Y-m-d", $user['User']['date_created'])); ?>&nbsp;
</td>
<?php
if (Configure::read('Plugin.CustomAuth_enable') && !Configure::read('Plugin.CustomAuth_required')):

View File

@ -20,7 +20,7 @@
'default', 'type', 'options', 'placeholder', 'label', 'empty', 'rows', 'div', 'required'
);
$fieldsArrayForPersistence = array();
$formCreate = $this->Form->create($modelForForm);
$formCreate = $this->Form->create($modelForForm, ['class' => 'genericForm']);
if (!empty($data['fields'])) {
foreach ($data['fields'] as $fieldData) {
if (isset($fieldData['requirements']) && !$fieldData['requirements']) {
@ -48,13 +48,18 @@
}
$params['class'] = $class;
} else {
$params['class'] = '';
if (!empty($fieldData['type']) && $fieldData['type'] != 'checkbox') {
$params['class'] = 'span6';
}
}
foreach ($simpleFieldAllowedlist as $f) {
if (!empty($fieldData[$f])) {
$params[$f] = $fieldData[$f];
}
}
if (!empty($params['type']) && $params['type'] === 'dropdown') {
$params['type'] = 'select';
}
$temp = $this->Form->input($fieldData['field'], $params);
$fieldsArrayForPersistence []= $modelForForm . Inflector::camelize($fieldData['field']);
if (!empty($fieldData['hidden'])) {

View File

@ -1,10 +1,11 @@
<?php
if ($ajax) {
$submit = isset($ajaxSubmit) ? $ajaxSubmit : '$(\'.genericForm\').submit();';
echo sprintf(
'%s%s',
sprintf(
'<button id="submitButton" class="btn btn-primary" onClick="%s">%s</button>',
$ajaxSubmit,
$submit,
__('Submit')
),
sprintf(

View File

@ -0,0 +1,59 @@
<?php
/*
* Toggle element - a simple checkbox with the current state selected
* On click, issues a GET to a given endpoint, retrieving a form with the
* value flipped, which is immediately POSTed.
* to fetch it.
*
*/
$data = Hash::extract($row, $field['data_path']);
$seed = rand();
$checkboxId = 'GenericToggle-' . $seed;
$tempboxId = 'TempBox-' . $seed;
echo sprintf(
'<input type="checkbox" id="%s" %s><span id="%s" class="hidden">',
$checkboxId,
empty($data[0]) ? '' : 'checked',
$tempboxId
);
?>
<script type="text/javascript">
$(document).ready(function() {
var url = baseurl + "<?= h($field['url']) ?>";
<?php
if (!empty($field['url_params_data_paths'][0])) {
$id = Hash::extract($row, $field['url_params_data_paths'][0]);
echo 'url = url + "/' . h($id[0]) . '";';
}
?>
$('#<?= $checkboxId ?>').on('click', function() {
$.ajax({
type:"get",
url: url,
error:function() {
showMessage('fail', '<?= __('Could not retrieve current state.') ?>.');
},
success: function (data, textStatus) {
$('#<?= $tempboxId ?>').html(data);
// Make @mokaddem aka Graphman happy
var $form = $('#<?= $tempboxId ?>').find('form');
$.ajax({
data: $form.serialize(),
cache: false,
type:"post",
url: $form.attr('action'),
success:function(data, textStatus) {
showMessage('success', '<?= __('Field updated.') ?>.');
},
error:function() {
showMessage('fail', '<?= __('Could not update field.') ?>.');
},
complete:function() {
$('#<?= $tempboxId ?>').empty();
}
});
}
});
});
});
</script>

View File

@ -0,0 +1,9 @@
<?php
$authKey = Hash::extract($row, $field['data_path']);
echo sprintf(
'<span class="red bold">%s</span>%s<span class="red bold">%s</span>',
h($authKey['authkey_start']),
str_repeat('&bull;', 32),
h($authKey['authkey_end'])
);
?>

View File

@ -0,0 +1,34 @@
<?php
$data = Hash::extract($row, $field['data_path']);
if (is_array($data)) {
if (count($data) > 1) {
$data = implode(', ', $data);
} else {
if (count($data) > 0) {
$data = $data[0];
} else {
$data = '';
}
}
}
$data = h($data);
if (is_numeric($data)) {
if ($data == 0) {
$data = '<span class="text-primary font-weight-bold">' . __('Indefinite') . '</span>';
} else {
if ($data <= time()) {
$data = '<span class="text-danger font-weight-bold">' . __('Expired') . '</span>';
} else {
$data = '<span class="text-success font-weight-bold">' . date('Y-m-d H:i:s', $data) . '</span>';
}
}
}
if (!empty($field['onClick'])) {
$data = sprintf(
'<span onClick="%s">%s</span>',
$field['onClick'],
$data
);
}
echo $data;
?>

View File

@ -9,10 +9,12 @@
$data = Hash::extract($row, $field['data_path']);
$seed = rand();
$checkboxId = 'GenericToggle-' . $seed;
$checkboxClass = empty($field['checkbox_class']) ? 'genericCheckbox' : h($field['checkbox_class']);
$tempboxId = 'TempBox-' . $seed;
echo sprintf(
'<input type="checkbox" id="%s" %s><span id="%s" class="hidden">',
'<input type="checkbox" id="%s" class="%s" %s><span id="%s" class="hidden">',
$checkboxId,
$checkboxClass,
empty($data[0]) ? '' : 'checked',
$tempboxId
);
@ -27,6 +29,11 @@ $(document).ready(function() {
}
?>
$('#<?= $checkboxId ?>').on('click', function() {
<?php
if (!empty($field['beforeHook'])) {
echo $field['beforeHook'];
}
?>
$.ajax({
type:"get",
url: url,
@ -50,6 +57,11 @@ $(document).ready(function() {
},
complete:function() {
$('#<?= $tempboxId ?>').empty();
<?php
if (!empty($field['afterHook'])) {
echo $field['afterHook'];
}
?>
}
});
}

View File

@ -28,6 +28,9 @@
)
);
}
if (!empty($field['decorator'])) {
$valueField = $field['decorator']($valueField);
}
$rowHtml .= sprintf(
'<td%s%s%s%s%s%s%s>%s</td>',
(empty($field['id'])) ? '' : sprintf('id="%s"', $field['id']),

View File

@ -0,0 +1,21 @@
<?php
echo sprintf('<div %s>', empty($ajax) ? 'class="index"' : '');
echo $this->element('genericElements/IndexTable/index_table', $scaffold_data);
echo '</div>';
if (empty($ajax)) {
echo $this->element('/genericElements/SideMenu/side_menu', $menuData);
}
?>
<script type="text/javascript">
var passedArgsArray = <?php echo $passedArgs; ?>;
$(document).ready(function() {
$('#quickFilterButton').click(function() {
runIndexQuickFilter();
});
$('#quickFilterField').on('keypress', function (e) {
if(e.which === 13) {
runIndexQuickFilter();
}
});
});
</script>

View File

@ -1,10 +1,6 @@
<?php
$canAccess = function ($controller, $action) use ($me, $aclComponent) {
$response = $aclComponent->checkAccess($me, $controller, $action, true);
if ($response === 404) {
throw new Exception("Invalid controller '$controller' specified for menu requirements.");
}
return $response === true;
return $aclComponent->canUserAccess($me, $controller, $action);
};
$this->set('menuItem', $menuItem);
@ -549,23 +545,28 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider');
break;
case 'globalActions':
if (((Configure::read('MISP.disableUserSelfManagement') && $isAdmin) || !Configure::read('MISP.disableUserSelfManagement')) && ($menuItem === 'edit' || $menuItem === 'view' || $menuItem === 'change_pw')) {
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'url' => $baseurl . '/users/edit',
'text' => __('Edit My Profile')
));
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'url' => $baseurl . '/users/change_pw',
'text' => __('Change Password')
));
if ($menuItem === 'edit' || $menuItem === 'view' || $menuItem === 'change_pw') {
if ($canAccess('users', 'edit')) {
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'url' => $baseurl . '/users/edit',
'text' => __('Edit My Profile')
));
}
if ($canAccess('users', 'change_pw')) {
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'url' => $baseurl . '/users/change_pw',
'text' => __('Change Password')
));
} else if (Configure::read('Plugin.CustomAuth_custom_password_reset')) {
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'custom_pw_reset',
'url' => Configure::read('Plugin.CustomAuth_custom_password_reset'),
'text' => __('Change Password')
));
}
echo $divider;
} else if (Configure::read('Plugin.CustomAuth_custom_password_reset')) {
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'custom_pw_reset',
'url' => Configure::read('Plugin.CustomAuth_custom_password_reset'),
'text' => __('Reset Password')
));
}
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'view',
'url' => $baseurl . '/users/view/me',
@ -771,19 +772,21 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider');
'text' => __('Restore Deleted Events')
));
}
if ($menuItem === 'editUser' || $menuItem === 'viewUser') {
if ($menuItem === 'editUser' || $menuItem === 'viewUser' || $menuItem === 'authKeyIndex') {
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'viewUser',
'url' => $baseurl . '/admin/users/view/' . h($id),
'text' => __('View User')
));
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'onClick' => array(
'function' => 'initiatePasswordReset',
'params' => array($id)
),
'text' => __('Reset Password')
));
if ($canAccess('users', 'initiatePasswordReset')) {
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'onClick' => array(
'function' => 'initiatePasswordReset',
'params' => array($id)
),
'text' => __('Reset Password')
));
}
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
'element_id' => 'editUser',
'url' => $baseurl . '/admin/users/edit/' . h($id),

View File

@ -0,0 +1,59 @@
<?php
$alignments = '';
$extracted = $data;
if (!empty($field['path'])) {
if (strpos('.', $field['path']) !== false) {
$extracted = Cake\Utility\Hash::extract($data, empty($field['path']) ? 'individual' : $field['path']);
} else {
$extracted = $data[$field['path']];
}
}
if ($field['scope'] === 'individuals') {
foreach ($extracted['alignments'] as $alignment) {
$alignments .= sprintf(
'<div><span class="font-weight-bold">%s</span> @ %s <a href="#" class="fas fa-trash" onClick="%s"></a></div>',
h($alignment['type']),
sprintf(
'<a href="/organisations/view/%s">%s</a>',
h($alignment['organisation']['id']),
h($alignment['organisation']['name'])
),
sprintf(
"populateAndLoadModal(%s);",
sprintf(
"'/alignments/delete/%s'",
$alignment['id']
)
)
);
}
} else if ($field['scope'] === 'organisations') {
foreach ($extracted['alignments'] as $alignment) {
$alignments .= sprintf(
'<div>[<span class="font-weight-bold">%s</span>] %s <a href="#" class="fas fa-trash" onClick="%s"></a></div>',
h($alignment['type']),
sprintf(
'<a href="/individuals/view/%s">%s</a>',
h($alignment['individual']['id']),
h($alignment['individual']['email'])
),
sprintf(
"populateAndLoadModal(%s);",
sprintf(
"'/alignments/delete/%s'",
$alignment['id']
)
)
);
}
}
echo sprintf(
'<div class="alignments-list">%s</div><div class="alignments-add-container"><button class="alignments-add-button btn btn-secondary btn-sm" onclick="%s">%s</button></div>',
$alignments,
sprintf(
"populateAndLoadModal('/alignments/add/%s/%s');",
h($field['scope']),
h($extracted['id'])
),
$field['scope'] === 'individuals' ? __('Add organisation') : __('Add individual')
);

View File

@ -0,0 +1,8 @@
<?php
$authKey = Hash::extract($data, $field['path']);
echo sprintf(
'<span class="red bold">%s</span>%s<span class="red bold">%s</span>',
h($authKey['authkey_start']),
str_repeat('&bull;', 32),
h($authKey['authkey_end'])
);

View File

@ -0,0 +1,7 @@
<?php
$value = Hash::extract($data, $field['path'])[0];
$mapping = !empty($field['mapping']) ? $field['mapping'] : [
false => '<i class="fas fa-times"></i>',
true => '<i class="fas fa-check"></i>'
];
echo $mapping[(bool)$value];

View File

@ -0,0 +1,7 @@
<?php
$value = Hash::extract($data, $field['path'])[0];
if (empty($value)) {
echo 'N/A';
} else {
echo date('Y-m-d', $value);
}

View File

@ -0,0 +1,3 @@
<?php
$value = Hash::extract($data, $field['path'])[0];
echo date('Y-m-d H:i:s', $value);

View File

@ -0,0 +1,23 @@
<?php
if (!empty($field['raw'])) {
$string = $field['raw'];
} else {
$value = Hash::extract($data, $field['path']);
$string = empty($value[0]) ? '' : $value[0];
}
if (!empty($field['url'])) {
if (!empty($field['url_vars'])) {
if (!is_array($field['url_vars'])) {
$field['url_vars'] = [$field['url_vars']];
}
foreach ($field['url_vars'] as $k => $path) {
$field['url'] = str_replace('{{' . $k . '}}', $this->Hash->extract($data, $path)[0], $field['url']);
}
}
$string = sprintf(
'<a href="%s">%s</a>',
h($field['url']),
$string
);
}
echo $string;

View File

@ -0,0 +1,3 @@
<?php
$value = Hash::extract($data, $field['path'])[0];
echo $field['mapping'][$value];

View File

@ -0,0 +1,9 @@
<?php
$id = Hash::extract($data, $field['path'])[0];
$pathName = Hash::extract($data, $field['pathName'])[0];
echo sprintf(
'<a href="%s/view/%s">%s</a>',
$field['model'],
h($id),
h($pathName)
);

View File

@ -0,0 +1,63 @@
<?php
/*
* create single view child index
*
*/
$randomId = bin2hex(openssl_random_pseudo_bytes(8));
if (!empty($child['url_params'])) {
if (!is_array($child['url_params'])) {
$child['url_params'] = [$child['url_params']];
}
foreach ($child['url_params'] as $i => $url_param) {
$child['url'] = str_replace('{{' . $i . '}}', $this->Hash->extract($data, $url_param)[0], $child['url']);
}
}
echo sprintf(
'<div class="card">%s%s</div>',
sprintf(
'<div class="card-header" id="heading-%s"><h5 class="mb0">%s</h5></div>',
$randomId,
sprintf(
'<button class="btn btn-link" data-toggle="collapse" data-target="#view-child-%s" aria-expanded="true" aria-controls="collapseOne">%s</button>',
$randomId,
h($child['title'])
)
),
sprintf(
'<div class="collapse %s" id="view-child-%s" data-parent="#accordion" labelledby="heading-%s"><div id="view-child-body-%s" class="card-body" data-content-url="%s" data-load-on="%s"></div></div>',
!empty($child['collapsed']) ? 'show' : 'collapsed',
$randomId,
$randomId,
$randomId,
h($child['url']),
empty($child['loadOn']) ? 'ready' : h($child['loadOn'])
)
);
?>
<script type="text/javascript">
$(document).ready(function() {
var url = $('#view-child-body-<?= h($randomId) ?>').data('content-url');
var loadon = $('#view-child-body-<?= h($randomId) ?>').data('load-on');
if (loadon === 'ready') {
$.ajax({
success:function (data, textStatus) {
$('#view-child-body-<?= h($randomId) ?>').html(data);
},
type: "get",
cache: false,
url: url,
});
} else {
$('#view-child-<?= h($randomId) ?>').on('hidden.bs.collapse', function () {
$.ajax({
success:function (data, textStatus) {
$('#view-child-body-<?= h($randomId) ?>').html(data);
},
type: "get",
cache: false,
url: url,
});
})
}
});
</script>

View File

@ -0,0 +1,91 @@
<?php
/*
* echo $this->element('/genericElements/SingleViews/single_view', [
* 'title' => '' //page title,
* 'description' => '' //description,
* 'description_html' => '' //html description, unsanitised,
* 'data' => $data, // the raw data passed for display
* 'fields' => [
* elements passed as to be displayed in the <ul> element.
* format:
* [
'key' => '' // key to be displayed
* 'path' => '' // path for the value to be parsed
* 'type' => '' // generic assumed if not filled, uses SingleViews/Fields/* elements
* ]
* ],
* 'children' => [
* // Additional elements attached to the currently viewed object. index views will be appended via ajax calls below.
[
* 'title' => '',
* 'url' => '', //cakephp compatible url, can be actual url or array for the constructor
* 'collapsed' => 0|1 // defaults to 0, whether to display it by default or not
* 'loadOn' => 'ready|expand' // load the data directly or only when expanded from a collapsed state
*
* ],
* ]
* ]);
*
*/
$listElements = '';
if (!empty($fields)) {
foreach ($fields as $field) {
if (empty($field['type'])) {
$field['type'] = 'generic';
}
$listElements .= sprintf(
'<tr><td class="meta_table_key">%s</td><td class="meta_table_value">%s</td></tr>',
h($field['key']),
$this->element(
'/genericElements/SingleViews/Fields/' . $field['type'] . 'Field',
['data' => $data, 'field' => $field]
)
);
}
}
if (!empty($data['metaFields'])) {
foreach ($data['metaFields'] as $metaField => $value) {
$listElements .= sprintf(
'<tr><td class="meta_table_key">%s</td><td class="meta_table_value">%s</td></tr>',
h($metaField),
$this->element(
'/genericElements/SingleViews/Fields/genericField',
[
'data' => $value,
'field' => [
'raw' => $value
]
]
)
);
}
}
$ajaxLists = '';
if (!empty($children)) {
foreach ($children as $child) {
$ajaxLists .= $this->element(
'/genericElements/SingleViews/child',
array(
'child' => $child,
'data' => $data
)
);
}
}
$title = empty($title) ?
__('%s view', Inflector::singularize(Inflector::humanize($this->request->params['controller']))) :
$title;
echo sprintf(
'<div class="view"><div class="row-fluid"><div class="span8">%s</div><div class="span4">%s</div></div><div id="accordion">%s</div></div></div>%s',
sprintf(
'<div><h2 class="ellipsis-overflow">%s</h2>%s%s<table class="meta_table table table-striped table-condensed">%s</table>',
h($title),
empty($description) ? '' : sprintf('<p>%s</p>', h($description)),
empty($description_html) ? '' : sprintf('<p>%s</p>', $description_html),
$listElements
),
'',
$ajaxLists,
$ajax ? '' : $this->element('/genericElements/SideMenu/side_menu', $menuData)
);
?>

View File

@ -0,0 +1,51 @@
<?php
if (empty($elementId)) {
$elementId = 'accordion-' . bin2hex(openssl_random_pseudo_bytes(8));
}
$elements = [];
$url = $baseurl . $url;
echo sprintf(
'<div class="accordion" id="%s"><div class="accordion-group">%s%s</div></div>',
h($elementId),
sprintf(
'<div class="accordion-heading">
<span class="accordion-toggle blue bold" data-toggle="collapse" data-parent="#%s" href="#%s" >%s %s</span>
</div>',
h($elementId),
h($elementId) . '-collapse',
h($title),
!empty($allowFullscreen) ? '' : sprintf(
'<span class="fas fa-external-link-alt" title="View %s full screen" onClick="window.location.href=\'%s\';"></span>',
h($title),
h($url)
)
),
sprintf(
'<div id="%s" class="accordion-body collapse"><div id="%s" class="accordion-inner" data-url="%s">&nbsp;</div></div>',
h($elementId) . '-collapse',
h($elementId) . '-collapse-inner',
h($url)
)
);
?>
<script type="text/javascript">
$(document).ready(function() {
var elementId = '#<?= h($elementId) ?>';
$(elementId).on('shown', function() {
$.ajax({
type:"get",
url: $(elementId + '-collapse-inner').data('url'),
beforeSend: function (XMLHttpRequest) {
$(".loading").show();
},
success:function (data) {
$(elementId + '-collapse-inner').html(data);
$(".loading").hide();
},
error:function() {
showMessage('fail', 'Something went wrong - could not fetch content.');
}
});
});
});
</script>

View File

@ -3,11 +3,7 @@
// New approach how to define menu requirements. It takes ACLs from ACLComponent.
// TODO: Use for every menu item
$canAccess = function ($controller, $action) use ($me, $aclComponent) {
$response = $aclComponent->checkAccess($me, $controller, $action, true);
if ($response === 404) {
throw new Exception("Invalid controller '$controller' specified for menu requirements.");
}
return $response === true;
return $aclComponent->canUserAccess($me, $controller, $action);
};
$menu = array(
@ -334,7 +330,8 @@
),
array(
'text' => __('Add User'),
'url' => $baseurl . '/admin/users/add'
'url' => $baseurl . '/admin/users/add',
'requirement' => $canAccess('users', 'admin_add'),
),
array(
'text' => __('Contact Users'),

View File

@ -428,6 +428,13 @@
<?php
endif;
?>
<h3><?php echo __('Upgrade authkeys keys to the advanced keys format'); ?><a id="advanced_authkey_update">&nbsp</a></h3>
<p>
<?php
echo __('MISP can store the user API keys either in the clear directly attached to the users, or as of recently, it can generate a list of hashed keys for different purposes. If the latter feature is enabled, it might be useful to move all existing keys over to the new format so that users do not lose access to the system. In order to do so, run the following functionality.');
?>
<?php echo $this->Form->postLink('<span class="btn btn-inverse" style="padding-top:1px;padding-bottom:1px;">' . __('Update Authkeys to advanced Authkeys') . '</span>', $baseurl . '/users/updateToAdvancedAuthKeys', array('escape' => false));?>
</p>
<h3><?php echo __('Clean model cache');?></h3>
<p><?php echo __('If you ever run into issues with missing database fields / tables, please run the following script to clean the model cache.');?></p>
<?php echo $this->Form->postLink('<span class="btn btn-inverse" style="padding-top:1px;padding-bottom:1px;">' . __('Clean cache') . '</span>', $baseurl . '/events/cleanModelCaches', array('escape' => false));?>

View File

@ -63,7 +63,7 @@
'html' => sprintf(
'%s%s',
!empty($setting['cli_only']) ? sprintf('<span class="bold">[<span class="red">%s</span>]</span> ', __('CLI only')) : '',
h($setting['description'])
$setting['description']
),
'class' => 'live_filter_target'
),

View File

@ -1,3 +1,6 @@
<?php
$api_key = empty(Configure::read('Security.advanced_authkeys')) ? $me['authkey'] : 'YOUR_API_KEY';
?>
<div class="event index">
<h2><?php echo __('Automation');?></h2>
<p><?php echo __('Automation functionality is designed to automatically feed other tools and systems with the data in your MISP repository.
@ -8,15 +11,26 @@
</p>
<span>
<?php
echo __(
'Your current key is: <code>%s</code>. You can %s this key.',
$me['authkey'],
$this->Form->postLink(
__('reset'),
array('controller' => 'users', 'action' => 'resetauthkey', 'me'),
array('div' => false)
)
);
if (empty(Configure::read('Security.advanced_authkeys'))) {
echo __(
'Your current key is: <code>%s</code>. You can %s this key.',
$me['authkey'],
$this->Form->postLink(
__('reset'),
array('controller' => 'users', 'action' => 'resetauthkey', 'me'),
array('div' => false)
)
);
} else {
echo __(
'You can view and manage your API keys under your profile, found %s',
sprintf(
'<a href="%s/users/view/me">%s</a>',
$baseurl,
__('here')
)
);
}
?>
</span>
<?php
@ -73,7 +87,7 @@
$headers = array(
'Accept: application/json',
'Content-type: application/json',
'Authorization: ' . $me['authkey']
'Authorization: ' . $api_key
);
$headers = implode("\n", $headers);
$body = json_encode(
@ -130,7 +144,7 @@
<p>JSON:</p>
<pre><?php
echo 'Headers' . PHP_EOL;
echo 'Authorization: ' . h($me['authkey']) . PHP_EOL;
echo 'Authorization: ' . h($api_key) . PHP_EOL;
echo 'Accept: application/json' . PHP_EOL;
echo 'Content-type: application/json';
?></pre>
@ -138,7 +152,7 @@
<p>XML:</p>
<pre><?php
echo 'Headers' . PHP_EOL;
echo 'Authorization: ' . h($me['authkey']) . PHP_EOL;
echo 'Authorization: ' . h($api_key) . PHP_EOL;
echo 'Accept: application/json' . PHP_EOL;
echo 'Content-type: application/json';
?></pre>
@ -303,7 +317,7 @@
<b>URL</b>: <?php echo $baseurl.'/events/index'; ?><br />
<b>Headers</b>:<br />
<pre><?php
echo 'Authorization: ' . $me['authkey'] . PHP_EOL;
echo 'Authorization: ' . $api_key . PHP_EOL;
echo 'Accept: application/json' . PHP_EOL;
echo 'Content-type: application/json';
?></pre>

View File

@ -2,61 +2,82 @@
App::uses('AppHelper', 'View/Helper');
// Helper to retrieve org images with the given parameters
class OrgImgHelper extends AppHelper {
const IMG_PATH = APP . WEBROOT_DIR . DS . 'img' . DS . 'orgs' . DS;
class OrgImgHelper extends AppHelper
{
const IMG_PATH = APP . WEBROOT_DIR . DS . 'img' . DS . 'orgs' . DS;
public function getOrgImg($options, $returnData = false, $raw = false) {
$imgOptions = array();
$possibleFields = array('id', 'name');
$size = !empty($options['size']) ? $options['size'] : 48;
foreach ($possibleFields as $field) {
if (isset($options[$field]) && file_exists(self::IMG_PATH . $options[$field] . '.png')) {
$imgOptions[$field] = $options[$field] . '.png';
break;
}
}
$baseurl = $this->_View->viewVars['baseurl'];
if (!empty($imgOptions)) {
foreach ($imgOptions as $field => $imgOption) {
$result = sprintf(
'<img src="%s/img/orgs/%s" title="%s" width="%s" height="%s">',
$baseurl,
$imgOption,
isset($options['name']) ? h($options['name']) : h($options['id']),
(int)$size,
(int)$size
);
public function getNameWithImg(array $organisation)
{
if (!isset($organisation['Organisation'])) {
return '';
}
if (!$raw) {
$result = sprintf(
'<a href="%s/organisations/view/%s">%s</a>',
$baseurl,
empty($options['id']) ? h($options['name']) : h($options['id']),
$result
);
}
break;
}
} else {
if ($raw) {
$result = sprintf(
'<span class="welcome">%s</span>',
h($options['name'])
);
} else {
$result = sprintf(
'<a href="%s/organisations/view/%s"><span class="welcome">%s</span></a>',
$baseurl,
empty($options['id']) ? h($options['name']) : h($options['id']),
h($options['name'])
);
}
}
if ($returnData) {
return $result;
} else {
echo $result;
$orgImgName = null;
foreach (['id', 'name'] as $field) {
if (isset($organisation['Organisation'][$field]) && file_exists(self::IMG_PATH . $organisation['Organisation'][$field] . '.png')) {
$orgImgName = $organisation['Organisation'][$field] . '.png';
break;
}
}
$baseurl = $this->_View->viewVars['baseurl'];
$link = $baseurl . '/organisations/view/' . (empty($organisation['Organisation']['id']) ? h($organisation['Organisation']['name']) : h($organisation['Organisation']['id']));
if ($orgImgName) {
$orgImgUrl = $baseurl . '/img/orgs/' . $orgImgName;
return sprintf('<a href="%s" style="background-image: url(\'%s\')" class="orgImg">%s</a>', $link, $orgImgUrl, h($organisation['Organisation']['name']));
} else {
return sprintf('<a href="%s">%s</a>', $link, h($organisation['Organisation']['name']));
}
}
public function getOrgImg($options, $returnData = false, $raw = false)
{
$orgImgName = null;
foreach (['id', 'name'] as $field) {
if (isset($options[$field]) && file_exists(self::IMG_PATH . $options[$field] . '.png')) {
$orgImgName = $options[$field] . '.png';
break;
}
}
$baseurl = $this->_View->viewVars['baseurl'];
if ($orgImgName) {
$size = !empty($options['size']) ? $options['size'] : 48;
$result = sprintf(
'<img src="%s/img/orgs/%s" title="%s" width="%s" height="%s">',
$baseurl,
$orgImgName,
isset($options['name']) ? h($options['name']) : h($options['id']),
(int)$size,
(int)$size
);
if (!$raw) {
$result = sprintf(
'<a href="%s/organisations/view/%s">%s</a>',
$baseurl,
empty($options['id']) ? h($options['name']) : h($options['id']),
$result
);
}
} else {
if ($raw) {
$result = sprintf(
'<span class="welcome">%s</span>',
h($options['name'])
);
} else {
$result = sprintf(
'<a href="%s/organisations/view/%s"><span class="welcome">%s</span></a>',
$baseurl,
empty($options['id']) ? h($options['name']) : h($options['id']),
h($options['name'])
);
}
}
if ($returnData) {
return $result;
} else {
echo $result;
}
}
}

View File

@ -1,71 +1,84 @@
<div class="roles form">
<?php echo $this->Form->create('Role'); ?>
<fieldset>
<legend><?php echo __('Add Role');?></legend>
<?php
echo $this->Form->input('restricted_to_site_admin', array(
'type' => 'checkbox',
'class' => 'checkbox readonlyenabled',
'label' => __('Restrict to site admins')
));
?>
<div class="input clear"></div>
<?php
echo $this->Form->input('name');
echo $this->Form->input('permission', array('type' => 'select', 'label' => __('Permissions'), 'options' => $options), array('value' => '3'));
?>
<div class="input clear"></div>
<?php
echo $this->Form->input('memory_limit', array('label' => __('Memory limit') . ' (' . h($default_memory_limit) . ')'));
echo $this->Form->input('max_execution_time', array('label' => __('Maximum execution time') . ' (' . h($default_max_execution_time) . ')'));
?>
<div class="input clear"></div>
<?php
echo $this->Form->input('enforce_rate_limit', array(
'type' => 'checkbox',
'label' => __('Enforce search rate limit')
));
?>
<div class="input clear"></div>
<div id="rateLimitCountContainer">
<?php
echo $this->Form->input('rate_limit_count', array('label' => __('# of searches / 15 min')));
?>
</div>
<div class="input clear"></div>
<?php
$counter = 1;
foreach ($permFlags as $k => $flag):
?>
<div class="permFlags<?php echo ' ' . ($flag['readonlyenabled'] ? 'readonlyenabled' : 'readonlydisabled'); ?>">
<?php
echo $this->Form->input($k, array(
'type' => 'checkbox',
'class' => sprintf(
'checkbox %s %s',
($flag['readonlyenabled'] ? 'readonlyenabled' : 'readonlydisabled'),
empty($flag['site_admin_optional']) ? 'site_admin_enforced' : 'site_admin_optional'
),
'checked' => false,
'label' => $flag['text'],
));
if ($counter%3 == 0) echo "<div class='input clear'></div>";
$counter++;
?>
</div>
<?php
endforeach;
?>
</fieldset>
<?php
echo $this->Form->button(__('Add'), array('class' => 'btn btn-primary'));
echo $this->Form->end();
?>
</div>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'admin', 'menuItem' => 'addRole'));
?>
$fields = [
[
'field' => 'restricted_to_site_admin',
'label' => __('Restrict to site admins'),
'type' => 'checkbox',
'class' => 'readonlyenabled'
],
[
'field' => 'name',
'stayInLine' => 1
],
[
'field' => 'permission',
'label' => __('Permissions'),
'type' => 'select',
'options' => $dropdownData['options'],
'value' => '3',
'class' => 'span3'
],
[
'field' => 'memory_limit',
'label' => __('Memory limit (%s)', $default_memory_limit),
'stayInLine' => 1
],
[
'field' => 'max_execution_time',
'label' => __('Maximum execution time (%ss)', $default_max_execution_time)
],
[
'field' => 'enforce_rate_limit',
'label' => __('Enforce search rate limit'),
'type' => 'checkbox',
],
[
'field' => 'rate_limit_count',
'label' => __('# of searches / 15 min'),
'div' => [
'id' => 'rateLimitCountContainer'
]
]
];
$counter = 0;
foreach ($permFlags as $k => $flag) {
$counter += 1;
$fields[] = [
'field' => $k,
'label' => h($flag['text']),
'checked' => false,
'type' => 'checkbox',
'div' => [
'class' => sprintf(
'permFlags %s checkbox',
($flag['readonlyenabled'] ? 'readonlyenabled' : 'readonlydisabled')
)
],
'class' => sprintf(
'checkbox %s %s',
($flag['readonlyenabled'] ? 'readonlyenabled' : 'readonlydisabled'),
empty($flag['site_admin_optional']) ? 'site_admin_enforced' : 'site_admin_optional'
),
'stayInLine' => ($counter%3 != 0)
];
}
echo $this->element('genericElements/Form/genericForm', [
'data' => [
'description' => false,
'title' => __('Add Role'),
'fields' => $fields,
'submit' => [
'action' => $this->request->params['action'],
'ajaxSubmit' => 'submitGenericFormInPlace();'
]
]
]);
if (!$ajax) {
echo $this->element('/genericElements/SideMenu/side_menu', $menuData);
}
?>
<script type="text/javascript">
$(function() {
checkRolePerms();
@ -78,4 +91,3 @@ echo $this->Form->end();
});
});
</script>
<?php echo $this->Js->writeBuffer();

View File

@ -1,113 +1,118 @@
<div class="roles index">
<h2><?php echo __('Roles');?></h2>
<div class="pagination">
<ul>
<?php
$this->Paginator->options(array(
'update' => '.span12',
'evalScripts' => true,
'before' => '$(".progress").show()',
'complete' => '$(".progress").hide()',
));
echo $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
?>
</ul>
</div>
<table class="table table-striped table-hover table-condensed">
<tr>
<th><?php echo $this->Paginator->sort('id');?></th>
<th><?php echo __('Default');?></th>
<th><?php echo $this->Paginator->sort('name');?></th>
<th><?php echo $this->Paginator->sort('restricted_to_site_admin', __('Restricted to site admins'));?></th>
<th><?php echo $this->Paginator->sort('permission', __('Permissions'));?></th>
<?php
foreach ($permFlags as $k => $flags):
?>
<th title="<?php echo h($flags['title']); ?>"><?php echo $this->Paginator->sort($k, $flags['text']);?></th>
<?php
endforeach;
?>
<th><?php echo $this->Paginator->sort('memory_limit', __('Memory limit'));?></th>
<th><?php echo $this->Paginator->sort('max_execution_time', __('Max execution time'));?></th>
<th><?php echo $this->Paginator->sort('rate_limit_count', __('Searches / 15 mins'));?></th>
<th class="actions"><?php echo __('Actions');?></th>
</tr><?php
foreach ($list as $item): ?>
<tr>
<td><?php echo $this->Html->link(h($item['Role']['id']), array('admin' => true, 'action' => 'edit', $item['Role']['id'])); ?>&nbsp;</td>
<td class="short" style="text-align:center;width:20px;"><input class="servers_default_role_checkbox" type="checkbox" aria-label="<?php echo __('Default role'); ?>" data-id="<?php echo h($item['Role']['id']); ?>" <?php if ($default_role_id && $default_role_id == $item['Role']['id']) echo 'checked'; ?>></td>
<td><?php echo h($item['Role']['name']); ?>&nbsp;</td>
<td class="short"><span class="<?php if ($item['Role']['restricted_to_site_admin']) echo 'icon-ok'; ?>" role="img" aria-label="<?php echo $item['Role']['restricted_to_site_admin'] ? __('Yes') : __('No'); ?>"></span>&nbsp;</td>
<td><?php echo h($options[$item['Role']['permission']]); ?>&nbsp;</td>
<?php
foreach ($permFlags as $k => $flags) {
$flagName = Inflector::Humanize(substr($k, 5));
echo sprintf(
'<td class="short"><span class="%s" role="img" aria-label="%s" title="%s"></span>&nbsp;</td>',
($item['Role'][$k]) ? 'icon-ok' : '',
($item['Role'][$k]) ? __('Yes') : __('No'),
sprintf(
__('%s permission %s'),
h($flagName),
$item['Role'][$k] ? 'granted' : 'denied'
)
);
}
?>
<td class="short">
<?php
if (empty($item['Role']['memory_limit'])) {
echo h($default_memory_limit);
} else {
echo h($item['Role']['memory_limit']);
}
?>
</td>
<td class="short">
<?php
if (empty($item['Role']['max_execution_time'])) {
echo h($default_max_execution_time);
} else {
echo h($item['Role']['max_execution_time']);
}
?>
</td>
<td class="short">
<?php
if (empty($item['Role']['rate_limit_count']) || empty($item['Role']['enforce_rate_limit'])) {
echo 'N/A';
} else {
echo h(intval($item['Role']['rate_limit_count']));
}
?>
</td>
<td class="short action-links">
<?php echo $this->Html->link('', array('admin' => true, 'action' => 'edit', $item['Role']['id']), array('class' => 'fa fa-edit', 'title' => __('Edit'), 'aria-label' => __('Edit'))); ?>
<?php echo $this->Form->postLink('', array('admin' => true, 'action' => 'delete', $item['Role']['id']), array('class' => 'fa fa-trash', 'title' => __('Delete'), 'aria-label' => __('Delete')), __('Are you sure you want to delete %s?', $item['Role']['name'])); ?>
</td>
</tr><?php
endforeach; ?>
</table>
<p>
<?php
echo $this->Paginator->counter(array(
'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}')
));
?>
</p>
<div class="pagination">
<ul>
<?php
echo $this->Paginator->prev('&laquo; ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
echo $this->Paginator->next(__('next') . ' &raquo;', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
?>
</ul>
</div>
</div>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'admin', 'menuItem' => 'indexRole'));
$fields = [
[
'name' => __('Id'),
'sort' => 'Role.id',
'data_path' => 'Role.id'
],
[
'name' => __('Default'),
'data_path' => 'Role.default',
'element' => 'toggle',
'url' => '/admin/roles/set_default',
'url_params_data_paths' => ['Role.id'],
'checkbox_class' => 'defaultRoleCheckbox',
'beforeHook' => "$('.defaultRoleCheckbox').prop('checked', false); $(this).prop('checked', true);"
],
[
'name' => __('Name'),
'sort' => 'Role.name',
'data_path' => 'Role.name'
]
];
foreach ($permFlags as $k => $permFlag) {
$fields[] = [
'name' => Inflector::Humanize(substr($k, 5)),
'sort' => 'Role.' . $k,
'data_path' => 'Role.' . $k,
'element' => 'boolean'
];
}
$fields[] = [
'name' => __('Memory Limit'),
'sort' => 'Role.memory_limit',
'data_path' => 'Role.memory_limit',
'decorator' => function($value) use ($default_memory_limit) {
return empty($value) ? $default_memory_limit : h($value);
}
];
$fields[] = [
'name' => __('Max execution time'),
'sort' => 'Role.max_execution_time',
'data_path' => 'Role.max_execution_time',
'decorator' => function($value) use ($default_max_execution_time) {
return (empty($value) ? $default_max_execution_time : h($value)) . 's';
}
];
$fields[] = [
'name' => __('Searches / 15 mins'),
'sort' => 'Role.rate_limit_count',
'data_path' => 'Role.rate_limit_count',
'decorator' => function($value)
{
return (empty($value) ? __('N/A') : h($value));
}
];
echo $this->element('genericElements/IndexTable/scaffold', [
'scaffold_data' => [
'data' => [
'data' => $data,
'top_bar' => [
'pull' => 'right',
'children' => [
[
'type' => 'simple',
'children' => [
'data' => [
'type' => 'simple',
'text' => __('Add role'),
'class' => 'btn btn-primary',
'onClick' => 'openGenericModal',
'onClickParams' => [
sprintf(
'%s/admin/roles/add',
$baseurl
)
]
]
]
],
[
'type' => 'search',
'button' => __('Filter'),
'placeholder' => __('Enter value to search'),
'data' => '',
'searchKey' => 'quickFilter'
]
]
],
'fields' => $fields,
'title' => empty($ajax) ? __('Roles') : false,
'description' => empty($ajax) ? __('Instance specific permission roles.') : false,
'actions' => [
[
'url' => $baseurl . '/admin/roles/edit/',
'url_params_data_paths' => array(
'Role.id'
),
'icon' => 'edit'
],
[
'onclick' => sprintf(
'openGenericModal(\'%s/admin/roles/delete/[onclick_params_data_path]\');',
$baseurl
),
'onclick_params_data_path' => 'Role.id',
'icon' => 'trash'
]
]
]
]
]);
?>

View File

@ -0,0 +1,4 @@
<?php
echo $this->Form->create('Role');
echo $this->Form->end();
?>

View File

@ -1,29 +1,40 @@
<?php
$table_data = array();
$table_data[] = array('key' => __('Id'), 'value' => $role['Role']['id']);
$table_data[] = array('key' => __('Name'), 'value' => $role['Role']['name']);
$table_data[] = array('key' => __('Permission level'), 'value' => $premissionLevelName[$role['Role']['permission']]);
foreach ($role['Role'] as $k => $item) {
if (substr($k, 0, 5) === 'perm_' && !in_array($k, array('perm_add', 'perm_modify', 'perm_modify_org', 'perm_publish', 'perm_full'))) {
$name = substr($k, 5);
if (in_array($name, array('add', 'modify', 'modify_org', 'publish', 'full'))) {
continue;
}
$table_data[] = array(
'key' => Inflector::humanize(h($name)),
'value_class' => $role['Role'][$k] ? 'green' : 'red',
'value' => $role['Role'][$k] ? 'Granted' : 'Denied'
);
}
}
echo sprintf(
'<div class="roles view row-fluid"><div class="span8" style="margin:0px;">%s</div></div>%s',
sprintf(
'<h2>%s</h2>%s',
__('Role'),
$this->element('genericElements/viewMetaTable', array('table_data' => $table_data))
),
$this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'globalActions', 'menuItem' => 'roles'))
);
?>
$fields = [
[
'key' => __('Id'),
'path' => 'Role.id'
],
[
'key' => __('Name'),
'path' => 'Role.name'
],
[
'key' => __('Permission level'),
'path' => 'Role.permission',
'type' => 'mapping',
'mapping' => $permissionLevelName
]
];
foreach ($permFlags as $permFlag => $permFlagData) {
$fields[] = [
'key' => $permFlagData['text'],
'title' => $permFlagData['title'],
'path' => 'Role.' . $permFlag,
'type' => 'boolean',
'mapping' => [
false => '<span class="red bold">Denied</span>',
true => '<span class="green bold">Granted</span>'
]
];
}
echo $this->element(
'genericElements/SingleViews/single_view',
[
'title' => 'Role view',
'data' => $data,
'fields' => $fields,
'children' => [
]
]
);

View File

@ -1,42 +1,40 @@
<div class="dashboard_element">
<?php
if (!empty($api_info['api_info'])) {
echo '<h4 class="blue bold">API info</h4>';
foreach ($api_info['api_info'] as $key => $value) {
if (!empty($value)) {
if (is_array($value)) {
foreach ($value as $k => $v) {
$value[$k] = h($v);
}
if (isset($value['OR']) || isset($value['AND']) || isset($value['NOT'])) {
$temp = array();
foreach ($value as $k => $v) {
$temp[] = $k . ': ' . implode(', ', $v);
}
$value = $temp;
}
$temp = array();
foreach($value as $k => $field) {
$fieldName = $field;
$infoHtml = '';
if ($key === 'mandatory' || $key === 'optional') {
if (is_array($field)) {
foreach($field as $subfield) {
$infoHtml .= '<i id="infofield-'. $subfield .'" class="fa fa-info restclient-infofield" style="margin-left: 5px; width: 12px; height: 12px;"></i>';
}
$fieldName = $k;
} else {
$infoHtml = '<i id="infofield-'. $field .'" class="fa fa-info restclient-infofield" style="margin-left: 5px; width: 12px; height: 12px;"></i>';
}
}
$temp[] = $fieldName . $infoHtml;
}
$value = implode('<br />', $temp);
} else {
$value = h($value);
echo '<h4 class="blue bold">API info</h4>';
foreach ($api_info as $key => $value) {
if (!empty($value)) {
if (is_array($value)) {
foreach ($value as $k => $v) {
$value[$k] = h($v);
}
echo sprintf('<span class=blue>%s</span>:<br /><div style="padding-left:10px;">%s</div>', ucfirst(h($key)), $value);
if (isset($value['OR']) || isset($value['AND']) || isset($value['NOT'])) {
$temp = array();
foreach ($value as $k => $v) {
$temp[] = $k . ': ' . implode(', ', $v);
}
$value = $temp;
}
$temp = array();
foreach ($value as $k => $field) {
$fieldName = $field;
$infoHtml = '';
if ($key === 'mandatory' || $key === 'optional') {
if (is_array($field)) {
foreach($field as $subfield) {
$infoHtml .= '<i id="infofield-'. $subfield .'" class="fa fa-info restclient-infofield" style="margin-left: 5px; width: 12px; height: 12px;"></i>';
}
$fieldName = $k;
} else {
$infoHtml = '<i id="infofield-'. $field .'" class="fa fa-info restclient-infofield" style="margin-left: 5px; width: 12px; height: 12px;"></i>';
}
}
$temp[] = $fieldName . $infoHtml;
}
$value = implode('<br />', $temp);
} else {
$value = h($value);
}
echo sprintf('<span class=blue>%s</span>:<br /><div style="padding-left:10px;">%s</div>', ucfirst(h($key)), $value);
}
}
?>

View File

@ -85,7 +85,6 @@
)
));
?>
</div>
<script type="text/javascript">
var role_perms = <?= json_encode($role_perms) ?>;
function checkPermConditions() {

View File

@ -37,7 +37,7 @@
$passwordPopover = '<span class="blue bold">' . __('Minimal length') . '</span>: ' . h($length) . '<br>';
$passwordPopover .= '<span class="blue bold">' . __('Complexity') . '</span>: ' . h($complexity);
echo $this->Form->input('password', array(
'label' => __('Password') . ' <span id="PasswordPopover" class="fas fa-info-circle"></span>'
'label' => __('Password') . ' <span id="PasswordPopover" data-content="' . h($passwordPopover) . '" class="fas fa-info-circle"></span>'
));
echo $this->Form->input('confirm_password', array('type' => 'password', 'div' => array('class' => 'input password required')));
?>
@ -59,9 +59,9 @@
}
echo $this->Form->input('role_id', $roleOptions);
echo $this->Form->input('authkey', array('value' => $authkey, 'readonly' => 'readonly', 'div' => 'input clear'));
echo $this->Form->input('nids_sid');
echo $this->Form->input('nids_sid', ['label' => __('NIDS SID')]);
?>
<div id = "syncServers" class="hidden">
<div id="syncServers" class="hidden">
<?php
echo $this->Form->input('server_id', array('label' => __('Sync user for'), 'div' => 'clear', 'options' => $servers));
?>
@ -70,6 +70,7 @@
echo $this->Form->input('gpgkey', array('label' => __('GnuPG key'), 'div' => 'clear', 'class' => 'input-xxlarge', 'placeholder' => __('Paste the user\'s GnuPG key here or try to retrieve it from the CIRCL key server by clicking on "Fetch GnuPG key" below.')));
?>
<div class="clear"><span role="button" tabindex="0" aria-label="<?php echo __('Fetch the user\'s GnuPG key');?>" onClick="lookupPGPKey('UserEmail');" class="btn btn-inverse" style="margin-bottom:10px;"><?php echo __('Fetch GnuPG key');?></span></div>
<div class="user-edit-checkboxes" style="margin-bottom: 1em">
<?php
if (Configure::read('SMIME.enabled')) echo $this->Form->input('certif_public', array('label' => __('S/MIME Public certificate (PEM format)'), 'div' => 'clear', 'class' => 'input-xxlarge', 'placeholder' => __('Paste the user\'s S/MIME public key in PEM format here.')));
$default_publish_alert = Configure::check('MISP.default_publish_alert') ? Configure::read('MISP.default_publish_alert') : true;
@ -83,9 +84,6 @@
'type' => 'checkbox',
'checked' => isset($this->request->data['User']['contactalert']) ? $this->request->data['User']['contactalert'] : true
));
?>
<div class="clear"></div>
<?php
echo $this->Form->input('disabled', array('type' => 'checkbox', 'label' => __('Disable this user account')));
echo $this->Form->input('notify', array(
'label' => __('Send credentials automatically'),
@ -93,9 +91,10 @@
'checked' => isset($this->request->data['User']['notify']) ? $this->request->data['User']['notify'] : true
));
?>
</div>
</fieldset>
<?php
echo $this->Form->button(__('Submit'), array('class' => 'btn btn-primary'));
echo $this->Form->button(__('Create user'), array('class' => 'btn btn-primary'));
echo $this->Form->end();?>
</div>
<?php
@ -116,11 +115,5 @@ $(function() {
$('#UserExternalAuthRequired').change(function() {
checkUserExternalAuth();
});
$('#PasswordPopover').popover("destroy").popover({
placement: 'right',
html: 'true',
trigger: 'hover',
content: <?= json_encode($passwordPopover); ?>
});
});
</script>

View File

@ -1,9 +1,12 @@
<div class="users form">
<?php echo $this->Form->create('User', array('novalidate'=>true));?>
<?php echo $this->Form->create('User', array('novalidate' => true));?>
<fieldset>
<legend><?php echo __('Admin Edit User'); ?></legend>
<?php
echo $this->Form->input('email');
echo $this->Form->input('email', [
'disabled' => !$canChangeLogin,
'data-disabled-reason' => !$canChangePassword ? __('User login change is disabled on this instance') : '',
]);
?>
<div class="clear"></div>
<?php
@ -29,16 +32,20 @@
<div class="clear"></div>
<div id="passwordDivDiv">
<?php
echo $this->Form->input('enable_password', array('type' => 'checkbox', 'label' => __('Set password')));
echo $this->Form->input('enable_password', [
'type' => 'checkbox',
'label' => __('Set password'),
'disabled' => !$canChangePassword,
'data-disabled-reason' => !$canChangePassword ? __('User password change is disabled on this instance') : '',
]);
?>
<a class="useCursorPointer" onclick="$('#resetAuthKeyForm').submit();"><?= __('Reset Auth Key') ?></a>
<div id="PasswordDiv">
<div class="clear"></div>
<?php
$passwordPopover = '<span class=\"blue bold\">' . __('Length') .'</span>: ' . h($length) . '<br />';
$passwordPopover .= '<span class=\"blue bold\">' . __('Complexity') .'</span>: ' . h($complexity);
$passwordPopover = '<span class="blue bold">' . __('Length') .'</span>: ' . h($length) . '<br>';
$passwordPopover .= '<span class="blue bold">' . __('Complexity') .'</span>: ' . h($complexity);
echo $this->Form->input('password', array(
'label' => __('Password') . ' <span id="PasswordPopover" class="fas fa-info-circle"></span>'
'label' => __('Password') . ' <span id="PasswordPopover" data-content="' . h($passwordPopover) .'" class="fas fa-info-circle"></span>'
));
echo $this->Form->input('confirm_password', array('type' => 'password', 'div' => array('class' => 'input password required')));
?>
@ -53,10 +60,11 @@
));
}
echo $this->Form->input('role_id', array('label' => __('Role'))); // TODO ACL, User edit role_id.
echo $this->Form->input('authkey', array('disabled' => 'disabled', 'div' => 'input clear'));
echo $this->Form->input('nids_sid');
$authkeyLabel = __('Authkey') . ' <a class="useCursorPointer" onclick="$(\'#resetAuthKeyForm\').submit();">' . __('(Reset)') . '</a>';
echo $this->Form->input('authkey', array('disabled' => true, 'div' => 'input clear', 'label' => $authkeyLabel));
echo $this->Form->input('nids_sid', ['label' => __('NIDS SID')]);
?>
<div id = "syncServers" class="hidden">
<div id="syncServers" class="hidden">
<?php
echo $this->Form->input('server_id', array('label' => __('Sync user for'), 'div' => 'clear', 'options' => $servers));
?>
@ -66,16 +74,21 @@
?>
<div class="clear"><span role="button" tabindex="0" aria-label="<?php echo __('Fetch the user\'s GnuPG key');?>" onClick="lookupPGPKey('UserEmail');" class="btn btn-inverse" style="margin-bottom:10px;"><?php echo __('Fetch GnuPG key');?></span></div>
<?php
if (Configure::read('SMIME.enabled')) echo $this->Form->input('certif_public', array('label' => __('S/MIME Public certificate (PEM format)'), 'div' => 'clear', 'class' => 'input-xxlarge', 'placeholder' => __('Paste the user\'s S/MIME public key in PEM format here.')));
if (Configure::read('SMIME.enabled')) {
echo $this->Form->input('certif_public', array('label' => __('S/MIME Public certificate (PEM format)'), 'div' => 'clear', 'class' => 'input-xxlarge', 'placeholder' => __('Paste the user\'s S/MIME public key in PEM format here.')));
}
echo '<div class="user-edit-checkboxes">';
echo $this->Form->input('termsaccepted', array('type' => 'checkbox', 'label' => __('Terms accepted')));
echo $this->Form->input('change_pw', array('type' => 'checkbox', 'label' => __('Change Password')));
echo $this->Form->input('change_pw', [
'type' => 'checkbox',
'label' => __('User must change password after next login'),
'disabled' => !$canChangePassword,
'data-disabled-reason' => !$canChangePassword ? __('User password change is disabled on this instance') : '',
]);
echo $this->Form->input('autoalert', array('label' => __('Receive alerts when events are published'), 'type' => 'checkbox'));
echo $this->Form->input('contactalert', array('label' => __('Receive alerts from "contact reporter" requests'), 'type' => 'checkbox'));
?>
<div class="clear"></div>
<?php
echo $this->Form->input('disabled', array('type' => 'checkbox', 'label' => __('Disable this user account')));
echo '</div>';
?>
</fieldset>
<div style="border-bottom: 1px solid #e5e5e5;width:100%;">&nbsp;</div>
@ -87,7 +100,7 @@
?>
</div>
<?php
echo $this->Form->button(__('Submit'), array('class' => 'btn btn-primary'));
echo $this->Form->button(__('Edit user'), array('class' => 'btn btn-primary'));
echo $this->Form->end();
echo $this->Form->create('User', array(
'url' => array('controller' => 'users', 'action' => 'resetauthkey', $id),
@ -102,7 +115,7 @@
<script type="text/javascript">
var syncRoles = <?php echo json_encode($syncRoles); ?>;
$(document).ready(function() {
$(function() {
syncUserSelected();
$('#UserRoleId').change(function() {
syncUserSelected();
@ -115,11 +128,5 @@
$('#UserExternalAuthRequired').change(function() {
checkUserExternalAuth();
});
$('#PasswordPopover').popover("destroy").popover({
placement: 'right',
html: 'true',
trigger: 'hover',
content: '<?php echo $passwordPopover; ?>'
});
});
</script>

View File

@ -69,7 +69,9 @@
'sort' => 'User.authkey',
'class' => 'bold',
'data_path' => 'User.authkey',
'privacy' => 1
'onClick' => 'quickSelect(this);',
'privacy' => 1,
'requirement' => empty(Configure::read('Security.advanced_authkeys'))
),
array(
'name' => __('Autoalert'),

View File

@ -1,146 +0,0 @@
<?php
$table_data = array();
$table_data[] = array('key' => __('ID'), 'value' => $user['User']['id']);
$table_data[] = array(
'key' => __('Email'),
'html' => sprintf(
'%s <a class="fas fa-envelope" style="color: #333" href="%s/admin/users/quickEmail/%s" title="%s"></a>',
h($user['User']['email']),
$baseurl,
h($user['User']['id']),
__('Send email to user')
)
);
$table_data[] = array(
'key' => __('Organisation'),
'html' => sprintf(
'<a href="%s/organisations/view/%s">%s</a>',
$baseurl,
h($user['Organisation']['id']),
h($user['Organisation']['name'])
)
);
$table_data[] = array('key' => __('Role'), 'html' => $this->Html->link($user['Role']['name'], array('controller' => 'roles', 'action' => 'view', $user['Role']['id'])));
$table_data[] = array('key' => __('Autoalert'), 'boolean' => $user['User']['autoalert']);
$table_data[] = array('key' => __('Contactalert'), 'boolean' => $user['User']['contactalert']);
$authkey_data = sprintf(
'<span class="privacy-value quickSelect authkey" data-hidden-value="%s">****************************************</span>&nbsp;<i class="privacy-toggle fas fa-eye useCursorPointer" title="%s"></i>%s',
h($user['User']['authkey']),
__('Reveal hidden value'),
sprintf(
' (%s)',
$this->Form->postLink(__('reset'), array('action' => 'resetauthkey', $user['User']['id']))
)
);
$table_data[] = array(
'key' => __('Authkey'),
'html' => $authkey_data
);
if (Configure::read('Plugin.CustomAuth_enable') && !empty($user['User']['external_auth_key'])) {
$header = Configure::read('Plugin.CustomAuth_header') ?: 'Authorization';
$table_data[] = array(
'key' => __('Customauth header'),
'html' => sprintf(
'%s: <span class="green bold">%s</span>',
h($header),
h($user['User']['external_auth_key'])
)
);
}
$table_data[] = array(
'key' => __('Invited By'),
'html' => empty($user2['User']['email']) ? 'N/A' : sprintf('<a href="%s/admin/users/view/%s">%s</a>', $baseurl, h($user2['User']['id']), h($user2['User']['email'])),
);
$org_admin_data = array();
foreach ($user['User']['orgAdmins'] as $orgAdminId => $orgAdminEmail) {
$org_admin_data[] = sprintf(
'<a href="%s/admin/users/view/%s">%s</a> <a class="fas fa-envelope" style="color: #333" href="%s/admin/users/quickEmail/%s" title="%s"></a>',
$baseurl,
h($orgAdminId),
h($orgAdminEmail),
$baseurl,
h($orgAdminId),
__('Send email to user')
);
}
$table_data[] = array('key' => __('Org admin'), 'html' => implode('<br>', $org_admin_data));
$table_data[] = array('key' => __('NIDS Start SID'), 'value' => $user['User']['nids_sid']);
$table_data[] = array('key' => __('Terms accepted'), 'boolean' => $user['User']['termsaccepted']);
$table_data[] = array('key' => __('Must change password'), 'boolean' => $user['User']['change_pw']);
$table_data[] = array(
'key' => __('PGP key'),
'element' => 'genericElements/key',
'element_params' => array('key' => $user['User']['gpgkey']),
);
if (!empty($user['User']['gpgkey'])) {
$table_data[] = array(
'key' => __('GnuPG fingerprint'),
'class_value' => "quickSelect bold " . $user['User']['gpgkey'] ? 'green' : 'bold red',
'value' => $user['User']['fingerprint'] ? chunk_split($user['User']['fingerprint'], 4, ' ') : 'N/A'
);
$table_data[] = array(
'key' => __('GnuPG status'),
'class_value' => "bold" . (empty($user['User']['pgp_status']) || $user['User']['pgp_status'] != 'OK') ? 'red': 'green',
'value' => !empty($user['User']['pgp_status']) ? $user['User']['pgp_status'] : 'N/A'
);
}
if (Configure::read('SMIME.enabled')) {
$table_data[] = array(
'key' => __('S/MIME Public certificate'),
'element' => 'genericElements/key',
'element_params' => array('key' => $user['User']['certif_public']),
);
}
$table_data[] = array(
'key' => __('News read at'),
'value' => $user['User']['newsread'] ? date('Y-m-d H:i:s', $user['User']['newsread']) : __('N/A')
);
$table_data[] = array(
'key' => __('Disabled'),
'class' => empty($user['User']['disabled']) ? '' : 'background-red',
'boolean' => $user['User']['disabled']
);
echo $this->element('genericElements/assetLoader', array(
'css' => array('vis', 'distribution-graph'),
'js' => array('vis', 'network-distribution-graph')
));
echo sprintf(
'<div class="users view row-fluid"><div class="span8" style="margin:0;">%s%s</div>%s</div>',
sprintf(
'<h2>%s</h2>%s',
__('User %s', h($user['User']['email'])),
$this->element('genericElements/viewMetaTable', array('table_data' => $table_data))
),
sprintf(
'<br><a href="%s" class="btn btn-inverse" download>%s</a>',
sprintf(
'%s/users/view/%s.json',
$baseurl,
h($user['User']['id'])
),
__('Download user profile for data portability')
),
'<div id="userEvents"></div>'
);
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'admin', 'menuItem' => 'viewUser'));
?>
<script type="text/javascript">
$(function () {
$.ajax({
url: '<?php echo $baseurl . "/events/index/searchemail:" . urlencode(h($user['User']['email'])); ?>',
type:'GET',
beforeSend: function () {
$(".loading").show();
},
error: function(){
$('#userEvents').html('An error has occurred, please reload the page.');
},
success: function(response){
$('#userEvents').html(response);
},
complete: function() {
$(".loading").hide();
}
});
});
</script>

View File

@ -6,7 +6,7 @@
$passwordPopover = '<span class="blue bold">' . __('Minimal length') . '</span>: ' . h($length) . '<br>';
$passwordPopover .= '<span class="blue bold">' . __('Complexity') . '</span>: ' . h($complexity);
echo $this->Form->input('password', array(
'label' => __('Password') . ' <span id="PasswordPopover" class="fas fa-info-circle"></span>', 'autofocus'
'label' => __('Password') . ' <span id="PasswordPopover" data-content="' . h($passwordPopover) . '" class="fas fa-info-circle"></span>', 'autofocus'
));
echo $this->Form->input('confirm_password', array('type' => 'password', 'div' => array('class' => 'input password required')));
?>
@ -24,16 +24,6 @@ echo $this->Form->button(__('Submit'), array('class' => 'btn btn-primary'));
echo $this->Form->end();
?>
</div>
<script type="text/javascript">
$(function() {
$('#PasswordPopover').popover("destroy").popover({
placement: 'right',
html: 'true',
trigger: 'hover',
content: <?= json_encode($passwordPopover) ?>
});
});
</script>
<?php
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'globalActions', 'menuItem' => 'change_pw'));
?>

View File

@ -3,20 +3,22 @@
<fieldset>
<legend><?php echo __('Edit My Profile'); ?></legend>
<?php
echo $this->Form->input('email');
echo $this->Form->input('email', ['disabled' => $canChangeLogin ? false : 'disabled']);
?>
<div class="input clear"></div>
<?php
if ($canChangePassword) {
$passwordPopover = '<span class="blue bold">' . __('Minimal length') . '</span>: ' . h($length) . '<br>';
$passwordPopover .= '<span class="blue bold">' . __('Complexity') . '</span>: ' . h($complexity);
echo $this->Form->input('password', array(
'label' => __('Password') . ' <span id="PasswordPopover" class="fas fa-info-circle"></span>'
'label' => __('Password') . ' <span id="PasswordPopover" data-content="' . h($passwordPopover) . '" class="fas fa-info-circle"></span>'
));
echo $this->Form->input('confirm_password', array('type' => 'password', 'div' => array('class' => 'input password required')));
}
?>
<div class="input clear"></div>
<?php
echo $this->Form->input('nids_sid');
echo $this->Form->input('nids_sid', ['label' => __('NIDS SID')]);
?>
<div class="input clear"></div>
<?php
@ -24,9 +26,13 @@
?>
<div class="clear"><span role="button" tabindex="0" aria-label="<?php echo __('Fetch GnuPG key');?>" onClick="lookupPGPKey('UserEmail');" class="btn btn-inverse" style="margin-bottom:10px;"><?php echo __('Fetch GnuPG key');?></span></div>
<?php
if (Configure::read('SMIME.enabled')) echo $this->Form->input('certif_public', array('label' => __('S/MIME Public certificate (PEM format)'), 'div' => 'clear', 'class' => 'input-xxlarge'));
if (Configure::read('SMIME.enabled')) {
echo $this->Form->input('certif_public', array('label' => __('S/MIME Public certificate (PEM format)'), 'div' => 'clear', 'class' => 'input-xxlarge'));
}
echo '<div class="user-edit-checkboxes">';
echo $this->Form->input('autoalert', array('label' => __('Receive alerts when events are published'), 'type' => 'checkbox'));
echo $this->Form->input('contactalert', array('label' => __('Receive alerts from "contact reporter" requests'), 'type' => 'checkbox'));
echo '</div>';
?>
</fieldset>
<div style="border-bottom: 1px solid #e5e5e5;width:100%;">&nbsp;</div>
@ -38,7 +44,7 @@
?>
</div>
<?php
echo $this->Form->button(__('Submit'), array('class' => 'btn btn-primary'));
echo $this->Form->button(__('Edit'), array('class' => 'btn btn-primary'));
echo $this->Form->end();
?>
</div>
@ -46,14 +52,4 @@
$user['User']['id'] = $id;
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'globalActions', 'menuItem' => 'edit', 'user' => $user));
?>
<script type="text/javascript">
$(function() {
$('#PasswordPopover').popover("destroy").popover({
placement: 'right',
html: 'true',
trigger: 'hover',
content: <?= json_encode($passwordPopover) ?>
});
});
</script>
<?php echo $this->Js->writeBuffer();

View File

@ -1,36 +1,82 @@
<?php
$table_data = array();
$table_data[] = array('key' => __('ID'), 'value' => $user['User']['id']);
$table_data[] = array('key' => __('Email'), 'value' => $user['User']['email']);
$table_data[] = array('key' => __('Organisation'), 'value' => $user['Organisation']['name']);
$table_data[] = array(
'key' => __('Email'),
'html' => sprintf(
'%s <a class="fas fa-envelope" style="color: #333" href="%s/admin/users/quickEmail/%s" title="%s"></a>',
h($user['User']['email']),
$baseurl,
h($user['User']['id']),
__('Send email to user')
)
);
$table_data[] = array(
'key' => __('Organisation'),
'html' => $this->OrgImg->getNameWithImg($user),
);
$table_data[] = array('key' => __('Role'), 'html' => $this->Html->link($user['Role']['name'], array('controller' => 'roles', 'action' => 'view', $user['Role']['id'])));
$table_data[] = array('key' => __('Autoalert'), 'boolean' => $user['User']['autoalert']);
$table_data[] = array('key' => __('Contactalert'), 'boolean' => $user['User']['contactalert']);
$authkey_data = sprintf(
'<a onclick="requestAPIAccess();" style="cursor:pointer;">%s</a>',
__('Request API access')
);
if ($user['Role']['perm_auth']) {
if (!$admin_view && !$user['Role']['perm_auth']) {
$table_data[] = array(
'key' => __('Auth key'),
'html' => sprintf('<a onclick="requestAPIAccess();" class="useCursorPointer">%s</a>', __('Request API access')),
);
}
if (empty(Configure::read('Security.advanced_authkeys')) && $user['Role']['perm_auth']) {
$authkey_data = sprintf(
'<span class="privacy-value quickSelect authkey" data-hidden-value="%s">****************************************</span>&nbsp;<i class="privacy-toggle fas fa-eye useCursorPointer" title="%s"></i>%s',
h($user['User']['authkey']),
__('Reveal hidden value'),
(Configure::read('MISP.disableUserSelfManagement') && !$isAdmin) ? '' :
sprintf(
' (%s)',
$this->Form->postLink(__('reset'), array('action' => 'resetauthkey', $user['User']['id']))
sprintf(
' (%s)',
$this->Form->postLink(__('reset'), array('action' => 'resetauthkey', $user['User']['id']))
)
);
$table_data[] = array(
'key' => __('Auth key'),
'html' => $authkey_data
);
}
)
if (Configure::read('Plugin.CustomAuth_enable') && !empty($user['User']['external_auth_key'])) {
$header = Configure::read('Plugin.CustomAuth_header') ?: 'Authorization';
$table_data[] = array(
'key' => __('Customauth header'),
'html' => sprintf(
'%s: <span class="green bold">%s</span>',
h($header),
h($user['User']['external_auth_key'])
)
);
}
$table_data[] = array(
'key' => __('Authkey'),
'html' => $authkey_data
'key' => __('Invited By'),
'html' => empty($user2['User']['email']) ? 'N/A' : sprintf('<a href="%s/admin/users/view/%s">%s</a>', $baseurl, h($user2['User']['id']), h($user2['User']['email'])),
);
$org_admin_data = array();
if ($admin_view) {
foreach ($user['User']['orgAdmins'] as $orgAdminId => $orgAdminEmail) {
$org_admin_data[] = sprintf(
'<a href="%s/admin/users/view/%s">%s</a> <a class="fas fa-envelope" style="color: #333" href="%s/admin/users/quickEmail/%s" title="%s"></a>',
$baseurl,
h($orgAdminId),
h($orgAdminEmail),
$baseurl,
h($orgAdminId),
__('Send email to user')
);
}
$table_data[] = array('key' => __('Org admin'), 'html' => implode('<br>', $org_admin_data));
}
$table_data[] = array('key' => __('NIDS Start SID'), 'value' => $user['User']['nids_sid']);
$table_data[] = array('key' => __('Terms accepted'), 'boolean' => $user['User']['termsaccepted']);
$table_data[] = array('key' => __('Must change password'), 'boolean' => $user['User']['change_pw']);
$table_data[] = array(
'key' => __('GnuPG key'),
'key' => __('PGP key'),
'element' => 'genericElements/key',
'element_params' => array('key' => $user['User']['gpgkey']),
);
@ -38,12 +84,12 @@
$table_data[] = array(
'key' => __('GnuPG fingerprint'),
'class_value' => "quickSelect bold " . $user['User']['gpgkey'] ? 'green' : 'bold red',
'html' => $user['User']['fingerprint'] ? chunk_split(h($user['User']['fingerprint']), 4, ' ') : 'N/A'
'value' => $user['User']['fingerprint'] ? chunk_split($user['User']['fingerprint'], 4, ' ') : 'N/A'
);
$table_data[] = array(
'key' => __('GnuPG status'),
'class_value' => "bold" . (empty($user['User']['pgp_status']) || $user['User']['pgp_status'] != 'OK') ? 'red': 'green',
'html' => !empty($user['User']['pgp_status']) ? h($user['User']['pgp_status']) : 'N/A'
'value' => !empty($user['User']['pgp_status']) ? $user['User']['pgp_status'] : 'N/A'
);
}
if (Configure::read('SMIME.enabled')) {
@ -53,18 +99,40 @@
'element_params' => array('key' => $user['User']['certif_public']),
);
}
$table_data[] = array(
'key' => __('News read at'),
'value' => $user['User']['newsread'] ? date('Y-m-d H:i:s', $user['User']['newsread']) : __('N/A')
);
$table_data[] = array(
'key' => __('Disabled'),
'class' => empty($user['User']['disabled']) ? '' : 'background-red',
'boolean' => $user['User']['disabled']
);
echo $this->element('genericElements/assetLoader', array(
'css' => array('vis', 'distribution-graph'),
'js' => array('vis', 'network-distribution-graph')
));
$current_menu = [
'admin_view' => ['menuList' => 'admin', 'menuItem' => 'viewUser'],
'view' => ['menuList' => 'globalActions', 'menuItem' => 'view']
];
echo sprintf(
'<div class="users view"><div class="row-fluid"><div class="span8" style="margin:0px;">%s</div></div>%s</div>%s',
'<div class="users view"><div class="row-fluid"><div class="span8" style="margin:0px;">%s</div></div>%s<div style="margin-top:20px;">%s%s</div></div>%s',
sprintf(
'<h2>%s</h2>%s',
__('User %s', h($user['User']['email'])),
$this->element('genericElements/viewMetaTable', array('table_data' => $table_data))
),
sprintf(
'<a href="%s" class="btn btn-inverse" download>%s</a>',
$baseurl . '/users/view/me.json',
'<br><a href="%s" class="btn btn-inverse" download>%s</a>',
sprintf(
'%s/users/view/%s.json',
$baseurl,
h($user['User']['id'])
),
__('Download user profile for data portability')
),
$this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'globalActions', 'menuItem' => 'view'))
$me['Role']['perm_auth'] ? $this->element('/genericElements/accordion', array('title' => 'Authkeys', 'url' => '/auth_keys/index/' . h($user['User']['id']))) : '',
$this->element('/genericElements/accordion', array('title' => 'Events', 'url' => '/events/index/searchemail:' . urlencode(h($user['User']['email'])))),
$this->element('/genericElements/SideMenu/side_menu', $current_menu[$admin_view ? 'admin_view' : 'view'])
);

View File

@ -0,0 +1,27 @@
<div id="genericModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="genericModalLabel" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
<span aria-hidden="true">&times;</span>
</button>
<h3 id="genericModalLabel"><?= __('Delete %s', Inflector::singularize(Inflector::humanize($this->params['controller']))) ?></h3>
</div>
<div class="modal-body modal-body-long">
<p><?= __('Are you sure you want to delete %s #%s?', h(Inflector::singularize($this->params['controller'])), h($id)) ?></p>
</div>
<div class="modal-footer">
<?= $this->Form->postLink(
'Delete',
$this->request->here(),
['class' => 'btn btn-primary button-execute']
)
?>
<button type="button" class="btn btn-secondary cancel-button" data-dismiss="modal"><?= __('Cancel') ?></button>
</div>
</div>
<script type="text/javascript">
$(document).keydown(function(e) {
if(e.which === 13 && e.ctrlKey) {
$('.button-execute').click();
}
});
</script>

View File

@ -2645,3 +2645,28 @@ table tr:hover .down-expand-button {
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
font-size: 11px;
}
/*
Make user edit checkboxes nicer
*/
.user-edit-checkboxes div.input {
float: none;
width: inherit;
margin-top: 3px;
}
.user-edit-checkboxes .checkbox input[type="checkbox"] {
margin-top: 4px;
}
.user-edit-checkboxes label {
display: inline;
}
a.orgImg {
background-repeat: no-repeat;
background-size: 20px;
padding-left: 25px;
padding-top: 2px;
padding-bottom: 2px;
}

View File

@ -35,7 +35,7 @@ function rgb2hex(rgb) {
}
function xhrFailCallback(xhr) {
if (xhr.status === 403) {
if (xhr.status === 403 || xhr.status === 405) {
showMessage('fail', 'Not allowed.');
} else if (xhr.status === 404) {
showMessage('fail', 'Resource not found.');
@ -300,18 +300,18 @@ function initiatePasswordReset(id) {
$.get(baseurl + "/users/initiatePasswordReset/" + id, function(data) {
$("#confirmation_box").html(data);
openPopup("#confirmation_box");
});
}).fail(xhrFailCallback)
}
function submitPasswordReset(id) {
var formData = $('#PromptForm').serialize();
var url = baseurl + "/users/initiatePasswordReset/" + id;
$.ajax({
beforeSend: function (XMLHttpRequest) {
beforeSend: function () {
$(".loading").show();
},
data: formData,
success:function (data, textStatus) {
success: function (data) {
handleGenericAjaxResponse(data);
},
complete:function() {
@ -319,9 +319,9 @@ function submitPasswordReset(id) {
$("#confirmation_box").fadeOut();
$("#gray_out").fadeOut();
},
type:"post",
type: "post",
cache: false,
url:url,
url: url,
});
}
@ -4684,6 +4684,14 @@ $(document).ready(function() {
return $(this).data('disabled-reason');
}
});
$('#PasswordPopover').popover("destroy").popover({
placement: 'right',
html: 'true',
trigger: 'hover',
content: function () {
return $(this).data('content');
}
});
$(".queryPopover").click(function() {
url = $(this).data('url');
id = $(this).data('id');
@ -5298,6 +5306,20 @@ function changeLocationFromIndexDblclick(row_index) {
window.location = href;
}
function submitGenericFormInPlace() {
$.ajax({
type: "POST",
url: $('.genericForm').attr('action'),
data: $('.genericForm').serialize(), // serializes the form's elements.
success: function(data)
{
$('#genericModal').remove();
$('body').append(data);
$('#genericModal').modal();
}
});
}
function openIdSelection(clicked, scope, action) {
var onclick = 'redirectIdSelection(\'' + scope + '\', \'' + action + '\')'
var html = '<div class="input-append">'

View File

@ -426,7 +426,7 @@ function addHoverInfo(url) {
if(apiInfo !== undefined && apiInfo !== '') {
$('#infofield-'+field).popover({
trigger: 'hover',
content: ''+field+':'+ apiInfo,
content: field + ': ' + apiInfo,
});
} else { // no help, delete icon
$('#infofield-'+field).remove();
@ -555,4 +555,4 @@ function setupCodeMirror() {
cm.showHint()
}
});
}
}

View File

@ -384,6 +384,107 @@
"extra": ""
}
],
"auth_keys": [
{
"column_name": "id",
"is_nullable": "NO",
"data_type": "int",
"character_maximum_length": null,
"numeric_precision": "10",
"collation_name": null,
"column_type": "int(10) unsigned",
"column_default": null,
"extra": "auto_increment"
},
{
"column_name": "uuid",
"is_nullable": "NO",
"data_type": "varchar",
"character_maximum_length": "40",
"numeric_precision": null,
"collation_name": "utf8mb4_unicode_ci",
"column_type": "varchar(40)",
"column_default": null,
"extra": ""
},
{
"column_name": "authkey",
"is_nullable": "YES",
"data_type": "varchar",
"character_maximum_length": "72",
"numeric_precision": null,
"collation_name": "ascii_general_ci",
"column_type": "varchar(72)",
"column_default": "NULL",
"extra": ""
},
{
"column_name": "authkey_start",
"is_nullable": "YES",
"data_type": "varchar",
"character_maximum_length": "4",
"numeric_precision": null,
"collation_name": "ascii_general_ci",
"column_type": "varchar(4)",
"column_default": "NULL",
"extra": ""
},
{
"column_name": "authkey_end",
"is_nullable": "YES",
"data_type": "varchar",
"character_maximum_length": "4",
"numeric_precision": null,
"collation_name": "ascii_general_ci",
"column_type": "varchar(4)",
"column_default": "NULL",
"extra": ""
},
{
"column_name": "created",
"is_nullable": "NO",
"data_type": "int",
"character_maximum_length": null,
"numeric_precision": "10",
"collation_name": null,
"column_type": "int(10) unsigned",
"column_default": null,
"extra": ""
},
{
"column_name": "expiration",
"is_nullable": "NO",
"data_type": "int",
"character_maximum_length": null,
"numeric_precision": "10",
"collation_name": null,
"column_type": "int(10) unsigned",
"column_default": null,
"extra": ""
},
{
"column_name": "user_id",
"is_nullable": "NO",
"data_type": "int",
"character_maximum_length": null,
"numeric_precision": "10",
"collation_name": null,
"column_type": "int(10) unsigned",
"column_default": null,
"extra": ""
},
{
"column_name": "comment",
"is_nullable": "YES",
"data_type": "text",
"character_maximum_length": "65535",
"numeric_precision": null,
"collation_name": "utf8mb4_unicode_ci",
"column_type": "text",
"column_default": "NULL",
"extra": ""
}
],
"bruteforces": [
{
"column_name": "ip",
@ -6976,6 +7077,14 @@
"event_id": false,
"tag_id": false
},
"auth_keys": {
"id": true,
"authkey_start": false,
"authkey_end": false,
"created": false,
"expiration": false,
"user_id": false
},
"bruteforces": [],
"cake_sessions": {
"id": true,
@ -7348,5 +7457,5 @@
"id": true
}
},
"db_version": "60"
"db_version": "61"
}