mirror of https://github.com/MISP/MISP
new: [3.x] initial skeleton added
- basic functionality available for authentication - user/org/role management - logging - requires a MISP 2.x database as the datastorepull/9075/head
parent
827bf50f3c
commit
35932dbf45
|
@ -1,116 +1,13 @@
|
|||
# Ignore tooling config files
|
||||
tools/misp-wipe/misp-wipe.conf
|
||||
tools/misp-backup/misp-backup.conf
|
||||
|
||||
# Ignore mkdocs site directory and mkdocs virtualenv
|
||||
site/
|
||||
tools/mkdocs
|
||||
|
||||
# venv
|
||||
/venv/*
|
||||
|
||||
# Other
|
||||
/vendors
|
||||
/app/Vendor
|
||||
/app/vendor
|
||||
/app/composer*
|
||||
!/app/composer.phar
|
||||
!/app/composer.license
|
||||
/app/composer.json
|
||||
/lib
|
||||
/.project
|
||||
/.settings
|
||||
/.buildpath
|
||||
/.idea
|
||||
.DS_Store
|
||||
/.htaccess
|
||||
/app/Vendor
|
||||
/README
|
||||
/app/tmp/GPG*
|
||||
/app/tmp/sessions/sess_*
|
||||
/app/tmp/logs/*.log
|
||||
/app/tmp/cache/models/myapp*
|
||||
/app/tmp/cache/persistent/myapp*
|
||||
/app/tmp/cache/views/myapp*
|
||||
/app/tmp/cache/misp_feed*
|
||||
/app/files/*
|
||||
/app/tmp/cache/feeds/*.cache
|
||||
app/Lib/EventWarning/Custom/*
|
||||
!app/Lib/EventWarning/Custom/empty
|
||||
!/app/files/feed-metadata
|
||||
!/app/files/empty
|
||||
!/app/files/scripts/
|
||||
!/app/files/warninglists
|
||||
!/app/files/warninglists/*
|
||||
!/app/files/noticelists
|
||||
!/app/files/noticelists/*
|
||||
!/app/files/misp-galaxy
|
||||
!/app/files/misp-galaxy/*
|
||||
!/app/files/misp-objects
|
||||
!/app/files/misp-objects/*
|
||||
!/app/files/misp-decaying-models
|
||||
!/app/files/misp-decaying-models/*
|
||||
!/app/files/misp-workflow-blueprints
|
||||
!/app/files/misp-workflow-blueprints/*
|
||||
/app/files/scripts/*.pyc
|
||||
/app/files/scripts/*.py~
|
||||
/app/files/scripts/__pycache__
|
||||
/app/files/scripts/yara/__pycache__
|
||||
/app/files/scripts/yara/*.pyc
|
||||
/app/files/scripts/yara/*.py~
|
||||
/app/files/scripts/mispzmq/*
|
||||
!/app/files/scripts/mispzmq/mispzmq.py
|
||||
!/app/files/scripts/mispzmq/mispzmqtest.py
|
||||
/app/files/scripts/tmp/*
|
||||
!/app/files/scripts/tmp/empty
|
||||
/app/files/scripts/stix2/*
|
||||
!/app/files/scripts/stix2/misp2stix2*.py
|
||||
!/app/files/scripts/stix2/stix2misp*.py
|
||||
!/app/files/empty
|
||||
/app/files/terms/*
|
||||
!/app/files/terms/empty
|
||||
/app/webroot/img/logo.png
|
||||
/app/webroot/img/custom/*
|
||||
!/app/webroot/img/custom/empty
|
||||
/app/webroot/img/orgs/*
|
||||
!/app/webroot/img/orgs/ADMIN.png
|
||||
!/app/webroot/img/orgs/MIL.be.png
|
||||
!/app/webroot/img/orgs/MISP.png
|
||||
!/app/webroot/img/orgs/NATO.png
|
||||
!/app/webroot/img/orgs/NCIRC.png
|
||||
/app/Config/bootstrap.php
|
||||
/app/Config/database.php
|
||||
/app/Config/core.php
|
||||
/app/Config/config.php
|
||||
/app/Console/Command/training.json
|
||||
/app/Lib/cakephp
|
||||
/app/webroot/gpg.asc
|
||||
/app/tmp/logs
|
||||
/app/tmp/cached_exports/xml/*
|
||||
/app/tmp/cached_exports/json/*
|
||||
/app/tmp/cached_exports/text/*
|
||||
/app/tmp/cached_exports/rpz/*
|
||||
/app/tmp/cached_exports/snort/*
|
||||
/app/tmp/cached_exports/suricata/*
|
||||
/app/tmp/cached_exports/md5/*
|
||||
/app/tmp/cached_exports/sha1/*
|
||||
/app/tmp/cached_exports/csv_all/*
|
||||
/app/tmp/cached_exports/csv_sig/*
|
||||
/app/tmp/cached_exports/stix/*
|
||||
/app/tmp/cached_exports/sha256/*
|
||||
/app/tmp/cached_exports/bro/*
|
||||
/app/Plugin/CakeResque
|
||||
.gnupg
|
||||
.smime
|
||||
*.swp
|
||||
*.iml
|
||||
.ropeproject/
|
||||
vagrant/.vagrant/
|
||||
vagrant/*.log
|
||||
/app/Lib/Dashboard/Custom/*
|
||||
!/app/Lib/Dashboard/Custom/empty
|
||||
/app/View/Emails/html/Custom/*
|
||||
!/app/View/Emails/html/Custom/empty
|
||||
/app/View/Emails/text/Custom/*
|
||||
!/app/View/Emails/text/Custom/empty
|
||||
|
||||
composer.lock
|
||||
config/app_local.php
|
||||
config/Migrations/schema-dump-default.lock
|
||||
logs
|
||||
tmp
|
||||
vendor
|
||||
webroot/theme/node_modules
|
||||
webroot/scss/*.css
|
||||
.vscode
|
||||
docker/run/
|
||||
.phpunit.result.cache
|
||||
config.json
|
||||
phpunit.xml
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
[submodule "app/Lib/cakephp"]
|
||||
path = app/Lib/cakephp
|
||||
url = https://github.com/MISP/cakephp.git
|
||||
branch = 2.x
|
||||
[submodule "PyMISP"]
|
||||
path = PyMISP
|
||||
url = https://github.com/MISP/PyMISP.git
|
||||
[submodule "app/files/taxonomies"]
|
||||
path = app/files/taxonomies
|
||||
url = https://github.com/MISP/misp-taxonomies.git
|
||||
[submodule "app/files/warninglists"]
|
||||
path = app/files/warninglists
|
||||
url = https://github.com/MISP/misp-warninglists.git
|
||||
[submodule "app/files/misp-galaxy"]
|
||||
path = app/files/misp-galaxy
|
||||
url = https://github.com/MISP/misp-galaxy
|
||||
[submodule "app/files/misp-objects"]
|
||||
path = app/files/misp-objects
|
||||
url = https://github.com/MISP/misp-objects
|
||||
[submodule "misp-vagrant"]
|
||||
path = misp-vagrant
|
||||
url = https://github.com/MISP/misp-vagrant.git
|
||||
[submodule "cti-python-stix2"]
|
||||
path = app/files/scripts/cti-python-stix2
|
||||
url = https://github.com/MISP/cti-python-stix2
|
||||
[submodule "app/files/noticelists"]
|
||||
path = app/files/noticelists
|
||||
url = https://github.com/MISP/misp-noticelist
|
||||
[submodule "app/files/misp-decaying-models"]
|
||||
path = app/files/misp-decaying-models
|
||||
url = https://github.com/MISP/misp-decaying-models.git
|
||||
[submodule "app/files/scripts/misp-opendata"]
|
||||
path = app/files/scripts/misp-opendata
|
||||
url = https://github.com/MISP/misp-opendata
|
||||
[submodule "app/files/scripts/misp-stix"]
|
||||
path = app/files/scripts/misp-stix
|
||||
url = https://github.com/misp/misp-stix.git
|
||||
branch = main
|
||||
[submodule "app/files/scripts/python-stix"]
|
||||
path = app/files/scripts/python-stix
|
||||
url = https://github.com/STIXProject/python-stix
|
||||
[submodule "app/files/scripts/python-cybox"]
|
||||
path = app/files/scripts/python-cybox
|
||||
url = https://github.com/CybOXProject/python-cybox
|
||||
[submodule "app/files/scripts/mixbox"]
|
||||
path = app/files/scripts/mixbox
|
||||
url = https://github.com/CybOXProject/mixbox
|
||||
[submodule "app/files/scripts/python-maec"]
|
||||
path = app/files/scripts/python-maec
|
||||
url = https://github.com/MAECProject/python-maec
|
||||
[submodule "app/files/misp-workflow-blueprints"]
|
||||
path = app/files/misp-workflow-blueprints
|
||||
url = https://github.com/MISP/misp-workflow-blueprints
|
|
@ -0,0 +1,160 @@
|
|||
# Installing Cerebrate on RedHat Enterprise Linux (RHEL 8)
|
||||
>This installation instructions assume SELinux is enabled, and in Enforcing mode.
|
||||
>and that you want to keep it that way :)
|
||||
>You need to be root when running these commands.
|
||||
|
||||
## Prerequisites
|
||||
>Install needed packages:
|
||||
```Shell
|
||||
dnf install @httpd mariadb-server git @php unzip sqlite vim wget php-intl php-ldap php-mysqlnd php-pdo php-zip
|
||||
```
|
||||
## Install composer
|
||||
>Instructions taken from https://getcomposer.org/download/
|
||||
```PHP
|
||||
cd /root
|
||||
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
|
||||
php -r "if (hash_file('sha384', 'composer-setup.php') === '55ce33d7678c5a611085589f1f3ddf8b3c52d662cd01d4ba75c0ee0459970c2200a51f492d557530c71c15d8dba01eae') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
|
||||
php composer-setup.php --install-dir=/usr/bin --filename=composer
|
||||
php -r "unlink('composer-setup.php');"
|
||||
```
|
||||
|
||||
## Prepare MySQL for cerebrate
|
||||
>Enable and start mysql database. Select a secure password for root user, delete test user and database.
|
||||
|
||||
```Shell
|
||||
systemctl start mariadb
|
||||
systemctl enable mariadb
|
||||
mysql_secure_installation
|
||||
```
|
||||
### Create a new database, user and password for cerebrate
|
||||
```Shell
|
||||
mysql -u root -p
|
||||
```
|
||||
```SQL
|
||||
CREATE DATABASE cerebrate;
|
||||
CREATE USER 'cerebrate'@'localhost' IDENTIFIED BY 'CHANGE_ME_PASSWORD';
|
||||
GRANT USAGE ON *.* to cerebrate@localhost;
|
||||
GRANT ALL PRIVILEGES ON cerebrate.* to cerebrate@localhost;
|
||||
FLUSH PRIVILEGES;
|
||||
QUIT;
|
||||
```
|
||||
## Allow ports through the firewall
|
||||
```Shell
|
||||
firewall-cmd --zone=public --add-service=http --permanent
|
||||
firewall-cmd --zone=public --add-port=8001/tcp --permanent
|
||||
```
|
||||
> reload firewall and show applied firewall rules
|
||||
```Shell
|
||||
firewall-cmd --reload
|
||||
firewall-cmd --zone public --list-all
|
||||
```
|
||||
|
||||
## Main Cerebrate Installation
|
||||
>Steps to install Cerebrate on RHEL
|
||||
|
||||
### Clone this repository
|
||||
```Shell
|
||||
mkdir /var/www/cerebrate
|
||||
git clone https://github.com/cerebrate-project/cerebrate.git /var/www/cerebrate
|
||||
```
|
||||
|
||||
### Run composer
|
||||
```Shell
|
||||
mkdir -p /var/www/.composer
|
||||
chown -R apache.apache /var/www/.composer
|
||||
chown -R apache.apache /var/www/cerebrate
|
||||
cd /var/www/cerebrate
|
||||
composer install
|
||||
```
|
||||
>you will see a prompt: \
|
||||
>`Do you trust "cakephp/plugin-installer" to execurte code and wish to enable it now? (writes "allow-plugins" to composer.json) [y,n,d,?]` \
|
||||
>*repond with* `y` \
|
||||
>`Do you trust "dealerdirect/phpcodesniffer-composer-installer" to execute code and wish to enable it now? (writes "allow-plugins" to composer.json) [y,n,d,?]` \
|
||||
>*repond with* `y`
|
||||
|
||||
### Create your local configuration and set the db credentials
|
||||
```Shell
|
||||
cp -a /var/www/cerebrate/config/app_local.example.php /var/www/cerebrate/config/app_local.php
|
||||
cp -a /var/www/cerebrate/config/config.example.json /var/www/cerebrate/config/config.json
|
||||
```
|
||||
|
||||
### Modify the Datasource -> default array's in file `app_local.php`
|
||||
>Simply modify the `Datasources` section, to reflect your values for: username, password, and database
|
||||
>fields, as configured in the above [#create-a-new-database-user-and-password-for-cerebrate](<#create-a-new-database-user-and-password-for-cerebrate>)
|
||||
```Shell
|
||||
vim /var/www/cerebrate/config/app_local.php
|
||||
```
|
||||
```PHP
|
||||
'Datasources' => [
|
||||
'default' => [
|
||||
'host' => 'localhost',
|
||||
'username' => 'cerebrate',
|
||||
'password' => 'CHANGE_ME_PASSWORD',
|
||||
'database' => 'cerebrate',
|
||||
...
|
||||
```
|
||||
|
||||
### Run the database schema migrations
|
||||
```Shell
|
||||
usermod -s /bin/bash apache
|
||||
|
||||
chown -R apache.apache /var/www/.composer
|
||||
chown -R apache.apache /var/www/cerebrate
|
||||
|
||||
su apache <<'EOFi'
|
||||
/var/www/cerebrate/bin/cake migrations migrate
|
||||
/var/www/cerebrate/bin/cake migrations migrate -p tags
|
||||
/var/www/cerebrate/bin/cake migrations migrate -p ADmad/SocialAuth
|
||||
EOFi
|
||||
|
||||
usermod -s /sbin/nologin apache
|
||||
```
|
||||
|
||||
|
||||
### Clean cakephp caches
|
||||
```Shell
|
||||
rm /var/www/cerebrate/tmp/cache/models/*
|
||||
rm /var/www/cerebrate/tmp/cache/persistent/*
|
||||
```
|
||||
|
||||
### copy the Apache httpd template to the default apache configuration folder
|
||||
> in our case we used apache to serve this website, NGINX could also be used.
|
||||
```Shell
|
||||
cp -v /var/www/cerebrate/INSTALL/cerebrate_apache_dev.conf /etc/httpd/conf.d/.
|
||||
mkdir /var/log/apache2
|
||||
chown apache.root -R /var/log/apache2
|
||||
restorecon -Rv /etc/httpd/conf.d/*
|
||||
restorecon -Rv /var/log/*
|
||||
```
|
||||
### Make changes to the apache httpd site configuration file
|
||||
>Edit the file `/etc/httpd/conf.d/cerebrate_apache_dev.conf` change the two references of port 8000 to 8001
|
||||
```Shell
|
||||
vi /etc/httpd/conf.d/cerebrate_apache_dev.conf
|
||||
```
|
||||
### Make changes to SELinux
|
||||
>From the SELinux Manual page [services with non standard ports](<https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/using_selinux/configuring-selinux-for-applications-and-services-with-non-standard-configurations_using-selinux>)
|
||||
>We need SELinux to allow httpd to connect to our custom port 8001/tcp
|
||||
```SELinux Policy
|
||||
semanage port -a -t http_port_t -p tcp 8001
|
||||
```
|
||||
>Change SELinux context for folder /var/www/cerebrate
|
||||
```SELinux Policy
|
||||
semanage fcontext -a -t httpd_sys_content_t "/var/www/cerebrate(/.*)?"
|
||||
restorecon -Rv /var/www/cerebrate/
|
||||
chown apache.apache /var/www/cerebrate
|
||||
```
|
||||
|
||||
## Apply changes/restart Apache httpd
|
||||
>Look out for any errors during restart.
|
||||
```
|
||||
systemctl enable httpd
|
||||
systemctl restart httpd
|
||||
```
|
||||
|
||||
## Point your browser to: http://localhost:8001
|
||||
> If everything worked, you should be able to log in using the default credentials below:
|
||||
|
||||
```
|
||||
Username: admin
|
||||
Password: Password1234
|
||||
```
|
|
@ -1 +0,0 @@
|
|||
INSTALL.sh
|
|
@ -0,0 +1,171 @@
|
|||
## Important
|
||||
|
||||
** NOTE: CURRENTLY MISP 3.x IS IN DEVELOPMENT ONLY MODE LACKING MOST MAJOR FEATURES **
|
||||
|
||||
## Requirements
|
||||
|
||||
An Ubuntu server (22.04 at least highly recommended) - though other linux installations should work too.
|
||||
|
||||
- apache2 (or nginx), mysql/mariadb, sqlite need to be installed and running
|
||||
- php version 8.1+ is required
|
||||
- php extensions for intl, mysql, sqlite3, mbstring, xml need to be installed and running
|
||||
- php extention for curl (not required but makes composer run a little faster)
|
||||
- composer
|
||||
|
||||
## Network requirements
|
||||
|
||||
MISP communicates via HTTPS so in order to be able to connect to other MISP nodes, requiring the following ports to be open:
|
||||
- port 443 needs to be open for outbound connections to be able to pull data in
|
||||
- MISP also needs to be accessible (via port 443) from the outside if:
|
||||
- you wish to act as a hub node for a community where members are expected to pull data from your node or push data to it
|
||||
- you wish to be a member of a community and expect other parties to push data to you in real-time when they publish it
|
||||
|
||||
|
||||
## MISP installation instructions
|
||||
|
||||
It should be sufficient to issue the following command to install the dependencies:
|
||||
|
||||
- for apache
|
||||
|
||||
```bash
|
||||
sudo apt install apache2 mariadb-server git composer php-intl php-mbstring php-dom php-xml unzip php-ldap php-sqlite3 php-curl sqlite libapache2-mod-php php-mysql
|
||||
```
|
||||
|
||||
- for nginx
|
||||
```bash
|
||||
sudo apt install nginx mariadb-server git composer php-intl php-mbstring php-dom php-xml unzip php-ldap php-sqlite3 sqlite php-fpm php-curl php-mysql
|
||||
```
|
||||
|
||||
Clone this repository (for example into /var/www/MISP)
|
||||
|
||||
```bash
|
||||
sudo mkdir /var/www/misp
|
||||
sudo chown www-data:www-data /var/www/misp
|
||||
sudo -u www-data git clone https://github.com/MISP/MISP.git /var/www/MISP
|
||||
cd /var/www/MISP
|
||||
sudo -u www-data git checkout 3.x
|
||||
```
|
||||
|
||||
Run composer
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /var/www/.composer
|
||||
sudo chown www-data:www-data /var/www/.composer
|
||||
cd /var/www/MISP
|
||||
sudo -H -u www-data composer install
|
||||
```
|
||||
|
||||
Create a database for MISP
|
||||
|
||||
With a fresh install of Ubuntu sudo to the (system) root user before logging in as the mysql root
|
||||
```Bash
|
||||
sudo -i mysql -u root
|
||||
```
|
||||
|
||||
From SQL shell:
|
||||
```mysql
|
||||
mysql
|
||||
CREATE DATABASE misp;
|
||||
CREATE USER 'misp'@'localhost' IDENTIFIED BY 'YOUR_PASSWORD';
|
||||
GRANT USAGE ON *.* to misp@localhost;
|
||||
GRANT ALL PRIVILEGES ON misp.* to misp@localhost;
|
||||
FLUSH PRIVILEGES;
|
||||
QUIT;
|
||||
```
|
||||
|
||||
Or from Bash:
|
||||
```bash
|
||||
sudo mysql -e "CREATE DATABASE misp;"
|
||||
sudo mysql -e "CREATE USER 'misp'@'localhost' IDENTIFIED BY 'YOUR_PASSWORD';"
|
||||
sudo mysql -e "GRANT USAGE ON *.* to misp@localhost;"
|
||||
sudo mysql -e "GRANT ALL PRIVILEGES ON misp.* to misp@localhost;"
|
||||
sudo mysql -e "FLUSH PRIVILEGES;"
|
||||
```
|
||||
|
||||
create your local configuration and set the db credentials
|
||||
|
||||
```bash
|
||||
sudo -u www-data cp -a /var/www/misp/config/app_local.example.php /var/www/misp/config/app_local.php
|
||||
sudo -u www-data cp -a /var/www/misp/config/config.example.json /var/www/misp/config/config.json
|
||||
sudo -u www-data vim /var/www/misp/config/app_local.php
|
||||
```
|
||||
|
||||
mod_rewrite needs to be enabled if __using apache__:
|
||||
|
||||
```bash
|
||||
sudo a2enmod rewrite
|
||||
```
|
||||
|
||||
Simply modify the Datasource -> default array's username, password, database fields
|
||||
This would be, when following the steps above:
|
||||
|
||||
```php
|
||||
'Datasources' => [
|
||||
'default' => [
|
||||
'host' => 'localhost',
|
||||
'username' => 'misp',
|
||||
'password' => 'YOUR_PASSWORD',
|
||||
'database' => 'misp',
|
||||
```
|
||||
|
||||
### WARNING: DURING THE PRE-RELEASE STATE, USE AN EXISTING MISP DB'S DUMP AS A STARTING POINT
|
||||
|
||||
From your old MISP:
|
||||
|
||||
```
|
||||
mysqldump -u misp -p misp > misp_bkup.sql
|
||||
|
||||
```
|
||||
From the development 3.x branch MISP:
|
||||
|
||||
```
|
||||
mysql -u misp -p misp < misp_bkup.sql
|
||||
```
|
||||
|
||||
### Make sure you apply any pending deltas from TODO.TXT until migration scripts exist for them
|
||||
|
||||
|
||||
Run the database schema migrations
|
||||
```bash
|
||||
sudo -u www-data /var/www/misp/bin/cake migrations migrate
|
||||
```
|
||||
|
||||
Clean cakephp caches
|
||||
```bash
|
||||
sudo rm /var/www/misp/tmp/cache/models/*
|
||||
sudo rm /var/www/misp/tmp/cache/persistent/*
|
||||
```
|
||||
|
||||
Create an apache config file for misp / ssh key and point the document root to /var/www/misp/webroot and you're good to go
|
||||
|
||||
For development installs the following can be done for either apache or nginx:
|
||||
|
||||
```bash
|
||||
# Apache
|
||||
# This configuration is purely meant for local installations for development / testing
|
||||
# Using HTTP on an unhardened apache is by no means meant to be used in any production environment
|
||||
sudo cp /var/www/misp/INSTALL/misp_apache_dev.conf /etc/apache2/sites-available/
|
||||
sudo ln -s /etc/apache2/sites-available/misp_apache_dev.conf /etc/apache2/sites-enabled/
|
||||
sudo service apache2 restart
|
||||
```
|
||||
|
||||
OR
|
||||
|
||||
```bash
|
||||
# NGINX
|
||||
# This configuration is purely meant for local installations for development / testing
|
||||
# Using HTTP on an unhardened apache is by no means meant to be used in any production environment
|
||||
sudo cp /var/www/misp/INSTALL/misp_nginx.conf /etc/nginx/sites-available/
|
||||
sudo ln -s /etc/nginx/sites-available/misp_nginx.conf /etc/nginx/sites-enabled/
|
||||
sudo systemctl disable apache2 # may be required if apache is using port
|
||||
sudo service nginx restart
|
||||
sudo systemctl enable nginx
|
||||
|
||||
```
|
||||
|
||||
Now you can point your browser to: http://localhost:8000
|
||||
|
||||
To log in use the default credentials below:
|
||||
|
||||
- Username: admin
|
||||
- Password: Password1234
|
|
@ -1 +0,0 @@
|
|||
../docs/INSTALL.rhel7.md
|
3720
INSTALL/INSTALL.sh
3720
INSTALL/INSTALL.sh
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +0,0 @@
|
|||
; Generated by RHash v1.4.2 on 2022-05-23 at 12:45.34
|
||||
; Written by Kravchenko Aleksey (Akademgorodok) - http://rhash.sf.net/
|
||||
;
|
||||
; 160126 12:45.34 2022-05-23 INSTALL.sh
|
||||
INSTALL.sh 4296D40B11B3002DF3FDFD69A508ED5ECACB8C13 D32E5A4B0F37F4C937CD4F85927E998D917BCBE89E4E0E864FFD7EA09E29ADEF BD093D8018C351E3D3722646E269C4B60E6DA19F42150338CE6FD72FEE293B8B89AA69D48A84B19D3EFDDAE25EC9E646 ECACC3071E130058C3DDECC86E1CBF27DD4F11389D10F43B14293B1915F7A24F02D0DA51E299706A38C00F2D2A7505B0FE46E33B705E53594383CE65461F2B08
|
|
@ -1 +0,0 @@
|
|||
4296d40b11b3002df3fdfd69a508ed5ecacb8c13 INSTALL.sh
|
|
@ -1 +0,0 @@
|
|||
d32e5a4b0f37f4c937cd4f85927e998d917bcbe89e4e0e864ffd7ea09e29adef INSTALL.sh
|
|
@ -1 +0,0 @@
|
|||
bd093d8018c351e3d3722646e269c4b60e6da19f42150338ce6fd72fee293b8b89aa69d48a84b19d3efddae25ec9e646 INSTALL.sh
|
|
@ -1 +0,0 @@
|
|||
ecacc3071e130058c3ddecc86e1cbf27dd4f11389d10f43b14293b1915f7a24f02d0da51e299706a38c00f2d2a7505b0fe46e33b705e53594383ce65461f2b08 INSTALL.sh
|
|
@ -1,976 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
############################################################
|
||||
###### #
|
||||
##### Please AutoGenerated... ##
|
||||
#### Do NOT was ###
|
||||
### Manually It ####
|
||||
## Change this Script... #####
|
||||
# ######
|
||||
############################################################
|
||||
############################################################
|
||||
#INSTALLATION INSTRUCTIONS #
|
||||
##########################################################
|
||||
#------------------------- for Linux Distributions
|
||||
#
|
||||
#-------------------------------------------------------|
|
||||
# 0/ Quick MISP Instance on Debian Based Linux - Status |
|
||||
#-------------------------------------------------------|
|
||||
#
|
||||
# 20210406: CentOS 7.9 tested and working. -- sCl
|
||||
# 20210406: CentOS 8 tested and working. -- sCl
|
||||
# 20210406: CentOS Stream tested and working. -- sCl
|
||||
# 20210406: Ubuntu 21.04 tested and working. -- sCl
|
||||
# 20210406: Ubuntu 20.04.2 tested and working. -- sCl
|
||||
# 20210406: Ubuntu 18.04.5 tested and working. -- sCl
|
||||
# 20220303: Kali Linux 2022.1 tested and working. -- sCl
|
||||
#
|
||||
#
|
||||
#-------------------------------------------------------------------------------------------------|
|
||||
# 1/ For other Debian based Linux distributions, download script and run as **unprivileged** user |
|
||||
#-------------------------------------------------------------------------------------------------|
|
||||
#
|
||||
# The following installs only MISP Core:
|
||||
# $ wget --no-cache -O /tmp/INSTALL.sh https://raw.githubusercontent.com/MISP/MISP/2.4/INSTALL/INSTALL.sh ; bash /tmp/INSTALL.sh -c
|
||||
#
|
||||
# This will install MISP Core and misp-modules
|
||||
# $ wget --no-cache -O /tmp/INSTALL.sh https://raw.githubusercontent.com/MISP/MISP/2.4/INSTALL/INSTALL.sh ; bash /tmp/INSTALL.sh -c -M
|
||||
#
|
||||
#
|
||||
#-------------------------------------------------------|
|
||||
# 2/ For Kali, download and run Installer Script |
|
||||
#-------------------------------------------------------|
|
||||
#
|
||||
# To install MISP on Kali copy paste the following to your shell:
|
||||
# # wget --no-cache -O /tmp/misp-kali.sh https://raw.githubusercontent.com/MISP/MISP/2.4/INSTALL/INSTALL.sh && bash /tmp/misp-kali.sh
|
||||
# NO other version then 2022.x supported, kthxbai.
|
||||
# /!\ Please read the installer script before randomly doing the above.
|
||||
# The script is tested on a plain vanilla Kali Linux Boot CD and installs quite a few dependencies.
|
||||
#
|
||||
#
|
||||
#----------------------------------------------------------|
|
||||
# 3/ The following script has been partially autogenerated |
|
||||
#----------------------------------------------------------|
|
||||
#
|
||||
# To generate this script yourself, the following steps need to be taken.
|
||||
# $ git clone https://github.com/SteveClement/xsnippet.git
|
||||
# Make sure xsnippet resides somewhere in your $PATH - It is a shell script so a simple, copy to somewhere sane is enough.
|
||||
# $ git clone https://github.com/MISP/MISP.git
|
||||
# $ cd MISP/INSTALL ; ./INSTALL.tpl.sh
|
||||
#
|
||||
##
|
||||
###
|
||||
####----------------\
|
||||
## Developer Note |
|
||||
####--------------------------------------------------------------------------------------------------|
|
||||
## In theory the order does not matter as everything is a self-contained function. |
|
||||
# That said, ideally leave the order as is and do NOT change the lines as they are place-holders. |
|
||||
# Script files that do NOT have a #_name.sh are scripts that have NO functions. This is by design. |
|
||||
#-----------------------------------------------------------------------------------------------------|
|
||||
#
|
||||
# ToC #
|
||||
#
|
||||
#### BEGIN AUTOMATED SECTION ####
|
||||
#
|
||||
## 0_global-vars.sh ##
|
||||
## 0_support-functions.sh ##
|
||||
## 0_apt-upgrade.sh ##
|
||||
## 0_sudoKeeper.sh ##
|
||||
## 0_installCoreDeps.sh ##
|
||||
## 0_upgradePhp74.sh ##
|
||||
## 0_installDepsPhp74.sh ##
|
||||
## 0_installDepsPhp73.sh ##
|
||||
## 0_installDepsPhp72.sh ##
|
||||
## 0_installDepsPhp70.sh ##
|
||||
## 1_prepareDB.sh ##
|
||||
## 1_apacheConfig.sh ##
|
||||
## 1_mispCoreInstall.sh ##
|
||||
## 1_installCake.sh ##
|
||||
## 2_permissions.sh ##
|
||||
## 2_configMISP.sh ##
|
||||
## 2_core-cake.sh ##
|
||||
## 2_gnupg.sh ##
|
||||
## 2_logRotation.sh ##
|
||||
## 2_backgroundWorkers.sh ##
|
||||
## 3_misp-modules.sh ##
|
||||
## 3_misp-modules-cake.sh ##
|
||||
## 4_misp-dashboard.sh ##
|
||||
## 4_misp-dashboard-cake.sh ##
|
||||
## 5_mail_to_misp.sh ##
|
||||
## 6_ssdeep.sh ##
|
||||
## 6_viper.sh ##
|
||||
|
||||
## 0_RHEL_register.sh ##
|
||||
## 0_RHEL7_SCL.sh ##
|
||||
## 0_RHEL8_SCL.sh ##
|
||||
## 0_RHEL7_EPEL.sh ##
|
||||
## 0_CentOS_EPEL.sh ##
|
||||
## 0_EPEL_REMI.sh ##
|
||||
## 0_yumInstallCoreDeps7.sh ##
|
||||
## 0_yumInstallCoreDeps8.sh ##
|
||||
## 0_yumInstallHaveged.sh ##
|
||||
## 1_mispCoreInstall_RHEL7.sh ##
|
||||
## 1_mispCoreInstall_RHEL8.sh ##
|
||||
## 1_installCake_RHEL.sh ##
|
||||
## 1_prepareDB_RHEL.sh ##
|
||||
## 1_apacheConfig_RHEL7.sh ##
|
||||
## 1_apacheConfig_RHEL8.sh ##
|
||||
## 1_firewall_RHEL.sh ##
|
||||
## 2_permissions_RHEL7.sh ##
|
||||
## 2_permissions_RHEL8.sh ##
|
||||
## 2_logRotation_RHEL.sh ##
|
||||
## 2_configMISP_RHEL.sh ##
|
||||
## 3_configWorkers_RHEL.sh ##
|
||||
## 3_misp-modules_RHEL.sh ##
|
||||
|
||||
# No functions scripts:
|
||||
## apt-upgrade.sh ##
|
||||
## postfix.sh ##
|
||||
## interfaces.sh ##
|
||||
#
|
||||
### END AUTOMATED SECTION ###
|
||||
|
||||
# This function will generate the main installer.
|
||||
# It is a helper function for the maintainers of the installer.
|
||||
|
||||
colors () {
|
||||
# Some colors for easier debug and better UX (not colorblind compatible, PR welcome)
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
LBLUE='\033[1;34m'
|
||||
YELLOW='\033[0;33m'
|
||||
HIDDEN='\e[8m'
|
||||
NC='\033[0m'
|
||||
}
|
||||
|
||||
generateInstaller () {
|
||||
if [[ ! -f $(which xsnippet) ]]; then
|
||||
echo 'xsnippet is NOT installed. Clone the repository below and copy the xsnippet shell script somehwere in your $PATH'
|
||||
echo "git clone https://github.com/SteveClement/xsnippet.git"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$(echo $0 |grep -e '^\.\/')" != "./INSTALL.tpl.sh" ]]; then
|
||||
echo -e "${RED}iAmError!${NC}"
|
||||
echo -e "To generate the installer call it with './INSTALL.tpl.sh' otherwise things will break."
|
||||
echo -e "You called: ${RED}$0${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir installer ; cd installer
|
||||
cp ../INSTALL.tpl.sh .
|
||||
|
||||
# Pull code snippets out of Main Install Documents
|
||||
for f in `echo INSTALL.ubuntu2004.md INSTALL.ubuntu1804.md xINSTALL.debian10.md xINSTALL.tsurugi.md INSTALL.rhel7.md INSTALL.rhel8.md`; do
|
||||
xsnippet . ../../docs/${f}
|
||||
done
|
||||
|
||||
# Pull out code snippets from generic Install Documents
|
||||
for f in `echo globalVariables.md mail_to_misp-debian.md MISP_CAKE_init.md misp-dashboard-debian.md misp-dashboard-rhel.md misp-dashboard-cake.md misp-modules-debian.md misp-modules-rhel.md misp-modules-cake.md gnupg.md ssdeep-debian.md sudo_etckeeper.md supportFunctions.md viper-debian.md`; do
|
||||
xsnippet . ../../docs/generic/${f}
|
||||
done
|
||||
|
||||
# TODO: Fix the below.
|
||||
# $ for f in `echo ls [0-9]_*`; do
|
||||
# $ perl -pe 's/## ${f} ##/`cat ${f}`/ge' -i INSTALL.sh
|
||||
# $ done
|
||||
#
|
||||
# Temporary copy/paste holder
|
||||
perl -pe 's/^## 0_global-vars.sh ##/`cat 0_global-vars.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 0_apt-upgrade.sh ##/`cat 0_apt-upgrade.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 0_sudoKeeper.sh ##/`cat 0_sudoKeeper.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 0_installCoreDeps.sh ##/`cat 0_installCoreDeps.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 0_upgradePhp74.sh ##/`cat 0_upgradePhp74.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 0_installDepsPhp74.sh ##/`cat 0_installDepsPhp74.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 0_installDepsPhp73.sh ##/`cat 0_installDepsPhp73.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 0_installDepsPhp72.sh ##/`cat 0_installDepsPhp72.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 0_installDepsPhp70.sh ##/`cat 0_installDepsPhp70.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 1_prepareDB.sh ##/`cat 1_prepareDB.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 1_apacheConfig.sh ##/`cat 1_apacheConfig.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 1_mispCoreInstall.sh ##/`cat 1_mispCoreInstall.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 1_installCake.sh ##/`cat 1_installCake.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 2_permissions.sh ##/`cat 2_permissions.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 2_configMISP.sh ##/`cat 2_configMISP.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 0_support-functions.sh ##/`cat 0_support-functions.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 2_gnupg.sh ##/`cat 2_gnupg.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 2_logRotation.sh ##/`cat 2_logRotation.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 2_backgroundWorkers.sh ##/`cat 2_backgroundWorkers.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 2_core-cake.sh ##/`cat 2_core-cake.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 3_misp-modules-cake.sh ##/`cat 3_misp-modules-cake.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 3_misp-modules.sh ##/`cat 3_misp-modules.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 4_misp-dashboard-cake.sh ##/`cat 4_misp-dashboard-cake.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 4_misp-dashboard.sh ##/`cat 4_misp-dashboard.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 5_mail_to_misp.sh ##/`cat 5_mail_to_misp.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 6_viper.sh ##/`cat 6_viper.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 6_ssdeep.sh ##/`cat 6_ssdeep.sh`/ge' -i INSTALL.tpl.sh
|
||||
|
||||
perl -pe 's/^## 0_RHEL_register.sh ##/`cat 0_RHEL_register.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 0_RHEL7_SCL.sh ##/`cat 0_RHEL7_SCL.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 0_RHEL8_SCL.sh ##/`cat 0_RHEL8_SCL.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 0_CentOS_EPEL.sh ##/`cat 0_CentOS_EPEL.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 0_RHEL7_EPEL.sh ##/`cat 0_RHEL7_EPEL.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 0_yumInstallCoreDeps7.sh ##/`cat 0_yumInstallCoreDeps7.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 0_yumInstallCoreDeps8.sh ##/`cat 0_yumInstallCoreDeps8.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 0_yumInstallHaveged.sh ##/`cat 0_yumInstallHaveged.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 1_mispCoreInstall_RHEL7.sh ##/`cat 1_mispCoreInstall_RHEL7.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 1_mispCoreInstall_RHEL8.sh ##/`cat 1_mispCoreInstall_RHEL8.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 0_EPEL_REMI.sh ##/`cat 0_EPEL_REMI.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 1_installCake_RHEL.sh ##/`cat 1_installCake_RHEL.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 2_permissions_RHEL7.sh ##/`cat 2_permissions_RHEL7.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 2_permissions_RHEL8.sh ##/`cat 2_permissions_RHEL8.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 1_prepareDB_RHEL.sh ##/`cat 1_prepareDB_RHEL.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 1_apacheConfig_RHEL7.sh ##/`cat 1_apacheConfig_RHEL7.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 1_apacheConfig_RHEL8.sh ##/`cat 1_apacheConfig_RHEL8.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 1_firewall_RHEL.sh ##/`cat 1_firewall_RHEL.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 2_logRotation_RHEL.sh ##/`cat 2_logRotation_RHEL.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 2_configMISP_RHEL.sh ##/`cat 2_configMISP_RHEL.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 3_configWorkers_RHEL.sh ##/`cat 3_configWorkers_RHEL.sh`/ge' -i INSTALL.tpl.sh
|
||||
perl -pe 's/^## 3_misp-modules_RHEL.sh ##/`cat 3_misp-modules_RHEL.sh`/ge' -i INSTALL.tpl.sh
|
||||
|
||||
cp INSTALL.tpl.sh ../INSTALL.sh
|
||||
cd ..
|
||||
for ALGO in $(echo "1 256 384 512"); do
|
||||
shasum -a ${ALGO} INSTALL.sh > INSTALL.sh.sha${ALGO}
|
||||
done
|
||||
[[ "$(which rhash > /dev/null 2>&1 ; echo $?)" == "0" ]] && rhash --sfv --sha1 --sha256 --sha384 --sha512 INSTALL.sh > INSTALL.sh.sfv
|
||||
rm -rf installer
|
||||
echo -e "${LBLUE}Generated INSTALL.sh${NC}"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Simple debug function with message
|
||||
|
||||
# Make sure no alias exists
|
||||
[[ $(type -t debug) == "alias" ]] && unalias debug
|
||||
debug () {
|
||||
echo -e "${RED}Next step:${NC} ${GREEN}$1${NC}" > /dev/tty
|
||||
if [[ ! -z ${DEBUG} ]]; then
|
||||
NO_PROGRESS=1
|
||||
echo -e "${RED}Debug Mode${NC}, press ${LBLUE}enter${NC} to continue..." > /dev/tty
|
||||
exec 3>&1
|
||||
read
|
||||
else
|
||||
# [Set up conditional redirection](https://stackoverflow.com/questions/8756535/conditional-redirection-in-bash)
|
||||
#exec 3>&1 &>/dev/null
|
||||
:
|
||||
fi
|
||||
}
|
||||
|
||||
installSupported () {
|
||||
space
|
||||
echo "Proceeding with the installation of MISP core"
|
||||
space
|
||||
|
||||
# Check if sudo is installed and etckeeper - functionLocation('generic/sudo_etckeeper.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && checkSudoKeeper
|
||||
[[ ! -z ${MISP_USER} ]] && [[ ! -f /etc/sudoers.d/misp ]] && echo "%${MISP_USER} ALL=(ALL:ALL) NOPASSWD:ALL" |sudo tee /etc/sudoers.d/misp
|
||||
progress 4
|
||||
|
||||
# Set locale if not set - functionLocation('generic/supportFunctions.md')
|
||||
checkLocale
|
||||
|
||||
# Upgrade system to make sure we install the latest packages - functionLocation('INSTALL.ubuntu1804.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && aptUpgrade
|
||||
progress 4
|
||||
|
||||
# TODO: Double check how the user is added and subsequently used during the install.
|
||||
# TODO: Work on possibility to install as user X and install MISP for user Y
|
||||
# TODO: Check if logout needed. (run SUDO_CMD in installer)
|
||||
# <snippet-begin add-user.sh>
|
||||
# TODO: Double check how to properly handle postfix
|
||||
# <snippet-begin postfix.sh>
|
||||
|
||||
# Pull in all possible MISP Environment variables - functionLocation('generic/globalVariables.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && MISPvars
|
||||
progress 4
|
||||
|
||||
# Check if MISP user is installed and we do not run as root - functionLocation('generic/supportFunctions.md')
|
||||
checkID
|
||||
progress 4
|
||||
|
||||
# Starting friendly UI spinner
|
||||
#spin &
|
||||
#SPIN_PID=$!
|
||||
#disown
|
||||
#trap "kill -9 $SPIN_PID" `seq 0 15`
|
||||
|
||||
# Install Core Dependencies - functionLocation('INSTALL.ubuntu1804.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installCoreDeps
|
||||
progress 4
|
||||
|
||||
if [[ "$1" =~ ^PHP= ]]; then
|
||||
PHP_VER=$(echo $1 |cut -f2 -d=)
|
||||
if [[ "$PHP_VER" == 7.2 ]]; then
|
||||
# Install PHP 7.2 Dependencies - functionLocation('INSTALL.ubuntu1804.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp72
|
||||
elif [[ "$PHP_VER" == 7.3 ]]; then
|
||||
# Install PHP 7.3 Dependencies - functionLocation('generic/supportFunctions.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp73
|
||||
elif [[ "$PHP_VER" == 7.4 ]]; then
|
||||
# Install PHP 7.4 Dependencies - functionLocation('INSTALL.ubuntu2004.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp74
|
||||
elif [[ "$PHP_VER" == 7.0 ]]; then
|
||||
# Install PHP 7.0 Dependencies - functionLocation('generic/supportFunctions.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp70
|
||||
fi
|
||||
else
|
||||
# Install PHP 7.2 Dependencies by dangerous default - functionLocation('INSTALL.ubuntu1804.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp72
|
||||
fi
|
||||
progress 4
|
||||
|
||||
# Install Core MISP - functionLocation('INSTALL.ubuntu1804.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installCore
|
||||
progress 4
|
||||
|
||||
# Install PHP Cake - functionLocation('INSTALL.ubuntu1804.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installCake
|
||||
progress 4
|
||||
|
||||
# Make sure permissions are sane - functionLocation('INSTALL.ubuntu1804.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && permissions
|
||||
progress 4
|
||||
|
||||
# TODO: Mysql install functions, make it upgrade safe, double check
|
||||
# Setup Databse - functionLocation('INSTALL.ubuntu1804.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && prepareDB
|
||||
progress 4
|
||||
|
||||
# Roll Apache Config - functionLocation('INSTALL.ubuntu1804.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && apacheConfig
|
||||
progress 4
|
||||
|
||||
# Setup log logrotate - functionLocation('INSTALL.ubuntu1804.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && logRotation
|
||||
progress 4
|
||||
|
||||
# Generate MISP Config files - functionLocation('INSTALL.ubuntu1804.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && configMISP
|
||||
progress 4
|
||||
|
||||
# Generate GnuPG key - functionLocation('generic/gnupg.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && setupGnuPG
|
||||
progress 4
|
||||
|
||||
# Setup and start background workers - functionLocation('INSTALL.ubuntu1804.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && backgroundWorkers
|
||||
progress 4
|
||||
|
||||
# Run cake CLI for the core installation - functionLocation('generic/MISP_CAKE_init.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && coreCAKE
|
||||
progress 4
|
||||
|
||||
# Disable spinner
|
||||
#(kill $SPIN_PID 2>&1) >/dev/null
|
||||
|
||||
# Check if /usr/local/src is writeable by target install user - functionLocation('generic/supportFunctions.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && checkUsrLocalSrc
|
||||
progress 4
|
||||
|
||||
## Resume spinner
|
||||
#spin &
|
||||
#SPIN_PID=$!
|
||||
#disown
|
||||
#trap "kill -9 $SPIN_PID" `seq 0 15`
|
||||
|
||||
# Install misp-modules - functionLocation('generic/misp-modules-debian.md')
|
||||
[[ -n $MODULES ]] || [[ -n $ALL ]] && mispmodules
|
||||
progress 4
|
||||
|
||||
# Update Galaxies, Template Objects, Warning Lists, Notice Lists, Taxonomies - functionLocation('generic/MISP_CAKE_init.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && updateGOWNT
|
||||
progress 4
|
||||
|
||||
# Install misp-modules - functionLocation('generic/misp-modules-cake.md')
|
||||
[[ -n $MODULES ]] || [[ -n $ALL ]] && modulesCAKE
|
||||
progress 4
|
||||
|
||||
# Install Viper - functionLocation('generic/viper-debian.md')
|
||||
## FIXME: The current state of Viper is broken, disabling any use.
|
||||
##[[ -n $VIPER ]] || [[ -n $ALL ]] && viper
|
||||
##progress 4
|
||||
|
||||
# Install ssdeep - functionLocation('generic/ssdeep-debian.md')
|
||||
[[ -n $SSDEEP ]] || [[ -n $ALL ]] && ssdeep
|
||||
progress 4
|
||||
|
||||
# Install misp-dashboard - functionLocation('generic/misp-dashboard-debian.md')
|
||||
## FIXME: The current state of misp-dashboard is broken, disabling any use.
|
||||
##[[ -n $DASHBOARD ]] || [[ -n $ALL ]] && mispDashboard ; dashboardCAKE
|
||||
##progress 4
|
||||
|
||||
# Install Mail2MISP - functionLocation('generic/mail_to_misp-debian.md')
|
||||
[[ -n $MAIL2 ]] || [[ -n $ALL ]] && mail2misp
|
||||
progress 2
|
||||
|
||||
# Run tests
|
||||
runTests
|
||||
progress 2
|
||||
|
||||
# Run final script to inform the User what happened - functionLocation('generic/supportFunctions.md')
|
||||
theEnd
|
||||
}
|
||||
|
||||
# Main Kali Install function
|
||||
installMISPonKali () {
|
||||
# Kali might have a bug on installs where libc6 is not up to date, this forces bash and libc to update - functionLocation('generic/supportFunctions.md')
|
||||
kaliUpgrade
|
||||
|
||||
# Set locale if not set - functionLocation('generic/supportFunctions.md')
|
||||
checkLocale
|
||||
|
||||
# Set Base URL - functionLocation('generic/supportFunctions.md')
|
||||
setBaseURL
|
||||
|
||||
# Install PHP 7.4 (only php8.1 is available on latest Kali) - functionLocation('supportFunctions.md')
|
||||
installDepsKaliPhp74
|
||||
|
||||
# Install PHP 7.4 Dependencies - functionLocation('INSTALL.ubuntu2004.md')
|
||||
installDepsPhp74
|
||||
|
||||
# Set custom Kali only variables and tweaks
|
||||
space
|
||||
# The following disables sleep on kali/gnome
|
||||
### FIXME: Disabling for now, maybe source of some issues.
|
||||
##disableSleep
|
||||
##debug "Sleeping 3 seconds to make sure the disable sleep does not confuse the execution of the script."
|
||||
##sleep 3
|
||||
|
||||
# Kali specific dependencies - functionLocation('generic/supportFunctions.md')
|
||||
debug "Installing dependencies"
|
||||
installDeps
|
||||
|
||||
# Install Core Dependencies - functionLocation('INSTALL.ubuntu1804.md')
|
||||
installCoreDeps
|
||||
|
||||
debug "Enabling redis and gnupg modules"
|
||||
sudo phpenmod -v 7.4 redis
|
||||
sudo phpenmod -v 7.4 gnupg
|
||||
|
||||
debug "Apache2 ops: dismod: status - dissite: 000-default enmod: ssl rewrite headers php7.3 ensite: default-ssl"
|
||||
sudo a2dismod status
|
||||
sudo a2enmod ssl rewrite headers php7.4
|
||||
sudo a2dissite 000-default
|
||||
sudo a2ensite default-ssl
|
||||
|
||||
debug "Restarting mysql.service"
|
||||
sudo systemctl restart mysql.service
|
||||
|
||||
debug "git clone, submodule update everything"
|
||||
sudo mkdir ${PATH_TO_MISP}
|
||||
sudo chown ${WWW_USER}:${WWW_USER} ${PATH_TO_MISP}
|
||||
cd ${PATH_TO_MISP}
|
||||
false; while [[ $? -ne 0 ]]; do ${SUDO_WWW} git clone https://github.com/MISP/MISP.git ${PATH_TO_MISP}; done
|
||||
|
||||
${SUDO_WWW} git config core.filemode false
|
||||
|
||||
cd ${PATH_TO_MISP}
|
||||
false; while [[ $? -ne 0 ]]; do ${SUDO_WWW} git submodule update --progress --init --recursive; done
|
||||
# Make git ignore filesystem permission differences for submodules
|
||||
${SUDO_WWW} git submodule foreach --recursive git config core.filemode false
|
||||
|
||||
sudo mkdir /var/www/.cache/
|
||||
|
||||
MISP_USER_HOME=$(sudo -Hiu ${MISP_USER} env | grep HOME |cut -f 2 -d=)
|
||||
sudo mkdir ${MISP_USER_HOME}/.cache
|
||||
sudo chown ${MISP_USER}:${MISP_USER} ${MISP_USER_HOME}/.cache
|
||||
sudo chown ${WWW_USER}:${WWW_USER} /var/www/.cache
|
||||
|
||||
## Not really needed...
|
||||
## debug "Generating rc.local"
|
||||
## genRCLOCAL
|
||||
|
||||
debug "Setting up main MISP virtualenv"
|
||||
# Needs virtualenv
|
||||
${SUDO_WWW} virtualenv -p python3 ${PATH_TO_MISP}/venv
|
||||
|
||||
## FIXME: The current stat of misp-dashboard is broken, disabling any use.
|
||||
##debug "Installing MISP dashboard"
|
||||
##mispDashboard
|
||||
|
||||
debug "Installing python-cybox"
|
||||
cd ${PATH_TO_MISP}/app/files/scripts/python-cybox
|
||||
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install .
|
||||
|
||||
debug "Installing python-stix"
|
||||
cd ${PATH_TO_MISP}/app/files/scripts/python-stix
|
||||
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install .
|
||||
|
||||
debug "Installing maec"
|
||||
cd ${PATH_TO_MISP}/app/files/scripts/python-maec
|
||||
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install .
|
||||
|
||||
# Install misp-stix
|
||||
debug "Installing misp-stix"
|
||||
cd ${PATH_TO_MISP}/app/files/scripts/misp-stix
|
||||
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install .
|
||||
|
||||
debug "Installing mixbox"
|
||||
cd ${PATH_TO_MISP}/app/files/scripts/mixbox
|
||||
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install .
|
||||
|
||||
# install PyMISP
|
||||
debug "Installing PyMISP"
|
||||
cd ${PATH_TO_MISP}/PyMISP
|
||||
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install .
|
||||
|
||||
# install pydeep
|
||||
false; while [[ $? -ne 0 ]]; do ${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install git+https://github.com/kbandla/pydeep.git; done
|
||||
|
||||
# install lief
|
||||
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install lief
|
||||
|
||||
# install python-magic
|
||||
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install python-magic
|
||||
|
||||
# install plyara
|
||||
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install plyara
|
||||
|
||||
# install zmq needed by mispzmq
|
||||
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install zmq
|
||||
|
||||
debug "Installing cake"
|
||||
composer74
|
||||
|
||||
${SUDO_WWW} cp -fa ${PATH_TO_MISP}/INSTALL/setup/config.php ${PATH_TO_MISP}/app/Plugin/CakeResque/Config/config.php
|
||||
|
||||
sudo chown -R ${WWW_USER}:${WWW_USER} ${PATH_TO_MISP}
|
||||
sudo chmod -R 750 ${PATH_TO_MISP}
|
||||
sudo chmod -R g+ws ${PATH_TO_MISP}/app/tmp
|
||||
sudo chmod -R g+ws ${PATH_TO_MISP}/app/files
|
||||
sudo chmod -R g+ws ${PATH_TO_MISP}/app/files/scripts/tmp
|
||||
|
||||
debug "Setting up database"
|
||||
if [[ ! -e /var/lib/mysql/misp/users.ibd ]]; then
|
||||
# Kill the anonymous users
|
||||
sudo mysql -h ${DBHOST} -e "DROP USER IF EXISTS ''@'localhost'"
|
||||
# Because our hostname varies we'll use some Bash magic here.
|
||||
sudo mysql -h ${DBHOST} -e "DROP USER IF EXISTS ''@'$(hostname)'"
|
||||
# Kill off the demo database
|
||||
sudo mysql -h ${DBHOST} -e "DROP DATABASE IF EXISTS test"
|
||||
# No root remote logins
|
||||
sudo mysql -h ${DBHOST} -e "DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1')"
|
||||
# Make sure that NOBODY can access the server without a password
|
||||
sudo mysqladmin -h ${DBHOST} -u "${DBUSER_ADMIN}" password "${DBPASSWORD_ADMIN}"
|
||||
# Make our changes take effect
|
||||
sudo mysql -h ${DBHOST} -e "FLUSH PRIVILEGES"
|
||||
|
||||
sudo mysql -u ${DBUSER_ADMIN} -p${DBPASSWORD_ADMIN} -e "CREATE DATABASE ${DBNAME};"
|
||||
sudo mysql -u ${DBUSER_ADMIN} -p${DBPASSWORD_ADMIN} -e "GRANT USAGE ON *.* TO ${DBUSER_MISP}@localhost IDENTIFIED BY '${DBPASSWORD_MISP}';"
|
||||
sudo mysql -u ${DBUSER_ADMIN} -p${DBPASSWORD_ADMIN} -e "GRANT ALL PRIVILEGES ON ${DBNAME}.* TO '${DBUSER_MISP}'@'localhost';"
|
||||
sudo mysql -u ${DBUSER_ADMIN} -p${DBPASSWORD_ADMIN} -e "FLUSH PRIVILEGES;"
|
||||
|
||||
enableServices
|
||||
|
||||
debug "Populating database"
|
||||
${SUDO_WWW} cat ${PATH_TO_MISP}/INSTALL/MYSQL.sql | mysql -u ${DBUSER_MISP} -p${DBPASSWORD_MISP} ${DBNAME}
|
||||
|
||||
echo "<?php
|
||||
class DATABASE_CONFIG {
|
||||
public \$default = array(
|
||||
'datasource' => 'Database/Mysql',
|
||||
//'datasource' => 'Database/Postgres',
|
||||
'persistent' => false,
|
||||
'host' => '$DBHOST',
|
||||
'login' => '$DBUSER_MISP',
|
||||
'port' => 3306, // MySQL & MariaDB
|
||||
//'port' => 5432, // PostgreSQL
|
||||
'password' => '$DBPASSWORD_MISP',
|
||||
'database' => '$DBNAME',
|
||||
'prefix' => '',
|
||||
'encoding' => 'utf8',
|
||||
);
|
||||
}" | ${SUDO_WWW} tee ${PATH_TO_MISP}/app/Config/database.php
|
||||
else
|
||||
echo "There might be a database already existing here: /var/lib/mysql/misp/users.ibd"
|
||||
echo "Skipping any creations…"
|
||||
sleep 3
|
||||
fi
|
||||
|
||||
debug "Generating Certificate"
|
||||
sudo openssl req -newkey rsa:4096 -days 365 -nodes -x509 \
|
||||
-subj "/C=${OPENSSL_C}/ST=${OPENSSL_ST}/L=${OPENSSL_L}/O=${OPENSSL_O}/OU=${OPENSSL_OU}/CN=${OPENSSL_CN}/emailAddress=${OPENSSL_EMAILADDRESS}" \
|
||||
-keyout /etc/ssl/private/misp.local.key -out /etc/ssl/private/misp.local.crt
|
||||
|
||||
debug "Generating Apache Conf"
|
||||
genApacheConf
|
||||
|
||||
echo "127.0.0.1 misp.local" | sudo tee -a /etc/hosts
|
||||
|
||||
debug "Disabling site default-ssl, enabling misp-ssl"
|
||||
sudo a2dissite default-ssl
|
||||
sudo a2ensite misp-ssl
|
||||
|
||||
for key in upload_max_filesize post_max_size max_execution_time max_input_time memory_limit
|
||||
do
|
||||
sudo sed -i "s/^\($key\).*/\1 = $(eval echo \${$key})/" ${PHP_INI}
|
||||
done
|
||||
|
||||
debug "Restarting Apache2"
|
||||
sudo systemctl restart apache2
|
||||
|
||||
debug "Setting up logrotate"
|
||||
sudo cp ${PATH_TO_MISP}/INSTALL/misp.logrotate /etc/logrotate.d/misp
|
||||
sudo chmod 0640 /etc/logrotate.d/misp
|
||||
|
||||
${SUDO_WWW} cp -a ${PATH_TO_MISP}/app/Config/bootstrap.default.php ${PATH_TO_MISP}/app/Config/bootstrap.php
|
||||
${SUDO_WWW} cp -a ${PATH_TO_MISP}/app/Config/core.default.php ${PATH_TO_MISP}/app/Config/core.php
|
||||
${SUDO_WWW} cp -a ${PATH_TO_MISP}/app/Config/config.default.php ${PATH_TO_MISP}/app/Config/config.php
|
||||
|
||||
sudo chown -R ${WWW_USER}:${WWW_USER} ${PATH_TO_MISP}/app/Config
|
||||
sudo chmod -R 750 ${PATH_TO_MISP}/app/Config
|
||||
|
||||
debug "Setting up GnuPG"
|
||||
setupGnuPG
|
||||
|
||||
debug "Adding workers to systemd"
|
||||
backgroundWorkers
|
||||
|
||||
debug "Running Core Cake commands"
|
||||
coreCAKE
|
||||
|
||||
## FIXME: The current state of misp-dashboard is broken, disabling any use.
|
||||
##dashboardCAKE
|
||||
|
||||
debug "Update: Galaxies, Template Objects, Warning Lists, Notice Lists, Taxonomies"
|
||||
updateGOWNT
|
||||
|
||||
# This is not needed atm...
|
||||
##gitPullAllRCLOCAL
|
||||
|
||||
checkUsrLocalSrc
|
||||
|
||||
debug "Installing misp-modules"
|
||||
mispmodules
|
||||
modulesCAKE
|
||||
|
||||
## FIXME: The current state of Viper is broken, disabling any use.
|
||||
##debug "Installing Viper"
|
||||
##viper
|
||||
|
||||
debug "Installing ssdeep"
|
||||
ssdeep
|
||||
sudo phpenmod -v 7.3 ssdeep
|
||||
|
||||
debug "Setting permissions"
|
||||
permissions
|
||||
|
||||
debug "Running Then End!"
|
||||
theEnd
|
||||
}
|
||||
# End installMISPonKali ()
|
||||
|
||||
# Main Install on RHEL function
|
||||
installMISPRHEL () {
|
||||
if [[ -n $SSDEEP ]] || [[ -n $MAIL2 ]]; then
|
||||
echo "RHEL installation currently only supports Core and Modules"
|
||||
echo "Ignoring other options..."
|
||||
fi
|
||||
|
||||
if [[ -n $CORE ]] || [[ -n $ALL ]]; then
|
||||
space
|
||||
echo "Proceeding with MISP core installation on RHEL ${dist_version}"
|
||||
space
|
||||
|
||||
id -u "${MISP_USER}" > /dev/null
|
||||
if [[ $? -eq 1 ]]; then
|
||||
debug "Creating MISP user"
|
||||
sudo useradd -G wheel -m "${MISP_USER}"
|
||||
fi
|
||||
|
||||
# Register system if RHEL
|
||||
if [[ "${DISTRI}" =~ ^[rhel].* ]]; then
|
||||
registerRHEL
|
||||
fi
|
||||
|
||||
debug "Enabling Extras Repos (SCL)"
|
||||
if [[ "${DISTRI}" == "rhel7" ]]; then
|
||||
enableReposRHEL7
|
||||
enableEPEL
|
||||
debug "Installing System Dependencies"
|
||||
yumInstallCoreDeps7
|
||||
installEntropyRHEL
|
||||
debug "Installing MISP code"
|
||||
installCoreRHEL7
|
||||
debug "Install Cake PHP"
|
||||
installCake_RHEL
|
||||
debug "Setting File permissions"
|
||||
permissions_RHEL7
|
||||
debug "Preparing Database"
|
||||
prepareDB_RHEL
|
||||
apacheConfig_RHEL7
|
||||
fi
|
||||
|
||||
if [[ "${DISTRI%??}" == "fedora" ]]; then
|
||||
enableREMI_fedora
|
||||
yumInstallCoreDeps8
|
||||
installEntropyRHEL
|
||||
installCoreRHEL8
|
||||
installCake_RHEL
|
||||
permissions_RHEL8
|
||||
prepareDB_RHEL
|
||||
debug "Configuring Apache"
|
||||
apacheConfig_RHEL8
|
||||
fi
|
||||
|
||||
if [[ "${DIST_VER}" =~ ^[8].* ]]; then
|
||||
enableEPEL_REMI_8
|
||||
enableOptionalRHEL8
|
||||
yumInstallCoreDeps8
|
||||
installCoreRHEL8
|
||||
installCake_RHEL
|
||||
permissions_RHEL8
|
||||
prepareDB_RHEL
|
||||
apacheConfig_RHEL8
|
||||
fi
|
||||
|
||||
if [[ "${DISTRI}" == "centos7" ]]; then
|
||||
centosEPEL
|
||||
debug "Installing MISP code"
|
||||
debug "Installing System Dependencies"
|
||||
yumInstallCoreDeps7
|
||||
installEntropyRHEL
|
||||
installCoreRHEL7
|
||||
debug "Install Cake PHP"
|
||||
installCake_RHEL
|
||||
debug "Setting File permissions"
|
||||
permissions_RHEL7
|
||||
debug "Preparing Database"
|
||||
prepareDB_RHEL
|
||||
debug "Configuring Apache"
|
||||
apacheConfig_RHEL7
|
||||
fi
|
||||
|
||||
debug "Enabling Haveged for additional entropy"
|
||||
sudo yum install haveged -y
|
||||
sudo systemctl enable --now haveged.service
|
||||
|
||||
|
||||
debug "Setting up firewall"
|
||||
firewall_RHEL
|
||||
|
||||
debug "Enabling log rotation"
|
||||
logRotation_RHEL
|
||||
|
||||
debug "Configuring MISP"
|
||||
configMISP_RHEL
|
||||
|
||||
debug "Setting up background workers"
|
||||
configWorkersRHEL
|
||||
|
||||
debug "Optimizing Cake Installation"
|
||||
coreCAKE
|
||||
|
||||
debug "Updating tables"
|
||||
updateGOWNT
|
||||
|
||||
echo "Core Intallation finished, check on port 443 to see the Web UI"
|
||||
fi
|
||||
|
||||
if [[ -n $MODULES ]] || [[ -n $ALL ]]; then
|
||||
space
|
||||
echo "Installing MISP Modules"
|
||||
space
|
||||
|
||||
mispmodulesRHEL
|
||||
# Another sleep to avoid RC
|
||||
sleep 3
|
||||
modulesCAKE
|
||||
|
||||
echo "MISP modules installation finished."
|
||||
fi
|
||||
}
|
||||
# End installMISPRHEL ()
|
||||
|
||||
## End Function Section ##
|
||||
|
||||
colors
|
||||
debug "Checking if we are run as the installer template"
|
||||
if [[ "$0" == "./INSTALL.tpl.sh" || "$(echo $0 |grep -o -e 'INSTALL.tpl.sh')" == "INSTALL.tpl.sh" ]]; then
|
||||
generateInstaller
|
||||
fi
|
||||
|
||||
debug "Checking Linux distribution and flavour..."
|
||||
checkFlavour
|
||||
debug "Checking if we are uptodate and checksums match"
|
||||
checkInstaller
|
||||
|
||||
space
|
||||
debug "Setting MISP variables"
|
||||
MISPvars
|
||||
|
||||
debug "Checking for parameters or Unattended Kali Install"
|
||||
if [[ $# -eq 0 && "$0" != "/tmp/misp-kali.sh" ]]; then
|
||||
usage
|
||||
exit
|
||||
else
|
||||
debug "Setting install options with given parameters."
|
||||
# The setOpt/checkOpt function lives in generic/supportFunctions.md
|
||||
setOpt $@
|
||||
checkOpt core && echo "${LBLUE}MISP${NC} ${GREEN}core${NC} selected"
|
||||
checkOpt viper && echo "${GREEN}Viper${NC} selected"
|
||||
checkOpt modules && echo "${LBLUE}MISP${NC} ${GREEN}modules${NC} selected"
|
||||
checkOpt dashboard && echo "${LBLUE}MISP${NC} ${GREEN}dashboard${NC} selected"
|
||||
checkOpt mail2 && echo "${GREEN}Mail 2${NC} ${LBLUE}MISP${NC} selected"
|
||||
checkOpt all && echo "${GREEN}All options${NC} selected"
|
||||
checkOpt pre && echo "${GREEN}Pre-flight checks${NC} selected"
|
||||
checkOpt unattended && echo "${GREEN}unattended${NC} install selected"
|
||||
checkOpt upgrade && echo "${GREEN}upgrade${NC} install selected"
|
||||
checkOpt force && echo "${GREEN}force${NC} install selected"
|
||||
|
||||
# Check if at least core is selected if no other options that do not require core are set
|
||||
if [[ "$CORE" != "1" && "$ALL" != "1" && "$UPGRADE" != "1" && "$PRE" != "1" && "$0" != "/tmp/misp-kali.sh" ]]; then
|
||||
space
|
||||
usage
|
||||
echo "You need to at least select core, or -A to install everything."
|
||||
echo "$0 -c # Is the minima for install options"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Add upgrade option to do upgrade pre flight
|
||||
[[ -n $PRE ]] && preInstall
|
||||
|
||||
[[ -n $UPGRADE ]] && upgrade
|
||||
|
||||
[[ -n $NUKE ]] && nuke && exit
|
||||
|
||||
# TODO: Move support map to top
|
||||
|
||||
SUPPORT_MAP="
|
||||
x86_64-centos-7
|
||||
x86_64-rhel-7
|
||||
x86_64-centos-8
|
||||
x86_64-rhel-8
|
||||
x86_64-fedora-33
|
||||
x86_64-fedora-34
|
||||
x86_64-fedora-35
|
||||
x86_64-debian-stretch
|
||||
x86_64-debian-buster
|
||||
x86_64-ubuntu-bionic
|
||||
x86_64-ubuntu-focal
|
||||
x86_64-ubuntu-hirsute
|
||||
x86_64-ubuntu-jammy
|
||||
x86_64-kali-2021.4
|
||||
x86_64-kali-2022.1
|
||||
x86_64-kali-2022.2
|
||||
x86_64-kali-2022.3
|
||||
x86_64-kali-2022.4
|
||||
armv6l-raspbian-stretch
|
||||
armv7l-raspbian-stretch
|
||||
armv7l-raspbian-buster
|
||||
armv7l-debian-jessie
|
||||
armv7l-debian-stretch
|
||||
armv7l-debian-buster
|
||||
armv7l-ubuntu-bionic
|
||||
armv7l-ubuntu-focal
|
||||
aarch64-ubuntu-focal
|
||||
aarch64-ubuntu-hirsute
|
||||
"
|
||||
|
||||
# Check if we actually support this configuration
|
||||
if ! echo "$SUPPORT_MAP" | grep "$(uname -m)-$FLAVOUR-$dist_version" >/dev/null; then
|
||||
cat >&2 <<-'EOF'
|
||||
Either your platform is not easily detectable or is not supported by this
|
||||
installer script.
|
||||
Please visit the following URL for more detailed installation instructions:
|
||||
https://misp.github.io/MISP/
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If Ubuntu is detected, figure out which release it is and run the according scripts
|
||||
if [[ "${FLAVOUR}" == "ubuntu" ]]; then
|
||||
RELEASE=$(lsb_release -s -r| tr '[:upper:]' '[:lower:]')
|
||||
if [[ "${RELEASE}" == "18.04" ]]; then
|
||||
echo "Install on Ubuntu 18.04 LTS fully supported."
|
||||
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
|
||||
upgradeToPHP74
|
||||
installSupported PHP="7.4" && exit || exit
|
||||
fi
|
||||
if [[ "${RELEASE}" == "20.04" ]]; then
|
||||
echo "Install on Ubuntu 20.04 LTS fully supported."
|
||||
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
|
||||
installSupported PHP="7.4" && exit || exit
|
||||
fi
|
||||
if [[ "${RELEASE}" == "21.04" ]]; then
|
||||
echo "Install on Ubuntu 21.04 LTS fully supported."
|
||||
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
|
||||
installSupported PHP="7.4" && exit || exit
|
||||
fi
|
||||
if [[ "${RELEASE}" == "22.04" ]]; then
|
||||
echo "Install on Ubuntu 22.04 LTS fully supported."
|
||||
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
|
||||
upgradeToPHP74
|
||||
installSupported PHP="7.4" && exit || exit
|
||||
fi
|
||||
if [[ "${RELEASE}" == "18.10" ]]; then
|
||||
echo "Install on Ubuntu 18.10 partially supported, bye."
|
||||
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
|
||||
installSupported && exit || exit
|
||||
fi
|
||||
if [[ "${RELEASE}" == "19.04" ]]; then
|
||||
echo "Install on Ubuntu 19.04 partially supported bye."
|
||||
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
|
||||
installSupported && exit || exit
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${RELEASE}" == "19.10" ]]; then
|
||||
echo "Install on Ubuntu 19.10 not supported, bye"
|
||||
exit 1
|
||||
fi
|
||||
echo "Installation done!"
|
||||
exit
|
||||
fi
|
||||
|
||||
# If Debian/Raspbian is detected, figure out which release it is and run the according scripts
|
||||
if [[ "${FLAVOUR}" == "debian" ]] || [[ "${FLAVOUR}" == "raspbian" ]]; then
|
||||
CODE=$(lsb_release -s -c| tr '[:upper:]' '[:lower:]')
|
||||
if [[ "${CODE}" == "buster" ]]; then
|
||||
echo "Install on Debian testing fully supported."
|
||||
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
|
||||
installSupported PHP=7.3 && exit || exit
|
||||
fi
|
||||
if [[ "${CODE}" == "sid" ]]; then
|
||||
echo "Install on Debian unstable not fully supported."
|
||||
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
|
||||
installSupported PHP=7.3 && exit || exit
|
||||
fi
|
||||
if [[ "${CODE}" == "stretch" ]]; then
|
||||
echo "Install on Debian stable fully supported."
|
||||
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
|
||||
installSupported PHP=7.0 && exit || exit
|
||||
fi
|
||||
echo "Installation done!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# If Tsurugi is detected, figure out which release it is and run the according scripts
|
||||
if [[ "${FLAVOUR}" == "tsurugi" ]]; then
|
||||
CODE=$(lsb_release -s -c| tr '[:upper:]' '[:lower:]')
|
||||
if [[ "${CODE}" == "bamboo" ]]; then
|
||||
echo "Install on Tsurugi Lab partially supported."
|
||||
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
|
||||
fi
|
||||
if [[ "${CODE}" == "soy sauce" ]]; then
|
||||
echo "Install on Tsurugi Acquire partially supported."
|
||||
echo "Please report bugs/issues here: https://github.com/MISP/MISP/issues"
|
||||
fi
|
||||
echo "Installation done!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# If Kali Linux is detected, run the acccording scripts
|
||||
if [[ "${FLAVOUR}" == "kali" ]]; then
|
||||
KALI=1
|
||||
kaliOnTheR0ckz
|
||||
installMISPonKali
|
||||
echo "Installation done!"
|
||||
exit
|
||||
fi
|
||||
|
||||
# If RHEL/CentOS is detected, run appropriate script
|
||||
if [[ "${FLAVOUR}" == "rhel" ]] || [[ "${FLAVOUR}" == "centos" ]] || [[ "${FLAVOUR}" == "fedora" ]]; then
|
||||
installMISPRHEL
|
||||
echo "Installation done !"
|
||||
exit
|
||||
fi
|
|
@ -1 +0,0 @@
|
|||
../docs/INSTALL.ubuntu1804.md
|
|
@ -1 +0,0 @@
|
|||
../docs/UPDATE.md
|
|
@ -0,0 +1,18 @@
|
|||
# Upgrade misp
|
||||
|
||||
To upgrade a local misp installation, simply pull the new code from the remote `main` branch:
|
||||
|
||||
```bash
|
||||
sudo -u www-data git -C /var/www/misp/ pull origin main
|
||||
```
|
||||
|
||||
If you need to use a proxy, you can pass them to the command like this:
|
||||
|
||||
```bash
|
||||
https_proxy=http://proxy.local:8080 sudo -Eu www-data git -C /var/www/misp/ pull origin main
|
||||
```
|
||||
|
||||
To upgrade the database, login to the webinterface as administrator and call
|
||||
http://misp.local:8000/instance/migrationIndex
|
||||
Also available from the menu in the interface as "Database migration".
|
||||
Run all available upgrades.
|
|
@ -1,3 +0,0 @@
|
|||
-- Patch to add column in servers table in order to save 'publish_without_email'
|
||||
|
||||
ALTER TABLE `servers` ADD `publish_without_email` tinyint(1) NOT NULL DEFAULT 0;
|
|
@ -1,3 +0,0 @@
|
|||
-- Patch to add column in servers table in order to save 'unpublish_event'
|
||||
|
||||
ALTER TABLE `servers` ADD `unpublish_event` tinyint(1) NOT NULL DEFAULT 0;
|
|
@ -1,3 +0,0 @@
|
|||
-- Patch to add column in users table in order to save x509 certificate
|
||||
|
||||
ALTER TABLE `users` ADD `certif_public` longtext COLLATE utf8_bin NOT NULL AFTER `gpgkey`;
|
|
@ -1 +0,0 @@
|
|||
../docs/xINSTALL.OpenBSD.md
|
|
@ -1 +0,0 @@
|
|||
../docs/xINSTALL.centos7.md
|
10
LICENSE
10
LICENSE
|
@ -1,7 +1,7 @@
|
|||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
@ -633,8 +633,8 @@ the "copyright" line and a pointer to where the full notice is found.
|
|||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
|
@ -643,7 +643,7 @@ the "copyright" line and a pointer to where the full notice is found.
|
|||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
|
@ -658,4 +658,4 @@ specific requirements.
|
|||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
17
Pipfile
17
Pipfile
|
@ -1,17 +0,0 @@
|
|||
[[source]]
|
||||
name = "pypi"
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[packages]
|
||||
mixbox = "*"
|
||||
cybox = "*"
|
||||
stix = "*"
|
||||
maec = "*"
|
||||
pymisp = {path = "./PyMISP", extras=["fileobjects"]}
|
||||
stix2 = {git = "https://github.com/MISP/cti-python-stix2", ref = "61e9fc0748691f6b768acf47c18ef01b5dc0a854"}
|
||||
pydeep2 = "*"
|
||||
python-dateutil = "*"
|
||||
urllib3 = ">=1.26.5"
|
1
PyMISP
1
PyMISP
|
@ -1 +0,0 @@
|
|||
Subproject commit 9a7adb2e0d60d2edee9f541db808652875bae20e
|
135
README.md
135
README.md
|
@ -1,123 +1,62 @@
|
|||
MISP - Threat Intelligence Sharing Platform
|
||||
-------------------------------------------
|
||||
# cerebrate
|
||||
|
||||
![logo](./INSTALL/logos/misp-logo.png?raw=true "MISP")
|
||||
Cerebrate is an [open-source platform](https://github.com/cerebrate-project) meant to act as a trusted contact information provider and interconnection orchestrator for other security tools (such as [MISP](https://www.misp-project.org/)).
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>Latest Release</td>
|
||||
<td><a href="https://badge.fury.io/gh/MISP%2FMISP"><img src="https://badge.fury.io/gh/MISP%2FMISP.svg" alt="GitHub version" height="18"></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CI Action</td>
|
||||
<td><a href="https://github.com/MISP/MISP/actions?query=workflow%3Amisp"><img src="https://github.com/MISP/MISP/workflows/misp/badge.svg" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Gitter</td>
|
||||
<td><a href="https://gitter.im/MISP/MISP?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"><img src="https://badges.gitter.im/MISP/MISP.svg" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Twitter</td>
|
||||
<td><a href="https://twitter.com/MISPProject"><img src="https://img.shields.io/twitter/follow/MISPProject.svg?style=social&label=Follow" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Localization</td>
|
||||
<td><a href="https://crowdin.com/project/misp"><img src="https://badges.crowdin.net/misp/localized.svg" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Contributors</td>
|
||||
<td><img src="https://img.shields.io/github/contributors/MISP/MISP.svg" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>License</td>
|
||||
<td><img src="https://img.shields.io/github/license/MISP/MISP.svg" /></td>
|
||||
</tr>
|
||||
# Features
|
||||
|
||||
</table>
|
||||
- Advanced repository to manage individuals and organisations;
|
||||
- Key store for public encryption and signing cryptographic keys (e.g. PGP);
|
||||
- Distributed synchronisation model where multiple Cerebrate instances can be interconnected amongst organisations and/or departments;
|
||||
- Management of individuals and their affiliations to each organisations;
|
||||
- Advanced API and CLI to integrate with existing tools (e.g. importing existing directory information);
|
||||
- Dynamic model for creating new organisational structures;
|
||||
- Support existing organisational structures such as [FIRST.org](https://www.first.org/) directory, EU [CSIRTs network](https://csirtsnetwork.eu/);
|
||||
- Local tooling interconnection to easily connect existing tools with their native protocols;
|
||||
|
||||
MISP is an open source software solution for collecting, storing, distributing and sharing cyber security indicators and threats about cyber security incidents analysis and malware analysis. MISP is designed by and for incident analysts, security and ICT professionals or malware reversers to support their day-to-day operations to share structured information efficiently.
|
||||
Cerebrate is developed in the scope of the MeliCERTes v2 project.
|
||||
|
||||
The objective of MISP is to foster the sharing of structured information within the security community and abroad. MISP provides functionalities to support the exchange of information but also the consumption of said information by Network Intrusion Detection Systems (NIDS), LIDS but also log analysis tools, SIEMs.
|
||||
## Screenshots
|
||||
|
||||
MISP, Malware Information Sharing Platform and Threat Sharing, core functionalities are:
|
||||
![Dashboard](https://www.cerebrate-project.org/assets/images/screenshots/Screenshot%20from%202021-10-19%2016-31-56.png)
|
||||
|
||||
- An **efficient IOC and indicators** database allowing to store technical and non-technical information about malware samples, incidents, attackers and intelligence.
|
||||
- Automatic **correlation** finding relationships between attributes and indicators from malware, attack campaigns or analysis. The correlation engine includes correlation between attributes and more advanced correlations like Fuzzy hashing correlation (e.g. ssdeep) or CIDR block matching. Correlation can also be enabled or event disabled per attribute.
|
||||
- A **flexible data model** where complex [objects](https://www.misp-project.org/objects.html) can be expressed and **linked together to express threat intelligence, incidents or connected elements**.
|
||||
- Built-in **sharing functionality** to ease data sharing using different model of distributions. MISP can automatically synchronize events and attributes among different MISP instances. Advanced filtering functionalities can be used to meet each organization's sharing policy including a **flexible sharing group** capacity and an attribute level distribution mechanisms.
|
||||
- An **intuitive user-interface** for end-users to create, update and collaborate on events and attributes/indicators. A **graphical interface** to navigate seamlessly between events and their correlations. An **event graph** functionality to create and view relationships between objects and attributes. Advanced filtering functionalities and [warning lists](https://github.com/MISP/misp-warninglists) to help the analysts to contribute events and attributes and limit the risk of false-positives.
|
||||
- **storing data** in a structured format (allowing automated use of the database for various purposes) with an extensive support of cyber security indicators along fraud indicators as in the financial sector.
|
||||
- **export**: generating IDS, OpenIOC, plain text, CSV, MISP XML or JSON output to integrate with other systems (network IDS, host IDS, custom tools), Cache format (used for forensic tools), STIX (XML and JSON) 1 and 2, NIDS export (Suricata, Snort and Bro/Zeek) or RPZ zone. Many other formats can be easily added via the [misp-modules](https://github.com/MISP/misp-modules).
|
||||
- **import**: bulk-import, batch-import, import from OpenIOC, GFI sandbox, ThreatConnect CSV, MISP standard format or STIX 1.1/2.0. Many other formats easily added via the [misp-modules](https://github.com/MISP/misp-modules).
|
||||
- Flexible **free text import** tool to ease the integration of unstructured reports into MISP.
|
||||
- A gentle system to **collaborate** on events and attributes allowing MISP users to propose changes or updates to attributes/indicators.
|
||||
- **data-sharing**: automatically exchange and synchronize with other parties and trust-groups using MISP.
|
||||
- **delegating of sharing**: allows a simple pseudo-anonymous mechanism to delegate publication of event/indicators to another organization.
|
||||
- Flexible **API** to integrate MISP with your own solutions. MISP is bundled with [PyMISP](https://github.com/MISP/PyMISP) which is a flexible Python Library to fetch, add or update events attributes, handle malware samples or search for attributes. An exhaustive restSearch API to easily search for indicators in MISP and exports those in all the format supported by MISP.
|
||||
- **Adjustable taxonomy** to classify and tag events following your own classification schemes or [existing classification](https://github.com/MISP/misp-taxonomies). The taxonomy can be local to your MISP but also shareable among MISP instances.
|
||||
- **Intelligence vocabularies** called MISP galaxy and bundled with existing [threat actors, malware, RAT, ransomware or MITRE ATT&CK](https://www.misp-project.org/galaxy.html) which can be easily linked with events and attributes in MISP.
|
||||
- **Expansion modules in Python** to expand MISP with your own services or activate already available [misp-modules](https://github.com/MISP/misp-modules).
|
||||
- **Sighting support** to get observations from organizations concerning shared indicators and attributes. Sighting [can be contributed](https://www.circl.lu/doc/misp/automation/index.html#sightings-api) via MISP user-interface, API as MISP document or STIX sighting documents.
|
||||
- **STIX support**: import and export data in the STIX version 1 and version 2 format.
|
||||
- **Integrated encryption and signing of the notifications** via GnuPG and/or S/MIME depending on the user's preferences.
|
||||
- **Real-time** publish-subscribe channel within MISP to automatically get all changes (e.g. new events, indicators, sightings or tagging) in ZMQ (e.g. [misp-dashboard](https://github.com/MISP/misp-dashboard)) or Kafka publishing.
|
||||
List of individuals along with their affiliations
|
||||
|
||||
Exchanging info results in *faster detection* of targeted attacks and improves the detection ratio while reducing the false positives. We also avoid reversing similar malware as we know very fast that other teams or organizations have already analyzed a specific malware.
|
||||
![List of individuals](https://www.cerebrate-project.org/assets/images/screenshots/Screenshot%20from%202021-10-19%2016-32-35.png)
|
||||
|
||||
![MISP 2.4 overview](https://raw.githubusercontent.com/MISP/MISP/2.4/INSTALL/screenshots/misp-panorama.png)
|
||||
Adding organisations
|
||||
|
||||
A sample event encoded in MISP:
|
||||
![Adding an organisation](https://www.cerebrate-project.org/assets/images/screenshots/Screenshot%20from%202021-10-19%2016-33-04.png)
|
||||
|
||||
![MISP event view](./INSTALL/screenshots/event-view.png?raw=true "MISP")
|
||||
Everything is available via the API, here an example of a search query for all international organisations in the DB.
|
||||
|
||||
Website / Support
|
||||
------------------
|
||||
![API query](/documentation/images/orgs_api.png)
|
||||
|
||||
Checkout the [website](https://www.misp-project.org) for more information about MISP software, standards, tools and communities.
|
||||
Managing public keys and assigning them to users both for communication and validating signed information shared in the community
|
||||
|
||||
Information, news and updates are also regularly posted on the [MISP project twitter account](https://twitter.com/MISPProject) or the [news page](https://www.misp-project.org/news/).
|
||||
![Encryption key management](/documentation/images/add_encryption_key.png)
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
Dynamic model for creating new organisation structre
|
||||
|
||||
[MISP user-guide (MISP-book)](https://github.com/MISP/misp-book) is available [online](https://www.circl.lu/doc/misp/) or as [PDF](https://www.circl.lu/doc/misp/book.pdf) or as [EPUB](https://www.circl.lu/doc/misp/book.epub) or as [MOBI/Kindle](https://www.circl.lu/doc/misp/book.mobi).
|
||||
![Meta Field Templates](https://www.cerebrate-project.org/assets/images/screenshots/Screenshot%20from%202021-10-19%2016-38-21.png)
|
||||
|
||||
For the installation guide see the [INSTALL](https://github.com/MISP/MISP/tree/2.4/INSTALL) or [download section](https://www.misp-project.org/download/).
|
||||
# Requirements and installation
|
||||
|
||||
Contributing
|
||||
------------
|
||||
The platform is built on CakePHP 4 along with Bootstrap 4 and shares parts of the code-base with [MISP](https://www.github.com/MISP).
|
||||
|
||||
If you are interested to contribute to the MISP project, review our [contributing page](CONTRIBUTING.md). There are many ways to contribute
|
||||
and participate to the project.
|
||||
The installation is documented at the following location [INSTALL/INSTALL.md](INSTALL/INSTALL.md). For upgrades, look at [INSTALL/UPGRADE.md](INSTALL/UPGRADE.md)
|
||||
|
||||
Please see our [Code of conduct](code_of_conduct.md).
|
||||
Hardware requirements:
|
||||
|
||||
Feel free to fork the code, play with it, make some patches and send us the pull requests via the [issues](https://github.com/MISP/MISP/issues).
|
||||
A webserver with 4GB of memory and a single CPU core should be plenty for the current scope of Cerebrate. This might increase over the time with additional features being added, but the goal is to keep Cerebrate as lean as possible. Expect to have at least 40GB of disk space, depending on your log rotation strategy you might want to go higher.
|
||||
|
||||
Feel free to contact us, create [issues](https://github.com/MISP/MISP/issues), if you have questions, remarks or bug reports.
|
||||
For installation via docker, refer to the [cerebrate-docker](https://github.com/cerebrate-project/cerebrate-docker) repo.
|
||||
|
||||
There is one main branch:
|
||||
# License
|
||||
|
||||
- 2.4 (current stable version): what we consider as stable with frequent updates as hot-fixes.
|
||||
~~~~
|
||||
The software is released under the AGPLv3.
|
||||
|
||||
and features are developed in separated branches and then regularly merged into the 2.4 stable branch.
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
This software is licensed under [GNU Affero General Public License version 3](http://www.gnu.org/licenses/agpl-3.0.html)
|
||||
|
||||
* Copyright (C) 2012-2023 Christophe Vandeplas
|
||||
* Copyright (C) 2012 Belgian Defence
|
||||
* Copyright (C) 2012 NATO / NCIRC
|
||||
* Copyright (C) 2013-2023 Andras Iklody
|
||||
* Copyright (C) 2015-2023 CIRCL - Computer Incident Response Center Luxembourg
|
||||
* Copyright (C) 2016 Andreas Ziegler
|
||||
* Copyright (C) 2018-2023 Sami Mokaddem
|
||||
* Copyright (C) 2018-2023 Christian Studer
|
||||
* Copyright (C) 2015-2023 Alexandre Dulaunoy
|
||||
* Copyright (C) 2018-2022 Steve Clement
|
||||
* Copyright (C) 2020-2023 Jakub Onderka
|
||||
|
||||
For more information, [the list of authors and contributors](AUTHORS) is available.
|
||||
Copyright (C) 2019, 2021 Andras Iklody
|
||||
Copyright (C) 2020-2021 Sami Mokaddem
|
||||
Copyright (C) CIRCL - Computer Incident Response Center Luxembourg
|
||||
~~~~
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
DB changes:
|
||||
|
||||
`alter table audit_logs rename column `change` to `changed`;`
|
||||
`alter table audit_logs rename column `action` to `request_action`;`
|
||||
|
||||
|
||||
|
||||
Other changes:
|
||||
|
||||
change ID fields to bigint for tables where we expect extremely large data sets and/or growing ID spaces (correlations, logs)
|
|
@ -1,5 +0,0 @@
|
|||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine on
|
||||
RewriteRule ^$ webroot/ [L]
|
||||
RewriteRule (.*) webroot/$1 [L]
|
||||
</IfModule>
|
|
@ -1,182 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is loaded automatically by the app/webroot/index.php file after core.php
|
||||
*
|
||||
* This file should load/create any application wide configuration settings, such as
|
||||
* Caching, Logging, loading additional configuration files.
|
||||
*
|
||||
* You should also use this file to include any files that provide global functions/constants
|
||||
* that your application uses.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Cache Engine Configuration
|
||||
* Default settings provided below
|
||||
*
|
||||
* File storage engine.
|
||||
*
|
||||
* Cache::config('default', array(
|
||||
* 'engine' => 'File', //[required]
|
||||
* 'duration'=> 3600, //[optional]
|
||||
* 'probability'=> 100, //[optional]
|
||||
* 'path' => CACHE, //[optional] use system tmp directory - remember to use absolute path
|
||||
* 'prefix' => 'cake_', //[optional] prefix every cache file with this string
|
||||
* 'lock' => false, //[optional] use file locking
|
||||
* 'serialize' => true, // [optional]
|
||||
* 'mask' => 0666, // [optional] permission mask to use when creating cache files
|
||||
* ));
|
||||
*
|
||||
* APC (http://pecl.php.net/package/APC)
|
||||
*
|
||||
* Cache::config('default', array(
|
||||
* 'engine' => 'Apc', //[required]
|
||||
* 'duration'=> 3600, //[optional]
|
||||
* 'probability'=> 100, //[optional]
|
||||
* 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
|
||||
* ));
|
||||
*
|
||||
* Xcache (http://xcache.lighttpd.net/)
|
||||
*
|
||||
* Cache::config('default', array(
|
||||
* 'engine' => 'Xcache', //[required]
|
||||
* 'duration'=> 3600, //[optional]
|
||||
* 'probability'=> 100, //[optional]
|
||||
* 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
|
||||
* 'user' => 'user', //user from xcache.admin.user settings
|
||||
* 'password' => 'password', //plaintext password (xcache.admin.pass)
|
||||
* ));
|
||||
*
|
||||
* Memcache (http://memcached.org/)
|
||||
*
|
||||
* Cache::config('default', array(
|
||||
* 'engine' => 'Memcache', //[required]
|
||||
* 'duration'=> 3600, //[optional]
|
||||
* 'probability'=> 100, //[optional]
|
||||
* 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
|
||||
* 'servers' => array(
|
||||
* '127.0.0.1:11211' // localhost, default port 11211
|
||||
* ), //[optional]
|
||||
* 'persistent' => true, // [optional] set this to false for non-persistent connections
|
||||
* 'compress' => false, // [optional] compress data in Memcache (slower, but uses less memory)
|
||||
* ));
|
||||
*
|
||||
* Wincache (http://php.net/wincache)
|
||||
*
|
||||
* Cache::config('default', array(
|
||||
* 'engine' => 'Wincache', //[required]
|
||||
* 'duration'=> 3600, //[optional]
|
||||
* 'probability'=> 100, //[optional]
|
||||
* 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
|
||||
* ));
|
||||
*
|
||||
* Redis (http://http://redis.io/)
|
||||
*
|
||||
* Cache::config('default', array(
|
||||
* 'engine' => 'Redis', //[required]
|
||||
* 'duration'=> 3600, //[optional]
|
||||
* 'probability'=> 100, //[optional]
|
||||
* 'prefix' => Inflector::slug(APP_DIR) . '_', //[optional] prefix every cache file with this string
|
||||
* 'server' => '127.0.0.1' // localhost
|
||||
* 'port' => 6379 // default port 6379
|
||||
* 'timeout' => 0 // timeout in seconds, 0 = unlimited
|
||||
* 'persistent' => true, // [optional] set this to false for non-persistent connections
|
||||
* ));
|
||||
*/
|
||||
Cache::config('default', array('engine' => 'File'));
|
||||
Configure::load('config');
|
||||
|
||||
$appendPort = true;
|
||||
$relativePaths = false;
|
||||
|
||||
if (!$relativePaths) {
|
||||
if (isset($_SERVER['SERVER_NAME'])) $serverName = $_SERVER['SERVER_NAME'];
|
||||
else if (isset($_SERVER['HTTP_HOST'])) $serverName = $_SERVER['HTTP_HOST'];
|
||||
else if (isset($_SERVER['SERVER_ADDR'])) $serverName = $_SERVER['SERVER_ADDR'];
|
||||
|
||||
if (!Configure::read('MISP.baseurl') && isset($serverName)) {
|
||||
if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443)) {
|
||||
$protocol = 'https';
|
||||
} else {
|
||||
$protocol = 'http';
|
||||
}
|
||||
if (!isset($_SERVER['SERVER_PORT']) || in_array($_SERVER['SERVER_PORT'], array('443', '80')) || !$appendPort) {
|
||||
Configure::write('MISP.baseurl', sprintf($protocol . '://%s', $serverName));
|
||||
} else {
|
||||
Configure::write('MISP.baseurl', sprintf($protocol . '://%s:%d', $serverName, $_SERVER['SERVER_PORT']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure base URL for CakePHP
|
||||
*/
|
||||
if (Configure::read('MISP.baseurl')) {
|
||||
$regex = "%^(?<fullBaseUrl>(?<proto>https?)://(?<host>(?:(?:\w|-)+\.)+[a-z]{2,5})(?::(?<port>[0-9]+))?)(?<base>/[a-z0-9_\-\.]+)?$%i";
|
||||
if (preg_match($regex, Configure::read('MISP.baseurl'), $matches)) {
|
||||
if (isset($matches['base'])) {
|
||||
Configure::write('App.base', $matches['base']);
|
||||
Configure::write('App.fullBaseUrl', $matches['fullBaseUrl']);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Plugins need to be loaded manually, you can either load them one by one or all of them in a single call
|
||||
* Uncomment one of the lines below, as you need. make sure you read the documentation on CakePlugin to use more
|
||||
* advanced ways of loading plugins
|
||||
*
|
||||
* CakePlugin::loadAll(); // Loads all plugins at once
|
||||
* CakePlugin::load('DebugKit'); //Loads a single plugin named DebugKit
|
||||
*
|
||||
*/
|
||||
|
||||
CakePlugin::load('SysLog');
|
||||
CakePlugin::load('Assets'); // having Logable
|
||||
CakePlugin::load('SysLogLogable');
|
||||
|
||||
/**
|
||||
* Uncomment the following line to enable client SSL certificate authentication.
|
||||
* It's also necessary to configure the plugin — for more information, please read app/Plugin/CertAuth/reame.md
|
||||
*/
|
||||
// CakePlugin::load('CertAuth');
|
||||
// CakePlugin::load('ShibbAuth');
|
||||
// CakePlugin::load('LinOTPAuth');
|
||||
/**
|
||||
* You can attach event listeners to the request lifecyle as Dispatcher Filter . By Default CakePHP bundles two filters:
|
||||
*
|
||||
* - AssetDispatcher filter will serve your asset files (css, images, js, etc) from your themes and plugins
|
||||
* - CacheDispatcher filter will read the Cache.check configure variable and try to serve cached content generated from controllers
|
||||
*
|
||||
* Feel free to remove or add filters as you see fit for your application. A few examples:
|
||||
*
|
||||
* Configure::write('Dispatcher.filters', array(
|
||||
* 'MyCacheFilter', // will use MyCacheFilter class from the Routing/Filter package in your app.
|
||||
* 'MyPlugin.MyFilter', // will use MyFilter class from the Routing/Filter package in MyPlugin plugin.
|
||||
* array('callable' => $aFunction, 'on' => 'before', 'priority' => 9), // A valid PHP callback type to be called on beforeDispatch
|
||||
* array('callable' => $anotherMethod, 'on' => 'after'), // A valid PHP callback type to be called on afterDispatch
|
||||
*
|
||||
* ));
|
||||
*/
|
||||
Configure::write('Dispatcher.filters', array(
|
||||
'AssetDispatcher',
|
||||
'CacheDispatcher'
|
||||
));
|
||||
|
||||
/**
|
||||
* Configures default file logging options
|
||||
*/
|
||||
App::uses('CakeLog', 'Log');
|
||||
CakeLog::config('debug', array(
|
||||
'engine' => 'FileLog',
|
||||
'types' => array('notice', 'info', 'debug'),
|
||||
'file' => 'debug',
|
||||
));
|
||||
CakeLog::config('error', array(
|
||||
'engine' => 'FileLog',
|
||||
'types' => array('warning', 'error', 'critical', 'alert', 'emergency'),
|
||||
'file' => 'error',
|
||||
));
|
||||
|
||||
// comment the following out if you do not with to use the background processing (not recommended)
|
||||
CakePlugin::loadAll(array(
|
||||
'CakeResque' => array('bootstrap' => true)
|
||||
));
|
|
@ -1,271 +0,0 @@
|
|||
<?php
|
||||
$config = array(
|
||||
'debug' => 0,
|
||||
'Security' => array(
|
||||
'level' => 'medium',
|
||||
'salt' => '',
|
||||
'cipherSeed' => '',
|
||||
'require_password_confirmation' => true,
|
||||
'auth_enforced' => false,
|
||||
'rest_client_baseurl' => 'https://localhost',
|
||||
'advanced_authkeys' => true,
|
||||
'password_policy_length' => 12,
|
||||
'password_policy_complexity' => '/^((?=.*\\d)|(?=.*\\W+))(?![\\n])(?=.*[A-Z])(?=.*[a-z]).*$|.{16,}/',
|
||||
'self_registration_message' => 'If you would like to send us a registration request, please fill out the form below. Make sure you fill out as much information as possible in order to ease the task of the administrators.',
|
||||
'allow_self_registration' => false,
|
||||
'rest_client_enable_arbitrary_urls' => false,
|
||||
'user_monitoring_enabled' => false,
|
||||
'authkey_keep_session' => false,
|
||||
'disable_local_feed_access' => false,
|
||||
'enable_svg_logos' => false,
|
||||
//'auth' => array('CertAuth.Certificate'), // additional authentication methods
|
||||
//'auth' => array('ShibbAuth.ApacheShibb'),
|
||||
//'auth' => array('AadAuth.AadAuthenticate'),
|
||||
//'auth' => array('LinOTPAuth.LinOTP'),
|
||||
),
|
||||
'MISP' => array(
|
||||
'baseurl' => '',
|
||||
'footermidleft' => '',
|
||||
'footermidright' => '',
|
||||
'org' => 'ORGNAME',
|
||||
'showorg' => true,
|
||||
'threatlevel_in_email_subject' => true,
|
||||
'email_subject_TLP_string' => 'tlp:amber',
|
||||
'email_subject_tag' => 'tlp',
|
||||
'email_subject_include_tag_name' => true,
|
||||
'background_jobs' => true,
|
||||
'osuser' => 'www-data',
|
||||
'email' => 'email@example.com',
|
||||
'contact' => 'email@example.com',
|
||||
'cveurl' => 'https://cve.circl.lu/cve/',
|
||||
'cweurl' => 'https://cve.circl.lu/cwe/',
|
||||
'disablerestalert' => false,
|
||||
'default_event_distribution' => '1',
|
||||
'default_attribute_distribution' => 'event',
|
||||
'tagging' => true,
|
||||
'full_tags_on_event_index' => true,
|
||||
'attribute_tagging' => true,
|
||||
'full_tags_on_attribute_index' => true,
|
||||
'footer_logo' => '',
|
||||
'take_ownership_xml_import' => false,
|
||||
'unpublishedprivate' => false,
|
||||
'disable_emailing' => false,
|
||||
'manage_workers' => true,
|
||||
'python_bin' => null,
|
||||
'external_baseurl' => '',
|
||||
'forceHTTPSforPreLoginRequestedURL' => false,
|
||||
'showCorrelationsOnIndex' => true,
|
||||
'default_event_tag_collection' => 0,
|
||||
'language' => 'eng',
|
||||
'proposals_block_attributes' => false,
|
||||
'redis_host' => '127.0.0.1',
|
||||
'redis_port' => 6379,
|
||||
'redis_database' => 13,
|
||||
'redis_password' => '',
|
||||
'ssdeep_correlation_threshold' => 40,
|
||||
'extended_alert_subject' => false,
|
||||
'default_event_threat_level' => '4',
|
||||
'newUserText' => 'Dear new MISP user,\\n\\nWe would hereby like to welcome you to the $org MISP community.\\n\\n Use the credentials below to log into MISP at $misp, where you will be prompted to manually change your password to something of your own choice.\\n\\nUsername: $username\\nPassword: $password\\n\\nIf you have any questions, don\'t hesitate to contact us at: $contact.\\n\\nBest regards,\\nYour $org MISP support team',
|
||||
'passwordResetText' => 'Dear MISP user,\\n\\nA password reset has been triggered for your account. Use the below provided temporary password to log into MISP at $misp, here you will be prompted to manually change your password to something of your own choice.\\n\\nUsername: $username\\nYour temporary password: $password\\n\\nIf you have any questions, don\'t hesitate to contact us at: $contact.\\n\\nBest regards,\\n Your $org MISP support team',
|
||||
'enableEventBlocklisting' => true,
|
||||
'enableOrgBlocklisting' => true,
|
||||
'log_client_ip' => false,
|
||||
'log_auth' => false,
|
||||
'store_api_access_time' => false,
|
||||
'disableUserSelfManagement' => false,
|
||||
'disable_user_login_change' => false,
|
||||
'disable_user_password_change' => false,
|
||||
'disable_user_add' => false,
|
||||
'block_event_alert' => false,
|
||||
'block_event_alert_tag' => 'no-alerts="true"',
|
||||
'block_old_event_alert' => false,
|
||||
'block_old_event_alert_age' => 0,
|
||||
'block_old_event_alert_by_date' => 0,
|
||||
'incoming_tags_disabled_by_default' => false,
|
||||
'maintenance_message' => 'Great things are happening! MISP is undergoing maintenance, but will return shortly. You can contact the administration at $email.',
|
||||
'welcome_text_top' => 'Initial Install, please configure',
|
||||
'welcome_text_bottom' => '',
|
||||
'attachments_dir' => null,
|
||||
'download_attachments_on_load' => true,
|
||||
'title_text' => 'MISP',
|
||||
'terms_download' => false,
|
||||
'showorgalternate' => false,
|
||||
'event_view_filter_fields' => 'id, uuid, value, comment, type, category, Tag.name',
|
||||
'live' => true,
|
||||
'uuid' => '',
|
||||
'delegation' => true,
|
||||
'max_correlations_per_event' => 5000,
|
||||
'disable_auto_logout' => false,
|
||||
'log_paranoid_skip_db' => false,
|
||||
'log_paranoid' => false,
|
||||
'log_user_ips' => false,
|
||||
'event_alert_republish_ban' => true,
|
||||
'event_alert_republish_ban_threshold' => 120,
|
||||
'event_alert_republish_ban_refresh_on_retry' => true,
|
||||
'user_email_notification_ban' => true,
|
||||
'user_email_notification_ban_time_threshold' => 120,
|
||||
'user_email_notification_ban_amount_threshold' => 10,
|
||||
'user_email_notification_ban_refresh_on_retry' => true,
|
||||
'warning_for_all' => true,
|
||||
'enable_synchronisation_filtering_on_type' => false,
|
||||
),
|
||||
'GnuPG' => array(
|
||||
'onlyencrypted' => false,
|
||||
'email' => '',
|
||||
'homedir' => '',
|
||||
'password' => '',
|
||||
'bodyonlyencrypted' => false,
|
||||
'sign' => true,
|
||||
'obscure_subject' => false,
|
||||
'binary' => '/usr/bin/gpg'
|
||||
),
|
||||
'SMIME' => array(
|
||||
'enabled' => false,
|
||||
'email' => '',
|
||||
'cert_public_sign' => '',
|
||||
'key_sign' => '',
|
||||
'password' => '',
|
||||
),
|
||||
'Proxy' => array(
|
||||
'host' => '',
|
||||
'port' => '',
|
||||
'method' => '',
|
||||
'user' => '',
|
||||
'password' => '',
|
||||
),
|
||||
'SecureAuth' => array(
|
||||
'amount' => 5,
|
||||
'expire' => 300,
|
||||
),
|
||||
'SimpleBackgroundJobs' => array(
|
||||
'enabled' => false,
|
||||
'redis_host' => 'localhost',
|
||||
'redis_port' => 6379,
|
||||
'redis_password' => '',
|
||||
'redis_database' => 1,
|
||||
'redis_namespace' => 'background_jobs',
|
||||
'max_job_history_ttl' => 86400,
|
||||
'supervisor_host' => 'localhost',
|
||||
'supervisor_port' => 9001,
|
||||
'supervisor_user' => 'supervisor',
|
||||
'supervisor_password' => '',
|
||||
),
|
||||
// Uncomment the following to enable client SSL certificate authentication
|
||||
/*
|
||||
'CertAuth' => array(
|
||||
|
||||
// CA
|
||||
'ca' => array('FIRST.Org'), // List of CAs authorized
|
||||
'caId' => 'O', // Certificate field used to verify the CA. In this example, the field O (organization) of the client certificate has to equal to 'FIRST.Org' in order to validate the CA
|
||||
|
||||
// User/client configuration
|
||||
'userModel' => 'User', // name of the User class (MISP class) to check if the user exists
|
||||
'userModelKey' => 'email', // User field that will be used for querying. In this example, the field email of the MISP accounts will be used to search if the user exists.
|
||||
'map' => array( // maps client certificate attributes to User properties. This map will be used as conditions to find if the user exists. In this example, the client certificate fields 'O' (organization) and 'emailAddress' have to match with the MISP fields 'org' and 'email' to validate the user.
|
||||
'O' => 'org',
|
||||
'emailAddress' => 'email',
|
||||
),
|
||||
|
||||
// Synchronization/RestAPI
|
||||
'syncUser' => true, // should the User be synchronized with an external REST API
|
||||
'userDefaults' => array( // default user attributes, only used when creating new users. By default, new users are "Read only" users (role_id: 6).
|
||||
'role_id' => 6,
|
||||
),
|
||||
'restApi' => array( // API parameters
|
||||
'url' => 'https://example.com/data/users', // URL to query
|
||||
'headers' => array(), // additional headers, used for authentication
|
||||
'param' => array('email' => 'email'), // query parameters to add to the URL, mapped to User properties
|
||||
'map' => array( // maps REST result to the User properties
|
||||
'uid' => 'nids_sid',
|
||||
'team' => 'org',
|
||||
'email' => 'email',
|
||||
'pgp_public' => 'gpgkey',
|
||||
),
|
||||
),
|
||||
'userDefaults' => array('role_id' => 6), // default attributes for new users. By default, new users are "Read only" users (role_id: 6).
|
||||
),
|
||||
*/
|
||||
/*
|
||||
'ApacheShibbAuth' => array( // Configuration for shibboleth authentication
|
||||
'apacheEnv' => 'REMOTE_USER', // If proxy variable = HTTP_REMOTE_USER
|
||||
'MailTag' => 'EMAIL_TAG',
|
||||
'OrgTag' => 'FEDERATION_TAG',
|
||||
'GroupTag' => 'GROUP_TAG',
|
||||
'GroupSeparator' => ';',
|
||||
'GroupRoleMatching' => array( // 3:User, 1:admin. May be good to set "1" for the first user
|
||||
'group_three' => 3,
|
||||
'group_two' => 2,
|
||||
'group_one' => 1,
|
||||
),
|
||||
'DefaultOrg' => 'DEFAULT_ORG',
|
||||
),
|
||||
*/
|
||||
/*
|
||||
'LinOTPAuth' => array( // Configuration for the LinOTP authentication
|
||||
'baseUrl' => 'https://linotp', // The base URL of LinOTP
|
||||
'realm' => 'lino', // the (default) realm of all the users logging in through this system
|
||||
'userModel' => 'User', // name of the User class (MISP class) to check if the user exists
|
||||
'userModelKey' => 'email', // User field that will be used for querying.
|
||||
'verifyssl' => true, // Verify TLS Certificate or not
|
||||
'mixedauth' => false, // false=>Query only LinOTP or true=>OTP from LinOTP, Password from MISP
|
||||
),
|
||||
*/
|
||||
// Warning: The following is a 3rd party contribution and still untested (including security) by the MISP-project team.
|
||||
// Feel free to enable it and report back to us if you run into any issues.
|
||||
//
|
||||
// Uncomment the following to enable Kerberos authentication
|
||||
// needs PHP LDAP support enabled (e.g. compile flag --with-ldap or Debian package php5-ldap)
|
||||
/*
|
||||
'ApacheSecureAuth' => array( // Configuration for kerberos authentication
|
||||
'apacheEnv' => 'REMOTE_USER', // If proxy variable = HTTP_REMOTE_USER, If BasicAuth ldap = PHP_AUTH_USER
|
||||
'ldapServer' => 'ldap://example.com', // FQDN or IP
|
||||
'ldapProtocol' => 3,
|
||||
'ldapNetworkTimeout' => -1, // use -1 for unlimited network timeout
|
||||
'ldapReaderUser' => 'cn=userWithReadAccess,ou=users,dc=example,dc=com', // DN ou RDN LDAP with reader user right
|
||||
'ldapReaderPassword' => 'UserPassword', // the LDAP reader user password
|
||||
'ldapDN' => 'dc=example,dc=com',
|
||||
'ldapSearchFilter' => '', // Search filter to limit results from ldapsearh fx to specific group. FX
|
||||
//'ldapSearchFilter' => '(objectclass=InetOrgPerson)(!(nsaccountlock=True))(memberOf=cn=misp,cn=groups,cn=accounts,dc=example,dc=com)',
|
||||
'ldapSearchAttribut' => 'uid', // filter for search
|
||||
'ldapFilter' => array(
|
||||
'mail',
|
||||
// 'memberOf', //Needed filter if roles should be added depending on group membership.
|
||||
),
|
||||
'ldapDefaultRoleId' => 3, // 3:User, 1:admin. May be good to set "1" for the first user
|
||||
//ldapDefaultRoleId can also be set as an array to support creating users into different group, depending on ldap membership.
|
||||
//This will only work if the ldap server supports memberOf
|
||||
//'ldapDefaultRoleId' => array(
|
||||
// 'misp_admin' => 1,
|
||||
// 'misp_orgadmin' => 2,
|
||||
// 'misp_user' => 3,
|
||||
// 'misp_publisher' => 4,
|
||||
// 'misp_syncuser' => 5,
|
||||
// 'misp_readonly' => 6,
|
||||
// ),
|
||||
//
|
||||
'ldapDefaultOrg' => '1', // uses 1st local org in MISP if undefined,
|
||||
'ldapAllowReferrals' => true, // allow or disallow chasing LDAP referrals
|
||||
//'ldapEmailField' => array('emailAddress, 'mail'), // Optional : fields from which the email address should be retrieved. Default to 'mail' only. If more than one field is set (e.g. 'emailAddress' and 'mail' in this example), only the first one will be used.
|
||||
//'updateUser' => true, // Optional : Will update user on LDAP login to update user fields (e.g. role)
|
||||
),
|
||||
*/
|
||||
|
||||
// Warning: The following is a 3rd party contribution and still untested (including security) by the MISP-project team.
|
||||
// Feel free to enable it and report back to us if you run into any issues.
|
||||
//
|
||||
// Uncomment the following to enable Azure AD authentication
|
||||
/*
|
||||
'AadAuth' => array(
|
||||
'client_id' => '', // Client ID (see Azure AD)
|
||||
'ad_tenant' => '', // Directory ID (see Azure AD)
|
||||
'client_secret' => '', // Client secret (see Azure AD)
|
||||
'redirect_uri' => '', // Your MISP URI, must be the same as in Azure AD
|
||||
'auth_provider' => 'https://login.microsoftonline.com/', // Can be left to this default
|
||||
'auth_provider_user' => 'https://graph.microsoft.com/', // Can be left to this default
|
||||
'misp_user' => 'MISP Users', // The AD group for MISP users
|
||||
'misp_orgadmin' => 'MISP Administrators', // The AD group for MISP administrators
|
||||
'misp_siteadmin' => 'MISP Site Administrators', // The AD group for MISP site administrators
|
||||
'check_ad_groups' => true // Should we check if the user belongs to one of the above AD groups?
|
||||
),
|
||||
*/
|
||||
);
|
|
@ -1,298 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This is core configuration file.
|
||||
*
|
||||
* Use it to configure core behavior of Cake.
|
||||
*
|
||||
* PHP 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package app.Config
|
||||
* @since CakePHP(tm) v 0.2.9
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
|
||||
/**
|
||||
* CakePHP Debug Level:
|
||||
*
|
||||
* Production Mode:
|
||||
* 0: No error messages, errors, or warnings shown. Flash messages redirect.
|
||||
*
|
||||
* Development Mode:
|
||||
* 1: Errors and warnings shown, model caches refreshed, flash messages halted.
|
||||
* 2: As in 1, but also with full debug messages and SQL output.
|
||||
*
|
||||
* In production mode, flash messages redirect after a time interval.
|
||||
* In development mode, you need to click the flash message to continue.
|
||||
*/
|
||||
Configure::write('debug', 0); // 0 = for production, 2 = full debug mode
|
||||
|
||||
/**
|
||||
* Configure the Error handler used to handle errors for your application. By default
|
||||
* ErrorHandler::handleError() is used. It will display errors using Debugger, when debug > 0
|
||||
* and log errors with CakeLog when debug = 0.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `handler` - callback - The callback to handle errors. You can set this to any callable type,
|
||||
* including anonymous functions.
|
||||
* - `level` - int - The level of errors you are interested in capturing.
|
||||
* - `trace` - boolean - Include stack traces for errors in log files.
|
||||
*
|
||||
* @see ErrorHandler for more information on error handling and configuration.
|
||||
*/
|
||||
Configure::write('Error', array(
|
||||
'handler' => 'ErrorHandler::handleError',
|
||||
'level' => E_ALL & ~E_DEPRECATED,
|
||||
'trace' => true
|
||||
));
|
||||
|
||||
/**
|
||||
* Configure the Exception handler used for uncaught exceptions. By default,
|
||||
* ErrorHandler::handleException() is used. It will display a HTML page for the exception, and
|
||||
* while debug > 0, framework errors like Missing Controller will be displayed. When debug = 0,
|
||||
* framework errors will be coerced into generic HTTP errors.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `handler` - callback - The callback to handle exceptions. You can set this to any callback type,
|
||||
* including anonymous functions.
|
||||
* - `renderer` - string - The class responsible for rendering uncaught exceptions. If you choose a custom class you
|
||||
* should place the file for that class in app/Lib/Error. This class needs to implement a render method.
|
||||
* - `log` - boolean - Should Exceptions be logged?
|
||||
*
|
||||
* @see ErrorHandler for more information on exception handling and configuration.
|
||||
*/
|
||||
Configure::write('Exception', array(
|
||||
'handler' => 'ErrorHandler::handleException',
|
||||
'renderer' => 'ExceptionRenderer',
|
||||
'log' => true,
|
||||
'skipLog' => array(
|
||||
'NotFoundException',
|
||||
'ForbiddenException',
|
||||
)
|
||||
));
|
||||
|
||||
/**
|
||||
* Application wide charset encoding
|
||||
*/
|
||||
Configure::write('App.encoding', 'UTF-8');
|
||||
|
||||
/**
|
||||
* To configure CakePHP *not* to use mod_rewrite and to
|
||||
* use CakePHP pretty URLs, remove these .htaccess
|
||||
* files:
|
||||
*
|
||||
* /.htaccess
|
||||
* /app/.htaccess
|
||||
* /app/webroot/.htaccess
|
||||
*
|
||||
* And uncomment the App.baseUrl below:
|
||||
*/
|
||||
//Configure::write('App.baseUrl', env('SCRIPT_NAME'));
|
||||
|
||||
/**
|
||||
* Uncomment the define below to use CakePHP prefix routes.
|
||||
*
|
||||
* The value of the define determines the names of the routes
|
||||
* and their associated controller actions:
|
||||
*
|
||||
* Set to an array of prefixes you want to use in your application. Use for
|
||||
* admin or other prefixed routes.
|
||||
*
|
||||
* Routing.prefixes = array('admin', 'manager');
|
||||
*
|
||||
* Enables:
|
||||
* `admin_index()` and `/admin/controller/index`
|
||||
* `manager_index()` and `/manager/controller/index`
|
||||
*
|
||||
*/
|
||||
Configure::write('Routing.prefixes', array('admin'));
|
||||
|
||||
/**
|
||||
* Turn off all caching application-wide.
|
||||
*
|
||||
*/
|
||||
Configure::write('Cache.disable', false);
|
||||
|
||||
/**
|
||||
* Enable cache checking.
|
||||
*
|
||||
* If set to true, for view caching you must still use the controller
|
||||
* public $cacheAction inside your controllers to define caching settings.
|
||||
* You can either set it controller-wide by setting public $cacheAction = true,
|
||||
* or in each action using $this->cacheAction = true.
|
||||
*
|
||||
*/
|
||||
//Configure::write('Cache.check', true);
|
||||
|
||||
/**
|
||||
* Defines the default error type when using the log() function. Used for
|
||||
* differentiating error logging and debugging. Currently PHP supports LOG_DEBUG.
|
||||
*/
|
||||
define('LOG_ERROR', LOG_ERR);
|
||||
|
||||
/**
|
||||
* Session configuration.
|
||||
*
|
||||
* Contains an array of settings to use for session configuration. The defaults key is
|
||||
* used to define a default preset to use for sessions, any settings declared here will override
|
||||
* the settings of the default config.
|
||||
*
|
||||
* ## Options
|
||||
*
|
||||
* - `Session.cookie` - The name of the cookie to use. Defaults to 'CAKEPHP'
|
||||
* - `Session.timeout` - The number of minutes you want sessions to live for. This timeout is handled by CakePHP
|
||||
* - `Session.cookieTimeout` - The number of minutes you want session cookies to live for.
|
||||
* - `Session.checkAgent` - Do you want the user agent to be checked when starting sessions? You might want to set the
|
||||
* value to false, when dealing with older versions of IE, Chrome Frame or certain web-browsing devices and AJAX
|
||||
* - `Session.defaults` - The default configuration set to use as a basis for your session.
|
||||
* There are four builtins: php, cake, cache, database.
|
||||
* - `Session.handler` - Can be used to enable a custom session handler. Expects an array of callables,
|
||||
* that can be used with `session_save_handler`. Using this option will automatically add `session.save_handler`
|
||||
* to the ini array.
|
||||
* - `Session.autoRegenerate` - Enabling this setting, turns on automatic renewal of sessions, and
|
||||
* sessionids that change frequently. See CakeSession::$requestCountdown.
|
||||
* - `Session.ini` - An associative array of additional ini values to set.
|
||||
*
|
||||
* The built in defaults are:
|
||||
*
|
||||
* - 'php' - Uses settings defined in your php.ini.
|
||||
* - 'cake' - Saves session files in CakePHP's /tmp directory.
|
||||
* - 'database' - Uses CakePHP's database sessions.
|
||||
* - 'cache' - Use the Cache class to save sessions.
|
||||
*
|
||||
* To define a custom session handler, save it at /app/Model/Datasource/Session/<name>.php.
|
||||
* Make sure the class implements `CakeSessionHandlerInterface` and set Session.handler to <name>
|
||||
*
|
||||
* To use database sessions, run the app/Config/Schema/sessions.php schema using
|
||||
* the cake shell command: cake schema create Sessions
|
||||
*
|
||||
*/
|
||||
Configure::write('Session', array(
|
||||
'timeout' => 60, // Session timeout, default is 1 hour
|
||||
'cookie_timeout' => 10080 , // Cookie timeout, default is 1 week
|
||||
'defaults' => 'php',
|
||||
'autoRegenerate' => false,
|
||||
'checkAgent' => false,
|
||||
));
|
||||
|
||||
// Set session cookie SameSite parameter to Lax if this param is supported and no default value is set
|
||||
if (PHP_VERSION_ID >= 70300 && empty(ini_get('session.cookie_samesite'))) {
|
||||
Configure::write('Session.ini', [
|
||||
'session.cookie_samesite' => 'Lax'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The level of CakePHP security.
|
||||
*/
|
||||
Configure::write('Security.level', 'medium');
|
||||
|
||||
/**
|
||||
* A random string used in security hashing methods.
|
||||
*/
|
||||
Configure::write('Security.salt', 'Rooraenietu8Eeyo<Qu2eeNfterd-dd+');
|
||||
|
||||
/**
|
||||
* A random numeric string (digits only) used to encrypt/decrypt strings.
|
||||
*/
|
||||
Configure::write('Security.cipherSeed', '395786739573056621429506834955');
|
||||
|
||||
/**
|
||||
* Apply timestamps with the last modified time to static assets (js, css, images).
|
||||
* Will append a querystring parameter containing the time the file was modified. This is
|
||||
* useful for invalidating browser caches.
|
||||
*
|
||||
* Set to `true` to apply timestamps when debug > 0. Set to 'force' to always enable
|
||||
* timestamping regardless of debug value.
|
||||
*/
|
||||
//Configure::write('Asset.timestamp', true);
|
||||
|
||||
/**
|
||||
* Compress CSS output by removing comments, whitespace, repeating tags, etc.
|
||||
* This requires a/var/cache directory to be writable by the web server for caching.
|
||||
* and /vendors/csspp/csspp.php
|
||||
*
|
||||
* To use, prefix the CSS link URL with '/ccss/' instead of '/css/' or use HtmlHelper::css().
|
||||
*/
|
||||
//Configure::write('Asset.filter.css', 'css.php');
|
||||
|
||||
/**
|
||||
* Plug in your own custom JavaScript compressor by dropping a script in your webroot to handle the
|
||||
* output, and setting the config below to the name of the script.
|
||||
*
|
||||
* To use, prefix your JavaScript link URLs with '/cjs/' instead of '/js/' or use JavaScriptHelper::link().
|
||||
*/
|
||||
//Configure::write('Asset.filter.js', 'custom_javascript_output_filter.php');
|
||||
|
||||
/**
|
||||
* The classname and database used in CakePHP's
|
||||
* access control lists.
|
||||
*/
|
||||
Configure::write('Acl.classname', 'DbAcl');
|
||||
Configure::write('Acl.database', 'default');
|
||||
|
||||
/**
|
||||
* Uncomment this line and correct your server timezone to fix
|
||||
* any date & time related errors.
|
||||
*/
|
||||
//date_default_timezone_set('UTC');
|
||||
|
||||
/**
|
||||
* Pick the caching engine to use. If APC is enabled use it.
|
||||
* If running via cli - apc is disabled by default. ensure it's available and enabled in this case
|
||||
*
|
||||
* Note: 'default' and other application caches should be configured in app/Config/bootstrap.php.
|
||||
* Please check the comments in boostrap.php for more info on the cache engines available
|
||||
* and their setttings.
|
||||
*/
|
||||
$engine = 'File';
|
||||
if (function_exists('apcu_dec') && (PHP_SAPI !== 'cli' || ini_get('apc.enable_cli'))) {
|
||||
require_once APP . 'Plugin/ApcuCache/Engine/ApcuEngine.php'; // it is not possible to use plugin
|
||||
$engine = 'Apcu'; // faster version of ApcEngine
|
||||
}
|
||||
|
||||
// In development mode, caches should expire quickly.
|
||||
$duration = '+999 days';
|
||||
if (Configure::read('debug') >= 1) {
|
||||
$duration = '+10 seconds';
|
||||
}
|
||||
|
||||
// Prefix each application on the same server with a different string, to avoid Memcache and APC conflicts.
|
||||
$prefix = 'myapp_';
|
||||
|
||||
/**
|
||||
* Configure the cache used for general framework caching. Path information,
|
||||
* object listings, and translation cache files are stored with this configuration.
|
||||
*/
|
||||
Cache::config('_cake_core_', array(
|
||||
'engine' => $engine,
|
||||
'prefix' => $prefix . 'cake_core_',
|
||||
'path' => CACHE . 'persistent' . DS,
|
||||
'serialize' => ($engine === 'File'),
|
||||
'duration' => $duration
|
||||
));
|
||||
|
||||
/**
|
||||
* Configure the cache for model and datasource caches. This cache configuration
|
||||
* is used to store schema descriptions, and table listings in connections.
|
||||
*/
|
||||
Cache::config('_cake_model_', array(
|
||||
'engine' => $engine,
|
||||
'prefix' => $prefix . 'cake_model_',
|
||||
'path' => CACHE . 'models' . DS,
|
||||
'serialize' => ($engine === 'File'),
|
||||
'duration' => $duration
|
||||
));
|
||||
|
||||
|
||||
//Comment the following out if you do not with to use the background workers (not recommended)
|
||||
require_once dirname(__DIR__) . '/Vendor/autoload.php';
|
|
@ -1,75 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This is core configuration file.
|
||||
*
|
||||
* Use it to configure core behaviour of Cake.
|
||||
*
|
||||
* PHP 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package app.Config
|
||||
* @since CakePHP(tm) v 0.2.9
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
/**
|
||||
* In this file you set up your database connection details.
|
||||
*
|
||||
* @package cake.config
|
||||
*/
|
||||
/**
|
||||
* Database configuration class.
|
||||
* You can specify multiple configurations for production, development and testing.
|
||||
*
|
||||
* datasource => The name of a supported datasource; valid options are as follows:
|
||||
* Database/Mysql - MySQL 4 & 5,
|
||||
* Database/Sqlite - SQLite (PHP5 only),
|
||||
* Database/Postgres - PostgreSQL 7 and higher,
|
||||
* Database/Sqlserver - Microsoft SQL Server 2005 and higher
|
||||
*
|
||||
* You can add custom database datasources (or override existing datasources) by adding the
|
||||
* appropriate file to app/Model/Datasource/Database. Datasources should be named 'MyDatasource.php',
|
||||
*
|
||||
*
|
||||
* persistent => true / false
|
||||
* Determines whether or not the database should use a persistent connection
|
||||
*
|
||||
* host =>
|
||||
* the host you connect to the database. To add a socket or port number, use 'port' => #
|
||||
*
|
||||
* prefix =>
|
||||
* Uses the given prefix for all the tables in this database. This setting can be overridden
|
||||
* on a per-table basis with the Model::$tablePrefix property.
|
||||
*
|
||||
* schema =>
|
||||
* For Postgres specifies which schema you would like to use the tables in. Postgres defaults to 'public'.
|
||||
*
|
||||
* encoding =>
|
||||
* For MySQL, Postgres specifies the character encoding to use when connecting to the
|
||||
* database. Uses database default not specified.
|
||||
*
|
||||
* unix_socket =>
|
||||
* For MySQL to connect via socket specify the `unix_socket` parameter instead of `host` and `port`
|
||||
*/
|
||||
class DATABASE_CONFIG {
|
||||
|
||||
public $default = array(
|
||||
'datasource' => 'Database/Mysql',
|
||||
//'datasource' => 'Database/Postgres',
|
||||
'persistent' => false,
|
||||
'host' => 'localhost',
|
||||
'login' => 'db login',
|
||||
'port' => 3306, // MySQL & MariaDB
|
||||
//'port' => 5432, // PostgreSQL
|
||||
'password' => 'db password',
|
||||
'database' => 'misp',
|
||||
'prefix' => '',
|
||||
'encoding' => 'utf8',
|
||||
);
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This is email configuration file.
|
||||
*
|
||||
* Use it to configure email transports of Cake.
|
||||
*
|
||||
* PHP 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package app.Config
|
||||
* @since CakePHP(tm) v 2.0.0
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
/**
|
||||
* In this file you set up your send email details.
|
||||
*
|
||||
* @package cake.config
|
||||
*/
|
||||
|
||||
/**
|
||||
* Email configuration class.
|
||||
* You can specify multiple configurations for production, development and testing.
|
||||
*
|
||||
* transport => The name of a supported transport; valid options are as follows:
|
||||
* Mail - Send using PHP mail function
|
||||
* Smtp - Send using SMTP
|
||||
* Debug - Do not send the email, just return the result
|
||||
*
|
||||
* You can add custom transports (or override existing transports) by adding the
|
||||
* appropriate file to app/Network/Email. Transports should be named 'YourTransport.php',
|
||||
* where 'Your' is the name of the transport.
|
||||
*
|
||||
* from =>
|
||||
* The origin email. See CakeEmail::from() about the valid values
|
||||
*
|
||||
*/
|
||||
class EmailConfig {
|
||||
|
||||
// to set the return-path header, simply uncomment the line below and change you@localhost to the desired e-mail address
|
||||
public $default = array(
|
||||
'transport' => 'Mail',
|
||||
'charset' => 'utf-8',
|
||||
'headers' => array('Precedence' => 'bulk'),
|
||||
//'additionalParameters' => '-f you@localhost'
|
||||
);
|
||||
|
||||
public $smtp = array(
|
||||
'transport' => 'Smtp',
|
||||
'from' => array('site@localhost' => 'My Site'),
|
||||
'host' => 'localhost',
|
||||
'port' => 25,
|
||||
'timeout' => 30,
|
||||
'username' => 'user',
|
||||
'password' => 'secret',
|
||||
'client' => null,
|
||||
'log' => false,
|
||||
//'charset' => 'utf-8',
|
||||
//'headerCharset' => 'utf-8',
|
||||
);
|
||||
|
||||
public $fast = array(
|
||||
'from' => 'you@localhost',
|
||||
'sender' => null,
|
||||
'to' => null,
|
||||
'cc' => null,
|
||||
'bcc' => null,
|
||||
'replyTo' => null,
|
||||
'readReceipt' => null,
|
||||
'returnPath' => null,
|
||||
'messageId' => true,
|
||||
'subject' => null,
|
||||
'message' => null,
|
||||
'headers' => null,
|
||||
'viewRender' => null,
|
||||
'template' => false,
|
||||
'layout' => false,
|
||||
'viewVars' => null,
|
||||
'attachments' => null,
|
||||
'emailFormat' => null,
|
||||
'transport' => 'Smtp',
|
||||
'host' => 'localhost',
|
||||
'port' => 25,
|
||||
'timeout' => 30,
|
||||
'username' => 'user',
|
||||
'password' => 'secret',
|
||||
'client' => null,
|
||||
'log' => true,
|
||||
//'charset' => 'utf-8',
|
||||
//'headerCharset' => 'utf-8',
|
||||
);
|
||||
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Routes configuration
|
||||
*
|
||||
* In this file, you set up routes to your controllers and their actions.
|
||||
* Routes are very important mechanism that allows you to freely connect
|
||||
* different urls to chosen controllers and their actions (functions).
|
||||
*
|
||||
* PHP 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package app.Config
|
||||
* @since CakePHP(tm) v 0.2.9
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
/**
|
||||
* Here, we are connecting '/' (base path) to controller called 'Pages',
|
||||
* its action called 'display', and we pass a param to select the view file
|
||||
* to use (in this case, /app/View/Pages/home.ctp)...
|
||||
*/
|
||||
Router::connect('/', array('controller' => 'events', 'action' => 'index'));
|
||||
|
||||
// admin Paginator
|
||||
Router::connect('/allowedlists/admin_index/*', array('controller' => 'allowedlists', 'action' => 'index', 'admin' => true));
|
||||
Router::connect('/users/admin_index/*', array('controller' => 'users', 'action' => 'index', 'admin' => true));
|
||||
Router::connect('/roles/admin_index/*', array('controller' => 'roles', 'action' => 'index', 'admin' => true));
|
||||
Router::connect('/logs/admin_search/*', array('controller' => 'logs', 'action' => 'search', 'admin' => true));
|
||||
Router::connect('/audit_logs/admin_index/*', array('controller' => 'audit_logs', 'action' => 'index', 'admin' => true));
|
||||
Router::connect('/access_logs/admin_index/*', array('controller' => 'access_logs', 'action' => 'index', 'admin' => true));
|
||||
Router::connect('/logs/admin_index/*', array('controller' => 'logs', 'action' => 'index', 'admin' => true));
|
||||
Router::connect('/regexp/admin_index/*', array('controller' => 'regexp', 'action' => 'index', 'admin' => true));
|
||||
|
||||
// Activate REST
|
||||
Router::mapResources(array('events', 'attributes'));
|
||||
Router::parseExtensions('xml', 'json', 'csv');
|
||||
|
||||
Router::connectNamed(
|
||||
array('attributesPage' => array('controller' => 'events', 'action' => 'view'))
|
||||
);
|
||||
/**
|
||||
* Load all plugin routes. See the CakePlugin documentation on
|
||||
* how to customize the loading of plugin routes.
|
||||
*/
|
||||
CakePlugin::routes();
|
||||
|
||||
/**
|
||||
* Load the CakePHP default routes. Only remove this if you do not want to use
|
||||
* the built-in default routes.
|
||||
*/
|
||||
require CAKE . 'Config' . DS . 'routes.php';
|
|
@ -1,164 +0,0 @@
|
|||
<?php
|
||||
|
||||
App::uses('ComponentCollection', 'Controller');
|
||||
App::uses('RestResponseComponent', 'Controller/Component');
|
||||
App::uses('File', 'Utility');
|
||||
|
||||
class APIShell extends AppShell {
|
||||
|
||||
private $filename = 'API_Doc.md';
|
||||
private $notice = '';
|
||||
|
||||
public function startup() {
|
||||
$collection = new ComponentCollection();
|
||||
$this->RestResponseComponent = $collection->load('RestResponse');
|
||||
$this->notice = __('The following API documentation is derived directly from [MISP RestResponseComponent\'s source code](app/Controller/Component/RestResponseComponent.php)');
|
||||
}
|
||||
|
||||
public function genDoc()
|
||||
{
|
||||
$basicUser = [
|
||||
'id' => 0,
|
||||
'email' => 'SYSTEM',
|
||||
'Organisation' => ['name' => 'SYSTEM'],
|
||||
'Role' => [
|
||||
'perm_site_admin' => true,
|
||||
]
|
||||
];
|
||||
$api = $this->RestResponseComponent->getScopedApiInfo($basicUser);
|
||||
$apiFieldsContraint = $this->RestResponseComponent->getAllApisFieldsConstraint($basicUser);
|
||||
|
||||
$doc = $this->genNoticeDoc();
|
||||
foreach ($api as $model => $apiEntries) {
|
||||
$doc .= $this->genMDTitle(Inflector::humanize($model), 1);
|
||||
foreach ($apiEntries as $apiEntry) {
|
||||
$baseURL = $apiEntry['url'];
|
||||
$offset = strpos($baseURL, '/[');
|
||||
if ($offset !== false) {
|
||||
$baseURL = substr($baseURL, 0, $offset);
|
||||
}
|
||||
$fieldsConstraints = $apiFieldsContraint[$baseURL];
|
||||
$doc .= $this->genEndpointDoc($apiEntry, $fieldsConstraints);
|
||||
}
|
||||
}
|
||||
$saved = $this->saveDoc($doc);
|
||||
if ($saved) {
|
||||
echo __('Successfully saved API documentation') . PHP_EOL;
|
||||
} else {
|
||||
echo __('Could not save API documentation') . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
private function genEndpointDoc($apiEntry, $fieldsConstraints=[])
|
||||
{
|
||||
$doc = $this->genMDTitle(Inflector::humanize($apiEntry['action']), 2);
|
||||
$doc .= $apiEntry['description'] . PHP_EOL;
|
||||
$doc .= $this->genMDCode($apiEntry['url']);
|
||||
$doc .= $this->genMDBR();
|
||||
|
||||
$doc .= $this->genMDTitle(__('URL Parameters'), 3);
|
||||
$doc .= $this->genParamsDoc($apiEntry, 'params', $fieldsConstraints);
|
||||
$doc .= $this->genMDBR();
|
||||
|
||||
$doc .= $this->genMDTitle(__('Parameters'), 3);
|
||||
if (!empty($apiEntry['mandatory'])) {
|
||||
$doc .= $this->genMDTitle(__('Mandatory'), 4);
|
||||
$doc .= $this->genParamsDoc($apiEntry, 'mandatory', $fieldsConstraints);
|
||||
$doc .= $this->genMDBR();
|
||||
}
|
||||
if (!empty($apiEntry['optional'])) {
|
||||
$doc .= $this->genMDTitle(__('Optional'), 4);
|
||||
$doc .= $this->genParamsDoc($apiEntry, 'optional', $fieldsConstraints);
|
||||
}
|
||||
$doc .= $this->genMDBR();
|
||||
return $doc;
|
||||
}
|
||||
|
||||
private function genNoticeDoc()
|
||||
{
|
||||
$doc = $this->genMDTitle(__('API Documentation'), 1);
|
||||
$doc .= $this->notice . PHP_EOL;
|
||||
return $doc;
|
||||
}
|
||||
|
||||
private function genParamsDoc($apiEntry, $paramName, $fieldsConstraints)
|
||||
{
|
||||
$doc = '';
|
||||
if (!empty($apiEntry[$paramName])) {
|
||||
$header = [__('Name'), __('Type'), __('Description')];
|
||||
$rows = [];
|
||||
foreach ($apiEntry[$paramName] as $param) {
|
||||
if (is_array($param)) {
|
||||
$type = 'Object';
|
||||
$description = json_encode($param);
|
||||
} else {
|
||||
if (isset($fieldsConstraints[$param]) && !is_null($fieldsConstraints[$param])) {
|
||||
$type = $fieldsConstraints[$param]['type'];
|
||||
$description = !empty($fieldsConstraints[$param]['help']) ? $fieldsConstraints[$param]['help'] : '';
|
||||
} else {
|
||||
$type = '';
|
||||
$description = '';
|
||||
}
|
||||
}
|
||||
$rows[] = [
|
||||
$param,
|
||||
$type,
|
||||
$description
|
||||
];
|
||||
}
|
||||
$doc = $this->genMDTable($header, $rows);
|
||||
}
|
||||
return $doc;
|
||||
}
|
||||
|
||||
private function saveDoc($MDDoc)
|
||||
{
|
||||
$file = new File(ROOT . DS . 'docs' . DS . $this->filename, true);
|
||||
$saved = $file->write($MDDoc);
|
||||
$file->close();
|
||||
return $saved;
|
||||
}
|
||||
|
||||
private function genMDBR($count=1)
|
||||
{
|
||||
return str_repeat(PHP_EOL, $count);
|
||||
}
|
||||
|
||||
private function genMDTitle($text, $level=1)
|
||||
{
|
||||
return sprintf('%s %s', str_repeat('#', $level), $text . PHP_EOL);
|
||||
}
|
||||
|
||||
private function genMDTable($header, $rows)
|
||||
{
|
||||
$doc = $this->genMDTableRow($header);
|
||||
$doc .= $this->genMDTableRow(array_map(function () {
|
||||
return '--';
|
||||
}, $header));
|
||||
foreach ($rows as $row) {
|
||||
$doc .= $this->genMDTableRow($row);
|
||||
}
|
||||
return $doc;
|
||||
}
|
||||
|
||||
private function genMDTableRow($row)
|
||||
{
|
||||
$doc = '| ';
|
||||
foreach ($row as $entry) {
|
||||
$entryText = is_array($entry) ? json_encode($entry) : $entry;
|
||||
$doc .= $entryText . ' |';
|
||||
}
|
||||
$doc .= PHP_EOL;
|
||||
return $doc;
|
||||
}
|
||||
|
||||
private function genLink($text, $url)
|
||||
{
|
||||
return sprintf('[%s](%s)', $text, $url);
|
||||
}
|
||||
|
||||
private function genMDCode($code)
|
||||
{
|
||||
return '```' . PHP_EOL . $code . PHP_EOL . '```';
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,100 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* AppShell file
|
||||
*
|
||||
* PHP 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @since CakePHP(tm) v 2.0
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
|
||||
App::uses('AppModel', 'Model');
|
||||
App::uses('BackgroundJobsTool', 'Tools');
|
||||
|
||||
/**
|
||||
* Application Shell
|
||||
*
|
||||
* Add your application-wide methods in the class below, your shells
|
||||
* will inherit them.
|
||||
*
|
||||
* @package app.Console.Command
|
||||
*/
|
||||
abstract class AppShell extends Shell
|
||||
{
|
||||
public $tasks = array('ConfigLoad');
|
||||
|
||||
/** @var BackgroundJobsTool */
|
||||
private $BackgroundJobsTool;
|
||||
|
||||
public function initialize()
|
||||
{
|
||||
$this->ConfigLoad = $this->Tasks->load('ConfigLoad');
|
||||
$this->ConfigLoad->execute();
|
||||
|
||||
parent::initialize();
|
||||
}
|
||||
|
||||
public function perform()
|
||||
{
|
||||
$this->initialize();
|
||||
$this->{array_shift($this->args)}();
|
||||
}
|
||||
|
||||
protected function _welcome()
|
||||
{
|
||||
// disable welcome message
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $data
|
||||
* @return string
|
||||
* @throws JsonException
|
||||
*/
|
||||
protected function json($data)
|
||||
{
|
||||
return JsonTool::encode($data, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
protected function toBoolean($value)
|
||||
{
|
||||
$value = strtolower($value);
|
||||
switch ($value) {
|
||||
case 'true':
|
||||
case '1':
|
||||
return true;
|
||||
case 'false':
|
||||
case '0':
|
||||
return false;
|
||||
default:
|
||||
$this->error("Invalid state value `$value`, it must be `true`, `false`, `1`, or `0`.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BackgroundJobsTool
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function getBackgroundJobsTool()
|
||||
{
|
||||
if (!isset($this->BackgroundJobsTool)) {
|
||||
$settings = ['enabled' => false];
|
||||
if (!empty(Configure::read('SimpleBackgroundJobs.enabled'))) {
|
||||
$settings = Configure::read('SimpleBackgroundJobs');
|
||||
}
|
||||
$this->BackgroundJobsTool = new BackgroundJobsTool($settings);
|
||||
}
|
||||
return $this->BackgroundJobsTool;
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Reset a password
|
||||
*
|
||||
* arg0 = email
|
||||
* arg1 = new password
|
||||
* @deprecated Use UserShell instead
|
||||
*/
|
||||
class AuthkeyShell extends AppShell {
|
||||
|
||||
public $uses = array('User', 'Log');
|
||||
|
||||
public function main()
|
||||
{
|
||||
$this->err('This method is deprecated. Next time please use `cake user change_authkey [user] [authkey]` command.');
|
||||
|
||||
if (!isset($this->args[0]) || empty($this->args[0])) echo 'MISP authkey reset command line tool.' . PHP_EOL . 'To assign a new authkey for a user:' . PHP_EOL . APP . 'Console/cake Authkey [email] [auth_key | optional]' . PHP_EOL;
|
||||
else {
|
||||
// get the users that need their password hashed
|
||||
$user = $this->User->find('first', array('conditions' => array('email' => $this->args[0]), 'recursive' => -1, 'contain' => 'Organisation'));
|
||||
if (empty($user)) {
|
||||
echo 'User not found. Make sure you use the correct syntax: /var/www/MISP/app/Console/cake Authkey [email]' . PHP_EOL;
|
||||
exit;
|
||||
}
|
||||
$this->User->id = $user['User']['id'];
|
||||
$newkey = $this->User->generateAuthKey();
|
||||
if (isset($this->args[1]) && !empty($this->args[1])) {
|
||||
$newkey = $this->args[1];
|
||||
if(!ctype_alnum($this->args[1]) || strlen($this->args[1]) != 40) {
|
||||
echo 'MISP authkey reset command line tool.' . PHP_EOL . 'To assign a new authkey for a user:' . PHP_EOL . APP . 'Console/cake Authkey [email] [api_key | optional]' . PHP_EOL;
|
||||
echo 'Authkey must be a 40 character Alphanumeric string.' . PHP_EOL;
|
||||
exit;
|
||||
}
|
||||
}
|
||||
if ($this->User->saveField('authkey', $newkey)) {
|
||||
$logTitle = 'Authentication key for user ' . $user['User']['id'] . ' (' . $user['User']['email'] . ')';
|
||||
$this->Log->createLogEntry('SYSTEM', 'reset_auth_key', 'User', $user['User']['id'], $logTitle, array('authkey' => array($user['User']['authkey'], $newkey)));
|
||||
echo $newkey . PHP_EOL;
|
||||
} else {
|
||||
echo 'Could not update account for User.id = ', $user['User']['id'], PHP_EOL;
|
||||
echo json_encode($this->User->validationErrors) . PHP_EOL;
|
||||
$this->out(print_r($this->User->invalidFields(), true));
|
||||
}
|
||||
}
|
||||
exit;
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Reset a password
|
||||
*
|
||||
* arg0 = baseurl
|
||||
* @deprecated
|
||||
*/
|
||||
class BaseurlShell extends AppShell {
|
||||
|
||||
public $uses = array('Server');
|
||||
|
||||
public function main()
|
||||
{
|
||||
$this->err('This method is deprecated. Next time please use `cake admin setSetting MISP.baseurl [baseurl]` command.');
|
||||
|
||||
$baseurl = $this->args[0];
|
||||
$result = $this->Server->testBaseURL($baseurl);
|
||||
if (true !== $result) {
|
||||
echo $result . PHP_EOL;
|
||||
} else {
|
||||
$this->Server->serverSettingsSaveValue('MISP.baseurl', $baseurl);
|
||||
echo 'Baseurl updated. Have a very safe and productive day.', PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
class DevShell extends AppShell {
|
||||
|
||||
public $uses = [];
|
||||
|
||||
public function cleanFeedDefault() {
|
||||
$this->out(__('Massaging the feed metadata file.'));
|
||||
$data = file_get_contents(APP . 'files/feed-metadata/defaults.json');
|
||||
if (empty($data)) {
|
||||
$this->stdout->styles('error');
|
||||
$this->out(__('Could not read the defaults.json file at %s. Exiting', APP . 'files/feed-metadata/defaults.json'));
|
||||
} else {
|
||||
$data = json_decode($data, true);
|
||||
$validFields = [
|
||||
'Feed' => [
|
||||
'name', 'provider', 'url', 'rules', 'enabled', 'distribution',
|
||||
'default', 'source_format', 'fixed_event', 'delta_merge',
|
||||
'publish', 'override_ids', 'settings', 'input_source',
|
||||
'delete_local_file', 'lookup_visible'
|
||||
],
|
||||
'Tag' => [
|
||||
'name', 'colour', 'exportable', 'hide_tag'
|
||||
]
|
||||
];
|
||||
foreach ($data as $k => $feedData) {
|
||||
$temp = [];
|
||||
foreach ($validFields as $scope => $fieldNames) {
|
||||
foreach ($fieldNames as $fieldName) {
|
||||
if (isset($feedData[$scope][$fieldName])) {
|
||||
$temp[$scope][$fieldName] = $feedData[$scope][$fieldName];
|
||||
}
|
||||
}
|
||||
}
|
||||
$data[$k] = $temp;
|
||||
}
|
||||
if (!empty($data)) {
|
||||
file_put_contents(APP . 'files/feed-metadata/defaults.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||
$this->out(__(
|
||||
'Done. The feed definitions contain %s feeds and can be found at %s.',
|
||||
count($data),
|
||||
APP . 'files/feed-metadata/defaults.json'
|
||||
));
|
||||
} else {
|
||||
$this->stdout->styles('error');
|
||||
$this->out(__('Something went wrong.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,659 +0,0 @@
|
|||
<?php
|
||||
App::uses('Folder', 'Utility');
|
||||
App::uses('File', 'Utility');
|
||||
App::uses('FileAccessTool', 'Tools');
|
||||
require_once 'AppShell.php';
|
||||
|
||||
/**
|
||||
* @property User $User
|
||||
* @property Event $Event
|
||||
* @property Job $Job
|
||||
* @property Tag $Tag
|
||||
* @property Server $Server
|
||||
* @property Correlation $Correlation
|
||||
*/
|
||||
class EventShell extends AppShell
|
||||
{
|
||||
public $uses = array('Event', 'Post', 'Attribute', 'Job', 'User', 'Task', 'Allowedlist', 'Server', 'Organisation', 'Correlation', 'Tag');
|
||||
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
$parser->addSubcommand('import', array(
|
||||
'help' => __('Import event from file into MISP.'),
|
||||
'parser' => array(
|
||||
'arguments' => array(
|
||||
'user_id' => ['help' => __('User ID that will owner of uploaded event.'), 'required' => true],
|
||||
'file' => ['help' => __('Path to JSON MISP file, can be gzipped or bz2 compressed.'), 'required' => true],
|
||||
),
|
||||
'options' => [
|
||||
'take-ownership' => ['boolean' => true],
|
||||
'publish' => ['boolean' => true],
|
||||
],
|
||||
)
|
||||
));
|
||||
$parser->addSubcommand('testEventNotificationEmail', [
|
||||
'help' => __('Generate event notification email in EML format.'),
|
||||
'parser' => [
|
||||
'arguments' => [
|
||||
'event_id' => ['help' => __('Event ID'), 'required' => true],
|
||||
'user_id' => ['help' => __('User ID'), 'required' => true],
|
||||
],
|
||||
'options' => [
|
||||
'send' => ['help' => __('Send email to given user'), 'boolean' => true],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$parser->addSubcommand('duplicateTags', [
|
||||
'help' => __('Show duplicate tags'),
|
||||
]);
|
||||
$parser->addSubcommand('generateTopCorrelations', [
|
||||
'help' => __('Generate top correlations'),
|
||||
]);
|
||||
$parser->addSubcommand('mergeTags', [
|
||||
'help' => __('Merge tags'),
|
||||
'parser' => [
|
||||
'arguments' => array(
|
||||
'source' => ['help' => __('Source tag ID or name. Source tag will be deleted.'), 'required' => true],
|
||||
'destination' => ['help' => __('Destination tag ID or name.'), 'required' => true],
|
||||
)
|
||||
],
|
||||
]);
|
||||
return $parser;
|
||||
}
|
||||
|
||||
public function import()
|
||||
{
|
||||
list($userId, $path) = $this->args;
|
||||
$user = $this->getUser($userId);
|
||||
|
||||
if (!file_exists($path)) {
|
||||
$this->error("File '$path' does not exist.");
|
||||
}
|
||||
if (!is_readable($path)) {
|
||||
$this->error("File '$path' is not readable.");
|
||||
}
|
||||
|
||||
$pathInfo = pathinfo($path);
|
||||
if ($pathInfo['extension'] === 'gz') {
|
||||
$content = file_get_contents("compress.zlib://$path");
|
||||
$extension = pathinfo($pathInfo['filename'], PATHINFO_EXTENSION);
|
||||
} else if ($pathInfo['extension'] === 'bz2') {
|
||||
$content = file_get_contents("compress.bzip2://$path");
|
||||
$extension = pathinfo($pathInfo['filename'], PATHINFO_EXTENSION);
|
||||
} else {
|
||||
$content = file_get_contents($path);
|
||||
$extension = $pathInfo['extension'];
|
||||
}
|
||||
|
||||
if ($content === false) {
|
||||
$this->error("Could not read content from '$path'.");
|
||||
}
|
||||
|
||||
$isXml = $extension === 'xml';
|
||||
$takeOwnership = $this->param('take_ownership');
|
||||
$publish = $this->param('publish');
|
||||
$results = $this->Event->addMISPExportFile($user, $content, $isXml, $takeOwnership, $publish);
|
||||
|
||||
foreach ($results as $result) {
|
||||
if (is_numeric($result['result'])) {
|
||||
$this->out("Event #{$result['id']}: {$result['info']} imported.");
|
||||
} else {
|
||||
$this->out("Could not import event because of validation errors: " . json_encode($result['validationIssues']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function mergeTags()
|
||||
{
|
||||
list($source, $destination) = $this->args;
|
||||
$output = $this->Tag->mergeTag($source, $destination);
|
||||
$this->out("Merged tag `{$output['source_tag']['Tag']['name']}` into `{$output['destination_tag']['Tag']['name']}`");
|
||||
$this->out(__("%s attribute or event tags changed", $output['changed']));
|
||||
}
|
||||
|
||||
public function duplicateTags()
|
||||
{
|
||||
$output = $this->Tag->duplicateTags();
|
||||
$this->out($this->json($output));
|
||||
}
|
||||
|
||||
public function doPublish()
|
||||
{
|
||||
if (empty($this->args[0])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Do publish'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$id = $this->args[0];
|
||||
$this->Event->id = $id;
|
||||
if (!$this->Event->exists()) {
|
||||
throw new NotFoundException(__('Invalid event'));
|
||||
}
|
||||
$this->Job->create();
|
||||
$data = array(
|
||||
'worker' => 'default',
|
||||
'job_type' => 'doPublish',
|
||||
'job_input' => $id,
|
||||
'status' => 0,
|
||||
'retries' => 0,
|
||||
'org' => 0,
|
||||
'message' => 'Job created.',
|
||||
);
|
||||
$this->Job->save($data);
|
||||
// update the event and set the from field to the current instance's organisation from the bootstrap. We also need to save id and info for the logs.
|
||||
$this->Event->recursive = -1;
|
||||
$event = $this->Event->read(null, $id);
|
||||
$event['Event']['published'] = 1;
|
||||
$fieldList = array('published', 'id', 'info');
|
||||
$this->Event->save($event, array('fieldList' => $fieldList));
|
||||
// only allow form submit CSRF protection.
|
||||
$this->Job->save([
|
||||
'status' => Job::STATUS_COMPLETED,
|
||||
'message' => 'Job done.'
|
||||
]);
|
||||
}
|
||||
|
||||
public function correlateValue()
|
||||
{
|
||||
$value = $this->args[0];
|
||||
|
||||
if (!empty($this->args[1])) {
|
||||
$this->Job->id = intval($this->args[1]);
|
||||
} else {
|
||||
$this->Job->createJob(
|
||||
'SYSTEM',
|
||||
Job::WORKER_DEFAULT,
|
||||
'correlateValue',
|
||||
$value,
|
||||
'Job created.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->Correlation->correlateValue($value, $this->Job->id);
|
||||
$this->Job->save([
|
||||
'status' => Job::STATUS_COMPLETED,
|
||||
'message' => 'Job done.',
|
||||
'progress' => 100
|
||||
]);
|
||||
}
|
||||
|
||||
public function cache()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Cache event'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$timeStart = time();
|
||||
$userId = $this->args[0];
|
||||
$id = $this->args[1];
|
||||
$user = $this->getUser($userId);
|
||||
$this->Job->id = $id;
|
||||
$export_type = $this->args[2];
|
||||
file_put_contents('/tmp/test', $export_type);
|
||||
$exportTypes = $this->Event->exportTypes();
|
||||
$typeData = $exportTypes[$export_type];
|
||||
if (!in_array($export_type, array_keys($exportTypes))) {
|
||||
$this->Job->saveField('progress', 100);
|
||||
$timeDelta = (time()-$timeStart);
|
||||
$this->Job->saveField('message', 'Job Failed due to invalid export format. (in '.$timeDelta.'s)');
|
||||
$this->Job->saveField('date_modified', date("Y-m-d H:i:s"));
|
||||
return false;
|
||||
}
|
||||
if ($export_type == 'text') {
|
||||
$types = array_keys($this->Attribute->typeDefinitions);
|
||||
$typeCount = count($types);
|
||||
foreach ($types as $k => $type) {
|
||||
$typeData['params']['type'] = $type;
|
||||
$this->__runCaching($user, $typeData, false, $export_type, '_' . $type);
|
||||
$this->Job->saveField('message', 'Processing all attributes of type '. $type . '.');
|
||||
$this->Job->saveField('progress', intval($k / $typeCount));
|
||||
}
|
||||
} else {
|
||||
$this->__runCaching($user, $typeData, $id, $export_type);
|
||||
}
|
||||
$this->Job->saveField('progress', 100);
|
||||
$timeDelta = (time()-$timeStart);
|
||||
$this->Job->saveField('message', 'Job done. (in '.$timeDelta.'s)');
|
||||
$this->Job->saveField('date_modified', date("Y-m-d H:i:s"));
|
||||
}
|
||||
|
||||
private function __runCaching($user, $typeData, $id, $export_type, $subType = '')
|
||||
{
|
||||
$export_type = strtolower($typeData['type']);
|
||||
$final = $this->{$typeData['scope']}->restSearch($user, $typeData['params']['returnFormat'], $typeData['params'], false, $id);
|
||||
$dir = new Folder(APP . 'tmp/cached_exports/' . $export_type, true, 0750);
|
||||
//echo PHP_EOL . $dir->pwd() . DS . 'misp.' . $export_type . $subType . '.ADMIN' . $typeData['extension'] . PHP_EOL;
|
||||
if ($user['Role']['perm_site_admin']) {
|
||||
$file = new File($dir->pwd() . DS . 'misp.' . $export_type . $subType . '.ADMIN' . $typeData['extension']);
|
||||
} else {
|
||||
$file = new File($dir->pwd() . DS . 'misp.' . $export_type . $subType . '.' . $user['Organisation']['name'] . $typeData['extension']);
|
||||
}
|
||||
$file->write($final);
|
||||
$file->close();
|
||||
return true;
|
||||
}
|
||||
|
||||
public function cachebro()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[1])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Cache bro'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$timeStart = time();
|
||||
$userId = $this->args[0];
|
||||
$user = $this->getUser($userId);
|
||||
$id = $this->args[1];
|
||||
$this->Job->id = $id;
|
||||
$this->Job->saveField('progress', 1);
|
||||
App::uses('BroExport', 'Export');
|
||||
$export = new BroExport();
|
||||
$types = array_keys($export->mispTypes);
|
||||
$typeCount = count($types);
|
||||
$dir = new Folder(APP . DS . '/tmp/cached_exports/bro', true, 0750);
|
||||
if ($user['Role']['perm_site_admin']) {
|
||||
$file = new File($dir->pwd() . DS . 'misp.bro.ADMIN.intel');
|
||||
} else {
|
||||
$file = new File($dir->pwd() . DS . 'misp.bro.' . $user['Organisation']['name'] . '.intel');
|
||||
}
|
||||
|
||||
$file->write('');
|
||||
$skipHeader = false;
|
||||
foreach ($types as $k => $type) {
|
||||
$final = $this->Attribute->bro($user, $type, false, false, false, false, false, false, $skipHeader);
|
||||
$skipHeader = true;
|
||||
foreach ($final as $attribute) {
|
||||
$file->append($attribute . PHP_EOL);
|
||||
}
|
||||
$this->Job->saveField('progress', $k / $typeCount * 100);
|
||||
}
|
||||
$file->close();
|
||||
$timeDelta = (time()-$timeStart);
|
||||
$this->Job->saveField('progress', 100);
|
||||
$this->Job->saveField('message', 'Job done. (in '.$timeDelta.'s)');
|
||||
$this->Job->saveField('date_modified', date("Y-m-d H:i:s"));
|
||||
}
|
||||
|
||||
public function alertemail()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Alert email'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$userId = $this->args[0];
|
||||
$jobId = $this->args[1];
|
||||
$eventId = $this->args[2];
|
||||
$oldpublish = isset($this->args[3]) ? $this->args[3] : null;
|
||||
$user = $this->getUser($userId);
|
||||
$this->Event->sendAlertEmail($eventId, $user, $oldpublish, $jobId);
|
||||
}
|
||||
|
||||
public function contactemail()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[1]) || !isset($this->args[2]) ||
|
||||
empty($this->args[3]) || empty($this->args[4])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Contact email'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$id = $this->args[0];
|
||||
$message = $this->args[1];
|
||||
$all = $this->args[2];
|
||||
$userId = $this->args[3];
|
||||
$jobId = $this->args[4];
|
||||
|
||||
$user = $this->getUser($userId);
|
||||
$result = $this->Event->sendContactEmail($id, $message, $all, $user);
|
||||
$this->Job->saveStatus($jobId, $result);
|
||||
}
|
||||
|
||||
public function postsemail()
|
||||
{
|
||||
if (
|
||||
empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2]) ||
|
||||
empty($this->args[3]) || empty($this->args[4]) || empty($this->args[5])
|
||||
) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Posts email'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$userId = intval($this->args[0]);
|
||||
$postId = intval($this->args[1]);
|
||||
$eventId = intval($this->args[2]);
|
||||
$title = $this->args[3];
|
||||
$message = $this->args[4];
|
||||
$this->Job->id = intval($this->args[5]);
|
||||
|
||||
$result = $this->Post->sendPostsEmail($userId, $postId, $eventId, $title, $message);
|
||||
|
||||
if ($result) {
|
||||
$this->Job->save([
|
||||
'progress' => 100,
|
||||
'message' => 'Emails sent.',
|
||||
'date_modified' => date('Y-m-d H:i:s'),
|
||||
'status' => Job::STATUS_COMPLETED
|
||||
]);
|
||||
} else {
|
||||
$this->Job->save([
|
||||
'date_modified' => date('Y-m-d H:i:s'),
|
||||
'status' => Job::STATUS_FAILED
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function enqueueCaching()
|
||||
{
|
||||
if (empty($this->args[0])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Enqueue caching'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$timestamp = $this->args[0];
|
||||
$task = $this->Task->findByType('cache_exports');
|
||||
|
||||
// If the next execution time and the timestamp don't match, it means that this task is no longer valid as the time for the execution has since being scheduled
|
||||
// been updated.
|
||||
if ($task['Task']['next_execution_time'] != $timestamp) return;
|
||||
|
||||
$users = $this->User->find('all', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array(
|
||||
'Role.perm_site_admin' => 0,
|
||||
'User.disabled' => 0,
|
||||
),
|
||||
'contain' => array(
|
||||
'Organisation' => array('fields' => array('name')),
|
||||
'Role' => array('fields' => array('perm_site_admin'))
|
||||
),
|
||||
'fields' => array('User.org_id', 'User.id'),
|
||||
'group' => array('User.org_id')
|
||||
));
|
||||
$site_admin = $this->User->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array(
|
||||
'Role.perm_site_admin' => 1,
|
||||
'User.disabled' => 0
|
||||
),
|
||||
'contain' => array(
|
||||
'Organisation' => array('fields' => array('name')),
|
||||
'Role' => array('fields' => array('perm_site_admin'))
|
||||
),
|
||||
'fields' => array('User.org_id', 'User.id')
|
||||
));
|
||||
$users[] = $site_admin;
|
||||
|
||||
if ($task['Task']['timer'] > 0) $this->Task->reQueue($task, 'cache', 'EventShell', 'enqueueCaching', false, false);
|
||||
|
||||
// Queue a set of exports for admins. This "ADMIN" organisation. The organisation of the admin users doesn't actually matter, it is only used to indentify
|
||||
// the special cache files containing all events
|
||||
$i = 0;
|
||||
foreach ($users as $user) {
|
||||
foreach ($this->Event->exportTypes() as $k => $type) {
|
||||
if ($k == 'stix') continue;
|
||||
$this->Job->cache($k, $user['User']);
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
$this->Task->id = $task['Task']['id'];
|
||||
$this->Task->saveField('message', $i . ' job(s) started at ' . date('d/m/Y - H:i:s') . '.');
|
||||
}
|
||||
|
||||
public function publish()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[2]) || empty($this->args[3])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Publish event'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$id = $this->args[0];
|
||||
$passAlong = $this->args[1];
|
||||
$jobId = $this->args[2];
|
||||
$userId = $this->args[3];
|
||||
$user = $this->getUser($userId);
|
||||
$job = $this->Job->read(null, $jobId);
|
||||
$this->Event->Behaviors->unload('SysLogLogable.SysLogLogable');
|
||||
$result = $this->Event->publish($id, $passAlong);
|
||||
$job['Job']['progress'] = 100;
|
||||
$job['Job']['status'] = Job::STATUS_COMPLETED;
|
||||
$job['Job']['date_modified'] = date("Y-m-d H:i:s");
|
||||
if ($result) {
|
||||
$job['Job']['message'] = 'Event published.';
|
||||
} else {
|
||||
$job['Job']['message'] = 'Event published, but the upload to other instances may have failed.';
|
||||
}
|
||||
$this->Job->save($job);
|
||||
$log = ClassRegistry::init('Log');
|
||||
$log->createLogEntry($user, 'publish', 'Event', $id, 'Event (' . $id . '): published.', 'published () => (1)');
|
||||
}
|
||||
|
||||
public function publish_sightings()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[2]) || empty($this->args[3])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Publish sightings'] . PHP_EOL);
|
||||
}
|
||||
|
||||
list($id, $passAlong, $jobId, $userId) = $this->args;
|
||||
$user = $this->getUser($userId);
|
||||
|
||||
$sightingsUuidsToPush = [];
|
||||
if (isset($this->args[4])) { // push just specific sightings
|
||||
$sightingsUuidsToPush = $this->getBackgroundJobsTool()->fetchDataFile($this->args[4]);
|
||||
}
|
||||
|
||||
$this->Event->Behaviors->unload('SysLogLogable.SysLogLogable');
|
||||
$result = $this->Event->publishSightings($id, $passAlong, $sightingsUuidsToPush);
|
||||
|
||||
$count = count($sightingsUuidsToPush);
|
||||
$message = $count === 0 ? "All sightings published" : "$count sightings published";
|
||||
if ($result) {
|
||||
$message .= '.';
|
||||
} else {
|
||||
$message .= ', but the upload to other instances may have failed.';
|
||||
}
|
||||
$this->Job->saveStatus($jobId, true, $message);
|
||||
|
||||
$log = ClassRegistry::init('Log');
|
||||
$title = $count === 0 ? "All sightings for event published." : "$count sightings for event published.";
|
||||
$log->createLogEntry($user, 'publish_sightings', 'Event', $id, $title, 'publish_sightings updated');
|
||||
}
|
||||
|
||||
public function publish_galaxy_clusters()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2]) || !array_key_exists(3, $this->args)) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Publish Galaxy clusters'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$clusterId = $this->args[0];
|
||||
$jobId = $this->args[1];
|
||||
$userId = $this->args[2];
|
||||
$passAlong = $this->args[3];
|
||||
$user = $this->getUser($userId);
|
||||
$job = $this->Job->read(null, $jobId);
|
||||
$this->GalaxyCluster = ClassRegistry::init('GalaxyCluster');
|
||||
$result = $this->GalaxyCluster->publish($clusterId, $passAlong=$passAlong);
|
||||
$job['Job']['progress'] = 100;
|
||||
$job['Job']['date_modified'] = date("Y-m-d H:i:s");
|
||||
if ($result) {
|
||||
$job['Job']['message'] = 'Galaxy cluster published.';
|
||||
} else {
|
||||
$job['Job']['message'] = 'Galaxy cluster published, but the upload to other instances may have failed.';
|
||||
}
|
||||
$this->Job->save($job);
|
||||
$log = ClassRegistry::init('Log');
|
||||
$log->createLogEntry($user, 'publish', 'GalaxyCluster', $clusterId, 'GalaxyCluster (' . $clusterId . '): published.', 'published () => (1)');
|
||||
}
|
||||
|
||||
public function enrichment()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Run enrichment'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$userId = $this->args[0];
|
||||
$user = $this->getUser($userId);
|
||||
$eventId = $this->args[1];
|
||||
$modulesRaw = $this->args[2];
|
||||
try {
|
||||
$modules = json_decode($modulesRaw, true);
|
||||
} catch (Exception $e) {
|
||||
die('Invalid module JSON');
|
||||
}
|
||||
if (!empty($this->args[3])) {
|
||||
$jobId = $this->args[3];
|
||||
} else {
|
||||
$this->Job->create();
|
||||
$data = array(
|
||||
'worker' => 'default',
|
||||
'job_type' => 'enrichment',
|
||||
'job_input' => 'Event: ' . $eventId . ' modules: ' . $modulesRaw,
|
||||
'status' => 0,
|
||||
'retries' => 0,
|
||||
'org' => $user['Organisation']['name'],
|
||||
'message' => 'Enriching event.',
|
||||
);
|
||||
$this->Job->save($data);
|
||||
$jobId = $this->Job->id;
|
||||
}
|
||||
$job = $this->Job->read(null, $jobId);
|
||||
$options = array(
|
||||
'user' => $user,
|
||||
'event_id' => $eventId,
|
||||
'modules' => $modules
|
||||
);
|
||||
$result = $this->Event->enrichment($options);
|
||||
$job['Job']['progress'] = 100;
|
||||
$job['Job']['date_modified'] = date("Y-m-d H:i:s");
|
||||
if ($result) {
|
||||
$job['Job']['message'] = 'Added ' . $result . ' attribute' . ($result > 1 ? 's.' : '.');
|
||||
} else {
|
||||
$job['Job']['message'] = 'Enrichment finished, but no attributes added.';
|
||||
}
|
||||
echo $job['Job']['message'] . PHP_EOL;
|
||||
$this->Job->save($job);
|
||||
$log = ClassRegistry::init('Log');
|
||||
$log->createLogEntry($user, 'enrichment', 'Event', $eventId, 'Event (' . $eventId . '): enriched.', 'enriched () => (1)');
|
||||
}
|
||||
|
||||
public function processfreetext()
|
||||
{
|
||||
if (empty($this->args[0])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Process free text'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$inputFile = $this->args[0];
|
||||
$inputData = $this->getBackgroundJobsTool()->fetchDataFile($inputFile);
|
||||
Configure::write('CurrentUserId', $inputData['user']['id']);
|
||||
$this->Event->processFreeTextData(
|
||||
$inputData['user'],
|
||||
$inputData['attributes'],
|
||||
$inputData['id'],
|
||||
$inputData['default_comment'],
|
||||
$inputData['proposals'],
|
||||
$inputData['adhereToWarninglists'],
|
||||
$inputData['jobId']
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function processmoduleresult()
|
||||
{
|
||||
if (empty($this->args[0])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Process module result'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$inputFile = $this->args[0];
|
||||
$inputData = $this->getBackgroundJobsTool()->fetchDataFile($inputFile);
|
||||
Configure::write('CurrentUserId', $inputData['user']['id']);
|
||||
$this->Event->processModuleResultsData(
|
||||
$inputData['user'],
|
||||
$inputData['misp_format'],
|
||||
$inputData['id'],
|
||||
$inputData['default_comment'],
|
||||
$inputData['jobId']
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function recoverEvent()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[1])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['event_management_tasks']['data']['Recover event'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$jobId = $this->args[0];
|
||||
$id = $this->args[1];
|
||||
$job = $this->Job->read(null, $jobId);
|
||||
$job['Job']['progress'] = 1;
|
||||
$job['Job']['date_modified'] = date("Y-m-d H:i:s");
|
||||
$job['Job']['message'] = __('Recovering event %s', $id);
|
||||
$this->Job->save($job);
|
||||
$result = $this->Event->recoverEvent($id);
|
||||
$job['Job']['progress'] = 100;
|
||||
$job['Job']['date_modified'] = date("Y-m-d H:i:s");
|
||||
$job['Job']['message'] = __('Recovery complete. Event #%s recovered, using %s log entries.', $id, $result);
|
||||
$this->Job->save($job);
|
||||
}
|
||||
|
||||
public function testEventNotificationEmail()
|
||||
{
|
||||
list($eventId, $userId) = $this->args;
|
||||
$send = $this->param('send');
|
||||
|
||||
$user = $this->getUser($userId);
|
||||
$eventForUser = $this->Event->fetchEvent($user, [
|
||||
'eventid' => $eventId,
|
||||
'includeAllTags' => true,
|
||||
'includeEventCorrelations' => true,
|
||||
'noEventReports' => true,
|
||||
'noSightings' => true,
|
||||
'metadata' => Configure::read('MISP.event_alert_metadata_only') || Configure::read('MISP.publish_alerts_summary_only'),
|
||||
]);
|
||||
if (empty($eventForUser)) {
|
||||
$this->error("Event with ID $eventId not exists or given user don't have permission to access it.");
|
||||
}
|
||||
|
||||
$emailTemplate = $this->Event->prepareAlertEmail($eventForUser[0], $user);
|
||||
|
||||
App::uses('SendEmail', 'Tools');
|
||||
App::uses('GpgTool', 'Tools');
|
||||
$sendEmail = new SendEmail(GpgTool::initializeGpg());
|
||||
if (!$send) {
|
||||
$sendEmail->setTransport('Debug');
|
||||
}
|
||||
$result = $sendEmail->sendToUser(['User' => $user], null, $emailTemplate);
|
||||
|
||||
if ($send) {
|
||||
var_dump($result);
|
||||
} else {
|
||||
echo $result['contents']['headers'] . "\n\n" . $result['contents']['message'] . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
* @return array
|
||||
*/
|
||||
private function getUser($userId)
|
||||
{
|
||||
$user = $this->User->getAuthUser($userId, true);
|
||||
if (empty($user)) {
|
||||
$this->error("User with ID $userId does not exist.");
|
||||
}
|
||||
Configure::write('CurrentUserId', $user['id']); // for audit logging purposes
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function generateTopCorrelations()
|
||||
{
|
||||
$jobId = $this->args[0] ?? null;
|
||||
if ($jobId) {
|
||||
$job = $this->Job->read(null, $jobId);
|
||||
$job['Job']['progress'] = 1;
|
||||
$job['Job']['date_modified'] = date("Y-m-d H:i:s");
|
||||
$job['Job']['message'] = __('Generating top correlations list.');
|
||||
$this->Job->save($job);
|
||||
}
|
||||
$this->Correlation->generateTopCorrelations($jobId);
|
||||
if ($jobId) {
|
||||
$job['Job']['progress'] = 100;
|
||||
$job['Job']['date_modified'] = date("Y-m-d H:i:s");
|
||||
$job['Job']['message'] = __('Job done.');
|
||||
$this->Job->save($job);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
<?php
|
||||
/*
|
||||
* Enable/disable misp
|
||||
*
|
||||
* arg0 = [0|1]
|
||||
*/
|
||||
class LiveShell extends AppShell {
|
||||
|
||||
public $uses = array('Server');
|
||||
|
||||
public function main()
|
||||
{
|
||||
$live = $this->args[0];
|
||||
if ($live != 0 && $live != 1) {
|
||||
echo 'Invalid parameters. Usage: /var/www/MISP/app/Console/cake Live [0|1]';
|
||||
} else {
|
||||
$this->Server->serverSettingsSaveValue('MISP.live', $live==1);
|
||||
}
|
||||
$status = $live ? 'MISP is now live. Users can now log in.' : 'MISP is now disabled. Only site admins can log in.';
|
||||
echo $status;
|
||||
}
|
||||
}
|
|
@ -1,213 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @property Log $Log
|
||||
* @property AuditLog $AuditLog
|
||||
* @property AccessLog $AccessLog
|
||||
* @property Server $Server
|
||||
*/
|
||||
class LogShell extends AppShell
|
||||
{
|
||||
public $uses = ['Log', 'AuditLog', 'AccessLog', 'Server'];
|
||||
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
$parser->addSubcommand('auditStatistics', [
|
||||
'help' => __('Show statistics for audit logs.'),
|
||||
]);
|
||||
$parser->addSubcommand('accessStatistics', [
|
||||
'help' => __('Show statistics for access logs.'),
|
||||
]);
|
||||
$parser->addSubcommand('statistics', [
|
||||
'help' => __('Show statistics for application logs.'),
|
||||
]);
|
||||
$parser->addSubcommand('export', [
|
||||
'help' => __('Export application logs to compressed file in JSON Lines format (one JSON encoded line per entry).'),
|
||||
'parser' => [
|
||||
'arguments' => [
|
||||
'file' => ['help' => __('Path to output file'), 'required' => true],
|
||||
],
|
||||
'options' => [
|
||||
'without-changes' => ['boolean' => true, 'help' => __('Do not include add, edit or delete actions.')],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$parser->addSubcommand('recompress', [
|
||||
'help' => __('Recompress compressed data in logs.'),
|
||||
]);
|
||||
$parser->addSubcommand('accessLogRetention', [
|
||||
'help' => __('Delete logs that are older than specified duration.'),
|
||||
'parser' => array(
|
||||
'arguments' => array(
|
||||
'duration' => ['help' => __('Duration in days'), 'required' => true],
|
||||
),
|
||||
),
|
||||
]);
|
||||
return $parser;
|
||||
}
|
||||
|
||||
public function export()
|
||||
{
|
||||
list($path) = $this->args;
|
||||
$withoutChanges = $this->param('without-changes');
|
||||
|
||||
if (file_exists($path)) {
|
||||
$this->error("File $path already exists");
|
||||
}
|
||||
|
||||
$file = gzopen($path, 'wb4'); // Compression level 4 is best compromise between time and size
|
||||
if ($file === false) {
|
||||
$this->error("Could not open $path for writing");
|
||||
}
|
||||
|
||||
/** @var ProgressShellHelper $progress */
|
||||
$progress = $this->helper('progress');
|
||||
$progress->init([
|
||||
'total' => $this->Log->tableRows(), // just estimate, but fast
|
||||
'width' => 50,
|
||||
]);
|
||||
|
||||
$lastId = 0;
|
||||
while (true) {
|
||||
$conditions = ['Log.id >' => $lastId]; // much faster than offset
|
||||
if ($withoutChanges) {
|
||||
$conditions['NOT'] = ['Log.action' => ['add', 'edit', 'delete']];
|
||||
}
|
||||
$logs = $this->Log->find('all', [
|
||||
'conditions' => $conditions,
|
||||
'recursive' => -1,
|
||||
'limit' => 100000,
|
||||
'order' => ['Log.id ASC'],
|
||||
]);
|
||||
if (empty($logs)) {
|
||||
break;
|
||||
}
|
||||
$lines = '';
|
||||
foreach ($logs as $log) {
|
||||
$log = $log['Log'];
|
||||
foreach (['id', 'model_id', 'user_id'] as $field) {
|
||||
$log[$field] = (int)$log[$field]; // Convert to int to save space
|
||||
}
|
||||
if (empty($log['description'])) {
|
||||
unset($log['description']);
|
||||
}
|
||||
if (empty($log['ip'])) {
|
||||
unset($log['ip']);
|
||||
}
|
||||
$log['created'] = strtotime($log['created']); // to save space
|
||||
if ($log['id'] > $lastId) {
|
||||
$lastId = $log['id'];
|
||||
}
|
||||
$lines .= JsonTool::encode($log) . "\n";
|
||||
}
|
||||
if (gzwrite($file, $lines) === false) {
|
||||
$this->error("Could not write data to $path");
|
||||
}
|
||||
$progress->increment(count($logs));
|
||||
$progress->draw();
|
||||
}
|
||||
gzclose($file);
|
||||
$this->out('Done');
|
||||
}
|
||||
|
||||
public function statistics()
|
||||
{
|
||||
$count = $this->Log->find('count');
|
||||
$first = $this->Log->find('first', [
|
||||
'recursive' => -1,
|
||||
'fields' => ['created'],
|
||||
'order' => ['id ASC'],
|
||||
]);
|
||||
$last = $this->Log->find('first', [
|
||||
'recursive' => -1,
|
||||
'fields' => ['created'],
|
||||
'order' => ['id DESC'],
|
||||
]);
|
||||
|
||||
$this->out(str_pad(__('Count:'), 20) . $count);
|
||||
$this->out(str_pad(__('First:'), 20) . $first['Log']['created']);
|
||||
$this->out(str_pad(__('Last:'), 20) . $last['Log']['created']);
|
||||
|
||||
$usage = $this->Server->dbSpaceUsage()['logs'];
|
||||
$this->out(str_pad(__('Data size:'), 20) . CakeNumber::toReadableSize($usage['data_in_bytes']));
|
||||
$this->out(str_pad(__('Index size:'), 20) . CakeNumber::toReadableSize($usage['index_in_bytes']));
|
||||
$this->out(str_pad(__('Reclaimable size:'), 20) . CakeNumber::toReadableSize($usage['reclaimable_in_bytes']), 2);
|
||||
}
|
||||
|
||||
public function auditStatistics()
|
||||
{
|
||||
$count = $this->AuditLog->find('count');
|
||||
$first = $this->AuditLog->find('first', [
|
||||
'recursive' => -1,
|
||||
'fields' => ['created'],
|
||||
'order' => ['id ASC'],
|
||||
]);
|
||||
$last = $this->AuditLog->find('first', [
|
||||
'recursive' => -1,
|
||||
'fields' => ['created'],
|
||||
'order' => ['id DESC'],
|
||||
]);
|
||||
|
||||
$this->out(str_pad(__('Count:'), 20) . $count);
|
||||
$this->out(str_pad(__('First:'), 20) . $first['AuditLog']['created']);
|
||||
$this->out(str_pad(__('Last:'), 20) . $last['AuditLog']['created']);
|
||||
|
||||
$usage = $this->Server->dbSpaceUsage()['audit_logs'];
|
||||
$this->out(str_pad(__('Data size:'), 20) . CakeNumber::toReadableSize($usage['data_in_bytes']));
|
||||
$this->out(str_pad(__('Index size:'), 20) . CakeNumber::toReadableSize($usage['index_in_bytes']));
|
||||
$this->out(str_pad(__('Reclaimable size:'), 20) . CakeNumber::toReadableSize($usage['reclaimable_in_bytes']), 2);
|
||||
|
||||
// Just to fetch compressionStats
|
||||
$this->AuditLog->find('column', [
|
||||
'fields' => ['change'],
|
||||
]);
|
||||
|
||||
$this->out('Change field:');
|
||||
$this->out('-------------');
|
||||
$this->out(str_pad(__('Compressed items:'), 20) . $this->AuditLog->compressionStats['compressed']);
|
||||
$this->out(str_pad(__('Total size:'), 20) . CakeNumber::toReadableSize($this->AuditLog->compressionStats['bytes_total']));
|
||||
$this->out(str_pad(__('Uncompressed size:'), 20) . CakeNumber::toReadableSize($this->AuditLog->compressionStats['bytes_uncompressed']));
|
||||
$this->out(str_pad(__('Compressed size:'), 20) . CakeNumber::toReadableSize($this->AuditLog->compressionStats['bytes_compressed']));
|
||||
}
|
||||
|
||||
public function accessStatistics()
|
||||
{
|
||||
$count = $this->AccessLog->find('count');
|
||||
$first = $this->AccessLog->find('first', [
|
||||
'recursive' => -1,
|
||||
'fields' => ['created'],
|
||||
'order' => ['id ASC'],
|
||||
]);
|
||||
$last = $this->AccessLog->find('first', [
|
||||
'recursive' => -1,
|
||||
'fields' => ['created'],
|
||||
'order' => ['id DESC'],
|
||||
]);
|
||||
|
||||
$this->out(str_pad(__('Count:'), 20) . $count);
|
||||
$this->out(str_pad(__('First:'), 20) . $first['AccessLog']['created']);
|
||||
$this->out(str_pad(__('Last:'), 20) . $last['AccessLog']['created']);
|
||||
|
||||
$usage = $this->Server->dbSpaceUsage()['access_logs'];
|
||||
$this->out(str_pad(__('Data size:'), 20) . CakeNumber::toReadableSize($usage['data_in_bytes']));
|
||||
$this->out(str_pad(__('Index size:'), 20) . CakeNumber::toReadableSize($usage['index_in_bytes']));
|
||||
$this->out(str_pad(__('Reclaimable size:'), 20) . CakeNumber::toReadableSize($usage['reclaimable_in_bytes']), 2);
|
||||
}
|
||||
|
||||
public function recompress()
|
||||
{
|
||||
$this->AuditLog->recompress();
|
||||
}
|
||||
|
||||
public function accessLogRetention()
|
||||
{
|
||||
list($duration) = $this->args;
|
||||
if ($duration <= 0 || !is_numeric($duration)) {
|
||||
$this->error("Invalid duration specified.");
|
||||
}
|
||||
$duration = new DateTime("-$duration days");
|
||||
$deleted = $this->AccessLog->deleteOldLogs($duration);
|
||||
$this->out(__n("Deleted %s entry", "Deleted %s entries", $deleted, $deleted));
|
||||
}
|
||||
}
|
|
@ -1,563 +0,0 @@
|
|||
<?php
|
||||
class Ls22Shell extends AppShell
|
||||
{
|
||||
public $uses = ['Server'];
|
||||
private $__servers = [];
|
||||
|
||||
private function __getInstances($path)
|
||||
{
|
||||
if (empty($path)) {
|
||||
$path = 'instances.csv';
|
||||
}
|
||||
$file = file_get_contents($path);
|
||||
$lines = explode(PHP_EOL, $file);
|
||||
foreach ($lines as $k => $line) {
|
||||
if ($k === 0) {
|
||||
continue;
|
||||
}
|
||||
$fields = explode(',', $line);
|
||||
if (count($fields) === 4 && $fields[1] === 'admin@admin.test') {
|
||||
$this->__servers[] = [
|
||||
'Server' => [
|
||||
'url' => trim($fields[0]),
|
||||
'authkey' => trim($fields[2])
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
$parser->addSubcommand('enableTaxonomy', [
|
||||
'help' => __('Enable a taxonomy with all its tags.'),
|
||||
'parser' => array(
|
||||
'options' => array(
|
||||
'instances' => [
|
||||
'help' => 'Path to the instance file, by default "instances.csv" from the local directory',
|
||||
'short' => 'i',
|
||||
'required' => true
|
||||
],
|
||||
'taxonomy' => [
|
||||
'help' => 'The name of the taxonomy to enable, such as "tlp"',
|
||||
'short' => 't',
|
||||
'required' => true
|
||||
],
|
||||
'misp_url_filter' => [
|
||||
'help' => 'The url of the instance to enable it for - otherwise all are selected',
|
||||
'short' => 'm',
|
||||
'required' => false
|
||||
]
|
||||
),
|
||||
),
|
||||
]);
|
||||
$parser->addSubcommand('checkSyncConnections', [
|
||||
'help' => __('Check the given sync connection(s) for the given server(s).'),
|
||||
'parser' => array(
|
||||
'options' => array(
|
||||
'instances' => [
|
||||
'help' => 'Path to the instance file, by default "instances.csv" from the local directory',
|
||||
'short' => 'i',
|
||||
'required' => true
|
||||
],
|
||||
'misp_url_filter' => [
|
||||
'help' => 'The url of the instance to execute changes on. If not set, all are updated.',
|
||||
'short' => 'm',
|
||||
'required' => false
|
||||
],
|
||||
'synced_misp_url_filter' => [
|
||||
'help' => 'The sync connection to modify on each valid instance (as selected by the misp_url_filter). If not set, all sync connections on the selected instances will be updated.',
|
||||
'short' => 's',
|
||||
'required' => false
|
||||
]
|
||||
),
|
||||
),
|
||||
]);
|
||||
$parser->addSubcommand('modifySyncConnection', [
|
||||
'help' => __('Modify sync connection(s).'),
|
||||
'parser' => array(
|
||||
'options' => array(
|
||||
'instances' => [
|
||||
'help' => 'Path to the instance file, by default "instances.csv" from the local directory',
|
||||
'short' => 'i',
|
||||
'required' => true
|
||||
],
|
||||
'misp_url_filter' => [
|
||||
'help' => 'The url of the instance to execute changes on. If not set, all are updated.',
|
||||
'short' => 'm',
|
||||
'required' => false
|
||||
],
|
||||
'synced_misp_url_filter' => [
|
||||
'help' => 'The sync connection to modify on each valid instance (as selected by the misp_url_filter). If not set, all sync connections on the selected instances will be updated.',
|
||||
'short' => 's',
|
||||
'required' => false
|
||||
],
|
||||
'json' => [
|
||||
'help' => 'JSON delta to push (such as \'{"push": 1}\').',
|
||||
'short' => 'j',
|
||||
'required' => true
|
||||
]
|
||||
),
|
||||
),
|
||||
]);
|
||||
$parser->addSubcommand('addWarninglist', [
|
||||
'help' => __('Inject warninglist'),
|
||||
'parser' => array(
|
||||
'options' => array(
|
||||
'instances' => [
|
||||
'help' => 'Path to the instance file, by default "instances.csv" from the local directory',
|
||||
'short' => 'i',
|
||||
'required' => true
|
||||
],
|
||||
'warninglist' => [
|
||||
'help' => 'Path to the warninglist file',
|
||||
'short' => 'w',
|
||||
'required' => true
|
||||
]
|
||||
),
|
||||
),
|
||||
]);
|
||||
$parser->addSubcommand('status', [
|
||||
'help' => __('Check if the instances are available / the API key works.'),
|
||||
'parser' => array(
|
||||
'options' => array(
|
||||
'instances' => [
|
||||
'help' => 'Path to the instance file, by default "instances.csv" from the local directory',
|
||||
'short' => 'i',
|
||||
'required' => true
|
||||
]
|
||||
),
|
||||
),
|
||||
]);
|
||||
$parser->addSubcommand('scores', [
|
||||
'help' => __('Generate the scores for all BTs.'),
|
||||
'parser' => array(
|
||||
'options' => array(
|
||||
'instances' => [
|
||||
'help' => 'Path to the instance file, by default "instances.csv" from the local directory',
|
||||
'short' => 'i',
|
||||
'required' => true
|
||||
],
|
||||
'server_url' => [
|
||||
'help' => 'URL of the server to query for the scores. If nothing is specified, the first valid entry from instances.csv is taken.',
|
||||
'short' => 's',
|
||||
'required' => false
|
||||
],
|
||||
'from' => [
|
||||
'help' => 'Lower bound of the date. Accepts timestamp or date distance (such as 1d or 5h). Defaults to unbounded.',
|
||||
'short' => 'f',
|
||||
'required' => false
|
||||
],
|
||||
'to' => [
|
||||
'help' => 'Upper bound of the date. Accepts timestamp or date distance (such as 1d or 5h). Defaults to unbounded.',
|
||||
'short' => 't',
|
||||
'required' => false
|
||||
],
|
||||
'org' => [
|
||||
'help' => 'Name the org that should be evaluated. If not set, all will be included.',
|
||||
'short' => 'o',
|
||||
'required' => false
|
||||
]
|
||||
),
|
||||
),
|
||||
]);
|
||||
return $parser;
|
||||
}
|
||||
|
||||
public function checkSyncConnections()
|
||||
{
|
||||
$this->__getInstances($this->param('instances'));
|
||||
$results = [];
|
||||
$instanceFilter = $this->param('misp_url_filter');
|
||||
$syncedInstanceFilter = $this->param('synced_misp_url_filter');
|
||||
foreach ($this->__servers as $server) {
|
||||
if (!empty($instanceFilter) && strtolower(trim($server['Server']['url'])) !== strtolower(trim($instanceFilter))) {
|
||||
continue;
|
||||
}
|
||||
$HttpSocket = $this->Server->setupHttpSocket($server, null);
|
||||
$request = $this->Server->setupSyncRequest($server, 'Server');
|
||||
$start_time = microtime(true);
|
||||
$response = $HttpSocket->get($server['Server']['url'] . '/servers/index', false, $request);
|
||||
$baseline = round((microtime(true) - $start_time) * 1000);
|
||||
if (!$response->isOk()) {
|
||||
$this->out($server['Server']['url'] . ': ' . '<error>Connection or auth failed</error>', 1, Shell::NORMAL);
|
||||
continue;
|
||||
}
|
||||
$synced_servers = json_decode($response->body, true);
|
||||
foreach ($synced_servers as $synced_server) {
|
||||
$success = false;
|
||||
if (empty($syncedInstanceFilter) || strtolower($synced_server['Server']['url']) === strtolower($syncedInstanceFilter)) {
|
||||
$start_time = microtime(true);
|
||||
$response = $HttpSocket->get($server['Server']['url'] . '/servers/testConnection/' . $synced_server['Server']['id'], '{}', $request);
|
||||
$execution_time = round((microtime(true) - $start_time) * 1000) - $baseline;
|
||||
if ($response->isOk()) {
|
||||
$success = true;
|
||||
}
|
||||
$this->out(
|
||||
sprintf(
|
||||
'%s connection to %s: %s (%sms)',
|
||||
$server['Server']['url'],
|
||||
$synced_server['Server']['url'],
|
||||
sprintf(
|
||||
'<%s>%s</%s>',
|
||||
$success ? 'info' : 'error',
|
||||
$success ? 'Success' : 'Failed',
|
||||
$success ? 'info' : 'error'
|
||||
),
|
||||
$execution_time
|
||||
),
|
||||
1,
|
||||
Shell::NORMAL
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function modifySyncConnection()
|
||||
{
|
||||
$this->__getInstances($this->param('instances'));
|
||||
$results = [];
|
||||
$instanceFilter = $this->param('misp_url_filter');
|
||||
$syncedInstanceFilter = $this->param('synced_misp_url_filter');
|
||||
$json = $this->param('json');
|
||||
foreach ($this->__servers as $server) {
|
||||
if (!empty($instanceFilter) && strtolower(trim($server['Server']['url'])) !== strtolower(trim($instanceFilter))) {
|
||||
continue;
|
||||
}
|
||||
$HttpSocket = $this->Server->setupHttpSocket($server, null);
|
||||
$request = $this->Server->setupSyncRequest($server, 'Server');
|
||||
$response = $HttpSocket->get($server['Server']['url'] . '/servers/index', false, $request);
|
||||
if (!$response->isOk()) {
|
||||
$this->out($server['Server']['url'] . ': ' . '<error>Connection or auth failed</error>', 1, Shell::NORMAL);
|
||||
}
|
||||
$synced_servers = json_decode($response->body, true);
|
||||
$success = false;
|
||||
foreach ($synced_servers as $synced_server) {
|
||||
if (empty($syncedInstanceFilter) || strtolower($synced_server['Server']['url']) === strtolower($syncedInstanceFilter)) {
|
||||
debug($json);
|
||||
$response = $HttpSocket->post($server['Server']['url'] . '/servers/edit/' . $synced_server['Server']['id'], $json, $request);
|
||||
debug($response->body);
|
||||
if ($response->isOk()) {
|
||||
$success = true;
|
||||
}
|
||||
$this->out(
|
||||
sprintf(
|
||||
'%s connection to %s: %s',
|
||||
$server['Server']['url'],
|
||||
$synced_server['Server']['url'],
|
||||
sprintf(
|
||||
'<%s>%s</%s>',
|
||||
$success ? 'info' : 'error',
|
||||
$success ? 'Success' : 'Failed',
|
||||
$success ? 'info' : 'error'
|
||||
)
|
||||
),
|
||||
1,
|
||||
Shell::NORMAL
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function enableTaxonomy()
|
||||
{
|
||||
$taxonomyToEnable = $this->param('taxonomy');
|
||||
$instanceFilter = $this->param('misp_url_filter');
|
||||
if (empty($taxonomyToEnable)) {
|
||||
$this->error('No taxonomy provided', 'Provide a taxonomy by specifying the -t or --taxonomy options.');
|
||||
}
|
||||
$this->__getInstances($this->param('instances'));
|
||||
$results = [];
|
||||
foreach ($this->__servers as $server) {
|
||||
if (!empty($instanceFilter) && strtolower(trim($server['Server']['url'])) !== strtolower(trim($instanceFilter))) {
|
||||
continue;
|
||||
}
|
||||
$HttpSocket = $this->Server->setupHttpSocket($server, null);
|
||||
$request = $this->Server->setupSyncRequest($server, 'Server');
|
||||
$response = $HttpSocket->get($server['Server']['url'] . '/taxonomies/index', false, $request);
|
||||
if (!$response->isOk()) {
|
||||
$this->out($server['Server']['url'] . ': ' . '<error>Connection or auth failed</error>', 1, Shell::NORMAL);
|
||||
}
|
||||
$taxonomies = json_decode($response->body, true);
|
||||
$success = false;
|
||||
foreach ($taxonomies as $taxonomy) {
|
||||
if ($taxonomy['Taxonomy']['namespace'] === $taxonomyToEnable) {
|
||||
$response = $HttpSocket->post($server['Server']['url'] . '/taxonomies/enable/' . $taxonomy['Taxonomy']['id'], '{}', $request);
|
||||
if ($response->isOk()) {
|
||||
$response = $HttpSocket->post($server['Server']['url'] . '/taxonomies/addTag/' . $taxonomy['Taxonomy']['id'], '{}', $request);
|
||||
if ($response->isOk()) {
|
||||
$success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$results[$server['Server']['url']] = $success ? 'Success' : 'Failed';
|
||||
$statusWrapped = sprintf(
|
||||
'<%s>%s</%s>',
|
||||
$success ? 'info' : 'error',
|
||||
$results[$server['Server']['url']],
|
||||
$success ? 'info' : 'error'
|
||||
);
|
||||
$this->out($server['Server']['url'] . ': ' . $statusWrapped, 1, Shell::NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
public function status()
|
||||
{
|
||||
$this->__getInstances($this->param('instances'));
|
||||
$results = [];
|
||||
foreach ($this->__servers as $server) {
|
||||
$HttpSocket = $this->Server->setupHttpSocket($server, null);
|
||||
$request = $this->Server->setupSyncRequest($server, 'Server');
|
||||
$start_time = microtime(true);
|
||||
$fatal_error = false;
|
||||
try {
|
||||
$response = $HttpSocket->get($server['Server']['url'] . '/users/view/me', false, $request);
|
||||
} catch (Exception $e) {
|
||||
$fatal_error = true;
|
||||
echo "\x07";
|
||||
$statusWrapped = sprintf(
|
||||
'<error>%s %s: %s</error>',
|
||||
'Something went wrong while trying to reach',
|
||||
$server['Server']['url'],
|
||||
$e->getMessage()
|
||||
);
|
||||
}
|
||||
if (!$fatal_error) {
|
||||
$execution_time = round((microtime(true) - $start_time) * 1000);
|
||||
$statusWrapped = sprintf(
|
||||
'<%s>%s</%s>',
|
||||
$response->isOk() ? 'info' : 'error',
|
||||
$response->isOk() ? 'OK (' . $execution_time . 'ms)' : 'Failed. (' . $response->code . ')',
|
||||
$response->isOk() ? 'info' : 'error'
|
||||
);
|
||||
}
|
||||
$this->out($server['Server']['url'] . ': ' . $statusWrapped, 1, Shell::NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
public function addWarninglist()
|
||||
{
|
||||
$path = $this->param('warninglist');
|
||||
if (empty($path)) {
|
||||
$this->error('No warninglist provided', 'Provide a path to a file containing a warninglist JSON by specifying the -w or --warninglist options.');
|
||||
}
|
||||
$file = file_get_contents($path);
|
||||
$this->__getInstances($this->param('instances'));
|
||||
$results = [];
|
||||
foreach ($this->__servers as $server) {
|
||||
$HttpSocket = $this->Server->setupHttpSocket($server, null);
|
||||
$request = $this->Server->setupSyncRequest($server, 'Server');
|
||||
$start_time = microtime(true);
|
||||
$response = $HttpSocket->post($server['Server']['url'] . '/warninglists/add', $file, $request);
|
||||
$statusWrapped = sprintf(
|
||||
'<%s>%s</%s>',
|
||||
$response->isOk() ? 'info' : 'error',
|
||||
$response->isOk() ? 'OK' : 'Could not create warninglist',
|
||||
$response->isOk() ? 'info' : 'error'
|
||||
);
|
||||
$this->out($server['Server']['url'] . ': ' . $statusWrapped, 1, Shell::NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
public function scores()
|
||||
{
|
||||
$results = [];
|
||||
$this->__getInstances($this->param('instances'));
|
||||
$server = null;
|
||||
if (!empty($this->param['server_url'])) {
|
||||
foreach ($this->__servers as $temp_server) {
|
||||
if ($temp_server['Server']['url'] === $this->param['server_url']) {
|
||||
$server = $temp_server;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$server = $this->__servers[0];
|
||||
}
|
||||
$HttpSocket = $this->Server->setupHttpSocket($server, null);
|
||||
$request = $this->Server->setupSyncRequest($server);
|
||||
$response = $HttpSocket->get($server['Server']['url'] . '/organisations/index/scope:all', false, $request);
|
||||
$orgs = json_decode($response->body(), true);
|
||||
$this->out(__('Organisations fetched. %d found.', count($orgs)), 1, Shell::VERBOSE);
|
||||
$org_mapping = [];
|
||||
foreach ($orgs as $org) {
|
||||
if (!empty($this->param('org')) && $org['Organisation']['name'] !== $this->param('org')) {
|
||||
continue;
|
||||
}
|
||||
if ($org['Organisation']['name'] === 'YT') {
|
||||
continue;
|
||||
}
|
||||
$org_mapping[$org['Organisation']['name']] = $org['Organisation']['id'];
|
||||
}
|
||||
if (!empty($this->param['from'])) {
|
||||
$time_range[] = $this->param['from'];
|
||||
}
|
||||
if (!empty($this->param['to'])) {
|
||||
if (empty($time_range)) {
|
||||
$time_range[] = '365d';
|
||||
}
|
||||
$time_range[] = $this->param['to'];
|
||||
}
|
||||
foreach ($org_mapping as $org_name => $org_id) {
|
||||
$time_range = [];
|
||||
$params = [
|
||||
'org' => $org_id
|
||||
];
|
||||
if (!empty($time_range)) {
|
||||
$params['publish_timestamp'] = $time_range;
|
||||
}
|
||||
$response = $HttpSocket->post($server['Server']['url'] . '/events/restSearch', json_encode($params), $request);
|
||||
$events = json_decode($response->body(), true);
|
||||
$this->out(__('Events fetched from %s. %d found.', $org_name, count($events['response'])), 1, Shell::VERBOSE);
|
||||
$results[$org_name] = [
|
||||
'attribute_count' => 0,
|
||||
'object_count' => 0,
|
||||
'connected_elements' => 0,
|
||||
'event_tags' => 0,
|
||||
'attribute_tags' => 0,
|
||||
'attack' => 0,
|
||||
'other' => 0,
|
||||
'attribute_attack' => 0,
|
||||
'attribute_other' => 0,
|
||||
'score' => 0,
|
||||
'warnings' => 0
|
||||
];
|
||||
foreach ($events['response'] as $event) {
|
||||
if (!empty($event['Event']['Tag'])) {
|
||||
foreach ($event['Event']['Tag'] as $tag) {
|
||||
if (substr($tag['name'], 0, 32) === 'misp-galaxy:mitre-attack-pattern') {
|
||||
$results[$org_name]['attack'] += 1;
|
||||
} else {
|
||||
$results[$org_name]['other'] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($event['Event']['Galaxy'])) {
|
||||
foreach ($event['Event']['Galaxy'] as $galaxy) {
|
||||
if ($galaxy['type'] === 'mitre-attack-pattern') {
|
||||
$results[$org_name]['attack'] += 1;
|
||||
} else {
|
||||
$results[$org_name]['other'] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($event['Event']['Attribute'] as $attribute) {
|
||||
if (!empty($attribute['referenced_by'])) {
|
||||
$results[$org_name]['connected_elements'] +=1;
|
||||
}
|
||||
if (!empty($attribute['Tag'])) {
|
||||
foreach ($attribute['Tag'] as $tag) {
|
||||
if (substr($tag['name'], 0, 32) === 'misp-galaxy:mitre-attack-pattern') {
|
||||
$results[$org_name]['attribute_attack'] += 1;
|
||||
} else {
|
||||
$results[$org_name]['attribute_other'] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($attribute['warnings'])) {
|
||||
$result[$org_name]['warnings'] += 1;
|
||||
}
|
||||
}
|
||||
$results[$org_name]['attribute_count'] += count($event['Event']['Attribute']);
|
||||
if (!empty($event['Event']['Object'])) {
|
||||
foreach ($event['Event']['Object'] as $object) {
|
||||
$results[$org_name]['attribute_count'] += count($object['Attribute']);
|
||||
$results[$org_name]['object_count'] += 1;
|
||||
if (!empty($object['ObjectReference'])) {
|
||||
$results[$org_name]['connected_elements'] += 1;
|
||||
}
|
||||
foreach ($object['Attribute'] as $attribute) {
|
||||
if (!empty($attribute['Tag'])) {
|
||||
foreach ($attribute['Tag'] as $tag) {
|
||||
if (substr($tag['name'], 0, 32) === 'misp-galaxy:mitre-attack-pattern') {
|
||||
$results[$org_name]['attribute_attack'] += 1;
|
||||
} else {
|
||||
$results[$org_name]['attribute_other'] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
$scores = [];
|
||||
foreach ($results as $k => $result) {
|
||||
$totalCount = $result['attribute_count'] + $result['object_count'];
|
||||
if ($totalCount) {
|
||||
if (empty($result['warnings'])) {
|
||||
$results[$k]['metrics']['warnings'] = 100;
|
||||
} else if (100 * $result['warnings'] < $result['attribute_count']) {
|
||||
$results[$k]['metrics']['warnings'] = 50;
|
||||
} else {
|
||||
$results[$k]['metrics']['warnings'] = 0;
|
||||
}
|
||||
$results[$k]['metrics']['connectedness'] = 100 * ($result['connected_elements'] / ($result['attribute_count'] + $result['object_count']));
|
||||
$results[$k]['metrics']['attack_weight'] = 100 * (2*($result['attack']) + $result['attribute_attack']) / ($result['attribute_count'] + $result['object_count']);
|
||||
$results[$k]['metrics']['other_weight'] = 100 * (2*($result['other']) + $result['attribute_other']) / ($result['attribute_count'] + $result['object_count']);
|
||||
}
|
||||
foreach (['connectedness', 'attack_weight', 'other_weight', 'warnings'] as $metric) {
|
||||
if (empty($results[$k]['metrics'][$metric])) {
|
||||
$results[$k]['metrics'][$metric] = 0;
|
||||
}
|
||||
if ($results[$k]['metrics'][$metric] > 100) {
|
||||
$results[$k]['metrics'][$metric] = 100;
|
||||
}
|
||||
}
|
||||
$results[$k]['score'] = round(
|
||||
20 * $results[$k]['metrics']['warnings'] +
|
||||
20 * $results[$k]['metrics']['connectedness'] +
|
||||
40 * $results[$k]['metrics']['attack_weight'] +
|
||||
20 * $results[$k]['metrics']['other_weight']
|
||||
) / 100;
|
||||
$scores[$k]['total'] = $results[$k]['score'];
|
||||
$scores[$k]['warnings'] = round(20 * $results[$k]['metrics']['warnings']);
|
||||
$scores[$k]['connectedness'] = round(20 * $results[$k]['metrics']['connectedness']);
|
||||
$scores[$k]['attack_weight'] = round(40 * $results[$k]['metrics']['attack_weight']);
|
||||
$scores[$k]['other_weight'] = round(20 * $results[$k]['metrics']['other_weight']);
|
||||
}
|
||||
arsort($scores, SORT_DESC);
|
||||
$this->out(str_repeat('=', 128), 1, Shell::NORMAL);
|
||||
$this->out(sprintf(
|
||||
'| %s | %s | %s |',
|
||||
str_pad('Org', 10, ' ', STR_PAD_RIGHT),
|
||||
str_pad('Graph', 100, ' ', STR_PAD_RIGHT),
|
||||
str_pad('Score', 8, ' ', STR_PAD_RIGHT)
|
||||
), 1, Shell::NORMAL);
|
||||
$this->out(str_repeat('=', 128), 1, Shell::NORMAL);
|
||||
foreach ($scores as $org => $score) {
|
||||
$score_string[0] = str_repeat('█', round($score['warnings']/100));
|
||||
$score_string[1] = str_repeat('█', round($score['connectedness']/100));
|
||||
$score_string[2] = str_repeat('█', round($score['attack_weight']/100));
|
||||
$score_string[3] = str_repeat('█', round($score['other_weight']/100));
|
||||
$this->out(sprintf(
|
||||
'| %s | %s | %s |',
|
||||
str_pad($org, 10, ' ', STR_PAD_RIGHT),
|
||||
sprintf(
|
||||
'<error>%s</error><warning>%s</warning><question>%s</question><info>%s</info>%s',
|
||||
$score_string[0],
|
||||
$score_string[1],
|
||||
$score_string[2],
|
||||
$score_string[3],
|
||||
str_repeat(' ', 100 - mb_strlen(implode('', $score_string)))
|
||||
),
|
||||
str_pad($score['total'] . '%', 8, ' ', STR_PAD_RIGHT)
|
||||
), 1, Shell::NORMAL);
|
||||
}
|
||||
$this->out(str_repeat('=', 128), 1, Shell::NORMAL);
|
||||
$this->out(sprintf(
|
||||
'| Legend: %s %s %s %s %s |',
|
||||
'<error>█: Warnings</error>',
|
||||
'<warning>█: Connectedness</warning>',
|
||||
'<question>█: ATT&CK context</question>',
|
||||
'<info>█: Other Context</info>',
|
||||
str_repeat(' ', 52)
|
||||
), 1, Shell::NORMAL);
|
||||
$this->out(str_repeat('=', 128), 1, Shell::NORMAL);
|
||||
file_put_contents(APP . 'tmp/report.json', json_encode($results, JSON_PRETTY_PRINT));
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Reset a password
|
||||
*
|
||||
* arg0 = email
|
||||
* arg1 = new password
|
||||
* @deprecated Use UserShell instead
|
||||
*/
|
||||
class PasswordShell extends AppShell {
|
||||
|
||||
public $uses = array('User');
|
||||
|
||||
public function main()
|
||||
{
|
||||
$this->err('This method is deprecated. Next time please use `cake user change_pw [user] [password]` command.');
|
||||
|
||||
if (!isset($this->args[0]) || empty($this->args[0]) || !isset($this->args[1]) || empty($this->args[1])) echo 'MISP password reset command line tool.' . PHP_EOL . 'To assign a new password for a user:' . PHP_EOL . APP . 'Console/cake Password [email] [password]' . PHP_EOL;
|
||||
else {
|
||||
// get the users that need their password hashed
|
||||
$results = $this->User->find('first', array('conditions' => array('email' => $this->args[0]), 'recursive' => -1));
|
||||
if (empty($results)) {
|
||||
echo 'User not found. Make sure you use the correct syntax: /var/www/MISP/app/Console/cake Password [email] [password]' . PHP_EOL;
|
||||
exit;
|
||||
}
|
||||
$results['User']['password'] = $this->args[1];
|
||||
$results['User']['confirm_password'] = $this->args[1];
|
||||
$change_pw = 1;
|
||||
if (!empty($this->params['override_password_change'])) {
|
||||
$change_pw = 0;
|
||||
}
|
||||
$results['User']['change_pw'] = $change_pw;
|
||||
if (!$this->User->save($results)) {
|
||||
echo 'Could not update account for User.id = ', $results['User']['id'], PHP_EOL;
|
||||
echo json_encode($this->User->validationErrors) . PHP_EOL;
|
||||
$this->out(print_r($this->User->invalidFields(), true));
|
||||
}
|
||||
echo 'Updated ', PHP_EOL;
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
$parser->addOption('override_password_change', array(
|
||||
'short' => 'o',
|
||||
'help' => __('override password change'),
|
||||
'boolean' => 1
|
||||
));
|
||||
return $parser;
|
||||
}
|
||||
}
|
|
@ -1,827 +0,0 @@
|
|||
<?php
|
||||
App::uses('Folder', 'Utility');
|
||||
App::uses('File', 'Utility');
|
||||
require_once 'AppShell.php';
|
||||
|
||||
/**
|
||||
* @property Job $Job
|
||||
* @property Server $Server
|
||||
* @property Feed $Feed
|
||||
* @property User $User
|
||||
*/
|
||||
class ServerShell extends AppShell
|
||||
{
|
||||
public $uses = array('Server', 'Task', 'Job', 'User', 'Feed', 'TaxiiServer');
|
||||
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
$parser->addSubcommand('fetchIndex', [
|
||||
'help' => __('Fetch remote instance event index.'),
|
||||
'parser' => array(
|
||||
'arguments' => array(
|
||||
'server_id' => ['help' => __('Remote server ID.'), 'required' => true],
|
||||
),
|
||||
)
|
||||
]);
|
||||
return $parser;
|
||||
}
|
||||
|
||||
public function list()
|
||||
{
|
||||
$servers = $this->Server->find('all', [
|
||||
'fields' => ['Server.id', 'Server.name', 'Server.url'],
|
||||
'recursive' => 0
|
||||
]);
|
||||
foreach ($servers as $server) {
|
||||
echo sprintf(
|
||||
'%sServer #%s :: %s :: %s',
|
||||
PHP_EOL,
|
||||
$server['Server']['id'],
|
||||
$server['Server']['name'],
|
||||
$server['Server']['url']
|
||||
);
|
||||
}
|
||||
echo PHP_EOL;
|
||||
}
|
||||
|
||||
public function listServers()
|
||||
{
|
||||
$servers = $this->Server->find('all', [
|
||||
'fields' => ['Server.id', 'Server.name', 'Server.url'],
|
||||
'recursive' => 0
|
||||
]);
|
||||
$res = ['servers' => array_column($servers, 'Server')];
|
||||
echo $this->json($res) . PHP_EOL;
|
||||
}
|
||||
|
||||
public function test()
|
||||
{
|
||||
if (empty($this->args[0])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Test'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$serverId = intval($this->args[0]);
|
||||
$server = $this->getServer($serverId);
|
||||
|
||||
$res = $this->Server->runConnectionTest($server, false);
|
||||
echo $this->json($res) . PHP_EOL;
|
||||
}
|
||||
|
||||
public function fetchIndex()
|
||||
{
|
||||
$serverId = intval($this->args[0]);
|
||||
$server = $this->getServer($serverId);
|
||||
$serverSync = new ServerSyncTool($server, $this->Server->setupSyncRequest($server));
|
||||
$index = $this->Server->getEventIndexFromServer($serverSync);
|
||||
echo $this->json($index) . PHP_EOL;
|
||||
}
|
||||
|
||||
public function pullAll()
|
||||
{
|
||||
if (empty($this->args[0])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['PullAll'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$userId = $this->args[0];
|
||||
$user = $this->getUser($userId);
|
||||
|
||||
if (!empty($this->args[1])) {
|
||||
$technique = $this->args[1];
|
||||
} else {
|
||||
$technique = 'full';
|
||||
}
|
||||
|
||||
$servers = $this->Server->find('list', array(
|
||||
'conditions' => array('Server.pull' => 1),
|
||||
'recursive' => -1,
|
||||
'order' => 'Server.priority',
|
||||
'fields' => array('Server.id', 'Server.name'),
|
||||
));
|
||||
|
||||
foreach ($servers as $serverId => $serverName) {
|
||||
$jobId = $this->Job->createJob($user, Job::WORKER_DEFAULT, 'pull', "Server: $serverId", 'Pulling.');
|
||||
$backgroundJobId = $this->getBackgroundJobsTool()->enqueue(
|
||||
BackgroundJobsTool::DEFAULT_QUEUE,
|
||||
BackgroundJobsTool::CMD_SERVER,
|
||||
[
|
||||
'pull',
|
||||
$user['id'],
|
||||
$serverId,
|
||||
$technique,
|
||||
$jobId,
|
||||
],
|
||||
true,
|
||||
$jobId
|
||||
);
|
||||
|
||||
$this->out("Enqueued pulling from $serverName server as job $backgroundJobId");
|
||||
}
|
||||
}
|
||||
|
||||
public function pull()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[1])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Pull'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$userId = $this->args[0];
|
||||
$user = $this->getUser($userId);
|
||||
$serverId = $this->args[1];
|
||||
$server = $this->getServer($serverId);
|
||||
if (!empty($this->args[2])) {
|
||||
$technique = $this->args[2];
|
||||
} else {
|
||||
$technique = 'full';
|
||||
}
|
||||
if (!empty($this->args[3])) {
|
||||
$jobId = $this->args[3];
|
||||
} else {
|
||||
$jobId = $this->Job->createJob($user, Job::WORKER_DEFAULT, 'pull', 'Server: ' . $serverId, 'Pulling.');
|
||||
}
|
||||
$force = false;
|
||||
if (!empty($this->args[4]) && $this->args[4] === 'force') {
|
||||
$force = true;
|
||||
}
|
||||
try {
|
||||
$result = $this->Server->pull($user, $technique, $server, $jobId, $force);
|
||||
if (is_array($result)) {
|
||||
$message = __('Pull completed. %s events pulled, %s events could not be pulled, %s proposals pulled, %s sightings pulled, %s clusters pulled.', count($result[0]), count($result[1]), $result[2], $result[3], $result[4]);
|
||||
$this->Job->saveStatus($jobId, true, $message);
|
||||
} else {
|
||||
$message = __('ERROR: %s', $result);
|
||||
$this->Job->saveStatus($jobId, false, $message);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->Job->saveStatus($jobId, false, __('ERROR: %s', $e->getMessage()));
|
||||
throw $e;
|
||||
}
|
||||
echo $message . PHP_EOL;
|
||||
}
|
||||
|
||||
public function push()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[1])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Push'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$userId = $this->args[0];
|
||||
$user = $this->getUser($userId);
|
||||
$serverId = $this->args[1];
|
||||
$server = $this->getServer($serverId);
|
||||
$technique = empty($this->args[2]) ? 'full' : $this->args[2];
|
||||
if (!empty($this->args[3])) {
|
||||
$jobId = $this->args[3];
|
||||
} else {
|
||||
$jobId = $this->Job->createJob($user, Job::WORKER_DEFAULT, 'push', 'Server: ' . $serverId, 'Pushing.');
|
||||
}
|
||||
$this->Job->read(null, $jobId);
|
||||
|
||||
App::uses('SyncTool', 'Tools');
|
||||
$syncTool = new SyncTool();
|
||||
$HttpSocket = $syncTool->setupHttpSocket($server);
|
||||
$result = $this->Server->push($serverId, $technique, $jobId, $HttpSocket, $user);
|
||||
|
||||
if ($result !== true && !is_array($result)) {
|
||||
$message = 'Job failed. Reason: ' . $result;
|
||||
$this->Job->saveStatus($jobId, false, $message);
|
||||
} else {
|
||||
$message = 'Job done.';
|
||||
$this->Job->saveStatus($jobId, true, $message);
|
||||
}
|
||||
|
||||
if (isset($this->args[4])) {
|
||||
$this->Task->id = $this->args[5];
|
||||
$message = 'Job(s) started at ' . date('d/m/Y - H:i:s') . '.';
|
||||
$this->Task->saveField('message', $message);
|
||||
echo $message . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
public function pushAll()
|
||||
{
|
||||
$userId = $this->args[0];
|
||||
$user = $this->getUser($userId);
|
||||
|
||||
$technique = isset($this->args[1]) ? $this->args[1] : 'full';
|
||||
|
||||
$servers = $this->Server->find('list', array(
|
||||
'conditions' => array('Server.push' => 1),
|
||||
'recursive' => -1,
|
||||
'order' => 'Server.priority',
|
||||
'fields' => array('Server.id', 'Server.name'),
|
||||
));
|
||||
|
||||
foreach ($servers as $serverId => $serverName) {
|
||||
$jobId = $this->getBackgroundJobsTool()->enqueue(
|
||||
BackgroundJobsTool::DEFAULT_QUEUE,
|
||||
BackgroundJobsTool::CMD_SERVER,
|
||||
[
|
||||
'push',
|
||||
$user['id'],
|
||||
$serverId,
|
||||
$technique
|
||||
]
|
||||
);
|
||||
|
||||
$this->out("Enqueued pushing from $serverName server as job $jobId");
|
||||
}
|
||||
}
|
||||
|
||||
public function listFeeds()
|
||||
{
|
||||
$fields = [
|
||||
'id' => 3,
|
||||
'source_format' => 10,
|
||||
'provider' => 15,
|
||||
'url' => 50,
|
||||
'enabled' => 8,
|
||||
'caching_enabled' => 7
|
||||
];
|
||||
$feeds = $this->Feed->find('all', [
|
||||
'recursive' => -1,
|
||||
'fields' => array_keys($fields)
|
||||
]);
|
||||
$outputStyle = (empty($this->args[0]) || $this->args[0] === 'json') ? 'json' : 'table';
|
||||
if ($outputStyle === 'table') {
|
||||
$this->out(str_repeat('=', 114));
|
||||
$this->out(sprintf(
|
||||
'| %s | %s | %s | %s | %s | %s |',
|
||||
str_pad('ID', $fields['id'], ' ', STR_PAD_RIGHT),
|
||||
str_pad('Format', $fields['source_format'], ' ', STR_PAD_RIGHT),
|
||||
str_pad('Provider', $fields['provider'], ' ', STR_PAD_RIGHT),
|
||||
str_pad('Url', $fields['url'], ' ', STR_PAD_RIGHT),
|
||||
str_pad('Fetching', $fields['enabled'], ' ', STR_PAD_RIGHT),
|
||||
str_pad('Caching', $fields['caching_enabled'], ' ', STR_PAD_RIGHT)
|
||||
), 1, Shell::NORMAL);
|
||||
$this->out(str_repeat('=', 114));
|
||||
foreach ($feeds as $feed) {
|
||||
$this->out(sprintf(
|
||||
'| %s | %s | %s | %s | %s | %s |',
|
||||
str_pad($feed['Feed']['id'], $fields['id'], ' ', STR_PAD_RIGHT),
|
||||
str_pad($feed['Feed']['source_format'], $fields['source_format'], ' ', STR_PAD_RIGHT),
|
||||
str_pad(mb_substr($feed['Feed']['provider'], 0, 13), $fields['provider'], ' ', STR_PAD_RIGHT),
|
||||
str_pad(
|
||||
mb_substr($feed['Feed']['url'], 0, 48),
|
||||
$fields['url'],
|
||||
' ',
|
||||
STR_PAD_RIGHT
|
||||
),
|
||||
$feed['Feed']['enabled'] ?
|
||||
'<info>' . str_pad(__('Yes'), $fields['enabled'], ' ', STR_PAD_RIGHT) . '</info>':
|
||||
str_pad(__('No'), $fields['enabled'], ' ', STR_PAD_RIGHT),
|
||||
$feed['Feed']['caching_enabled'] ?
|
||||
'<info>' . str_pad(__('Yes'), $fields['caching_enabled'], ' ', STR_PAD_RIGHT) . '</info>':
|
||||
str_pad(__('No'), $fields['caching_enabled'], ' ', STR_PAD_RIGHT)
|
||||
), 1, Shell::NORMAL);
|
||||
}
|
||||
$this->out(str_repeat('=', 114));
|
||||
} else {
|
||||
$this->out(json_encode($feeds, JSON_PRETTY_PRINT));
|
||||
}
|
||||
}
|
||||
|
||||
public function viewFeed()
|
||||
{
|
||||
if (empty($this->args[0])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['View Feed'] . PHP_EOL);
|
||||
}
|
||||
$feed = $this->Feed->find('first', [
|
||||
'conditions' => [
|
||||
'id' => $this->args[0]
|
||||
],
|
||||
'recursive' => -1
|
||||
]);
|
||||
if (empty($feed)) {
|
||||
throw new NotFoundException(__('Invalid feed.'));
|
||||
}
|
||||
$outputStyle = (empty($this->args[1]) || $this->args[1] === 'json') ? 'json' : 'table';
|
||||
if ($outputStyle === 'table') {
|
||||
$this->out(str_repeat('=', 114));
|
||||
foreach ($feed['Feed'] as $field => $value) {
|
||||
$this->out(sprintf(
|
||||
'| %s | %s |',
|
||||
str_pad($field, 20, ' ', STR_PAD_RIGHT),
|
||||
str_pad($value, 87)
|
||||
), 1, Shell::NORMAL);
|
||||
}
|
||||
$this->out(str_repeat('=', 114));
|
||||
} else {
|
||||
$this->out(json_encode($feed));
|
||||
}
|
||||
}
|
||||
|
||||
public function toggleFeed()
|
||||
{
|
||||
if (empty($this->args[0])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Toggle feed fetching'] . PHP_EOL);
|
||||
}
|
||||
$feed = $this->Feed->find('first', [
|
||||
'conditions' => [
|
||||
'id' => $this->args[0]
|
||||
],
|
||||
'recursive' => -1
|
||||
]);
|
||||
if (empty($feed)) {
|
||||
throw new NotFoundException(__('Invalid feed.'));
|
||||
}
|
||||
$feed['Feed']['enabled'] = ($feed['Feed']['enabled']) ? 0 : 1;
|
||||
if ($this->Feed->save($feed)) {
|
||||
$this->out(__('Feed fetching %s for feed %s', ($feed['Feed']['enabled'] ? __('enabled') : __('disabled')), $feed['Feed']['id']));
|
||||
} else {
|
||||
$this->out(__('Could not toggle fetching for feed %s', $feed['Feed']['id']));
|
||||
}
|
||||
}
|
||||
|
||||
public function toggleFeedCaching()
|
||||
{
|
||||
if (empty($this->args[0])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Toggle feed caching'] . PHP_EOL);
|
||||
}
|
||||
$feed = $this->Feed->find('first', [
|
||||
'conditions' => [
|
||||
'id' => $this->args[0]
|
||||
],
|
||||
'recursive' => -1
|
||||
]);
|
||||
if (empty($feed)) {
|
||||
throw new NotFoundException(__('Invalid feed.'));
|
||||
}
|
||||
$feed['Feed']['caching_enabled'] = ($feed['Feed']['caching_enabled']) ? 0 : 1;
|
||||
if ($this->Feed->save($feed)) {
|
||||
$this->out(__('Feed caching %s for feed %s', ($feed['Feed']['enabled'] ? __('enabled') : __('disabled')), $feed['Feed']['id']));
|
||||
} else {
|
||||
$this->out(__('Could not toggle caching for feed %s', $feed['Feed']['id']));
|
||||
}
|
||||
}
|
||||
|
||||
public function loadDefaultFeeds()
|
||||
{
|
||||
$this->Feed->load_default_feeds();
|
||||
$this->out(__('Default feed metadata loaded.'));
|
||||
}
|
||||
|
||||
public function fetchFeed()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[1])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Fetch feeds as local data'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$userId = $this->args[0];
|
||||
$user = $this->getUser($userId);
|
||||
$feedId = $this->args[1];
|
||||
Configure::write('CurrentUserId', $userId);
|
||||
if (!empty($this->args[2])) {
|
||||
$jobId = $this->args[2];
|
||||
} else {
|
||||
$jobId = $this->Job->createJob($user, Job::WORKER_DEFAULT, 'fetch_feeds', 'Feed: ' . $feedId, 'Starting fetch from Feed.');
|
||||
}
|
||||
if ($feedId === 'all') {
|
||||
$feedIds = $this->Feed->find('column', array(
|
||||
'fields' => array('Feed.id'),
|
||||
'conditions' => array('Feed.enabled' => 1)
|
||||
));
|
||||
$successes = 0;
|
||||
$fails = 0;
|
||||
foreach ($feedIds as $k => $feedId) {
|
||||
$this->Job->saveProgress($jobId, 'Fetching feed: ' . $feedId, 100 * $k / count($feedIds));
|
||||
$result = $this->Feed->downloadFromFeedInitiator($feedId, $user);
|
||||
if ($result) {
|
||||
$successes++;
|
||||
} else {
|
||||
$fails++;
|
||||
}
|
||||
}
|
||||
$message = 'Job done. ' . $successes . ' feeds pulled successfully, ' . $fails . ' feeds could not be pulled.';
|
||||
$this->Job->saveStatus($jobId, true, $message);
|
||||
echo $message . PHP_EOL;
|
||||
} else {
|
||||
$feedEnabled = $this->Feed->hasAny([
|
||||
'Feed.enabled' => 1,
|
||||
'Feed.id' => $feedId,
|
||||
]);
|
||||
if ($feedEnabled) {
|
||||
$result = $this->Feed->downloadFromFeedInitiator($feedId, $user, $jobId);
|
||||
if (!$result) {
|
||||
$this->Job->saveStatus($jobId, false, 'Job failed. See error log for more details.');
|
||||
echo 'Job failed.' . PHP_EOL;
|
||||
} else {
|
||||
$this->Job->saveStatus($jobId, true);
|
||||
echo 'Job done.' . PHP_EOL;
|
||||
}
|
||||
} else {
|
||||
$message = "Feed with ID $feedId not found or not enabled.";
|
||||
$this->Job->saveStatus($jobId, false, $message);
|
||||
echo $message . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function cacheServer()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[1])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Cache server'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$userId = $this->args[0];
|
||||
$user = $this->getUser($userId);
|
||||
$scope = $this->args[1];
|
||||
if (!empty($this->args[2])) {
|
||||
$jobId = $this->args[2];
|
||||
} else {
|
||||
$this->Job->create();
|
||||
$data = array(
|
||||
'worker' => 'default',
|
||||
'job_type' => 'cache_servers',
|
||||
'job_input' => 'Server: ' . $scope,
|
||||
'status' => 0,
|
||||
'retries' => 0,
|
||||
'org' => $user['Organisation']['name'],
|
||||
'message' => 'Starting server caching.',
|
||||
);
|
||||
$this->Job->save($data);
|
||||
$jobId = $this->Job->id;
|
||||
}
|
||||
$result = $this->Server->cacheServerInitiator($user, $scope, $jobId);
|
||||
if ($result !== true) {
|
||||
$message = 'Job Failed. Reason: ' . $result;
|
||||
$this->Job->saveStatus($jobId, false, $message);
|
||||
} else {
|
||||
$message = 'Job done.';
|
||||
$this->Job->saveStatus($jobId, true, $message);
|
||||
}
|
||||
echo $message . PHP_EOL;
|
||||
}
|
||||
|
||||
public function cacheServerAll()
|
||||
{
|
||||
$userId = $this->args[0];
|
||||
$user = $this->getUser($userId);
|
||||
|
||||
$servers = $this->Server->find('list', array(
|
||||
'conditions' => array('Server.pull' => 1),
|
||||
'recursive' => -1,
|
||||
'order' => 'Server.priority',
|
||||
'fields' => array('Server.id', 'Server.name'),
|
||||
));
|
||||
|
||||
foreach ($servers as $serverId => $serverName) {
|
||||
$jobId = $this->getBackgroundJobsTool()->enqueue(
|
||||
BackgroundJobsTool::DEFAULT_QUEUE,
|
||||
BackgroundJobsTool::CMD_SERVER,
|
||||
[
|
||||
'cacheServer',
|
||||
$user['id'],
|
||||
$serverId
|
||||
]
|
||||
);
|
||||
|
||||
$this->out("Enqueued cacheServer from $serverName server as job $jobId");
|
||||
}
|
||||
}
|
||||
|
||||
public function cacheFeed()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[1])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Cache feeds for quick lookups'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$userId = $this->args[0];
|
||||
$user = $this->getUser($userId);
|
||||
$scope = $this->args[1];
|
||||
if (!empty($this->args[2])) {
|
||||
$jobId = $this->args[2];
|
||||
} else {
|
||||
$jobId = $this->Job->createJob($user, Job::WORKER_DEFAULT, 'cache_feeds', 'Feed: ' . $scope, 'Starting feed caching.');
|
||||
}
|
||||
try {
|
||||
$result = $this->Feed->cacheFeedInitiator($user, $jobId, $scope);
|
||||
} catch (Exception $e) {
|
||||
CakeLog::error($e->getMessage());
|
||||
$result = false;
|
||||
}
|
||||
|
||||
if ($result === false) {
|
||||
$message = __('Job failed. See error logs for more details.');
|
||||
$this->Job->saveStatus($jobId, false, $message);
|
||||
|
||||
} else {
|
||||
$total = $result['successes'] + $result['fails'];
|
||||
$message = __n(
|
||||
'%s feed from %s cached. Failed: %s',
|
||||
'%s feeds from %s cached. Failed: %s',
|
||||
$result['successes'], $result['successes'], $total, $result['fails']
|
||||
);
|
||||
if ($result['fails'] > 0) {
|
||||
$message .= ' ' . __('See error logs for more details.');
|
||||
}
|
||||
$this->Job->saveStatus($jobId, true, $message);
|
||||
}
|
||||
echo $message . PHP_EOL;
|
||||
}
|
||||
|
||||
public function enqueuePull()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Enqueue pull'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$timestamp = $this->args[0];
|
||||
$userId = $this->args[1];
|
||||
$taskId = $this->args[2];
|
||||
$task = $this->Task->read(null, $taskId);
|
||||
if ($timestamp != $task['Task']['next_execution_time']) {
|
||||
return;
|
||||
}
|
||||
if ($task['Task']['timer'] > 0) $this->Task->reQueue($task, 'default', 'ServerShell', 'enqueuePull', $userId, $taskId);
|
||||
$user = $this->User->getAuthUser($userId);
|
||||
$servers = $this->Server->find('all', array('recursive' => -1, 'conditions' => array('pull' => 1)));
|
||||
$count = count($servers);
|
||||
$failCount = 0;
|
||||
foreach ($servers as $k => $server) {
|
||||
$this->Job->create();
|
||||
$data = array(
|
||||
'worker' => 'default',
|
||||
'job_type' => 'pull',
|
||||
'job_input' => 'Server: ' . $server['Server']['id'],
|
||||
'retries' => 0,
|
||||
'org' => $user['Organisation']['name'],
|
||||
'org_id' => $user['org_id'],
|
||||
'process_id' => 'Part of scheduled pull',
|
||||
'message' => 'Pulling.',
|
||||
);
|
||||
$this->Job->save($data);
|
||||
$jobId = $this->Job->id;
|
||||
$result = $this->Server->pull($user, 'full', $server, $jobId);
|
||||
$this->Job->save(array(
|
||||
'id' => $jobId,
|
||||
'message' => 'Job done.',
|
||||
'progress' => 100,
|
||||
'status' => 4
|
||||
));
|
||||
if (is_numeric($result[0])) {
|
||||
switch ($result[0]) {
|
||||
case '1' :
|
||||
$this->Job->saveField('message', 'Not authorised. This is either due to an invalid auth key, or due to the sync user not having authentication permissions enabled on the remote server.');
|
||||
break;
|
||||
case '2' :
|
||||
$this->Job->saveField('message', $result[1]);
|
||||
break;
|
||||
case '3' :
|
||||
$this->Job->saveField('message', 'Sorry, incremental pushes are not yet implemented.');
|
||||
break;
|
||||
case '4' :
|
||||
$this->Job->saveField('message', 'Invalid technique chosen.');
|
||||
break;
|
||||
|
||||
}
|
||||
$failCount++;
|
||||
}
|
||||
}
|
||||
$this->Task->id = $task['Task']['id'];
|
||||
$this->Task->saveField('message', count($servers) . ' job(s) completed at ' . date('d/m/Y - H:i:s') . '. Failed jobs: ' . $failCount . '/' . $count);
|
||||
}
|
||||
|
||||
public function enqueueFeedFetch()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Enqueue feed fetch'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$timestamp = $this->args[0];
|
||||
$userId = $this->args[1];
|
||||
$taskId = $this->args[2];
|
||||
$task = $this->Task->read(null, $taskId);
|
||||
if ($timestamp != $task['Task']['next_execution_time']) {
|
||||
return;
|
||||
}
|
||||
if ($task['Task']['timer'] > 0) $this->Task->reQueue($task, 'default', 'ServerShell', 'enqueueFeedFetch', $userId, $taskId);
|
||||
$user = $this->User->getAuthUser($userId);
|
||||
$failCount = 0;
|
||||
$feeds = $this->Feed->find('all', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('enabled' => true)
|
||||
));
|
||||
foreach ($feeds as $k => $feed) {
|
||||
$this->Job->create();
|
||||
$data = array(
|
||||
'worker' => 'default',
|
||||
'job_type' => 'feed_fetch',
|
||||
'job_input' => 'Feed: ' . $feed['Feed']['id'],
|
||||
'retries' => 0,
|
||||
'org' => $user['Organisation']['name'],
|
||||
'org_id' => $user['org_id'],
|
||||
'process_id' => 'Part of scheduled feed fetch',
|
||||
'message' => 'Pulling.',
|
||||
);
|
||||
$this->Job->save($data);
|
||||
$jobId = $this->Job->id;
|
||||
$result = $this->Feed->downloadFromFeedInitiator($feed['Feed']['id'], $user, $jobId);
|
||||
$this->Job->save(array(
|
||||
'message' => 'Job done.',
|
||||
'progress' => 100,
|
||||
'status' => 4
|
||||
));
|
||||
if ($result !== true) {
|
||||
$this->Job->saveField('message', 'Could not fetch feed.');
|
||||
$failCount++;
|
||||
}
|
||||
}
|
||||
$this->Task->id = $task['Task']['id'];
|
||||
$this->Task->saveField('message', count($feeds) . ' job(s) completed at ' . date('d/m/Y - H:i:s') . '. Failed jobs: ' . $failCount . '/' . count($feeds));
|
||||
}
|
||||
|
||||
public function enqueueFeedCache()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Enqueue feed cache'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$timestamp = $this->args[0];
|
||||
$userId = $this->args[1];
|
||||
$taskId = $this->args[2];
|
||||
$task = $this->Task->read(null, $taskId);
|
||||
if ($timestamp != $task['Task']['next_execution_time']) {
|
||||
return;
|
||||
}
|
||||
if ($task['Task']['timer'] > 0) $this->Task->reQueue($task, 'default', 'ServerShell', 'enqueueFeedCache', $userId, $taskId);
|
||||
$user = $this->User->getAuthUser($userId);
|
||||
$this->Job->create();
|
||||
$data = array(
|
||||
'worker' => 'default',
|
||||
'job_type' => 'feed_cache',
|
||||
'job_input' => '',
|
||||
'retries' => 0,
|
||||
'org' => $user['Organisation']['name'],
|
||||
'org_id' => $user['org_id'],
|
||||
'process_id' => 'Part of scheduled feed caching',
|
||||
'message' => 'Caching.',
|
||||
);
|
||||
$this->Job->save($data);
|
||||
$jobId = $this->Job->id;
|
||||
try {
|
||||
$result = $this->Feed->cacheFeedInitiator($user, $jobId, 'all');
|
||||
} catch (Exception $e) {
|
||||
CakeLog::error($e->getMessage());
|
||||
$result = false;
|
||||
}
|
||||
|
||||
if ($result === false) {
|
||||
$message = __('Job failed. See error logs for more details.');
|
||||
$this->Job->saveStatus($jobId, false, $message);
|
||||
|
||||
} else {
|
||||
$total = $result['successes'] + $result['fails'];
|
||||
$message = __n(
|
||||
'%s feed from %s cached. Failed: %s',
|
||||
'%s feeds from %s cached. Failed: %s',
|
||||
$result['successes'], $total, $result['fails']
|
||||
);
|
||||
if ($result['fails'] > 0) {
|
||||
$message .= ' ' . __('See error logs for more details.');
|
||||
}
|
||||
$this->Job->saveStatus($jobId, true, $message);
|
||||
}
|
||||
|
||||
$this->Task->id = $task['Task']['id'];
|
||||
$this->Task->saveField('message', 'Job completed at ' . date('d/m/Y - H:i:s'));
|
||||
}
|
||||
|
||||
public function enqueuePush()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Enqueue push'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$timestamp = $this->args[0];
|
||||
$taskId = $this->args[1];
|
||||
$userId = $this->args[2];
|
||||
$this->Task->id = $taskId;
|
||||
$task = $this->Task->read(null, $taskId);
|
||||
if ($timestamp != $task['Task']['next_execution_time']) {
|
||||
return;
|
||||
}
|
||||
if ($task['Task']['timer'] > 0) $this->Task->reQueue($task, 'default', 'ServerShell', 'enqueuePush', $userId, $taskId);
|
||||
|
||||
$this->User->recursive = -1;
|
||||
$user = $this->User->getAuthUser($userId);
|
||||
$servers = $this->Server->find('all', array('recursive' => -1, 'conditions' => array('push' => 1)));
|
||||
foreach ($servers as $k => $server) {
|
||||
$this->Job->create();
|
||||
$data = array(
|
||||
'worker' => 'default',
|
||||
'job_type' => 'push',
|
||||
'job_input' => 'Server: ' . $server['Server']['id'],
|
||||
'retries' => 0,
|
||||
'org' => $user['Organisation']['name'],
|
||||
'org_id' => $user['org_id'],
|
||||
'process_id' => 'Part of scheduled push',
|
||||
'message' => 'Pushing.',
|
||||
);
|
||||
$this->Job->save($data);
|
||||
$jobId = $this->Job->id;
|
||||
App::uses('SyncTool', 'Tools');
|
||||
$syncTool = new SyncTool();
|
||||
$HttpSocket = $syncTool->setupHttpSocket($server);
|
||||
$this->Server->push($server['Server']['id'], 'full', $jobId, $HttpSocket, $user);
|
||||
}
|
||||
$this->Task->id = $task['Task']['id'];
|
||||
$this->Task->saveField('message', count($servers) . ' job(s) completed at ' . date('d/m/Y - H:i:s') . '.');
|
||||
}
|
||||
|
||||
public function sendPeriodicSummaryToUsers()
|
||||
{
|
||||
$periods = $this->__getPeriodsForToday();
|
||||
$start_time = time();
|
||||
echo __n('Started periodic summary generation for the %s period', 'Started periodic summary generation for periods: %s', count($periods), implode(', ', $periods)) . PHP_EOL;
|
||||
foreach ($periods as $period) {
|
||||
$users = $this->User->getSubscribedUsersForPeriod($period);
|
||||
echo __n('%s user has subscribed for the `%s` period', '%s users has subscribed for the `%s` period', count($users), count($users), $period) . PHP_EOL;
|
||||
foreach ($users as $user) {
|
||||
echo __('Sending `%s` report to `%s`', $period, $user['User']['email']) . PHP_EOL;
|
||||
$emailTemplate = $this->User->generatePeriodicSummary($user['User']['id'], $period, false);
|
||||
if ($emailTemplate === null) {
|
||||
continue; // no new event for this user
|
||||
}
|
||||
$this->User->sendEmail($user, $emailTemplate, false, null);
|
||||
}
|
||||
}
|
||||
echo __('All reports sent. Task took %s seconds', time() - $start_time) . PHP_EOL;
|
||||
}
|
||||
|
||||
private function __getPeriodsForToday(): array
|
||||
{
|
||||
$today = new DateTime();
|
||||
$periods = ['daily'];
|
||||
if ($today->format('j') == 1) {
|
||||
$periods[] = 'monthly';
|
||||
}
|
||||
if ($today->format('N') == 1) {
|
||||
$periods[] = 'weekly';
|
||||
}
|
||||
return $periods;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
* @return array
|
||||
*/
|
||||
private function getUser($userId)
|
||||
{
|
||||
$user = $this->User->getAuthUser($userId);
|
||||
if (empty($user)) {
|
||||
$this->error('User ID do not match an existing user.');
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $serverId
|
||||
* @return array
|
||||
*/
|
||||
private function getServer($serverId)
|
||||
{
|
||||
$server = $this->Server->find('first', [
|
||||
'conditions' => ['Server.id' => $serverId],
|
||||
'recursive' => -1,
|
||||
]);
|
||||
if (!$server) {
|
||||
$this->error("Server with ID $serverId doesn't exists.");
|
||||
}
|
||||
return $server;
|
||||
}
|
||||
|
||||
public function push_taxii()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[1])) {
|
||||
die('Usage: ' . $this->Server->command_line_functions['console_automation_tasks']['data']['Push Taxii'] . PHP_EOL);
|
||||
}
|
||||
|
||||
$userId = $this->args[0];
|
||||
$user = $this->getUser($userId);
|
||||
$serverId = $this->args[1];
|
||||
if (!empty($this->args[3])) {
|
||||
$jobId = $this->args[3];
|
||||
} else {
|
||||
$jobId = $this->Job->createJob($user, Job::WORKER_DEFAULT, 'push_taxii', 'Server: ' . $serverId, 'Pushing.');
|
||||
}
|
||||
$this->Job->read(null, $jobId);
|
||||
|
||||
$result = $this->TaxiiServer->push($serverId, $technique, $jobId, $HttpSocket, $user);
|
||||
|
||||
if ($result !== true && !is_array($result)) {
|
||||
$message = 'Job failed. Reason: ' . $result;
|
||||
$this->Job->saveStatus($jobId, false, $message);
|
||||
} else {
|
||||
$message = 'Job done.';
|
||||
$this->Job->saveStatus($jobId, true, $message);
|
||||
}
|
||||
|
||||
if (isset($this->args[4])) {
|
||||
$this->Task->id = $this->args[5];
|
||||
$message = 'Job(s) started at ' . date('d/m/Y - H:i:s') . '.';
|
||||
$this->Task->saveField('message', $message);
|
||||
echo $message . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
App::uses('ProcessTool', 'Tools');
|
||||
|
||||
class StartWorkerShell extends AppShell
|
||||
{
|
||||
/** @var Worker */
|
||||
private $worker;
|
||||
|
||||
/** @var int */
|
||||
private $maxExecutionTime;
|
||||
|
||||
const DEFAULT_MAX_EXECUTION_TIME = 86400; // 1 day
|
||||
|
||||
public function getOptionParser(): ConsoleOptionParser
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
$parser
|
||||
->addArgument('queue', [
|
||||
'help' => 'Name of the queue to process.',
|
||||
'choices' => $this->getBackgroundJobsTool()->getQueues(),
|
||||
'required' => true
|
||||
])
|
||||
->addOption(
|
||||
'maxExecutionTime',
|
||||
[
|
||||
'help' => 'Worker maximum execution time (seconds) before it self-destruct. Zero means unlimited.',
|
||||
'default' => self::DEFAULT_MAX_EXECUTION_TIME,
|
||||
'required' => false
|
||||
]
|
||||
);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
public function main()
|
||||
{
|
||||
$this->worker = new Worker(
|
||||
[
|
||||
'pid' => getmypid(),
|
||||
'queue' => $this->args[0],
|
||||
'user' => ProcessTool::whoami(),
|
||||
]
|
||||
);
|
||||
|
||||
$this->maxExecutionTime = (int)$this->params['maxExecutionTime'];
|
||||
|
||||
CakeLog::info("[WORKER PID: {$this->worker->pid()}][{$this->worker->queue()}] - starting to process background jobs...");
|
||||
|
||||
while (true) {
|
||||
$this->checkMaxExecutionTime();
|
||||
|
||||
$job = $this->getBackgroundJobsTool()->dequeue($this->worker->queue());
|
||||
if ($job) {
|
||||
$this->runJob($job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BackgroundJob $job
|
||||
*/
|
||||
private function runJob(BackgroundJob $job)
|
||||
{
|
||||
CakeLog::info("[WORKER PID: {$this->worker->pid()}][{$this->worker->queue()}] - launching job with ID: {$job->id()}...");
|
||||
|
||||
try {
|
||||
$job->setStatus(BackgroundJob::STATUS_RUNNING);
|
||||
|
||||
$command = implode(' ', array_merge([$job->command()], $job->args()));
|
||||
CakeLog::info("[JOB ID: {$job->id()}] - started command `$command`.");
|
||||
$this->getBackgroundJobsTool()->update($job);
|
||||
|
||||
$job->run();
|
||||
|
||||
if ($job->status() === BackgroundJob::STATUS_COMPLETED) {
|
||||
CakeLog::info("[JOB ID: {$job->id()}] - completed.");
|
||||
} else {
|
||||
CakeLog::error("[JOB ID: {$job->id()}] - failed with error code {$job->returnCode()}. STDERR: {$job->error()}. STDOUT: {$job->output()}.");
|
||||
}
|
||||
} catch (Exception $exception) {
|
||||
CakeLog::error("[WORKER PID: {$this->worker->pid()}][{$this->worker->queue()}] - job ID: {$job->id()} failed with exception: {$exception->getMessage()}");
|
||||
$job->setStatus(BackgroundJob::STATUS_FAILED);
|
||||
}
|
||||
$this->getBackgroundJobsTool()->update($job);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if worker maximum execution time is reached, and exits if so.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function checkMaxExecutionTime()
|
||||
{
|
||||
if ($this->maxExecutionTime === 0) {
|
||||
return;
|
||||
}
|
||||
if ((time() - $this->worker->createdAt()) > $this->maxExecutionTime) {
|
||||
CakeLog::info("[WORKER PID: {$this->worker->pid()}][{$this->worker->queue()}] - worker max execution time reached, exiting gracefully worker...");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,278 +0,0 @@
|
|||
<?php
|
||||
class StatisticsShell extends AppShell {
|
||||
|
||||
public $uses = array('Event', 'User', 'Organisation', 'Log', 'Correlation');
|
||||
|
||||
public function contributors()
|
||||
{
|
||||
$from = empty($this->args[0]) ? null : $this->args[0];
|
||||
$to = empty($this->args[1]) ? null : $this->args[1];
|
||||
$repos = [
|
||||
ROOT,
|
||||
ROOT . '/PyMISP',
|
||||
ROOT . '/app/files/misp-galaxy',
|
||||
ROOT . '/app/files/misp-objects',
|
||||
ROOT . '/app/files/noticelists',
|
||||
ROOT . '/app/files/taxonomies',
|
||||
ROOT . '/app/files/warninglists'
|
||||
];
|
||||
$results = [];
|
||||
foreach ($repos as $repo) {
|
||||
$results = $this->extract($results, $repo, $from, $to);
|
||||
}
|
||||
echo PHP_EOL . PHP_EOL . array_sum($results) . ' commits from ' . count($results) . ' unique contributors.' . PHP_EOL . PHP_EOL;
|
||||
arsort($results);
|
||||
foreach ($results as $email => $count) {
|
||||
echo $email . ': ' . $count . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
private function extract($results, $repo, $from, $to)
|
||||
{
|
||||
$data = shell_exec(
|
||||
sprintf(
|
||||
'git --git-dir ' . $repo . '/.git shortlog -sne %s %s',
|
||||
$from ? (sprintf('--since="%s"', $from)) : '',
|
||||
$to ? (sprintf('--since="%s"', $to)) : ''
|
||||
)
|
||||
);
|
||||
$data = explode(PHP_EOL, $data);
|
||||
foreach ($data as $line) {
|
||||
$line = trim($line);
|
||||
if (empty($line)) {
|
||||
continue;
|
||||
}
|
||||
$email = null;
|
||||
$count = null;
|
||||
preg_match('/\<.+\>/', $line, $email);
|
||||
$email = trim($email[0], '<>');
|
||||
preg_match('/^[0-9]+/', $line, $count);
|
||||
if (isset($results[$email])) {
|
||||
$results[$email] += $count[0];
|
||||
} else {
|
||||
$results[$email] = $count[0];
|
||||
}
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function analyse_slow_logs()
|
||||
{
|
||||
$path = $this->args[0];
|
||||
$raw = file_get_contents($path);
|
||||
$raw = explode("\n", $raw);
|
||||
$data = [
|
||||
'users' => [],
|
||||
'non_sync_action_users' => [],
|
||||
'endpoints' => []
|
||||
];
|
||||
$this->User = ClassRegistry::init('User');
|
||||
$users = $this->User->find('list', [
|
||||
'fields' => ['id', 'email']
|
||||
]);
|
||||
foreach ($raw as $line) {
|
||||
if (empty($line)) {
|
||||
continue;
|
||||
}
|
||||
if ($line[0] === '/' && $line[1] === '*') {
|
||||
$temp = preg_match('/\/\*\s\[User\:\s([0-9]+)\]/', $line, $matches);
|
||||
if (!empty($matches[1])) {
|
||||
$user = $matches[1];
|
||||
if (isset($data['users'][$user])) {
|
||||
$data['users'][$user] += 1;
|
||||
} else {
|
||||
$data['users'][$user] = 1;
|
||||
}
|
||||
}
|
||||
$temp = preg_match('/\]\s([a-z\:\s]*)/', $line, $matches);
|
||||
if (!empty($matches[1])) {
|
||||
$endpoint = $matches[1];
|
||||
$endpoint = trim($endpoint);
|
||||
if (isset($data['endpoints'][$endpoint])) {
|
||||
$data['endpoints'][$endpoint] += 1;
|
||||
} else {
|
||||
$data['endpoints'][$endpoint] = 1;
|
||||
}
|
||||
}
|
||||
if (!in_array($endpoint, ['events :: add', 'events :: edit', 'events :: index'])) {
|
||||
if (isset($data['non_sync_action_users'][$user])) {
|
||||
$data['non_sync_action_users'][$user] += 1;
|
||||
} else {
|
||||
$data['non_sync_action_users'][$user] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
arsort($data['endpoints']);
|
||||
arsort($data['users']);
|
||||
arsort($data['non_sync_action_users']);
|
||||
echo "\n\n==================================\nCount | User\n";
|
||||
echo "\n\n==================================\nSlow queries by user general\n==================================\nCount | User | Email\n";
|
||||
foreach ($data['users'] as $user_id => $count) {
|
||||
echo sprintf(
|
||||
"%s | %s | %s\n",
|
||||
str_pad($count, 5),
|
||||
str_pad($user_id, 5),
|
||||
!empty($users[$user_id]) ? $users[$user_id] : ''
|
||||
);
|
||||
}
|
||||
echo "\n\n==================================\nSlow queries by user excluding sync\n==================================\nCount | User | Email\n";
|
||||
foreach ($data['non_sync_action_users'] as $user_id => $count) {
|
||||
echo sprintf(
|
||||
"%s | %s | %s\n",
|
||||
str_pad($count, 5),
|
||||
str_pad($user_id, 5),
|
||||
!empty($users[$user_id]) ? $users[$user_id] : ''
|
||||
);
|
||||
}
|
||||
echo "\n\n==================================\nSlow queries by endpoint\n==================================\nCount | Endpoint\n";
|
||||
foreach ($data['endpoints'] as $endpoint => $count) {
|
||||
echo sprintf(
|
||||
"%s | %s\n",
|
||||
str_pad($count, 5),
|
||||
$endpoint
|
||||
);
|
||||
}
|
||||
echo "==================================\n\n";
|
||||
}
|
||||
|
||||
public function orgEngagement()
|
||||
{
|
||||
$orgs = $this->Organisation->find('list', [
|
||||
'recursive' => -1,
|
||||
'fields' => ['Organisation.id', 'Organisation.id'],
|
||||
'conditions' => ['Organisation.local' => 1]
|
||||
]);
|
||||
$orgs = array_values($orgs);
|
||||
$total_orgs = count($orgs);
|
||||
$data = [];
|
||||
$orgCreations = $this->Log->find('list', [
|
||||
'conditions' => [
|
||||
'model' => 'Organisation',
|
||||
'action' => 'add'
|
||||
],
|
||||
'fields' => ['Log.model_id', 'Log.created']
|
||||
]);
|
||||
$localOrgs = $this->Organisation->find('count', [
|
||||
'conditions' => [
|
||||
'Organisation.local' => 1
|
||||
]
|
||||
]);
|
||||
foreach ($orgs as $k => $org) {
|
||||
echo sprintf(__('Processing organisation %s / %s.%s', $k+1, $total_orgs, PHP_EOL));
|
||||
$temp = [
|
||||
'org_id' => $org
|
||||
];
|
||||
if (empty($orgCreations[$org])) {
|
||||
continue;
|
||||
} else {
|
||||
$temp['org_creation_timestamp'] = strtotime($orgCreations[$org]);
|
||||
}
|
||||
$first_event = $this->Event->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => [
|
||||
'orgc_id' => $org
|
||||
],
|
||||
'order' => ['Event.id ASC'],
|
||||
'fields' => ['Event.id']
|
||||
]);
|
||||
if (empty($first_event)) {
|
||||
continue;
|
||||
}
|
||||
$first_event_creation = $this->Log->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => [
|
||||
'model_id' => $first_event['Event']['id'],
|
||||
'model' => 'Event',
|
||||
'action' => 'add'
|
||||
]
|
||||
]);
|
||||
if (empty($first_event_creation)) {
|
||||
continue;
|
||||
}
|
||||
$temp['first_event_creation'] = strtotime($first_event_creation['Log']['created']);
|
||||
$temp['time_until_first_event'] = $temp['first_event_creation'] - $temp['org_creation_timestamp'];
|
||||
$data[] = $temp;
|
||||
}
|
||||
$average_time_to_first_event = 0;
|
||||
foreach ($data as $org_data) {
|
||||
$average_time_to_first_event += (int)$org_data['time_until_first_event'] / 60 / 60 / 24;
|
||||
}
|
||||
echo PHP_EOL . str_repeat('-', 63) . PHP_EOL;
|
||||
echo __('Total local orgs: %s%s', $localOrgs, PHP_EOL);
|
||||
echo __('Local orgs with event creations: %s%s', count($data), PHP_EOL);
|
||||
echo __('Average days until first event: %s', (int)($average_time_to_first_event / count($data)));
|
||||
echo PHP_EOL . str_repeat('-', 63) . PHP_EOL;
|
||||
}
|
||||
|
||||
public function yearlyOrgGrowth()
|
||||
{
|
||||
$orgCreations = $this->Log->find('list', [
|
||||
'conditions' => [
|
||||
'model' => 'Organisation',
|
||||
'action' => 'add'
|
||||
],
|
||||
'fields' => ['Log.model_id', 'Log.created']
|
||||
]);
|
||||
$localOnly = empty($this->args[0]) ? false : true;
|
||||
if ($localOnly) {
|
||||
$orgs = $this->Organisation->find('list', [
|
||||
'recursive' => -1,
|
||||
'fields' => ['Organisation.id', 'Organisation.local']
|
||||
]);
|
||||
foreach ($orgs as $org_id => $local) {
|
||||
if (!$local && isset($orgCreations[$org_id])) {
|
||||
unset($orgCreations[$org_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$years = [];
|
||||
foreach ($orgCreations as $orgCreation) {
|
||||
$year = substr($orgCreation, 0, 4);
|
||||
if (empty($years[$year])) {
|
||||
$years[$year] = 0;
|
||||
}
|
||||
$years[$year] += 1;
|
||||
}
|
||||
ksort($years);
|
||||
$yearOverYear = [];
|
||||
$previous = 0;
|
||||
echo PHP_EOL . str_repeat('-', 63) . PHP_EOL;
|
||||
echo __('Year over year growth of organisation count.');
|
||||
echo PHP_EOL . str_repeat('-', 63) . PHP_EOL;
|
||||
$currentYear = date("Y");
|
||||
foreach ($years as $year => $count) {
|
||||
$prognosis = '';
|
||||
if ($year == $currentYear) {
|
||||
$percentage_passed = (strtotime(($year +1) . '-01-01') - strtotime(($year) . '-01-01')) / (time() - (strtotime($year . '-01-01')));
|
||||
$prognosis = sprintf(' (%s by the end of the year at current rate)', round($percentage_passed * $count));
|
||||
}
|
||||
echo __('%s: %s %s%s', $year, $count - $previous, $prognosis, PHP_EOL);
|
||||
}
|
||||
echo str_repeat('-', 63) . PHP_EOL;
|
||||
}
|
||||
|
||||
// (R)etrieval (o)f (m)ember (m)etrics (e)valuation (l)ist (f)or (s)tatistics
|
||||
public function rommelfs()
|
||||
{
|
||||
$this->out(json_encode([
|
||||
'events' => $this->Event->find('count'),
|
||||
'attributes' => $this->Event->Attribute->find('count',
|
||||
['conditions' => ['Attribute.deleted' => 0], 'recursive' => -1]
|
||||
),
|
||||
'objects' => $this->Event->Object->find('count',
|
||||
['conditions' => ['Object.deleted' => 0], 'recursive' => -1]
|
||||
),
|
||||
'correlations' => $this->Correlation->find('count') / 2,
|
||||
'users' => $this->User->find('count',
|
||||
['conditions' => ['User.disabled' => 0], 'recursive' => -1]
|
||||
),
|
||||
'local_organisations' => $this->Organisation->find('count',
|
||||
['conditions' => ['Organisation.local' => 1], 'recursive' => -1]
|
||||
),
|
||||
'external_organisations' => $this->Organisation->find('count',
|
||||
['conditions' => ['Organisation.local' => 0], 'recursive' => -1]
|
||||
)
|
||||
], JSON_PRETTY_PRINT));
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
<?php
|
||||
class SupportShell extends AppShell {
|
||||
|
||||
public $uses = array();
|
||||
|
||||
private $__fields = false;
|
||||
private $__countries = array();
|
||||
private $__whitelistedFields = array(
|
||||
'ISO',
|
||||
'ISO3',
|
||||
'Country',
|
||||
'Capital',
|
||||
'Area',
|
||||
'Population',
|
||||
'Continent',
|
||||
'tld',
|
||||
'CurrencyCode',
|
||||
'CurrencyName',
|
||||
'Languages'
|
||||
);
|
||||
|
||||
public function getGeoNames()
|
||||
{
|
||||
$raw = file_get_contents('http://download.geonames.org/export/dump/countryInfo.txt');
|
||||
$raw = explode(PHP_EOL, $raw);
|
||||
$lastCommentLine = '';
|
||||
foreach ($raw as $line) {
|
||||
if (empty($line)) {
|
||||
continue;
|
||||
}
|
||||
if ($line[0] === '#') {
|
||||
$lastCommentLine = $line;
|
||||
} else {
|
||||
if (!$this->__fields) {
|
||||
$this->__setHeaders($lastCommentLine);
|
||||
}
|
||||
$line = preg_split("/[\t]/", $line);
|
||||
$temp = array();
|
||||
foreach ($line as $pos => $value) {
|
||||
$field = $this->__fields[$pos];
|
||||
if (in_array($field, $this->__whitelistedFields)) {
|
||||
$temp[$field] = $value;
|
||||
}
|
||||
}
|
||||
$this->__countries[] = $temp;
|
||||
}
|
||||
}
|
||||
$clusters = array(
|
||||
'authors' => array('geonames.org'),
|
||||
'category' => 'country',
|
||||
'description' => 'Country meta information based on the database provided by geonames.org.',
|
||||
'name' => 'Country',
|
||||
'source' => 'MISP Project',
|
||||
'type' => 'country',
|
||||
'uuid' => '84668357-5a8c-4bdd-9f0f-6b50b2aee4c1',
|
||||
'version' => empty($this->args[0]) ? 1 : intval($this->args[0])
|
||||
);
|
||||
foreach ($this->__countries as $country) {
|
||||
$countryName = $country['Country'];
|
||||
unset($country['Country']);
|
||||
$clusters['values'][] = array(
|
||||
'description' => $countryName,
|
||||
'uuid' => '84668357-5a8c-4bdd-9f0f-6b50b2' . bin2hex($country['ISO3']),
|
||||
'value' => strtolower($countryName),
|
||||
'meta' => $country
|
||||
);
|
||||
}
|
||||
$galaxy = array(
|
||||
'description' => 'Country meta information based on the database provided by geonames.org.',
|
||||
'icon' => 'globe',
|
||||
'name' => 'Country',
|
||||
'namespace' => 'misp',
|
||||
'type' => 'country',
|
||||
'uuid' => '84668357-5a8c-4bdd-9f0f-6b50b2aee4c1',
|
||||
'version' => empty($this->args[0]) ? 1 : intval($this->args[0])
|
||||
);
|
||||
file_put_contents('cluster.json', json_encode($clusters, JSON_PRETTY_PRINT));
|
||||
file_put_contents('galaxy.json', json_encode($galaxy, JSON_PRETTY_PRINT));
|
||||
echo PHP_EOL . PHP_EOL . 'cluster.json and galaxy.json created.' . PHP_EOL . PHP_EOL;
|
||||
}
|
||||
|
||||
private function __setHeaders($line)
|
||||
{
|
||||
$line = substr($line, 1);
|
||||
$this->__fields = preg_split("/[\t]/", $line);
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
class ConfigLoadTask extends Shell
|
||||
{
|
||||
public function execute()
|
||||
{
|
||||
Configure::load('config');
|
||||
|
||||
if (Configure::read('MISP.system_setting_db')) {
|
||||
App::uses('SystemSetting', 'Model');
|
||||
SystemSetting::setGlobalSetting();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,691 +0,0 @@
|
|||
<?php
|
||||
/*
|
||||
* Reset a password
|
||||
*
|
||||
* arg0 = email
|
||||
* arg1 = new password
|
||||
*/
|
||||
class TrainingShell extends AppShell {
|
||||
|
||||
public $uses = array('User', 'Organisation', 'Server');
|
||||
|
||||
private $__currentUrl = false;
|
||||
private $__currentAuthKey = false;
|
||||
private $__simulate = false;
|
||||
private $__config = false;
|
||||
private $__report = array();
|
||||
private $__verbose = false;
|
||||
private $__interactive = false;
|
||||
|
||||
public function simulate()
|
||||
{
|
||||
$this->__simulate = true;
|
||||
$this->setup();
|
||||
}
|
||||
|
||||
public function changePasswords()
|
||||
{
|
||||
$this->__verbose = !empty($this->params['verbose']);
|
||||
$this->__interactive = !empty($this->params['interactive']);
|
||||
$this->__config = file_get_contents(APP . 'Console/Command/training.json');
|
||||
$this->__config = json_decode($this->__config, true);
|
||||
$this->__report = array();
|
||||
for ($i = $this->__config['ID_start']; $i < ($this->__config['ID_start'] + $this->__config['number_of_misps_to_configure']); $i++) {
|
||||
$id = $i;
|
||||
if ($this->__config['ID_zero_out']) {
|
||||
if ($id < 10) {
|
||||
$id = '0' . $id;
|
||||
}
|
||||
}
|
||||
$this->__currentUrl = str_replace('$ID', $id, $this->__config['server_blueprint']);
|
||||
if ($this->__interactive) {
|
||||
$question = sprintf('Configure instance at %s?', $this->__currentUrl);
|
||||
$input = $this->__user_input($question, array('y', 'n'));
|
||||
if ($input === 'n') {
|
||||
$this->__printReport('Stopping execution. Data created so far:' . PHP_EOL . PHP_EOL);
|
||||
die();
|
||||
}
|
||||
}
|
||||
if ($this->__verbose) {
|
||||
echo 'INFO - Instance to configure' . $this->__currentUrl . PHP_EOL;
|
||||
}
|
||||
$org = str_replace('$ID', $id, $this->__config['org_blueprint']);
|
||||
$this->__report['servers'][$this->__currentUrl]['users'] = $this->__resetPasswords($org, $id);
|
||||
}
|
||||
$this->__printReport('Password change complete. Please find the modifications below:' . PHP_EOL . PHP_EOL);
|
||||
}
|
||||
|
||||
public function setup()
|
||||
{
|
||||
$this->__verbose = !empty($this->params['verbose']);
|
||||
$this->__interactive = !empty($this->params['interactive']);
|
||||
$this->__config = file_get_contents(APP . 'Console/Command/training.json');
|
||||
if (empty($this->__config)) {
|
||||
echo 'No config file found. Make sure that training.json exists and is configured.';
|
||||
die();
|
||||
}
|
||||
$this->__config = json_decode($this->__config, true);
|
||||
$this->__report = array();
|
||||
for ($i = $this->__config['ID_start']; $i < ($this->__config['ID_start'] + $this->__config['number_of_misps_to_configure']); $i++) {
|
||||
$id = $i;
|
||||
if ($this->__config['ID_zero_out']) {
|
||||
if ($id < 10) {
|
||||
$id = '0' . $id;
|
||||
}
|
||||
}
|
||||
$this->__currentUrl = str_replace('$ID', $id, $this->__config['server_blueprint']);
|
||||
if ($this->__interactive) {
|
||||
$question = sprintf('Configure instance at %s?', $this->__currentUrl);
|
||||
$input = $this->__user_input($question, array('y', 'n'));
|
||||
if ($input === 'n') {
|
||||
$this->__printReport('Stopping execution. Data created so far:' . PHP_EOL . PHP_EOL);
|
||||
die();
|
||||
}
|
||||
}
|
||||
if ($this->__verbose) {
|
||||
echo 'INFO - Instance to configure' . $this->__currentUrl . PHP_EOL;
|
||||
}
|
||||
$org = $this->__createOrgFromBlueprint($id);
|
||||
$this->__setSetting('MISP.host_org_id', $org['Organisation']['remote_org_id'], $id, $org['Organisation']['name']);
|
||||
$this->__report['servers'][$this->__currentUrl]['host_org_id'] = $org['Organisation']['remote_org_id'];
|
||||
$this->__report['remote_orgs'][] = array('id' => $org['Organisation']['remote_org_id'], 'name' => $org['Organisation']['name']);
|
||||
$role_id = $this->__createRole($this->__config['role_blueprint']);
|
||||
$this->__report['servers'][$this->__currentUrl]['training_role_id'] = $role_id;
|
||||
$sync_user = $this->__createSyncUserLocally($org['Organisation']['remote_org_id'], $org['Organisation']['name'], $org['Organisation']['id']);
|
||||
$this->__report['users'][] = $sync_user;
|
||||
$local_host_org = $this->__getLocalHostOrgId();
|
||||
$hub_org_id_on_remote = $this->__createOrg($local_host_org);
|
||||
$external_baseurl = empty(Configure::read('MISP.external_baseurl')) ? Configure::read('MISP.baseurl') : Configure::read('MISP.external_baseurl');
|
||||
$this->__report['servers'][$this->__currentUrl]['sync_connections'][] = $this->__addSyncConnection($external_baseurl, 'Exercise hub', $local_host_org, $hub_org_id_on_remote, $sync_user);
|
||||
$this->__report['servers'][$this->__currentUrl]['users'] = $this->__createUsers($org['Organisation']['remote_org_id'], $role_id, $org['Organisation']['name'], $id);
|
||||
if (!empty($this->__config['create_sync_both_ways'])) {
|
||||
$this->__createReverseSyncConnection($org['Organisation']['id'], $org['Organisation']['name'], $local_host_org);
|
||||
}
|
||||
if (!empty($this->__config['create_admin_user'])) {
|
||||
$this->__report['servers'][$this->__currentUrl['users']][] = $this->__addAdminUserRemotely($i, $org['Organisation']['name'], $org['Organisation']['remote_org_id']);
|
||||
}
|
||||
if (!empty($this->__config['settings'])) {
|
||||
foreach ($this->__config['settings'] as $key => $value)
|
||||
$this->__setSetting($key, $value, $id, $org['Organisation']['name']);
|
||||
}
|
||||
if ($this->__config['reset_admin_credentials']) {
|
||||
$this->__report['servers'][$this->__currentUrl]['management_account'] = $this->__reset_admin_credentials($this->__report);
|
||||
}
|
||||
}
|
||||
$this->__printReport('Setup complete. Please find the modifications below:' . PHP_EOL . PHP_EOL);
|
||||
}
|
||||
|
||||
private function __createOrgFromBlueprint($id)
|
||||
{
|
||||
$org = str_replace('$ID', $id, $this->__config['org_blueprint']);
|
||||
$org_id = $this->Organisation->createOrgFromName($org, 1, true);
|
||||
if (empty($org_id)) {
|
||||
sprintf("Something went wrong. Could not create organisation with the following input: \n\n", $org);
|
||||
}
|
||||
$org_data = $this->Organisation->find('first', array(
|
||||
'recursive' => -1,
|
||||
'fields' => array('name', 'uuid', 'local', 'id'),
|
||||
'conditions' => array('Organisation.id' => $org_id)
|
||||
));
|
||||
$org_data['Organisation']['remote_org_id'] = $this->__createOrg($org_data);
|
||||
return $org_data;
|
||||
}
|
||||
|
||||
private function __getLocalHostOrgId()
|
||||
{
|
||||
$org = $this->Organisation->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array(
|
||||
'Organisation.id' => empty(Configure::read('MISP.host_org_id')) ? -1 : Configure::read('MISP.host_org_id')
|
||||
),
|
||||
'fields' => array(
|
||||
'name', 'id', 'uuid'
|
||||
)
|
||||
));
|
||||
if (empty($org)) {
|
||||
$this->__printReport('Stopping execution, no host_org_id set on the current instance, or the setting points to a non-existing org. Data created so far:' . PHP_EOL . PHP_EOL);
|
||||
die();
|
||||
}
|
||||
return $org;
|
||||
}
|
||||
|
||||
private function __createReverseSyncConnection($remote_org_id_on_local, $org_name, $host_org_id_on_local)
|
||||
{
|
||||
$sync_user = $this->__addSyncUserRemotely();
|
||||
$this->__report['servers'][$this->__currentUrl]['users'][] = $sync_user;
|
||||
$sync_server = $this->__addSyncConnectionLocally($this->__currentUrl, $org_name . '_misp', $remote_org_id_on_local, $sync_user, $host_org_id_on_local);
|
||||
if ($sync_server) {
|
||||
$this->__report['sync'][] = $sync_server;
|
||||
}
|
||||
}
|
||||
|
||||
private function __printReport($message)
|
||||
{
|
||||
echo json_encode($this->__report, JSON_PRETTY_PRINT);
|
||||
$this->__report = '';
|
||||
return true;
|
||||
}
|
||||
|
||||
private function __findRemoteRoleId($role_name)
|
||||
{
|
||||
$options = array(
|
||||
'url' => $this->__currentUrl . '/roles/index',
|
||||
'method' => 'GET'
|
||||
);
|
||||
$response = $this->__queryRemoteMISP($options, true);
|
||||
if ($response->code == 200) {
|
||||
$roles = json_decode($response->body, true);
|
||||
foreach ($roles as $role) {
|
||||
if ($role['Role']['name'] == $role_name) {
|
||||
return $role['Role']['id'];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->__responseError($response, $options);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function __getRemoteAdminUser()
|
||||
{
|
||||
$options = array(
|
||||
'url' => $this->__currentUrl . '/users/view/me',
|
||||
'method' => 'GET'
|
||||
);
|
||||
$response = $this->__queryRemoteMISP($options, true);
|
||||
if ($response->code == 200) {
|
||||
return json_decode($response->body, true);
|
||||
} else {
|
||||
$this->__responseError($response, $options);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function __addAdminUserRemotely($i, $org, $remote_org_id)
|
||||
{
|
||||
$email = $this->__config['user_blueprint'];
|
||||
$email = str_replace('$ORGNAME', $org, $email);
|
||||
$email = str_replace('$ID', $i, $email);
|
||||
$email = 'admin' . substr($email, strpos($email, '@'));
|
||||
$admin_role_id = $this->__findRemoteRoleId('Admin');
|
||||
if (!$admin_role_id) {
|
||||
echo 'Remote instance lacks the required role (Admin).' . PHP_EOL ;
|
||||
die();
|
||||
}
|
||||
$options = array(
|
||||
'url' => $this->__currentUrl . '/admin/users/index/searchall:' . $email,
|
||||
'method' => 'GET'
|
||||
);
|
||||
$response = $this->__queryRemoteMISP($options, true);
|
||||
if ($response->code != 200) {
|
||||
$this->__responseError($response, $options);
|
||||
}
|
||||
$newKey = $this->User->generateRandomPassword(32);
|
||||
if (empty(json_decode($response->body, true))) {
|
||||
$user = array(
|
||||
'email' => $email,
|
||||
'password' => $newKey,
|
||||
'role_id' => $admin_role_id,
|
||||
'org_id' => $remote_org_id
|
||||
);
|
||||
$options = array(
|
||||
'url' => $this->__currentUrl . '/admin/users/add',
|
||||
'method' => 'POST',
|
||||
'body' => $user
|
||||
);
|
||||
$response = $this->__queryRemoteMISP($options, true);
|
||||
if ($response->code != 200) {
|
||||
$this->__responseError($response, $options);
|
||||
} else {
|
||||
$response_data = json_decode($response->body, true);
|
||||
if (!$this->__simulate) {
|
||||
$user['authkey'] = $response_data['User']['authkey'];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$user = json_decode($response->body, true)[0]['User'];
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
private function __addSyncUserRemotely()
|
||||
{
|
||||
$sync_user_role_id = $this->__findRemoteRoleId('Sync user');
|
||||
if (!$sync_user_role_id) {
|
||||
echo 'Remote instance lacks the required role (Sync user).' . PHP_EOL ;
|
||||
die();
|
||||
}
|
||||
$remote_admin = $this->__getRemoteAdminUser();
|
||||
if (!$remote_admin) {
|
||||
echo 'Remote instance did not return the admin user\'s information.' . PHP_EOL ;
|
||||
die();
|
||||
}
|
||||
$email = $remote_admin['User']['email'];
|
||||
$email = 'sync' . substr($email, strpos($email, '@'));
|
||||
$options = array(
|
||||
'url' => $this->__currentUrl . '/admin/users/index/searchall:' . $email,
|
||||
'method' => 'GET'
|
||||
);
|
||||
$response = $this->__queryRemoteMISP($options, true);
|
||||
if ($response->code != 200) {
|
||||
$this->__responseError($response, $options);
|
||||
}
|
||||
$newKey = $this->User->generateRandomPassword(32);
|
||||
if (empty(json_decode($response->body, true))) {
|
||||
$user = array(
|
||||
'email' => $email,
|
||||
'password' => $newKey,
|
||||
'role_id' => $sync_user_role_id,
|
||||
'org_id' => $remote_admin['User']['role_id']
|
||||
);
|
||||
$options = array(
|
||||
'url' => $this->__currentUrl . '/admin/users/add',
|
||||
'method' => 'POST',
|
||||
'body' => $user
|
||||
);
|
||||
$response = $this->__queryRemoteMISP($options, true);
|
||||
if ($response->code != 200) {
|
||||
$this->__responseError($response, $options);
|
||||
} else {
|
||||
$response_data = json_decode($response->body, true);
|
||||
if (!$this->__simulate) {
|
||||
$user['authkey'] = $response_data['User']['authkey'];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$user = json_decode($response->body, true)[0]['User'];
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
private function __addSyncConnectionLocally($baseurl, $org_name, $remote_org_id_on_local, $sync_user, $host_org_id_on_local)
|
||||
{
|
||||
$this->Server->create();
|
||||
$server = array(
|
||||
"name" => $org_name,
|
||||
"url" => $baseurl,
|
||||
"authkey" => $sync_user['authkey'],
|
||||
"push" => 1,
|
||||
"pull" => 1,
|
||||
"remote_org_id" => $sync_user['org_id'],
|
||||
"self_signed" => 1,
|
||||
"org_id" => Configure::read('MISP.host_org_id')
|
||||
);
|
||||
$result = $this->Server->save($server);
|
||||
if (!$result) {
|
||||
echo sprintf(
|
||||
'Could not add connection to %s. Reason: %s.' . PHP_EOL,
|
||||
$baseurl,
|
||||
json_encode($this->Server->validationErrors)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return $server;
|
||||
}
|
||||
|
||||
private function __addSyncConnection($baseurl, $name, $local_host_org, $hub_org_id_on_remote, $sync_user)
|
||||
{
|
||||
$server = array(
|
||||
'name' => $name,
|
||||
'url' => $baseurl,
|
||||
'authkey' => $sync_user['User']['authkey'],
|
||||
'remote_org_id' => $hub_org_id_on_remote,
|
||||
'push' => 1,
|
||||
'pull' => 1,
|
||||
'self_signed' => 1
|
||||
);
|
||||
$options = array(
|
||||
'url' => $this->__currentUrl . '/servers/add',
|
||||
'method' => 'POST',
|
||||
'body' => $server
|
||||
);
|
||||
$response = $this->__queryRemoteMISP($options, true);
|
||||
if ($response->code != 200) {
|
||||
$this->__responseError($response, $options);
|
||||
} else {
|
||||
$response_data = json_decode($response->body, true);
|
||||
return array(
|
||||
'url' => $response_data['Server']['url'],
|
||||
'authkey' => $response_data['Server']['authkey']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function __createSyncUserLocally($remote_org_id, $org, $local_org_id)
|
||||
{
|
||||
$sync_role = $this->User->Role->find('first', array('recursive' => -1, 'conditions' => array('Role.name' => 'Sync user')));
|
||||
$sync_role = $sync_role['Role']['id'];
|
||||
$this->User->create();
|
||||
$user = array(
|
||||
'external_auth_required' => 0,
|
||||
'external_auth_key' => '',
|
||||
'server_id' => 0,
|
||||
'gpgkey' => '',
|
||||
'certif_public' => '',
|
||||
'autoalert' => 0,
|
||||
'contactalert' => 0,
|
||||
'disabled' => 0,
|
||||
'newsread' => 0,
|
||||
'change_pw' => 1,
|
||||
'authkey' => $this->User->generateAuthKey(),
|
||||
'termsaccepted' => 0,
|
||||
'org_id' => $local_org_id,
|
||||
'role_id' => $sync_role,
|
||||
'email' => 'sync_user@' . $org . '.test'
|
||||
);
|
||||
$result = $this->User->save($user);
|
||||
if (!$result) {
|
||||
echo 'Could not add sync user due to validation error. Error: ' . json_encode($this->User->validationErrors) . PHP_EOL . PHP_EOL;
|
||||
echo 'Input was: ' . json_encode($user, true) . PHP_EOL . PHP_EOL;
|
||||
}
|
||||
$user = $this->User->find('first', array('recursive' => -1, 'conditions' => array('User.email' => 'sync_user@' . $org . '.test')));
|
||||
return $user;
|
||||
}
|
||||
|
||||
private function __responseError($response, $options)
|
||||
{
|
||||
echo sprintf(
|
||||
"Received a non-200 response (%s). Aborting.\nQueried URL: %s\n Query type: %s\n Request payload: %s\n\n",
|
||||
$response->code,
|
||||
$options['url'],
|
||||
$options['method'],
|
||||
empty($options['body']) ? '' : json_encode($options['body'], JSON_PRETTY_PRINT)
|
||||
);
|
||||
if ($this->__interactive) {
|
||||
$question = 'The above error can cause the issues to compound if you continue. For example, not creating an organisation that subsequently created users should belong to will fail. Would you like to continue?';
|
||||
$input = $this->__user_input($question, array('y', 'n'));
|
||||
if ($input === 'y') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
$this->__printReport('Setup failed. Output of what has been created:' . PHP_EOL . PHP_EOL);
|
||||
die();
|
||||
}
|
||||
|
||||
private function __resetPasswords($org, $i)
|
||||
{
|
||||
$summary = array();
|
||||
for ($j = 1; $j < (1 + $this->__config['user_count']); $j++) {
|
||||
$email = $this->__config['user_blueprint'];
|
||||
$email = str_replace('$ID', $i, $email);
|
||||
$email = str_replace('$ORGNAME', $org, $email);
|
||||
$email = str_replace('$USER_ITERATOR', $j, $email);
|
||||
$options = array(
|
||||
'url' => $this->__currentUrl . '/admin/users/index/searchall:' . $email,
|
||||
'method' => 'GET'
|
||||
);
|
||||
$response = $this->__queryRemoteMISP($options, true);
|
||||
if ($response->code != 200) {
|
||||
$this->__responseError($response, $options);
|
||||
}
|
||||
$newKey = $this->User->generateRandomPassword(32);
|
||||
$user = json_decode($response->body, true);
|
||||
if (!empty($user)) {
|
||||
$user = $user[0];
|
||||
$user['User']['password'] = $newKey;
|
||||
$user['User']['confirm_password'] = $newKey;
|
||||
$options = array(
|
||||
'url' => $this->__currentUrl . '/admin/users/edit/' . $user['User']['id'],
|
||||
'method' => 'POST',
|
||||
'body' => $user
|
||||
);
|
||||
$response = $this->__queryRemoteMISP($options, true);
|
||||
if ($response->code != 200) {
|
||||
$this->__responseError($response, $options);
|
||||
} else {
|
||||
$response_data = json_decode($response->body, true);
|
||||
if ($this->__simulate) {
|
||||
$summary[] = array(
|
||||
'id' => $user['User']['id'],
|
||||
'email' => $user['User']['email'],
|
||||
'password' => $newKey,
|
||||
);
|
||||
} else {
|
||||
$user['User']['authkey'] = $response_data['User']['authkey'];
|
||||
$summary[] = array(
|
||||
'id' => $user['User']['id'],
|
||||
'email' => $user['User']['email'],
|
||||
'password' => $newKey,
|
||||
'authkey' => $user['User']['authkey']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $summary;
|
||||
}
|
||||
|
||||
private function __createUsers($remote_org_id, $role_id, $org, $i)
|
||||
{
|
||||
$summary = array();
|
||||
for ($j = 1; $j < (1 + $this->__config['user_count']); $j++) {
|
||||
$email = $this->__config['user_blueprint'];
|
||||
$email = str_replace('$ID', $i, $email);
|
||||
$email = str_replace('$ORGNAME', $org, $email);
|
||||
$email = str_replace('$USER_ITERATOR', $j, $email);
|
||||
$options = array(
|
||||
'url' => $this->__currentUrl . '/admin/users/index/searchall:' . $email,
|
||||
'method' => 'GET'
|
||||
);
|
||||
$response = $this->__queryRemoteMISP($options, true);
|
||||
if ($response->code != 200) {
|
||||
$this->__responseError($response, $options);
|
||||
}
|
||||
$newKey = $this->User->generateRandomPassword(32);
|
||||
if (empty(json_decode($response->body, true))) {
|
||||
$user = array(
|
||||
'email' => $email,
|
||||
'password' => $newKey,
|
||||
'role_id' => $role_id,
|
||||
'org_id' => $remote_org_id
|
||||
);
|
||||
$options = array(
|
||||
'url' => $this->__currentUrl . '/admin/users/add',
|
||||
'method' => 'POST',
|
||||
'body' => $user
|
||||
);
|
||||
$response = $this->__queryRemoteMISP($options, true);
|
||||
if ($response->code != 200) {
|
||||
$this->__responseError($response, $options);
|
||||
} else {
|
||||
$response_data = json_decode($response->body, true);
|
||||
if ($this->__simulate) {
|
||||
$summary[] = $user;
|
||||
} else {
|
||||
$user['authkey'] = $response_data['User']['authkey'];
|
||||
$summary[] = $user;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $summary;
|
||||
}
|
||||
|
||||
private function __createRole($blueprint)
|
||||
{
|
||||
$blueprint = array('Role' => $blueprint);
|
||||
$options = array(
|
||||
'url' => $this->__currentUrl . '/roles/index',
|
||||
'method' => 'GET'
|
||||
);
|
||||
$response = $this->__queryRemoteMISP($options, true);
|
||||
if ($response->code == 200) {
|
||||
$roles = json_decode($response->body, true);
|
||||
$found = false;
|
||||
foreach ($roles as $role) {
|
||||
if ($role['Role']['name'] == $blueprint['Role']['name']) {
|
||||
return $role['Role']['id'];
|
||||
}
|
||||
}
|
||||
$options = array(
|
||||
'url' => $this->__currentUrl . '/admin/roles/add',
|
||||
'method' => 'POST',
|
||||
'body' => $blueprint
|
||||
);
|
||||
$response = $this->__queryRemoteMISP($options, true);
|
||||
if ($response->code != 200) {
|
||||
$this->__responseError($response, $options);
|
||||
} else {
|
||||
$response_data = json_decode($response->body, true);
|
||||
return $response_data['Role']['id'];
|
||||
}
|
||||
} else {
|
||||
$this->__responseError($response, $options);
|
||||
}
|
||||
}
|
||||
|
||||
private function __reset_admin_credentials()
|
||||
{
|
||||
$credentials = array(
|
||||
'authkey' => $this->User->generateAuthKey(),
|
||||
'password' => $this->User->generateRandomPassword(32)
|
||||
);
|
||||
$this->__queryRemoteMISP(array(
|
||||
'url' => $this->__currentUrl . '/admin/users/edit/1',
|
||||
'body' => array('User' => array(
|
||||
'password' => $credentials['password'],
|
||||
'authkey' => $credentials['authkey']
|
||||
)),
|
||||
'method' => 'POST'
|
||||
));
|
||||
return $credentials;
|
||||
}
|
||||
|
||||
private function __createOrg($org_data)
|
||||
{
|
||||
$options = array(
|
||||
'url' => $this->__currentUrl . '/organisations/index.json',
|
||||
'method' => 'GET'
|
||||
);
|
||||
$response = $this->__queryRemoteMISP($options, true);
|
||||
if ($response->code != 200) {
|
||||
$this->__responseError($response, $options);
|
||||
} else {
|
||||
$response_data = json_decode($response->body, true);
|
||||
$found = false;
|
||||
foreach ($response_data as $existingOrg) {
|
||||
if ($existingOrg['Organisation']['name'] == $org_data['Organisation']['name']) {
|
||||
return $existingOrg['Organisation']['id'];
|
||||
}
|
||||
}
|
||||
if (isset($org_data['Organisation'])) {
|
||||
$org_data = $org_data['Organisation'];
|
||||
}
|
||||
unset($org_data['id']);
|
||||
$options = array(
|
||||
'body' => $org_data,
|
||||
'url' => $this->__currentUrl . '/admin/organisations/add',
|
||||
'method' => 'POST'
|
||||
);
|
||||
$response = $this->__queryRemoteMISP($options, true);
|
||||
if ($response->code != 200) {
|
||||
$this->__responseError($response, $options);
|
||||
}
|
||||
$options = array(
|
||||
'url' => $this->__currentUrl . '/organisations/view/' . $org_data['uuid'],
|
||||
'method' => 'GET'
|
||||
);
|
||||
$response = $this->__queryRemoteMISP($options, true);
|
||||
if ($response->code != 200) {
|
||||
$this->__responseError($response, $options);
|
||||
}
|
||||
$response_data = json_decode($response->body, true);
|
||||
return $response_data['Organisation']['id'];
|
||||
}
|
||||
}
|
||||
|
||||
private function __user_input($question, $valid_input_options)
|
||||
{
|
||||
$valid_input = false;
|
||||
while (!$valid_input) {
|
||||
echo sprintf(
|
||||
'%s (%s)' . PHP_EOL,
|
||||
$question,
|
||||
implode('/', $valid_input_options)
|
||||
);
|
||||
$handle = fopen ("php://stdin","r");
|
||||
$input = trim(strtolower(fgets($handle)));
|
||||
if (in_array($input, $valid_input_options)) {
|
||||
$valid_input = true;
|
||||
}
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
|
||||
private function __setSetting($key, $value, $i, $org)
|
||||
{
|
||||
$value = str_replace('$ID', $i, $value);
|
||||
$value = str_replace('$ORGNAME', $org, $value);
|
||||
$options = array(
|
||||
'url' => $this->__currentUrl . '/servers/serverSettingsEdit/' . $key,
|
||||
'method' => 'POST',
|
||||
'body' => array('value' => $value)
|
||||
);
|
||||
$response = $this->__queryRemoteMISP($options, true);
|
||||
if ($response->code != 200) {
|
||||
$this->__responseError($response, $options);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function __queryRemoteMISP($options, $returnFullResponse = false)
|
||||
{
|
||||
$params = array();
|
||||
App::uses('HttpSocket', 'Network/Http');
|
||||
$params['ssl_allow_self_signed'] = true;
|
||||
$params['ssl_verify_peer_name'] = false;
|
||||
$params['ssl_verify_peer'] = false;
|
||||
$HttpSocket = new HttpSocket($params);
|
||||
$request = array(
|
||||
'header' => array(
|
||||
'Authorization' => $this->__config['authkey'],
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/json'
|
||||
)
|
||||
);
|
||||
if ($this->__simulate) {
|
||||
if ($this->__verbose) {
|
||||
echo 'SIMULATION - query to be executed:' . PHP_EOL . json_encode($options) . PHP_EOL . ' using request object:' . PHP_EOL . json_encode($request) . PHP_EOL . PHP_EOL;
|
||||
}
|
||||
$response = new class{};
|
||||
$response->code = 200;
|
||||
$response->body = '{"id": 666, "Organisation": {"id": 666}, "User": {"id": 666, "email": "foo"}, "Role": {"id": 666}, "Server": {"url": "https://foo.bar", "authkey": "bla"}}';
|
||||
return $response;
|
||||
} else {
|
||||
if ($this->__verbose) {
|
||||
echo 'EXEC - query to be executed:' . PHP_EOL . json_encode($options) . PHP_EOL . ' using request object:' . PHP_EOL . json_encode($request) . PHP_EOL . PHP_EOL;
|
||||
}
|
||||
if ($options['method'] === 'POST') {
|
||||
$response = $HttpSocket->post($options['url'], json_encode($options['body']), $request);
|
||||
} else {
|
||||
$response = $HttpSocket->get($options['url'], '', $request);
|
||||
}
|
||||
if ($returnFullResponse) {
|
||||
return $response;
|
||||
}
|
||||
if ($response->code != 200) {
|
||||
$this->__responseError($response, $options);
|
||||
} else {
|
||||
return json_decode($response->body, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
$parser->addOption('verbose', array(
|
||||
'short' => 'v',
|
||||
'help' => __('verbose mode'),
|
||||
'boolean' => 1
|
||||
))->addOption('interactive', array(
|
||||
'short' => 'i',
|
||||
'help' => __('interactive mode'),
|
||||
'boolean' => 1
|
||||
));
|
||||
return $parser;
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
<?php
|
||||
class UserInitShell extends AppShell {
|
||||
public $uses = array('User', 'Role', 'Organisation', 'Server', 'ConnectionManager');
|
||||
public function main() {
|
||||
if (!Configure::read('Security.salt')) {
|
||||
$this->loadModel('Server');
|
||||
$this->Server->serverSettingsSaveValue('Security.salt', $this->User->generateRandomPassword(32));
|
||||
}
|
||||
$dataSourceConfig = ConnectionManager::getDataSource('default')->config;
|
||||
$dataSource = $dataSourceConfig['datasource'];
|
||||
$this->Role->Behaviors->unload('SysLogLogable.SysLogLogable');
|
||||
$this->User->Behaviors->unload('SysLogLogable.SysLogLogable');
|
||||
// populate the DB with the first role (site admin) if it's empty
|
||||
if ($this->Role->find('count') == 0 ) {
|
||||
$siteAdmin = array('Role' => array(
|
||||
'id' => 1,
|
||||
'name' => 'Site Admin',
|
||||
'permission' => 3,
|
||||
'perm_add' => 1,
|
||||
'perm_modify' => 1,
|
||||
'perm_modify_org' => 1,
|
||||
'perm_publish' => 1,
|
||||
'perm_sync' => 1,
|
||||
'perm_admin' => 1,
|
||||
'perm_audit' => 1,
|
||||
'perm_auth' => 1,
|
||||
'perm_site_admin' => 1,
|
||||
'perm_regexp_access' => 1,
|
||||
'perm_sharing_group' => 1,
|
||||
'perm_tagger' => 1,
|
||||
'perm_template' => 1
|
||||
));
|
||||
$this->Role->save($siteAdmin);
|
||||
// PostgreSQL: update value of auto incremented serial primary key after setting the column by force
|
||||
if ($dataSource == 'Database/Postgres') {
|
||||
$sql = "SELECT setval('roles_id_seq', (SELECT MAX(id) FROM roles));";
|
||||
$this->Role->query($sql);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->Organisation->find('count', array('conditions' => array('Organisation.local' => true))) == 0) {
|
||||
$date = date('Y-m-d H:i:s');
|
||||
$org = array('Organisation' => array(
|
||||
'id' => 1,
|
||||
'name' => !empty(Configure::read('MISP.org')) ? Configure::read('MISP.org') : 'ADMIN',
|
||||
'description' => 'Automatically generated admin organisation',
|
||||
'type' => 'ADMIN',
|
||||
'date_created' => $date,
|
||||
'uuid' => CakeText::uuid(),
|
||||
'local' => 1
|
||||
));
|
||||
$this->Organisation->save($org);
|
||||
// PostgreSQL: update value of auto incremented serial primary key after setting the column by force
|
||||
if ($dataSource == 'Database/Postgres') {
|
||||
$sql = "SELECT setval('organisations_id_seq', (SELECT MAX(id) FROM organisations));";
|
||||
$this->Organisation->query($sql);
|
||||
}
|
||||
$org_id = $this->Organisation->id;
|
||||
} else {
|
||||
$hostOrg = $this->Organisation->find('first', array('conditions' => array('Organisation.name' => Configure::read('MISP.org')), 'recursive' => -1));
|
||||
if (!empty($hostOrg)) $org_id = $hostOrg['Organisation']['id'];
|
||||
else {
|
||||
$firstOrg = $this->Organisation->find('first', array('conditions' => array('Organisation.local' => true), 'order' => 'Organisation.id ASC'));
|
||||
$org_id = $firstOrg['Organisation']['id'];
|
||||
}
|
||||
}
|
||||
|
||||
// populate the DB with the first user if it's empty
|
||||
if ($this->User->find('count') == 0 ) {
|
||||
$auth_key = $this->User->createInitialUser($org_id);
|
||||
if (!empty($auth_key)) {
|
||||
echo $auth_key . PHP_EOL;
|
||||
} else {
|
||||
echo 'Could not generate the initial user!' . PHP_EOL;
|
||||
}
|
||||
} else {
|
||||
echo 'Script aborted: MISP instance already initialised.' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,455 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @property User $User
|
||||
* @property Log $Log
|
||||
*/
|
||||
class UserShell extends AppShell
|
||||
{
|
||||
public $uses = ['User', 'Log'];
|
||||
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
$parser->addSubcommand('list', [
|
||||
'help' => __('Get list of user accounts.'),
|
||||
'parser' => [
|
||||
'arguments' => [
|
||||
'userId' => ['help' => __('User ID or e-mail address.'), 'required' => true],
|
||||
],
|
||||
'options' => [
|
||||
'json' => ['help' => __('Output as JSON.'), 'boolean' => true],
|
||||
],
|
||||
]
|
||||
]);
|
||||
$parser->addSubcommand('authkey', [
|
||||
'help' => __('Get information about given authkey.'),
|
||||
'parser' => [
|
||||
'arguments' => [
|
||||
'authkey' => ['help' => __('Authentication key. If not provide, it will be read from STDIN.')],
|
||||
],
|
||||
]
|
||||
]);
|
||||
$parser->addSubcommand('authkey_valid', [
|
||||
'help' => __('Check if given authkey by STDIN is valid.'),
|
||||
]);
|
||||
$parser->addSubcommand('block', [
|
||||
'help' => __('Immediately block user.'),
|
||||
'parser' => [
|
||||
'arguments' => [
|
||||
'userId' => ['help' => __('User ID or e-mail address.'), 'required' => true],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$parser->addSubcommand('unblock', [
|
||||
'help' => __('Unblock blocked user.'),
|
||||
'parser' => [
|
||||
'arguments' => [
|
||||
'userId' => ['help' => __('User ID or e-mail address.'), 'required' => true],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$parser->addSubcommand('check_validity', [
|
||||
'help' => __('Check users validity from external identity provider and block not valid user.'),
|
||||
'parser' => [
|
||||
'arguments' => [
|
||||
'userId' => ['help' => __('User ID or e-mail address. If not provided, all users will be checked.'), 'required' => false],
|
||||
],
|
||||
'options' => [
|
||||
'block_invalid' => ['help' => __('Block user that are considered invalid.'), 'boolean' => true],
|
||||
'update' => ['help' => __('Update user role or organisation.'), 'boolean' => true],
|
||||
],
|
||||
]
|
||||
]);
|
||||
$parser->addSubcommand('change_pw', [
|
||||
'help' => __('Change user password.'),
|
||||
'parser' => [
|
||||
'arguments' => [
|
||||
'userId' => ['help' => __('User ID or e-mail address.'), 'required' => true],
|
||||
'password' => ['help' => __('New user password.'), 'required' => true],
|
||||
],
|
||||
'options' => [
|
||||
'no_password_change' => ['help' => __('Do not require password change.'), 'boolean' => true],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$parser->addSubcommand('change_authkey', [
|
||||
'help' => __('Change authkey. When advanced authkeys are enabled, old authkeys will be disabled.'),
|
||||
'parser' => [
|
||||
'arguments' => [
|
||||
'userId' => ['help' => __('User ID or e-mail address.'), 'required' => true],
|
||||
'authKey' => ['help' => __('Optional new authentication key.'), 'required' => false],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$parser->addSubcommand('user_ips', [
|
||||
'help' => __('Show IP addresses that user uses to access MISP.'),
|
||||
'parser' => [
|
||||
'arguments' => [
|
||||
'userId' => ['help' => __('User ID or e-mail address.'), 'required' => true],
|
||||
],
|
||||
'options' => [
|
||||
'json' => ['help' => __('Output as JSON.'), 'boolean' => true],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$parser->addSubcommand('ip_user', [
|
||||
'help' => __('Get user ID for user IP. If multiple users use the same IP, only last user ID will be returned.'),
|
||||
'parser' => [
|
||||
'arguments' => [
|
||||
'ip' => ['help' => __('IPv4 or IPv6 address.'), 'required' => true],
|
||||
],
|
||||
'options' => [
|
||||
'json' => ['help' => __('Output as JSON.'), 'boolean' => true],
|
||||
],
|
||||
],
|
||||
]);
|
||||
return $parser;
|
||||
}
|
||||
|
||||
public function list()
|
||||
{
|
||||
$userId = isset($this->args[0]) ? $this->args[0] : null;
|
||||
if ($userId) {
|
||||
$conditions = ['OR' => [
|
||||
'User.id' => $userId,
|
||||
'User.email LIKE' => "%$userId%",
|
||||
'User.sub LIKE' => "%$userId%",
|
||||
]];
|
||||
} else {
|
||||
$conditions = [];
|
||||
}
|
||||
|
||||
if ($this->params['json']) {
|
||||
// do not fetch sensitive or big values
|
||||
$schema = $this->User->schema();
|
||||
unset($schema['authkey']);
|
||||
unset($schema['password']);
|
||||
unset($schema['gpgkey']);
|
||||
unset($schema['certif_public']);
|
||||
|
||||
$fields = array_keys($schema);
|
||||
$fields[] = 'Role.*';
|
||||
$fields[] = 'Organisation.*';
|
||||
|
||||
$users = $this->User->find('all', [
|
||||
'recursive' => -1,
|
||||
'fields' => $fields,
|
||||
'conditions' => $conditions,
|
||||
'contain' => ['Organisation', 'Role', 'UserSetting'],
|
||||
]);
|
||||
|
||||
$this->out($this->json($users));
|
||||
} else {
|
||||
$users = $this->User->find('column', [
|
||||
'fields' => ['email'],
|
||||
'conditions' => $conditions,
|
||||
]);
|
||||
foreach ($users as $user) {
|
||||
$this->out($user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function authkey()
|
||||
{
|
||||
if (isset($this->args[0])) {
|
||||
$authkey = $this->args[0];
|
||||
} else {
|
||||
$authkey = fgets(STDIN); // read line from STDIN
|
||||
}
|
||||
$authkey = trim($authkey);
|
||||
if (strlen($authkey) !== 40) {
|
||||
$this->error('Authkey has not valid format.');
|
||||
}
|
||||
if (Configure::read('Security.advanced_authkeys')) {
|
||||
$user = $this->User->AuthKey->getAuthUserByAuthKey($authkey, true);
|
||||
if (empty($user)) {
|
||||
$this->error("Given authkey doesn't belong to any user.");
|
||||
}
|
||||
|
||||
$isExpired = $user['authkey_expiration'] && $user['authkey_expiration'] < time();
|
||||
|
||||
$this->out($this->json([
|
||||
'user_id' => $user['id'],
|
||||
'email' => $user['email'],
|
||||
'org_id' => $user['org_id'],
|
||||
'authkey_id' => $user['authkey_id'],
|
||||
'authkey_expiration' => $user['authkey_expiration'],
|
||||
'authkey_expired' => $isExpired,
|
||||
'allowed_ips' => $user['allowed_ips'],
|
||||
'authkey_read_only' => $user['authkey_read_only'],
|
||||
]));
|
||||
|
||||
$this->_stop($isExpired ? 2 : 0);
|
||||
} else {
|
||||
$user = $this->User->getAuthUserByAuthkey($authkey);
|
||||
if (empty($user)) {
|
||||
$this->error("Given authkey doesn't belong to any user.");
|
||||
}
|
||||
$this->out($this->json([
|
||||
'user_id' => $user['id'],
|
||||
'email' => $user['email'],
|
||||
'org_id' => $user['org_id'],
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads line from stdin and checks if authkey is valid. Returns '1' to stdout if key is valid and '0' if not.
|
||||
*/
|
||||
public function authkey_valid()
|
||||
{
|
||||
$cache = [];
|
||||
do {
|
||||
$authkey = fgets(STDIN); // read line from STDIN
|
||||
$authkey = trim($authkey);
|
||||
if (strlen($authkey) !== 40) {
|
||||
fwrite(STDOUT, "0\n"); // authkey is not in valid format
|
||||
continue;
|
||||
}
|
||||
$time = time();
|
||||
// Generate hash from authkey to not store raw authkey in memory
|
||||
$keyHash = hash('sha256', $authkey, true);
|
||||
if (isset($cache[$keyHash]) && $cache[$keyHash][1] > $time) {
|
||||
fwrite(STDOUT, $cache[$keyHash][0] ? "1\n" : "0\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
$user = false;
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
try {
|
||||
if (Configure::read('Security.advanced_authkeys')) {
|
||||
$user = $this->User->AuthKey->getAuthUserByAuthKey($authkey);
|
||||
} else {
|
||||
$user = $this->User->getAuthUserByAuthkey($authkey);
|
||||
}
|
||||
break;
|
||||
} catch (PDOException $e) {
|
||||
$this->log($e->getMessage());
|
||||
// Reconnect in case of failure and try again
|
||||
try {
|
||||
$this->User->getDataSource()->connect();
|
||||
} catch (MissingConnectionException $e) {
|
||||
sleep(1);
|
||||
$this->log($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$user = (bool)$user;
|
||||
// Cache results for 5 seconds
|
||||
$cache[$keyHash] = [$user, $time + 5];
|
||||
fwrite(STDOUT, $user ? "1\n" : "0\n");
|
||||
} while (true);
|
||||
}
|
||||
|
||||
public function block()
|
||||
{
|
||||
list($userId) = $this->args;
|
||||
$user = $this->getUser($userId);
|
||||
if ($user['disabled']) {
|
||||
$this->error("User $userId is already blocked.");
|
||||
}
|
||||
$this->User->updateField($user, 'disabled', true);
|
||||
$this->out("User $userId blocked.");
|
||||
}
|
||||
|
||||
public function unblock()
|
||||
{
|
||||
list($userId) = $this->args;
|
||||
$user = $this->getUser($userId);
|
||||
if (!$user['disabled']) {
|
||||
$this->error("User $userId is not blocked.");
|
||||
}
|
||||
$this->User->updateField($user, 'disabled', false);
|
||||
$this->out("User $userId unblocked.");
|
||||
}
|
||||
|
||||
public function check_validity()
|
||||
{
|
||||
$auth = Configure::read('Security.auth');
|
||||
if (!$auth) {
|
||||
$this->error('External authentication is not enabled');
|
||||
}
|
||||
if (!is_array($auth)) {
|
||||
throw new Exception("`Security.auth` config value must be array.");
|
||||
}
|
||||
if (!in_array('OidcAuth.Oidc', $auth, true)) {
|
||||
$this->error('This method is currently supported just by OIDC auth provider');
|
||||
}
|
||||
|
||||
App::uses('Oidc', 'OidcAuth.Lib');
|
||||
$oidc = new Oidc($this->User);
|
||||
|
||||
$conditions = ['User.disabled' => false]; // fetch just not disabled users
|
||||
|
||||
$userId = isset($this->args[0]) ? $this->args[0] : null;
|
||||
if ($userId) {
|
||||
$conditions['OR'] = [
|
||||
'User.id' => $userId,
|
||||
'User.email LIKE' => "%$userId%",
|
||||
'User.sub LIKE' => "%$userId%",
|
||||
];
|
||||
}
|
||||
|
||||
$users = $this->User->find('all', [
|
||||
'recursive' => -1,
|
||||
'contain' => ['UserSetting'],
|
||||
'conditions' => $conditions,
|
||||
]);
|
||||
$blockInvalid = $this->params['block_invalid'];
|
||||
$update = $this->params['update'];
|
||||
|
||||
foreach ($users as $user) {
|
||||
$user['User']['UserSetting'] = $user['UserSetting'];
|
||||
$user = $user['User'];
|
||||
|
||||
if ($blockInvalid) {
|
||||
$result = $oidc->blockInvalidUser($user, true, $update);
|
||||
} else {
|
||||
$result = $oidc->isUserValid($user, true, $update);
|
||||
}
|
||||
|
||||
$this->out("{$user['email']}: " . ($result ? '<success>valid</success>' : '<error>invalid</error>'));
|
||||
}
|
||||
}
|
||||
|
||||
public function change_pw()
|
||||
{
|
||||
list($userId, $newPassword) = $this->args;
|
||||
$user = $this->getUser($userId);
|
||||
|
||||
$user['password'] = $newPassword;
|
||||
$user['confirm_password'] = $newPassword;
|
||||
$user['change_pw'] = !$this->params['no_password_change'];
|
||||
|
||||
if (!$this->User->save($user)) {
|
||||
$this->out("Could not update password for user $userId.");
|
||||
$this->out($this->json($this->User->validationErrors));
|
||||
$this->_stop(self::CODE_ERROR);
|
||||
}
|
||||
|
||||
$this->out("Password for $userId changed.");
|
||||
}
|
||||
|
||||
public function change_authkey()
|
||||
{
|
||||
$newkey = null;
|
||||
if (isset($this->args[1])) {
|
||||
list($userId, $newkey) = $this->args;
|
||||
} else {
|
||||
list($userId) = $this->args;
|
||||
}
|
||||
$user = $this->getUser($userId);
|
||||
|
||||
# validate new authentication key if provided
|
||||
if (!empty($newkey) && (strlen($newkey) != 40 || !ctype_alnum($newkey))) {
|
||||
$this->error('The new auth key needs to be 40 characters long and only alphanumeric.');
|
||||
}
|
||||
|
||||
if (empty(Configure::read('Security.advanced_authkeys'))) {
|
||||
$oldKey = $user['authkey'];
|
||||
if (empty($newkey)) {
|
||||
$newkey = $this->User->generateAuthKey();
|
||||
}
|
||||
$this->User->updateField($user, 'authkey', $newkey);
|
||||
$this->Log->createLogEntry('SYSTEM', 'reset_auth_key', 'User', $user['id'],
|
||||
__('Authentication key for user %s (%s) updated.', $user['id'], $user['email']),
|
||||
['authkey' => [$oldKey, $newkey]]
|
||||
);
|
||||
$this->out("Authentication key changed to: $newkey");
|
||||
} else {
|
||||
$newkey = $this->User->AuthKey->resetAuthKey($user['id'], null, $newkey);
|
||||
if ($newkey) {
|
||||
$this->out("Old authentication keys disabled and new key created: $newkey");
|
||||
} else {
|
||||
$this->error('There is problem with changing auth key.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function user_ips()
|
||||
{
|
||||
list($userId) = $this->args;
|
||||
$user = $this->getUser($userId);
|
||||
|
||||
if (empty(Configure::read('MISP.log_user_ips'))) {
|
||||
$this->out('<warning>Storing user IP addresses is disabled.</warning>');
|
||||
}
|
||||
|
||||
$ips = $this->User->setupRedisWithException()->smembers('misp:user_ip:' . $user['id']);
|
||||
|
||||
if ($this->params['json']) {
|
||||
$this->out($this->json($ips));
|
||||
} else {
|
||||
$this->hr();
|
||||
$this->out("User #{$user['id']}: {$user['email']}");
|
||||
$this->hr();
|
||||
$this->out(implode(PHP_EOL, $ips));
|
||||
}
|
||||
}
|
||||
|
||||
public function ip_user()
|
||||
{
|
||||
list($ip) = $this->args;
|
||||
if (!filter_var($ip, FILTER_VALIDATE_IP)) {
|
||||
$this->error("IP `$ip` is not valid IPv4 or IPv6 address");
|
||||
}
|
||||
|
||||
if (empty(Configure::read('MISP.log_user_ips'))) {
|
||||
$this->out('<warning>Storing user IP addresses is disabled.</warning>');
|
||||
}
|
||||
|
||||
$userId = $this->User->setupRedisWithException()->get('misp:ip_user:' . $ip);
|
||||
if (empty($userId)) {
|
||||
$this->out('No hits.');
|
||||
$this->_stop();
|
||||
}
|
||||
|
||||
$user = $this->User->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('User.id' => $userId),
|
||||
'fields' => ['id', 'email'],
|
||||
));
|
||||
|
||||
if (empty($user)) {
|
||||
$this->error("User with ID $userId doesn't exists anymore.");
|
||||
}
|
||||
|
||||
if ($this->params['json']) {
|
||||
$this->out($this->json([
|
||||
'ip' => $ip,
|
||||
'id' => $user['User']['id'],
|
||||
'email' => $user['User']['email'],
|
||||
]));
|
||||
} else {
|
||||
$this->out(sprintf(
|
||||
'%s==============================%sIP: %s%s==============================%sUser #%s: %s%s==============================%s',
|
||||
PHP_EOL, PHP_EOL, $ip, PHP_EOL, PHP_EOL, $user['User']['id'], $user['User']['email'], PHP_EOL, PHP_EOL
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|int $userId
|
||||
* @return array
|
||||
*/
|
||||
private function getUser($userId)
|
||||
{
|
||||
// Do not fetch password from database
|
||||
$schema = $this->User->schema();
|
||||
unset($schema['password']);
|
||||
|
||||
$conditions = is_numeric($userId) ? ['User.id' => $userId] : ['User.email' => $userId];
|
||||
$user = $this->User->find('first', [
|
||||
'conditions' => $conditions,
|
||||
'recursive' => -1,
|
||||
'fields' => array_keys($schema),
|
||||
]);
|
||||
if (empty($user)) {
|
||||
$this->error("User `$userId` not found.");
|
||||
}
|
||||
return $user['User'];
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once 'AppShell.php';
|
||||
|
||||
class WorkflowShell extends AppShell {
|
||||
|
||||
public $uses = ['Job', 'Workflow'];
|
||||
|
||||
public function executeWorkflowForTrigger()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2]) || empty($this->args[3])) {
|
||||
die(__('Invalid number of arguments.'));
|
||||
}
|
||||
|
||||
$trigger_id = $this->args[0];
|
||||
$data = JsonTool::decode($this->args[1]);
|
||||
$logging = JsonTool::decode($this->args[2]);
|
||||
$jobId = $this->args[3];
|
||||
|
||||
$blockingErrors = [];
|
||||
$executionSuccess = $this->Workflow->executeWorkflowForTrigger($trigger_id, $data, $blockingErrors);
|
||||
|
||||
$job = $this->Job->read(null, $jobId);
|
||||
$job['Job']['progress'] = 100;
|
||||
$job['Job']['status'] = Job::STATUS_COMPLETED;
|
||||
$job['Job']['date_modified'] = date("Y-m-d H:i:s");
|
||||
if ($executionSuccess) {
|
||||
$job['Job']['message'] = __('Workflow for trigger `%s` completed execution', $trigger_id);
|
||||
} else {
|
||||
$errorMessage = implode(', ', $blockingErrors);
|
||||
$message = __('Error while executing workflow for trigger `%s`: %s. %s%s', $trigger_id, $logging['message'], PHP_EOL . __('Returned message: %s', $errorMessage));
|
||||
$job['Job']['message'] = $message;
|
||||
}
|
||||
$this->Job->save($job);
|
||||
}
|
||||
|
||||
public function walkGraph()
|
||||
{
|
||||
if (empty($this->args[0]) || empty($this->args[1]) || empty($this->args[2]) || empty($this->args[3])) {
|
||||
die(__('Invalid number of arguments.'));
|
||||
}
|
||||
|
||||
$workflow_id = (int)$this->args[0];
|
||||
$workflow = $this->Workflow->fetchWorkflow($workflow_id);
|
||||
$node_id_to_exec = (int)$this->args[1];
|
||||
$roamingData = JsonTool::decode($this->args[2]);
|
||||
$for_path = $this->args[3];
|
||||
$jobId = $this->args[4];
|
||||
|
||||
$concurrentErrors = [];
|
||||
$walkResult = [];
|
||||
$executionSuccess = $this->Workflow->walkGraph(
|
||||
$workflow,
|
||||
$node_id_to_exec,
|
||||
$for_path,
|
||||
$roamingData,
|
||||
$concurrentErrors,
|
||||
$walkResult
|
||||
);
|
||||
$job = $this->Job->read(null, $jobId);
|
||||
$job['Job']['progress'] = 100;
|
||||
$job['Job']['status'] = Job::STATUS_COMPLETED;
|
||||
$job['Job']['date_modified'] = date("Y-m-d H:i:s");
|
||||
if ($executionSuccess) {
|
||||
$job['Job']['message'] = __('Workflow concurrent task executed %s nodes starting from node %s.', count($walkResult['executed_nodes']), $node_id_to_exec);
|
||||
} else {
|
||||
$message = __('Error while executing workflow concurrent task. %s', PHP_EOL . implode(', ', $concurrentErrors));
|
||||
$this->Workflow->logExecutionError($workflow, $message);
|
||||
$job['Job']['message'] = $message;
|
||||
}
|
||||
$this->Job->save($job);
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
{
|
||||
"ID_start": 1,
|
||||
"ID_zero_out": 0,
|
||||
"number_of_misps_to_configure": 5,
|
||||
"reset_admin_credentials": 1,
|
||||
"create_sync_both_ways": 0,
|
||||
"create_admin_user": 0,
|
||||
"settings": {
|
||||
|
||||
},
|
||||
"authkey": "111111111111111111111111111111111",
|
||||
"server_blueprint": "https://my_remote_misp$ID",
|
||||
"user_blueprint": "user$USER_ITERATOR@user.$ORGNAME.test",
|
||||
"user_count": 2,
|
||||
"org_blueprint": "org$ID",
|
||||
"role_blueprint": {
|
||||
"name": "training_participant",
|
||||
"perm_add": true,
|
||||
"perm_modify": true,
|
||||
"perm_modify_org": true,
|
||||
"perm_publish": true,
|
||||
"perm_delegate": true,
|
||||
"perm_sync": true,
|
||||
"perm_admin": true,
|
||||
"perm_audit": true,
|
||||
"perm_auth": true,
|
||||
"perm_site_admin": false,
|
||||
"perm_regexp_access": true,
|
||||
"perm_tagger": true,
|
||||
"perm_template": true,
|
||||
"perm_sharing_group": true,
|
||||
"perm_tag_editor": true,
|
||||
"perm_sighting": true,
|
||||
"perm_object_template": true,
|
||||
"default_role": false,
|
||||
"memory_limit": "",
|
||||
"max_execution_time": "",
|
||||
"restricted_to_site_admin": false,
|
||||
"perm_publish_zmq": true,
|
||||
"permission": "3",
|
||||
"permission_description": "training_participant"
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
################################################################################
|
||||
#
|
||||
# Bake is a shell script for running CakePHP bake script
|
||||
# PHP 5
|
||||
#
|
||||
# CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
# Copyright 2005-2012, Cake Software Foundation, Inc.
|
||||
#
|
||||
# Licensed under The MIT License
|
||||
# Redistributions of files must retain the above copyright notice.
|
||||
#
|
||||
# @copyright Copyright 2005-2012, Cake Software Foundation, Inc.
|
||||
# @link http://cakephp.org CakePHP(tm) Project
|
||||
# @package app.Console
|
||||
# @since CakePHP(tm) v 2.0
|
||||
# @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
#
|
||||
################################################################################
|
||||
LIB=$(cd -P -- "$(dirname -- "$0")" && pwd -P) && LIB=$LIB/$(basename -- "$0")
|
||||
|
||||
while [ -h "$LIB" ]; do
|
||||
DIR=$(dirname -- "$LIB")
|
||||
SYM=$(readlink "$LIB")
|
||||
LIB=$(cd "$DIR" && cd $(dirname -- "$SYM") && pwd)/$(basename -- "$SYM")
|
||||
done
|
||||
|
||||
LIB=$(dirname -- "$LIB")/
|
||||
APP=$(dirname $(cd $(dirname $0) && pwd))
|
||||
|
||||
exec php -q "$LIB"cake.php -working "$APP" "$@"
|
||||
|
||||
exit;
|
|
@ -1,45 +0,0 @@
|
|||
#!/usr/bin/php -q
|
||||
<?php
|
||||
/**
|
||||
* Command-line code generation utility to automate programmer chores.
|
||||
*
|
||||
* PHP 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2012, Cake Software Foundation, Inc.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package app.Console
|
||||
* @since CakePHP(tm) v 2.0
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
if (!defined('DS')) {
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
}
|
||||
$dispatcher = 'Cake' . DS . 'Console' . DS . 'ShellDispatcher.php';
|
||||
|
||||
if (function_exists('ini_set')) {
|
||||
$root = dirname(__DIR__, 2);
|
||||
$appDir = basename(dirname(__DIR__));
|
||||
$composerInstall = $root . DS . $appDir . DS . 'Vendor' . DS . 'cakephp' . DS . 'cakephp' . DS . 'lib';
|
||||
|
||||
if (file_exists($composerInstall . DS . $dispatcher)) {
|
||||
$install = $composerInstall; // prefer compose install
|
||||
} else {
|
||||
$install = $root . DS . $appDir . DS . 'Lib' . DS . 'cakephp' . DS . 'lib';
|
||||
}
|
||||
|
||||
ini_set('include_path', $install . PATH_SEPARATOR . ini_get('include_path'));
|
||||
unset($root, $appDir, $install, $composerInstall);
|
||||
}
|
||||
|
||||
if (!include $dispatcher) {
|
||||
trigger_error('Could not locate CakePHP core files.', E_USER_ERROR);
|
||||
}
|
||||
unset($dispatcher);
|
||||
|
||||
return ShellDispatcher::run($argv);
|
|
@ -1 +0,0 @@
|
|||
./cake admin dumpCurrentDatabaseSchema
|
|
@ -1,19 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Check if run as root
|
||||
if [ "$EUID" -eq 0 ]; then
|
||||
echo "Please DO NOT run the worker script as root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract base directory where this script is and cd into it
|
||||
cd "${0%/*}"
|
||||
../cake CakeResque.CakeResque stop --all
|
||||
../cake CakeResque.CakeResque start --interval 5 --queue default
|
||||
../cake CakeResque.CakeResque start --interval 5 --queue prio
|
||||
../cake CakeResque.CakeResque start --interval 5 --queue cache
|
||||
../cake CakeResque.CakeResque start --interval 5 --queue email
|
||||
../cake CakeResque.CakeResque start --interval 5 --queue update
|
||||
../cake CakeResque.CakeResque startscheduler --interval 5
|
||||
|
||||
exit 0
|
|
@ -1,58 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# TODO: Put some logic inside if many worker PIDs are detected
|
||||
|
||||
# Extract base directory where this script is and cd into it
|
||||
cd "${0%/*}"
|
||||
|
||||
# Set to the current webroot owner
|
||||
WWW_USER=$(ls -l ../cake |awk {'print $3'}|tail -1)
|
||||
|
||||
# In most cases the owner of the cake script is also the user as which it should be executed.
|
||||
if [[ "$USER" != "$WWW_USER" ]]; then
|
||||
echo "You run this script as $USER and the owner of the cake command is $WWW_USER. This might be an issue."
|
||||
fi
|
||||
|
||||
# Check if run as root
|
||||
if [[ "$EUID" -eq "0" ]]; then
|
||||
echo "Please DO NOT run the worker script as root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if jq is present and enable advanced checks
|
||||
if [[ "$(jq -V > /dev/null 2> /dev/null; echo $?)" != 0 ]]; then
|
||||
echo "jq is not installed, disabling advanced checks."
|
||||
ADVANCED="0"
|
||||
else
|
||||
ADVANCED="1"
|
||||
fi
|
||||
|
||||
if [[ "$ADVANCED" == "1" ]]; then
|
||||
for worker in `echo cache default email prio scheduler update`; do
|
||||
workerStatus=$(../cake Admin getWorkers |tail -n +7 |jq -r ".$worker" |jq -r '.ok')
|
||||
PIDcount=$(../cake admin getWorkers |tail -n +7 |jq -r ".$worker.workers" |grep pid | wc -l)
|
||||
echo -n "$worker has $PIDcount PID(s)"
|
||||
if [[ "$workerStatus" != "true" ]]; then
|
||||
echo ", trying to restart."
|
||||
if [[ "$worker" != "scheduler" ]]; then
|
||||
../cake CakeResque.CakeResque start --interval 5 --queue $worker
|
||||
else
|
||||
../cake CakeResque.CakeResque startscheduler --interval 5
|
||||
fi
|
||||
else
|
||||
echo ", up and running."
|
||||
fi
|
||||
done
|
||||
exit 0
|
||||
else
|
||||
|
||||
../cake CakeResque.CakeResque stop --all
|
||||
../cake CakeResque.CakeResque start --interval 5 --queue default
|
||||
../cake CakeResque.CakeResque start --interval 5 --queue prio
|
||||
../cake CakeResque.CakeResque start --interval 5 --queue cache
|
||||
../cake CakeResque.CakeResque start --interval 5 --queue email
|
||||
../cake CakeResque.CakeResque start --interval 5 --queue update
|
||||
../cake CakeResque.CakeResque startscheduler --interval 5
|
||||
|
||||
exit 0
|
||||
fi
|
|
@ -1,22 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# TODO: Put some logic inside if many worker PIDs are detected
|
||||
|
||||
# Extract base directory where this script is and cd into it
|
||||
cd "${0%/*}"
|
||||
|
||||
# Set to the current webroot owner
|
||||
WWW_USER=$(ls -l ../cake |awk {'print $3'}|tail -1)
|
||||
|
||||
# In most cases the owner of the cake script is also the user as which it should be executed.
|
||||
if [[ "$USER" != "$WWW_USER" ]]; then
|
||||
echo "You run this script as $USER and the owner of the cake command is $WWW_USER. This might be an issue."
|
||||
fi
|
||||
|
||||
# Check if run as root
|
||||
if [[ "$EUID" -eq "0" ]]; then
|
||||
echo "Please DO NOT run the worker script as root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
../cake CakeResque.CakeResque stop --all
|
|
@ -1,228 +0,0 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property AccessLog $AccessLog
|
||||
*/
|
||||
class AccessLogsController extends AppController
|
||||
{
|
||||
public $components = [
|
||||
'RequestHandler',
|
||||
];
|
||||
|
||||
public $paginate = [
|
||||
'recursive' => -1,
|
||||
'limit' => 60,
|
||||
'fields' => ['id', 'created', 'user_id', 'org_id', 'authkey_id', 'ip', 'request_method', 'request_id', 'controller', 'action', 'url', 'response_code', 'memory_usage', 'duration', 'query_count'],
|
||||
'contain' => [
|
||||
'User' => ['fields' => ['id', 'email', 'org_id']],
|
||||
'Organisation' => ['fields' => ['id', 'name', 'uuid']],
|
||||
],
|
||||
'order' => [
|
||||
'AccessLog.id' => 'DESC'
|
||||
],
|
||||
];
|
||||
|
||||
public function admin_index()
|
||||
{
|
||||
$params = $this->IndexFilter->harvestParameters([
|
||||
'created',
|
||||
'ip',
|
||||
'user',
|
||||
'org',
|
||||
'request_id',
|
||||
'authkey_id',
|
||||
'api_request',
|
||||
'request_method',
|
||||
'controller',
|
||||
'action',
|
||||
'url',
|
||||
'user_agent',
|
||||
'memory_usage',
|
||||
'duration',
|
||||
'query_count',
|
||||
'response_code',
|
||||
]);
|
||||
|
||||
$conditions = $this->__searchConditions($params);
|
||||
|
||||
if ($this->_isRest()) {
|
||||
$list = $this->AccessLog->find('all', [
|
||||
'conditions' => $conditions,
|
||||
'contain' => $this->paginate['contain'],
|
||||
]);
|
||||
foreach ($list as &$item) {
|
||||
if (!empty($item['AccessLog']['request'])) {
|
||||
$item['AccessLog']['request'] = base64_encode($item['AccessLog']['request']);
|
||||
}
|
||||
}
|
||||
return $this->RestResponse->viewData($list, 'json');
|
||||
}
|
||||
if (empty(Configure::read('MISP.log_skip_access_logs_in_application_logs'))) {
|
||||
$this->Flash->warning(__('Access logs are logged in both application logs and access logs. Make sure you reconfigure your log monitoring tools and update MISP.log_skip_access_logs_in_application_logs.'));
|
||||
}
|
||||
|
||||
$this->AccessLog->virtualFields['has_query_log'] = 'query_log IS NOT NULL';
|
||||
$this->paginate['fields'][] = 'has_query_log';
|
||||
$this->paginate['conditions'] = $conditions;
|
||||
$list = $this->paginate();
|
||||
|
||||
$this->set('list', $list);
|
||||
$this->set('title_for_layout', __('Access logs'));
|
||||
}
|
||||
|
||||
public function admin_request($id)
|
||||
{
|
||||
$request = $this->AccessLog->find('first', [
|
||||
'conditions' => ['AccessLog.id' => $id],
|
||||
'fields' => ['AccessLog.request'],
|
||||
]);
|
||||
if (empty($request)) {
|
||||
throw new NotFoundException(__('Access log not found'));
|
||||
}
|
||||
|
||||
if (empty($request['AccessLog']['request'])) {
|
||||
throw new NotFoundException(__('Request body is empty'));
|
||||
}
|
||||
|
||||
$contentType = explode(';', $request['AccessLog']['request_content_type'], 2)[0];
|
||||
if ($contentType === 'application/x-www-form-urlencoded' || $contentType === 'multipart/form-data') {
|
||||
parse_str($request['AccessLog']['request'], $output);
|
||||
// highlight PHP array
|
||||
$highlighted = highlight_string("<?php " . var_export($output, true), true);
|
||||
$highlighted = trim($highlighted);
|
||||
$highlighted = preg_replace("|^\\<code\\>\\<span style\\=\"color\\: #[a-fA-F0-9]{0,6}\"\\>|", "", $highlighted, 1); // remove prefix
|
||||
$highlighted = preg_replace("|\\</code\\>\$|", "", $highlighted, 1); // remove suffix 1
|
||||
$highlighted = trim($highlighted); // remove line breaks
|
||||
$highlighted = preg_replace("|\\</span\\>\$|", "", $highlighted, 1); // remove suffix 2
|
||||
$highlighted = trim($highlighted); // remove line breaks
|
||||
$highlighted = preg_replace("|^(\\<span style\\=\"color\\: #[a-fA-F0-9]{0,6}\"\\>)(<\\?php )(.*?)(\\</span\\>)|", "\$1\$3\$4", $highlighted); // remove custom added "<?php "
|
||||
$data = $highlighted;
|
||||
} else {
|
||||
$data = h($request['AccessLog']['request']);
|
||||
}
|
||||
|
||||
$this->set('request', $data);
|
||||
}
|
||||
|
||||
public function admin_queryLog($id)
|
||||
{
|
||||
$request = $this->AccessLog->find('first', [
|
||||
'conditions' => ['AccessLog.id' => $id],
|
||||
'fields' => ['AccessLog.query_log'],
|
||||
]);
|
||||
if (empty($request)) {
|
||||
throw new NotFoundException(__('Access log not found'));
|
||||
}
|
||||
|
||||
if (empty($request['AccessLog']['query_log'])) {
|
||||
throw new NotFoundException(__('Query log is empty'));
|
||||
}
|
||||
|
||||
$this->set('queryLog', $request['AccessLog']['query_log']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
private function __searchConditions(array $params)
|
||||
{
|
||||
$qbRules = [];
|
||||
foreach ($params as $key => $value) {
|
||||
if ($key === 'created') {
|
||||
$qbRules[] = [
|
||||
'id' => $key,
|
||||
'operator' => is_array($value) ? 'between' : 'greater_or_equal',
|
||||
'value' => $value,
|
||||
];
|
||||
} else {
|
||||
if (is_array($value)) {
|
||||
$value = implode('||', $value);
|
||||
}
|
||||
$qbRules[] = [
|
||||
'id' => $key,
|
||||
'value' => $value,
|
||||
];
|
||||
}
|
||||
}
|
||||
$this->set('qbRules', $qbRules);
|
||||
|
||||
$conditions = [];
|
||||
if (isset($params['user'])) {
|
||||
if (is_numeric($params['user'])) {
|
||||
$conditions['AccessLog.user_id'] = $params['user'];
|
||||
} else {
|
||||
$user = $this->User->find('first', [
|
||||
'conditions' => ['User.email' => $params['user']],
|
||||
'fields' => ['id'],
|
||||
]);
|
||||
if (!empty($user)) {
|
||||
$conditions['AccessLog.user_id'] = $user['User']['id'];
|
||||
} else {
|
||||
$conditions['AccessLog.user_id'] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($params['ip'])) {
|
||||
$conditions['AccessLog.ip'] = inet_pton($params['ip']);
|
||||
}
|
||||
foreach (['authkey_id', 'request_id', 'controller', 'action'] as $field) {
|
||||
if (isset($params[$field])) {
|
||||
$conditions['AccessLog.' . $field] = $params[$field];
|
||||
}
|
||||
}
|
||||
if (isset($params['url'])) {
|
||||
$conditions['AccessLog.url LIKE'] = "%{$params['url']}%";
|
||||
}
|
||||
if (isset($params['user_agent'])) {
|
||||
$conditions['AccessLog.user_agent LIKE'] = "%{$params['user_agent']}%";
|
||||
}
|
||||
if (isset($params['memory_usage'])) {
|
||||
$conditions['AccessLog.memory_usage >='] = ($params['memory_usage'] * 1024);
|
||||
}
|
||||
if (isset($params['memory_usage'])) {
|
||||
$conditions['AccessLog.memory_usage >='] = ($params['memory_usage'] * 1024);
|
||||
}
|
||||
if (isset($params['duration'])) {
|
||||
$conditions['AccessLog.duration >='] = $params['duration'];
|
||||
}
|
||||
if (isset($params['query_count'])) {
|
||||
$conditions['AccessLog.query_count >='] = $params['query_count'];
|
||||
}
|
||||
if (isset($params['request_method'])) {
|
||||
$methodId = array_flip(AccessLog::REQUEST_TYPES)[$params['request_method']] ?? -1;
|
||||
$conditions['AccessLog.request_method'] = $methodId;
|
||||
}
|
||||
if (isset($params['org'])) {
|
||||
if (is_numeric($params['org'])) {
|
||||
$conditions['AccessLog.org_id'] = $params['org'];
|
||||
} else {
|
||||
$org = $this->AccessLog->Organisation->fetchOrg($params['org']);
|
||||
if ($org) {
|
||||
$conditions['AccessLog.org_id'] = $org['id'];
|
||||
} else {
|
||||
$conditions['AccessLog.org_id'] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($params['created'])) {
|
||||
$tempData = is_array($params['created']) ? $params['created'] : [$params['created']];
|
||||
foreach ($tempData as $k => $v) {
|
||||
$tempData[$k] = $this->AccessLog->resolveTimeDelta($v);
|
||||
}
|
||||
if (count($tempData) === 1) {
|
||||
$conditions['AccessLog.created >='] = date("Y-m-d H:i:s", $tempData[0]);
|
||||
} else {
|
||||
if ($tempData[0] < $tempData[1]) {
|
||||
$temp = $tempData[1];
|
||||
$tempData[1] = $tempData[0];
|
||||
$tempData[0] = $temp;
|
||||
}
|
||||
$conditions['AND'][] = ['AccessLog.created <=' => date("Y-m-d H:i:s", $tempData[0])];
|
||||
$conditions['AND'][] = ['AccessLog.created >=' => date("Y-m-d H:i:s", $tempData[1])];
|
||||
}
|
||||
}
|
||||
return $conditions;
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property AdminCrudComponent $AdminCrud
|
||||
*/
|
||||
class AllowedlistsController extends AppController
|
||||
{
|
||||
public $components = array(
|
||||
'AdminCrud'
|
||||
);
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 60,
|
||||
'order' => array(
|
||||
'Allowedlist.name' => 'ASC'
|
||||
)
|
||||
);
|
||||
|
||||
public function admin_add()
|
||||
{
|
||||
$this->set('action', 'add');
|
||||
$this->AdminCrud->adminAdd();
|
||||
}
|
||||
|
||||
public function admin_index()
|
||||
{
|
||||
$this->AdminCrud->adminIndex();
|
||||
$this->render('index');
|
||||
}
|
||||
|
||||
public function admin_edit($id = null)
|
||||
{
|
||||
$this->AdminCrud->adminEdit($id);
|
||||
$this->set('action', 'edit');
|
||||
$this->set('id', $id);
|
||||
$this->render('admin_add');
|
||||
}
|
||||
|
||||
public function admin_delete($id = null)
|
||||
{
|
||||
$this->AdminCrud->adminDelete($id);
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->recursive = 0;
|
||||
$this->set('list', $this->paginate());
|
||||
}
|
||||
}
|
|
@ -1,310 +0,0 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
class ApiController extends AppController
|
||||
{
|
||||
public function beforeFilter()
|
||||
{
|
||||
parent::beforeFilter();
|
||||
$this->Security->unlockedActions[] = 'getApiInfo';
|
||||
}
|
||||
|
||||
public function openapi()
|
||||
{
|
||||
$user = $this->_closeSession();
|
||||
if (!$user['Role']['perm_auth']) {
|
||||
$this->Flash->warning(__('Your role do not allow API access.'));
|
||||
} else if ($this->User->advancedAuthkeysEnabled() && !$this->User->AuthKey->userHasAuthKey($user['id'])) {
|
||||
$this->Flash->warning(__('You don\'t have auth key to use this API. You can generate one at your profile.'));
|
||||
}
|
||||
$this->set('title_for_layout', __('OpenAPI'));
|
||||
}
|
||||
|
||||
public function viewDeprecatedFunctionUse()
|
||||
{
|
||||
$data = $this->Deprecation->getDeprecatedAccessList();
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($data, $this->response->type());
|
||||
} else {
|
||||
$this->layout = false;
|
||||
$this->set('data', $data);
|
||||
}
|
||||
}
|
||||
|
||||
public function getAllApis()
|
||||
{
|
||||
$user = $this->_closeSession();
|
||||
$allValidApis = $this->RestResponse->getAllApis($user);
|
||||
$allValidApisFieldsConstraint = $this->RestResponse->getAllApisFieldsConstraint($user);
|
||||
$output = [
|
||||
'allValidApis' => $allValidApis,
|
||||
'fieldsConstraint' => $allValidApisFieldsConstraint,
|
||||
];
|
||||
return $this->RestResponse->viewData($output, 'json');
|
||||
}
|
||||
|
||||
public function getApiInfo()
|
||||
{
|
||||
$relative_path = $this->request->data['url'];
|
||||
$result = $this->RestResponse->getApiInfo($relative_path);
|
||||
if ($this->_isRest()) {
|
||||
if (!empty($result)) {
|
||||
$result['api_info'] = $result;
|
||||
}
|
||||
return $this->RestResponse->viewData($result, $this->response->type());
|
||||
}
|
||||
if (empty($result)) {
|
||||
return $this->RestResponse->viewData(' ', $this->response->type());
|
||||
}
|
||||
$this->layout = false;
|
||||
$this->autoRender = false;
|
||||
$this->set('api_info', $result);
|
||||
$this->render('ajax/get_api_info');
|
||||
}
|
||||
|
||||
public function rest()
|
||||
{
|
||||
if ($this->request->is('post')) {
|
||||
$request = $this->request->data;
|
||||
if (!empty($request['Server'])) {
|
||||
$request = $this->request->data['Server'];
|
||||
}
|
||||
$curl = '';
|
||||
$python = '';
|
||||
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 = sprintf(
|
||||
"Authorization: %s \nAccept: application/json\nContent-type: application/json",
|
||||
__('YOUR_API_KEY')
|
||||
);
|
||||
$this->set('header', $header);
|
||||
|
||||
if ($this->User->advancedAuthkeysEnabled() && !$this->User->AuthKey->userHasAuthKey($this->Auth->user('id'))) {
|
||||
$this->Flash->warning(__('You don\'t have auth key to use this REST client. You can generate one at your profile.'));
|
||||
}
|
||||
|
||||
$allAccessibleApis = $this->RestResponse->getAccessibleApis($this->Auth->user());
|
||||
$this->set('allAccessibleApis', $allAccessibleApis);
|
||||
$this->set('title_for_layout', __('REST client'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $request
|
||||
* @param string $curl
|
||||
* @param string $python
|
||||
* @return array|false
|
||||
*/
|
||||
private function __doRestQuery(array $request, &$curl = false, &$python = false)
|
||||
{
|
||||
$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);
|
||||
}
|
||||
|
||||
if (empty($request['body'])) {
|
||||
$historyBody = '';
|
||||
} else if (strlen($request['body']) > 65535) {
|
||||
$historyBody = ''; // body is too long to save into history table
|
||||
} else {
|
||||
$historyBody = $request['body'];
|
||||
}
|
||||
|
||||
$rest_history_item = array(
|
||||
'headers' => $logHeaders,
|
||||
'body' => $historyBody,
|
||||
'url' => $request['url'],
|
||||
'http_method' => $request['method'],
|
||||
'use_full_path' => empty($request['use_full_path']) ? false : $request['use_full_path'],
|
||||
'show_result' => $request['show_result'],
|
||||
'skip_ssl' => $request['skip_ssl_validation'],
|
||||
'bookmark' => $request['bookmark'],
|
||||
'bookmark_name' => $request['name'],
|
||||
'timestamp' => time(),
|
||||
);
|
||||
if (!empty($request['url'])) {
|
||||
if (empty($request['use_full_path']) || empty(Configure::read('Security.rest_client_enable_arbitrary_urls'))) {
|
||||
$path = preg_replace('#^(://|[^/?])+#', '', $request['url']);
|
||||
$url = empty(Configure::read('Security.rest_client_baseurl')) ? (Configure::read('MISP.baseurl') . $path) : (Configure::read('Security.rest_client_baseurl') . $path);
|
||||
unset($request['url']);
|
||||
} else {
|
||||
$url = $request['url'];
|
||||
}
|
||||
} else {
|
||||
throw new InvalidArgumentException('URL not set.');
|
||||
}
|
||||
|
||||
$params = ['timeout' => 300];
|
||||
if (!empty($request['skip_ssl_validation'])) {
|
||||
$params['ssl_verify_peer'] = false;
|
||||
$params['ssl_verify_host'] = false;
|
||||
$params['ssl_verify_peer_name'] = false;
|
||||
$params['ssl_allow_self_signed'] = true;
|
||||
}
|
||||
App::uses('HttpSocketExtended', 'Tools');
|
||||
$HttpSocket = new HttpSocketExtended($params);
|
||||
|
||||
$temp_headers = empty($request['header']) ? [] : explode("\n", $request['header']);
|
||||
$request['header'] = array(
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/json',
|
||||
'User-Agent' => 'MISP REST Client',
|
||||
);
|
||||
foreach ($temp_headers as $header) {
|
||||
$header = explode(':', $header);
|
||||
$header[0] = trim($header[0]);
|
||||
$header[1] = trim($header[1]);
|
||||
$request['header'][$header[0]] = $header[1];
|
||||
}
|
||||
$start = microtime(true);
|
||||
if (
|
||||
!empty($request['method']) &&
|
||||
$request['method'] === 'GET'
|
||||
) {
|
||||
if ($curl !== false) {
|
||||
$curl = $this->__generateCurlQuery('get', $request, $url);
|
||||
}
|
||||
if ($python !== false) {
|
||||
$python = $this->__generatePythonScript($request, $url);
|
||||
}
|
||||
$response = $HttpSocket->get($url, false, array('header' => $request['header']));
|
||||
} elseif (
|
||||
!empty($request['method']) &&
|
||||
$request['method'] === 'POST' &&
|
||||
!empty($request['body'])
|
||||
) {
|
||||
if ($curl !== false) {
|
||||
$curl = $this->__generateCurlQuery('post', $request, $url);
|
||||
}
|
||||
if ($python !== false) {
|
||||
$python = $this->__generatePythonScript($request, $url);
|
||||
}
|
||||
$response = $HttpSocket->post($url, $request['body'], array('header' => $request['header']));
|
||||
} elseif (
|
||||
!empty($request['method']) &&
|
||||
$request['method'] === 'DELETE'
|
||||
) {
|
||||
if ($curl !== false) {
|
||||
$curl = $this->__generateCurlQuery('delete', $request, $url);
|
||||
}
|
||||
if ($python !== false) {
|
||||
$python = $this->__generatePythonScript($request, $url);
|
||||
}
|
||||
$response = $HttpSocket->delete($url, false, array('header' => $request['header']));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
$viewData = [
|
||||
'duration' => round((microtime(true) - $start) * 1000, 2) . ' ms',
|
||||
'url' => $url,
|
||||
'code' => $response->code,
|
||||
'headers' => $response->headers,
|
||||
];
|
||||
|
||||
if (!empty($request['show_result'])) {
|
||||
$viewData['data'] = $response->body;
|
||||
} else {
|
||||
if ($response->isOk()) {
|
||||
$viewData['data'] = 'Success.';
|
||||
} else {
|
||||
$viewData['data'] = 'Something went wrong.';
|
||||
}
|
||||
}
|
||||
$rest_history_item['outcome'] = $response->code;
|
||||
|
||||
$this->loadModel('RestClientHistory');
|
||||
$this->RestClientHistory->insert($this->Auth->user(), $rest_history_item);
|
||||
|
||||
return $viewData;
|
||||
}
|
||||
|
||||
private function __generatePythonScript(array $request, $url)
|
||||
{
|
||||
$slashCounter = 0;
|
||||
$baseurl = '';
|
||||
$relative = '';
|
||||
$verifyCert = ($url[4] === 's') ? 'True' : 'False';
|
||||
for ($i = 0; $i < strlen($url); $i++) {
|
||||
//foreach ($url as $url[$i]) {
|
||||
if ($url[$i] === '/') {
|
||||
$slashCounter += 1;
|
||||
if ($slashCounter == 3) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($slashCounter < 3) {
|
||||
$baseurl .= $url[$i];
|
||||
} else {
|
||||
$relative .= $url[$i];
|
||||
}
|
||||
}
|
||||
$python_script =
|
||||
sprintf(
|
||||
'misp_url = \'%s\'
|
||||
misp_key = \'%s\'
|
||||
misp_verifycert = %s
|
||||
relative_path = \'%s\'
|
||||
body = %s
|
||||
|
||||
from pymisp import ExpandedPyMISP
|
||||
|
||||
misp = ExpandedPyMISP(misp_url, misp_key, misp_verifycert)
|
||||
misp.direct_call(relative_path, body)
|
||||
',
|
||||
$baseurl,
|
||||
$request['header']['Authorization'],
|
||||
$verifyCert,
|
||||
$relative,
|
||||
(empty($request['body']) ? 'None' : $request['body'])
|
||||
);
|
||||
return $python_script;
|
||||
}
|
||||
|
||||
private function __generateCurlQuery($type, array $request, $url)
|
||||
{
|
||||
if ($type === 'get') {
|
||||
$curl = sprintf(
|
||||
'curl \%s -H "Authorization: %s" \%s -H "Accept: %s" \%s -H "Content-type: %s" \%s %s',
|
||||
PHP_EOL,
|
||||
$request['header']['Authorization'],
|
||||
PHP_EOL,
|
||||
$request['header']['Accept'],
|
||||
PHP_EOL,
|
||||
$request['header']['Content-Type'],
|
||||
PHP_EOL,
|
||||
$url
|
||||
);
|
||||
} else {
|
||||
$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'])),
|
||||
PHP_EOL,
|
||||
$request['header']['Authorization'],
|
||||
PHP_EOL,
|
||||
$request['header']['Accept'],
|
||||
PHP_EOL,
|
||||
$request['header']['Content-Type'],
|
||||
PHP_EOL,
|
||||
$url
|
||||
);
|
||||
}
|
||||
return $curl;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,591 +0,0 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
App::uses('AuditLog', 'Model');
|
||||
|
||||
/**
|
||||
* @property AuditLog $AuditLog
|
||||
*/
|
||||
class AuditLogsController extends AppController
|
||||
{
|
||||
public $components = [
|
||||
'RequestHandler',
|
||||
];
|
||||
|
||||
/** @var array */
|
||||
private $actions;
|
||||
|
||||
/** @var string[] */
|
||||
private $models = [
|
||||
'Attribute',
|
||||
'Allowedlist',
|
||||
'AuthKey',
|
||||
'Cerebrate',
|
||||
'CorrelationExclusion',
|
||||
'Event',
|
||||
'EventBlocklist',
|
||||
'EventReport',
|
||||
'Feed',
|
||||
'DecayingModel',
|
||||
'Object',
|
||||
'ObjectTemplate',
|
||||
'Organisation',
|
||||
'OrgBlocklist',
|
||||
'Post',
|
||||
'Regexp',
|
||||
'Role',
|
||||
'Server',
|
||||
'ShadowAttribute',
|
||||
'SharingGroup',
|
||||
'SystemSetting',
|
||||
'Tag',
|
||||
'TagCollection',
|
||||
'TagCollectionTag',
|
||||
'Task',
|
||||
'Taxonomy',
|
||||
'Template',
|
||||
'Thread',
|
||||
'User',
|
||||
'UserSetting',
|
||||
'Galaxy',
|
||||
'GalaxyCluster',
|
||||
'GalaxyClusterBlocklist',
|
||||
'GalaxyClusterRelation',
|
||||
'News',
|
||||
'Warninglist',
|
||||
'Workflow',
|
||||
'WorkflowBlueprint',
|
||||
];
|
||||
|
||||
public $paginate = [
|
||||
'recursive' => -1,
|
||||
'limit' => 60,
|
||||
'fields' => ['id', 'created', 'user_id', 'org_id', 'action', 'model', 'model_id', 'model_title', 'event_id', 'change'],
|
||||
'contain' => [
|
||||
'User' => ['fields' => ['id', 'email', 'org_id']],
|
||||
'Organisation' => ['fields' => ['id', 'name', 'uuid']],
|
||||
],
|
||||
'order' => [
|
||||
'AuditLog.id' => 'DESC'
|
||||
],
|
||||
];
|
||||
|
||||
public function __construct($request = null, $response = null)
|
||||
{
|
||||
parent::__construct($request, $response);
|
||||
$this->actions = [
|
||||
AuditLog::ACTION_ADD => __('Add'),
|
||||
AuditLog::ACTION_EDIT => __('Edit'),
|
||||
AuditLog::ACTION_SOFT_DELETE => __('Soft delete'),
|
||||
AuditLog::ACTION_DELETE => __('Delete'),
|
||||
AuditLog::ACTION_UNDELETE => __('Undelete'),
|
||||
AuditLog::ACTION_TAG => __('Tag'),
|
||||
AuditLog::ACTION_TAG_LOCAL => __('Tag'),
|
||||
AuditLog::ACTION_REMOVE_TAG => __('Remove tag'),
|
||||
AuditLog::ACTION_REMOVE_TAG_LOCAL => __('Remove tag'),
|
||||
AuditLog::ACTION_GALAXY => __('Galaxy cluster'),
|
||||
AuditLog::ACTION_GALAXY_LOCAL => __('Galaxy cluster'),
|
||||
AuditLog::ACTION_REMOVE_GALAXY => __('Remove galaxy cluster'),
|
||||
AuditLog::ACTION_REMOVE_GALAXY_LOCAL => __('Remove galaxy cluster'),
|
||||
AuditLog::ACTION_PUBLISH => __('Publish'),
|
||||
AuditLog::ACTION_PUBLISH_SIGHTINGS => __('Publish sightings'),
|
||||
];
|
||||
}
|
||||
|
||||
public function admin_index()
|
||||
{
|
||||
$this->paginate['fields'][] = 'ip';
|
||||
$this->paginate['fields'][] = 'request_type';
|
||||
$this->paginate['fields'][] = 'authkey_id';
|
||||
|
||||
if ($this->_isRest()) {
|
||||
$this->paginate['fields'][] = 'request_id';
|
||||
}
|
||||
if (!Configure::read('MISP.log_new_audit')) {
|
||||
$this->Flash->warning(__("Audit log is not enabled. See 'MISP.log_new_audit' in the Server Settings. (Administration -> Server Settings -> MISP tab)"));
|
||||
}
|
||||
$params = $this->IndexFilter->harvestParameters([
|
||||
'ip',
|
||||
'user',
|
||||
'request_id',
|
||||
'authkey_id',
|
||||
'model',
|
||||
'model_id',
|
||||
'event_id',
|
||||
'model_title',
|
||||
'action',
|
||||
'org',
|
||||
'created',
|
||||
'request_type',
|
||||
]);
|
||||
|
||||
$this->paginate['conditions'] = $this->__searchConditions($params);
|
||||
$list = $this->paginate();
|
||||
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($list, 'json');
|
||||
}
|
||||
|
||||
$list = $this->__appendModelLinks($list);
|
||||
foreach ($list as $k => $item) {
|
||||
$list[$k]['AuditLog']['action_human'] = $this->actions[$item['AuditLog']['action']];
|
||||
}
|
||||
|
||||
$this->set('list', $list);
|
||||
$this->set('actions', [
|
||||
AuditLog::ACTION_ADD => __('Add'),
|
||||
AuditLog::ACTION_EDIT => __('Edit'),
|
||||
AuditLog::ACTION_SOFT_DELETE => __('Soft delete'),
|
||||
AuditLog::ACTION_DELETE => __('Delete'),
|
||||
AuditLog::ACTION_UNDELETE => __('Undelete'),
|
||||
AuditLog::ACTION_TAG . '||' . AuditLog::ACTION_TAG_LOCAL => __('Tag'),
|
||||
AuditLog::ACTION_REMOVE_TAG . '||' . AuditLog::ACTION_REMOVE_TAG_LOCAL => __('Remove tag'),
|
||||
AuditLog::ACTION_GALAXY . '||' . AuditLog::ACTION_GALAXY_LOCAL => __('Galaxy cluster'),
|
||||
AuditLog::ACTION_REMOVE_GALAXY . '||' . AuditLog::ACTION_REMOVE_GALAXY_LOCAL => __('Remove galaxy cluster'),
|
||||
AuditLog::ACTION_PUBLISH => __('Publish'),
|
||||
AuditLog::ACTION_PUBLISH_SIGHTINGS => $this->actions[AuditLog::ACTION_PUBLISH_SIGHTINGS],
|
||||
]);
|
||||
$models = $this->models;
|
||||
sort($models);
|
||||
$this->set('models', $models);
|
||||
$this->set('title_for_layout', __('Audit logs'));
|
||||
}
|
||||
|
||||
public function eventIndex($eventId, $org = null)
|
||||
{
|
||||
$event = $this->AuditLog->Event->fetchSimpleEvent($this->Auth->user(), $eventId);
|
||||
if (empty($event)) {
|
||||
throw new NotFoundException('Invalid event.');
|
||||
}
|
||||
|
||||
$this->paginate['conditions'] = $this->__createEventIndexConditions($event);
|
||||
|
||||
$params = $this->IndexFilter->harvestParameters(['created', 'org']);
|
||||
if ($org) {
|
||||
$params['org'] = $org;
|
||||
}
|
||||
$this->paginate['conditions'][] = $this->__searchConditions($params);
|
||||
|
||||
$list = $this->paginate();
|
||||
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
// Remove all user info about users from different org
|
||||
$orgUserIds = $this->User->find('column', [
|
||||
'conditions' => ['User.org_id' => $this->Auth->user('org_id')],
|
||||
'fields' => ['User.id'],
|
||||
]);
|
||||
foreach ($list as $k => $item) {
|
||||
if ($item['AuditLog']['user_id'] == 0) {
|
||||
continue;
|
||||
}
|
||||
if (!in_array($item['User']['id'], $orgUserIds)) {
|
||||
unset($list[$k]['User']);
|
||||
unset($list[$k]['AuditLog']['user_id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($list, 'json');
|
||||
}
|
||||
|
||||
foreach ($list as $k => $item) {
|
||||
$list[$k]['AuditLog']['action_human'] = $this->actions[$item['AuditLog']['action']];
|
||||
}
|
||||
|
||||
$this->set('data', $list);
|
||||
$this->set('event', $event);
|
||||
$this->set('mayModify', $this->__canModifyEvent($event));
|
||||
$this->set('menuData', [
|
||||
'menuList' => 'event',
|
||||
'menuItem' => 'eventLog'
|
||||
]);
|
||||
}
|
||||
|
||||
public function fullChange($id)
|
||||
{
|
||||
$log = $this->AuditLog->find('first', [
|
||||
'conditions' => ['id' => $id],
|
||||
'recursive' => -1,
|
||||
'fields' => ['change', 'action'],
|
||||
]);
|
||||
if (empty($log)) {
|
||||
throw new Exception('Log not found.');
|
||||
}
|
||||
$this->set('log', $log);
|
||||
}
|
||||
|
||||
public function returnDates($org = 'all')
|
||||
{
|
||||
$user = $this->_closeSession();
|
||||
if (!$user['Role']['perm_sharing_group'] && !empty(Configure::read('Security.hide_organisation_index_from_users'))) {
|
||||
if ($org !== 'all' && $org !== $user['Organisation']['name']) {
|
||||
throw new MethodNotAllowedException('Invalid organisation.');
|
||||
}
|
||||
}
|
||||
|
||||
$data = $this->AuditLog->returnDates($org);
|
||||
return $this->RestResponse->viewData($data, $this->response->type());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function __searchConditions(array $params)
|
||||
{
|
||||
$qbRules = [];
|
||||
foreach ($params as $key => $value) {
|
||||
if ($key === 'model' && strpos($value, ':') !== false) {
|
||||
$parts = explode(':', $value);
|
||||
$qbRules[] = [
|
||||
'id' => 'model',
|
||||
'value' => $parts[0],
|
||||
];
|
||||
$qbRules[] = [
|
||||
'id' => 'model_id',
|
||||
'value' => $parts[1],
|
||||
];
|
||||
} elseif ($key === 'created') {
|
||||
$qbRules[] = [
|
||||
'id' => $key,
|
||||
'operator' => is_array($value) ? 'between' : 'greater_or_equal',
|
||||
'value' => $value,
|
||||
];
|
||||
} else {
|
||||
if (is_array($value)) {
|
||||
$value = implode('||', $value);
|
||||
}
|
||||
$qbRules[] = [
|
||||
'id' => $key,
|
||||
'value' => $value,
|
||||
];
|
||||
}
|
||||
}
|
||||
$this->set('qbRules', $qbRules);
|
||||
|
||||
$conditions = [];
|
||||
if (isset($params['user'])) {
|
||||
if (strtoupper($params['user']) === 'SYSTEM') {
|
||||
$conditions['AuditLog.user_id'] = 0;
|
||||
} else if (is_numeric($params['user'])) {
|
||||
$conditions['AuditLog.user_id'] = $params['user'];
|
||||
} else {
|
||||
$user = $this->User->find('first', [
|
||||
'conditions' => ['User.email' => $params['user']],
|
||||
'fields' => ['id'],
|
||||
]);
|
||||
if (!empty($user)) {
|
||||
$conditions['AuditLog.user_id'] = $user['User']['id'];
|
||||
} else {
|
||||
$conditions['AuditLog.user_id'] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($params['ip'])) {
|
||||
$conditions['AuditLog.ip'] = inet_pton($params['ip']);
|
||||
}
|
||||
if (isset($params['authkey_id'])) {
|
||||
$conditions['AuditLog.authkey_id'] = $params['authkey_id'];
|
||||
}
|
||||
if (isset($params['request_id'])) {
|
||||
$conditions['AuditLog.request_id'] = $params['request_id'];
|
||||
}
|
||||
if (isset($params['request_type'])) {
|
||||
$conditions['AuditLog.request_type'] = $params['request_type'];
|
||||
}
|
||||
if (isset($params['model'])) {
|
||||
$conditions['AuditLog.model'] = $params['model'];
|
||||
}
|
||||
if (isset($params['model_id'])) {
|
||||
$conditions['AuditLog.model_id'] = $params['model_id'];
|
||||
}
|
||||
if (isset($params['event_id'])) {
|
||||
$conditions['AuditLog.event_id'] = $params['event_id'];
|
||||
}
|
||||
if (isset($params['model_title'])) {
|
||||
$conditions['AuditLog.model_title LIKE'] = '%' . $params['model_title'] . '%';
|
||||
}
|
||||
if (isset($params['action'])) {
|
||||
$conditions['AuditLog.action'] = $params['action'];
|
||||
}
|
||||
if (isset($params['org'])) {
|
||||
if (is_numeric($params['org'])) {
|
||||
$conditions['AuditLog.org_id'] = $params['org'];
|
||||
} else {
|
||||
$org = $this->AuditLog->Organisation->fetchOrg($params['org']);
|
||||
if ($org) {
|
||||
$conditions['AuditLog.org_id'] = $org['id'];
|
||||
} else {
|
||||
$conditions['AuditLog.org_id'] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($params['created'])) {
|
||||
$tempData = is_array($params['created']) ? $params['created'] : [$params['created']];
|
||||
foreach ($tempData as $k => $v) {
|
||||
$tempData[$k] = $this->AuditLog->resolveTimeDelta($v);
|
||||
}
|
||||
if (count($tempData) === 1) {
|
||||
$conditions['AuditLog.created >='] = date("Y-m-d H:i:s", $tempData[0]);
|
||||
} else {
|
||||
if ($tempData[0] < $tempData[1]) {
|
||||
$temp = $tempData[1];
|
||||
$tempData[1] = $tempData[0];
|
||||
$tempData[0] = $temp;
|
||||
}
|
||||
$conditions['AND'][] = ['AuditLog.created <=' => date("Y-m-d H:i:s", $tempData[0])];
|
||||
$conditions['AND'][] = ['AuditLog.created >=' => date("Y-m-d H:i:s", $tempData[1])];
|
||||
}
|
||||
}
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create conditions that will include just events parts that user can see.
|
||||
* @param array $event
|
||||
* @return array
|
||||
*/
|
||||
private function __createEventIndexConditions(array $event)
|
||||
{
|
||||
if ($this->_isSiteAdmin() || $event['Event']['orgc_id'] == $this->Auth->user('org_id')) {
|
||||
// Site admins and event owners can see all changes
|
||||
return ['event_id' => $event['Event']['id']];
|
||||
}
|
||||
|
||||
$event = $this->AuditLog->Event->fetchEvent($this->Auth->user(), [
|
||||
'eventid' => $event['Event']['id'],
|
||||
'sgReferenceOnly' => 1,
|
||||
'deleted' => [0, 1],
|
||||
'deleted_proposals' => 1,
|
||||
'noSightings' => true,
|
||||
'includeEventCorrelations' => false,
|
||||
'excludeGalaxy' => true,
|
||||
])[0];
|
||||
|
||||
$attributeIds = [];
|
||||
$objectIds = [];
|
||||
$proposalIds = array_column($event['ShadowAttribute'], 'id');
|
||||
$objectReferenceId = [];
|
||||
foreach ($event['Attribute'] as $aa) {
|
||||
$attributeIds[] = $aa['id'];
|
||||
if (!empty($aa['ShadowAttribute'])) {
|
||||
foreach ($aa['ShadowAttribute'] as $sa) {
|
||||
$proposalIds[] = $sa['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($event['Attribute']);
|
||||
foreach ($event['Object'] as $ob) {
|
||||
foreach ($ob['Attribute'] as $aa) {
|
||||
$attributeIds[] = $aa['id'];
|
||||
if (!empty($aa['ShadowAttribute'])) {
|
||||
foreach ($aa['ShadowAttribute'] as $sa) {
|
||||
$proposalIds[] = $sa['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($ob['ObjectReference'] as $or) {
|
||||
$objectReferenceId[] = $or['id'];
|
||||
}
|
||||
$objectIds[] = $ob['id'];
|
||||
}
|
||||
unset($event['Object']);
|
||||
|
||||
$conditions = [];
|
||||
$conditions['AND']['event_id'] = $event['Event']['id'];
|
||||
$conditions['AND']['OR'][] = ['model' => 'Event'];
|
||||
|
||||
$parts = [
|
||||
'Attribute' => $attributeIds,
|
||||
'ShadowAttribute' => $proposalIds,
|
||||
'Object' => $objectIds,
|
||||
'ObjectReference' => $objectReferenceId,
|
||||
'EventReport' => array_column($event['EventReport'], 'id'),
|
||||
];
|
||||
|
||||
foreach ($parts as $model => $modelIds) {
|
||||
if (!empty($modelIds)) {
|
||||
$conditions['AND']['OR'][] = [
|
||||
'AND' => [
|
||||
'model' => $model,
|
||||
'model_id' => $modelIds,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate link to model view if exists and use has permission to access it.
|
||||
* @param array $auditLogs
|
||||
* @return array
|
||||
*/
|
||||
private function __appendModelLinks(array $auditLogs)
|
||||
{
|
||||
$models = [];
|
||||
foreach ($auditLogs as $auditLog) {
|
||||
if (isset($models[$auditLog['AuditLog']['model']])) {
|
||||
$models[$auditLog['AuditLog']['model']][] = $auditLog['AuditLog']['model_id'];
|
||||
} else {
|
||||
$models[$auditLog['AuditLog']['model']] = [$auditLog['AuditLog']['model_id']];
|
||||
}
|
||||
}
|
||||
|
||||
$eventIds = isset($models['Event']) ? $models['Event'] : [];
|
||||
|
||||
if (isset($models['ObjectReference'])) {
|
||||
$this->loadModel('ObjectReference');
|
||||
$objectReferences = $this->ObjectReference->find('list', [
|
||||
'conditions' => ['ObjectReference.id' => array_unique($models['ObjectReference'])],
|
||||
'fields' => ['ObjectReference.id', 'ObjectReference.object_id'],
|
||||
]);
|
||||
}
|
||||
|
||||
if (isset($models['Object']) || isset($objectReferences)) {
|
||||
$objectIds = array_unique(array_merge(
|
||||
isset($models['Object']) ? $models['Object'] : [],
|
||||
isset($objectReferences) ? array_values($objectReferences) : []
|
||||
));
|
||||
$this->loadModel('MispObject');
|
||||
$conditions = $this->MispObject->buildConditions($this->Auth->user());
|
||||
$conditions['Object.id'] = $objectIds;
|
||||
$objects = $this->MispObject->find('all', [
|
||||
'conditions' => $conditions,
|
||||
'contain' => ['Event'],
|
||||
'fields' => ['Object.id', 'Object.event_id', 'Object.uuid', 'Object.deleted'],
|
||||
]);
|
||||
$objects = array_column(array_column($objects, 'Object'), null, 'id');
|
||||
$eventIds = array_merge($eventIds, array_column($objects, 'event_id'));
|
||||
}
|
||||
|
||||
if (isset($models['Attribute'])) {
|
||||
$this->loadModel('Attribute');
|
||||
$attributes = $this->Attribute->fetchAttributesSimple($this->Auth->user(), [
|
||||
'conditions' => ['Attribute.id' => array_unique($models['Attribute'])],
|
||||
'fields' => ['Attribute.id', 'Attribute.event_id', 'Attribute.uuid', 'Attribute.deleted'],
|
||||
]);
|
||||
$attributes = array_column(array_column($attributes, 'Attribute'), null, 'id');
|
||||
$eventIds = array_merge($eventIds, array_column($attributes, 'event_id'));
|
||||
}
|
||||
|
||||
if (isset($models['ShadowAttribute'])) {
|
||||
$this->loadModel('ShadowAttribute');
|
||||
$conditions = $this->ShadowAttribute->buildConditions($this->Auth->user());
|
||||
$conditions['AND'][] = ['ShadowAttribute.id' => array_unique($models['ShadowAttribute'])];
|
||||
$shadowAttributes = $this->ShadowAttribute->find('all', [
|
||||
'conditions' => $conditions,
|
||||
'fields' => ['ShadowAttribute.id', 'ShadowAttribute.event_id', 'ShadowAttribute.uuid', 'ShadowAttribute.deleted'],
|
||||
'contain' => ['Event', 'Attribute'],
|
||||
]);
|
||||
$shadowAttributes = array_column(array_column($shadowAttributes, 'ShadowAttribute'), null, 'id');
|
||||
$eventIds = array_merge($eventIds, array_column($shadowAttributes, 'event_id'));
|
||||
}
|
||||
|
||||
if (!empty($eventIds)) {
|
||||
$this->loadModel('Event');
|
||||
$conditions = $this->Event->createEventConditions($this->Auth->user());
|
||||
$conditions['Event.id'] = array_unique($eventIds);
|
||||
$events = $this->Event->find('list', [
|
||||
'conditions' => $conditions,
|
||||
'fields' => ['Event.id', 'Event.info'],
|
||||
]);
|
||||
}
|
||||
|
||||
$links = [
|
||||
'ObjectTemplate' => 'objectTemplates',
|
||||
'AuthKey' => 'auth_keys',
|
||||
'GalaxyCluster' => 'galaxy_clusters',
|
||||
'Galaxy' => 'galaxies',
|
||||
'Organisation' => 'organisation',
|
||||
'Warninglist' => 'warninglists',
|
||||
'User' => 'admin/users',
|
||||
'Role' => 'roles',
|
||||
'EventReport' => 'eventReports',
|
||||
'SharingGroup' => 'sharing_groups',
|
||||
'Taxonomy' => 'taxonomies',
|
||||
];
|
||||
|
||||
$existingObjects = [];
|
||||
foreach ($links as $modelName => $foo) {
|
||||
if (isset($models[$modelName])) {
|
||||
$this->loadModel($modelName);
|
||||
$data = $this->{$modelName}->find('column', [
|
||||
'conditions' => ['id' => array_unique($models[$modelName])],
|
||||
'fields' => ['id'],
|
||||
]);
|
||||
$existingObjects[$modelName] = array_flip($data);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($auditLogs as $k => $auditLog) {
|
||||
$auditLog = $auditLog['AuditLog'];
|
||||
$modelId = (int)$auditLog['model_id'];
|
||||
$url = null;
|
||||
$eventInfo = null;
|
||||
switch ($auditLog['model']) {
|
||||
case 'Event':
|
||||
if (isset($events[$modelId])) {
|
||||
$url = '/events/view/' . $modelId;
|
||||
$eventInfo = $events[$modelId];
|
||||
}
|
||||
break;
|
||||
case 'ObjectReference':
|
||||
if (isset($objectReferences[$modelId]) && isset($objects[$objectReferences[$modelId]])) {
|
||||
$url = '/events/view/' . $objects[$objectReferences[$modelId]]['event_id'] . '/focus:' . $objects[$objectReferences[$modelId]]['uuid'];
|
||||
if ($objects[$objectReferences[$modelId]]['deleted']) {
|
||||
$url .= '/deleted:2';
|
||||
}
|
||||
if (isset($events[$objects[$objectReferences[$modelId]]['event_id']])) {
|
||||
$eventInfo = $events[$objects[$objectReferences[$modelId]]['event_id']];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'Object':
|
||||
if (isset($objects[$modelId])) {
|
||||
$url = '/events/view/' . $objects[$modelId]['event_id'] . '/focus:' . $objects[$modelId]['uuid'];
|
||||
if ($objects[$modelId]['deleted']) {
|
||||
$url .= '/deleted:2';
|
||||
}
|
||||
if (isset($events[$objects[$modelId]['event_id']])) {
|
||||
$eventInfo = $events[$objects[$modelId]['event_id']];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'Attribute':
|
||||
if (isset($attributes[$modelId])) {
|
||||
$url = '/events/view/' . $attributes[$modelId]['event_id'] . '/focus:' . $attributes[$modelId]['uuid'];
|
||||
if ($attributes[$modelId]['deleted']) {
|
||||
$url .= '/deleted:2';
|
||||
}
|
||||
if (isset($events[$attributes[$modelId]['event_id']])) {
|
||||
$eventInfo = $events[$attributes[$modelId]['event_id']];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'ShadowAttribute':
|
||||
if (isset($shadowAttributes[$modelId])) {
|
||||
$url = '/events/view/' . $shadowAttributes[$modelId]['event_id'] . '/focus:' . $shadowAttributes[$modelId]['uuid'];
|
||||
if (isset($events[$shadowAttributes[$modelId]['event_id']])) {
|
||||
$eventInfo = $events[$shadowAttributes[$modelId]['event_id']];
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (isset($existingObjects[$auditLog['model']][$modelId])) {
|
||||
$url = '/' . $links[$auditLog['model']] . '/view/' . $modelId;
|
||||
} else {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
if ($url) {
|
||||
$auditLogs[$k]['AuditLog']['model_link'] = $this->baseurl . $url;
|
||||
}
|
||||
if ($eventInfo) {
|
||||
$auditLogs[$k]['AuditLog']['event_info'] = $eventInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return $auditLogs;
|
||||
}
|
||||
}
|
|
@ -1,257 +0,0 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property AuthKey $AuthKey
|
||||
*/
|
||||
class AuthKeysController extends AppController
|
||||
{
|
||||
public $components = array(
|
||||
'CRUD',
|
||||
'RequestHandler'
|
||||
);
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 60,
|
||||
'order' => array(
|
||||
'AuthKey.name' => 'ASC',
|
||||
)
|
||||
);
|
||||
|
||||
public function index($id = false)
|
||||
{
|
||||
$conditions = $this->__prepareConditions();
|
||||
$canCreateAuthkey = true;
|
||||
if ($id) {
|
||||
$this->set('user_id', $id);
|
||||
if ($this->_isAdmin()) {
|
||||
if ($this->_isSiteAdmin()) {
|
||||
$canCreateAuthkey = true;
|
||||
} else {
|
||||
$user = $this->AuthKey->User->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => [
|
||||
'User.id' => $id,
|
||||
'User.disabled' => false
|
||||
],
|
||||
'fields' => ['User.id', 'User.org_id', 'User.disabled'],
|
||||
'contain' => [
|
||||
'Role' => [
|
||||
'fields' => [
|
||||
'Role.perm_site_admin', 'Role.perm_admin'
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
if ($user['Role']['perm_site_admin'] || ($user['Role']['perm_admin'] && $user['User']['id'] !== $this->Auth->user('id'))) {
|
||||
$canCreateAuthkey = false;
|
||||
} else {
|
||||
$canCreateAuthkey = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$canCreateAuthkey = (int)$id === (int)$this->Auth->user('id');
|
||||
}
|
||||
$conditions['AND'][] = ['AuthKey.user_id' => $id];
|
||||
}
|
||||
$this->set('canCreateAuthkey', $canCreateAuthkey);
|
||||
$keyUsageEnabled = Configure::read('MISP.log_user_ips') && Configure::read('MISP.log_user_ips_authkeys');
|
||||
$this->CRUD->index([
|
||||
'filters' => ['User.email', 'authkey_start', 'authkey_end', 'comment', 'User.id'],
|
||||
'quickFilters' => ['comment', 'authkey_start', 'authkey_end', 'User.email'],
|
||||
'contain' => ['User.id', 'User.email'],
|
||||
'conditions' => $conditions,
|
||||
'afterFind' => function (array $authKeys) use ($keyUsageEnabled) {
|
||||
if ($keyUsageEnabled) {
|
||||
$keyIds = Hash::extract($authKeys, "{n}.AuthKey.id");
|
||||
$lastUsedById = $this->AuthKey->getLastUsageForKeys($keyIds);
|
||||
}
|
||||
foreach ($authKeys as &$authKey) {
|
||||
if ($keyUsageEnabled) {
|
||||
$lastUsed = $lastUsedById[$authKey['AuthKey']['id']];
|
||||
$authKey['AuthKey']['last_used'] = $lastUsed;
|
||||
}
|
||||
unset($authKey['AuthKey']['authkey']);
|
||||
}
|
||||
return $authKeys;
|
||||
}
|
||||
]);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->set('title_for_layout', __('Auth Keys'));
|
||||
$this->set('advancedEnabled', !empty(Configure::read('Security.advanced_authkeys')));
|
||||
$this->set('keyUsageEnabled', $keyUsageEnabled);
|
||||
$this->set('menuData', [
|
||||
'menuList' => $this->_isSiteAdmin() ? 'admin' : 'globalActions',
|
||||
'menuItem' => 'authkeys_index',
|
||||
]);
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
{
|
||||
$this->CRUD->delete($id, [
|
||||
'conditions' => $this->__prepareConditions(),
|
||||
'contain' => ['User'],
|
||||
]);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$this->CRUD->edit($id, [
|
||||
'conditions' => $this->__prepareConditions(),
|
||||
'afterFind' => function (array $authKey) {
|
||||
unset($authKey['AuthKey']['authkey']);
|
||||
if (is_array($authKey['AuthKey']['allowed_ips'])) {
|
||||
$authKey['AuthKey']['allowed_ips'] = implode("\n", $authKey['AuthKey']['allowed_ips']);
|
||||
}
|
||||
$authKey['AuthKey']['expiration'] = date('Y-m-d H:i:s', $authKey['AuthKey']['expiration']);
|
||||
return $authKey;
|
||||
},
|
||||
'fields' => ['comment', 'allowed_ips', 'expiration', 'read_only'],
|
||||
'contain' => ['User.id', 'User.org_id']
|
||||
]);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->set('dropdownData', [
|
||||
'user' => $this->User->find('list', [
|
||||
'sort' => ['username' => 'asc'],
|
||||
'conditions' => ['id' => $this->request->data['AuthKey']['user_id']],
|
||||
])
|
||||
]);
|
||||
$this->set('menuData', [
|
||||
'menuList' => $this->_isSiteAdmin() ? 'admin' : 'globalActions',
|
||||
'menuItem' => 'authKeyAdd',
|
||||
]);
|
||||
$this->set('edit', true);
|
||||
$this->set('validity', Configure::read('Security.advanced_authkeys_validity'));
|
||||
$this->set('title_for_layout', __('Edit auth key'));
|
||||
$this->render('add');
|
||||
}
|
||||
|
||||
public function add($user_id = false)
|
||||
{
|
||||
$options = $this->IndexFilter->harvestParameters(['user_id']);
|
||||
if (!empty($params['user_id'])) {
|
||||
$user_id = $options['user_id'];
|
||||
}
|
||||
$params = [
|
||||
'displayOnSuccess' => 'authkey_display',
|
||||
'override' => ['authkey' => null], // do not allow to use own key, always generate random one
|
||||
'afterFind' => function (array $authKey, array $savedData) { // remove hashed key from response
|
||||
unset($authKey['AuthKey']['authkey']);
|
||||
$authKey['AuthKey']['authkey_raw'] = $savedData['AuthKey']['authkey_raw'];
|
||||
return $authKey;
|
||||
}
|
||||
];
|
||||
if ($user_id === 'me' || $user_id === false) {
|
||||
$user_id = $this->Auth->user('id');
|
||||
}
|
||||
$selectConditions = [];
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
if ($this->_isAdmin()) {
|
||||
$role_ids = $this->AuthKey->User->Role->find('column', [
|
||||
'fields' => ['Role.id'],
|
||||
'conditions' => [
|
||||
'AND' => [
|
||||
'Role.perm_site_admin' => false,
|
||||
'Role.perm_auth' => true,
|
||||
'Role.perm_admin' => false
|
||||
]
|
||||
]
|
||||
]);
|
||||
$user_ids = $this->AuthKey->User->find('column', [
|
||||
'fields' => ['User.id'],
|
||||
'conditions' => [
|
||||
'User.org_id' => $this->Auth->user('org_id'),
|
||||
'OR' => [
|
||||
'User.role_id' => $role_ids,
|
||||
'User.id' => $this->Auth->user('id')
|
||||
]
|
||||
]
|
||||
]);
|
||||
if (!empty($user_id)) {
|
||||
if (in_array($user_id, $user_ids)) {
|
||||
$user_ids = [$user_id];
|
||||
} else {
|
||||
throw new MethodNotAllowedException(__('Invalid user or insufficient privileges to create an authkey for the given user.'));
|
||||
}
|
||||
}
|
||||
$selectConditions['AND'][] = ['User.id' => $user_ids];
|
||||
$params['override']['user_id'] = $user_ids[0];
|
||||
} else {
|
||||
$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;
|
||||
}
|
||||
$dropdownData = [
|
||||
'user' => $this->AuthKey->User->find('list', [
|
||||
'sort' => ['username' => 'asc'],
|
||||
'conditions' => $selectConditions,
|
||||
])
|
||||
];
|
||||
$this->set(compact('dropdownData'));
|
||||
$this->set('title_for_layout', __('Add auth key'));
|
||||
$this->set('menuData', [
|
||||
'menuList' => $this->_isSiteAdmin() ? 'admin' : 'globalActions',
|
||||
'menuItem' => 'authKeyAdd',
|
||||
]);
|
||||
$this->set('validity', Configure::read('Security.advanced_authkeys_validity'));
|
||||
}
|
||||
|
||||
public function view($id = false)
|
||||
{
|
||||
$this->CRUD->view($id, [
|
||||
'contain' => ['User.id', 'User.email'],
|
||||
'conditions' => $this->__prepareConditions(),
|
||||
'afterFind' => function (array $authKey) {
|
||||
unset($authKey['AuthKey']['authkey']);
|
||||
return $authKey;
|
||||
}
|
||||
]);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
|
||||
if (Configure::read('MISP.log_user_ips') && Configure::read('MISP.log_user_ips_authkeys')) {
|
||||
list($keyUsage, $lastUsed, $uniqueIps) = $this->AuthKey->getKeyUsage($id);
|
||||
$this->set('keyUsage', $keyUsage);
|
||||
$this->set('lastUsed', $lastUsed);
|
||||
$this->set('uniqueIps', $uniqueIps);
|
||||
}
|
||||
|
||||
$this->set('title_for_layout', __('Auth key'));
|
||||
$this->set('menuData', [
|
||||
'menuList' => $this->_isSiteAdmin() ? 'admin' : 'globalActions',
|
||||
'menuItem' => 'authKeyView',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return conditions according to current user permission.
|
||||
* @return array
|
||||
*/
|
||||
private function __prepareConditions()
|
||||
{
|
||||
$user = $this->Auth->user();
|
||||
if ($user['Role']['perm_site_admin']) {
|
||||
$conditions = []; // site admin can see all keys
|
||||
} else if ($user['Role']['perm_admin']) {
|
||||
$conditions['AND'][]['User.org_id'] = $user['org_id']; // org admin can see his/her user org auth keys
|
||||
} else {
|
||||
$conditions['AND'][]['User.id'] = $user['id'];
|
||||
}
|
||||
return $conditions;
|
||||
}
|
||||
}
|
|
@ -1,316 +0,0 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
class CerebratesController extends AppController
|
||||
{
|
||||
public $components = array('Session', 'RequestHandler');
|
||||
|
||||
public function beforeFilter()
|
||||
{
|
||||
parent::beforeFilter();
|
||||
}
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 60,
|
||||
'maxLimit' => 9999
|
||||
);
|
||||
|
||||
public function index()
|
||||
{
|
||||
$params = [
|
||||
'filters' => ['name', 'url', 'uuid'],
|
||||
'quickFilters' => ['name']
|
||||
];
|
||||
$this->CRUD->index($params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->set('menuData', array('menuList' => 'sync', 'menuItem' => 'list_cerebrates'));
|
||||
}
|
||||
|
||||
public function add()
|
||||
{
|
||||
$params = [];
|
||||
$this->CRUD->add($params);
|
||||
if ($this->restResponsePayload) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
|
||||
$this->loadModel('Organisation');
|
||||
$orgs = $this->Organisation->find('list', [
|
||||
'recursive' => -1,
|
||||
'fields' => ['id', 'name'],
|
||||
'order' => ['lower(name)' => 'ASC']
|
||||
]);
|
||||
$dropdownData = [
|
||||
'org_id' => $orgs
|
||||
];
|
||||
$this->set(compact('dropdownData'));
|
||||
$this->set('menuData', array('menuList' => 'sync', 'menuItem' => 'add_cerebrate'));
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$this->set('menuData', array('menuList' => 'sync', 'menuItem' => 'edit_cerebrate'));
|
||||
$this->set('id', $id);
|
||||
$params = [];
|
||||
$this->CRUD->edit($id, $params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
|
||||
$this->loadModel('Organisation');
|
||||
$orgs = $this->Organisation->find('list', [
|
||||
'recursive' => -1,
|
||||
'fields' => ['id', 'name'],
|
||||
'order' => ['lower(name)' => 'ASC']
|
||||
]);
|
||||
$dropdownData = [
|
||||
'org_id' => $orgs
|
||||
];
|
||||
$this->set(compact('dropdownData'));
|
||||
$this->render('add');
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
{
|
||||
$this->CRUD->delete($id);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
public function view($id)
|
||||
{
|
||||
$this->set('menuData', ['menuList' => 'sync', 'menuItem' => 'view_cerebrate']);
|
||||
$this->CRUD->view($id, ['contain' => ['Organisation.name', 'Organisation.uuid', 'Organisation.id']]);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->set('id', $id);
|
||||
}
|
||||
|
||||
public function pull_orgs($id)
|
||||
{
|
||||
$this->set('menuData', ['menuList' => 'sync', 'menuItem' => 'previewCerebrateOrgs']);
|
||||
$cerebrate = $this->Cerebrate->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => ['Cerebrate.id' => $id]
|
||||
]);
|
||||
if (empty($cerebrate)) {
|
||||
throw new NotFoundException(__('Invalid Cerebrate instance ID provided.'));
|
||||
}
|
||||
|
||||
if ($this->request->is('post')) {
|
||||
$result = $this->Cerebrate->queryInstance([
|
||||
'cerebrate' => $cerebrate,
|
||||
'path' => '/organisations/index',
|
||||
'params' => $this->IndexFilter->harvestParameters([
|
||||
'name',
|
||||
'uuid',
|
||||
'quickFilter'
|
||||
]),
|
||||
'type' => 'GET'
|
||||
]);
|
||||
$result = $this->Cerebrate->saveRemoteOrgs($result);
|
||||
$message = __('Added %s new organisations, updated %s existing organisations, %s failures.', $result['add'], $result['edit'], $result['fails']);
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('Cerebrates', 'pull_orgs', $cerebrate_id, false, $message);
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect($this->referer());
|
||||
}
|
||||
} else {
|
||||
$this->set('id', $cerebrate['Cerebrate']['id']);
|
||||
$this->set('title', __('Sync organisation information'));
|
||||
$this->set('question', __('Are you sure you want to download and add / update the remote organisations from the Cerebrate node?'));
|
||||
$this->set('actionName', __('Pull all'));
|
||||
$this->layout = false;
|
||||
$this->render('/genericTemplates/confirm');
|
||||
}
|
||||
}
|
||||
|
||||
public function pull_sgs($id)
|
||||
{
|
||||
$this->set('menuData', ['menuList' => 'sync', 'menuItem' => 'previewCerebrateSgs']);
|
||||
$cerebrate = $this->Cerebrate->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => ['Cerebrate.id' => $id]
|
||||
]);
|
||||
if (empty($cerebrate)) {
|
||||
throw new NotFoundException(__('Invalid Cerebrate instance ID provided.'));
|
||||
}
|
||||
|
||||
if ($this->request->is('post')) {
|
||||
$result = $this->Cerebrate->queryInstance([
|
||||
'cerebrate' => $cerebrate,
|
||||
'path' => '/sharingGroups/index',
|
||||
'params' => $this->IndexFilter->harvestParameters([
|
||||
'name',
|
||||
'uuid',
|
||||
'quickFilter'
|
||||
]),
|
||||
'type' => 'GET'
|
||||
]);
|
||||
$result = $this->Cerebrate->saveRemoteSgs($result, $this->Auth->user());
|
||||
$message = __('Added %s new sharing groups, updated %s existing sharing groups, %s failures.', $result['add'], $result['edit'], $result['fails']);
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('Cerebrates', 'pull_sgs', $cerebrate_id, false, $message);
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect($this->referer());
|
||||
}
|
||||
} else {
|
||||
$this->set('id', $cerebrate['Cerebrate']['id']);
|
||||
$this->set('title', __('Sync sharing group information'));
|
||||
$this->set('question', __('Are you sure you want to download and add / update the remote sharing group from the Cerebrate node?'));
|
||||
$this->set('actionName', __('Pull all'));
|
||||
$this->layout = false;
|
||||
$this->render('/genericTemplates/confirm');
|
||||
}
|
||||
}
|
||||
|
||||
public function preview_orgs($id)
|
||||
{
|
||||
$this->set('menuData', ['menuList' => 'sync', 'menuItem' => 'previewCerebrateOrgs']);
|
||||
$cerebrate = $this->Cerebrate->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => ['Cerebrate.id' => $id]
|
||||
]);
|
||||
if (empty($cerebrate)) {
|
||||
throw new NotFoundException(__('Invalid Cerebrate instance ID provided.'));
|
||||
}
|
||||
$result = $this->Cerebrate->queryInstance([
|
||||
'cerebrate' => $cerebrate,
|
||||
'path' => '/organisations/index',
|
||||
'params' => $this->IndexFilter->harvestParameters([
|
||||
'name',
|
||||
'uuid',
|
||||
'quickFilter'
|
||||
]),
|
||||
'type' => 'GET'
|
||||
]);
|
||||
$result = $this->Cerebrate->checkRemoteOrgs($result);
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($result, $this->response->type());
|
||||
} else {
|
||||
App::uses('CustomPaginationTool', 'Tools');
|
||||
$customPagination = new CustomPaginationTool();
|
||||
$customPagination->truncateAndPaginate($result, $this->params, false, true);
|
||||
$this->set('data', $result);
|
||||
$this->set('cerebrate', $cerebrate);
|
||||
}
|
||||
}
|
||||
|
||||
public function download_org($cerebrate_id, $org_id)
|
||||
{
|
||||
if ($this->request->is('post')) {
|
||||
$cerebrate = $this->Cerebrate->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => ['Cerebrate.id' => $cerebrate_id]
|
||||
]);
|
||||
if (empty($cerebrate)) {
|
||||
throw new NotFoundException(__('Invalid Cerebrate instance ID provided.'));
|
||||
}
|
||||
$result = $this->Cerebrate->queryInstance([
|
||||
'cerebrate' => $cerebrate,
|
||||
'path' => '/organisations/view/' . $org_id,
|
||||
'type' => 'GET'
|
||||
]);
|
||||
$saveResult = $this->Cerebrate->captureOrg($result);
|
||||
if ($this->_isRest()) {
|
||||
if (is_array($saveResult)) {
|
||||
return $this->RestResponse->viewData($saveResult, $this->response->type());
|
||||
} else {
|
||||
return $this->RestResponse->saveFailResponse('Cerebrates', 'download_org', $cerebrate_id . '/' . $org_id, $saveResult);
|
||||
}
|
||||
} else {
|
||||
if (is_array($saveResult)) {
|
||||
$this->Flash->success(__('Organisation downloaded.'));
|
||||
} else {
|
||||
$this->Flash->error($saveResult);
|
||||
}
|
||||
$this->redirect($this->referer());
|
||||
}
|
||||
} else {
|
||||
$this->set('id', $data[$modelName]['id']);
|
||||
$this->set('title', __('Download organisation information'));
|
||||
$this->set('question', __('Are you sure you want to download and add / update the remote organisation?'));
|
||||
$this->set('actionName', __('Download'));
|
||||
$this->layout = false;
|
||||
$this->render('/genericTemplates/confirm');
|
||||
}
|
||||
}
|
||||
|
||||
public function preview_sharing_groups($id)
|
||||
{
|
||||
$this->set('menuData', ['menuList' => 'sync', 'menuItem' => 'previewCerebrateSGs']);
|
||||
$cerebrate = $this->Cerebrate->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => ['Cerebrate.id' => $id]
|
||||
]);
|
||||
if (empty($cerebrate)) {
|
||||
throw new NotFoundException(__('Invalid Cerebrate instance ID provided.'));
|
||||
}
|
||||
$result = $this->Cerebrate->queryInstance([
|
||||
'cerebrate' => $cerebrate,
|
||||
'path' => '/sharingGroups/index',
|
||||
'params' => $this->IndexFilter->harvestParameters([
|
||||
'name',
|
||||
'uuid',
|
||||
'quickFilter'
|
||||
]),
|
||||
'type' => 'GET'
|
||||
]);
|
||||
$result = $this->Cerebrate->checkRemoteSharingGroups($result);
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($result, $this->response->type());
|
||||
} else {
|
||||
App::uses('CustomPaginationTool', 'Tools');
|
||||
$customPagination = new CustomPaginationTool();
|
||||
$customPagination->truncateAndPaginate($result, $this->params, false, true);
|
||||
$this->set('data', $result);
|
||||
$this->set('cerebrate', $cerebrate);
|
||||
}
|
||||
}
|
||||
|
||||
public function download_sg($cerebrate_id, $sg_id)
|
||||
{
|
||||
if ($this->request->is('post')) {
|
||||
$cerebrate = $this->Cerebrate->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => ['Cerebrate.id' => $cerebrate_id]
|
||||
]);
|
||||
if (empty($cerebrate)) {
|
||||
throw new NotFoundException(__('Invalid Cerebrate instance ID provided.'));
|
||||
}
|
||||
$result = $this->Cerebrate->queryInstance([
|
||||
'cerebrate' => $cerebrate,
|
||||
'path' => '/sharingGroups/view/' . $sg_id,
|
||||
'type' => 'GET'
|
||||
]);
|
||||
$saveResult = $this->Cerebrate->captureSg($result, $this->Auth->user());
|
||||
if ($this->_isRest()) {
|
||||
if (is_array($saveResult)) {
|
||||
return $this->RestResponse->viewData($saveResult, $this->response->type());
|
||||
} else {
|
||||
return $this->RestResponse->saveFailResponse('Cerebrates', 'download_sg', $cerebrate_id . '/' . $sg_id, $saveResult);
|
||||
}
|
||||
} else {
|
||||
if (is_array($saveResult)) {
|
||||
$this->Flash->success(__('Sharing Group downloaded.'));
|
||||
} else {
|
||||
$this->Flash->error($saveResult);
|
||||
}
|
||||
$this->redirect($this->referer());
|
||||
}
|
||||
} else {
|
||||
$this->set('id', $cerebrate_id);
|
||||
$this->set('title', __('Download sharing group information'));
|
||||
$this->set('question', __('Are you sure you want to download and add / update the remote sharing group?'));
|
||||
$this->set('actionName', __('Download'));
|
||||
$this->layout = false;
|
||||
$this->render('/genericTemplates/confirm');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,178 +0,0 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property Community $Community
|
||||
*/
|
||||
class CommunitiesController extends AppController
|
||||
{
|
||||
public $components = array('Session', 'RequestHandler');
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 60,
|
||||
'maxLimit' => 9999
|
||||
);
|
||||
|
||||
public function index()
|
||||
{
|
||||
$filters = $this->IndexFilter->harvestParameters(array('context', 'value'));
|
||||
if (empty($filters['context'])) {
|
||||
$filters['context'] = 'vetted';
|
||||
}
|
||||
if (!empty($filters['value'])) {
|
||||
$filters['value'] = strtolower($filters['value']);
|
||||
} else {
|
||||
$filters['value'] = false;
|
||||
}
|
||||
$community_list = $this->Community->getCommunityList($filters['context'], $filters['value']);
|
||||
|
||||
//foreach ($community)
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($community_list, $this->response->type());
|
||||
}
|
||||
App::uses('CustomPaginationTool', 'Tools');
|
||||
$customPagination = new CustomPaginationTool();
|
||||
$customPagination->truncateAndPaginate($community_list, $this->params, $this->modelClass, true);
|
||||
$this->set('community_list', $community_list);
|
||||
$this->set('context', $filters['context']);
|
||||
}
|
||||
|
||||
public function view($id)
|
||||
{
|
||||
|
||||
$community = $this->Community->getCommunity($id);
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($community, $this->response->type());
|
||||
} else {
|
||||
$this->set('community', $community);
|
||||
}
|
||||
}
|
||||
|
||||
public function requestAccess($id)
|
||||
{
|
||||
$community = $this->Community->getCommunity($id);
|
||||
$this->loadModel('User');
|
||||
$gpgkey = $this->User->find('first', array(
|
||||
'conditions' => array('User.id' => $this->Auth->user('id')),
|
||||
'recursive' => -1,
|
||||
'fields' => array('User.gpgkey')
|
||||
));
|
||||
if (!empty($gpgkey['User']['gpgkey'])) {
|
||||
$gpgkey = $gpgkey['User']['gpgkey'];
|
||||
} else {
|
||||
$gpgkey = '';
|
||||
}
|
||||
if (!$this->request->is('post')) {
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->describe('Communities', 'requestAccess', false, $this->response->type());
|
||||
}
|
||||
$this->request->data['Server']['email'] = $this->Auth->user('email');
|
||||
$this->request->data['Server']['org_name'] = $this->Auth->user('Organisation')['name'];
|
||||
$this->request->data['Server']['org_uuid'] = $this->Auth->user('Organisation')['uuid'];
|
||||
$this->request->data['Server']['gpgkey'] = $gpgkey;
|
||||
} else {
|
||||
if (empty($this->request->data['Server'])) {
|
||||
$this->request->data = array('Server' => $this->request->data);
|
||||
}
|
||||
$body = sprintf(
|
||||
'To whom it may concern,
|
||||
|
||||
On behalf of my organisation (%s - %s),
|
||||
I would hereby like to request %saccess to your MISP community:
|
||||
%s
|
||||
|
||||
A brief description of my organisation:
|
||||
%s
|
||||
|
||||
My e-mail address that I wish to use as my username:
|
||||
%s
|
||||
%s%s
|
||||
|
||||
Thank you in advance!',
|
||||
empty($this->request->data['Server']['org_name']) ? $this->Auth->user('Organisation')['name'] : $this->request->data['Server']['org_name'],
|
||||
empty($this->request->data['Server']['org_uuid']) ? $this->Auth->user('Organisation')['uuid'] : $this->request->data['Server']['org_uuid'],
|
||||
empty($this->request->data['Server']['sync']) ? '' : 'synchronisation ',
|
||||
$community['name'],
|
||||
empty($this->request->data['Server']['org_description']) ? '' : $this->request->data['Server']['org_description'],
|
||||
empty($this->request->data['Server']['email']) ? '' : $this->request->data['Server']['email'],
|
||||
empty($this->request->data['Server']['message']) ? '' : sprintf(
|
||||
'%sAdditional information:%s%s%s',
|
||||
PHP_EOL,
|
||||
PHP_EOL,
|
||||
$this->request->data['Server']['message'],
|
||||
PHP_EOL
|
||||
),
|
||||
!empty($this->request->data['Server']['anonymise']) ? '' : sprintf(
|
||||
'%sServer used to issue the request%sServer url: %sServer uuid: %sServer version: %s',
|
||||
PHP_EOL,
|
||||
PHP_EOL,
|
||||
(empty(Configure::read('MISP.external_baseurl')) ? Configure::read('MISP.baseurl') : Configure::read('MISP.external_baseurl')) . PHP_EOL,
|
||||
Configure::read('MISP.uuid') . PHP_EOL,
|
||||
$this->mispVersion . PHP_EOL
|
||||
)
|
||||
);
|
||||
$imgPath = APP . WEBROOT_DIR . DS . 'img' . DS . 'orgs' . DS;
|
||||
$possibleFields = array('id', 'name');
|
||||
$image = false;
|
||||
App::uses('File', 'Utility');
|
||||
foreach ($possibleFields as $field) {
|
||||
if (isset($options[$field])) {
|
||||
$file = new File($imgPath . $options[$field] . 'png');
|
||||
if ($file->exists()) {
|
||||
$image = $file->read();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isset($this->request->data['Server']['gpgkey'])) {
|
||||
$this->request->data['Server']['gpgkey'] = $gpgkey;
|
||||
}
|
||||
if (!empty($image)) {
|
||||
$params['attachments']['logo.png'] = $image;
|
||||
}
|
||||
if (!empty($this->request->data['Server']['gpgkey'])) {
|
||||
$params['attachments']['requestor.asc'] = $this->request->data['Server']['gpgkey'];
|
||||
}
|
||||
$params = array();
|
||||
$params['to'] = $community['email'];
|
||||
$params['reply-to'] = empty($this->request->data['Server']['email']) ? $this->Auth->user('email') : $this->request->data['Server']['email'];
|
||||
$params['requestor_gpgkey'] = empty($this->request->data['Server']['gpgkey']) ? $gpgkey : $this->request->data['Server']['gpgkey'];
|
||||
$params['gpgkey'] = $community['pgp_key'];
|
||||
$params['body'] = $body;
|
||||
$params['subject'] = '[' . $community['name'] . '] Requesting MISP access';
|
||||
$params['mock'] = !empty($this->request->data['Server']['mock']) ? $this->request->data['Server']['mock'] : 0;
|
||||
if (!empty(Configure::read('MISP.disable_emailing'))) {
|
||||
$params['mock'] = 1;
|
||||
}
|
||||
$result = $this->User->sendEmailExternal($params);
|
||||
$message = $result ? __('Request sent.') : __('Something went wrong and the request could not be sent.');
|
||||
if ($this->_isRest()) {
|
||||
if ($result === true) {
|
||||
return $this->RestResponse->saveSuccessResponse('Communities', 'requestAccess', $id, false, $message);
|
||||
} elseif ($result) {
|
||||
return $this->RestResponse->viewData($result);
|
||||
} else {
|
||||
return $this->RestResponse->saveFailResponse('Communities', 'requestAccess', false, $message);
|
||||
}
|
||||
} else {
|
||||
if ($result === true) {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect(array('controller' => 'communities', 'action' => 'view', $id));
|
||||
} elseif ($result) {
|
||||
$this->set('result', $result);
|
||||
if (empty($this->request->data['Server']['mock'])) {
|
||||
$this->Flash->error(__('The message could not be sent (either because e-mailing is disabled or because encryption is misconfigured), however, you can view the e-mail that would have been sent below. Feel free to send it manually.'));
|
||||
}
|
||||
$this->render('request_access_email');
|
||||
} else {
|
||||
$this->Flash->error($message);
|
||||
$this->redirect(array('controller' => 'communities', 'action' => 'view', $id));
|
||||
}
|
||||
}
|
||||
if (!empty($this->request->data['Server']['mock'])) {
|
||||
$this->set('mock', $this->request->data['Server']['mock']);
|
||||
}
|
||||
}
|
||||
$this->set('community', $community);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,106 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* create, read, update and delete (CRUD)
|
||||
*/
|
||||
|
||||
class AdminCrudComponent extends AuthComponent
|
||||
{
|
||||
|
||||
/**
|
||||
* add method
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function adminAdd()
|
||||
{
|
||||
if ($this->controller->request->is('post')) {
|
||||
$this->controller->{$this->controller->defaultModel}->create();
|
||||
if ($this->controller->{$this->controller->defaultModel}->save($this->controller->request->data)) {
|
||||
$this->controller->Session->setFlash(sprintf(__('The %s has been saved.'), strtolower($this->controller->defaultModel)));
|
||||
$this->controller->redirect(array('action' => 'index'));
|
||||
} else {
|
||||
if (!($this->Session->check('Message.flash'))) {
|
||||
$this->controller->Session->setFlash(sprintf(__('The %s could not be saved. Please, try again.'), strtolower($this->controller->defaultModel)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* index method
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function adminIndex()
|
||||
{
|
||||
$this->controller->recursive = 0;
|
||||
$this->controller->set('list', $this->controller->paginate());
|
||||
}
|
||||
|
||||
/**
|
||||
* edit method
|
||||
*
|
||||
* @param string $id
|
||||
* @return void
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function adminEdit($id = null)
|
||||
{
|
||||
$this->controller->{$this->controller->defaultModel}->id = $id;
|
||||
if (!$this->controller->{$this->controller->defaultModel}->exists()) {
|
||||
throw new NotFoundException(sprintf(__('Invalid %s'), strtolower($this->controller->defaultModel)));
|
||||
}
|
||||
if ($this->controller->request->is('post') || $this->controller->request->is('put')) {
|
||||
$this->controller->request->data[$this->controller->defaultModel]['id'] = $id;
|
||||
if ($this->controller->{$this->controller->defaultModel}->save($this->controller->request->data)) {
|
||||
$this->controller->Session->setFlash(sprintf(__('The %s has been saved'), strtolower($this->controller->defaultModel)));
|
||||
$this->controller->redirect(array('action' => 'index'));
|
||||
} else {
|
||||
if (!($this->Session->check('Message.flash'))) {
|
||||
$this->controller->Session->setFlash(sprintf(__('The %s could not be saved. Please, try again.'), strtolower($this->controller->defaultModel)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->controller->request->data[$this->controller->defaultModel]['id'] = $id;
|
||||
$this->controller->request->data = $this->controller->{$this->controller->defaultModel}->read(null, $id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* delete method
|
||||
*
|
||||
* @param string $id
|
||||
* @return void
|
||||
* @throws MethodNotAllowedException
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function adminDelete($id = null)
|
||||
{
|
||||
if (!$this->controller->request->is('post')) {
|
||||
throw new MethodNotAllowedException();
|
||||
}
|
||||
$this->controller->{$this->controller->defaultModel}->id = $id;
|
||||
if (!$this->controller->{$this->controller->defaultModel}->exists()) {
|
||||
throw new NotFoundException(sprintf(__('Invalid %s'), strtolower($this->controller->defaultModel)));
|
||||
}
|
||||
if ($this->controller->{$this->controller->defaultModel}->delete()) {
|
||||
$this->controller->Session->setFlash(sprintf(__('%s deleted'), $this->controller->defaultModel));
|
||||
$this->controller->redirect(array('action' => 'index'));
|
||||
}
|
||||
$this->controller->Session->setFlash(sprintf(__('%s was not deleted'), $this->controller->defaultModel));
|
||||
$this->controller->redirect(array('action' => 'index'));
|
||||
}
|
||||
|
||||
public $controller;
|
||||
|
||||
public function initialize(Controller $controller)
|
||||
{
|
||||
$this->controller = $controller;
|
||||
}
|
||||
|
||||
public function startup(Controller $controller)
|
||||
{
|
||||
$this->controller = $controller;
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
|
||||
App::uses('AuthComponent', 'Controller/Component');
|
||||
|
||||
class ApacheSecureAuthComponent extends AuthComponent
|
||||
{
|
||||
|
||||
/**
|
||||
* No brute force safeguard needed because Apache does the job
|
||||
*
|
||||
* If $user is provided that data will be stored as the logged in user. If $user is empty or not
|
||||
* specified, the request will be used to identify a user. If the identification was successful,
|
||||
* the user record is written to the session key specified in AuthComponent::$sessionKey. Logging in
|
||||
* will also change the session id in order to help mitigate session replays.
|
||||
*
|
||||
* @param mixed $user Either an array of user data or null to identify a user using the current request.
|
||||
* @return boolean True on login success, false on failure
|
||||
* @link http://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#identifying-users-and-logging-them-in
|
||||
* @throws ForbiddenException
|
||||
*/
|
||||
public function login($user = null)
|
||||
{
|
||||
$this->_setDefaults();
|
||||
if (empty($user)) {
|
||||
// "envvar" is defined in AppController.php
|
||||
$usernameField = $this->authenticate['Apache']['fields']['envvar'];
|
||||
if (isset($_SERVER[$usernameField])) {
|
||||
$username = $_SERVER[$usernameField];
|
||||
// check if the user credentials are valid
|
||||
$user = $this->identify($this->request, $this->response);
|
||||
unset($user['gpgkey']);
|
||||
}
|
||||
}
|
||||
if ($user) {
|
||||
$this->Session->renew();
|
||||
$this->Session->write(self::$sessionKey, $user);
|
||||
}
|
||||
return $this->loggedIn();
|
||||
}
|
||||
}
|
|
@ -1,186 +0,0 @@
|
|||
<?php
|
||||
|
||||
App::uses('BaseAuthenticate', 'Controller/Component/Auth');
|
||||
|
||||
/*
|
||||
* custom class for Apache-based authentication
|
||||
*
|
||||
* User for ApacheAuthenticate you can pass in settings to which fields, model and additional conditions
|
||||
* are used. See FormAuthenticate::$settings for more information.
|
||||
* TODO: clarification needed, text almost the same as in lib/Cake/Controller/Component/Auth/FormAuthenticate.php
|
||||
*
|
||||
* @package Controller.Component.Auth
|
||||
* @since 2.0
|
||||
* @see ApacheAuthComponent::$authenticate
|
||||
*/
|
||||
|
||||
class ApacheAuthenticate extends BaseAuthenticate
|
||||
{
|
||||
|
||||
/**
|
||||
* Authentication class
|
||||
*
|
||||
* @param CakeRequest $request The request that contains login information.
|
||||
* @param CakeResponse $response Unused response object.
|
||||
* @return mixed False on login failure. An array of User data on success.
|
||||
*/
|
||||
private function isUserMemberOf($group, $ldapUserData)
|
||||
{
|
||||
// return true of false depeding on if user is a member of group.
|
||||
$returnCode = false;
|
||||
unset($ldapUserData[0]['memberof']["count"]);
|
||||
foreach ($ldapUserData[0]['memberof'] as $result) {
|
||||
$r = explode(",", $result, 2);
|
||||
$ldapgroup = explode("=", $r[0]);
|
||||
if ($ldapgroup[1] == $group) {
|
||||
$returnCode = true;
|
||||
}
|
||||
}
|
||||
return $returnCode;
|
||||
}
|
||||
|
||||
private function getEmailAddress($ldapEmailField, $ldapUserData)
|
||||
{
|
||||
// return the email address of an LDAP user if one of the fields in $ldapEmaiLField exists
|
||||
foreach($ldapEmailField as $field) {
|
||||
if (isset($ldapUserData[0][$field][0])) {
|
||||
return $ldapUserData[0][$field][0];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public function authenticate(CakeRequest $request, CakeResponse $response)
|
||||
{
|
||||
|
||||
// Get information user for MISP auth
|
||||
$envvar = $this->settings['fields']['envvar'];
|
||||
$mispUsername = $_SERVER[$envvar];
|
||||
|
||||
// make LDAP request to get user email required for MISP auth
|
||||
$ldapdn = Configure::read('ApacheSecureAuth.ldapDN');
|
||||
$ldaprdn = Configure::read('ApacheSecureAuth.ldapReaderUser'); // DN ou RDN LDAP
|
||||
$ldappass = Configure::read('ApacheSecureAuth.ldapReaderPassword');
|
||||
$ldapSearchFilter = Configure::read('ApacheSecureAuth.ldapSearchFilter');
|
||||
$ldapEmailField = Configure::read('ApacheSecureAuth.ldapEmailField');
|
||||
|
||||
// LDAP connection
|
||||
ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, Configure::read('ApacheSecureAuth.ldapNetworkTimeout', -1));
|
||||
$ldapconn = ldap_connect(Configure::read('ApacheSecureAuth.ldapServer'))
|
||||
or die('LDAP server connection failed');
|
||||
|
||||
// LDAP protocol configuration
|
||||
ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, Configure::read('ApacheSecureAuth.ldapProtocol'));
|
||||
ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, Configure::read('ApacheSecureAuth.ldapAllowReferrals', true));
|
||||
|
||||
if ($ldapconn) {
|
||||
// LDAP bind
|
||||
$ldapbind = ldap_bind($ldapconn, $ldaprdn, $ldappass);
|
||||
// authentication verification
|
||||
if (!$ldapbind) {
|
||||
die("LDAP bind failed");
|
||||
}
|
||||
// example for searchFiler: '(objectclass=InetOrgPerson)(!(nsaccountlock=True))(memberOf=cn=misp,cn=groups,cn=accounts,dc=example,dc=com)'
|
||||
// example for searchAttribut: '(uuid=ApacheUser)'
|
||||
if (!empty($ldapSearchFilter)) {
|
||||
$filter = '(&' . $ldapSearchFilter . '(' . Configure::read('ApacheSecureAuth.ldapSearchAttribut') . '=' . $_SERVER[$envvar] . '))';
|
||||
} else {
|
||||
$filter = '(' . Configure::read('ApacheSecureAuth.ldapSearchAttribut') . '=' . $_SERVER[$envvar] . ')';
|
||||
}
|
||||
// example: mail
|
||||
$getLdapUserInfo = Configure::read('ApacheSecureAuth.ldapFilter');
|
||||
|
||||
$result = ldap_search($ldapconn, $ldapdn, $filter, $getLdapUserInfo)
|
||||
or die("Error in LDAP search query: " . ldap_error($ldapconn));
|
||||
|
||||
$ldapUserData = ldap_get_entries($ldapconn, $result);
|
||||
|
||||
// find the email address in the query's result
|
||||
// first if the ldapEmailField option is not specified, look for the email address in the default field
|
||||
if (!isset($ldapEmailField) && isset($ldapUserData[0]['mail'][0])) {
|
||||
// assign the real user for MISP
|
||||
$mispUsername = $ldapUserData[0]['mail'][0];
|
||||
} else if (isset($ldapEmailField)) {
|
||||
$mispUsername = $this->getEmailAddress($ldapEmailField, $ldapUserData);
|
||||
} else {
|
||||
die("User not found in LDAP");
|
||||
}
|
||||
|
||||
// close LDAP connection
|
||||
ldap_close($ldapconn);
|
||||
}
|
||||
|
||||
// Find user with real username (mail)
|
||||
$user = $this->_findUser($mispUsername);
|
||||
|
||||
if ($user) {
|
||||
if (!Configure::read('ApacheSecureAuth.updateUser')) {
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
|
||||
// insert user in database if not existent
|
||||
$userModel = ClassRegistry::init($this->settings['userModel']);
|
||||
$org_id = Configure::read('ApacheSecureAuth.ldapDefaultOrg');
|
||||
// If not in config, take default org
|
||||
if (!isset($org_id)) {
|
||||
$firstOrg = $userModel->Organisation->find(
|
||||
'first',
|
||||
array(
|
||||
'conditions' => array(
|
||||
'Organisation.local' => true),
|
||||
'order' => 'Organisation.id ASC'
|
||||
)
|
||||
);
|
||||
$org_id = $firstOrg['Organisation']['id'];
|
||||
}
|
||||
|
||||
// Set roleid depending on group membership
|
||||
$roleIds = Configure::read('ApacheSecureAuth.ldapDefaultRoleId');
|
||||
if (is_array($roleIds)) {
|
||||
foreach ($roleIds as $key => $id) {
|
||||
if ($this->isUserMemberOf($key, $ldapUserData)) {
|
||||
$roleId = $roleIds[$key];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$roleId = $roleIds;
|
||||
}
|
||||
|
||||
if (!$user) {
|
||||
// create user
|
||||
$userData = array('User' => array(
|
||||
'email' => $mispUsername,
|
||||
'org_id' => $org_id,
|
||||
'password' => '',
|
||||
'confirm_password' => '',
|
||||
'authkey' => $userModel->generateAuthKey(),
|
||||
'nids_sid' => 4000000,
|
||||
'newsread' => 0,
|
||||
'role_id' => $roleId,
|
||||
'change_pw' => 0
|
||||
));
|
||||
// save user
|
||||
$userModel->save($userData, false);
|
||||
} else {
|
||||
if (!isset($roleId)) {
|
||||
// User has no role anymore, disable user
|
||||
$user['disabled'] = 1;
|
||||
return false;
|
||||
} else {
|
||||
// Update existing user
|
||||
$user['email'] = $mispUsername;
|
||||
$user['org_id'] = $org_id;
|
||||
$user['role_id'] = $roleId;
|
||||
# Reenable user in case it has been disabled
|
||||
$user['disabled'] = 0;
|
||||
}
|
||||
|
||||
$userModel->save($user, false);
|
||||
}
|
||||
|
||||
return $this->_findUser(
|
||||
$mispUsername
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
App::uses('AbstractPasswordHasher', 'Controller/Component/Auth');
|
||||
|
||||
class BlowfishConstantPasswordHasher extends AbstractPasswordHasher
|
||||
{
|
||||
/**
|
||||
* @param string $password
|
||||
* @return string
|
||||
*/
|
||||
public function hash($password)
|
||||
{
|
||||
$hash = password_hash($password, PASSWORD_BCRYPT);
|
||||
if ($hash === false) {
|
||||
throw new RuntimeException('Could not generate hashed password');
|
||||
}
|
||||
return $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password
|
||||
* @param string $hashedPassword
|
||||
* @return bool
|
||||
*/
|
||||
public function check($password, $hashedPassword)
|
||||
{
|
||||
return password_verify($password, $hashedPassword);
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
App::uses('SecurityComponent', 'Controller/Component');
|
||||
|
||||
/**
|
||||
* @property SessionComponent $Session
|
||||
*/
|
||||
class BetterSecurityComponent extends SecurityComponent
|
||||
{
|
||||
/**
|
||||
* Do not generate CSRF token. This make sense for REST calls and for calls that do not use tokens. So session
|
||||
* will not be big with csrfLimit (by default 100) of token.
|
||||
* @var bool
|
||||
*/
|
||||
public $doNotGenerateToken = false;
|
||||
|
||||
public function blackHole(Controller $controller, $error = '', SecurityException $exception = null)
|
||||
{
|
||||
$action = $controller->request->params['action'];
|
||||
$unlockedActions = JsonTool::encode($this->unlockedActions);
|
||||
$isRest = $controller->IndexFilter->isRest() ? '1' : '0';
|
||||
$this->log("Blackhole exception when accessing $controller->here (isRest: $isRest, action: $action, unlockedActions: $unlockedActions): {$exception->getMessage()}"); // log blackhole exception
|
||||
return parent::blackHole($controller, $error, $exception);
|
||||
}
|
||||
|
||||
public function generateToken(CakeRequest $request)
|
||||
{
|
||||
if (isset($request->params['requested']) && $request->params['requested'] === 1) {
|
||||
if ($this->Session->check('_Token')) {
|
||||
$request->params['_Token'] = $this->Session->read('_Token');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->doNotGenerateToken) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// No need to hash random data
|
||||
$authKey = bin2hex(Security::randomBytes(16));
|
||||
$token = array(
|
||||
'key' => $authKey,
|
||||
'allowedControllers' => $this->allowedControllers,
|
||||
'allowedActions' => $this->allowedActions,
|
||||
'unlockedFields' => array_merge($this->disabledFields, $this->unlockedFields),
|
||||
'csrfTokens' => array(),
|
||||
);
|
||||
|
||||
if ($this->Session->check('_Token')) {
|
||||
$tokenData = $this->Session->read('_Token');
|
||||
if (!empty($tokenData['csrfTokens']) && is_array($tokenData['csrfTokens'])) {
|
||||
$token['csrfTokens'] = $this->_expireTokens($tokenData['csrfTokens']);
|
||||
}
|
||||
}
|
||||
if ($this->csrfUseOnce || empty($token['csrfTokens'])) {
|
||||
$token['csrfTokens'][$authKey] = strtotime($this->csrfExpires);
|
||||
}
|
||||
if (!$this->csrfUseOnce) {
|
||||
$csrfTokens = array_keys($token['csrfTokens']);
|
||||
$authKey = $csrfTokens[0];
|
||||
$token['key'] = $authKey;
|
||||
$token['csrfTokens'][$authKey] = strtotime($this->csrfExpires);
|
||||
}
|
||||
$this->Session->write('_Token', $token);
|
||||
$request->params['_Token'] = array(
|
||||
'key' => $token['key'],
|
||||
'unlockedFields' => $token['unlockedFields'],
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid possible timing attacks by using `hash_equals` method to compare hashes.
|
||||
* @param Controller $controller
|
||||
* @return bool
|
||||
*/
|
||||
protected function _validatePost(Controller $controller)
|
||||
{
|
||||
$token = $this->_validToken($controller);
|
||||
$hashParts = $this->_hashParts($controller);
|
||||
$check = sha1(implode('', $hashParts));
|
||||
|
||||
if (hash_equals($token, $check)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$msg = self::DEFAULT_EXCEPTION_MESSAGE;
|
||||
if (Configure::read('debug')) {
|
||||
$msg = $this->_debugPostTokenNotMatching($controller, $hashParts);
|
||||
}
|
||||
|
||||
throw new AuthSecurityException($msg);
|
||||
}
|
||||
}
|
|
@ -1,215 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* create, read, update and delete (CRUD)
|
||||
*/
|
||||
|
||||
class BlocklistComponent extends Component
|
||||
{
|
||||
public $settings = array();
|
||||
public $defaultModel = '';
|
||||
|
||||
public $components = array('RestResponse');
|
||||
|
||||
public function index($rest = false, $filters = array())
|
||||
{
|
||||
if (!empty($filters)) {
|
||||
$this->controller->paginate['conditions'] = $filters;
|
||||
}
|
||||
if ($rest) {
|
||||
$data = $this->controller->{$this->controller->defaultModel}->find('all', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => isset($this->controller->paginate['conditions']) ? $this->controller->paginate['conditions'] : []
|
||||
));
|
||||
$blocklist = [];
|
||||
foreach ($data as $item) {
|
||||
$blocklist[] = $item[$this->controller->defaultModel];
|
||||
}
|
||||
return $this->RestResponse->viewData($blocklist);
|
||||
} else {
|
||||
$this->controller->set('response', $this->controller->paginate());
|
||||
}
|
||||
}
|
||||
|
||||
public function add($rest = false)
|
||||
{
|
||||
if ($this->controller->request->is('post')) {
|
||||
if ($rest) {
|
||||
if ($this->controller->response->type() === 'application/json') {
|
||||
$isJson = true;
|
||||
$data = $this->controller->request->input('json_decode', true);
|
||||
} else {
|
||||
$data = $this->controller->request->data;
|
||||
}
|
||||
if (isset($data['request'])) {
|
||||
$data = $data['request'];
|
||||
}
|
||||
} else {
|
||||
$data = $this->controller->request->data;
|
||||
}
|
||||
if (!isset($data[$this->controller->defaultModel])) {
|
||||
$data = [$this->controller->defaultModel => $data];
|
||||
}
|
||||
if (!isset($data[$this->controller->defaultModel])) {
|
||||
throw new InvalidArgumentException(__('Pass a list of uuids via the "uuids" key in the request object.'));
|
||||
}
|
||||
if (is_array($data[$this->controller->defaultModel]['uuids'])) {
|
||||
$uuids = $data[$this->controller->defaultModel]['uuids'];
|
||||
} else {
|
||||
$uuids = explode(PHP_EOL, trim($data[$this->controller->defaultModel]['uuids']));
|
||||
}
|
||||
$successes = array();
|
||||
$fails = array();
|
||||
foreach ($uuids as $uuid) {
|
||||
$uuid = trim($uuid);
|
||||
if (strlen($uuid) == 36) {
|
||||
$this->controller->{$this->controller->defaultModel}->create();
|
||||
$object = array();
|
||||
foreach ($this->controller->{$this->controller->defaultModel}->blocklistFields as $f) {
|
||||
if ($f === $this->controller->{$this->controller->defaultModel}->blocklistTarget . '_uuid') {
|
||||
$object[$f] = $uuid;
|
||||
} else {
|
||||
$object[$f] = !empty($data[$this->controller->defaultModel][$f]) ? $data[$this->controller->defaultModel][$f] : '';
|
||||
}
|
||||
}
|
||||
if ($this->controller->{$this->controller->defaultModel}->save($object)) {
|
||||
$successes[] = $uuid;
|
||||
} else {
|
||||
$fails[] = $uuid;
|
||||
}
|
||||
} else {
|
||||
$fails[] = $uuid;
|
||||
}
|
||||
}
|
||||
$message = sprintf(__('Done. Added %d new entries to the blocklist. %d entries could not be saved.'), count($successes), count($fails));
|
||||
if ($rest) {
|
||||
$result = [
|
||||
'result' => [
|
||||
'successes' => $successes,
|
||||
'fails' => $fails
|
||||
],
|
||||
'message' => $message
|
||||
];
|
||||
return $this->RestResponse->viewData($result);
|
||||
} else {
|
||||
$this->controller->Flash->success($message);
|
||||
$this->controller->redirect(array('action' => 'index'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function edit($rest = false, $id)
|
||||
{
|
||||
if (Validation::uuid($id)) {
|
||||
$blockEntry = $this->controller->{$this->controller->defaultModel}->find('first', [
|
||||
'conditions' => array(
|
||||
$this->controller->{$this->controller->defaultModel}->blocklistTarget . '_uuid' => $id
|
||||
)
|
||||
]);
|
||||
} else {
|
||||
$blockEntry = $this->controller->{$this->controller->defaultModel}->find('first', array('conditions' => array('id' => $id)));
|
||||
}
|
||||
if (empty($blockEntry)) {
|
||||
throw new NotFoundException(__('Blocklist item not found.'));
|
||||
}
|
||||
$this->controller->set('blockEntry', $blockEntry);
|
||||
if ($this->controller->request->is('post')) {
|
||||
if ($rest) {
|
||||
if ($this->controller->response->type() === 'application/json') {
|
||||
$isJson = true;
|
||||
$data = $this->controller->request->input('json_decode', true);
|
||||
} else {
|
||||
$data = $this->controller->request->data;
|
||||
}
|
||||
if (isset($data['request'])) {
|
||||
$data = $data['request'];
|
||||
}
|
||||
if (!isset($data[$this->controller->defaultModel])) {
|
||||
$data = [$this->controller->defaultModel => $data];
|
||||
}
|
||||
} else {
|
||||
$data = $this->controller->request->data;
|
||||
}
|
||||
$fields = $this->controller->{$this->controller->defaultModel}->blocklistFields;
|
||||
foreach ($fields as $f) {
|
||||
if ($f == 'uuid') {
|
||||
continue;
|
||||
}
|
||||
if (isset($data[$this->controller->defaultModel][$f])) {
|
||||
$blockEntry[$this->controller->defaultModel][$f] = $data[$this->controller->defaultModel][$f];
|
||||
}
|
||||
}
|
||||
if ($this->controller->{$this->controller->defaultModel}->save($blockEntry)) {
|
||||
if ($rest) {
|
||||
return $this->RestResponse->viewData(
|
||||
$this->controller->{$this->controller->defaultModel}->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => [
|
||||
'id' => $this->controller->{$this->controller->defaultModel}->id
|
||||
]
|
||||
])
|
||||
);
|
||||
} else {
|
||||
$this->controller->Flash->success(__('Blocklist item added.'));
|
||||
$this->controller->redirect(array('action' => 'index'));
|
||||
}
|
||||
} else {
|
||||
if ($rest) {
|
||||
throw new MethodNotAllowedException('Could not save the blocklist item.');
|
||||
} else {
|
||||
$this->controller->Flash->error(__('Could not save the blocklist item'));
|
||||
$this->controller->redirect(array('action' => 'index'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function delete($rest = false, $id)
|
||||
{
|
||||
if (Validation::uuid($id)) {
|
||||
$blockEntry = $this->controller->{$this->controller->defaultModel}->find('first', [
|
||||
'conditions' => array(
|
||||
$this->controller->{$this->controller->defaultModel}->blocklistTarget . '_uuid' => $id
|
||||
)
|
||||
]);
|
||||
} else {
|
||||
$blockEntry = $this->controller->{$this->controller->defaultModel}->find('first', array('conditions' => array('id' => $id)));
|
||||
}
|
||||
if (empty($blockEntry)) {
|
||||
throw new NotFoundException(__('Invalid blocklist entry'));
|
||||
}
|
||||
|
||||
if ($this->controller->{$this->controller->defaultModel}->delete($blockEntry[$this->controller->defaultModel]['id'])) {
|
||||
$message = __('Blocklist entry removed');
|
||||
if ($rest) {
|
||||
return $this->RestResponse->saveSuccessResponse($this->controller->defaultModel, 'delete', $id, false, $message);
|
||||
}
|
||||
$this->controller->Flash->success($message);
|
||||
} else {
|
||||
$message = __('Could not remove the blocklist entry');
|
||||
if ($rest) {
|
||||
return $this->RestResponse->saveFailResponse($this->controller->defaultModel, 'delete', $id, $message);
|
||||
}
|
||||
$this->controller->error($message);
|
||||
}
|
||||
$this->controller->redirect(array('action' => 'index'));
|
||||
}
|
||||
|
||||
public $controller;
|
||||
|
||||
public function initialize(Controller $controller)
|
||||
{
|
||||
$this->controller = $controller;
|
||||
}
|
||||
|
||||
public function startup(Controller $controller)
|
||||
{
|
||||
$this->controller = $controller;
|
||||
}
|
||||
|
||||
public function __construct(ComponentCollection $collection, $settings = array())
|
||||
{
|
||||
$this->settings = Set::merge($this->settings, $settings);
|
||||
parent::__construct($collection, $this->settings);
|
||||
}
|
||||
}
|
|
@ -1,385 +0,0 @@
|
|||
<?php
|
||||
class CRUDComponent extends Component
|
||||
{
|
||||
/** @var AppController */
|
||||
public $Controller;
|
||||
|
||||
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(array $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->modelClass}->find('all', $query);
|
||||
if (isset($options['afterFind'])) {
|
||||
if (is_callable($options['afterFind'])) {
|
||||
$data = $options['afterFind']($data);
|
||||
} else {
|
||||
$data = $this->Controller->{$this->Controller->modelClass}->{$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'])) {
|
||||
if (is_callable($options['afterFind'])) {
|
||||
$data = $options['afterFind']($data);
|
||||
} else {
|
||||
$data = $this->Controller->{$this->Controller->defaultModel}->{$options['afterFind']}($data);
|
||||
}
|
||||
}
|
||||
$this->Controller->set('data', $data);
|
||||
}
|
||||
}
|
||||
|
||||
public function add(array $params = [])
|
||||
{
|
||||
$modelName = $this->Controller->modelClass;
|
||||
$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;
|
||||
}
|
||||
}
|
||||
unset($input[$modelName]['id']);
|
||||
if (!empty($params['fields'])) {
|
||||
$data = [];
|
||||
foreach ($params['fields'] as $field) {
|
||||
$data[$field] = $input[$modelName][$field];
|
||||
}
|
||||
} else {
|
||||
$data = $input;
|
||||
}
|
||||
if (isset($params['beforeSave'])) {
|
||||
$data = $params['beforeSave']($data);
|
||||
}
|
||||
/** @var Model $model */
|
||||
$model = $this->Controller->{$modelName};
|
||||
$savedData = $model->save($data);
|
||||
if ($savedData) {
|
||||
if (isset($params['afterSave'])) {
|
||||
$params['afterSave']($data);
|
||||
}
|
||||
$data = $model->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => [
|
||||
'id' => $model->id
|
||||
]
|
||||
]);
|
||||
if (empty($data)) {
|
||||
throw new Exception("Something went wrong, saved data not found in database.");
|
||||
}
|
||||
if (isset($params['afterFind'])) {
|
||||
$data = $params['afterFind']($data, $savedData);
|
||||
}
|
||||
$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(['action' => 'view', $model->id], true));
|
||||
$this->Controller->render($params['displayOnSuccess']);
|
||||
return;
|
||||
}
|
||||
|
||||
$redirect = isset($params['redirect']) ? $params['redirect'] : ['action' => 'index'];
|
||||
if (!empty($params['redirect_controller'])) {
|
||||
if (is_array($redirect)) {
|
||||
$redirect['controller'] = $params['redirect_controller'];
|
||||
} else {
|
||||
$redirect = '/' . $params['redirect_controller'] . '/' . $redirect;
|
||||
}
|
||||
}
|
||||
// For AJAX requests doesn't make sense to redirect, redirect must be done on javascript side in `submitGenericFormInPlace`
|
||||
if ($this->Controller->request->is('ajax')) {
|
||||
$redirect = Router::url($redirect);
|
||||
$this->Controller->restResponsePayload = $this->Controller->RestResponse->viewData(['redirect' => $redirect], 'json');
|
||||
} else {
|
||||
$this->Controller->redirect($redirect);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$message = __('%s could not be added.', $modelName);
|
||||
if ($this->Controller->IndexFilter->isRest()) {
|
||||
$controllerName = $this->Controller->params['controller'];
|
||||
$actionName = $this->Controller->params['action'];
|
||||
$this->Controller->restResponsePayload = $this->Controller->RestResponse->saveFailResponse($controllerName, $actionName, false, $model->validationErrors, 'json');
|
||||
} else {
|
||||
$this->Controller->Flash->error($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->Controller->set('entity', $data);
|
||||
}
|
||||
|
||||
public function edit(int $id, array $params = [])
|
||||
{
|
||||
$modelName = $this->Controller->modelClass;
|
||||
if (empty($id)) {
|
||||
throw new NotFoundException(__('Invalid %s.', $modelName));
|
||||
}
|
||||
$query = isset($params['get']) ? $params['get'] : [
|
||||
'recursive' => -1,
|
||||
'conditions' => [
|
||||
$modelName . '.id' => $id
|
||||
],
|
||||
];
|
||||
if (!empty($params['conditions'])) {
|
||||
$query['conditions']['AND'][] = $params['conditions'];
|
||||
}
|
||||
if (!empty($params['contain'])) {
|
||||
$query['contain'] = $params['contain'];
|
||||
}
|
||||
/** @var Model $model */
|
||||
$model = $this->Controller->{$modelName};
|
||||
$data = $model->find('first', $query);
|
||||
if (empty($data)) {
|
||||
throw new NotFoundException(__('Invalid %s.', $modelName));
|
||||
}
|
||||
if (isset($params['afterFind'])) {
|
||||
$data = $params['afterFind']($data);
|
||||
}
|
||||
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) {
|
||||
if(isset($input[$modelName][$field])){
|
||||
$data[$modelName][$field] = $input[$modelName][$field];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($input[$modelName] as $field => $fieldData) {
|
||||
$data[$modelName][$field] = $fieldData;
|
||||
}
|
||||
}
|
||||
if (isset($params['beforeSave'])) {
|
||||
$data = $params['beforeSave']($data);
|
||||
}
|
||||
if ($model->save($data)) {
|
||||
if (isset($params['afterSave'])) {
|
||||
$params['afterSave']($data);
|
||||
}
|
||||
$message = __('%s updated.', $modelName);
|
||||
if ($this->Controller->IndexFilter->isRest()) {
|
||||
$this->Controller->restResponsePayload = $this->Controller->RestResponse->viewData($data, 'json');
|
||||
return;
|
||||
} else {
|
||||
$this->Controller->Flash->success($message);
|
||||
$this->Controller->redirect(isset($params['redirect']) ? $params['redirect'] : ['action' => 'index']);
|
||||
}
|
||||
} else {
|
||||
if ($this->Controller->IndexFilter->isRest()) {
|
||||
$controllerName = $this->Controller->params['controller'];
|
||||
$actionName = $this->Controller->params['action'];
|
||||
$this->Controller->restResponsePayload = $this->Controller->RestResponse->saveFailResponse($controllerName, $actionName, false, $model->validationErrors, 'json');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->Controller->request->data = $data;
|
||||
}
|
||||
$this->Controller->set('entity', $data);
|
||||
}
|
||||
|
||||
public function view(int $id, array $params = [])
|
||||
{
|
||||
$modelName = $this->Controller->modelClass;
|
||||
if (empty($id)) {
|
||||
throw new NotFoundException(__('Invalid %s.', $modelName));
|
||||
}
|
||||
$query = [
|
||||
'recursive' => -1,
|
||||
'conditions' => [$modelName . '.id' => $id],
|
||||
'contain' => empty($params['contain']) ? [] : $params['contain']
|
||||
];
|
||||
if (!empty($params['conditions'])) {
|
||||
$query['conditions']['AND'][] = $params['conditions'];
|
||||
}
|
||||
$data = $this->Controller->{$modelName}->find('first', $query);
|
||||
if (empty($data)) {
|
||||
throw new NotFoundException(__('Invalid %s.', $modelName));
|
||||
}
|
||||
if (isset($params['afterFind'])) {
|
||||
$data = $params['afterFind']($data);
|
||||
}
|
||||
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 = [])
|
||||
{
|
||||
$this->prepareResponse();
|
||||
$modelName = $this->Controller->modelClass;
|
||||
if (empty($id)) {
|
||||
throw new NotFoundException(__('Invalid %s.', $modelName));
|
||||
}
|
||||
$conditions = [];
|
||||
$conditions['AND'][] = [$modelName . '.id' => $id];
|
||||
if (!empty($params['conditions'])) {
|
||||
$conditions['AND'][] = $params['conditions'];
|
||||
}
|
||||
$data = $this->Controller->{$modelName}->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => $conditions,
|
||||
'contain' => empty($params['contain']) ? [] : $params['contain'],
|
||||
]);
|
||||
if (empty($data)) {
|
||||
throw new NotFoundException(__('Invalid %s.', $modelName));
|
||||
}
|
||||
$validationError = null;
|
||||
if (isset($params['validate'])) {
|
||||
try {
|
||||
$params['validate']($data);
|
||||
} catch (Exception $e) {
|
||||
$validationError = $e->getMessage();
|
||||
if ($this->Controller->IndexFilter->isRest()) {
|
||||
$this->Controller->restResponsePayload = $this->Controller->RestResponse->saveFailResponse($modelName, 'delete', $id, $validationError);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($params['beforeDelete'])) {
|
||||
$data = $params['beforeDelete']($data);
|
||||
if (empty($data)) {
|
||||
throw new MethodNotAllowedException('Something went wrong, delete action failed.');
|
||||
}
|
||||
}
|
||||
if ($validationError === null && $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()) {
|
||||
$this->Controller->restResponsePayload = $this->Controller->RestResponse->saveSuccessResponse($modelName, 'delete', $id, 'json', $message);
|
||||
return;
|
||||
} else {
|
||||
$this->Controller->Flash->success($message);
|
||||
$this->Controller->redirect($this->Controller->referer());
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->Controller->set('validationError', $validationError);
|
||||
$this->Controller->set('id', $data[$modelName]['id']);
|
||||
$this->Controller->set('data', $data);
|
||||
$this->Controller->layout = 'ajax';
|
||||
$this->Controller->render('/genericTemplates/delete');
|
||||
}
|
||||
|
||||
public function setQuickFilters($params, array $query, $quickFilterFields)
|
||||
{
|
||||
if (!empty($params['quickFilter']) && !empty($quickFilterFields)) {
|
||||
$queryConditions = [];
|
||||
$filter = '%' . strtolower($params['quickFilter']) . '%';
|
||||
foreach ($quickFilterFields as $filterField) {
|
||||
$queryConditions["LOWER($filterField) LIKE"] = $filter;
|
||||
}
|
||||
$query['conditions']['OR'] = $queryConditions;
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function setFilters(array $params, array $query)
|
||||
{
|
||||
// For CakePHP 2, we don't need to distinguish between simpleFilters and relatedFilters
|
||||
//$params = $this->massageFilters($params);
|
||||
if (!empty($params)) {
|
||||
foreach ($params 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)
|
||||
{
|
||||
$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->modelClass}) {
|
||||
$massagedFilters['simpleFilters'][implode('.', $param)] = $paramValue;
|
||||
} else {
|
||||
$massagedFilters['relatedFilters'][implode('.', $param)] = $paramValue;
|
||||
}
|
||||
} else {
|
||||
$massagedFilters['simpleFilters'][$param] = $paramValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $massagedFilters;
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
<?php
|
||||
class CompressedRequestHandlerComponent extends Component
|
||||
{
|
||||
public function startup(Controller $controller)
|
||||
{
|
||||
$contentEncoding = $_SERVER['HTTP_CONTENT_ENCODING'] ?? null;
|
||||
if (!empty($contentEncoding)) {
|
||||
if ($contentEncoding === 'br') {
|
||||
$controller->request->setInput($this->decodeBrotliEncodedContent($controller));
|
||||
} else if ($contentEncoding === 'gzip') {
|
||||
$controller->request->setInput($this->decodeGzipEncodedContent($controller));
|
||||
} else {
|
||||
throw new BadRequestException("Unsupported content encoding '$contentEncoding'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function supportedEncodings()
|
||||
{
|
||||
$supportedEncodings = [];
|
||||
if (function_exists('gzdecode')) {
|
||||
$supportedEncodings[] = 'gzip';
|
||||
}
|
||||
if (function_exists('brotli_uncompress')) {
|
||||
$supportedEncodings[] = 'br';
|
||||
}
|
||||
return $supportedEncodings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
private function decodeGzipEncodedContent(Controller $controller)
|
||||
{
|
||||
if (function_exists('gzdecode')) {
|
||||
$decoded = gzdecode($controller->request->input());
|
||||
if ($decoded === false) {
|
||||
throw new BadRequestException('Invalid compressed data.');
|
||||
}
|
||||
return $decoded;
|
||||
} else {
|
||||
throw new BadRequestException("This server doesn't support GZIP compressed requests.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Controller $controller
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
private function decodeBrotliEncodedContent(Controller $controller)
|
||||
{
|
||||
if (function_exists('brotli_uncompress')) {
|
||||
$decoded = brotli_uncompress($controller->request->input());
|
||||
if ($decoded === false) {
|
||||
throw new BadRequestException('Invalid compressed data.');
|
||||
}
|
||||
return $decoded;
|
||||
} else {
|
||||
throw new BadRequestException("This server doesn't support brotli compressed requests.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
<?php
|
||||
class DeprecationComponent extends Component
|
||||
{
|
||||
/*
|
||||
* Deprecated endpoints
|
||||
* - simple controller->action structure
|
||||
* - each endpoint can be set to to a deprecation warning message or false
|
||||
*/
|
||||
private $deprecatedEndpoints;
|
||||
|
||||
public function initialize(Controller $controller)
|
||||
{
|
||||
$this->deprecatedEndpoints = array(
|
||||
'attributes' => array(
|
||||
'rpz' => __('Use /attributes/restSearch to export RPZ rules.'),
|
||||
'text' => __('Use /attributes/restSearch to export flat indicator lists.')
|
||||
),
|
||||
'events' => array(
|
||||
'addIOC' => __('Use MISP modules to import in OpenIOC format.'),
|
||||
'csv' => __('Use /events/restSearch to export in CSV format.'),
|
||||
'export' => __('Use the REST client to refine your search conditions and export in any of the given formats with much more control.'),
|
||||
'hids' => __('Use /events/restSearch to export hashes.'),
|
||||
'nids' => __('Use /events/restSearch to export in the various NIDS formats.'),
|
||||
'stix' => __('Use /events/restSearch to export in STIX format.'),
|
||||
'stix2' => __('Use /events/restSearch to export in STIX2 format.'),
|
||||
'xml' => __('Use /events/restSearch to export in XML format. It is highly recommended to use JSON whenever possible.')
|
||||
),
|
||||
'posts' => array(
|
||||
'add' => false,
|
||||
'index' => false
|
||||
),
|
||||
'templates' => array(
|
||||
'add' => false,
|
||||
'populateEventFromTemplate' => false
|
||||
),
|
||||
'allowedlists' => array(
|
||||
'admin_add' => false
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $controller
|
||||
* @param string $action
|
||||
* @param int|null $user_id
|
||||
* @return false|string
|
||||
*/
|
||||
public function checkDeprecation($controller, $action, $user_id)
|
||||
{
|
||||
if (isset($this->deprecatedEndpoints[$controller][$action])) {
|
||||
if ($user_id) {
|
||||
$this->__logDeprecatedAccess($controller, $action, $user_id);
|
||||
}
|
||||
if ($this->deprecatedEndpoints[$controller][$action]) {
|
||||
return $this->deprecatedEndpoints[$controller][$action];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $controller
|
||||
* @param string $action
|
||||
* @param int $user_id
|
||||
* @return void
|
||||
*/
|
||||
private function __logDeprecatedAccess($controller, $action, $user_id)
|
||||
{
|
||||
try {
|
||||
RedisTool::init()->hincrby(
|
||||
'misp:deprecation',
|
||||
"$controller:$action:$user_id",
|
||||
1
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
public function getDeprecatedAccessList()
|
||||
{
|
||||
$rearranged = array();
|
||||
$redis = RedisTool::init();
|
||||
$result = $redis->hGetAll('misp:deprecation');
|
||||
if (!empty($result)) {
|
||||
foreach ($result as $key => $value) {
|
||||
$key_components = explode(':', $key);
|
||||
$rearranged[$key_components[0]][$key_components[1]][$key_components[2]] = (int)$value;
|
||||
if (empty($rearranged[$key_components[0]][$key_components[1]]['total'])) {
|
||||
$rearranged[$key_components[0]][$key_components[1]]['total'] = (int)$value;
|
||||
} else {
|
||||
$rearranged[$key_components[0]][$key_components[1]]['total'] += (int)$value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $rearranged;
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
|
||||
class IOCExportComponent extends Component
|
||||
{
|
||||
// removed in favour of th IOCExportTool
|
||||
// File used as a placeholder to avoid model caching issues
|
||||
}
|
|
@ -1,672 +0,0 @@
|
|||
<?php
|
||||
|
||||
class IOCImportComponent extends Component
|
||||
{
|
||||
|
||||
// rework of handling composite attributes
|
||||
// each entry is only triggered if an "AND" branch contains the exact list of parsed attribute types that are set in the components field
|
||||
// Note that some indicators are not immediately discarded by the parser even if they cannot be turned into an attribute
|
||||
// The reason for this is that they might not be convertable to an attribute, but they might be converted into a part of a composite attribute
|
||||
// For example: There is no Registry value type in MISP, but there is a regkey|value type. Meaning that a Registry value can be turned into an attribute
|
||||
// as long as a registry key is "AND"ed with the value in the OpenIOC file.
|
||||
|
||||
// notes about the format for the composition:
|
||||
// - components have to be in alphabetical order
|
||||
// - returnFormat has to be a valid MISP type
|
||||
// - returnCategory has to be a valid MISP category
|
||||
// - replace: passed attribute values will replace the $[component position] substring to form the final attribute value
|
||||
private $attributeComposition = array(
|
||||
array(
|
||||
'components' => array('filename', 'md5'),
|
||||
'returnFormat' => 'filename|md5',
|
||||
'returnCategory' => 'Payload installation',
|
||||
'replace'=> '$0|$1'
|
||||
),
|
||||
array(
|
||||
'components' => array('filename', 'sha1'),
|
||||
'returnFormat' => 'filename|sha1',
|
||||
'returnCategory' => 'Payload installation',
|
||||
'replace'=> '$0|$1'
|
||||
),
|
||||
array(
|
||||
'components' => array('filename', 'sha256'),
|
||||
'returnFormat' => 'filename|sha256',
|
||||
'returnCategory' => 'Payload installation',
|
||||
'replace'=> '$0|$1'
|
||||
),
|
||||
array(
|
||||
'components' => array('regkey', 'tempRegValue'),
|
||||
'returnFormat' => 'regkey|value',
|
||||
'returnCategory' => 'Persistence mechanism',
|
||||
'replace'=> '$0|$1'
|
||||
),
|
||||
array(
|
||||
'components' => array('filename', 'tempCertificateSubject'),
|
||||
'returnFormat' => 'filename',
|
||||
'returnCategory' => 'Payload installation',
|
||||
'replace'=> '$0'
|
||||
),
|
||||
array(
|
||||
'components' => array('filename', 'tempExtension'),
|
||||
'returnFormat' => 'filename',
|
||||
'returnCategory' => 'Payload installation',
|
||||
'replace'=> '$0.$1'
|
||||
),
|
||||
array(
|
||||
'components' => array('regkey', 'tempRegName', 'tempRegValue'),
|
||||
'returnFormat' => 'regkey|value',
|
||||
'returnCategory' => 'Persistence mechanism',
|
||||
'replace'=> '$0$1|$2'
|
||||
),
|
||||
);
|
||||
|
||||
// Indicators that we can safely remove if they pop up within an AND branch
|
||||
private $discardableIndicators = array(
|
||||
//'FileItem/PEInfo/Exports/NumberOfFunctions',
|
||||
//'FileItem/PEInfo/Exports/ExportedFunctions/string',
|
||||
//'FileItem/PEInfo/ImportedModules/Module/ImportedFunctions/string',
|
||||
//'FileItem/SizeInBytes',
|
||||
//'FileItem/PEInfo/PETimeStamp',
|
||||
//'FileItem/PEInfo/DigitalSignature/SignatureExists',
|
||||
//'FileItem/PEInfo/DigitalSignature/CertificateSubject',
|
||||
);
|
||||
|
||||
// the default settings for the IDS flag / type
|
||||
private $typeToIdsSettings = array(
|
||||
'filename' => true,
|
||||
'md5' => true,
|
||||
'sha1' => true,
|
||||
'sha256' => true,
|
||||
'ip-src' => true,
|
||||
'ip-dst' => true,
|
||||
'domain' => true,
|
||||
'hostname' => true,
|
||||
'email-dst' => true,
|
||||
'email-src' => true,
|
||||
'email-subject' => true,
|
||||
'email-attachment' => true,
|
||||
'url' => true,
|
||||
'user-agent' => false,
|
||||
'regkey' => true,
|
||||
'snort' => true,
|
||||
'pattern-in-file' => true,
|
||||
'comment' => false,
|
||||
'other' => false,
|
||||
'filename|md5' => true,
|
||||
'filename|sha1' => true,
|
||||
'filename|sha256' => true,
|
||||
'regkey|value' => true,
|
||||
);
|
||||
|
||||
// Set up the data that you would like to fetch from the ioc's root and add as comments.
|
||||
private $iocinfo = array('info', 'uuid', 'date', 'long_info');
|
||||
|
||||
// The maximum number of combinations that the AND branch resolver should allow
|
||||
// Basically, AND branches with several OR sub branches can be converted into OR branches with many AND sub branches, so that we can try to eliminate the AND-s
|
||||
// by omitting the discardable indicators and by grouping the remaining indicators into valid attribute pairs.
|
||||
// The number of possible combinations that will be tested can be calculated by multiplying each of the leaves in the OR sub-branches.
|
||||
// AND: 1,2,OR(3,4), OR(5,6) where the AND branch contains leaves 1 and 2 and the 2 OR sub-branches contain leaves 3,4 and 5,6 respectively.
|
||||
// In this case we have 2*2 combinations:
|
||||
// OR: AND(1,2,3,5), AND(1,2,3,6), AND(1,2,4,5), AND(1,2,4,6)
|
||||
// $hardLimit sets the maximum number of combinations allowed
|
||||
private $hardLimit = 10000;
|
||||
|
||||
// tracks the failed indicatorItems
|
||||
private $fails = array();
|
||||
// uuids of successfully added indicatoritems and indicators, used for the creation of the graph
|
||||
private $saved_uuids = array();
|
||||
|
||||
private $tempLeaves = array();
|
||||
|
||||
// used to store the event id and the distribution, so that they can be added to attribtes during the conversion
|
||||
private $event_id = null;
|
||||
private $distribution = array();
|
||||
|
||||
// stores the graph that will be passed back along with the event and attributes
|
||||
private $graph = array();
|
||||
|
||||
private $filename = "";
|
||||
|
||||
public function readXML($data, $id, $dist, $filename)
|
||||
{
|
||||
$this->Attribute = ClassRegistry::init('Attribute');
|
||||
$this->filename = $filename;
|
||||
$this->fails = array();
|
||||
$this->distribution = $dist;
|
||||
$this->event_id = $id;
|
||||
// import XML class
|
||||
App::uses('Xml', 'Utility');
|
||||
|
||||
// now parse it
|
||||
$xmlArray['ioc'] = json_decode(json_encode((array) simplexml_load_string($data)), 1);
|
||||
|
||||
// Since the tree created by simplexml is a bit of a pain to traverse (partially because of branches with 1 leaves and with several leaves ending up in a different format -
|
||||
// $branch['leaf'] vs $branch[0]['leaf'] we convert it to an easier to deal with tree structure
|
||||
// This tree also only contains the information that we care about.
|
||||
$tree = array(
|
||||
'type' => 'OR',
|
||||
'branches' => array(),
|
||||
'leaves' => array()
|
||||
);
|
||||
if (isset($xmlArray['ioc']['@attributes']['id'])) {
|
||||
$tree['uuid'] = $xmlArray['ioc']['@attributes']['id'];
|
||||
}
|
||||
$temp = '';
|
||||
if (isset($xmlArray['ioc']['short_description'])) {
|
||||
$temp = $xmlArray['ioc']['short_description'];
|
||||
if (isset($xmlArray['ioc']['authored_by'])) {
|
||||
$temp .= PHP_EOL . 'by ' . $xmlArray['ioc']['authored_by'];
|
||||
}
|
||||
}
|
||||
if ($temp !== '') {
|
||||
$tree['info'] = $temp;
|
||||
}
|
||||
if (isset($xmlArray['ioc']['description'])) {
|
||||
$tree['longinfo'] = $xmlArray['ioc']['description'];
|
||||
}
|
||||
if (isset($xmlArray['ioc']['authored_date'])) {
|
||||
$tree['date'] = $xmlArray['ioc']['authored_date'];
|
||||
}
|
||||
|
||||
$tree['branches'] = $this->__createRootNode($xmlArray);
|
||||
|
||||
// Once we're done, let's back the tree up for later use, so we can start shuffling things around and converting it to our own attribute format
|
||||
$oldTree = $tree;
|
||||
// Let's start analysing and altering the tree so that we can keep as much data as possible
|
||||
$tree = $this->__traverseAndAnalyse($tree);
|
||||
$tree = $this->__resolveBranch($tree, $tree['uuid'], 'OR', $tree['leaves'], true);
|
||||
$attributes = null;
|
||||
if (isset($tree['branches'][0]['leaves'])) {
|
||||
$attributes = $tree['branches'][0]['leaves'];
|
||||
}
|
||||
if (isset($tree['leaves'])) {
|
||||
$attributes = $tree['leaves'];
|
||||
}
|
||||
unset($tree['branches'], $tree['leaves'], $tree['type']);
|
||||
// set the basic info the event in case we want to populate the uuid, info and date fields
|
||||
$event = $tree;
|
||||
// attach the attributes to the event
|
||||
$event['Attribute'] = $attributes;
|
||||
|
||||
$duplicateFilter = array();
|
||||
// check if we have any attributes, if yes, add their UUIDs to our list of success-array
|
||||
if (count($event['Attribute']) > 0) {
|
||||
foreach ($event['Attribute'] as $k => $attribute) {
|
||||
$condensed = strtolower($attribute['value']) . $attribute['category'] . $attribute['type'];
|
||||
if (!in_array($condensed, $duplicateFilter)) {
|
||||
$this->saved_uuids[] = $attribute['uuid'];
|
||||
$duplicateFilter[] = $condensed;
|
||||
$event['Attribute'][$k]['uuid'] = CakeText::uuid();
|
||||
} else {
|
||||
unset($event['Attribute'][$k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// using the previously populated array, set a flag on each branch and leaf indicating whether they were successfully added or not, to colour the graph
|
||||
$oldTree = $this->__setSuccesses($oldTree);
|
||||
$this->__graphBranches($oldTree, 0);
|
||||
|
||||
// Add a special attribute that captures the basic data about the .ioc such as the ioc-s uuid, info, long info, author, etc.
|
||||
// Define the fields used in the global iocinfo variable.
|
||||
foreach ($this->iocinfo as $k => $v) {
|
||||
if (isset($event[$v])) {
|
||||
$event['Attribute'][] = array('uuid' => CakeText::uuid(), 'category' => 'Other', 'type' => 'comment', 'event_id' => $id, 'value' => $v . ': ' . $event[$v], 'to_ids' => $this->typeToIdsSettings['comment'], 'distribution' => $this->distribution, 'comment' => 'OpenIOC import from file ' . $filename);
|
||||
}
|
||||
}
|
||||
|
||||
// attach the graph to the event
|
||||
$event['Graph'] = $this->graph;
|
||||
// attach the failures to the event
|
||||
$event['Fails'] = $this->fails;
|
||||
// Set the initual OR branch to being successful (= green on the graph)
|
||||
if (count($this->saved_uuids) > 1) {
|
||||
$event['Graph'][0][1] = true;
|
||||
}
|
||||
// return the whole package and let the eventscontroller dissect it
|
||||
return $event;
|
||||
}
|
||||
|
||||
// traverse the oldTree and set the successful branches and leaves to "success true" if they got added to the attribute tree. Otherwise set false.
|
||||
private function __setSuccesses($branch)
|
||||
{
|
||||
foreach ($branch['leaves'] as $key => $value) {
|
||||
$branch['leaves'][$key]['success'] = (in_array($value['uuid'], $this->saved_uuids) ? true : false);
|
||||
if (!array_key_exists('success', $value) || !$value['success']) {
|
||||
$this->fails[] = $value;
|
||||
}
|
||||
}
|
||||
foreach ($branch['branches'] as $key => $value) {
|
||||
$branch['branches'][$key] = $this->__setSuccesses($value);
|
||||
}
|
||||
$branch['success'] = (in_array($branch['uuid'], $this->saved_uuids) ? true : false);
|
||||
return $branch;
|
||||
}
|
||||
|
||||
private function __traverseAndAnalyse($array)
|
||||
{
|
||||
if (count($array['leaves']) > 0) {
|
||||
foreach ($array['leaves'] as $key => $leaf) {
|
||||
$array['leaves'][$key] = $this->__analyseIndicator($leaf);
|
||||
}
|
||||
}
|
||||
if (count($array['branches']) > 0) {
|
||||
foreach ($array['branches'] as $key => $branch) {
|
||||
$array['branches'][$key] = $this->__traverseAndAnalyse($branch);
|
||||
}
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
// dissect the indicator and convert it into an attribute
|
||||
private function __analyseIndicator($attribute)
|
||||
{
|
||||
$attribute['distribution'] = $this->distribution;
|
||||
if (isset($attribute['type'])) {
|
||||
$type = $attribute['type'];
|
||||
} else {
|
||||
$type = '';
|
||||
}
|
||||
$temp = $this->__checkType($attribute['search'], $type);
|
||||
if ($attribute['condition'] !== 'containsnot') {
|
||||
if (!$temp) {
|
||||
return false;
|
||||
}
|
||||
$attribute['category'] = $temp[0];
|
||||
$attribute['type'] = $temp[1];
|
||||
} else {
|
||||
$attribute['category'] = 'Other';
|
||||
$attribute['type'] = 'other';
|
||||
$attribute['value'] = 'containsnot: ' . $attribute['value'];
|
||||
}
|
||||
if (isset($this->typeToIdsSettings[$attribute['type']])) {
|
||||
$attribute['to_ids'] = $this->typeToIdsSettings[$attribute['type']];
|
||||
}
|
||||
// If we couldn't figure out the category / type and got Other/other, append the search term in the value
|
||||
if ($temp[0] == 'Other' && $temp[1] == 'other') {
|
||||
$attribute['value'] = $attribute['search'] . ': ' . $attribute['value'];
|
||||
}
|
||||
$attribute['comment'] = 'OpenIOC import from file ' . $this->filename . PHP_EOL . 'Original UUID: ' . $attribute['uuid'];
|
||||
return $attribute;
|
||||
}
|
||||
|
||||
private function __createRootNode($xmlArray)
|
||||
{
|
||||
$array = array();
|
||||
if ($this->__isAssoc($xmlArray['ioc']['definition']['Indicator'])) {
|
||||
foreach ($xmlArray['ioc']['definition'] as $key => $value) {
|
||||
$array[] = $this->__createBranchNode($value);
|
||||
}
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
private function __createBranchNode($array)
|
||||
{
|
||||
$node = array('leaves' => array(), 'branches' => array());
|
||||
foreach ($array as $key => $value) {
|
||||
if ($key === '@attributes') {
|
||||
$node['type'] = $value['operator'];
|
||||
$node['uuid'] = $value['id'];
|
||||
}
|
||||
if ($key === 'IndicatorItem') {
|
||||
if ($this->__isAssoc($value)) {
|
||||
$temp = $value;
|
||||
$value = array();
|
||||
$value[0] = $temp;
|
||||
}
|
||||
foreach ($value as $ii) {
|
||||
$leaf = array('uuid' => $ii['@attributes']['id'], 'condition' => $ii['@attributes']['condition'], 'search' => $ii['Context']['@attributes']['search'], 'value' => $ii['Content']);
|
||||
array_push($node['leaves'], $leaf);
|
||||
}
|
||||
}
|
||||
if ($key === 'Indicator') {
|
||||
if ($this->__isAssoc($value)) {
|
||||
$temp = $value;
|
||||
$value = array();
|
||||
$value[0] = $temp;
|
||||
}
|
||||
foreach ($value as $k => $v) {
|
||||
array_push($node['branches'], $this->__createBranchNode($v));
|
||||
}
|
||||
}
|
||||
}
|
||||
return $node;
|
||||
}
|
||||
|
||||
// Neat way of checking whether an array is associative or not - during the conversion from XML, if a Node has 1 child it will be represented as $node['child'] instead of $node[0]['child']
|
||||
// By figuring out whether we're dealing with a numerical or an associative array, we can avoid this issue
|
||||
private function __isAssoc($array)
|
||||
{
|
||||
return (bool)count(array_filter(array_keys($array), 'is_string'));
|
||||
}
|
||||
|
||||
private function __checkType($search, $type)
|
||||
{
|
||||
// Here we have to figure out how to best map the indicator to an attribute. This is an initial mapping, needs lots of tweaks still
|
||||
// Keep in mind: names starting with "temp" will only be used for composite types, then changed to Other -> other.
|
||||
switch ($search) {
|
||||
case 'FileItem/FileName':
|
||||
case 'DriverItem/DriverName':
|
||||
case 'FileItem/FullPath':
|
||||
return array('Payload installation', 'filename', true);
|
||||
break;
|
||||
case 'FileItem/Md5sum':
|
||||
return array('Payload installation', 'md5', true);
|
||||
break;
|
||||
case 'TaskItem/sha1sum':
|
||||
case 'FileItem/Sha1sum':
|
||||
return array('Payload installation', 'sha1', true);
|
||||
break;
|
||||
case 'FileItem/Sha256sum':
|
||||
return array('Payload installation', 'sha256', true);
|
||||
break;
|
||||
case 'PortItem/remoteIP':
|
||||
return array('Network activity', 'ip-src', true);
|
||||
break;
|
||||
case 'RouteEntryItem/Gateway':
|
||||
case 'RouteEntryItem/Destination':
|
||||
if ($type === 'IP') {
|
||||
return array('Network activity', 'ip-dst', true);
|
||||
break;
|
||||
}
|
||||
if ($type === 'string' or $type === 'String') {
|
||||
return array('Network activity', 'domain', true);
|
||||
break;
|
||||
}
|
||||
// no break
|
||||
case 'Network/DNS':
|
||||
return array('Network activity', 'domain', true);
|
||||
break;
|
||||
case 'Email/To':
|
||||
return array('Payload delivery', 'email-dst', true);
|
||||
break;
|
||||
case 'Email/From':
|
||||
return array('Payload delivery', 'email-src', true);
|
||||
break;
|
||||
case 'Email/Subject':
|
||||
return array('Payload delivery', 'email-subject', true);
|
||||
break;
|
||||
case 'Email/Attachment/Na':
|
||||
return array('Payload delivery', 'email-attachment', true);
|
||||
break;
|
||||
case 'UrlHistoryItem/URL':
|
||||
case 'UrlHistoryItem/VisitFrom':
|
||||
case 'FileDownloadHistoryItem/SourceURL':
|
||||
case 'FormHistoryItem/FormSubmitURL':
|
||||
return array('Network activity', 'url', true);
|
||||
break;
|
||||
case 'Network/UserAgent':
|
||||
return array('Network activity', 'user-agent', false);
|
||||
break;
|
||||
case 'RegistryItem/KeyPath':
|
||||
case 'RegistryItem/Modified':
|
||||
case 'RegistryItem/Path':
|
||||
return array('Persistence mechanism', 'regkey', true);
|
||||
break;
|
||||
case 'RegistryItem/ValueName':
|
||||
return array('Persistence mechanism', 'tempRegName', false);
|
||||
case 'Snort/Snort':
|
||||
return array('Network activity', 'snort', true);
|
||||
break;
|
||||
case 'TaskItem/Comment':
|
||||
return array('Other', 'comment', false);
|
||||
break;
|
||||
case 'CookieHistoryItem/HostName':
|
||||
case 'FormHistoryItem/HostName':
|
||||
case 'SystemInfoItem/Hostname':
|
||||
case 'UrlHistoryItem/HostName':
|
||||
case 'DnsEntryItem/RecordName':
|
||||
case 'DnsEntryItem/Host':
|
||||
return array('Network activity', 'hostname', true);
|
||||
break;
|
||||
case 'RegistryItem/Text':
|
||||
case 'RegistryItem/Value':
|
||||
return array('Persistence mechanism', 'tempRegValue', false);
|
||||
break;
|
||||
// We don't keep the following, they are often used with AND and a filename. We'll only keep the filename in those cases.
|
||||
case 'FileItem/PEInfo/DigitalSignature/CertificateSubject':
|
||||
case 'FileItem/PEInfo/DigitalSignature/SignatureExists':
|
||||
return array('Payload delivery', 'tempCertificateSubject', false);
|
||||
break;
|
||||
case 'FileItem/PEInfo/DetectedAnomalies/string':
|
||||
return array('Payload delivery', 'pattern-in-file', true);
|
||||
break;
|
||||
}
|
||||
return array('Other', 'other', false);
|
||||
}
|
||||
|
||||
// Create the array used in the visualisation of the original ioc file
|
||||
private function __graphBranches($array, $level)
|
||||
{
|
||||
$level++;
|
||||
$spaces = '';
|
||||
for ($i = 1; $i < $level; $i++) {
|
||||
$spaces .= ' ';
|
||||
}
|
||||
foreach ($array['leaves'] as $leaf) {
|
||||
$this->graph[] = array(($spaces . '|__' . $leaf['search'] . ': ' . $leaf['condition'] . ': ' . $leaf['value']), $leaf['success']);
|
||||
}
|
||||
foreach ($array['branches'] as $branch) {
|
||||
$this->graph[] = array(($spaces . '|__' . $branch['type']), $branch['success']);
|
||||
$this->__graphBranches($branch, $level);
|
||||
}
|
||||
}
|
||||
|
||||
private function __resolveBranch($branch, $uuid, $type, &$leaves, $root = false)
|
||||
{
|
||||
// Resolve any deeper branching before we attempt to resolve this, as we might be able to turn it into a single attribute
|
||||
foreach ($branch['branches'] as $key => $value) {
|
||||
$r = $this->__resolveBranch($value, $branch['uuid'], $branch['type'], $branch['leaves']);
|
||||
if ($r === 'getFromTemp') {
|
||||
unset($branch['branches'][$key]);
|
||||
foreach ($this->tempLeaves as $tempLeaf) {
|
||||
$branch['leaves'][] = $tempLeaf;
|
||||
}
|
||||
$this->tempLeaves = array();
|
||||
} else {
|
||||
$branch['branches'][$key] = $r;
|
||||
if (!$branch['branches'][$key]) {
|
||||
unset($branch['branches'][$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// if marked for rebasing the indices:
|
||||
$branch['branches'] = array_values($branch['branches']);
|
||||
// First, let's see if we can get rid of some of the indicators in here
|
||||
foreach ($branch['leaves'] as $key => $value) {
|
||||
if ($this->__checkOmit($value)) {
|
||||
unset($branch['leaves'][$key]);
|
||||
}
|
||||
}
|
||||
// try to reverse AND-OR
|
||||
// If we are in an AND branch that only has 1 level of extra branching, consisting of only OR branches
|
||||
if ($branch['type'] === 'AND' && ((count($branch['branches']) > 1) || (count($branch['branches']) > 0 && count($branch['leaves']) > 0))) {
|
||||
// There's hope to be able to resolve the branch
|
||||
$hope = true;
|
||||
$combinations = 1;
|
||||
$ors = 0;
|
||||
$uuid = $branch['uuid'];
|
||||
// go through each of the branches contained in the AND branch
|
||||
foreach ($branch['branches'] as $bk => $bv) {
|
||||
// if the branch is an AND branch or if the branch further branches out - lose hope
|
||||
if ($bv['type'] === 'AND' || count($bv['branches']) > 0) {
|
||||
$hope = false;
|
||||
}
|
||||
$combinations = $combinations * count($bv['leaves']);
|
||||
$ors++;
|
||||
}
|
||||
|
||||
// if the number of possible combinations is higher than the hard limit, don't even attempt trying to resolve the AND branch
|
||||
if ($combinations > $this->hardLimit) {
|
||||
$hope = false;
|
||||
}
|
||||
|
||||
if ($hope && count($branch['branches']) > 0) {
|
||||
$combinations = $this->__findCombinations($branch['branches']);
|
||||
$current['branches'] = array('type' => 'AND', 'branches' => array());
|
||||
$temp = array();
|
||||
foreach ($combinations as $key => $current) {
|
||||
foreach ($branch['leaves'] as $leaf) {
|
||||
array_push($combinations[$key], $leaf);
|
||||
}
|
||||
$temp[] = array('type' => 'AND', 'leaves' => $current, 'branches' => array(), 'uuid' => $uuid);
|
||||
}
|
||||
$branch['type'] = 'OR';
|
||||
$branch['leaves'] = array();
|
||||
$branch['branches'] = $temp;
|
||||
// Try to resolve all the branches again now that they've been altered
|
||||
foreach ($branch['branches'] as $key => $value) {
|
||||
$branch['branches'][$key] = $this->__resolveBranch($value, $branch['uuid'], $branch['type'], $branch['leaves']);
|
||||
if ($branch['branches'][$key] == null) {
|
||||
unset($branch['branches'][$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Resolve any AND branches without any further branching
|
||||
if (count($branch['leaves']) != 0 && count($branch['branches']) == 0 && $branch['type'] === 'AND') {
|
||||
$branch['leaves'] = array($this->__resolveAndBranch($branch['leaves'], $uuid));
|
||||
if ($branch['leaves'][0] == null) {
|
||||
unset($branch['leaves']);
|
||||
} else {
|
||||
$this->saved_uuids[] = $branch['uuid'];
|
||||
}
|
||||
}
|
||||
if (isset($branch['leaves']) && count($branch['leaves']) == 1 && count($branch['branches']) == 0) {
|
||||
$leaves[] = $branch['leaves'][0];
|
||||
$branch['leaves'] = $leaves;
|
||||
}
|
||||
|
||||
if (($branch['type'] == 'OR') && count($branch['branches']) == 0 && count($branch['leaves']) != 0) {
|
||||
if (!$root) {
|
||||
$this->tempLeaves = $branch['leaves'];
|
||||
$this->saved_uuids[] = $uuid;
|
||||
return 'getFromTemp';
|
||||
}
|
||||
}
|
||||
// If we have no branches and no leaves left after all of this, return nothing and unset this branch
|
||||
if ((!isset($branch['leaves']) || count($branch['leaves']) == 0) && count($branch['branches']) == 0 && !isset($branch['long_info'])) {
|
||||
return;
|
||||
}
|
||||
return $branch;
|
||||
}
|
||||
|
||||
// Find the possible combinations of several OR branches within an AND branch
|
||||
private function __findCombinations($arrays, $i = 0)
|
||||
{
|
||||
if (!isset($arrays[$i]['leaves'])) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// If there's only 1 OR branch, return the indicatorItems in the same format as if there were more. It can still get ANDed with the leaves of the parent branch
|
||||
if ($i == count($arrays) - 1) {
|
||||
$temp = array();
|
||||
foreach ($arrays[$i]['leaves'] as $current) {
|
||||
$temp[] = array($current);
|
||||
}
|
||||
return $temp;
|
||||
}
|
||||
|
||||
// get combinations from subsequent arrays
|
||||
$tmp = $this->__findCombinations($arrays, $i + 1);
|
||||
|
||||
$result = array();
|
||||
// Build an array of AND-ed combinations
|
||||
foreach ($arrays[$i]['leaves'] as $v) {
|
||||
foreach ($tmp as $t) {
|
||||
// Watch out for associative vs non associative arrays, we want each indicator to be assigned enclosed in a numerical array instead of just merging all of its associative values into the AND array
|
||||
if ($this->__isAssoc($t)) {
|
||||
$result[] = is_array($t) ?
|
||||
array_merge(array($v), array($t)) :
|
||||
array($v, $t);
|
||||
} else {
|
||||
$result[] = is_array($t) ?
|
||||
array_merge(array($v), $t) :
|
||||
array($v, $t);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function __resolveAndBranch($array, $id)
|
||||
{
|
||||
// Let's see how many indicators we have left and take action accordingly
|
||||
$count = count($array);
|
||||
if ($count == 0) {
|
||||
return null;
|
||||
} elseif ($count == 1) {
|
||||
return $array[0];
|
||||
} else {
|
||||
$att = array();
|
||||
for ($i=0; $i < $count; $i++) {
|
||||
$att[$i] = $this->__analyseIndicator($array[$i]);
|
||||
}
|
||||
$attempt = $this->__convertToCompositeAttribute($att, $id);
|
||||
if ($attempt) {
|
||||
$attempt['uuid'] = $att[0]['uuid'];
|
||||
$this->saved_uuids[] = $id;
|
||||
foreach ($att as $temp) {
|
||||
$this->saved_uuids[] = $temp['uuid'];
|
||||
}
|
||||
return $attempt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// We have a list of attributes that we can omit in nested logical branches - the idea is to always make sure that we don't insert attributes
|
||||
// that in the source were specified to be taken together with another indicator only.
|
||||
private function __checkOmit($leaf)
|
||||
{
|
||||
foreach ($this->discardableIndicators as $current) {
|
||||
// check if search is set - if not, it's already a composite attribute
|
||||
if (isset($leaf['search']) && $leaf['search'] === $current) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempt to convert the two attributes retrieved from an AND indicator into a single attribute, if they are eligible to be converted. If not, add it to the array of failures.
|
||||
private function __convertToCompositeAttribute($att, $uuid)
|
||||
{
|
||||
// check if the current attribute is one of the known pairs saved in the array $attributePairs
|
||||
$tempArray = $values = $uuids = array();
|
||||
foreach ($att as $temp) {
|
||||
$tempArray[$temp['type']] = $temp;
|
||||
}
|
||||
ksort($tempArray);
|
||||
$keys = array_keys($tempArray);
|
||||
$att = array_values($tempArray);
|
||||
foreach ($att as $temp) {
|
||||
$values[] = $temp['value'];
|
||||
$uuids[] = $temp['uuid'];
|
||||
}
|
||||
|
||||
foreach ($this->attributeComposition as $composition) {
|
||||
if (count($composition['components']) != count($att)) {
|
||||
continue;
|
||||
}
|
||||
if ($keys === $composition['components']) {
|
||||
$value = $composition['replace'];
|
||||
foreach ($values as $k => $v) {
|
||||
$value = str_replace('$' . $k, $v, $value);
|
||||
}
|
||||
return array(
|
||||
'type' => $composition['returnFormat'],
|
||||
'category' => $composition['returnCategory'],
|
||||
'value' => $value,
|
||||
'to_ids' => $this->typeToIdsSettings[$composition['returnFormat']],
|
||||
'distribution' => $this->distribution,
|
||||
'comment' => 'OpenIOC import from file ' . $this->filename . ' - Original UUIDs:' . PHP_EOL . implode(PHP_EOL, $uuids),
|
||||
);
|
||||
}
|
||||
}
|
||||
// If no match found, return false, it's not a valid composite attribute for MISP
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Get filter parameters from index searches
|
||||
*/
|
||||
|
||||
class IndexFilterComponent extends Component
|
||||
{
|
||||
/** @var Controller */
|
||||
public $Controller;
|
||||
public $isRest = null;
|
||||
|
||||
// Used for isApiFunction(), 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
|
||||
const AUTOMATION_ARRAY = array(
|
||||
'events' => array('csv', 'nids', 'hids', 'xml', 'restSearch', 'stix', 'updateGraph', 'downloadOpenIOCEvent'),
|
||||
'attributes' => array('text', 'downloadAttachment', 'returnAttributes', 'restSearch', 'rpz', 'bro'),
|
||||
'objects' => array('restSearch'),
|
||||
);
|
||||
|
||||
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 = [])
|
||||
{
|
||||
$request = $this->Controller->request;
|
||||
$data = [];
|
||||
if ($request->is('post')) {
|
||||
if (empty($request->data)) {
|
||||
$exception = $this->Controller->RestResponse->throwException(
|
||||
400,
|
||||
__('Either specify the search terms in the url, or POST a json with the filter parameters.'),
|
||||
'/' . $request->params['controller'] . '/' . $this->Controller->action
|
||||
);
|
||||
return false;
|
||||
} else {
|
||||
if (isset($request->data['request'])) {
|
||||
$data = $request->data['request'];
|
||||
} else {
|
||||
$data = $request->data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$data = $this->__massageData($data, $request, $paramArray);
|
||||
|
||||
$this->Controller->set('passedArgs', json_encode($this->Controller->passedArgs));
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function __massageData($data, $request, $paramArray)
|
||||
{
|
||||
$data = array_filter($data, function($paramName) use ($paramArray) {
|
||||
return !empty($paramArray[$paramName]);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
|
||||
if (!empty($paramArray)) {
|
||||
foreach ($paramArray as $p) {
|
||||
if (isset($request->params['named'][$p])) {
|
||||
$data[$p] = str_replace(';', ':', $request->params['named'][$p]);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($data as &$v) {
|
||||
if (is_string($v)) {
|
||||
$v = trim($v);
|
||||
if (strpos($v, '||')) {
|
||||
$v = explode('||', $v);
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($v);
|
||||
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->isJson() || $this->Controller->RequestHandler->isXml() || $this->isCsv())) {
|
||||
$this->isRest = true;
|
||||
return true;
|
||||
} else {
|
||||
$this->isRest = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function isJson()
|
||||
{
|
||||
return $this->Controller->request->header('Accept') === 'application/json' || $this->Controller->RequestHandler->prefers() === 'json';
|
||||
}
|
||||
|
||||
public function isCsv()
|
||||
{
|
||||
return $this->Controller->request->header('Accept') === 'text/csv' || $this->Controller->RequestHandler->prefers() === 'csv';
|
||||
}
|
||||
|
||||
public function isXml()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $controller
|
||||
* @param string $action
|
||||
* @return bool
|
||||
*/
|
||||
public function isApiFunction($controller, $action)
|
||||
{
|
||||
return isset(self::AUTOMATION_ARRAY[$controller]) && in_array($action, self::AUTOMATION_ARRAY[$controller], true);
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
<?php
|
||||
App::uses('Component', 'Controller');
|
||||
|
||||
class RateLimitComponent extends Component
|
||||
{
|
||||
const LIMITED_FUNCTIONS = array(
|
||||
'attributes' => array(
|
||||
'restSearch' => 1
|
||||
),
|
||||
'events' => array(
|
||||
'restSearch' => 1
|
||||
)
|
||||
);
|
||||
|
||||
public $components = array('RestResponse');
|
||||
|
||||
/**
|
||||
* @param array $user
|
||||
* @param string $controller
|
||||
* @param string $action
|
||||
* @param array $info
|
||||
* @param string $responseType
|
||||
* @return bool
|
||||
* @throws RedisException
|
||||
*/
|
||||
public function check(array $user, $controller, $action, &$info = array(), $responseType)
|
||||
{
|
||||
if (!empty($user['Role']['enforce_rate_limit']) && isset(self::LIMITED_FUNCTIONS[$controller][$action])) {
|
||||
if ($user['Role']['rate_limit_count'] == 0) {
|
||||
throw new MethodNotAllowedException(__('API searches are not allowed for this user role.'));
|
||||
}
|
||||
try {
|
||||
$redis = RedisTool::init();
|
||||
} catch (Exception $e) {
|
||||
return true; // redis is not available, allow access
|
||||
}
|
||||
$uuid = Configure::read('MISP.uuid') ?: 'no-uuid';
|
||||
$keyName = 'misp:' . $uuid . ':rate_limit:' . $user['id'];
|
||||
$count = $redis->get($keyName);
|
||||
if ($count !== false && $count >= $user['Role']['rate_limit_count']) {
|
||||
$info = array(
|
||||
'limit' => $user['Role']['rate_limit_count'],
|
||||
'reset' => $redis->ttl($keyName),
|
||||
'remaining' => $user['Role']['rate_limit_count'] - $count,
|
||||
);
|
||||
return $this->RestResponse->throwException(
|
||||
429,
|
||||
__('Rate limit exceeded.'),
|
||||
'/' . $controller . '/' . $action,
|
||||
$responseType
|
||||
);
|
||||
} else {
|
||||
if ($count === false) {
|
||||
$redis->setEx($keyName, 900, 1);
|
||||
} else {
|
||||
$redis->setEx($keyName, $redis->ttl($keyName), intval($count) + 1);
|
||||
}
|
||||
}
|
||||
$count += 1;
|
||||
$info = array(
|
||||
'limit' => $user['Role']['rate_limit_count'],
|
||||
'reset' => $redis->ttl($keyName),
|
||||
'remaining' => $user['Role']['rate_limit_count'] - $count
|
||||
);
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,58 +0,0 @@
|
|||
<?php
|
||||
|
||||
App::uses('Component', 'Controller');
|
||||
|
||||
class RestSearchComponent extends Component
|
||||
{
|
||||
//This array determines the order for ordered_url_params, as a result it is not advised to remove or change existing values
|
||||
public $paramArray = array(
|
||||
'Attribute' => array(
|
||||
//following parameters are not used for attributes anymore: attackGalaxy
|
||||
'returnFormat', 'value' , 'type', 'category', 'org', 'tags', 'from', 'to', 'last', 'eventid', 'withAttachments', 'uuid', 'publish_timestamp',
|
||||
'published', 'timestamp','enforceWarninglist', 'to_ids', 'deleted', 'includeEventUuid', 'event_timestamp', 'threat_level_id', 'includeEventTags',
|
||||
'includeProposals', 'returnFormat', 'limit', 'page', 'requested_attributes', 'includeContext', 'headerless',
|
||||
'includeWarninglistHits', 'attackGalaxy', 'object_relation', 'includeSightings', 'includeCorrelations', 'includeDecayScore',
|
||||
'decayingModel', 'excludeDecayed', 'modelOverrides', 'includeFullModel', 'score', 'attribute_timestamp', 'first_seen', 'last_seen',
|
||||
'threat_level_id'
|
||||
),
|
||||
'Event' => array(
|
||||
'returnFormat', 'value', 'type', 'category', 'org', 'tags', 'searchall', 'from', 'to', 'last', 'eventid', 'withAttachments',
|
||||
'metadata', 'uuid', 'publish_timestamp', 'timestamp', 'published', 'enforceWarninglist', 'sgReferenceOnly',
|
||||
'limit', 'page', 'requested_attributes', 'includeContext', 'headerless', 'includeWarninglistHits', 'attackGalaxy', 'to_ids', 'deleted',
|
||||
'excludeLocalTags', 'date', 'includeSightingdb', 'tag', 'object_relation', 'threat_level_id'
|
||||
),
|
||||
'Object' => array(
|
||||
'returnFormat', 'value' , 'type', 'category', 'org', 'tags', 'from', 'to', 'last', 'eventid', 'withAttachments', 'uuid', 'publish_timestamp',
|
||||
'published', 'timestamp','enforceWarninglist', 'to_ids', 'deleted', 'includeEventUuid', 'event_timestamp', 'threat_level_id', 'includeEventTags',
|
||||
'includeProposals', 'returnFormat', 'limit', 'page', 'requested_attributes', 'includeContext', 'headerless',
|
||||
'includeWarninglistHits', 'attackGalaxy', 'object_relation'
|
||||
),
|
||||
'Sighting' => array(
|
||||
'context', 'returnFormat', 'id', 'type', 'from', 'to', 'last', 'org_id', 'source', 'includeAttribute', 'includeEvent'
|
||||
),
|
||||
'GalaxyCluster' => array(
|
||||
'page', 'limit', 'id', 'uuid', 'galaxy_id', 'galaxy_uuid', 'version', 'distribution', 'org', 'orgc', 'tag', 'custom', 'sgReferenceOnly', 'minimal',
|
||||
)
|
||||
);
|
||||
|
||||
public function getFilename($filters, $scope, $responseType)
|
||||
{
|
||||
$filename = false;
|
||||
if ($scope === 'Event') {
|
||||
$filename = 'misp.event.';
|
||||
if (!empty($filters['eventid']) && !is_array($filters['eventid'])) {
|
||||
if (Validation::uuid(trim($filters['eventid']))) {
|
||||
$filename .= trim($filters['eventid']);
|
||||
} else if (!empty(intval(trim($filters['eventid'])))) {
|
||||
$filename .= intval(trim($filters['eventid']));
|
||||
}
|
||||
} else {
|
||||
$filename .= 'list';
|
||||
}
|
||||
}
|
||||
if ($filename !== false) {
|
||||
$filename .= '.' . $responseType;
|
||||
}
|
||||
return $filename;
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
|
||||
class ToolboxComponent extends Component
|
||||
{
|
||||
public function findIdByUuid($model, $id, $allowEmpty = false) {
|
||||
if (empty($id) && $allowEmpty) {
|
||||
return $id;
|
||||
}
|
||||
if (Validation::uuid($id)) {
|
||||
$data = $model->find('first', array(
|
||||
'conditions' => array($model->alias . '.uuid' => $id),
|
||||
'recursive' => -1,
|
||||
'fields' => array($model->alias . '.id')
|
||||
));
|
||||
if (empty($data)) {
|
||||
throw new NotFoundException(__('Invalid %s.', $model->alias));
|
||||
}
|
||||
return $data[$model->alias]['id'];
|
||||
} else {
|
||||
if (!is_numeric($id)) {
|
||||
throw new NotFoundException(__('Invalid %s.', $model->alias));
|
||||
}
|
||||
$data = $model->find('first', array(
|
||||
'conditions' => array($model->alias . '.id' => $id),
|
||||
'recursive' => -1,
|
||||
'fields' => array($model->alias . '.id')
|
||||
));
|
||||
if (empty($data)) {
|
||||
throw new NotFoundException(__('Invalid %s.', $model->alias));
|
||||
} else {
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property CorrelationExclusion $CorrelationExclusion
|
||||
*/
|
||||
class CorrelationExclusionsController extends AppController
|
||||
{
|
||||
public $components = array(
|
||||
'CRUD',
|
||||
'RequestHandler'
|
||||
);
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 60,
|
||||
'order' => array(
|
||||
'CorrelationExclusion.value' => 'ASC',
|
||||
)
|
||||
);
|
||||
|
||||
public function index($id = false)
|
||||
{
|
||||
$this->CRUD->index([
|
||||
'filters' => ['value', 'comment'],
|
||||
'quickFilters' => ['value', 'comment']
|
||||
]);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->set('title_for_layout', __('Correlation Exclusions index'));
|
||||
$this->set('menuData', [
|
||||
'menuList' => 'correlationExclusions',
|
||||
'menuItem' => 'index'
|
||||
]);
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
{
|
||||
$this->CRUD->delete($id);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
public function add()
|
||||
{
|
||||
$options = [
|
||||
'filters' => ['value', 'redirect', 'redirect_controller', 'comment']
|
||||
];
|
||||
$params = $this->IndexFilter->harvestParameters($options['filters']);
|
||||
if (!empty($params['value'])) {
|
||||
$this->request->data['CorrelationExclusion']['value'] = $params['value'];
|
||||
}
|
||||
if (!empty($params['comment'])) {
|
||||
$this->request->data['CorrelationExclusion']['comment'] = $params['comment'];
|
||||
}
|
||||
$this->CRUD->add($params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$dropdownData = [];
|
||||
$this->set(compact('dropdownData'));
|
||||
$this->set('menuData', [
|
||||
'menuList' => 'correlationExclusions',
|
||||
'menuItem' => 'add',
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$this->set('menuData', [
|
||||
'menuList' => 'correlationExclusions',
|
||||
'menuItem' => 'edit',
|
||||
]);
|
||||
$this->set('id', $id);
|
||||
$params = [
|
||||
'fields' => ['comment']
|
||||
];
|
||||
$this->CRUD->edit($id, $params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
|
||||
$this->loadModel('Organisation');
|
||||
$orgs = $this->Organisation->find('list', [
|
||||
'recursive' => -1,
|
||||
'fields' => ['id', 'name'],
|
||||
'order' => ['lower(name)' => 'ASC']
|
||||
]);
|
||||
$dropdownData = [
|
||||
'org_id' => $orgs
|
||||
];
|
||||
$this->set(compact('dropdownData'));
|
||||
$this->render('add');
|
||||
}
|
||||
|
||||
public function view($id = false)
|
||||
{
|
||||
$this->CRUD->view($id);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
|
||||
$this->set('title_for_layout', __('Correlation Exclusion'));
|
||||
$this->set('menuData', [
|
||||
'menuList' => 'correlationExclusions',
|
||||
'menuItem' => 'view',
|
||||
]);
|
||||
}
|
||||
|
||||
public function clean()
|
||||
{
|
||||
if ($this->request->is('post')) {
|
||||
$this->CorrelationExclusion->cleanRouter($this->Auth->user());
|
||||
$message = __('Correlations cleanup initiated, based on the exclusion rules.');
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('CorrelationExclusion', 'clean', false, false, $message);
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect($this->referer());
|
||||
}
|
||||
} else {
|
||||
$this->set('title', __('Clean up correlations'));
|
||||
$this->set('question', __('Execute the cleaning of all correlations that are at odds with the exclusion rules? This will delete all matching correlations.'));
|
||||
$this->set('actionName', __('Clean'));
|
||||
$this->layout = false;
|
||||
$this->render('/genericTemplates/confirm');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,224 +0,0 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property Correlation $Correlation
|
||||
*/
|
||||
class CorrelationsController extends AppController
|
||||
{
|
||||
public $components = array('RequestHandler');
|
||||
|
||||
public function top()
|
||||
{
|
||||
$query = [
|
||||
'limit' => 50,
|
||||
'page' => 1
|
||||
];
|
||||
if (!empty($this->params['named']['limit'])) {
|
||||
$query['limit'] = $this->params['named']['limit'];
|
||||
}
|
||||
if (!empty($this->params['named']['page'])) {
|
||||
$query['page'] = $this->params['named']['page'];
|
||||
}
|
||||
if ($this->_isRest()) {
|
||||
$data = $this->Correlation->findTop($query);
|
||||
return $this->RestResponse->viewData($data, 'json');
|
||||
} else {
|
||||
$data = $this->Correlation->findTop($query);
|
||||
$age = $this->Correlation->getTopTime();
|
||||
$age = time() - $age;
|
||||
$unit = 's';
|
||||
if ($age >= 60) {
|
||||
$age = ceil($age / 60);
|
||||
$unit = 'm';
|
||||
if ($age >= 60) {
|
||||
$age = ceil($age / 60);
|
||||
$unit = 'h';
|
||||
if ($age >= 24) {
|
||||
$age = ceil($age / 24);
|
||||
$unit = 'd';
|
||||
if ($age >= 365) {
|
||||
$age = ceil($age / 365);
|
||||
$unit = 'y';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->__setPagingParams($query['page'], $query['limit'], count($data), 'named');
|
||||
|
||||
$this->set('age', $age);
|
||||
$this->set('age_unit', $unit);
|
||||
$this->set('data', $data);
|
||||
$this->set('title_for_layout', __('Top correlations index'));
|
||||
$this->set('menuData', [
|
||||
'menuList' => 'correlationExclusions',
|
||||
'menuItem' => 'top'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function generateTopCorrelations()
|
||||
{
|
||||
$result = $this->Correlation->generateTopCorrelationsRouter();
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($result, 'json');
|
||||
} else {
|
||||
if ($result === false) {
|
||||
$message = __('No correlations found. Nothing to rank.');
|
||||
} else if ($result === true) {
|
||||
$message = __('Top correlation list regenerated.');
|
||||
} else {
|
||||
$message = __('Top correlation list generation queued for background processing. Job ID: %s.', $result);
|
||||
}
|
||||
$this->Flash->success($message);
|
||||
$this->redirect(['controller' => 'correlations', 'action' => 'top']);
|
||||
}
|
||||
}
|
||||
|
||||
public function overCorrelations()
|
||||
{
|
||||
$query = [
|
||||
'limit' => 50,
|
||||
'page' => 1,
|
||||
'order' => 'occurrence desc'
|
||||
];
|
||||
foreach ($query as $customParam => $foo) {
|
||||
if (isset($this->request->params['named'][$customParam])) {
|
||||
$query[$customParam] = $this->request->params['named'][$customParam];
|
||||
}
|
||||
}
|
||||
if (isset($this->request->params['named']['scope'])) {
|
||||
$limit = $this->Correlation->OverCorrelatingValue->getLimit();
|
||||
if ($this->request->params['named']['scope'] === 'over_correlating') {
|
||||
$scope = 'over_correlating';
|
||||
$query['conditions'][] = ['occurrence >=' => $limit];
|
||||
} else if ($this->request->params['named']['scope'] === 'not_over_correlating') {
|
||||
$query['conditions'][] = ['occurrence <' => $limit];
|
||||
$scope = 'not_over_correlating';
|
||||
}
|
||||
} else {
|
||||
$scope = 'all';
|
||||
}
|
||||
$data = $this->Correlation->OverCorrelatingValue->getOverCorrelations($query);
|
||||
$data = $this->Correlation->attachExclusionsToOverCorrelations($data);
|
||||
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($data, 'json');
|
||||
}
|
||||
|
||||
$this->__setPagingParams($query['page'], $query['limit'], count($data), 'named');
|
||||
$this->set('data', $data);
|
||||
$this->set('scope', $scope);
|
||||
$this->set('title_for_layout', __('Index of over correlating values'));
|
||||
$this->set('menuData', [
|
||||
'menuList' => 'correlationExclusions',
|
||||
'menuItem' => 'over'
|
||||
]);
|
||||
}
|
||||
|
||||
public function switchEngine(string $engine)
|
||||
{
|
||||
$this->loadModel('Server');
|
||||
if (!isset($this->Correlation->validEngines[$engine])) {
|
||||
throw new MethodNotAllowedException(__('Not a valid engine choice. Please make sure you pass one of the following: ', implode(', ', array_keys($this->Correlation->validEngines))));
|
||||
}
|
||||
if ($this->request->is('post')) {
|
||||
$setting = $this->Server->getSettingData('MISP.correlation_engine');
|
||||
$result = $this->Server->serverSettingsEditValue($this->Auth->user(), $setting, $engine);
|
||||
if ($result === true) {
|
||||
$message = __('Engine switched.');
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('Correlations', 'switchEngine', false, $this->response->type(), $message);
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect(['controller' => 'servers', 'action' => 'serverSettings', 'correlations']);
|
||||
}
|
||||
} else {
|
||||
$message = __('Couldn\'t switch to the requested engine.');
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('Correlations', 'switchEngine', false, $message, $this->response->type());
|
||||
} else {
|
||||
$this->Flash->error($message);
|
||||
$this->redirect(['controller' => 'servers', 'action' => 'serverSettings', 'correlations']);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->set('engine', $engine);
|
||||
$this->render('ajax/switch_engine_confirmation');
|
||||
}
|
||||
}
|
||||
|
||||
public function truncate(string $engine)
|
||||
{
|
||||
if (!isset($this->Correlation->validEngines[$engine])) {
|
||||
throw new MethodNotAllowedException(__('Not a valid engine choice. Please make sure you pass one of the following: ', implode(', ', array_keys($this->Correlation->validEngines))));
|
||||
}
|
||||
if ($this->request->is('post')) {
|
||||
if (!Configure::read('MISP.background_jobs')) {
|
||||
$result = $this->Correlation->truncate($this->Auth->user(), $engine);
|
||||
$message = $result ? __('Table truncated.') : __('Could not truncate table');
|
||||
if ($this->_isRest()) {
|
||||
if ($result) {
|
||||
$this->RestResponse->saveSuccessResponse('Correlations', 'truncate', false, $this->response->type(), $message);
|
||||
} else {
|
||||
$this->RestResponse->saveFailResponse('Correlations', 'truncate', false, $message, $this->response->type());
|
||||
}
|
||||
} else {
|
||||
$this->Flash->{$result ? 'success' : 'error'}($message);
|
||||
$this->redirect(['controller' => 'servers', 'action' => 'serverSettings', 'correlations']);
|
||||
}
|
||||
} else {
|
||||
$job = ClassRegistry::init('Job');
|
||||
$jobId = $job->createJob(
|
||||
'SYSTEM',
|
||||
Job::WORKER_DEFAULT,
|
||||
'truncate table',
|
||||
$this->Correlation->validEngines[$engine],
|
||||
'Job created.'
|
||||
);
|
||||
|
||||
$this->Correlation->Attribute->getBackgroundJobsTool()->enqueue(
|
||||
BackgroundJobsTool::DEFAULT_QUEUE,
|
||||
BackgroundJobsTool::CMD_ADMIN,
|
||||
[
|
||||
'truncateTable',
|
||||
$this->Auth->user('id'),
|
||||
$engine,
|
||||
$jobId
|
||||
],
|
||||
true,
|
||||
$jobId
|
||||
);
|
||||
|
||||
$message = __('Job queued. You can view the progress if you navigate to the active jobs view (Administration -> Jobs).');
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('Correlations', 'truncate', false, $this->response->type(), $message);
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect(['controller' => 'servers', 'action' => 'serverSettings', 'correlations']);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->set('engine', $engine);
|
||||
$this->set('table_name', $this->Correlation->validEngines[$engine]);
|
||||
$this->render('ajax/truncate_confirmation');
|
||||
}
|
||||
}
|
||||
|
||||
public function generateOccurrences()
|
||||
{
|
||||
$this->loadModel('OverCorrelatingValue');
|
||||
$this->OverCorrelatingValue->generateOccurrencesRouter();
|
||||
if (Configure::read('MISP.background_jobs')) {
|
||||
$message = __('Job queued.');
|
||||
} else {
|
||||
$message = __('Over-correlations counted successfully.');
|
||||
}
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('Correlations', 'generateOccurrences', false, $this->response->type(), $message);
|
||||
}
|
||||
$this->Flash->info($message);
|
||||
$this->redirect(['controller' => 'correlations', 'action' => 'overCorrelations']);
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property CryptographicKey $CryptographicKey
|
||||
*/
|
||||
class CryptographicKeysController extends AppController
|
||||
{
|
||||
public $components = array('Session', 'RequestHandler');
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 60,
|
||||
'maxLimit' => 9999
|
||||
);
|
||||
|
||||
public function add($type, $parent_id)
|
||||
{
|
||||
if (empty($type) || empty($parent_id)) {
|
||||
throw new MethodNotAllowedException(__('No type and/or parent_id supplied.'));
|
||||
}
|
||||
if ($type === 'Event') {
|
||||
$existingEvent = $this->CryptographicKey->Event->fetchSimpleEvent(
|
||||
$this->Auth->user(),
|
||||
$parent_id,
|
||||
[
|
||||
'conditions' => [
|
||||
'Event.orgc_id' => $this->Auth->user('org_id')
|
||||
]
|
||||
]
|
||||
);
|
||||
if (empty($existingEvent)) {
|
||||
throw new MethodNotAllowedException(__('Invalid Event.'));
|
||||
}
|
||||
}
|
||||
$params = [
|
||||
'beforeSave' => function ($data) use($type, $parent_id) {
|
||||
$data['CryptographicKey']['parent_type'] = $type;
|
||||
$data['CryptographicKey']['parent_id'] = $parent_id;
|
||||
return $data;
|
||||
},
|
||||
'redirect' => [
|
||||
'controller' => Inflector::tableize($type),
|
||||
'action' => 'view',
|
||||
$parent_id
|
||||
]
|
||||
];
|
||||
$this->CRUD->add($params);
|
||||
if ($this->restResponsePayload) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$instanceKey = file_exists(APP . 'webroot/gpg.asc') ? FileAccessTool::readFromFile(APP . 'webroot/gpg.asc') : '';
|
||||
$this->set('instanceKey', $instanceKey);
|
||||
$this->set('menuData', array('menuList' => 'cryptographic_keys', 'menuItem' => 'add_cryptographic_key'));
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
{
|
||||
$user = $this->Auth->user();
|
||||
$this->CRUD->delete($id, [
|
||||
'beforeDelete' => function ($data) use($user) {
|
||||
$parent_type = $data['CryptographicKey']['parent_type'];
|
||||
$tempModel = ClassRegistry::init($parent_type);
|
||||
$existingData = $tempModel->find('first', [
|
||||
'conditions' => [
|
||||
$parent_type . '.id' => $data['CryptographicKey']['parent_id']
|
||||
],
|
||||
'recursive' => -1
|
||||
]);
|
||||
if ($parent_type === 'Event') {
|
||||
if (!$user['Role']['perm_site_admin'] && $existingData['Event']['orgc_id'] !== $user['org_id']) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
]);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
public function view($id)
|
||||
{
|
||||
$key = $this->CryptographicKey->find('first', [
|
||||
'recursive' => -1,
|
||||
'fields' => ['id', 'type', 'key_data', 'fingerprint'],
|
||||
'conditions' => ['CryptographicKey.id' => $id]
|
||||
]);
|
||||
$this->set('id', $id);
|
||||
$this->set('title', __('Viewing %s key #%s', h($key['CryptographicKey']['type']), h($key['CryptographicKey']['id'])));
|
||||
$this->set(
|
||||
'html',
|
||||
sprintf(
|
||||
'<span class="quickSelect">%s</span>',
|
||||
nl2br(h($key['CryptographicKey']['key_data']))
|
||||
)
|
||||
);
|
||||
$this->layout = false;
|
||||
$this->render('/genericTemplates/display');
|
||||
}
|
||||
}
|
|
@ -1,433 +0,0 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property Dashboard $Dashboard
|
||||
*/
|
||||
class DashboardsController extends AppController
|
||||
{
|
||||
public $components = array('Session', 'RequestHandler');
|
||||
public $helpers = array('ScopedCSS');
|
||||
|
||||
public function beforeFilter()
|
||||
{
|
||||
parent::beforeFilter();
|
||||
$this->Security->unlockedActions[] = 'renderWidget';
|
||||
$this->Security->unlockedActions[] = 'getForm';
|
||||
if ($this->request->action === 'renderWidget') {
|
||||
$this->Security->doNotGenerateToken = true;
|
||||
}
|
||||
}
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 60,
|
||||
'maxLimit' => 9999
|
||||
);
|
||||
|
||||
public function index($template_id = false)
|
||||
{
|
||||
if (empty($template_id)) {
|
||||
$params = array(
|
||||
'conditions' => array(
|
||||
'UserSetting.user_id' => $this->Auth->user('id'),
|
||||
'UserSetting.setting' => 'dashboard'
|
||||
)
|
||||
);
|
||||
$userSettings = $this->User->UserSetting->find('first', $params);
|
||||
} else {
|
||||
$dashboardTemplate = $this->Dashboard->getDashboardTemplate($this->Auth->user(), $template_id);
|
||||
if (empty($dashboardTemplate)) {
|
||||
throw new NotFoundException(__('Invalid dashboard template.'));
|
||||
}
|
||||
}
|
||||
if (empty($userSettings) && empty($dashboardTemplate)) {
|
||||
$dashboardTemplate = $this->Dashboard->getDashboardTemplate($this->Auth->user());
|
||||
}
|
||||
if (empty($userSettings)) {
|
||||
if (empty($dashboardTemplate)) {
|
||||
$value = array(
|
||||
array(
|
||||
'widget' => 'MispStatusWidget',
|
||||
'config' => array(
|
||||
),
|
||||
'position' => array(
|
||||
'x' => 0,
|
||||
'y' => 0,
|
||||
'width' => 2,
|
||||
'height' => 2
|
||||
)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$value = $dashboardTemplate['Dashboard']['value'];
|
||||
if (!is_array($value)) {
|
||||
$value = json_decode($value, true);
|
||||
}
|
||||
}
|
||||
$userSettings = array(
|
||||
'UserSetting' => array(
|
||||
'setting' => 'dashboard',
|
||||
'value' => $value
|
||||
)
|
||||
);
|
||||
}
|
||||
$widgets = array();
|
||||
foreach ($userSettings['UserSetting']['value'] as $widget) {
|
||||
try {
|
||||
$dashboardWidget = $this->Dashboard->loadWidget($this->Auth->user(), $widget['widget']);
|
||||
$widget['width'] = $dashboardWidget->width;
|
||||
$widget['height'] = $dashboardWidget->height;
|
||||
$widget['title'] = $dashboardWidget->title;
|
||||
$widgets[] = $widget;
|
||||
} catch (Exception $e) {
|
||||
// continue, we just don't load the widget
|
||||
}
|
||||
}
|
||||
$this->set('widgets', $widgets);
|
||||
}
|
||||
|
||||
public function getForm($action = 'edit')
|
||||
{
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$data = $this->request->data;
|
||||
if ($action === 'edit' && !isset($data['widget'])) {
|
||||
throw new InvalidArgumentException(__('No widget name passed.'));
|
||||
}
|
||||
if (empty($data['config'])) {
|
||||
$data['config'] = '';
|
||||
}
|
||||
if ($action === 'add') {
|
||||
$data['widget_options'] = $this->Dashboard->loadAllWidgets($this->Auth->user());
|
||||
} else {
|
||||
$dashboardWidget = $this->Dashboard->loadWidget($this->Auth->user(), $data['widget']);
|
||||
$data['description'] = empty($dashboardWidget->description) ? '' : $dashboardWidget->description;
|
||||
$data['params'] = empty($dashboardWidget->params) ? array() : $dashboardWidget->params;
|
||||
$data['params'] = array_merge($data['params'], array('widget_config' => __('Configuration of the widget that will be passed to the render. Check the view for more information')));
|
||||
$data['params'] = array_merge(array('alias' => __('Alias to use as the title of the widget')), $data['params']);
|
||||
}
|
||||
$this->set('data', $data);
|
||||
$this->layout = false;
|
||||
$this->render($action);
|
||||
}
|
||||
}
|
||||
|
||||
public function updateSettings()
|
||||
{
|
||||
if ($this->request->is('post')) {
|
||||
if (!isset($this->request->data['Dashboard']['value'])) {
|
||||
throw new InvalidArgumentException(__('No setting data found.'));
|
||||
}
|
||||
$data = array(
|
||||
'UserSetting' => array(
|
||||
'setting' => 'dashboard',
|
||||
'value' => $this->request->data['Dashboard']['value']
|
||||
)
|
||||
);
|
||||
$result = $this->User->UserSetting->setSetting($this->Auth->user(), $data);
|
||||
if ($result) {
|
||||
return $this->RestResponse->saveSuccessResponse('Dashboard', 'updateSettings', false, false, __('Settings updated.'));
|
||||
}
|
||||
return $this->RestResponse->saveFailResponse('Dashboard', 'updateSettings', false, $this->User->UserSetting->validationErrors, $this->response->type());
|
||||
}
|
||||
}
|
||||
|
||||
public function getEmptyWidget($widget, $k = 1)
|
||||
{
|
||||
$dashboardWidget = $this->Dashboard->loadWidget($this->Auth->user(), $widget);
|
||||
if (empty($dashboardWidget)) {
|
||||
throw new NotFoundException(__('Invalid widget.'));
|
||||
}
|
||||
$this->layout = false;
|
||||
$widget = array(
|
||||
'config' => isset($dashboardWidget->config) ? $dashboardWidget->height : '',
|
||||
'title' => $dashboardWidget->title,
|
||||
'alias' => isset($dashboardWidget->alias) ? $dashboardWidget->alias : $dashboardWidget->title,
|
||||
'widget' => $widget
|
||||
);
|
||||
$this->set('k', $k);
|
||||
$this->set('widget', $widget);
|
||||
}
|
||||
|
||||
public function renderWidget($widget_id, $force = false)
|
||||
{
|
||||
$user = $this->_closeSession();
|
||||
|
||||
if (!$this->request->is('post')) {
|
||||
throw new MethodNotAllowedException(__('This endpoint can only be reached via POST requests.'));
|
||||
}
|
||||
|
||||
if (empty($this->request->data['data'])) {
|
||||
$this->request->data = array('data' => $this->request->data);
|
||||
}
|
||||
if (empty($this->request->data['data'])) {
|
||||
throw new MethodNotAllowedException(__('You need to specify the widget to use along with the configuration.'));
|
||||
}
|
||||
$value = $this->request->data['data'];
|
||||
$valueConfig = $this->_jsonDecode($value['config']);
|
||||
$dashboardWidget = $this->Dashboard->loadWidget($user, $value['widget']);
|
||||
|
||||
$cacheLifetime = $dashboardWidget->cacheLifetime ?? false;
|
||||
if ($cacheLifetime !== false) {
|
||||
$orgScope = $this->_isSiteAdmin() ? 0 : $user['org_id'];
|
||||
$lookupHash = sha1($value['widget'] . $value['config'], true);
|
||||
$cacheKey = "misp:dashboard:$orgScope:$lookupHash";
|
||||
|
||||
$redis = RedisTool::init();
|
||||
$data = $redis->get($cacheKey);
|
||||
if (!empty($data)) {
|
||||
$data = RedisTool::deserialize($data);
|
||||
} else {
|
||||
$data = $dashboardWidget->handler($user, $valueConfig);
|
||||
$redis->setex($cacheKey, $cacheLifetime, RedisTool::serialize($data));
|
||||
}
|
||||
} else {
|
||||
$data = $dashboardWidget->handler($user, $valueConfig);
|
||||
}
|
||||
|
||||
$config = array(
|
||||
'render' => $dashboardWidget->render,
|
||||
'autoRefreshDelay' => empty($dashboardWidget->autoRefreshDelay) ? false : $dashboardWidget->autoRefreshDelay,
|
||||
'widget_config' => empty($valueConfig['widget_config']) ? array() : $valueConfig['widget_config']
|
||||
);
|
||||
|
||||
$this->layout = false;
|
||||
$this->set('title', $dashboardWidget->title);
|
||||
$this->set('widget_id', $widget_id);
|
||||
$this->set('data', $data);
|
||||
$this->set('config', $config);
|
||||
$this->render('widget_loader');
|
||||
}
|
||||
|
||||
public function import()
|
||||
{
|
||||
if ($this->request->is('post')) {
|
||||
if (!empty($this->request->data['Dashboard'])) {
|
||||
$this->request->data = json_decode($this->request->data['Dashboard']['value'], true);
|
||||
}
|
||||
if (!empty($this->request->data['UserSetting'])) {
|
||||
$this->request->data = $this->request->data['UserSetting']['value'];
|
||||
}
|
||||
$result = $this->Dashboard->import($this->Auth->user(), $this->request->data);
|
||||
if ($this->_isRest()) {
|
||||
if ($result) {
|
||||
return $this->RestResponse->saveSuccessResponse('Dashboard', 'import', false, false, __('Settings updated.'));
|
||||
}
|
||||
return $this->RestResponse->saveFailResponse('Dashboard', 'import', false, __('Settings could not be updated.'), $this->response->type());
|
||||
} else {
|
||||
if ($result) {
|
||||
$this->Flash->success(__('Settings updated.'));
|
||||
} else {
|
||||
$this->Flash->error(__('Settings could not be updated.'));
|
||||
}
|
||||
$this->redirect($this->baseurl . '/dashboards');
|
||||
}
|
||||
}
|
||||
$this->layout = false;
|
||||
}
|
||||
|
||||
public function export()
|
||||
{
|
||||
$data = $this->Dashboard->export($this->Auth->user());
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($data, $this->response->type());
|
||||
} else {
|
||||
$this->set('data', $data);
|
||||
$this->layout = false;
|
||||
}
|
||||
}
|
||||
|
||||
public function saveTemplate($update = false)
|
||||
{
|
||||
if (!empty($update)) {
|
||||
$conditions = array('Dashboard.id' => $update);
|
||||
if (Validation::uuid($update)) {
|
||||
$conditions = array('Dashboard.uuid' => $update);
|
||||
}
|
||||
$existingDashboard = $this->Dashboard->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => $conditions
|
||||
));
|
||||
if (
|
||||
empty($existingDashboard) ||
|
||||
(!$this->_isSiteAdmin() && $existingDashboard['Dashboard']['user_id'] != $this->Auth->user('id'))
|
||||
) {
|
||||
throw new NotFoundException(__('Invalid dashboard template.'));
|
||||
}
|
||||
}
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
if (isset($this->request->data['Dashboard'])) {
|
||||
$this->request->data = $this->request->data['Dashboard'];
|
||||
}
|
||||
$data = $this->request->data;
|
||||
if (empty($update)) { // save the template stored in user setting and make it persistent
|
||||
$data['value'] = $this->User->UserSetting->getSetting($this->Auth->user('id'), 'dashboard');
|
||||
}
|
||||
$result = $this->Dashboard->saveDashboardTemplate($this->Auth->user(), $data, $update);
|
||||
if ($this->_isRest()) {
|
||||
if ($result) {
|
||||
return $this->RestResponse->saveSuccessResponse('Dashboard', 'saveDashboardTemplate', false, false, __('Dashboard template updated.'));
|
||||
}
|
||||
return $this->RestResponse->saveFailResponse('Dashboard', 'import', false, __('Dashboard template could not be updated.'), $this->response->type());
|
||||
} else {
|
||||
if ($result) {
|
||||
$this->Flash->success(__('Dashboard template updated.'));
|
||||
} else {
|
||||
$this->Flash->error(__('Dashboard template could not be updated.'));
|
||||
}
|
||||
$this->redirect($this->baseurl . '/dashboards/listTemplates');
|
||||
}
|
||||
} else {
|
||||
$this->layout = false;
|
||||
}
|
||||
$permFlags = array(0 => __('Unrestricted'));
|
||||
foreach ($this->User->Role->permFlags as $perm_flag => $perm_data) {
|
||||
$permFlags[$perm_flag] = $perm_data['text'];
|
||||
}
|
||||
$options = array(
|
||||
'org_id' => (
|
||||
array(
|
||||
0 => __('Unrestricted')
|
||||
) + // avoid re-indexing
|
||||
$this->User->Organisation->find('list', array(
|
||||
'fields' => array(
|
||||
'Organisation.id', 'Organisation.name'
|
||||
),
|
||||
'conditions' => array('Organisation.local' => 1)
|
||||
))
|
||||
),
|
||||
'role_id' => (
|
||||
array(
|
||||
0 => __('Unrestricted')
|
||||
) + // avoid re-indexing
|
||||
$this->User->Role->find('list', array(
|
||||
'fields' => array(
|
||||
'Role.id', 'Role.name'
|
||||
)
|
||||
))
|
||||
),
|
||||
'role_perms' => $permFlags
|
||||
);
|
||||
if (!empty($update)) {
|
||||
$this->request->data = $existingDashboard;
|
||||
}
|
||||
$this->set('options', $options);
|
||||
}
|
||||
|
||||
public function listTemplates()
|
||||
{
|
||||
$conditions = array();
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
$permission_flags = array();
|
||||
foreach ($this->Auth->user('Role') as $perm => $value) {
|
||||
if (strpos($perm, 'perm_') !== false && !empty($value)) {
|
||||
$permission_flags[] = $perm;
|
||||
}
|
||||
}
|
||||
$conditions['AND'] = array(
|
||||
array(
|
||||
'OR' => array(
|
||||
'Dashboard.user_id' => $this->Auth->user('id'),
|
||||
'AND' => array(
|
||||
'Dashboard.selectable' => 1,
|
||||
array(
|
||||
'OR' => array(
|
||||
array('Dashboard.restrict_to_org_id' => $this->Auth->user('org_id')),
|
||||
array('Dashboard.restrict_to_org_id' => 0)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'OR' => array(
|
||||
array('Dashboard.restrict_to_role_id' => $this->Auth->user('role_id')),
|
||||
array('Dashboard.restrict_to_role_id' => 0)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'OR' => array(
|
||||
array('Dashboard.restrict_to_permission_flag' => $permission_flags),
|
||||
array('Dashboard.restrict_to_permission_flag' => 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
if (!empty($this->passedArgs['value'])) {
|
||||
$conditions['AND'][] = array(
|
||||
'OR' => array(
|
||||
'LOWER(Dashboard.name) LIKE' => '%' . strtolower(trim($this->passedArgs['value'])) . '%',
|
||||
'LOWER(Dashboard.description) LIKE' => '%' . strtolower(trim($this->passedArgs['value'])) . '%',
|
||||
'LOWER(Dashboard.uuid) LIKE' => strtolower(trim($this->passedArgs['value']))
|
||||
)
|
||||
);
|
||||
}
|
||||
$this->paginate['conditions'] = $conditions;
|
||||
if ($this->_isRest()) {
|
||||
$params = array(
|
||||
'conditions' => $conditions,
|
||||
'recursive' => -1
|
||||
);
|
||||
$paramsToPass = array('limit', 'page');
|
||||
foreach ($paramsToPass as $p) {
|
||||
if (!empty($this->passedArgs[$p])) {
|
||||
$params[$p] = $this->passedArgs[$p];
|
||||
}
|
||||
}
|
||||
$data = $this->Dashboard->find('all', $params);
|
||||
foreach ($data as &$element) {
|
||||
$element['Dashboard']['value'] = json_decode($element['Dashboard']['value'], true);
|
||||
}
|
||||
return $this->RestResponse->viewData(
|
||||
$data,
|
||||
$this->response->type()
|
||||
);
|
||||
} else {
|
||||
$this->paginate['contain'] = array(
|
||||
'User.id', 'User.email'
|
||||
);
|
||||
$data = $this->paginate();
|
||||
foreach ($data as &$element) {
|
||||
$element['Dashboard']['value'] = json_decode($element['Dashboard']['value'], true);
|
||||
$widgets = array();
|
||||
foreach ($element['Dashboard']['value'] as $val) {
|
||||
$widgets[$val['widget']] = 1;
|
||||
}
|
||||
$element['Dashboard']['widgets'] = array_keys($widgets);
|
||||
sort($element['Dashboard']['widgets']);
|
||||
if ($element['Dashboard']['user_id'] != $this->Auth->user('id')) {
|
||||
$element['User']['email'] = '';
|
||||
}
|
||||
}
|
||||
$this->set('passedArgs', json_encode($this->passedArgs));
|
||||
$this->set('data', $data);
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteTemplate($id)
|
||||
{
|
||||
$conditions = array();
|
||||
if (Validation::uuid($id)) {
|
||||
$conditions['AND'][] = array('Dashboard.uuid' => $id);
|
||||
} else {
|
||||
$conditions['AND'][] = array('Dashboard.id' => $id);
|
||||
}
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
$conditions['AND'][] = array('Dashboard.user_id' => $this->Auth->user('id'));
|
||||
}
|
||||
$dashboard = $this->Dashboard->find('first', array(
|
||||
'conditions' => $conditions,
|
||||
'recursive' => -1
|
||||
));
|
||||
if (empty($dashboard)) {
|
||||
throw new NotFoundException(__('Invalid dashboard template.'));
|
||||
}
|
||||
$this->Dashboard->delete($dashboard['Dashboard']['id']);
|
||||
$message = __('Dashboard template removed.');
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('Dashboard', 'delete', $id, false, $message);
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect($this->baseurl . '/dashboards/listTemplates');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,709 +0,0 @@
|
|||
<?php
|
||||
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
class DecayingModelController extends AppController
|
||||
{
|
||||
public $components = array('RequestHandler');
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 50,
|
||||
'order' => array(
|
||||
'DecayingModel.ID' => 'desc'
|
||||
)
|
||||
);
|
||||
|
||||
public function update($force=false)
|
||||
{
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$this->DecayingModel->update($force, $this->Auth->user());
|
||||
$message = __('Default decaying models updated');
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('DecayingModel', 'update', false, $this->response->type(), $message);
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect(array('controller' => 'decayingModel', 'action' => 'index'));
|
||||
}
|
||||
} else {
|
||||
throw new MethodNotAllowedException(__("This method is not allowed"));
|
||||
}
|
||||
}
|
||||
|
||||
public function export($model_id)
|
||||
{
|
||||
$model = $this->DecayingModel->fetchModel($this->Auth->user(), $model_id, true);
|
||||
if (empty($model)) {
|
||||
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
|
||||
}
|
||||
unset($model['DecayingModel']['id'], $model['DecayingModel']['uuid'], $model['DecayingModel']['org_id'], $model['DecayingModelMapping']);
|
||||
return $this->RestResponse->viewData($model, $this->response->type());
|
||||
}
|
||||
|
||||
public function import()
|
||||
{
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$data = $this->request->data['DecayingModel'];
|
||||
$text = FileAccessTool::getTempUploadedFile($data['submittedjson'], $data['json']);
|
||||
$json = json_decode($text, true);
|
||||
if ($json === null) {
|
||||
throw new MethodNotAllowedException(__('Error while decoding JSON'));
|
||||
}
|
||||
|
||||
unset($json['id']);
|
||||
unset($json['uuid']);
|
||||
$json['default'] = 0;
|
||||
$json['org_id'] = $this->Auth->user()['org_id'];
|
||||
|
||||
$attribute_types = array();
|
||||
if (!empty($json['attribute_types'])) {
|
||||
$attribute_types = $json['attribute_types'];
|
||||
unset($json['attribute_types']);
|
||||
}
|
||||
|
||||
if ($this->DecayingModel->save($json)) {
|
||||
$saved_model = array(
|
||||
'model_id' => $this->DecayingModel->id,
|
||||
'attribute_types' => $attribute_types
|
||||
);
|
||||
if (!empty($saved_model['attribute_types'])) {
|
||||
$result = $this->DecayingModel->DecayingModelMapping->resetMappingForModel($saved_model, $this->Auth->user());
|
||||
} else {
|
||||
$result = true;
|
||||
}
|
||||
if (!empty($result)) {
|
||||
$this->Flash->success(__('The model has been imported.'));
|
||||
} else {
|
||||
$this->Flash->error(__('The model has been imported. However importing mapping failed.'));
|
||||
}
|
||||
} else {
|
||||
$this->Flash->error(__('Error while importing model.'));
|
||||
}
|
||||
$this->redirect(array('action' => 'index'));
|
||||
}
|
||||
}
|
||||
|
||||
public function view($id)
|
||||
{
|
||||
$decaying_model = $this->DecayingModel->fetchModel($this->Auth->user(), $id, true);
|
||||
if (empty($decaying_model)) {
|
||||
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
|
||||
}
|
||||
$this->set('id', $id);
|
||||
$this->set('decaying_model', $decaying_model);
|
||||
$available_formulas = $this->DecayingModel->listAvailableFormulas();
|
||||
$this->set('available_formulas', $available_formulas);
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($decaying_model, $this->response->type());
|
||||
}
|
||||
}
|
||||
|
||||
// Sets pagination condition for url parameters
|
||||
private function __setIndexFilterConditions($passedArgs)
|
||||
{
|
||||
$white_list_url_parameters = array('sort', 'direction');
|
||||
$passedArgsArray = array();
|
||||
foreach ($passedArgs as $k => $v) {
|
||||
switch ($k) {
|
||||
case 'my_models':
|
||||
$passedArgsArray[$k] = $v;
|
||||
if ($v) {
|
||||
$this->paginate['conditions']['AND'] = array('DecayingModel.org_id' => $this->Auth->user('Organisation')['id']);
|
||||
}
|
||||
break;
|
||||
case 'default_models':
|
||||
$passedArgsArray[$k] = $v;
|
||||
if ($v) {
|
||||
$this->paginate['conditions']['AND'] = array('not' => array('DecayingModel.uuid' => null));
|
||||
}
|
||||
break;
|
||||
case 'all_orgs':
|
||||
$passedArgsArray[$k] = $v;
|
||||
if ($v) {
|
||||
$this->paginate['conditions']['AND'] = array('DecayingModel.all_orgs' => $v);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (in_array($k, $white_list_url_parameters)) {
|
||||
$passedArgsArray[$k] = $v;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $passedArgsArray;
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$conditions = array();
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
$conditions['OR'] = array(
|
||||
'org_id' => $this->Auth->user('Organisation')['id'],
|
||||
'all_orgs' => 1
|
||||
);
|
||||
$this->paginate = Set::merge($this->paginate, array(
|
||||
'conditions' => $conditions
|
||||
));
|
||||
}
|
||||
$passedArgsArray = $this->__setIndexFilterConditions($this->passedArgs);
|
||||
$this->set('passedArgsArray', $passedArgsArray);
|
||||
$this->set('decayingModels', $this->paginate());
|
||||
$available_formulas = $this->DecayingModel->listAvailableFormulas();
|
||||
$this->set('available_formulas', $available_formulas);
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($this->paginate(), $this->response->type());
|
||||
}
|
||||
}
|
||||
|
||||
public function add()
|
||||
{
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
if (!isset($this->request->data['DecayingModel'])) {
|
||||
$this->request->data = array('DecayingModel' => $this->request->data);
|
||||
}
|
||||
|
||||
$this->request->data['DecayingModel']['org_id'] = $this->Auth->user()['org_id'];
|
||||
unset($this->request->data['DecayingModel']['id']);
|
||||
unset($this->request->data['DecayingModel']['uuid']);
|
||||
$this->request->data['DecayingModel']['default'] = 0;
|
||||
|
||||
if (empty($this->request->data['DecayingModel']['name'])) {
|
||||
throw new MethodNotAllowedException(__("The model must have a name"));
|
||||
}
|
||||
$this->request->data = $this->__adjustJSONData($this->request->data);
|
||||
if ($this->request->data === false) {
|
||||
return false;
|
||||
}
|
||||
$attribute_types = array();
|
||||
if (!empty($this->request->data['attribute_types'])) {
|
||||
$attribute_types = $this->request->data['attribute_types'];
|
||||
unset($this->request->data['attribute_types']);
|
||||
}
|
||||
if ($this->DecayingModel->save($this->request->data)) {
|
||||
$success_message = __('The model has been saved.');
|
||||
if (!empty($saved_model['attribute_types'])) {
|
||||
if (!$this->DecayingModel->DecayingModelMapping->resetMappingForModel($saved_model, $this->Auth->user())) {
|
||||
$success_message = __('The model has been saved. However importing mapping failed.');
|
||||
}
|
||||
}
|
||||
if ($this->request->is('ajax') || $this->_isRest()) {
|
||||
$saved = $this->DecayingModel->fetchModel($this->Auth->user(), $this->DecayingModel->id, true, array(), true);
|
||||
if (empty($saved)) {
|
||||
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
|
||||
}
|
||||
$response = array('data' => $saved, 'action' => 'add');
|
||||
return $this->RestResponse->viewData($response, $this->response->type());
|
||||
} else {
|
||||
$this->Flash->success($sucess_messaqge);
|
||||
$this->redirect(array('action' => 'index'));
|
||||
}
|
||||
} else {
|
||||
if ($this->request->is('ajax') || $this->_isRest()) {
|
||||
$response = array(
|
||||
'action' => 'add',
|
||||
'saved' => false,
|
||||
'errors' => array(__('The model could not be saved. Please try again.'))
|
||||
);
|
||||
return $this->RestResponse->viewData($response, $this->response->type());
|
||||
} else {
|
||||
$this->Flash->error(__('The model could not be saved. Please try again.' . $this->here));
|
||||
$this->redirect($this->here);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->set('action', 'add');
|
||||
$available_formulas = $this->DecayingModel->listAvailableFormulas();
|
||||
$formulas = array();
|
||||
foreach ($available_formulas as $formulaName => $f) {
|
||||
$formulas[$formulaName] = $formulaName;
|
||||
}
|
||||
$this->set('available_formulas', $formulas);
|
||||
}
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$decaying_model = $this->DecayingModel->fetchModel($this->Auth->user(), $id);
|
||||
if (empty($decaying_model)) {
|
||||
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
|
||||
}
|
||||
$enforceRestrictedEdition = $decaying_model['DecayingModel']['default'];
|
||||
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
|
||||
$this->request->data['DecayingModel']['id'] = $id;
|
||||
$fieldListToSave = array('enabled', 'all_orgs');
|
||||
if (!$enforceRestrictedEdition) {
|
||||
$fieldListToSave = array_merge($fieldListToSave, array('name', 'description', 'parameters', 'formula'));
|
||||
$this->request->data = $this->__adjustJSONData($this->request->data);
|
||||
if ($this->request->data === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$save_result = $this->DecayingModel->save($this->request->data, true, $fieldListToSave);
|
||||
if ($save_result) {
|
||||
if ($this->request->is('ajax') || $this->_isRest()) {
|
||||
$saved = $this->DecayingModel->fetchModel($this->Auth->user(), $this->DecayingModel->id, true, array(), true);
|
||||
if (empty($saved)) {
|
||||
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
|
||||
}
|
||||
$response = array('data' => $saved, 'action' => 'edit');
|
||||
return $this->RestResponse->viewData($response, $this->response->type());
|
||||
} else {
|
||||
$this->Flash->success(__('The model has been saved.'));
|
||||
$this->redirect(array('action' => 'index'));
|
||||
}
|
||||
} else {
|
||||
if ($this->request->is('ajax') || $this->_isRest()) {
|
||||
$saved = $this->DecayingModel->fetchModel($this->Auth->user(), $this->DecayingModel->id);
|
||||
if (empty($saved)) {
|
||||
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
|
||||
}
|
||||
$response = array('data' => $saved, 'action' => 'edit', 'saved' => false);
|
||||
return $this->RestResponse->viewData($response, $this->response->type());
|
||||
} else {
|
||||
$this->Flash->error(__('The model could not be saved. Please try again.' . $this->here));
|
||||
$this->redirect($this->here);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->request->data = $decaying_model;
|
||||
$this->set('id', $id);
|
||||
$this->set('decayingModel', $decaying_model);
|
||||
$this->set('restrictEdition', $enforceRestrictedEdition);
|
||||
$this->set('action', 'edit');
|
||||
$available_formulas = $this->DecayingModel->listAvailableFormulas();
|
||||
$formulas = array();
|
||||
foreach ($available_formulas as $formulaName => $f) {
|
||||
$formulas[$formulaName] = $formulaName;
|
||||
}
|
||||
$this->set('available_formulas', $formulas);
|
||||
$this->render('add');
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust or flash the error to the user
|
||||
private function __adjustJSONData($json)
|
||||
{
|
||||
if (isset($json['DecayingModel']['parameters'])) {
|
||||
if (isset($json['DecayingModel']['parameters']['settings']) && !is_array($json['DecayingModel']['parameters']['settings'])) {
|
||||
$settings = json_decode($json['DecayingModel']['parameters']['settings'], true);
|
||||
if ($settings === null) {
|
||||
$this->Flash->error(__('Invalid JSON `Settings`.'));
|
||||
return false;
|
||||
}
|
||||
$json['DecayingModel']['parameters']['settings'] = $settings;
|
||||
}
|
||||
if (!isset($json['DecayingModel']['parameters']['lifetime'])) {
|
||||
$this->Flash->error(__('Invalid parameter `lifetime`.'));
|
||||
return false;
|
||||
}
|
||||
if (!isset($json['DecayingModel']['parameters']['decay_speed'])) {
|
||||
$this->Flash->error(__('Invalid parameter `decay_speed`.'));
|
||||
return false;
|
||||
}
|
||||
if (!isset($json['DecayingModel']['parameters']['threshold'])) {
|
||||
$this->Flash->error(__('Invalid parameter `threshold`.'));
|
||||
return false;
|
||||
}
|
||||
if (!isset($json['DecayingModel']['parameters']['default_base_score'])) {
|
||||
$this->Flash->error(__('Invalid parameter `default_base_score`.'));
|
||||
return false;
|
||||
}
|
||||
if (isset($json['DecayingModel']['parameters']['base_score_config']) && $json['DecayingModel']['parameters']['base_score_config'] != '') {
|
||||
if (!is_array($json['DecayingModel']['parameters']['base_score_config'])) {
|
||||
$encoded = json_decode($json['DecayingModel']['parameters']['base_score_config'], true);
|
||||
if ($encoded === null) {
|
||||
$this->Flash->error(__('Invalid parameter `base_score_config`.'));
|
||||
return false;
|
||||
}
|
||||
$json['DecayingModel']['parameters']['base_score_config'] = $encoded;
|
||||
}
|
||||
} else {
|
||||
$json['DecayingModel']['parameters']['base_score_config'] = new stdClass();
|
||||
}
|
||||
} else {
|
||||
$this->Flash->error(__('Missing JSON key `parameters`.'));
|
||||
return false;
|
||||
}
|
||||
$json['DecayingModel']['parameters'] = json_encode($json['DecayingModel']['parameters']);
|
||||
return $json;
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
{
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$decaying_model = $this->DecayingModel->fetchModel($this->Auth->user(), $id);
|
||||
if (empty($decaying_model)) {
|
||||
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
|
||||
}
|
||||
if (
|
||||
!$this->DecayingModel->isEditableByCurrentUser($this->Auth->user(), $decaying_model) ||
|
||||
$decaying_model['DecayingModel']['default']
|
||||
) {
|
||||
throw new MethodNotAllowedException(__('You are not authorised to delete this model.'));
|
||||
}
|
||||
|
||||
if ($this->DecayingModel->delete($id, true)) {
|
||||
if ($this->request->is('ajax')) {
|
||||
$response = array('action' => 'delete', 'saved' => true);
|
||||
return $this->RestResponse->viewData($response, $this->response->type());
|
||||
} else {
|
||||
$this->Flash->success(__('Decaying Model deleted.'));
|
||||
}
|
||||
} else {
|
||||
$error_message = __('The Decaying Model could not be deleted.');
|
||||
if ($this->request->is('ajax')) {
|
||||
$response = array('action' => 'delete', 'saved' => false, 'errors' => array($error_message));
|
||||
return $this->RestResponse->viewData($response, $this->response->type());
|
||||
} else {
|
||||
$this->Flash->error($error_message);
|
||||
}
|
||||
}
|
||||
$this->redirect(array('action' => 'index'));
|
||||
}
|
||||
}
|
||||
|
||||
public function enable($id)
|
||||
{
|
||||
$decaying_model = $this->DecayingModel->fetchModel($this->Auth->user(), $id);
|
||||
if (empty($decaying_model)) {
|
||||
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
|
||||
}
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
if (!$this->DecayingModel->isEditableByCurrentUser($this->Auth->user(), $decaying_model)) {
|
||||
throw new MethodNotAllowedException(__('You are not authorised to enable this model.'));
|
||||
}
|
||||
|
||||
$decaying_model['DecayingModel']['enabled'] = 1;
|
||||
if ($this->DecayingModel->save($decaying_model)) {
|
||||
$model = $this->DecayingModel->fetchModel($this->Auth->user(), $id, true, array(), true);
|
||||
if (empty($model)) {
|
||||
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
|
||||
}
|
||||
$response = array('data' => $model, 'action' => 'enable');
|
||||
if ($this->request->is('ajax')) {
|
||||
return $this->RestResponse->viewData($response, $this->response->type());
|
||||
} else if ($this->_isRest()) {
|
||||
return $this->RestResponse->successResponse($id, __('Decaying model enabled'), $model);
|
||||
}
|
||||
$this->Flash->success(__('Decaying Model enabled.'));
|
||||
} else {
|
||||
if ($this->request->is('ajax')) { // ajax caller expect data to be returned to update the DOM accordingly
|
||||
$model = $this->DecayingModel->fetchModel($this->Auth->user(), $id, true, array(), true);
|
||||
if (empty($model)) {
|
||||
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
|
||||
}
|
||||
$response = array('data' => $model, 'action' => 'enable');
|
||||
return $this->RestResponse->viewData($response, $this->response->type());
|
||||
} elseif ($this->_isRest()) {
|
||||
$response = array('errors' => $array(__('Error while enabling decaying model')), 'action' => 'enable');
|
||||
return $this->RestResponse->viewData($response, $this->response->type());
|
||||
}
|
||||
$this->Flash->error(__('Error while enabling decaying model'));
|
||||
}
|
||||
$this->redirect(array('action' => 'index'));
|
||||
} else {
|
||||
$this->set('model', $decaying_model['DecayingModel']);
|
||||
$this->render('ajax/enable_form');
|
||||
}
|
||||
}
|
||||
|
||||
public function disable($id)
|
||||
{
|
||||
$decaying_model = $this->DecayingModel->fetchModel($this->Auth->user(), $id);
|
||||
if (empty($decaying_model)) {
|
||||
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
|
||||
}
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
if (!$this->DecayingModel->isEditableByCurrentUser($this->Auth->user(), $decaying_model)) {
|
||||
throw new MethodNotAllowedException(__('You are not authorised to disable this model.'));
|
||||
}
|
||||
|
||||
$decaying_model['DecayingModel']['enabled'] = 0;
|
||||
if ($this->DecayingModel->save($decaying_model)) {
|
||||
$model = $this->DecayingModel->fetchModel($this->Auth->user(), $id, true, array(), true);
|
||||
if (empty($model)) {
|
||||
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
|
||||
}
|
||||
$response = array('data' => $model, 'action' => 'disable');
|
||||
if ($this->request->is('ajax')) {
|
||||
return $this->RestResponse->viewData($response, $this->response->type());
|
||||
} else if ($this->_isRest()) {
|
||||
return $this->RestResponse->successResponse($id, __('Decaying model disabled'), $model);
|
||||
}
|
||||
$this->Flash->success(__('Decaying Model disabled.'));
|
||||
} else {
|
||||
if ($this->request->is('ajax')) { // ajax caller expect data to be returned to update the DOM accordingly
|
||||
$model = $this->DecayingModel->fetchModel($this->Auth->user(), $id, true, array(), true);
|
||||
if (empty($model)) {
|
||||
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
|
||||
}
|
||||
$response = array('data' => $model, 'action' => 'disable');
|
||||
return $this->RestResponse->viewData($response, $this->response->type());
|
||||
} elseif ($this->_isRest()) {
|
||||
$response = array('errors' => $array(__('Error while enabling decaying model')), 'action' => 'disable');
|
||||
return $this->RestResponse->viewData($response, $this->response->type());
|
||||
}
|
||||
$this->Flash->error(__('Error while disabling decaying model'));
|
||||
}
|
||||
$this->redirect(array('action' => 'index'));
|
||||
} else {
|
||||
$this->set('model', $decaying_model['DecayingModel']);
|
||||
$this->render('ajax/disable_form');
|
||||
}
|
||||
}
|
||||
|
||||
public function decayingTool()
|
||||
{
|
||||
$parameters = array(
|
||||
'lifetime' => array(
|
||||
'value' => 30,
|
||||
'step' => 1,
|
||||
'max' => 365,
|
||||
'greek' => '',
|
||||
'unit' => 'days',
|
||||
'name' => __('Lifetime'),
|
||||
'info' => __('Lifetime of the attribute, or time after which the score will be 0')
|
||||
),
|
||||
'decay_speed' => array(
|
||||
'value' => 0.3,
|
||||
'step' => 0.1,
|
||||
'max' => 10,
|
||||
'greek' => '',
|
||||
'name' => __('Decay speed'),
|
||||
'info' => __('Decay speed at which an indicator will loose score')
|
||||
),
|
||||
'threshold' => array(
|
||||
'value' => 30,
|
||||
'step' => 1,
|
||||
'max' => 100,
|
||||
'greek' => '',
|
||||
'name' => __('Cutoff threshold'),
|
||||
'info' => __('Cutoff value at which an indicator will be marked as decayed instead of 0')
|
||||
)
|
||||
);
|
||||
$types = $this->User->Event->Attribute->typeDefinitions;
|
||||
$this->loadModel('ObjectTemplateElement');
|
||||
$objectTypes = $this->ObjectTemplateElement->getAllAvailableTypes();
|
||||
array_walk($objectTypes, function(&$item, $key) use ($types) {
|
||||
$item["isObject"] = true;
|
||||
$isAttribute = isset($types[$key]);
|
||||
if ($isAttribute) {
|
||||
$item["isAttribute"] = true;
|
||||
$item["default_category"] = $types[$key]['default_category'];
|
||||
$item["to_ids"] = $types[$key]['to_ids'];
|
||||
} else {
|
||||
$item["default_category"] = $item["category"];
|
||||
}
|
||||
});
|
||||
$types = array_merge($types, $objectTypes);
|
||||
ksort($types);
|
||||
$savedDecayingModels = $this->DecayingModel->fetchAllAllowedModels($this->Auth->user());
|
||||
$available_formulas = $this->DecayingModel->listAvailableFormulas();
|
||||
|
||||
$this->set('available_formulas', $available_formulas);
|
||||
$this->set('parameters', $parameters);
|
||||
$this->set('types', $types);
|
||||
$this->set('savedModels', $savedDecayingModels);
|
||||
$associated_models = $this->DecayingModel->DecayingModelMapping->getAssociatedModels($this->Auth->user()); // mapping Attribute.type => Models
|
||||
$this->set('associated_models', $associated_models);
|
||||
}
|
||||
|
||||
public function getAllDecayingModels()
|
||||
{
|
||||
$filters = $this->request->query;
|
||||
$savedDecayingModels = $this->DecayingModel->fetchAllAllowedModels($this->Auth->user(), true, $filters);
|
||||
return $this->RestResponse->viewData($savedDecayingModels, $this->response->type());
|
||||
}
|
||||
|
||||
public function decayingToolBasescore()
|
||||
{
|
||||
$taxonomies = $this->DecayingModel->listTaxonomiesWithNumericalValue();
|
||||
$this->set('taxonomies', $taxonomies['taxonomies']);
|
||||
$this->set('taxonomies_not_having_numerical_value', $taxonomies['not_having_numerical_value']);
|
||||
$this->set('excluded_taxonomies', $taxonomies['excluded_taxonomies']);
|
||||
}
|
||||
|
||||
public function decayingToolSimulation($model_id)
|
||||
{
|
||||
$decaying_model = $this->DecayingModel->fetchModel($this->Auth->user(), $model_id);
|
||||
if (empty($decaying_model)) {
|
||||
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
|
||||
}
|
||||
if (isset($this->request->params['named']['attribute_id'])) {
|
||||
$this->set('attribute_id', $this->request->params['named']['attribute_id']);
|
||||
}
|
||||
$this->set('user', $this->Auth->user());
|
||||
$this->set('decaying_model', $decaying_model);
|
||||
$allowed_models = $this->DecayingModel->fetchAllAllowedModels($this->Auth->user());
|
||||
$this->set('all_models', $allowed_models);
|
||||
}
|
||||
|
||||
// TODO: Consider using the export tool to perform the post treatement
|
||||
// as this does not mirror a complete restSearch (not using fetchAttribute)
|
||||
public function decayingToolRestSearch($continue = false)
|
||||
{
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$body = $this->request->data['decayingToolRestSearch']['filters'];
|
||||
$decoded_body = json_decode($body, true);
|
||||
if (is_null($decoded_body)) {
|
||||
throw new Exception(__("Error Processing Request, can't parse the body"));
|
||||
}
|
||||
$this->request->data = $decoded_body;
|
||||
$paramArray = array(
|
||||
'value' , 'type', 'category', 'org', 'tags', 'from', 'to', 'last', 'eventid', 'withAttachments', 'uuid', 'publish_timestamp',
|
||||
'timestamp', 'enforceWarninglist', 'to_ids', 'deleted', 'includeEventUuid', 'event_timestamp', 'threat_level_id', 'includeEventTags',
|
||||
'includeProposals', 'returnFormat', 'published', 'limit', 'page', 'requested_attributes', 'includeContext', 'headerless',
|
||||
'includeWarninglistHits', 'attackGalaxy', 'object_relation', 'id', 'includeDecayScore', 'includeFullModel', 'decayingModel', 'excludeDecayed', 'modelOverrides',
|
||||
'score'
|
||||
);
|
||||
$filterData = array(
|
||||
'request' => $this->request,
|
||||
'named_params' => $this->params['named'],
|
||||
'paramArray' => $paramArray,
|
||||
'ordered_url_params' => compact($paramArray)
|
||||
);
|
||||
$exception = false;
|
||||
$filters = $this->_harvestParameters($filterData, $exception);
|
||||
if ($filters === false) {
|
||||
return $exception;
|
||||
}
|
||||
$filters['includeEventTags'] = 1;
|
||||
if (!isset($filters['excludeDecayed'])) {
|
||||
$filters['excludeDecayed'] = 0;
|
||||
}
|
||||
$filters['includeDecayScore'] = 1;
|
||||
if (isset($filters['id'])) { // allows searching by id
|
||||
if (Validation::uuid($filters['id'])) {
|
||||
$filters['uuid'] = $filters['id'];
|
||||
} else {
|
||||
$attributes = $this->User->Event->Attribute->fetchAttributes($this->Auth->user(), array(
|
||||
'conditions' => array('Attribute.id' => $filters['id']),
|
||||
'flatten' => 1
|
||||
));
|
||||
if (!empty($attributes)) {
|
||||
$filters['uuid'] = $attributes[0]['Attribute']['uuid'];
|
||||
} else {
|
||||
$filters['uuid'] = '-1'; // force no result
|
||||
}
|
||||
}
|
||||
unset($filters['id']);
|
||||
}
|
||||
unset($filterData);
|
||||
$this->Session->write('search_attributes_filters', json_encode($filters));
|
||||
} elseif ($continue === 'results') {
|
||||
$filters = $this->Session->read('search_attributes_filters');
|
||||
if (empty($filters)) {
|
||||
$filters = array();
|
||||
} else {
|
||||
$filters = json_decode($filters, true);
|
||||
}
|
||||
}
|
||||
if (isset($filters)) {
|
||||
$params = $this->User->Event->Attribute->restSearch($this->Auth->user(), 'json', $filters, true);
|
||||
if (!isset($params['conditions']['Attribute.deleted'])) {
|
||||
$params['conditions']['Attribute.deleted'] = 0;
|
||||
}
|
||||
$this->paginate = $params;
|
||||
if (empty($this->paginate['limit'])) {
|
||||
$this->paginate['limit'] = 60;
|
||||
}
|
||||
if (empty($this->paginate['page'])) {
|
||||
$this->paginate['page'] = 1;
|
||||
}
|
||||
$this->paginate['recursive'] = -1;
|
||||
$this->paginate['contain'] = array(
|
||||
'Event' => array(
|
||||
'fields' => array('Event.id', 'Event.orgc_id', 'Event.org_id', 'Event.info', 'Event.user_id', 'Event.date'),
|
||||
'Orgc' => array('fields' => array('Orgc.id', 'Orgc.name')),
|
||||
'Org' => array('fields' => array('Org.id', 'Org.name'))
|
||||
),
|
||||
'AttributeTag' => array('Tag'),
|
||||
'Object' => array(
|
||||
'fields' => array('Object.id', 'Object.distribution', 'Object.sharing_group_id')
|
||||
)
|
||||
);
|
||||
$attributes = $this->paginate($this->User->Event->Attribute);
|
||||
|
||||
if (!empty($options['overrideLimit'])) {
|
||||
$overrideLimit = true;
|
||||
} else {
|
||||
$overrideLimit = false;
|
||||
}
|
||||
$this->loadModel('GalaxyCluster');
|
||||
$cluster_names = $this->GalaxyCluster->find('list', array('fields' => array('GalaxyCluster.tag_name'), 'group' => array('GalaxyCluster.tag_name', 'GalaxyCluster.id')));
|
||||
$this->loadModel('Sighting');
|
||||
$eventTags = array();
|
||||
foreach ($attributes as $k => $attribute) {
|
||||
$attributes[$k]['Attribute']['AttributeTag'] = $attributes[$k]['AttributeTag'];
|
||||
$attributes[$k]['Attribute'] = $this->User->Event->massageTags($this->Auth->user(), $attributes[$k]['Attribute'], 'Attribute');
|
||||
unset($attributes[$k]['AttributeTag']);
|
||||
foreach ($attributes[$k]['Attribute']['AttributeTag'] as $k2 => $attributeTag) {
|
||||
if (in_array($attributeTag['Tag']['name'], $cluster_names)) {
|
||||
unset($attributes[$k]['Attribute']['AttributeTag'][$k2]);
|
||||
}
|
||||
}
|
||||
if (!empty($params['includeEventTags'])) {
|
||||
$tagConditions = array('EventTag.event_id' => $attribute['Event']['id']);
|
||||
if (empty($params['includeAllTags'])) {
|
||||
$tagConditions['Tag.exportable'] = 1;
|
||||
}
|
||||
$temp = $this->User->Event->EventTag->find('all', array(
|
||||
'recursive' => -1,
|
||||
'contain' => array('Tag'),
|
||||
'conditions' => $tagConditions
|
||||
));
|
||||
foreach ($temp as $tag) {
|
||||
$attributes[$k]['Attribute']['EventTag'][] = $tag;
|
||||
}
|
||||
}
|
||||
if (empty($filters['decayingModel'])) {
|
||||
$filters['decayingModel'] = false;
|
||||
}
|
||||
$model_overrides = isset($filters['modelOverrides']) ? $filters['modelOverrides'] : array();
|
||||
if (isset($filters['score'])) {
|
||||
$model_overrides['threshold'] = intval($filters['score']);
|
||||
}
|
||||
$attributes[$k]['Attribute'] = $this->DecayingModel->attachScoresToAttribute($this->Auth->user(), $attributes[$k]['Attribute'], $filters['decayingModel'], $model_overrides);
|
||||
if ($filters['excludeDecayed'] && !empty($attributes[$k]['Attribute']['decay_score'])) { // filter out decayed attribute
|
||||
$decayed_flag = true;
|
||||
foreach ($attributes[$k]['Attribute']['decay_score'] as $decayResult) {
|
||||
$decayed_flag = $decayed_flag && $decayResult['decayed'];
|
||||
}
|
||||
if ($decayed_flag) {
|
||||
unset($attributes[$k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->set('sightingsData', $this->Sighting->attributesStatistics($attributes, $this->Auth->user()));
|
||||
$this->set('attributes', $attributes);
|
||||
$this->set('attrDescriptions', $this->User->Event->Attribute->fieldDescriptions);
|
||||
$this->set('typeDefinitions', $this->User->Event->Attribute->typeDefinitions);
|
||||
$this->set('categoryDefinitions', $this->User->Event->Attribute->categoryDefinitions);
|
||||
$this->set('shortDist', $this->User->Event->Attribute->shortDist);
|
||||
} else {
|
||||
$this->render('decayingToolRestSearchForm');
|
||||
}
|
||||
}
|
||||
|
||||
public function decayingToolComputeSimulation($model_id, $attribute_id)
|
||||
{
|
||||
if (!$this->request->is('ajax')) {
|
||||
throw new MethodNotAllowedException(__("This method is only accessible via AJAX."));
|
||||
}
|
||||
$model_overrides = array();
|
||||
if (isset($this->params['named']['modelOverride'])) {
|
||||
$model_overrides = $this->params['named']['modelOverride'];
|
||||
$model_overrides = json_decode($model_overrides, true);
|
||||
if ($model_overrides === null) {
|
||||
$model_overrides = array();
|
||||
}
|
||||
}
|
||||
if (isset($this->params['named']['score'])) {
|
||||
$model_overrides['threshold'] = intval($this->params['named']['score']);
|
||||
}
|
||||
$score_overtime = $this->DecayingModel->getScoreOvertime($this->Auth->user(), $model_id, $attribute_id, $model_overrides);
|
||||
return $this->RestResponse->viewData($score_overtime, $this->response->type());
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
<?php
|
||||
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
class DecayingModelMappingController extends AppController
|
||||
{
|
||||
public $components = array('RequestHandler');
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 50,
|
||||
'order' => array(
|
||||
'DecayingModel.name' => 'asc'
|
||||
)
|
||||
);
|
||||
|
||||
public function viewAssociatedTypes($model_id) {
|
||||
$associated_types = $this->DecayingModelMapping->getAssociatedTypes($this->Auth->user(), $model_id);
|
||||
return $this->RestResponse->viewData($associated_types, $this->response->type());
|
||||
}
|
||||
|
||||
|
||||
public function linkAttributeTypeToModel($model_id) {
|
||||
$model = $this->DecayingModelMapping->DecayingModel->fetchModel($this->Auth->user(), $model_id);
|
||||
if (empty($model)) {
|
||||
throw new NotFoundException(__('No Decaying Model with the provided ID exists'));
|
||||
}
|
||||
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$this->request->data['DecayingModelMapping']['model_id'] = $model_id;
|
||||
if (!isset($this->request->data['DecayingModelMapping']['org_id'])) {
|
||||
$this->request->data['DecayingModelMapping']['org_id'] = $this->Auth->user()['org_id'];
|
||||
}
|
||||
if (empty($this->request->data['DecayingModelMapping']['attributetypes'])) {
|
||||
throw new MethodNotAllowedException(_("The model must link to at least one attribute type"));
|
||||
} else {
|
||||
$decoded = json_decode($this->request->data['DecayingModelMapping']['attributetypes'], true);
|
||||
if ($decoded === null) {
|
||||
throw new MethodNotAllowedException(_("Invalid JSON: attribute type"));
|
||||
}
|
||||
$this->request->data['DecayingModelMapping']['attribute_types'] = $decoded;
|
||||
unset($this->request->data['DecayingModelMapping']['attributetypes']);
|
||||
}
|
||||
|
||||
$response = $this->DecayingModelMapping->resetMappingForModel($this->request->data['DecayingModelMapping'], $this->Auth->user());
|
||||
return $this->RestResponse->viewData($response, $this->response->type());
|
||||
} else {
|
||||
$this->set('model_id', $model_id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
class EventBlocklistsController extends AppController
|
||||
{
|
||||
public $components = array('Session', 'RequestHandler', 'BlockList');
|
||||
|
||||
public function beforeFilter()
|
||||
{
|
||||
parent::beforeFilter();
|
||||
if (false === Configure::read('MISP.enableEventBlocklisting')) {
|
||||
$this->Flash->info(__('Event Blocklisting is not currently enabled on this instance.'));
|
||||
$this->redirect('/');
|
||||
}
|
||||
}
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 60,
|
||||
'maxLimit' => 9999, // LATER we will bump here on a problem once we have more than 9999 events <- no we won't, this is the max a user van view/page.
|
||||
'order' => array(
|
||||
'EventBlocklist.created' => 'DESC'
|
||||
),
|
||||
);
|
||||
|
||||
public function index()
|
||||
{
|
||||
$passedArgsArray = array();
|
||||
$passedArgs = $this->passedArgs;
|
||||
$params = array();
|
||||
$validParams = array('event_uuid', 'comment', 'event_info', 'event_orgc');
|
||||
foreach ($validParams as $validParam) {
|
||||
if (!empty($this->params['named'][$validParam])) {
|
||||
$params[$validParam] = $this->params['named'][$validParam];
|
||||
}
|
||||
}
|
||||
if (!empty($this->params['named']['searchall'])) {
|
||||
$params['AND']['OR'] = array(
|
||||
'event_uuid' => $this->params['named']['searchall'],
|
||||
'comment' => $this->params['named']['searchall'],
|
||||
'event_info' => $this->params['named']['searchall'],
|
||||
'event_orgc' => $this->params['named']['searchall']
|
||||
);
|
||||
}
|
||||
$this->set('passedArgs', json_encode($passedArgs));
|
||||
$this->set('passedArgsArray', $passedArgsArray);
|
||||
return $this->BlockList->index($this->_isRest(), $params);
|
||||
}
|
||||
|
||||
public function add()
|
||||
{
|
||||
return $this->BlockList->add($this->_isRest());
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
return $this->BlockList->edit($this->_isRest(), $id);
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
{
|
||||
return $this->BlockList->delete($this->_isRest(), $id);
|
||||
}
|
||||
|
||||
public function massDelete()
|
||||
{
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
if (!isset($this->request->data['EventBlocklist'])) {
|
||||
$this->request->data = array('EventBlocklist' => $this->request->data);
|
||||
}
|
||||
$ids = $this->request->data['EventBlocklist']['ids'];
|
||||
$event_ids = json_decode($ids, true);
|
||||
if (empty($event_ids)) {
|
||||
throw new NotFoundException(__('Invalid event IDs.'));
|
||||
}
|
||||
$result = $this->EventBlocklist->deleteAll(array('EventBlocklist.id' => $event_ids));
|
||||
if ($result) {
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('EventBlocklist', 'Deleted', $ids, $this->response->type());
|
||||
} else {
|
||||
$this->Flash->success('Blocklist entry removed');
|
||||
$this->redirect(array('controller' => 'eventBlocklists', 'action' => 'index'));
|
||||
}
|
||||
} else {
|
||||
$error = __('Failed to delete Event from EventBlocklist. Error: ') . PHP_EOL . h($result);
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('EventBlocklist', 'Deleted', false, $error, $this->response->type());
|
||||
} else {
|
||||
$this->Flash->error($error);
|
||||
$this->redirect(array('controller' => 'eventBlocklists', 'action' => 'index'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$ids = json_decode($this->request->query('ids'), true);
|
||||
if (empty($ids)) {
|
||||
throw new NotFoundException(__('Invalid event blocklist IDs.'));
|
||||
|
||||
}
|
||||
$this->set('event_ids', $ids);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,280 +0,0 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
class EventDelegationsController extends AppController
|
||||
{
|
||||
public $components = array('Session', 'RequestHandler');
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 60,
|
||||
'maxLimit' => 9999, // LATER we will bump here on a problem once we have more than 9999 events <- no we won't, this is the max a user van view/page.
|
||||
'order' => array(
|
||||
'EventDelegations.id' => 'DESC'
|
||||
),
|
||||
);
|
||||
|
||||
public function view($id)
|
||||
{
|
||||
$delegation = $this->EventDelegation->find('first', array(
|
||||
'conditions' => array('EventDelegation.id' => $id),
|
||||
'recursive' => -1,
|
||||
'contain' => array('Org', 'Event', 'RequesterOrg', 'SharingGroup'),
|
||||
));
|
||||
if (empty($delegation) || (!$this->_isSiteAdmin() && $this->Auth->user('org_id') != $delegation['EventDelegation']['org_id'] && $this->Auth->user('org_id') != $delegation['EventDelegation']['requester_org_id'])) {
|
||||
throw new MethodNotAllowedException('You are not authorised to do that.');
|
||||
}
|
||||
$delegation['requested_distribution_level'] = $delegation['EventDelegation']['distribution'] == -1 ? false : $this->EventDelegation->Event->distributionLevels[$delegation['EventDelegation']['distribution']];
|
||||
$this->set('delegation', $delegation);
|
||||
$this->render('ajax/view');
|
||||
}
|
||||
|
||||
public function delegateEvent($id)
|
||||
{
|
||||
$id = $this->Toolbox->findIdByUuid($this->EventDelegation->Event, $id);
|
||||
$event = $this->EventDelegation->Event->find('first', array(
|
||||
'conditions' => array('Event.id' => $id),
|
||||
'recursive' => -1,
|
||||
'fields' => array('Event.id', 'Event.orgc_id', 'Event.distribution')
|
||||
));
|
||||
if (empty($event)) {
|
||||
throw new MethodNotAllowedException('You are not authorised to do that.');
|
||||
}
|
||||
if (!$this->_isSiteAdmin() && $this->Auth->user('org_id') !== $event['Event']['orgc_id']) {
|
||||
throw new MethodNotAllowedException('You are not authorised to do that.');
|
||||
}
|
||||
if (!Configure::read('MISP.unpublishedprivate') && $event['Event']['distribution'] != 0) {
|
||||
throw new MethodNotAllowedException('Only events with the distribution setting "Your Organisation Only" can be delegated.');
|
||||
}
|
||||
$existingDelegations = $this->EventDelegation->find('first', array('conditions' => array('event_id' => $id), 'recursive' => -1));
|
||||
if (!empty($existingDelegations)) {
|
||||
throw new MethodNotAllowedException('This event already has a pending delegation request. Please revoke that before creating a new request.');
|
||||
}
|
||||
if ($this->request->is('post')) {
|
||||
if (empty($this->request->data['EventDelegation'])) {
|
||||
$this->request->data = array('EventDelegation' => $this->request->data);
|
||||
}
|
||||
if (empty($this->request->data['EventDelegation']['distribution'])) {
|
||||
$this->request->data['EventDelegation']['distribution'] = 0;
|
||||
}
|
||||
if ($this->request->data['EventDelegation']['distribution'] != 4) {
|
||||
$this->request->data['EventDelegation']['sharing_group_id'] = '0';
|
||||
}
|
||||
$this->request->data['EventDelegation']['event_id'] = $event['Event']['id'];
|
||||
$this->request->data['EventDelegation']['requester_org_id'] = $this->Auth->user('org_id');
|
||||
$org_id = $this->Toolbox->findIdByUuid($this->EventDelegation->Event->Org, $this->request->data['EventDelegation']['org_id']);
|
||||
$this->request->data['EventDelegation']['org_id'] = $org_id;
|
||||
$this->EventDelegation->create();
|
||||
$result = $this->EventDelegation->save($this->request->data['EventDelegation']);
|
||||
$org = $this->EventDelegation->Event->Org->find('first', array(
|
||||
'conditions' => array('id' => $org_id),
|
||||
'recursive' => -1,
|
||||
'fields' => array('name')
|
||||
));
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
$this->Log->create();
|
||||
if (empty($result)) {
|
||||
$this->Log->save(array(
|
||||
'org' => $this->Auth->user('Organisation')['name'],
|
||||
'model' => 'Event',
|
||||
'model_id' => $event['Event']['id'],
|
||||
'email' => $this->Auth->user('email'),
|
||||
'action' => 'request_delegation',
|
||||
'user_id' => $this->Auth->user('id'),
|
||||
'title' => 'Request of event delegation failed',
|
||||
'change' => 'Request of the delegation of event ' . $event['Event']['id'] . ' to organisation ' . $org['Org']['name'] . ' failed.',
|
||||
));
|
||||
throw new InvalidArgumentException('Invalid input, could not create the Delegation Request.');
|
||||
}
|
||||
$this->Log->save(array(
|
||||
'org' => $this->Auth->user('Organisation')['name'],
|
||||
'model' => 'Event',
|
||||
'model_id' => $event['Event']['id'],
|
||||
'email' => $this->Auth->user('email'),
|
||||
'action' => 'request_delegation',
|
||||
'user_id' => $this->Auth->user('id'),
|
||||
'title' => 'Requested event delegation',
|
||||
'change' => 'Requested the delegation of event ' . $event['Event']['id'] . ' to organisation ' . $org['Org']['name'],
|
||||
));
|
||||
if ($this->_isRest()) {
|
||||
$delegation_request = $this->EventDelegation->find('first', array(
|
||||
'conditions' => array(
|
||||
'EventDelegation.id' => $this->EventDelegation->id
|
||||
),
|
||||
'recursive' => -1
|
||||
));
|
||||
return $this->RestResponse->viewData($delegation_request, $this->response->type());
|
||||
}
|
||||
if (!$this->_isRest()) {
|
||||
$this->Flash->success('Delegation request created.');
|
||||
$this->redirect('/events/view/' . $id);
|
||||
} else {
|
||||
$delegationRequest = $this->EventDelegation->find("first", array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('EventDelegation.id' => $this->EventDelegation->id)
|
||||
));
|
||||
return $this->RestResponse->viewData($delegationRequest, $this->response->type());
|
||||
}
|
||||
} else {
|
||||
$orgs = $this->EventDelegation->Event->Org->find('list', array(
|
||||
'conditions' => array(
|
||||
'Org.id !=' => $this->Auth->user('org_id'),
|
||||
'Org.local' => 1,
|
||||
),
|
||||
'fields' => array('name'),
|
||||
'order' => array('lower(name) ASC')
|
||||
));
|
||||
$distribution = $this->EventDelegation->Event->distributionLevels;
|
||||
$sgs = $this->EventDelegation->Event->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'name', true);
|
||||
if (empty($sgs)) {
|
||||
unset($distribution[4]);
|
||||
}
|
||||
$distribution[-1] = 'Recipient decides';
|
||||
$this->set('distributionOptions', array('-1' => 'Recipient decides') + $distribution);
|
||||
$this->set('org', $orgs);
|
||||
$this->set('sgOptions', $sgs);
|
||||
$this->set('id', $id);
|
||||
$this->render('ajax/delegate_event');
|
||||
}
|
||||
}
|
||||
|
||||
public function acceptDelegation($id)
|
||||
{
|
||||
$delegation = $this->EventDelegation->find('first', array(
|
||||
'conditions' => array('EventDelegation.id' => $id),
|
||||
'recursive' => -1,
|
||||
'contain' => array('Org', 'Event', 'RequesterOrg'),
|
||||
));
|
||||
if (empty($delegation) || (!$this->_isSiteAdmin() && $this->Auth->user('org_id') != $delegation['EventDelegation']['org_id'])) {
|
||||
throw new MethodNotAllowedException('You are not authorised to do that.');
|
||||
}
|
||||
if ($this->request->is('post')) {
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
$this->Log->create();
|
||||
$this->Log->save(array(
|
||||
'org' => $this->Auth->user('Organisation')['name'],
|
||||
'model' => 'Event',
|
||||
'model_id' => $delegation['Event']['id'],
|
||||
'email' => $this->Auth->user('email'),
|
||||
'action' => 'accept_delegation',
|
||||
'user_id' => $this->Auth->user('id'),
|
||||
'title' => 'Accepted event delegation',
|
||||
'change' => 'Starting the transfer of event ' . $delegation['Event']['id'] . ' to organisation ' . $this->Auth->user('Organisation')['name'],
|
||||
));
|
||||
$result = $this->EventDelegation->transferEvent($delegation, $this->Auth->user());
|
||||
$this->EventDelegation->delete($delegation['EventDelegation']['id']);
|
||||
if ($result) {
|
||||
$this->Log->create();
|
||||
$this->Log->save(array(
|
||||
'org' => $this->Auth->user('Organisation')['name'],
|
||||
'model' => 'Event',
|
||||
'model_id' => 0,
|
||||
'email' => $this->Auth->user('email'),
|
||||
'action' => 'accept_delegation',
|
||||
'user_id' => $this->Auth->user('id'),
|
||||
'title' => 'Completed event delegation',
|
||||
'change' => 'Event ' . $delegation['Event']['id'] . ' successfully transferred to organisation ' . $this->Auth->user('Organisation')['name'],
|
||||
));
|
||||
$message = 'Event ownership transferred.';
|
||||
if (!$this->_isRest()) {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect(array('controller' => 'events', 'action' => 'view', $result));
|
||||
} else {
|
||||
return $this->RestResponse->saveSuccessResponse('EventDelegation', 'acceptDelegation', $id, $this->response->type(), $message);
|
||||
}
|
||||
} else {
|
||||
$message = 'Something went wrong and the event could not be transferred.';
|
||||
if (!$this->_isRest()) {
|
||||
$this->Flash->error($message);
|
||||
$this->redirect(array('controller' => 'Event', 'action' => 'view', $delegation['EventDelegation']['event_id']));
|
||||
} else {
|
||||
return $this->RestResponse->saveFailResponse('EventDelegation', 'acceptDelegation', $id, $message, $this->response->type());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->set('delegationRequest', $delegation);
|
||||
$this->render('ajax/accept_delegation');
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteDelegation($id)
|
||||
{
|
||||
$delegation = $this->EventDelegation->find('first', array(
|
||||
'conditions' => array('EventDelegation.id' => $id),
|
||||
'recursive' => -1,
|
||||
'contain' => array('Org', 'Event', 'RequesterOrg'),
|
||||
));
|
||||
if (empty($delegation) || (!$this->_isSiteAdmin() && !in_array($this->Auth->user('org_id'), array($delegation['EventDelegation']['requester_org_id'], $delegation['EventDelegation']['org_id'])))) {
|
||||
throw new MethodNotAllowedException('You are not authorised to do that.');
|
||||
}
|
||||
if ($this->request->is('post')) {
|
||||
$this->EventDelegation->delete($delegation['EventDelegation']['id']);
|
||||
$message = 'Delegation request deleted.';
|
||||
if (!$this->_isRest()) {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect(array('controller' => 'events', 'action' => 'index'));
|
||||
} else {
|
||||
return $this->RestResponse->saveSuccessResponse('EventDelegation', 'deleteDelegation', $id, $this->response->type(), $message);
|
||||
}
|
||||
|
||||
} else {
|
||||
$this->set('delegationRequest', $delegation);
|
||||
$this->render('ajax/delete_delegation');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$context = 'pending';
|
||||
if ($this->request->is('post') && !empty($this->request->data['context'])) {
|
||||
$context = $this->request->data['context'];
|
||||
} else if (!empty($this->params['named']['context'])) {
|
||||
$context = $this->params['named']['context'];
|
||||
}
|
||||
if ($context === 'pending') {
|
||||
$conditions = array('EventDelegation.org_id' => $this->Auth->user('org_id'));
|
||||
} else if ($context === 'issued') {
|
||||
$conditions = array('EventDelegation.requester_org_id' => $this->Auth->user('org_id'));
|
||||
} else {
|
||||
throw new InvalidArgumentException('Invalid context. Expected values: pending or issued.');
|
||||
}
|
||||
if (!empty($this->params['named']['value'])) {
|
||||
$temp = array();
|
||||
$temp['lower(EventDelegation.message) like'] = '%' . strtolower(trim($this->params['named']['value'])) . '%';
|
||||
$temp['lower(Event.info) like'] = '%' . strtolower(trim($this->params['named']['value'])) . '%';
|
||||
$temp['lower(Org.name) like'] = '%' . strtolower(trim($this->params['named']['value'])) . '%';
|
||||
$temp['lower(RequesterOrg.name) like'] = '%' . strtolower(trim($this->params['named']['value'])) . '%';
|
||||
$conditions['AND'][] = array('OR' => $temp);
|
||||
}
|
||||
$org_fields = array('id', 'name', 'uuid');
|
||||
$event_fields = array('id', 'info', 'uuid', 'analysis', 'distribution', 'threat_level_id', 'date', 'attribute_count');
|
||||
$params = array(
|
||||
'conditions' => $conditions,
|
||||
'recursive' => -1,
|
||||
'contain' => array(
|
||||
'Event' => array('fields' => $event_fields),
|
||||
'Org' => array('fields' => $org_fields),
|
||||
'RequesterOrg' => array('fields' => $org_fields)
|
||||
)
|
||||
);
|
||||
$this->paginate = array_merge($this->paginate, $params);
|
||||
$delegation_requests = $this->paginate();
|
||||
foreach ($delegation_requests as $k => $v) {
|
||||
if ($v['EventDelegation']['distribution'] == -1) {
|
||||
unset($delegation_requests[$k]['EventDelegation']['distribution']);
|
||||
}
|
||||
if ($v['EventDelegation']['sharing_group_id'] == 0) {
|
||||
unset($delegation_requests[$k]['EventDelegation']['sharing_group_id']);
|
||||
}
|
||||
unset($v['EventDelegation']);
|
||||
$delegation_requests[$k]['EventDelegation'] = array_merge($delegation_requests[$k]['EventDelegation'], $v);
|
||||
$delegation_requests[$k] = array('EventDelegation' => $delegation_requests[$k]['EventDelegation']);
|
||||
}
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($delegation_requests, $this->response->type());
|
||||
} else {
|
||||
$this->set('context', $context);
|
||||
$this->set('delegation_requests', $delegation_requests);
|
||||
$this->set('passedArgs', json_encode($this->passedArgs, true));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,182 +0,0 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property EventGraph $EventGraph
|
||||
*/
|
||||
class EventGraphController extends AppController
|
||||
{
|
||||
public $components = array(
|
||||
'RequestHandler'
|
||||
);
|
||||
|
||||
public function view($event_id = false, $graph_id = null)
|
||||
{
|
||||
if ($event_id === false) {
|
||||
throw new MethodNotAllowedException(__('No event ID set.'));
|
||||
}
|
||||
|
||||
$this->loadModel('Event');
|
||||
$event = $this->Event->fetchSimpleEvent($this->Auth->user(), $event_id);
|
||||
if (empty($event)) {
|
||||
throw new NotFoundException('Invalid event');
|
||||
}
|
||||
|
||||
// fetch eventGraphs
|
||||
$conditions = [
|
||||
'EventGraph.event_id' => $event['Event']['id'],
|
||||
'EventGraph.org_id' => $this->Auth->user('org_id'),
|
||||
];
|
||||
if (!is_null($graph_id)) {
|
||||
$conditions['EventGraph.id'] = $graph_id;
|
||||
}
|
||||
$eventGraphs = $this->EventGraph->find('all', array(
|
||||
'order' => 'EventGraph.timestamp DESC',
|
||||
'conditions' => $conditions,
|
||||
'contain' => array(
|
||||
'User' => array(
|
||||
'fields' => array(
|
||||
'User.email'
|
||||
)
|
||||
)
|
||||
)
|
||||
));
|
||||
return $this->RestResponse->viewData($eventGraphs, $this->response->type());
|
||||
}
|
||||
|
||||
public function viewPicture($event_id, $graph_id)
|
||||
{
|
||||
$this->loadModel('Event');
|
||||
$event = $this->Event->fetchSimpleEvent($this->Auth->user(), $event_id);
|
||||
if (empty($event)) {
|
||||
throw new NotFoundException('Invalid event');
|
||||
}
|
||||
|
||||
$conditions = [
|
||||
'EventGraph.event_id' => $event['Event']['id'],
|
||||
'EventGraph.org_id' => $this->Auth->user('org_id'),
|
||||
'EventGraph.id' => $graph_id,
|
||||
];
|
||||
$eventGraph = $this->EventGraph->find('first', array(
|
||||
'conditions' => $conditions,
|
||||
'contain' => array(
|
||||
'User' => array(
|
||||
'fields' => array(
|
||||
'User.email'
|
||||
)
|
||||
)
|
||||
)
|
||||
));
|
||||
if (empty($eventGraph)) {
|
||||
throw new MethodNotAllowedException('Invalid event graph');
|
||||
}
|
||||
$imageData = $this->EventGraph->getPictureData($eventGraph);
|
||||
return new CakeResponse(array('body' => $imageData, 'type' => 'png'));
|
||||
}
|
||||
|
||||
public function add($event_id = false)
|
||||
{
|
||||
if (empty($event_id)) {
|
||||
throw new MethodNotAllowedException(__('No event ID set.'));
|
||||
}
|
||||
|
||||
if ($this->request->is('get')) {
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->describe('EventGraph', 'add', false, $this->response->type());
|
||||
}
|
||||
|
||||
if (!$this->_isSiteAdmin() && (!$this->userRole['perm_modify'] && !$this->userRole['perm_modify_org'])) {
|
||||
throw new NotFoundException(__('Invalid event'));
|
||||
}
|
||||
|
||||
$this->set('action', 'add');
|
||||
$this->set('event_id', $event_id);
|
||||
$this->render('ajax/eventGraph_add_form');
|
||||
} else {
|
||||
$this->loadModel('Event');
|
||||
$event = $this->Event->fetchSimpleEvent($this->Auth->user(), $event_id);
|
||||
if (empty($event)) {
|
||||
throw new NotFoundException('Invalid event');
|
||||
}
|
||||
if (!$this->ACL->canModifyEvent($this->Auth->user(), $event)) {
|
||||
throw new ForbiddenException(__('You do not have permission to do that.'));
|
||||
}
|
||||
if (!isset($this->request->data['EventGraph']['network_json'])) {
|
||||
throw new MethodNotAllowedException('No network data set');
|
||||
}
|
||||
if (!JsonTool::isValid($this->request->data['EventGraph']['network_json'])) {
|
||||
throw new MethodNotAllowedException('Network data is not valid JSON.');
|
||||
}
|
||||
|
||||
$eventGraph = ['EventGraph' => [
|
||||
'event_id' => $event['Event']['id'],
|
||||
'network_json' => $this->request->data['EventGraph']['network_json'],
|
||||
'user_id' => $this->Auth->user('id'),
|
||||
'org_id' => $this->Auth->user('org_id'),
|
||||
]];
|
||||
|
||||
if (!isset($this->request->data['EventGraph']['network_name'])) {
|
||||
$eventGraph['EventGraph']['network_name'] = null;
|
||||
} else {
|
||||
$eventGraph['EventGraph']['network_name'] = $this->request->data['EventGraph']['network_name'];
|
||||
}
|
||||
|
||||
if (isset($this->request->data['EventGraph']['preview_img'])) {
|
||||
$eventGraph['EventGraph']['preview_img'] = $this->request->data['EventGraph']['preview_img'];
|
||||
}
|
||||
|
||||
$result = $this->EventGraph->save(
|
||||
$eventGraph,
|
||||
true,
|
||||
array(
|
||||
'event_id',
|
||||
'network_json',
|
||||
'network_name',
|
||||
'timestamp',
|
||||
'user_id',
|
||||
'org_id',
|
||||
'preview_img',
|
||||
)
|
||||
);
|
||||
if ($result) {
|
||||
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'eventGraph saved.')), 'status'=>200, 'type' => 'json'));
|
||||
} else {
|
||||
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'eventGraph could not be saved.')), 'status'=>200, 'type' => 'json'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
{
|
||||
if (!$this->request->is('post')) {
|
||||
$this->set('id', $id);
|
||||
$this->render('ajax/eventGraph_delete_form');
|
||||
} else {
|
||||
$this->set('id', $id);
|
||||
$conditions = array('id' => $id);
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
$conditions['org_id'] = $this->Auth->user('org_id');
|
||||
}
|
||||
$eventGraph = $this->EventGraph->find('first', array(
|
||||
'conditions' => $conditions,
|
||||
'recursive' => -1,
|
||||
'fields' => array('id', 'event_id', 'user_id'),
|
||||
));
|
||||
if (empty($eventGraph)) {
|
||||
throw new NotFoundException('Invalid EventGraph');
|
||||
}
|
||||
if ($this->request->is('post')) {
|
||||
// only creator (or siteAdmin) can delete the eventGraph
|
||||
if (($eventGraph['EventGraph']['user_id'] != $this->Auth->user()['id']) && !$this->_isSiteAdmin()) {
|
||||
throw new MethodNotAllowedException('This eventGraph does not belong to you.');
|
||||
}
|
||||
$result = $this->EventGraph->delete($id);
|
||||
if ($result) {
|
||||
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'EventGraph deleted.')), 'status'=>200, 'type' => 'json'));
|
||||
} else {
|
||||
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => 'EventGraph not deleted.')), 'status'=>200, 'type' => 'json'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,532 +0,0 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property EventReport $EventReport
|
||||
*/
|
||||
class EventReportsController extends AppController
|
||||
{
|
||||
public $components = array(
|
||||
'AdminCrud',
|
||||
'RequestHandler'
|
||||
);
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 60,
|
||||
'order' => array(
|
||||
'EventReport.event_id' => 'ASC',
|
||||
'EventReport.name' => 'ASC'
|
||||
),
|
||||
'recursive' => -1,
|
||||
'contain' => array(
|
||||
'SharingGroup' => array('fields' => array('id', 'name', 'uuid')),
|
||||
'Event' => array(
|
||||
'fields' => array('Event.id', 'Event.orgc_id', 'Event.org_id', 'Event.info', 'Event.user_id', 'Event.date'),
|
||||
'Orgc' => array('fields' => array('Orgc.id', 'Orgc.name')),
|
||||
'Org' => array('fields' => array('Org.id', 'Org.name'))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
public function add($eventId = false)
|
||||
{
|
||||
if ($this->request->is('get') && $this->_isRest()) {
|
||||
return $this->RestResponse->describe('EventReports', 'add', false, $this->response->type());
|
||||
}
|
||||
if ($eventId === false) {
|
||||
throw new MethodNotAllowedException(__('No event ID set.'));
|
||||
}
|
||||
$event = $this->__canModifyReport($eventId);
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
if (!isset($this->request->data['EventReport'])) {
|
||||
$this->request->data['EventReport'] = $this->request->data;
|
||||
}
|
||||
$report = $this->request->data;
|
||||
$errors = $this->EventReport->addReport($this->Auth->user(), $report, $eventId);
|
||||
$redirectTarget = array('controller' => 'events', 'action' => 'view', $eventId);
|
||||
if (!empty($errors)) {
|
||||
return $this->__getFailResponseBasedOnContext($errors, null, 'add', $this->EventReport->id, $redirectTarget);
|
||||
} else {
|
||||
$successMessage = __('Report saved.');
|
||||
$report = $this->EventReport->simpleFetchById($this->Auth->user(), $this->EventReport->id);
|
||||
return $this->__getSuccessResponseBasedOnContext($successMessage, $report, 'add', false, $redirectTarget);
|
||||
}
|
||||
}
|
||||
$this->set('event_id', $eventId);
|
||||
$this->set('action', 'add');
|
||||
$this->__injectDistributionLevelToViewContext();
|
||||
$this->__injectSharingGroupsDataToViewContext();
|
||||
}
|
||||
|
||||
|
||||
public function view($reportId, $ajax=false)
|
||||
{
|
||||
$report = $this->EventReport->simpleFetchById($this->Auth->user(), $reportId);
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($report, $this->response->type());
|
||||
}
|
||||
$this->set('ajax', $ajax);
|
||||
$this->set('id', $reportId);
|
||||
$this->set('report', $report);
|
||||
$this->set('title_for_layout', __('Event report %s', $report['EventReport']['name']));
|
||||
$this->__injectDistributionLevelToViewContext();
|
||||
$this->__injectPermissionsToViewContext($this->Auth->user(), $report);
|
||||
}
|
||||
|
||||
public function getProxyMISPElements($reportId)
|
||||
{
|
||||
if (!$this->_isRest()) {
|
||||
throw new MethodNotAllowedException(__('This function can only be reached via the API.'));
|
||||
}
|
||||
$user = $this->_closeSession();
|
||||
$report = $this->EventReport->simpleFetchById($user, $reportId);
|
||||
$proxyMISPElements = $this->EventReport->getProxyMISPElements($user, $report['EventReport']['event_id']);
|
||||
return $this->RestResponse->viewData($proxyMISPElements, $this->response->type());
|
||||
}
|
||||
|
||||
public function viewSummary($reportId)
|
||||
{
|
||||
$report = $this->EventReport->simpleFetchById($this->Auth->user(), $reportId);
|
||||
$this->set('id', $reportId);
|
||||
$this->set('report', $report);
|
||||
$this->__injectDistributionLevelToViewContext();
|
||||
$this->__injectPermissionsToViewContext($this->Auth->user(), $report);
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$savedReport = $this->EventReport->fetchIfAuthorized($this->Auth->user(), $id, 'edit', $throwErrors=true, $full=true);
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$newReport = $this->request->data;
|
||||
$newReport = $this->__applyDataFromSavedReport($newReport, $savedReport);
|
||||
$errors = $this->EventReport->editReport($this->Auth->user(), $newReport, $savedReport['EventReport']['event_id']);
|
||||
$redirectTarget = array('controller' => 'eventReports', 'action' => 'view', $id);
|
||||
if (!empty($errors)) {
|
||||
return $this->__getFailResponseBasedOnContext($validationErrors, array(), 'edit', $id, $redirectTarget);
|
||||
} else {
|
||||
$successMessage = __('Report saved.');
|
||||
$report = $this->EventReport->simpleFetchById($this->Auth->user(), $this->EventReport->id);
|
||||
return $this->__getSuccessResponseBasedOnContext($successMessage, $report, 'edit', $id, $redirectTarget);
|
||||
}
|
||||
} else {
|
||||
$this->request->data = $savedReport;
|
||||
}
|
||||
|
||||
$this->set('id', $savedReport['EventReport']['id']);
|
||||
$this->set('event_id', $savedReport['EventReport']['event_id']);
|
||||
$this->set('action', 'edit');
|
||||
$this->__injectDistributionLevelToViewContext();
|
||||
$this->__injectSharingGroupsDataToViewContext();
|
||||
$this->render('add');
|
||||
}
|
||||
|
||||
public function delete($id, $hard=false)
|
||||
{
|
||||
$report = $this->EventReport->fetchIfAuthorized($this->Auth->user(), $id, 'delete', $throwErrors=true, $full=false);
|
||||
if ($this->request->is('post')) {
|
||||
if (!empty($this->request->data['hard'])) {
|
||||
$hard = true;
|
||||
}
|
||||
$errors = $this->EventReport->deleteReport($this->Auth->user(), $report, $hard);
|
||||
$redirectTarget = $this->referer();
|
||||
if (empty($errors)) {
|
||||
$successMessage = __('Event Report %s %s deleted', $id, $hard ? __('hard') : __('soft'));
|
||||
return $this->__getSuccessResponseBasedOnContext($successMessage, null, 'delete', $id, $redirectTarget);
|
||||
} else {
|
||||
$errorMessage = __('Event Report %s could not be %s deleted.%sReasons: %s', $id, $hard ? __('hard') : __('soft'), PHP_EOL, json_encode($errors));
|
||||
return $this->__getFailResponseBasedOnContext($errorMessage, array(), 'edit', $id, $redirectTarget);
|
||||
}
|
||||
} else {
|
||||
if (!$this->request->is('ajax')) {
|
||||
throw new MethodNotAllowedException(__('This function can only be reached via AJAX.'));
|
||||
} else {
|
||||
$this->layout = false;
|
||||
$this->set('report', $report);
|
||||
$this->render('ajax/delete');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function restore($id)
|
||||
{
|
||||
$report = $this->EventReport->fetchIfAuthorized($this->Auth->user(), $id, 'edit', $throwErrors=true, $full=false);
|
||||
if ($this->request->is('post')) {
|
||||
$errors = $this->EventReport->restoreReport($this->Auth->user(), $id);
|
||||
$redirectTarget = $this->referer();
|
||||
if (empty($errors)) {
|
||||
$successMessage = __('Event Report %s restored', $id);
|
||||
return $this->__getSuccessResponseBasedOnContext($successMessage, null, 'restore', $id, $redirectTarget);
|
||||
} else {
|
||||
$errorMessage = __('Event Report %s could not be %s restored.%sReasons: %s', $id, PHP_EOL, json_encode($errors));
|
||||
return $this->__getFailResponseBasedOnContext($errorMessage, array(), 'restore', $id, $redirectTarget);
|
||||
}
|
||||
} else {
|
||||
if (!$this->request->is('ajax')) {
|
||||
throw new MethodNotAllowedException(__('This function can only be reached via AJAX.'));
|
||||
} else {
|
||||
$this->layout = false;
|
||||
$this->set('report', $report);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$filters = $this->IndexFilter->harvestParameters(['event_id', 'value', 'context', 'index_for_event', 'extended_event']);
|
||||
$filters['embedded_view'] = $this->request->is('ajax');
|
||||
$compiledConditions = $this->__generateIndexConditions($filters);
|
||||
if ($this->_isRest()) {
|
||||
$reports = $this->EventReport->find('all', [
|
||||
'recursive' => -1,
|
||||
'conditions' => $compiledConditions,
|
||||
'contain' => EventReport::DEFAULT_CONTAIN,
|
||||
]);
|
||||
return $this->RestResponse->viewData($reports, $this->response->type());
|
||||
} else {
|
||||
$this->paginate['conditions']['AND'][] = $compiledConditions;
|
||||
$reports = $this->paginate();
|
||||
$this->set('reports', $reports);
|
||||
$this->__injectIndexVariablesToViewContext($filters);
|
||||
if (!empty($filters['index_for_event'])) {
|
||||
if (empty($filters['event_id'])) {
|
||||
throw new MethodNotAllowedException("When requesting index for event, event ID must be provided.");
|
||||
}
|
||||
try {
|
||||
$this->__canModifyReport($filters['event_id']);
|
||||
$canModify = true;
|
||||
} catch (Exception $e) {
|
||||
$canModify = false;
|
||||
}
|
||||
$this->set('canModify', $canModify);
|
||||
$this->set('extendedEvent', !empty($filters['extended_event']));
|
||||
$fetcherModule = $this->EventReport->isFetchURLModuleEnabled();
|
||||
$this->set('importModuleEnabled', is_array($fetcherModule));
|
||||
$this->render('ajax/indexForEvent');
|
||||
} else {
|
||||
$this->set('title_for_layout', __('Event Reports'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function extractAllFromReport($reportId)
|
||||
{
|
||||
if (!$this->request->is('ajax')) {
|
||||
throw new MethodNotAllowedException(__('This function can only be reached via AJAX.'));
|
||||
}
|
||||
if ($this->request->is('post')) {
|
||||
$report = $this->EventReport->fetchIfAuthorized($this->Auth->user(), $reportId, 'edit', $throwErrors=true, $full=false);
|
||||
$results = $this->EventReport->getComplexTypeToolResultWithReplacements($this->Auth->user(), $report);
|
||||
$report['EventReport']['content'] = $results['replacementResult']['contentWithReplacements'];
|
||||
$contextResults = $this->EventReport->extractWithReplacements($this->Auth->user(), $report, ['replace' => true]);
|
||||
$suggestionResult = $this->EventReport->transformFreeTextIntoSuggestion($contextResults['contentWithReplacements'], $results['complexTypeToolResult']);
|
||||
$errors = $this->EventReport->applySuggestions($this->Auth->user(), $report, $suggestionResult['contentWithSuggestions'], $suggestionResult['suggestionsMapping']);
|
||||
if (empty($errors)) {
|
||||
if (!empty($this->data['EventReport']['tag_event'])) {
|
||||
$this->EventReport->attachTagsAfterReplacements($this->Auth->User(), $contextResults['replacedContext'], $report['EventReport']['event_id']);
|
||||
}
|
||||
$report = $this->EventReport->simpleFetchById($this->Auth->user(), $reportId);
|
||||
$data = [ 'report' => $report ];
|
||||
$successMessage = __('Automatic extraction applied to Event Report %s', $reportId);
|
||||
return $this->__getSuccessResponseBasedOnContext($successMessage, $data, 'applySuggestions', $reportId);
|
||||
} else {
|
||||
$errorMessage = __('Automatic extraction could not be applied to Event Report %s.%sReasons: %s', $reportId, PHP_EOL, json_encode($errors));
|
||||
return $this->__getFailResponseBasedOnContext($errorMessage, array(), 'applySuggestions', $reportId);
|
||||
}
|
||||
}
|
||||
$this->layout = false;
|
||||
$this->set('reportId', $reportId);
|
||||
$this->render('ajax/extractAllFromReport');
|
||||
}
|
||||
|
||||
public function extractFromReport($reportId)
|
||||
{
|
||||
if (!$this->request->is('ajax')) {
|
||||
throw new MethodNotAllowedException(__('This function can only be reached via AJAX.'));
|
||||
} else {
|
||||
$report = $this->EventReport->fetchIfAuthorized($this->Auth->user(), $reportId, 'view', $throwErrors=true, $full=false);
|
||||
$dataResults = $this->EventReport->getComplexTypeToolResultWithReplacements($this->Auth->user(), $report);
|
||||
$report['EventReport']['content'] = $dataResults['replacementResult']['contentWithReplacements'];
|
||||
$contextResults = $this->EventReport->extractWithReplacements($this->Auth->user(), $report);
|
||||
$typeToCategoryMapping = $this->EventReport->Event->Attribute->typeToCategoryMapping();
|
||||
$data = [
|
||||
'complexTypeToolResult' => $dataResults['complexTypeToolResult'],
|
||||
'typeToCategoryMapping' => $typeToCategoryMapping,
|
||||
'replacementValues' => $dataResults['replacementResult']['replacedValues'],
|
||||
'replacementContext' => $contextResults['replacedContext']
|
||||
];
|
||||
return $this->RestResponse->viewData($data, $this->response->type());
|
||||
}
|
||||
}
|
||||
|
||||
public function replaceSuggestionInReport($reportId)
|
||||
{
|
||||
if (!$this->request->is('ajax')) {
|
||||
throw new MethodNotAllowedException(__('This function can only be reached via AJAX.'));
|
||||
} else {
|
||||
$report = $this->EventReport->fetchIfAuthorized($this->Auth->user(), $reportId, 'edit', $throwErrors=true, $full=false);
|
||||
if ($this->request->is('post')) {
|
||||
$errors = [];
|
||||
$suggestions = $this->_jsonDecode($this->data['EventReport']['suggestions']);
|
||||
if (!empty($suggestions['content']) && !empty($suggestions['mapping'])) {
|
||||
$errors = $this->EventReport->applySuggestions($this->Auth->user(), $report, $suggestions['content'], $suggestions['mapping']);
|
||||
} else {
|
||||
$errors[] = __('`content` and `mapping` key cannot be empty');
|
||||
}
|
||||
if (empty($errors)) {
|
||||
$report = $this->EventReport->simpleFetchById($this->Auth->user(), $reportId);
|
||||
$results = $this->EventReport->getComplexTypeToolResultWithReplacements($this->Auth->user(), $report);
|
||||
$contextResults = $this->EventReport->extractWithReplacements($this->Auth->user(), $report);
|
||||
$data = [
|
||||
'report' => $report,
|
||||
'complexTypeToolResult' => $results['complexTypeToolResult'],
|
||||
'replacementValues' => $results['replacementResult']['replacedValues'],
|
||||
'replacementContext' => $contextResults['replacedContext']
|
||||
];
|
||||
$successMessage = __('Suggestions applied to Event Report %s', $reportId);
|
||||
return $this->__getSuccessResponseBasedOnContext($successMessage, $data, 'applySuggestions', $reportId);
|
||||
} else {
|
||||
$errorMessage = __('Suggestions could not be applied to Event Report %s.%sReasons: %s', $reportId, PHP_EOL, json_encode($errors));
|
||||
return $this->__getFailResponseBasedOnContext($errorMessage, array(), 'applySuggestions', $reportId);
|
||||
}
|
||||
}
|
||||
$this->layout = false;
|
||||
$this->render('ajax/replaceSuggestionInReport');
|
||||
}
|
||||
}
|
||||
|
||||
public function importReportFromUrl($event_id)
|
||||
{
|
||||
if (!$this->request->is('ajax')) {
|
||||
throw new MethodNotAllowedException(__('This function can only be reached via AJAX.'));
|
||||
}
|
||||
$fetcherModule = $this->EventReport->isFetchURLModuleEnabled();
|
||||
if ($this->request->is('post')) {
|
||||
if (empty($this->data['EventReport']['url'])) {
|
||||
throw new MethodNotAllowedException(__('An URL must be provided'));
|
||||
}
|
||||
$url = $this->data['EventReport']['url'];
|
||||
$markdown = $this->EventReport->downloadMarkdownFromURL($event_id, $url);
|
||||
$errors = [];
|
||||
if (!empty($markdown)) {
|
||||
$report = [
|
||||
'name' => __('Report from - %s (%s)', $url, time()),
|
||||
'distribution' => 5,
|
||||
'content' => $markdown
|
||||
];
|
||||
$errors = $this->EventReport->addReport($this->Auth->user(), $report, $event_id);
|
||||
} else {
|
||||
$errors[] = __('Could not fetch report from URL. Fetcher module not enabled or could not download the page');
|
||||
}
|
||||
$redirectTarget = array('controller' => 'events', 'action' => 'view', $event_id);
|
||||
if (!empty($errors)) {
|
||||
return $this->__getFailResponseBasedOnContext($errors, array(), 'addFromURL', $this->EventReport->id, $redirectTarget);
|
||||
} else {
|
||||
$successMessage = __('Report downloaded and created');
|
||||
$report = $this->EventReport->simpleFetchById($this->Auth->user(), $this->EventReport->id);
|
||||
return $this->__getSuccessResponseBasedOnContext($successMessage, $report, 'addFromURL', false, $redirectTarget);
|
||||
}
|
||||
}
|
||||
$this->set('importModuleEnabled', is_array($fetcherModule));
|
||||
$this->set('event_id', $event_id);
|
||||
$this->layout = false;
|
||||
$this->render('ajax/importReportFromUrl');
|
||||
}
|
||||
|
||||
public function reportFromEvent($eventId)
|
||||
{
|
||||
$event = $this->__canModifyReport($eventId);
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$filters = $this->_jsonDecode($this->data['EventReport']['filters']);
|
||||
$options['conditions'] = $filters;
|
||||
$options['conditions'] = array_filter($filters, function($v) {
|
||||
return $v !== '';
|
||||
});
|
||||
$options['event_id'] = $eventId;
|
||||
App::uses('ReportFromEvent', 'EventReport');
|
||||
$optionFields = array_keys((new ReportFromEvent())->acceptedOptions);
|
||||
foreach ($optionFields as $field) {
|
||||
if (isset($this->data['EventReport'][$field])) {
|
||||
$options[$field] = $this->data['EventReport'][$field];
|
||||
}
|
||||
}
|
||||
$markdown = $this->EventReport->getReportFromEvent($this->Auth->user(), $options);
|
||||
if (!empty($markdown)) {
|
||||
$report = [
|
||||
'name' => __('Event report (%s)', time()),
|
||||
'distribution' => 5,
|
||||
'content' => $markdown
|
||||
];
|
||||
$errors = $this->EventReport->addReport($this->Auth->user(), $report, $eventId);
|
||||
} else {
|
||||
$errors[] = __('Could not generate markdown from the event');
|
||||
}
|
||||
$redirectTarget = array('controller' => 'events', 'action' => 'view', $eventId);
|
||||
if (!empty($errors)) {
|
||||
return $this->__getFailResponseBasedOnContext($errors, array(), 'add', $this->EventReport->id, $redirectTarget);
|
||||
} else {
|
||||
$successMessage = __('Report saved.');
|
||||
$report = $this->EventReport->simpleFetchById($this->Auth->user(), $this->EventReport->id);
|
||||
return $this->__getSuccessResponseBasedOnContext($successMessage, $report, 'add', false, $redirectTarget);
|
||||
}
|
||||
}
|
||||
$this->set('event_id', $eventId);
|
||||
$this->layout = false;
|
||||
$this->render('ajax/reportFromEvent');
|
||||
}
|
||||
|
||||
private function __generateIndexConditions($filters = [])
|
||||
{
|
||||
$aclConditions = $this->EventReport->buildACLConditions($this->Auth->user());
|
||||
$eventConditions = [];
|
||||
if (!empty($filters['event_id'])) {
|
||||
$extendingEventIds = [];
|
||||
if (!empty($filters['extended_event'])) {
|
||||
$extendingEventIds = $this->EventReport->Event->getExtendingEventIdsFromEvent($this->Auth->user(), $filters['event_id']);
|
||||
}
|
||||
$eventConditions = ['EventReport.event_id' => array_merge([$filters['event_id']], $extendingEventIds)];
|
||||
}
|
||||
|
||||
$contextConditions = [];
|
||||
if (empty($filters['context'])) {
|
||||
$filters['context'] = 'default';
|
||||
}
|
||||
if ($filters['context'] == 'deleted') {
|
||||
$contextConditions['EventReport.deleted'] = true;
|
||||
} elseif ($filters['context'] == 'default') {
|
||||
$contextConditions['EventReport.deleted'] = false;
|
||||
}
|
||||
$searchConditions = [];
|
||||
if (empty($filters['value'])) {
|
||||
$filters['value'] = '';
|
||||
} else {
|
||||
$searchall = '%' . strtolower($filters['value']) . '%';
|
||||
$searchConditions = array(
|
||||
'OR' => array(
|
||||
'LOWER(EventReport.name) LIKE' => $searchall,
|
||||
'LOWER(EventReport.content) LIKE' => $searchall,
|
||||
'EventReport.id' => $searchall,
|
||||
'EventReport.uuid' => $searchall
|
||||
)
|
||||
);
|
||||
}
|
||||
$compiledConditions = [
|
||||
'AND' => [
|
||||
$aclConditions,
|
||||
$eventConditions,
|
||||
$contextConditions,
|
||||
$searchConditions,
|
||||
]
|
||||
];
|
||||
return $compiledConditions;
|
||||
}
|
||||
|
||||
private function __getSuccessResponseBasedOnContext($message, $data = null, $action = '', $id = false, $redirect = array())
|
||||
{
|
||||
if ($this->_isRest()) {
|
||||
if (!is_null($data)) {
|
||||
return $this->RestResponse->viewData($data, $this->response->type());
|
||||
} else {
|
||||
return $this->RestResponse->saveSuccessResponse($this->EventReport->alias, $action, $id, false, $message);
|
||||
}
|
||||
} elseif ($this->request->is('ajax')) {
|
||||
return $this->RestResponse->saveSuccessResponse($this->EventReport->alias, $action, $id, false, $message, $data);
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect($redirect);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private function __getFailResponseBasedOnContext($message, $data = null, $action = '', $id = false, $redirect = array())
|
||||
{
|
||||
if (is_array($message)) {
|
||||
$message = implode(', ', $message);
|
||||
}
|
||||
if ($this->_isRest()) {
|
||||
if ($data !== null) {
|
||||
return $this->RestResponse->viewData($data, $this->response->type());
|
||||
} else {
|
||||
return $this->RestResponse->saveFailResponse('EventReport', $action, $id, $message);
|
||||
}
|
||||
} elseif ($this->request->is('ajax')) {
|
||||
return $this->RestResponse->saveFailResponse('EventReport', $action, $id, $message, false, $data);
|
||||
} else {
|
||||
$this->Flash->error($message);
|
||||
$this->redirect($this->referer());
|
||||
}
|
||||
}
|
||||
|
||||
private function __injectIndexVariablesToViewContext($filters)
|
||||
{
|
||||
if (!empty($filters['context'])) {
|
||||
$this->set('context', $filters['context']);
|
||||
} else {
|
||||
$this->set('context', 'default');
|
||||
}
|
||||
if (!empty($filters['event_id'])) {
|
||||
$this->set('event_id', $filters['event_id']);
|
||||
}
|
||||
if (isset($filters['embedded_view'])) {
|
||||
$this->set('embedded_view', $filters['embedded_view']);
|
||||
} else {
|
||||
$this->set('embedded_view', false);
|
||||
}
|
||||
if (!empty($filters['value'])) {
|
||||
$this->set('searchall', $filters['value']);
|
||||
} else {
|
||||
$this->set('searchall', '');
|
||||
}
|
||||
$this->__injectDistributionLevelToViewContext();
|
||||
}
|
||||
|
||||
private function __injectDistributionLevelToViewContext()
|
||||
{
|
||||
$distributionLevels = $this->EventReport->Event->Attribute->distributionLevels;
|
||||
$this->set('distributionLevels', $distributionLevels);
|
||||
$this->set('initialDistribution', $this->EventReport->Event->Attribute->defaultDistribution());
|
||||
}
|
||||
|
||||
private function __injectSharingGroupsDataToViewContext()
|
||||
{
|
||||
$sgs = $this->EventReport->Event->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'name', 1);
|
||||
$this->set('sharingGroups', $sgs);
|
||||
}
|
||||
|
||||
private function __injectPermissionsToViewContext(array $user, array $report)
|
||||
{
|
||||
$canEdit = $this->ACL->canEditEventReport($user, $report);
|
||||
$this->set('canEdit', $canEdit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $eventId
|
||||
* @return array
|
||||
* @throws NotFoundException
|
||||
* @throws ForbiddenException
|
||||
*/
|
||||
private function __canModifyReport($eventId)
|
||||
{
|
||||
$event = $this->EventReport->Event->fetchSimpleEvent($this->Auth->user(), $eventId);
|
||||
if (empty($event)) {
|
||||
throw new NotFoundException(__('Invalid event'));
|
||||
}
|
||||
if (!$this->__canModifyEvent($event)) {
|
||||
throw new ForbiddenException(__('You do not have permission to do that.'));
|
||||
}
|
||||
return $event;
|
||||
}
|
||||
|
||||
private function __applyDataFromSavedReport($newReport, $savedReport)
|
||||
{
|
||||
if (!isset($newReport['EventReport'])) {
|
||||
$newReport = array('EventReport' => $newReport);
|
||||
}
|
||||
$ignoreFieldList = ['id', 'uuid', 'event_id', 'deleted'];
|
||||
foreach (EventReport::CAPTURE_FIELDS as $field) {
|
||||
if (!in_array($field, $ignoreFieldList) && isset($newReport['EventReport'][$field])) {
|
||||
$savedReport['EventReport'][$field] = $newReport['EventReport'][$field];
|
||||
}
|
||||
}
|
||||
return $savedReport;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,58 +0,0 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
class FavouriteTagsController extends AppController
|
||||
{
|
||||
public $components = array('Session', 'RequestHandler');
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 60,
|
||||
'maxLimit' => 9999, // LATER we will bump here on a problem once we have more than 9999 events <- no we won't, this is the max a user van view/page.
|
||||
'order' => array(
|
||||
'FavouriteTag.id' => 'DESC'
|
||||
),
|
||||
);
|
||||
|
||||
public function toggle()
|
||||
{
|
||||
if (!$this->request->is('post')) {
|
||||
throw new MethodNotAllowedException('This action is only available via POST requests.');
|
||||
}
|
||||
if (!is_numeric($this->request->data['FavouriteTag']['data'])) {
|
||||
throw new MethodNotAllowedException('Invalid tag ID.');
|
||||
}
|
||||
$this->FavouriteTag->Tag->id = $this->request->data['FavouriteTag']['data'];
|
||||
if (!$this->FavouriteTag->Tag->exists()) {
|
||||
throw new MethodNotAllowedException('Invalid tag ID.');
|
||||
}
|
||||
$data = array('tag_id' => $this->request->data['FavouriteTag']['data'], 'user_id' => $this->Auth->user('id'));
|
||||
$existingFavourite = $this->FavouriteTag->find('first', array('conditions' => $data, 'recursive' => -1));
|
||||
$success = false;
|
||||
if (empty($existingFavourite)) {
|
||||
$message = 'Adding the tag to your favourites';
|
||||
$this->FavouriteTag->create();
|
||||
if ($this->FavouriteTag->save($data)) {
|
||||
$success = true;
|
||||
}
|
||||
} else {
|
||||
$message = 'Removing the tag from your favourites';
|
||||
if ($this->FavouriteTag->deleteAll($data, false)) {
|
||||
$success = true;
|
||||
}
|
||||
}
|
||||
if ($success) {
|
||||
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => $message . ' was successful.')), 'status'=>200, 'type' => 'json'));
|
||||
} else {
|
||||
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'fails' => $message . ' has failed.')), 'status'=>200, 'type' => 'json'));
|
||||
}
|
||||
}
|
||||
|
||||
public function getToggleField()
|
||||
{
|
||||
if (!$this->request->is('ajax')) {
|
||||
throw new MethodNotAllowedException('This action is available via AJAX only.');
|
||||
}
|
||||
$this->layout = false;
|
||||
$this->render('ajax/getToggleField');
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue