コード例 #1
0
 def post(self, request, format=None):
     """POST handler."""
     from_ = str(request.POST['from'])
     to_ = str(request.POST['to'])
     body = str(request.POST['body'])
     network = get_network_from_user(request.user)
     if network.billing_enabled and network.ledger.balance <= 0:
         # Shouldn't this be a 402 payment required? -kurtis
         return Response("operator has no credit in Endaga account",
                         status=status.HTTP_400_BAD_REQUEST)
     q = models.Number.objects.get(number=from_)
     if (q.kind not in SendSMS.HANDLERS):
         #shouldn't this be a 404 not found? -kurtis
         return Response("Invalid sending number",
                         status=status.HTTP_400_BAD_REQUEST)
     else:
         provider = SendSMS.HANDLERS[q.kind][0](
             SendSMS.HANDLERS[q.kind][1],  #username
             SendSMS.HANDLERS[q.kind][2],  #password
             SendSMS.HANDLERS[q.kind][3],  #inbound_sms
             SendSMS.HANDLERS[q.kind][4],  #outbound_sms
             SendSMS.HANDLERS[q.kind][5])  #inbound_voice
         try:
             provider.send(to_, from_, body)
         except Exception:
             message = '%s to: %s, from: %s, body len: %s' % (
                 provider, to_, from_, len(body))
             raise Exception(message)
         # Bill the operator.
         cost_to_operator = network.calculate_operator_cost(
             'off_network_send', 'sms', destination_number=to_)
         network.bill_for_sms(cost_to_operator, 'outside_sms')
         return Response("", status=status.HTTP_202_ACCEPTED)
コード例 #2
0
ファイル: api_v2.py プロジェクト: shivkumarsah/ccm
 def delete(self, request, tower_uuid):
     network = get_network_from_user(request.user)
     tower = models.BTS.objects.get(uuid=tower_uuid)
     if tower.network and tower.network != network:
         return Response("Network is not associated with that BTS.",
                         status=status.HTTP_403_FORBIDDEN)
     # Create a DerigisteredBTS instance.
     dbts = models.DeregisteredBTS(uuid=tower.uuid, secret=tower.secret)
     dbts.save()
     # Create a 'deregister_bts' UsageEvent.
     now = datetime.datetime.now(pytz.utc)
     if tower.nickname:
         name = 'tower "%s" (%s)' % (tower.nickname, tower.uuid)
     else:
         name = 'tower %s' % tower.uuid
     event = models.UsageEvent.objects.create(date=now,
                                              bts_uuid=tower.uuid,
                                              kind='deregister_bts',
                                              reason='deregistered %s' %
                                              name)
     event.save()
     # TODO(matt): generate revocation certs
     # And finally delete the BTS.
     tower.delete()
     return Response("")
コード例 #3
0
    def post(self, request, format=None):
        """ Request a number and associate with a subscriber. """
        if not ("bts_uuid" in request.POST or "imsi" in request.POST):
            return Response("", status=status.HTTP_400_BAD_REQUEST)

        bts_uuid = str(request.POST['bts_uuid'])
        imsi = str(request.POST['imsi'])

        network = get_network_from_user(request.user)
        try:
            bts = models.BTS.objects.get(uuid=bts_uuid, network=network)
        except models.BTS.DoesNotExist:
            return Response("User is not associated with that BTS.",
                            status=status.HTTP_403_FORBIDDEN)

        # If the IMSI is already in use, and associated with another network,
        # prevent the registration of a new number. If it's associated with
        # this network, simply return the currently-associated number. N.B.,
        # this effectively enforces a 1-1 mapping of subscriber to number
        # currently.
        try:
            subscriber = models.Subscriber.objects.get(imsi=imsi)
            if subscriber.network != network:
                return Response("IMSI already registered to another network",
                                status=status.HTTP_409_CONFLICT)
        except models.Subscriber.DoesNotExist:
            # Create a new subscriber if one matching this IMSI didn't already
            # exist.
            subscriber = models.Subscriber(network=network,
                                           imsi=imsi,
                                           balance=0,
                                           bts=bts)
            subscriber.save()

        # If the subscriber already exists, we should return the associated
        # phone number and update the BTS to match what is being used.
        n = models.Number.objects.filter(subscriber=subscriber).first()
        if not n:  # Otherwise, pick a random available number and associate it.
            with transaction.atomic():
                n = models.Number.objects.filter(
                    state="available",
                    country_id=network.number_country).first()

                if not n:
                    return Response("No number available",
                                    status=status.HTTP_404_NOT_FOUND)

                n.state = "inuse"
                n.network = network
                n.subscriber = subscriber
                n.save()
                n.charge()

        return Response(
            {
                'number': n.number,
                'subscriber': subscriber.imsi,
                'balance': subscriber.balance
            },
            status=status.HTTP_200_OK)
