Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • Pavel.Valach/warden
1 result
Show changes
Commits on Source (20)
Showing
with 435 additions and 120 deletions
---
server_admin: "{{ root@inventory_hostname }}"
warden_filer_bin_path: /opt/warden-filer
warden_filer_lib_path: /var/lib/warden_filer
warden_filer_run_path: /run/warden_filer
warden_client_cert_path: /etc/ssl/certs/warden.cert.pem
warden_client_key_path: /etc/ssl/private/warden.key.pem
warden_client_id_store: /var/lib/warden_filer/warden_filer.id
......
---
- name: Checkout Warden repository
git:
repo: https://homeproj.cesnet.cz/git/warden.git/
repo: https://gitlab.cesnet.cz/713/warden/warden.git
version: warden-client-3.0-beta3
dest: /tmp/warden_client_repository
- name: Create bin dir for warden_filer
file:
path: "{{ warden_filer_bin_path }}"
state: directory
owner: root
group: root
mode: "755"
- name: Create lib and run dir for warden_filer
file:
path: "{{ item }}"
state: directory
owner: "{{ warden_filer_uid }}"
group: "{{ warden_filer_gid }}"
mode: "755"
with_items:
- "{{ warden_filer_lib_path }}"
- "{{ warden_filer_run_path }}"
- name: Install Filer binaries
copy:
src: "/tmp/warden_client_repository/{{ src }}"
dest: "{{ warden_filer_bin_path }}/{{ dest }}"
remote_src: true
src: "/tmp/warden_client_repository/{{ item.src }}"
dest: "{{ warden_filer_bin_path }}/{{ item.dest }}"
mode: "755"
with_items:
- src: warden_client/warden_client.py
dest: warden_client.py
......@@ -17,15 +38,26 @@
- src: warden_filer/check_file_count
dest: check_file_count
- name: Link Filer binary to /usr/local/bin
file:
src: "{{ warden_filer_bin_path }}/warden_filer.py"
dest: "/usr/local/bin/warden_filer.py"
state: link
owner: root
group: root
mode: "755"
- name: Install Warden Filer config
template:
src: "{{ item }}"
dest: "/{{ item }}"
with_items:
- etc/warden_filer.cfg
- etc/defaults/warden_filer_receiver
- etc/default/warden_filer_receiver
- name: Install Warden Filer init script
copy:
remote_src: true
src: /tmp/warden_client_repository/warden_filer/warden_filer_receiver
dest: /etc/init.d/warden_filer_receiver
mode: "755"
---
- name: Checkout Warden repository
git:
repo: https://homeproj.cesnet.cz/git/warden.git/
repo: https://gitlab.cesnet.cz/713/warden/warden.git
version: warden-server-3.0-beta3
dest: /tmp/warden_server_repository
......
......@@ -22,10 +22,7 @@ def main(args):
banners = [{'name': banner_name_en, 'database' : "Database Size:", 'events' : "Number of Events:", 'senders' : "Number of Senders:", 'receivers' : "Number of Receivers:", 'created' : "Banner Created:"}, {'name': banner_name_cz, 'database' : "Velikost databáze:", 'events' : "Suma všech událostí:", 'senders' : "Odesílající klienti:", 'receivers' : "Přijímající klienti:", 'created' : "Banner vytvořen:"}]
# We have DB credentials
user = "warden"
password = "w4rd3n&u53r"
database = "warden3"
host = "localhost"
host, database, user, password = sys.argv[1:]
db = MySQLdb.connect(host = host, user = user, passwd = password, db = database)
cursor = db.cursor()
......
......@@ -36,14 +36,11 @@ B. Configuration
4. Setup backend call (warden-map.py) in a crontab.
NOTE: Please make sure you will have stored warden-map.json file
in the fronted folder.
EXAMPLE: ./warden-map.py --events 100 \
--client cz.cesnet.warden.map \
in the frontend folder.
EXAMPLE: ./warden-map.py --client cz.cesnet.warden.map \
--key certs/key.pem \
--cert certs/cert.pem \
--cacert certs/cacert.pem \
--secret SeCreT \
--output ../fronted/
--output ../frontend/
5. Enjoy your map.
......
......@@ -6,86 +6,83 @@
# Copyright (C) 2016 Cesnet z.s.p.o
# Use of this source is governed by a 3-clause BSD-style license, see LICENSE file.
import json
import codecs
import time
import argparse
import GeoIP
import requests
def getLastEvents(events, client, key, cert, cacert, secret):
def getLastEvents(client, key, cert):
try:
ses = Session()
req = Request('POST', 'https://warden-hub.cesnet.cz/warden3/getEvents?client='+ client + ('&secret='+ secret if secret else "")+'&count=' + events)
pre = req.prepare()
res = ses.send(pre, cert = (cert, key), verify=cacert)
except requests.exceptions.RequestException as error:
print error
sys.exit(1)
res = requests.post(
'https://warden-hub.cesnet.cz/warden3/getEvents?client=%s' % (client,),
cert=(cert, key)
)
data = res.json()
i = 0
eventsList = []
for p in data['events']:
event = {}
if i < events:
for key, value in { 'event': 'Category', 'time': 'DetectTime', 'origin': 'Source', 'destination': 'Target'}.iteritems():
if value in p:
if (key == 'origin') or (key == 'destination'):
event[key] = {}
if 'IP4' in p[value][0]:
event[key]['ip'] = p[value][0]['IP4']
else:
event[key] = {}
elif (key == 'event'):
event[key] = ', '.join(p[value])
for key, value in { 'event': 'Category', 'time': 'DetectTime', 'origin': 'Source', 'destination': 'Target'}.items():
if value in p:
if (key == 'origin') or (key == 'destination'):
event[key] = {}
if 'IP4' in p[value][0]:
event[key]['ip'] = p[value][0]['IP4'][0]
else:
event[key] = p[value]
else:
if (key == 'origin') or (key == 'destination'):
event[key] = {}
else:
event[key] = {}
if 'ip' in event['origin']:
eventsList.append(event)
i += 1
else:
break
elif (key == 'event'):
event[key] = ', '.join(p[value])
else:
event[key] = p[value]
else:
if (key == 'origin') or (key == 'destination'):
event[key] = {}
else:
event[key] = {}
if 'ip' in event['origin']:
eventsList.append(event)
i += 1
return eventsList
def getGeolocation(ip):
try:
response = requests.get('http://freegeoip.net/json/' + str(ip[0]))
except requests.exceptions.RequestException as error:
print error
sys.exit(1)
def getGeolocation(ip, db):
try:
json_data = json.loads(response.text)
except ValueError as error:
print error
sys.exit(1)
data = db.record_by_addr(ip)
return {'latitude': json_data['latitude'], 'longitude': json_data['longitude'], 'country_name': json_data['country_name'], 'city': json_data['city']}
if not data:
return {}
else:
return {
'latitude': data['latitude'],
'longitude': data['longitude'],
'country_name': data['country_name'] if data['country_name'] else None,
'city': data['city'] if data['city'] else None
}
def main(args):
events = args.events[0]
client = args.client[0]
key = args.key[0]
cert = args.cert[0]
cacert = args.cacert[0]
secret = args.secret[0]
if args.output is not None:
path = args.output[0] + 'warden-map.json'
else:
path = 'warden-map.json'
wardenEvents = getLastEvents(events, client, key, cert, cacert, secret)
db = GeoIP.open("GeoLiteCity.dat", GeoIP.GEOIP_MEMORY_CACHE)
db.set_charset(GeoIP.GEOIP_CHARSET_UTF8)
wardenEvents = getLastEvents(client, key, cert)
for p in wardenEvents:
for target in {'origin', 'destination'}:
geoData = {}
if 'ip' in p[target]:
geoData = getGeolocation(p[target]['ip'])
geoData = getGeolocation(p[target]['ip'], db)
for value in {'latitude', 'longitude', 'country_name', 'city'}:
if value in geoData:
if not geoData[value]:
......@@ -102,22 +99,13 @@ def main(args):
p[target]['latitude'] = 49.743
p[target]['longitude'] = 15.338
try:
with open(path, 'w') as outfile:
json.dump(wardenEvents, outfile)
except IOError:
print "Error: File does not appear to exist."
sys.exit(1)
wardenEvents.append(int(time.time()));
return 0
with open(path, 'w') as outfile:
json.dump(wardenEvents, outfile)
if __name__ == '__main__':
import sys
import json
from requests import Request, Session
import requests
import argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Creates warden-map.json for warden-map.html frontend.',
formatter_class=lambda prog: argparse.HelpFormatter(prog,max_help_position=30))
......@@ -126,18 +114,12 @@ if __name__ == '__main__':
requiredNamed = parser.add_argument_group('required arguments')
requiredNamed.add_argument('--events', metavar='<number>', type=str, required=True,
nargs=1, help='count of events for a map')
requiredNamed.add_argument('--client', metavar='<org.ex.cl>', type=str, required=True,
nargs=1, help='client name')
requiredNamed.add_argument('--key', metavar='path/key.pem', type=str, required=True,
nargs=1, help='SSL key for a client')
requiredNamed.add_argument('--cert', metavar='path/cert.pem', type=str, required=True,
nargs=1, help='SSL cert for a client')
requiredNamed.add_argument('--cacert', metavar='path/cacert.pem', type=str, required=True,
nargs=1, help='SSL cacert for a client')
requiredNamed.add_argument('--secret', metavar='<SeCreT>', type=str, required=True,
nargs=1, help='secret key for a client')
args = parser.parse_args()
main(args)
......@@ -11,6 +11,10 @@
body {
font-family: 'Oswald', sans-serif;
background: #00253D;
border: 0px;
padding: 0px;
margin: 0px;
}
h2 {
......@@ -22,7 +26,7 @@ h2 {
}
#country {
color: #0062a2;
color: #0062a2; /* Cesnet blue */
font-weight: bold;
}
......@@ -35,25 +39,29 @@ table {
}
table th {
color: #0062a2;
color: #0062a2; /* Cesnet blue */
padding: 0;
}
table td {
color: #4b4d4a;
color: #4b4d4a; /* Greenish gray */
padding: 0;
}
#container {
overflow: hidden;
border: 2px solid #0062a2;
border-radius: 5px;
background: white;
/* border: 2px solid #0062a2;
border: 0px;
padding: 0px;
margin: 0px;
border-radius: 5px;*/
position: relative;
width: 1280px;
height: 720px;
/* width: 1280px;
height: 720px;*/
max-width: 100%;
max-height: 100%
width: 100%;
height: 100vh;*/
}
.zoom-button {
......@@ -77,3 +85,54 @@ table td {
padding: 10px;
color: #0062a2;
}
#warden-logo {
position: absolute;
top: 30px;
left: 30px;
background: white;
padding: 10px;
border-radius: 10px;
width: 240px;
height: 92px;
text-align: center;
}
#cesnet-logo {
position: absolute;
top: 30px;
right: 30px;
background: white;
padding: 10px;
border-radius: 10px;
width: 240px;
height: 92px;
text-align: center;
}
#legend-box {
position: absolute;
bottom: 30px;
left: 30px;
background-color: rgba(0,0,0,0.3);
color: white;
padding: 10px;
border-radius: 10px;
/*width: 240px;
height: 92px;
text-align: center;*/
}
#heading {
position: absolute;
top: 30px;
left: 50%;
width: 40em;
height: 92px;
margin-left: -20em;
font-size: xx-large;
color: white;
text-align: center;
vertical-align: middle;
line-height: 92px;
}
......@@ -205,20 +205,197 @@ Zoom.prototype._getNextScale = function(direction) {
return scaleSet[shift];
};
function defaults(obj) {
Array.prototype.slice.call(arguments, 1).forEach(function(source) {
if (source) {
for (var prop in source) {
// Deep copy if property not set
if (obj[prop] == null) {
if (typeof source[prop] == 'function') {
obj[prop] = source[prop];
}
else {
obj[prop] = JSON.parse(JSON.stringify(source[prop]));
}
}
}
}
});
return obj;
}
function val( datumValue, optionsValue, context ) {
if ( typeof context === 'undefined' ) {
context = optionsValue;
optionsValues = undefined;
}
var value = typeof datumValue !== 'undefined' ? datumValue : optionsValue;
if (typeof value === 'undefined') {
return null;
}
if ( typeof value === 'function' ) {
var fnContext = [context];
if ( context.geography ) {
fnContext = [context.geography, context.data];
}
return value.apply(null, fnContext);
}
else {
return value;
}
}
var cat_color = {
"Abusive": "MediumPurple",
"Malware": "Red",
"Recon": "LightSlateGray",
"Attempt": "GhostWhite",
"Intrusion": "DarkTurquoise",
"Availability": "HotPink",
"Information": "PaleTurquoise",
"Fraud": "Yellow",
"Vulnerable": "DarkGoldenRod",
"Anomaly": "Brown",
"Other": "Green"
}
var cat_desc = {
"Abusive": "spam",
"Malware": "virus, worm, trojan, malware",
"Recon": "scanning, sniffing",
"Attempt": "bruteforce, exploitation attempt",
"Intrusion": "botnet, successful exploit",
"Availability": "(D)DOS",
"Information": "wiretapping, spoofing, hijacking",
"Fraud": "phishing, scam",
"Vulnerable": "open for abuse",
"Anomaly": "unusual traffic",
"Other": "unknown/unidentified"
}
function handleArcs (layer, data, options) {
var self = this,
svg = this.svg;
if ( !data || (data && !data.slice) ) {
throw "Datamaps Error - arcs must be an array";
}
// For some reason arc options were put in an `options` object instead of the parent arc
// I don't like this, so to match bubbles and other plugins I'm moving it
// This is to keep backwards compatability
for ( var i = 0; i < data.length; i++ ) {
data[i] = defaults(data[i], data[i].options);
delete data[i].options;
}
if ( typeof options === "undefined" ) {
options = defaultOptions.arcConfig;
}
var arcs = layer.selectAll('path.datamaps-arc').data( data, JSON.stringify );
var path = d3.geo.path()
.projection(self.projection);
arcs
.enter()
.append('svg:path')
.attr('class', 'datamaps-arc')
.style('stroke-linecap', 'round')
.style('stroke', function(datum) {
/* return val(datum.strokeColor, options.strokeColor, datum);*/
for (cat in cat_color) {
if (datum.event.startsWith(cat)) {
return cat_color[cat];
}
}
return "Green";
})
.style('fill', 'none')
.style('stroke-width', function(datum) {
return val(datum.strokeWidth, options.strokeWidth, datum);
})
.attr('d', function(datum) {
var originXY, destXY;
originXY = self.latLngToXY(val(datum.origin.latitude, datum), val(datum.origin.longitude, datum))
destXY = self.latLngToXY(val(datum.destination.latitude, datum), val(datum.destination.longitude, datum));
var midXY = [ (originXY[0] + destXY[0]) / 2, (originXY[1] + destXY[1]) / 2];
if (options.greatArc) {
// TODO: Move this to inside `if` clause when setting attr `d`
var greatArc = d3.geo.greatArc()
.source(function(d) { return [val(d.origin.longitude, d), val(d.origin.latitude, d)]; })
.target(function(d) { return [val(d.destination.longitude, d), val(d.destination.latitude, d)]; });
return path(greatArc(datum))
}
var sharpness = val(datum.arcSharpness, options.arcSharpness, datum);
return "M" + originXY[0] + ',' + originXY[1] + "S" + (midXY[0] + (50 * sharpness)) + "," + (midXY[1] - (75 * sharpness)) + "," + destXY[0] + "," + destXY[1];
})
.attr('data-info', function(datum) {
return JSON.stringify(datum);
})
.on('mouseover', function ( datum ) {
var $this = d3.select(this);
if (options.popupOnHover) {
self.updatePopup($this, datum, options, svg);
}
})
.on('mouseout', function ( datum ) {
var $this = d3.select(this);
d3.selectAll('.datamaps-hoverover').style('display', 'none');
})
.transition()
.style('fill', function(datum, i) {
/*
Thank you Jake Archibald, this is awesome.
Source: http://jakearchibald.com/2013/animated-line-drawing-svg/
*/
var length = this.getTotalLength();
this.style.transition = this.style.WebkitTransition = 'none';
this.style.strokeDasharray = length + ' ' + length;
this.style.strokeDashoffset = length;
this.getBoundingClientRect();
this.style.transition = this.style.WebkitTransition = 'stroke-dashoffset ' + val(datum.animationSpeed, options.animationSpeed, datum) + 'ms ' + datum.delay*1000 + 'ms ease-out';
this.style.strokeDashoffset = '0';
return 'none';
});
arcs.exit()
.transition()
.duration(1000)
.style('opacity', 0)
.remove();
}
var main_data = [];
var prev_data = 0;
// Configuration of datamap canvas
// Futher reading can be found at https://datamaps.github.io/
function Datamap() {
this.$container = $("#container");
this.instance = new Datamaps({
instance = this.instance = new Datamaps({
scope: 'world',
element: this.$container.get(0),
done: this._handleMapReady.bind(this),
projection: 'mercator',
fills: {
defaultFill: '#dcdcda'
/*defaultFill: '#454545'*/
defaultFill: 'black'
},
geographyConfig: {
borderColor: '#fdfdfd',
hideAntarctica: true,
borderColor: '#0062a2',
highlightFillColor: '#4b4d4a',
highlightBorderColor: '#fdfdfd',
popupOnHover: true,
......@@ -226,18 +403,18 @@ function Datamap() {
return '<div class="hoverinfo" id="country">' + geography.properties.name + '</div>';
},
},
arcConfig: {
ph_arcConfig: {
strokeColor: '#0062a2',
strokeWidth: 1,
arcSharpness: 5,
animationSpeed: 4000, // Milliseconds
strokeWidth: 2,
arcSharpness: 2, /* 5 */
animationSpeed: 3000, // Milliseconds
popupOnHover: true,
// Case with latitude and longitude
popupTemplate: function(geography, data) {
if ( ( data.origin && data.destination ) && data.origin.latitude && data.origin.longitude && data.destination.latitude && data.destination.longitude ) {
// Content of info table
str = '<div class="hoverinfo"><table id="event"><tr><th>Warden Event</th></tr><tr><td>Type</td><td>'+ JSON.stringify(data.event) +'</td></tr><tr><td>Detect Time</td><td>'+ JSON.stringify(data.time) +'</td></tr><tr><th>Event origin</th></tr><tr><td>IP</td><td>' + JSON.stringify(data.origin.ip) + '</td></tr><tr><td>City & Country</td><td>' + JSON.stringify(data.origin.city) + ',&nbsp;' + JSON.stringify(data.origin.country_name) + '</td></tr><tr><td>GPS</td><td>' + JSON.stringify(data.origin.latitude) + ',&nbsp;' + JSON.stringify(data.origin.longitude) + '</td></tr><tr><th>Event Destination</th></tr><tr><td>IP</td><td>' + JSON.stringify(data.destination.ip) + '</td></tr><tr><td>City & Country</td><td>' + JSON.stringify(data.destination.city) + ',&nbsp;' + JSON.stringify(data.destination.country_name) + '</td></tr><tr><td>GPS</td><td>' + JSON.stringify(data.destination.latitude) + ',&nbsp;' + JSON.stringify(data.destination.longitude) + '</td></tr></table></div>';
return str.replace(/"/g,"");
return str.replace(/&quot;/g,"");
}
// Missing information
else {
......@@ -246,15 +423,48 @@ function Datamap() {
}
}
});
var instance = this.instance;
// Link to a json file with information for drawing.
// NOTE: Change path if you separate backend and fronend!
d3.json("./warden-map.json", function(error, data) {
instance.arc(data);
});
};
legend_data = d3.select("#legend")
.selectAll("li")
.data(Object.keys(cat_color).sort())
.enter()
.append("li")
.append("span")
.style("color", function(datum) { return cat_color[datum]})
.text(function(datum) { return datum; })
.append("span")
.text(function(datum) { return "" + cat_desc[datum]})
.style("color", "white");
instance.addPlugin('ph_arc', handleArcs);
setInterval(function(){
d3.json("./warden-map.json", function(error, data) {
if (data) {
var cur_data = data.pop()
var cur_time = new Date().getTime();
if (cur_data != prev_data) {
prev_data = cur_data;
for (var i=0; i<data.length; i++) {
data[i].arrivalTime = cur_time;
data[i].delay = i/data.length;
}
main_data = main_data.concat(data);
}
}
var trimmed_data = [];
for (var i=0; i<main_data.length; i++) {
if (main_data[i].arrivalTime + 3500 > cur_time) {
trimmed_data.push(main_data[i]);
}
}
main_data = trimmed_data;
trimmed_data = cur_time = cur_data = error = data = null;
instance.ph_arc(main_data);
});
}, 1000);
};
Datamap.prototype._handleMapReady = function(datamap) {
this.zoom = new Zoom({
......
......@@ -20,9 +20,10 @@
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="./js/datamaps.world.hires.min.js"></script>
<script src="./js/datamaps.world.min.js"></script>
<script src="./js/warden-map.js"></script>
<!--
<h2>Warden Map</h2>
<div id="tools">
<button class="zoom-button" data-zoom="reset">&#x2302</button>
......@@ -30,7 +31,15 @@
<button class="zoom-button" data-zoom="in">+</button>
<div id="zoom-info"></div>
</div>
-->
<div id="container"></div>
<div id="heading">Attacks, detected in CESNET network<br/>
SABU - Sharing and Analysis of Security Events
</div>
<div id="legend-box">
<p><b>Reported to Warden right <i>now</i>.</b></p>
<ul id="legend"></ul>
</div>
<!-- Draw datamap into id="container" -->
<script>new Datamap();</script>
......
......@@ -53,7 +53,7 @@ C.1. Event description format
IDEA - Intrusion Detection Extensible Alert, flexible extensible format
for security events, see:
https://csirt.cesnet.cz/IDEA
https://idea.cesnet.cz/
C.2. Event serial ID
......
......@@ -470,6 +470,18 @@ def get_configs():
def get_uid_gid(str_id, get_nam_func):
if str_id:
try:
id = int(str_id)
except ValueError:
id = get_nam_func(str_id)[2]
else:
id = None
return id
if __name__ == "__main__":
args = get_args()
......@@ -482,12 +494,17 @@ if __name__ == "__main__":
try:
if args.daemon:
from pwd import getpwnam
from grp import getgrnam
uid = get_uid_gid(fconfig.get("uid"), getpwnam)
gid = get_uid_gid(fconfig.get("gid"), getgrnam)
daemonize(
work_dir = fconfig.get("work_dir", "."),
chroot_dir = fconfig.get("chroot_dir"),
umask = fconfig.get("umask"),
uid = fconfig.get("uid"),
gid = fconfig.get("gid"),
uid = uid,
gid = gid,
pidfile = args.pid_file,
files_preserve = get_logger_files(wclient.logger),
signals = {
......
......@@ -23,9 +23,6 @@ import subprocess
import shlex
import tempfile
import M2Crypto
# *ph* server vulnerable to logjam, local openssl too new, use hammer to disable Diffie-Helmann
import ssl
ssl._DEFAULT_CIPHERS += ":!DH"
import ejbcaws
......@@ -253,7 +250,13 @@ class EjbcaRegistry(OpenSSLRegistry):
subjectDN = self.subject_dn_template % client.name
)
edata["subjectAltName"] = ",".join(("RFC822NAME=%s" % a for a in client.admins))
edata["status"] = self.status_str_to_ejbca.get(client.status, edata["status"])
try:
edata["status"] = self.status_str_to_ejbca.get(client.status)
except KeyError:
# Unknown status - either came from EJBCA and translated to
# "Other", or something wrong came in later. Let's just
# keep original EJBCA status unchanged.
pass
if client.pwd:
edata["password"] = client.pwd
edata["clearPwd"] = True
......
......@@ -8,11 +8,15 @@ import sys
import warnings
from os import path
from copy import deepcopy
import unittest2 as unittest
import MySQLdb as my
from warden_server import build_server
import warden_server
if sys.version_info >= (3, 10):
import unittest
else:
import unittest2 as unittest
if sys.version_info[0] >= 3:
from io import StringIO
else:
......@@ -408,8 +412,9 @@ def init_user():
conn = None
try:
conn = my.connect(user='root', passwd=getpass.getpass('Enter MySQL Root password:'))
with conn as cur: # Not a canonical connector implementation, for sure
cur.execute("GRANT SELECT, INSERT, UPDATE, CREATE, DELETE, DROP ON *.* TO %s@'localhost' IDENTIFIED BY %s", (USER, PASSWORD))
with conn.cursor() as cur:
cur.execute("CREATE USER IF NOT EXISTS %s@'localhost' IDENTIFIED BY %s", (USER, PASSWORD))
cur.execute("GRANT SELECT, INSERT, UPDATE, CREATE, DELETE, DROP ON *.* TO %s@'localhost'", (USER,))
conn.commit()
print("DB User set up successfuly")
except my.OperationalError as ex:
......
......@@ -493,7 +493,7 @@ class MySQL(ObjectBase):
with io.open(tagmap_filename, "r", encoding="utf-8") as tagmap_fd:
self.tagmap = json.load(tagmap_fd)
self.tagmap_other = self.catmap["Other"] # Catch error soon, avoid lookup later
self.tagmap_other = self.tagmap["Other"] # Catch error soon, avoid lookup later
self.con = None
......@@ -714,10 +714,11 @@ class MySQL(ObjectBase):
if group or nogroup:
subquery = []
for name in (group or nogroup):
subquery.append("c.name = %s") # exact client
escaped_name = name.replace('&', '&&').replace("_", "&_").replace("%", "&%") # escape for LIKE
subquery.append("c.name = %s") # exact client
params.append(name)
subquery.append("c.name LIKE %s") # whole subtree
params.append(name + ".%")
subquery.append("c.name LIKE CONCAT(%s, '.%%') ESCAPE '&'") # whole subtree
params.append(escaped_name)
query.append(" AND %s (%s)" % (self._get_not(group), " OR ".join(subquery)))
......@@ -1095,6 +1096,7 @@ class WardenHandler(ObjectBase):
if self.get_events_limit:
count = min(count, self.get_events_limit)
count = max(0, count)
res = self.db.fetch_events(self.req.client, id, count, cat, nocat, tag, notag, group, nogroup)
......