Esempio n. 1
0
        def _log_to_logger(*args, **kwargs):
            start_time = int(round(time.time() * 1000))
            request_time = datetime.now()

            # Log information about the request before it is processed for debugging purposes
            REQUESTS_LOG.info('%s | %s | %s | %s' % (request_time,
                                                     request.remote_addr,
                                                     request.method,
                                                     request.url))

            if request.headers is not None:
                for key, value in request.headers.items():
                    REQUESTS_LOG.info('  HEADERS | %s: %s' % (key, value))

            if request.json is not None:
                for key, value in request.json.items():
                    REQUESTS_LOG.info('  BODY | %s: %s' % (key, value))

            actual_response = response
            try:
                actual_response = fn(*args, **kwargs)
            except Exception as ex:
                response_status = '500 ' + str(ex)
                LOG.error('%s caused an exception: %s' % (request.url, ex))
                error_traceback = traceback.format_exc()
                for line in error_traceback.split('\n'):
                    LOG.error(line)

                if get_mail_on_exception() is True:
                    variables = {'HOST': get_host(),
                                 'TRACEBACK': error_traceback}
                    body_template = os.path.join('server_exception')
                    sendmail(recipients=get_notification_email(),
                             subject='Exception occurred @ %s' % get_host(),
                             body_template=body_template,
                             variables=variables)

            else:
                response_status = response.status

            end_time = int(round(time.time() * 1000))
            REQUESTS_LOG.info('%s | %s | %s | %s | %s | %s ms' % (request_time,
                                                                  request.remote_addr,
                                                                  request.method,
                                                                  request.url,
                                                                  response_status,
                                                                  end_time - start_time))
            return actual_response
Esempio n. 2
0
 def arm(self):
     if self.phase == SwitchPhase.PHASE_0:
         self.phase = SwitchPhase.PHASE_1
         self.activation_time = int(time.time()) + self.timeout
         LOG.info(
             "Dead Man's Switch %s has been armed, will activate in %s seconds on %s"
             % (self.id, self.timeout,
                datetime.fromtimestamp(
                    self.activation_time).strftime('%Y-%m-%d %H:%M:%S')))
         email_variables = {
             'activation_time':
             datetime.fromtimestamp(
                 self.activation_time).strftime('%Y-%m-%d %H:%M:%S')
         }
         sendmail(self.warning_email,
                  "Warning: Dead Man's Switch %s has been armed" % self.id,
                  'deadmansswitchwarning', email_variables)
         self.save()
Esempio n. 3
0
    def run(self):
        """
        Run the action

        :return: True upon success, False upon failure
        """
        return sendmail(recipients=self.mail_recipients,
                        subject=self.mail_subject,
                        body_template=self.mail_body_template,
                        variables=self.mail_variables,
                        images=self.mail_images,
                        attachments=self.mail_attachments)
Esempio n. 4
0
    def conditions_fulfilled(self):
        if self.timeout is None or self.activation_time is None or self.warning_email is None:
            return False

        email_variables = {
            'activation_time':
            datetime.fromtimestamp(
                self.activation_time).strftime('%Y-%m-%d %H:%M:%S')
        }

        if self.phase == SwitchPhase.PHASE_1 and int(
                time.time()) >= int(self.activation_time -
                                    (self.timeout * 0.5)):
            # 50% of timeout has passed, send first warning and move to phase 2
            self.phase = SwitchPhase.PHASE_2
            LOG.info(
                "Dead Man's Switch %s is now in phase %s, sending first warning email"
                % (self.id, self.phase))
            sendmail(
                self.warning_email,
                "First warning: Dead Man's Switch %s at 50 percent" % self.id,
                'deadmansswitchwarning', email_variables)
            self.save()

        if self.phase == SwitchPhase.PHASE_2 and int(
                time.time()) >= int(self.activation_time -
                                    (self.timeout * 0.25)):
            # 75% of timeout has passed, send second warning and move to phase 3
            self.phase = SwitchPhase.PHASE_3
            LOG.info(
                "Dead Man's Switch %s is now in phase %s, sending second warning email"
                % (self.id, self.phase))
            sendmail(
                self.warning_email,
                "Second warning: Dead Man's Switch %s at 75 percent" % self.id,
                'deadmansswitchwarning', email_variables)
            self.save()

        if self.phase == SwitchPhase.PHASE_3 and int(
                time.time()) >= int(self.activation_time -
                                    (self.timeout * 0.1)):
            # 90% of timeout has passed, send final warning and move to phase 4
            self.phase = SwitchPhase.PHASE_4
            LOG.info(
                "Dead Man's Switch %s is now in phase %s, sending final warning email"
                % (self.id, self.phase))
            sendmail(
                self.warning_email,
                "Final warning: Dead Man's Switch %s at 90 percent" % self.id,
                'deadmansswitchwarning', email_variables)
            self.save()

        if self.phase == SwitchPhase.PHASE_4 and int(time.time()) >= int(
                self.activation_time):
            # 90% of timeout has passed, send final warning and move to phase 4
            self.phase = SwitchPhase.PHASE_5
            LOG.info(
                "Dead Man's Switch %s is now in phase %s, activating trigger" %
                (self.id, self.phase))
            sendmail(self.warning_email,
                     "Dead Man's Switch %s activated" % self.id,
                     'deadmansswitchactivated', email_variables)
            self.save()

        return self.phase == SwitchPhase.PHASE_5
