import logging
import os
import random
from abc import ABC, abstractmethod
from random import randint
from time import sleep, strftime

HOSTNAME = ['defence-first.rs', 'defence-first.de', 'defence-first.ru']
HOSTIP = ['78.218.236.218', '87.236.11.212', '54.147.165.86']
SOURCEIP = ['163.189.141.53', '204.164.10.7', '213.166.160.236', '123.197.235.233', '77.28.21.14']
USERNAMES = ['user1', 'user2', 'user3', 'user4', 'user5']
FACILITY = ['KERN', 'USER', 'MAIL', 'DAEMON', 'AUTH', 'SYSLOG', 'LPR', 'NEWS',
            'UUCP', 'CLOCK_DAEMON', 'AUTHPRIV', 'FTP', 'NTP', 'LOGAUDIT', 'LOGALERT',
            'CRON', 'LOCAL0', 'LOCAL1', 'LOCAL2', 'LOCAL3', 'LOCAL4', 'LOCAL5', 'LOCAL6', 'LOCAL7']
SEVERITY = ['DEBUG', 'INFORMATIONAL', 'NOTICE', 'WARNING', 'ERROR', 'CRITICAL', 'ALERT', 'EMERGENCY']
FORMAT = '%(asctime)s     %(hostname)s-Application-%(hostip)s-%(sourceip)s     %(severity)s-%(facility)s     %(' \
         'message)s '
RESOURCES = ['index.html', 'document.xml', 'dashboard.html']

LOGS_PATH = 'logs'


class State(ABC):
    @abstractmethod
    def run(self, context):
        return NotImplemented


class DoSAttack(State):
    def run(self, context):
        d = {'hostname': HOSTNAME[0], 'hostip': HOSTIP[0], 'severity': SEVERITY[1],
             'facility': FACILITY[1]}

        http_response_code = '200'
        for i in range(25):
            if i >= 20:
                http_response_code = '503'
                d['severity'] = SEVERITY[5]

            for sourceip in SOURCEIP:
                d['sourceip'] = sourceip
                context.logger.info('Requested resource index.html {}'.format(http_response_code), extra=d)

        context.state = NormalState()


class NormalState(State):
    def run(self, context):
        normal = {'hostname': HOSTNAME[1], 'hostip': HOSTIP[1], 'severity': SEVERITY[1],
                  'facility': FACILITY[1]}

        while True:
            normal['sourceip'] = random.choice(SOURCEIP)
            if random.random() < 0.3:
                context.logger.info(
                    'Successful authorization on username "{}"'.format(USERNAMES[SOURCEIP.index(normal['sourceip'])]),
                    extra=normal)
            else:
                context.logger.info('Requested resource {} 200'.format(random.choice(RESOURCES)), extra=normal)
            sleep(1)

            if random.random() < 0.1:
                rand = randint(1, 3)

                if rand == 1:
                    context.state = DoSAttack()
                elif rand == 2:
                    context.state = BruteForce()
                elif rand == 3:
                    context.state = DatabaseError()

                context.state.run(context)


class BruteForce(State):
    def run(self, context):
        attack = {'hostname': HOSTNAME[1], 'hostip': HOSTIP[1], 'sourceip': SOURCEIP[0], 'severity': SEVERITY[2],
                  'facility': FACILITY[4]}
        normal = {'hostname': HOSTNAME[1], 'hostip': HOSTIP[1], 'severity': SEVERITY[1],
                  'facility': FACILITY[1]}

        for i in range(30):
            if i > 5:
                attack['severity'] = SEVERITY[3]

            if random.random() < 0.45:
                normal['sourceip'] = random.choice(SOURCEIP)
                context.logger.info('Requested resource {} 200'.format(random.choice(RESOURCES)), extra=normal)
                sleep(0.5)

            context.logger.info('Failed authorization on username "user1"', extra=attack)
            sleep(0.5)

        context.state = NormalState()


class DatabaseError(State):
    def run(self, context):
        d = {'hostname': HOSTNAME[2], 'hostip': HOSTIP[2], 'sourceip': SOURCEIP[0], 'severity': SEVERITY[4],
             'facility': FACILITY[3]}

        context.logger.info('Database error', extra=d)
        sleep(1)

        context.state = NormalState()


