예제 #1
0
파일: auth.py 프로젝트: samuelcolvin/nosht
async def login(request):
    m = await parse_request(request, LoginModel, headers_=HEADER_CROSS_ORIGIN)

    repeat_cache_key = f'login-attempt:{get_ip(request)}'
    login_attempted = await request.app['redis'].get(repeat_cache_key)

    if login_attempted:
        await check_grecaptcha(m, request, error_headers=HEADER_CROSS_ORIGIN)

    if m.password != request.app['settings'].dummy_password:
        r = await request['conn'].fetchrow(LOGIN_USER_SQL,
                                           request['company_id'], m.email)
        if r:
            user = dict(r)
            password_hash = user.pop('password_hash')
        else:
            # still try hashing to avoid timing attack
            user = dict()
            password_hash = None

        password_hash = password_hash or request.app['dummy_password_hash']

        if bcrypt.checkpw(m.password.encode(), password_hash.encode()):
            return successful_login(user, request.app, HEADER_CROSS_ORIGIN)

    await request.app['redis'].setex(repeat_cache_key, 60, b'1')
    return json_response(status='invalid',
                         message='invalid email or password',
                         headers_=HEADER_CROSS_ORIGIN,
                         status_=470)
예제 #2
0
async def company_set_footer_link(request):
    m = await parse_request(request, FooterLinksModel)
    v = json.dumps([l.dict() for l in m.links], separators=(',', ':'))
    await request['conn'].execute(
        'UPDATE companies SET footer_links=$1 WHERE id=$2', v,
        request['company_id'])
    return json_response(status='success')
예제 #3
0
async def email_def_retrieve(request):
    trigger = get_trigger(request)
    email_def = await request['conn'].fetchrow(
        'SELECT active, subject, title, body FROM email_definitions WHERE trigger=$1 and company=$2',
        trigger,
        request['company_id'],
    )
    if email_def:
        data = {
            'trigger': trigger,
            'customised': True,
            'active': email_def['active'],
            'subject': email_def['subject'],
            'title': email_def['title'],
            'body': email_def['body'],
        }
    else:
        defaults = EMAIL_DEFAULTS[Triggers(trigger)]
        data = {
            'trigger': trigger,
            'customised': False,
            'active': True,
            'subject': defaults['subject'],
            'title': defaults['title'],
            'body': defaults['body'].strip('\n '),
        }
    return json_response(**data)
예제 #4
0
파일: donate.py 프로젝트: bakasa/nosht
async def donation_image_upload(request):
    co_id = request['company_id']
    don_opt_id = int(request.match_info['pk'])
    r = await request['conn'].fetchrow(
        """
        SELECT co.slug, cat.slug, d.image
        FROM donation_options AS d
        JOIN categories AS cat ON d.category = cat.id
        JOIN companies AS co ON cat.company = co.id
        WHERE d.id = $1 AND cat.company = $2
        """,
        don_opt_id,
        co_id,
    )
    if not r:
        raise JsonErrors.HTTPNotFound(message='donation option not found')

    co_slug, cat_slug, old_image = r
    content = await request_image(request, expected_size=IMAGE_SIZE)

    upload_path = Path(co_slug) / cat_slug / str(don_opt_id)
    image_url = await upload_other(
        content, upload_path=upload_path, settings=request.app['settings'], req_size=IMAGE_SIZE, thumb=True,
    )

    await request['conn'].execute('UPDATE donation_options SET image=$1 WHERE id=$2', image_url, don_opt_id)

    if old_image:
        await delete_image(old_image, request.app['settings'])
    return json_response(status='success')
예제 #5
0
async def booking_info(request):
    event_id = await check_event_sig(request)

    conn: BuildPgConnection = request['conn']
    settings = request.app['settings']
    tickets_remaining = await conn.fetchval(
        'SELECT check_tickets_remaining($1, $2)', event_id,
        settings.ticket_ttl)
    existing_tickets = await conn.fetchval(
        """
        SELECT COUNT(*)
        FROM tickets
        JOIN actions AS a ON tickets.reserve_action = a.id
        WHERE tickets.event=$1 AND a.user_id=$2 AND status='booked'
        """,
        event_id,
        request['session']['user_id'],
    )
    ticket_types = await conn.fetch(
        "SELECT id, name, price::float FROM ticket_types WHERE event=$1 AND mode='ticket' AND active=TRUE ORDER BY id",
        event_id,
    )
    return json_response(
        tickets_remaining=tickets_remaining if
        (tickets_remaining and tickets_remaining < 10) else None,
        existing_tickets=existing_tickets or 0,
        ticket_types=[dict(tt) for tt in ticket_types],
    )
