Ejemplo n.º 1
0
def send(template_data={}, **kwargs):
    # Determine if message should send
    if util.is_development() and not config.should_deliver_smtp_dev:
        logging.info('\n\n---------------------------------------------------')
        logging.info('Email not sent, config.should_deliver_smtp_dev False.')
        logging.info('Email params:')
        logging.info(kwargs)
        logging.info(template_data)
        if kwargs['template'] == 'authorship_email.html':
            logging.info(
                '/{entity_type}/{entity_id}/accept_authorship?uid={to_uid}&token={token}'
                .format(**template_data)
            )
        logging.info('\n---------------------------------------------------\n')
        return

    subject = render(kwargs['subject'], **template_data)

    # Determine if using html string or a template
    body = ''
    if 'body' in kwargs:
        body = render(kwargs['body'], **template_data)
    elif 'template' in kwargs:
        body = render_template(kwargs['template'], **template_data)

    # JSON for Mandrill HTTP POST request
    sv = ndb.Key('SecretValue', 'mandrill_api_key').get()
    api_key = getattr(sv, 'value', config.mandrill_api_key)
    if not util.is_development() and api_key is config.mandrill_api_key:
        logging.error("No mandrill api_key set in production!")
    json_mandrill = {
        "key": api_key,
        "message": {
            "html": body,
            "subject": subject,
            "from_email": config.from_server_email_address,
            "from_name": "BELE Library",
            "inline_css": True,
            "to": format_to_address(kwargs['to_address'])
        }
    }

    # URL for Mandrill HTTP POST request
    url = "https://mandrillapp.com/api/1.0/messages/send.json"
    rpc = urlfetch.create_rpc()
    urlfetch.make_fetch_call(rpc, url=url,
        payload=json.dumps(json_mandrill),
        method=urlfetch.POST,
        headers={'Content-Type': 'application/x-www-form-urlencoded'})
    try:
        result = rpc.get_result()
        logging.info(u"...{}".format(result.status_code))
        logging.info(result.content)
        if result.status_code == 200:
            text = result.content
    except urlfetch.DownloadError:
        # Request timed out or failed.
        logging.error('Email failed to send.')
        result = None
    return result
Ejemplo n.º 2
0
 def delete_everything(self):
     if self.user.user_type == 'god' and util.is_development():
         util.delete_everything()
     else:
         raise PermissionDenied("Only gods working on a development server "
                                "can delete everything.")
     return True
Ejemplo n.º 3
0
def mandrill_send(template_data={}, **kwargs):

    # Determine if message should send
    if util.is_development() and not config.should_deliver_smtp_dev:
        logging.info('Email not sent, check config!')
        return None

    subject = render(kwargs['subject'], **template_data)

    # Add in default template data
    template_data['to_address'] = kwargs.get('to_address', None)
    template_data['domain'] = util.get_domain()
    # Python keeps time to the microsecond, but we don't need it, and
    # it's easier to render as ISO 8601 without it.
    template_data['server_time'] = datetime.datetime.today().replace(microsecond=0)
    template_data['contact_email_address'] = config.from_server_email_address

    # Determine if using html string or a template
    html_body = None
    if 'html' in kwargs:
        html_body = kwargs['html']
    elif 'body' in kwargs:
        html_body = render(kwargs['body'], **template_data)
    elif 'template' in kwargs:
        html_body = render_template(kwargs['template'], **template_data)

    text_body = kwargs.get('text', None)

    if util.is_localhost() or util.is_testing():
        sender = _send_localhost_and_testing
    elif util.is_development():
        sender = _send_development
    else:
        sender = _send_production

    optional_mandrill_keys = ('from_address', 'reply_to', 'from_name')
    optional_mandrill_kwargs = {k: kwargs[k] for k in optional_mandrill_keys
                                if k in kwargs}

    return sender(kwargs['to_address'], subject, html_body, text_body,
                  kwargs.get('mandrill_template', None),
                  kwargs.get('mandrill_template_content', None),
                  kwargs.get('cc_address', None),
                  kwargs.get('bcc_address', None),
                  **optional_mandrill_kwargs)
Ejemplo n.º 4
0
def get_public_rsa():
    """Get key for verifying asymmetric token. Defaults to config."""
    sv_entity = SecretValue.get_by_id('jwt_public_rsa')
    if sv_entity is None:
        if not util.is_development():
            logging.error("No default jwt rsa public key set in production!")
        return config.default_jwt_public_rsa
    else:
        return sv_entity.value