class Context:
    def __init__(self):
        self.state = NormalState()
        formatter = logging.Formatter(FORMAT, "%Y-%m-%d %H:%M:%S")

        logger = logging.getLogger('simulator')

        if not os.path.exists(LOGS_PATH):
            os.mkdir(LOGS_PATH)

        fileHandler = logging.FileHandler(
            os.path.join(LOGS_PATH, 'application_log-{}.log'.format(strftime('%Y-%m-%d'))))
        fileHandler.setFormatter(formatter)

        consoleHandler = logging.StreamHandler()
        consoleHandler.setFormatter(formatter)

        logger.addHandler(fileHandler)
        logger.addHandler(consoleHandler)

        logger.setLevel(logging.INFO)

        self.logger = logger

    def run(self):
        self.state.run(self)

    @property
    def state(self):
        return self._state

    @state.setter
    def state(self, value):
        self._state = value


if __name__ == '__main__':
    sm = Context()
    sm.run()

[((103, 8, 103, 16), 'time.sleep', 'sleep', ({(103, 14, 103, 15): '(1)'}, {}), '(1)', False, 'from time import sleep, strftime\n'), ((111, 20, 111, 66), 'logging.Formatter', 'logging.Formatter', ({(111, 38, 111, 44): 'FORMAT', (111, 46, 111, 65): '"""%Y-%m-%d %H:%M:%S"""'}, {}), "(FORMAT, '%Y-%m-%d %H:%M:%S')", False, 'import logging\n'), ((113, 17, 113, 47), 'logging.getLogger', 'logging.getLogger', ({(113, 35, 113, 46): '"""simulator"""'}, {}), "('simulator')", False, 'import logging\n'), ((122, 25, 122, 48), 'logging.StreamHandler', 'logging.StreamHandler', ({}, {}), '()', False, 'import logging\n'), ((53, 33, 53, 56), 'random.choice', 'random.choice', ({(53, 47, 53, 55): 'SOURCEIP'}, {}), '(SOURCEIP)', False, 'import random\n'), ((60, 12, 60, 20), 'time.sleep', 'sleep', ({(60, 18, 60, 19): '(1)'}, {}), '(1)', False, 'from time import sleep, strftime\n'), ((92, 12, 92, 22), 'time.sleep', 'sleep', ({(92, 18, 92, 21): '(0.5)'}, {}), '(0.5)', False, 'from time import sleep, strftime\n'), ((115, 15, 115, 40), 'os.path.exists', 'os.path.exists', ({(115, 30, 115, 39): 'LOGS_PATH'}, {}), '(LOGS_PATH)', False, 'import os\n'), ((116, 12, 116, 31), 'os.mkdir', 'os.mkdir', ({(116, 21, 116, 30): 'LOGS_PATH'}, {}), '(LOGS_PATH)', False, 'import os\n'), ((54, 15, 54, 30), 'random.random', 'random.random', ({}, {}), '()', False, 'import random\n'), ((62, 15, 62, 30), 'random.random', 'random.random', ({}, {}), '()', False, 'import random\n'), ((63, 23, 63, 36), 'random.randint', 'randint', ({(63, 31, 63, 32): '1', (63, 34, 63, 35): '3'}, {}), '(1, 3)', False, 'from random import randint\n'), ((86, 15, 86, 30), 'random.random', 'random.random', ({}, {}), '()', False, 'import random\n'), ((87, 37, 87, 60), 'random.choice', 'random.choice', ({(87, 51, 87, 59): 'SOURCEIP'}, {}), '(SOURCEIP)', False, 'import random\n'), ((89, 16, 89, 26), 'time.sleep', 'sleep', ({(89, 22, 89, 25): '(0.5)'}, {}), '(0.5)', False, 'from time import sleep, strftime\n'), ((119, 68, 119, 88), 'time.strftime', 'strftime', ({(119, 77, 119, 87): '"""%Y-%m-%d"""'}, {}), "('%Y-%m-%d')", False, 'from time import sleep, strftime\n'), ((59, 71, 59, 95), 'random.choice', 'random.choice', ({(59, 85, 59, 94): 'RESOURCES'}, {}), '(RESOURCES)', False, 'import random\n'), ((88, 71, 88, 95), 'random.choice', 'random.choice', ({(88, 85, 88, 94): 'RESOURCES'}, {}), '(RESOURCES)', False, 'import random\n')]