예제 #6
0
async def event_tickets(request):
    event_id = await _check_event_permissions(request)
    not_admin = request['session']['role'] != 'admin'
    tickets = []
    settings = request.app['settings']
    conn: BuildPgConnection = request['conn']
    for t in await conn.fetch(event_tickets_sql, event_id):
        ticket = {
            'ticket_id': ticket_id_signed(t['id'], settings),
            **t,
        }
        if not_admin:
            ticket.pop('guest_email')
            ticket.pop('buyer_email')
        tickets.append(ticket)

    waiting_list = [
        dict(r) for r in await conn.fetch(event_waiting_list_sql, event_id)
    ]
    if not_admin:
        [r.pop('email') for r in waiting_list]

    donations = [
        dict(r) for r in await conn.fetch(event_donations_sql, event_id)
    ]
    if not_admin:
        [r.pop('user_email') for r in donations]
    return json_response(tickets=tickets,
                         waiting_list=waiting_list,
                         donations=donations)
예제 #7
0
async def company_upload(request):
    field_name = request.match_info['field']
    assert field_name in {'image', 'logo'}, field_name  # double check
    content = await request_image(
        request, expected_size=None if field_name == 'image' else LOGO_SIZE)

    co_id = request['company_id']
    co_slug, old_image = await request['conn'].fetchrow_b(
        'SELECT slug, :image_field FROM companies WHERE id=:id',
        image_field=V(field_name),
        id=co_id)

    upload_path = Path(co_slug) / 'co' / field_name
    method = upload_background if field_name == 'image' else upload_logo
    image_url = await method(content,
                             upload_path=upload_path,
                             settings=request.app['settings'])

    await request['conn'].execute_b('UPDATE companies SET :set WHERE id=:id',
                                    set=V(field_name) == image_url,
                                    id=co_id)

    if old_image:
        await delete_image(old_image, request.app['settings'])
    return json_response(status='success')
예제 #8
0
async def set_event_description_image(request):
    event_id = await _check_event_permissions(request, check_upcoming=True)
    content = await request_image(request,
                                  expected_size=description_image_size)

    image = await request['conn'].fetchval(
        'SELECT description_image from events WHERE id=$1', event_id)
    if image:
        await delete_image(image, request.app['settings'])

    co_slug, cat_slug, event_slug = await request['conn'].fetchrow(
        slugs_sql, request['company_id'], event_id)
    upload_path = Path(co_slug) / cat_slug / event_slug / 'description'

    image_url = await upload_other(
        content,
        upload_path=upload_path,
        settings=request.app['settings'],
        req_size=description_image_size,
        thumb=True,
    )
    async with request['conn'].transaction():
        await request['conn'].execute(
            'UPDATE events SET description_image=$1 WHERE id=$2', image_url,
            event_id)
        await record_action(
            request,
            request['session']['user_id'],
            ActionTypes.edit_event,
            event_id=event_id,
            subtype='set-image-description',
        )
    return json_response(status='success')
예제 #9
0
async def authenticate_token(request):
    m = await parse_request(request, AuthTokenModel)
    auth_session = decrypt_json(request.app, m.token, ttl=10)
    session = await new_session(request)
    session.update(auth_session)

    await record_action(request, session['user_id'], ActionTypes.login)
    return json_response(status='success')
예제 #10
0
async def category_delete_image(request):
    m = await parse_request(request, ImageActionModel)

    # _get_cat_img_path is required to check the category is on the right company
    await _get_cat_img_path(request)

    await delete_image(m.image, request.app['settings'])
    return json_response(status='success')
예제 #11
0
파일: categories.py 프로젝트: bakasa/nosht
async def category_set_image(request):
    m = await parse_request(request, ImageModel)
    await _check_image_exists(request, m)

    cat_id = int(request.match_info['cat_id'])
    await request['conn'].execute(
        'UPDATE categories SET image = $1 WHERE id = $2', m.image, cat_id)
    return json_response(status='success')
예제 #12
0
파일: main.py 프로젝트: samuelcolvin/nosht
 async def add(self) -> web.Response:
     m = await parse_request(self.request, self.model)
     data = await self.prepare_add_data(m.dict())
     try:
         pk = await self.add_execute(**data)
     except UniqueViolationError as e:
         raise self.conflict_exc(e)
     else:
         return json_response(status='ok', pk=pk, status_=201)
예제 #13
0
파일: stripe.py 프로젝트: bakasa/nosht
async def get_payment_method_details(request):
    payment_method_id = request.match_info['payment_method']
    data = await get_stripe_payment_method(
        payment_method_id=payment_method_id,
        company_id=request['company_id'],
        user_id=request['session']['user_id'],
        app=request.app,
        conn=request['conn'])
    return json_response(**data)
