Example #1
0
    def post(self, request):
        phone_number = str(self.request.data.get('phone_number', '')).strip()
        users = lookup_users_by_phone(phone_number)
        users += self.lookup_local_users_by_phone(phone_number)
        if not len(users):
            return Response({'error': 'not found'}, status=403)

        SMSAuthCode.objects.filter(phone_number=phone_number).delete()

        auth_code = SMSAuthCode.generate_code()
        salt = crypto.get_random_string()
        iterations = randint(10000, 10100)
        encrypted_code = crypto.pbkdf2(auth_code, salt, iterations)

        code = SMSAuthCode.objects.create(
            phone_number=phone_number,
            code=encrypted_code,
            salt=salt,
            iterations=iterations
        )
        for user in users:
            code.users.add(user)

        try:
            send_sms(phone_number, 'Din innloggingskode er: %s' % auth_code)
        except SMSGatewayDeliveryError:
            return Response({'error': 'unable to send sms'}, 503)
        except Exception:
            return Response({'error': 'server error'}, 503)

        return Response({'status': 'ok'})
Example #2
0
def memberid_sms(request):
    """This is a membership service that lets you get your memberid by providing your phone number.
    Note that a lot of phone number entries in Focus are bogus (email, date of birth, or
    poorly formatted) and some are also foreign, which we allow for now.
    We are currently relying on the SMS service to fail if a bogus number
    happens to fall through."""

    # Robots etc, just redirect them
    if not 'phone_mobile' in request.POST:
        return redirect('membership.views.service')

    # Start recording this request - details will be filled underway
    sms_request = SMSServiceRequest()
    sms_request.phone_number_input = request.POST['phone_mobile']
    sms_request.ip = request.META['REMOTE_ADDR']
    if request.user.is_authenticated():
        sms_request.user = request.user

    sms_request.count = memberid_sms_count(request.META['REMOTE_ADDR'])
    if sms_request.count > 10 and request.META['REMOTE_ADDR'] not in settings.SMS_RESTRICTION_WHITELIST:
        sms_request.blocked = True
        sms_request.save()
        return HttpResponse(json.dumps({'status': 'too_high_frequency'}))

    users = lookup_users_by_phone(request.POST['phone_mobile'])
    if len(users) == 0:
        sms_request.save()
        return HttpResponse(json.dumps({'status': 'no_match'}))
    elif len(users) == 1:
        user = users[0]
    elif len(users) > 1:
        # Usually, this will be because household members have the same number as their parents.
        # Check if any of these are related, and in that case, use the parent.
        user = None
        for user_to_check in users:
            if user_to_check.is_household_member() and \
                    user_to_check.get_parent() is not None and \
                    user_to_check.get_parent() in users:
                # Ah, this parent is in the result set - probably the one we want, use it
                user = user_to_check.get_parent()
                break
        if user is None:
            # Multiple hits, and they are not related. What do? Pick a random hit for now.
            user = users[0]
    else:
        raise Exception("A negative number of actors resulted from raw query. This is very strange, please investigate immediately.")

    sms_request.memberid = user.memberid
    sms_request.save()

    # Delete the actor cache in case the number was recently updated; the cache may differ from our raw lookup above
    user.get_actor().clear_cache()
    return HttpResponse(json.dumps(send_sms_receipt(request, user)))
Example #3
0
def memberid_sms(request):
    """This membership service tries to send a membership receipt by SMS to the given phone number.
    Note that a lot of phone number entries in Focus are bogus (email, date of birth, or poorly
    formatted) and some are also foreign, which is allowed for now. We are currently relying on the
    SMS service to fail if a bogus number happens to fall through."""
    if 'phone_mobile' not in request.POST:
        raise PermissionDenied

    request_count = memberid_sms_count(request.META['REMOTE_ADDR'])
    if request_count > 10 and request.META['REMOTE_ADDR'] not in settings.SMS_RESTRICTION_WHITELIST:
        return HttpResponse(json.dumps({'status': 'too_high_frequency'}))

    users = lookup_users_by_phone(request.POST['phone_mobile'])
    if len(users) == 0:
        return HttpResponse(json.dumps({'status': 'no_match'}))
    elif len(users) == 1:
        user = users[0]
        # Delete the user cache in case the number was recently updated; the cache may differ from
        # our raw lookup above
        user.clear_cache()
        return HttpResponse(json.dumps(send_sms_receipt(request, user)))
    else:
        # Multiple matches. This is typically a number related to members of a single family. This
        # is fine, since the receipt will include all family members, regardless of who it's sent
        # to. However, if that's not the case, we need to send one SMS for each matched but
        # unrelated user.
        recipients = []
        for user in users:
            # If this user is not in the family of any other recipients, add them to the recipient
            # list
            if not any([r.has_family() and user in r.family.all_members() for r in recipients]):
                recipients.append(user)

        statuses = []
        for user in recipients:
            # Delete the user cache in case the number was recently updated; the cache may differ
            # from our raw lookup above
            user.clear_cache()
            statuses.append(send_sms_receipt(request, user)['status'])

        if all([s == 'ok' for s in statuses]):
            return HttpResponse(json.dumps({'status': 'ok'}))
        else:
            return HttpResponse(json.dumps({'status': 'service_fail'}))