Ejemplo n.º 5
0
def get_secret():
    """Get secret for signing/verifying symmetric token. Defaults to config."""
    sv_entity = SecretValue.get_by_id('jwt_secret')
    if sv_entity is None:
        if not util.is_development():
            logging.error("No default jwt secret set in production!")
        return config.default_jwt_secret
    else:
        return sv_entity.value
Ejemplo n.º 6
0
    def reset(self, table_definitions):
        """Drop all given tables and re-created them.

        Takes a dictionary of table name to with CREATE TABLE query string.
        """
        if not util.is_development():
            raise Exception("You REALLY don't want to do that.")

        for table, definition in table_definitions.items():
            self.query('DROP TABLE IF EXISTS `{}`;'.format(table))
            self.query(definition)
Ejemplo n.º 7
0
def send(template_data={}, **kwargs):

    subject = render(kwargs['subject'], **template_data)

    # Determine if using html string or a template
    body = ''
    if 'body' in kwargs:
        body = render(kwargs['body'], **template_data)
    elif 'template' in kwargs:
        body = render_template(kwargs['template'], **template_data)

    # JSON for Mandrill HTTP POST request
    json_mandrill = {
        # "key": this is added later, to make sure it's not logged.
        "message": {
            "html": body,
            "subject": subject,
            "from_email": config.from_yellowstone_email_address,
            "from_name": "PERTS",
            "inline_css": True,
            "to": format_to_address(kwargs['to_address'])
        }
    }

    # Determine if message should send
    if util.is_development() and not config.should_deliver_smtp_dev:
        logging.info('Email not sent, check config!')
        logging.info(json_mandrill)
        return None

    api_key = SecretValue.get_by_key_name('mandrill_api_key').value
    if api_key is None:
        raise Exception("No SecretValue set for 'mandrill_api_key'")
    json_mandrill['key'] = api_key

    # URL for Mandrill HTTP POST request
    url = "https://mandrillapp.com/api/1.0/messages/send.json"
    rpc = urlfetch.create_rpc()
    urlfetch.make_fetch_call(
        rpc,
        url=url,
        payload=json.dumps(json_mandrill),
        method=urlfetch.POST,
        headers={'Content-Type': 'application/x-www-form-urlencoded'},
    )
    try:
        result = rpc.get_result()
        if result.status_code == 200:
            text = result.content
    except urlfetch.DownloadError:
        # Request timed out or failed.
        logging.error('Email failed to send.')
        result = None
    return result
Ejemplo n.º 8
0
    def reset(self, table_definitions):
        """Drop all given tables and re-created them.

        Takes a dictionary of table name to with CREATE TABLE query string.
        """
        if not util.is_development():
            raise Exception("You REALLY don't want to do that.")

        for table, definition in table_definitions.items():
            self.query('DROP TABLE IF EXISTS `{}`;'.format(table))
            self.query(definition)
Ejemplo n.º 9
0
    def mail_log(self):
        body = self.body + self.get_recent_log()
        subject = self.subject + self.get_error_summary()

        if util.is_development():
            logging.warning("This is a development environment."
                            "Mail NOT really sent.")
        else:
            mail.send_mail(self.from_address, self.to_address, subject, body)

        self.last_email = self.now
        return (subject, body)
Ejemplo n.º 10
0
def call(api_path, payload):
    """Make a synchronous call to the Mandrill API.

    See https://mandrillapp.com/api/docs/

    Args:
        api_path: str, just the last two parts of the path, e.g.
            'messages/send.json'. Varies by what part of the API you're using.
        payload: dict, always omitting the api key, which is added here in
            this function.

    Returns: None or parsed JSON from Mandrill response.
    """

    sv_entity = ndb.Key('SecretValue', 'mandrill_api_key').get()
    if sv_entity:
        payload['key'] = sv_entity.value
    else:
        if not util.is_development():
            logging.error("No mandrill api key set in production!")
        payload['key'] = config.default_mandrill_api_key

    try:
        result = urlfetch.fetch(
            url='https://mandrillapp.com/api/1.0/{}'.format(api_path),
            payload=json.dumps(payload),
            method=urlfetch.POST,
            headers={'Content-Type': 'application/x-www-form-urlencoded'},
            # Default deadline appears to be 5 seconds, based on the numerous
            # errors we're getting. Give the Mandrill API more time to respond.
            # https://cloud.google.com/appengine/docs/standard/python/refdocs/google.appengine.api.urlfetch#google.appengine.api.urlfetch.fetch
            deadline=60,  # seconds
        )
    except urlfetch.DownloadError:
        logging.error("Caught urlfetch.DownloadError. "
                      "Request timed out or failed.")
        content = None
    else:
        if not result or result.status_code != 200:
            logging.error("Non-200 response from Mandrill.")
            content = None
        else:
            content = json.loads(result.content)
        logging.info("urlfetch result: {} {}".format(result.status_code,
                                                     result.content))

    return content
