mirror of https://github.com/MISP/MISP
Merge remote-tracking branch 'origin/develop' into feature-galaxy-element-tree-view
commit
25f276defd
|
@ -0,0 +1,67 @@
|
|||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ 2.4 ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ 2.4 ]
|
||||
schedule:
|
||||
- cron: '31 16 * * 1'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'javascript', 'python' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
|
@ -20,7 +20,7 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
os: [ubuntu-20.04]
|
||||
php: ['7.2', '7.3', '7.4']
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
|
@ -66,9 +66,14 @@ jobs:
|
|||
run: |
|
||||
git submodule update --init --recursive
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install python3 python3-venv virtualenv python3-pip python3-nose python3-redis python3-lxml apache2 curl libapache2-mod-php libfuzzy-dev libemail-address-perl libemail-outlook-message-perl
|
||||
sudo pip3 install --upgrade pip setuptools requests pyzmq poetry
|
||||
if [[ $php_version == "7.4" ]]; then
|
||||
# Repo is missing for unknown reason
|
||||
LC_ALL=C.UTF-8 sudo apt-add-repository ppa:ondrej/php -y
|
||||
fi
|
||||
sudo apt-get -y install curl python3 python3-zmq python3-requests python3-pip python3-nose python3-redis python3-lxml apache2 libapache2-mod-php$php_version libfuzzy-dev
|
||||
sudo pip3 install virtualenv # virtualenv must be instaled from pip and not from ubuntu pacckages
|
||||
sudo pip3 install --upgrade -r requirements.txt
|
||||
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -
|
||||
sudo chown $USER:www-data $HOME/.composer
|
||||
pushd app
|
||||
sudo -H -u $USER php composer.phar install --no-progress
|
||||
|
@ -185,7 +190,7 @@ jobs:
|
|||
sudo chmod -R 777 ./tests
|
||||
# Start workers
|
||||
# Dirty install python stuff
|
||||
virtualenv -p python3 ./venv
|
||||
python3 -m virtualenv -p python3 ./venv
|
||||
sudo -E su $USER -c 'app/Console/cake Admin setSetting "MISP.python_bin" "$GITHUB_WORKSPACE/venv/bin/python"'
|
||||
. ./venv/bin/activate
|
||||
pushd cti-python-stix2
|
||||
|
@ -209,9 +214,11 @@ jobs:
|
|||
|
||||
- name: Run tests
|
||||
run: |
|
||||
source $HOME/.poetry/env # enable poetry binary
|
||||
./app/Vendor/bin/parallel-lint --exclude app/Lib/cakephp/ --exclude app/Vendor/ --exclude app/Lib/random_compat/ -e php,ctp app/
|
||||
./app/Vendor/bin/phpunit app/Test/ComplexTypeToolTest.php
|
||||
./app/Vendor/bin/phpunit app/Test/JSONConverterToolTest.php
|
||||
./app/Vendor/bin/phpunit app/Test/CidrToolTest.php
|
||||
|
||||
# Ensure the perms of config files
|
||||
sudo chown -R $USER:www-data `pwd`/app/Config
|
||||
|
@ -220,14 +227,15 @@ jobs:
|
|||
sudo chown -R $USER:www-data `pwd`/app/Config
|
||||
sudo chmod -R 777 `pwd`/app/Config
|
||||
|
||||
python3 tests/testlive_security.py
|
||||
|
||||
pushd tests
|
||||
./curl_tests_GH.sh $AUTH $HOST
|
||||
popd
|
||||
|
||||
pushd PyMISP
|
||||
poetry install -E fileobjects -E openioc -E virustotal -E docs -E pdfexport -E email
|
||||
poetry run python tests/testlive_comprehensive.py
|
||||
poetry add lxml
|
||||
poetry run python ../tests/testlive_security.py -v
|
||||
poetry run python tests/test_mispevent.py
|
||||
popd
|
||||
cp PyMISP/tests/keys.py PyMISP/examples/events/
|
||||
|
|
|
@ -620,7 +620,7 @@ preInstall () {
|
|||
DBPASSWORD_MISP=$(cat database.php |grep -v // |grep -e password |tr -d \' |tr -d \ |tr -d , |tr -d \> |cut -f 2 -d=)
|
||||
DBUSER_MISP=$(cat database.php |grep -v // |grep -e login |tr -d \' |tr -d \ |tr -d , |tr -d \> |cut -f 2 -d=)
|
||||
DBNAME=$(cat database.php |grep -v // |grep -e database |tr -d \' |tr -d \ |tr -d , |tr -d \> |cut -f 2 -d=)
|
||||
AUTH_KEY=$(mysql --disable-column-names -B -u $DBUSER_MISP -p"$DBPASSWORD_MISP" $DBNAME -e 'SELECT authkey FROM users WHERE role_id=1 LIMIT 1')
|
||||
AUTH_KEY=$(mysql -h $DBHOST --disable-column-names -B -u $DBUSER_MISP -p"$DBPASSWORD_MISP" $DBNAME -e 'SELECT authkey FROM users WHERE role_id=1 LIMIT 1')
|
||||
|
||||
# Check if db exists
|
||||
[[ -d "/var/lib/mysql/$DBNAME" ]] && MISP_DB_DIR_EXISTS=1 && echo "/var/lib/mysql/$DBNAME exists"
|
||||
|
@ -675,6 +675,7 @@ kaliSpaceSaver () {
|
|||
echo "${RED}Not implement${NC}"
|
||||
}
|
||||
|
||||
# FIXME: Kali now uses kali/kali instead of root/toor
|
||||
# Because Kali is l33t we make sure we DO NOT run as root
|
||||
kaliOnTheR0ckz () {
|
||||
totalRoot=$(df -k | grep /$ |awk '{ print $2 }')
|
||||
|
@ -776,6 +777,7 @@ installRNG () {
|
|||
kaliUpgrade () {
|
||||
debug "Running various Kali upgrade tasks"
|
||||
checkAptLock
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt update
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt install --only-upgrade bash libc6 -y
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt autoremove -y
|
||||
}
|
||||
|
@ -1071,8 +1073,8 @@ nuke () {
|
|||
sleep 10
|
||||
sudo rm -rvf /usr/local/src/{misp-modules,viper,mail_to_misp,LIEF,faup}
|
||||
sudo rm -rvf /var/www/MISP
|
||||
sudo mysqladmin drop misp
|
||||
sudo mysql -e "DROP USER misp@localhost"
|
||||
sudo mysqladmin -h $DBHOST drop misp
|
||||
sudo mysql -h $DBHOST -e "DROP USER misp@localhost"
|
||||
}
|
||||
|
||||
# Final function to let the user know what happened
|
||||
|
@ -1197,8 +1199,9 @@ installDepsPhp74 () {
|
|||
libapache2-mod-php \
|
||||
php php-cli \
|
||||
php-dev \
|
||||
php-json php-xml php-mysql php-opcache php-readline php-mbstring php-zip \
|
||||
php-json php-xml php-mysql php7.4-opcache php-readline php-mbstring php-zip \
|
||||
php-redis php-gnupg \
|
||||
php-intl php-bcmath \
|
||||
php-gd
|
||||
|
||||
for key in upload_max_filesize post_max_size max_execution_time max_input_time memory_limit
|
||||
|
@ -1250,6 +1253,7 @@ installDepsPhp72 () {
|
|||
php-dev \
|
||||
php-json php-xml php-mysql php7.2-opcache php-readline php-mbstring php-zip \
|
||||
php-redis php-gnupg \
|
||||
php-intl php-bcmath \
|
||||
php-gd
|
||||
|
||||
for key in upload_max_filesize post_max_size max_execution_time max_input_time memory_limit
|
||||
|
@ -1292,25 +1296,25 @@ prepareDB () {
|
|||
|
||||
debug "Setting up database"
|
||||
# Kill the anonymous users
|
||||
sudo mysql -e "DROP USER IF EXISTS ''@'localhost'"
|
||||
sudo mysql -h $DBHOST -e "DROP USER IF EXISTS ''@'localhost'"
|
||||
# Because our hostname varies we'll use some Bash magic here.
|
||||
sudo mysql -e "DROP USER IF EXISTS ''@'$(hostname)'"
|
||||
sudo mysql -h $DBHOST -e "DROP USER IF EXISTS ''@'$(hostname)'"
|
||||
# Kill off the demo database
|
||||
sudo mysql -e "DROP DATABASE IF EXISTS test"
|
||||
sudo mysql -h $DBHOST -e "DROP DATABASE IF EXISTS test"
|
||||
# No root remote logins
|
||||
sudo mysql -e "DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1')"
|
||||
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 -u "${DBUSER_ADMIN}" password "${DBPASSWORD_ADMIN}"
|
||||
sudo mysqladmin -h $DBHOST -u "${DBUSER_ADMIN}" password "${DBPASSWORD_ADMIN}"
|
||||
# Make our changes take effect
|
||||
sudo mysql -e "FLUSH PRIVILEGES"
|
||||
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 "CREATE USER '${DBUSER_MISP}'@'localhost' IDENTIFIED BY '${DBPASSWORD_MISP}';"
|
||||
sudo mysql -u "${DBUSER_ADMIN}" -p"${DBPASSWORD_ADMIN}" -e "GRANT USAGE ON *.* to '${DBUSER_MISP}'@'localhost';"
|
||||
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;"
|
||||
sudo mysql -h $DBHOST -u "${DBUSER_ADMIN}" -p"${DBPASSWORD_ADMIN}" -e "CREATE DATABASE ${DBNAME};"
|
||||
sudo mysql -h $DBHOST -u "${DBUSER_ADMIN}" -p"${DBPASSWORD_ADMIN}" -e "CREATE USER '${DBUSER_MISP}'@'localhost' IDENTIFIED BY '${DBPASSWORD_MISP}';"
|
||||
sudo mysql -h $DBHOST -u "${DBUSER_ADMIN}" -p"${DBPASSWORD_ADMIN}" -e "GRANT USAGE ON *.* to '${DBUSER_MISP}'@'localhost';"
|
||||
sudo mysql -h $DBHOST -u "${DBUSER_ADMIN}" -p"${DBPASSWORD_ADMIN}" -e "GRANT ALL PRIVILEGES on ${DBNAME}.* to '${DBUSER_MISP}'@'localhost';"
|
||||
sudo mysql -h $DBHOST -u "${DBUSER_ADMIN}" -p"${DBPASSWORD_ADMIN}" -e "FLUSH PRIVILEGES;"
|
||||
# Import the empty MISP database from MYSQL.sql
|
||||
${SUDO_WWW} cat ${PATH_TO_MISP}/INSTALL/MYSQL.sql | mysql -u "${DBUSER_MISP}" -p"${DBPASSWORD_MISP}" ${DBNAME}
|
||||
${SUDO_WWW} cat ${PATH_TO_MISP}/INSTALL/MYSQL.sql | mysql -h $DBHOST -u "${DBUSER_MISP}" -p"${DBPASSWORD_MISP}" ${DBNAME}
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -1372,7 +1376,7 @@ installCore () {
|
|||
for dependency in CybOXProject/python-cybox STIXProject/python-stix MAECProject/python-maec CybOXProject/mixbox; do
|
||||
false; while [[ $? -ne 0 ]]; do checkAptLock; ${SUDO_WWW} git clone https://github.com/${dependency}.git ${PATH_TO_MISP_SCRIPTS}/${dependency##*/}; done
|
||||
${SUDO_WWW} git -C ${PATH_TO_MISP_SCRIPTS}/${dependency##*/} config core.filemode false
|
||||
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install ${PATH_TO_MISP_SCRIPTS}/${dependency##/*}
|
||||
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install ${PATH_TO_MISP_SCRIPTS}/${dependency##*/}
|
||||
done
|
||||
|
||||
debug "Install python-stix2"
|
||||
|
@ -1384,8 +1388,8 @@ installCore () {
|
|||
# FIXME: Remove libfaup etc once the egg has the library baked-in
|
||||
sudo apt-get install cmake libcaca-dev liblua5.3-dev -y
|
||||
cd /tmp
|
||||
false; while [[ $? -ne 0 ]]; do [[ ! -d "faup" ]] && ${SUDO_CMD} git clone git://github.com/stricaud/faup.git faup; done
|
||||
false; while [[ $? -ne 0 ]]; do [[ ! -d "gtcaca" ]] && ${SUDO_CMD} git clone git://github.com/stricaud/gtcaca.git gtcaca; done
|
||||
false; while [[ $? -ne 0 ]]; do [[ ! -d "faup" ]] && ${SUDO_CMD} git clone https://github.com/stricaud/faup.git faup; done
|
||||
false; while [[ $? -ne 0 ]]; do [[ ! -d "gtcaca" ]] && ${SUDO_CMD} git clone https://github.com/stricaud/gtcaca.git gtcaca; done
|
||||
sudo chown -R ${MISP_USER}:${MISP_USER} faup gtcaca
|
||||
cd gtcaca
|
||||
${SUDO_CMD} mkdir -p build
|
||||
|
@ -1421,7 +1425,7 @@ installCore () {
|
|||
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install -U setuptools pip lief zmq redis python-magic plyara
|
||||
for dependency in CybOXProject/python-cybox STIXProject/python-stix MAECProject/python-maec CybOXProject/mixbox; do
|
||||
false; while [[ $? -ne 0 ]]; do checkAptLock; ${SUDO_WWW} git -C ${PATH_TO_MISP_SCRIPTS}/${dependency##*/} pull; done
|
||||
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install -U ${PATH_TO_MISP_SCRIPTS}/${dependency##/*}
|
||||
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install -U ${PATH_TO_MISP_SCRIPTS}/${dependency##*/}
|
||||
done
|
||||
|
||||
${SUDO_WWW} ${PATH_TO_MISP}/venv/bin/pip install -U ${PATH_TO_MISP}/cti-python-stix2
|
||||
|
@ -1557,6 +1561,7 @@ coreCAKE () {
|
|||
# Various plugin sightings settings
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "Plugin.Sightings_policy" 0
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "Plugin.Sightings_anonymise" false
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "Plugin.Sightings_anonymise_as" 1
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "Plugin.Sightings_range" 365
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "Plugin.Sightings_sighting_db_enable" false
|
||||
|
||||
|
@ -1592,11 +1597,14 @@ coreCAKE () {
|
|||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "MISP.default_event_threat_level" 4
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "MISP.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"
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "MISP.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, where 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,\\nYour \$org MISP support team"
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "MISP.enableEventBlacklisting" true
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "MISP.enableOrgBlacklisting" true
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "MISP.enableEventBlocklisting" true
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "MISP.enableOrgBlocklisting" true
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "MISP.log_client_ip" false
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "MISP.log_auth" false
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "MISP.disableUserSelfManagement" false
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "MISP.disable_user_login_change" false
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "MISP.disable_user_password_change" false
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "MISP.disable_user_add" false
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "MISP.block_event_alert" false
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "MISP.block_event_alert_tag" "no-alerts=\"true\""
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "MISP.block_old_event_alert" false
|
||||
|
@ -1617,6 +1625,10 @@ coreCAKE () {
|
|||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "MISP.event_view_filter_fields" "id, uuid, value, comment, type, category, Tag.name"
|
||||
|
||||
# Force defaults to make MISP Server Settings less GREEN
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "debug" 0
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "Security.auth_enforced" false
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "Security.rest_client_baseurl" ""
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "Security.advanced_authkeys" false
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "Security.password_policy_length" 12
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "Security.password_policy_complexity" '/^((?=.*\d)|(?=.*\W+))(?![\n])(?=.*[A-Z])(?=.*[a-z]).*$|.{16,}/'
|
||||
$SUDO_WWW $RUN_PHP -- $CAKE Admin setSetting "Security.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."
|
||||
|
@ -1632,7 +1644,7 @@ coreCAKE () {
|
|||
updateGOWNT () {
|
||||
# AUTH_KEY Place holder in case we need to **curl** somehing in the future
|
||||
#
|
||||
$SUDO_WWW $RUN_MYSQL -- mysql -u $DBUSER_MISP -p$DBPASSWORD_MISP misp -e "SELECT authkey FROM users;" | tail -1 > /tmp/auth.key
|
||||
$SUDO_WWW $RUN_MYSQL -- mysql -h $DBHOST -u $DBUSER_MISP -p$DBPASSWORD_MISP misp -e "SELECT authkey FROM users;" | tail -1 > /tmp/auth.key
|
||||
AUTH_KEY=$(cat /tmp/auth.key)
|
||||
rm /tmp/auth.key
|
||||
|
||||
|
@ -1731,8 +1743,8 @@ mispmodules () {
|
|||
fi
|
||||
|
||||
# Install faup/gtcaca
|
||||
[[ ! -d "faup" ]] && false; while [[ $? -ne 0 ]]; do $SUDO_CMD git clone git://github.com/stricaud/faup.git faup; done
|
||||
[[ ! -d "gtcaca" ]] && false; while [[ $? -ne 0 ]]; do $SUDO_CMD git clone git://github.com/stricaud/gtcaca.git gtcaca; done
|
||||
[[ ! -d "faup" ]] && false; while [[ $? -ne 0 ]]; do $SUDO_CMD git clone https://github.com/stricaud/faup.git faup; done
|
||||
[[ ! -d "gtcaca" ]] && false; while [[ $? -ne 0 ]]; do $SUDO_CMD git clone https://github.com/stricaud/gtcaca.git gtcaca; done
|
||||
sudo chown -R ${MISP_USER}:${MISP_USER} faup gtcaca
|
||||
# Install gtcaca
|
||||
cd gtcaca
|
||||
|
@ -1740,7 +1752,7 @@ mispmodules () {
|
|||
cd build
|
||||
$SUDO_CMD cmake .. && $SUDO_CMD make
|
||||
sudo make install
|
||||
cd /usr/loca/src/faup
|
||||
cd /usr/local/src/faup
|
||||
# Install faup
|
||||
$SUDO_CMD mkdir -p build
|
||||
cd build
|
||||
|
@ -1757,6 +1769,7 @@ mispmodules () {
|
|||
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install -I -r REQUIREMENTS
|
||||
sudo chgrp staff .
|
||||
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install -I .
|
||||
$SUDO_WWW ${PATH_TO_MISP}/venv/bin/pip install censys pyfaup
|
||||
|
||||
# Start misp-modules as a service
|
||||
sudo cp /usr/local/src/misp-modules/etc/systemd/system/misp-modules.service /etc/systemd/system/
|
||||
|
@ -1769,6 +1782,7 @@ mispmodules () {
|
|||
# Enable Enrichment, set better timeouts
|
||||
$SUDO_WWW $CAKE Admin setSetting "Plugin.Enrichment_services_enable" true
|
||||
$SUDO_WWW $CAKE Admin setSetting "Plugin.Enrichment_hover_enable" true
|
||||
$SUDO_WWW $CAKE Admin setSetting "Plugin.Enrichment_hover_popover_only" false
|
||||
$SUDO_WWW $CAKE Admin setSetting "Plugin.Enrichment_timeout" 300
|
||||
$SUDO_WWW $CAKE Admin setSetting "Plugin.Enrichment_hover_timeout" 150
|
||||
# TODO:"Investigate why the next one fails"
|
||||
|
@ -1903,8 +1917,8 @@ mail2misp () {
|
|||
sudo apt-get install cmake libcaca-dev liblua5.3-dev -y
|
||||
false; while [[ $? -ne 0 ]]; do $SUDO_CMD git clone https://github.com/MISP/mail_to_misp.git; done
|
||||
## TODO: The below fails miserably (obviously) if faup/gtcac dirs exist, let's just make the dangerous assumption (for the sake of the installer, that they exist)
|
||||
##[[ ! -d "faup" ]] && false; while [[ $? -ne 0 ]]; do $SUDO_CMD git clone git://github.com/stricaud/faup.git faup; done
|
||||
##[[ ! -d "gtcaca" ]] && false; while [[ $? -ne 0 ]]; do $SUDO_CMD git clone git://github.com/stricaud/gtcaca.git gtcaca; done
|
||||
##[[ ! -d "faup" ]] && false; while [[ $? -ne 0 ]]; do $SUDO_CMD git clone https://github.com/stricaud/faup.git faup; done
|
||||
##[[ ! -d "gtcaca" ]] && false; while [[ $? -ne 0 ]]; do $SUDO_CMD git clone https://github.com/stricaud/gtcaca.git gtcaca; done
|
||||
sudo chown -R ${MISP_USER}:${MISP_USER} faup mail_to_misp gtcaca
|
||||
cd gtcaca
|
||||
$SUDO_CMD mkdir -p build
|
||||
|
@ -2172,8 +2186,8 @@ installCoreRHEL () {
|
|||
# BROKEN: This needs to be tested on RHEL/CentOS
|
||||
##sudo apt-get install cmake libcaca-dev liblua5.3-dev -y
|
||||
cd /tmp
|
||||
[[ ! -d "faup" ]] && $SUDO_CMD git clone git://github.com/stricaud/faup.git faup
|
||||
[[ ! -d "gtcaca" ]] && $SUDO_CMD git clone git://github.com/stricaud/gtcaca.git gtcaca
|
||||
[[ ! -d "faup" ]] && $SUDO_CMD git clone https://github.com/stricaud/faup.git faup
|
||||
[[ ! -d "gtcaca" ]] && $SUDO_CMD git clone https://github.com/stricaud/gtcaca.git gtcaca
|
||||
sudo chown -R ${MISP_USER}:${MISP_USER} faup gtcaca
|
||||
cd gtcaca
|
||||
$SUDO_CMD mkdir -p build
|
||||
|
@ -2296,12 +2310,12 @@ EOF
|
|||
|
||||
sudo systemctl restart rh-mariadb102-mariadb
|
||||
|
||||
scl enable rh-mariadb102 "mysql -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e 'CREATE DATABASE $DBNAME;'"
|
||||
scl enable rh-mariadb102 "mysql -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e \"GRANT USAGE on *.* to $DBUSER_MISP@localhost IDENTIFIED by '$DBPASSWORD_MISP';\""
|
||||
scl enable rh-mariadb102 "mysql -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e \"GRANT ALL PRIVILEGES on $DBNAME.* to '$DBUSER_MISP'@'localhost';\""
|
||||
scl enable rh-mariadb102 "mysql -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e 'FLUSH PRIVILEGES;'"
|
||||
scl enable rh-mariadb102 "mysql -h $DBHOST -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e 'CREATE DATABASE $DBNAME;'"
|
||||
scl enable rh-mariadb102 "mysql -h $DBHOST -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e \"GRANT USAGE on *.* to $DBUSER_MISP@localhost IDENTIFIED by '$DBPASSWORD_MISP';\""
|
||||
scl enable rh-mariadb102 "mysql -h $DBHOST -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e \"GRANT ALL PRIVILEGES on $DBNAME.* to '$DBUSER_MISP'@'localhost';\""
|
||||
scl enable rh-mariadb102 "mysql -h $DBHOST -u $DBUSER_ADMIN -p$DBPASSWORD_ADMIN -e 'FLUSH PRIVILEGES;'"
|
||||
|
||||
$SUDO_WWW cat $PATH_TO_MISP/INSTALL/MYSQL.sql | sudo scl enable rh-mariadb102 "mysql -u $DBUSER_MISP -p$DBPASSWORD_MISP $DBNAME"
|
||||
$SUDO_WWW cat $PATH_TO_MISP/INSTALL/MYSQL.sql | sudo scl enable rh-mariadb102 "mysql -h $DBHOST -u $DBUSER_MISP -p$DBPASSWORD_MISP $DBNAME"
|
||||
}
|
||||
|
||||
apacheConfig_RHEL () {
|
||||
|
@ -2529,6 +2543,7 @@ mispmodulesRHEL () {
|
|||
# pip install
|
||||
$SUDO_WWW $PATH_TO_MISP/venv/bin/pip install -U -I -r REQUIREMENTS
|
||||
$SUDO_WWW $PATH_TO_MISP/venv/bin/pip install -U .
|
||||
$SUDO_WWW $PATH_TO_MISP/venv/bin/pip install pyfaup censys
|
||||
sudo yum install rubygem-rouge rubygem-asciidoctor zbar-devel opencv-devel -y
|
||||
|
||||
echo "[Unit]
|
||||
|
@ -2770,10 +2785,10 @@ installSupported () {
|
|||
# Install PHP 7.2 Dependencies - functionLocation('INSTALL.ubuntu1804.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp72
|
||||
elif [[ "$PHP_VER" == 7.3 ]]; then
|
||||
# Install PHP 7.4 Dependencies - functionLocation('INSTALL.ubuntu2004.md')
|
||||
# Install PHP 7.3 Dependencies - functionLocation('generic/supportFunctions.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp73
|
||||
elif [[ "$PHP_VER" == 7.4 ]]; then
|
||||
# Install PHP 7.3 Dependencies - functionLocation('generic/supportFunctions.md')
|
||||
# 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')
|
||||
|
@ -2875,7 +2890,7 @@ installSupported () {
|
|||
|
||||
# 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('')
|
||||
# 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')
|
||||
|
@ -2884,8 +2899,8 @@ installMISPonKali () {
|
|||
# Set Base URL - functionLocation('generic/supportFunctions.md')
|
||||
setBaseURL
|
||||
|
||||
# Install PHP 7.3 Dependencies - functionLocation('generic/supportFunctions.md')
|
||||
installDepsPhp73
|
||||
# Install PHP 7.4 Dependencies - functionLocation('INSTALL.ubuntu2004.md')
|
||||
installDepsPhp74
|
||||
|
||||
# Set custom Kali only variables and tweaks
|
||||
space
|
||||
|
@ -2903,12 +2918,12 @@ installMISPonKali () {
|
|||
installCoreDeps
|
||||
|
||||
debug "Enabling redis and gnupg modules"
|
||||
sudo phpenmod -v 7.3 redis
|
||||
sudo phpenmod -v 7.3 gnupg
|
||||
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.3
|
||||
sudo a2enmod ssl rewrite headers php7.4
|
||||
sudo a2dissite 000-default
|
||||
sudo a2ensite default-ssl
|
||||
|
||||
|
@ -3011,26 +3026,18 @@ installMISPonKali () {
|
|||
|
||||
debug "Setting up database"
|
||||
if [[ ! -e /var/lib/mysql/misp/users.ibd ]]; then
|
||||
echo "
|
||||
set timeout 10
|
||||
spawn sudo mysql_secure_installation
|
||||
expect \"Enter current password for root (enter for none):\"
|
||||
send -- \"\r\"
|
||||
expect \"Set root password?\"
|
||||
send -- \"y\r\"
|
||||
expect \"New password:\"
|
||||
send -- \"${DBPASSWORD_ADMIN}\r\"
|
||||
expect \"Re-enter new password:\"
|
||||
send -- \"${DBPASSWORD_ADMIN}\r\"
|
||||
expect \"Remove anonymous users?\"
|
||||
send -- \"y\r\"
|
||||
expect \"Disallow root login remotely?\"
|
||||
send -- \"y\r\"
|
||||
expect \"Remove test database and access to it?\"
|
||||
send -- \"y\r\"
|
||||
expect \"Reload privilege tables now?\"
|
||||
send -- \"y\r\"
|
||||
expect eof" | expect -f -
|
||||
# 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';"
|
||||
|
@ -3281,9 +3288,6 @@ x86_64-debian-stretch
|
|||
x86_64-debian-buster
|
||||
x86_64-ubuntu-bionic
|
||||
x86_64-ubuntu-focal
|
||||
x86_64-kali-2020.1
|
||||
x86_64-kali-2020.2
|
||||
x86_64-kali-2020.3
|
||||
x86_64-kali-2020.4
|
||||
armv6l-raspbian-stretch
|
||||
armv7l-raspbian-stretch
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
; Generated by RHash v1.3.9 on 2020-12-01 at 09:56.39
|
||||
; Generated by RHash v1.3.9 on 2021-02-03 at 15:13.46
|
||||
; Written by Kravchenko Aleksey (Akademgorodok) - http://rhash.sf.net/
|
||||
;
|
||||
; 136271 09:56.39 2020-12-01 INSTALL.sh
|
||||
INSTALL.sh 3EBA91CFD1E3B5F87417A2943BBACD91EBE15A3C FE4C15F83C1BC84CF2099DD4F71382B0726EF620EA6740C8587FF6797067281B 37E30E0D3C58745F0E4F82AA7770D69C30750B809DDA210AA371E6557C07004972DFBE44775B31192171E56DA0641292 376336E61DF47AFA4DC701ED58658092580A7A2F501DD63994066C919B3E90E351631A33761887373529D88BC37029CE135942D15BE3BBB07D004A47D61A6C4C
|
||||
; 137499 15:13.46 2021-02-03 INSTALL.sh
|
||||
INSTALL.sh 5645164D7C2701EC0E0FF7D33CD8263D41B27947 803115D518C0EF187B041B942B0298CEAF329F4ADD07DB405B508D6E7ABB5D45 6B8019972E761EDC6E8CD1335E8B280376315DD762F6C98AE36C149D7960A0FC5D3C6E3F228DCB2949BDCB747942B557 ABE5D6541D23895E863BAB8BE306E54058897823EA703E6A8A225097ED8CA24F365441E8E98851576E5289B34D81C351C1A1A2520A0865BFE0D37AD77D018282
|
||||
|
|
|
@ -1 +1 @@
|
|||
3eba91cfd1e3b5f87417a2943bbacd91ebe15a3c INSTALL.sh
|
||||
5645164d7c2701ec0e0ff7d33cd8263d41b27947 INSTALL.sh
|
||||
|
|
|
@ -1 +1 @@
|
|||
fe4c15f83c1bc84cf2099dd4f71382b0726ef620ea6740c8587ff6797067281b INSTALL.sh
|
||||
803115d518c0ef187b041b942b0298ceaf329f4add07db405b508d6e7abb5d45 INSTALL.sh
|
||||
|
|
|
@ -1 +1 @@
|
|||
37e30e0d3c58745f0e4f82aa7770d69c30750b809dda210aa371e6557c07004972dfbe44775b31192171e56da0641292 INSTALL.sh
|
||||
6b8019972e761edc6e8cd1335e8b280376315dd762f6c98ae36c149d7960a0fc5d3c6e3f228dcb2949bdcb747942b557 INSTALL.sh
|
||||
|
|
|
@ -1 +1 @@
|
|||
376336e61df47afa4dc701ed58658092580a7a2f501dd63994066c919b3e90e351631a33761887373529d88bc37029ce135942d15be3bbb07d004a47d61a6c4c INSTALL.sh
|
||||
abe5d6541d23895e863bab8be306e54058897823ea703e6a8a225097ed8ca24f365441e8e98851576e5289b34d81c351c1a1a2520a0865bfe0d37ad77d018282 INSTALL.sh
|
||||
|
|
|
@ -284,10 +284,10 @@ installSupported () {
|
|||
# Install PHP 7.2 Dependencies - functionLocation('INSTALL.ubuntu1804.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp72
|
||||
elif [[ "$PHP_VER" == 7.3 ]]; then
|
||||
# Install PHP 7.4 Dependencies - functionLocation('INSTALL.ubuntu2004.md')
|
||||
# Install PHP 7.3 Dependencies - functionLocation('generic/supportFunctions.md')
|
||||
[[ -n $CORE ]] || [[ -n $ALL ]] && installDepsPhp73
|
||||
elif [[ "$PHP_VER" == 7.4 ]]; then
|
||||
# Install PHP 7.3 Dependencies - functionLocation('generic/supportFunctions.md')
|
||||
# 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')
|
||||
|
@ -389,7 +389,7 @@ installSupported () {
|
|||
|
||||
# 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('')
|
||||
# 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')
|
||||
|
@ -398,8 +398,8 @@ installMISPonKali () {
|
|||
# Set Base URL - functionLocation('generic/supportFunctions.md')
|
||||
setBaseURL
|
||||
|
||||
# Install PHP 7.3 Dependencies - functionLocation('generic/supportFunctions.md')
|
||||
installDepsPhp73
|
||||
# Install PHP 7.4 Dependencies - functionLocation('INSTALL.ubuntu2004.md')
|
||||
installDepsPhp74
|
||||
|
||||
# Set custom Kali only variables and tweaks
|
||||
space
|
||||
|
@ -417,12 +417,12 @@ installMISPonKali () {
|
|||
installCoreDeps
|
||||
|
||||
debug "Enabling redis and gnupg modules"
|
||||
sudo phpenmod -v 7.3 redis
|
||||
sudo phpenmod -v 7.3 gnupg
|
||||
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.3
|
||||
sudo a2enmod ssl rewrite headers php7.4
|
||||
sudo a2dissite 000-default
|
||||
sudo a2ensite default-ssl
|
||||
|
||||
|
@ -525,26 +525,18 @@ installMISPonKali () {
|
|||
|
||||
debug "Setting up database"
|
||||
if [[ ! -e /var/lib/mysql/misp/users.ibd ]]; then
|
||||
echo "
|
||||
set timeout 10
|
||||
spawn sudo mysql_secure_installation
|
||||
expect \"Enter current password for root (enter for none):\"
|
||||
send -- \"\r\"
|
||||
expect \"Set root password?\"
|
||||
send -- \"y\r\"
|
||||
expect \"New password:\"
|
||||
send -- \"${DBPASSWORD_ADMIN}\r\"
|
||||
expect \"Re-enter new password:\"
|
||||
send -- \"${DBPASSWORD_ADMIN}\r\"
|
||||
expect \"Remove anonymous users?\"
|
||||
send -- \"y\r\"
|
||||
expect \"Disallow root login remotely?\"
|
||||
send -- \"y\r\"
|
||||
expect \"Remove test database and access to it?\"
|
||||
send -- \"y\r\"
|
||||
expect \"Reload privilege tables now?\"
|
||||
send -- \"y\r\"
|
||||
expect eof" | expect -f -
|
||||
# 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';"
|
||||
|
@ -795,9 +787,6 @@ x86_64-debian-stretch
|
|||
x86_64-debian-buster
|
||||
x86_64-ubuntu-bionic
|
||||
x86_64-ubuntu-focal
|
||||
x86_64-kali-2020.1
|
||||
x86_64-kali-2020.2
|
||||
x86_64-kali-2020.3
|
||||
x86_64-kali-2020.4
|
||||
armv6l-raspbian-stretch
|
||||
armv7l-raspbian-stretch
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Main INSTALL Documentation for the MISP Project.
|
||||
# INSTALL Documentation for the MISP Project.
|
||||
|
||||
To have a more web friendly view please visit the mkdocs generated gh-pages site [here](https://misp.github.io/MISP/)
|
||||
|
||||
|
@ -6,21 +6,185 @@ The text files in this folder are symlink to ../docs - Which is the actual sourc
|
|||
|
||||
Currently the following install guides are being tested on a regular basis:
|
||||
```
|
||||
INSTALL.kali.txt
|
||||
INSTALL.ubuntu1804.txt
|
||||
INSTALL.ubuntu1804.md
|
||||
INSTALL.ubuntu2004.md
|
||||
INSTALL.kali.md
|
||||
INSTALL.rhel7.md
|
||||
INSTALL.rhel8.md
|
||||
```
|
||||
|
||||
A folder of interest might be 'old'. In old you will find previous INSTALL guides.
|
||||
|
||||
Files prefixed with 'CONFIG.' are CONFIGuration guides and not full blown INSTALL guides.
|
||||
|
||||
UPDATE.txt give you a brief overview on how to update MISP to the latest version, as well as some other core dependencies that can be updated.
|
||||
UPDATE.md gives you a brief overview on how to update MISP to the latest version, as well as some other core dependencies that can be updated.
|
||||
|
||||
Install guides with the 'x' prefix, are marked as Experimental.
|
||||
|
||||
The following are tested on a semi-regular basis:
|
||||
```
|
||||
xINSTALL.centos7.txt
|
||||
xINSTALL.debian_testing.txt
|
||||
xINSTALL.Arch.txt
|
||||
xINSTALL.centos7.md
|
||||
xINSTALL.debian10.md
|
||||
```
|
||||
|
||||
# INSTALL.sh hacking
|
||||
|
||||
First of all, please read the *INSTALL.sh* script. Running a random piece of shell script that randomly invokes sudo left right and center is dangerous. (Without a sword)
|
||||
|
||||
Now read *INSTALL.tpl.sh*. This is the generator for *INSTALL.sh*.
|
||||
|
||||
If for example you want to modify *INSTALL.sh*, NEVER EVER touch *INSTALL.sh*. This will break the checksum and I will be very, very angry.
|
||||
|
||||
*INSTALL.tpl.sh* will source the various Markdown files and generate the main installer. Meaning, if changes happen they mostly happen in the .md files.
|
||||
The advantage being that when the manual documentation is up to date the installer is up to date.
|
||||
|
||||
|
||||
There are 2 scenarios here.
|
||||
|
||||
1. There is an issue or improvement to be made in *INSTALL.ubuntu2004.md* for example.
|
||||
2. A core *INSTALL.sh* issue or improvement needs to be done.
|
||||
|
||||
You will need *xsnippet* that extracts bits of shell code from the .md files:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/bin
|
||||
git clone https://github.com/SteveClement/xsnippet.git
|
||||
cd xsnippet; cp xsnippet ~/bin/
|
||||
export PATH="$PATH:~/bin" # By now you are aware that this needs to be in your $PATH, aren't you. #PAAF
|
||||
```
|
||||
|
||||
You need *rhash* too:
|
||||
```bash
|
||||
sudo apt install rhash
|
||||
```
|
||||
|
||||
Now you are ready.
|
||||
|
||||
To test if you are really ready, do the following:
|
||||
|
||||
```
|
||||
git clone https://github.com/MISP/MISP.git
|
||||
cd MISP/INSTALL ; ./INSTALL.tpl.sh
|
||||
```
|
||||
|
||||
The only file that should have been changed is: *INSTALL.sh.sfv*
|
||||
And nothing on *stdout* should have been displayed, and the exit code would have been obviously 0.
|
||||
|
||||
## Scenario 1
|
||||
|
||||
The easiest scenario. Everythin between *# <snippet-begin* is relevant to the to be generated installer. Change to your hearts' content, run the *INSTALL.tpl.sh* script and now the following files will have changed:
|
||||
|
||||
```
|
||||
modified: INSTALL.sh
|
||||
modified: INSTALL.sh.sfv
|
||||
modified: INSTALL.sh.sha1
|
||||
modified: INSTALL.sh.sha256
|
||||
modified: INSTALL.sh.sha384
|
||||
modified: INSTALL.sh.sha512
|
||||
modified: ../docs/INSTALL.ubuntu1804.md
|
||||
```
|
||||
|
||||
Perfect, this looks as if it worked. This is typical, if the .md changes, the *INSTALL.sh* checksum will obviously change too. Important to note, this needs to be reflected on the *2.4* branch.
|
||||
Otherwise your changes are not taken into account or something might even break if things are out of sync.
|
||||
|
||||
If for example you change a markdown file and the checksums have NOT changed. This means either that the changed markdown file is not yet supported by the installer. Or that you changed *INSTALL.ubuntu2004.md*
|
||||
The Ubuntu 18.04 install documentation is the main Ubuntu installer file.
|
||||
Ideally you merge your changed between the 18.04 and 20.04 and run the generator again.
|
||||
|
||||
## Scenario 2
|
||||
|
||||
This scenario is more complex. Have you read the *INSTALL.tpl.sh* yet? If no, please do not continue before having read it.
|
||||
|
||||
Good, now that you read it you noticed that there are references to the folder *docs/generic* this folder includes generic files that are shared between platforms.
|
||||
|
||||
For core changes, the most interesting and important files are:
|
||||
|
||||
- *globalVariables.md*
|
||||
- *MISP_CAKE_init.md*
|
||||
- *supportFunctions.md*
|
||||
|
||||
### globalVariables
|
||||
This is the most interesting file, it will bootstrap the install environment of the MISP-Server to be.
|
||||
What I always use, even for just debugging MISP issues in general:
|
||||
```bash
|
||||
eval "$(curl -fsSL https://raw.githubusercontent.com/MISP/MISP/2.4/docs/generic/globalVariables.md | grep -v \`\`\`)"
|
||||
MISPvars
|
||||
```
|
||||
|
||||
This will expose a standard MISP environment to my current working environment, a few important notes and potential caveats: Familiarize yourself with ":-" variables, static list:
|
||||
```bash
|
||||
MISP_USER="${MISP_USER:-misp}"
|
||||
MISP_PASSWORD="${MISP_PASSWORD:-$(openssl rand -hex 32)}"
|
||||
PATH_TO_MISP="${PATH_TO_MISP:-/var/www/MISP}"
|
||||
FQDN="${FQDN:-misp.local}"
|
||||
MISP_BASEURL="${MISP_BASEURL:-""}"
|
||||
DBHOST="${DBHOST:-localhost}"
|
||||
DBNAME="${DBNAME:-misp}"
|
||||
DBUSER_ADMIN="${DBUSER_ADMIN:-root}"
|
||||
DBPASSWORD_ADMIN="${DBPASSWORD_ADMIN:-$(openssl rand -hex 32)}"
|
||||
DBUSER_MISP="${DBUSER_MISP:-misp}"
|
||||
DBPASSWORD_MISP="${DBPASSWORD_MISP:-$(openssl rand -hex 32)}"
|
||||
```
|
||||
|
||||
Those are variables, if they are set in the current scope, via export for example, the will NOT be set with a default value.
|
||||
|
||||
|
||||
### MISP_CAKE_init
|
||||
This file includes all the cake commands to configure the MISP instance via the CLI.
|
||||
As always, have you read the file?
|
||||
|
||||
From its' header:
|
||||
|
||||
```
|
||||
# Core cake commands to tweak MISP and aleviate some of the configuration pains
|
||||
# The $RUN_PHP is ONLY set on RHEL/CentOS installs and can thus be ignored
|
||||
# This file is NOT an excuse to NOT read the settings and familiarize ourselves with them ;)
|
||||
```
|
||||
|
||||
|
||||
### supportFunctions
|
||||
|
||||
The list below will give you a hint what the supportFunctions do.
|
||||
Reading the code will be more or less self-explanatory, plus it is documented.
|
||||
|
||||
For a static overview, as of 20210114 the following functions are in that file:
|
||||
|
||||
```
|
||||
usage () {
|
||||
containsElement () {
|
||||
checkOpt () {
|
||||
setOpt () {
|
||||
command_exists () {
|
||||
checkCoreOS () {
|
||||
checkFlavour () {
|
||||
check_forked () {
|
||||
checkInstaller () {
|
||||
checkManufacturer () {
|
||||
space () {
|
||||
progress () {
|
||||
checkLocale () {
|
||||
checkFail () {
|
||||
ask_o () {
|
||||
clean () {
|
||||
checkID () {
|
||||
preInstall () {
|
||||
upgrade () {
|
||||
checkUsrLocalSrc () {
|
||||
kaliSpaceSaver () {
|
||||
kaliOnTheR0ckz () {
|
||||
setBaseURL () {
|
||||
installRNG () {
|
||||
kaliUpgrade () {
|
||||
disableSleep () {
|
||||
checkAptLock () {
|
||||
installDepsPhp70 () {
|
||||
installDepsPhp73 () {
|
||||
installDeps () {
|
||||
fixRedis () {
|
||||
genApacheConf () {
|
||||
gitPullAllRCLOCAL () {
|
||||
composer () {
|
||||
enableServices () {
|
||||
genRCLOCAL () {
|
||||
runTests () {
|
||||
nuke () {
|
||||
theEnd () {
|
||||
```
|
||||
|
|
|
@ -23,34 +23,46 @@
|
|||
},
|
||||
"lxml": {
|
||||
"hashes": [
|
||||
"sha256:03984196d00670b2ab14ae0ea83d5cc0cfa4f5a42558afa9ab5fa745995328f5",
|
||||
"sha256:0815b0c9f897468de6a386dc15917a0becf48cc92425613aa8bbfc7f0f82951f",
|
||||
"sha256:175f3825f075cf02d15099eb52658457cf0ff103dcf11512b5d2583e1d40f58b",
|
||||
"sha256:30e14c62d88d1e01a26936ecd1c6e784d4afc9aa002bba4321c5897937112616",
|
||||
"sha256:3210da6f36cf4b835ff1be853962b22cc354d506f493b67a4303c88bbb40d57b",
|
||||
"sha256:40f60819fbd5bad6e191ba1329bfafa09ab7f3f174b3d034d413ef5266963294",
|
||||
"sha256:43b26a865a61549919f8a42e094dfdb62847113cf776d84bd6b60e4e3fc20ea3",
|
||||
"sha256:4a03dd682f8e35a10234904e0b9508d705ff98cf962c5851ed052e9340df3d90",
|
||||
"sha256:62f382cddf3d2e52cf266e161aa522d54fd624b8cc567bc18f573d9d50d40e8e",
|
||||
"sha256:7b98f0325be8450da70aa4a796c4f06852949fe031878b4aa1d6c417a412f314",
|
||||
"sha256:846a0739e595871041385d86d12af4b6999f921359b38affb99cdd6b54219a8f",
|
||||
"sha256:a3080470559938a09a5d0ec558c005282e99ac77bf8211fb7b9a5c66390acd8d",
|
||||
"sha256:ad841b78a476623955da270ab8d207c3c694aa5eba71f4792f65926dc46c6ee8",
|
||||
"sha256:afdd75d9735e44c639ffd6258ce04a2de3b208f148072c02478162d0944d9da3",
|
||||
"sha256:b4fbf9b552faff54742bcd0791ab1da5863363fb19047e68f6592be1ac2dab33",
|
||||
"sha256:b90c4e32d6ec089d3fa3518436bdf5ce4d902a0787dbd9bb09f37afe8b994317",
|
||||
"sha256:b91cfe4438c741aeff662d413fd2808ac901cc6229c838236840d11de4586d63",
|
||||
"sha256:bdb0593a42070b0a5f138b79b872289ee73c8e25b3f0bea6564e795b55b6bcdd",
|
||||
"sha256:c4e4bca2bb68ce22320297dfa1a7bf070a5b20bcbaec4ee023f83d2f6e76496f",
|
||||
"sha256:cec4ab14af9eae8501be3266ff50c3c2aecc017ba1e86c160209bb4f0423df6a",
|
||||
"sha256:e83b4b2bf029f5104bc1227dbb7bf5ace6fd8fabaebffcd4f8106fafc69fc45f",
|
||||
"sha256:e995b3734a46d41ae60b6097f7c51ba9958648c6d1e0935b7e0ee446ee4abe22",
|
||||
"sha256:f679d93dec7f7210575c85379a31322df4c46496f184ef650d3aba1484b38a2d",
|
||||
"sha256:fd213bb5166e46974f113c8228daaef1732abc47cb561ce9c4c8eaed4bd3b09b",
|
||||
"sha256:fdcb57b906dbc1f80666e6290e794ab8fb959a2e17aa5aee1758a85d1da4533f",
|
||||
"sha256:ff424b01d090ffe1947ec7432b07f536912e0300458f9a7f48ea217dd8362b86"
|
||||
"sha256:0448576c148c129594d890265b1a83b9cd76fd1f0a6a04620753d9a6bcfd0a4d",
|
||||
"sha256:127f76864468d6630e1b453d3ffbbd04b024c674f55cf0a30dc2595137892d37",
|
||||
"sha256:1471cee35eba321827d7d53d104e7b8c593ea3ad376aa2df89533ce8e1b24a01",
|
||||
"sha256:2363c35637d2d9d6f26f60a208819e7eafc4305ce39dc1d5005eccc4593331c2",
|
||||
"sha256:2e5cc908fe43fe1aa299e58046ad66981131a66aea3129aac7770c37f590a644",
|
||||
"sha256:2e6fd1b8acd005bd71e6c94f30c055594bbd0aa02ef51a22bbfa961ab63b2d75",
|
||||
"sha256:366cb750140f221523fa062d641393092813b81e15d0e25d9f7c6025f910ee80",
|
||||
"sha256:42ebca24ba2a21065fb546f3e6bd0c58c3fe9ac298f3a320147029a4850f51a2",
|
||||
"sha256:4e751e77006da34643ab782e4a5cc21ea7b755551db202bc4d3a423b307db780",
|
||||
"sha256:4fb85c447e288df535b17ebdebf0ec1cf3a3f1a8eba7e79169f4f37af43c6b98",
|
||||
"sha256:50c348995b47b5a4e330362cf39fc503b4a43b14a91c34c83b955e1805c8e308",
|
||||
"sha256:535332fe9d00c3cd455bd3dd7d4bacab86e2d564bdf7606079160fa6251caacf",
|
||||
"sha256:535f067002b0fd1a4e5296a8f1bf88193080ff992a195e66964ef2a6cfec5388",
|
||||
"sha256:5be4a2e212bb6aa045e37f7d48e3e1e4b6fd259882ed5a00786f82e8c37ce77d",
|
||||
"sha256:60a20bfc3bd234d54d49c388950195d23a5583d4108e1a1d47c9eef8d8c042b3",
|
||||
"sha256:648914abafe67f11be7d93c1a546068f8eff3c5fa938e1f94509e4a5d682b2d8",
|
||||
"sha256:681d75e1a38a69f1e64ab82fe4b1ed3fd758717bed735fb9aeaa124143f051af",
|
||||
"sha256:68a5d77e440df94011214b7db907ec8f19e439507a70c958f750c18d88f995d2",
|
||||
"sha256:69a63f83e88138ab7642d8f61418cf3180a4d8cd13995df87725cb8b893e950e",
|
||||
"sha256:6e4183800f16f3679076dfa8abf2db3083919d7e30764a069fb66b2b9eff9939",
|
||||
"sha256:6fd8d5903c2e53f49e99359b063df27fdf7acb89a52b6a12494208bf61345a03",
|
||||
"sha256:791394449e98243839fa822a637177dd42a95f4883ad3dec2a0ce6ac99fb0a9d",
|
||||
"sha256:7a7669ff50f41225ca5d6ee0a1ec8413f3a0d8aa2b109f86d540887b7ec0d72a",
|
||||
"sha256:7e9eac1e526386df7c70ef253b792a0a12dd86d833b1d329e038c7a235dfceb5",
|
||||
"sha256:7ee8af0b9f7de635c61cdd5b8534b76c52cd03536f29f51151b377f76e214a1a",
|
||||
"sha256:8246f30ca34dc712ab07e51dc34fea883c00b7ccb0e614651e49da2c49a30711",
|
||||
"sha256:8c88b599e226994ad4db29d93bc149aa1aff3dc3a4355dd5757569ba78632bdf",
|
||||
"sha256:923963e989ffbceaa210ac37afc9b906acebe945d2723e9679b643513837b089",
|
||||
"sha256:94d55bd03d8671686e3f012577d9caa5421a07286dd351dfef64791cf7c6c505",
|
||||
"sha256:97db258793d193c7b62d4e2586c6ed98d51086e93f9a3af2b2034af01450a74b",
|
||||
"sha256:a9d6bc8642e2c67db33f1247a77c53476f3a166e09067c0474facb045756087f",
|
||||
"sha256:cd11c7e8d21af997ee8079037fff88f16fda188a9776eb4b81c7e4c9c0a7d7fc",
|
||||
"sha256:d8d3d4713f0c28bdc6c806a278d998546e8efc3498949e3ace6e117462ac0a5e",
|
||||
"sha256:e0bfe9bb028974a481410432dbe1b182e8191d5d40382e5b8ff39cdd2e5c5931",
|
||||
"sha256:f4822c0660c3754f1a41a655e37cb4dbbc9be3d35b125a37fab6f82d47674ebc",
|
||||
"sha256:f83d281bb2a6217cd806f4cf0ddded436790e66f393e124dfe9731f6b3fb9afe",
|
||||
"sha256:fc37870d6716b137e80d19241d0e2cff7a7643b925dfa49b4c8ebd1295eb506e"
|
||||
],
|
||||
"version": "==4.3.3"
|
||||
"index": "pypi",
|
||||
"version": "==4.6.2"
|
||||
},
|
||||
"maec": {
|
||||
"hashes": [
|
||||
|
@ -70,10 +82,9 @@
|
|||
},
|
||||
"ordered-set": {
|
||||
"hashes": [
|
||||
"sha256:41c7ba85e7619cd4c71e38d4cd434f84de8473b826919eb79274b3a11b940b4d",
|
||||
"sha256:f9b703ea9aa9c1db44412c5ba1c16cf8b7ad7ef37a685e4da2fd3754b40f8f6a"
|
||||
"sha256:ba93b2df055bca202116ec44b9bead3df33ea63a7d5827ff8e16738b97f33a95"
|
||||
],
|
||||
"version": "==3.1"
|
||||
"version": "==4.0.2"
|
||||
},
|
||||
"pydeep": {
|
||||
"git": "https://github.com/kbandla/pydeep.git",
|
||||
|
@ -88,17 +99,17 @@
|
|||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
"sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb",
|
||||
"sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"
|
||||
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
|
||||
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
|
||||
],
|
||||
"version": "==2.8.0"
|
||||
"version": "==2.8.1"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
|
||||
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
|
||||
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
|
||||
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
|
||||
],
|
||||
"version": "==1.12.0"
|
||||
"version": "==1.15.0"
|
||||
},
|
||||
"stix": {
|
||||
"hashes": [
|
||||
|
|
2
PyMISP
2
PyMISP
|
@ -1 +1 @@
|
|||
Subproject commit ded44278af8f427577f27c4c8293f7e8723148c4
|
||||
Subproject commit c87bcf15acbe591fc026ebec4cf8452749405f22
|
|
@ -9,8 +9,8 @@ MISP - Threat Intelligence Sharing Platform
|
|||
<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>Travis</td>
|
||||
<td><a href="https://travis-ci.org/MISP/MISP"><img src="https://img.shields.io/travis/MISP/MISP/2.4.svg" /></a></td>
|
||||
<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>
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"major":2, "minor":4, "hotfix":135}
|
||||
{"major":2, "minor":4, "hotfix":137}
|
||||
|
|
|
@ -6,6 +6,7 @@ $config = array(
|
|||
'level' => 'medium',
|
||||
'salt' => '',
|
||||
'cipherSeed' => '',
|
||||
'require_password_confirmation' => true
|
||||
//'auth'=>array('CertAuth.Certificate'), // additional authentication methods
|
||||
//'auth'=>array('ShibbAuth.ApacheShibb'),
|
||||
),
|
||||
|
|
|
@ -214,19 +214,24 @@ class AdminShell extends AppShell
|
|||
if (empty($user)) {
|
||||
echo 'User with ID: ' . $userId . ' not found' . PHP_EOL;
|
||||
$result = $this->ObjectTemplate->update();
|
||||
if ($result) {
|
||||
echo 'Object templates updated' . PHP_EOL;
|
||||
} else {
|
||||
echo 'Could not update object templates' . PHP_EOL;
|
||||
}
|
||||
} else {
|
||||
$result = $this->ObjectTemplate->update($user, false,false);
|
||||
if ($result) {
|
||||
echo 'Object templates updated' . PHP_EOL;
|
||||
} else {
|
||||
echo 'Could not update object templates' . PHP_EOL;
|
||||
}
|
||||
|
||||
$successes = count(!empty($result['success']) ? $result['success'] : []);
|
||||
$fails = count(!empty($result['fails']) ? $result['fails'] : []);
|
||||
$message = '';
|
||||
if ($successes == 0 && $fails == 0) {
|
||||
$message = __('All object templates are up to date already.');
|
||||
} elseif ($successes == 0 && $fails > 0) {
|
||||
$message = __('Could not update any of the object templates.');
|
||||
} elseif ($successes > 0 ) {
|
||||
$message = __('Successfully updated %s object templates.', $successes);
|
||||
if ($fails != 0) {
|
||||
$message .= __(' However, could not update %s object templates.', $fails);
|
||||
}
|
||||
}
|
||||
echo $message . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -655,4 +660,15 @@ class AdminShell extends AppShell
|
|||
echo $result . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
public function cleanExcludedCorrelations()
|
||||
{
|
||||
$jobId = $this->args[0];
|
||||
$this->CorrelationExclusion = ClassRegistry::init('CorrelationExclusion');
|
||||
$this->CorrelationExclusion->clean($jobId);
|
||||
$this->Job->id = $jobId;
|
||||
$this->Job->saveField('progress', 100);
|
||||
$this->Job->saveField('message', 'Job done.');
|
||||
$this->Job->saveField('status', 4);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,70 @@ class EventShell extends AppShell
|
|||
public $uses = array('Event', 'Post', 'Attribute', 'Job', 'User', 'Task', 'Allowedlist', 'Server', 'Organisation');
|
||||
public $tasks = array('ConfigLoad');
|
||||
|
||||
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],
|
||||
],
|
||||
)
|
||||
));
|
||||
return $parser;
|
||||
}
|
||||
|
||||
public function import()
|
||||
{
|
||||
list($userId, $path) = $this->args;
|
||||
$user = $this->User->getAuthUser($userId);
|
||||
if (empty($user)) {
|
||||
$this->error("User with ID $userId does not exists.");
|
||||
}
|
||||
|
||||
if (!file_exists($path)) {
|
||||
$this->error("File '$path' does not exists.");
|
||||
}
|
||||
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 doPublish()
|
||||
{
|
||||
$this->ConfigLoad->execute();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
class StatisticsShell extends AppShell {
|
||||
|
||||
public $uses = array('Event', 'User', 'Organisation', 'Log');
|
||||
public $uses = array('Event', 'User', 'Organisation', 'Log', 'Correlation');
|
||||
|
||||
public function contributors()
|
||||
{
|
||||
|
@ -251,4 +251,28 @@ class StatisticsShell extends AppShell {
|
|||
}
|
||||
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,27 +1,4 @@
|
|||
<?php
|
||||
/**
|
||||
* Application level Controller
|
||||
*
|
||||
* This file is application-wide controller file. You can put all
|
||||
* application-wide controller-related methods here.
|
||||
*
|
||||
* 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.Controller
|
||||
* @since CakePHP(tm) v 0.2.9
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
|
||||
// TODO GnuPG encryption has issues when keys are expired
|
||||
|
||||
App::uses('ConnectionManager', 'Model');
|
||||
App::uses('Controller', 'Controller');
|
||||
App::uses('File', 'Utility');
|
||||
|
@ -36,31 +13,30 @@ App::uses('RequestRearrangeTool', 'Tools');
|
|||
* @package app.Controller
|
||||
* @link http://book.cakephp.org/2.0/en/controllers.html#the-app-controller
|
||||
*
|
||||
* @throws ForbiddenException // TODO Exception
|
||||
* @property ACLComponent $ACL
|
||||
* @property RestResponseComponent $RestResponse
|
||||
* @property CRUDComponent $CRUD
|
||||
* @property IndexFilterComponent $IndexFilter
|
||||
* @property RateLimitComponent $RateLimit
|
||||
*/
|
||||
class AppController extends Controller
|
||||
{
|
||||
public $defaultModel = '';
|
||||
|
||||
public $debugMode = false;
|
||||
public $helpers = array('OrgImg', 'FontAwesome', 'UserName', 'DataPathCollector');
|
||||
|
||||
public $helpers = array('Utility', 'OrgImg', 'FontAwesome', 'UserName', 'DataPathCollector');
|
||||
|
||||
private $__queryVersion = '119';
|
||||
public $pyMispVersion = '2.4.135';
|
||||
private $__queryVersion = '122';
|
||||
public $pyMispVersion = '2.4.137';
|
||||
public $phpmin = '7.2';
|
||||
public $phprec = '7.4';
|
||||
public $phptoonew = '8.0';
|
||||
public $pythonmin = '3.6';
|
||||
public $pythonrec = '3.7';
|
||||
public $isApiAuthed = false;
|
||||
private $isApiAuthed = false;
|
||||
|
||||
public $baseurl = '';
|
||||
public $sql_dump = false;
|
||||
|
||||
private $isRest = null;
|
||||
public $restResponsePayload = null;
|
||||
|
||||
// Used for _isAutomation(), a check that returns true if the controller & action combo matches an action that is a non-xml and non-json automation method
|
||||
|
@ -72,6 +48,8 @@ class AppController extends Controller
|
|||
);
|
||||
|
||||
protected $_legacyParams = array();
|
||||
/** @var array */
|
||||
public $userRole;
|
||||
|
||||
/** @var User */
|
||||
public $User;
|
||||
|
@ -114,14 +92,12 @@ class AppController extends Controller
|
|||
|
||||
public function beforeFilter()
|
||||
{
|
||||
$this->Auth->loginRedirect = Configure::read('MISP.baseurl') . '/users/routeafterlogin';
|
||||
$this->_setupBaseurl();
|
||||
$this->Auth->loginRedirect = $this->baseurl. '/users/routeafterlogin';
|
||||
|
||||
$customLogout = Configure::read('Plugin.CustomAuth_custom_logout');
|
||||
if ($customLogout) {
|
||||
$this->Auth->logoutRedirect = $customLogout;
|
||||
} else {
|
||||
$this->Auth->logoutRedirect = Configure::read('MISP.baseurl') . '/users/login';
|
||||
}
|
||||
$this->Auth->logoutRedirect = $customLogout ?: ($this->baseurl . '/users/login');
|
||||
|
||||
$this->__sessionMassage();
|
||||
if (Configure::read('Security.allow_cors')) {
|
||||
// Add CORS headers
|
||||
|
@ -152,8 +128,8 @@ class AppController extends Controller
|
|||
$this->sql_dump = intval($this->params['named']['sql']);
|
||||
}
|
||||
|
||||
$this->_setupDatabaseConnection();
|
||||
$this->_setupDebugMode();
|
||||
$this->_setupDatabaseConnection();
|
||||
|
||||
$this->set('ajax', $this->request->is('ajax'));
|
||||
$this->set('queryVersion', $this->__queryVersion);
|
||||
|
@ -166,17 +142,19 @@ class AppController extends Controller
|
|||
Configure::write('Config.language', 'eng');
|
||||
}
|
||||
|
||||
//if fresh installation (salt empty) generate a new salt
|
||||
// For fresh installation (salt empty) generate a new salt
|
||||
if (!Configure::read('Security.salt')) {
|
||||
$this->loadModel('Server');
|
||||
$this->Server->serverSettingsSaveValue('Security.salt', $this->User->generateRandomPassword(32));
|
||||
}
|
||||
|
||||
// Check if the instance has a UUID, if not assign one.
|
||||
if (!Configure::read('MISP.uuid')) {
|
||||
$this->loadModel('Server');
|
||||
$this->Server->serverSettingsSaveValue('MISP.uuid', CakeText::uuid());
|
||||
}
|
||||
// check if Apache provides kerberos authentication data
|
||||
|
||||
// Check if Apache provides kerberos authentication data
|
||||
$authUserFields = $this->User->describeAuthFields();
|
||||
$envvar = Configure::read('ApacheSecureAuth.apacheEnv');
|
||||
if ($envvar && isset($_SERVER[$envvar])) {
|
||||
|
@ -196,10 +174,9 @@ class AppController extends Controller
|
|||
}
|
||||
Configure::write('CurrentController', $this->params['controller']);
|
||||
Configure::write('CurrentAction', $this->params['action']);
|
||||
$versionArray = $this->{$this->modelClass}->checkMISPVersion();
|
||||
$versionArray = $this->User->checkMISPVersion();
|
||||
$this->mispVersion = implode('.', array_values($versionArray));
|
||||
$this->Security->blackHoleCallback = 'blackHole';
|
||||
$this->_setupBaseurl();
|
||||
|
||||
// send users away that are using ancient versions of IE
|
||||
// Make sure to update this if IE 20 comes out :)
|
||||
|
@ -213,6 +190,27 @@ class AppController extends Controller
|
|||
$userLoggedIn = $this->__customAuthentication($_SERVER);
|
||||
}
|
||||
if ($this->_isRest()) {
|
||||
$jsonDecode = function ($dataToDecode) {
|
||||
if (empty($dataToDecode)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
if (defined('JSON_THROW_ON_ERROR')) {
|
||||
// JSON_THROW_ON_ERROR is supported since PHP 7.3
|
||||
return json_decode($dataToDecode, true, 512, JSON_THROW_ON_ERROR);
|
||||
} else {
|
||||
$decoded = json_decode($dataToDecode, true);
|
||||
if ($decoded === null) {
|
||||
throw new UnexpectedValueException('Could not parse JSON: ' . json_last_error_msg(), json_last_error());
|
||||
}
|
||||
return $decoded;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
throw new HttpException('Invalid JSON input. Make sure that the JSON input is a correctly formatted JSON string. This request has been blocked to avoid an unfiltered request.', 405, $e);
|
||||
}
|
||||
};
|
||||
// Throw exception if JSON in request is invalid. Default CakePHP behaviour would just ignore that error.
|
||||
$this->RequestHandler->addInputType('json', [$jsonDecode]);
|
||||
$this->Security->unlockedActions = array($this->action);
|
||||
}
|
||||
|
||||
|
@ -227,232 +225,68 @@ class AppController extends Controller
|
|||
// REST authentication
|
||||
if ($this->_isRest() || $this->_isAutomation()) {
|
||||
// disable CSRF for REST access
|
||||
if (array_key_exists('Security', $this->components)) {
|
||||
if (isset($this->components['Security'])) {
|
||||
$this->Security->csrfCheck = false;
|
||||
}
|
||||
// If enabled, allow passing the API key via a named parameter (for crappy legacy systems only)
|
||||
$namedParamAuthkey = false;
|
||||
if (Configure::read('Security.allow_unsafe_apikey_named_param') && !empty($this->params['named']['apikey'])) {
|
||||
$namedParamAuthkey = $this->params['named']['apikey'];
|
||||
}
|
||||
// Authenticate user with authkey in Authorization HTTP header
|
||||
if (!empty($_SERVER['HTTP_AUTHORIZATION']) || !empty($namedParamAuthkey)) {
|
||||
$found_misp_auth_key = false;
|
||||
$authentication = explode(',', $_SERVER['HTTP_AUTHORIZATION']);
|
||||
if (!empty($namedParamAuthkey)) {
|
||||
$authentication[] = $namedParamAuthkey;
|
||||
}
|
||||
$user = false;
|
||||
foreach ($authentication as $auth_key) {
|
||||
if (preg_match('/^[a-zA-Z0-9]{40}$/', trim($auth_key))) {
|
||||
$found_misp_auth_key = true;
|
||||
$temp = $this->checkAuthUser(trim($auth_key));
|
||||
if ($temp) {
|
||||
$user['User'] = $temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($found_misp_auth_key) {
|
||||
if ($user) {
|
||||
unset($user['User']['gpgkey']);
|
||||
unset($user['User']['certif_public']);
|
||||
// User found in the db, add the user info to the session
|
||||
if (Configure::read('MISP.log_auth')) {
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
$this->Log->create();
|
||||
$log = array(
|
||||
'org' => $user['User']['Organisation']['name'],
|
||||
'model' => 'User',
|
||||
'model_id' => $user['User']['id'],
|
||||
'email' => $user['User']['email'],
|
||||
'action' => 'auth',
|
||||
'title' => 'Successful authentication using API key',
|
||||
'change' => 'HTTP method: ' . $_SERVER['REQUEST_METHOD'] . PHP_EOL . 'Target: ' . $this->here,
|
||||
);
|
||||
$this->Log->save($log);
|
||||
}
|
||||
$this->Session->renew();
|
||||
$this->Session->write(AuthComponent::$sessionKey, $user['User']);
|
||||
$this->isApiAuthed = true;
|
||||
} else {
|
||||
// User not authenticated correctly
|
||||
// reset the session information
|
||||
$redis = $this->{$this->modelClass}->setupRedis();
|
||||
if ($redis && !$redis->exists('misp:auth_fail_throttling:' . trim($auth_key))) {
|
||||
$redis->set('misp:auth_fail_throttling:' . trim($auth_key), 1);
|
||||
$redis->expire('misp:auth_fail_throttling:' . trim($auth_key), 3600);
|
||||
$this->Session->destroy();
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
$this->Log->create();
|
||||
$log = array(
|
||||
'org' => 'SYSTEM',
|
||||
'model' => 'User',
|
||||
'model_id' => 0,
|
||||
'email' => 'SYSTEM',
|
||||
'action' => 'auth_fail',
|
||||
'title' => 'Failed authentication using API key (' . trim($auth_key) . ')',
|
||||
'change' => null,
|
||||
);
|
||||
$this->Log->save($log);
|
||||
}
|
||||
throw new ForbiddenException('Authentication failed. Please make sure you pass the API key of an API enabled user along in the Authorization header.');
|
||||
}
|
||||
unset($user);
|
||||
}
|
||||
}
|
||||
if ($this->Auth->user() == null) {
|
||||
if ($this->__loginByAuthKey() === false || $this->Auth->user() === null) {
|
||||
throw new ForbiddenException('Authentication failed. Please make sure you pass the API key of an API enabled user along in the Authorization header.');
|
||||
}
|
||||
} elseif (!$this->Session->read(AuthComponent::$sessionKey)) {
|
||||
$this->_loadAuthenticationPlugins();
|
||||
}
|
||||
}
|
||||
$this->set('externalAuthUser', $userLoggedIn);
|
||||
// user must accept terms
|
||||
//
|
||||
// grab the base path from our base url for use in the following checks
|
||||
$base_dir = parse_url($this->baseurl, PHP_URL_PATH);
|
||||
|
||||
// if MISP is running out of the web root already, just set this variable to blank so we don't wind up with '//' in the following if statements
|
||||
if ($base_dir == '/') {
|
||||
$base_dir = '';
|
||||
}
|
||||
$user = $this->Auth->user();
|
||||
if ($user) {
|
||||
Configure::write('CurrentUserId', $user['id']);
|
||||
$this->__logAccess($user);
|
||||
|
||||
if ($this->Auth->user()) {
|
||||
Configure::write('CurrentUserId', $this->Auth->user('id'));
|
||||
$this->User->setMonitoring($this->Auth->user());
|
||||
if (Configure::read('MISP.log_user_ips')) {
|
||||
$redis = $this->{$this->modelClass}->setupRedis();
|
||||
if ($redis) {
|
||||
$redis->set('misp:ip_user:' . trim($_SERVER['REMOTE_ADDR']), $this->Auth->user('id'));
|
||||
$redis->expire('misp:ip_user:' . trim($_SERVER['REMOTE_ADDR']), 60*60*24*30);
|
||||
$redis->sadd('misp:user_ip:' . $this->Auth->user('id'), trim($_SERVER['REMOTE_ADDR']));
|
||||
// Try to run updates
|
||||
if ($user['Role']['perm_site_admin'] || (Configure::read('MISP.live') && !$this->_isRest())) {
|
||||
$this->User->runUpdates();
|
||||
}
|
||||
|
||||
// Put username to response header for webserver or proxy logging
|
||||
if (Configure::read('Security.username_in_response_header')) {
|
||||
$headerValue = $user['email'];
|
||||
if (isset($user['logged_by_authkey']) && $user['logged_by_authkey']) {
|
||||
$headerValue .= isset($user['authkey_id']) ? "/API/{$user['authkey_id']}" : '/API/default';
|
||||
}
|
||||
$this->response->header('X-Username', $headerValue);
|
||||
$this->RestResponse->setHeader('X-Username', $headerValue);
|
||||
}
|
||||
// update script
|
||||
if ($this->Auth->user('Role')['perm_site_admin'] || (Configure::read('MISP.live') && !$this->_isRest())) {
|
||||
$this->{$this->modelClass}->runUpdates();
|
||||
|
||||
if (!$this->__verifyUser($user)) {
|
||||
$this->_stop(); // just for sure
|
||||
}
|
||||
$user = $this->Auth->user();
|
||||
if (!isset($user['force_logout']) || $user['force_logout']) {
|
||||
$this->loadModel('User');
|
||||
$this->User->id = $this->Auth->user('id');
|
||||
$this->User->saveField('force_logout', false);
|
||||
|
||||
if (isset($user['logged_by_authkey']) && $user['logged_by_authkey'] && !($this->_isRest() || $this->_isAutomation())) {
|
||||
throw new ForbiddenException("When user is authenticated by authkey, just REST request can be processed");
|
||||
}
|
||||
if ($this->Auth->user('disabled')) {
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
$this->Log->create();
|
||||
$log = array(
|
||||
'org' => $this->Auth->user('Organisation')['name'],
|
||||
'model' => 'User',
|
||||
'model_id' => $this->Auth->user('id'),
|
||||
'email' => $this->Auth->user('email'),
|
||||
'action' => 'auth_fail',
|
||||
'title' => 'Login attempt by disabled user.',
|
||||
'change' => null,
|
||||
);
|
||||
$this->Log->save($log);
|
||||
$this->Auth->logout();
|
||||
if ($this->_isRest()) {
|
||||
throw new ForbiddenException('Authentication failed. Your user account has been disabled.');
|
||||
} else {
|
||||
$this->Flash->error('Your user account has been disabled.', array('key' => 'error'));
|
||||
$this->_redirectToLogin();
|
||||
}
|
||||
|
||||
// Put token expiration time to response header that can be processed by automation tool
|
||||
if (isset($user['authkey_expiration']) && $user['authkey_expiration']) {
|
||||
$expiration = date('c', $user['authkey_expiration']);
|
||||
$this->response->header('X-Auth-Key-Expiration', $expiration);
|
||||
$this->RestResponse->setHeader('X-Auth-Key-Expiration', $expiration);
|
||||
}
|
||||
|
||||
$this->set('default_memory_limit', ini_get('memory_limit'));
|
||||
if (isset($this->Auth->user('Role')['memory_limit'])) {
|
||||
if ($this->Auth->user('Role')['memory_limit'] !== '') {
|
||||
ini_set('memory_limit', $this->Auth->user('Role')['memory_limit']);
|
||||
}
|
||||
if (isset($user['Role']['memory_limit']) && $user['Role']['memory_limit'] !== '') {
|
||||
ini_set('memory_limit', $user['Role']['memory_limit']);
|
||||
}
|
||||
$this->set('default_max_execution_time', ini_get('max_execution_time'));
|
||||
if (isset($this->Auth->user('Role')['max_execution_time'])) {
|
||||
if ($this->Auth->user('Role')['max_execution_time'] !== '') {
|
||||
ini_set('max_execution_time', $this->Auth->user('Role')['max_execution_time']);
|
||||
}
|
||||
if (isset($user['Role']['max_execution_time']) && $user['Role']['max_execution_time'] !== '') {
|
||||
ini_set('max_execution_time', $user['Role']['max_execution_time']);
|
||||
}
|
||||
} else {
|
||||
$pre_auth_actions = array('login', 'register');
|
||||
if (!empty(Configure::read('Security.email_otp_enabled'))) {
|
||||
$pre_auth_actions[] = 'email_otp';
|
||||
}
|
||||
if ($this->params['controller'] !== 'users' || !in_array($this->params['action'], $pre_auth_actions)) {
|
||||
if (!$this->request->is('ajax')) {
|
||||
$this->Session->write('pre_login_requested_url', $this->here);
|
||||
}
|
||||
$this->_redirectToLogin();
|
||||
}
|
||||
}
|
||||
|
||||
// check if MISP is live
|
||||
if ($this->Auth->user() && !Configure::read('MISP.live')) {
|
||||
$role = $this->getActions();
|
||||
if (!$role['perm_site_admin']) {
|
||||
$message = Configure::read('MISP.maintenance_message');
|
||||
if (empty($message)) {
|
||||
$this->loadModel('Server');
|
||||
$message = $this->Server->serverSettings['MISP']['maintenance_message']['value'];
|
||||
}
|
||||
if (strpos($message, '$email') && Configure::read('MISP.email')) {
|
||||
$email = Configure::read('MISP.email');
|
||||
$message = str_replace('$email', $email, $message);
|
||||
}
|
||||
$this->Flash->info($message);
|
||||
$this->Auth->logout();
|
||||
throw new MethodNotAllowedException($message);//todo this should pb be removed?
|
||||
} else {
|
||||
$this->Flash->error(__('Warning: MISP is currently disabled for all users. Enable it in Server Settings (Administration -> Server Settings -> MISP tab -> live). An update might also be in progress, you can see the progress in ') , array('params' => array('url' => $this->baseurl . '/servers/updateProgress/', 'urlName' => __('Update Progress')), 'clear' => 1));
|
||||
}
|
||||
}
|
||||
if ($this->Session->check(AuthComponent::$sessionKey)) {
|
||||
if ($this->action !== 'checkIfLoggedIn' || $this->request->params['controller'] !== 'users') {
|
||||
$this->User->id = $this->Auth->user('id');
|
||||
if (!$this->User->exists()) {
|
||||
$message = __('Something went wrong. Your user account that you are authenticated with doesn\'t exist anymore.');
|
||||
if ($this->_isRest) {
|
||||
echo $this->RestResponse->throwException(
|
||||
401,
|
||||
$message
|
||||
);
|
||||
} else {
|
||||
$this->Flash->info($message);
|
||||
}
|
||||
$this->Auth->logout();
|
||||
$this->_redirectToLogin();
|
||||
}
|
||||
if (!empty(Configure::read('MISP.terms_file')) && !$this->Auth->user('termsaccepted') && (!in_array($this->request->here, array($base_dir.'/users/terms', $base_dir.'/users/logout', $base_dir.'/users/login', $base_dir.'/users/downloadTerms')))) {
|
||||
//if ($this->_isRest()) throw new MethodNotAllowedException('You have not accepted the terms of use yet, please log in via the web interface and accept them.');
|
||||
if (!$this->_isRest()) {
|
||||
$this->redirect(array('controller' => 'users', 'action' => 'terms', 'admin' => false));
|
||||
}
|
||||
} elseif ($this->Auth->user('change_pw') && (!in_array($this->request->here, array($base_dir.'/users/terms', $base_dir.'/users/change_pw', $base_dir.'/users/logout', $base_dir.'/users/login')))) {
|
||||
//if ($this->_isRest()) throw new MethodNotAllowedException('Your user account is expecting a password change, please log in via the web interface and change it before proceeding.');
|
||||
if (!$this->_isRest()) {
|
||||
$this->redirect(array('controller' => 'users', 'action' => 'change_pw', 'admin' => false));
|
||||
}
|
||||
} elseif (!$this->_isRest() && !($this->params['controller'] == 'news' && $this->params['action'] == 'index') && (!in_array($this->request->here, array($base_dir.'/users/terms', $base_dir.'/users/change_pw', $base_dir.'/users/logout', $base_dir.'/users/login')))) {
|
||||
$newsread = $this->User->field('newsread', array('User.id' => $this->Auth->user('id')));
|
||||
$this->loadModel('News');
|
||||
$latest_news = $this->News->field('date_created', array(), 'date_created DESC');
|
||||
if ($latest_news && $newsread < $latest_news) {
|
||||
$this->redirect(array('controller' => 'news', 'action' => 'index', 'admin' => false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($base_dir);
|
||||
// We don't want to run these role checks before the user is logged in, but we want them available for every view once the user is logged on
|
||||
// instead of using checkAction(), like we normally do from controllers when trying to find out about a permission flag, we can use getActions()
|
||||
// getActions returns all the flags in a single SQL query
|
||||
if ($this->Auth->user()) {
|
||||
$this->set('mispVersion', implode('.', array($versionArray['major'], $versionArray['minor'], 0)));
|
||||
$this->set('mispVersion', "{$versionArray['major']}.{$versionArray['minor']}.0");
|
||||
$this->set('mispVersionFull', $this->mispVersion);
|
||||
$role = $this->getActions();
|
||||
$this->set('me', $this->Auth->user());
|
||||
$this->set('me', $user);
|
||||
$role = $user['Role'];
|
||||
$this->set('isAdmin', $role['perm_admin']);
|
||||
$this->set('isSiteAdmin', $role['perm_site_admin']);
|
||||
$this->set('hostOrgUser', $this->Auth->user('org_id') == Configure::read('MISP.host_org_id'));
|
||||
$this->set('hostOrgUser', $user['org_id'] == Configure::read('MISP.host_org_id'));
|
||||
$this->set('isAclAdd', $role['perm_add']);
|
||||
$this->set('isAclModify', $role['perm_modify']);
|
||||
$this->set('isAclModifyOrg', $role['perm_modify_org']);
|
||||
|
@ -475,48 +309,26 @@ class AppController extends Controller
|
|||
$this->set('aclComponent', $this->ACL);
|
||||
$this->userRole = $role;
|
||||
|
||||
$this->set('loggedInUserName', $this->__convertEmailToName($this->Auth->user('email')));
|
||||
$this->set('loggedInUserName', $this->__convertEmailToName($user['email']));
|
||||
$this->__accessMonitor($user);
|
||||
|
||||
if (
|
||||
Configure::read('MISP.log_paranoid') ||
|
||||
!empty(Configure::read('Security.monitored'))
|
||||
) {
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
$this->Log->create();
|
||||
$change = 'HTTP method: ' . $_SERVER['REQUEST_METHOD'] . PHP_EOL . 'Target: ' . $this->here;
|
||||
if (
|
||||
(
|
||||
$this->request->is('post') ||
|
||||
$this->request->is('put')
|
||||
) &&
|
||||
(
|
||||
!empty(Configure::read('MISP.log_paranoid_include_post_body')) ||
|
||||
!empty(Configure::read('Security.monitored'))
|
||||
)
|
||||
) {
|
||||
$payload = $this->request->input();
|
||||
if (!empty($payload['_Token'])) {
|
||||
unset($payload['_Token']);
|
||||
}
|
||||
$change .= PHP_EOL . 'Request body: ' . json_encode($payload);
|
||||
}
|
||||
$log = array(
|
||||
'org' => $this->Auth->user('Organisation')['name'],
|
||||
'model' => 'User',
|
||||
'model_id' => $this->Auth->user('id'),
|
||||
'email' => $this->Auth->user('email'),
|
||||
'action' => 'request',
|
||||
'title' => 'Paranoid log entry',
|
||||
'change' => $change,
|
||||
);
|
||||
$this->Log->save($log);
|
||||
}
|
||||
} else {
|
||||
$pre_auth_actions = array('login', 'register', 'getGpgPublicKey');
|
||||
if (!empty(Configure::read('Security.email_otp_enabled'))) {
|
||||
$pre_auth_actions[] = 'email_otp';
|
||||
}
|
||||
if (!$this->_isControllerAction(['users' => $pre_auth_actions])) {
|
||||
if (!$this->request->is('ajax')) {
|
||||
$this->Session->write('pre_login_requested_url', $this->here);
|
||||
}
|
||||
$this->_redirectToLogin();
|
||||
}
|
||||
|
||||
$this->set('me', false);
|
||||
}
|
||||
|
||||
if ($this->Auth->user() && $this->_isSiteAdmin()) {
|
||||
if (Configure::read('Session.defaults') == 'database') {
|
||||
if (Configure::read('Session.defaults') === 'database') {
|
||||
$db = ConnectionManager::getDataSource('default');
|
||||
$sqlResult = $db->query('SELECT COUNT(id) AS session_count FROM cake_sessions WHERE expires < ' . time() . ';');
|
||||
if (isset($sqlResult[0][0]['session_count']) && $sqlResult[0][0]['session_count'] > 1000) {
|
||||
|
@ -545,28 +357,329 @@ class AppController extends Controller
|
|||
}
|
||||
}
|
||||
}
|
||||
$this->components['RestResponse']['sql_dump'] = $this->sql_dump;
|
||||
|
||||
// Notifications and homepage is not necessary for AJAX or REST requests
|
||||
if ($this->Auth->user() && !$this->_isRest() && !$this->request->is('ajax')) {
|
||||
if ($this->request->params['controller'] === 'users' && $this->request->params['action'] === 'dashboard') {
|
||||
$notifications = $this->{$this->modelClass}->populateNotifications($this->Auth->user());
|
||||
$notifications = $this->User->populateNotifications($this->Auth->user());
|
||||
} else {
|
||||
$notifications = $this->{$this->modelClass}->populateNotifications($this->Auth->user(), 'fast');
|
||||
$notifications = $this->User->populateNotifications($this->Auth->user(), 'fast');
|
||||
}
|
||||
$this->set('notifications', $notifications);
|
||||
|
||||
$this->loadModel('UserSetting');
|
||||
$homepage = $this->UserSetting->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array(
|
||||
'UserSetting.user_id' => $this->Auth->user('id'),
|
||||
'UserSetting.setting' => 'homepage'
|
||||
),
|
||||
'contain' => array('User.id', 'User.org_id')
|
||||
));
|
||||
$homepage = $this->User->UserSetting->getValueForUser($this->Auth->user('id'), 'homepage');
|
||||
if (!empty($homepage)) {
|
||||
$this->set('homepage', $homepage['UserSetting']['value']);
|
||||
$this->set('homepage', $homepage);
|
||||
}
|
||||
if (version_compare(phpversion(), '8.0') >= 0) {
|
||||
$this->Flash->error(__('WARNING: MISP is currently running under PHP 8.0, which is unsupported. Background jobs will fail, so please contact your administrator to run a supported PHP version (such as 7.4)'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|bool True if authkey was correct, False if incorrect and Null if not provided
|
||||
* @throws Exception
|
||||
*/
|
||||
private function __loginByAuthKey()
|
||||
{
|
||||
if (Configure::read('Security.authkey_keep_session') && $this->Auth->user()) {
|
||||
// Do not check authkey if session is establish and correct, just close session to allow multiple requests
|
||||
session_write_close();
|
||||
return true;
|
||||
}
|
||||
|
||||
// If enabled, allow passing the API key via a named parameter (for crappy legacy systems only)
|
||||
$namedParamAuthkey = false;
|
||||
if (Configure::read('Security.allow_unsafe_apikey_named_param') && !empty($this->params['named']['apikey'])) {
|
||||
$namedParamAuthkey = $this->params['named']['apikey'];
|
||||
}
|
||||
// Authenticate user with authkey in Authorization HTTP header
|
||||
if (!empty($_SERVER['HTTP_AUTHORIZATION']) || !empty($namedParamAuthkey)) {
|
||||
$foundMispAuthKey = false;
|
||||
$authentication = explode(',', $_SERVER['HTTP_AUTHORIZATION']);
|
||||
if (!empty($namedParamAuthkey)) {
|
||||
$authentication[] = $namedParamAuthkey;
|
||||
}
|
||||
$user = false;
|
||||
foreach ($authentication as $authKey) {
|
||||
$authKey = trim($authKey);
|
||||
if (preg_match('/^[a-zA-Z0-9]{40}$/', $authKey)) {
|
||||
$foundMispAuthKey = true;
|
||||
$temp = $this->checkAuthUser($authKey);
|
||||
if ($temp) {
|
||||
$user = $temp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($foundMispAuthKey) {
|
||||
$authKeyToStore = substr($authKey, 0, 4)
|
||||
. str_repeat('*', 32)
|
||||
. substr($authKey, -4);
|
||||
if ($user) {
|
||||
unset($user['gpgkey']);
|
||||
unset($user['certif_public']);
|
||||
// User found in the db, add the user info to the session
|
||||
if (Configure::read('MISP.log_auth')) {
|
||||
$this->loadModel('Log');
|
||||
$this->Log->create();
|
||||
$log = array(
|
||||
'org' => $user['Organisation']['name'],
|
||||
'model' => 'User',
|
||||
'model_id' => $user['id'],
|
||||
'email' => $user['email'],
|
||||
'action' => 'auth',
|
||||
'title' => "Successful authentication using API key ($authKeyToStore)",
|
||||
'change' => 'HTTP method: ' . $_SERVER['REQUEST_METHOD'] . PHP_EOL . 'Target: ' . $this->here,
|
||||
);
|
||||
$this->Log->save($log);
|
||||
}
|
||||
$this->Session->renew();
|
||||
$this->Session->write(AuthComponent::$sessionKey, $user);
|
||||
$this->isApiAuthed = true;
|
||||
return true;
|
||||
} else {
|
||||
// User not authenticated correctly
|
||||
// reset the session information
|
||||
$redis = $this->User->setupRedis();
|
||||
// Do not log every fail, but just once per hour
|
||||
if ($redis && !$redis->exists('misp:auth_fail_throttling:' . $authKeyToStore)) {
|
||||
$redis->setex('misp:auth_fail_throttling:' . $authKeyToStore, 3600, 1);
|
||||
$this->loadModel('Log');
|
||||
$this->Log->create();
|
||||
$log = array(
|
||||
'org' => 'SYSTEM',
|
||||
'model' => 'User',
|
||||
'model_id' => 0,
|
||||
'email' => 'SYSTEM',
|
||||
'action' => 'auth_fail',
|
||||
'title' => "Failed authentication using API key ($authKeyToStore)",
|
||||
'change' => null,
|
||||
);
|
||||
$this->Log->save($log);
|
||||
}
|
||||
$this->Session->destroy();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if:
|
||||
* - user exists in database
|
||||
* - is not disabled
|
||||
* - need to force logout
|
||||
* - accepted terms and conditions
|
||||
* - must change password
|
||||
* - reads latest news
|
||||
*
|
||||
* @param array $user
|
||||
* @return bool
|
||||
*/
|
||||
private function __verifyUser(array $user)
|
||||
{
|
||||
// Skip these checks for 'checkIfLoggedIn' action to make that call fast
|
||||
if ($this->_isControllerAction(['users' => ['checkIfLoggedIn']])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load last user profile modification from database
|
||||
$userFromDb = $this->User->find('first', [
|
||||
'conditions' => ['id' => $user['id']],
|
||||
'recursive' => -1,
|
||||
'fields' => ['date_modified'],
|
||||
]);
|
||||
|
||||
// Check if user with given ID exists
|
||||
if (!$userFromDb) {
|
||||
$message = __('Something went wrong. Your user account that you are authenticated with doesn\'t exist anymore.');
|
||||
if ($this->_isRest()) {
|
||||
// TODO: Why not exception?
|
||||
$response = $this->RestResponse->throwException(401, $message);
|
||||
$response->send();
|
||||
$this->_stop();
|
||||
} else {
|
||||
$this->Flash->info($message);
|
||||
$this->Auth->logout();
|
||||
$this->_redirectToLogin();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if session data contain latest changes from db
|
||||
if ((int)$user['date_modified'] < (int)$userFromDb['User']['date_modified']) {
|
||||
$user = $this->_refreshAuth(); // session data are old, reload from database
|
||||
}
|
||||
|
||||
// Check if MISP access is enabled
|
||||
if (!Configure::read('MISP.live')) {
|
||||
if (!$user['Role']['perm_site_admin']) {
|
||||
$message = Configure::read('MISP.maintenance_message');
|
||||
if (empty($message)) {
|
||||
$this->loadModel('Server');
|
||||
$message = $this->Server->serverSettings['MISP']['maintenance_message']['value'];
|
||||
}
|
||||
if (strpos($message, '$email') && Configure::read('MISP.email')) {
|
||||
$email = Configure::read('MISP.email');
|
||||
$message = str_replace('$email', $email, $message);
|
||||
}
|
||||
$this->Flash->info($message);
|
||||
$this->Auth->logout();
|
||||
throw new MethodNotAllowedException($message);//todo this should pb be removed?
|
||||
} else {
|
||||
$this->Flash->error(__('Warning: MISP is currently disabled for all users. Enable it in Server Settings (Administration -> Server Settings -> MISP tab -> live). An update might also be in progress, you can see the progress in ') , array('params' => array('url' => $this->baseurl . '/servers/updateProgress/', 'urlName' => __('Update Progress')), 'clear' => 1));
|
||||
}
|
||||
}
|
||||
|
||||
// Force logout doesn't make sense for API key authentication
|
||||
if (!$this->isApiAuthed && $user['force_logout']) {
|
||||
$this->User->id = $user['id'];
|
||||
$this->User->saveField('force_logout', false);
|
||||
$this->Auth->logout();
|
||||
$this->_redirectToLogin();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($user['disabled']) {
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
$this->Log->createLogEntry($user, 'auth_fail', 'User', $user['id'], 'Login attempt by disabled user.');
|
||||
|
||||
$this->Auth->logout();
|
||||
if ($this->_isRest()) {
|
||||
throw new ForbiddenException('Authentication failed. Your user account has been disabled.');
|
||||
} else {
|
||||
$this->Flash->error(__('Your user account has been disabled.'));
|
||||
$this->_redirectToLogin();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if auth key is not expired. Make sense when Security.authkey_keep_session is enabled.
|
||||
if (isset($user['authkey_expiration']) && $user['authkey_expiration']) {
|
||||
$time = isset($_SERVER['REQUEST_TIME']) ? $_SERVER['REQUEST_TIME'] : time();
|
||||
if ($user['authkey_expiration'] < $time) {
|
||||
$this->Auth->logout();
|
||||
throw new ForbiddenException('Auth key is expired');
|
||||
}
|
||||
}
|
||||
|
||||
$isUserRequest = !$this->_isRest() && !$this->request->is('ajax') && !$this->_isAutomation();
|
||||
// Next checks makes sense just for user direct HTTP request, so skip REST and AJAX calls
|
||||
if (!$isUserRequest) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if user accepted terms and conditions
|
||||
if (!$user['termsaccepted'] && !empty(Configure::read('MISP.terms_file')) && !$this->_isControllerAction(['users' => ['terms', 'logout', 'login', 'downloadTerms']])) {
|
||||
//if ($this->_isRest()) throw new MethodNotAllowedException('You have not accepted the terms of use yet, please log in via the web interface and accept them.');
|
||||
$this->redirect(array('controller' => 'users', 'action' => 'terms', 'admin' => false));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if user must change password
|
||||
if ($user['change_pw'] && !$this->_isControllerAction(['users' => ['terms', 'change_pw', 'logout', 'login']])) {
|
||||
//if ($this->_isRest()) throw new MethodNotAllowedException('Your user account is expecting a password change, please log in via the web interface and change it before proceeding.');
|
||||
$this->redirect(array('controller' => 'users', 'action' => 'change_pw', 'admin' => false));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if user must read news
|
||||
if (!$this->_isControllerAction(['news' => ['index'], 'users' => ['terms', 'change_pw', 'login', 'logout']])) {
|
||||
$this->loadModel('News');
|
||||
$latestNewsCreated = $this->News->field('date_created', array(), 'date_created DESC');
|
||||
if ($latestNewsCreated && $user['newsread'] < $latestNewsCreated) {
|
||||
$this->redirect(array('controller' => 'news', 'action' => 'index', 'admin' => false));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $actionsToCheck
|
||||
* @return bool
|
||||
*/
|
||||
private function _isControllerAction($actionsToCheck = [])
|
||||
{
|
||||
$controller = Inflector::variable($this->request->params['controller']);
|
||||
if (!isset($actionsToCheck[$controller])) {
|
||||
return false;
|
||||
}
|
||||
return in_array($this->action, $actionsToCheck[$controller], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* User access monitoring
|
||||
* @param array $user
|
||||
*/
|
||||
private function __logAccess(array $user)
|
||||
{
|
||||
$logUserIps = Configure::read('MISP.log_user_ips');
|
||||
if (!$logUserIps) {
|
||||
return;
|
||||
}
|
||||
|
||||
$redis = $this->User->setupRedis();
|
||||
if (!$redis) {
|
||||
return;
|
||||
}
|
||||
|
||||
$remoteAddress = trim($_SERVER['REMOTE_ADDR']);
|
||||
|
||||
$pipe = $redis->multi(Redis::PIPELINE);
|
||||
// keep for 30 days
|
||||
$pipe->setex('misp:ip_user:' . $remoteAddress, 60 * 60 * 24 * 30, $user['id']);
|
||||
$pipe->sadd('misp:user_ip:' . $user['id'], $remoteAddress);
|
||||
|
||||
// Log key usage if enabled
|
||||
if (isset($user['authkey_id']) && Configure::read('MISP.log_user_ips_authkeys')) {
|
||||
// Use request time if defined
|
||||
$time = isset($_SERVER['REQUEST_TIME']) ? $_SERVER['REQUEST_TIME'] : time();
|
||||
$hashKey = date("Y-m-d", $time) . ":$remoteAddress";
|
||||
$pipe->hIncrBy("misp:authkey_usage:{$user['authkey_id']}", $hashKey, 1);
|
||||
// delete after one year of inactivity
|
||||
$pipe->expire("misp:authkey_usage:{$user['authkey_id']}", 3600 * 24 * 365);
|
||||
$pipe->set("misp:authkey_last_usage:{$user['authkey_id']}", $time);
|
||||
}
|
||||
$pipe->exec();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $user
|
||||
* @throws Exception
|
||||
*/
|
||||
private function __accessMonitor(array $user)
|
||||
{
|
||||
$userMonitoringEnabled = Configure::read('Security.user_monitoring_enabled');
|
||||
if ($userMonitoringEnabled) {
|
||||
$redis = $this->User->setupRedis();
|
||||
$userMonitoringEnabled = $redis && $redis->sismember('misp:monitored_users', $user['id']);
|
||||
}
|
||||
|
||||
if (Configure::read('MISP.log_paranoid') || $userMonitoringEnabled) {
|
||||
$change = 'HTTP method: ' . $_SERVER['REQUEST_METHOD'] . PHP_EOL . 'Target: ' . $this->here;
|
||||
if (
|
||||
(
|
||||
$this->request->is('post') ||
|
||||
$this->request->is('put')
|
||||
) &&
|
||||
(
|
||||
!empty(Configure::read('MISP.log_paranoid_include_post_body')) ||
|
||||
$userMonitoringEnabled
|
||||
)
|
||||
) {
|
||||
$payload = $this->request->input();
|
||||
$change .= PHP_EOL . 'Request body: ' . $payload;
|
||||
}
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
try {
|
||||
$this->Log->createLogEntry($user, 'request', 'User', $user['id'], 'Paranoid log entry', $change);
|
||||
} catch (Exception $e) {
|
||||
// When `MISP.log_skip_db_logs_completely` is enabled, Log::createLogEntry method throws exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -601,7 +714,7 @@ class AppController extends Controller
|
|||
|
||||
public function afterFilter()
|
||||
{
|
||||
if ($this->isApiAuthed && $this->_isRest() && $this->Session->started()) {
|
||||
if ($this->isApiAuthed && $this->_isRest() && !Configure::read('Security.authkey_keep_session')) {
|
||||
$this->Session->destroy();
|
||||
}
|
||||
}
|
||||
|
@ -648,16 +761,17 @@ class AppController extends Controller
|
|||
/*
|
||||
* Sanitize the configured `MISP.baseurl` and expose it to the view as `baseurl`.
|
||||
*/
|
||||
protected function _setupBaseurl() {
|
||||
protected function _setupBaseurl()
|
||||
{
|
||||
// Let us access $baseurl from all views
|
||||
$baseurl = Configure::read('MISP.baseurl');
|
||||
if (substr($baseurl, -1) == '/') {
|
||||
if (substr($baseurl, -1) === '/') {
|
||||
// if the baseurl has a trailing slash, remove it. It can lead to issues with the CSRF protection
|
||||
$baseurl = rtrim($baseurl, '/');
|
||||
$this->loadModel('Server');
|
||||
$this->Server->serverSettingsSaveValue('MISP.baseurl', $baseurl);
|
||||
}
|
||||
if (trim($baseurl) == 'http://') {
|
||||
if (trim($baseurl) === 'http://') {
|
||||
$this->Server->serverSettingsSaveValue('MISP.baseurl', '');
|
||||
}
|
||||
$this->baseurl = $baseurl;
|
||||
|
@ -683,8 +797,6 @@ class AppController extends Controller
|
|||
throw new BadRequestException('The request has been black-holed');
|
||||
}
|
||||
|
||||
public $userRole = null;
|
||||
|
||||
protected function _isRest()
|
||||
{
|
||||
return $this->IndexFilter->isRest();
|
||||
|
@ -692,12 +804,7 @@ class AppController extends Controller
|
|||
|
||||
protected function _isAutomation()
|
||||
{
|
||||
foreach ($this->automationArray as $controllerName => $controllerActions) {
|
||||
if ($this->params['controller'] == $controllerName && in_array($this->params['action'], $controllerActions)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return $this->IndexFilter->isApiFunction($this->params['controller'], $this->params['action']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -829,34 +936,12 @@ class AppController extends Controller
|
|||
return $data;
|
||||
}
|
||||
|
||||
// pass an action to this method for it to check the active user's access to the action
|
||||
public function checkAction($action = 'perm_sync')
|
||||
{
|
||||
$this->loadModel('Role');
|
||||
$this->Role->recursive = -1;
|
||||
$role = $this->Role->findById($this->Auth->user('role_id'));
|
||||
if ($role['Role'][$action]) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// returns the role of the currently authenticated user as an array, used to set the permission variables for views in the AppController's beforeFilter() method
|
||||
public function getActions()
|
||||
{
|
||||
$this->loadModel('Role');
|
||||
$this->Role->recursive = -1;
|
||||
$role = $this->Role->findById($this->Auth->user('role_id'));
|
||||
return $role['Role'];
|
||||
}
|
||||
|
||||
public function checkAuthUser($authkey)
|
||||
{
|
||||
if (Configure::read('Security.advanced_authkeys')) {
|
||||
$this->loadModel('AuthKey');
|
||||
$user = $this->AuthKey->getAuthUserByAuthKey($authkey);
|
||||
} else {
|
||||
$this->loadModel('User');
|
||||
$user = $this->User->getAuthUserByAuthKey($authkey);
|
||||
}
|
||||
|
||||
|
@ -866,22 +951,16 @@ class AppController extends Controller
|
|||
if (!$user['Role']['perm_auth']) {
|
||||
return false;
|
||||
}
|
||||
if ($user['Role']['perm_site_admin']) {
|
||||
$user['siteadmin'] = true;
|
||||
}
|
||||
$user['logged_by_authkey'] = true;
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function checkExternalAuthUser($authkey)
|
||||
{
|
||||
$this->loadModel('User');
|
||||
$user = $this->User->getAuthUserByExternalAuth($authkey);
|
||||
if (empty($user)) {
|
||||
return false;
|
||||
}
|
||||
if ($user['Role']['perm_site_admin']) {
|
||||
$user['siteadmin'] = true;
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
|
@ -1270,7 +1349,7 @@ class AppController extends Controller
|
|||
$final = $this->$scope->restSearch($user, $returnFormat, $filters, false, false, $elementCounter, $renderView);
|
||||
if (!empty($renderView) && !empty($final)) {
|
||||
$this->layout = false;
|
||||
$final = json_decode($final, true);
|
||||
$final = json_decode($final->intoString(), true);
|
||||
foreach ($final as $key => $data) {
|
||||
$this->set($key, $data);
|
||||
}
|
||||
|
@ -1331,4 +1410,30 @@ class AppController extends Controller
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh user data in session, but keep information about authkey.
|
||||
* @return array User data in auth format
|
||||
*/
|
||||
protected function _refreshAuth()
|
||||
{
|
||||
$sessionUser = $this->Auth->user();
|
||||
$user = $this->User->getAuthUser($sessionUser['id']);
|
||||
if (!$user) {
|
||||
throw new RuntimeException("User with ID {$sessionUser['id']} not exists.");
|
||||
}
|
||||
if (isset($sessionUser['authkey_id'])) {
|
||||
$this->loadModel('AuthKey');
|
||||
if (!$this->AuthKey->exists($sessionUser['authkey_id'])) {
|
||||
throw new RuntimeException("Auth key with ID {$sessionUser['authkey_id']} not exists.");
|
||||
}
|
||||
}
|
||||
foreach (['authkey_id', 'authkey_expiration', 'logged_by_authkey'] as $copy) {
|
||||
if (isset($sessionUser[$copy])) {
|
||||
$user[$copy] = $sessionUser[$copy];
|
||||
}
|
||||
}
|
||||
$this->Auth->login($user);
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,20 +83,20 @@ class AttributesController extends AppController
|
|||
}
|
||||
return $this->RestResponse->viewData($attributes, $this->response->type());
|
||||
}
|
||||
$orgTable = $this->Attribute->Event->Orgc->find('list', array(
|
||||
'fields' => array('Orgc.id', 'Orgc.name')
|
||||
));
|
||||
|
||||
$orgTable = $this->Attribute->Event->Orgc->find('all', [
|
||||
'fields' => ['Orgc.id', 'Orgc.name', 'Orgc.uuid'],
|
||||
]);
|
||||
$orgTable = Hash::combine($orgTable, '{n}.Orgc.id', '{n}.Orgc');
|
||||
foreach ($attributes as &$attribute) {
|
||||
if (isset($orgTable[$attribute['Event']['orgc_id']])) {
|
||||
$attribute['Event']['Orgc'] = [
|
||||
'id' => $attribute['Event']['orgc_id'],
|
||||
'name' => $orgTable[$attribute['Event']['orgc_id']],
|
||||
];
|
||||
$attribute['Event']['Orgc'] = $orgTable[$attribute['Event']['orgc_id']];
|
||||
}
|
||||
}
|
||||
|
||||
list($attributes, $sightingsData) = $this->__searchUI($attributes);
|
||||
$this->set('sightingsData', $sightingsData);
|
||||
$this->set('orgTable', $orgTable);
|
||||
$this->set('orgTable', array_column($orgTable, 'name', 'id'));
|
||||
$this->set('shortDist', $this->Attribute->shortDist);
|
||||
$this->set('attributes', $attributes);
|
||||
$this->set('attrDescriptions', $this->Attribute->fieldDescriptions);
|
||||
|
@ -115,7 +115,7 @@ class AttributesController extends AppController
|
|||
if (!$this->userRole['perm_add']) {
|
||||
throw new MethodNotAllowedException(__('You do not have permissions to create attributes'));
|
||||
}
|
||||
$event = $this->Attribute->Event->fetchSimpleEvent($this->Auth->user(), $eventId);
|
||||
$event = $this->Attribute->Event->fetchSimpleEvent($this->Auth->user(), $eventId, ['contain' => ['Orgc']]);
|
||||
if (!$event) {
|
||||
throw new NotFoundException(__('Invalid event'));
|
||||
}
|
||||
|
@ -355,7 +355,7 @@ class AttributesController extends AppController
|
|||
|
||||
public function add_attachment($eventId = null)
|
||||
{
|
||||
$event = $this->Attribute->Event->fetchSimpleEvent($this->Auth->user(), $eventId);
|
||||
$event = $this->Attribute->Event->fetchSimpleEvent($this->Auth->user(), $eventId, ['contain' => ['Orgc']]);
|
||||
if (empty($event)) {
|
||||
throw new NotFoundException(__('Invalid Event.'));
|
||||
}
|
||||
|
@ -998,6 +998,7 @@ class AttributesController extends AppController
|
|||
'includeAllTags' => false,
|
||||
'includeAttributeUuid' => true,
|
||||
'flatten' => true,
|
||||
'deleted' => [0, 1]
|
||||
);
|
||||
|
||||
if ($this->_isRest()) {
|
||||
|
@ -1505,7 +1506,6 @@ class AttributesController extends AppController
|
|||
'request' => $this->request,
|
||||
'named_params' => $this->params['named'],
|
||||
'paramArray' => $paramArray,
|
||||
'ordered_url_params' => @compact($paramArray),
|
||||
'additional_delimiters' => PHP_EOL
|
||||
);
|
||||
$exception = false;
|
||||
|
@ -1591,21 +1591,16 @@ class AttributesController extends AppController
|
|||
);
|
||||
$attributes = $this->paginate();
|
||||
|
||||
$orgTable = $this->Attribute->Event->Orgc->find('list', array(
|
||||
'fields' => ['Orgc.id', 'Orgc.name'],
|
||||
));
|
||||
$orgTable = $this->Attribute->Event->Orgc->find('all', [
|
||||
'fields' => ['Orgc.id', 'Orgc.name', 'Orgc.uuid'],
|
||||
]);
|
||||
$orgTable = Hash::combine($orgTable, '{n}.Orgc.id', '{n}.Orgc');
|
||||
foreach ($attributes as &$attribute) {
|
||||
if (isset($orgTable[$attribute['Event']['orgc_id']])) {
|
||||
$attribute['Event']['Orgc'] = [
|
||||
'id' => $attribute['Event']['orgc_id'],
|
||||
'name' => $orgTable[$attribute['Event']['orgc_id']],
|
||||
];
|
||||
$attribute['Event']['Orgc'] = $orgTable[$attribute['Event']['orgc_id']];
|
||||
}
|
||||
if (isset($orgTable[$attribute['Event']['org_id']])) {
|
||||
$attribute['Event']['Org'] = [
|
||||
'id' => $attribute['Event']['org_id'],
|
||||
'name' => $orgTable[$attribute['Event']['org_id']],
|
||||
];
|
||||
$attribute['Event']['Org'] = $orgTable[$attribute['Event']['org_id']];
|
||||
}
|
||||
}
|
||||
if ($this->_isRest()) {
|
||||
|
@ -1634,7 +1629,7 @@ class AttributesController extends AppController
|
|||
}
|
||||
}
|
||||
}
|
||||
$this->set('orgTable', $orgTable);
|
||||
$this->set('orgTable', array_column($orgTable, 'name', 'id'));
|
||||
$this->set('filters', $filters);
|
||||
$this->set('attributes', $attributes);
|
||||
$this->set('isSearch', 1);
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property AuthKey $AuthKey
|
||||
*/
|
||||
class AuthKeysController extends AppController
|
||||
{
|
||||
public $components = array(
|
||||
|
@ -23,13 +26,22 @@ class AuthKeysController extends AppController
|
|||
$this->set('user_id', $id);
|
||||
$conditions['AND'][] = ['AuthKey.user_id' => $id];
|
||||
}
|
||||
$keyUsageEnabled = Configure::read('MISP.log_user_ips') && Configure::read('MISP.log_user_ips_authkeys');
|
||||
$this->CRUD->index([
|
||||
'filters' => ['User.username', 'authkey', 'comment', 'User.id'],
|
||||
'quickFilters' => ['authkey', 'comment'],
|
||||
'contain' => ['User'],
|
||||
'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) {
|
||||
'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;
|
||||
|
@ -38,8 +50,12 @@ class AuthKeysController extends AppController
|
|||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->set('metaGroup', $this->_isAdmin ? 'admin' : 'globalActions');
|
||||
$this->set('metaAction', 'authkeys_index');
|
||||
$this->set('title_for_layout', __('Auth Keys'));
|
||||
$this->set('keyUsageEnabled', $keyUsageEnabled);
|
||||
$this->set('menuData', [
|
||||
'menuList' => $this->_isSiteAdmin() ? 'admin' : 'globalActions',
|
||||
'menuItem' => 'authkeys_index',
|
||||
]);
|
||||
}
|
||||
|
||||
public function delete($id)
|
||||
|
@ -61,18 +77,22 @@ class AuthKeysController extends AppController
|
|||
|
||||
public function add($user_id = false)
|
||||
{
|
||||
$this->set('menuData', array('menuList' => $this->_isSiteAdmin() ? 'admin' : 'globalActions', 'menuItem' => 'authKeyAdd'));
|
||||
$params = [
|
||||
'displayOnSuccess' => 'authkey_display',
|
||||
'saveModelVariable' => ['authkey_raw']
|
||||
'saveModelVariable' => ['authkey_raw'],
|
||||
'override' => ['authkey' => null], // do not allow to use own key, always generate random one
|
||||
'afterFind' => function ($authKey) { // remove hashed key from response
|
||||
unset($authKey['AuthKey']['authkey']);
|
||||
return $authKey;
|
||||
}
|
||||
];
|
||||
$selectConditions = [];
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
$selectConditions['AND'][] = ['User.id' => $this->Auth->user('id')];
|
||||
$params['override'] = ['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];
|
||||
$params['override']['user_id'] = $user_id;
|
||||
}
|
||||
$this->CRUD->add($params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
|
@ -86,6 +106,11 @@ class AuthKeysController extends AppController
|
|||
])
|
||||
];
|
||||
$this->set(compact('dropdownData'));
|
||||
$this->set('menuData', [
|
||||
'menuList' => $this->_isSiteAdmin() ? 'admin' : 'globalActions',
|
||||
'menuItem' => 'authKeyAdd',
|
||||
]);
|
||||
$this->set('validity', Configure::read('Security.advanced_authkeys_validity'));
|
||||
}
|
||||
|
||||
public function view($id = false)
|
||||
|
@ -101,6 +126,15 @@ class AuthKeysController extends AppController
|
|||
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',
|
||||
|
|
|
@ -30,13 +30,11 @@ class CerebratesController extends AppController
|
|||
|
||||
public function add()
|
||||
{
|
||||
$this->set('menuData', array('menuList' => 'sync', 'menuItem' => 'add_cerebrate'));
|
||||
$params = [];
|
||||
$this->CRUD->add($params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
if ($this->restResponsePayload) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->set('permFlags', $this->Role->permFlags);
|
||||
|
||||
$this->loadModel('Organisation');
|
||||
$orgs = $this->Organisation->find('list', [
|
||||
|
@ -48,6 +46,7 @@ class CerebratesController extends AppController
|
|||
'org_id' => $orgs
|
||||
];
|
||||
$this->set(compact('dropdownData'));
|
||||
$this->set('menuData', array('menuList' => 'sync', 'menuItem' => 'add_cerebrate'));
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
|
@ -59,7 +58,6 @@ class CerebratesController extends AppController
|
|||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->set('permFlags', $this->Role->permFlags);
|
||||
|
||||
$this->loadModel('Organisation');
|
||||
$orgs = $this->Organisation->find('list', [
|
||||
|
|
|
@ -15,13 +15,11 @@ class ACLComponent extends Component
|
|||
private $__aclList = array(
|
||||
'*' => array(
|
||||
'blackhole' => array(),
|
||||
'checkAction' => array(),
|
||||
'checkAuthUser' => array(),
|
||||
'checkExternalAuthUser' => array(),
|
||||
'cleanModelCaches' => array(),
|
||||
'debugACL' => array(),
|
||||
'generateCount' => array(),
|
||||
'getActions' => array(),
|
||||
'pruneDuplicateUUIDs' => array(),
|
||||
'queryACL' => array(),
|
||||
'removeDuplicateEvents' => array(),
|
||||
|
@ -89,6 +87,13 @@ class ACLComponent extends Component
|
|||
'pull_orgs' => [],
|
||||
'view' => []
|
||||
],
|
||||
'correlationExclusions' => [
|
||||
'add' => [],
|
||||
'clean' => [],
|
||||
'delete' => [],
|
||||
'index' => [],
|
||||
'view' => []
|
||||
],
|
||||
'dashboards' => array(
|
||||
'getForm' => array('*'),
|
||||
'index' => array('*'),
|
||||
|
@ -161,11 +166,11 @@ class ACLComponent extends Component
|
|||
]
|
||||
),
|
||||
'eventDelegations' => array(
|
||||
'acceptDelegation' => array('perm_add'),
|
||||
'delegateEvent' => array('perm_delegate'),
|
||||
'deleteDelegation' => array('perm_add'),
|
||||
'index' => array('*'),
|
||||
'view' => array('*'),
|
||||
'acceptDelegation' => array('AND' => ['delegation_enabled', 'perm_add']),
|
||||
'delegateEvent' => array('AND' => ['delegation_enabled', 'perm_delegate']),
|
||||
'deleteDelegation' => array('AND' => ['delegation_enabled', 'perm_add']),
|
||||
'index' => array('delegation_enabled'),
|
||||
'view' => array('delegation_enabled'),
|
||||
),
|
||||
'eventReports' => array(
|
||||
'add' => array('perm_add'),
|
||||
|
@ -409,6 +414,7 @@ class ACLComponent extends Component
|
|||
'edit' => array('perm_object_template'),
|
||||
'delete' => array('perm_object_template'),
|
||||
'getToggleField' => array(),
|
||||
'getRaw' => array('perm_object_template'),
|
||||
'objectChoice' => array('*'),
|
||||
'objectMetaChoice' => array('perm_add'),
|
||||
'view' => array('*'),
|
||||
|
@ -463,7 +469,6 @@ class ACLComponent extends Component
|
|||
'admin_add' => array(),
|
||||
'admin_delete' => array(),
|
||||
'admin_edit' => array(),
|
||||
'admin_index' => array('perm_admin'),
|
||||
'admin_set_default' => array(),
|
||||
'index' => array('*'),
|
||||
'view' => array('*'),
|
||||
|
@ -501,6 +506,7 @@ class ACLComponent extends Component
|
|||
'postTest' => array('perm_sync'),
|
||||
'previewEvent' => array(),
|
||||
'previewIndex' => array(),
|
||||
'compareServers' => [],
|
||||
'pull' => array(),
|
||||
'purgeSessions' => array(),
|
||||
'push' => array(),
|
||||
|
@ -539,6 +545,7 @@ class ACLComponent extends Component
|
|||
'generateCorrelation' => array(),
|
||||
'index' => array('*'),
|
||||
'view' => array('*'),
|
||||
'viewPicture' => array('*'),
|
||||
),
|
||||
'sharingGroups' => array(
|
||||
'add' => array('perm_sharing_group'),
|
||||
|
@ -617,6 +624,7 @@ class ACLComponent extends Component
|
|||
'taxonomyMassUnhide' => array('perm_tagger'),
|
||||
'toggleRequired' => array('perm_site_admin'),
|
||||
'update' => array(),
|
||||
'import' => [],
|
||||
'view' => array('*'),
|
||||
'unhideTag' => array('perm_tagger'),
|
||||
'hideTag' => array('perm_tagger'),
|
||||
|
@ -687,6 +695,7 @@ class ACLComponent extends Component
|
|||
'verifyCertificate' => array(),
|
||||
'verifyGPG' => array(),
|
||||
'view' => array('*'),
|
||||
'getGpgPublicKey' => array('*'),
|
||||
),
|
||||
'userSettings' => array(
|
||||
'index' => array('*'),
|
||||
|
@ -748,6 +757,9 @@ class ACLComponent extends Component
|
|||
}
|
||||
return true;
|
||||
};
|
||||
$this->dynamicChecks['delegation_enabled'] = function (array $user) {
|
||||
return (bool)Configure::read('MISP.delegation');
|
||||
};
|
||||
}
|
||||
|
||||
private function __checkLoggedActions($user, $controller, $action)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
class CRUDComponent extends Component
|
||||
{
|
||||
/** @var AppController */
|
||||
public $Controller = null;
|
||||
|
||||
public function initialize(Controller $controller, $settings=array()) {
|
||||
|
@ -15,7 +16,7 @@ class CRUDComponent extends Component
|
|||
}
|
||||
}
|
||||
|
||||
public function index($options)
|
||||
public function index(array $options)
|
||||
{
|
||||
$this->prepareResponse();
|
||||
if (!empty($options['quickFilters'])) {
|
||||
|
@ -75,8 +76,6 @@ class CRUDComponent extends Component
|
|||
$input[$modelName][$field] = $value;
|
||||
}
|
||||
}
|
||||
if (isset($input[$modelName]['id'])) {
|
||||
}
|
||||
unset($input[$modelName]['id']);
|
||||
if (!empty($params['fields'])) {
|
||||
$data = [];
|
||||
|
@ -86,20 +85,25 @@ class CRUDComponent extends Component
|
|||
} else {
|
||||
$data = $input;
|
||||
}
|
||||
if ($this->Controller->{$modelName}->save($data)) {
|
||||
$data = $this->Controller->{$modelName}->find('first', [
|
||||
/** @var Model $model */
|
||||
$model = $this->Controller->{$modelName};
|
||||
if ($model->save($data)) {
|
||||
$data = $model->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => [
|
||||
'id' => $this->Controller->{$modelName}->id
|
||||
'id' => $model->id
|
||||
]
|
||||
]);
|
||||
if (!empty($params['saveModelVariable'])) {
|
||||
foreach ($params['saveModelVariable'] as $var) {
|
||||
if (isset($this->Controller->{$modelName}->$var)) {
|
||||
$data[$modelName][$var] = $this->Controller->{$modelName}->$var;
|
||||
if (isset($model->$var)) {
|
||||
$data[$modelName][$var] = $model->$var;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($params['afterFind'])) {
|
||||
$data = $params['afterFind']($data);
|
||||
}
|
||||
$message = __('%s added.', $modelName);
|
||||
if ($this->Controller->IndexFilter->isRest()) {
|
||||
$this->Controller->restResponsePayload = $this->Controller->RestResponse->viewData($data, 'json');
|
||||
|
@ -111,12 +115,22 @@ class CRUDComponent extends Component
|
|||
$this->Controller->render($params['displayOnSuccess']);
|
||||
return;
|
||||
}
|
||||
$this->Controller->redirect(['action' => 'index']);
|
||||
|
||||
$redirect = isset($params['redirect']) ? $params['redirect'] : ['action' => 'index'];
|
||||
// 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);
|
||||
}
|
||||
|
@ -125,7 +139,7 @@ class CRUDComponent extends Component
|
|||
$this->Controller->set('entity', $data);
|
||||
}
|
||||
|
||||
public function edit(int $id, array $params = []): void
|
||||
public function edit(int $id, array $params = [])
|
||||
{
|
||||
$modelName = $this->Controller->defaultModel;
|
||||
if (empty($id)) {
|
||||
|
@ -161,9 +175,10 @@ class CRUDComponent extends Component
|
|||
$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(['action' => 'index']);
|
||||
$this->Controller->redirect(isset($params['redirect']) ? $params['redirect'] : ['action' => 'index']);
|
||||
}
|
||||
} else {
|
||||
if ($this->Controller->IndexFilter->isRest()) {
|
||||
|
@ -176,7 +191,7 @@ class CRUDComponent extends Component
|
|||
$this->Controller->set('entity', $data);
|
||||
}
|
||||
|
||||
public function view(int $id, array $params = []): void
|
||||
public function view(int $id, array $params = [])
|
||||
{
|
||||
$modelName = $this->Controller->defaultModel;
|
||||
if (empty($id)) {
|
||||
|
@ -204,7 +219,7 @@ class CRUDComponent extends Component
|
|||
}
|
||||
}
|
||||
|
||||
public function delete(int $id, array $params = []): void
|
||||
public function delete(int $id, array $params = [])
|
||||
{
|
||||
$this->prepareResponse();
|
||||
$modelName = $this->Controller->defaultModel;
|
||||
|
@ -224,7 +239,18 @@ class CRUDComponent extends Component
|
|||
if (empty($data)) {
|
||||
throw new NotFoundException(__('Invalid %s.', $modelName));
|
||||
}
|
||||
if ($this->Controller->request->is('post') || $this->Controller->request->is('delete')) {
|
||||
$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 ($validationError === null && $this->Controller->request->is('post') || $this->Controller->request->is('delete')) {
|
||||
if (!empty($params['modelFunction'])) {
|
||||
$result = $this->Controller->$modelName->{$params['modelFunction']}($id);
|
||||
} else {
|
||||
|
@ -234,36 +260,39 @@ class CRUDComponent extends Component
|
|||
$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');
|
||||
}
|
||||
|
||||
|
||||
protected function setQuickFilters($params, $query, $quickFilterFields)
|
||||
protected function setQuickFilters($params, array $query, $quickFilterFields)
|
||||
{
|
||||
$queryConditions = [];
|
||||
if (!empty($params['quickFilter']) && !empty($quickFilterFields)) {
|
||||
$queryConditions = [];
|
||||
$filter = '%' . strtolower($params['quickFilter']) . '%';
|
||||
foreach ($quickFilterFields as $filterField) {
|
||||
$queryConditions[$filterField] = $params['quickFilter'];
|
||||
$queryConditions["LOWER($filterField) LIKE"] = $filter;
|
||||
}
|
||||
$query['conditions']['OR'][] = $queryConditions;
|
||||
$query['conditions']['OR'] = $queryConditions;
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function setFilters($params, $query)
|
||||
protected function setFilters(array $params, array $query)
|
||||
{
|
||||
$params = $this->massageFilters($params);
|
||||
if (!empty($params['simpleFilters'])) {
|
||||
foreach ($params['simpleFilters'] as $filter => $filterValue) {
|
||||
// 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;
|
||||
}
|
||||
|
@ -291,7 +320,7 @@ class CRUDComponent extends Component
|
|||
return $query;
|
||||
}
|
||||
|
||||
protected function massageFilters(array $params): array
|
||||
protected function massageFilters(array $params)
|
||||
{
|
||||
$massagedFilters = [
|
||||
'simpleFilters' => [],
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
|
||||
class IndexFilterComponent extends Component
|
||||
{
|
||||
public $Controller = false;
|
||||
/** @var Controller */
|
||||
public $Controller;
|
||||
public $isRest = null;
|
||||
|
||||
public function initialize(Controller $controller) {
|
||||
|
@ -74,7 +75,7 @@ class IndexFilterComponent extends Component
|
|||
}
|
||||
}
|
||||
}
|
||||
$this->Controller->set('passedArgs', json_encode($this->Controller->passedArgs, true));
|
||||
$this->Controller->set('passedArgs', json_encode($this->Controller->passedArgs));
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
@ -85,12 +86,7 @@ class IndexFilterComponent extends Component
|
|||
return $this->isRest;
|
||||
}
|
||||
$api = $this->isApiFunction($this->Controller->request->params['controller'], $this->Controller->request->params['action']);
|
||||
if (isset($this->Controller->RequestHandler) && ($api || $this->Controller->RequestHandler->isXml() || $this->isJson() || $this->isCsv())) {
|
||||
if ($this->isJson()) {
|
||||
if (!empty($this->Controller->request->input()) && empty($this->Controller->request->input('json_decode'))) {
|
||||
throw new MethodNotAllowedException('Invalid JSON input. Make sure that the JSON input is a correctly formatted JSON string. This request has been blocked to avoid an unfiltered request.');
|
||||
}
|
||||
}
|
||||
if (isset($this->Controller->RequestHandler) && ($api || $this->isJson() || $this->Controller->RequestHandler->isXml() || $this->isCsv())) {
|
||||
$this->isRest = true;
|
||||
return true;
|
||||
} else {
|
||||
|
@ -99,11 +95,8 @@ class IndexFilterComponent extends Component
|
|||
}
|
||||
}
|
||||
|
||||
public function isJson($data=false)
|
||||
public function isJson()
|
||||
{
|
||||
if ($data) {
|
||||
return (json_decode($data) != null) ? true : false;
|
||||
}
|
||||
return $this->Controller->request->header('Accept') === 'application/json' || $this->Controller->RequestHandler->prefers() === 'json';
|
||||
}
|
||||
|
||||
|
@ -117,12 +110,13 @@ class IndexFilterComponent extends Component
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $controller
|
||||
* @param string $action
|
||||
* @return bool
|
||||
*/
|
||||
public function isApiFunction($controller, $action)
|
||||
{
|
||||
if (isset($this->Controller->automationArray[$controller]) && in_array($action, $this->Controller->automationArray[$controller])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return isset($this->Controller->automationArray[$controller]) && in_array($action, $this->Controller->automationArray[$controller], true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @property ACLComponent $ACL
|
||||
*/
|
||||
class RestResponseComponent extends Component
|
||||
{
|
||||
public $components = array('ACL');
|
||||
|
@ -238,9 +241,9 @@ class RestResponseComponent extends Component
|
|||
'optional' => array('type', 'source', 'timestamp', 'date', 'time')
|
||||
),
|
||||
'restSearch' => array(
|
||||
'description' => "Search MISP sightings using a list of filter parameters and return the data in the JSON format. The search is available on an event, attribute or instance level, just select the scope via the URL (/sighting/restSearch/event vs /sighting/restSearch/attribute vs /sighting/restSearch/). id MUST be provided if context is set.",
|
||||
'description' => "Search MISP sightings using a list of filter parameters and return the data in the JSON format. The search is available on an event, attribute or instance level, just select the scope via the URL (/sighting/restSearch/event vs /sighting/restSearch/attribute vs /sighting/restSearch/). id or uuid MUST be provided if context is set.",
|
||||
'mandatory' => array('returnFormat'),
|
||||
'optional' => array('id', 'type', 'from', 'to', 'last', 'org_id', 'source', 'includeAttribute', 'includeEvent'),
|
||||
'optional' => array('id', 'uuid', 'type', 'from', 'to', 'last', 'org_id', 'source', 'includeAttribute', 'includeEvent'),
|
||||
'params' => array('context')
|
||||
),
|
||||
),
|
||||
|
@ -343,7 +346,8 @@ class RestResponseComponent extends Component
|
|||
$this->__setup();
|
||||
$result = array();
|
||||
foreach ($this->__scopedFieldsConstraint as $controller => $actions) {
|
||||
$controller = Inflector::tableize($controller);
|
||||
// EventGraph controller has different rule
|
||||
$controller = $controller === 'EventGraph' ? 'event_graph' : Inflector::tableize($controller);
|
||||
foreach ($actions as $action => $data) {
|
||||
if ($this->ACL->canUserAccess($user, $controller, $action)) {
|
||||
$admin_routing = '';
|
||||
|
@ -364,7 +368,8 @@ class RestResponseComponent extends Component
|
|||
$this->__setup();
|
||||
$result = array();
|
||||
foreach ($this->__descriptions as $controller => $actions) {
|
||||
$controller = Inflector::tableize($controller);
|
||||
// EventGraph controller has different rule
|
||||
$controller = $controller === 'EventGraph' ? 'event_graph' : Inflector::tableize($controller);
|
||||
foreach ($actions as $action => $data) {
|
||||
if ($this->ACL->canUserAccess($user, $controller, $action)) {
|
||||
$admin_routing = '';
|
||||
|
@ -514,11 +519,12 @@ class RestResponseComponent extends Component
|
|||
} else {
|
||||
$type = $format;
|
||||
}
|
||||
$dumpSql = !empty($this->Controller->sql_dump) && Configure::read('debug') > 1;
|
||||
if (!$raw) {
|
||||
if (is_string($response)) {
|
||||
$response = array('message' => $response);
|
||||
}
|
||||
if (Configure::read('debug') > 1 && !empty($this->Controller->sql_dump)) {
|
||||
if ($dumpSql) {
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
if ($this->Controller->sql_dump === 2) {
|
||||
$response = array('sql_dump' => $this->Log->getDataSource()->getLog(false, false));
|
||||
|
@ -526,9 +532,11 @@ class RestResponseComponent extends Component
|
|||
$response['sql_dump'] = $this->Log->getDataSource()->getLog(false, false);
|
||||
}
|
||||
}
|
||||
$response = json_encode($response, JSON_PRETTY_PRINT);
|
||||
// Do not pretty print response for automatic tools
|
||||
$flags = $this->isAutomaticTool() ? JSON_UNESCAPED_UNICODE : JSON_PRETTY_PRINT;
|
||||
$response = json_encode($response, $flags);
|
||||
} else {
|
||||
if (Configure::read('debug') > 1 && !empty($this->Controller->sql_dump)) {
|
||||
if ($dumpSql) {
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
if ($this->Controller->sql_dump === 2) {
|
||||
$response = json_encode(array('sql_dump' => $this->Log->getDataSource()->getLog(false, false)));
|
||||
|
@ -542,7 +550,15 @@ class RestResponseComponent extends Component
|
|||
}
|
||||
}
|
||||
}
|
||||
$cakeResponse = new CakeResponse(array('body' => $response, 'status' => $code, 'type' => $type));
|
||||
|
||||
App::uses('TmpFileTool', 'Tools');
|
||||
if ($response instanceof TmpFileTool) {
|
||||
App::uses('CakeResponseTmp', 'Tools');
|
||||
$cakeResponse = new CakeResponseTmp(['status' => $code, 'type' => $type]);
|
||||
$cakeResponse->file($response);
|
||||
} else {
|
||||
$cakeResponse = new CakeResponse(array('body' => $response, 'status' => $code, 'type' => $type));
|
||||
}
|
||||
|
||||
if (Configure::read('Security.allow_cors')) {
|
||||
$headers["Access-Control-Allow-Headers"] = "Origin, Content-Type, Authorization, Accept";
|
||||
|
@ -568,6 +584,16 @@ class RestResponseComponent extends Component
|
|||
return $cakeResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if request comes from automatic tool, like other MISP instance or PyMISP
|
||||
* @return bool
|
||||
*/
|
||||
public function isAutomaticTool()
|
||||
{
|
||||
$userAgent = CakeRequest::header('User-Agent');
|
||||
return $userAgent && (substr($userAgent, 0, 6) === 'PyMISP' || substr($userAgent, 0, 4) === 'MISP');
|
||||
}
|
||||
|
||||
private function __generateURL($action, $controller, $id)
|
||||
{
|
||||
$controller = Inflector::underscore(Inflector::pluralize($controller));
|
||||
|
@ -1792,8 +1818,6 @@ class RestResponseComponent extends Component
|
|||
private function __overwriteReturnFormat($scope, $action, &$field) {
|
||||
switch($scope) {
|
||||
case "Attribute":
|
||||
$field['values'] = array_keys(ClassRegistry::init($scope)->validFormats);
|
||||
break;
|
||||
case "Event":
|
||||
$field['values'] = array_keys(ClassRegistry::init($scope)->validFormats);
|
||||
break;
|
||||
|
@ -1837,33 +1861,36 @@ class RestResponseComponent extends Component
|
|||
$field['values'][] = array('label' => h($model_name), 'value' => $i);
|
||||
}
|
||||
}
|
||||
private function __overwriteTags($scope, $action, &$field) {
|
||||
$this->{$scope} = ClassRegistry::init("Tag");
|
||||
$tags = $this->{$scope}->find('list', array(
|
||||
'recursive' => -1,
|
||||
'fields' => array('name')
|
||||
));
|
||||
foreach($tags as $i => $tag) {
|
||||
$tagname = htmlspecialchars($tag);
|
||||
$tags[$tagname] = $tagname;
|
||||
unset($tags[$i]);
|
||||
|
||||
private function __overwriteTags($scope, $action, &$field)
|
||||
{
|
||||
static $values;
|
||||
if ($values === null) {
|
||||
$tagModel = ClassRegistry::init("Tag");
|
||||
$tags = $tagModel->find('column', array(
|
||||
'fields' => array('Tag.name')
|
||||
));
|
||||
$values = [];
|
||||
foreach ($tags as $tag) {
|
||||
$tagname = htmlspecialchars($tag);
|
||||
$values[$tagname] = $tagname;
|
||||
}
|
||||
}
|
||||
$field['values'] = $tags;
|
||||
$field['values'] = $values;
|
||||
|
||||
if ($action == 'attachTagToObject') {
|
||||
$field['help'] = __('Also supports array of tags');
|
||||
}
|
||||
}
|
||||
private function __overwriteNationality($scope, $action, &$field) {
|
||||
$field['values'] = ClassRegistry::init("Organisation")->countries;
|
||||
$field['values'] = ClassRegistry::init("Organisation")->getCountries();
|
||||
}
|
||||
private function __overwriteAction($scope, $action, &$field) {
|
||||
$field['values'] = array_keys(ClassRegistry::init("Log")->actionDefinitions);
|
||||
}
|
||||
private function __overwriteRoleId($scope, $action, &$field) {
|
||||
$this->{$scope} = ClassRegistry::init("Role");
|
||||
$roles = $this->{$scope}->find('list', array(
|
||||
'recursive' => -1,
|
||||
$roles = $this->{$scope}->find('column', array(
|
||||
'fields' => array('name')
|
||||
));
|
||||
$field['values'] = $roles;
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property AuthKey $AuthKey
|
||||
*/
|
||||
class CorrelationExclusionsController extends AppController
|
||||
{
|
||||
public $components = array(
|
||||
'Security',
|
||||
'CRUD',
|
||||
'RequestHandler'
|
||||
);
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 60,
|
||||
'order' => array(
|
||||
'CorrelationExclusion.value' => 'ASC',
|
||||
)
|
||||
);
|
||||
|
||||
public function index($id = false)
|
||||
{
|
||||
$this->CRUD->index([
|
||||
'filters' => ['value'],
|
||||
'quickFilters' => ['value']
|
||||
]);
|
||||
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($user_id = false)
|
||||
{
|
||||
$params = [];
|
||||
$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 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 = 'ajax';
|
||||
$this->render('/genericTemplates/confirm');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property Dashboard $Dashboard
|
||||
*/
|
||||
class DashboardsController extends AppController
|
||||
{
|
||||
public $components = array('Session', 'RequestHandler');
|
||||
|
@ -147,47 +150,50 @@ class DashboardsController extends AppController
|
|||
|
||||
public function renderWidget($widget_id, $force = false)
|
||||
{
|
||||
if ($this->request->is('post')) {
|
||||
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'];
|
||||
$dashboardWidget = $this->Dashboard->loadWidget($this->Auth->user(), $value['widget']);
|
||||
$this->layout = false;
|
||||
$this->set('title', $dashboardWidget->title);
|
||||
$redis = $this->Dashboard->setupRedis();
|
||||
$org_scope = $this->_isSiteAdmin() ? 0 : $this->Auth->user('org_id');
|
||||
$lookup_hash = hash('sha256', $value['widget'] . $value['config']);
|
||||
$data = $redis->get('misp:dashboard:' . $org_scope . ':' . $lookup_hash);
|
||||
if (!isset($dashboardWidget->cacheLifetime)) {
|
||||
$dashboardWidget->cacheLifetime = false;
|
||||
}
|
||||
if (empty($dashboardWidget->cacheLifetime) || empty($data)) {
|
||||
$data = $dashboardWidget->handler($this->Auth->user(), json_decode($value['config'], true));
|
||||
if (!empty($dashboardWidget->cacheLifetime)) {
|
||||
$redis->set('misp:dashboard:' . $org_scope . ':' . $lookup_hash, json_encode(array('data' => $data)));
|
||||
$redis->expire('misp:dashboard:' . $org_scope . ':' . $lookup_hash, $dashboardWidget->cacheLifetime);
|
||||
}
|
||||
} else {
|
||||
$data = json_decode($data, true)['data'];
|
||||
}
|
||||
$valueConfig = json_decode($value['config'], true);
|
||||
$config = array(
|
||||
'render' => $dashboardWidget->render,
|
||||
'autoRefreshDelay' => empty($dashboardWidget->autoRefreshDelay) ? false : $dashboardWidget->autoRefreshDelay,
|
||||
'widget_config' => empty($valueConfig['widget_config']) ? array() : $valueConfig['widget_config']
|
||||
);
|
||||
$this->set('widget_id', $widget_id);
|
||||
$this->set('data', $data);
|
||||
$this->set('config', $config);
|
||||
$this->render('widget_loader');
|
||||
} else {
|
||||
if (!$this->request->is('post')) {
|
||||
throw new MethodNotAllowedException(__('This endpoint can only be reached via POST requests.'));
|
||||
}
|
||||
|
||||
@session_write_close(); // allow concurrent AJAX requests (session hold lock by default)
|
||||
|
||||
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 = json_decode($value['config'], true);
|
||||
$dashboardWidget = $this->Dashboard->loadWidget($this->Auth->user(), $value['widget']);
|
||||
|
||||
$redis = $this->Dashboard->setupRedis();
|
||||
$org_scope = $this->_isSiteAdmin() ? 0 : $this->Auth->user('org_id');
|
||||
$lookup_hash = hash('sha256', $value['widget'] . $value['config']);
|
||||
$cacheKey = 'misp:dashboard:' . $org_scope . ':' . $lookup_hash;
|
||||
$data = $redis->get($cacheKey);
|
||||
if (!isset($dashboardWidget->cacheLifetime)) {
|
||||
$dashboardWidget->cacheLifetime = false;
|
||||
}
|
||||
if (empty($dashboardWidget->cacheLifetime) || empty($data)) {
|
||||
$data = $dashboardWidget->handler($this->Auth->user(), $valueConfig);
|
||||
if (!empty($dashboardWidget->cacheLifetime)) {
|
||||
$redis->setex($cacheKey, $dashboardWidget->cacheLifetime, json_encode(array('data' => $data)));
|
||||
}
|
||||
} else {
|
||||
$data = json_decode($data, true)['data'];
|
||||
}
|
||||
$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()
|
||||
|
@ -230,6 +236,7 @@ class DashboardsController extends AppController
|
|||
|
||||
public function saveTemplate($update = false)
|
||||
{
|
||||
$this->loadModel('UserSetting');
|
||||
if (!empty($update)) {
|
||||
$conditions = array('Dashboard.id' => $update);
|
||||
if (Validation::uuid($update)) {
|
||||
|
@ -391,7 +398,7 @@ class DashboardsController extends AppController
|
|||
$element['User']['email'] = '';
|
||||
}
|
||||
}
|
||||
$this->set('passedArgs', $this->passedArgs);
|
||||
$this->set('passedArgs', json_encode($this->passedArgs));
|
||||
$this->set('data', $data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ class EventDelegationsController extends AppController
|
|||
'order' => array('lower(name) ASC')
|
||||
));
|
||||
$distribution = $this->EventDelegation->Event->distributionLevels;
|
||||
$sgs = $this->EventDelegation->Event->SharingGroup->fetchAllAuthorised($this->Auth->User, 'name', true);
|
||||
$sgs = $this->EventDelegation->Event->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'name', true);
|
||||
if (empty($sgs)) {
|
||||
unset($distribution[4]);
|
||||
}
|
||||
|
|
|
@ -124,12 +124,14 @@ class EventReportsController extends AppController
|
|||
{
|
||||
$report = $this->EventReport->fetchIfAuthorized($this->Auth->user(), $id, 'delete', $throwErrors=true, $full=false);
|
||||
if ($this->request->is('post')) {
|
||||
$errors = $this->EventReport->deleteReport($this->Auth->user(), $report, $hard=$hard);
|
||||
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'));
|
||||
$report = $hard ? null : $this->EventReport->simpleFetchById($this->Auth->user(), $id);
|
||||
return $this->__getSuccessResponseBasedOnContext($successMessage, $report, 'delete', $id, $redirectTarget);
|
||||
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);
|
||||
|
@ -153,8 +155,7 @@ class EventReportsController extends AppController
|
|||
$redirectTarget = $this->referer();
|
||||
if (empty($errors)) {
|
||||
$successMessage = __('Event Report %s restored', $id);
|
||||
$report = $this->EventReport->simpleFetchById($this->Auth->user(), $id);
|
||||
return $this->__getSuccessResponseBasedOnContext($successMessage, $report, 'restore', $id, $redirectTarget);
|
||||
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);
|
||||
|
@ -209,31 +210,30 @@ class EventReportsController extends AppController
|
|||
{
|
||||
if (!$this->request->is('ajax')) {
|
||||
throw new MethodNotAllowedException(__('This function can only be reached via AJAX.'));
|
||||
} else {
|
||||
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 = 'ajax';
|
||||
$this->set('reportId', $reportId);
|
||||
$this->render('ajax/extractAllFromReport');
|
||||
}
|
||||
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 = 'ajax';
|
||||
$this->set('reportId', $reportId);
|
||||
$this->render('ajax/extractAllFromReport');
|
||||
}
|
||||
|
||||
public function extractFromReport($reportId)
|
||||
|
|
|
@ -136,12 +136,12 @@ class EventsController extends AppController
|
|||
$includeConditions['OR'][] = array('lower(Attribute.value2) LIKE' => $i);
|
||||
}
|
||||
|
||||
$includeIDs = array_values($this->Event->Attribute->fetchAttributes($this->Auth->user(), array(
|
||||
$includeIDs = $this->Event->Attribute->fetchAttributes($this->Auth->user(), array(
|
||||
'conditions' => $includeConditions,
|
||||
'flatten' => true,
|
||||
'event_ids' => true,
|
||||
'list' => true,
|
||||
)));
|
||||
));
|
||||
}
|
||||
|
||||
if (!empty($exclude)) {
|
||||
|
@ -151,12 +151,12 @@ class EventsController extends AppController
|
|||
$excludeConditions['OR'][] = array('lower(Attribute.value2) LIKE' => $e);
|
||||
}
|
||||
|
||||
$excludeIDs = array_values($this->Event->Attribute->fetchAttributes($this->Auth->user(), array(
|
||||
$excludeIDs = $this->Event->Attribute->fetchAttributes($this->Auth->user(), array(
|
||||
'conditions' => $excludeConditions,
|
||||
'flatten' => true,
|
||||
'event_ids' => true,
|
||||
'list' => true,
|
||||
)));
|
||||
));
|
||||
}
|
||||
}
|
||||
// return -1 as the only value in includedIDs if both arrays are empty. This will mean that no events will be shown if there was no hit
|
||||
|
@ -191,15 +191,13 @@ class EventsController extends AppController
|
|||
$conditions = array(
|
||||
'OR' => $subconditions,
|
||||
);
|
||||
$attributeHits = $this->Event->Attribute->fetchAttributes($this->Auth->user(), array(
|
||||
$result = $this->Event->Attribute->fetchAttributes($this->Auth->user(), array(
|
||||
'conditions' => $conditions,
|
||||
'flatten' => 1,
|
||||
'event_ids' => true,
|
||||
'list' => true,
|
||||
));
|
||||
|
||||
$result = array_values($attributeHits);
|
||||
|
||||
// we now have a list of event IDs that match on an attribute level, and the user can see it. Let's also find all of the events that match on other criteria!
|
||||
// What is interesting here is that we no longer have to worry about the event's releasability. With attributes this was a different case,
|
||||
// because we might run into a situation where a user can see an event but not a specific attribute
|
||||
|
@ -234,10 +232,9 @@ class EventsController extends AppController
|
|||
foreach ($values as $v) {
|
||||
$subconditions[] = array('lower(name) LIKE' => $v);
|
||||
}
|
||||
$orgs = $this->Event->Org->find('list', array(
|
||||
$orgs = $this->Event->Org->find('column', array(
|
||||
'conditions' => $subconditions,
|
||||
'recursive' => -1,
|
||||
'fields' => array('id')
|
||||
'fields' => array('Org.id')
|
||||
));
|
||||
|
||||
$conditions = empty($result) ? [] : ['NOT' => ['id' => $result]]; // Do not include events that we already found
|
||||
|
@ -246,11 +243,10 @@ class EventsController extends AppController
|
|||
$conditions['OR'][] = array('lower(uuid) LIKE' => $v);
|
||||
}
|
||||
if (!empty($orgs)) {
|
||||
$conditions['OR']['orgc_id'] = array_values($orgs);
|
||||
$conditions['OR']['orgc_id'] = $orgs;
|
||||
}
|
||||
$otherEvents = $this->Event->find('list', array(
|
||||
'recursive' => -1,
|
||||
'fields' => array('id'),
|
||||
$otherEvents = $this->Event->find('column', array(
|
||||
'fields' => array('Event.id'),
|
||||
'conditions' => $conditions,
|
||||
));
|
||||
foreach ($otherEvents as $eventId) {
|
||||
|
@ -311,7 +307,6 @@ class EventsController extends AppController
|
|||
} else {
|
||||
$pieces = explode('|', $v);
|
||||
}
|
||||
$temp = array();
|
||||
$eventidConditions = array();
|
||||
foreach ($pieces as $piece) {
|
||||
$piece = trim($piece);
|
||||
|
@ -454,9 +449,9 @@ class EventsController extends AppController
|
|||
$test = array();
|
||||
foreach ($pieces as $piece) {
|
||||
if ($piece[0] == '!') {
|
||||
$this->paginate['conditions']['AND'][] = array('lower(Event.info)' . ' NOT LIKE' => '%' . strtolower(substr($piece, 1)) . '%');
|
||||
$this->paginate['conditions']['AND'][] = array('lower(Event.info) NOT LIKE' => '%' . strtolower(substr($piece, 1)) . '%');
|
||||
} else {
|
||||
$test['OR'][] = array('lower(Event.info)' . ' LIKE' => '%' . strtolower($piece) . '%');
|
||||
$test['OR'][] = array('lower(Event.info) LIKE' => '%' . strtolower($piece) . '%');
|
||||
}
|
||||
}
|
||||
$this->paginate['conditions']['AND'][] = $test;
|
||||
|
@ -495,17 +490,13 @@ class EventsController extends AppController
|
|||
$filterString .= '!' . $piece;
|
||||
continue;
|
||||
}
|
||||
$block = $this->Event->EventTag->find('all', array(
|
||||
'conditions' => array('EventTag.tag_id' => $tagName['Tag']['id']),
|
||||
'fields' => 'event_id',
|
||||
'recursive' => -1,
|
||||
$block = $this->Event->EventTag->find('column', array(
|
||||
'conditions' => array('EventTag.tag_id' => $tagName['Tag']['id']),
|
||||
'fields' => ['EventTag.event_id'],
|
||||
));
|
||||
if (!empty($block)) {
|
||||
$sqlSubQuery = 'Event.id NOT IN (';
|
||||
foreach ($block as $b) {
|
||||
$sqlSubQuery .= $b['EventTag']['event_id'] . ',';
|
||||
}
|
||||
$tagRules['AND'][] = substr($sqlSubQuery, 0, -1) . ')';
|
||||
$sqlSubQuery = 'Event.id NOT IN (' . implode(",", $block) . ')';
|
||||
$tagRules['AND'][] = $sqlSubQuery;
|
||||
}
|
||||
if ($filterString != "") {
|
||||
$filterString .= "|";
|
||||
|
@ -532,18 +523,14 @@ class EventsController extends AppController
|
|||
continue;
|
||||
}
|
||||
|
||||
$allow = $this->Event->EventTag->find('all', array(
|
||||
'conditions' => array('EventTag.tag_id' => $tagName['Tag']['id']),
|
||||
'fields' => 'event_id',
|
||||
'recursive' => -1,
|
||||
$allow = $this->Event->EventTag->find('column', array(
|
||||
'conditions' => array('EventTag.tag_id' => $tagName['Tag']['id']),
|
||||
'fields' => ['EventTag.event_id'],
|
||||
));
|
||||
if (!empty($allow)) {
|
||||
$sqlSubQuery = 'Event.id IN (';
|
||||
foreach ($allow as $a) {
|
||||
$setOR = true;
|
||||
$sqlSubQuery .= $a['EventTag']['event_id'] . ',';
|
||||
}
|
||||
$tagRules['OR'][] = substr($sqlSubQuery, 0, -1) . ')';
|
||||
$setOR = true;
|
||||
$sqlSubQuery = 'Event.id IN ('. implode(",", $allow) . ')';
|
||||
$tagRules['OR'][] = $sqlSubQuery;
|
||||
}
|
||||
if ($filterString != "") {
|
||||
$filterString .= "|";
|
||||
|
@ -687,22 +674,9 @@ class EventsController extends AppController
|
|||
}
|
||||
}
|
||||
}
|
||||
$this->set('passedArgs', json_encode($passedArgs));
|
||||
|
||||
// check each of the passed arguments whether they're a filter (could also be a sort for example) and if yes, add it to the pagination conditions
|
||||
$passedArgsArray = $this->__setIndexFilterConditions($passedArgs, $urlparams);
|
||||
if (!$this->_isRest()) {
|
||||
$this->paginate['contain'] = array_merge($this->paginate['contain'], array('User.email', 'EventTag'));
|
||||
} else {
|
||||
$this->paginate['contain'] = array_merge($this->paginate['contain'], array('User.email'));
|
||||
}
|
||||
$this->set('urlparams', $urlparams);
|
||||
$this->set('passedArgsArray', $passedArgsArray);
|
||||
$this->paginate = Set::merge($this->paginate, array('contain' => array(
|
||||
'ThreatLevel' => array(
|
||||
'fields' => array(
|
||||
'ThreatLevel.name'))
|
||||
),
|
||||
));
|
||||
$this->loadModel('GalaxyCluster');
|
||||
|
||||
// for REST, don't use the pagination. With this, we'll escape the limit of events shown on the index.
|
||||
|
@ -721,11 +695,13 @@ class EventsController extends AppController
|
|||
if (isset($this->paginate['conditions'])) {
|
||||
$rules['conditions'] = $this->paginate['conditions'];
|
||||
}
|
||||
if (!empty($passedArgs['searchminimal']) || !empty($passedArgs['minimal'])) {
|
||||
unset($rules['contain']);
|
||||
$minimal = !empty($passedArgs['searchminimal']) || !empty($passedArgs['minimal']);
|
||||
if ($minimal) {
|
||||
$rules['recursive'] = -1;
|
||||
$rules['fields'] = array('id', 'timestamp', 'sighting_timestamp', 'published', 'uuid');
|
||||
$rules['contain'] = array('Orgc.uuid');
|
||||
} else {
|
||||
$rules['contain'][] = 'EventTag';
|
||||
}
|
||||
$paginationRules = array('page', 'limit', 'sort', 'direction', 'order');
|
||||
foreach ($paginationRules as $paginationRule) {
|
||||
|
@ -733,86 +709,72 @@ class EventsController extends AppController
|
|||
$rules[$paginationRule] = $passedArgs[$paginationRule];
|
||||
}
|
||||
}
|
||||
$counting_rules = $rules;
|
||||
if (!empty($counting_rules['limit'])) {
|
||||
unset($counting_rules['limit']);
|
||||
}
|
||||
if (!empty($counting_rules['page'])) {
|
||||
unset($counting_rules['page']);
|
||||
}
|
||||
$absolute_total = $this->Event->find('count', $counting_rules);
|
||||
|
||||
if (empty($rules['limit'])) {
|
||||
$events = array();
|
||||
$i = 1;
|
||||
$continue = true;
|
||||
$rules['limit'] = 20000;
|
||||
while ($continue) {
|
||||
while (true) {
|
||||
$rules['page'] = $i;
|
||||
$temp = $this->Event->find('all', $rules);
|
||||
if (!empty($temp)) {
|
||||
$resultCount = count($temp);
|
||||
if ($resultCount !== 0) {
|
||||
$events = array_merge($events, $temp);
|
||||
} else {
|
||||
$continue = false;
|
||||
}
|
||||
if ($resultCount < $rules['limit']) {
|
||||
break;
|
||||
}
|
||||
$i += 1;
|
||||
}
|
||||
$absolute_total = count($events);
|
||||
} else {
|
||||
$events = $this->Event->find('all', $rules);
|
||||
$counting_rules = $rules;
|
||||
unset($counting_rules['limit']);
|
||||
unset($counting_rules['page']);
|
||||
$absolute_total = $this->Event->find('count', $counting_rules);
|
||||
|
||||
$events = $absolute_total === 0 ? [] : $this->Event->find('all', $rules);
|
||||
}
|
||||
$total_events = count($events);
|
||||
foreach ($events as $k => $event) {
|
||||
if (empty($event['SharingGroup']['name'])) {
|
||||
unset($events[$k]['SharingGroup']);
|
||||
|
||||
if (!$minimal) {
|
||||
$tagIds = [];
|
||||
foreach (array_column($events, 'EventTag') as $eventTags) {
|
||||
foreach (array_column($eventTags, 'tag_id') as $tagId) {
|
||||
$tagIds[$tagId] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (empty($passedArgs['searchminimal']) && empty($passedArgs['minimal'])) {
|
||||
$passes = ceil($total_events / 1000);
|
||||
for ($i = 0; $i < $passes; $i++) {
|
||||
$event_tag_objects = array();
|
||||
$event_tag_ids = array();
|
||||
$elements = 1000;
|
||||
if ($i == ($passes-1)) {
|
||||
$elements = ($total_events % 1000);
|
||||
}
|
||||
for ($j = 0; $j < $elements; $j++) {
|
||||
$event_tag_ids[$events[($i*1000) + $j]['Event']['id']] = true;
|
||||
}
|
||||
$eventTags = $this->Event->EventTag->find('all', array(
|
||||
if (!empty($tagIds)) {
|
||||
$tags = $this->Event->EventTag->Tag->find('all', [
|
||||
'conditions' => [
|
||||
'Tag.id' => array_keys($tagIds),
|
||||
'Tag.exportable' => 1,
|
||||
],
|
||||
'recursive' => -1,
|
||||
'conditions' => array(
|
||||
'EventTag.event_id' => array_keys($event_tag_ids)
|
||||
),
|
||||
'contain' => array(
|
||||
'Tag' => array(
|
||||
'conditions' => array('Tag.exportable' => 1),
|
||||
'fields' => array('Tag.id', 'Tag.name', 'Tag.colour', 'Tag.is_galaxy')
|
||||
)
|
||||
)
|
||||
));
|
||||
foreach ($eventTags as $ket => $et) {
|
||||
if (empty($et['Tag']['id'])) {
|
||||
unset($eventTags[$ket]);
|
||||
} else {
|
||||
$et['EventTag']['Tag'] = $et['Tag'];
|
||||
unset($et['Tag']);
|
||||
if (empty($event_tag_objects[$et['EventTag']['event_id']])) {
|
||||
$event_tag_objects[$et['EventTag']['event_id']] = array($et['EventTag']);
|
||||
'fields' => ['Tag.id', 'Tag.name', 'Tag.colour', 'Tag.is_galaxy'],
|
||||
]);
|
||||
unset($tagIds);
|
||||
$tags = array_column(array_column($tags, 'Tag'), null, 'id');
|
||||
|
||||
foreach ($events as $k => $event) {
|
||||
if (empty($event['EventTag'])) {
|
||||
continue;
|
||||
}
|
||||
foreach ($event['EventTag'] as $k2 => $et) {
|
||||
if (!isset($tags[$et['tag_id']])) {
|
||||
unset($events[$k]['EventTag'][$k2]); // tag not exists or is not exportable
|
||||
} else {
|
||||
$event_tag_objects[$et['EventTag']['event_id']][] = $et['EventTag'];
|
||||
$events[$k]['EventTag'][$k2]['Tag'] = $tags[$et['tag_id']];
|
||||
}
|
||||
}
|
||||
}
|
||||
for ($j = 0; $j < $elements; $j++) {
|
||||
if (!empty($event_tag_objects[$events[($i*1000) + $j]['Event']['id']])) {
|
||||
$events[($i*1000) + $j]['EventTag'] = $event_tag_objects[$events[($i*1000) + $j]['Event']['id']];
|
||||
} else {
|
||||
$events[($i*1000) + $j]['EventTag'] = array();
|
||||
}
|
||||
}
|
||||
$events = $this->GalaxyCluster->attachClustersToEventIndex($this->Auth->user(), $events, false, false);
|
||||
}
|
||||
$events = $this->GalaxyCluster->attachClustersToEventIndex($this->Auth->user(), $events);
|
||||
foreach ($events as $key => $event) {
|
||||
$temp = $events[$key]['Event'];
|
||||
if (empty($event['SharingGroup']['name'])) {
|
||||
unset($event['SharingGroup']);
|
||||
}
|
||||
|
||||
$temp = $event['Event'];
|
||||
$temp['Org'] = $event['Org'];
|
||||
$temp['Orgc'] = $event['Orgc'];
|
||||
unset($temp['user_id']);
|
||||
|
@ -827,39 +789,53 @@ class EventsController extends AppController
|
|||
if ($this->response->type() === 'application/xml') {
|
||||
$events = array('Event' => $events);
|
||||
}
|
||||
return $this->RestResponse->viewData($events, $this->response->type(), false, false, false, array('X-Result-Count' => $absolute_total));
|
||||
} else {
|
||||
foreach ($events as $key => $event) {
|
||||
$event['Event']['orgc_uuid'] = $event['Orgc']['uuid'];
|
||||
$events[$key] = $event['Event'];
|
||||
}
|
||||
return $this->RestResponse->viewData($events, $this->response->type(), false, false, false, array('X-Result-Count' => $absolute_total));
|
||||
}
|
||||
} else {
|
||||
$events = $this->paginate();
|
||||
foreach ($events as $k => $event) {
|
||||
if (empty($event['SharingGroup']['name'])) {
|
||||
unset($events[$k]['SharingGroup']);
|
||||
}
|
||||
return $this->RestResponse->viewData($events, $this->response->type(), false, false, false, ['X-Result-Count' => $absolute_total]);
|
||||
}
|
||||
|
||||
$this->paginate['contain']['ThreatLevel'] = [
|
||||
'fields' => array('ThreatLevel.name')
|
||||
];
|
||||
$this->paginate['contain'][] = 'EventTag';
|
||||
if ($this->_isSiteAdmin()) {
|
||||
$this->paginate['contain'][] = 'User.email';
|
||||
}
|
||||
|
||||
$events = $this->paginate();
|
||||
|
||||
if (count($events) === 1 && isset($this->passedArgs['searchall'])) {
|
||||
$this->redirect(array('controller' => 'events', 'action' => 'view', $events[0]['Event']['id']));
|
||||
}
|
||||
|
||||
foreach ($events as $k => $event) {
|
||||
if (empty($event['SharingGroup']['name'])) {
|
||||
unset($events[$k]['SharingGroup']);
|
||||
}
|
||||
if (count($events) == 1 && isset($this->passedArgs['searchall'])) {
|
||||
$this->redirect(array('controller' => 'events', 'action' => 'view', $events[0]['Event']['id']));
|
||||
}
|
||||
$events = $this->Event->attachTagsToEvents($events);
|
||||
if (Configure::read('MISP.showCorrelationsOnIndex')) {
|
||||
$events = $this->Event->attachCorrelationCountToEvents($this->Auth->user(), $events);
|
||||
}
|
||||
if (Configure::read('MISP.showSightingsCountOnIndex')) {
|
||||
$events = $this->Event->attachSightingsCountToEvents($this->Auth->user(), $events);
|
||||
}
|
||||
if (Configure::read('MISP.showProposalsCountOnIndex')) {
|
||||
$events = $this->Event->attachProposalsCountToEvents($this->Auth->user(), $events);
|
||||
}
|
||||
if (Configure::read('MISP.showDiscussionsCountOnIndex')) {
|
||||
$events = $this->Event->attachDiscussionsCountToEvents($this->Auth->user(), $events);
|
||||
}
|
||||
$events = $this->GalaxyCluster->attachClustersToEventIndex($this->Auth->user(), $events, true, false);
|
||||
$this->set('events', $events);
|
||||
}
|
||||
$events = $this->Event->attachTagsToEvents($events);
|
||||
if (Configure::read('MISP.showCorrelationsOnIndex')) {
|
||||
$events = $this->Event->attachCorrelationCountToEvents($this->Auth->user(), $events);
|
||||
}
|
||||
if (Configure::read('MISP.showSightingsCountOnIndex')) {
|
||||
$events = $this->Event->attachSightingsCountToEvents($this->Auth->user(), $events);
|
||||
}
|
||||
if (Configure::read('MISP.showProposalsCountOnIndex')) {
|
||||
$events = $this->Event->attachProposalsCountToEvents($this->Auth->user(), $events);
|
||||
}
|
||||
if (Configure::read('MISP.showDiscussionsCountOnIndex')) {
|
||||
$events = $this->Event->attachDiscussionsCountToEvents($this->Auth->user(), $events);
|
||||
}
|
||||
$events = $this->GalaxyCluster->attachClustersToEventIndex($this->Auth->user(), $events, true, false);
|
||||
|
||||
if ($this->params['ext'] === 'csv') {
|
||||
App::uses('CsvExport', 'Export');
|
||||
$export = new CsvExport();
|
||||
return $this->RestResponse->viewData($export->eventIndex($events), 'csv');
|
||||
}
|
||||
|
||||
$user = $this->Auth->user();
|
||||
|
@ -882,16 +858,17 @@ class EventsController extends AppController
|
|||
$this->Flash->info(__('No GnuPG key set in your profile. To receive attributes in emails, submit your public key in your profile.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->set('events', $events);
|
||||
$this->set('eventDescriptions', $this->Event->fieldDescriptions);
|
||||
$this->set('analysisLevels', $this->Event->analysisLevels);
|
||||
$this->set('distributionLevels', $this->Event->distributionLevels);
|
||||
$this->set('shortDist', $this->Event->shortDist);
|
||||
$this->set('distributionData', $this->genDistributionGraph(-1));
|
||||
if ($this->params['ext'] === 'csv') {
|
||||
App::uses('CsvExport', 'Export');
|
||||
$export = new CsvExport();
|
||||
return $this->RestResponse->viewData($export->eventIndex($events), 'csv');
|
||||
}
|
||||
$this->set('urlparams', $urlparams);
|
||||
$this->set('passedArgsArray', $passedArgsArray);
|
||||
$this->set('passedArgs', json_encode($passedArgs));
|
||||
|
||||
if ($this->request->is('ajax')) {
|
||||
$this->autoRender = false;
|
||||
$this->layout = false;
|
||||
|
@ -962,17 +939,10 @@ class EventsController extends AppController
|
|||
}
|
||||
}
|
||||
$this->set('filtering', json_encode($filtering));
|
||||
$tags = $this->Event->EventTag->Tag->find('all', array('recursive' => -1));
|
||||
$tagNames = array();
|
||||
$tagNames = $this->Event->EventTag->Tag->find('list', array('recursive' => -1, 'fields' => ['Tag.id', 'Tag.name']));
|
||||
$tagJSON = array();
|
||||
foreach ($tags as $k => $v) {
|
||||
$tagNames[$v['Tag']['id']] = $v['Tag']['name'];
|
||||
$tagJSON[] = array('id' => $v['Tag']['id'], 'value' => h($v['Tag']['name']));
|
||||
}
|
||||
$conditions = array();
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
$eIds = $this->Event->fetchEventIds($this->Auth->user(), false, false, false, true);
|
||||
$conditions['AND'][] = array('Event.id' => $eIds);
|
||||
foreach ($tagNames as $tagId => $tagName) {
|
||||
$tagJSON[] = array('id' => $tagId, 'value' => h($tagName));
|
||||
}
|
||||
$rules = array('published', 'eventid', 'tag', 'date', 'eventinfo', 'threatlevel', 'distribution', 'sharinggroup', 'analysis', 'attribute', 'hasproposal');
|
||||
if ($this->_isSiteAdmin()) {
|
||||
|
@ -997,7 +967,6 @@ class EventsController extends AppController
|
|||
$this->set('tags', $tagNames);
|
||||
$this->set('tagJSON', json_encode($tagJSON));
|
||||
$this->set('rules', $rules);
|
||||
$this->set('baseurl', Configure::read('MISP.baseurl'));
|
||||
$this->layout = 'ajax';
|
||||
}
|
||||
|
||||
|
@ -1643,17 +1612,17 @@ class EventsController extends AppController
|
|||
}
|
||||
|
||||
if ($this->_isRest()) {
|
||||
$this->set('event', $event);
|
||||
} else {
|
||||
$this->set('deleted', isset($deleted) ? ($deleted == 2 ? 0 : 1) : 0);
|
||||
$this->set('includeRelatedTags', (!empty($this->params['named']['includeRelatedTags'])) ? 1 : 0);
|
||||
$this->set('includeDecayScore', (!empty($this->params['named']['includeDecayScore'])) ? 1 : 0);
|
||||
|
||||
if ($this->_isSiteAdmin() && $event['Event']['orgc_id'] !== $this->Auth->user('org_id')) {
|
||||
$this->Flash->info(__('You are currently logged in as a site administrator and about to edit an event not belonging to your organisation. This goes against the sharing model of MISP. Use a normal user account for day to day work.'));
|
||||
}
|
||||
$this->__viewUI($event, $continue, $fromEvent);
|
||||
return $this->__restResponse($event);
|
||||
}
|
||||
|
||||
$this->set('deleted', isset($deleted) ? ($deleted == 2 ? 0 : 1) : 0);
|
||||
$this->set('includeRelatedTags', (!empty($this->params['named']['includeRelatedTags'])) ? 1 : 0);
|
||||
$this->set('includeDecayScore', (!empty($this->params['named']['includeDecayScore'])) ? 1 : 0);
|
||||
|
||||
if ($this->_isSiteAdmin() && $event['Event']['orgc_id'] !== $this->Auth->user('org_id')) {
|
||||
$this->Flash->info(__('You are currently logged in as a site administrator and about to edit an event not belonging to your organisation. This goes against the sharing model of MISP. Use a normal user account for day to day work.'));
|
||||
}
|
||||
$this->__viewUI($event, $continue, $fromEvent);
|
||||
}
|
||||
|
||||
private function __startPivoting($id, $info, $date)
|
||||
|
@ -1945,9 +1914,7 @@ class EventsController extends AppController
|
|||
if (!empty($validationErrors)) {
|
||||
$event['errors'] = $validationErrors;
|
||||
}
|
||||
$this->set('event', $event);
|
||||
$this->render('view');
|
||||
return true;
|
||||
return $this->__restResponse($event);
|
||||
} else {
|
||||
// redirect to the view of the newly created event
|
||||
$this->Flash->success(__('The event has been saved'));
|
||||
|
@ -2143,11 +2110,11 @@ class EventsController extends AppController
|
|||
$this->Flash->error(__('Could not import STIX document: ' . $result));
|
||||
}
|
||||
} else {
|
||||
$max_size = intval(ini_get('post_max_size'));
|
||||
if (intval(ini_get('upload_max_filesize')) < $max_size) {
|
||||
$max_size = intval(ini_get('upload_max_filesize'));
|
||||
$maxUploadSize = intval(ini_get('post_max_size'));
|
||||
if (intval(ini_get('upload_max_filesize')) < $maxUploadSize) {
|
||||
$maxUploadSize = intval(ini_get('upload_max_filesize'));
|
||||
}
|
||||
$this->Flash->error(__('File upload failed. Make sure that you select a stix file to be uploaded and that the file doesn\'t exceed the maximum file size of ' . $max_size . '.'));
|
||||
$this->Flash->error(__('File upload failed. Make sure that you select a STIX file to be uploaded and that the file doesn\'t exceed the maximum file size of %s MB.', $maxUploadSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2179,7 +2146,7 @@ class EventsController extends AppController
|
|||
}
|
||||
}
|
||||
}
|
||||
$target_event = $this->Event->fetchSimpleEvent($this->Auth->user(), $target_id);
|
||||
$target_event = $this->Event->fetchSimpleEvent($this->Auth->user(), $target_id, ['contain' => ['Orgc']]);
|
||||
if (empty($target_event)) {
|
||||
throw new NotFoundException(__('Invalid target event.'));
|
||||
}
|
||||
|
@ -2271,7 +2238,7 @@ class EventsController extends AppController
|
|||
if ($this->request->is('get') && $this->_isRest()) {
|
||||
return $this->RestResponse->describe('Events', 'edit', false, $this->response->type());
|
||||
}
|
||||
$event = $this->Event->fetchSimpleEvent($this->Auth->user(), $id);
|
||||
$event = $this->Event->fetchSimpleEvent($this->Auth->user(), $id, ['contain' => ['Orgc']]);
|
||||
if (!$event) {
|
||||
throw new NotFoundException(__('Invalid event'));
|
||||
}
|
||||
|
@ -2310,9 +2277,7 @@ class EventsController extends AppController
|
|||
$metadata = $this->request->param('named.metadata');
|
||||
$results = $this->Event->fetchEvent($this->Auth->user(), ['eventid' => $id, 'metadata' => $metadata]);
|
||||
$event = $results[0];
|
||||
$this->set('event', $event);
|
||||
$this->render('view');
|
||||
return true;
|
||||
return $this->__restResponse($event);
|
||||
} else {
|
||||
$message = 'Error';
|
||||
if ($this->_isRest()) {
|
||||
|
@ -2531,19 +2496,12 @@ class EventsController extends AppController
|
|||
|
||||
public function publishSightings($id = null)
|
||||
{
|
||||
$id = $this->Toolbox->findIdByUuid($this->Event, $id);
|
||||
$event = $this->Event->fetchEvent(
|
||||
$this->Auth->user(),
|
||||
array(
|
||||
'eventid' => $id,
|
||||
'metadata' => 1
|
||||
)
|
||||
);
|
||||
$event = $this->Event->fetchSimpleEvent($this->Auth->user(), $id);
|
||||
if (empty($event)) {
|
||||
throw new NotFoundException(__('Invalid event'));
|
||||
}
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$result = $this->Event->publishRouter($id, null, $this->Auth->user(), 'sightings');
|
||||
$result = $this->Event->publishRouter($event['Event']['id'], null, $this->Auth->user(), 'sightings');
|
||||
if (!Configure::read('MISP.background_jobs')) {
|
||||
if (!is_array($result)) {
|
||||
// redirect to the view event page
|
||||
|
@ -2568,12 +2526,12 @@ class EventsController extends AppController
|
|||
if (!empty($errors)) {
|
||||
$this->set('errors', $errors);
|
||||
}
|
||||
$this->set('url', $this->baseurl . '/events/publishSightings/' . $id);
|
||||
$this->set('id', $id);
|
||||
$this->set('url', $this->baseurl . '/events/publishSightings/' . $event['Event']['id']);
|
||||
$this->set('id', $event['Event']['id']);
|
||||
$this->set('_serialize', array('name', 'message', 'url', 'id', 'errors'));
|
||||
} else {
|
||||
$this->Flash->success($message);
|
||||
$this->redirect(array('action' => 'view', $id));
|
||||
$this->redirect(array('action' => 'view', $event['Event']['id']));
|
||||
}
|
||||
} else {
|
||||
$this->set('id', $id);
|
||||
|
@ -2742,8 +2700,8 @@ class EventsController extends AppController
|
|||
// Users with a GnuPG key will get the mail encrypted, other users will get the mail unencrypted
|
||||
public function contact($id = null)
|
||||
{
|
||||
$events = $this->Event->fetchEvent($this->Auth->user(), array('eventid' => $id));
|
||||
if (empty($events)) {
|
||||
$event = $this->Event->fetchSimpleEvent($this->Auth->user(), $id, ['contain' => ['Orgc']]);
|
||||
if (empty($event)) {
|
||||
throw new NotFoundException(__('Invalid event'));
|
||||
}
|
||||
// User has filled in his contact form, send out the email.
|
||||
|
@ -2758,7 +2716,7 @@ class EventsController extends AppController
|
|||
throw new MethodNotAllowedException($error);
|
||||
} else {
|
||||
$this->Flash->error($error);
|
||||
$this->redirect(array('action' => 'contact', $id));
|
||||
$this->redirect(array('action' => 'contact', $event['Event']['id']));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2769,31 +2727,29 @@ class EventsController extends AppController
|
|||
$user = $this->Auth->user();
|
||||
$user = $this->Event->User->fillKeysToUser($user);
|
||||
|
||||
$success = $this->Event->sendContactEmailRouter($id, $message, $creator_only, $user);
|
||||
$success = $this->Event->sendContactEmailRouter($event['Event']['id'], $message, $creator_only, $user);
|
||||
if ($success) {
|
||||
$return_message = __('Email sent to the reporter.');
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('Events', 'contact', $id, $this->response->type(), $return_message);
|
||||
return $this->RestResponse->saveSuccessResponse('Events', 'contact', $event['Event']['id'], $this->response->type(), $return_message);
|
||||
} else {
|
||||
$this->Flash->success($return_message);
|
||||
// redirect to the view event page
|
||||
$this->redirect(array('action' => 'view', $id));
|
||||
$this->redirect(array('action' => 'view', $event['Event']['id']));
|
||||
}
|
||||
} else {
|
||||
$return_message = __('Sending of email failed.');
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('Events', 'contact', $id, $return_message, $this->response->type());
|
||||
return $this->RestResponse->saveFailResponse('Events', 'contact', $event['Event']['id'], $return_message, $this->response->type());
|
||||
} else {
|
||||
$this->Flash->error($return_message, 'default', array(), 'error');
|
||||
// redirect to the view event page
|
||||
$this->redirect(array('action' => 'view', $id));
|
||||
$this->redirect(array('action' => 'view', $event['Event']['id']));
|
||||
}
|
||||
}
|
||||
}
|
||||
// User didn't see the contact form yet. Present it to him.
|
||||
if (empty($this->data)) {
|
||||
$this->data = $events[0];
|
||||
}
|
||||
$this->set('event', $event);
|
||||
$this->set('mayModify', $this->__canModifyEvent($event));
|
||||
}
|
||||
|
||||
public function automation($legacy = false)
|
||||
|
@ -3618,8 +3574,7 @@ class EventsController extends AppController
|
|||
$this->set('sgs', $sgs);
|
||||
$this->set('event', $event);
|
||||
$this->set('mayModify', $this->__canModifyEvent($event));
|
||||
$this->set('typeList', array_keys($this->Event->Attribute->typeDefinitions));
|
||||
$this->set('defaultCategories', $this->Event->Attribute->defaultCategories);
|
||||
$this->set('typeDefinitions', $this->Event->Attribute->typeDefinitions);
|
||||
$this->set('typeCategoryMapping', $typeCategoryMapping);
|
||||
foreach ($typeCategoryMapping as $k => $v) {
|
||||
$typeCategoryMapping[$k] = array_values($v);
|
||||
|
@ -3656,7 +3611,7 @@ class EventsController extends AppController
|
|||
$attribute['category'] = $attribute['default_category'];
|
||||
unset($attribute['default_category']);
|
||||
} else {
|
||||
$attribute['category'] = $this->Event->Attribute->defaultCategories[$attribute['type']];
|
||||
$attribute['category'] = $this->Event->Attribute->typeDefinitions[$attribute['type']]['default_category'];
|
||||
}
|
||||
$attribute['distribution'] = $distribution;
|
||||
$attribute['event_id'] = $event['Event']['id'];
|
||||
|
@ -3857,104 +3812,105 @@ class EventsController extends AppController
|
|||
if (empty($event)) {
|
||||
throw new NotFoundException(__('Event not found or you are not authorised to view it.'));
|
||||
}
|
||||
$id = $event['Event']['id'];
|
||||
// #TODO i18n
|
||||
$exports = array(
|
||||
'xml' => array(
|
||||
'url' => $this->baseurl . '/events/restSearch/xml/eventid:' . $id . '.xml',
|
||||
'text' => 'MISP XML (metadata + all attributes)',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => true,
|
||||
'checkbox_text' => 'Encode Attachments',
|
||||
'checkbox_set' => $this->baseurl . '/events/restSearch/xml/eventid:' . $id . '/withAttachments:1.xml',
|
||||
'checkbox_default' => true
|
||||
'url' => $this->baseurl . '/events/restSearch/xml/eventid:' . $id . '.xml',
|
||||
'text' => 'MISP XML (metadata + all attributes)',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => true,
|
||||
'checkbox_text' => 'Encode Attachments',
|
||||
'checkbox_set' => $this->baseurl . '/events/restSearch/xml/eventid:' . $id . '/withAttachments:1.xml',
|
||||
'checkbox_default' => true
|
||||
),
|
||||
'json' => array(
|
||||
'url' => $this->baseurl . '/events/restSearch/json/eventid:' . $id . '.json',
|
||||
'text' => 'MISP JSON (metadata + all attributes)',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => true,
|
||||
'checkbox_text' => 'Encode Attachments',
|
||||
'checkbox_set' => $this->baseurl . '/events/restSearch/json/withAttachments:1/eventid:' . $id . '.json',
|
||||
'checkbox_default' => true
|
||||
'url' => $this->baseurl . '/events/restSearch/json/eventid:' . $id . '.json',
|
||||
'text' => 'MISP JSON (metadata + all attributes)',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => true,
|
||||
'checkbox_text' => 'Encode Attachments',
|
||||
'checkbox_set' => $this->baseurl . '/events/restSearch/json/withAttachments:1/eventid:' . $id . '.json',
|
||||
'checkbox_default' => true
|
||||
),
|
||||
'openIOC' => array(
|
||||
'url' => $this->baseurl . '/events/restSearch/openioc/to_ids:1/published:1/eventid:' . $id . '.json',
|
||||
'text' => 'OpenIOC (all indicators marked to IDS)',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => false,
|
||||
'url' => $this->baseurl . '/events/restSearch/openioc/to_ids:1/published:1/eventid:' . $id . '.json',
|
||||
'text' => 'OpenIOC (all indicators marked to IDS)',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => false,
|
||||
),
|
||||
'csv' => array(
|
||||
'url' => $this->baseurl . '/events/restSearch/returnFormat:csv/to_ids:1/published:1/includeContext:0/eventid:' . $id,
|
||||
'text' => 'CSV',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => true,
|
||||
'checkbox_text' => 'Include non-IDS marked attributes',
|
||||
'checkbox_set' => $this->baseurl . '/events/restSearch/returnFormat:csv/to_ids:1||0/published:1||0/includeContext:0/eventid:' . $id
|
||||
'url' => $this->baseurl . '/events/restSearch/returnFormat:csv/to_ids:1/published:1/includeContext:0/eventid:' . $id,
|
||||
'text' => 'CSV',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => true,
|
||||
'checkbox_text' => 'Include non-IDS marked attributes',
|
||||
'checkbox_set' => $this->baseurl . '/events/restSearch/returnFormat:csv/to_ids:1||0/published:1||0/includeContext:0/eventid:' . $id
|
||||
),
|
||||
'csv_with_context' => array(
|
||||
'url' => $this->baseurl . '/events/restSearch/returnFormat:csv/to_ids:1/published:1/includeContext:1/eventid:' . $id,
|
||||
'text' => 'CSV with additional context',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => true,
|
||||
'checkbox_text' => 'Include non-IDS marked attributes',
|
||||
'checkbox_set' => $this->baseurl . '/events/restSearch/returnFormat:csv/to_ids:1||0/published:1||0/includeContext:1/eventid:' . $id
|
||||
'url' => $this->baseurl . '/events/restSearch/returnFormat:csv/to_ids:1/published:1/includeContext:1/eventid:' . $id,
|
||||
'text' => 'CSV with additional context',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => true,
|
||||
'checkbox_text' => 'Include non-IDS marked attributes',
|
||||
'checkbox_set' => $this->baseurl . '/events/restSearch/returnFormat:csv/to_ids:1||0/published:1||0/includeContext:1/eventid:' . $id
|
||||
),
|
||||
'stix_xml' => array(
|
||||
'url' => $this->baseurl . '/events/restSearch/stix/eventid:' . $id,
|
||||
'text' => 'STIX XML (metadata + all attributes)',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => true,
|
||||
'checkbox_text' => 'Encode Attachments',
|
||||
'checkbox_set' => $this->baseurl . '/events/restSearch/stix/eventid:' . $id . '/withAttachments:1'
|
||||
'url' => $this->baseurl . '/events/restSearch/stix/eventid:' . $id,
|
||||
'text' => 'STIX 1 XML (metadata + all attributes)',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => true,
|
||||
'checkbox_text' => 'Encode Attachments',
|
||||
'checkbox_set' => $this->baseurl . '/events/restSearch/stix/eventid:' . $id . '/withAttachments:1'
|
||||
),
|
||||
'stix_json' => array(
|
||||
'url' => $this->baseurl . '/events/restSearch/stix-json/eventid:' . $id,
|
||||
'text' => 'STIX JSON (metadata + all attributes)',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => true,
|
||||
'checkbox_text' => 'Encode Attachments',
|
||||
'checkbox_set' => $this->baseurl . '/events/restSearch/stix-json/withAttachments:1/eventid:' . $id
|
||||
'url' => $this->baseurl . '/events/restSearch/stix-json/eventid:' . $id,
|
||||
'text' => 'STIX 1 JSON (metadata + all attributes)',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => true,
|
||||
'checkbox_text' => 'Encode Attachments',
|
||||
'checkbox_set' => $this->baseurl . '/events/restSearch/stix-json/withAttachments:1/eventid:' . $id
|
||||
),
|
||||
'stix2_json' => array(
|
||||
'url' => $this->baseurl . '/events/restSearch/stix2/eventid:' . $id,
|
||||
'text' => 'STIX2 (requires the STIX 2 library)',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => true,
|
||||
'checkbox_text' => 'Encode Attachments',
|
||||
'checkbox_set' => $this->baseurl . '/events/restSearch/stix2/eventid:' . $id . '/withAttachments:1'
|
||||
'url' => $this->baseurl . '/events/restSearch/stix2/eventid:' . $id,
|
||||
'text' => 'STIX 2',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => true,
|
||||
'checkbox_text' => 'Encode Attachments',
|
||||
'checkbox_set' => $this->baseurl . '/events/restSearch/stix2/eventid:' . $id . '/withAttachments:1'
|
||||
),
|
||||
'rpz' => array(
|
||||
'url' => $this->baseurl . '/attributes/restSearch/returnFormat:rpz/published:1||0/eventid:' . $id,
|
||||
'text' => 'RPZ Zone file',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => false,
|
||||
'url' => $this->baseurl . '/attributes/restSearch/returnFormat:rpz/published:1||0/eventid:' . $id,
|
||||
'text' => 'RPZ Zone file',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => false,
|
||||
),
|
||||
'suricata' => array(
|
||||
'url' => $this->baseurl . '/events/restSearch/returnFormat:suricata/published:1||0/eventid:' . $id,
|
||||
'text' => 'Download Suricata rules',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => false,
|
||||
'url' => $this->baseurl . '/events/restSearch/returnFormat:suricata/published:1||0/eventid:' . $id,
|
||||
'text' => 'Download Suricata rules',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => false,
|
||||
),
|
||||
'snort' => array(
|
||||
'url' => $this->baseurl . '/events/restSearch/returnFormat:snort/published:1||0/eventid:' . $id,
|
||||
'text' => 'Download Snort rules',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => false,
|
||||
'url' => $this->baseurl . '/events/restSearch/returnFormat:snort/published:1||0/eventid:' . $id,
|
||||
'text' => 'Download Snort rules',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => false,
|
||||
),
|
||||
'bro' => array(
|
||||
'url' => $this->baseurl . '/attributes/bro/download/all/false/' . $id,
|
||||
// 'url' => '/attributes/restSearch/returnFormat:bro/published:1||0/eventid:' . $id,
|
||||
'text' => 'Download Bro rules',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => false
|
||||
'url' => $this->baseurl . '/attributes/bro/download/all/false/' . $id,
|
||||
// 'url' => '/attributes/restSearch/returnFormat:bro/published:1||0/eventid:' . $id,
|
||||
'text' => 'Download Bro rules',
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => false
|
||||
),
|
||||
'text' => array(
|
||||
'text' => 'Export all attribute values as a text file',
|
||||
'url' => $this->baseurl . '/attributes/restSearch/returnFormat:text/published:1||0/eventid:' . $id,
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => true,
|
||||
'checkbox_text' => 'Include non-IDS marked attributes',
|
||||
'checkbox_set' => $this->baseurl . '/attributes/restSearch/returnFormat:text/published:1||0/to_ids:1||0/eventid:' . $id
|
||||
'text' => 'Export all attribute values as a text file',
|
||||
'url' => $this->baseurl . '/attributes/restSearch/returnFormat:text/published:1||0/eventid:' . $id,
|
||||
'requiresPublished' => false,
|
||||
'checkbox' => true,
|
||||
'checkbox_text' => 'Include non-IDS marked attributes',
|
||||
'checkbox_set' => $this->baseurl . '/attributes/restSearch/returnFormat:text/published:1||0/to_ids:1||0/eventid:' . $id
|
||||
),
|
||||
);
|
||||
if ($event['Event']['published'] == 0) {
|
||||
|
@ -3975,10 +3931,10 @@ class EventsController extends AppController
|
|||
if (is_array($modules) && !empty($modules)) {
|
||||
foreach ($modules['modules'] as $module) {
|
||||
$exports[$module['name']] = array(
|
||||
'url' => $this->baseurl . '/events/exportModule/' . $module['name'] . '/' . $id,
|
||||
'text' => Inflector::humanize($module['name']),
|
||||
'requiresPublished' => true,
|
||||
'checkbox' => false,
|
||||
'url' => $this->baseurl . '/events/exportModule/' . $module['name'] . '/' . $id,
|
||||
'text' => Inflector::humanize($module['name']),
|
||||
'requiresPublished' => true,
|
||||
'checkbox' => false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -4293,9 +4249,10 @@ class EventsController extends AppController
|
|||
|
||||
public function viewGraph($id)
|
||||
{
|
||||
// Event data are fetched by 'updateGraph', here we need just metadata.
|
||||
$event = $this->Event->fetchEvent($this->Auth->user(), array(
|
||||
'eventid' => $id,
|
||||
'includeGranularCorrelations' => 1
|
||||
'metadata' => true,
|
||||
));
|
||||
if (empty($event)) {
|
||||
throw new MethodNotAllowedException(__('Invalid Event.'));
|
||||
|
@ -4349,8 +4306,8 @@ class EventsController extends AppController
|
|||
$grapher = new DistributionGraphTool();
|
||||
|
||||
$this->loadModel('Server');
|
||||
$servers = $this->Server->find('list', array(
|
||||
'fields' => array('name'),
|
||||
$servers = $this->Server->find('column', array(
|
||||
'fields' => array('Server.name'),
|
||||
));
|
||||
$grapher->construct($this->Event, $servers, $this->Auth->user(), $extended);
|
||||
$json = $grapher->get_distributions_graph($id);
|
||||
|
@ -4906,8 +4863,7 @@ class EventsController extends AppController
|
|||
$this->set('event', array('Event' => $attribute[0]['Event']));
|
||||
}
|
||||
$this->set('resultArray', $resultArray);
|
||||
$this->set('typeList', array_keys($this->Event->Attribute->typeDefinitions));
|
||||
$this->set('defaultCategories', $this->Event->Attribute->defaultCategories);
|
||||
$this->set('typeDefinitions', $this->Event->Attribute->typeDefinitions);
|
||||
$this->set('typeCategoryMapping', $typeCategoryMapping);
|
||||
$this->set('title', 'Enrichment Results');
|
||||
$this->set('importComment', $importComment);
|
||||
|
@ -5096,8 +5052,7 @@ class EventsController extends AppController
|
|||
}
|
||||
$this->set('event', $event);
|
||||
$this->set('resultArray', $resultArray);
|
||||
$this->set('typeList', array_keys($this->Event->Attribute->typeDefinitions));
|
||||
$this->set('defaultCategories', $this->Event->Attribute->defaultCategories);
|
||||
$this->set('typeDefinitions', $this->Event->Attribute->typeDefinitions);
|
||||
$this->set('typeCategoryMapping', $typeCategoryMapping);
|
||||
$render_name = 'resolved_attributes';
|
||||
}
|
||||
|
@ -5121,6 +5076,7 @@ class EventsController extends AppController
|
|||
$this->set('module', $module);
|
||||
$this->set('eventId', $eventId);
|
||||
$this->set('event', $event);
|
||||
$this->set('mayModify', $this->__canModifyEvent($event));
|
||||
}
|
||||
|
||||
public function exportModule($module, $id, $standard = false)
|
||||
|
@ -5721,4 +5677,37 @@ class EventsController extends AppController
|
|||
}
|
||||
return $this->RestResponse->viewData($allConflicts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $event
|
||||
* @return CakeResponseTmp
|
||||
* @throws Exception
|
||||
*/
|
||||
private function __restResponse(array $event)
|
||||
{
|
||||
$tmpFile = new TmpFileTool();
|
||||
|
||||
if ($this->request->is('json')) {
|
||||
App::uses('JSONConverterTool', 'Tools');
|
||||
$converter = new JSONConverterTool();
|
||||
if ($this->RestResponse->isAutomaticTool()) {
|
||||
foreach ($converter->streamConvert($event) as $part) {
|
||||
$tmpFile->write($part);
|
||||
}
|
||||
} else {
|
||||
$tmpFile->write($converter->convert($event));
|
||||
}
|
||||
$format = 'json';
|
||||
} elseif ($this->request->is('xml')) {
|
||||
App::uses('XMLConverterTool', 'Tools');
|
||||
$converter = new XMLConverterTool();
|
||||
foreach ($converter->frameCollection($converter->convert($event)) as $chunk) {
|
||||
$tmpFile->write($chunk);
|
||||
}
|
||||
$format = 'xml';
|
||||
} else {
|
||||
throw new Exception("Invalid format, only JSON or XML is supported.");
|
||||
}
|
||||
return $this->RestResponse->viewData($tmpFile, $format, false, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -773,8 +773,10 @@ class FeedsController extends AppController
|
|||
return $this->RestResponse->viewData($event, $this->response->type());
|
||||
}
|
||||
if (is_array($event)) {
|
||||
$this->loadModel('Warninglist');
|
||||
$this->Warninglist->attachWarninglistToAttributes($event['Event']['Attribute']);
|
||||
if (isset($event['Event']['Attribute'])) {
|
||||
$this->loadModel('Warninglist');
|
||||
$this->Warninglist->attachWarninglistToAttributes($event['Event']['Attribute']);
|
||||
}
|
||||
|
||||
$this->loadModel('Event');
|
||||
$params = $this->Event->rearrangeEventForView($event, $this->passedArgs, $all);
|
||||
|
|
|
@ -19,11 +19,7 @@ class GalaxiesController extends AppController
|
|||
public function index()
|
||||
{
|
||||
$aclConditions = array();
|
||||
$filters = $this->IndexFilter->harvestParameters(array('context', 'value'));
|
||||
$contextConditions = array();
|
||||
if (empty($filters['context'])) {
|
||||
$filters['context'] = 'all';
|
||||
}
|
||||
$filters = $this->IndexFilter->harvestParameters(array('value'));
|
||||
$searchConditions = array();
|
||||
if (empty($filters['value'])) {
|
||||
$filters['value'] = '';
|
||||
|
@ -45,18 +41,16 @@ class GalaxiesController extends AppController
|
|||
array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array(
|
||||
'AND' => array($contextConditions, $searchConditions, $aclConditions)
|
||||
'AND' => array($searchConditions, $aclConditions)
|
||||
)
|
||||
)
|
||||
);
|
||||
return $this->RestResponse->viewData($galaxies, $this->response->type());
|
||||
} else {
|
||||
$this->paginate['conditions']['AND'][] = $contextConditions;
|
||||
$this->paginate['conditions']['AND'][] = $searchConditions;
|
||||
$this->paginate['conditions']['AND'][] = $aclConditions;
|
||||
$galaxies = $this->paginate();
|
||||
$this->set('galaxyList', $galaxies);
|
||||
$this->set('context', $filters['context']);
|
||||
$this->set('searchall', $filters['value']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ class LogsController extends AppController
|
|||
$conditions['AND'][] = array('created <= ' => date("Y-m-d H:i:s", $tempData[0]));
|
||||
$conditions['AND'][] = array('created >= ' => date("Y-m-d H:i:s", $tempData[1]));
|
||||
}
|
||||
} else {
|
||||
} else if ($filter !== 'limit' && $filter !== 'page') {
|
||||
$data = array('OR' => $data);
|
||||
$conditions = $this->Log->generic_add_filter($conditions, $data, 'Log.' . $filter);
|
||||
}
|
||||
|
@ -88,20 +88,13 @@ class LogsController extends AppController
|
|||
$log_entries = $this->Log->find('all', $params);
|
||||
return $this->RestResponse->viewData($log_entries, 'json');
|
||||
} else {
|
||||
if (!$this->userRole['perm_audit']) {
|
||||
$this->redirect(array('controller' => 'events', 'action' => 'index', 'admin' => false));
|
||||
}
|
||||
$this->set('isSearch', 0);
|
||||
$this->recursive = 0;
|
||||
$validFilters = $this->Log->logMeta;
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
$orgRestriction = $this->Auth->user('Organisation')['name'];
|
||||
$conditions['Log.org'] = $orgRestriction;
|
||||
$this->paginate = array(
|
||||
'limit' => 60,
|
||||
'conditions' => $conditions,
|
||||
'order' => array('Log.id' => 'DESC')
|
||||
);
|
||||
$this->paginate['conditions'] = $conditions;
|
||||
} else {
|
||||
$validFilters = array_merge_recursive($validFilters, $this->Log->logMetaAdmin);
|
||||
}
|
||||
|
@ -115,17 +108,16 @@ class LogsController extends AppController
|
|||
}
|
||||
|
||||
// Shows a minimalistic history for the currently selected event
|
||||
public function event_index($id)
|
||||
public function event_index($id, $org = null)
|
||||
{
|
||||
// check if the user has access to this event...
|
||||
$mayModify = false;
|
||||
$this->loadModel('Event');
|
||||
$event = $this->Event->fetchEvent($this->Auth->user(), array(
|
||||
'eventid' => $id,
|
||||
'includeAllTags' => 1,
|
||||
'sgReferenceOnly' => 1,
|
||||
'deleted' => [0, 1],
|
||||
'deleted_proposals' => 1
|
||||
'deleted_proposals' => 1,
|
||||
'noSightings' => true,
|
||||
'noEventReports' => true,
|
||||
));
|
||||
if (empty($event)) {
|
||||
throw new NotFoundException('Invalid event.');
|
||||
|
@ -190,57 +182,54 @@ class LogsController extends AppController
|
|||
)
|
||||
);
|
||||
}
|
||||
// send unauthorised people away. Only site admins and users of the same org may see events that are "your org only". Everyone else can proceed for all other levels of distribution
|
||||
$mineOrAdmin = true;
|
||||
if (!$this->_isSiteAdmin() && $event['Event']['org_id'] != $this->Auth->user('org_id')) {
|
||||
$mineOrAdmin = false;
|
||||
}
|
||||
$this->set('published', $event['Event']['published']);
|
||||
if ($mineOrAdmin && $this->userRole['perm_modify']) {
|
||||
$mayModify = true;
|
||||
|
||||
if ($org) {
|
||||
$conditions['org'] = $org;
|
||||
}
|
||||
|
||||
$fieldList = array('title', 'created', 'model', 'model_id', 'action', 'change', 'org', 'email');
|
||||
$this->paginate = array(
|
||||
'limit' => 60,
|
||||
'conditions' => $conditions,
|
||||
'order' => array('Log.id' => 'DESC'),
|
||||
'fields' => $fieldList
|
||||
);
|
||||
$this->paginate['fields'] = array('title', 'created', 'model', 'model_id', 'action', 'change', 'org', 'email');
|
||||
$this->paginate['conditions'] = $conditions;
|
||||
|
||||
$list = $this->paginate();
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
$this->loadModel('User');
|
||||
$emails = $this->User->find('list', array(
|
||||
'conditions' => array('User.org_id' => $this->Auth->user('org_id')),
|
||||
'fields' => array('User.id', 'User.email')
|
||||
$orgEmails = $this->User->find('column', array(
|
||||
'conditions' => array('User.org_id' => $this->Auth->user('org_id')),
|
||||
'fields' => array('User.email')
|
||||
));
|
||||
foreach ($list as $k => $item) {
|
||||
if (!in_array($item['Log']['email'], $emails)) {
|
||||
if (!in_array($item['Log']['email'], $orgEmails, true)) {
|
||||
$list[$k]['Log']['email'] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->_isRest()) {
|
||||
foreach ($list as $k => $item) {
|
||||
$list[$k] = $item['Log'];
|
||||
}
|
||||
$list = array('Log' => $list);
|
||||
$list = array('Log' => array_column($list, 'Log'));
|
||||
return $this->RestResponse->viewData($list, $this->response->type());
|
||||
} else {
|
||||
$this->set('event', $event);
|
||||
$this->set('list', $list);
|
||||
$this->set('eventId', $id);
|
||||
$this->set('mayModify', $mayModify);
|
||||
}
|
||||
|
||||
// send unauthorised people away. Only site admins and users of the same org may see events that are "your org only". Everyone else can proceed for all other levels of distribution
|
||||
$mineOrAdmin = true;
|
||||
if (!$this->_isSiteAdmin() && $event['Event']['org_id'] != $this->Auth->user('org_id')) {
|
||||
$mineOrAdmin = false;
|
||||
}
|
||||
|
||||
$mayModify = false;
|
||||
if ($mineOrAdmin && $this->userRole['perm_modify']) {
|
||||
$mayModify = true;
|
||||
}
|
||||
|
||||
$this->set('published', $event['Event']['published']);
|
||||
$this->set('event', $event);
|
||||
$this->set('list', $list);
|
||||
$this->set('eventId', $id);
|
||||
$this->set('mayModify', $mayModify);
|
||||
}
|
||||
|
||||
public $helpers = array('Js' => array('Jquery'), 'Highlight');
|
||||
|
||||
public function admin_search($new = false)
|
||||
{
|
||||
if (!$this->userRole['perm_audit']) {
|
||||
$this->redirect(array('controller' => 'events', 'action' => 'index', 'admin' => false));
|
||||
}
|
||||
$orgRestriction = null;
|
||||
if ($this->_isSiteAdmin()) {
|
||||
$orgRestriction = false;
|
||||
|
|
|
@ -17,24 +17,19 @@ class NewsController extends AppController
|
|||
{
|
||||
$this->paginate['contain'] = array('User' => array('fields' => array('User.email')));
|
||||
$newsItems = $this->paginate();
|
||||
$this->loadModel('User');
|
||||
$currentUser = $this->User->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('User.id' => $this->Auth->user('id')),
|
||||
'fields' => array('User.newsread')
|
||||
));
|
||||
|
||||
$newsread = $this->Auth->user('newsread');
|
||||
foreach ($newsItems as $key => $item) {
|
||||
if ($item['News']['date_created'] > $currentUser['User']['newsread']) {
|
||||
if ($item['News']['date_created'] > $newsread) {
|
||||
$newsItems[$key]['News']['new'] = true;
|
||||
} else {
|
||||
$newsItems[$key]['News']['new'] = false;
|
||||
}
|
||||
}
|
||||
$this->User->id = $this->Auth->user('id');
|
||||
//if ($this->User->exists()) {
|
||||
$this->User->saveField('newsread', time());
|
||||
$this->set('newsItems', $newsItems);
|
||||
//}
|
||||
|
||||
$this->loadModel('User');
|
||||
$this->User->updateField($this->Auth->user(), 'newsread', time());
|
||||
}
|
||||
|
||||
public function add()
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
<?php
|
||||
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property ObjectTemplate $ObjectTemplate
|
||||
*/
|
||||
class ObjectTemplatesController extends AppController
|
||||
{
|
||||
public $components = array('Security' ,'RequestHandler', 'Session');
|
||||
|
@ -17,24 +19,24 @@ class ObjectTemplatesController extends AppController
|
|||
'recursive' => -1
|
||||
);
|
||||
|
||||
public function objectMetaChoice($event_id) {
|
||||
$metas = $this->ObjectTemplate->find('list', array(
|
||||
'recursive' => -1,
|
||||
public function objectMetaChoice($event_id)
|
||||
{
|
||||
$metas = $this->ObjectTemplate->find('column', array(
|
||||
'conditions' => array('ObjectTemplate.active' => 1),
|
||||
'fields' => array('meta-category', 'meta-category'),
|
||||
'group' => array('ObjectTemplate.meta-category'),
|
||||
'order' => array('ObjectTemplate.meta-category asc')
|
||||
'fields' => array('ObjectTemplate.meta-category'),
|
||||
'order' => array('ObjectTemplate.meta-category asc'),
|
||||
'unique' => true,
|
||||
));
|
||||
|
||||
$items = array();
|
||||
$items[] = array(
|
||||
$eventId = h($event_id);
|
||||
$items = [[
|
||||
'name' => __('All Objects'),
|
||||
'value' => $this->baseurl . "/ObjectTemplates/objectChoice/" . h($event_id) . "/" . "0"
|
||||
);
|
||||
foreach($metas as $meta) {
|
||||
'value' => $this->baseurl . "/ObjectTemplates/objectChoice/$eventId/0"
|
||||
]];
|
||||
foreach ($metas as $meta) {
|
||||
$items[] = array(
|
||||
'name' => $meta,
|
||||
'value' => $this->baseurl . "/ObjectTemplates/objectChoice/" . h($event_id) . "/" . h($meta)
|
||||
'value' => $this->baseurl . "/ObjectTemplates/objectChoice/$eventId/" . h($meta)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -134,19 +136,20 @@ class ObjectTemplatesController extends AppController
|
|||
}
|
||||
$this->ObjectTemplate->id = $id;
|
||||
if (!$this->ObjectTemplate->exists()) {
|
||||
throw new NotFoundException('Invalid ObjectTemplate');
|
||||
throw new NotFoundException('Invalid Object Template');
|
||||
}
|
||||
if ($this->ObjectTemplate->delete()) {
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('ObjectTemplates', 'admin_delete', $id, $this->response->type());
|
||||
} else {
|
||||
$this->Flash->success(__('ObjectTemplate deleted'));
|
||||
$this->Flash->success(__('Object Template deleted'));
|
||||
}
|
||||
}
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('ObjectTemplates', 'admin_delete', $id, $this->ObjectTemplate->validationErrors, $this->response->type());
|
||||
} else {
|
||||
$this->Flash->error('ObjectTemplate could not be deleted');
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('ObjectTemplates', 'admin_delete', $id, $this->ObjectTemplate->validationErrors, $this->response->type());
|
||||
} else {
|
||||
$this->Flash->error('Object Template could not be deleted');
|
||||
}
|
||||
}
|
||||
$this->redirect($this->referer());
|
||||
}
|
||||
|
@ -295,4 +298,13 @@ class ObjectTemplatesController extends AppController
|
|||
$this->layout = 'ajax';
|
||||
$this->render('ajax/getToggleField');
|
||||
}
|
||||
|
||||
public function getRaw($uuidOrName)
|
||||
{
|
||||
$template = $this->ObjectTemplate->getRawFromDisk($uuidOrName);
|
||||
if (empty($template)) {
|
||||
throw new NotFoundException(__('Template not found'));
|
||||
}
|
||||
return $this->RestResponse->viewData($template, $this->response->type());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ class ObjectsController extends AppController
|
|||
'ObjectTemplateElement'
|
||||
)
|
||||
));
|
||||
$event = $this->MispObject->Event->fetchSimpleEvent($this->Auth->user(), $event_id);
|
||||
$event = $this->MispObject->Event->fetchSimpleEvent($this->Auth->user(), $event_id, ['contain' => ['Orgc']]);
|
||||
if (empty($event)) {
|
||||
throw new NotFoundException(__('Invalid event.'));
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ class ObjectsController extends AppController
|
|||
}
|
||||
}
|
||||
// Find the event that is to be updated
|
||||
$event = $this->MispObject->Event->fetchSimpleEvent($this->Auth->user(), $eventId);
|
||||
$event = $this->MispObject->Event->fetchSimpleEvent($this->Auth->user(), $eventId, ['contain' => ['Orgc']]);
|
||||
if (empty($event)) {
|
||||
throw new NotFoundException(__('Invalid event.'));
|
||||
}
|
||||
|
@ -808,6 +808,9 @@ class ObjectsController extends AppController
|
|||
$this->MispObject->Event->insertLock($this->Auth->user(), $eventId);
|
||||
}
|
||||
if ($this->request->is('post') || $this->request->is('delete')) {
|
||||
if (!empty($this->request->data['hard'])) {
|
||||
$hard = true;
|
||||
}
|
||||
if ($this->__delete($object['Object']['id'], $hard)) {
|
||||
$message = 'Object deleted.';
|
||||
if ($this->request->is('ajax')) {
|
||||
|
|
|
@ -47,57 +47,62 @@ class OrganisationsController extends AppController
|
|||
$searchall = $this->passedArgs['searchall'];
|
||||
}
|
||||
|
||||
|
||||
if (isset($searchall) && !empty($searchall)) {
|
||||
$passedArgs['searchall'] = $searchall;
|
||||
$allSearchFields = array('name', 'description', 'nationality', 'sector', 'type', 'contacts', 'restricted_to_domain', 'uuid');
|
||||
$searchTerm = '%' . strtolower($passedArgs['searchall']) . '%';
|
||||
foreach ($allSearchFields as $field) {
|
||||
$conditions['OR'][] = array('LOWER(Organisation.' . $field . ') LIKE' => '%' . strtolower($passedArgs['searchall']) . '%');
|
||||
$conditions['OR'][] = array('LOWER(Organisation.' . $field . ') LIKE' => $searchTerm);
|
||||
}
|
||||
}
|
||||
$this->set('passedArgs', json_encode($passedArgs));
|
||||
|
||||
$this->paginate['conditions'] = $conditions;
|
||||
$usersPerOrg = $this->User->getMembersCount();
|
||||
|
||||
$this->Organisation->addCountField('user_count', $this->User, ['User.org_id = Organisation.id']);
|
||||
if ($this->_isRest()) {
|
||||
unset($this->paginate['limit']);
|
||||
$orgs = $this->Organisation->find('all', $this->paginate);
|
||||
} else {
|
||||
if (isset($this->params['named']['viewall']) && $this->params['named']['viewall']) {
|
||||
$orgCount = $this->Organisation->find('count');
|
||||
$this->paginate['limit'] = $orgCount;
|
||||
$viewAll = isset($this->params['named']['viewall']) && $this->params['named']['viewall'];
|
||||
if ($viewAll) {
|
||||
unset($this->paginate['limit']);
|
||||
}
|
||||
$this->set('viewall', isset($this->params['named']['viewall']) ? $this->params['named']['viewall'] : false);
|
||||
$this->set('viewall', $viewAll);
|
||||
$orgs = $this->paginate();
|
||||
}
|
||||
|
||||
$this->loadModel('User');
|
||||
$org_creator_ids = array();
|
||||
foreach ($orgs as $k => $org) {
|
||||
if (isset($usersPerOrg[$org['Organisation']['id']])) {
|
||||
$orgs[$k]['Organisation']['user_count'] = $usersPerOrg[$org['Organisation']['id']];
|
||||
}
|
||||
if ($this->_isSiteAdmin()) {
|
||||
if (!in_array($org['Organisation']['created_by'], array_keys($org_creator_ids))) {
|
||||
$email = $this->User->find('first', array('recursive' => -1, 'fields' => array('id', 'email'), 'conditions' => array('id' => $org['Organisation']['created_by'])));
|
||||
if (!isset($org_creator_ids[$org['Organisation']['created_by']])) {
|
||||
$email = $this->User->find('first', array(
|
||||
'recursive' => -1,
|
||||
'fields' => array('id', 'email'),
|
||||
'conditions' => array('id' => $org['Organisation']['created_by']))
|
||||
);
|
||||
if (!empty($email)) {
|
||||
$org_creator_ids[$org['Organisation']['created_by']] = $email['User']['email'];
|
||||
} else {
|
||||
$org_creator_ids[$org['Organisation']['created_by']] = 'Unknown';
|
||||
$org_creator_ids[$org['Organisation']['created_by']] = __('Unknown');
|
||||
}
|
||||
}
|
||||
$orgs[$k]['Organisation']['created_by_email'] = $org_creator_ids[$org['Organisation']['created_by']];
|
||||
} else {
|
||||
unset($orgs[$k]['Organisation']['created_by']);
|
||||
}
|
||||
}
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($orgs, $this->response->type());
|
||||
} else {
|
||||
foreach ($orgs as &$org) {
|
||||
$org['Organisation']['country_code'] = $this->Organisation->getCountryCode($org['Organisation']['nationality']);
|
||||
}
|
||||
|
||||
$this->set('named', $this->params['named']);
|
||||
$this->set('scope', $scope);
|
||||
$this->set('orgs', $orgs);
|
||||
}
|
||||
foreach ($orgs as &$org) {
|
||||
$org['Organisation']['country_code'] = $this->Organisation->getCountryCode($org['Organisation']['nationality']);
|
||||
}
|
||||
|
||||
$this->set('named', $this->params['named']);
|
||||
$this->set('scope', $scope);
|
||||
$this->set('orgs', $orgs);
|
||||
$this->set('passedArgs', json_encode($passedArgs));
|
||||
}
|
||||
|
||||
public function admin_add()
|
||||
|
@ -122,14 +127,7 @@ class OrganisationsController extends AppController
|
|||
}
|
||||
}
|
||||
if ($this->Organisation->save($this->request->data)) {
|
||||
if (isset($this->request->data['Organisation']['logo']['size']) && $this->request->data['Organisation']['logo']['size'] > 0 && $this->request->data['Organisation']['logo']['error'] == 0) {
|
||||
$filename = basename($this->Organisation->id . '.png');
|
||||
if (preg_match("/^[0-9a-z\-\_\.]*\.(png)$/i", $filename)) {
|
||||
if (!empty($this->request->data['Organisation']['logo']['tmp_name']) && is_uploaded_file($this->request->data['Organisation']['logo']['tmp_name'])) {
|
||||
$result = move_uploaded_file($this->request->data['Organisation']['logo']['tmp_name'], APP . 'webroot/img/orgs/' . $filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->__uploadLogo($this->Organisation->id);
|
||||
if ($this->_isRest()) {
|
||||
$org = $this->Organisation->find('first', array(
|
||||
'conditions' => array('Organisation.id' => $this->Organisation->id),
|
||||
|
@ -198,14 +196,7 @@ class OrganisationsController extends AppController
|
|||
}
|
||||
$this->request->data['Organisation']['id'] = $id;
|
||||
if ($this->Organisation->save($this->request->data)) {
|
||||
if (isset($this->request->data['Organisation']['logo']['size']) && $this->request->data['Organisation']['logo']['size'] > 0 && $this->request->data['Organisation']['logo']['error'] == 0) {
|
||||
$filename = basename($this->request->data['Organisation']['id'] . '.png');
|
||||
if (preg_match("/^[0-9a-z\-\_\.]*\.(png)$/i", $filename)) {
|
||||
if (!empty($this->request->data['Organisation']['logo']['tmp_name']) && is_uploaded_file($this->request->data['Organisation']['logo']['tmp_name'])) {
|
||||
$result = move_uploaded_file($this->request->data['Organisation']['logo']['tmp_name'], APP . 'webroot/img/orgs/' . $filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->__uploadLogo($this->Organisation->id);
|
||||
if ($this->_isRest()) {
|
||||
$org = $this->Organisation->find('first', array(
|
||||
'conditions' => array('Organisation.id' => $this->Organisation->id),
|
||||
|
@ -308,76 +299,63 @@ class OrganisationsController extends AppController
|
|||
|
||||
public function view($id)
|
||||
{
|
||||
if (Validation::uuid($id)) {
|
||||
$temp = $this->Organisation->find('first', array('recursive' => -1, 'fields' => array('Organisation.id'), 'conditions' => array('Organisation.uuid' => $id)));
|
||||
if (empty($temp)) {
|
||||
throw new NotFoundException(__('Invalid organisation.'));
|
||||
}
|
||||
$id = $temp['Organisation']['id'];
|
||||
} elseif (!is_numeric($id)) {
|
||||
$temp = $this->Organisation->find('first', array('recursive' => -1, 'fields' => array('Organisation.id'), 'conditions' => array('Organisation.name' => urldecode($id))));
|
||||
if (empty($temp)) {
|
||||
throw new NotFoundException(__('Invalid organisation.'));
|
||||
}
|
||||
$id = $temp['Organisation']['id'];
|
||||
if (is_numeric($id)) {
|
||||
$conditions = ['Organisation.id' => $id];
|
||||
} else if (Validation::uuid($id)) {
|
||||
$conditions = ['Organisation.uuid' => $id];
|
||||
} else {
|
||||
$conditions = ['Organisation.name' => urldecode($id)];
|
||||
}
|
||||
$this->Organisation->id = $id;
|
||||
if (!$this->Organisation->exists()) {
|
||||
|
||||
if ($this->request->is('head')) { // Just check if org exists and user can access it
|
||||
$org = $this->Organisation->find('first', array(
|
||||
'conditions' => $conditions,
|
||||
'recursive' => -1,
|
||||
'fields' => ['id'],
|
||||
));
|
||||
$exists = $org && $this->Organisation->canSee($this->Auth->user(), $org['Organisation']['id']);
|
||||
return new CakeResponse(['status' => $exists ? 200 : 404]);
|
||||
}
|
||||
|
||||
$fields = ['id', 'name', 'date_created', 'date_modified', 'type', 'nationality', 'sector', 'contacts', 'description', 'local', 'uuid', 'restricted_to_domain', 'created_by'];
|
||||
if ($this->_isRest()) {
|
||||
$this->Organisation->addCountField('user_count', $this->User, ['User.org_id = Organisation.id']);
|
||||
$fields[] = 'user_count';
|
||||
}
|
||||
|
||||
$org = $this->Organisation->find('first', array(
|
||||
'conditions' => $conditions,
|
||||
'recursive' => -1,
|
||||
'fields' => $fields,
|
||||
));
|
||||
if (!$org || !$this->Organisation->canSee($this->Auth->user(), $org['Organisation']['id'])) {
|
||||
throw new NotFoundException(__('Invalid organisation'));
|
||||
}
|
||||
$fullAccess = false;
|
||||
$fields = array('id', 'name', 'date_created', 'date_modified', 'type', 'nationality', 'sector', 'contacts', 'description', 'local', 'uuid', 'restricted_to_domain');
|
||||
if ($this->_isSiteAdmin() || ($this->_isAdmin() && $this->Auth->user('Organisation')['id'] == $id)) {
|
||||
$fullAccess = true;
|
||||
$fields = array_merge($fields, array('created_by'));
|
||||
}
|
||||
$org = $this->Organisation->find('first', array(
|
||||
'conditions' => array('id' => $id),
|
||||
'fields' => $fields,
|
||||
'recursive' => -1
|
||||
));
|
||||
if (!$this->Auth->user('Role')['perm_sharing_group'] && Configure::read('Security.hide_organisation_index_from_users')) {
|
||||
$this->loadModel('Event');
|
||||
$event = $this->Event->find('first', array(
|
||||
'fields' => array('Event.id'),
|
||||
'recursive' => -1,
|
||||
'conditions' => array('Event.orgc_id' => $org['Organisation']['id'])
|
||||
));
|
||||
if (empty($event)) {
|
||||
$proposal = $this->Event->ShadowAttribute->find('first', array(
|
||||
'fields' => array('ShadowAttribute.id'),
|
||||
'recursive' => -1,
|
||||
'conditions' => array('ShadowAttribute.org_id' => $org['Organisation']['id'])
|
||||
));
|
||||
if (empty($proposal)) {
|
||||
throw new NotFoundException(__('Invalid organisation'));
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->set('local', $org['Organisation']['local']);
|
||||
|
||||
$fullAccess = $this->_isSiteAdmin() || ($this->_isAdmin() && $this->Auth->user('Organisation')['id'] == $org['Organisation']['id']);
|
||||
if ($fullAccess) {
|
||||
$creator = $this->Organisation->User->find(
|
||||
'first',
|
||||
array(
|
||||
'conditions' => array('User.id' => $org['Organisation']['created_by']),
|
||||
'fields' => array('email'),
|
||||
'recursive' => -1
|
||||
)
|
||||
);
|
||||
$creator = $this->Organisation->User->find('first', array(
|
||||
'conditions' => array('User.id' => $org['Organisation']['created_by']),
|
||||
'fields' => array('email'),
|
||||
'recursive' => -1
|
||||
));
|
||||
if (!empty($creator)) {
|
||||
$org['Organisation']['created_by_email'] = $creator['User']['email'];
|
||||
}
|
||||
}
|
||||
if ($this->_isRest()) {
|
||||
$org['Organisation']['user_count'] = $this->Organisation->User->getMembersCount($org['Organisation']['id']);
|
||||
return $this->RestResponse->viewData($org, $this->response->type());
|
||||
} else {
|
||||
$org['Organisation']['country_code'] = $this->Organisation->getCountryCode($org['Organisation']['nationality']);
|
||||
$this->set('fullAccess', $fullAccess);
|
||||
$this->set('org', $org);
|
||||
$this->set('id', $id);
|
||||
unset($org['Organisation']['created_by']);
|
||||
}
|
||||
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($org, $this->response->type());
|
||||
}
|
||||
|
||||
$org['Organisation']['country_code'] = $this->Organisation->getCountryCode($org['Organisation']['nationality']);
|
||||
$this->set('local', $org['Organisation']['local']);
|
||||
$this->set('fullAccess', $fullAccess);
|
||||
$this->set('org', $org);
|
||||
$this->set('id', $org['Organisation']['id']);
|
||||
$this->set('title_for_layout', __('Organisation %s', $org['Organisation']['name']));
|
||||
}
|
||||
|
||||
public function fetchOrgsForSG($idList = '{}', $type)
|
||||
|
@ -487,4 +465,25 @@ class OrganisationsController extends AppController
|
|||
$this->render('ajax/merge');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function __uploadLogo($orgId)
|
||||
{
|
||||
if (!isset($this->request->data['Organisation']['logo']['size'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$logo = $this->request->data['Organisation']['logo'];
|
||||
if ($logo['size'] > 0 && $logo['error'] == 0) {
|
||||
$extension = pathinfo($logo['name'], PATHINFO_EXTENSION);
|
||||
$filename = $orgId . '.' . ($extension === 'svg' ? 'svg' : 'png');
|
||||
if (!empty($logo['tmp_name']) && is_uploaded_file($logo['tmp_name'])) {
|
||||
return move_uploaded_file($logo['tmp_name'], APP . 'webroot/img/orgs/' . $filename);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,6 @@ App::uses('AppController', 'Controller');
|
|||
*/
|
||||
class RolesController extends AppController
|
||||
{
|
||||
public $options = array('0' => 'Read Only', '1' => 'Manage My Own Events', '2' => 'Manage Organization Events', '3' => 'Manage & Publish Organization Events'); // FIXME move this to Role Model
|
||||
|
||||
public $components = array(
|
||||
'Security',
|
||||
'Session',
|
||||
|
@ -28,36 +26,32 @@ class RolesController extends AppController
|
|||
|
||||
public function view($id=false)
|
||||
{
|
||||
$this->set('menuData', ['menuList' => 'globalActions', 'menuItem' => 'roles']);
|
||||
$this->CRUD->view($id);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->set('permissionLevelName', $this->Role->premissionLevelName);
|
||||
$this->set('permFlags', $this->Role->permFlags);
|
||||
$this->set('menuData', ['menuList' => 'globalActions', 'menuItem' => 'roles']);
|
||||
}
|
||||
|
||||
public function admin_add()
|
||||
{
|
||||
$this->set('menuData', array('menuList' => 'admin', 'menuItem' => 'addRole'));
|
||||
$params = [];
|
||||
$selectConditions = [];
|
||||
$params = ['redirect' => ['action' => 'index', 'admin' => false]];
|
||||
$this->CRUD->add($params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
if ($this->restResponsePayload) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->set('permFlags', $this->Role->permFlags);
|
||||
$dropdownData = [
|
||||
'options' => $this->options
|
||||
'options' => $this->Role->premissionLevelName,
|
||||
];
|
||||
$this->set(compact('dropdownData'));
|
||||
$this->set('menuData', array('menuList' => 'admin', 'menuItem' => 'addRole'));
|
||||
}
|
||||
|
||||
public function admin_edit($id = null)
|
||||
{
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
$this->redirect(array('controller' => 'roles', 'action' => 'index', 'admin' => false));
|
||||
}
|
||||
$this->Role->id = $id;
|
||||
if (!$this->Role->exists() && !$this->request->is('get')) {
|
||||
throw new NotFoundException(__('Invalid Role'));
|
||||
|
@ -76,7 +70,7 @@ class RolesController extends AppController
|
|||
return $this->RestResponse->viewData($role, $this->response->type());
|
||||
} else {
|
||||
$this->Flash->success(__('The Role has been saved'));
|
||||
$this->redirect(array('action' => 'index'));
|
||||
$this->redirect(array('action' => 'index', 'admin' => false));
|
||||
}
|
||||
} else {
|
||||
if ($this->_isRest()) {
|
||||
|
@ -94,12 +88,30 @@ class RolesController extends AppController
|
|||
$this->request->data['Role']['id'] = $id;
|
||||
$this->request->data = $this->Role->read(null, $id);
|
||||
}
|
||||
$this->set('options', $this->options);
|
||||
$this->set('options', $this->Role->premissionLevelName);
|
||||
$this->set('permFlags', $this->Role->permFlags);
|
||||
$this->set('id', $id);
|
||||
}
|
||||
|
||||
public function admin_index($id = false)
|
||||
public function admin_delete($id = null)
|
||||
{
|
||||
$this->CRUD->delete($id, [
|
||||
'validate' => function (array $role) {
|
||||
$usersWithRole = $this->User->find('count', [
|
||||
'conditions' => ['role_id' => $role['Role']['id']],
|
||||
'recursive' => -1,
|
||||
]);
|
||||
if ($usersWithRole) {
|
||||
throw new Exception(__("It is not possible to delete role that is assigned to users."));
|
||||
}
|
||||
}
|
||||
]);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$params = [
|
||||
'filters' => ['name'],
|
||||
|
@ -108,43 +120,21 @@ class RolesController extends AppController
|
|||
$this->loadModel('AdminSetting');
|
||||
$default_setting = $this->AdminSetting->getSetting('default_role');
|
||||
foreach ($elements as &$role) {
|
||||
$role['Role']['default'] = ($role['Role']['id'] == $default_setting) ? true : false;
|
||||
$role['Role']['default'] = $role['Role']['id'] == $default_setting;
|
||||
}
|
||||
return $elements;
|
||||
}
|
||||
];
|
||||
//$this->paginate['fields'] = ['id', 'name'];
|
||||
$this->CRUD->index($params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
$this->set('options', $this->Role->premissionLevelName);
|
||||
$this->set('permFlags', $this->Role->permFlags);
|
||||
$this->set('menuData', array('menuList' => 'globalActions', 'menuItem' => 'roles'));
|
||||
}
|
||||
|
||||
public function admin_delete($id = null)
|
||||
{
|
||||
$this->CRUD->delete($id);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->recursive = 0;
|
||||
if ($this->_isRest()) {
|
||||
$roles = $this->Role->find('all', array(
|
||||
'recursive' => -1
|
||||
));
|
||||
return $this->RestResponse->viewData($roles, $this->response->type());
|
||||
} else {
|
||||
$this->set('list', $this->paginate());
|
||||
$this->set('permFlags', $this->Role->permFlags);
|
||||
$this->loadModel('AdminSetting');
|
||||
$this->set('default_role_id', $this->AdminSetting->getSetting('default_role'));
|
||||
$this->set('options', $this->options);
|
||||
}
|
||||
$this->set('menuData', $this->_isAdmin() ?
|
||||
['menuList' => 'admin', 'menuItem' => 'indexRole'] :
|
||||
['menuList' => 'globalActions', 'menuItem' => 'roles']
|
||||
);
|
||||
}
|
||||
|
||||
public function admin_set_default($role_id = false)
|
||||
|
|
|
@ -86,10 +86,8 @@ class ServersController extends AppController
|
|||
{
|
||||
$urlparams = '';
|
||||
$passedArgs = array();
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
throw new MethodNotAllowedException('You are not authorised to do that.');
|
||||
}
|
||||
$server = $this->Server->find('first', array('conditions' => array('Server.id' => $id), 'recursive' => -1, 'fields' => array('Server.id', 'Server.url', 'Server.name')));
|
||||
|
||||
$server = $this->Server->find('first', array('conditions' => array('Server.id' => $id), 'recursive' => -1));
|
||||
if (empty($server)) {
|
||||
throw new NotFoundException('Invalid server ID.');
|
||||
}
|
||||
|
@ -115,15 +113,15 @@ class ServersController extends AppController
|
|||
$combinedArgs['limit'] = 60;
|
||||
}
|
||||
try {
|
||||
list($events, $total_count) = $this->Server->previewIndex($id, $this->Auth->user(), $combinedArgs);
|
||||
list($events, $total_count) = $this->Server->previewIndex($server, $this->Auth->user(), $combinedArgs);
|
||||
} catch (Exception $e) {
|
||||
$this->Flash->error(__('Download failed.') . ' ' . $e->getMessage());
|
||||
$this->redirect(array('action' => 'index'));
|
||||
}
|
||||
|
||||
$this->loadModel('Event');
|
||||
$threat_levels = $this->Event->ThreatLevel->find('all');
|
||||
$this->set('threatLevels', Set::combine($threat_levels, '{n}.ThreatLevel.id', '{n}.ThreatLevel.name'));
|
||||
$threat_levels = $this->Event->ThreatLevel->find('list', ['fields' => ['id', 'name']]);
|
||||
$this->set('threatLevels', $threat_levels);
|
||||
App::uses('CustomPaginationTool', 'Tools');
|
||||
$customPagination = new CustomPaginationTool();
|
||||
$params = $customPagination->createPaginationRules($events, $this->passedArgs, $this->alias);
|
||||
|
@ -150,19 +148,15 @@ class ServersController extends AppController
|
|||
|
||||
public function previewEvent($serverId, $eventId, $all = false)
|
||||
{
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
throw new MethodNotAllowedException('You are not authorised to do that.');
|
||||
}
|
||||
$server = $this->Server->find('first', array(
|
||||
'conditions' => array('Server.id' => $serverId),
|
||||
'recursive' => -1,
|
||||
'fields' => array('Server.id', 'Server.url', 'Server.name'))
|
||||
);
|
||||
));
|
||||
if (empty($server)) {
|
||||
throw new NotFoundException('Invalid server ID.');
|
||||
}
|
||||
try {
|
||||
$event = $this->Server->previewEvent($serverId, $eventId);
|
||||
$event = $this->Server->previewEvent($server, $eventId);
|
||||
} catch (NotFoundException $e) {
|
||||
throw new NotFoundException(__("Event '%s' not found.", $eventId));
|
||||
} catch (Exception $e) {
|
||||
|
@ -170,6 +164,10 @@ class ServersController extends AppController
|
|||
$this->redirect(array('action' => 'previewIndex', $serverId));
|
||||
}
|
||||
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($event, $this->response->type());
|
||||
}
|
||||
|
||||
$this->loadModel('Warninglist');
|
||||
if (isset($event['Event']['Attribute'])) {
|
||||
$this->Warninglist->attachWarninglistToAttributes($event['Event']['Attribute']);
|
||||
|
@ -200,8 +198,17 @@ class ServersController extends AppController
|
|||
$this->set($alias, $currentModel->{$variable});
|
||||
}
|
||||
}
|
||||
$threat_levels = $this->Event->ThreatLevel->find('all');
|
||||
$this->set('threatLevels', Set::combine($threat_levels, '{n}.ThreatLevel.id', '{n}.ThreatLevel.name'));
|
||||
$threat_levels = $this->Event->ThreatLevel->find('list', ['fields' => ['id', 'name']]);
|
||||
$this->set('threatLevels', $threat_levels);
|
||||
$this->set('title_for_layout', __('Remote event preview'));
|
||||
}
|
||||
|
||||
public function compareServers()
|
||||
{
|
||||
list($servers, $overlap) = $this->Server->serverEventsOverlap();
|
||||
$this->set('servers', $servers);
|
||||
$this->set('overlap', $overlap);
|
||||
$this->set('title_for_layout', __('Server overlap analysis matrix'));
|
||||
}
|
||||
|
||||
public function filterEventIndex($id)
|
||||
|
@ -929,27 +936,6 @@ class ServersController extends AppController
|
|||
$this->render('/Elements/healthElements/settings_row');
|
||||
}
|
||||
|
||||
private function __loadAvailableLanguages()
|
||||
{
|
||||
return $this->Server->loadAvailableLanguages();
|
||||
}
|
||||
|
||||
private function __loadTagCollections()
|
||||
{
|
||||
return $this->Server->loadTagCollections($this->Auth->user());
|
||||
}
|
||||
|
||||
private function __loadLocalOrgs()
|
||||
{
|
||||
$this->loadModel('Organisation');
|
||||
$local_orgs = $this->Organisation->find('list', array(
|
||||
'conditions' => array('local' => 1),
|
||||
'recursive' => -1,
|
||||
'fields' => array('Organisation.id', 'Organisation.name')
|
||||
));
|
||||
return array_replace(array(0 => __('No organisation selected.')), $local_orgs);
|
||||
}
|
||||
|
||||
public function serverSettings($tab=false)
|
||||
{
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
|
@ -975,7 +961,6 @@ class ServersController extends AppController
|
|||
$mixboxVersion = array(0 => __('Incorrect mixbox version installed, found $current, expecting $expected'), 1 => __('OK'));
|
||||
$maecVersion = array(0 => __('Incorrect maec version installed, found $current, expecting $expected'), 1 => __('OK'));
|
||||
$pymispVersion = array(0 => __('Incorrect PyMISP version installed, found $current, expecting $expected'), 1 => __('OK'));
|
||||
$plyaraVersion = array(0 => __('Incorrect plyara version installed, found $current, expecting $expected'), 1 => __('OK'));
|
||||
$sessionErrors = array(0 => __('OK'), 1 => __('High'), 2 => __('Alternative setting used'), 3 => __('Test failed'));
|
||||
$moduleErrors = array(0 => __('OK'), 1 => __('System not enabled'), 2 => __('No modules found'));
|
||||
|
||||
|
@ -1015,8 +1000,8 @@ class ServersController extends AppController
|
|||
$tabs[$result['tab']]['severity'] = $result['level'];
|
||||
}
|
||||
}
|
||||
if (isset($result['optionsSource']) && !empty($result['optionsSource'])) {
|
||||
$result['options'] = $this->{'__load' . $result['optionsSource']}();
|
||||
if (isset($result['optionsSource']) && is_callable($result['optionsSource'])) {
|
||||
$result['options'] = $result['optionsSource']();
|
||||
}
|
||||
$dumpResults[] = $result;
|
||||
if ($result['tab'] == $tab) {
|
||||
|
@ -1032,13 +1017,12 @@ class ServersController extends AppController
|
|||
$diagnostic_errors = 0;
|
||||
App::uses('File', 'Utility');
|
||||
App::uses('Folder', 'Utility');
|
||||
$additionalViewVars = array();
|
||||
if ($tab == 'files') {
|
||||
if ($tab === 'files') {
|
||||
$files = $this->__manageFiles();
|
||||
$this->set('files', $files);
|
||||
}
|
||||
// Only run this check on the diagnostics tab
|
||||
if ($tab == 'diagnostics' || $tab == 'download' || $this->_isRest()) {
|
||||
if ($tab === 'diagnostics' || $tab === 'download' || $this->_isRest()) {
|
||||
$php_ini = php_ini_loaded_file();
|
||||
$this->set('php_ini', $php_ini);
|
||||
|
||||
|
@ -1058,36 +1042,35 @@ class ServersController extends AppController
|
|||
$this->set('branch', $gitStatus['branch']);
|
||||
$this->set('commit', $gitStatus['commit']);
|
||||
$this->set('latestCommit', $gitStatus['latestCommit']);
|
||||
$phpSettings = array(
|
||||
'max_execution_time' => array(
|
||||
'explanation' => 'The maximum duration that a script can run (does not affect the background workers). A too low number will break long running scripts like comprehensive API exports',
|
||||
'recommended' => 300,
|
||||
'unit' => false
|
||||
),
|
||||
'memory_limit' => array(
|
||||
'explanation' => 'The maximum memory that PHP can consume. It is recommended to raise this number since certain exports can generate a fair bit of memory usage',
|
||||
'recommended' => 2048,
|
||||
'unit' => 'M'
|
||||
),
|
||||
'upload_max_filesize' => array(
|
||||
'explanation' => 'The maximum size that an uploaded file can be. It is recommended to raise this number to allow for the upload of larger samples',
|
||||
'recommended' => 50,
|
||||
'unit' => 'M'
|
||||
),
|
||||
'post_max_size' => array(
|
||||
'explanation' => 'The maximum size of a POSTed message, this has to be at least the same size as the upload_max_filesize setting',
|
||||
'recommended' => 50,
|
||||
'unit' => 'M'
|
||||
)
|
||||
|
||||
$phpSettings = array(
|
||||
'max_execution_time' => array(
|
||||
'explanation' => 'The maximum duration that a script can run (does not affect the background workers). A too low number will break long running scripts like comprehensive API exports',
|
||||
'recommended' => 300,
|
||||
'unit' => 'seconds',
|
||||
),
|
||||
'memory_limit' => array(
|
||||
'explanation' => 'The maximum memory that PHP can consume. It is recommended to raise this number since certain exports can generate a fair bit of memory usage',
|
||||
'recommended' => 2048,
|
||||
'unit' => 'MB'
|
||||
),
|
||||
'upload_max_filesize' => array(
|
||||
'explanation' => 'The maximum size that an uploaded file can be. It is recommended to raise this number to allow for the upload of larger samples',
|
||||
'recommended' => 50,
|
||||
'unit' => 'MB'
|
||||
),
|
||||
'post_max_size' => array(
|
||||
'explanation' => 'The maximum size of a POSTed message, this has to be at least the same size as the upload_max_filesize setting',
|
||||
'recommended' => 50,
|
||||
'unit' => 'MB'
|
||||
)
|
||||
);
|
||||
|
||||
foreach ($phpSettings as $setting => $settingArray) {
|
||||
$phpSettings[$setting]['value'] = ini_get($setting);
|
||||
if ($settingArray['unit']) {
|
||||
$phpSettings[$setting]['value'] = intval(rtrim($phpSettings[$setting]['value'], $phpSettings[$setting]['unit']));
|
||||
} else {
|
||||
$phpSettings[$setting]['value'] = intval($phpSettings[$setting]['value']);
|
||||
$phpSettings[$setting]['value'] = $this->Server->getIniSetting($setting);
|
||||
if ($phpSettings[$setting]['value'] && $settingArray['unit'] && $settingArray['unit'] === 'MB') {
|
||||
// convert basic unit to M
|
||||
$phpSettings[$setting]['value'] = (int) floor($phpSettings[$setting]['value'] / 1024 / 1024);
|
||||
}
|
||||
}
|
||||
$this->set('phpSettings', $phpSettings);
|
||||
|
@ -1133,7 +1116,9 @@ class ServersController extends AppController
|
|||
$attachmentScan = ['status' => false, 'error' => $e->getMessage()];
|
||||
}
|
||||
|
||||
$additionalViewVars = array('gpgStatus', 'sessionErrors', 'proxyStatus', 'sessionStatus', 'zmqStatus', 'stixVersion', 'cyboxVersion', 'mixboxVersion', 'maecVersion', 'stix2Version', 'pymispVersion', 'moduleStatus', 'yaraStatus', 'gpgErrors', 'proxyErrors', 'zmqErrors', 'stixOperational', 'stix', 'moduleErrors', 'moduleTypes', 'dbDiagnostics', 'dbSchemaDiagnostics', 'redisInfo', 'attachmentScan');
|
||||
$view = compact('gpgStatus', 'sessionErrors', 'proxyStatus', 'sessionStatus', 'zmqStatus', 'stixVersion', 'cyboxVersion', 'mixboxVersion', 'maecVersion', 'stix2Version', 'pymispVersion', 'moduleStatus', 'yaraStatus', 'gpgErrors', 'proxyErrors', 'zmqErrors', 'stixOperational', 'stix', 'moduleErrors', 'moduleTypes', 'dbDiagnostics', 'dbSchemaDiagnostics', 'redisInfo', 'attachmentScan');
|
||||
} else {
|
||||
$view = [];
|
||||
}
|
||||
// check whether the files are writeable
|
||||
$writeableDirs = $this->Server->writeableDirsDiagnostics($diagnostic_errors);
|
||||
|
@ -1144,13 +1129,8 @@ class ServersController extends AppController
|
|||
// check if the encoding is not set to utf8
|
||||
$dbEncodingStatus = $this->Server->databaseEncodingDiagnostics($diagnostic_errors);
|
||||
|
||||
$viewVars = array(
|
||||
'diagnostic_errors', 'tabs', 'tab', 'issues', 'finalSettings', 'writeableErrors', 'readableErrors', 'writeableDirs', 'writeableFiles', 'readableFiles', 'extensions', 'dbEncodingStatus'
|
||||
);
|
||||
$viewVars = array_merge($viewVars, $additionalViewVars);
|
||||
foreach ($viewVars as $viewVar) {
|
||||
$this->set($viewVar, ${$viewVar});
|
||||
}
|
||||
$view = array_merge($view, compact('diagnostic_errors', 'tabs', 'tab', 'issues', 'finalSettings', 'writeableErrors', 'readableErrors', 'writeableDirs', 'writeableFiles', 'readableFiles', 'extensions', 'dbEncodingStatus'));
|
||||
$this->set($view);
|
||||
|
||||
$workerIssueCount = 4;
|
||||
$worker_array = array();
|
||||
|
@ -1200,6 +1180,7 @@ class ServersController extends AppController
|
|||
$this->set('phpversion', phpversion());
|
||||
$this->set('phpmin', $this->phpmin);
|
||||
$this->set('phprec', $this->phprec);
|
||||
$this->set('phptoonew', $this->phptoonew);
|
||||
$this->set('pythonmin', $this->pythonmin);
|
||||
$this->set('pythonrec', $this->pythonrec);
|
||||
$this->set('pymisp', $this->pymisp);
|
||||
|
@ -1296,47 +1277,48 @@ class ServersController extends AppController
|
|||
}
|
||||
}
|
||||
|
||||
public function idTranslator() {
|
||||
|
||||
// The id translation feature is limited to people from the host org
|
||||
if (!$this->_isSiteAdmin() && $this->Auth->user('org_id') != Configure::read('MISP.host_org_id')) {
|
||||
throw new MethodNotAllowedException(__('You don\'t have the required privileges to do that.'));
|
||||
}
|
||||
|
||||
//We retrieve the list of remote servers that we can query
|
||||
$options = array();
|
||||
$options['conditions'] = array("pull" => true);
|
||||
$servers = $this->Server->find('all', $options);
|
||||
public function idTranslator($localId = null)
|
||||
{
|
||||
// We retrieve the list of remote servers that we can query
|
||||
$servers = $this->Server->find('all', [
|
||||
'conditions' => ['OR' => ['pull' => true, 'push' => true]],
|
||||
'recursive' => -1,
|
||||
'order' => ['Server.priority ASC'],
|
||||
]);
|
||||
|
||||
// We generate the list of servers for the dropdown
|
||||
$displayServers = array();
|
||||
foreach($servers as $s) {
|
||||
$displayServers[] = array('name' => $s['Server']['name'],
|
||||
'value' => $s['Server']['id']);
|
||||
foreach ($servers as $s) {
|
||||
$displayServers[] = [
|
||||
'name' => $s['Server']['name'],
|
||||
'value' => $s['Server']['id'],
|
||||
];
|
||||
}
|
||||
$this->set('servers', $displayServers);
|
||||
|
||||
if ($this->request->is('post')) {
|
||||
if ($localId || $this->request->is('post')) {
|
||||
if ($localId && $this->request->is('get')) {
|
||||
$this->request->data['Event']['local'] = 'local';
|
||||
$this->request->data['Event']['uuid'] = $localId;
|
||||
}
|
||||
$remote_events = array();
|
||||
if(!empty($this->request->data['Event']['uuid']) && $this->request->data['Event']['local'] == "local") {
|
||||
if (!empty($this->request->data['Event']['uuid']) && $this->request->data['Event']['local'] === "local") {
|
||||
$local_event = $this->Event->fetchSimpleEvent($this->Auth->user(), $this->request->data['Event']['uuid']);
|
||||
} else if (!empty($this->request->data['Event']['uuid']) && $this->request->data['Event']['local'] == "remote" && !empty($this->request->data['Server']['id'])) {
|
||||
} else if (!empty($this->request->data['Event']['uuid']) && $this->request->data['Event']['local'] === "remote" && !empty($this->request->data['Server']['id'])) {
|
||||
//We check on the remote server for any event with this id and try to find a match locally
|
||||
$conditions = array('AND' => array('Server.id' => $this->request->data['Server']['id'], 'Server.pull' => true));
|
||||
$remote_server = $this->Server->find('first', array('conditions' => $conditions));
|
||||
if(!empty($remote_server)) {
|
||||
if (!empty($remote_server)) {
|
||||
try {
|
||||
$remote_event = $this->Event->downloadEventFromServer($this->request->data['Event']['uuid'], $remote_server, null, true);
|
||||
} catch (Exception $e) {
|
||||
$error_msg = __("Issue while contacting the remote server to retrieve event information");
|
||||
$this->logException($error_msg, $e);
|
||||
$this->Flash->error($error_msg);
|
||||
$this->Flash->error(__("Issue while contacting the remote server to retrieve event information"));
|
||||
return;
|
||||
}
|
||||
|
||||
$local_event = $this->Event->fetchSimpleEvent($this->Auth->user(), $remote_event[0]['uuid']);
|
||||
//we record it to avoid re-querying the same server in the 2nd phase
|
||||
if(!empty($local_event)) {
|
||||
// we record it to avoid re-querying the same server in the 2nd phase
|
||||
if (!empty($local_event)) {
|
||||
$remote_events[] = array(
|
||||
"server_id" => $remote_server['Server']['id'],
|
||||
"server_name" => $remote_server['Server']['name'],
|
||||
|
@ -1346,38 +1328,40 @@ class ServersController extends AppController
|
|||
}
|
||||
}
|
||||
}
|
||||
if(empty($local_event)) {
|
||||
$this->Flash->error( __("This event could not be found or you don't have permissions to see it."));
|
||||
if (empty($local_event)) {
|
||||
$this->Flash->error(__("This event could not be found or you don't have permissions to see it."));
|
||||
return;
|
||||
} else {
|
||||
$this->Flash->success(__('The event has been found.'));
|
||||
}
|
||||
|
||||
// In the second phase, we query all configured sync servers to get their info on the event
|
||||
foreach($servers as $s) {
|
||||
foreach ($servers as $server) {
|
||||
// We check if the server was not already contacted in phase 1
|
||||
if(count($remote_events) > 0 && $remote_events[0]['server_id'] == $s['Server']['id']) {
|
||||
if (count($remote_events) > 0 && $remote_events[0]['server_id'] == $server['Server']['id']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$remote_event = $this->Event->downloadEventFromServer($local_event['Event']['uuid'], $s, null, true);
|
||||
$remote_event_id = $remote_event[0]['id'];
|
||||
$remoteEvent = $this->Event->downloadEventFromServer($local_event['Event']['uuid'], $server, null, true);
|
||||
} catch (Exception $e) {
|
||||
$this->logException("Couldn't download event from server", $e);
|
||||
$remote_event_id = null;
|
||||
$remoteEvent = null;
|
||||
}
|
||||
if ($remoteEvent) {
|
||||
$remoteEventId = $remoteEvent[0]['id'];
|
||||
}
|
||||
$remote_events[] = array(
|
||||
"server_id" => $s['Server']['id'],
|
||||
"server_name" => $s['Server']['name'],
|
||||
"url" => isset($remote_event_id) ? $s['Server']['url']."/events/view/".$remote_event_id : $s['Server']['url'],
|
||||
"remote_id" => isset($remote_event_id) ? $remote_event_id : false
|
||||
"server_id" => $server['Server']['id'],
|
||||
"server_name" => $server['Server']['name'],
|
||||
"url" => isset($remoteEventId) ? $server['Server']['url'] . "/events/view/" . $remoteEventId : $server['Server']['url'],
|
||||
"remote_id" => isset($remoteEventId) ? $remoteEventId : false
|
||||
);
|
||||
}
|
||||
|
||||
$this->set('local_event', $local_event);
|
||||
$this->set('remote_events', $remote_events);
|
||||
}
|
||||
$this->set('title_for_layout', __('Event ID translator'));
|
||||
}
|
||||
|
||||
public function getSubmodulesStatus() {
|
||||
|
@ -1428,8 +1412,8 @@ class ServersController extends AppController
|
|||
$setting['value'] = $value;
|
||||
}
|
||||
$setting['setting'] = $setting['name'];
|
||||
if (isset($setting['optionsSource']) && !empty($setting['optionsSource'])) {
|
||||
$setting['options'] = $this->{'__load' . $setting['optionsSource']}();
|
||||
if (isset($setting['optionsSource']) && is_callable($setting['optionsSource'])) {
|
||||
$setting['options'] = $setting['optionsSource']();
|
||||
}
|
||||
$subGroup = explode('.', $setting['name']);
|
||||
if ($subGroup[0] === 'Plugin') {
|
||||
|
@ -1448,7 +1432,7 @@ class ServersController extends AppController
|
|||
if (!isset($this->request->data['Server'])) {
|
||||
$this->request->data = array('Server' => $this->request->data);
|
||||
}
|
||||
if (!isset($this->request->data['Server']['value'])) {
|
||||
if (!isset($this->request->data['Server']['value']) || !is_scalar($this->request->data['Server']['value'])) {
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('Servers', 'serverSettingsEdit', false, 'Invalid input. Expected: {"value": "new_setting"}', $this->response->type());
|
||||
}
|
||||
|
@ -1491,7 +1475,7 @@ class ServersController extends AppController
|
|||
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => 'Field updated.')), 'status'=>200, 'type' => 'json'));
|
||||
}
|
||||
} else {
|
||||
if ($this->_isRest) {
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveFailResponse('Servers', 'serverSettingsEdit', false, $result, $this->response->type());
|
||||
} else {
|
||||
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'errors' => $result)), 'status'=>200, 'type' => 'json'));
|
||||
|
@ -1627,28 +1611,31 @@ class ServersController extends AppController
|
|||
|
||||
public function postTest()
|
||||
{
|
||||
if ($this->request->is('post')) {
|
||||
// Fix for PHP-FPM / Nginx / etc
|
||||
// Fix via https://www.popmartian.com/tipsntricks/2015/07/14/howto-use-php-getallheaders-under-fastcgi-php-fpm-nginx-etc/
|
||||
if (!function_exists('getallheaders')) {
|
||||
$headers = [];
|
||||
foreach ($_SERVER as $name => $value) {
|
||||
if (substr($name, 0, 5) == 'HTTP_') {
|
||||
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$headers = getallheaders();
|
||||
}
|
||||
$result = array();
|
||||
$result['body'] = $this->request->data;
|
||||
$result['headers']['Content-type'] = isset($headers['Content-type']) ? $headers['Content-type'] : 0;
|
||||
$result['headers']['Accept'] = isset($headers['Accept']) ? $headers['Accept'] : 0;
|
||||
$result['headers']['Authorization'] = isset($headers['Authorization']) ? 'OK' : 0;
|
||||
return new CakeResponse(array('body'=> json_encode($result), 'type' => 'json'));
|
||||
} else {
|
||||
if (!$this->request->is('post')) {
|
||||
throw new MethodNotAllowedException('Invalid request, expecting a POST request.');
|
||||
}
|
||||
// Fix for PHP-FPM / Nginx / etc
|
||||
// Fix via https://www.popmartian.com/tipsntricks/2015/07/14/howto-use-php-getallheaders-under-fastcgi-php-fpm-nginx-etc/
|
||||
if (!function_exists('getallheaders')) {
|
||||
$headers = [];
|
||||
foreach ($_SERVER as $name => $value) {
|
||||
if (substr($name, 0, 5) === 'HTTP_') {
|
||||
$headers[strtolower(str_replace('_', '-', substr($name, 5)))] = $value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$headers = getallheaders();
|
||||
$headers = array_change_key_case($headers, CASE_LOWER);
|
||||
}
|
||||
$result = [
|
||||
'body' => $this->request->data,
|
||||
'headers' => [
|
||||
'Content-type' => isset($headers['content-type']) ? $headers['content-type'] : 0,
|
||||
'Accept' => isset($headers['accept']) ? $headers['accept'] : 0,
|
||||
'Authorization' => isset($headers['authorization']) ? 'OK' : 0,
|
||||
],
|
||||
];
|
||||
return new CakeResponse(array('body'=> json_encode($result), 'type' => 'json'));
|
||||
}
|
||||
|
||||
public function getRemoteUser($id)
|
||||
|
@ -1667,10 +1654,6 @@ class ServersController extends AppController
|
|||
|
||||
public function testConnection($id = false)
|
||||
{
|
||||
if (!$this->Auth->user('Role')['perm_sync'] && !$this->Auth->user('Role')['perm_site_admin']) {
|
||||
throw new MethodNotAllowedException('You don\'t have permission to do that.');
|
||||
}
|
||||
|
||||
$server = $this->Server->find('first', ['conditions' => ['Server.id' => $id]]);
|
||||
if (!$server) {
|
||||
throw new NotFoundException(__('Invalid server'));
|
||||
|
@ -1728,7 +1711,8 @@ class ServersController extends AppController
|
|||
'version' => implode('.', $version),
|
||||
'mismatch' => $mismatch,
|
||||
'newer' => $newer,
|
||||
'post' => isset($post) ? $post : 'too old',
|
||||
'post' => isset($post) ? $post['status'] : 'too old',
|
||||
'response_encoding' => isset($post['content-encoding']) ? $post['content-encoding'] : null,
|
||||
'client_certificate' => $result['client_certificate'],
|
||||
)
|
||||
),
|
||||
|
@ -1838,9 +1822,19 @@ class ServersController extends AppController
|
|||
$result = $this->Server->checkoutMain();
|
||||
}
|
||||
|
||||
public function update()
|
||||
public function update($branch = false)
|
||||
{
|
||||
if ($this->request->is('post')) {
|
||||
$branch = false;
|
||||
$filterData = array(
|
||||
'request' => $this->request,
|
||||
'named_params' => $this->params['named'],
|
||||
'paramArray' => ['branch'],
|
||||
'ordered_url_params' => @compact($paramArray),
|
||||
'additional_delimiters' => PHP_EOL
|
||||
);
|
||||
$exception = false;
|
||||
$settings = $this->_harvestParameters($filterData, $exception);
|
||||
$status = $this->Server->getCurrentGitStatus();
|
||||
$raw = array();
|
||||
if (empty($status['branch'])) { // do not try to update if you are not on branch
|
||||
|
@ -1848,7 +1842,10 @@ class ServersController extends AppController
|
|||
$raw[] = $msg;
|
||||
$update = $msg;
|
||||
} else {
|
||||
$update = $this->Server->update($status, $raw);
|
||||
if ($settings === false) {
|
||||
$settings = [];
|
||||
}
|
||||
$update = $this->Server->update($status, $raw, $settings);
|
||||
}
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData(array('results' => $raw), $this->response->type());
|
||||
|
|
|
@ -828,6 +828,52 @@ class ShadowAttributesController extends AppController
|
|||
$this->set('_serialize', array('ShadowAttribute'));
|
||||
}
|
||||
|
||||
public function viewPicture($id, $thumbnail=false)
|
||||
{
|
||||
$conditions['ShadowAttribute.id'] = $id;
|
||||
$conditions['ShadowAttribute.type'] = 'attachment';
|
||||
$options = array(
|
||||
'conditions' => $conditions,
|
||||
'includeAllTags' => false,
|
||||
'includeAttributeUuid' => true,
|
||||
'flatten' => true,
|
||||
'deleted' => [0, 1]
|
||||
);
|
||||
|
||||
|
||||
$sa = $this->ShadowAttribute->find('first', array(
|
||||
'recursive' => -1,
|
||||
'contain' => ['Event', 'Attribute'], // required because of conditions
|
||||
'fields' => array(
|
||||
'ShadowAttribute.id', 'ShadowAttribute.old_id', 'ShadowAttribute.event_id', 'ShadowAttribute.type', 'ShadowAttribute.category', 'ShadowAttribute.uuid', 'ShadowAttribute.to_ids', 'ShadowAttribute.value', 'ShadowAttribute.comment', 'ShadowAttribute.org_id', 'ShadowAttribute.first_seen', 'ShadowAttribute.last_seen',
|
||||
),
|
||||
'conditions' => $conditions,
|
||||
));
|
||||
if (empty($sa)) {
|
||||
throw new NotFoundException(__('Invalid proposal.'));
|
||||
}
|
||||
|
||||
if (!$this->ShadowAttribute->Attribute->isImage($sa['ShadowAttribute'])) {
|
||||
throw new NotFoundException("ShadowAttribute is not an image.");
|
||||
}
|
||||
if ($this->_isRest()) {
|
||||
if ($this->ShadowAttribute->typeIsAttachment($sa['ShadowAttribute']['type'])) {
|
||||
$encodedFile = $this->ShadowAttribute->base64EncodeAttachment($sa['ShadowAttribute']);
|
||||
$sa['ShadowAttribute']['data'] = $encodedFile;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($sa['ShadowAttribute']['data'], $this->response->type());
|
||||
} else {
|
||||
$width = isset($this->request->params['named']['width']) ? $this->request->params['named']['width'] : 200;
|
||||
$height = isset($this->request->params['named']['height']) ? $this->request->params['named']['height'] : 200;
|
||||
$imageData = $this->ShadowAttribute->getPictureData($sa, $thumbnail, $width, $height);
|
||||
$extension = pathinfo($sa['ShadowAttribute']['value'], PATHINFO_EXTENSION);
|
||||
return new CakeResponse(array('body' => $imageData, 'type' => strtolower($extension)));
|
||||
}
|
||||
}
|
||||
|
||||
public function index($eventId = false)
|
||||
{
|
||||
$conditions = array();
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property SharingGroup $SharingGroup
|
||||
*/
|
||||
class SharingGroupsController extends AppController
|
||||
{
|
||||
public $components = array('Session', 'RequestHandler');
|
||||
|
@ -11,31 +14,29 @@ class SharingGroupsController extends AppController
|
|||
if (!empty($this->request->params['admin']) && !$this->_isSiteAdmin()) {
|
||||
$this->redirect('/');
|
||||
}
|
||||
$sgs = $this->SharingGroup->fetchAllAuthorised($this->Auth->user());
|
||||
$this->paginate = Set::merge($this->paginate, array('conditions' => array('SharingGroup.id' => $sgs)));
|
||||
}
|
||||
|
||||
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(
|
||||
'SharingGroup.name' => 'ASC'
|
||||
'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(
|
||||
'SharingGroup.name' => 'ASC'
|
||||
),
|
||||
'fields' => array('SharingGroup.id', 'SharingGroup.uuid', 'SharingGroup.name', 'SharingGroup.description', 'SharingGroup.releasability', 'SharingGroup.local', 'SharingGroup.active'),
|
||||
'contain' => array(
|
||||
'SharingGroupOrg' => array(
|
||||
'Organisation' => array('fields' => array('Organisation.name', 'Organisation.id', 'Organisation.uuid'))
|
||||
),
|
||||
'fields' => array('SharingGroup.id', 'SharingGroup.uuid', 'SharingGroup.name', 'SharingGroup.description', 'SharingGroup.releasability', 'SharingGroup.local', 'SharingGroup.active'),
|
||||
'contain' => array(
|
||||
'SharingGroupOrg' => array(
|
||||
'Organisation' => array('fields' => array('Organisation.name', 'Organisation.id', 'Organisation.uuid'))
|
||||
),
|
||||
'Organisation' => array(
|
||||
'fields' => array('Organisation.id', 'Organisation.name', 'Organisation.uuid'),
|
||||
),
|
||||
'SharingGroupServer' => array(
|
||||
'fields' => array('SharingGroupServer.all_orgs'),
|
||||
'Server' => array(
|
||||
'fields' => array('Server.name', 'Server.id')
|
||||
)
|
||||
)
|
||||
'Organisation' => array(
|
||||
'fields' => array('Organisation.id', 'Organisation.name', 'Organisation.uuid'),
|
||||
),
|
||||
'SharingGroupServer' => array(
|
||||
'fields' => array('SharingGroupServer.all_orgs'),
|
||||
'Server' => array(
|
||||
'fields' => array('Server.name', 'Server.id')
|
||||
)
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
public function add()
|
||||
|
@ -43,12 +44,6 @@ class SharingGroupsController extends AppController
|
|||
if (!$this->userRole['perm_sharing_group']) {
|
||||
throw new MethodNotAllowedException('You don\'t have the required privileges to do that.');
|
||||
}
|
||||
$orgs = $this->SharingGroup->Organisation->find('all', array(
|
||||
'conditions' => array('local' => 1),
|
||||
'recursive' => -1,
|
||||
'fields' => array('id', 'name', 'uuid')
|
||||
));
|
||||
|
||||
if ($this->request->is('post')) {
|
||||
if ($this->_isRest()) {
|
||||
if (isset($this->request->data['SharingGroup'])) {
|
||||
|
@ -129,6 +124,12 @@ class SharingGroupsController extends AppController
|
|||
} elseif ($this->_isRest()) {
|
||||
return $this->RestResponse->describe('SharingGroup', 'add', false, $this->response->type());
|
||||
}
|
||||
|
||||
$orgs = $this->SharingGroup->Organisation->find('all', array(
|
||||
'conditions' => array('local' => 1),
|
||||
'recursive' => -1,
|
||||
'fields' => array('id', 'name', 'uuid')
|
||||
));
|
||||
$this->set('orgs', $orgs);
|
||||
$this->set('localInstance', empty(Configure::read('MISP.external_baseurl')) ? Configure::read('MISP.baseurl') : Configure::read('MISP.external_baseurl'));
|
||||
// We just pass true and allow the user to edit, since he/she is just about to create the SG. This is needed to reuse the view for the edit
|
||||
|
@ -143,33 +144,28 @@ class SharingGroupsController extends AppController
|
|||
if (empty($id)) {
|
||||
throw new NotFoundException('Invalid sharing group.');
|
||||
}
|
||||
// add check for perm_sharing_group
|
||||
$this->SharingGroup->id = $id;
|
||||
if (!$this->SharingGroup->exists()) {
|
||||
throw new NotFoundException('Invalid sharing group.');
|
||||
}
|
||||
if (!$this->_isSiteAdmin() && !$this->SharingGroup->checkIfAuthorisedExtend($this->Auth->user(), $id)) {
|
||||
throw new MethodNotAllowedException('Action not allowed.');
|
||||
}
|
||||
|
||||
// check if the user is eligible to edit the SG (original creator or extend)
|
||||
$sharingGroup = $this->SharingGroup->find('first', array(
|
||||
'conditions' => array('SharingGroup.id' => $id),
|
||||
'conditions' => Validation::uuid($id) ? ['SharingGroup.uuid' => $id] : ['SharingGroup.id' => $id],
|
||||
'recursive' => -1,
|
||||
'contain' => array(
|
||||
'SharingGroupOrg' => array(
|
||||
'Organisation' => array('name', 'local', 'id')
|
||||
),
|
||||
'SharingGroupServer' => array(
|
||||
'Server' => array(
|
||||
'fields' => array('name', 'url', 'id')
|
||||
)
|
||||
),
|
||||
'Organisation' => array(
|
||||
'fields' => array('name', 'local', 'id')
|
||||
),
|
||||
'SharingGroupOrg' => array(
|
||||
'Organisation' => array('name', 'local', 'id')
|
||||
),
|
||||
'SharingGroupServer' => array(
|
||||
'Server' => array(
|
||||
'fields' => array('name', 'url', 'id')
|
||||
)
|
||||
),
|
||||
'Organisation' => array(
|
||||
'fields' => array('name', 'local', 'id')
|
||||
),
|
||||
),
|
||||
));
|
||||
if (!$this->SharingGroup->checkIfAuthorisedExtend($this->Auth->user(), $sharingGroup['SharingGroup']['id'])) {
|
||||
throw new MethodNotAllowedException('Action not allowed.');
|
||||
}
|
||||
if ($this->request->is('post')) {
|
||||
if ($this->_isRest()) {
|
||||
if (isset($this->request->data['SharingGroup'])) {
|
||||
|
@ -179,24 +175,24 @@ class SharingGroupsController extends AppController
|
|||
$id = $this->SharingGroup->captureSG($this->request->data, $this->Auth->user());
|
||||
if ($id) {
|
||||
$sg = $this->SharingGroup->fetchAllAuthorised($this->Auth->user(), 'simplified', false, $id);
|
||||
return $this->RestResponse->viewData($sg, $this->response->type());
|
||||
return $this->RestResponse->viewData($sg[0], $this->response->type());
|
||||
} else {
|
||||
return $this->RestResponse->saveFailResponse('SharingGroup', 'add', false, 'Could not save sharing group.', $this->response->type());
|
||||
}
|
||||
} else {
|
||||
$json = json_decode($this->request->data['SharingGroup']['json'], true);
|
||||
$sg = $json['sharingGroup'];
|
||||
$sg['id'] = $id;
|
||||
$sg['id'] = $sharingGroup['SharingGroup']['id'];
|
||||
$fields = array('name', 'releasability', 'description', 'active', 'roaming');
|
||||
$existingSG = $this->SharingGroup->find('first', array('recursive' => -1, 'conditions' => array('SharingGroup.id' => $id)));
|
||||
$existingSG = $this->SharingGroup->find('first', array('recursive' => -1, 'conditions' => array('SharingGroup.id' => $sharingGroup['SharingGroup']['id'])));
|
||||
foreach ($fields as $field) {
|
||||
$existingSG['SharingGroup'][$field] = $sg[$field];
|
||||
}
|
||||
unset($existingSG['SharingGroup']['modified']);
|
||||
if ($this->SharingGroup->save($existingSG)) {
|
||||
$this->SharingGroup->SharingGroupOrg->updateOrgsForSG($id, $json['organisations'], $sharingGroup['SharingGroupOrg'], $this->Auth->user());
|
||||
$this->SharingGroup->SharingGroupServer->updateServersForSG($id, $json['servers'], $sharingGroup['SharingGroupServer'], $json['sharingGroup']['roaming'], $this->Auth->user());
|
||||
$this->redirect('/SharingGroups/view/' . $id);
|
||||
$this->SharingGroup->SharingGroupOrg->updateOrgsForSG($sharingGroup['SharingGroup']['id'], $json['organisations'], $sharingGroup['SharingGroupOrg'], $this->Auth->user());
|
||||
$this->SharingGroup->SharingGroupServer->updateServersForSG($sharingGroup['SharingGroup']['id'], $json['servers'], $sharingGroup['SharingGroupServer'], $json['sharingGroup']['roaming'], $this->Auth->user());
|
||||
$this->redirect('/SharingGroups/view/' . $sharingGroup['SharingGroup']['id']);
|
||||
} else {
|
||||
$validationReplacements = array(
|
||||
'notempty' => 'This field cannot be left empty.',
|
||||
|
@ -221,7 +217,7 @@ class SharingGroupsController extends AppController
|
|||
'fields' => array('id', 'name')
|
||||
));
|
||||
$this->set('sharingGroup', $sharingGroup);
|
||||
$this->set('id', $id);
|
||||
$this->set('id', $sharingGroup['SharingGroup']['id']);
|
||||
$this->set('orgs', $orgs);
|
||||
$this->set('localInstance', empty(Configure::read('MISP.external_baseurl')) ? Configure::read('MISP.baseurl') : Configure::read('MISP.external_baseurl'));
|
||||
// We just pass true and allow the user to edit, since he/she is just about to create the SG. This is needed to reuse the view for the edit
|
||||
|
@ -236,15 +232,15 @@ class SharingGroupsController extends AppController
|
|||
if (!$this->request->is('post') && !$this->request->is('delete')) {
|
||||
throw new MethodNotAllowedException(__('Action not allowed, post or delete request expected.'));
|
||||
}
|
||||
if (!$this->SharingGroup->checkIfOwner($this->Auth->user(), $id)) {
|
||||
$deletedSg = $this->SharingGroup->find('first', array(
|
||||
'conditions' => Validation::uuid($id) ? ['uuid' => $id] : ['id' => $id],
|
||||
'recursive' => -1,
|
||||
'fields' => ['id', 'active'],
|
||||
));
|
||||
if (empty($deletedSg) || !$this->SharingGroup->checkIfOwner($this->Auth->user(), $deletedSg['SharingGroup']['id'])) {
|
||||
throw new MethodNotAllowedException('Action not allowed.');
|
||||
}
|
||||
$deletedSg = $this->SharingGroup->find('first', array(
|
||||
'conditions' => array('id' => $id),
|
||||
'recursive' => -1,
|
||||
'fields' => array('active')
|
||||
));
|
||||
if ($this->SharingGroup->delete($id)) {
|
||||
if ($this->SharingGroup->delete($deletedSg['SharingGroup']['id'])) {
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->saveSuccessResponse('SharingGroups', 'delete', $id, $this->response->type());
|
||||
}
|
||||
|
@ -265,62 +261,142 @@ class SharingGroupsController extends AppController
|
|||
|
||||
public function index($passive = false)
|
||||
{
|
||||
if ($passive === 'true') {
|
||||
$passive = true;
|
||||
}
|
||||
if ($passive === true) {
|
||||
$this->paginate['conditions'][] = array('SharingGroup.active' => 0);
|
||||
} else {
|
||||
$this->paginate['conditions'][] = array('SharingGroup.active' => 1);
|
||||
}
|
||||
$result = $this->paginate();
|
||||
// check if the current user can modify or delete the SG
|
||||
foreach ($result as $k => $sg) {
|
||||
if ($sg['Organisation']['uuid'] == $this->Auth->user('Organisation')['uuid'] && $this->userRole['perm_sharing_group']) {
|
||||
$result[$k]['editable'] = true;
|
||||
$passive = $passive === 'true';
|
||||
$authorizedSgIds = $this->SharingGroup->fetchAllAuthorised($this->Auth->user());
|
||||
$this->paginate['conditions'][] = array('SharingGroup.id' => $authorizedSgIds);
|
||||
$this->paginate['conditions'][] = array('SharingGroup.active' => $passive === true ? 0 : 1);
|
||||
|
||||
if (isset($this->params['named']['value'])) {
|
||||
$term = '%' . strtolower($this->params['named']['value']) . '%';
|
||||
if ($this->__showOrgs()) {
|
||||
$sgIds = $this->SharingGroup->SharingGroupOrg->find('column', [
|
||||
'conditions' => [
|
||||
'OR' => [
|
||||
'Organisation.uuid LIKE' => $term,
|
||||
'LOWER(Organisation.name) LIKE' => $term,
|
||||
],
|
||||
'SharingGroupOrg.sharing_group_id' => $authorizedSgIds,
|
||||
],
|
||||
'contain' => ['Organisation'],
|
||||
'fields' => ['SharingGroupOrg.sharing_group_id'],
|
||||
]);
|
||||
} else {
|
||||
$result[$k]['editable'] = false;
|
||||
$sgIds = [];
|
||||
}
|
||||
$this->paginate['conditions'][]['OR'] = [
|
||||
'SharingGroup.id' => $sgIds,
|
||||
'SharingGroup.uuid LIKE' => $term,
|
||||
'LOWER(SharingGroup.name) LIKE' => $term,
|
||||
'LOWER(SharingGroup.description) LIKE' => $term,
|
||||
'LOWER(SharingGroup.releasability) LIKE' => $term,
|
||||
'LOWER(Organisation.name) LIKE' => $term,
|
||||
];
|
||||
}
|
||||
|
||||
if ($this->__showOrgs() && isset($this->params['named']['searchorg'])) {
|
||||
$orgs = explode('|', $this->params['named']['searchorg']);
|
||||
$conditions = [];
|
||||
foreach ($orgs as $org) {
|
||||
$exclude = $org[0] === '!';
|
||||
if ($exclude) {
|
||||
$org = substr($org, 1);
|
||||
}
|
||||
$org = $this->SharingGroup->Organisation->fetchOrg($org);
|
||||
if ($org) {
|
||||
if ($exclude) {
|
||||
$conditions['AND'][] = ['org_id !=' => $org['id']];
|
||||
} else {
|
||||
$conditions['OR'][] = ['org_id' => $org['id']];
|
||||
}
|
||||
}
|
||||
}
|
||||
$sgIds = $this->SharingGroup->SharingGroupOrg->find('column', [
|
||||
'conditions' => $conditions,
|
||||
'fields' => ['SharingGroupOrg.sharing_group_id'],
|
||||
]);
|
||||
if (empty($sgIds)) {
|
||||
$sgIds = -1;
|
||||
}
|
||||
$this->paginate['conditions'][] = ['SharingGroup.id' => $sgIds];
|
||||
}
|
||||
|
||||
// To allow sort sharing group by number of organisation and also show org count when user don't have permission ot see them
|
||||
$this->SharingGroup->addCountField('org_count', $this->SharingGroup->SharingGroupOrg, ['SharingGroupOrg.sharing_group_id = SharingGroup.id']);
|
||||
$this->paginate['fields'][] = 'SharingGroup.org_count';
|
||||
|
||||
if (!$this->__showOrgs()) {
|
||||
unset($this->paginate['contain']['SharingGroupOrg']);
|
||||
unset($this->paginate['contain']['SharingGroupServer']);
|
||||
}
|
||||
|
||||
$result = $this->paginate();
|
||||
|
||||
// check if the current user can modify or delete the SG
|
||||
$userOrganisationUuid = $this->Auth->user()['Organisation']['uuid'];
|
||||
foreach ($result as $k => $sg) {
|
||||
$editable = false;
|
||||
$deletable = false;
|
||||
|
||||
if ($this->userRole['perm_site_admin'] || ($this->userRole['perm_sharing_group'] && $sg['Organisation']['uuid'] === $userOrganisationUuid)) {
|
||||
$editable = true;
|
||||
$deletable = true;
|
||||
} else if ($this->userRole['perm_sharing_group']) {
|
||||
if (!empty($sg['SharingGroupOrg'])) {
|
||||
foreach ($sg['SharingGroupOrg'] as $sgo) {
|
||||
if ($sgo['org_id'] == $this->Auth->user('org_id') && $sgo['extend']) {
|
||||
$result[$k]['editable'] = true;
|
||||
if ($sgo['extend'] && $sgo['org_id'] == $this->Auth->user('org_id')) {
|
||||
$editable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result[$k]['editable'] = $editable;
|
||||
$result[$k]['deletable'] = $deletable;
|
||||
}
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData(['response' => $result], $this->response->type()); // 'response' to keep BC
|
||||
}
|
||||
$this->set('passive', $passive);
|
||||
if ($this->_isRest()) {
|
||||
$this->set('response', $result);
|
||||
$this->set('_serialize', array('response'));
|
||||
} else {
|
||||
$this->set('sharingGroups', $result);
|
||||
}
|
||||
$this->set('sharingGroups', $result);
|
||||
$this->set('passedArgs', $passive ? 'true' : '[]');
|
||||
$this->set('title_for_layout', __('Sharing Groups'));
|
||||
}
|
||||
|
||||
public function view($id)
|
||||
{
|
||||
if ($this->request->is('head')) { // Just check if sharing group exists and user can access it
|
||||
$exists = $this->SharingGroup->checkIfAuthorised($this->Auth->user(), $id);
|
||||
return new CakeResponse(['status' => $exists ? 200 : 404]);
|
||||
}
|
||||
if (!$this->SharingGroup->checkIfAuthorised($this->Auth->user(), $id)) {
|
||||
throw new MethodNotAllowedException('Sharing group doesn\'t exist or you do not have permission to access it.');
|
||||
}
|
||||
$this->SharingGroup->id = $id;
|
||||
$this->SharingGroup->contain(
|
||||
array(
|
||||
'SharingGroupOrg' => array(
|
||||
'Organisation' => array(
|
||||
'fields' => array('id', 'name', 'uuid', 'local')
|
||||
)
|
||||
),
|
||||
'Organisation',
|
||||
'SharingGroupServer' => array(
|
||||
'Server' => array(
|
||||
'fields' => array('id', 'name', 'url')
|
||||
)
|
||||
|
||||
$contain = array(
|
||||
'Organisation',
|
||||
'SharingGroupOrg' => array(
|
||||
'Organisation' => array(
|
||||
'fields' => array('id', 'name', 'uuid', 'local')
|
||||
)
|
||||
),
|
||||
'SharingGroupServer' => array(
|
||||
'Server' => array(
|
||||
'fields' => array('id', 'name', 'url')
|
||||
)
|
||||
)
|
||||
);
|
||||
$this->SharingGroup->read();
|
||||
$sg = $this->SharingGroup->data;
|
||||
|
||||
if (!$this->__showOrgs()) {
|
||||
unset($contain['SharingGroupOrg']);
|
||||
unset($contain['SharingGroupServer']);
|
||||
$this->SharingGroup->addCountField('org_count', $this->SharingGroup->SharingGroupOrg, ['SharingGroupOrg.sharing_group_id = SharingGroup.id']);
|
||||
}
|
||||
|
||||
$sg = $this->SharingGroup->find('first', [
|
||||
'conditions' => Validation::uuid($id) ? ['SharingGroup.uuid' => $id] : ['SharingGroup.id' => $id],
|
||||
'contain' => $contain,
|
||||
]);
|
||||
if (isset($sg['SharingGroupServer'])) {
|
||||
foreach ($sg['SharingGroupServer'] as $key => $sgs) {
|
||||
if ($sgs['server_id'] == 0) {
|
||||
|
@ -334,25 +410,38 @@ class SharingGroupsController extends AppController
|
|||
}
|
||||
if ($sg['SharingGroup']['sync_user_id']) {
|
||||
$this->loadModel('User');
|
||||
$sync_user = $this->User->find('first', array(
|
||||
'conditions' => array('User.id' => $sg['SharingGroup']['sync_user_id']),
|
||||
'recursive' => -1,
|
||||
'fields' => array('User.id', 'User.org_id'),
|
||||
'contain' => array('Organisation' => array(
|
||||
'fields' => array('Organisation.name')
|
||||
))
|
||||
$syncUser = $this->User->find('first', array(
|
||||
'conditions' => array('User.id' => $sg['SharingGroup']['sync_user_id']),
|
||||
'recursive' => -1,
|
||||
'fields' => array('User.id'),
|
||||
'contain' => array('Organisation' => array(
|
||||
'fields' => array('Organisation.id', 'Organisation.name', 'Organisation.uuid'),
|
||||
))
|
||||
));
|
||||
if (empty($sync_user)) {
|
||||
if (empty($syncUser)) {
|
||||
$sg['SharingGroup']['sync_org_name'] = 'N/A';
|
||||
} else {
|
||||
$sg['SharingGroup']['sync_org_name'] = $syncUser['Organisation']['name'];
|
||||
$sg['SharingGroup']['sync_org'] = $syncUser['Organisation'];
|
||||
}
|
||||
$sg['SharingGroup']['sync_org_name'] = $sync_user['Organisation']['name'];
|
||||
}
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($sg, $this->response->type());
|
||||
}
|
||||
$this->set('mayModify', $this->SharingGroup->checkIfAuthorisedExtend($this->Auth->user(), $id));
|
||||
$this->set('id', $id);
|
||||
|
||||
$this->loadModel('Event');
|
||||
$conditions = $this->Event->createEventConditions($this->Auth->user());
|
||||
$conditions['AND']['sharing_group_id'] = $sg['SharingGroup']['id'];
|
||||
$sg['SharingGroup']['event_count'] = $this->Event->find('count', [
|
||||
'conditions' => $conditions,
|
||||
'recursive' => -1,
|
||||
'callbacks' => false,
|
||||
]);
|
||||
|
||||
$this->set('mayModify', $this->SharingGroup->checkIfAuthorisedExtend($this->Auth->user(), $sg['SharingGroup']['id']));
|
||||
$this->set('id', $sg['SharingGroup']['id']);
|
||||
$this->set('sg', $sg);
|
||||
$this->set('title_for_layout', __('Sharing Group %s', $sg['SharingGroup']['name']));
|
||||
}
|
||||
|
||||
private function __initialiseSGQuickEdit($id, $request)
|
||||
|
@ -538,4 +627,12 @@ class SharingGroupsController extends AppController
|
|||
return $this->RestResponse->saveFailResponse('SharingGroup', $action, false, $object_type . ' could not be ' . $actionType . ' the sharing group.', $this->response->type());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function __showOrgs()
|
||||
{
|
||||
return $this->Auth->user()['Role']['perm_sharing_group'] || !Configure::read('Security.hide_organisations_in_sharing_groups');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -334,24 +334,23 @@ class SightingsController extends AppController
|
|||
// Save sightings synced over, restricted to sync users
|
||||
public function bulkSaveSightings($eventId = false)
|
||||
{
|
||||
if ($this->request->is('post')) {
|
||||
if (empty($this->request->data['Sighting'])) {
|
||||
$sightings = $this->request->data;
|
||||
} else {
|
||||
$sightings = $this->request->data['Sighting'];
|
||||
}
|
||||
$saved = $this->Sighting->bulkSaveSightings($eventId, $sightings, $this->Auth->user());
|
||||
if (is_numeric($saved)) {
|
||||
if ($saved > 0) {
|
||||
return new CakeResponse(array('body'=> json_encode(array('saved' => true, 'success' => $saved . ' sightings added.')), 'status' => 200, 'type' => 'json'));
|
||||
} else {
|
||||
return new CakeResponse(array('body'=> json_encode(array('saved' => false, 'success' => 'No sightings added.')), 'status' => 200, 'type' => 'json'));
|
||||
}
|
||||
} else {
|
||||
throw new MethodNotAllowedException($saved);
|
||||
}
|
||||
} else {
|
||||
if (!$this->request->is('post')) {
|
||||
throw new MethodNotAllowedException('This method is only accessible via POST requests.');
|
||||
}
|
||||
if (empty($this->request->data['Sighting'])) {
|
||||
$sightings = $this->request->data;
|
||||
} else {
|
||||
$sightings = $this->request->data['Sighting'];
|
||||
}
|
||||
try {
|
||||
$saved = $this->Sighting->bulkSaveSightings($eventId, $sightings, $this->Auth->user());
|
||||
if ($saved > 0) {
|
||||
return new CakeResponse(array('body' => json_encode(array('saved' => true, 'success' => $saved . ' sightings added.')), 'status' => 200, 'type' => 'json'));
|
||||
} else {
|
||||
return new CakeResponse(array('body' => json_encode(array('saved' => false, 'success' => 'No sightings added.')), 'status' => 200, 'type' => 'json'));
|
||||
}
|
||||
} catch (NotFoundException $e) {
|
||||
throw new MethodNotAllowedException($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@ class TagsController extends AppController
|
|||
'request' => $this->request,
|
||||
'named_params' => $this->params['named'],
|
||||
'paramArray' => ['favouritesOnly', 'filter', 'searchall', 'name', 'search', 'exclude_statistics'],
|
||||
'ordered_url_params' => @compact($paramArray)
|
||||
);
|
||||
$exception = false;
|
||||
$passedArgsArray = $this->_harvestParameters($filterData, $exception);
|
||||
|
@ -574,9 +573,6 @@ class TagsController extends AppController
|
|||
|
||||
public function selectTag($id, $taxonomy_id, $scope = 'event', $filterData = '')
|
||||
{
|
||||
if (!$this->_isSiteAdmin() && !$this->userRole['perm_tagger']) {
|
||||
throw new NotFoundException('You don\'t have permission to do that.');
|
||||
}
|
||||
$this->loadModel('Taxonomy');
|
||||
$expanded = array();
|
||||
$this->set('taxonomy_id', $taxonomy_id);
|
||||
|
@ -629,12 +625,14 @@ class TagsController extends AppController
|
|||
$expanded = $tags;
|
||||
}
|
||||
} elseif ($taxonomy_id === 'all') {
|
||||
$conditions = [];
|
||||
$conditions = [
|
||||
'Tag.name NOT LIKE' => 'misp-galaxy:%',
|
||||
'Tag.hide_tag' => 0,
|
||||
];
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
$conditions[] = array('Tag.org_id' => array(0, $this->Auth->user('org_id')));
|
||||
$conditions[] = array('Tag.user_id' => array(0, $this->Auth->user('id')));
|
||||
$conditions['Tag.org_id'] = array(0, $this->Auth->user('org_id'));
|
||||
$conditions['Tag.user_id'] = array(0, $this->Auth->user('id'));
|
||||
}
|
||||
$conditions['Tag.hide_tag'] = 0;
|
||||
$allTags = $this->Tag->find('all', array(
|
||||
'conditions' => $conditions,
|
||||
'recursive' => -1,
|
||||
|
@ -643,10 +641,7 @@ class TagsController extends AppController
|
|||
));
|
||||
$tags = array();
|
||||
foreach ($allTags as $tag) {
|
||||
$isGalaxyTag = strpos($tag['Tag']['name'], 'misp-galaxy:') === 0;
|
||||
if (!$isGalaxyTag) {
|
||||
$tags[$tag['Tag']['id']] = $tag['Tag'];
|
||||
}
|
||||
$tags[$tag['Tag']['id']] = $tag['Tag'];
|
||||
}
|
||||
unset($allTags);
|
||||
$expanded = $tags;
|
||||
|
@ -654,45 +649,28 @@ class TagsController extends AppController
|
|||
$taxonomies = $this->Taxonomy->getTaxonomy($taxonomy_id);
|
||||
$tags = array();
|
||||
if (!empty($taxonomies['entries'])) {
|
||||
$isSiteAdmin = $this->_isSiteAdmin();
|
||||
foreach ($taxonomies['entries'] as $entry) {
|
||||
if (!empty($entry['existing_tag']['Tag'])) {
|
||||
$tags[$entry['existing_tag']['Tag']['id']] = $entry['existing_tag']['Tag'];
|
||||
$expanded[$entry['existing_tag']['Tag']['id']] = $entry['expanded'];
|
||||
$tag = $entry['existing_tag']['Tag'];
|
||||
if ($tag['hide_tag']) {
|
||||
continue; // do not include hidden tags
|
||||
}
|
||||
if (!$isSiteAdmin) {
|
||||
// Skip all tags that this user cannot use for tagging, determined by the org restriction on tags
|
||||
if ($tag['org_id'] != '0' && $tag['org_id'] != $this->Auth->user('org_id')) {
|
||||
continue;
|
||||
}
|
||||
if ($tag['user_id'] != '0' && $tag['user_id'] != $this->Auth->user('id')) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$tags[$tag['id']] = $tag;
|
||||
$expanded[$tag['id']] = $entry['expanded'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unset all tags that this user cannot use for tagging, determined by the org restriction on tags
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
$banned_tags = $this->Tag->find('list', array(
|
||||
'conditions' => array(
|
||||
'NOT' => array(
|
||||
'Tag.org_id' => array(
|
||||
0,
|
||||
$this->Auth->user('org_id')
|
||||
),
|
||||
'Tag.user_id' => array(
|
||||
0,
|
||||
$this->Auth->user('id')
|
||||
)
|
||||
)
|
||||
),
|
||||
'fields' => array('Tag.id')
|
||||
));
|
||||
foreach ($banned_tags as $banned_tag) {
|
||||
unset($tags[$banned_tag]);
|
||||
unset($expanded[$banned_tag]);
|
||||
}
|
||||
}
|
||||
|
||||
$hidden_tags = $this->Tag->find('list', array(
|
||||
'conditions' => array('Tag.hide_tag' => 1),
|
||||
'fields' => array('Tag.id')
|
||||
));
|
||||
foreach ($hidden_tags as $hidden_tag) {
|
||||
unset($tags[$hidden_tag]);
|
||||
unset($expanded[$hidden_tag]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1108,18 +1086,25 @@ class TagsController extends AppController
|
|||
$conditions['OR'][] = array('LOWER(Tag.name) LIKE' => $t);
|
||||
}
|
||||
} else {
|
||||
foreach ($tag as $k => $t) {
|
||||
$conditions['OR'][] = array('Tag.name' => $t);
|
||||
foreach ($tag as $t) {
|
||||
if (is_numeric($t)) {
|
||||
$conditions['OR'][] = ['Tag.id' => $t];
|
||||
} else {
|
||||
$conditions['OR'][] = array('Tag.name' => $t);
|
||||
}
|
||||
}
|
||||
}
|
||||
$tags = $this->Tag->find('all', array(
|
||||
'conditions' => $conditions,
|
||||
'recursive' => -1
|
||||
));
|
||||
if (!$searchIfTagExists && empty($tags)) {
|
||||
$tags = [];
|
||||
foreach ($tag as $i => $tagName) {
|
||||
$tags[] = ['Tag' => ['name' => $tagName], 'simulatedTag' => true];
|
||||
if (!$searchIfTagExists) {
|
||||
$foundTagNames = Hash::extract($tags, "{n}.Tag.name");
|
||||
foreach ($tag as $tagName) {
|
||||
if (!in_array($tagName, $foundTagNames, true)) {
|
||||
// Tag not found, insert simulated tag
|
||||
$tags[] = ['Tag' => ['name' => $tagName], 'simulatedTag' => true];
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->loadModel('Taxonomy');
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<?php
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property Taxonomy $Taxonomy
|
||||
*/
|
||||
class TaxonomiesController extends AppController
|
||||
{
|
||||
public $components = array('Session', 'RequestHandler');
|
||||
|
@ -22,6 +25,15 @@ class TaxonomiesController extends AppController
|
|||
public function index()
|
||||
{
|
||||
$this->paginate['recursive'] = -1;
|
||||
|
||||
if (!empty($this->passedArgs['value'])) {
|
||||
$this->paginate['conditions']['id'] = $this->__search($this->passedArgs['value']);
|
||||
}
|
||||
|
||||
if (isset($this->passedArgs['enabled'])) {
|
||||
$this->paginate['conditions']['enabled'] = $this->passedArgs['enabled'] ? 1 : 0;
|
||||
}
|
||||
|
||||
if ($this->_isRest()) {
|
||||
$keepFields = array('conditions', 'contain', 'recursive', 'sort');
|
||||
$searchParams = array();
|
||||
|
@ -41,13 +53,17 @@ class TaxonomiesController extends AppController
|
|||
$total += empty($predicate['TaxonomyEntry']) ? 1 : count($predicate['TaxonomyEntry']);
|
||||
}
|
||||
$taxonomies[$key]['total_count'] = $total;
|
||||
$taxonomies[$key]['current_count'] = $this->Tag->find('count', array('conditions' => array('lower(Tag.name) LIKE ' => strtolower($taxonomy['Taxonomy']['namespace']) . ':%', 'hide_tag' => 0)));
|
||||
$taxonomies[$key]['current_count'] = $this->Tag->find('count', array(
|
||||
'conditions' => array('lower(Tag.name) LIKE ' => strtolower($taxonomy['Taxonomy']['namespace']) . ':%', 'hide_tag' => 0),
|
||||
'recursive' => -1,
|
||||
));
|
||||
unset($taxonomies[$key]['TaxonomyPredicate']);
|
||||
}
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($taxonomies, $this->response->type());
|
||||
} else {
|
||||
$this->set('taxonomies', $taxonomies);
|
||||
$this->set('passedArgsArray', $this->passedArgs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,18 +85,18 @@ class TaxonomiesController extends AppController
|
|||
}
|
||||
$this->loadModel('EventTag');
|
||||
$this->loadModel('AttributeTag');
|
||||
|
||||
$tagIds = array_column(array_column(array_column($taxonomy['entries'], 'existing_tag'), 'Tag'), 'id');
|
||||
$eventCount = $this->EventTag->countForTags($tagIds, $this->Auth->user());
|
||||
$attributeTags = $this->AttributeTag->countForTags($tagIds, $this->Auth->user());
|
||||
|
||||
foreach ($taxonomy['entries'] as $key => $value) {
|
||||
$count = 0;
|
||||
$count_a = 0;
|
||||
if (!empty($value['existing_tag'])) {
|
||||
foreach ($value['existing_tag'] as $et) {
|
||||
$count = $this->EventTag->find('count', array(
|
||||
'conditions' => array('EventTag.tag_id' => $et['id'])
|
||||
));
|
||||
$count_a = $this->AttributeTag->find('count', array(
|
||||
'conditions' => array('AttributeTag.tag_id' => $et['id'])
|
||||
));
|
||||
}
|
||||
$tagId = $value['existing_tag']['Tag']['id'];
|
||||
$count = isset($eventCount[$tagId]) ? $eventCount[$tagId] : 0;
|
||||
$count_a = isset($attributeTags[$tagId]) ? $attributeTags[$tagId] : 0;
|
||||
}
|
||||
$taxonomy['entries'][$key]['events'] = $count;
|
||||
$taxonomy['entries'][$key]['attributes'] = $count_a;
|
||||
|
@ -168,11 +184,21 @@ class TaxonomiesController extends AppController
|
|||
}
|
||||
}
|
||||
|
||||
public function import()
|
||||
{
|
||||
if (!$this->request->is('post')) {
|
||||
throw new MethodNotAllowedException('This endpoint requires a POST request.');
|
||||
}
|
||||
try {
|
||||
$id = $this->Taxonomy->import($this->request->data);
|
||||
return $this->view($id);
|
||||
} catch (Exception $e) {
|
||||
return $this->RestResponse->saveFailResponse('Taxonomy', 'import', false, $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function update()
|
||||
{
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
throw new MethodNotAllowedException(__('You don\'t have permission to do that.'));
|
||||
}
|
||||
$result = $this->Taxonomy->update();
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
$fails = 0;
|
||||
|
@ -380,27 +406,18 @@ class TaxonomiesController extends AppController
|
|||
|
||||
public function taxonomyMassConfirmation($id)
|
||||
{
|
||||
if (!$this->_isSiteAdmin() && !$this->userRole['perm_tagger']) {
|
||||
throw new NotFoundException(__('You don\'t have permission to do that.'));
|
||||
}
|
||||
$this->set('id', $id);
|
||||
$this->render('ajax/taxonomy_mass_confirmation');
|
||||
}
|
||||
|
||||
public function taxonomyMassHide($id)
|
||||
{
|
||||
if (!$this->_isSiteAdmin() && !$this->userRole['perm_tagger']) {
|
||||
throw new NotFoundException(__('You don\'t have permission to do that.'));
|
||||
}
|
||||
$this->set('id', $id);
|
||||
$this->render('ajax/taxonomy_mass_hide');
|
||||
}
|
||||
|
||||
public function taxonomyMassUnhide($id)
|
||||
{
|
||||
if (!$this->_isSiteAdmin() && !$this->userRole['perm_tagger']) {
|
||||
throw new NotFoundException(__('You don\'t have permission to do that.'));
|
||||
}
|
||||
$this->set('id', $id);
|
||||
$this->render('ajax/taxonomy_mass_unhide');
|
||||
}
|
||||
|
@ -410,7 +427,7 @@ class TaxonomiesController extends AppController
|
|||
if ($this->request->is('post')) {
|
||||
$result = $this->Taxonomy->delete($id, true);
|
||||
if ($result) {
|
||||
$this->Flash->success(__('Taxonomy successfuly deleted.'));
|
||||
$this->Flash->success(__('Taxonomy successfully deleted.'));
|
||||
$this->redirect(array('controller' => 'taxonomies', 'action' => 'index'));
|
||||
} else {
|
||||
$this->Flash->error(__('Taxonomy could not be deleted.'));
|
||||
|
@ -443,12 +460,47 @@ class TaxonomiesController extends AppController
|
|||
} else {
|
||||
return $this->RestResponse->saveFailResponse('Taxonomy', 'toggleRequired', $id, $this->validationError, $this->response->type());
|
||||
}
|
||||
} else {
|
||||
$this->set('required', !$taxonomy['Taxonomy']['required']);
|
||||
$this->set('id', $id);
|
||||
$this->autoRender = false;
|
||||
$this->layout = 'ajax';
|
||||
$this->render('ajax/toggle_required');
|
||||
}
|
||||
|
||||
$this->set('required', !$taxonomy['Taxonomy']['required']);
|
||||
$this->set('id', $id);
|
||||
$this->autoRender = false;
|
||||
$this->layout = 'ajax';
|
||||
$this->render('ajax/toggle_required');
|
||||
}
|
||||
|
||||
private function __search($value)
|
||||
{
|
||||
$value = mb_strtolower(trim($value));
|
||||
$searchTerm = "%$value%";
|
||||
$taxonomyPredicateIds = $this->Taxonomy->TaxonomyPredicate->TaxonomyEntry->find('column', [
|
||||
'fields' => ['TaxonomyEntry.taxonomy_predicate_id'],
|
||||
'conditions' => ['OR' => [
|
||||
'LOWER(value) LIKE' => $searchTerm,
|
||||
'LOWER(expanded) LIKE' => $searchTerm,
|
||||
]],
|
||||
'unique' => true,
|
||||
]);
|
||||
|
||||
$taxonomyIds = $this->Taxonomy->TaxonomyPredicate->find('column', [
|
||||
'fields' => ['TaxonomyPredicate.taxonomy_id'],
|
||||
'conditions' => ['OR' => [
|
||||
'id' => $taxonomyPredicateIds,
|
||||
'LOWER(value) LIKE' => $searchTerm,
|
||||
'LOWER(expanded) LIKE' => $searchTerm,
|
||||
]],
|
||||
'unique' => true,
|
||||
]);
|
||||
|
||||
$taxonomyIds = $this->Taxonomy->find('column', [
|
||||
'fields' => ['Taxonomy.id'],
|
||||
'conditions' => ['OR' => [
|
||||
'id' => $taxonomyIds,
|
||||
'LOWER(namespace) LIKE' => $searchTerm,
|
||||
'LOWER(description) LIKE' => $searchTerm,
|
||||
]],
|
||||
]);
|
||||
|
||||
return $taxonomyIds;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
<?php
|
||||
|
||||
App::uses('AppController', 'Controller');
|
||||
|
||||
/**
|
||||
* @property Thread $Thread
|
||||
*/
|
||||
class ThreadsController extends AppController
|
||||
{
|
||||
public $components = array(
|
||||
|
@ -94,16 +96,16 @@ class ThreadsController extends AppController
|
|||
}
|
||||
if ($thread_id) {
|
||||
$this->paginate = array(
|
||||
'limit' => 10,
|
||||
'conditions' => array('Post.thread_id' => $thread_id),
|
||||
'contain' => array(
|
||||
'User' => array(
|
||||
'fields' => array('User.email', 'User.id'),
|
||||
'Organisation' => array(
|
||||
'fields' => array('id', 'name')
|
||||
),
|
||||
),
|
||||
'limit' => 10,
|
||||
'conditions' => array('Post.thread_id' => $thread_id),
|
||||
'contain' => array(
|
||||
'User' => array(
|
||||
'fields' => array('User.email', 'User.id'),
|
||||
'Organisation' => array(
|
||||
'fields' => array('id', 'uuid', 'name')
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
if ($this->_isRest()) {
|
||||
$posts = $this->Thread->Post->find('all', array(
|
||||
|
@ -114,7 +116,14 @@ class ThreadsController extends AppController
|
|||
$posts = $this->paginate('Post');
|
||||
}
|
||||
foreach ($posts as $k => $post) {
|
||||
$posts[$k]['Post']['org_name'] = empty($post['User']['id']) ? 'Deactivated user' : $post['User']['Organisation']['name'];
|
||||
if (!empty($post['User']['id'])) {
|
||||
$posts[$k]['Post']['org_id'] = $post['User']['Organisation']['id'];
|
||||
$posts[$k]['Post']['org_uuid'] = $post['User']['Organisation']['uuid'];
|
||||
$posts[$k]['Post']['org_name'] = $post['User']['Organisation']['name'];
|
||||
} else {
|
||||
$posts[$k]['Post']['org_name'] = 'Deactivated user'; // to keep BC
|
||||
}
|
||||
|
||||
if ($this->_isSiteAdmin() || $this->Auth->user('org_id') == $post['User']['org_id']) {
|
||||
$posts[$k]['Post']['user_email'] = empty($post['User']['id']) ? 'Unavailable' : $post['User']['email'];
|
||||
}
|
||||
|
|
|
@ -15,15 +15,15 @@ class UsersController extends AppController
|
|||
);
|
||||
|
||||
public $paginate = array(
|
||||
'limit' => 60,
|
||||
'recursive' => -1,
|
||||
'order' => array(
|
||||
'Organisation.name' => 'ASC'
|
||||
),
|
||||
'contain' => array(
|
||||
'Organisation' => array('id', 'name'),
|
||||
'Role' => array('id', 'name', 'perm_auth', 'perm_site_admin')
|
||||
)
|
||||
'limit' => 60,
|
||||
'recursive' => -1,
|
||||
'order' => array(
|
||||
'Organisation.name' => 'ASC'
|
||||
),
|
||||
'contain' => array(
|
||||
'Organisation' => array('id', 'uuid', 'name'),
|
||||
'Role' => array('id', 'name', 'perm_auth', 'perm_site_admin')
|
||||
)
|
||||
);
|
||||
|
||||
public $helpers = array('Js' => array('Jquery'));
|
||||
|
@ -33,7 +33,7 @@ class UsersController extends AppController
|
|||
parent::beforeFilter();
|
||||
|
||||
// what pages are allowed for non-logged-in users
|
||||
$allowedActions = array('login', 'logout');
|
||||
$allowedActions = array('login', 'logout', 'getGpgPublicKey');
|
||||
if(!empty(Configure::read('Security.email_otp_enabled'))) {
|
||||
$allowedActions[] = 'email_otp';
|
||||
}
|
||||
|
@ -51,14 +51,6 @@ class UsersController extends AppController
|
|||
if (!$this->_isSiteAdmin() && $this->Auth->user('id') != $id) {
|
||||
throw new NotFoundException(__('Invalid user or not authorised.'));
|
||||
}
|
||||
if (!is_numeric($id) && !empty($id)) {
|
||||
$userId = $this->User->find('first', array(
|
||||
'conditions' => array('email' => $id),
|
||||
'fields' => array('id')
|
||||
));
|
||||
$id = $userid['User']['id'];
|
||||
}
|
||||
$user = $this->User->read(null, $id);
|
||||
$user = $this->User->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('User.id' => $id),
|
||||
|
@ -182,7 +174,7 @@ class UsersController extends AppController
|
|||
}
|
||||
if (!$abortPost) {
|
||||
// What fields should be saved (allowed to be saved)
|
||||
$fieldList = array('autoalert', 'gpgkey', 'certif_public', 'nids_sid', 'contactalert', 'disabled');
|
||||
$fieldList = array('autoalert', 'gpgkey', 'certif_public', 'nids_sid', 'contactalert', 'disabled', 'date_modified');
|
||||
if ($this->__canChangeLogin()) {
|
||||
$fieldList[] = 'email';
|
||||
}
|
||||
|
@ -217,7 +209,6 @@ class UsersController extends AppController
|
|||
return $this->RestResponse->viewData($this->__massageUserObject($user), $this->response->type());
|
||||
} else {
|
||||
$this->Flash->success(__('The profile has been updated'));
|
||||
$this->_refreshAuth();
|
||||
$this->redirect(array('action' => 'view', $id));
|
||||
}
|
||||
} else {
|
||||
|
@ -305,7 +296,6 @@ class UsersController extends AppController
|
|||
return $this->RestResponse->saveSuccessResponse('User', 'change_pw', false, $this->response->type(), $message);
|
||||
}
|
||||
$this->Flash->success($message);
|
||||
$this->_refreshAuth();
|
||||
$this->redirect(array('action' => 'view', $id));
|
||||
} else {
|
||||
$message = __('The password could not be updated. Make sure you meet the minimum password length / complexity requirements.');
|
||||
|
@ -341,7 +331,7 @@ class UsersController extends AppController
|
|||
$this->User->virtualFields['org_ci'] = 'UPPER(Organisation.name)';
|
||||
$urlParams = "";
|
||||
$passedArgsArray = array();
|
||||
$booleanFields = array('autoalert', 'contactalert', 'termsaccepted');
|
||||
$booleanFields = array('autoalert', 'contactalert', 'termsaccepted', 'disabled');
|
||||
$textFields = array('role', 'email', 'all', 'authkey');
|
||||
// org admins can't see users of other orgs
|
||||
if ($this->_isSiteAdmin()) {
|
||||
|
@ -494,8 +484,11 @@ class UsersController extends AppController
|
|||
public function admin_filterUserIndex()
|
||||
{
|
||||
$passedArgsArray = array();
|
||||
$booleanFields = array('autoalert', 'contactalert', 'termsaccepted');
|
||||
$textFields = array('role', 'email', 'authkey');
|
||||
$booleanFields = array('autoalert', 'contactalert', 'termsaccepted', 'disabled');
|
||||
$textFields = array('role', 'email');
|
||||
if (empty(Configure::read('Security.advanced_authkeys'))) {
|
||||
$textFields[] = 'authkey';
|
||||
}
|
||||
$showOrg = 0;
|
||||
// org admins can't see users of other orgs
|
||||
if ($this->_isSiteAdmin()) {
|
||||
|
@ -542,17 +535,15 @@ class UsersController extends AppController
|
|||
$roleNames[$v['Role']['id']] = $v['Role']['name'];
|
||||
$roleJSON[] = array('id' => $v['Role']['id'], 'value' => $v['Role']['name']);
|
||||
}
|
||||
$temp = $this->User->Organisation->find('all', array(
|
||||
'conditions' => array('local' => 1),
|
||||
'recursive' => -1,
|
||||
'fields' => array('id', 'name'),
|
||||
'order' => array('LOWER(name) ASC')
|
||||
));
|
||||
$orgs = array();
|
||||
foreach ($temp as $org) {
|
||||
$orgs[$org['Organisation']['id']] = $org['Organisation']['name'];
|
||||
if ($showOrg) {
|
||||
$orgs = $this->User->Organisation->find('list', array(
|
||||
'conditions' => array('local' => 1),
|
||||
'recursive' => -1,
|
||||
'fields' => array('id', 'name'),
|
||||
'order' => array('LOWER(name) ASC')
|
||||
));
|
||||
$this->set('orgs', $orgs);
|
||||
}
|
||||
$this->set('orgs', $orgs);
|
||||
$this->set('roles', $roleNames);
|
||||
$this->set('roleJSON', json_encode($roleJSON));
|
||||
$rules = $this->_arrayToValuesIndexArray($rules);
|
||||
|
@ -563,29 +554,21 @@ class UsersController extends AppController
|
|||
|
||||
public function admin_view($id = null)
|
||||
{
|
||||
$contain = [
|
||||
'UserSetting',
|
||||
'Role',
|
||||
'Organisation'
|
||||
];
|
||||
if (!empty(Configure::read('Security.advanced_authkeys'))) {
|
||||
$contain['AuthKey'] = [
|
||||
'conditions' => [
|
||||
'OR' => [
|
||||
'AuthKey.expiration' => 0,
|
||||
'AuthKey.expiration <' => time()
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
$user = $this->User->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('User.id' => $id),
|
||||
'contain' => $contain
|
||||
'contain' => [
|
||||
'UserSetting',
|
||||
'Role',
|
||||
'Organisation'
|
||||
]
|
||||
));
|
||||
if (empty($user)) {
|
||||
throw new NotFoundException(__('Invalid user'));
|
||||
}
|
||||
if (!$this->_isSiteAdmin() && !($this->_isAdmin() && $this->Auth->user('org_id') == $user['User']['org_id'])) {
|
||||
throw new MethodNotAllowedException();
|
||||
}
|
||||
if (!empty($user['User']['gpgkey'])) {
|
||||
$pgpDetails = $this->User->verifySingleGPG($user);
|
||||
$user['User']['pgp_status'] = isset($pgpDetails[2]) ? $pgpDetails[2] : 'OK';
|
||||
|
@ -598,10 +581,6 @@ class UsersController extends AppController
|
|||
if (!empty(Configure::read('Security.advanced_authkeys'))) {
|
||||
unset($user['User']['authkey']);
|
||||
}
|
||||
$this->set('user', $user);
|
||||
if (!$this->_isSiteAdmin() && !($this->_isAdmin() && $this->Auth->user('org_id') == $user['User']['org_id'])) {
|
||||
throw new MethodNotAllowedException();
|
||||
}
|
||||
if ($this->_isRest()) {
|
||||
$user['User']['password'] = '*****';
|
||||
$temp = array();
|
||||
|
@ -614,14 +593,13 @@ class UsersController extends AppController
|
|||
'Role' => $user['Role'],
|
||||
'UserSetting' => $user['UserSetting']
|
||||
), $this->response->type());
|
||||
return $this->RestResponse->viewData(array('User' => $user['User']), $this->response->type());
|
||||
} else {
|
||||
$user2 = $this->User->find('first', array('conditions' => array('User.id' => $user['User']['invited_by']), 'recursive' => -1));
|
||||
$this->set('id', $id);
|
||||
$this->set('user2', $user2);
|
||||
$this->set('admin_view', true);
|
||||
$this->render('view');
|
||||
}
|
||||
$this->set('user', $user);
|
||||
$user2 = $this->User->find('first', array('conditions' => array('User.id' => $user['User']['invited_by']), 'recursive' => -1));
|
||||
$this->set('id', $id);
|
||||
$this->set('user2', $user2);
|
||||
$this->set('admin_view', true);
|
||||
$this->render('view');
|
||||
}
|
||||
|
||||
public function admin_add()
|
||||
|
@ -927,8 +905,8 @@ class UsersController extends AppController
|
|||
if (isset($this->request->data['User']['role_id']) && !array_key_exists($this->request->data['User']['role_id'], $syncRoles)) {
|
||||
$this->request->data['User']['server_id'] = 0;
|
||||
}
|
||||
$fields = array();
|
||||
$blockedFields = array('id', 'invited_by');
|
||||
$fields = [];
|
||||
$blockedFields = array('id', 'invited_by', 'date_modified');
|
||||
if (!$this->_isSiteAdmin()) {
|
||||
$blockedFields[] = 'org_id';
|
||||
}
|
||||
|
@ -979,11 +957,15 @@ class UsersController extends AppController
|
|||
throw new Exception('You are not authorised to assign that role to a user.');
|
||||
}
|
||||
}
|
||||
if (!empty($fields) && $this->User->save($this->request->data, true, $fields)) {
|
||||
$fields[] = 'date_modified'; // time will be inserted in `beforeSave` action
|
||||
if ($this->User->save($this->request->data, true, $fields)) {
|
||||
// newValues to array
|
||||
$fieldsNewValues = array();
|
||||
foreach ($fields as $field) {
|
||||
if ($field != 'confirm_password') {
|
||||
if ($field === 'date_modified') {
|
||||
continue;
|
||||
}
|
||||
if ($field !== 'confirm_password') {
|
||||
$newValue = $this->data['User'][$field];
|
||||
if (gettype($newValue) == 'array') {
|
||||
$newValueStr = '';
|
||||
|
@ -1026,7 +1008,6 @@ class UsersController extends AppController
|
|||
return $this->RestResponse->viewData($user, $this->response->type());
|
||||
} else {
|
||||
$this->Flash->success(__('The user has been saved'));
|
||||
$this->_refreshAuth(); // in case we modify ourselves
|
||||
$this->redirect(array('action' => 'index'));
|
||||
}
|
||||
} else {
|
||||
|
@ -1134,6 +1115,7 @@ class UsersController extends AppController
|
|||
|
||||
public function login()
|
||||
{
|
||||
$oldHash = false;
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$this->Bruteforce = ClassRegistry::init('Bruteforce');
|
||||
if (!empty($this->request->data['User']['email'])) {
|
||||
|
@ -1142,6 +1124,17 @@ class UsersController extends AppController
|
|||
throw new ForbiddenException('You have reached the maximum number of login attempts. Please wait ' . $expire . ' seconds and try again.');
|
||||
}
|
||||
}
|
||||
// Check the length of the user's authkey match old format. This can be removed in future.
|
||||
$userPass = $this->User->find('first', [
|
||||
'conditions' => ['User.email' => $this->request->data['User']['email']],
|
||||
'fields' => ['User.password'],
|
||||
'recursive' => -1,
|
||||
]);
|
||||
if (!empty($userPass) && strlen($userPass['User']['password']) === 40) {
|
||||
$oldHash = true;
|
||||
unset($this->Auth->authenticate['Form']['passwordHasher']); // use default password hasher
|
||||
$this->Auth->constructAuthenticate();
|
||||
}
|
||||
}
|
||||
if ($this->request->is('post') && Configure::read('Security.email_otp_enabled')) {
|
||||
$user = $this->Auth->identify($this->request, $this->response);
|
||||
|
@ -1154,6 +1147,12 @@ class UsersController extends AppController
|
|||
$this->set('formLoginEnabled', $formLoginEnabled);
|
||||
|
||||
if ($this->Auth->login()) {
|
||||
if ($oldHash) {
|
||||
// Convert old style password hash to blowfish
|
||||
$passwordToSave = $this->request->data['User']['password'];
|
||||
// Password is converted to hashed form automatically
|
||||
$this->User->save(['id' => $this->Auth->user('id'), 'password' => $passwordToSave], false, ['password']);
|
||||
}
|
||||
$this->_postlogin();
|
||||
} else {
|
||||
$dataSourceConfig = ConnectionManager::getDataSource('default')->config;
|
||||
|
@ -1264,16 +1263,9 @@ class UsersController extends AppController
|
|||
// Events list
|
||||
$url = $this->Session->consume('pre_login_requested_url');
|
||||
if (empty($url)) {
|
||||
$homepage = $this->User->UserSetting->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array(
|
||||
'UserSetting.user_id' => $this->Auth->user('id'),
|
||||
'UserSetting.setting' => 'homepage'
|
||||
),
|
||||
'contain' => array('User.id', 'User.org_id')
|
||||
));
|
||||
$homepage = $this->User->UserSetting->getValueForUser($this->Auth->user('id'), 'homepage');
|
||||
if (!empty($homepage)) {
|
||||
$url = $homepage['UserSetting']['value']['path'];
|
||||
$url = $homepage['path'];
|
||||
} else {
|
||||
$url = array('controller' => 'events', 'action' => 'index');
|
||||
}
|
||||
|
@ -1321,7 +1313,6 @@ class UsersController extends AppController
|
|||
}
|
||||
if (!$this->_isRest()) {
|
||||
$this->Flash->success(__('New authkey generated.', true));
|
||||
$this->_refreshAuth();
|
||||
$this->redirect($this->referer());
|
||||
} else {
|
||||
return $this->RestResponse->saveSuccessResponse('User', 'resetauthkey', $id, $this->response->type(), 'Authkey updated: ' . $newkey);
|
||||
|
@ -1448,9 +1439,7 @@ class UsersController extends AppController
|
|||
public function terms()
|
||||
{
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$this->User->id = $this->Auth->user('id');
|
||||
$this->User->saveField('termsaccepted', true);
|
||||
$this->_refreshAuth(); // refresh auth info
|
||||
$this->User->updateField($this->Auth->user(), 'termsaccepted', true);
|
||||
$this->Flash->success(__('You accepted the Terms and Conditions.'));
|
||||
$this->redirect(array('action' => 'routeafterlogin'));
|
||||
}
|
||||
|
@ -1834,9 +1823,11 @@ class UsersController extends AppController
|
|||
$params['conditions'] = array('Organisation.id' => $this->Auth->user('org_id'));
|
||||
}
|
||||
$orgs = $this->User->Organisation->find('all', $params);
|
||||
|
||||
$local_orgs_params = $params;
|
||||
$local_orgs_params['conditions']['Organisation.local'] = 1;
|
||||
$local_orgs = $this->User->Organisation->find('all', $local_orgs_params);
|
||||
$local_orgs_count = $this->User->Organisation->find('count', $local_orgs_params);
|
||||
|
||||
$this->loadModel('Log');
|
||||
$year = date('Y');
|
||||
$month = date('n');
|
||||
|
@ -1864,7 +1855,7 @@ class UsersController extends AppController
|
|||
$stats['user_count'] = $this->User->find('count', array('recursive' => -1));
|
||||
$stats['user_count_pgp'] = $this->User->find('count', array('recursive' => -1, 'conditions' => array('User.gpgkey !=' => '')));
|
||||
$stats['org_count'] = count($orgs);
|
||||
$stats['local_org_count'] = count($local_orgs);
|
||||
$stats['local_org_count'] = $local_orgs_count;
|
||||
$stats['contributing_org_count'] = $this->User->Event->find('count', array('recursive' => -1, 'group' => array('Event.orgc_id')));
|
||||
$stats['average_user_per_org'] = round($stats['user_count'] / $stats['local_org_count'], 1);
|
||||
|
||||
|
@ -1875,7 +1866,6 @@ class UsersController extends AppController
|
|||
$stats['post_count'] = $this->Thread->Post->find('count', array('recursive' => -1));
|
||||
$stats['post_count_month'] = $this->Thread->Post->find('count', array('conditions' => array('Post.date_created >' => date("Y-m-d H:i:s", $this_month)), 'recursive' => -1));
|
||||
|
||||
|
||||
if ($this->_isRest()) {
|
||||
$data = array(
|
||||
'stats' => $stats
|
||||
|
@ -1954,7 +1944,6 @@ class UsersController extends AppController
|
|||
} elseif ($params['scope'] == 'external') {
|
||||
$conditions['Organisation.local'] = 0;
|
||||
}
|
||||
$orgs = array();
|
||||
$orgs = $this->Organisation->find('all', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => $conditions,
|
||||
|
@ -2111,8 +2100,6 @@ class UsersController extends AppController
|
|||
|
||||
private function __statisticsTags($params = array())
|
||||
{
|
||||
$trending_tags = array();
|
||||
$all_tags = array();
|
||||
if ($this->_isRest()) {
|
||||
return $this->tagStatisticsGraph();
|
||||
} else {
|
||||
|
@ -2130,25 +2117,27 @@ class UsersController extends AppController
|
|||
} else {
|
||||
$galaxy_id = $mitre_galaxy_id;
|
||||
}
|
||||
$organisations = $this->User->Organisation->find('all', array(
|
||||
'recursive' => -1,
|
||||
|
||||
$organisations = $this->User->Organisation->find('list', array(
|
||||
'recursive' => -1,
|
||||
'fields' => ['id', 'name'],
|
||||
));
|
||||
array_unshift($organisations, array('Organisation' => array('id' => 0, 'name' => 'All')));
|
||||
foreach ($organisations as $id => $foo) {
|
||||
if (!$this->User->Organisation->canSee($this->Auth->user(), $id)) {
|
||||
unset($organisations[$id]);
|
||||
}
|
||||
}
|
||||
$organisations = array_merge([0 => __('All')], $organisations);
|
||||
$this->set('organisations', $organisations);
|
||||
$picked_organisation = 0;
|
||||
|
||||
if (isset($params['organisation']) && $params['organisation'] != 0) {
|
||||
$org = $this->User->Organisation->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('id' => $params['organisation']),
|
||||
));
|
||||
if (!empty($org)) {
|
||||
$picked_organisation = $org;
|
||||
$this->set('picked_organisation', $picked_organisation);
|
||||
if (isset($organisations[$params['organisation']])) {
|
||||
$this->set('picked_organisation_id', $params['organisation']);
|
||||
} else {
|
||||
$this->set('picked_organisation', array('Organisation' => array('id' => '')));
|
||||
throw new NotFoundException(__("Invalid organisation"));
|
||||
}
|
||||
} else {
|
||||
$this->set('picked_organisation', array('Organisation' => array('id' => '')));
|
||||
$this->set('picked_organisation_id', -1);
|
||||
}
|
||||
|
||||
$rest_response_empty = true;
|
||||
|
@ -2290,18 +2279,6 @@ class UsersController extends AppController
|
|||
$this->set('users', $user_results);
|
||||
}
|
||||
|
||||
// Refreshes the Auth session with new/updated data
|
||||
protected function _refreshAuth()
|
||||
{
|
||||
$oldUser = $this->Auth->user();
|
||||
$newUser = $this->User->find('first', array('conditions' => array('User.id' => $oldUser['id']), 'recursive' => -1,'contain' => array('Organisation', 'Role')));
|
||||
// Rearrange it a bit to match the Auth object created during the login
|
||||
$newUser['User']['Role'] = $newUser['Role'];
|
||||
$newUser['User']['Organisation'] = $newUser['Organisation'];
|
||||
unset($newUser['Organisation'], $newUser['Role']);
|
||||
$this->Auth->login($newUser['User']);
|
||||
}
|
||||
|
||||
public function searchGpgKey($email = false)
|
||||
{
|
||||
if (!$email) {
|
||||
|
@ -2329,6 +2306,26 @@ class UsersController extends AppController
|
|||
return new CakeResponse(array('body' => $key));
|
||||
}
|
||||
|
||||
public function getGpgPublicKey()
|
||||
{
|
||||
if (!Configure::read("MISP.download_gpg_from_homedir")) {
|
||||
throw new MethodNotAllowedException("Downloading GPG public key from homedir is not allowed.");
|
||||
}
|
||||
|
||||
$key = $this->User->getGpgPublicKey();
|
||||
if (!$key) {
|
||||
throw new NotFoundException("Public key not found.");
|
||||
}
|
||||
|
||||
list($fingeprint, $publicKey) = $key;
|
||||
$response = new CakeResponse(array(
|
||||
'body' => $publicKey,
|
||||
'type' => 'text/plain',
|
||||
));
|
||||
$response->download($fingeprint . '.asc');
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function checkIfLoggedIn()
|
||||
{
|
||||
return new CakeResponse(array('body'=> 'OK','status' => 200));
|
||||
|
|
|
@ -7,41 +7,43 @@ class MispSystemResourceWidget
|
|||
public $width = 3;
|
||||
public $height = 3;
|
||||
public $params = array(
|
||||
'treshold' => 'Treshold for disk space'
|
||||
'threshold' => 'Threshold for disk space'
|
||||
);
|
||||
public $description = 'Basic widget showing some system server statistics.';
|
||||
public $cacheLifetime = false;
|
||||
public $autoRefreshDelay = 30;
|
||||
public $placeholder =
|
||||
'{
|
||||
"treshold": "85"
|
||||
"threshold": "85"
|
||||
}';
|
||||
|
||||
public function handler($user, $options = array())
|
||||
public function handler(array $user, $options = array())
|
||||
{
|
||||
// Keep BC with typo value
|
||||
$threshold = isset($options['threshold']) ? $options['threshold'] : (isset($options['treshold']) ? $options['treshold'] : 85);
|
||||
|
||||
$drive = round((1 - disk_free_space(getcwd())/disk_total_space(getcwd()))*100,2);
|
||||
$cwd = getcwd();
|
||||
$drive = round((1 - disk_free_space($cwd)/disk_total_space($cwd))*100,2);
|
||||
$driveFree = $drive . "%";
|
||||
$driveFreeClass = "";
|
||||
if ($drive > intval($options['treshold'])) {
|
||||
$driveFree = $drive . "% - [Above Treshhold]";
|
||||
if ($drive > intval($threshold)) {
|
||||
$driveFree = $drive . "% - [Above Threshold]";
|
||||
$driveFreeClass = "red";
|
||||
}
|
||||
|
||||
$sysload = sys_getloadavg();
|
||||
|
||||
preg_match('#MemFree:[\s\t]+([\d]+)\s+kB#', file_get_contents('/proc/meminfo'), $matches);
|
||||
$meminfo = file_get_contents('/proc/meminfo');
|
||||
preg_match('#MemFree:[\s\t]+([\d]+)\s+kB#', $meminfo, $matches);
|
||||
$memoryFree = $matches[1];
|
||||
preg_match('#MemTotal:[\s\t]+([\d]+)\s+kB#', file_get_contents('/proc/meminfo'), $matches);
|
||||
preg_match('#MemTotal:[\s\t]+([\d]+)\s+kB#', $meminfo, $matches);
|
||||
$memoryTotal = $matches[1];
|
||||
|
||||
$data = array(
|
||||
array( 'title' => __('User'), 'value' => $user['email']),
|
||||
array( 'title' => __('System'), 'value' => php_uname()),
|
||||
array( 'title' => __('Disk usage'), 'value' => h($driveFree), 'class' => $driveFreeClass),
|
||||
array( 'title' => __('Load'), 'value' => h($sysload[0] . " - " . $sysload[1] . " - " . $sysload[2])),
|
||||
array( 'title' => __('Memory'), 'value' => h(round($memoryFree/1024,2) . "M free (" . round((1 - $memoryFree/$memoryTotal)*100,2) . "% used)")),
|
||||
);
|
||||
array( 'title' => __('User'), 'value' => $user['email']),
|
||||
array( 'title' => __('System'), 'value' => php_uname()),
|
||||
array( 'title' => __('Disk usage'), 'value' => h($driveFree), 'class' => $driveFreeClass),
|
||||
array( 'title' => __('Load'), 'value' => h(implode(" - ", sys_getloadavg()))),
|
||||
array( 'title' => __('Memory'), 'value' => h(round($memoryFree / 1024,2) . " MB free (" . round((1 - $memoryFree/$memoryTotal)*100,2) . " % used)")),
|
||||
);
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,36 +20,41 @@ class TrendingTagsWidget
|
|||
"include": ["misp-galaxy:", "my-internal-taxonomy"]
|
||||
}';
|
||||
public $description = 'Widget showing the trending tags over the past x seconds, along with the possibility to include/exclude tags.';
|
||||
public $cacheLifetime = 600;
|
||||
|
||||
public function handler($user, $options = array())
|
||||
{
|
||||
$this->Event = ClassRegistry::init('Event');
|
||||
$params = array(
|
||||
'metadata' => 1,
|
||||
'timestamp' => time() - (empty($options['time_window']) ? 8640000 : $options['time_window'])
|
||||
);
|
||||
/** @var Event $eventModel */
|
||||
$eventModel = ClassRegistry::init('Event');
|
||||
$threshold = empty($options['threshold']) ? 10 : $options['threshold'];
|
||||
$eventIds = $this->Event->filterEventIds($user, $params);
|
||||
$params['eventid'] = $eventIds;
|
||||
$events = array();
|
||||
$params = [
|
||||
'timestamp' => time() - (empty($options['time_window']) ? 8640000 : $options['time_window']),
|
||||
];
|
||||
$eventIds = $eventModel->filterEventIds($user, $params);
|
||||
|
||||
$tags = [];
|
||||
$tagColours = [];
|
||||
if (!empty($eventIds)) {
|
||||
$events = $this->Event->fetchEvent($user, $params);
|
||||
}
|
||||
$tags = array();
|
||||
$tagColours = array();
|
||||
foreach ($events as $event) {
|
||||
foreach ($event['EventTag'] as $et) {
|
||||
if ($this->checkTag($options, $et['Tag']['name'])) {
|
||||
if (empty($tags[$et['Tag']['name']])) {
|
||||
$tags[$et['Tag']['name']] = 1;
|
||||
$tagColours[$et['Tag']['name']] = $et['Tag']['colour'];
|
||||
} else {
|
||||
$tags[$et['Tag']['name']] += 1;
|
||||
}
|
||||
$eventTags = $eventModel->EventTag->find('all', [
|
||||
'conditions' => ['EventTag.event_id' => $eventIds],
|
||||
'contain' => ['Tag' => ['fields' => ['name', 'colour']]],
|
||||
'recursive' => -1,
|
||||
'fields' => ['id'],
|
||||
]);
|
||||
|
||||
foreach ($eventTags as $eventTag) {
|
||||
$tagName = $eventTag['Tag']['name'];
|
||||
if (isset($tags[$tagName])) {
|
||||
$tags[$tagName]++;
|
||||
} else if ($this->checkTag($options, $tagName)) {
|
||||
$tags[$tagName] = 1;
|
||||
$tagColours[$tagName] = $eventTag['Tag']['colour'];
|
||||
}
|
||||
}
|
||||
|
||||
arsort($tags);
|
||||
}
|
||||
arsort($tags);
|
||||
|
||||
$data['data'] = array_slice($tags, 0, $threshold);
|
||||
$data['colours'] = $tagColours;
|
||||
return $data;
|
||||
|
|
|
@ -27,6 +27,7 @@ class BroExport
|
|||
'domain|ip' => array('brotype' => 'DOMAIN', 'composite' => 'ADDR'),
|
||||
'url' => array('brotype' => 'URL', 'replace' => array('#^https?://#', '')),
|
||||
'user-agent' => array('brotype' => 'SOFTWARE'),
|
||||
'ja3-fingerprint-md5' => array('brotype' => 'JA3'),
|
||||
'md5' => array('brotype' => 'FILE_HASH'),
|
||||
'malware-sample' => array('brotype' => 'FILE_NAME', 'composite' => 'FILE_HASH'),
|
||||
'filename|md5' => array('brotype' => 'FILE_NAME', 'composite' => 'FILE_HASH'),
|
||||
|
@ -79,6 +80,9 @@ class BroExport
|
|||
array('domain', 1),
|
||||
array('domain|ip', 1)
|
||||
),
|
||||
'ja3-fingerprint-md5' => array(
|
||||
array('ja3-fingerprint-md5', 1)
|
||||
),
|
||||
'email' => array(
|
||||
array('email', 1),
|
||||
array('email-src', 1),
|
||||
|
|
|
@ -6,6 +6,7 @@ class CsvExport
|
|||
public $default_fields = array('uuid', 'event_id', 'category', 'type', 'value', 'comment', 'to_ids', 'timestamp', 'object_relation', 'attribute_tag');
|
||||
public $default_obj_fields = array('object_uuid', 'object_name', 'object_meta-category');
|
||||
public $requested_fields = array();
|
||||
public $decaying_fields = array('decay_score_score', 'decay_score_decayed');
|
||||
public $non_restrictive_export = true;
|
||||
|
||||
public function handler($data, $options = array())
|
||||
|
@ -22,6 +23,9 @@ class CsvExport
|
|||
|
||||
public function modify_params($user, $params)
|
||||
{
|
||||
if (!empty($params['includeDecayScore'])) {
|
||||
$this->enable_decaying();
|
||||
}
|
||||
if (empty($params['contain'])) {
|
||||
$params['contain'] = array();
|
||||
}
|
||||
|
@ -36,6 +40,11 @@ class CsvExport
|
|||
return $params;
|
||||
}
|
||||
|
||||
public function enable_decaying()
|
||||
{
|
||||
$this->default_fields = array_merge($this->default_fields, $this->decaying_fields);
|
||||
}
|
||||
|
||||
private function __attributesHandler($attribute, $options)
|
||||
{
|
||||
$attribute = $this->__addMetadataToAttributeAtomic($attribute);
|
||||
|
@ -44,6 +53,17 @@ class CsvExport
|
|||
$attribute['object_name'] = $attribute['Object']['name'];
|
||||
$attribute['object_meta-category'] = $attribute['Object']['meta-category'];
|
||||
}
|
||||
if (!empty($attribute['decay_score'])) {
|
||||
$all_scores = Hash::extract($attribute, 'decay_score.{n}.score');
|
||||
$all_decayed = Hash::extract($attribute, 'decay_score.{n}.decayed');
|
||||
$avg_score = array_sum($all_scores)/count($all_scores);
|
||||
$avg_decayed = count(array_intersect([true], $all_decayed)) > 0;
|
||||
$attribute['decay_score_score'] = $avg_score;
|
||||
$attribute['decay_score_decayed'] = $avg_decayed;
|
||||
} else {
|
||||
$attribute['decay_score_score'] = 0;
|
||||
$attribute['decay_score_decayed'] = false;
|
||||
}
|
||||
return $this->__addLine($attribute, $options);
|
||||
}
|
||||
|
||||
|
|
|
@ -189,6 +189,12 @@ class NidsExport
|
|||
case 'user-agent':
|
||||
$this->userAgentRule($ruleFormat, $item['Attribute'], $sid);
|
||||
break;
|
||||
case 'ja3-fingerprint-md5':
|
||||
$this->ja3Rule($ruleFormat, $item['Attribute'], $sid);
|
||||
break;
|
||||
case 'ja3s-fingerprint-md5': // Atribute type doesn't exists yet (2020-12-10) but ready when created.
|
||||
$this->ja3sRule($ruleFormat, $item['Attribute'], $sid);
|
||||
break;
|
||||
case 'snort':
|
||||
$this->snortRule($ruleFormat, $item['Attribute'], $sid, $ruleFormatMsg, $ruleFormatReference);
|
||||
// no break
|
||||
|
@ -503,6 +509,16 @@ class NidsExport
|
|||
);
|
||||
}
|
||||
|
||||
public function ja3Rule($ruleFormat, $attribute, &$sid)
|
||||
{
|
||||
//Empty because Snort doesn't support JA3 Rules
|
||||
}
|
||||
|
||||
public function ja3sRule($ruleFormat, $attribute, &$sid)
|
||||
{
|
||||
//Empty because Snort doesn't support JA3S Rules
|
||||
}
|
||||
|
||||
public function snortRule($ruleFormat, $attribute, &$sid, $ruleFormatMsg, $ruleFormatReference)
|
||||
{
|
||||
// LATER nids - test using lots of snort rules, some rules don't contain all the necessary to be a valid rule.
|
||||
|
|
|
@ -229,4 +229,49 @@ class NidsSuricataExport extends NidsExport
|
|||
1 // rev
|
||||
);
|
||||
}
|
||||
|
||||
public function ja3Rule($ruleFormat, $attribute, &$sid)
|
||||
{
|
||||
$overruled = $this->checkWhitelist($attribute['value']);
|
||||
$attribute['value'] = NidsExport::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
|
||||
$content = 'ja3.hash; content:"' . $attribute['value'] . '"; fast_pattern;';
|
||||
$this->rules[] = sprintf(
|
||||
$ruleFormat,
|
||||
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
|
||||
'tls', // proto
|
||||
'any', // src_ip
|
||||
'any', // src_port
|
||||
'->', // direction
|
||||
'any', // dst_ip
|
||||
'any', // dst_port
|
||||
'JA3 Hash: ' . $attribute['value'], // msg
|
||||
$content, // rule_content
|
||||
'tag:session,600,seconds;', // tag
|
||||
$sid, // sid
|
||||
1 // rev
|
||||
);
|
||||
}
|
||||
|
||||
// For Future use once JA3S Hash Attribute type is created
|
||||
public function ja3sRule($ruleFormat, $attribute, &$sid)
|
||||
{
|
||||
$overruled = $this->checkWhitelist($attribute['value']);
|
||||
$attribute['value'] = NidsExport::replaceIllegalChars($attribute['value']); // substitute chars not allowed in rule
|
||||
$content = 'ja3s.hash; content:"' . $attribute['value'] . '"; fast_pattern;';
|
||||
$this->rules[] = sprintf(
|
||||
$ruleFormat,
|
||||
($overruled) ? '#OVERRULED BY WHITELIST# ' : '',
|
||||
'tls', // proto
|
||||
'any', // src_ip
|
||||
'any', // src_port
|
||||
'->', // direction
|
||||
'any', // dst_ip
|
||||
'any', // dst_port
|
||||
'JA3S Hash: ' . $attribute['value'], // msg
|
||||
$content, // rule_content
|
||||
'tag:session,600,seconds;', // tag
|
||||
$sid, // sid
|
||||
1 // rev
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
class CakeResponseTmp extends CakeResponse
|
||||
{
|
||||
public function file($path, $options = array())
|
||||
{
|
||||
if ($path instanceof TmpFileTool) {
|
||||
$this->header('Content-Length', $path->size());
|
||||
$this->_clearBuffer();
|
||||
$this->_file = $path;
|
||||
} else {
|
||||
parent::file($path, $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param File|TmpFileTool $file
|
||||
* @param array $range
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function _sendFile($file, $range)
|
||||
{
|
||||
if ($file instanceof TmpFileTool) {
|
||||
set_time_limit(0);
|
||||
session_write_close();
|
||||
|
||||
foreach ($file->intoChunks() as $chunk) {
|
||||
if (!$this->_isActive()) {
|
||||
$file->close();
|
||||
return false;
|
||||
}
|
||||
echo $chunk;
|
||||
$this->_flushBuffer();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return parent::_sendFile($file, $range);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
class CidrTool
|
||||
{
|
||||
/** @var array */
|
||||
private $ipv4 = [];
|
||||
|
||||
/**
|
||||
* Minimum netmask for IPv4 in list. 33 because maximum netmask is 32..
|
||||
* @var int
|
||||
*/
|
||||
private $minimumIpv4Mask = 33;
|
||||
|
||||
/** @var array */
|
||||
private $ipv6 = [];
|
||||
|
||||
public function __construct(array $list)
|
||||
{
|
||||
$this->filterInputList($list);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value IPv4 or IPv6 address or range
|
||||
* @return false|string
|
||||
*/
|
||||
public function contains($value)
|
||||
{
|
||||
$valueMask = null;
|
||||
if (strpos($value, '/') !== false) {
|
||||
list($value, $valueMask) = explode('/', $value);
|
||||
}
|
||||
|
||||
$match = false;
|
||||
if (filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||
// This code converts IP address to all possible CIDRs that can contains given IP address
|
||||
// and then check if given hash table contains that CIDR.
|
||||
$ip = ip2long($value);
|
||||
// Start from 1, because doesn't make sense to check 0.0.0.0/0 match
|
||||
for ($bits = $this->minimumIpv4Mask; $bits <= 32; $bits++) {
|
||||
$mask = -1 << (32 - $bits);
|
||||
$needle = long2ip($ip & $mask) . "/$bits";
|
||||
if (isset($this->ipv4[$needle])) {
|
||||
$match = $needle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} elseif (filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
$value = unpack('n*', inet_pton($value));
|
||||
foreach ($this->ipv6 as $netmask => $lv) {
|
||||
foreach ($lv as $l) {
|
||||
if ($this->ipv6InCidr($value, $l, $netmask)) {
|
||||
$match = inet_ntop($l) . "/$netmask";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($match && $valueMask) {
|
||||
$matchMask = explode('/', $match)[1];
|
||||
if ($valueMask < $matchMask) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $match;
|
||||
}
|
||||
|
||||
/**
|
||||
* Using solution from https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/IpUtils.php
|
||||
*
|
||||
* @param array $ip
|
||||
* @param string $cidr
|
||||
* @param int $netmask
|
||||
* @return bool
|
||||
*/
|
||||
private function ipv6InCidr($ip, $cidr, $netmask)
|
||||
{
|
||||
$bytesAddr = unpack('n*', $cidr);
|
||||
for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; ++$i) {
|
||||
$left = $netmask - 16 * ($i - 1);
|
||||
$left = ($left <= 16) ? $left : 16;
|
||||
$mask = ~(0xffff >> $left) & 0xffff;
|
||||
if (($bytesAddr[$i] & $mask) != ($ip[$i] & $mask)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter out invalid IPv4 or IPv4 CIDR and append maximum netmask if no netmask is given.
|
||||
* @param array $list
|
||||
*/
|
||||
private function filterInputList(array $list)
|
||||
{
|
||||
foreach ($list as $v) {
|
||||
$parts = explode('/', $v, 2);
|
||||
$ipBytes = inet_pton($parts[0]);
|
||||
if ($ipBytes === false) {
|
||||
continue; // IP address part of CIDR is invalid
|
||||
}
|
||||
$maximumNetmask = strlen($ipBytes) === 4 ? 32 : 128;
|
||||
|
||||
if (isset($parts[1]) && ($parts[1] > $maximumNetmask || $parts[1] < 0)) {
|
||||
// Netmask part of CIDR is invalid
|
||||
continue;
|
||||
}
|
||||
|
||||
$mask = isset($parts[1]) ? $parts[1] : $maximumNetmask;
|
||||
if ($maximumNetmask === 32) {
|
||||
if ($mask < $this->minimumIpv4Mask) {
|
||||
$this->minimumIpv4Mask = (int)$mask;
|
||||
}
|
||||
if (!isset($parts[1])) {
|
||||
$v = "$v/$maximumNetmask"; // If CIDR doesnt contains '/', we will consider CIDR as /32
|
||||
}
|
||||
$this->ipv4[$v] = true;
|
||||
} else {
|
||||
$this->ipv6[$mask][] = $ipBytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -177,7 +177,7 @@ class ComplexTypeTool
|
|||
unset($input);
|
||||
|
||||
$iocArray = [];
|
||||
foreach ($tmpFile->csv($delimiter) as $row) {
|
||||
foreach ($tmpFile->intoParsedCsv($delimiter) as $row) {
|
||||
if (!empty($row[0][0]) && $row[0][0] === '#') { // Comment
|
||||
continue;
|
||||
}
|
||||
|
@ -234,6 +234,7 @@ class ComplexTypeTool
|
|||
if (isset($resultArray[$typeArray['value']])) {
|
||||
continue;
|
||||
}
|
||||
$typeArray['original_value'] = $ioc;
|
||||
$resultArray[$typeArray['value']] = $typeArray;
|
||||
}
|
||||
return array_values($resultArray);
|
||||
|
|
|
@ -4,13 +4,14 @@
|
|||
private $__lookupTables = array();
|
||||
private $__related_events = array();
|
||||
private $__related_attributes = array();
|
||||
private $__eventModel = false;
|
||||
/** @var Event */
|
||||
private $__eventModel;
|
||||
private $__taxonomyModel = false;
|
||||
private $__galaxyClusterModel = false;
|
||||
private $__user = false;
|
||||
private $__json = array();
|
||||
|
||||
public function construct($eventModel, $taxonomyModel, $galaxyClusterModel, $user, $json)
|
||||
public function construct(Event $eventModel, $taxonomyModel, $galaxyClusterModel, $user, $json)
|
||||
{
|
||||
$this->__eventModel = $eventModel;
|
||||
$this->__taxonomyModel = $taxonomyModel;
|
||||
|
@ -26,7 +27,15 @@
|
|||
|
||||
private function __expandEvent($id)
|
||||
{
|
||||
$event = $this->__eventModel->fetchEvent($this->__user, array('eventid' => $id, 'flatten' => 0, 'includeTagRelations' => 1, 'includeGalaxy' => 1, 'includeGranularCorrelations' => 1));
|
||||
$event = $this->__eventModel->fetchEvent($this->__user, array(
|
||||
'eventid' => $id,
|
||||
'flatten' => 0,
|
||||
'includeTagRelations' => 1,
|
||||
'includeGalaxy' => 1,
|
||||
'includeGranularCorrelations' => 1,
|
||||
'noSightings' => true,
|
||||
'sgReferenceOnly' => true,
|
||||
));
|
||||
if (empty($event)) {
|
||||
return $this->__json;
|
||||
}
|
||||
|
@ -91,11 +100,11 @@
|
|||
{
|
||||
foreach ($objects as $k => $object) {
|
||||
$include = $full;
|
||||
if (!$include) {
|
||||
if (!$include && isset($object['Attribute'])) {
|
||||
foreach ($object['Attribute'] as $attribute) {
|
||||
if (isset($this->__related_attributes[$attribute['id']])) {
|
||||
$include = true;
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,7 +73,8 @@ class CryptGpgExtended extends Crypt_GPG
|
|||
}
|
||||
|
||||
/**
|
||||
* Return key info without importing it.
|
||||
* Return key info without importing it when GPG supports --import-options show-only, otherwise just import and
|
||||
* then return details.
|
||||
*
|
||||
* @param string $key
|
||||
* @return Crypt_GPG_Key[]
|
||||
|
@ -82,6 +83,18 @@ class CryptGpgExtended extends Crypt_GPG
|
|||
*/
|
||||
public function keyInfo($key)
|
||||
{
|
||||
$version = $this->engine->getVersion();
|
||||
if (version_compare($version, '2.1.23', 'le')) {
|
||||
$importResult = $this->importKey($key);
|
||||
$keys = [];
|
||||
foreach ($importResult['fingerprints'] as $fingerprint) {
|
||||
foreach ($this->getKeys($fingerprint) as $key) {
|
||||
$keys[] = $key;
|
||||
}
|
||||
}
|
||||
return $keys;
|
||||
}
|
||||
|
||||
$input = $this->_prepareInput($key, false, false);
|
||||
|
||||
$output = '';
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
<?php
|
||||
class DistributionGraphTool
|
||||
{
|
||||
private $__user = false;
|
||||
/** @var array */
|
||||
private $__user;
|
||||
private $__json = array();
|
||||
/** @var Event */
|
||||
private $__eventModel;
|
||||
/** @var Organisation */
|
||||
private $__organisationModel;
|
||||
/** @var array */
|
||||
private $__serverList;
|
||||
|
||||
public function construct(Event $eventModel, array $servers, array $user, $extended_view=0)
|
||||
{
|
||||
|
@ -18,12 +21,16 @@ class DistributionGraphTool
|
|||
$this->__extended_view = $extended_view;
|
||||
|
||||
// construct distribution info
|
||||
$this->__json['distributionInfo'] = array();
|
||||
$sgs = $this->__eventModel->SharingGroup->fetchAllAuthorised($this->__user, 'simplified', 1);
|
||||
$sgs = $this->__eventModel->SharingGroup->fetchAllAuthorised($this->__user, 'distribution_graph', true);
|
||||
$this->__json['allSharingGroup'] = h($sgs);
|
||||
$distributionLevels = $this->__eventModel->distributionLevels;
|
||||
foreach ($distributionLevels as $key => $value) {
|
||||
$this->__json['distributionInfo'][$key] = array('key' => h($value), 'desc' => h($this->__eventModel->distributionDescriptions[$key]['formdesc']), 'value' => h($key));
|
||||
|
||||
$this->__json['distributionInfo'] = array();
|
||||
foreach ($this->__eventModel->distributionLevels as $key => $value) {
|
||||
$this->__json['distributionInfo'][$key] = [
|
||||
'key' => h($value),
|
||||
'desc' => h($this->__eventModel->distributionDescriptions[$key]['formdesc']),
|
||||
'value' => h($key)
|
||||
];
|
||||
}
|
||||
$this->__json['distributionInfo'][5] = ""; // inherit event. Will be deleted afterward
|
||||
|
||||
|
@ -72,27 +79,26 @@ class DistributionGraphTool
|
|||
$this->__addAdditionalDistributionInfo(3, "All other communities"); // add current community
|
||||
|
||||
// connected
|
||||
$servers = $this->__serverList;
|
||||
$this->__addAdditionalDistributionInfo(2, "This community"); // add current community
|
||||
foreach ($servers as $server) {
|
||||
foreach ($this->__serverList as $server) {
|
||||
$this->__addAdditionalDistributionInfo(2, $server);
|
||||
}
|
||||
|
||||
// community
|
||||
$orgs = $this->__organisationModel->find('list', array(
|
||||
'fields' => array('name'),
|
||||
'conditions' => array('local' => true)
|
||||
$orgConditions = $this->__organisationModel->createConditions($this->__user);
|
||||
$orgConditions['local'] = true;
|
||||
$orgConditions['id !='] = $this->__user['Organisation']['id'];
|
||||
$orgs = $this->__organisationModel->find('column', array(
|
||||
'fields' => ['name'],
|
||||
'conditions' => $orgConditions,
|
||||
));
|
||||
$thisOrg = $this->__user['Organisation']['name'];
|
||||
$this->__addAdditionalDistributionInfo(1, $thisOrg); // add current community
|
||||
foreach ($orgs as $org) {
|
||||
if ($thisOrg != $org) {
|
||||
$this->__addAdditionalDistributionInfo(1, $org);
|
||||
}
|
||||
foreach ($orgs as $orgName) {
|
||||
$this->__addAdditionalDistributionInfo(1, $orgName);
|
||||
}
|
||||
|
||||
// org only
|
||||
$thisOrg = $this->__user['Organisation']['name'];
|
||||
$this->__addAdditionalDistributionInfo(0, $thisOrg); // add current community
|
||||
}
|
||||
|
||||
|
|
|
@ -77,6 +77,11 @@
|
|||
if (!($check1 && $check2)) {
|
||||
unset($event['Object'][$i]);
|
||||
}
|
||||
foreach($obj['ObjectReference'] as $j => $rel) {
|
||||
if ($rel['deleted']) {
|
||||
unset($event['Object'][$i]['ObjectReference'][$j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($event['Attribute'] as $i => $attr) {
|
||||
$check1 = $this->__satisfy_val_filtering($attr, false);
|
||||
|
@ -519,7 +524,7 @@
|
|||
public function get_reference_data($uuid)
|
||||
{
|
||||
$objectReference = $this->__refModel->ObjectReference->find('all', array(
|
||||
'conditions' => array('ObjectReference.uuid' => $uuid),
|
||||
'conditions' => array('ObjectReference.uuid' => $uuid, 'ObjectReference.deleted' => false),
|
||||
'recursive' => -1,
|
||||
//'fields' => array('ObjectReference.id', 'relationship_type', 'comment', 'referenced_uuid')
|
||||
));
|
||||
|
|
|
@ -46,7 +46,7 @@ class GpgTool
|
|||
*/
|
||||
public function searchGpgKey($search)
|
||||
{
|
||||
$uri = 'https://pgp.circl.lu/pks/lookup?search=' . urlencode($search) . '&op=index&fingerprint=on&options=mr';
|
||||
$uri = 'https://openpgp.circl.lu/pks/lookup?search=' . urlencode($search) . '&op=index&fingerprint=on&options=mr';
|
||||
$response = $this->keyServerLookup($uri);
|
||||
if ($response->code == 404) {
|
||||
return array(); // no keys found
|
||||
|
@ -63,7 +63,7 @@ class GpgTool
|
|||
*/
|
||||
public function fetchGpgKey($fingerprint)
|
||||
{
|
||||
$uri = 'https://pgp.circl.lu/pks/lookup?search=0x' . urlencode($fingerprint) . '&op=get&options=mr';
|
||||
$uri = 'https://openpgp.circl.lu/pks/lookup?search=0x' . urlencode($fingerprint) . '&op=get&options=mr';
|
||||
$response = $this->keyServerLookup($uri);
|
||||
if ($response->code == 404) {
|
||||
return null; // key with given fingerprint not found
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
App::uses('HttpSocketResponse', 'Network/Http');
|
||||
App::uses('HttpSocket', 'Network/Http');
|
||||
|
||||
class HttpClientJsonException extends Exception
|
||||
{
|
||||
/** @var HttpSocketResponse */
|
||||
private $response;
|
||||
|
||||
public function __construct($message, HttpSocketResponseExtended $response, Throwable $previous = null)
|
||||
{
|
||||
$this->response = $response;
|
||||
parent::__construct($message, 0, $previous);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HttpSocketResponse
|
||||
*/
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
}
|
||||
|
||||
class HttpSocketResponseExtended extends HttpSocketResponse
|
||||
{
|
||||
/**
|
||||
* @param string $message
|
||||
* @throws SocketException
|
||||
*/
|
||||
public function parseResponse($message)
|
||||
{
|
||||
parent::parseResponse($message);
|
||||
|
||||
$contentEncoding = $this->getHeader('Content-Encoding');
|
||||
if ($contentEncoding === 'gzip' && function_exists('gzdecode')) {
|
||||
$this->body = gzdecode($this->body);
|
||||
if ($this->body === false) {
|
||||
throw new SocketException("Response should be gzip encoded, but gzip decoding failed.");
|
||||
}
|
||||
} else if ($contentEncoding === 'br' && function_exists('brotli_uncompress')) {
|
||||
$this->body = brotli_uncompress($this->body);
|
||||
if ($this->body === false) {
|
||||
throw new SocketException("Response should be brotli encoded, but brotli decoding failed.");
|
||||
}
|
||||
} else if ($contentEncoding) {
|
||||
throw new SocketException("Remote server returns unsupported content encoding '$contentEncoding'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes JSON string and throws exception if string is not valid JSON.
|
||||
*
|
||||
* @return array
|
||||
* @throws HttpClientJsonException
|
||||
*/
|
||||
public function json()
|
||||
{
|
||||
try {
|
||||
if (defined('JSON_THROW_ON_ERROR')) {
|
||||
// JSON_THROW_ON_ERROR is supported since PHP 7.3
|
||||
$decoded = json_decode($this->body, true, 512, JSON_THROW_ON_ERROR);
|
||||
} else {
|
||||
$decoded = json_decode($this->body, true);
|
||||
if ($decoded === null) {
|
||||
throw new UnexpectedValueException('Could not parse JSON: ' . json_last_error_msg(), json_last_error());
|
||||
}
|
||||
}
|
||||
return $decoded;
|
||||
} catch (Exception $e) {
|
||||
throw new HttpClientJsonException('Could not parse response as JSON.', $this, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supports response compression and also decodes response as JSON
|
||||
*/
|
||||
class HttpSocketExtended extends HttpSocket
|
||||
{
|
||||
public $responseClass = 'HttpSocketResponseExtended';
|
||||
|
||||
public function __construct($config = array())
|
||||
{
|
||||
parent::__construct($config);
|
||||
if (isset($config['compress']) && $config['compress']) {
|
||||
$acceptEncoding = $this->acceptedEncodings();
|
||||
if (!empty($acceptEncoding)) {
|
||||
$this->config['request']['header']['Accept-Encoding'] = implode(', ', $this->acceptedEncodings());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $request
|
||||
* @return HttpSocketResponseExtended
|
||||
*/
|
||||
public function request($request = array())
|
||||
{
|
||||
// Reset last error
|
||||
$this->lastError = [];
|
||||
|
||||
/** @var HttpSocketResponseExtended $response */
|
||||
$response = parent::request($request);
|
||||
if ($response === false) {
|
||||
throw new InvalidArgumentException("Invalid argument provided.");
|
||||
}
|
||||
// Convert connection timeout to SocketException
|
||||
if (!empty($this->lastError)) {
|
||||
throw new SocketException($this->lastError['msg']);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns accepted content encodings (compression algorithms)
|
||||
* @return string[]
|
||||
*/
|
||||
private function acceptedEncodings()
|
||||
{
|
||||
$supportedEncoding = [];
|
||||
// Enable brotli compressed responses if PHP has 'brotli_uncompress' method
|
||||
if (function_exists('brotli_uncompress')) {
|
||||
$supportedEncoding[] = 'br';
|
||||
}
|
||||
// Enable gzipped responses if PHP has 'gzdecode' method
|
||||
if (function_exists('gzdecode')) {
|
||||
$supportedEncoding[] = 'gzip';
|
||||
}
|
||||
return $supportedEncoding;
|
||||
}
|
||||
}
|
|
@ -105,18 +105,19 @@ class JSONConverterTool
|
|||
}
|
||||
|
||||
/**
|
||||
* Event to JSON stream convertor.
|
||||
* Event to JSON convertor, but that is intended for machine to machine communication
|
||||
* @param array $event
|
||||
* @return Generator<string>
|
||||
*/
|
||||
public function streamConvert(array $event)
|
||||
{
|
||||
$event = $this->convert($event, false, true);
|
||||
|
||||
// Fast and inaccurate way how to check if event is too big for to convert in one call. This can be changed in future.
|
||||
$isBigEvent = (isset($event['Event']['Attribute']) ? count($event['Event']['Attribute']) : 0) +
|
||||
(isset($event['Event']['Object']) ? count($event['Event']['Object']) : 0) > 100;
|
||||
if (!$isBigEvent) {
|
||||
yield json_encode($event, JSON_PRETTY_PRINT);
|
||||
yield json_encode($event, JSON_UNESCAPED_UNICODE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -127,11 +128,11 @@ class JSONConverterTool
|
|||
yield ($firstKey === $key ? '' : ',') . json_encode($key) . ":[";
|
||||
$firstInnerKey = key($value);
|
||||
foreach ($value as $i => $attribute) {
|
||||
yield ($firstInnerKey === $i ? '' : ',') . json_encode($attribute);
|
||||
yield ($firstInnerKey === $i ? '' : ',') . json_encode($attribute, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
yield "]";
|
||||
} else {
|
||||
yield ($firstKey === $key ? '' : ',') . json_encode($key) . ":" . json_encode($value);
|
||||
yield ($firstKey === $key ? '' : ',') . json_encode($key) . ":" . json_encode($value, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
}
|
||||
if (isset($event['errors'])) {
|
||||
|
|
|
@ -320,7 +320,7 @@ class SendEmail
|
|||
if (!$this->gpg) {
|
||||
throw new SendEmailException("GPG encryption is enabled, but GPG is not configured.");
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
$fingerprint = $this->importAndValidateGpgPublicKey($params['gpgkey']);
|
||||
} catch (Crypt_GPG_NoDataException $e) {
|
||||
|
@ -415,7 +415,7 @@ class SendEmail
|
|||
if (!$this->gpg) {
|
||||
throw new SendEmailException("GPG signing is enabled, but GPG is not initialized. Check debug log why GPG could not be initialized.");
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
$fingerprint = $this->importAndValidateGpgPublicKey($user['User']['gpgkey']);
|
||||
} catch (Crypt_GPG_NoDataException $e) {
|
||||
|
@ -446,7 +446,9 @@ class SendEmail
|
|||
}
|
||||
|
||||
if (!$canEncryptGpg && $canEncryptSmime) {
|
||||
$this->signBySmime($email);
|
||||
if (!empty(Configure::read('SMIME.cert_public_sign')) && !empty(Configure::read('SMIME.key_sign'))) {
|
||||
$this->signBySmime($email);
|
||||
}
|
||||
$this->encryptBySmime($email, $user['User']['certif_public']);
|
||||
$encrypted = true;
|
||||
}
|
||||
|
@ -727,14 +729,14 @@ class SendEmail
|
|||
}
|
||||
|
||||
list($inputFile, $outputFile) = $this->createInputOutputFiles($body);
|
||||
$result = openssl_pkcs7_sign($inputFile->pwd(), $outputFile->pwd(), $certPublicSign, $keySign, array(), 0);
|
||||
$result = openssl_pkcs7_sign($inputFile->pwd(), $outputFile->pwd(), $certPublicSign, $keySign, array(), PKCS7_DETACHED);
|
||||
$inputFile->delete();
|
||||
|
||||
if ($result) {
|
||||
$data = $outputFile->read();
|
||||
$outputFile->delete();
|
||||
$parts = explode("\n\n", $data);
|
||||
return $parts[1] . "\n";
|
||||
return $parts[4] . "\n";
|
||||
|
||||
} else {
|
||||
$outputFile->delete();
|
||||
|
|
|
@ -2,10 +2,17 @@
|
|||
|
||||
class SyncTool
|
||||
{
|
||||
// take a server as parameter and return a HttpSocket object using the ssl options defined in the server settings
|
||||
/**
|
||||
* Take a server as parameter and return a HttpSocket object using the ssl options defined in the server settings
|
||||
* @param array|null $server
|
||||
* @param false $timeout
|
||||
* @param string $model
|
||||
* @return HttpSocketExtended
|
||||
* @throws Exception
|
||||
*/
|
||||
public function setupHttpSocket($server = null, $timeout = false, $model = 'Server')
|
||||
{
|
||||
$params = array();
|
||||
$params = ['compress' => true];
|
||||
if (!empty($server)) {
|
||||
if (!empty($server[$model]['cert_file'])) {
|
||||
$params['ssl_cafile'] = APP . "files" . DS . "certs" . DS . $server[$model]['id'] . '.pem';
|
||||
|
@ -33,12 +40,12 @@ class SyncTool
|
|||
|
||||
public function setupHttpSocketFeed($feed = null)
|
||||
{
|
||||
return $this->setupHttpSocket();
|
||||
return $this->createHttpSocket(['compress' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
* @return HttpSocket
|
||||
* @return HttpSocketExtended
|
||||
* @throws Exception
|
||||
*/
|
||||
public function createHttpSocket($params = array())
|
||||
|
@ -52,8 +59,8 @@ class SyncTool
|
|||
$params['ssl_cafile'] = $caPath;
|
||||
}
|
||||
|
||||
App::uses('HttpSocket', 'Network/Http');
|
||||
$HttpSocket = new HttpSocket($params);
|
||||
App::uses('HttpSocketExtended', 'Tools');
|
||||
$HttpSocket = new HttpSocketExtended($params);
|
||||
$proxy = Configure::read('Proxy');
|
||||
if (empty($params['skip_proxy']) && isset($proxy['host']) && !empty($proxy['host'])) {
|
||||
$HttpSocket->configProxy($proxy['host'], $proxy['port'], $proxy['method'], $proxy['user'], $proxy['password']);
|
||||
|
|
|
@ -14,7 +14,7 @@ class TmpFileTool
|
|||
public function __construct($maxInMemory = null)
|
||||
{
|
||||
if ($maxInMemory === null) {
|
||||
$maxInMemory = 2 * 1024 * 1024;
|
||||
$maxInMemory = 5 * 1024 * 1024;
|
||||
}
|
||||
$this->tmpfile = fopen("php://temp/maxmemory:$maxInMemory", "w+");
|
||||
if ($this->tmpfile === false) {
|
||||
|
@ -68,7 +68,7 @@ class TmpFileTool
|
|||
}
|
||||
|
||||
/**
|
||||
* Get one line from file parsed as CSV.
|
||||
* Returns generator of parsed CSV line from file.
|
||||
*
|
||||
* @param string $delimiter
|
||||
* @param string $enclosure
|
||||
|
@ -76,7 +76,7 @@ class TmpFileTool
|
|||
* @return Generator
|
||||
* @throws Exception
|
||||
*/
|
||||
public function csv($delimiter = ',', $enclosure = '"', $escape = "\\")
|
||||
public function intoParsedCsv($delimiter = ',', $enclosure = '"', $escape = "\\")
|
||||
{
|
||||
$this->rewind();
|
||||
$line = 0;
|
||||
|
@ -88,15 +88,16 @@ class TmpFileTool
|
|||
$line++;
|
||||
yield $result;
|
||||
}
|
||||
fclose($this->tmpfile);
|
||||
$this->tmpfile = null;
|
||||
$this->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns generator of line from file.
|
||||
*
|
||||
* @return Generator
|
||||
* @throws Exception
|
||||
*/
|
||||
public function lines()
|
||||
public function intoLines()
|
||||
{
|
||||
$this->rewind();
|
||||
while (!feof($this->tmpfile)) {
|
||||
|
@ -106,24 +107,64 @@ class TmpFileTool
|
|||
}
|
||||
yield $result;
|
||||
}
|
||||
fclose($this->tmpfile);
|
||||
$this->tmpfile = null;
|
||||
$this->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $chunkSize In bytes
|
||||
* @return Generator
|
||||
* @throws Exception
|
||||
*/
|
||||
public function intoChunks($chunkSize = 8192)
|
||||
{
|
||||
$this->rewind();
|
||||
while (!feof($this->tmpfile)) {
|
||||
$result = fread($this->tmpfile, $chunkSize);
|
||||
if ($result === false) {
|
||||
throw new Exception('Could not read from temporary file.');
|
||||
}
|
||||
yield $result;
|
||||
}
|
||||
$this->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function finish()
|
||||
public function intoString()
|
||||
{
|
||||
$this->rewind();
|
||||
$final = stream_get_contents($this->tmpfile);
|
||||
if ($final === false) {
|
||||
$string = stream_get_contents($this->tmpfile);
|
||||
if ($string === false) {
|
||||
throw new Exception('Could not read from temporary file.');
|
||||
}
|
||||
fclose($this->tmpfile);
|
||||
$this->tmpfile = null;
|
||||
return $final;
|
||||
$this->close();
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass data to output.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function intoOutput()
|
||||
{
|
||||
$this->rewind();
|
||||
if (fpassthru($this->tmpfile) === false) {
|
||||
throw new Exception('Could not pass temporary file to output.');
|
||||
}
|
||||
$this->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @throws Exception
|
||||
*/
|
||||
public function size()
|
||||
{
|
||||
$this->isOpen();
|
||||
return fstat($this->tmpfile)['size'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -132,7 +173,30 @@ class TmpFileTool
|
|||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->finish();
|
||||
return $this->intoString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if ($this->tmpfile) {
|
||||
$result = fclose($this->tmpfile);
|
||||
$this->tmpfile = null;
|
||||
return $result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
private function isOpen()
|
||||
{
|
||||
if ($this->tmpfile === null) {
|
||||
throw new Exception('Temporary file is already closed.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -142,6 +206,7 @@ class TmpFileTool
|
|||
*/
|
||||
private function rewind()
|
||||
{
|
||||
$this->isOpen();
|
||||
if (fseek($this->tmpfile, 0) === -1) {
|
||||
throw new Exception('Could not seek to start of temporary file.');
|
||||
}
|
||||
|
|
|
@ -198,13 +198,18 @@ class XMLConverterTool
|
|||
$field = str_replace($this->__toEscape, $this->__escapeWith, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $input
|
||||
* @param false $mispVersion
|
||||
* @return Generator
|
||||
*/
|
||||
public function frameCollection($input, $mispVersion = false)
|
||||
{
|
||||
$result = '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL . '<response>' . PHP_EOL;
|
||||
$result .= $input;
|
||||
yield '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL . '<response>' . PHP_EOL;
|
||||
yield $input . PHP_EOL;
|
||||
if ($mispVersion) {
|
||||
$result .= '<xml_version>' . $mispVersion . '</xml_version>';
|
||||
yield '<xml_version>' . $mispVersion . '</xml_version>';
|
||||
}
|
||||
return $result . '</response>' . PHP_EOL;
|
||||
yield '</response>' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit d0c51b37422d0d2c99be74045d4439a674259308
|
||||
Subproject commit cf14e6546ec44e3369e3531add11fdb946656280
|
|
@ -55,6 +55,7 @@ class AppModel extends Model
|
|||
parent::__construct($id, $table, $ds);
|
||||
|
||||
$this->name = get_class($this);
|
||||
$this->findMethods['column'] = true;
|
||||
}
|
||||
|
||||
// deprecated, use $db_changes
|
||||
|
@ -87,7 +88,7 @@ class AppModel extends Model
|
|||
45 => false, 46 => false, 47 => false, 48 => false, 49 => false, 50 => false,
|
||||
51 => false, 52 => false, 53 => false, 54 => false, 55 => false, 56 => false,
|
||||
57 => false, 58 => false, 59 => false, 60 => false, 61 => false, 62 => false,
|
||||
63 => true, 64 => false
|
||||
63 => true, 64 => false, 65 => false
|
||||
);
|
||||
|
||||
public $advanced_updates_description = array(
|
||||
|
@ -1557,6 +1558,15 @@ class AppModel extends Model
|
|||
KEY `org_id` (`org_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
|
||||
break;
|
||||
case 65:
|
||||
$sqlArray[] = "CREATE TABLE IF NOT EXISTS `correlation_exclusions` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`value` text NOT NULL,
|
||||
`from_json` tinyint(1) default 0,
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `value` (`value`(255))
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
|
||||
break;
|
||||
case 'fixNonEmptySharingGroupID':
|
||||
$sqlArray[] = 'UPDATE `events` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
|
||||
$sqlArray[] = 'UPDATE `attributes` SET `sharing_group_id` = 0 WHERE `distribution` != 4;';
|
||||
|
@ -1627,10 +1637,8 @@ class AppModel extends Model
|
|||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
$now = new DateTime();
|
||||
// switch MISP instance live to false
|
||||
if ($liveOff) {
|
||||
$this->Server = Classregistry::init('Server');
|
||||
|
@ -1643,7 +1651,7 @@ class AppModel extends Model
|
|||
$this->__setUpdateProgress(0, $total_update_count, $command);
|
||||
$str_index_array = array();
|
||||
foreach($indexArray as $toIndex) {
|
||||
$str_index_array[] = __('Indexing ') . sprintf('%s -> %s', $toIndex[0], $toIndex[1]);
|
||||
$str_index_array[] = __('Indexing %s -> %s', $toIndex[0], $toIndex[1]);
|
||||
}
|
||||
$this->__setUpdateCmdMessages(array_merge($sqlArray, $str_index_array));
|
||||
$flagStop = false;
|
||||
|
@ -1679,10 +1687,10 @@ class AppModel extends Model
|
|||
'email' => 'SYSTEM',
|
||||
'action' => 'update_database',
|
||||
'user_id' => 0,
|
||||
'title' => __('Successfuly executed the SQL query for ') . $command,
|
||||
'title' => __('Successfully executed the SQL query for ') . $command,
|
||||
'change' => sprintf(__('The executed SQL query was: %s'), $sql)
|
||||
));
|
||||
$this->__setUpdateResMessages($i, sprintf(__('Successfuly executed the SQL query for %s'), $command));
|
||||
$this->__setUpdateResMessages($i, sprintf(__('Successfully executed the SQL query for %s'), $command));
|
||||
} catch (Exception $e) {
|
||||
$errorMessage = $e->getMessage();
|
||||
$this->Log->create();
|
||||
|
@ -1736,14 +1744,13 @@ class AppModel extends Model
|
|||
}
|
||||
}
|
||||
}
|
||||
$this->__setUpdateProgress(count($sqlArray)+count($indexArray), false);
|
||||
$this->__setUpdateProgress(count($sqlArray) + count($indexArray), false);
|
||||
}
|
||||
if ($clean) {
|
||||
$this->cleanCacheFiles();
|
||||
}
|
||||
if ($liveOff) {
|
||||
$liveSetting = 'MISP.live';
|
||||
$this->Server->serverSettingsSaveValue($liveSetting, true);
|
||||
$this->Server->serverSettingsSaveValue('MISP.live', true);
|
||||
}
|
||||
if (!$flagStop && $errorCount == 0) {
|
||||
$this->__postUpdate($command);
|
||||
|
@ -2132,11 +2139,20 @@ class AppModel extends Model
|
|||
}
|
||||
}
|
||||
if ($requiresLogout) {
|
||||
$this->updateDatabase('destroyAllSessions');
|
||||
$this->refreshSessions();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update date_modified for all users, this will ensure that all users will refresh their session data.
|
||||
*/
|
||||
private function refreshSessions()
|
||||
{
|
||||
$this->User = ClassRegistry::init('User');
|
||||
$this->User->updateAll(['date_modified' => time()]);
|
||||
}
|
||||
|
||||
private function __setUpdateProgress($current, $total=false, $toward_db_version=false)
|
||||
{
|
||||
$updateProgress = $this->getUpdateProgress();
|
||||
|
@ -2735,7 +2751,7 @@ class AppModel extends Model
|
|||
{
|
||||
static $versionArray;
|
||||
if ($versionArray === null) {
|
||||
$file = new File(ROOT . DS . 'VERSION.json', true);
|
||||
$file = new File(ROOT . DS . 'VERSION.json');
|
||||
$versionArray = $this->jsonDecode($file->read());
|
||||
$file->close();
|
||||
}
|
||||
|
@ -3011,6 +3027,66 @@ class AppModel extends Model
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find method that allows to fetch just one column from database.
|
||||
* @param $state
|
||||
* @param $query
|
||||
* @param array $results
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function _findColumn($state, $query, $results = array())
|
||||
{
|
||||
if ($state === 'before') {
|
||||
if (count($query['fields']) === 1) {
|
||||
if (strpos($query['fields'][0], '.') === false) {
|
||||
$query['fields'][0] = $this->alias . '.' . $query['fields'][0];
|
||||
}
|
||||
|
||||
$query['column'] = $query['fields'][0];
|
||||
if (isset($query['unique']) && $query['unique']) {
|
||||
$query['fields'] = array("DISTINCT {$query['fields'][0]}");
|
||||
} else {
|
||||
$query['fields'] = array($query['fields'][0]);
|
||||
}
|
||||
} else {
|
||||
throw new Exception("Invalid number of column, expected one, " . count($query['fields']) . " given");
|
||||
}
|
||||
|
||||
if (!isset($query['recursive'])) {
|
||||
$query['recursive'] = -1;
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
// Faster version of `Hash::extract`
|
||||
foreach (explode('.', $query['column']) as $part) {
|
||||
$results = array_column($results, $part);
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param AppModel $model
|
||||
* @param array $conditions
|
||||
*/
|
||||
public function addCountField($field, AppModel $model, array $conditions)
|
||||
{
|
||||
$db = $this->getDataSource();
|
||||
$subQuery = $db->buildStatement(
|
||||
array(
|
||||
'fields' => ['COUNT(*)'],
|
||||
'table' => $db->fullTableName($model),
|
||||
'alias' => $model->alias,
|
||||
'conditions' => $conditions,
|
||||
),
|
||||
$model
|
||||
);
|
||||
$this->virtualFields[$field] = $subQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log exception with backtrace and with nested exceptions.
|
||||
*
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -42,22 +42,38 @@ class AuthKey extends AppModel
|
|||
$this->data['AuthKey']['authkey_end'] = substr($authkey, -4);
|
||||
$this->data['AuthKey']['authkey_raw'] = $authkey;
|
||||
$this->authkey_raw = $authkey;
|
||||
|
||||
$validity = Configure::read('Security.advanced_authkeys_validity');
|
||||
if (empty($this->data['AuthKey']['expiration'])) {
|
||||
$this->data['AuthKey']['expiration'] = 0;
|
||||
$this->data['AuthKey']['expiration'] = $validity ? strtotime("+$validity days") : 0;
|
||||
} else {
|
||||
$this->data['AuthKey']['expiration'] = strtotime($this->data['AuthKey']['expiration']);
|
||||
$expiration = is_numeric($this->data['AuthKey']['expiration']) ?
|
||||
(int)$this->data['AuthKey']['expiration'] :
|
||||
strtotime($this->data['AuthKey']['expiration']);
|
||||
|
||||
if ($expiration === false) {
|
||||
$this->invalidate('expiration', __('Expiration must be in YYYY-MM-DD format.'));
|
||||
}
|
||||
if ($validity && $expiration > strtotime("+$validity days")) {
|
||||
$this->invalidate('expiration', __('Maximal key validity is %s days.', $validity));
|
||||
}
|
||||
$this->data['AuthKey']['expiration'] = $expiration;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $authkey
|
||||
* @return array|false
|
||||
*/
|
||||
public function getAuthUserByAuthKey($authkey)
|
||||
{
|
||||
$start = substr($authkey, 0, 4);
|
||||
$end = substr($authkey, -4);
|
||||
$existing_authkeys = $this->find('all', [
|
||||
'recursive' => -1,
|
||||
'fields' => ['authkey', 'user_id'],
|
||||
'fields' => ['id', 'authkey', 'user_id', 'expiration'],
|
||||
'conditions' => [
|
||||
'OR' => [
|
||||
'expiration >' => time(),
|
||||
|
@ -70,7 +86,12 @@ class AuthKey extends AppModel
|
|||
$passwordHasher = $this->getHasher();
|
||||
foreach ($existing_authkeys as $existing_authkey) {
|
||||
if ($passwordHasher->check($authkey, $existing_authkey['AuthKey']['authkey'])) {
|
||||
return $this->User->getAuthUser($existing_authkey['AuthKey']['user_id']);
|
||||
$user = $this->User->getAuthUser($existing_authkey['AuthKey']['user_id']);
|
||||
if ($user) {
|
||||
$user['authkey_id'] = $existing_authkey['AuthKey']['id'];
|
||||
$user['authkey_expiration'] = $existing_authkey['AuthKey']['expiration'];
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -105,6 +126,67 @@ class AuthKey extends AppModel
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getKeyUsage($id)
|
||||
{
|
||||
$redis = $this->setupRedisWithException();
|
||||
$data = $redis->hGetAll("misp:authkey_usage:$id");
|
||||
|
||||
$output = [];
|
||||
$uniqueIps = [];
|
||||
foreach ($data as $key => $count) {
|
||||
list($date, $ip) = explode(':', $key);
|
||||
$uniqueIps[$ip] = true;
|
||||
if (isset($output[$date])) {
|
||||
$output[$date] += $count;
|
||||
} else {
|
||||
$output[$date] = $count;
|
||||
}
|
||||
}
|
||||
// Data from redis are not sorted
|
||||
ksort($output);
|
||||
|
||||
$lastUsage = $redis->get("misp:authkey_last_usage:$id");
|
||||
$lastUsage = $lastUsage === false ? null : (int)$lastUsage;
|
||||
|
||||
return [$output, $lastUsage, count($uniqueIps)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $ids
|
||||
* @return array<DateTime|null>
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getLastUsageForKeys(array $ids)
|
||||
{
|
||||
$redis = $this->setupRedisWithException();
|
||||
$keys = array_map(function($id) {
|
||||
return "misp:authkey_last_usage:$id";
|
||||
}, $ids);
|
||||
$lastUsages = $redis->mget($keys);
|
||||
$output = [];
|
||||
foreach (array_values($ids) as $i => $id) {
|
||||
$output[$id] = $lastUsages[$i] === false ? null : (int)$lastUsages[$i];
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* When key is deleted, update after `date_modified` for user that was assigned to that key, so session data
|
||||
* will be realoaded and canceled.
|
||||
* @see AppController::_refreshAuth
|
||||
*/
|
||||
public function afterDelete()
|
||||
{
|
||||
parent::afterDelete();
|
||||
$userId = $this->data['AuthKey']['user_id'];
|
||||
$this->User->updateAll(['date_modified' => time()], ['User.id' => $userId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AbstractPasswordHasher
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
App::uses('AppModel', 'Model');
|
||||
App::uses('RandomTool', 'Tools');
|
||||
|
||||
class CorrelationExclusion extends AppModel
|
||||
{
|
||||
public $recursive = -1;
|
||||
|
||||
public $key = 'misp:correlation_exclusions';
|
||||
|
||||
public $actsAs = array(
|
||||
'SysLogLogable.SysLogLogable' => array(
|
||||
'userModel' => 'User',
|
||||
'userKey' => 'user_id',
|
||||
'change' => 'full'),
|
||||
'Containable',
|
||||
);
|
||||
|
||||
public function afterSave($created, $options = array())
|
||||
{
|
||||
$this->cacheValues();
|
||||
}
|
||||
|
||||
public function afterDelete()
|
||||
{
|
||||
$this->cacheValues();
|
||||
}
|
||||
|
||||
public function cacheValues()
|
||||
{
|
||||
try {
|
||||
$redis = $this->setupRedisWithException();
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
$redis->del($this->key);
|
||||
$exclusions = $this->find('column', [
|
||||
'fields' => ['value']
|
||||
]);
|
||||
$redis->sAddArray($this->key, $exclusions);
|
||||
}
|
||||
|
||||
public function cleanRouter($user)
|
||||
{
|
||||
if (Configure::read('MISP.background_jobs')) {
|
||||
$this->Job = ClassRegistry::init('Job');
|
||||
$this->Job->create();
|
||||
$data = [
|
||||
'worker' => 'default',
|
||||
'job_type' => 'clean_correlation_exclusions',
|
||||
'job_input' => '',
|
||||
'status' => 0,
|
||||
'retries' => 0,
|
||||
'org' => $user['Organisation']['name'],
|
||||
'message' => __('Cleaning up excluded correlations.'),
|
||||
];
|
||||
$this->Job->save($data);
|
||||
$jobId = $this->Job->id;
|
||||
$process_id = CakeResque::enqueue(
|
||||
'default',
|
||||
'AdminShell',
|
||||
['cleanExcludedCorrelations', $jobId],
|
||||
true
|
||||
);
|
||||
$this->Job->saveField('process_id', $process_id);
|
||||
$message = __('Cleanup queued for background execution.');
|
||||
} else {
|
||||
$this->clean();
|
||||
}
|
||||
}
|
||||
|
||||
public function clean($jobId = false)
|
||||
{
|
||||
try {
|
||||
$redis = $this->setupRedisWithException();
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
$this->Correlation = ClassRegistry::init('Correlation');
|
||||
$exclusions = $redis->sMembers($this->key);
|
||||
$conditions = [];
|
||||
$exclusions = array_chunk($exclusions, 100);
|
||||
if ($jobId) {
|
||||
$this->Job = ClassRegistry::init('Job');
|
||||
$this->Job->id = $jobId;
|
||||
}
|
||||
$total = count($exclusions);
|
||||
foreach ($exclusions as $exclusion_chunk) {
|
||||
$i = 0;
|
||||
foreach ($exclusion_chunk as $exclusion) {
|
||||
$i += 1;
|
||||
if (!empty($exclusion)) {
|
||||
if ($exclusion[0] === '%' || substr($exclusion, -1) === '%') {
|
||||
$conditions['OR'][] = ['Correlation.value LIKE' => $exclusion];
|
||||
} else {
|
||||
$conditions['OR']['Correlation.value'][] = $exclusion;
|
||||
}
|
||||
}
|
||||
if (!empty($conditions)) {
|
||||
$this->Correlation->deleteAll($conditions);
|
||||
}
|
||||
if ($i % 100 === 0) {
|
||||
$this->Job->saveProgress($jobId, 'Chunk ' . $i . '/' . $total, $i * 100 / $total);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,8 @@ App::uses('TmpFileTool', 'Tools');
|
|||
* @property Attribute $Attribute
|
||||
* @property ShadowAttribute $ShadowAttribute
|
||||
* @property EventTag $EventTag
|
||||
* @property SharingGroup $SharingGroup
|
||||
* @property ThreatLevel $ThreatLevel
|
||||
*/
|
||||
class Event extends AppModel
|
||||
{
|
||||
|
@ -61,120 +63,7 @@ class Event extends AppModel
|
|||
|
||||
public $shortDist = array(0 => 'Organisation', 1 => 'Community', 2 => 'Connected', 3 => 'All', 4 => ' sharing Group');
|
||||
|
||||
public $export_types = array(
|
||||
'json' => array(
|
||||
'extension' => '.json',
|
||||
'type' => 'JSON',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 0,
|
||||
'params' => array('includeAttachments' => 1, 'ignore' => 1, 'returnFormat' => 'json'),
|
||||
'description' => 'Click this to download all events and attributes that you have access to in MISP JSON format.',
|
||||
),
|
||||
'xml' => array(
|
||||
'extension' => '.xml',
|
||||
'type' => 'XML',
|
||||
'scope' => 'Event',
|
||||
'params' => array('includeAttachments' => 1, 'ignore' => 1, 'returnFormat' => 'xml'),
|
||||
'requiresPublished' => 0,
|
||||
'description' => 'Click this to download all events and attributes that you have access to in MISP XML format.',
|
||||
),
|
||||
'csv_sig' => array(
|
||||
'extension' => '.csv',
|
||||
'type' => 'CSV_Sig',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('published' => 1, 'to_ids' => 1, 'returnFormat' => 'csv'),
|
||||
'description' => 'Click this to download all attributes that are indicators and that you have access to <small>(except file attachments)</small> in CSV format.',
|
||||
),
|
||||
'csv_all' => array(
|
||||
'extension' => '.csv',
|
||||
'type' => 'CSV_All',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 0,
|
||||
'params' => array('ignore' => 1, 'returnFormat' => 'csv'),
|
||||
'description' => 'Click this to download all attributes that you have access to <small>(except file attachments)</small> in CSV format.',
|
||||
),
|
||||
'suricata' => array(
|
||||
'extension' => '.rules',
|
||||
'type' => 'Suricata',
|
||||
'scope' => 'Attribute',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'suricata'),
|
||||
'description' => 'Click this to download all network related attributes that you have access to under the Suricata rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a allowedlist containing host, domain name and IP numbers to exclude from the NIDS export.',
|
||||
),
|
||||
'snort' => array(
|
||||
'extension' => '.rules',
|
||||
'type' => 'Snort',
|
||||
'scope' => 'Attribute',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'snort'),
|
||||
'description' => 'Click this to download all network related attributes that you have access to under the Snort rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a allowedlist containing host, domain name and IP numbers to exclude from the NIDS export.',
|
||||
),
|
||||
'bro' => array(
|
||||
'extension' => '.intel',
|
||||
'type' => 'Bro',
|
||||
'scope' => 'Attribute',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'bro'),
|
||||
'description' => 'Click this to download all network related attributes that you have access to under the Bro rule format. Only published events and attributes marked as IDS Signature are exported. Administration is able to maintain a allowedlist containing host, domain name and IP numbers to exclude from the NIDS export.',
|
||||
),
|
||||
'stix' => array(
|
||||
'extension' => '.xml',
|
||||
'type' => 'STIX',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'stix', 'includeAttachments' => 1),
|
||||
'description' => 'Click this to download a STIX document containing the STIX version of all events and attributes that you have access to.'
|
||||
),
|
||||
'stix-json' => array(
|
||||
'extension' => '.json',
|
||||
'type' => 'STIX',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'stix', 'includeAttachments' => 1),
|
||||
'description' => 'Click this to download a STIX document containing the STIX version of all events and attributes that you have access to.'
|
||||
),
|
||||
'stix2' => array(
|
||||
'extension' => '.json',
|
||||
'type' => 'STIX2',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'stix2', 'includeAttachments' => 1),
|
||||
'description' => 'Click this to download a STIX2 document containing the STIX2 version of all events and attributes that you have access to.'
|
||||
),
|
||||
'rpz' => array(
|
||||
'extension' => '.txt',
|
||||
'type' => 'RPZ',
|
||||
'scope' => 'Attribute',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'rpz'),
|
||||
'description' => 'Click this to download an RPZ Zone file generated from all ip-src/ip-dst, hostname, domain attributes. This can be useful for DNS level firewalling. Only published events and attributes marked as IDS Signature are exported.'
|
||||
),
|
||||
'text' => array(
|
||||
'extension' => '.txt',
|
||||
'type' => 'TEXT',
|
||||
'scope' => 'Attribute',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'text', 'includeAttachments' => 1),
|
||||
'description' => 'Click on one of the buttons below to download all the attributes with the matching type. This list can be used to feed forensic software when searching for susipicious files. Only published events and attributes marked as IDS Signature are exported.'
|
||||
),
|
||||
'yara' => array(
|
||||
'extension' => '.yara',
|
||||
'type' => 'Yara',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'yara'),
|
||||
'description' => 'Click this to download Yara rules generated from all relevant attributes.'
|
||||
),
|
||||
'yara-json' => array(
|
||||
'extension' => '.json',
|
||||
'type' => 'Yara',
|
||||
'scope' => 'Event',
|
||||
'requiresPublished' => 1,
|
||||
'params' => array('returnFormat' => 'yara-json'),
|
||||
'description' => 'Click this to download Yara rules generated from all relevant attributes. Rules are returned in a JSON format with information about origin (generated or parsed) and validity.'
|
||||
),
|
||||
);
|
||||
public $export_types = [];
|
||||
|
||||
public $validFormats = array(
|
||||
'attack' => array('html', 'AttackExport', 'html'),
|
||||
|
@ -653,10 +542,10 @@ class Event extends AppModel
|
|||
}
|
||||
}
|
||||
|
||||
public function attachtagsToEvents($events)
|
||||
public function attachTagsToEvents(array $events)
|
||||
{
|
||||
$tagsToFetch = array();
|
||||
foreach ($events as $k => $event) {
|
||||
foreach ($events as $event) {
|
||||
if (!empty($event['EventTag'])) {
|
||||
foreach ($event['EventTag'] as $et) {
|
||||
$tagsToFetch[$et['tag_id']] = $et['tag_id'];
|
||||
|
@ -668,11 +557,11 @@ class Event extends AppModel
|
|||
'recursive' => -1,
|
||||
'order' => false
|
||||
));
|
||||
$tags = Set::combine($tags, '{n}.Tag.id', '{n}');
|
||||
$tags = array_column(array_column($tags, 'Tag'), null, 'id');
|
||||
foreach ($events as $k => $event) {
|
||||
if (!empty($event['EventTag'])) {
|
||||
foreach ($event['EventTag'] as $k2 => $et) {
|
||||
$events[$k]['EventTag'][$k2]['Tag'] = $tags[$et['tag_id']]['Tag'];
|
||||
$events[$k]['EventTag'][$k2]['Tag'] = $tags[$et['tag_id']];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -687,7 +576,7 @@ class Event extends AppModel
|
|||
$sgids = array(-1);
|
||||
}
|
||||
$this->Correlation = ClassRegistry::init('Correlation');
|
||||
$eventIds = Set::extract('/Event/id', $events);
|
||||
$eventIds = array_column(array_column($events, 'Event'), 'id');
|
||||
$conditionsCorrelation = $this->__buildEventConditionsCorrelation($user, $eventIds, $sgids);
|
||||
$correlations = $this->Correlation->find('all', array(
|
||||
'fields' => array('Correlation.1_event_id', 'count(distinct(Correlation.event_id)) as count'),
|
||||
|
@ -697,14 +586,14 @@ class Event extends AppModel
|
|||
));
|
||||
$correlations = Hash::combine($correlations, '{n}.Correlation.1_event_id', '{n}.0.count');
|
||||
foreach ($events as &$event) {
|
||||
$event['Event']['correlation_count'] = (isset($correlations[$event['Event']['id']])) ? $correlations[$event['Event']['id']] : 0;
|
||||
$event['Event']['correlation_count'] = isset($correlations[$event['Event']['id']]) ? $correlations[$event['Event']['id']] : 0;
|
||||
}
|
||||
return $events;
|
||||
}
|
||||
|
||||
public function attachSightingsCountToEvents($user, $events)
|
||||
{
|
||||
$eventIds = Set::extract('/Event/id', $events);
|
||||
$eventIds = array_column(array_column($events, 'Event'), 'id');
|
||||
$this->Sighting = ClassRegistry::init('Sighting');
|
||||
$sightings = $this->Sighting->find('all', array(
|
||||
'fields' => array('Sighting.event_id', 'count(distinct(Sighting.id)) as count'),
|
||||
|
@ -714,14 +603,14 @@ class Event extends AppModel
|
|||
));
|
||||
$sightings = Hash::combine($sightings, '{n}.Sighting.event_id', '{n}.0.count');
|
||||
foreach ($events as $key => $event) {
|
||||
$events[$key]['Event']['sightings_count'] = (isset($sightings[$event['Event']['id']])) ? $sightings[$event['Event']['id']] : 0;
|
||||
$events[$key]['Event']['sightings_count'] = isset($sightings[$event['Event']['id']]) ? $sightings[$event['Event']['id']] : 0;
|
||||
}
|
||||
return $events;
|
||||
}
|
||||
|
||||
public function attachProposalsCountToEvents($user, $events)
|
||||
{
|
||||
$eventIds = Set::extract('/Event/id', $events);
|
||||
$eventIds = array_column(array_column($events, 'Event'), 'id');
|
||||
$proposals = $this->ShadowAttribute->find('all', array(
|
||||
'fields' => array('ShadowAttribute.event_id', 'count(distinct(ShadowAttribute.id)) as count'),
|
||||
'conditions' => array('event_id' => $eventIds, 'deleted' => 0),
|
||||
|
@ -730,14 +619,14 @@ class Event extends AppModel
|
|||
));
|
||||
$proposals = Hash::combine($proposals, '{n}.ShadowAttribute.event_id', '{n}.0.count');
|
||||
foreach ($events as $key => $event) {
|
||||
$events[$key]['Event']['proposals_count'] = (isset($proposals[$event['Event']['id']])) ? $proposals[$event['Event']['id']] : 0;
|
||||
$events[$key]['Event']['proposals_count'] = isset($proposals[$event['Event']['id']]) ? $proposals[$event['Event']['id']] : 0;
|
||||
}
|
||||
return $events;
|
||||
}
|
||||
|
||||
public function attachDiscussionsCountToEvents($user, $events)
|
||||
{
|
||||
$eventIds = Set::extract('/Event/id', $events);
|
||||
$eventIds = array_column(array_column($events, 'Event'), 'id');
|
||||
$this->Thread = ClassRegistry::init('Thread');
|
||||
$threads = $this->Thread->find('list', array(
|
||||
'conditions' => array('Thread.event_id' => $eventIds),
|
||||
|
@ -842,18 +731,16 @@ class Event extends AppModel
|
|||
// ii. Atttibute has a distribution between 1-3 (community only, connected communities, all orgs)
|
||||
// iii. Attribute has a sharing group that the user is accessible to view
|
||||
$conditionsCorrelation = $this->__buildEventConditionsCorrelation($user, $eventId, $sgids);
|
||||
$correlations = $this->Correlation->find('list', array(
|
||||
'fields' => array('Correlation.event_id', 'Correlation.event_id'),
|
||||
'conditions' => $conditionsCorrelation,
|
||||
'recursive' => 0,
|
||||
'group' => 'Correlation.event_id',
|
||||
'order' => array('Correlation.event_id DESC')));
|
||||
$relatedEventIds = $this->Correlation->find('column', array(
|
||||
'fields' => array('Correlation.event_id'),
|
||||
'conditions' => $conditionsCorrelation,
|
||||
'unique' => true,
|
||||
));
|
||||
|
||||
if (empty($correlations)) {
|
||||
if (empty($relatedEventIds)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$relatedEventIds = array_values($correlations);
|
||||
// now look up the event data for these attributes
|
||||
$conditions = $this->createEventConditions($user);
|
||||
$conditions['AND'][] = array('Event.id' => $relatedEventIds);
|
||||
|
@ -1087,7 +974,7 @@ class Event extends AppModel
|
|||
public function uploadEventToServer($event, $server, $HttpSocket = null, $scope = 'events')
|
||||
{
|
||||
$this->Server = ClassRegistry::init('Server');
|
||||
$push = $this->Server->checkVersionCompatibility($server['Server']['id'], false, $HttpSocket);
|
||||
$push = $this->Server->checkVersionCompatibility($server, false, $HttpSocket);
|
||||
if ($scope === 'events' && empty($push['canPush'])) {
|
||||
return 'The remote user is not a sync user - the upload of the event has been blocked.';
|
||||
} elseif ($scope === 'sightings' && empty($push['canPush']) && empty($push['canSight'])) {
|
||||
|
@ -1187,14 +1074,18 @@ class Event extends AppModel
|
|||
if (is_numeric($event)) {
|
||||
return $event;
|
||||
}
|
||||
$url = $server['Server']['url'];
|
||||
$HttpSocket = $this->setupHttpSocket($server, $HttpSocket);
|
||||
$request = $this->setupSyncRequest($server);
|
||||
if ($scope === 'sightings') {
|
||||
$scope .= '/bulkSaveSightings';
|
||||
$urlPath = $event['Event']['uuid'];
|
||||
}
|
||||
$url = $server['Server']['url'];
|
||||
$uri = $url . '/' . $scope . $this->__getLastUrlPathComponent($urlPath);
|
||||
if ($scope === 'event') {
|
||||
// After creating or editing event, it is not necessary to fetch full event
|
||||
$uri .= '/metadata:1';
|
||||
}
|
||||
$data = json_encode($event);
|
||||
if (!empty(Configure::read('Security.sync_audit'))) {
|
||||
$pushLogEntry = sprintf(
|
||||
|
@ -1466,40 +1357,32 @@ class Event extends AppModel
|
|||
* @param int $eventId
|
||||
* @param array $server
|
||||
* @param null|HttpSocket $HttpSocket
|
||||
* @param boolean $metadataOnly, if True, we only retrieve the metadata
|
||||
* without attributes and attachments which is much faster
|
||||
* @param boolean $metadataOnly, if True, we only retrieve the metadata, without attributes and attachments which is much faster
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function downloadEventFromServer($eventId, $server, $HttpSocket=null, $metadataOnly=false)
|
||||
public function downloadEventFromServer($eventId, $server, HttpSocket $HttpSocket=null, $metadataOnly=false)
|
||||
{
|
||||
$url = $server['Server']['url'];
|
||||
$HttpSocket = $this->setupHttpSocket($server, $HttpSocket);
|
||||
$request = $this->setupSyncRequest($server);
|
||||
if ($metadataOnly) {
|
||||
$uri = $url . '/events/index';
|
||||
$data = ['eventid' => $eventId];
|
||||
$data = json_encode($data);
|
||||
$data = json_encode(['eventid' => $eventId]);
|
||||
$response = $HttpSocket->post($uri, $data, $request);
|
||||
} else {
|
||||
$uri = $url . '/events/view/' . $eventId . '/deleted[]:0/deleted[]:1/excludeGalaxy:1';
|
||||
if (!empty($server['Server']['internal'])) {
|
||||
$uri = $uri . '/excludeLocalTags:1';
|
||||
}
|
||||
$response = $HttpSocket->get($uri, $data = '', $request);
|
||||
$response = $HttpSocket->get($uri, [], $request);
|
||||
}
|
||||
|
||||
if ($response === false) {
|
||||
throw new Exception("Could not reach '$uri'.");
|
||||
} else if (!$response->isOk()) {
|
||||
if (!$response->isOk()) {
|
||||
throw new Exception("Fetching the '$uri' failed with HTTP error {$response->code}: {$response->reasonPhrase}");
|
||||
}
|
||||
|
||||
$event = json_decode($response->body, true);
|
||||
if ($event === null) {
|
||||
throw new Exception('Could not parse event JSON: ' . json_last_error_msg(), json_last_error());
|
||||
}
|
||||
return $event;
|
||||
return $this->jsonDecode($response->body);
|
||||
}
|
||||
|
||||
public function quickDelete($event)
|
||||
|
@ -1802,11 +1685,10 @@ class Event extends AppModel
|
|||
{
|
||||
$conditions = $this->createEventConditions($user);
|
||||
$conditions['AND'][] = $params['conditions'];
|
||||
$results = array_values($this->find('list', array(
|
||||
$results = $this->find('column', array(
|
||||
'conditions' => $conditions,
|
||||
'recursive' => -1,
|
||||
'fields' => array('Event.id')
|
||||
)));
|
||||
));
|
||||
return $results;
|
||||
}
|
||||
|
||||
|
@ -1882,9 +1764,9 @@ class Event extends AppModel
|
|||
if ($list) {
|
||||
$params = array(
|
||||
'conditions' => $conditions,
|
||||
'recursive' => -1,
|
||||
'fields' => ['Event.id'],
|
||||
);
|
||||
$results = array_values($this->find('list', $params));
|
||||
$results = $this->find('column', $params);
|
||||
} else {
|
||||
$params = array(
|
||||
'conditions' => $conditions,
|
||||
|
@ -2587,6 +2469,12 @@ class Event extends AppModel
|
|||
$event['Event']['extensionEvents'][$eventMeta['id']] = $eventMeta;
|
||||
$thingsToMerge = array('Attribute', 'Object', 'ShadowAttribute', 'Galaxy');
|
||||
foreach ($thingsToMerge as $thingToMerge) {
|
||||
if (!isset($event[$thingToMerge])) {
|
||||
$event[$thingToMerge] = [];
|
||||
}
|
||||
if (!isset($extensionEvent[$thingToMerge])) {
|
||||
$extensionEvent[$thingToMerge] = [];
|
||||
}
|
||||
$event[$thingToMerge] = array_merge($event[$thingToMerge], $extensionEvent[$thingToMerge]);
|
||||
}
|
||||
// Merge event reports if requested
|
||||
|
@ -2697,7 +2585,7 @@ class Event extends AppModel
|
|||
$existingOrg = $this->Orgc->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('Orgc.name' => $org),
|
||||
'fields' => array('Orgc.name', 'Orgc.id')
|
||||
'fields' => array('Orgc.id')
|
||||
));
|
||||
if (empty($existingOrg)) {
|
||||
$params['org']['OR'][$k] = -1;
|
||||
|
@ -2714,7 +2602,7 @@ class Event extends AppModel
|
|||
$existingOrg = $this->Orgc->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('Orgc.name' => $org),
|
||||
'fields' => array('Orgc.name', 'Orgc.id')
|
||||
'fields' => array('Orgc.id')
|
||||
));
|
||||
if (!empty($existingOrg)) {
|
||||
$temp[] = $existingOrg['Orgc']['id'];
|
||||
|
@ -3197,16 +3085,18 @@ class Event extends AppModel
|
|||
$userCount = count($usersWithAccess);
|
||||
$this->UserSetting = ClassRegistry::init('UserSetting');
|
||||
foreach ($usersWithAccess as $k => $user) {
|
||||
if ($this->UserSetting->checkPublishFilter($user, $event)) {
|
||||
// Fetch event for user that will receive alert e-mail to respect all ACLs
|
||||
$eventForUser = $this->fetchEvent($user, [
|
||||
'eventid' => $id,
|
||||
'includeAllTags' => true,
|
||||
'includeEventCorrelations' => true,
|
||||
])[0];
|
||||
// Fetch event for user that will receive alert e-mail to respect all ACLs
|
||||
$eventForUser = $this->fetchEvent($user, [
|
||||
'eventid' => $id,
|
||||
'includeAllTags' => true,
|
||||
'includeEventCorrelations' => true,
|
||||
'noEventReports' => true,
|
||||
'noSightings' => true,
|
||||
])[0];
|
||||
|
||||
if ($this->UserSetting->checkPublishFilter($user, $eventForUser)) {
|
||||
$body = $this->__buildAlertEmailBody($eventForUser, $user, $oldpublish);
|
||||
$this->User->sendEmail(array('User' => $user), $body, $bodyNoEnc, $subject);
|
||||
$this->User->sendEmail(['User' => $user], $body, $bodyNoEnc, $subject);
|
||||
}
|
||||
if ($jobId) {
|
||||
$this->Job->saveProgress($jobId, null, $k / $userCount * 100);
|
||||
|
@ -4204,7 +4094,7 @@ class Event extends AppModel
|
|||
if (isset($data['Event']['EventReport'])) {
|
||||
foreach ($data['Event']['EventReport'] as $i => $report) {
|
||||
$nothingToChange = false;
|
||||
$result = $this->EventReport->editReport($user, $report, $this->id, true, $nothingToChange);
|
||||
$result = $this->EventReport->editReport($user, ['EventReport' => $report], $this->id, true, $nothingToChange);
|
||||
if (!empty($result)) {
|
||||
$validationErrors['EventReport'][] = $result;
|
||||
}
|
||||
|
@ -6149,8 +6039,8 @@ class Event extends AppModel
|
|||
}
|
||||
}
|
||||
}
|
||||
$data[$dataType . 'Tag'] = array_values($data[$dataType . 'Tag']);
|
||||
}
|
||||
$data[$dataType . 'Tag'] = array_values($data[$dataType . 'Tag']);
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
@ -6994,7 +6884,7 @@ class Event extends AppModel
|
|||
unset($result);
|
||||
unset($temp);
|
||||
$tmpfile->write($exportTool->footer($exportToolParams));
|
||||
return $tmpfile->finish();
|
||||
return $tmpfile;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -7046,7 +6936,7 @@ class Event extends AppModel
|
|||
'model_id' => 0,
|
||||
'email' => 'SYSTEM',
|
||||
'action' => 'error',
|
||||
'title' => sprintf('Event fetch potential memory exhaustion. During the fetching of events, a large event (#%s) was detected that exceeds the available PHP memory. Consider raising the PHP max_memory setting to at least %sM', $largest_event_id, ceil($largest_event/$memory_scaling_factor)),
|
||||
'title' => sprintf('Event fetch potential memory exhaustion.' . PHP_EOL . 'During the fetching of events, a large event (#%s) was detected that exceeds the available PHP memory.' . PHP_EOL . 'Consider raising the PHP max_memory setting to at least %sM', $largest_event_id, ceil($largest_event/$memory_scaling_factor)),
|
||||
'change' => null,
|
||||
));
|
||||
}
|
||||
|
|
|
@ -161,6 +161,8 @@ class EventReport extends AppModel
|
|||
$errors[] = __('Event Report not found.');
|
||||
return $errors;
|
||||
}
|
||||
} else {
|
||||
$report['EventReport']['id'] = $existingReport['EventReport']['id'];
|
||||
}
|
||||
|
||||
if ($fromPull) {
|
||||
|
@ -521,7 +523,8 @@ class EventReport extends AppModel
|
|||
return $errors;
|
||||
}
|
||||
|
||||
public function applySuggestions($user, $report, $contentWithSuggestions, $suggestionsMapping) {
|
||||
public function applySuggestions(array $user, array $report, $contentWithSuggestions, array $suggestionsMapping)
|
||||
{
|
||||
$errors = [];
|
||||
$replacedContent = $contentWithSuggestions;
|
||||
$success = 0;
|
||||
|
@ -546,10 +549,10 @@ class EventReport extends AppModel
|
|||
return $errors;
|
||||
}
|
||||
|
||||
public function applySuggestionsInText($contentWithSuggestions, $attribute, $value)
|
||||
public function applySuggestionsInText($contentWithSuggestions, array $attribute, $value)
|
||||
{
|
||||
$textToBeReplaced = sprintf('@[suggestion](%s)', $value);
|
||||
$textToInject = sprintf('@[attribute](%s)', $attribute['Attribute']['uuid']);
|
||||
$textToBeReplaced = "@[suggestion]($value)";
|
||||
$textToInject = "@[attribute]({$attribute['Attribute']['uuid']})";
|
||||
$replacedContent = str_replace($textToBeReplaced, $textToInject, $contentWithSuggestions);
|
||||
return $replacedContent;
|
||||
}
|
||||
|
@ -642,25 +645,36 @@ class EventReport extends AppModel
|
|||
];
|
||||
}
|
||||
|
||||
public function transformFreeTextIntoSuggestion($content, $complexTypeToolResult)
|
||||
public function transformFreeTextIntoSuggestion($content, array $complexTypeToolResult)
|
||||
{
|
||||
$replacedContent = $content;
|
||||
$suggestionsMapping = [];
|
||||
$typeToCategoryMapping = $this->Event->Attribute->typeToCategoryMapping();
|
||||
foreach ($complexTypeToolResult as $i => $complexTypeToolEntry) {
|
||||
|
||||
// Sort by original value string length, longest values first
|
||||
usort($complexTypeToolResult, function ($a, $b) {
|
||||
$strlenA = strlen($a['original_value']);
|
||||
$strlenB = strlen($b['original_value']);
|
||||
if ($strlenA === $strlenB) {
|
||||
return 0;
|
||||
}
|
||||
return ($strlenA < $strlenB) ? 1 : -1;
|
||||
});
|
||||
|
||||
$suggestionsMapping = [];
|
||||
foreach ($complexTypeToolResult as $complexTypeToolEntry) {
|
||||
$textToBeReplaced = $complexTypeToolEntry['value'];
|
||||
$textToInject = sprintf('@[suggestion](%s)', $textToBeReplaced);
|
||||
$textToInject = "@[suggestion]($textToBeReplaced)";
|
||||
$suggestionsMapping[$textToBeReplaced] = [
|
||||
'category' => $typeToCategoryMapping[$complexTypeToolEntry['default_type']][0],
|
||||
'type' => $complexTypeToolEntry['default_type'],
|
||||
'value' => $textToBeReplaced,
|
||||
'to_ids' => $complexTypeToolEntry['to_ids'],
|
||||
];
|
||||
$replacedContent = str_replace($textToBeReplaced, $textToInject, $replacedContent);
|
||||
$replacedContent = str_replace($complexTypeToolEntry['original_value'], $textToInject, $replacedContent);
|
||||
}
|
||||
return [
|
||||
'contentWithSuggestions' => $replacedContent,
|
||||
'suggestionsMapping' => $suggestionsMapping
|
||||
'suggestionsMapping' => $suggestionsMapping,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -674,21 +688,17 @@ class EventReport extends AppModel
|
|||
return $complexTypeToolResult;
|
||||
}
|
||||
|
||||
public function getComplexTypeToolResultFromReport($content)
|
||||
public function getComplexTypeToolResultWithReplacements(array $user, array $report)
|
||||
{
|
||||
App::uses('ComplexTypeTool', 'Tools');
|
||||
$complexTypeTool = new ComplexTypeTool();
|
||||
$this->Warninglist = ClassRegistry::init('Warninglist');
|
||||
$complexTypeTool->setTLDs($this->Warninglist->fetchTLDLists());
|
||||
$complexTypeToolResult = $complexTypeTool->checkComplexRouter($content, 'freetext');
|
||||
return $complexTypeToolResult;
|
||||
}
|
||||
|
||||
public function getComplexTypeToolResultWithReplacements($user, $report)
|
||||
{
|
||||
$complexTypeToolResult = $this->getComplexTypeToolResultFromReport($report['EventReport']['content']);
|
||||
$complexTypeToolResult = $complexTypeTool->checkFreeText($report['EventReport']['content']);
|
||||
$replacementResult = $this->transformFreeTextIntoReplacement($user, $report, $complexTypeToolResult);
|
||||
$complexTypeToolResult = $this->getComplexTypeToolResultFromReport($replacementResult['contentWithReplacements']);
|
||||
$complexTypeToolResult = $complexTypeTool->checkFreeText($replacementResult['contentWithReplacements']);
|
||||
|
||||
return [
|
||||
'complexTypeToolResult' => $complexTypeToolResult,
|
||||
'replacementResult' => $replacementResult,
|
||||
|
@ -700,6 +710,7 @@ class EventReport extends AppModel
|
|||
*
|
||||
* @param array $user
|
||||
* @param array $report
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function extractWithReplacements(array $user, array $report, array $options = [])
|
||||
|
@ -713,7 +724,6 @@ class EventReport extends AppModel
|
|||
'attack' => true,
|
||||
];
|
||||
$options = array_merge($baseOptions, $options);
|
||||
$originalContent = $report['EventReport']['content'];
|
||||
$this->GalaxyCluster = ClassRegistry::init('GalaxyCluster');
|
||||
$mitreAttackGalaxyId = $this->GalaxyCluster->Galaxy->getMitreAttackGalaxyId();
|
||||
$clusterContain = ['Tag'];
|
||||
|
@ -734,17 +744,21 @@ class EventReport extends AppModel
|
|||
'contain' => $clusterContain
|
||||
]);
|
||||
|
||||
$originalContent = $report['EventReport']['content'];
|
||||
// Remove all existing event report markers
|
||||
$content = preg_replace("/@\[(attribute|tag|galaxymatrix)]\([^)]*\)/", '', $originalContent);
|
||||
|
||||
if ($options['tags']) {
|
||||
$this->Tag = ClassRegistry::init('Tag');
|
||||
$tags = $this->Tag->fetchUsableTags($user);
|
||||
foreach ($tags as $i => $tag) {
|
||||
foreach ($tags as $tag) {
|
||||
$tagName = $tag['Tag']['name'];
|
||||
$found = $this->isValidReplacementTag($originalContent, $tagName);
|
||||
$found = $this->isValidReplacementTag($content, $tagName);
|
||||
if ($found) {
|
||||
$replacedContext[$tagName][$tagName] = $tag['Tag'];
|
||||
} else {
|
||||
$tagNameUpper = strtoupper($tagName);
|
||||
$found = $this->isValidReplacementTag($originalContent, $tagNameUpper);
|
||||
$found = $this->isValidReplacementTag($content, $tagNameUpper);
|
||||
if ($found) {
|
||||
$replacedContext[$tagNameUpper][$tagName] = $tag['Tag'];
|
||||
}
|
||||
|
@ -752,10 +766,10 @@ class EventReport extends AppModel
|
|||
}
|
||||
}
|
||||
|
||||
foreach ($clusters as $i => $cluster) {
|
||||
foreach ($clusters as $cluster) {
|
||||
$cluster['GalaxyCluster']['colour'] = '#0088cc';
|
||||
$tagName = $cluster['GalaxyCluster']['tag_name'];
|
||||
$found = $this->isValidReplacementTag($originalContent, $tagName);
|
||||
$found = $this->isValidReplacementTag($content, $tagName);
|
||||
if ($found) {
|
||||
$replacedContext[$tagName][$tagName] = $cluster['GalaxyCluster'];
|
||||
}
|
||||
|
@ -765,10 +779,10 @@ class EventReport extends AppModel
|
|||
$replacedContext[$cluster['GalaxyCluster']['value']][$tagName] = $cluster['GalaxyCluster'];
|
||||
}
|
||||
if ($options['synonyms']) {
|
||||
foreach ($cluster['GalaxyElement'] as $j => $element) {
|
||||
foreach ($cluster['GalaxyElement'] as $element) {
|
||||
if (strlen($element['value']) >= $options['synonyms_min_characters']) {
|
||||
$toSearch = ' ' . $element['value'] . ' ';
|
||||
$found = strpos($originalContent, $toSearch) !== false;
|
||||
$found = strpos($content, $toSearch) !== false;
|
||||
if ($found) {
|
||||
$replacedContext[$element['value']][$tagName] = $cluster['GalaxyCluster'];
|
||||
}
|
||||
|
@ -783,22 +797,22 @@ class EventReport extends AppModel
|
|||
'conditions' => ['GalaxyCluster.galaxy_id' => $mitreAttackGalaxyId],
|
||||
'contain' => $clusterContain
|
||||
]);
|
||||
foreach ($attackClusters as $i => $cluster) {
|
||||
foreach ($attackClusters as $cluster) {
|
||||
$cluster['GalaxyCluster']['colour'] = '#0088cc';
|
||||
$tagName = $cluster['GalaxyCluster']['tag_name'];
|
||||
$toSearch = ' ' . $cluster['GalaxyCluster']['value'] . ' ';
|
||||
$found = strpos($originalContent, $toSearch) !== false;
|
||||
$found = strpos($content, $toSearch) !== false;
|
||||
if ($found) {
|
||||
$replacedContext[$cluster['GalaxyCluster']['value']][$tagName] = $cluster['GalaxyCluster'];
|
||||
} else {
|
||||
$clusterParts = explode(' - ', $cluster['GalaxyCluster']['value'], 2);
|
||||
$toSearch = ' ' . $clusterParts[0] . ' ';
|
||||
$found = strpos($originalContent, $toSearch) !== false;
|
||||
$found = strpos($content, $toSearch) !== false;
|
||||
if ($found) {
|
||||
$replacedContext[$clusterParts[0]][$tagName] = $cluster['GalaxyCluster'];
|
||||
} else {
|
||||
} else if (isset($clusterParts[1])) {
|
||||
$toSearch = ' ' . $clusterParts[1] . ' ';
|
||||
$found = strpos($originalContent, $toSearch) !== false;
|
||||
$found = strpos($content, $toSearch) !== false;
|
||||
if ($found) {
|
||||
$replacedContext[$clusterParts[1]][$tagName] = $cluster['GalaxyCluster'];
|
||||
}
|
||||
|
@ -810,14 +824,32 @@ class EventReport extends AppModel
|
|||
'replacedContext' => $replacedContext
|
||||
];
|
||||
if ($options['replace']) {
|
||||
// Sort by original value string length, longest values first
|
||||
uksort($replacedContext, function ($a, $b) {
|
||||
$strlenA = strlen($a);
|
||||
$strlenB = strlen($b);
|
||||
if ($strlenA === $strlenB) {
|
||||
return 0;
|
||||
}
|
||||
return ($strlenA < $strlenB) ? 1 : -1;
|
||||
});
|
||||
|
||||
$content = $originalContent;
|
||||
$secondPassReplace = [];
|
||||
// Replace in two pass to prevent double replace
|
||||
$id = 0;
|
||||
foreach ($replacedContext as $rawText => $replacements) {
|
||||
// Replace with first one until a better strategy is found
|
||||
reset($replacements);
|
||||
$replacement = key($replacements);
|
||||
$textToInject = sprintf('@[tag](%s)', $replacement);
|
||||
$content = str_replace($rawText, $textToInject, $content);
|
||||
++$id;
|
||||
$content = str_replace($rawText, "@[mark]($id)", $content);
|
||||
$secondPassReplace[$id] = "@[tag]($replacement)";
|
||||
}
|
||||
|
||||
$content = preg_replace_callback("/@\[mark]\(([^)]*)\)/", function ($matches) use ($secondPassReplace) {
|
||||
return $secondPassReplace[$matches[1]];
|
||||
}, $content);
|
||||
$toReturn['contentWithReplacements'] = $content;
|
||||
}
|
||||
return $toReturn;
|
||||
|
@ -835,7 +867,6 @@ class EventReport extends AppModel
|
|||
'event_id' => $event_id,
|
||||
'url' => $url
|
||||
];
|
||||
$module = $this->isFetchURLModuleEnabled();
|
||||
if (!empty($module)) {
|
||||
$result = $this->Module->queryModuleServer($modulePayload, false);
|
||||
if (empty($result['results'][0]['values'][0])) {
|
||||
|
@ -853,7 +884,7 @@ class EventReport extends AppModel
|
|||
}
|
||||
|
||||
/**
|
||||
* findValidReplacementTag Search if tagName is in content and is not wrapped in a tag reference
|
||||
* findValidReplacementTag Search if tagName is in content
|
||||
*
|
||||
* @param string $content
|
||||
* @param string $tagName
|
||||
|
@ -861,25 +892,8 @@ class EventReport extends AppModel
|
|||
*/
|
||||
private function isValidReplacementTag($content, $tagName)
|
||||
{
|
||||
$lastIndex = 0;
|
||||
$allIndices = [];
|
||||
$toSearch = strpos($tagName, ':') === false ? ' ' . $tagName . ' ' : $tagName;
|
||||
while (($lastIndex = strpos($content, $toSearch, $lastIndex)) !== false) {
|
||||
$allIndices[] = $lastIndex;
|
||||
$lastIndex = $lastIndex + strlen($toSearch);
|
||||
}
|
||||
if (empty($allIndices)) {
|
||||
return false;
|
||||
} else {
|
||||
$wrapper = '@[tag](';
|
||||
foreach ($allIndices as $i => $index) {
|
||||
$stringBeforeTag = substr($content, $index - strlen($wrapper), strlen($wrapper));
|
||||
if ($stringBeforeTag != $wrapper) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return strpos($content, $toSearch) !== false;
|
||||
}
|
||||
|
||||
public function attachTagsAfterReplacements($user, $replacedContext, $eventId)
|
||||
|
|
|
@ -180,7 +180,7 @@ class Feed extends AppModel
|
|||
$tmpFile->write(trim($data));
|
||||
unset($data);
|
||||
|
||||
return $tmpFile->csv();
|
||||
return $tmpFile->intoParsedCsv();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -364,18 +364,18 @@ class Feed extends AppModel
|
|||
$redisResultToAttributePosition = [];
|
||||
|
||||
foreach ($attributes as $k => $attribute) {
|
||||
if (in_array($attribute['type'], $this->Attribute->nonCorrelatingTypes)) {
|
||||
if (in_array($attribute['type'], $this->Attribute->nonCorrelatingTypes, true)) {
|
||||
continue; // attribute type is not correlateable
|
||||
}
|
||||
if (!empty($attribute['disable_correlation'])) {
|
||||
continue; // attribute correlation is disabled
|
||||
}
|
||||
|
||||
if (in_array($attribute['type'], $compositeTypes)) {
|
||||
if (in_array($attribute['type'], $compositeTypes, true)) {
|
||||
list($value1, $value2) = explode('|', $attribute['value']);
|
||||
$parts = [$value1];
|
||||
|
||||
if (!in_array($attribute['type'], $this->Attribute->primaryOnlyCorrelatingTypes)) {
|
||||
if (!in_array($attribute['type'], $this->Attribute->primaryOnlyCorrelatingTypes, true)) {
|
||||
$parts[] = $value2;
|
||||
}
|
||||
} else {
|
||||
|
@ -442,12 +442,17 @@ class Feed extends AppModel
|
|||
if (!isset($event[$scope][$sourceId])) {
|
||||
$event[$scope][$sourceId] = $source[$scope];
|
||||
}
|
||||
|
||||
$attributePosition = $redisResultToAttributePosition[$hitIds[$k]];
|
||||
$attributes[$attributePosition][$scope][] = $source[$scope];
|
||||
$alreadyAttached = isset($attributes[$attributePosition][$scope]) &&
|
||||
in_array($sourceId, array_column($attributes[$attributePosition][$scope], 'id'));
|
||||
if (!$alreadyAttached) {
|
||||
$attributes[$attributePosition][$scope][] = $source[$scope];
|
||||
}
|
||||
$sourceHasHit = true;
|
||||
}
|
||||
}
|
||||
// Append also exact MISP feed event UUID
|
||||
// Append also exact MISP feed or server event UUID
|
||||
// TODO: This can be optimised in future to do that in one pass
|
||||
if ($sourceHasHit && ($scope === 'Server' || $source[$scope]['source_format'] === 'misp')) {
|
||||
$pipe = $redis->multi(Redis::PIPELINE);
|
||||
|
@ -475,7 +480,9 @@ class Feed extends AppModel
|
|||
$attributePosition = $eventUuidHitPosition[$sourceHitPos];
|
||||
foreach ($attributes[$attributePosition][$scope] as $tempKey => $tempFeed) {
|
||||
if ($tempFeed['id'] == $feedId) {
|
||||
$attributes[$attributePosition][$scope][$tempKey]['event_uuids'][] = $eventUuid;
|
||||
if (empty($attributes[$attributePosition][$scope][$tempKey]['event_uuids']) || !in_array($eventUuid, $attributes[$attributePosition][$scope][$tempKey]['event_uuids'])) {
|
||||
$attributes[$attributePosition][$scope][$tempKey]['event_uuids'][] = $eventUuid;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -598,11 +605,6 @@ class Feed extends AppModel
|
|||
)
|
||||
);
|
||||
|
||||
// Enable gzipped responses if PHP has 'gzdecode' method
|
||||
if (function_exists('gzdecode')) {
|
||||
$result['header']['Accept-Encoding'] = 'gzip';
|
||||
}
|
||||
|
||||
$commit = $this->checkMIPSCommit();
|
||||
if ($commit) {
|
||||
$result['header']['commit'] = $commit;
|
||||
|
@ -1759,30 +1761,22 @@ class Feed extends AppModel
|
|||
|
||||
$request = $this->__createFeedRequest($feed['Feed']['headers']);
|
||||
|
||||
if ($followRedirect) {
|
||||
$response = $this->getFollowRedirect($HttpSocket, $uri, $request);
|
||||
} else {
|
||||
$response = $HttpSocket->get($uri, array(), $request);
|
||||
try {
|
||||
if ($followRedirect) {
|
||||
$response = $this->getFollowRedirect($HttpSocket, $uri, $request);
|
||||
} else {
|
||||
$response = $HttpSocket->get($uri, array(), $request);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
throw new Exception("Fetching the '$uri' failed with exception: {$e->getMessage()}", 0, $e);
|
||||
}
|
||||
|
||||
if ($response === false) {
|
||||
throw new Exception("Could not reach '$uri'.");
|
||||
} else if ($response->code != 200) { // intentionally !=
|
||||
if ($response->code != 200) { // intentionally !=
|
||||
throw new Exception("Fetching the '$uri' failed with HTTP error {$response->code}: {$response->reasonPhrase}");
|
||||
}
|
||||
|
||||
$data = $response->body;
|
||||
|
||||
$contentEncoding = $response->getHeader('Content-Encoding');
|
||||
if ($contentEncoding === 'gzip') {
|
||||
$data = gzdecode($data);
|
||||
if ($data === false) {
|
||||
throw new Exception("Fetching the '$uri' failed, response should be gzip encoded, but gzip decoding failed.");
|
||||
}
|
||||
} else if ($contentEncoding) {
|
||||
throw new Exception("Fetching the '$uri' failed, because remote server returns unsupported content encoding '$contentEncoding'");
|
||||
}
|
||||
|
||||
$contentType = $response->getHeader('Content-Type');
|
||||
if ($contentType === 'application/zip') {
|
||||
$zipFile = new File($this->tempFileName());
|
||||
|
|
|
@ -92,7 +92,7 @@ class Galaxy extends AppModel
|
|||
return $this->find('list', array('recursive' => -1, 'fields' => array('type', 'id')));
|
||||
}
|
||||
|
||||
private function __update_prepare_template(array $cluster_package, array $galaxies): array
|
||||
private function __update_prepare_template(array $cluster_package, array $galaxies)
|
||||
{
|
||||
return [
|
||||
'source' => isset($cluster_package['source']) ? $cluster_package['source'] : '',
|
||||
|
@ -104,7 +104,7 @@ class Galaxy extends AppModel
|
|||
];
|
||||
}
|
||||
|
||||
private function __getPreExistingClusters(array $galaxies, array $cluster_package): array
|
||||
private function __getPreExistingClusters(array $galaxies, array $cluster_package)
|
||||
{
|
||||
$temp = $this->GalaxyCluster->find('all', array(
|
||||
'conditions' => array(
|
||||
|
@ -120,7 +120,7 @@ class Galaxy extends AppModel
|
|||
return $existingClusters;
|
||||
}
|
||||
|
||||
private function __deleteOutdated(bool $force, array $cluster_package, array $existingClusters): array
|
||||
private function __deleteOutdated(bool $force, array $cluster_package, array $existingClusters)
|
||||
{
|
||||
// Delete all existing outdated clusters
|
||||
$cluster_ids_to_delete = array();
|
||||
|
@ -152,7 +152,7 @@ class Galaxy extends AppModel
|
|||
return $cluster_package;
|
||||
}
|
||||
|
||||
private function __createClusters($cluster_package, $template): array
|
||||
private function __createClusters($cluster_package, $template)
|
||||
{
|
||||
$relations = [];
|
||||
$elements = [];
|
||||
|
@ -377,12 +377,23 @@ class Galaxy extends AppModel
|
|||
$result = $this->Tag->$connectorModel->save($toSave);
|
||||
if ($result) {
|
||||
if ($target_type !== 'tag_collection') {
|
||||
$date = new DateTime();
|
||||
if ($target_type === 'event') {
|
||||
$event = $target;
|
||||
} else if ($target_type === 'attribute') {
|
||||
$target['Attribute']['timestamp'] = $date->getTimestamp();
|
||||
$this->Tag->AttributeTag->Attribute->save($target);
|
||||
if (!empty($target['Attribute']['object_id'])) {
|
||||
$container_object = $this->Tag->AttributeTag->Attribute->Object->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => ['id' => $target['Attribute']['object_id']]
|
||||
]);
|
||||
$container_object['Object']['timestamp'] = $date->getTimestamp();
|
||||
$this->Tag->AttributeTag->Attribute->Object->save($container_object);
|
||||
}
|
||||
}
|
||||
$this->Tag->EventTag->Event->insertLock($user, $event['Event']['id']);
|
||||
$event['Event']['published'] = 0;
|
||||
$date = new DateTime();
|
||||
$event['Event']['timestamp'] = $date->getTimestamp();
|
||||
$this->Tag->EventTag->Event->save($event);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
App::uses('AppModel', 'Model');
|
||||
App::uses('TmpFileTool', 'Tools');
|
||||
|
||||
class GalaxyCluster extends AppModel
|
||||
{
|
||||
public $useTable = 'galaxy_clusters';
|
||||
|
@ -867,8 +869,18 @@ class GalaxyCluster extends AppModel
|
|||
return $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array $user
|
||||
* @return array|mixed
|
||||
*/
|
||||
public function getCluster($name, $user)
|
||||
{
|
||||
$isGalaxyTag = strpos($name, 'misp-galaxy:') === 0;
|
||||
if (!$isGalaxyTag) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isset($this->__clusterCache[$name])) {
|
||||
return $this->__clusterCache[$name];
|
||||
}
|
||||
|
@ -1104,8 +1116,8 @@ class GalaxyCluster extends AppModel
|
|||
);
|
||||
}
|
||||
|
||||
$tmpfile = tmpfile();
|
||||
fwrite($tmpfile, $exportTool->header($exportToolParams));
|
||||
$tmpfile = new TmpFileTool();
|
||||
$tmpfile->write($exportTool->header($exportToolParams));
|
||||
$loop = false;
|
||||
if (empty($params['limit'])) {
|
||||
$memory_in_mb = $this->convert_to_memory_limit_to_mb(ini_get('memory_limit'));
|
||||
|
@ -1115,48 +1127,32 @@ class GalaxyCluster extends AppModel
|
|||
$params['page'] = 1;
|
||||
}
|
||||
$this->__iteratedFetch($user, $params, $loop, $tmpfile, $exportTool, $exportToolParams, $elementCounter);
|
||||
fwrite($tmpfile, $exportTool->footer($exportToolParams));
|
||||
fseek($tmpfile, 0);
|
||||
if (fstat($tmpfile)['size']) {
|
||||
$final = fread($tmpfile, fstat($tmpfile)['size']);
|
||||
} else {
|
||||
$final = '';
|
||||
}
|
||||
fclose($tmpfile);
|
||||
return $final;
|
||||
$tmpfile->write($exportTool->footer($exportToolParams));
|
||||
return $tmpfile;
|
||||
}
|
||||
|
||||
private function __iteratedFetch($user, &$params, &$loop, &$tmpfile, $exportTool, $exportToolParams, &$elementCounter = 0)
|
||||
private function __iteratedFetch($user, $params, $loop, TmpFileTool $tmpfile, $exportTool, $exportToolParams, &$elementCounter = 0)
|
||||
{
|
||||
$continue = true;
|
||||
while ($continue) {
|
||||
$temp = '';
|
||||
$elementCounter = 0;
|
||||
$separator = $exportTool->separator($exportToolParams);
|
||||
do {
|
||||
$results = $this->fetchGalaxyClusters($user, $params, $full=$params['full']);
|
||||
if (empty($results)) {
|
||||
$loop = false;
|
||||
return true;
|
||||
break; // nothing found, skip rest
|
||||
}
|
||||
if ($elementCounter !== 0 && !empty($results)) {
|
||||
$temp .= $exportTool->separator($exportToolParams);
|
||||
$resultCount = count($results);
|
||||
$elementCounter += $resultCount;
|
||||
foreach ($results as $cluster) {
|
||||
$handlerResult = $exportTool->handler($cluster, $exportToolParams);
|
||||
if ($handlerResult !== '') {
|
||||
$tmpfile->writeWithSeparator($handlerResult, $separator);
|
||||
}
|
||||
}
|
||||
if ($resultCount < $params['limit']) {
|
||||
break;
|
||||
}
|
||||
$params['page'] += 1;
|
||||
$i = 0;
|
||||
foreach ($results as $cluster) {
|
||||
$elementCounter++;
|
||||
$handlerResult = $exportTool->handler($cluster, $exportToolParams);
|
||||
$temp .= $handlerResult;
|
||||
if ($handlerResult !== '') {
|
||||
if ($i != count($results) -1) {
|
||||
$temp .= $exportTool->separator($exportToolParams);
|
||||
}
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
if (!$loop) {
|
||||
$continue = false;
|
||||
}
|
||||
fwrite($tmpfile, $temp);
|
||||
}
|
||||
} while ($loop);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1407,9 +1403,9 @@ class GalaxyCluster extends AppModel
|
|||
{
|
||||
$clusterTagNames = [];
|
||||
foreach ($events as $event) {
|
||||
foreach ($event['EventTag'] as $k2 => $eventTag) {
|
||||
foreach ($event['EventTag'] as $eventTag) {
|
||||
if ($eventTag['Tag']['is_galaxy']) {
|
||||
$clusterTagNames[] = strtolower($eventTag['Tag']['name']);
|
||||
$clusterTagNames[strtolower($eventTag['Tag']['name'])] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1419,7 +1415,7 @@ class GalaxyCluster extends AppModel
|
|||
}
|
||||
|
||||
$options = [
|
||||
'conditions' => ['LOWER(GalaxyCluster.tag_name)' => $clusterTagNames],
|
||||
'conditions' => ['LOWER(GalaxyCluster.tag_name)' => array_keys($clusterTagNames)],
|
||||
];
|
||||
if (!$fetchFullCluster) {
|
||||
$options['contain'] = ['Galaxy'];
|
||||
|
@ -1564,7 +1560,7 @@ class GalaxyCluster extends AppModel
|
|||
{
|
||||
$this->Server = ClassRegistry::init('Server');
|
||||
$this->Log = ClassRegistry::init('Log');
|
||||
$push = $this->Server->checkVersionCompatibility($server['Server']['id'], false, $HttpSocket);
|
||||
$push = $this->Server->checkVersionCompatibility($server, false, $HttpSocket);
|
||||
if (empty($push['canPush']) && empty($push['canPushGalaxyCluster'])) {
|
||||
return __('The remote user does not have the permission to manipulate galaxies - the upload of the galaxy clusters has been blocked.');
|
||||
}
|
||||
|
@ -1837,12 +1833,12 @@ class GalaxyCluster extends AppModel
|
|||
* - string <full> pull everything
|
||||
* - string <update> pull updates of cluster present locally
|
||||
* - string <pull_relevant_clusters> pull clusters based on tags present locally
|
||||
* @return void The number of pulled clusters
|
||||
* @return int The number of pulled clusters
|
||||
*/
|
||||
public function pullGalaxyClusters(array $user, array $server, $technique = 'full')
|
||||
{
|
||||
$this->Server = ClassRegistry::init('Server');
|
||||
$compatible = $this->Server->checkVersionCompatibility($server['Server']['id'], $user)['supportEditOfGalaxyCluster'];
|
||||
$compatible = $this->Server->checkVersionCompatibility($server, $user)['supportEditOfGalaxyCluster'];
|
||||
if (!$compatible) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -1875,12 +1871,11 @@ class GalaxyCluster extends AppModel
|
|||
$clusterIds = $this->Server->getElligibleClusterIdsFromServerForPull($server, $HttpSocket=null, $onlyUpdateLocalCluster=true, $elligibleClusters=$localClustersToUpdate);
|
||||
} elseif ("pull_relevant_clusters" === $technique) {
|
||||
// Fetch all local custom cluster tags then fetch their corresponding clusters on the remote end
|
||||
$tagNames = $this->Tag->find('list', array(
|
||||
$tagNames = $this->Tag->find('column', array(
|
||||
'conditions' => array(
|
||||
'Tag.is_custom_galaxy' => true
|
||||
),
|
||||
'fields' => array('Tag.name'),
|
||||
'recursive' => -1,
|
||||
));
|
||||
$clusterUUIDs = array();
|
||||
$re = '/^misp-galaxy:[^:="]+="(?<uuid>[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})"$/m';
|
||||
|
|
|
@ -336,6 +336,11 @@ class Log extends AppModel
|
|||
$elasticSearchClient->pushDocument($logIndex, "log", $data);
|
||||
}
|
||||
|
||||
// Do not save request action logs to syslog, because they contain no information
|
||||
if ($data['Log']['action'] === 'request') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// write to syslogd as well if enabled
|
||||
if ($this->syslog === null) {
|
||||
if (Configure::read('Security.syslog')) {
|
||||
|
|
|
@ -81,6 +81,8 @@ class MispObject extends AppModel
|
|||
)
|
||||
);
|
||||
|
||||
private $__objectDuplicationCheckCache = [];
|
||||
|
||||
public function buildFilterConditions(&$params)
|
||||
{
|
||||
$conditions = [];
|
||||
|
@ -302,32 +304,44 @@ class MispObject extends AppModel
|
|||
{
|
||||
$newObjectAttributes = array();
|
||||
$existingObjectAttributes = array();
|
||||
foreach ($object['Attribute'] as $attribute) {
|
||||
if (isset($object['Object']['Attribute'])) {
|
||||
$attributeArray = $object['Object']['Attribute'];
|
||||
} else {
|
||||
$attributeArray = $object['Attribute'];
|
||||
}
|
||||
foreach ($attributeArray as $attribute) {
|
||||
if ($attribute['type'] === 'malware-sample') {
|
||||
if (strpos($attribute['value'], '|') === false && !empty($attribute['data'])) {
|
||||
$attribute['value'] = $attribute['value'] . '|' . md5(base64_decode($attribute['data']));
|
||||
}
|
||||
}
|
||||
$newObjectAttributes[] = hash(
|
||||
'sha256',
|
||||
$attribute['object_relation'] . $attribute['category'] . $attribute['type'] . $attribute['value']
|
||||
$attribute['object_relation'] . $attribute['category'] . $attribute['type'] . $this->data['Attribute']['value'] = $this->Attribute->modifyBeforeValidation($attribute['type'], $attribute['value'])
|
||||
);
|
||||
}
|
||||
$newObjectAttributeCount = count($newObjectAttributes);
|
||||
$existingObjects = $this->find('all', array(
|
||||
'recursive' => -1,
|
||||
'contain' => array(
|
||||
'Attribute' => array(
|
||||
'fields' => array('value', 'type', 'category', 'object_relation'),
|
||||
'conditions' => array('Attribute.deleted' => 0)
|
||||
)
|
||||
),
|
||||
'fields' => array('template_uuid'),
|
||||
'conditions' => array('template_uuid' => $object['Object']['template_uuid'], 'Object.deleted' => 0)
|
||||
));
|
||||
if (!isset($this->__objectDuplicationCheckCache[$object['Object']['template_uuid']])) {
|
||||
$this->__objectDuplicationCheckCache[$object['Object']['template_uuid']] = $this->find('all', array(
|
||||
'recursive' => -1,
|
||||
'contain' => array(
|
||||
'Attribute' => array(
|
||||
'fields' => array('value', 'type', 'category', 'object_relation'),
|
||||
'conditions' => array('Attribute.deleted' => 0)
|
||||
)
|
||||
),
|
||||
'fields' => array('template_uuid'),
|
||||
'conditions' => array('template_uuid' => $object['Object']['template_uuid'], 'Object.deleted' => 0, 'event_id' => $eventId)
|
||||
));
|
||||
}
|
||||
$oldObjects = array();
|
||||
foreach ($existingObjects as $k => $existingObject) {
|
||||
foreach ($this->__objectDuplicationCheckCache[$object['Object']['template_uuid']] as $k => $existingObject) {
|
||||
$temp = array();
|
||||
if (!empty($existingObject['Attribute']) && $newObjectAttributeCount == count($existingObject['Attribute'])) {
|
||||
foreach ($existingObject['Attribute'] as $existingAttribute) {
|
||||
$temp[] = hash(
|
||||
'sha256',
|
||||
$attribute['object_relation'] . $existingAttribute['category'] . $existingAttribute['type'] . $existingAttribute['value']
|
||||
$existingAttribute['object_relation'] . $existingAttribute['category'] . $existingAttribute['type'] . $existingAttribute['value']
|
||||
);
|
||||
}
|
||||
if (empty(array_diff($temp, $newObjectAttributes))) {
|
||||
|
@ -884,6 +898,23 @@ class MispObject extends AppModel
|
|||
if (!isset($object['Object'])) {
|
||||
$object = array('Object' => $object);
|
||||
}
|
||||
if (!empty($object['Object']['breakOnDuplicate'])) {
|
||||
$duplicate = $this->checkForDuplicateObjects($object, $eventId);
|
||||
if ($duplicate) {
|
||||
$log->create();
|
||||
$log->save(array(
|
||||
'org' => $user['Organisation']['name'],
|
||||
'model' => 'Object',
|
||||
'model_id' => 0,
|
||||
'email' => $user['email'],
|
||||
'action' => 'add',
|
||||
'user_id' => $user['id'],
|
||||
'title' => 'Object dropped due to it being a duplicate and breakOnDuplicate being requested for Event ' . $eventId,
|
||||
'change' => 'Duplicate object found.',
|
||||
));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (empty($log)) {
|
||||
$log = ClassRegistry::init('Log');
|
||||
}
|
||||
|
@ -1443,7 +1474,7 @@ class MispObject extends AppModel
|
|||
}
|
||||
$this->__iteratedFetch($user, $params, $loop, $tmpfile, $exportTool, $exportToolParams, $elementCounter);
|
||||
$tmpfile->write($exportTool->footer($exportToolParams));
|
||||
return $tmpfile->finish();
|
||||
return $tmpfile;
|
||||
}
|
||||
|
||||
private function __iteratedFetch($user, &$params, &$loop, TmpFileTool $tmpfile, $exportTool, $exportToolParams, &$elementCounter = 0)
|
||||
|
|
|
@ -31,6 +31,8 @@ class ObjectTemplate extends AppModel
|
|||
public $validate = array(
|
||||
);
|
||||
|
||||
public $objectsDir = APP . 'files/misp-objects/objects';
|
||||
|
||||
public function afterFind($results, $primary = false)
|
||||
{
|
||||
foreach ($results as $k => $result) {
|
||||
|
@ -49,10 +51,9 @@ class ObjectTemplate extends AppModel
|
|||
|
||||
public function update($user = false, $type = false, $force = false)
|
||||
{
|
||||
$objectsDir = APP . 'files/misp-objects/objects';
|
||||
$directories = glob($objectsDir . '/*', GLOB_ONLYDIR);
|
||||
$directories = $this->getTemplateDirectoryPaths();
|
||||
foreach ($directories as $k => $dir) {
|
||||
$dir = str_replace($objectsDir, '', $dir);
|
||||
$dir = str_replace($this->objectsDir, '', $dir);
|
||||
$directories[$k] = $dir;
|
||||
}
|
||||
$updated = array();
|
||||
|
@ -60,10 +61,10 @@ class ObjectTemplate extends AppModel
|
|||
if ($type && '/' . $type != $dir) {
|
||||
continue;
|
||||
}
|
||||
if (!file_exists($objectsDir . DS . $dir . DS . 'definition.json')) {
|
||||
if (!file_exists($this->objectsDir . DS . $dir . DS . 'definition.json')) {
|
||||
continue;
|
||||
}
|
||||
$file = new File($objectsDir . DS . $dir . DS . 'definition.json');
|
||||
$file = new File($this->objectsDir . DS . $dir . DS . 'definition.json');
|
||||
$template = json_decode($file->read(), true);
|
||||
$file->close();
|
||||
if (!isset($template['version'])) {
|
||||
|
@ -316,4 +317,56 @@ class ObjectTemplate extends AppModel
|
|||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
public function getRawFromDisk($uuidOrName)
|
||||
{
|
||||
$template = [];
|
||||
if (Validation::uuid($uuidOrName)) {
|
||||
foreach ($this->readTemplatesFromDisk() as $templateFromDisk) {
|
||||
if ($templateFromDisk['uuid'] == $uuidOrName) {
|
||||
$template = $templateFromDisk;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$allTemplateNames = $this->getTemplateDirectoryPaths(false);
|
||||
if (in_array($uuidOrName, $allTemplateNames)) { // ensure the path is not out of scope
|
||||
$template = $this->readTemplateFromDisk($this->getFullPathFromTemplateName($uuidOrName));
|
||||
}
|
||||
}
|
||||
return $template;
|
||||
}
|
||||
|
||||
private function readTemplateFromDisk($path)
|
||||
{
|
||||
$file = new File($path, false);
|
||||
if (!$file->exists()) {
|
||||
return false;
|
||||
}
|
||||
$template = json_decode($file->read(), true);
|
||||
$file->close();
|
||||
return $template;
|
||||
}
|
||||
|
||||
private function readTemplatesFromDisk()
|
||||
{
|
||||
foreach ($this->getTemplateDirectoryPaths() as $dirpath) {
|
||||
$filepath = $dirpath . DS . 'definition.json';
|
||||
$template = $this->readTemplateFromDisk($filepath);
|
||||
if (isset($template['uuid'])) {
|
||||
yield $template;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getTemplateDirectoryPaths($fullPath=true)
|
||||
{
|
||||
$dir = new Folder($this->objectsDir, false);
|
||||
return $dir->read(true, false, $fullPath)[0];
|
||||
}
|
||||
|
||||
private function getFullPathFromTemplateName($templateName)
|
||||
{
|
||||
return $this->objectsDir . DS . $templateName . DS . 'definition.json';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
<?php
|
||||
App::uses('AppModel', 'Model');
|
||||
App::uses('ConnectionManager', 'Model');
|
||||
|
||||
/**
|
||||
* @property Event $Event
|
||||
*/
|
||||
class Organisation extends AppModel
|
||||
{
|
||||
public $useTable = 'organisations';
|
||||
|
@ -233,20 +237,27 @@ class Organisation extends AppModel
|
|||
return $existingOrg[$this->alias]['id'];
|
||||
}
|
||||
|
||||
public function createOrgFromName($name, $user_id, $local)
|
||||
/**
|
||||
* @param string $name Organisation name
|
||||
* @param int $userId Organisation creator
|
||||
* @param bool $local True if organisation should be marked as local
|
||||
* @return int Existing or newly created organisation ID
|
||||
* @throws Exception
|
||||
*/
|
||||
public function createOrgFromName($name, $userId, $local)
|
||||
{
|
||||
$existingOrg = $this->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('name' => $name)
|
||||
));
|
||||
$existingOrg = $this->find('first', [
|
||||
'recursive' => -1,
|
||||
'conditions' => ['name' => $name],
|
||||
'fields' => ['id'],
|
||||
]);
|
||||
if (empty($existingOrg)) {
|
||||
$this->create();
|
||||
$organisation = array(
|
||||
'uuid' =>CakeText::uuid(),
|
||||
'name' => $name,
|
||||
'local' => $local,
|
||||
'created_by' => $user_id
|
||||
);
|
||||
$organisation = [
|
||||
'name' => $name,
|
||||
'local' => $local,
|
||||
'created_by' => $userId,
|
||||
];
|
||||
$this->save($organisation);
|
||||
return $this->id;
|
||||
}
|
||||
|
@ -472,6 +483,82 @@ class Organisation extends AppModel
|
|||
return $suggestedOrg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide organisation view from users if they haven't yet contributed data and Security.hide_organisation_index_from_users is enabled
|
||||
*
|
||||
* @see Organisation::canSee if you want to check multiple orgs
|
||||
* @param array $user
|
||||
* @param int $orgId
|
||||
* @return bool
|
||||
*/
|
||||
public function canSee(array $user, $orgId)
|
||||
{
|
||||
if ($user['org_id'] == $orgId) {
|
||||
return true; // User can see his own org.
|
||||
}
|
||||
if (!$user['Role']['perm_sharing_group'] && Configure::read('Security.hide_organisation_index_from_users')) {
|
||||
// Check if there is event from given org that can current user see
|
||||
$eventConditions = $this->Event->createEventConditions($user);
|
||||
$eventConditions['AND']['Event.orgc_id'] = $orgId;
|
||||
$event = $this->Event->find('first', array(
|
||||
'fields' => array('Event.id'),
|
||||
'recursive' => -1,
|
||||
'conditions' => $eventConditions,
|
||||
));
|
||||
if (empty($event)) {
|
||||
$proposalConditions = $this->Event->ShadowAttribute->buildConditions($user);
|
||||
$proposalConditions['AND']['ShadowAttribute.org_id'] = $orgId;
|
||||
$proposal = $this->Event->ShadowAttribute->find('first', array(
|
||||
'fields' => array('ShadowAttribute.id'),
|
||||
'recursive' => -1,
|
||||
'conditions' => $proposalConditions,
|
||||
'contain' => ['Event', 'Attribute'],
|
||||
));
|
||||
if (empty($proposal)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create conditions for fetching orgs based on user permission.
|
||||
* @see Organisation::canSee if you want to check just one org
|
||||
* @param array $user
|
||||
* @return array|array[]
|
||||
*/
|
||||
public function createConditions(array $user)
|
||||
{
|
||||
if (!$user['Role']['perm_sharing_group'] && Configure::read('Security.hide_organisation_index_from_users')) {
|
||||
$allowedOrgs = [$user['org_id']];
|
||||
|
||||
$eventConditions = $this->Event->createEventConditions($user);
|
||||
$orgsWithEvent = $this->Event->find('column', [
|
||||
'fields' => ['Event.orgc_id'],
|
||||
'conditions' => $eventConditions,
|
||||
'unique' => true,
|
||||
]);
|
||||
$allowedOrgs = array_merge($allowedOrgs, $orgsWithEvent);
|
||||
|
||||
$proposalConditions = $this->Event->ShadowAttribute->buildConditions($user);
|
||||
// Do not check orgs that we already can see
|
||||
$proposalConditions['AND'][]['NOT'] = ['ShadowAttribute.org_id' => $allowedOrgs];
|
||||
$orgsWithProposal = $this->Event->ShadowAttribute->find('column', [
|
||||
'fields' => ['ShadowAttribute.org_id'],
|
||||
'conditions' => $proposalConditions,
|
||||
'contain' => ['Event', 'Attribute'],
|
||||
'unique' => true,
|
||||
'order' => false,
|
||||
]);
|
||||
|
||||
$allowedOrgs = array_merge($allowedOrgs, $orgsWithProposal);
|
||||
return ['AND' => ['id' => $allowedOrgs]];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private function getCountryGalaxyCluster()
|
||||
{
|
||||
static $list;
|
||||
|
@ -507,10 +594,9 @@ class Organisation extends AppModel
|
|||
*/
|
||||
public function getCountries()
|
||||
{
|
||||
$countries = ['International'];
|
||||
foreach ($this->getCountryGalaxyCluster() as $country) {
|
||||
$countries[] = $country['description'];
|
||||
}
|
||||
$countries = array_column($this->getCountryGalaxyCluster(), 'description');
|
||||
sort($countries);
|
||||
array_unshift($countries, 'Internation');
|
||||
return $countries;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
<?php
|
||||
App::uses('AppModel', 'Model');
|
||||
|
||||
/**
|
||||
* @property User $User
|
||||
*/
|
||||
class Role extends AppModel
|
||||
{
|
||||
public $recursive = -1;
|
||||
public $validate = array(
|
||||
'valueNotEmpty' => array(
|
||||
'rule' => array('valueNotEmpty'),
|
||||
|
@ -232,6 +236,18 @@ class Role extends AppModel
|
|||
return true;
|
||||
}
|
||||
|
||||
public function afterSave($created, $options = array())
|
||||
{
|
||||
// After role change, update `date_modified` field for all user with this role to apply this change to already
|
||||
// logged users.
|
||||
if (!$created && !empty($this->data)) {
|
||||
$roleId = $this->data['Role']['id'];
|
||||
$this->User->updateAll(['date_modified' => time()], ['role_id' => $roleId]);
|
||||
}
|
||||
|
||||
parent::afterSave($created, $options);
|
||||
}
|
||||
|
||||
public function afterFind($results, $primary = false)
|
||||
{
|
||||
foreach ($results as $key => $val) {
|
||||
|
|
5081
app/Model/Server.php
5081
app/Model/Server.php
File diff suppressed because it is too large
Load Diff
|
@ -8,6 +8,8 @@ App::uses('ComplexTypeTool', 'Tools');
|
|||
/**
|
||||
* @property Event $Event
|
||||
* @property Attribute $Attribute
|
||||
* @property-read array $typeDefinitions
|
||||
* @property-read array $categoryDefinitions
|
||||
*/
|
||||
class ShadowAttribute extends AppModel
|
||||
{
|
||||
|
@ -73,9 +75,6 @@ class ShadowAttribute extends AppModel
|
|||
'attachment'
|
||||
);
|
||||
|
||||
// definitions of categories
|
||||
public $categoryDefinitions;
|
||||
|
||||
public $order = array("ShadowAttribute.event_id" => "DESC", "ShadowAttribute.type" => "ASC");
|
||||
|
||||
public $validate = array(
|
||||
|
@ -149,11 +148,22 @@ class ShadowAttribute extends AppModel
|
|||
)
|
||||
);
|
||||
|
||||
public function __construct($id = false, $table = null, $ds = null)
|
||||
public function __isset($name)
|
||||
{
|
||||
parent::__construct($id, $table, $ds);
|
||||
$this->categoryDefinitions = $this->Attribute->categoryDefinitions;
|
||||
$this->typeDefinitions = $this->Attribute->typeDefinitions;
|
||||
if ($name === 'typeDefinitions' || $name === 'categoryDefinitions') {
|
||||
return true;
|
||||
}
|
||||
return parent::__isset($name);
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
if ($name === 'categoryDefinitions') {
|
||||
return $this->Attribute->categoryDefinitions;
|
||||
} else if ($name === 'typeDefinitions') {
|
||||
return $this->Attribute->typeDefinitions;
|
||||
}
|
||||
return parent::__get($name);
|
||||
}
|
||||
|
||||
// The Associations below have been created with all possible keys, those that are not needed can be removed
|
||||
|
@ -354,7 +364,7 @@ class ShadowAttribute extends AppModel
|
|||
{
|
||||
$category = $this->data['ShadowAttribute']['category'];
|
||||
if (isset($this->categoryDefinitions[$category]['types'])) {
|
||||
return in_array($fields['type'], $this->categoryDefinitions[$category]['types']);
|
||||
return in_array($fields['type'], $this->categoryDefinitions[$category]['types'], true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -481,13 +491,13 @@ class ShadowAttribute extends AppModel
|
|||
*/
|
||||
public function getEventContributors($eventId)
|
||||
{
|
||||
$orgs = $this->find('all', array(
|
||||
'fields' => array('DISTINCT(ShadowAttribute.org_id)'),
|
||||
$orgIds = $this->find('column', array(
|
||||
'fields' => array('ShadowAttribute.org_id'),
|
||||
'conditions' => array('event_id' => $eventId),
|
||||
'recursive' => -1,
|
||||
'unique' => true,
|
||||
'order' => false
|
||||
));
|
||||
if (empty($orgs)) {
|
||||
if (empty($orgIds)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -495,8 +505,8 @@ class ShadowAttribute extends AppModel
|
|||
return $this->Organisation->find('list', array(
|
||||
'recursive' => -1,
|
||||
'fields' => array('id', 'name'),
|
||||
'conditions' => array('Organisation.id' => Hash::extract($orgs, "{n}.ShadowAttribute.org_id")))
|
||||
);
|
||||
'conditions' => array('Organisation.id' => $orgIds)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -809,4 +819,49 @@ class ShadowAttribute extends AppModel
|
|||
));
|
||||
}
|
||||
}
|
||||
|
||||
public function saveAttachment($shadowAttribute, $path_suffix='')
|
||||
{
|
||||
$result = $this->loadAttachmentTool()->saveShadow($shadowAttribute['event_id'], $shadowAttribute['id'], $shadowAttribute['data'], $path_suffix);
|
||||
if ($result) {
|
||||
$this->loadAttachmentScan()->backgroundScan(AttachmentScan::TYPE_SHADOW_ATTRIBUTE, $shadowAttribute);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $shadowAttribute
|
||||
* @param bool $thumbnail
|
||||
* @param int $maxWidth - When $thumbnail is true
|
||||
* @param int $maxHeight - When $thumbnail is true
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getPictureData(array $shadowAttribute, $thumbnail=false, $maxWidth=200, $maxHeight=200)
|
||||
{
|
||||
if ($thumbnail && extension_loaded('gd')) {
|
||||
if ($maxWidth == 200 && $maxHeight == 200) {
|
||||
// Return thumbnail directly if already exists
|
||||
try {
|
||||
return $this->getAttachment($shadowAttribute['ShadowAttribute'], $path_suffix = '_thumbnail');
|
||||
} catch (NotFoundException $e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
// Thumbnail doesn't exists, we need to generate it
|
||||
$imageData = $this->getAttachment($shadowAttribute['ShadowAttribute']);
|
||||
$imageData = $this->Attribute->resizeImage($imageData, $maxWidth, $maxHeight);
|
||||
|
||||
// Save just when requested default thumbnail size
|
||||
if ($maxWidth == 200 && $maxHeight == 200) {
|
||||
$shadowAttribute['ShadowAttribute']['data'] = $imageData;
|
||||
$this->saveAttachment($shadowAttribute['ShadowAttribute'], $path_suffix='_thumbnail');
|
||||
}
|
||||
} else {
|
||||
$imageData = $this->getAttachment($shadowAttribute['ShadowAttribute']);
|
||||
}
|
||||
|
||||
return $imageData;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,26 +111,20 @@ class SharingGroup extends AppModel
|
|||
return false;
|
||||
}
|
||||
|
||||
public function fetchAllAuthorisedForServer($server)
|
||||
{
|
||||
$sgs = $this->SharingGroupOrg->fetchAllAuthorised($server['RemoteOrg']['id']);
|
||||
$sgs = array_merge($sgs, $this->SharingGroupServer->fetchAllSGsForServer($server['Server']['id']));
|
||||
return $sgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all sharing groups that the user is allowed to see.
|
||||
* Scope can be:
|
||||
* - full: Entire SG object with all organisations and servers attached
|
||||
* - simplified: Just imporant fields from SG, organisations and servers
|
||||
* - name: array in ID => name key => value format
|
||||
* - uuid
|
||||
* - false: array with all IDs
|
||||
* - simplified: Just important fields from SG, organisations and servers
|
||||
* - sharing_group: specific scope that fetch just necessary information for generating distribution graph
|
||||
* - name: array in ID => name format
|
||||
* - uuid: array in ID => uuid format
|
||||
* - false: array with all sharing group IDs
|
||||
*
|
||||
* @param array $user
|
||||
* @param string|false $scope
|
||||
* @param bool $active
|
||||
* @param int|false $id
|
||||
* @param bool $active If true, return only active sharing groups
|
||||
* @param int|array|false $id
|
||||
* @return array
|
||||
*/
|
||||
public function fetchAllAuthorised(array $user, $scope = false, $active = false, $id = false)
|
||||
|
@ -144,11 +138,10 @@ class SharingGroup extends AppModel
|
|||
}
|
||||
|
||||
if ($user['Role']['perm_site_admin']) {
|
||||
$ids = array_values($this->find('list', array(
|
||||
'recursive' => -1,
|
||||
$ids = $this->find('column', array(
|
||||
'fields' => array('id'),
|
||||
'conditions' => $conditions
|
||||
)));
|
||||
));
|
||||
} else {
|
||||
$ids = array_unique(array_merge(
|
||||
$this->SharingGroupServer->fetchAllAuthorised(),
|
||||
|
@ -200,6 +193,23 @@ class SharingGroup extends AppModel
|
|||
'order' => 'SharingGroup.name ASC'
|
||||
));
|
||||
return $this->appendOrgsAndServers($sgs, $fieldsOrg, $fieldsServer);
|
||||
} elseif ($scope === 'distribution_graph') {
|
||||
// Specific scope that fetch just necessary information for distribution graph
|
||||
// @see DistributionGraphTool
|
||||
$canSeeOrgs = $user['Role']['perm_sharing_group'] || !Configure::read('Security.hide_organisations_in_sharing_groups');
|
||||
$sgs = $this->find('all', array(
|
||||
'contain' => $canSeeOrgs ? ['SharingGroupOrg' => ['org_id']] : [],
|
||||
'conditions' => $conditions,
|
||||
'fields' => ['SharingGroup.id', 'SharingGroup.name', 'SharingGroup.org_id'],
|
||||
'order' => 'SharingGroup.name ASC'
|
||||
));
|
||||
if ($canSeeOrgs) {
|
||||
return $this->appendOrgsAndServers($sgs, ['id', 'name'], []);
|
||||
}
|
||||
foreach ($sgs as &$sg) {
|
||||
$sg['SharingGroupOrg'] = [];
|
||||
}
|
||||
return $sgs;
|
||||
} elseif ($scope === 'name') {
|
||||
$sgs = $this->find('list', array(
|
||||
'recursive' => -1,
|
||||
|
@ -230,8 +240,10 @@ class SharingGroup extends AppModel
|
|||
{
|
||||
$orgsToFetch = [];
|
||||
$serverToFetch = [];
|
||||
foreach($sharingGroups as $sg) {
|
||||
$orgsToFetch[$sg['SharingGroup']['org_id']] = true;
|
||||
foreach ($sharingGroups as $sg) {
|
||||
if (isset($sg['SharingGroup']['org_id'])) {
|
||||
$orgsToFetch[$sg['SharingGroup']['org_id']] = true;
|
||||
}
|
||||
if (isset($sg['SharingGroupOrg'])) {
|
||||
foreach ($sg['SharingGroupOrg'] as $sgo) {
|
||||
$orgsToFetch[$sgo['org_id']] = true;
|
||||
|
@ -272,7 +284,7 @@ class SharingGroup extends AppModel
|
|||
}
|
||||
|
||||
foreach ($sharingGroups as &$sg) {
|
||||
if (isset($orgsById[$sg['SharingGroup']['org_id']])) {
|
||||
if (isset($sg['SharingGroup']['org_id']) && isset($orgsById[$sg['SharingGroup']['org_id']])) {
|
||||
$sg['Organisation'] = $orgsById[$sg['SharingGroup']['org_id']];
|
||||
}
|
||||
|
||||
|
@ -426,23 +438,23 @@ class SharingGroup extends AppModel
|
|||
return $this->__sgAuthorisationCache['access'][boolval($adminCheck)][$id];
|
||||
}
|
||||
if (Validation::uuid($id)) {
|
||||
$sgid = $this->SharingGroup->find('first', array(
|
||||
$sgid = $this->find('first', array(
|
||||
'conditions' => array('SharingGroup.uuid' => $id),
|
||||
'recursive' => -1,
|
||||
'fields' => array('SharingGroup.id')
|
||||
));
|
||||
if (empty($sgid)) {
|
||||
throw new MethodNotAllowedException('Invalid sharing group.');
|
||||
return false;
|
||||
}
|
||||
$id = $sgid['SharingGroup']['id'];
|
||||
} else {
|
||||
if (!$this->exists($id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!isset($user['id'])) {
|
||||
throw new MethodNotAllowedException('Invalid user.');
|
||||
}
|
||||
$this->id = $id;
|
||||
if (!$this->exists()) {
|
||||
return false;
|
||||
}
|
||||
if (($adminCheck && $user['Role']['perm_site_admin']) || $this->SharingGroupServer->checkIfAuthorised($id) || $this->SharingGroupOrg->checkIfAuthorised($id, $user['org_id'])) {
|
||||
$this->__sgAuthorisationCache['access'][boolval($adminCheck)][$id] = true;
|
||||
return true;
|
||||
|
@ -451,24 +463,28 @@ class SharingGroup extends AppModel
|
|||
return false;
|
||||
}
|
||||
|
||||
public function checkIfOwner($user, $id)
|
||||
/**
|
||||
* @param array $user
|
||||
* @param string|int $id Sharing group ID or UUID
|
||||
* @return bool False if sharing group doesn't exists or user org is not sharing group owner
|
||||
*/
|
||||
public function checkIfOwner(array $user, $id)
|
||||
{
|
||||
if (!isset($user['id'])) {
|
||||
throw new MethodNotAllowedException('Invalid user.');
|
||||
}
|
||||
$this->id = $id;
|
||||
if (!$this->exists()) {
|
||||
$sg = $this->find('first', array(
|
||||
'conditions' => Validation::uuid($id) ? ['SharingGroup.uuid' => $id] : ['SharingGroup.id' => $id],
|
||||
'recursive' => -1,
|
||||
'fields' => array('org_id'),
|
||||
));
|
||||
if (empty($sg)) {
|
||||
return false;
|
||||
}
|
||||
if ($user['Role']['perm_site_admin']) {
|
||||
return true;
|
||||
}
|
||||
$sg = $this->find('first', array(
|
||||
'conditions' => array('SharingGroup.id' => $id),
|
||||
'recursive' => -1,
|
||||
'fields' => array('id', 'org_id'),
|
||||
));
|
||||
return ($sg['SharingGroup']['org_id'] == $user['org_id']);
|
||||
return $sg['SharingGroup']['org_id'] == $user['org_id'];
|
||||
}
|
||||
|
||||
// Get all organisation ids that can see a SG
|
||||
|
@ -684,7 +700,7 @@ class SharingGroup extends AppModel
|
|||
if ($force) {
|
||||
$sgids = $existingSG['SharingGroup']['id'];
|
||||
$editedSG = $existingSG['SharingGroup'];
|
||||
$attributes = array('name', 'releasability', 'description', 'created', 'modified', 'active');
|
||||
$attributes = ['name', 'releasability', 'description', 'created', 'modified', 'active', 'roaming'];
|
||||
foreach ($attributes as $a) {
|
||||
if (isset($sg[$a])) {
|
||||
$editedSG[$a] = $sg[$a];
|
||||
|
|
|
@ -78,12 +78,11 @@ class SharingGroupOrg extends AppModel
|
|||
*/
|
||||
public function fetchAllAuthorised($org_id)
|
||||
{
|
||||
$sgs = $this->find('list', array(
|
||||
$sgs = $this->find('column', array(
|
||||
'conditions' => array('org_id' => $org_id),
|
||||
'recursive' => -1,
|
||||
'fields' => array('sharing_group_id'),
|
||||
'fields' => array('SharingGroupOrg.sharing_group_id'),
|
||||
));
|
||||
return array_values($sgs);
|
||||
return $sgs;
|
||||
}
|
||||
|
||||
// pass a sharing group ID and an organisation ID, returns true if it has a matching attached organisation object
|
||||
|
|
|
@ -87,12 +87,11 @@ class SharingGroupServer extends AppModel
|
|||
// This basically lists all SGs that allow everyone on the instance to see events tagged with it
|
||||
public function fetchAllAuthorised()
|
||||
{
|
||||
$sgs = $this->find('list', array(
|
||||
$sgs = $this->find('column', array(
|
||||
'conditions' => array('all_orgs' => 1, 'server_id' => 0),
|
||||
'recursive' => -1,
|
||||
'fields' => array('sharing_group_id'),
|
||||
'fields' => array('SharingGroupServer.sharing_group_id'),
|
||||
));
|
||||
return array_values($sgs);
|
||||
return $sgs;
|
||||
}
|
||||
|
||||
// pass a sharing group ID, returns true if it has an attached server object with "all_orgs" ticked
|
||||
|
@ -108,20 +107,4 @@ class SharingGroupServer extends AppModel
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function fetchAllSGsForServer($server_id)
|
||||
{
|
||||
$sgs = $this->find('all', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('server_id' => $server_id)
|
||||
));
|
||||
if (empty($sgs)) {
|
||||
return array();
|
||||
}
|
||||
$sgids = array();
|
||||
foreach ($sgs as $temp) {
|
||||
$sgids[] = $temp[$this->alias]['id'];
|
||||
}
|
||||
return $sgids;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,8 @@ class Sighting extends AppModel
|
|||
// Possible values of `Plugin.Sightings_policy` setting
|
||||
const SIGHTING_POLICY_EVENT_OWNER = 0,
|
||||
SIGHTING_POLICY_SIGHTING_REPORTER = 1,
|
||||
SIGHTING_POLICY_EVERYONE = 2;
|
||||
SIGHTING_POLICY_EVERYONE = 2,
|
||||
SIGHTING_POLICY_HOST_ORG = 3; // the same as SIGHTING_POLICY_EVENT_OWNER, but also sightings from host org are visible
|
||||
|
||||
private $orgCache = [];
|
||||
|
||||
|
@ -124,23 +125,54 @@ class Sighting extends AppModel
|
|||
}
|
||||
}
|
||||
|
||||
public function captureSighting($sighting, $attribute_id, $event_id, $user)
|
||||
/**
|
||||
* @param array $sightings
|
||||
* @param int $attributeId
|
||||
* @param int $eventId
|
||||
* @param array $user
|
||||
* @return bool
|
||||
*/
|
||||
public function captureSightings(array $sightings, $attributeId, $eventId, array $user)
|
||||
{
|
||||
$org_id = 0;
|
||||
if (!empty($sighting['Organisation'])) {
|
||||
$org_id = $this->Organisation->captureOrg($sighting['Organisation'], $user);
|
||||
}
|
||||
if (isset($sighting['id'])) {
|
||||
// Since sightings are immutable (it is not possible to change it from web interface), we can check
|
||||
// if sighting with given uuid already exists and skip them
|
||||
$existingSighting = $this->existing($sightings);
|
||||
|
||||
// Fetch existing organisations in bulk
|
||||
$existingOrganisations = $this->existingOrganisations($sightings);
|
||||
|
||||
$toSave = [];
|
||||
foreach ($sightings as $sighting) {
|
||||
if (isset($existingSighting[$sighting['uuid']])) {
|
||||
continue; // already exists, skip
|
||||
}
|
||||
|
||||
$orgId = 0;
|
||||
if (isset($sighting['Organisation'])) {
|
||||
if (isset($existingOrganisations[$sighting['Organisation']['uuid']])) {
|
||||
$orgId = $existingOrganisations[$sighting['Organisation']['uuid']];
|
||||
} else {
|
||||
$orgId = $this->Organisation->captureOrg($sighting['Organisation'], $user);
|
||||
}
|
||||
}
|
||||
unset($sighting['id']);
|
||||
|
||||
$sighting['org_id'] = $orgId;
|
||||
$sighting['event_id'] = $eventId;
|
||||
$sighting['attribute_id'] = $attributeId;
|
||||
$toSave[] = $sighting;
|
||||
}
|
||||
$sighting['org_id'] = $org_id;
|
||||
$sighting['event_id'] = $event_id;
|
||||
$sighting['attribute_id'] = $attribute_id;
|
||||
$this->create();
|
||||
return $this->save($sighting);
|
||||
|
||||
return $this->saveMany($toSave);
|
||||
}
|
||||
|
||||
public function getSighting($id, $user)
|
||||
/**
|
||||
* @param int $id
|
||||
* @param array $user
|
||||
* @param bool $withEvent
|
||||
* @return array
|
||||
*/
|
||||
public function getSighting($id, array $user, $withEvent = true)
|
||||
{
|
||||
$sighting = $this->find('first', array(
|
||||
'recursive' => -1,
|
||||
|
@ -149,10 +181,7 @@ class Sighting extends AppModel
|
|||
'fields' => array('Attribute.value', 'Attribute.id', 'Attribute.uuid', 'Attribute.type', 'Attribute.category', 'Attribute.to_ids')
|
||||
),
|
||||
'Event' => array(
|
||||
'fields' => array('Event.id', 'Event.uuid', 'Event.orgc_id', 'Event.org_id', 'Event.info'),
|
||||
'Orgc' => array(
|
||||
'fields' => array('Orgc.name')
|
||||
)
|
||||
'fields' => $withEvent ? ['Event.id', 'Event.uuid', 'Event.orgc_id', 'Event.org_id', 'Event.info'] : ['Event.org_id'],
|
||||
)
|
||||
),
|
||||
'conditions' => array('Sighting.id' => $id)
|
||||
|
@ -161,11 +190,7 @@ class Sighting extends AppModel
|
|||
return array();
|
||||
}
|
||||
|
||||
if (!isset($event)) {
|
||||
$event = array('Event' => $sighting['Event']);
|
||||
}
|
||||
|
||||
$ownEvent = $user['Role']['perm_site_admin'] || $event['Event']['org_id'] == $user['org_id'];
|
||||
$ownEvent = $user['Role']['perm_site_admin'] || $sighting['Event']['org_id'] == $user['org_id'];
|
||||
if (!$ownEvent) {
|
||||
$sightingPolicy = $this->sightingsPolicy();
|
||||
// if sighting policy == 0 then return false if the sighting doesn't belong to the user
|
||||
|
@ -180,27 +205,30 @@ class Sighting extends AppModel
|
|||
return array();
|
||||
}
|
||||
}
|
||||
}
|
||||
$anonymise = Configure::read('Plugin.Sightings_anonymise');
|
||||
if ($anonymise) {
|
||||
if ($sighting['Sighting']['org_id'] != $user['org_id']) {
|
||||
unset($sighting['Sighting']['org_id']);
|
||||
unset($sighting['Organisation']);
|
||||
else if ($sightingPolicy === self::SIGHTING_POLICY_HOST_ORG) {
|
||||
if ($sighting['Sighting']['org_id'] != $user['org_id'] || $sighting['Sighting']['org_id'] != Configure::read('MISP.host_org_id')) {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
}
|
||||
// rearrange it to match the event format of fetchevent
|
||||
if (isset($sighting['Organisation'])) {
|
||||
$sighting['Sighting']['Organisation'] = $sighting['Organisation'];
|
||||
unset($sighting['Organisation']);
|
||||
|
||||
// Put event organisation name from cache
|
||||
if ($withEvent) {
|
||||
$sighting['Event']['Orgc']['name'] = $this->getOrganisationById($sighting['Event']['orgc_id'])['name'];
|
||||
}
|
||||
|
||||
$anonymise = Configure::read('Plugin.Sightings_anonymise');
|
||||
if ($anonymise && $sighting['Sighting']['org_id'] != $user['org_id']) {
|
||||
unset($sighting['Sighting']['org_id']);
|
||||
}
|
||||
// rearrange it to match the event format of fetchevent
|
||||
$result = array(
|
||||
'Sighting' => $sighting['Sighting']
|
||||
);
|
||||
$result['Sighting']['Event'] = $sighting['Event'];
|
||||
$result['Sighting']['Attribute'] = $sighting['Attribute'];
|
||||
if (!empty($sighting['Organisation'])) {
|
||||
$result['Sighting']['Organisation'] = $sighting['Organisation'];
|
||||
if ($withEvent) {
|
||||
$result['Sighting']['Event'] = $sighting['Event'];
|
||||
}
|
||||
$result['Sighting']['Attribute'] = $sighting['Attribute'];
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
@ -223,6 +251,8 @@ class Sighting extends AppModel
|
|||
$sightingsPolicy = $this->sightingsPolicy();
|
||||
if ($sightingsPolicy === self::SIGHTING_POLICY_EVENT_OWNER) {
|
||||
$conditions['Sighting.org_id'] = $user['org_id'];
|
||||
} else if ($sightingsPolicy === self::SIGHTING_POLICY_HOST_ORG) {
|
||||
$conditions['Sighting.org_id'] = [$user['org_id'], Configure::read('MISP.host_org_id')];
|
||||
}
|
||||
// TODO: Currently, we dont support `SIGHTING_POLICY_SIGHTING_REPORTER` for tags
|
||||
$sparklineData = [];
|
||||
|
@ -231,7 +261,7 @@ class Sighting extends AppModel
|
|||
$objectElement = ucfirst($context) . 'Tag';
|
||||
foreach ($sightings as $sighting) {
|
||||
$tagId = $sighting[$objectElement]['tag_id'];
|
||||
$date = $sighting['Sighting']['date_sighting'];
|
||||
$date = $sighting['Sighting']['date'];
|
||||
$count = (int)$sighting['Sighting']['sighting_count'];
|
||||
|
||||
if (isset($sparklineData[$tagId][$date]['sighting'])) {
|
||||
|
@ -269,6 +299,8 @@ class Sighting extends AppModel
|
|||
if (!$this->isReporter($attribute['Event']['id'], $user['org_id'])) {
|
||||
continue; // skip attribute
|
||||
}
|
||||
} else if ($sightingsPolicy === self::SIGHTING_POLICY_HOST_ORG) {
|
||||
$attributeConditions['Sighting.org_id'] = [$user['org_id'], Configure::read('MISP.host_org_id')];
|
||||
}
|
||||
}
|
||||
$conditions['OR'][] = $attributeConditions;
|
||||
|
@ -290,19 +322,21 @@ class Sighting extends AppModel
|
|||
return ['data' => [], 'csv' => []];
|
||||
}
|
||||
|
||||
$sightingPolicy = $this->sightingsPolicy();
|
||||
$sightingsPolicy = $this->sightingsPolicy();
|
||||
|
||||
$conditions = [];
|
||||
foreach ($events as $event) {
|
||||
$eventCondition = ['Sighting.event_id' => $event['Event']['id']];
|
||||
$ownEvent = $user['Role']['perm_site_admin'] || $event['Event']['org_id'] == $user['org_id'];
|
||||
if (!$ownEvent) {
|
||||
if ($sightingPolicy === self::SIGHTING_POLICY_EVENT_OWNER) {
|
||||
if ($sightingsPolicy === self::SIGHTING_POLICY_EVENT_OWNER) {
|
||||
$eventCondition['Sighting.org_id'] = $user['org_id'];
|
||||
} else if ($sightingPolicy === self::SIGHTING_POLICY_SIGHTING_REPORTER) {
|
||||
} else if ($sightingsPolicy === self::SIGHTING_POLICY_SIGHTING_REPORTER) {
|
||||
if (!$this->isReporter($event['Event']['id'], $user['org_id'])) {
|
||||
continue;
|
||||
}
|
||||
} else if ($sightingsPolicy === self::SIGHTING_POLICY_HOST_ORG) {
|
||||
$eventCondition['Sighting.org_id'] = [$user['org_id'], Configure::read('MISP.host_org_id')];
|
||||
}
|
||||
}
|
||||
$conditions['OR'][] = $eventCondition;
|
||||
|
@ -324,18 +358,18 @@ class Sighting extends AppModel
|
|||
}
|
||||
|
||||
// Returns date in `Y-m-d` format
|
||||
$this->virtualFields['date_sighting'] = $this->dateVirtualColumn();
|
||||
$this->virtualFields['date'] = $this->dateVirtualColumn();
|
||||
$this->virtualFields['sighting_count'] = 'COUNT(id)';
|
||||
$this->virtualFields['last_timestamp'] = 'MAX(date_sighting)';
|
||||
$groupedSightings = $this->find('all', array(
|
||||
'conditions' => $conditions,
|
||||
'fields' => ['org_id', 'attribute_id', 'type', 'date_sighting', 'last_timestamp', 'sighting_count'],
|
||||
'fields' => ['org_id', 'attribute_id', 'type', 'date', 'last_timestamp', 'sighting_count'],
|
||||
'recursive' => -1,
|
||||
'group' => ['org_id', 'attribute_id', 'type', 'date_sighting'],
|
||||
'order' => ['date_sighting'], // from oldest
|
||||
'group' => ['org_id', 'attribute_id', 'type', 'date'],
|
||||
'order' => ['date'], // from oldest
|
||||
));
|
||||
unset(
|
||||
$this->virtualFields['date_sighting'],
|
||||
$this->virtualFields['date'],
|
||||
$this->virtualFields['sighting_count'],
|
||||
$this->virtualFields['last_timestamp']
|
||||
);
|
||||
|
@ -361,17 +395,17 @@ class Sighting extends AppModel
|
|||
]
|
||||
]);
|
||||
// Returns date in `Y-m-d` format
|
||||
$this->virtualFields['date_sighting'] = $this->dateVirtualColumn();
|
||||
$this->virtualFields['date'] = $this->dateVirtualColumn();
|
||||
$this->virtualFields['sighting_count'] = 'COUNT(Sighting.id)';
|
||||
$sightings = $this->find('all', array(
|
||||
$sightings = $this->find('all', [
|
||||
'recursive' => -1,
|
||||
'contain' => [ucfirst($context) . 'Tag'],
|
||||
'conditions' => $conditions,
|
||||
'fields' => [ucfirst($context) . 'Tag.tag_id', 'date_sighting', 'sighting_count'],
|
||||
'group' => [ucfirst($context) . 'Tag.id', 'date_sighting'],
|
||||
'order' => ['date_sighting'], // from oldest
|
||||
));
|
||||
unset($this->virtualFields['date_sighting'], $this->virtualFields['sighting_count']);
|
||||
'fields' => [ucfirst($context) . 'Tag.tag_id', 'date', 'sighting_count'],
|
||||
'group' => [ucfirst($context) . 'Tag.tag_id', 'date'],
|
||||
'order' => ['date'], // from oldest
|
||||
]);
|
||||
unset($this->virtualFields['date'], $this->virtualFields['sighting_count']);
|
||||
return $sightings;
|
||||
}
|
||||
|
||||
|
@ -389,7 +423,7 @@ class Sighting extends AppModel
|
|||
$type = $this->type[$sighting['type']];
|
||||
$orgName = isset($sighting['Organisation']['name']) ? $sighting['Organisation']['name'] : __('Others');
|
||||
$count = (int)$sighting['sighting_count'];
|
||||
$inRange = strtotime($sighting['date_sighting']) >= $range;
|
||||
$inRange = strtotime($sighting['date']) >= $range;
|
||||
|
||||
foreach ([$sighting['attribute_id'], 'all'] as $needle) {
|
||||
if (!isset($sightingsData[$needle][$type])) {
|
||||
|
@ -407,10 +441,10 @@ class Sighting extends AppModel
|
|||
}
|
||||
|
||||
if ($inRange) {
|
||||
if (isset($sparklineData[$needle][$sighting['date_sighting']][$type])) {
|
||||
$sparklineData[$needle][$sighting['date_sighting']][$type] += $count;
|
||||
if (isset($sparklineData[$needle][$sighting['date']][$type])) {
|
||||
$sparklineData[$needle][$sighting['date']][$type] += $count;
|
||||
} else {
|
||||
$sparklineData[$needle][$sighting['date_sighting']][$type] = $count;
|
||||
$sparklineData[$needle][$sighting['date']][$type] = $count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -517,13 +551,15 @@ class Sighting extends AppModel
|
|||
|
||||
$ownEvent = $user['Role']['perm_site_admin'] || $event['Event']['org_id'] == $user['org_id'];
|
||||
if (!$ownEvent) {
|
||||
$sightingPolicy = $this->sightingsPolicy();
|
||||
if ($sightingPolicy === self::SIGHTING_POLICY_EVENT_OWNER) {
|
||||
$sightingsPolicy = $this->sightingsPolicy();
|
||||
if ($sightingsPolicy === self::SIGHTING_POLICY_EVENT_OWNER) {
|
||||
$conditions['Sighting.org_id'] = $user['org_id'];
|
||||
} elseif ($sightingPolicy === self::SIGHTING_POLICY_SIGHTING_REPORTER) {
|
||||
} elseif ($sightingsPolicy === self::SIGHTING_POLICY_SIGHTING_REPORTER) {
|
||||
if (!$this->isReporter($event['Event']['id'], $user['org_id'])) {
|
||||
return array();
|
||||
}
|
||||
} else if ($sightingsPolicy === self::SIGHTING_POLICY_HOST_ORG) {
|
||||
$conditions['Sighting.org_id'] = [$user['org_id'], Configure::read('MISP.host_org_id')];
|
||||
}
|
||||
}
|
||||
if ($extraConditions !== false) {
|
||||
|
@ -659,6 +695,10 @@ class Sighting extends AppModel
|
|||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
* @deprecated
|
||||
*/
|
||||
public function addUuids()
|
||||
{
|
||||
$sightings = $this->find('all', array(
|
||||
|
@ -743,10 +783,14 @@ class Sighting extends AppModel
|
|||
return $sightings; // site admin can see all sightings, do not limit him
|
||||
}
|
||||
$sightingsPolicy = $this->sightingsPolicy();
|
||||
if ($sightingsPolicy === self::SIGHTING_POLICY_EVENT_OWNER) {
|
||||
if ($sightingsPolicy === self::SIGHTING_POLICY_EVENT_OWNER || $sightingsPolicy === self::SIGHTING_POLICY_HOST_ORG) {
|
||||
$userOrgId = $user['org_id'];
|
||||
$allowedOrgs = [$userOrgId];
|
||||
if ($sightingsPolicy === self::SIGHTING_POLICY_HOST_ORG) {
|
||||
$allowedOrgs[] = Configure::read('MISP.host_org_id');
|
||||
}
|
||||
foreach ($sightings as $k => $sighting) {
|
||||
if ($eventOwnerOrgIdList[$sighting['Sighting']['event_id']] !== $userOrgId && $sighting['Sighting']['org_id'] !== $userOrgId) {
|
||||
if ($eventOwnerOrgIdList[$sighting['Sighting']['event_id']] !== $userOrgId && !in_array($sighting['Sighting']['org_id'], $allowedOrgs)) {
|
||||
unset($sightings[$k]);
|
||||
}
|
||||
}
|
||||
|
@ -789,9 +833,9 @@ class Sighting extends AppModel
|
|||
if (isset($filters['context']) && !in_array($filters['context'], $allowedContext, true)) {
|
||||
throw new MethodNotAllowedException(__('Invalid context.'));
|
||||
}
|
||||
// ensure that an id is provided if context is set
|
||||
if (!empty($filters['context']) && !isset($filters['id'])) {
|
||||
throw new MethodNotAllowedException(__('An id must be provided if the context is set.'));
|
||||
// ensure that an id or uuid is provided if context is set
|
||||
if (!empty($filters['context']) && !(isset($filters['id']) || isset($filters['uuid'])) ) {
|
||||
throw new MethodNotAllowedException(__('An ID or UUID must be provided if the context is set.'));
|
||||
}
|
||||
|
||||
if (!isset($this->validFormats[$returnFormat][1])) {
|
||||
|
@ -811,7 +855,9 @@ class Sighting extends AppModel
|
|||
} else {
|
||||
$timeCondition = '30d';
|
||||
}
|
||||
$conditions = $this->Attribute->setTimestampConditions($timeCondition, array(), $scope = 'Sighting.date_sighting');
|
||||
|
||||
$contain = [];
|
||||
$conditions = $this->Attribute->setTimestampConditions($timeCondition, [], $scope = 'Sighting.date_sighting');
|
||||
|
||||
if (isset($filters['type'])) {
|
||||
$conditions['Sighting.type'] = $filters['type'];
|
||||
|
@ -824,7 +870,11 @@ class Sighting extends AppModel
|
|||
}
|
||||
foreach ($filters['org_id'] as $k => $org_id) {
|
||||
if (Validation::uuid($org_id)) {
|
||||
$org = $this->Organisation->find('first', array('conditions' => array('Organisation.uuid' => $org_id), 'recursive' => -1, 'fields' => array('Organisation.id')));
|
||||
$org = $this->Organisation->find('first', array(
|
||||
'conditions' => array('Organisation.uuid' => $org_id),
|
||||
'recursive' => -1,
|
||||
'fields' => array('Organisation.id'),
|
||||
));
|
||||
if (empty($org)) {
|
||||
$filters['org_id'][$k] = -1;
|
||||
} else {
|
||||
|
@ -847,56 +897,37 @@ class Sighting extends AppModel
|
|||
}
|
||||
}
|
||||
|
||||
// fetch sightings matching the query
|
||||
$sightings = $this->find('list', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => $conditions,
|
||||
'fields' => array('id'),
|
||||
));
|
||||
$sightings = array_values($sightings);
|
||||
|
||||
$filters['requested_attributes'] = array('id', 'attribute_id', 'event_id', 'org_id', 'date_sighting', 'uuid', 'source', 'type');
|
||||
|
||||
// apply ACL and sighting policies
|
||||
$allowedSightings = array();
|
||||
$additional_attribute_added = false;
|
||||
$additional_event_added = false;
|
||||
foreach ($sightings as $sid) {
|
||||
$sight = $this->getSighting($sid, $user);
|
||||
if (!empty($sight)) {
|
||||
$sight['Sighting']['value'] = $sight['Sighting']['Attribute']['value'];
|
||||
// by default, do not include event and attribute
|
||||
if (!isset($filters['includeAttribute']) || !$filters['includeAttribute']) {
|
||||
unset($sight["Sighting"]["Attribute"]);
|
||||
} else if (!$additional_attribute_added) {
|
||||
$filters['requested_attributes'] = array_merge($filters['requested_attributes'], array('attribute_uuid', 'attribute_type', 'attribute_category', 'attribute_to_ids', 'attribute_value'));
|
||||
$additional_attribute_added = true;
|
||||
}
|
||||
|
||||
if (!isset($filters['includeEvent']) || !$filters['includeEvent']) {
|
||||
unset($sight["Sighting"]["Event"]);
|
||||
} else if (!$additional_event_added) {
|
||||
$filters['requested_attributes'] = array_merge($filters['requested_attributes'], array('event_uuid', 'event_orgc_id', 'event_org_id', 'event_info', 'event_Orgc_name'));
|
||||
$additional_event_added = true;
|
||||
}
|
||||
if (!empty($sight)) {
|
||||
array_push($allowedSightings, $sight);
|
||||
}
|
||||
if (!empty($filters['uuid'])) {
|
||||
if ($filters['context'] === 'attribute') {
|
||||
$conditions['Attribute.uuid'] = $filters['uuid'];
|
||||
$contain[] = 'Attribute';
|
||||
} elseif ($filters['context'] === 'event') {
|
||||
$conditions['Event.uuid'] = $filters['uuid'];
|
||||
$contain[] = 'Event';
|
||||
}
|
||||
}
|
||||
|
||||
$params = array(
|
||||
'conditions' => array(), //result already filtered
|
||||
);
|
||||
// fetch sightings matching the query
|
||||
$sightingIds = $this->find('column', [
|
||||
'conditions' => $conditions,
|
||||
'fields' => ['Sighting.id'],
|
||||
'contain' => $contain,
|
||||
]);
|
||||
|
||||
if (!isset($this->validFormats[$returnFormat])) {
|
||||
// this is where the new code path for the export modules will go
|
||||
throw new NotFoundException('Invalid export format.');
|
||||
$includeAttribute = isset($filters['includeAttribute']) && $filters['includeAttribute'];
|
||||
$includeEvent = isset($filters['includeEvent']) && $filters['includeEvent'];
|
||||
$requestedAttributes = ['id', 'attribute_id', 'event_id', 'org_id', 'date_sighting', 'uuid', 'source', 'type'];
|
||||
if ($includeAttribute) {
|
||||
$requestedAttributes = array_merge($requestedAttributes, ['attribute_uuid', 'attribute_type', 'attribute_category', 'attribute_to_ids', 'attribute_value']);
|
||||
}
|
||||
if ($includeEvent) {
|
||||
$requestedAttributes = array_merge($requestedAttributes, ['event_uuid', 'event_orgc_id', 'event_org_id', 'event_info', 'event_Orgc_name']);
|
||||
}
|
||||
$filters['requested_attributes'] = $requestedAttributes;
|
||||
|
||||
$exportToolParams = array(
|
||||
'user' => $user,
|
||||
'params' => $params,
|
||||
'params' => ['conditions' => []], //result already filtered
|
||||
'returnFormat' => $returnFormat,
|
||||
'scope' => 'Sighting',
|
||||
'filters' => $filters
|
||||
|
@ -904,57 +935,110 @@ class Sighting extends AppModel
|
|||
|
||||
$tmpfile = new TmpFileTool();
|
||||
$tmpfile->write($exportTool->header($exportToolParams));
|
||||
$separator = $exportTool->separator($exportToolParams);
|
||||
|
||||
$temp = '';
|
||||
$i = 0;
|
||||
foreach ($allowedSightings as $sighting) {
|
||||
$temp .= $exportTool->handler($sighting, $exportToolParams);
|
||||
if ($temp !== '') {
|
||||
if ($i != count($allowedSightings) -1) {
|
||||
$temp .= $exportTool->separator($exportToolParams);
|
||||
foreach ($sightingIds as $sightingId) {
|
||||
// apply ACL and sighting policies
|
||||
$sighting = $this->getSighting($sightingId, $user, $includeEvent);
|
||||
if (!empty($sighting)) {
|
||||
$sighting['Sighting']['value'] = $sighting['Sighting']['Attribute']['value'];
|
||||
if (!$includeAttribute) {
|
||||
unset($sighting['Sighting']['Attribute']);
|
||||
}
|
||||
$tmpfile->writeWithSeparator($exportTool->handler($sighting, $exportToolParams), $separator);
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
$tmpfile->write($temp);
|
||||
|
||||
$tmpfile->write($exportTool->footer($exportToolParams));
|
||||
return $tmpfile->finish();
|
||||
return $tmpfile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $eventId Event ID or UUID
|
||||
* @param array $sightings
|
||||
* @param array $user
|
||||
* @param null $passAlong
|
||||
* @return int|string Number of saved sightings or error message as string
|
||||
* @param int|null $passAlong Server ID
|
||||
* @return int Number of saved sightings
|
||||
* @throws Exception
|
||||
*/
|
||||
public function bulkSaveSightings($eventId, $sightings, $user, $passAlong = null)
|
||||
public function bulkSaveSightings($eventId, array $sightings, array $user, $passAlong = null)
|
||||
{
|
||||
$event = $this->Event->fetchSimpleEvent($user, $eventId);
|
||||
if (empty($event)) {
|
||||
return 'Event not found or not accessible by this user.';
|
||||
throw new NotFoundException('Event not found or not accessible by this user.');
|
||||
}
|
||||
$saved = 0;
|
||||
|
||||
// Since sightings are immutable (it is not possible to change it from web interface), we can check
|
||||
// if sighting with given uuid already exists and skip them
|
||||
$existingSighting = $this->existing($sightings);
|
||||
|
||||
// Fetch existing organisations in bulk
|
||||
$existingOrganisations = $this->existingOrganisations($sightings);
|
||||
|
||||
// Fetch attributes IDs and event IDs
|
||||
$attributesToTransform = $this->Attribute->fetchAttributesSimple($user, [
|
||||
'conditions' => ['Attribute.uuid' => array_unique(array_column($sightings, 'attribute_uuid'))],
|
||||
'fields' => ['Attribute.id', 'Attribute.uuid', 'Attribute.event_id'],
|
||||
]);
|
||||
$attributes = [];
|
||||
foreach ($attributesToTransform as $attribute) {
|
||||
$attributes[$attribute['Attribute']['uuid']] = [$attribute['Attribute']['id'], $attribute['Attribute']['event_id']];
|
||||
}
|
||||
|
||||
$toSave = [];
|
||||
foreach ($sightings as $s) {
|
||||
if (isset($existingSighting[$s['uuid']])) {
|
||||
continue; // sighting already exists
|
||||
}
|
||||
if (!isset($attributes[$s['attribute_uuid']])) {
|
||||
continue; // attribute doesn't exists or user don't have permission to access it
|
||||
}
|
||||
list($attributeId, $eventId) = $attributes[$s['attribute_uuid']];
|
||||
|
||||
if ($s['type'] === '2') {
|
||||
// remove existing expiration by the same org if it exists
|
||||
$this->deleteAll(array(
|
||||
'Sighting.org_id' => $user['org_id'],
|
||||
'Sighting.type' => 2,
|
||||
'Sighting.attribute_id' => $attributeId,
|
||||
));
|
||||
}
|
||||
|
||||
$saveOnBehalfOf = false;
|
||||
if ($user['Role']['perm_sync']) {
|
||||
if (isset($s['org_id'])) {
|
||||
if ($s['org_id'] != 0 && !empty($s['Organisation'])) {
|
||||
$saveOnBehalfOf = $this->Event->Orgc->captureOrg($s['Organisation'], $user);
|
||||
if (isset($existingOrganisations[$s['Organisation']['uuid']])) {
|
||||
$saveOnBehalfOf = $existingOrganisations[$s['Organisation']['uuid']];
|
||||
} else {
|
||||
$saveOnBehalfOf = $this->Organisation->captureOrg($s['Organisation'], $user);
|
||||
}
|
||||
} else {
|
||||
$saveOnBehalfOf = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
$result = $this->saveSightings($s['attribute_uuid'], false, $s['date_sighting'], $user, $s['type'], $s['source'], $s['uuid'], false, $saveOnBehalfOf);
|
||||
if (is_numeric($result)) {
|
||||
$saved += $result;
|
||||
}
|
||||
|
||||
$toSave[] = [
|
||||
'attribute_id' => $attributeId,
|
||||
'event_id' => $eventId,
|
||||
'org_id' => $saveOnBehalfOf === false ? $user['org_id'] : $saveOnBehalfOf,
|
||||
'date_sighting' => $s['date_sighting'],
|
||||
'type' => $s['type'],
|
||||
'source' => $s['source'],
|
||||
'uuid' => $s['uuid'],
|
||||
];
|
||||
}
|
||||
if ($saved > 0) {
|
||||
if (empty($toSave)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($this->saveMany($toSave)) {
|
||||
$this->Event->publishRouter($event['Event']['id'], $passAlong, $user, 'sightings');
|
||||
return count($toSave);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return $saved;
|
||||
}
|
||||
|
||||
public function pullSightings($user, $server)
|
||||
|
@ -1005,6 +1089,36 @@ class Sighting extends AppModel
|
|||
return strtotime("-$rangeInDays days");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $sightings
|
||||
* @return array Existing sightings UUID in key
|
||||
*/
|
||||
private function existing(array $sightings)
|
||||
{
|
||||
$existingSighting = $this->find('column', [
|
||||
'fields' => ['Sighting.uuid'],
|
||||
'conditions' => ['uuid' => array_column($sightings, 'uuid')],
|
||||
]);
|
||||
// Move UUID to array key
|
||||
return array_flip($existingSighting);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $sightings
|
||||
* @return array Organisation UUID => Organisation ID
|
||||
*/
|
||||
private function existingOrganisations(array $sightings)
|
||||
{
|
||||
$organisations = array_column($sightings, 'Organisation');
|
||||
if (empty($organisations)) {
|
||||
return [];
|
||||
}
|
||||
return $this->Organisation->find('list', [
|
||||
'fields' => ['Organisation.uuid', 'Organisation.id'],
|
||||
'conditions' => ['Organisation.uuid' => array_unique(array_column($organisations, 'uuid'))],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sighting reporters setting
|
||||
* If the event has any sightings for the user's org, then the user is a sighting reporter for the event too.
|
||||
|
|
|
@ -91,6 +91,9 @@ class Tag extends AppModel
|
|||
if (!isset($this->data['Tag']['exportable'])) {
|
||||
$this->data['Tag']['exportable'] = 1;
|
||||
}
|
||||
if (isset($this->data['Tag']['name']) && strlen($this->data['Tag']['name']) >= 255) {
|
||||
$this->data['Tag']['name'] = substr($this->data['Tag']['name'], 0, 255);
|
||||
}
|
||||
$this->data['Tag']['is_galaxy'] = preg_match($this->reGalaxy, $this->data['Tag']['name']);
|
||||
$this->data['Tag']['is_custom_galaxy'] = preg_match($this->reCustomGalaxy, $this->data['Tag']['name']);
|
||||
return true;
|
||||
|
@ -174,8 +177,8 @@ class Tag extends AppModel
|
|||
{
|
||||
$conditions = array();
|
||||
if (!$user['Role']['perm_site_admin']) {
|
||||
$conditions['Tag.org_id'] = array(0, $user['User']['org_id']);
|
||||
$conditions['Tag.user_id'] = array(0, $user['User']['id']);
|
||||
$conditions['Tag.org_id'] = array(0, $user['org_id']);
|
||||
$conditions['Tag.user_id'] = array(0, $user['id']);
|
||||
$conditions['Tag.hide_tag'] = 0;
|
||||
}
|
||||
return $this->find('all', array('conditions' => $conditions, 'recursive' => -1));
|
||||
|
@ -463,13 +466,12 @@ class Tag extends AppModel
|
|||
|
||||
public function getTagsByName($tag_names, $containTagConnectors = true)
|
||||
{
|
||||
$contain = array('EventTag', 'AttributeTag');
|
||||
$tag_params = array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('name' => $tag_names)
|
||||
'recursive' => -1,
|
||||
'conditions' => array('name' => $tag_names)
|
||||
);
|
||||
if ($containTagConnectors) {
|
||||
$tag_params['contain'] = $contain;
|
||||
$tag_params['contain'] = array('EventTag', 'AttributeTag');
|
||||
}
|
||||
$tags_temp = $this->find('all', $tag_params);
|
||||
$tags = array();
|
||||
|
@ -479,16 +481,19 @@ class Tag extends AppModel
|
|||
return $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $namespace
|
||||
* @param bool $containTagConnectors
|
||||
* @return array Uppercase tag name in key
|
||||
*/
|
||||
public function getTagsForNamespace($namespace, $containTagConnectors = true)
|
||||
{
|
||||
|
||||
$contain = array('EventTag', 'AttributeTag');
|
||||
$tag_params = array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('UPPER(name) LIKE' => strtoupper($namespace) . '%'),
|
||||
'recursive' => -1,
|
||||
'conditions' => array('LOWER(name) LIKE' => strtolower($namespace) . '%'),
|
||||
);
|
||||
if ($containTagConnectors) {
|
||||
$tag_params['contain'] = $contain;
|
||||
$tag_params['contain'] = array('EventTag', 'AttributeTag');
|
||||
}
|
||||
$tags_temp = $this->find('all', $tag_params);
|
||||
$tags = array();
|
||||
|
@ -511,13 +516,11 @@ class Tag extends AppModel
|
|||
}
|
||||
$id = $tag['Tag']['id'];
|
||||
}
|
||||
$event_ids = $this->EventTag->find('list', array(
|
||||
'recursive' => -1,
|
||||
$event_ids = $this->EventTag->find('column', array(
|
||||
'conditions' => array('EventTag.tag_id' => $id),
|
||||
'fields' => array('EventTag.event_id', 'EventTag.event_id'),
|
||||
'order' => array('EventTag.event_id')
|
||||
'fields' => array('EventTag.event_id'),
|
||||
));
|
||||
$params = array('conditions' => array('Event.id' => array_values($event_ids)));
|
||||
$params = array('conditions' => array('Event.id' => $event_ids));
|
||||
$events = $this->EventTag->Event->fetchSimpleEvents($user, $params, true);
|
||||
foreach ($events as $k => $event) {
|
||||
$event['Event']['Orgc'] = $event['Orgc'];
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<?php
|
||||
App::uses('AppModel', 'Model');
|
||||
|
||||
/**
|
||||
* @property TaxonomyPredicate $TaxonomyPredicate
|
||||
*/
|
||||
class Taxonomy extends AppModel
|
||||
{
|
||||
public $useTable = 'taxonomies';
|
||||
|
@ -49,9 +52,9 @@ class Taxonomy extends AppModel
|
|||
if (!$file->exists()) {
|
||||
continue;
|
||||
}
|
||||
$vocab = json_decode($file->read(), true);
|
||||
$file->close();
|
||||
if ($vocab === null) {
|
||||
try {
|
||||
$vocab = $this->jsonDecode($file->read());
|
||||
} catch (Exception $e) {
|
||||
$updated['fails'][] = array('namespace' => $dir, 'fail' => "File machinetag.json is not valid JSON.");
|
||||
continue;
|
||||
}
|
||||
|
@ -91,17 +94,55 @@ class Taxonomy extends AppModel
|
|||
return $updated;
|
||||
}
|
||||
|
||||
private function __updateVocab($vocab, $current, $skipUpdateFields = array())
|
||||
/**
|
||||
* @param array $vocab
|
||||
* @return int Taxonomy ID
|
||||
* @throws Exception
|
||||
*/
|
||||
public function import(array $vocab)
|
||||
{
|
||||
foreach (['namespace', 'description', 'predicates'] as $requiredField) {
|
||||
if (!isset($vocab[$requiredField])) {
|
||||
throw new Exception("Required field '$requiredField' not provided.");
|
||||
}
|
||||
}
|
||||
if (!is_array($vocab['predicates'])) {
|
||||
throw new Exception("Field 'predicates' must be array.");
|
||||
}
|
||||
if (isset($vocab['values']) && !is_array($vocab['values'])) {
|
||||
throw new Exception("Field 'values' must be array.");
|
||||
}
|
||||
if (!isset($vocab['version'])) {
|
||||
$vocab['version'] = 1;
|
||||
}
|
||||
$current = $this->find('first', array(
|
||||
'conditions' => array('namespace' => $vocab['namespace']),
|
||||
'recursive' => -1,
|
||||
'fields' => array('version', 'enabled', 'namespace')
|
||||
));
|
||||
$result = $this->__updateVocab($vocab, $current);
|
||||
if (is_array($result)) {
|
||||
throw new Exception('Could not save taxonomy because of validation errors: ' . json_encode($result));
|
||||
}
|
||||
return (int)$result;
|
||||
}
|
||||
|
||||
private function __updateVocab(array $vocab, $current, array $skipUpdateFields = [])
|
||||
{
|
||||
$enabled = 0;
|
||||
$taxonomy = array();
|
||||
if (!empty($current)) {
|
||||
if ($current['Taxonomy']['enabled']) {
|
||||
$enabled = 1;
|
||||
}
|
||||
$this->deleteAll(array('Taxonomy.namespace' => $current['Taxonomy']['namespace']));
|
||||
$this->deleteAll(['Taxonomy.namespace' => $current['Taxonomy']['namespace']]);
|
||||
}
|
||||
$taxonomy['Taxonomy'] = array('namespace' => $vocab['namespace'], 'description' => $vocab['description'], 'version' => $vocab['version'], 'exclusive' => !empty($vocab['exclusive']), 'enabled' => $enabled);
|
||||
$taxonomy = ['Taxonomy' => [
|
||||
'namespace' => $vocab['namespace'],
|
||||
'description' => $vocab['description'],
|
||||
'version' => $vocab['version'],
|
||||
'exclusive' => !empty($vocab['exclusive']),
|
||||
'enabled' => $enabled,
|
||||
]];
|
||||
$predicateLookup = array();
|
||||
foreach ($vocab['predicates'] as $k => $predicate) {
|
||||
$taxonomy['Taxonomy']['TaxonomyPredicate'][$k] = $predicate;
|
||||
|
@ -109,14 +150,15 @@ class Taxonomy extends AppModel
|
|||
}
|
||||
if (!empty($vocab['values'])) {
|
||||
foreach ($vocab['values'] as $value) {
|
||||
if (empty($taxonomy['Taxonomy']['TaxonomyPredicate'][$predicateLookup[$value['predicate']]]['TaxonomyEntry'])) {
|
||||
$taxonomy['Taxonomy']['TaxonomyPredicate'][$predicateLookup[$value['predicate']]]['TaxonomyEntry'] = $value['entry'];
|
||||
$predicatePosition = $predicateLookup[$value['predicate']];
|
||||
if (empty($taxonomy['Taxonomy']['TaxonomyPredicate'][$predicatePosition]['TaxonomyEntry'])) {
|
||||
$taxonomy['Taxonomy']['TaxonomyPredicate'][$predicatePosition]['TaxonomyEntry'] = $value['entry'];
|
||||
} else {
|
||||
$taxonomy['Taxonomy']['TaxonomyPredicate'][$predicateLookup[$value['predicate']]]['TaxonomyEntry'] = array_merge($taxonomy['Taxonomy']['TaxonomyPredicate'][$predicateLookup[$value['predicate']]]['TaxonomyEntry'], $value['entry']);
|
||||
$taxonomy['Taxonomy']['TaxonomyPredicate'][$predicatePosition]['TaxonomyEntry'] = array_merge($taxonomy['Taxonomy']['TaxonomyPredicate'][$predicatePosition]['TaxonomyEntry'], $value['entry']);
|
||||
}
|
||||
}
|
||||
}
|
||||
$result = $this->saveAssociated($taxonomy, array('deep' => true));
|
||||
$result = $this->saveAssociated($taxonomy, ['deep' => true]);
|
||||
if ($result) {
|
||||
$this->__updateTags($this->id, $skipUpdateFields);
|
||||
return $this->id;
|
||||
|
@ -175,7 +217,7 @@ class Taxonomy extends AppModel
|
|||
if ($filter) {
|
||||
$namespaceLength = strlen($taxonomy['Taxonomy']['namespace']);
|
||||
foreach ($entries as $k => $entry) {
|
||||
if (strpos(substr(strtoupper($entry['tag']), $namespaceLength), strtoupper($filter)) === false) {
|
||||
if (strpos(substr(mb_strtolower($entry['tag']), $namespaceLength), mb_strtolower($filter)) === false) {
|
||||
unset($entries[$k]);
|
||||
}
|
||||
}
|
||||
|
@ -189,8 +231,7 @@ class Taxonomy extends AppModel
|
|||
public function getAllTaxonomyTags($inverse = false, $user = false, $full = false)
|
||||
{
|
||||
$this->Tag = ClassRegistry::init('Tag');
|
||||
$taxonomyIdList = $this->find('list', array('fields' => array('id')));
|
||||
$taxonomyIdList = array_keys($taxonomyIdList);
|
||||
$taxonomyIdList = $this->find('column', array('fields' => array('Taxonomy.id')));
|
||||
$allTaxonomyTags = array();
|
||||
foreach ($taxonomyIdList as $taxonomy) {
|
||||
$allTaxonomyTags = array_merge($allTaxonomyTags, array_keys($this->getTaxonomyTags($taxonomy, true)));
|
||||
|
@ -284,8 +325,8 @@ class Taxonomy extends AppModel
|
|||
if (empty($taxonomy)) {
|
||||
return false;
|
||||
}
|
||||
$tag_names = Hash::extract($taxonomy, 'entries.{n}.tag');
|
||||
$tags = $this->Tag->getTagsByName($tag_names, false);
|
||||
$tagNames = array_column($taxonomy['entries'], 'tag');
|
||||
$tags = $this->Tag->getTagsByName($tagNames, false);
|
||||
if (isset($taxonomy['entries'])) {
|
||||
foreach ($taxonomy['entries'] as $key => $temp) {
|
||||
if (isset($tags[strtoupper($temp['tag'])])) {
|
||||
|
@ -307,13 +348,12 @@ class Taxonomy extends AppModel
|
|||
|
||||
private function __updateTags($id, $skipUpdateFields = array())
|
||||
{
|
||||
$this->Tag = ClassRegistry::init('Tag');
|
||||
App::uses('ColourPaletteTool', 'Tools');
|
||||
$paletteTool = new ColourPaletteTool();
|
||||
$taxonomy = $this->__getTaxonomy($id, array('full' => true));
|
||||
$colours = $paletteTool->generatePaletteFromString($taxonomy['Taxonomy']['namespace'], count($taxonomy['entries']));
|
||||
$this->Tag = ClassRegistry::init('Tag');
|
||||
$tags = $this->Tag->getTagsForNamespace($taxonomy['Taxonomy']['namespace']);
|
||||
$tags = $this->Tag->getTagsForNamespace($taxonomy['Taxonomy']['namespace'], false);
|
||||
foreach ($taxonomy['entries'] as $k => $entry) {
|
||||
if (isset($tags[strtoupper($entry['tag'])])) {
|
||||
$temp = $tags[strtoupper($entry['tag'])];
|
||||
|
@ -501,12 +541,9 @@ class Taxonomy extends AppModel
|
|||
return $taxonomies;
|
||||
}
|
||||
|
||||
public function getTaxonomyForTag($tagName, $metaOnly = false, $fullTaxonomy = False)
|
||||
public function getTaxonomyForTag($tagName, $metaOnly = false, $fullTaxonomy = false)
|
||||
{
|
||||
if (preg_match('/^[^:="]+:[^:="]+="[^:="]+"$/i', $tagName)) {
|
||||
$temp = explode(':', $tagName);
|
||||
$pieces = array_merge(array($temp[0]), explode('=', $temp[1]));
|
||||
$pieces[2] = trim($pieces[2], '"');
|
||||
if (preg_match('/^([^:="]+):([^:="]+)="([^:="]+)"$/i', $tagName, $matches)) {
|
||||
$contain = array(
|
||||
'TaxonomyPredicate' => array(
|
||||
'TaxonomyEntry' => array()
|
||||
|
@ -514,15 +551,15 @@ class Taxonomy extends AppModel
|
|||
);
|
||||
if (!$fullTaxonomy) {
|
||||
$contain['TaxonomyPredicate']['conditions'] = array(
|
||||
'LOWER(TaxonomyPredicate.value)' => strtolower($pieces[1])
|
||||
'LOWER(TaxonomyPredicate.value)' => mb_strtolower($matches[2]),
|
||||
);
|
||||
$contain['TaxonomyPredicate']['TaxonomyEntry']['conditions'] = array(
|
||||
'LOWER(TaxonomyEntry.value)' => strtolower($pieces[2])
|
||||
'LOWER(TaxonomyEntry.value)' => mb_strtolower($matches[3]),
|
||||
);
|
||||
}
|
||||
$taxonomy = $this->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('LOWER(Taxonomy.namespace)' => strtolower($pieces[0])),
|
||||
'conditions' => array('LOWER(Taxonomy.namespace)' => mb_strtolower($matches[1])),
|
||||
'contain' => $contain
|
||||
));
|
||||
if ($metaOnly && !empty($taxonomy)) {
|
||||
|
@ -534,12 +571,12 @@ class Taxonomy extends AppModel
|
|||
$contain = array('TaxonomyPredicate' => array());
|
||||
if (!$fullTaxonomy) {
|
||||
$contain['TaxonomyPredicate']['conditions'] = array(
|
||||
'LOWER(TaxonomyPredicate.value)' => strtolower($pieces[1])
|
||||
'LOWER(TaxonomyPredicate.value)' => mb_strtolower($pieces[1])
|
||||
);
|
||||
}
|
||||
$taxonomy = $this->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array('LOWER(Taxonomy.namespace)' => strtolower($pieces[0])),
|
||||
'conditions' => array('LOWER(Taxonomy.namespace)' => mb_strtolower($pieces[0])),
|
||||
'contain' => $contain
|
||||
));
|
||||
if ($metaOnly && !empty($taxonomy)) {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<?php
|
||||
App::uses('AppModel', 'Model');
|
||||
|
||||
/**
|
||||
* @property TaxonomyEntry $TaxonomyEntry
|
||||
*/
|
||||
class TaxonomyPredicate extends AppModel
|
||||
{
|
||||
public $useTable = 'taxonomy_predicates';
|
||||
|
|
|
@ -641,39 +641,50 @@ class User extends AppModel
|
|||
// get the current user and rearrange it to be in the same format as in the auth component
|
||||
public function getAuthUser($id)
|
||||
{
|
||||
$user = $this->getUserById($id);
|
||||
if (empty($user)) {
|
||||
return $user;
|
||||
if (empty($id)) {
|
||||
throw new InvalidArgumentException('Invalid user ID.');
|
||||
}
|
||||
return $this->rearrangeToAuthForm($user);
|
||||
$conditions = ['User.id' => $id];
|
||||
return $this->getAuthUserByConditions($conditions);
|
||||
}
|
||||
|
||||
// get the current user and rearrange it to be in the same format as in the auth component
|
||||
public function getAuthUserByAuthkey($id)
|
||||
public function getAuthUserByAuthkey($authkey)
|
||||
{
|
||||
$conditions = array('User.authkey' => $id);
|
||||
$user = $this->find('first', array('conditions' => $conditions, 'recursive' => -1,'contain' => array('Organisation', 'Role', 'Server')));
|
||||
if (empty($user)) {
|
||||
return $user;
|
||||
if (empty($authkey)) {
|
||||
throw new InvalidArgumentException('Invalid user auth key.');
|
||||
}
|
||||
return $this->rearrangeToAuthForm($user);
|
||||
$conditions = array('User.authkey' => $authkey);
|
||||
return $this->getAuthUserByConditions($conditions);
|
||||
}
|
||||
|
||||
public function getAuthUserByExternalAuth($auth_key)
|
||||
{
|
||||
if (empty($auth_key)) {
|
||||
throw new InvalidArgumentException('Invalid user external auth key.');
|
||||
}
|
||||
$conditions = array(
|
||||
'User.external_auth_key' => $auth_key,
|
||||
'User.external_auth_required' => true
|
||||
);
|
||||
$user = $this->find('first', array(
|
||||
return $this->getAuthUserByConditions($conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $conditions
|
||||
* @return array|null
|
||||
*/
|
||||
private function getAuthUserByConditions(array $conditions)
|
||||
{
|
||||
$user = $this->find('first', [
|
||||
'conditions' => $conditions,
|
||||
'recursive' => -1,
|
||||
'contain' => array(
|
||||
'contain' => [
|
||||
'Organisation',
|
||||
'Role',
|
||||
'Server'
|
||||
)
|
||||
));
|
||||
'Server',
|
||||
],
|
||||
]);
|
||||
if (empty($user)) {
|
||||
return $user;
|
||||
}
|
||||
|
@ -696,9 +707,6 @@ class User extends AppModel
|
|||
$user['User']['Role'] = $user['Role'];
|
||||
$user['User']['Organisation'] = $user['Organisation'];
|
||||
$user['User']['Server'] = $user['Server'];
|
||||
if (isset($user['UserSetting'])) {
|
||||
$user['User']['UserSetting'] = $user['UserSetting'];
|
||||
}
|
||||
return $user['User'];
|
||||
}
|
||||
|
||||
|
@ -880,38 +888,6 @@ class User extends AppModel
|
|||
return $fields;
|
||||
}
|
||||
|
||||
public function getMembersCount($org_id = false)
|
||||
{
|
||||
// for Organizations List
|
||||
$conditions = array();
|
||||
$findType = 'all';
|
||||
if ($org_id !== false) {
|
||||
$findType = 'first';
|
||||
$conditions = array('User.org_id' => $org_id);
|
||||
}
|
||||
$fields = array('org_id', 'COUNT(User.id) AS num_members');
|
||||
$params = array(
|
||||
'fields' => $fields,
|
||||
'recursive' => -1,
|
||||
'group' => array('org_id'),
|
||||
'order' => array('org_id'),
|
||||
'conditions' => $conditions
|
||||
);
|
||||
$orgs = $this->find($findType, $params);
|
||||
if (empty($orgs)) {
|
||||
return 0;
|
||||
}
|
||||
if ($org_id !== false) {
|
||||
return $orgs[0]['num_members'];
|
||||
} else {
|
||||
$usersPerOrg = [];
|
||||
foreach ($orgs as $key => $value) {
|
||||
$usersPerOrg[$value['User']['org_id']] = $value[0]['num_members'];
|
||||
}
|
||||
return $usersPerOrg;
|
||||
}
|
||||
}
|
||||
|
||||
public function findAdminsResponsibleForUser($user)
|
||||
{
|
||||
$admin = $this->find('first', array(
|
||||
|
@ -967,7 +943,7 @@ class User extends AppModel
|
|||
if ($result) {
|
||||
$this->id = $user['User']['id'];
|
||||
$this->saveField('password', $password);
|
||||
$this->saveField('change_pw', '1');
|
||||
$this->updateField($user['User'], 'change_pw', 1);
|
||||
if ($simpleReturn) {
|
||||
return true;
|
||||
} else {
|
||||
|
@ -983,10 +959,9 @@ class User extends AppModel
|
|||
|
||||
public function getOrgAdminsForOrg($org_id, $excludeUserId = false)
|
||||
{
|
||||
$adminRoles = $this->Role->find('list', array(
|
||||
'recursive' => -1,
|
||||
$adminRoles = $this->Role->find('column', array(
|
||||
'conditions' => array('perm_admin' => 1),
|
||||
'fields' => array('Role.id', 'Role.id')
|
||||
'fields' => array('Role.id')
|
||||
));
|
||||
$conditions = array(
|
||||
'User.org_id' => $org_id,
|
||||
|
@ -1142,7 +1117,7 @@ class User extends AppModel
|
|||
if (empty(Configure::read('Security.advanced_authkeys'))) {
|
||||
$oldKey = $this->data['User']['authkey'];
|
||||
$newkey = $this->generateAuthKey();
|
||||
$this->saveField('authkey', $newkey);
|
||||
$this->updateField($updatedUser['User'], 'authkey', $newkey);
|
||||
$this->extralog(
|
||||
$user,
|
||||
'reset_auth_key',
|
||||
|
@ -1212,6 +1187,27 @@ class User extends AppModel
|
|||
$syslog->write('notice', "$description -- $action" . (empty($fieldResult) ? '' : ' -- ' . $result['Log']['change']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getGpgPublicKey()
|
||||
{
|
||||
$email = Configure::read('GnuPG.email');
|
||||
if (!$email) {
|
||||
throw new Exception("Configuration option 'GnuPG.email' is not set, public key cannot be exported.");
|
||||
}
|
||||
|
||||
$cryptGpg = $this->initializeGpg();
|
||||
$fingerprint = $cryptGpg->getFingerprint($email);
|
||||
if (!$fingerprint) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$publicKey = $cryptGpg->exportPublicKey($fingerprint);
|
||||
return array($fingerprint, $publicKey);
|
||||
}
|
||||
|
||||
public function getOrgActivity($orgId, $params=array())
|
||||
{
|
||||
$conditions = array();
|
||||
|
@ -1265,24 +1261,6 @@ class User extends AppModel
|
|||
return $data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the monitoring flag in Configure for the current user
|
||||
* Reads the state from redis
|
||||
*/
|
||||
public function setMonitoring($user)
|
||||
{
|
||||
if (
|
||||
!empty(Configure::read('Security.user_monitoring_enabled'))
|
||||
) {
|
||||
$redis = $this->setupRedis();
|
||||
if (!empty($redis->sismember('misp:monitored_users', $user['id']))) {
|
||||
Configure::write('Security.monitored', 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Configure::write('Security.monitored', 0);
|
||||
}
|
||||
|
||||
public function registerUser($added_by, $registration, $org_id, $role_id) {
|
||||
$user = array(
|
||||
'email' => $registration['data']['email'],
|
||||
|
@ -1382,7 +1360,7 @@ class User extends AppModel
|
|||
$name => $value,
|
||||
], true, ['id', $name, 'date_modified']);
|
||||
if (!$success) {
|
||||
throw new RuntimeException("Could not save field `$name` with value `$value` for user `{$user['id']}`.");
|
||||
throw new RuntimeException("Could not save setting $name for user {$user['id']}.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -201,55 +201,50 @@ class UserSetting extends AppModel
|
|||
|
||||
public function getDefaulRestSearchParameters($user)
|
||||
{
|
||||
$setting = $this->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array(
|
||||
'UserSetting.user_id' => $user['id'],
|
||||
'UserSetting.setting' => 'default_restsearch_parameters'
|
||||
)
|
||||
));
|
||||
$parameters = array();
|
||||
if (!empty($setting)) {
|
||||
$parameters = $setting['UserSetting']['value'];
|
||||
}
|
||||
return $parameters;
|
||||
return $this->getValueForUser($user['id'], 'default_restsearch_parameters') ?: [];
|
||||
}
|
||||
|
||||
public function getTagNumericalValueOverride($userId)
|
||||
{
|
||||
$setting = $this->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array(
|
||||
'UserSetting.user_id' => $userId,
|
||||
'UserSetting.setting' => 'tag_numerical_value_override'
|
||||
)
|
||||
));
|
||||
$parameters = array();
|
||||
if (!empty($setting)) {
|
||||
$parameters = $setting['UserSetting']['value'];
|
||||
}
|
||||
return $parameters;
|
||||
return $this->getValueForUser($userId, 'tag_numerical_value_override') ?: [];
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether the event is something the user is interested (to be alerted on)
|
||||
*
|
||||
/**
|
||||
* @param int $userId
|
||||
* @param string $setting
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function checkPublishFilter($user, $event)
|
||||
public function getValueForUser($userId, $setting)
|
||||
{
|
||||
$output = $this->find('first', array(
|
||||
'recursive' => -1,
|
||||
'fields' => ['value'],
|
||||
'conditions' => array(
|
||||
'UserSetting.user_id' => $userId,
|
||||
'UserSetting.setting' => $setting,
|
||||
)
|
||||
));
|
||||
if ($output) {
|
||||
return $output['UserSetting']['value'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the event is something the user is interested (to be alerted on)
|
||||
* @param $user
|
||||
* @param $event
|
||||
* @return bool
|
||||
*/
|
||||
public function checkPublishFilter(array $user, array $event)
|
||||
{
|
||||
$rule = $this->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array(
|
||||
'UserSetting.user_id' => $user['id'],
|
||||
'UserSetting.setting' => 'publish_alert_filter'
|
||||
)
|
||||
));
|
||||
$rule = $this->getValueForUser($user['id'], 'publish_alert_filter');
|
||||
// We should return true if no setting has been configured, or there's a setting with an empty value
|
||||
if (empty($rule) || empty($rule['UserSetting']['value'])) {
|
||||
if (empty($rule)) {
|
||||
return true;
|
||||
}
|
||||
// recursively evaluate the boolean tree to true/false and return the value
|
||||
$result = $this->__recursiveConvert($rule['UserSetting']['value'], $event);
|
||||
$result = $this->__recursiveConvert($rule, $event);
|
||||
if (isset($result[0])) {
|
||||
return $result[0];
|
||||
} else {
|
||||
|
@ -257,7 +252,7 @@ class UserSetting extends AppModel
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Convert a complex rule set recursively
|
||||
* takes as params a rule branch and an event to check against
|
||||
* evaluate whether the rule set evaluates as true/false
|
||||
|
@ -285,9 +280,9 @@ class UserSetting extends AppModel
|
|||
}
|
||||
}
|
||||
}
|
||||
$toReturn []= $temp;
|
||||
$toReturn[] = $temp;
|
||||
} else {
|
||||
$toReturn []= $this->__checkEvent($k, $v, $event);
|
||||
$toReturn[] = $this->__checkEvent($k, $v, $event);
|
||||
}
|
||||
}
|
||||
return $toReturn;
|
||||
|
@ -304,6 +299,7 @@ class UserSetting extends AppModel
|
|||
* - Tag.name (checks against both event and attribute tags)
|
||||
* - Orgc.uuid
|
||||
* - Orgc.name
|
||||
* - ThreatLevel.name
|
||||
* Values passed can be used for direct string comparisons or alternatively
|
||||
* as substring matches by encapsulating the string in a pair of "%" characters
|
||||
* Each rule can take a list of values
|
||||
|
@ -333,6 +329,8 @@ class UserSetting extends AppModel
|
|||
Hash::extract($event, 'Object.{n}.Attribute.{n}.AttributeTag.{n}.Tag.name'),
|
||||
Hash::extract($event, 'EventTag.{n}.Tag.name')
|
||||
);
|
||||
} else if ($rule === 'ThreatLevel.name') {
|
||||
$values = [$event['ThreatLevel']['name']];
|
||||
}
|
||||
if (!empty($values)) {
|
||||
foreach ($values as $extracted_value) {
|
||||
|
@ -406,18 +404,14 @@ class UserSetting extends AppModel
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $user_id
|
||||
* @param string $setting
|
||||
* @return array|mixed
|
||||
* @deprecated
|
||||
*/
|
||||
public function getSetting($user_id, $setting)
|
||||
{
|
||||
$setting = $this->find('first', array(
|
||||
'recursive' => -1,
|
||||
'conditions' => array(
|
||||
'UserSetting.user_id' => $user_id,
|
||||
'UserSetting.setting' => $setting
|
||||
)
|
||||
));
|
||||
if (empty($setting)) {
|
||||
return array();
|
||||
}
|
||||
return $setting['UserSetting']['value'];
|
||||
return $this->getValueForUser($user_id, $setting) ?: [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
App::uses('AppModel', 'Model');
|
||||
App::uses('CidrTool', 'Tools');
|
||||
|
||||
/**
|
||||
* @property WarninglistType $WarninglistType
|
||||
|
@ -154,7 +155,7 @@ class Warninglist extends AppModel
|
|||
if (!empty($saveToCache)) {
|
||||
$pipe = $redis->multi(Redis::PIPELINE);
|
||||
foreach ($saveToCache as $attributeKey => $json) {
|
||||
$redis->setex($attributeKey, 3600, $json); // cache for one hour
|
||||
$redis->setex($attributeKey, 8 * 3600, $json); // cache for eight hour
|
||||
}
|
||||
$pipe->exec();
|
||||
}
|
||||
|
@ -304,8 +305,7 @@ class Warninglist extends AppModel
|
|||
if ($id && $warninglist['Warninglist']['id'] != $id) {
|
||||
continue;
|
||||
}
|
||||
$entries = $this->WarninglistEntry->find('list', array(
|
||||
'recursive' => -1,
|
||||
$entries = $this->WarninglistEntry->find('column', array(
|
||||
'conditions' => array('warninglist_id' => $warninglist['Warninglist']['id']),
|
||||
'fields' => array('value')
|
||||
));
|
||||
|
@ -390,49 +390,15 @@ class Warninglist extends AppModel
|
|||
if ($redis !== false && $redis->exists('misp:warninglist_entries_cache:' . $id)) {
|
||||
return $redis->sMembers('misp:warninglist_entries_cache:' . $id);
|
||||
} else {
|
||||
$entries = array_values($this->WarninglistEntry->find('list', array(
|
||||
'recursive' => -1,
|
||||
$entries = $this->WarninglistEntry->find('column', array(
|
||||
'conditions' => array('warninglist_id' => $id),
|
||||
'fields' => array('value')
|
||||
)));
|
||||
'fields' => array('WarninglistEntry.value')
|
||||
));
|
||||
$this->cacheWarninglistEntries($entries, $id);
|
||||
return $entries;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter out invalid IPv4 or IPv4 CIDR and append maximum netmaks if no netmask is given.
|
||||
* @param array $inputValues
|
||||
* @return array
|
||||
*/
|
||||
private function filterCidrList($inputValues)
|
||||
{
|
||||
$outputValues = [];
|
||||
foreach ($inputValues as $v) {
|
||||
$v = strtolower($v);
|
||||
$parts = explode('/', $v, 2);
|
||||
if (filter_var($parts[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||
$maximumNetmask = 32;
|
||||
} else if (filter_var($parts[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
$maximumNetmask = 128;
|
||||
} else {
|
||||
// IP address part of CIDR is invalid
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($parts[1])) {
|
||||
// If CIDR doesnt contains '/', we will consider CIDR as /32 for IPv4 or /128 for IPv6
|
||||
$v = "$v/$maximumNetmask";
|
||||
} else if ($parts[1] > $maximumNetmask || $parts[1] < 0) {
|
||||
// Netmask part of CIDR is invalid
|
||||
continue;
|
||||
}
|
||||
|
||||
$outputValues[$v] = true;
|
||||
}
|
||||
return $outputValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* For 'hostname', 'string' and 'cidr' warninglist type, values are just in keys to save memory.
|
||||
*
|
||||
|
@ -461,7 +427,7 @@ class Warninglist extends AppModel
|
|||
}
|
||||
$values = $output;
|
||||
} else if ($warninglist['Warninglist']['type'] === 'cidr') {
|
||||
$values = $this->filterCidrList($values);
|
||||
$values = new CidrTool($values);
|
||||
}
|
||||
|
||||
$this->entriesCache[$id] = $values;
|
||||
|
@ -482,7 +448,7 @@ class Warninglist extends AppModel
|
|||
|
||||
if ($object['to_ids'] || $this->showForAll) {
|
||||
foreach ($warninglists as $list) {
|
||||
if (in_array('ALL', $list['types']) || in_array($object['type'], $list['types'])) {
|
||||
if (in_array('ALL', $list['types'], true) || in_array($object['type'], $list['types'], true)) {
|
||||
$result = $this->__checkValue($this->getFilteredEntries($list), $object['value'], $object['type'], $list['Warninglist']['type']);
|
||||
if ($result !== false) {
|
||||
$object['warnings'][] = array(
|
||||
|
@ -499,7 +465,7 @@ class Warninglist extends AppModel
|
|||
}
|
||||
|
||||
/**
|
||||
* @param array $listValues
|
||||
* @param array|CidrTool $listValues
|
||||
* @param string $value
|
||||
* @param string $type
|
||||
* @param string $listType
|
||||
|
@ -514,7 +480,7 @@ class Warninglist extends AppModel
|
|||
}
|
||||
foreach ($value as $v) {
|
||||
if ($listType === 'cidr') {
|
||||
$result = $this->__evalCidrList($listValues, $v);
|
||||
$result = $listValues->contains($v);
|
||||
} elseif ($listType === 'string') {
|
||||
$result = $this->__evalString($listValues, $v);
|
||||
} elseif ($listType === 'substring') {
|
||||
|
@ -538,75 +504,6 @@ class Warninglist extends AppModel
|
|||
return $this->__checkValue($listValues, $value, '', $type) !== false;
|
||||
}
|
||||
|
||||
private function __evalCidrList($listValues, $value)
|
||||
{
|
||||
$valueMask = null;
|
||||
if (strpos($value, '/') !== false) {
|
||||
list($value, $valueMask) = explode('/', $value);
|
||||
}
|
||||
|
||||
$match = false;
|
||||
if (filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||
// This code converts IP address to all possible CIDRs that can contains given IP address
|
||||
// and then check if given hash table contains that CIDR.
|
||||
$ip = ip2long($value);
|
||||
// Start from 1, because doesn't make sense to check 0.0.0.0/0 match
|
||||
for ($bits = 1; $bits <= 32; $bits++) {
|
||||
$mask = -1 << (32 - $bits);
|
||||
$needle = long2ip($ip & $mask) . "/$bits";
|
||||
if (isset($listValues[$needle])) {
|
||||
$match = $needle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} elseif (filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
foreach ($listValues as $lv => $foo) {
|
||||
if (strpos($lv, ':') !== false) { // Filter out IPv4 CIDR, IPv6 CIDR must contain colon
|
||||
if ($this->__ipv6InCidr($value, $lv)) {
|
||||
$match = $lv;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($match && $valueMask) {
|
||||
$matchMask = explode('/', $match)[1];
|
||||
if ($valueMask < $matchMask) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $match;
|
||||
}
|
||||
|
||||
/**
|
||||
* Using solution from https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/IpUtils.php
|
||||
*
|
||||
* @param string $ip
|
||||
* @param string $cidr
|
||||
* @return bool
|
||||
*/
|
||||
private function __ipv6InCidr($ip, $cidr)
|
||||
{
|
||||
list($address, $netmask) = explode('/', $cidr);
|
||||
|
||||
$bytesAddr = unpack('n*', inet_pton($address));
|
||||
$bytesTest = unpack('n*', inet_pton($ip));
|
||||
|
||||
for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; ++$i) {
|
||||
$left = $netmask - 16 * ($i - 1);
|
||||
$left = ($left <= 16) ? $left : 16;
|
||||
$mask = ~(0xffff >> $left) & 0xffff;
|
||||
if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for exact match.
|
||||
*
|
||||
|
@ -677,14 +574,13 @@ class Warninglist extends AppModel
|
|||
*/
|
||||
public function fetchTLDLists()
|
||||
{
|
||||
$tldLists = $this->find('list', array(
|
||||
$tldLists = $this->find('column', array(
|
||||
'conditions' => array('Warninglist.name' => $this->__tlds),
|
||||
'recursive' => -1,
|
||||
'fields' => array('Warninglist.id', 'Warninglist.id')
|
||||
'fields' => array('Warninglist.id')
|
||||
));
|
||||
$tlds = array();
|
||||
if (!empty($tldLists)) {
|
||||
$tlds = $this->WarninglistEntry->find('list', array(
|
||||
$tlds = $this->WarninglistEntry->find('column', array(
|
||||
'conditions' => array('WarninglistEntry.warninglist_id' => $tldLists),
|
||||
'fields' => array('WarninglistEntry.value')
|
||||
));
|
||||
|
@ -692,7 +588,7 @@ class Warninglist extends AppModel
|
|||
$tlds[$key] = strtolower($value);
|
||||
}
|
||||
}
|
||||
if (!in_array('onion', $tlds)) {
|
||||
if (!in_array('onion', $tlds, true)) {
|
||||
$tlds[] = 'onion';
|
||||
}
|
||||
return $tlds;
|
||||
|
@ -710,7 +606,7 @@ class Warninglist extends AppModel
|
|||
}
|
||||
|
||||
foreach ($warninglists as $warninglist) {
|
||||
if (in_array('ALL', $warninglist['types']) || in_array($attribute['type'], $warninglist['types'])) {
|
||||
if (in_array('ALL', $warninglist['types'], true) || in_array($attribute['type'], $warninglist['types'], true)) {
|
||||
$result = $this->__checkValue($this->getFilteredEntries($warninglist), $attribute['value'], $attribute['type'], $warninglist['Warninglist']['type']);
|
||||
if ($result !== false) {
|
||||
return false;
|
||||
|
|
|
@ -145,6 +145,7 @@ class ApacheShibbAuthenticate extends BaseAuthenticate
|
|||
* @param string $org
|
||||
* @param array $user
|
||||
* @return int
|
||||
* @throws Exception
|
||||
*/
|
||||
private function checkOrganization($org, $user)
|
||||
{
|
||||
|
@ -166,7 +167,7 @@ class ApacheShibbAuthenticate extends BaseAuthenticate
|
|||
if ($user) {
|
||||
$orgUserId = $user['id'];
|
||||
}
|
||||
$orgId = $orgModel->createOrgFromName($org, $orgUserId, 0); // Created with local set to 0 by default
|
||||
$orgId = $orgModel->createOrgFromName($org, $orgUserId, true);
|
||||
CakeLog::info("User organisation `$org` created with ID $orgId.");
|
||||
} else {
|
||||
$orgId = $orgAux['Organisation']['id'];
|
||||
|
@ -192,13 +193,13 @@ class ApacheShibbAuthenticate extends BaseAuthenticate
|
|||
foreach ($groupList as $group) {
|
||||
// TODO: Can be optimized inverting the search group and using only array_key_exists
|
||||
if (array_key_exists($group, $groupRoleMatching)) { //In case there is an group not defined in the config.php file
|
||||
CakeLog::write('info', "User group ${group} found.");
|
||||
CakeLog::write('info', "User group $group found.");
|
||||
$roleVal = $groupRoleMatching[$group];
|
||||
if ($roleVal <= $roleId || $roleId == -1) {
|
||||
$roleId = $roleVal;
|
||||
$roleChanged = true;
|
||||
}
|
||||
CakeLog::write('info', "User role ${roleId} assigned.");
|
||||
CakeLog::write('info', "User role $roleId assigned.");
|
||||
}
|
||||
}
|
||||
return array($roleChanged, $roleId);
|
||||
|
@ -217,7 +218,8 @@ class ApacheShibbAuthenticate extends BaseAuthenticate
|
|||
private function updateUserRole($roleChanged, array $user, $roleId, User $userModel)
|
||||
{
|
||||
if ($roleChanged && $user['role_id'] != $roleId) {
|
||||
CakeLog::write('warning', "User role changed from ${user['role_id']} to $roleId.");
|
||||
$message = "User role changed from ${user['role_id']} to $roleId for user ${user['email']} (${user['id']}).";
|
||||
CakeLog::write('warning', $message);
|
||||
$userModel->updateField($user, 'role_id', $roleId);
|
||||
}
|
||||
return $user;
|
||||
|
@ -233,7 +235,8 @@ class ApacheShibbAuthenticate extends BaseAuthenticate
|
|||
private function updateUserOrg($orgId, array $user, User $userModel)
|
||||
{
|
||||
if ($user['org_id'] != $orgId) {
|
||||
CakeLog::write('warning', "User organisation $orgId changed.");
|
||||
$message = "User organisation changed from ${user['org_id']} to $orgId for user ${user['email']} (${user['id']}).";
|
||||
CakeLog::write('warning', $message);
|
||||
$user['org_id'] = $orgId; // Different role either increase or decrease permissions
|
||||
$userModel->updateField($user, 'org_id', $orgId);
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ Edit your MISP apache configuration by adding the below (location depends on you
|
|||
```Apache
|
||||
<Location /Shibboleth.sso>
|
||||
SetHandler shib
|
||||
</Locations>
|
||||
</Location>
|
||||
```
|
||||
|
||||
Enable the plugin at bootstrap.php:
|
||||
|
@ -144,7 +144,7 @@ If used with Apache as webserver it might be useful to make a distinction to fil
|
|||
If you want the logout button to work for killing your session, you can use the CustomAuth plugin to configure a custom logout url, by default the url should be https://<host>/Shibboleth.sso/Logout. This leads to a local logout. If you want to also trigger a logout at the identity provider, you can use the return mechanism. In this case you will need to change the allowed redirects. Your logout url will look like https://<host>/Shibboleth.sso/Logout?return=https://<idp_host>/Logout. Edit your shibboleth configuration (often at /etc/shibboleth/shibboleth2.xml) as necessary. Relevant shibboleth documentation can be found at https://wiki.shibboleth.net/confluence/display/SP3/Logout and https://wiki.shibboleth.net/confluence/display/SP3/Sessions.
|
||||
```xml
|
||||
<Sessions lifetime="28800" timeout="3600" relayState="ss:mem"
|
||||
checkAddress="false" handlerSSL="false" cookieProps="https"
|
||||
checkAddress="false" handlerSSL="true" cookieProps="https"
|
||||
redirectLimit="exact+whitelist" redirectWhitelist="https://<idp_host>">
|
||||
```
|
||||
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
require_once __DIR__ . '/../Lib/Tools/CidrTool.php';
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class CidrToolTest extends TestCase
|
||||
{
|
||||
public function testEmptyList(): void
|
||||
{
|
||||
$cidrTool = new CidrTool([]);
|
||||
$this->assertFalse($cidrTool->contains('1.2.3.4'));
|
||||
}
|
||||
|
||||
public function testIpv4Fullmask(): void
|
||||
{
|
||||
$cidrTool = new CidrTool(['1.2.3.4/32']);
|
||||
$this->assertEquals('1.2.3.4/32', $cidrTool->contains('1.2.3.4'));
|
||||
}
|
||||
|
||||
public function testIpv4WithoutNetmask(): void
|
||||
{
|
||||
$cidrTool = new CidrTool(['1.2.3.4']);
|
||||
$this->assertEquals('1.2.3.4/32', $cidrTool->contains('1.2.3.4'));
|
||||
}
|
||||
|
||||
public function testIpv4(): void
|
||||
{
|
||||
$cidrTool = new CidrTool(['10.0.0.0/8', '8.0.0.0/8', '9.0.0.0/8']);
|
||||
$this->assertEquals('8.0.0.0/8', $cidrTool->contains('8.8.8.8'));
|
||||
$this->assertFalse($cidrTool->contains('::1'));
|
||||
$this->assertFalse($cidrTool->contains('7.1.2.3'));
|
||||
}
|
||||
|
||||
public function testIpv6(): void
|
||||
{
|
||||
$cidrTool = new CidrTool(['2001:0db8:1234::/48']);
|
||||
$this->assertEquals('2001:db8:1234::/48', $cidrTool->contains('2001:0db8:1234:0000:0000:0000:0000:0000'));
|
||||
$this->assertEquals('2001:db8:1234::/48', $cidrTool->contains('2001:0db8:1234:ffff:ffff:ffff:ffff:ffff'));
|
||||
$this->assertFalse($cidrTool->contains('2002:0db8:1234:ffff:ffff:ffff:ffff:ffff'));
|
||||
}
|
||||
}
|
|
@ -4,13 +4,6 @@
|
|||
<div class="pagination">
|
||||
<ul>
|
||||
<?php
|
||||
$this->Paginator->options(array(
|
||||
'update' => '.span12',
|
||||
'evalScripts' => true,
|
||||
'before' => '$(".progress").show()',
|
||||
'complete' => '$(".progress").hide()',
|
||||
));
|
||||
|
||||
echo $this->Paginator->prev('« ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
|
||||
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
|
||||
echo $this->Paginator->next(__('next') . ' »', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
|
||||
|
|
|
@ -4,13 +4,6 @@
|
|||
<div class="pagination">
|
||||
<ul>
|
||||
<?php
|
||||
$this->Paginator->options(array(
|
||||
'update' => '.span12',
|
||||
'evalScripts' => true,
|
||||
'before' => '$(".progress").show()',
|
||||
'complete' => '$(".progress").hide()',
|
||||
));
|
||||
|
||||
echo $this->Paginator->prev('« ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
|
||||
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
|
||||
echo $this->Paginator->next(__('next') . ' »', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
|
||||
|
|
|
@ -29,16 +29,10 @@
|
|||
<div class="pagination">
|
||||
<ul>
|
||||
<?php
|
||||
$this->Paginator->options(array(
|
||||
'update' => '.span12',
|
||||
'evalScripts' => true,
|
||||
'before' => '$(".progress").show()',
|
||||
'complete' => '$(".progress").hide()',
|
||||
));
|
||||
|
||||
echo $this->Paginator->prev('« ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
|
||||
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
|
||||
echo $this->Paginator->next(__('next') . ' »', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
|
||||
$paginator = $this->Paginator->prev('« ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
|
||||
$paginator .= $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
|
||||
$paginator .= $this->Paginator->next(__('next') . ' »', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
|
||||
echo $paginator;
|
||||
?>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -98,6 +92,7 @@
|
|||
if (!empty($attribute['Attribute']['RelatedAttribute'])) {
|
||||
$event['RelatedAttribute'] = array($attribute['Attribute']['id'] => $attribute['Attribute']['RelatedAttribute']);
|
||||
}
|
||||
$attribute['Attribute']['objectType'] = 'attribute';
|
||||
echo $this->element('/Events/View/row_attribute', array(
|
||||
'object' => $attribute['Attribute'],
|
||||
'k' => $k,
|
||||
|
@ -115,6 +110,13 @@
|
|||
}
|
||||
?>
|
||||
</table>
|
||||
<?php
|
||||
// Generate form for adding sighting just once, generation for every attribute is surprisingly too slow
|
||||
echo $this->Form->create('Sighting', ['id' => 'SightingForm', 'url' => $baseurl . '/sightings/add/', 'style' => 'display:none;']);
|
||||
echo $this->Form->input('id', ['label' => false, 'type' => 'number']);
|
||||
echo $this->Form->input('type', ['label' => false]);
|
||||
echo $this->Form->end();
|
||||
?>
|
||||
<p>
|
||||
<?php
|
||||
echo $this->Paginator->counter(array(
|
||||
|
@ -124,24 +126,18 @@
|
|||
</p>
|
||||
<div class="pagination">
|
||||
<ul>
|
||||
<?php
|
||||
echo $this->Paginator->prev('« ' . __('previous'), array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'prev disabled', 'escape' => false, 'disabledTag' => 'span'));
|
||||
echo $this->Paginator->numbers(array('modulus' => 20, 'separator' => '', 'tag' => 'li', 'currentClass' => 'active', 'currentTag' => 'span'));
|
||||
echo $this->Paginator->next(__('next') . ' »', array('tag' => 'li', 'escape' => false), null, array('tag' => 'li', 'class' => 'next disabled', 'escape' => false, 'disabledTag' => 'span'));
|
||||
?>
|
||||
<?= $paginator ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
if ($isSearch == 1){
|
||||
if ($isSearch == 1) {
|
||||
$class = 'searchAttributes2';
|
||||
} else {
|
||||
$class = 'listAttributes';
|
||||
}
|
||||
?>
|
||||
<?php
|
||||
echo $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'event-collection', 'menuItem' => $class));
|
||||
?>
|
||||
<?= $this->element('/genericElements/SideMenu/side_menu', array('menuList' => 'event-collection', 'menuItem' => $class)); ?>
|
||||
<script type="text/javascript">
|
||||
// tooltips
|
||||
$(function () {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue