First commit

This commit is contained in:
Félix Aime
2020-11-24 19:45:03 +01:00
parent c042b71634
commit 513f6b1b02
130 changed files with 76873 additions and 0 deletions

View File

@ -0,0 +1,15 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from sqlalchemy import create_engine, MetaData, Table
from sqlalchemy.orm import scoped_session, mapper
from sqlalchemy.orm.session import sessionmaker
import sys
parent = "/".join(sys.path[0].split("/")[:-2])
engine = create_engine('sqlite:////{}/tinycheck.sqlite3'.format(parent), convert_unicode=True)
metadata = MetaData(bind=engine)
session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
class Model(object):
query = session.query_property()

View File

@ -0,0 +1,88 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from flask import Blueprint, request, jsonify, send_file
from app.decorators import *
from app.classes.config import Config
import sys
config_bp = Blueprint("config", __name__)
config = Config()
@config_bp.route('/switch/<cat>/<key>', methods=['GET'])
@require_header_token
def switch(cat, key):
"""
Switch the Boolean value of a configuration key.
:return: status in JSON
"""
try:
value = config.read_config((cat, key))
if value:
config.write_config(cat, key, False)
res = { "status" : True,
"message" : "Key switched to false" }
else:
config.write_config(cat, key, True)
res = { "status" : True,
"message" : "Key switched to true" }
except:
res = { "status" : True,
"message" : "Issue while changing value" }
return jsonify(res)
@config_bp.route('/edit/<cat>/<key>/<path:value>', methods=['GET'])
@require_header_token
def edit(cat, key, value):
"""
Edit the string (or array) value of a configuration key.
:return: status in JSON
"""
value = value.split("|") if "|" in value else value
if config.write_config(cat, key, value):
res = { "status" : True,
"message" : "Configuration updated" }
else:
res = { "status" : False,
"message" : "Can't edit this configuration key" }
return jsonify(res)
@config_bp.route('/db/export', methods=['GET'])
@require_get_token
def export_db():
"""
Export the database.
:return: current database as attachment
"""
return config.export_db()
@config_bp.route('/db/import', methods=['POST'])
@require_header_token
def import_db():
"""
Import a database and replace the existant.
:return: status in JSON
"""
try:
f = request.files["file"]
assert f.read(15) == b"SQLite format 3"
d = "/".join(sys.path[0].split("/")[:-2])
f.save("/{}/tinycheck.sqlite3".format(d))
res = { "status" : True,
"message" : "Database updated" }
except:
res = { "status" : False,
"message" : "Error while database upload" }
return jsonify(res)
@config_bp.route('/list', methods=['GET'])
def list():
"""
List key, values of the configuration
:return: configuration in JSON
"""
res = config.export_config()
res["backend"]["password"] = ""
return jsonify(res)

View File

@ -0,0 +1,80 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from flask import Blueprint, jsonify, Response
from app.decorators import require_header_token, require_get_token
from app.classes.iocs import IOCs
import json
ioc_bp = Blueprint("ioc", __name__)
ioc = IOCs()
@ioc_bp.route('/add/<ioc_type>/<ioc_tag>/<ioc_tlp>/<path:ioc_value>', methods=['GET'])
@require_header_token
def add(ioc_type, ioc_tag, ioc_tlp, ioc_value):
"""
Parse and add an IOC to the database.
:return: status of the operation in JSON
"""
source = "backend"
res = IOCs.add(ioc_type, ioc_tag, ioc_tlp, ioc_value, source)
return jsonify(res)
@ioc_bp.route('/delete/<ioc_id>', methods=['GET'])
@require_header_token
def delete(ioc_id):
"""
Delete an IOC by its id to the database.
:return: status of the operation in JSON
"""
res = IOCs.delete(ioc_id)
return jsonify(res)
@ioc_bp.route('/search/<term>', methods=['GET'])
@require_header_token
def search(term):
"""
Search IOCs in the database.
:return: potential results in JSON.
"""
res = IOCs.search(term)
return jsonify({"results": [i for i in res]})
@ioc_bp.route('/get/types')
@require_header_token
def get_types():
"""
Retreive a list of IOCs types.
:return: list of types in JSON.
"""
res = IOCs.get_types()
return jsonify({"types": [t for t in res]})
@ioc_bp.route('/get/tags')
@require_header_token
def get_tags():
"""
Retreive a list of IOCs tags.
:return: list of types in JSON.
"""
res = IOCs.get_tags()
return jsonify({"tags": [t for t in res]})
@ioc_bp.route('/export')
@require_get_token
def get_all():
"""
Retreive a list of all IOCs.
:return: list of iocs in JSON.
"""
res = IOCs.get_all()
return Response(json.dumps({"iocs": [i for i in res]}),
mimetype='application/json',
headers={'Content-Disposition': 'attachment;filename=iocs-export.json'})