def memberid(request, version, format):
    librato.increment('sherpa.api.tailored.medlemsnummer.request')

    if request.method != 'GET':
        librato.increment('sherpa.api.tailored.medlemsnummer.response.400')
        raise BadRequest(
            "Unsupported HTTP verb '%s'" % request.method,
            code=error_codes.UNSUPPORTED_HTTP_VERB,
            http_code=400
        )

    require_focus(request)

    if 'mobilnummer' not in request.GET:
        librato.increment('sherpa.api.tailored.medlemsnummer.response.400')
        raise BadRequest(
            "Missing required 'mobilnummer' parameter",
            code=error_codes.MISSING_REQUIRED_PARAMETER,
            http_code=400
        )

    phone_number = request.GET['mobilnummer'].strip()
    users = lookup_users_by_phone(phone_number)

    # Send the recipient an SMS
    if len(users) == 0:
        librato.increment('sherpa.api.tailored.medlemsnummer.response.404')
        raise BadRequest(
            "A member with phone number '%s' wasn't found." % phone_number,
            code=error_codes.RESOURCE_NOT_FOUND,
            http_code=404
        )
    elif len(users) == 1:
        user = users[0]
    elif len(users) > 1:
        # Usually, this will be because household members have the same number as their parents.
        # Check if any of these are related, and in that case, use the parent.
        user = None
        for user_to_check in users:
            if user_to_check.is_related_member() and \
                    user_to_check.get_parent() is not None and \
                    user_to_check.get_parent() in users:
                # Ah, this parent is in the result set - probably the one we want, use it
                user = user_to_check.get_parent()
                break
        if user is None:
            # Multiple hits, and they are not related. What do? Pick a random hit for now.
            user = users[0]

    # Delete the user cache in case the number was recently updated; the cache may differ from our raw lookup above
    user.clear_cache()
    result = send_sms_receipt(request, user)
    if result['status'] == 'ok':
        librato.increment('sherpa.api.tailored.medlemsnummer.response.200')
        return HttpResponse(json.dumps({
            'status': 'ok',
            'message': 'An SMS was successfully sent to the member with the given phone number.',
        }))
    elif result['status'] == 'service_fail':
        librato.increment('sherpa.api.tailored.medlemsnummer.response.500')
        raise BadRequest(
            "There is a problem with our SMS gateway and we were unable to send the SMS.",
            code=error_codes.SMS_GATEWAY_ERROR,
            http_code=500
        )
    else:
        # Might happen if we add a new status code to the send_sms_receipt function and forget to account for it here
        logger.error(
            "Unknown SMS return status code '%s'" % result['status'],
            extra={'request': request}
        )
        librato.increment('sherpa.api.tailored.medlemsnummer.response.500')
        raise BadRequest(
            "An internal error occurred while trying to send the SMS. This error has been logged and we'll get it fixed asap.",
            code=error_codes.INTERNAL_ERROR,
            http_code=500
        )