예제 #14
0
async def category_default_image(request):
    m = await parse_request(request, ImageActionModel)

    path = await _get_cat_img_path(request)
    images = await list_images(path, request.app['settings'])
    if m.image not in images:
        raise JsonErrors.HTTPBadRequest(message='image does not exist')
    cat_id = int(request.match_info['cat_id'])
    await request['conn'].execute('UPDATE categories SET image = $1 WHERE id = $2', m.image, cat_id)
    return json_response(status='success')
예제 #15
0
async def switch_highlight(request):
    event_id = await _check_event_permissions(request)
    await request['conn'].execute(
        'UPDATE events SET highlight=NOT highlight WHERE id=$1', event_id)
    await record_action(request,
                        request['session']['user_id'],
                        ActionTypes.edit_event,
                        event_id=event_id,
                        subtype='switch-highlight')
    return json_response(status='ok')
예제 #16
0
async def email_def_browse(request):
    results = await request['conn'].fetch(
        'SELECT trigger, active FROM email_definitions WHERE company=$1',
        request['company_id'])
    lookup = {r['trigger']: r for r in results}
    return json_response(
        items=[{
            'trigger': t,
            'customised': t in lookup,
            'active': lookup[t]['active'] if t in lookup else True,
        } for t in Triggers])
예제 #17
0
def successful_login(user, app, headers_=None):
    auth_session = {
        'user_id': user['id'],
        'user_role': user['role'],
        'last_active': int(time())
    }
    auth_token = encrypt_json(app, auth_session)
    return json_response(status='success',
                         auth_token=auth_token,
                         user=user,
                         headers_=headers_)
예제 #18
0
async def clear_email_def(request):
    trigger = get_trigger(request)
    r = await request['conn'].execute(
        'DELETE FROM email_definitions WHERE trigger=$1 AND company=$2',
        trigger,
        request['company_id'],
    )
    if r == 'DELETE 1':
        return json_response(status='ok')
    else:
        raise JsonErrors.HTTPNotFound(
            message=f'email definition with trigger "{trigger}" not found')
예제 #19
0
async def set_event_image_existing(request):
    m = await parse_request(request, ImageModel)
    if not m.image.startswith(request.app['settings'].s3_domain):
        raise JsonErrors.HTTPBadRequest(message='image not allowed')

    await _delete_existing_image(request)

    event_id = int(request.match_info['id'])
    await request['conn'].execute('UPDATE events SET image=$1 WHERE id=$2', m.image, event_id)
    await record_action(request, request['session']['user_id'], ActionTypes.edit_event,
                        event_id=event_id, subtype='set-image-existing')
    return json_response(status='success')
예제 #20
0
async def remove_event_secondary_image(request):
    event_id = await _check_event_permissions(request, check_upcoming=True)

    image = await request['conn'].fetchval('select secondary_image from events where id=$1', event_id)
    if image:
        await delete_image(image, request.app['settings'])

    async with request['conn'].transaction():
        await request['conn'].execute('update events set secondary_image=null where id=$1', event_id)
        await record_action(request, request['session']['user_id'], ActionTypes.edit_event,
                            event_id=event_id, subtype='remove-image-secondary')
    return json_response(status='success')
예제 #21
0
파일: users.py 프로젝트: bakasa/nosht
async def switch_user_status(request):
    user_id = int(request.match_info['pk'])
    status = await request['conn'].fetchval(
        'SELECT status FROM users WHERE id=$1 AND company=$2',
        user_id,
        request['company_id'],
    )
    if not status:
        raise JsonErrors.HTTPNotFound(message='user not found')
    new_status = 'suspended' if status == 'active' else 'active'
    await request['conn'].execute('UPDATE users SET status=$1 WHERE id=$2', new_status, user_id)
    return json_response(new_status=new_status)
예제 #22
0
파일: categories.py 프로젝트: bakasa/nosht
async def category_delete_image(request):
    m = await parse_request(request, ImageModel)
    await _check_image_exists(request, m)
    cat_id = int(request.match_info['cat_id'])

    dft_image = await request['conn'].fetchval(
        'SELECT image FROM categories WHERE id=$1', cat_id)
    if dft_image == m.image:
        raise JsonErrors.HTTPBadRequest(
            message='default image may not be be deleted')

    await delete_image(m.image, request.app['settings'])
    return json_response(status='success')
예제 #23
0
파일: main.py 프로젝트: samuelcolvin/nosht
    async def edit(self, pk) -> web.Response:
        await self.check_item_permissions(pk)
        m = await parse_request_ignore_missing(self.request, self.model)

        data = await self.prepare_edit_data(pk, m.dict(exclude_unset=True))
        if not data:
            raise JsonErrors.HTTPBadRequest(message=f'no data to save')

        try:
            await self.edit_execute(pk, **data)
        except UniqueViolationError as e:
            raise self.conflict_exc(e)
        else:
            return json_response(status='ok')
