Ejemplo n.º 1
0
 def get_dt_tz(self):
     dt_tz = self.request.query.get('dttz') or 'utc'
     try:
         pytz.timezone(dt_tz)
     except KeyError:
         raise JsonErrors.HTTPBadRequest(f'unknown timezone: "{dt_tz}"')
     return dt_tz
Ejemplo n.º 2
0
    async def call(self, request):
        m = await self.request_data(SmsSendModel)
        with await self.redis as redis:
            group_key = f'group:{m.uid}'
            v = await redis.incr(group_key)
            if v > 1:
                raise JsonErrors.HTTPConflict(f'Send group with id "{m.uid}" already exists\n')
            await redis.expire(group_key, 86400)

        month_spend = None
        if m.cost_limit is not None:
            start, end = month_interval()
            month_spend = await get_sms_spend(self.app['pg'], m.company_code, start, end, m.method)
            if month_spend >= m.cost_limit:
                return self.json_response(
                    status='send limit exceeded', cost_limit=m.cost_limit, spend=month_spend, status_=402
                )

        async with self.app['pg'].acquire() as conn:
            company_id = await get_create_company_id(conn, m.company_code)
            group_id = await conn.fetchval_b(
                'insert into message_groups (:values__names) values :values returning id',
                values=Values(uuid=m.uid, company_id=company_id, message_method=m.method, from_name=m.from_name),
            )
        logger.info('%s sending %d SMSs', m.company_code, len(m.recipients))

        recipients = m.recipients
        m_base = m.copy(exclude={'recipients'})
        del m
        for recipient in recipients:
            await self.redis.enqueue_job('send_sms', group_id, company_id, recipient, m_base)

        return self.json_response(status='enqueued', spend=month_spend, status_=201)
Ejemplo n.º 3
0
    async def call(self, request):
        m: EmailSendModel = await self.request_data(EmailSendModel)
        with await self.redis as redis:
            group_key = f'group:{m.uid}'
            v = await redis.incr(group_key)
            if v > 1:
                raise JsonErrors.HTTPConflict(f'Send group with id "{m.uid}" already exists\n')
            await redis.expire(group_key, 86400)

        logger.info('sending %d emails (group %s) via %s for %s', len(m.recipients), m.uid, m.method, m.company_code)
        async with self.app['pg'].acquire() as conn:
            company_id = await get_create_company_id(conn, m.company_code)
            group_id = await conn.fetchval_b(
                'insert into message_groups (:values__names) values :values returning id',
                values=Values(
                    uuid=m.uid,
                    company_id=company_id,
                    message_method=m.method,
                    from_email=m.from_address.email,
                    from_name=m.from_address.name,
                ),
            )
        recipients = m.recipients
        m_base = m.copy(exclude={'recipients'})
        del m
        for recipient in recipients:
            await self.redis.enqueue_job('send_email', group_id, company_id, recipient, m_base)
        return PreResponse(text='201 job enqueued\n', status=201)
Ejemplo n.º 4
0
 def get_arg_int(self, name, default=None):
     v = self.request.query.get(name)
     if v is None:
         return default
     try:
         return int(v)
     except ValueError:
         raise JsonErrors.HTTPBadRequest(
             f"invalid get argument '{name}': {v!r}")
Ejemplo n.º 5
0
    async def authenticate(self, request):
        token = re.sub('^Basic *', '', request.headers.get(
            'Authorization', '')) or 'x'
        try:
            _, password = base64.b64decode(token).decode().split(':', 1)
        except (ValueError, UnicodeDecodeError):
            password = ''

        if not secrets.compare_digest(password,
                                      self.settings.admin_basic_auth_password):
            raise JsonErrors.HTTPUnauthorized(
                'Invalid basic auth', headers={'WWW-Authenticate': 'Basic'})
Ejemplo n.º 6
0
    async def authenticate(self, request):
        company = request.query.get('company', None)
        expires = request.query.get('expires', None)
        body = f'{company}:{expires}'.encode()
        expected_sig = hmac.new(self.settings.user_auth_key, body,
                                hashlib.sha256).hexdigest()
        signature = request.query.get('signature', '-')
        if not secrets.compare_digest(expected_sig, signature):
            raise JsonErrors.HTTPForbidden('Invalid token',
                                           headers=self.headers)

        try:
            self.session = Session(company=company, expires=expires)
        except ValidationError as e:
            raise JsonErrors.HTTPBadRequest(message='Invalid Data',
                                            details=e.errors(),
                                            headers=self.headers)
        if self.session.expires < datetime.utcnow().replace(
                tzinfo=timezone.utc):
            raise JsonErrors.HTTPForbidden('token expired',
                                           headers=self.headers)