View File

@ -0,0 +1,68 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from flask import Blueprint, jsonify, Response
from app.decorators import require_header_token, require_get_token
from app.classes.whitelist import WhiteList
import json
whitelist_bp = Blueprint("whitelist", __name__)
whitelist = WhiteList()
@whitelist_bp.route('/add/<elem_type>/<path:elem_value>', methods=['GET'])
@require_header_token
def add(elem_type, elem_value):
"""
Parse and add an element to be whitelisted.
:return: status of the operation in JSON
"""
source = "backend"
res = whitelist.add(elem_type, elem_value, source)
return jsonify(res)
@whitelist_bp.route('/delete/<elem_id>', methods=['GET'])
@require_header_token
def delete(elem_id):
"""
Delete an element by its id to the database.
:return: status of the operation in JSON
"""
res = whitelist.delete(elem_id)
return jsonify(res)
@whitelist_bp.route('/search/<element>', methods=['GET'])
@require_header_token
def search(element):
"""
Search elements in the database.
:return: potential results in JSON.
"""
res = whitelist.search(element)
return jsonify({"results": [e for e in res]})
@whitelist_bp.route('/get/types')
@require_header_token
def get_types():
"""
Retrieve a list of whitelisted elements types.
:return: list of types in JSON.
"""
res = whitelist.get_types()
return jsonify({"types": [t for t in res]})
@whitelist_bp.route('/export')
@require_get_token
def get_all():
"""
Retreive a list of all elements.
:return: list of elements in JSON.
"""
res = whitelist.get_all()
return Response(json.dumps({"elements": [e for e in res]}),
mimetype='application/json',
headers={'Content-Disposition': 'attachment;filename=whitelist-export.json'})

View File

@ -0,0 +1,107 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import yaml
import sys
import io
import os
import re
import hashlib
from functools import reduce
from flask import send_file
class Config(object):
def __init__(self):
self.dir = "/".join(sys.path[0].split("/")[:-2])
return None
def read_config(self, path):
"""
Read a single value from the configuration
:return: value (it can be any type)
"""
config = yaml.load(
open(os.path.join(self.dir, "config.yaml"), "r"), Loader=yaml.SafeLoader)
return reduce(dict.get, path, config)
def export_config(self):
"""
Export the configuration
:return: dict (configuration content)
"""
config = yaml.load(
open(os.path.join(self.dir, "config.yaml"), "r"), Loader=yaml.SafeLoader)
config["interfaces"] = self.get_wireless_interfaces()
return config
def write_config(self, cat, key, value):
"""
Write a new value in the configuration
:return: bool, operation status
"""
config = yaml.load(
open(os.path.join(self.dir, "config.yaml"), "r"), Loader=yaml.SafeLoader)
config[cat][key] = value if key != "password" else self.make_password(
value)
if cat == "network" and key == "in":
self.edit_configuration_files(value)
with open(os.path.join(self.dir, "config.yaml"), "w") as yaml_file:
yaml_file.write(yaml.dump(config, default_flow_style=False))
return True
def make_password(self, clear_text):
"""
Make a simple password hash (without salt)
"""
return hashlib.sha256(clear_text.encode()).hexdigest()
def export_db(self):
"""
Export the database.
:return: send_file (the database)
"""
with open(os.path.join(self.dir, "tinycheck.sqlite3"), "rb") as f:
return send_file(
io.BytesIO(f.read()),
mimetype="application/octet-stream",
as_attachment=True,
attachment_filename='tinycheck-export-db.sqlite')
def get_wireless_interfaces(self):
"""
List the Wireless interfaces installed on the box
:return: list of the interfaces
"""
try:
return [i for i in os.listdir("/sys/class/net/") if i.startswith("wlan")]
except:
return ["Fake iface1", "Fake iface 2"]
def edit_configuration_files(self, iface):
"""
Edit the DNSMasq and DHCPCD configuration files
:return: nothing.
"""
try:
if re.match("^wlan[0-9]{1}$", iface):
# Edit of DHCPD.conf
with open("/etc/dhcpcd.conf", 'r') as file:
content = file.readlines()
for i, line in enumerate(content):
if line.startswith("interface"):
content[i] = "interface {}\n".format(iface)
with open("/etc/dhcpcd.conf", 'w') as file:
file.writelines(content)
# Edit of DNSMASQ.conf
with open("/etc/dnsmasq.conf", 'r') as file:
content = file.readlines()
for i, line in enumerate(content):
if line.startswith("interface"):
content[i] = "interface={}\n".format(iface)
with open("/etc/dnsmasq.conf", 'w') as file:
file.writelines(content)
except:
pass

View File

@ -0,0 +1,133 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from app import db
from app.db.models import Ioc
from sqlalchemy.sql import exists
from app.definitions import definitions
from flask import escape
import re
import time
class IOCs(object):
def __init__(self):
return None
@staticmethod
def add(ioc_type, ioc_tag, ioc_tlp, ioc_value, source):
"""
Parse and add an IOC to the database.
:return: status of the operation in JSON
"""
ioc_value = ioc_value.lower() if ioc_type != "snort" else ioc_value
ioc_valid = False
if db.session.query(exists().where(Ioc.value == ioc_value)).scalar():
return {"status": False,
"message": "IOC already exists",
"ioc": escape(ioc_value)}
elif ioc_tlp in ["white", "green", "amber", "red"]:
if ioc_type == "unknown":
for t in definitions["iocs_types"]:
if t["regex"] and t["auto"]:
if re.match(t["regex"], ioc_value):
ioc_type = t["type"]
ioc_valid = True
elif ioc_type in [t["type"] for t in definitions["iocs_types"]]:
for t in definitions["iocs_types"]:
if t["type"] == ioc_type and t["regex"]:
if re.match(t["regex"], ioc_value):
ioc_valid = True
break
elif t["type"] == "snort" and ioc_value[0:6] == "alert ":
ioc_valid = True
break
else:
return {"status": True,
"message": "Wrong IOC type",
"ioc": escape(ioc_value),
"type": escape(ioc_type)}
if ioc_valid:
added_on = int(time.time())
db.session.add(Ioc(ioc_value, ioc_type, ioc_tlp,
ioc_tag, source, added_on))
db.session.commit()
return {"status": True,
"message": "IOC added",
"ioc": escape(ioc_value)}
else:
return {"status": False,
"message": "Wrong IOC format",
"ioc": escape(ioc_value)}
else:
return {"status": False,
"message": "Wrong IOC TLP",
"ioc": escape(ioc_value),
"type": escape(ioc_tlp)}
@staticmethod
def delete(ioc_id):
"""
Delete an IOC by its id to the database.
:return: status of the operation in JSON
"""
if db.session.query(exists().where(Ioc.id == ioc_id)).scalar():
db.session.query(Ioc).filter_by(id=ioc_id).delete()
db.session.commit()
return {"status": True,
"message": "IOC deleted"}
else:
return {"status": False,
"message": "IOC not found"}
@staticmethod
def search(term):
"""
Search IOCs in the database.
:return: generator of results.
"""
iocs = db.session.query(Ioc).filter(
Ioc.value.like(term.replace("*", "%"))).all()
for ioc in iocs:
ioc = ioc.__dict__
yield {"id": ioc["id"],
"type": ioc["type"],
"tag": ioc["tag"],
"tlp": ioc["tlp"],
"value": ioc["value"]}
@staticmethod
def get_types():
"""
Retreive a list of IOCs types.
:return: generator of iocs types.
"""
for t in definitions["iocs_types"]:
yield {"type": t["type"],
"name": t["name"]}
@staticmethod
def get_tags():
"""
Retreive a list of IOCs tags.
:return: generator of iocs tags.
"""
for t in definitions["iocs_tags"]:
yield {"tag": t["tag"],
"name": t["name"]}
@staticmethod
def get_all():
"""
Get all IOCs from the database
:return: generator of the records.
"""
for ioc in db.session.query(Ioc).all():
ioc = ioc.__dict__
yield {"id": ioc["id"],
"type": ioc["type"],
"tag": ioc["tag"],
"tlp": ioc["tlp"],
"value": ioc["value"]}