Example #5
0
def signup_add_participant(request, aktivitet_id, aktivitet_date_id):
    try:
        aktivitet_date = AktivitetDate.objects.select_related(
            *default_select_related,
        ).prefetch_related(
            *default_prefetch_related,
        ).get(id=aktivitet_date_id)

        for validation_func in [require_accepts_signups, require_stripe_account]:
            response = validation_func(request, aktivitet_date)
            if response is not None:
                return response
    except AktivitetDate.DoesNotExist:
        raise Http404

    signup_session = SignupSession(request, aktivitet_date)
    context = {
        'aktivitet_date': aktivitet_date,
        'signup_session': signup_session,
    }

    if request.method == 'GET':
        return render(request, 'common/aktiviteter/show/signup/add_participant.html', context)

    elif request.method == 'POST':
        if request.POST['submit'] == 'lookup_member':
            context['memberid'] = request.POST['memberid']
            context['lookup_memberid'] = True
            try:
                user = User.get_or_create_inactive(memberid=request.POST['memberid'])
                context['member'] = user

                # Is this user already added in the session, or a participant in
                # a separate group?
                session_participants = [p.user for p in signup_session.participants if p.user is not None]
                already_in_session = user in session_participants
                already_participant = signup_session.aktivitet_date.has_participant(user)
                if already_in_session or already_participant:
                    context['already_participating'] = True
                    return render(request, 'common/aktiviteter/show/signup/add_participant.html', context)

                if (aktivitet_date.only_members
                        and not user.payment.status['is_paid']):
                    context['member_has_not_paid'] = True
                    return render(
                        request,
                        'common/aktiviteter/show/signup/add_participant.html',
                        context
                    )
            except:
                messages.error(request, 'invalid_memberid')
            return render(request, 'common/aktiviteter/show/signup/add_participant.html', context)
        elif request.POST['submit'] == 'add_member_participant':
            user = User.objects.get(memberid=request.POST['memberid'])

            # Already a participant in a separate group? This was already
            # checked on lookup, but ensure they wasn't signed up somewhere
            # else in the meantime as well.
            if signup_session.aktivitet_date.has_participant(user):
                context['memberid'] = user.memberid
                context['lookup_memberid'] = True
                context['already_participating'] = True
                return render(request, 'common/aktiviteter/show/signup/add_participant.html', context)

            # Check if they're already added in the session; if so, ignore the
            # duplication and just return to participants list
            session_participants = [p.user for p in signup_session.participants if p.user is not None]
            if user not in session_participants:
                signup_session.participants.append(SignupSessionParticipant(
                    signup_session=signup_session,
                    id=None,
                    user=user,
                    first_name=user.get_first_name(),
                    last_name=user.get_last_name(),
                    birth_date=user.get_birth_date(),
                    email=user.get_email(),
                    phone=user.get_phone_mobile(),
                    state='signed_up',
                    use_parent_contact_info=False,
                    use_signee_contact_info=False,
                    discount_code='',
                    signee_relation='other',
                    answers=[],
                    extras=[],
                ))
                signup_session.save()
            return redirect('aktiviteter:signup_participants',
                            aktivitet_date.aktivitet.id, aktivitet_date.id)
        elif request.POST['submit'] == 'add_nonuser_participant':
            use_signee_contact_info = request.POST.get('use_signee_contact_info') == 'on'
            if use_signee_contact_info:
                # If using the signees contact info, don't validate contact info
                # input
                context['use_signee_contact_info'] = True
                form = ParticipantWithoutContactInfoForm(request.POST)
            else:
                form = ParticipantForm(request.POST)

            if aktivitet_date.aktivitet.participant_birth_date_required:
                form.fields['birth_date'].required = True

            context['add_nonuser'] = True
            context['form'] = form
            if not form.is_valid():
                return render(request, 'common/aktiviteter/show/signup/add_participant.html', context)
            else:
                first_name, last_name = form.cleaned_data['name'].rsplit(maxsplit=1)
                if use_signee_contact_info:
                    email = ''
                    phone = ''
                else:
                    email = form.cleaned_data['email']
                    phone = form.cleaned_data['phone']

                signup_session.participants.append(SignupSessionParticipant(
                    signup_session=signup_session,
                    id=None,
                    user=None,
                    first_name=first_name,
                    last_name=last_name,
                    birth_date=form.cleaned_data['birth_date'],
                    email=email,
                    phone=phone,
                    state='signed_up',
                    use_parent_contact_info=False,
                    use_signee_contact_info=use_signee_contact_info,
                    discount_code='',
                    signee_relation='other',
                    answers=[],
                    extras=[],
                ))

                if not use_signee_contact_info and 'confirm_nonuser' not in request.POST:
                    # Check if the submitted email and/or phone are already
                    # registered in the member system, and if so, give the
                    # signee a hint that they may add them as a member
                    email = form.cleaned_data['email']
                    phone = re.sub('\s', '', form.cleaned_data['phone'])
                    existing_email = Actor.objects.filter(email=email).exists()
                    existing_phone = len(lookup_users_by_phone(phone)) > 0

                    if existing_email or existing_phone:
                        context['existing_member'] = True
                        context['existing_email'] = existing_email
                        context['existing_phone'] = existing_phone
                        return render(
                            request,
                            'common/aktiviteter/show/signup/add_participant.html',
                            context,
                        )

                signup_session.save()
                return redirect('aktiviteter:signup_participants',
                                aktivitet_date.aktivitet.id, aktivitet_date.id)

    return render(request, 'common/aktiviteter/show/signup/add_participant.html', context)