Ejemplo n.º 11
0
    def init_database(self):
        """If necessary, create the database and populate it with tables."""
        # Only do this in development, because it takes some small amount of time
        # out of each query. We're willing to pay that cost in development for
        # ease of quick deployment, but we want to optimize for production.
        if not util.is_development() or util.is_codeship():
            return

        db_params = mysql_connection.get_params()

        with mysql_connection.connect(specify_db=False) as sql:
            params = (db_params['db_name'], )
            rows = sql.query('SHOW DATABASES LIKE %s', param_tuple=params)
            if len(rows) == 0:
                sql.query('CREATE DATABASE `{}`'.format(db_params['db_name']))
                sql.query('USE `{}`'.format(db_params['db_name']))
                sql.reset({
                    m.table: m.get_table_definition()
                    for m in get_sql_models()
                })
Ejemplo n.º 12
0
    def connect_to_db(self):
        """Establish connection to MySQL db instance.

        Either Google Cloud SQL or local MySQL server. Detects environment with
        functions from util module.
        """
        if util.is_localhost():
            env_type = 'localhost'
        elif util.is_development():
            env_type = 'development'
        else:
            env_type = 'production'

        creds = self.credentials[env_type]

        # Although the docs say you can specify a `cursorclass` keyword
        # here as an easy way to get dictionaries out instead of lists, that
        # only works in version 1.2.5, and App Engine only has 1.2.4b4
        # installed as of 2015-03-30. Don't use it unless you know the
        # production library has been updated.
        # tl;dr: the following not allowed!
        # self.connection = MySQLdb.connect(
        #     charset='utf8', cursorclass=MySQLdb.cursors.DictCursor, **creds)
        self.connection = MySQLdb.connect(charset='utf8', **creds)
Ejemplo n.º 13
0
import logging
import random  # ForgotPasswordHandler
import string  # ForgotPasswordHandler
import time
import traceback
import urllib  # ForgotPasswordHandler

from core import *
from named import *
from api import kind_to_class
from url_handlers import *
import util

# make sure to turn this off in production!!
# it exposes exception messages
debug = util.is_development()


class ApiHandler(BaseHandler):
    """Superclass for all api-related urls."""
    def dispatch(self):
        if self.datastore_connected():
            # Call the overridden dispatch(), which has the effect of running
            # the get() or post() etc. of the inheriting class.
            BaseHandler.dispatch(self)
        else:
            # Sometimes the datastore doesn't respond. I really don't know why.
            # Wait a bit and try again.
            attempts = int(self.request.get('connection_attempts', 0)) + 1
            if attempts <= 10:
                time.sleep(1)