コード例 #4
0
ファイル: api_v2.py プロジェクト: shivkumarsah/ccm
 def delete(self, request, imsi):
     network = get_network_from_user(request.user)
     subscriber = models.Subscriber.objects.get(imsi=imsi)
     if subscriber.network != network:
         return Response("Network is not associated with that Subscriber.",
                         status=status.HTTP_403_FORBIDDEN)
     # This is a valid request, begin processing.
     subscriber.deactivate()
     return Response("")
コード例 #5
0
    def get(self, request, format=None):
        """
        Update the current inbound URL, and return registration status.
        """
        if not all(
            [_ in request.GET for _ in ['bts_uuid', 'vpn_status', 'vpn_ip']]):
            return Response(status=status.HTTP_400_BAD_REQUEST)

        bts_uuid = request.GET['bts_uuid']
        vpn_up = request.GET['vpn_status'] == "up"
        vpn_ip = request.GET['vpn_ip']

        # Old versions of client didn't specify this, but always ran on port
        # 8081, so assume that as default.
        federer_port = request.GET.get('federer_port', "8081")

        network = get_network_from_user(request.user)
        try:
            bts = models.BTS.objects.get(uuid=bts_uuid, network=network)
        except models.BTS.DoesNotExist:
            return Response({"status": "BTS isn't registered."},
                            status=status.HTTP_403_FORBIDDEN)

        # we know the bts has the right token if it's authed, so provide the secret
        # TODO: cycle this to a random code
        if not bts.secret:
            bts.secret = bts.uuid
            bts.save()

        if bts.is_registered() and vpn_up:
            inbound = self.update_inbound_url(bts, vpn_ip, federer_port)
            bts.save()
            return Response(
                {
                    'status': 'registered, ok',
                    'inbound': inbound,
                    'bts_secret': bts.secret
                },
                status=status.HTTP_200_OK)
        elif vpn_up:
            # the BTS reports the VPN is up, and has authed w/ API key,
            #so we consider the BTS fully reigstered.
            bts.mark_registered()
            bts.mark_active()
            inbound = self.update_inbound_url(bts, vpn_ip, federer_port)
            bts.save()
            return Response(
                {
                    'status': 'unregistered -> registered',
                    'inbound': inbound,
                    'bts_secret': bts.secret
                },
                status=status.HTTP_200_OK)
        else:
            # TODO: add logic to handle lack of inbound URL
            return Response("", status=status.HTTP_404_NOT_FOUND)
コード例 #6
0
    def get(self, request, bts_uuid=None, number=None, format=None):
        """ Associate a number to a BTS.

        DEPRECATED (shasan 2016jan5) -- use the POST endpoint instead
        """
        if not (number or bts_uuid or "imsi" in request.GET):
            return Response("", status=status.HTTP_400_BAD_REQUEST)
        network = get_network_from_user(request.user)
        try:
            bts = models.BTS.objects.get(uuid=bts_uuid, network=network)
        except models.BTS.DoesNotExist:
            return Response("User is not associated with that BTS.",
                            status=status.HTTP_403_FORBIDDEN)
        # If the IMSI is already in use, and associated with another BTS,
        # prevent the registration of a new number.  However, we allow IMSIs
        # to register a second number on the IMSI's original BTS.
        imsi = request.GET['imsi']
        try:
            subscriber = models.Subscriber.objects.get(imsi=imsi)
            if subscriber.network != network:
                return Response("IMSI already registered",
                                status=status.HTTP_409_CONFLICT)
        except models.Subscriber.DoesNotExist:
            # Create a new subscriber if one matching this IMSI didn't already
            # exist.
            subscriber = models.Subscriber(network=network,
                                           imsi=imsi,
                                           balance=0,
                                           bts=bts)
            subscriber.save()

        with transaction.atomic():
            q = models.Number.objects.filter(number__exact="%s" % number)
            if len(q) > 0:
                n = q[0]
                # This is tricky. Numbers that get marked 'pending' will have
                # the network id already set, so this check fails and we set
                # the number as in-use. This is an artifact of the two-step
                # number registration process. So don't remove the network ID
                # check!
                if n.state != "available" and n.network != bts.network:
                    return Response("Number already in use.",
                                    status=status.HTTP_400_BAD_REQUEST)
                n.network = bts.network
                n.state = "inuse"
            else:
                # FIXME this should never happen -- all numbers should already
                # be in the system, unless we're associating an old BTS for the
                # first time (like w/ Bok)
                n = models.Number(number=number,
                                  state="inuse",
                                  network=bts.network)
            # Associate it with the subscriber and save.
            n.subscriber = subscriber
            n.save()
            return Response(None, status=status.HTTP_200_OK)
