示例#1
0
def login_user(request,
               service='google',
               save_redirect=None,
               token=None,
               is_force=None):
    if service and len(service) > 32:
        # Probably some injection spam -_-
        raise httpexceptions.HTTPBadRequest(detail="invalid service")

    user_id = get_user_id(request)

    if user_id and not is_force:
        return user_id

    if token and request.features.get('token_login'):
        # Only enabled during testing
        u = get(token=token)
        if u:
            login_user_id(request, u.id)
            return u.id

    request.session['next'] = save_redirect
    request.session.save()

    oauth = service_registry[service or 'google'](request)
    next, state = oauth.auth_url()  # is_force=is_force
    request.session['oauth_state'] = state
    raise httpexceptions.HTTPSeeOther(next)
示例#2
0
def handle_stripe(request, data):
    if not data['livemode']:
        return

    if data['type'] != "invoice.payment_succeeded":
        return

    if not data['data']['object']['total']:
        # Shortcut for empty totals (free accounts)
        return

    remote_id = data['user_id']
    accounts = model.Session.query(model.Account).filter_by(remote_id=remote_id, service='stripe')
    if not accounts:
        raise httpexceptions.HTTPNotFound('Account handler not found.')

    has_funnels = any(a.config.get('ga_funnels') for a in accounts)
    if not has_funnels:
        return

    # Validate webhook
    stripe_query = api.account.query_service(request, accounts[0])
    event_data = stripe_query.validate_webhook(data)
    if not event_data:
        raise httpexceptions.HTTPBadRequest('Webhook failed to validate: %s' % data['id'])

    count = 0
    for a in accounts:
        for ga_tracking_id in a.config.get('ga_funnels', []):
            count += 1
            tasks.service.stripe_webhook.delay(ga_tracking_id, a.id, event_data)

    log.info('stripe webhook: Queued %d funnels for account: %s' % (count, a.id))
示例#3
0
def index(request):
    """ The only app-routed view which delegates the rest of the API-related
    functionality. Collates the API result into the final payload and response
    object.

    This is bypassed if the API method is processed through a @handle_api view.
    """
    data = {
        'status': 'ok',
        'code': 200,
        'messages': [],
        'result': {},
    }

    format = request.params.get('format', 'json')
    if format not in ('json', 'redirect'):
        return httpexceptions.HTTPBadRequest('Invalid format requested: %s' %
                                             format)

    encode_settings = {'cls': SchemaEncoder}
    if request.params.get('pretty'):
        encode_settings['sort_keys'] = True
        encode_settings['indent'] = 4

    next = request.params.get('next') or request.referer

    try:
        r = api_controller(request)
        if r is not None:
            data['result'] = r

    except APIControllerError as e:
        data['messages'] += [e.message]
        data['code'] = e.code
        data['status'] = 'error'

    except (APIControllerError, LoginRequired) as e:
        data['messages'] += [e.message]
        data['code'] = e.code
        data['status'] = 'error'

        if isinstance(e, LoginRequired):
            next = e.next_url(request, next=next)

    data['messages'] += request.pop_flash()

    if format == 'redirect':
        for message in data['messages']:
            # Copy request-level messages to session storage to be displayed on
            # redirect.
            request.session.flash(message)
        return httpexceptions.HTTPSeeOther(next or '/')

    body = json.dumps(data, **encode_settings).encode()
    return Response(body, content_type='application/json', status=data['code'])
示例#4
0
def _namecheap_subscription_alter(request, data):
    nc_api = service_registry['namecheap'].instance

    event_id = data['event']['id']
    return_uri = data['event'].get('returnURI')
    remote_id = data['event']['user']['username']
    order = data['event']['order']

    q = model.Session.query(model.Account).filter_by(service='namecheap', remote_id=remote_id)
    q = q.options(orm.joinedload('user'))

    account = q.first()
    if not account:
        raise httpexceptions.HTTPBadRequest('Invalid remote id.')

    user = account.user
    old_plan = user.plan

    api.account.set_plan(user, order['pricing_plan_sku'], update_subscription=False)
    amount = 0
    if not user.num_remaining:
        amount = user.payment.prorate(old_plan=old_plan, new_plan=user.plan)
    if amount:
        try:
            user.payment.invoice(amount=amount, description='Briefmetrics: %s (Prorated)' % user.plan.option_str)
        except:
            log.error('Namecheap prorate invoice failed, reverting plan to %s: %s' % (old_plan.id, user))
            api.account.set_plan(user, old_plan.id)
            raise

    log.info('namecheap webhook: Altered %s' % user)

    ack = {
        'type': 'subscription_alter_resp',
        'id': event_id,
        'response': {
            'state': 'Active',
            'message': 'Changed plan to %s' % user.plan.option_str,
        }
    }

    if return_uri:
        # Confirm event, activate subscription
        r = nc_api.session.request('PUT', return_uri, json=ack) # Bypass our wrapper
        assert_response(r)

    return ack