Esempio n. 5
0
    def __init__(self):
        super(SpellbookRESTAPI, self).__init__()

        # Initialize variables
        self.host = get_host()
        self.port = get_port()

        # Log the requests to the REST API in a separate file by installing a custom LoggingPlugin
        self.install(self.log_to_logger)

        # Make sure that an api_keys.json file is present, the first time the server is started
        # a new random api key and secret pair will be generated
        if not os.path.isfile('json/private/api_keys.json'):
            LOG.info('Generating new API keys')
            initialize_api_keys_file()

        LOG.info('Starting Bitcoin Spellbook')

        try:
            get_hot_wallet()
        except Exception as ex:
            LOG.error('Unable to decrypt hot wallet: %s' % ex)
            sys.exit(1)

        LOG.info(
            'To make the server run in the background: use Control-Z, then use command: bg %1'
        )

        # Initialize the routes for the REST API
        self.route(
            '/', method='GET', callback=self.index
        )  # on linux this gets requested every minute or so, but not on windows
        self.route('/favicon.ico', method='GET', callback=self.get_favicon)

        # Route for ping, to test if server is online
        self.route('/spellbook/ping', method='GET', callback=self.ping)

        # Routes for managing blockexplorers
        self.route('/spellbook/explorers',
                   method='GET',
                   callback=self.get_explorers)
        self.route('/spellbook/explorers/<explorer_id:re:[a-zA-Z0-9_\-.]+>',
                   method='POST',
                   callback=self.save_explorer)
        self.route('/spellbook/explorers/<explorer_id:re:[a-zA-Z0-9_\-.]+>',
                   method='GET',
                   callback=self.get_explorer_config)
        self.route('/spellbook/explorers/<explorer_id:re:[a-zA-Z0-9_\-.]+>',
                   method='DELETE',
                   callback=self.delete_explorer)

        # Routes for retrieving data from the blockchain
        self.route('/spellbook/blocks/latest',
                   method='GET',
                   callback=self.get_latest_block)
        self.route('/spellbook/blocks/<height:int>',
                   method='GET',
                   callback=self.get_block_by_height)
        self.route('/spellbook/blocks/<block_hash:re:[a-f0-9]+>',
                   method='GET',
                   callback=self.get_block_by_hash)

        self.route('/spellbook/transactions/<txid:re:[a-f0-9]+>/prime_input',
                   method='GET',
                   callback=self.get_prime_input_address)
        self.route('/spellbook/transactions/<txid:re:[a-f0-9]+>',
                   method='GET',
                   callback=self.get_transaction)
        self.route(
            '/spellbook/addresses/<address:re:[a-zA-Z1-9]+>/transactions',
            method='GET',
            callback=self.get_transactions)
        self.route('/spellbook/addresses/<address:re:[a-zA-Z1-9]+>/balance',
                   method='GET',
                   callback=self.get_balance)
        self.route('/spellbook/addresses/<address:re:[a-zA-Z1-9]+>/utxos',
                   method='GET',
                   callback=self.get_utxos)

        # Routes for Simplified Inputs List (SIL)
        self.route('/spellbook/addresses/<address:re:[a-zA-Z1-9]+>/SIL',
                   method='GET',
                   callback=self.get_sil)

        # Routes for Profile
        self.route('/spellbook/addresses/<address:re:[a-zA-Z1-9]+>/profile',
                   method='GET',
                   callback=self.get_profile)

        # Routes for Simplified UTXO List (SUL)
        self.route('/spellbook/addresses/<address:re:[a-zA-Z1-9]+>/SUL',
                   method='GET',
                   callback=self.get_sul)

        # Routes for Linked Lists
        self.route('/spellbook/addresses/<address:re:[a-zA-Z1-9]+>/LAL',
                   method='GET',
                   callback=self.get_lal)
        self.route('/spellbook/addresses/<address:re:[a-zA-Z1-9]+>/LBL',
                   method='GET',
                   callback=self.get_lbl)
        self.route('/spellbook/addresses/<address:re:[a-zA-Z1-9]+>/LRL',
                   method='GET',
                   callback=self.get_lrl)
        self.route('/spellbook/addresses/<address:re:[a-zA-Z1-9]+>/LSL',
                   method='GET',
                   callback=self.get_lsl)

        # Routes for Random Address
        self.route('/spellbook/addresses/<address:re:[a-zA-Z1-9]+>/random/SIL',
                   method='GET',
                   callback=self.get_random_address_from_sil)
        self.route('/spellbook/addresses/<address:re:[a-zA-Z1-9]+>/random/LBL',
                   method='GET',
                   callback=self.get_random_address_from_lbl)
        self.route('/spellbook/addresses/<address:re:[a-zA-Z1-9]+>/random/LRL',
                   method='GET',
                   callback=self.get_random_address_from_lrl)
        self.route('/spellbook/addresses/<address:re:[a-zA-Z1-9]+>/random/LSL',
                   method='GET',
                   callback=self.get_random_address_from_lsl)

        # Routes for Triggers
        self.route('/spellbook/triggers',
                   method='GET',
                   callback=self.get_triggers)
        self.route('/spellbook/triggers/<trigger_id:re:[a-zA-Z0-9_\-.]+>',
                   method='GET',
                   callback=self.get_trigger)
        self.route('/spellbook/triggers/<trigger_id:re:[a-zA-Z0-9_\-.]+>',
                   method='POST',
                   callback=self.save_trigger)
        self.route('/spellbook/triggers/<trigger_id:re:[a-zA-Z0-9_\-.]+>',
                   method='DELETE',
                   callback=self.delete_trigger)
        self.route(
            '/spellbook/triggers/<trigger_id:re:[a-zA-Z0-9_\-.]+>/activate',
            method='GET',
            callback=self.activate_trigger)
        self.route(
            '/spellbook/triggers/<trigger_id:re:[a-zA-Z0-9_\-.]+>/message',
            method='POST',
            callback=self.verify_signed_message)
        self.route('/spellbook/triggers/<trigger_id:re:[a-zA-Z0-9_\-.]+>/get',
                   method='GET',
                   callback=self.http_get_request)
        self.route('/spellbook/triggers/<trigger_id:re:[a-zA-Z0-9_\-.]+>/post',
                   method='POST',
                   callback=self.http_post_request)
        self.route(
            '/spellbook/triggers/<trigger_id:re:[a-zA-Z0-9_\-.]+>/delete',
            method='DELETE',
            callback=self.http_delete_request)
        self.route(
            '/spellbook/triggers/<trigger_id:re:[a-zA-Z0-9_\-.]+>/check',
            method='GET',
            callback=self.check_trigger)
        self.route('/spellbook/check_triggers',
                   method='GET',
                   callback=self.check_all_triggers)

        # Additional routes for Rest API endpoints
        self.route('/api/<trigger_id:re:[a-zA-Z0-9_\-.]+>',
                   method='GET',
                   callback=self.http_get_request)
        self.route('/api/<trigger_id:re:[a-zA-Z0-9_\-.]+>',
                   method='OPTIONS',
                   callback=self.http_get_request)
        self.route('/api/<trigger_id:re:[a-zA-Z0-9_\-.]+>',
                   method='POST',
                   callback=self.http_post_request)
        self.route('/api/<trigger_id:re:[a-zA-Z0-9_\-.]+>',
                   method='DELETE',
                   callback=self.http_delete_request)
        self.route('/html/<trigger_id:re:[a-zA-Z0-9_\-.]+>',
                   method='GET',
                   callback=self.html_request)
        self.route('/api/<trigger_id:re:[a-zA-Z0-9_\-.]+>/message',
                   method='POST',
                   callback=self.verify_signed_message)

        self.route('/api/sign_message',
                   method='POST',
                   callback=self.sign_message)

        # Routes for QR image generation
        self.route('/api/qr', method='GET', callback=self.qr)

        # Routes for Actions
        self.route('/spellbook/actions',
                   method='GET',
                   callback=self.get_actions)
        self.route('/spellbook/actions/<action_id:re:[a-zA-Z0-9_\-.]+>',
                   method='GET',
                   callback=self.get_action)
        self.route('/spellbook/actions/<action_id:re:[a-zA-Z0-9_\-.]+>',
                   method='POST',
                   callback=self.save_action)
        self.route('/spellbook/actions/<action_id:re:[a-zA-Z0-9_\-.]+>',
                   method='DELETE',
                   callback=self.delete_action)
        self.route('/spellbook/actions/<action_id:re:[a-zA-Z0-9_\-.]+>/run',
                   method='GET',
                   callback=self.run_action)

        # Routes for retrieving log messages
        self.route('/spellbook/logs/<filter_string>',
                   method='GET',
                   callback=self.get_logs)

        # Routes for RevealSecret actions
        self.route('/spellbook/actions/<action_id:re:[a-zA-Z0-9_\-.]+>/reveal',
                   method='GET',
                   callback=self.get_reveal)

        # Check if there are explorers configured, this will also initialize the default explorers on first startup
        if len(get_explorers()) == 0:
            LOG.warning('No block explorers configured!')

        try:
            # start the webserver for the REST API
            if get_enable_ssl() is True:
                self.run(host=self.host,
                         port=self.port,
                         debug=False,
                         server='sslwebserver')
            else:
                self.run(host=self.host,
                         port=self.port,
                         debug=True,
                         server='cheroot')

        except Exception as ex:
            LOG.error('An exception occurred in the main loop: %s' % ex)
            error_traceback = traceback.format_exc()
            for line in error_traceback.split('\n'):
                LOG.error(line)

            if get_mail_on_exception() is True:
                variables = {'HOST': get_host(), 'TRACEBACK': error_traceback}
                body_template = os.path.join('server_exception')
                sendmail(recipients=get_notification_email(),
                         subject='Main loop Exception occurred @ %s' %
                         get_host(),
                         body_template=body_template,
                         variables=variables)