コード例 #7
0
 def get(self, request):
     if not "number" in request.GET:
         return Response("Missing number.",
                         status=status.HTTP_400_BAD_REQUEST)
     query_number = request.GET["number"]
     try:
         network = get_network_from_user(request.user)
         Number.objects.get(number=query_number, network=network)
         return Response("OK", status=status.HTTP_200_OK)
     except Number.DoesNotExist:
         return Response("Unauthorized",
                         status=status.HTTP_401_UNAUTHORIZED)
コード例 #8
0
    def get(self, request, bts_uuid=None, format=None):
        """Return a number that's usable by a BTS.

        We first check for "available" numbers in our database. If there are
        none, we buy a number from a provider, set it up, and return here. We
        have to specify a specific BTS to avoid a race condition when multiple
        BTS register for a number at once.
        """
        if not bts_uuid:
            return Response("No BTS UUID specified.",
                            status=status.HTTP_400_BAD_REQUEST)
        network = get_network_from_user(request.user)
        try:
            bts = models.BTS.objects.get(uuid=bts_uuid, network=network)
        except models.BTS.DoesNotExist:
            return Response("The specified BTS does not belong to the user.",
                            status=status.HTTP_403_FORBIDDEN)
        # First check for available numbers.  If a number is available, it's up
        # for grabs by anyone.
        with transaction.atomic():
            q = models.Number.objects.filter(state__exact="available")
            for n in q:
                # We do this here rather than in the db query since at this
                # time some numbers don't have a country field to query on. Can
                # probably be removed later. -- SH (2014 aug 21)
                # TODO(matt): this potentially sets a lot of numbers as
                #             pending..
                if n.country() == network.number_country:
                    n.state = "pending"
                    n.network = network
                    n.save()
                    n.charge()
                    return Response(int(n.number), status=status.HTTP_200_OK)

        # No number available, so we have to buy one from Nexmo.
        # TODO: Try to buy from multiple vendors
        np = NexmoProvider(
            settings.ENDAGA['NEXMO_ACCT_SID'],
            settings.ENDAGA['NEXMO_AUTH_TOKEN'],
            settings.ENDAGA['NEXMO_INBOUND_SMS_URL'],
            None,  #outbound_sms_url
            settings.ENDAGA['NEXMO_INBOUND_VOICE_HOST'],
            country=network.number_country)
        try:
            # This call creates the new number in the DB as a side effect.
            new_number = np.get_number(bts.network)
            print "New number is %s" % new_number
            return Response(new_number, status=status.HTTP_200_OK)
        except ValueError:
            return Response("Number not available",
                            status=status.HTTP_404_NOT_FOUND)