Ejemplo n.º 14
0
    def dispatch(self):
        """Wraps the other request handlers.

        * Manages sessions
        * Manages request profiling
        """
        util.profiler.add_event("BaseHandler.dispatch()")

        # ** Code to run before all handlers goes here. ** #

        # The App Engine runtime does weird caching of classes and class
        # properties such that you can't expect them to be cleanly segregated
        # or reset between requests. But we want to use this property to avoid
        # multiple lookups of the same user within a request. So make sure it
        # has a clean start.
        # https://cloud.google.com/appengine/docs/standard/python/how-requests-are-handled#app-caching
        self._user = None

        if util.is_localhost():
            # ports are arbitrary, but convenient
            os.environ['YELLOWSTONE_DOMAIN'] = 'localhost:9080'
            os.environ['YELLOWSTONE_PROTOCOL'] = 'http'
            os.environ['NEPTUNE_DOMAIN'] = 'localhost:8080'
            os.environ['NEPTUNE_PROTOCOL'] = 'http'
            os.environ['TRITON_DOMAIN'] = 'localhost:10080'
            os.environ['TRITON_PROTOCOL'] = 'http'
        else:
            # Various DOMAINs remain set as in app.yaml
            os.environ['YELLOWSTONE_PROTOCOL'] = 'https'
            os.environ['NEPTUNE_PROTOCOL'] = 'https'
            os.environ['TRITON_PROTOCOL'] = 'https'

        # Set the namespace, which varies by branch.
        namespace = os.environ['NAMESPACE']
        if namespace:
            logging.info("Setting namespace: {}".format(namespace))
            namespace_manager.set_namespace(namespace)

        # Newly deployed dev branches might not have a database in their
        # namespace yet.
        self.init_database()

        if self.using_sessions():
            # Get a session store for this request.
            self.session_store = sessions.get_store(request=self.request)

        # Allow load testing services to log in quickly.
        if util.is_development() and self.request.get('demo_login',
                                                      None) == 'wamxdkrwnkgey':
            user = User.get_by_id('User_demo')
            self.log_in(user)
            self.redirect(self.request.path)

        # Handler classes may set a class property `requires_auth` which triggers a check
        # for an authenticated user. If there isn't one, the request is immediately
        # rejeted with a 401. This does not apply to preflight OPTIONS calls which never
        # include Authorization headers (they're about figuring out the server's CORS
        # rules, not taking any actions).
        authed = getattr(self, 'requires_auth', False)
        options = self.request.method == 'OPTIONS'

        # This may be used by downstream handlers to override permissions if
        # necessary.
        self.allowed_by_jwt = self.jwt_allows_endpoint(self.get_endpoint_str())
        if self.allowed_by_jwt:
            logging.info("BaseHandler: this request is ALLOWED by the jwt.")

        if authed and not options:
            user = self.get_current_user()
            if user.user_type == 'public' and not self.allowed_by_jwt:
                return self.http_unauthorized()

        try:
            # Call the overridden dispatch(), which has the effect of running
            # the get() or post() etc. of the inheriting class.
            webapp2.RequestHandler.dispatch(self)

        finally:
            # ** Code to run after all handlers goes here. ** #

            if self.using_sessions():
                # Save all sessions.
                self.session_store.save_sessions(self.response)

            util.profiler.add_event("END")

            # Turn on for debugging/profiling.
            # logging.info(util.profiler)

            util.profiler.clear()
Ejemplo n.º 15
0
class CsvFile:
    """Uses Google Cloud Storage to manage csv files with certain presets."""

    mime_type = 'text/plain'

    if util.is_development():
        bucket = 'perts_dev'
    else:
        bucket = 'perts_prod'
    # You can specific some parameters here, see commented example.
    retry_params = gcs.RetryParams()
    # retry_params = gcs.RetryParams(initial_delay=0.2,
    #                                max_delay=5.0,
    #                                backoff_factor=2,
    #                                max_retry_period=15)

    # Only used during writes. Controls permissions. For types of permission,
    # see https://developers.google.com/storage/docs/accesscontrol#extension
    write_options = {
        'x-goog-acl': 'project-private',  # default permission
        # 'x-goog-meta-foo': 'foo',  # arbitrary meta data
        # 'x-goog-meta-bar': 'bar',
    }

    @classmethod
    def list_bucket_files(klass, bucket_name):
        iterable = gcs.listbucket('/' + bucket_name + '/')
        return [f.filename for f in iterable]

    def __init__(self, filename):
        self.relative_path = filename
        self.absolute_path = '/{}/{}'.format(self.bucket, self.relative_path)
        # create the file, if it doesn't exist already
        # This doesn't work.

        try:
            gcs_file = gcs.open(self.absolute_path,
                                mode='r',
                                retry_params=self.retry_params)
            gcs_file.close()
        except:
            self.write(json.dumps([]))

    def read(self):
        gcs_file = gcs.open(self.absolute_path,
                            mode='r',
                            retry_params=self.retry_params)
        string = gcs_file.read()
        gcs_file.close()
        return string

    def write(self, string):
        gcs_file = gcs.open(self.absolute_path,
                            mode='w',
                            content_type=self.mime_type,
                            retry_params=self.retry_params,
                            options=self.write_options)
        # unicode not allowed here
        gcs_file.write(str(string))
        gcs_file.close()

    @classmethod
    def delete(self, filename):
        """Delete method will remove a file from GCS, provided an absolute
        file path."""
        try:
            gcs.delete(filename)
            return "{} deleted.".format(filename)
        except gcs.NotFoundError:
            return 'GCS File Not Found'