View File

@ -0,0 +1,102 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from app import db
from app.db.models import Whitelist
from sqlalchemy.sql import exists
from app.definitions import definitions
from flask import escape
import re
import time
class WhiteList(object):
def __init__(self):
return None
@staticmethod
def add(elem_type, elem_value, source):
"""
Parse and add an element to be whitelisted.
:return: status of the operation in a dict
"""
elem_value = elem_value.lower()
elem_valid = False
if db.session.query(exists().where(Whitelist.element == elem_value)).scalar():
return {"status": False,
"message": "Element already whitelisted",
"element": escape(elem_value)}
elif elem_type == "unknown":
for t in definitions["whitelist_types"]:
if t["regex"] and t["auto"]:
if re.match(t["regex"], elem_value):
elem_type = t["type"]
elem_valid = True
break
elif elem_type in [t["type"] for t in definitions["whitelist_types"]]:
for t in definitions["whitelist_types"]:
if t["type"] == elem_type and t["regex"]:
if re.match(t["regex"], elem_value):
elem_valid = True
break
if elem_valid:
added_on = int(time.time())
db.session.add(Whitelist(elem_value, elem_type, source, added_on))
db.session.commit()
return {"status": True,
"message": "Element whitelisted",
"element": escape(elem_value)}
else:
return {"status": False,
"message": "Wrong element format",
"element": escape(elem_value)}
@staticmethod
def delete(elem_id):
"""
Delete an element by its id to the database.
:return: status of the operation in a dict
"""
if db.session.query(exists().where(Whitelist.id == elem_id)).scalar():
db.session.query(Whitelist).filter_by(id=elem_id).delete()
db.session.commit()
return {"status": True,
"message": "Element deleted"}
else:
return {"status": False,
"message": "Element not found"}
@staticmethod
def search(element):
"""
Search elements in the database.
:return: generator containing elements.
"""
elems = db.session.query(Whitelist).filter(
Whitelist.element.like(element.replace("*", "%"))).all()
for elem in elems:
elem = elem.__dict__
yield {"id": elem["id"],
"type": elem["type"],
"element": elem["element"]}
@staticmethod
def get_types():
"""
Get types of whitelisted elements.
:return: generator containing types.
"""
for t in definitions["whitelist_types"]:
yield {"type": t["type"], "name": t["name"]}
@staticmethod
def get_all():
"""
Retrieve all whitelisted elements.
:return: generator containing elements.
"""
for elem in db.session.query(Whitelist).all():
elem = elem.__dict__
yield {"type": elem["type"],
"element": elem["element"]}

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from sqlalchemy import create_engine, MetaData, Table
from sqlalchemy.orm import scoped_session, mapper
from sqlalchemy.orm.session import sessionmaker
import sys
parent = "/".join(sys.path[0].split("/")[:-2])
engine = create_engine(
'sqlite:////{}/tinycheck.sqlite3'.format(parent), convert_unicode=True)
metadata = MetaData(bind=engine)
session = scoped_session(sessionmaker(
autocommit=False, autoflush=False, bind=engine))
class Model(object):
query = session.query_property()

View File

@ -0,0 +1,20 @@
from app import db
class Ioc(db.Model):
def __init__(self, value, type, tlp, tag, source, added_on):
self.value = value
self.type = type
self.tlp = tlp
self.tag = tag
self.source = source
self.added_on = added_on
class Whitelist(db.Model):
def __init__(self, element, type, source, added_on):
self.element = element
self.type = type
self.source = source
self.added_on = added_on
db.mapper(Whitelist, db.Table('whitelist', db.metadata, autoload=True))
db.mapper(Ioc, db.Table('iocs', db.metadata, autoload=True))

View File