コード例 #9
0
    def post(self, request):
        """Handles POST requests."""

        try:
            bts_uuid, bts_status = self._process_post_data(request)
        except HTTP_415_Error as e:
            return Response(str(e),
                            status=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE)
        except HTTP_422_Error as e:
            return Response(str(e),
                            status=status.HTTP_422_UNPROCESSABLE_ENTITY)
        except (ValueError, KeyError):
            return Response("Invalid/missing checkin parameters.",
                            status=status.HTTP_400_BAD_REQUEST)

        # See if this BTS has been deregistered.
        try:
            dbts = models.DeregisteredBTS.objects.get(uuid=bts_uuid)
            resp = {
                'status': 'deregistered',
            }
            dbts.delete()
            return Response({'response': resp}, status=status.HTTP_200_OK)
        except models.DeregisteredBTS.DoesNotExist:
            pass
        # The BTS isn't deregistered, continue with the checkin as normal.
        network = get_network_from_user(request.user)
        try:
            bts = models.BTS.objects.get(uuid=bts_uuid, network=network)
        except models.BTS.DoesNotExist:
            return Response("Incorrect auth for BTS.",
                            status=status.HTTP_403_FORBIDDEN)

        try:
            resp = checkin.CheckinResponder(bts).process(bts_status)
        except Exception as e:
            print "Error handling checkin (BTS %s): %s" % (bts.uuid, e)
            print "BTS status: %s" % bts_status
            raise

        checkin_resp = Response({'response': resp}, status=status.HTTP_200_OK)

        def gzip_response_callback(response):
            if len(response.content) < MIN_COMPRESSIBLE_RESPONSE_SZ:
                return response
            gzipped_resp = gzip_middleware.process_response(request, response)
            return gzipped_resp

        checkin_resp.add_post_render_callback(gzip_response_callback)
        return checkin_resp
コード例 #10
0
 def delete(self, request, bts_uuid=None, number=None, format=None):
     """ Dis-associate a number from a BTS and mark it available. """
     if not (number or bts_uuid):
         return Response("", status=status.HTTP_400_BAD_REQUEST)
     network = get_network_from_user(request.user)
     try:
         bts = models.BTS.objects.get(uuid=bts_uuid, network=network)
     except models.BTS.DoesNotExist:
         return Response("User is not associated with that BTS.",
                         status=status.HTTP_403_FORBIDDEN)
     with transaction.atomic():
         q = models.Number.objects.filter(number__exact=number).filter(
             network=bts.network)
         for number in q:
             number.state = "available"
             number.network = None
             number.subscriber = None
             number.save()
             return Response(None, status=status.HTTP_200_OK)
         return Response(None, status=status.HTTP_404_NOT_FOUND)
コード例 #11
0
    def get(self, request):

        logger.warning("Use of deprecated API call %s" % (request.GET, ))

        if "uuid" not in request.GET:
            return Response("No uuid specified.",
                            status=status.HTTP_400_BAD_REQUEST)
        query_num = request.GET["uuid"]
        try:
            network = get_network_from_user(request.user)
            bts = BTS.objects.get(uuid=query_num)
            # Strip the protocol field and just return the rest, removing any
            # trailing slash.
            bts_info = urlparse.urlparse(bts.inbound_url)
            result = {
                'netloc': bts_info.netloc,
                'hostname': bts_info.hostname,
                'owner': bts.network.id
            }
            return Response(result, status=status.HTTP_200_OK)
        except Number.DoesNotExist:
            return Response("No such UUID", status=status.HTTP_404_NOT_FOUND)
コード例 #12
0
    def post(self, request, format=None):
        """
        Submit a CSR for signing, and get back a signed cert
        as well as an OpenVPN conf.
        """
        if not all([_ in request.POST for _ in ['bts_uuid', 'csr']]):
            return Response(status=status.HTTP_400_BAD_REQUEST)

        bts_uuid = request.POST['bts_uuid']
        csr = request.POST['csr']

        network = get_network_from_user(request.user)
        try:
            bts = models.BTS.objects.get(uuid=bts_uuid, network=network)
        except models.BTS.DoesNotExist:
            return Response({"status": "BTS isn't registered."},
                            status=status.HTTP_403_FORBIDDEN)

        r = requests.post('http://%s/csr' % settings.ENDAGA['KEYMASTER'],
                          data={
                              'ident': bts_uuid,
                              'csr': csr
                          })
        if r.status_code == 200:
            cert = json.loads(r.content)['certificate']
            bts = models.BTS.objects.get(uuid=bts_uuid)
            bts.certificate = cert
            bts.save()
            vpnconf = self.get_vpn_conf(bts)

            return Response({
                'certificate': cert,
                'vpnconf': vpnconf
            },
                            status=status.HTTP_200_OK)
        else:
            return Response("status: %d" % (r.status_code, ),
                            status=status.HTTP_400_BAD_REQUEST)