예제 #24
0
async def login_with(request):
    model, siw_method = LOGIN_MODELS[request.match_info['site']]
    m = await parse_request(request, model)
    details = await siw_method(m, app=request.app)
    email = details['email']
    r = await request['conn'].fetchrow(LOGIN_USER_SQL, request['company_id'],
                                       email)
    if r:
        user = dict(r)
        return successful_login(user, request.app)
    return json_response(
        status='invalid',
        message=f'User with email address "{email}" not found',
        status_=470)
예제 #25
0
async def event_tickets(request):
    event_id = await _check_event_permissions(request)
    not_admin = request['session']['role'] != 'admin'
    tickets = []
    settings = request.app['settings']
    for t in await request['conn'].fetch(event_tickets_sql, event_id):
        ticket = {
            'ticket_id': ticket_id_signed(t['id'], settings),
            **t,
        }
        if not_admin:
            ticket.pop('guest_email')
            ticket.pop('buyer_email')
        tickets.append(ticket)
    return json_response(tickets=tickets)
예제 #26
0
async def set_event_image_new(request):
    content = await request_image(request)

    await _delete_existing_image(request)

    event_id = int(request.match_info['id'])
    co_slug, cat_slug, event_slug = await request['conn'].fetchrow(slugs_sql, request['company_id'], event_id)

    upload_path = Path(co_slug) / cat_slug / event_slug

    image_url = await upload_background(content, upload_path, request.app['settings'])
    await request['conn'].execute('UPDATE events SET image=$1 WHERE id=$2', image_url, event_id)
    await record_action(request, request['session']['user_id'], ActionTypes.edit_event,
                        event_id=event_id, subtype='set-image-new')
    return json_response(status='success')
예제 #27
0
async def category_add_image(request):
    try:
        p = await request.post()
    except ValueError:
        raise HTTPRequestEntityTooLarge
    image = p['image']
    content = image.file.read()
    try:
        check_size_save(content)
    except ValueError as e:
        raise JsonErrors.HTTPBadRequest(message=str(e))

    upload_path = await _get_cat_img_path(request)
    await resize_upload(content, upload_path, request.app['settings'])

    return json_response(status='success')
예제 #28
0
async def email_def_edit(request):
    m = await parse_request(request, EmailDefModel)
    trigger = get_trigger(request)
    await request['conn'].execute_b("""
        INSERT INTO email_definitions AS ed (:values__names) VALUES :values
        ON CONFLICT (company, trigger) DO UPDATE SET
          subject=EXCLUDED.subject,
          title=EXCLUDED.title,
          body=EXCLUDED.body,
          active=EXCLUDED.active
        """,
                                    values=Values(
                                        trigger=trigger,
                                        company=request['company_id'],
                                        **m.dict()))
    return json_response(status='ok')
예제 #29
0
async def donation_after_prepare(request):
    donation_option_id = int(request.match_info['don_opt_id'])
    event_id = int(request.match_info['event_id'])
    conn = request['conn']
    r = await conn.fetchrow(
        """
        SELECT opt.name, opt.amount, cat.id
        FROM donation_options AS opt
        JOIN categories AS cat ON opt.category = cat.id
        WHERE opt.id = $1 AND opt.live AND cat.company = $2
        """,
        donation_option_id,
        request['company_id'],
    )
    if not r:
        raise JsonErrors.HTTPBadRequest(message='donation option not found')

    name, amount, cat_id = r
    event = await conn.fetchval(
        'SELECT 1 FROM events WHERE id=$1 AND category=$2', event_id, cat_id)
    if not event:
        raise JsonErrors.HTTPBadRequest(
            message='event not found on the same category as donation_option')

    user_id = request['session']['user_id']
    action_id = await record_action_id(request,
                                       user_id,
                                       ActionTypes.donate_prepare,
                                       event_id=event_id,
                                       donation_option_id=donation_option_id)

    client_secret = await stripe_payment_intent(
        user_id=user_id,
        price_cents=int(amount * 100),
        description=f'donation to {name} ({donation_option_id}) after booking',
        metadata={
            'purpose': 'donate',
            'event_id': event_id,
            'reserve_action_id': action_id,
            'user_id': user_id
        },
        company_id=request['company_id'],
        idempotency_key=f'idempotency-donate-{action_id}',
        app=request.app,
        conn=conn,
    )
    return json_response(client_secret=client_secret, action_id=action_id)
예제 #30
0
async def waiting_list_add(request):
    event_id = int(request.match_info['id'])

    conn: BuildPgConnection = request['conn']
    user_id = request['session']['user_id']
    wl_id = await conn.fetchval(
        """
        insert into waiting_list (event, user_id) values ($1, $2)
        on conflict (event, user_id) do nothing
        returning id
        """,
        event_id,
        user_id,
    )
    if wl_id:
        await request.app['email_actor'].waiting_list_add(wl_id)
    return json_response(status='ok')