@ -0,0 +1,65 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from flask import request, jsonify
from flask import current_app as app
from flask_httpauth import HTTPBasicAuth
from werkzeug.security import generate_password_hash, check_password_hash
from functools import wraps
from app.utils import read_config
import jwt
import hashlib
auth = HTTPBasicAuth()
@auth.verify_password
def check_creds(user, password):
"""
Check the credentials
:return: :bool: if the authentication succeed.
"""
if user == read_config(("backend", "login")) and check_password(password):
return True
def check_password(password):
"""
Password hashes comparison (submitted and the config one)
:return: True if there is a match between the two hases
"""
if read_config(("backend", "password")) == hashlib.sha256(password.encode()).hexdigest():
return True
def require_header_token(f):
"""
Check the JWT token validity in POST requests.
:return: decorated method
"""
@wraps(f)
def decorated(*args, **kwargs):
try:
token = request.headers['X-Token']
jwt.decode(token, app.config["SECRET_KEY"])
return f(*args, **kwargs)
except:
return jsonify({"message": "JWT verification failed"})
return decorated
def require_get_token(f):
"""
Check the JWT token validity in GET requests.
:return: decorated method
"""
@wraps(f)
def decorated(*args, **kwargs):
try:
token = request.args.get("token")
jwt.decode(token, app.config["SECRET_KEY"])
return f(*args, **kwargs)
except:
return jsonify({"message": "JWT verification failed"})
return decorated

View File

@ -0,0 +1,115 @@
definitions = {
"iocs_types" : [
{
"type" : "ip4addr",
"regex" : r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
"name" : "IPv4 Address",
"auto" : True
},
{
"type" : "ip6addr",
"regex" : r"^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$",
"name" : "IPv6 Address",
"auto" : True
},
{
"type" : "cidr",
"regex" : r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([0-9]|[1-2][0-9]|3[0-2]))?$",
"name" : "Network range",
"auto" : True
},
{
"type" : "domain",
"regex" : r"^((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}$",
"name" : "Domain name",
"auto" : True
},
{
"type" : "sha1cert",
"regex" : r"^[0-9a-f]{40}$",
"name" : "Certificate SHA1",
"auto" : True
},
{
"type" : "snort",
"regex" : False,
"name" : "Snort rule",
"auto" : False
},
{
"type" : "ns",
"regex" : r"^((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}$",
"name" : "Name Server",
"auto" : False
},
{
"type" : "freedns",
"regex" : r"^((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}$",
"name" : "Free DNS",
"auto" : False
},
{
"type" : "tld",
"regex" : r"^\.[a-z]{2,63}$",
"name" : "Suspect TLD",
"auto" : False
}
],
"iocs_tags" : [
{
"tag" : "apt",
"name" : "APT"
},
{
"tag" : "stalkerware",
"name" : "Stalkerware"
},
{
"tag" : "suspect",
"name" : "Suspect"
},
{
"tag" : "malicious",
"name" : "Malicious"
},
{
"tag" : "tracker",
"name" : "Tracker"
},
{
"tag" : "spyware",
"name" : "Spyware"
},
{
"tag" : "cybercrime",
"name" : "Cybercrime"
}
],
"whitelist_types" : [
{
"type" : "ip4addr",
"regex" : r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
"name" : "IPv4 Address",
"auto" : True
},
{
"type" : "ip6addr",
"regex" : r"^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$",
"name" : "IPv6 Address",
"auto" : True
},
{
"type" : "cidr",
"regex" : r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([0-9]|[1-2][0-9]|3[0-2]))?$",
"name" : "Network range",
"auto" : True
},
{
"type" : "domain",
"regex" : r"^((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}$",
"name" : "Domain name",
"auto" : True
}
]
}

View File

@ -0,0 +1,35 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import yaml
import sys
import os
from functools import reduce
def read_config(path):
"""
Read a value from the configuration
:return: value (it can be any type)
"""
dir = "/".join(sys.path[0].split("/")[:-2])
config = yaml.load(open(os.path.join(dir, "config.yaml"), "r"),
Loader=yaml.SafeLoader)
return reduce(dict.get, path, config)
def write_config(cat, key, value):
"""
Write a new value in the configuration
:return: bool, operation status
"""
try:
dir = "/".join(sys.path[0].split("/")[:-2])
config = yaml.load(open(os.path.join(dir, "config.yaml"),
"r"), Loader=yaml.SafeLoader)
config[cat][key] = value
with open(os.path.join(dir, "config.yaml"), "w") as yaml_file:
yaml_file.write(yaml.dump(config, default_flow_style=False))
return True
except:
return False