コード例 #13
0
    def post(self, request, format=None):
        required_fields = ['msgid', 'log_name']
        network = get_network_from_user(request.user)
        if not all([_ in request.POST for _ in required_fields]):
            return Response("Missing fields",
                            status=status.HTTP_400_BAD_REQUEST)

        if not 'file' in request.FILES:
            return Response("Missing file", status=status.HTTP_400_BAD_REQUEST)

        try:
            log_req = models.BTSLogfile.objects.get(uuid=request.POST['msgid'])
        except models.BTSLogfile.DoesNotExist:
            return Response("Unknown request.",
                            status=status.HTTP_400_BAD_REQUEST)

        if log_req.bts.network != network:
            return Response("Unauthorized.", status=status.HTTP_403_FORBIDDEN)

        log_req.logfile = request.FILES['file']
        log_req.status = 'done'
        log_req.save()

        return Response("OK", status=status.HTTP_200_OK)
コード例 #14
0
ファイル: api_v2.py プロジェクト: shivkumarsah/ccm
    def post(self, request, msisdn):
        network = get_network_from_user(request.user)
        number = models.Number.objects.get(number=msisdn)
        if (number.network and number.network != network
                and not request.user.is_staff):
            return Response("User is not associated with that Number %s %s." %
                            (number.network.pk, network.pk),
                            status=status.HTTP_403_FORBIDDEN)
        # Must post a valid 'state'.
        valid_states = ('available', 'released')
        if request.POST.get('state', None) not in valid_states:
            return Response("Must post a valid state.",
                            status=status.HTTP_400_BAD_REQUEST)
        # This is a valid request, begin processing.  First check if this is a
        # number-deactivation request.
        if (number.state == 'inuse'
                and request.POST.get('state') == 'available'):
            # Refuse to deactivate a subscriber's last number.
            if (len(models.Number.objects.filter(subscriber=number.subscriber))
                    <= 1):
                message = ("Cannot deactivate a subscriber's last number."
                           "  Instead, delete the subscriber.")
                return Response(message, status=status.HTTP_400_BAD_REQUEST)
            # If it's not the subscriber's only number, send an async post to
            # the BTS to deactivate the number.  Sign the request using JWT.
            bts = number.subscriber.bts
            url = '%s/config/deactivate_number' % bts.inbound_url
            data = {
                'number': msisdn,
                # Add a UUID as a nonce for the message.
                'msgid': str(uuid.uuid4()),
            }
            serializer = itsdangerous.JSONWebSignatureSerializer(bts.secret)
            signed_data = {
                'jwt': serializer.dumps(data),
            }
            tasks.async_post.delay(url, signed_data)
            # Create a 'deactivate_number' UsageEvent.
            now = datetime.datetime.now(pytz.utc)
            reason = 'deactivated phone number: %s' % number.number
            event = models.UsageEvent.objects.create(
                subscriber=number.subscriber,
                date=now,
                bts=bts,
                kind='deactivate_number',
                to_number=number.number,
                reason=reason,
                oldamt=number.subscriber.balance,
                newamt=number.subscriber.balance,
                change=0)
            event.save()
            # Diassociate the Number from its former Subscriber and Network.
            number.subscriber = None
            number.network = None
            number.state = request.POST.get('state')
            number.save()
            return Response("")

        # Check if this is a number-release request.
        if request.POST.get('state') == 'released':
            # User must be staff to do this.
            if not request.user.is_staff:
                return Response("", status=status.HTTP_404_NOT_FOUND)
            # The number must not be 'inuse.'
            if number.state == 'inuse':
                return Response("", status=status.HTTP_400_BAD_REQUEST)
            # The number cannot be associated with a Sub.
            if number.subscriber:
                return Response("", status=status.HTTP_400_BAD_REQUEST)
            # Validation passes, release (cancel) the number.
            nexmo_provider = NexmoProvider(
                settings.ENDAGA['NEXMO_ACCT_SID'],
                settings.ENDAGA['NEXMO_AUTH_TOKEN'],
                settings.ENDAGA['NEXMO_INBOUND_SMS_URL'],
                None,
                settings.ENDAGA['NEXMO_INBOUND_VOICE_HOST'],
                country=number.country_id)
            if nexmo_provider.cancel_number(number.number):
                # Success, delete the number.
                number.delete()
                return Response("", status=status.HTTP_200_OK)
            else:
                print 'deleting number %s failed' % number.number
                return Response("",
                                status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # Invalid request.
        return Response("", status=status.HTTP_400_BAD_REQUEST)