def send_stall_approved_email(stall): if stall.election.polling_places_loaded is False: location_info = stall.location_info stall_name = location_info["name"] else: location_info = model_to_dict(stall.polling_place) stall_name = location_info["premises"] token = str(getrandbits(128)) signature = make_confirmation_hash(stall.id, token) html = get_mail_template( "stall_approved_with_mail_optout", { "POLLING_PLACE_NAME": location_info["name"], "POLLING_PLACE_ADDRESS": location_info["address"], "STALL_NAME": stall.name, "STALL_DESCRIPTION": stall.description, "STALL_OPENING_HOURS": stall.opening_hours, "STALL_WEBSITE": stall.website, "DELICIOUSNESS": getFoodDescription(stall), "STALL_PERMALINK": "{site_url}/{election_name}/stalls/{stall_id}".format( site_url=get_env("PUBLIC_SITE_URL"), stall_id=stall.id, election_name=get_url_safe_election_name(stall.election)), "STALL_EDIT_URL": "{site_url}/edit-stall?stall_id={stall_id}&token={token}&signature={signature}" .format(site_url=get_env("PUBLIC_SITE_URL"), stall_id=stall.id, token=token, signature=signature), "CONFIRM_OPTOUT_URL": "{api_url}/0.1/mail/opt_out/?format=json&stall_id={stall_id}&token={token}&signature={signature}" .format(api_url=get_env("PUBLIC_API_BASE_URL"), stall_id=stall.id, token=token, signature=signature), }) return send({ "to": stall.email, "subject": "Your Democracy Sausage stall for {} has been approved!".format( stall_name), "html": html, })
def __init__(self, election, file, dry_run, config): def _get_config_or_none(param_name, config): return config[param_name] if self.has_config is True and param_name in config else None def check_config_is_valid(config): if config is not None: for field in config.keys(): if field not in allowed_fields: self.logger.error("Config: Invalid field '{}' in config".format(field)) if "address_fields" in config and "address_format" not in config: self.logger.error("Config: address_format required if address_fields provided") if "address_fields" not in config and "address_format" in config: self.logger.error("Config: address_fields required if address_format provided") return True self.election = election self.dry_run = dry_run self.logger = self.make_logger() allowed_fields = ["filters", "exclude_columns", "rename_columns", "extras", "cleaning_regexes", "address_fields", "address_format", "division_fields", "fix_data_issues", "geocoding", "bbox_validation", "multiple_division_handling"] self.has_config = True if config is not None and check_config_is_valid(config) else False self.raise_exception_if_errors() for field_name in allowed_fields: setattr(self, field_name, _get_config_or_none(field_name, config)) if self.geocoding is not None and self.geocoding["enabled"] is True: self.gmaps = googlemaps.Client(key=get_env("GOOGLE_GEOCODING_API_KEY")) self.file = file file_body = self.file.read() encoding = chardet.detect(file_body)["encoding"] self.reader = csv.DictReader(StringIO(file_body.decode(encoding))) self.polling_places = list(self.reader)
def verify_webhook(token, timestamp, signature): # Check if the timestamp is fresh if abs(time.time() - timestamp) > 30: # seconds return False return generate_signature(get_env("MAILGUN_API_KEY"), "{}{}".format(timestamp, token)) == signature
def send(body): r = requests.post( get_env("MAILGUN_API_BASE_URL") + "/messages", auth=("api", get_env("MAILGUN_API_KEY")), data={ **{ "from": get_env("MAILGUN_FROM_ADDRESS"), "h:Reply-To": get_env("MAILGUN_REPLY_TO_ADDRESS"), }, **body }) if r.status_code != 200: raise MailgunException("Mailgun Error ({}): {}".format( r.status_code, r.text)) return True
def send_stall_submitted_email(stall): if stall.election.polling_places_loaded is False: location_info = stall.location_info stall_name = location_info["name"] else: location_info = model_to_dict(stall.polling_place) stall_name = location_info["premises"] token = str(getrandbits(128)) signature = make_confirmation_hash(stall.id, token) html = get_mail_template( "stall_submitted", { "POLLING_PLACE_NAME": location_info["name"], "POLLING_PLACE_ADDRESS": location_info["address"], "STALL_NAME": stall.name, "STALL_DESCRIPTION": stall.description, "STALL_OPENING_HOURS": stall.opening_hours, "STALL_WEBSITE": stall.website, "DELICIOUSNESS": getFoodDescription(stall), "STALL_EDIT_URL": "{site_url}/edit-stall?stall_id={stall_id}&token={token}&signature={signature}" .format(site_url=get_env("PUBLIC_SITE_URL"), stall_id=stall.id, token=token, signature=signature), }) return send({ "to": stall.email, "subject": "Your Democracy Sausage stall for {} has been received!".format( stall_name), "html": html, })
import os import sentry_sdk from corsheaders.defaults import default_headers from sentry_sdk.integrations.django import DjangoIntegration from demsausage.util import get_env # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = get_env("SECRET_KEY") # Security SECURE_SSL_REDIRECT = True # https://stackoverflow.com/a/22284717 SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') SECURE_CONTENT_TYPE_NOSNIFF = True SECURE_BROWSER_XSS_FILTER = True SESSION_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True X_FRAME_OPTIONS = "DENY" CORS_ALLOW_CREDENTIALS = True CORS_ALLOWED_ORIGINS = list(get_env("ALLOWED_HOSTS_AND_WHITELIST").split(",")) CSRF_TRUSTED_ORIGINS = get_env("ALLOWED_HOSTS_AND_WHITELIST").replace( "https://", "").split(",")
def is_development(): return get_env("ENVIRONMENT") == "DEVELOPMENT"
def make_confirmation_hash(stall_id, token): return generate_signature(get_env("SECRET_KEY"), "{}{}".format(stall_id, token))