Ejemplo n.º 7
0
 async def call(self, request):
     morpheus_api = self.app['morpheus_api']
     ctx = dict(
         methods=[m.value for m in SendMethod],
         method=self.request.match_info.get(
             'method',
             self.request.query.get('method',
                                    SendMethod.email_mandrill.value)),
     )
     try:
         ctx.update(await self.get_context(morpheus_api))
     except ApiError as e:
         raise JsonErrors.HTTPBadRequest(str(e))
     return ctx
Ejemplo n.º 8
0
    async def call(self, request):
        try:
            event_data = (await request.post())['mandrill_events']
        except KeyError:
            raise JsonErrors.HTTPBadRequest('"mandrill_events" not found in post data')

        sig_generated = base64.b64encode(
            hmac.new(
                self.app['webhook_auth_key'],
                msg=(self.app['mandrill_webhook_url'] + 'mandrill_events' + event_data).encode(),
                digestmod=hashlib.sha1,
            ).digest()
        )
        sig_given = request.headers.get('X-Mandrill-Signature', '<missing>').encode()
        if not hmac.compare_digest(sig_generated, sig_given):
            raise JsonErrors.HTTPForbidden('invalid signature')
        try:
            events = ujson.loads(event_data)
        except ValueError as e:
            raise JsonErrors.HTTPBadRequest(f'invalid json data: {e}')

        await self.redis.enqueue_job('update_mandrill_webhooks', events)
        return PreResponse(text='message status updated\n')
Ejemplo n.º 9
0
    async def request_data(self, model: Type[BaseModel]) -> AModel:
        error_details = None
        try:
            data = await self.request.json()
        except ValueError:
            error_msg = 'Error decoding JSON'
        else:
            try:
                return model.parse_obj(data)
            except ValidationError as e:
                error_msg = 'Invalid Data'
                error_details = e.errors()

        raise JsonErrors.HTTPBadRequest(message=error_msg,
                                        details=error_details)
Ejemplo n.º 10
0
    async def call(self, request):
        data = await self.query(message_id=int(self.request.match_info['id']))
        if data['count'] == 0:
            raise JsonErrors.HTTPNotFound('message not found')
        data = data['items'][0]

        preview_path = self.app.router['user-preview'].url_for(**self.request.match_info)
        return dict(
            base_template='user/base-{}.jinja'.format('raw' if self.request.query.get('raw') else 'page'),
            title='{method} - {external_id}'.format(**data),
            id=data['external_id'],
            method=data['method'],
            details=self._details(data),
            events=[e async for e in self._events(data['id'])],
            preview_url=self.full_url(f'{preview_path}?{self.request.query_string}'),
            attachments=list(self._attachments(data)),
        )
Ejemplo n.º 11
0
    async def call(self, request):
        method = self.request.match_info['method']
        where = (Var('m.method') == method) & (Var('m.id') == int(request.match_info['id']))

        if self.session.company != '__all__':
            where &= Var('c.code') == self.session.company

        async with self.app['pg'].acquire() as conn:
            data = await conn.fetchrow_b(
                """
                select from_name, to_last_name, to_address, status, body, extra
                from messages m
                join message_groups j on m.group_id = j.id
                join companies c on m.company_id = c.id
                where :where
                """,
                where=where,
            )

        if not data:
            raise JsonErrors.HTTPNotFound('message not found')

        data = dict(data)
        body = data['body']
        # Remove links from preview
        body = re.sub('(href=").*?"', r'\1#"', body, flags=re.S | re.I)

        extra = json.loads(data['extra']) if data.get('extra') else {}
        if method.startswith('sms'):
            # need to render the sms so it makes sense to users
            return {
                'from': data['from_name'],
                'to': data['to_last_name'] or data['to_address'],
                'status': data['status'],
                'message': body,
                'extra': extra,
            }
        else:
            return {'raw': body}
Ejemplo n.º 12
0
 async def authenticate(self, request):
     auth_token = getattr(self.settings, self.auth_token_field)
     if not secrets.compare_digest(
             auth_token, request.headers.get('Authorization', '')):
         raise JsonErrors.HTTPForbidden('Invalid Authorization header')
Ejemplo n.º 13
0
async def get_company_id(conn, company_code: str) -> int:
    company_id = await conn.fetchval('select id from companies where code=$1', company_code)
    if not company_id:
        raise JsonErrors.HTTPNotFound('company not found')
    return company_id