示例#5
0
def handle_namecheap(request, data):
    if isinstance(data, list):
        # Stupid hack for Namecheap's broken QA code.
        data = data[0]

    event_token = data.get('event_token')
    if not event_token:
        return

    nc_api = service_registry['namecheap'].instance

    # Get event details
    try:
        r = nc_api.request('GET', '/v1/saas/saas/event/{token}'.format(token=event_token))
    except Exception as e:
        log.error('namecheap webhook: Failed token lookup: %s' % event_token)
        raise

    data = r.json()
    fn = {
        'subscription_create': _namecheap_subscription_create,
        'subscription_alter': _namecheap_subscription_alter,
        'subscription_cancel': _namecheap_subscription_cancel,
    }.get(data['type'])

    if not fn:
        raise httpexceptions.HTTPBadRequest('Invalid event type: %s' % data['type'])

    log.debug("namecheap webhook: Processing %s: %s" % (data['type'], data['event']))

    try:
        return fn(request, data)
    except Exception as e:
        if event_token in fail_cache:
            log.error('namecheap webhook: Failed %s (%s): %s (again, silenced)' % (data['type'], e, event_token))
            return
        log.error('namecheap webhook: Failed %s (%s): %s' % (data['type'], e, event_token))
        fail_cache.add(event_token)
        raise
示例#6
0
def _namecheap_subscription_cancel(request, data):
    nc_api = service_registry['namecheap'].instance

    event_id = data['event']['id']
    return_uri = data['event'].get('returnURI')
    remote_id = data['event']['user']['username']

    q = model.Session.query(model.Account).filter_by(service='namecheap', remote_id=remote_id)
    q = q.options(orm.joinedload('user'))

    account = q.first()
    if not account:
        raise httpexceptions.HTTPBadRequest('Invalid remote id.')

    user = account.user

    amount = 0
    if not user.num_remaining:
        amount = user.payment and user.payment.prorate()
    if amount:
        user.payment.invoice(amount=amount, description='Briefmetrics: Prorated refund')

    api.account.delete_payments(user)
    log.info('namecheap webhook: Cancelled %s' % user)

    ack = {
        'type': 'subscription_cancel_resp',
        'id': event_id,
        'response': {
            'state': 'Inactive',
        }
    }

    if return_uri:
        # Confirm event, activate subscription
        r = nc_api.session.request('PUT', return_uri, json=ack) # Bypass our wrapper
        assert_response(r)

    return ack
示例#7
0
def connect_oauth(request, oauth, user_required=False):
    user, account = get_account(request,
                                service=oauth.id,
                                user_required=user_required)

    code = request.params.get('code')
    if not code:
        raise httpexceptions.HTTPBadRequest('Missing code.')

    error = request.params.get('error')
    if error:
        raise APIError('Failed to connect: %s' % error)

    url = request.current_route_url().replace(
        'http://', 'https://')  # We lie, because honeybadger.

    try:
        token = oauth.auth_token(url)
    except InvalidGrantError:
        # Try again.
        raise httpexceptions.HTTPSeeOther(
            request.route_path('account_login', service=oauth.id))
    except OAuth2Error as e:
        raise APIError(
            "Unexpected authentication error, please try again: %s" %
            e.description)
    except ValueError as e:
        raise APIError("Unexpected response error, please try again: %s" % e)

    remote_id, email, display_name, remote_data = oauth.query_user()
    if not user:
        # New user
        user = get_or_create(
            email=email,
            service=oauth.id,
            token=token,
            display_name=display_name,
            remote_id=remote_id,
            remote_data=remote_data,
        )
        account = user.accounts[0]
    elif not account:
        # New account
        account = model.Account.create(
            display_name=display_name or user.display_name,
            user=user,
            oauth_token=token,
            service=oauth.id,
            remote_id=remote_id,
            remote_data=remote_data,
        )
    else:
        # Update account
        account.oauth_token = token
        account.remote_id = remote_id
        account.remote_data = remote_data
        force_skip = oauth.is_autocreate and Session.query(
            model.Report).filter_by(account_id=account.id).limit(1).count()
        if force_skip:
            # Already exists, skip autocreate.
            oauth.is_autocreate = False

    Session.commit()

    return account