Esempio n. 6
0
def uptime_check(email, ipfs=False, reboot=False, ssl=None):
    LOG.info('CPU: %s%%' % psutil.cpu_percent())
    LOG.info('RAM: %s' % str(psutil.virtual_memory()))
    LOG.info('Checking if spellbook server is still online')

    if ssl is None:
        url = 'http://{host}:{port}/spellbook/ping'.format(host=get_host(),
                                                           port=get_port())
    else:
        url = 'https://{host}:{port}/spellbook/ping'.format(host=ssl,
                                                            port=get_port())
    try:
        r = requests.get(url=url, timeout=10)
        response = r.json()
    except Exception as ex:
        LOG.error('Unable to ping spellbook server: %s' % ex)
        response = {}

    online = True if 'success' in response and response[
        'success'] is True else False

    if not online:
        LOG.error('Spellbook server is not online!')
        if email is not None:
            variables = {
                'HOST': get_host(),
                'SPELLBOOK_LOG': get_recent_spellbook_log(),
                'REQUESTS_LOG': get_recent_requests_log()
            }
            body_template = os.path.join('server_offline')
            success = sendmail(recipients=email,
                               subject='Spellbookserver @ %s is offline!' %
                               get_host(),
                               body_template=body_template,
                               variables=variables)
            if success is True:
                LOG.info('Email sent successfully')

                if reboot is True and platform.system() == 'Linux':
                    LOG.info('Rebooting server because uptime check failed!')
                    RunCommandProcess(command='sudo reboot').run()

            else:
                LOG.error('Email to %s failed!' % email)
    else:
        LOG.info('Server is online')

    if ipfs is True:
        try:
            response = add_str('ping')
        except Exception as ex:
            LOG.error('IPFS node is offline: %s' % ex)
            if email is not None:
                variables = {'HOST': get_host()}
                body_template = os.path.join('ipfs_offline')
                success = sendmail(recipients=email,
                                   subject='IPFS node @ %s is offline!' %
                                   get_host(),
                                   body_template=body_template,
                                   variables=variables)
                if success is True:
                    LOG.info('Email sent successfully')

                    if reboot is True and platform.system() == 'Linux':
                        LOG.info(
                            'Rebooting server because ipfs node is offline!')
                        RunCommandProcess(command='sudo reboot').run()

                else:
                    LOG.error('Email to %s failed!' % email)