Ejemplo n.º 1
0
def get_download_urls(request):
    base_url = "%s://%s" % ("https" if request.is_secure() else "http",
                            request.get_host())

    # TODO: once Dylan makes all subtitle languages available,
    #   don't hard-code this.
    download_sizes = {
        "en": 19.8,
    }

    downloads = {}
    for locale, size in download_sizes.iteritems():
        urlargs = {
            "version": kalite.version.VERSION,
            "platform": "all",
            "locale": locale
        }
        downloads[locale] = {
            "display_name":
            "",  # Will fill in when language list from subtitles is available.
            "size":
            size,
            "url":
            "%s%s" %
            (base_url, reverse("download_kalite_public", kwargs=urlargs)),
        }

    return JsonResponse(downloads)
Ejemplo n.º 2
0
def device_counters(data, session):

    device_counters = get_device_counters(
        zone=session.client_device.get_zone())
    return JsonResponse({
        "device_counters": device_counters,
    })
Ejemplo n.º 3
0
def learner_logs(request):

    lang = request.language
    page = request.GET.get("page", 1)

    limit = request.GET.get("limit", 50)

    # Look back a week by default
    time_window = request.GET.get("time_window", 7)

    start_date = request.GET.get("start_date", None)

    end_date = request.GET.get("end_date", None)

    topic_ids = request.GET.getlist("topic_id", [])

    learners = get_learners_from_GET(request)

    pages = int(ceil(len(learners)/float(limit)))

    if page*limit < len(learners):

        learners = learners[(page - 1)*limit: page*limit]

    log_types = request.GET.getlist("log_type", ["exercise", "video", "content"])

    output_logs = []

    output_objects = []

    end_date = datetime.datetime.strptime(end_date,'%Y/%m/%d') if end_date else datetime.datetime.now()

    start_date = datetime.datetime.strptime(start_date,'%Y/%m/%d') if start_date else end_date - datetime.timedelta(time_window)

    for log_type in log_types:
        LogModel, fields, id_field, obj_ids, objects = return_log_type_details(log_type, topic_ids)

        log_objects = LogModel.objects.filter(user__in=learners, **obj_ids).values(*fields)
        if not topic_ids:
            topic_objects = log_objects.filter(latest_activity_timestamp__gte=start_date, latest_activity_timestamp__lte=end_date)
            if topic_objects.count() == 0:
                topic_objects = log_objects
            objects = dict([(obj[id_field], get_content_cache(language=lang).get(obj[id_field], get_exercise_cache(language=lang).get(obj[id_field]))) for obj in topic_objects]).values()
        output_objects.extend(objects)
        output_logs.extend(log_objects)

    return JsonResponse({
        "logs": output_logs,
        "contents": output_objects,
        # Sometimes 'learners' gets collapsed to a list from the Queryset. This insures against that eventuality.
        "learners": [{
            "first_name": learner.first_name,
            "last_name": learner.last_name,
            "username": learner.username,
            "pk": learner.pk
            } for learner in learners],
        "page": page,
        "pages": pages,
        "limit": limit
    })
Ejemplo n.º 4
0
def get_kalite_version(request):
    assert kalite.version.VERSION in kalite.version.VERSION_INFO

    def versionkey(v):
        '''sorts a version. For now, it sorts them by release date. It returns
        a number in the hundreds range to make space for subsorts, such as version
        number.
        '''
        version, vdata = v
        date = datetime.datetime.strptime(vdata['release_date'], "%Y/%m/%d")
        return date.toordinal(
        ) / 1000  # divide by 1000 to turn into 100s range

    request_version = request.GET.get(
        "current_version",
        "0.10.0")  # default to first version that can understand this.
    needed_updates = [
        version
        for version, _ in sorted(kalite.version.VERSION_INFO.iteritems(),
                                 key=versionkey)
        if StrictVersion(request_version) < StrictVersion(version)
    ]  # versions are nice--they sort by string
    return JsonResponse({
        "version":
        kalite.version.VERSION,
        "version_info":
        OrderedDict([(v, kalite.version.VERSION_INFO[v])
                     for v in needed_updates]),
    })
Ejemplo n.º 5
0
def set_server_or_user_default_language(request):
    if request.method == 'GET':
        return JsonResponseMessageError(_(
            "Can only handle default language changes through POST requests"),
                                        status=405)

    elif request.method == 'POST':
        data = json.loads(
            request.raw_post_data
        )  # POST is getting interpreted wrong again by Django
        lang_code = data['lang']

        if request.is_django_user and lang_code != get_default_language():
            logging.debug("setting server default language to %s" % lang_code)
            set_default_language(lang_code)
        elif not request.is_django_user and request.is_logged_in and lang_code != request.session[
                "facility_user"].default_language:
            logging.debug("setting user default language to %s" % lang_code)
            request.session["facility_user"].default_language = lang_code
            request.session["facility_user"].save()

        if lang_code != request.session.get("default_language"):
            logging.debug("setting session language to %s" % lang_code)
            request.session["default_language"] = lang_code

        set_request_language(request, lang_code)

        return JsonResponse({"status": "OK"})
Ejemplo n.º 6
0
def set_server_or_user_default_language(request):
    """This function sets the default language for either the server or user.
    It is accessed via HTTP POST or GET.

    Required Args (POST or GET):
        lang (str): any supported ISO 639-1 language code

    Optional Args (GET):
        returnUrl (str): the URL to redirect the client to after setting the language
        allUsers (bool): when true, set the the default language for all users,
                         when false or missing, set the language for current user

    Returns:
        JSON status, unless a returnUrl is provided, in which case it returns
        a redirect when successful

    Example:
        To set the current user's language to Spanish and send them to
        the Math section, you could use the following link:

            /api/i18n/set_default_language/?lang=es&returnUrl=/learn/khan/math
    """

    returnUrl = ''
    allUsers = ''

    # GET requests are used by RACHEL to jump to a specific page with the
    # language already set so the user doesn't have to.
    if request.method == 'GET':
        data = request.GET
        if not 'lang' in data:
            return redirect('/')
        if 'returnUrl' in data:
            returnUrl = data['returnUrl']
        if 'allUsers' in data:
            allUsers = data['allUsers']
    elif request.method == 'POST':
        data = json.loads(request.raw_post_data) # POST is getting interpreted wrong again by Django

    lang_code = data['lang']

    if allUsers or (request.is_django_user and lang_code != get_default_language()):
        logging.debug("setting server default language to %s" % lang_code)
        set_default_language(lang_code)
    elif not request.is_django_user and request.is_logged_in and lang_code != request.session["facility_user"].default_language:
        logging.debug("setting user default language to %s" % lang_code)
        request.session["facility_user"].default_language = lang_code
        request.session["facility_user"].save()

    if lang_code != request.session.get("default_language"):
        logging.debug("setting session language to %s" % lang_code)
        request.session["default_language"] = lang_code

    set_request_language(request, lang_code)

    if not returnUrl:
        return JsonResponse({"status": "OK"})
    else:
        return redirect(returnUrl)
Ejemplo n.º 7
0
def get_update_topic_tree(request):

    parent = request.GET.get("parent")
    lang_code = request.GET.get(
        "lang"
    ) or request.language  # Get annotations for the current language.

    return JsonResponse(
        get_topic_update_nodes(parent=parent, language=lang_code))
Ejemplo n.º 8
0
def delete_language_pack(request):
    """
    API endpoint for deleting language pack which fetches the language code (in delete_id) which has to be deleted.
    That particular language folders are deleted and that language gets removed.
    """
    lang_code = simplejson.loads(request.body or "{}").get("lang")
    delete_language(lang_code)

    return JsonResponse({"success": _("Successfully deleted language pack for %(lang_name)s.") % {"lang_name": get_language_name(lang_code)}})
Ejemplo n.º 9
0
def narrative_view(request, narrative_id):
    """
    :param request: the request
    :param narrative_id: the narrative id, a url to be matched
    :return: a serialized JSON blob of the narrative dict
    """
    filename = os.path.join(settings.CONTENT_DATA_PATH, "narratives")
    narratives = open_json_or_yml(filename)
    the_narrative = {}
    for key, narr in narratives.iteritems():
        exp = re.compile(key)
        if exp.search(narrative_id):
            the_narrative[key] = narr
            break
    return JsonResponse(the_narrative)
Ejemplo n.º 10
0
def get_annotated_topic_tree(request, lang_code=None):
    call_command(
        "videoscan"
    )  # Could potentially be very slow, blocking request... but at least it's via an API request!

    lang_code = lang_code or request.language  # Get annotations for the current language.
    statusdict = dict(
        VideoFile.objects.values_list("youtube_id", "percent_complete"))

    return JsonResponse(
        annotate_topic_tree(softload_json(TOPICS_FILEPATHS.get(CHANNEL),
                                          logger=logging.debug,
                                          raises=False),
                            statusdict=statusdict,
                            lang_code=lang_code))
Ejemplo n.º 11
0
def device_upload(data, session):
    """This device is getting device-related objects from another device"""

    # TODO(jamalex): check that the uploaded devices belong to the client device's zone and whatnot
    # (although it will only save zones from here if centrally signed, and devices if registered in a zone)
    try:
        # Unserialize, knowing that the models were serialized by a client of its given version.
        #   dest_version assumed to be this device's version
        result = save_serialized_models(data.get("devices", "[]"),
                                        src_version=session.client_version)
    except Exception as e:
        logging.debug("Exception uploading devices (in api_views): %s" % e)
        result = {"error": e.message, "saved_model_count": 0}

    session.models_uploaded += result["saved_model_count"]
    session.errors += result.has_key("error")
    return JsonResponse(result)
Ejemplo n.º 12
0
def device_download(data, session):
    """This device is having its own devices downloaded"""
    zone = session.client_device.get_zone()
    devicezones = list(
        DeviceZone.all_objects.filter(
            zone=zone,
            device__in=data["devices"]))  # including deleted devicezones
    devices = [devicezone.device for devicezone in devicezones]
    session.models_downloaded += len(devices) + len(devicezones)

    # Return the objects serialized to the version of the other device.
    return JsonResponse({
        "devices":
        serialize(devices + devicezones,
                  dest_version=session.client_version,
                  ensure_ascii=False)
    })
Ejemplo n.º 13
0
def narrative_view(request, narrative_id):
    """
    :param request: the request
    :param narrative_id: the narrative id, a url to be matched
    :return: a serialized JSON blob of the narrative dict
    """
    the_narrative = {}
    for key, narr in NARRATIVES.iteritems():
        exp = re.compile(key)
        if exp.search(narrative_id):
            the_narrative[key] = narr
            break

    if not the_narrative:
        return JsonResponseMessageWarning(
            _("No inline help is available for this page."), status=404)

    return JsonResponse(the_narrative)
Ejemplo n.º 14
0
def narrative_view(request, narrative_id):
    """
    :param request: the request
    :param narrative_id: the narrative id, a url to be matched
    :return: a serialized JSON blob of the narrative dict
    """
    filename = os.path.join(settings.CONTENT_DATA_PATH, "narratives")
    narratives = open_json_or_yml(filename)
    the_narrative = {}
    for key, narr in narratives.iteritems():
        exp = re.compile(key)
        if exp.search(narrative_id):
            the_narrative[key] = narr
            break

    if not the_narrative:
        return JsonResponseMessageWarning(_("No inline help is available for this page."), status=404)

    return JsonResponse(the_narrative)
Ejemplo n.º 15
0
def content_recommender(request):
    """Populate response with recommendation(s)"""

    user_id = request.GET.get('user', None)
    user = request.session.get('facility_user')

    if not user:
        if request.user.is_authenticated() and request.user.is_superuser:
            user = get_object_or_404(FacilityUser, pk=user_id)
        else:
            return JsonResponseMessageError(
                "You are not authorized to view these recommendations.",
                status=401)

    resume = request.GET.get('resume', None)
    next = request.GET.get('next', None)
    explore = request.GET.get('explore', None)

    def set_bool_flag(flag_name, rec_dict):
        rec_dict[flag_name] = True
        return rec_dict

    # retrieve resume recommendation(s) and set resume boolean flag
    resume_recommendations = [
        set_bool_flag("resume", rec)
        for rec in get_resume_recommendations(user, request)
    ] if resume else []

    # retrieve next_steps recommendations, set next_steps boolean flag, and flatten results for api response
    next_recommendations = [
        set_bool_flag("next", rec)
        for rec in get_next_recommendations(user, request)
    ] if next else []

    # retrieve explore recommendations, set explore boolean flag, and flatten results for api response
    explore_recommendations = [
        set_bool_flag("explore", rec)
        for rec in get_explore_recommendations(user, request)
    ] if explore else []

    return JsonResponse(resume_recommendations + next_recommendations +
                        explore_recommendations)
Ejemplo n.º 16
0
def model_upload(data, session):
    """This device is getting data-related objects from another device."""

    if "models" not in data:
        return JsonResponseMessageError("Must provide models.",
                                        data={"saved_model_count": 0},
                                        status=400)
    try:
        # Unserialize, knowing that the models were serialized by a client of its given version.
        #   dest_version assumed to be this device's version
        result = save_serialized_models(data["models"],
                                        src_version=session.client_version)
    except Exception as e:
        print "Exception uploading models (in api_views): %s, %s, %s" % (
            e.__class__.__name__, e.message, e.args)
        result = {"error": e.message, "saved_model_count": 0}

    session.models_uploaded += result["saved_model_count"]
    session.errors += result.has_key("error")
    return JsonResponse(result)
Ejemplo n.º 17
0
def model_download(data, session):
    """This device is having its own data downloaded"""

    if "device_counters" not in data:
        return JsonResponseMessageError("Must provide device counters.",
                                        data={"count": 0},
                                        status=400)
    try:
        # Return the objects serialized to the version of the other device.
        result = get_serialized_models(data["device_counters"],
                                       zone=session.client_device.get_zone(),
                                       include_count=True,
                                       dest_version=session.client_version)
    except Exception as e:
        print "Exception downloading models (in api_views): %s, %s, %s" % (
            e.__class__.__name__, e.message, e.args)
        result = {"error": e.message, "count": 0}

    session.models_downloaded += result["count"]
    session.errors += result.has_key("error")
    return JsonResponse(result)
Ejemplo n.º 18
0
def content_item(request, channel, content_id):
    language = request.language

    content = get_content_item(channel=channel,
                               content_id=content_id,
                               language=language)

    if not content:
        content = {
            "title": "Unavailable Content",
            "description":
            "This content is unavailable. Either it must be downloaded, or the url is incorrect.",
            "available": False,
            "kind": "Video",
            "id": "unavailable_content",
            "slug": "unavailable_content",
            "path": "unavailable_content"
        }

    if not content.get("available", False):
        if request.is_admin:
            # TODO(bcipolli): add a link, with querystring args that auto-checks this content in the topic tree
            messages.warning(
                request,
                _("This content was not found! You can download it by going to the Manage > Videos page."
                  ))
        elif request.is_logged_in:
            messages.warning(
                request,
                _("This content was not found! Please contact your coach or an admin to have it downloaded."
                  ))
        elif not request.is_logged_in:
            messages.warning(
                request,
                _("This content was not found! You must login as an admin/coach to download the content."
                  ))

    content["messages"] = get_messages_for_api_calls(request)

    return JsonResponse(content)
Ejemplo n.º 19
0
def search_api(request, channel):
    query = request.GET.get("term")
    if query is None:
        return JsonResponseMessageError("No search term specified", status=404)

    query = query.lower()
    # search for topic, video or exercise with matching title

    matches, exact, pages = search_topic_nodes(query=query,
                                               channel=channel,
                                               language=request.language,
                                               page=1,
                                               items_per_page=15,
                                               exact=False)

    if not matches:
        messages.warning(
            request,
            _("Search completed, no content was found for your search. Try something else."
              ))

    return JsonResponse(matches)
Ejemplo n.º 20
0
def register_public_key_server_auto(request):
    """This function allows an anonymous client to request a device key
    to be associated with a new zone.

    This allows registration to occur without a single login; the device
    will be associated with a headless zone.
    """
    public_key = urllib.unquote(request.GET.get("device_key", ""))
    if RegisteredDevicePublicKey.objects.filter(public_key=public_key):
        return HttpResponseForbidden("Device is already registered.")

    # Create some zone.
    zone = Zone(name="Zone for public key %s" % public_key[:50])
    zone.save()

    # Add an association between a device 's public key and this zone,
    #   so that when registration is attempted by the distributed server
    #   with this key, it will register and receive this zone info.
    RegisteredDevicePublicKey(zone=zone, public_key=public_key).save()

    # Report success
    return JsonResponse({})
Ejemplo n.º 21
0
def set_server_or_user_default_language(request):

    returnUrl = ''
    allUsers = ''

    if request.method == 'GET':
        data = request.GET
        if 'returnUrl' in data:
            returnUrl = data['returnUrl']
        if 'allUsers' in data:
            allUsers = data['allUsers']
    elif request.method == 'POST':
        data = json.loads(
            request.raw_post_data
        )  # POST is getting interpreted wrong again by Django

    lang_code = data['lang']

    if allUsers or (request.is_django_user
                    and lang_code != get_default_language()):
        logging.debug("setting server default language to %s" % lang_code)
        set_default_language(lang_code)
    elif not request.is_django_user and request.is_logged_in and lang_code != request.session[
            "facility_user"].default_language:
        logging.debug("setting user default language to %s" % lang_code)
        request.session["facility_user"].default_language = lang_code
        request.session["facility_user"].save()

    if lang_code != request.session.get("default_language"):
        logging.debug("setting session language to %s" % lang_code)
        request.session["default_language"] = lang_code

    set_request_language(request, lang_code)

    if returnUrl == '':
        return JsonResponse({"status": "OK"})
    else:
        return redirect(returnUrl)
Ejemplo n.º 22
0
def topic_tree(request, channel):
    parent = request.GET.get("parent")
    return JsonResponse(
        get_topic_tree(channel=channel,
                       language=request.language,
                       parent=parent))
Ejemplo n.º 23
0
def check_update_progress(request, process_log):
    """
    API endpoint for getting progress data on downloads.
    """
    return JsonResponse(_process_log_to_dict(process_log))
Ejemplo n.º 24
0
def destroy_session(data, session):
    session.closed = True
    return JsonResponse({})
Ejemplo n.º 25
0
def installed_language_packs(request):
    return JsonResponse(get_installed_language_packs(force=True).values())
Ejemplo n.º 26
0
def assessment_item(request, assessment_item_id):
    assessment_item_dict = get_assessment_item_data(
        channel=getattr(request, "channel", "khan"),
        language=getattr(request, "language", "en"),
        assessment_item_id=assessment_item_id)
    return JsonResponse(assessment_item_dict)
Ejemplo n.º 27
0
def get_server_info(request):
    """This function is used to check connection to central or local server and also to get specific data from server.

    Args:
        The http request.

    Returns:
        A json object containing general data from the server.

    """
    device = None
    zone = None

    device_info = {"status": "OK", "invalid_fields": []}

    for field in request.GET.get("fields", "").split(","):

        if field == "version":
            device = device or Device.get_own_device()
            device_info[field] = device.get_version()

        elif field == "device_name":
            device = device or Device.get_own_device()
            device_info[field] = device.name

        elif field == "device_description":
            device = device or Device.get_own_device()
            device_info[field] = device.description

        elif field == "device_description":
            device = device or Device.get_own_device()
            device_info[field] = device.description

        elif field == "device_id":
            device = device or Device.get_own_device()
            device_info[field] = device.id

        elif field == "zone_name":
            if settings.CENTRAL_SERVER:
                continue
            device = device or Device.get_own_device()
            zone = zone or device.get_zone()
            device_info[field] = zone.name if zone else None

        elif field == "zone_id":
            if settings.CENTRAL_SERVER:
                continue
            device = device or Device.get_own_device()
            zone = zone or device.get_zone()
            device_info[field] = zone.id if zone else None

        elif field == "online":
            if settings.CENTRAL_SERVER:
                device_info[field] = True
            else:
                device_info[field] = am_i_online()

        elif field:
            # the field isn't one we know about, so add it to the list of invalid fields
            device_info["invalid_fields"].append(field)

    return JsonResponse(device_info)
Ejemplo n.º 28
0
def create_session(request):
    data = simplejson.loads(request.body or "{}")
    if "client_nonce" not in data:
        return JsonResponseMessageError("Client nonce must be specified.",
                                        status=400)
    if len(data["client_nonce"]) != 32 or re.match("[^0-9a-fA-F]",
                                                   data["client_nonce"]):
        return JsonResponseMessageError(
            "Client nonce is malformed (must be 32-digit hex).", status=400)
    if "client_device" not in data:
        return JsonResponseMessageError("Client device must be specified.",
                                        status=400)
    if "server_nonce" not in data:
        if SyncSession.objects.filter(
                client_nonce=data["client_nonce"]).count():
            return JsonResponseMessageError(
                "Session already exists; include server nonce and signature.",
                status=401)
        session = SyncSession()
        session.client_nonce = data["client_nonce"]
        session.client_os = data.get("client_os", "")
        session.client_version = data.get("client_version", "")
        session.ip = get_request_ip(request)
        try:
            client_device = Device.objects.get(pk=data["client_device"])
            session.client_device = client_device
        except Device.DoesNotExist:
            # This is the codepath for unregistered devices trying to start a session.
            #   This would only get hit, however, if they manually run syncmodels.
            # But still, good to keep track of!
            UnregisteredDevicePing.record_ping(id=data["client_device"],
                                               ip=session.ip)
            return JsonResponseMessageError(
                "Client device matching id could not be found. (id=%s)" %
                data["client_device"],
                status=400)

        session.server_nonce = uuid.uuid4().hex
        session.server_device = Device.get_own_device()
        if session.client_device.pk == session.server_device.pk:
            return JsonResponseMessageError(
                "I know myself when I see myself, and you're not me.",
                status=401)
        session.save()
    else:
        try:
            session = SyncSession.objects.get(
                client_nonce=data["client_nonce"])
        except SyncSession.DoesNotExist:
            return JsonResponseMessageError(
                "Session with specified client nonce could not be found.",
                status=401)
        if session.server_nonce != data["server_nonce"]:
            return JsonResponseMessageError(
                "Server nonce did not match saved value.", status=401)
        if not data.get("signature", ""):
            return JsonResponseMessageError("Must include signature.",
                                            status=400)
        if not session.verify_client_signature(data["signature"]):
            return JsonResponseMessageError("Signature did not match.",
                                            status=401)
        session.verified = True
        session.save()

    # Return the serialized session, in the version intended for the other device
    return JsonResponse({
        "session":
        serialize([session],
                  dest_version=session.client_version,
                  ensure_ascii=False,
                  sign=False,
                  increment_counters=False),
        "signature":
        session.sign(),
    })
Ejemplo n.º 29
0
def register_device(request):
    """Receives the client device info from the distributed server.
    Tries to register either because the device has been pre-registered,
    or because it has a valid INSTALL_CERTIFICATE."""
    # attempt to load the client device data from the request data
    data = simplejson.loads(request.body or "{}")
    if "client_device" not in data:
        return JsonResponseMessageError(
            "Serialized client device must be provided.", status=400)
    try:
        # When hand-shaking on the device models, since we don't yet know the version,
        #   we have to just TRY with our own version.
        #
        # This is currently "central server" code, so
        #   this will only fail (currently) if the central server version
        #   is less than the version of a client--something that should never happen
        try:
            local_version = Device.get_own_device().get_version()
            models = deserialize(data["client_device"],
                                 src_version=local_version,
                                 dest_version=local_version)
        except db_models.FieldDoesNotExist as fdne:
            raise Exception(
                "Central server version is lower than client version.  This is ... impossible!"
            )
        client_device = models.next().object
    except Exception as e:
        return JsonResponseMessageError(
            "Could not decode the client device model: %s" % e,
            code=EC.CLIENT_DEVICE_CORRUPTED,
            status=400)

    # Validate the loaded data
    if not isinstance(client_device, Device):
        return JsonResponseMessageError(
            "Client device must be an instance of the 'Device' model.",
            code=EC.CLIENT_DEVICE_NOT_DEVICE)

    try:
        if not client_device.verify():
            # We've been getting this verification error a lot, even when we shouldn't. Send more details to us by email so we can diagnose.
            msg = "\n\n".join([
                request.body,
                client_device._hashable_representation(),
                str(client_device.validate()), client_device.signed_by_id,
                client_device.id,
                str(request)
            ])
            send_mail("Client device did not verify", msg,
                      "*****@*****.**",
                      ["*****@*****.**"])
            return JsonResponseMessageError(
                "Client device must be self-signed with a signature matching its own public key!",
                code=EC.CLIENT_DEVICE_INVALID_SIGNATURE)
    except Exception as e:
        # Can't properly namespace to a particular Exception here, since the only reason we would be getting here is
        # that what should be proper exception namespacing in code being called isn't correctly catching this exception
        msg = "\n\n".join([
            request.body,
            client_device._hashable_representation(),
            "Exception: %s" % e,
            str(type(e)), client_device.signed_by_id, client_device.id,
            str(request)
        ])
        send_mail("Exception while verifying client device", msg,
                  "*****@*****.**",
                  ["*****@*****.**"])
        return JsonResponseMessageError(
            "Client device must be self-signed with a signature matching its own public key!",
            code=EC.CLIENT_DEVICE_INVALID_SIGNATURE)

    try:
        zone = register_self_registered_device(client_device, models, data)
    except Exception as e:
        if e.args[0] == "Client not yet on zone.":
            zone = None
        else:
            # Client not on zone: allow fall-through via "old route"

            # This is the codepath for unregistered devices trying to start a session.
            #   This would only get hit, however, if they visit the registration page.
            # But still, good to keep track of!
            UnregisteredDevicePing.record_ping(id=client_device.id,
                                               ip=get_request_ip(request))

            return JsonResponseMessageError(
                "Failed to validate the chain of trust (%s)." % e,
                code=EC.CHAIN_OF_TRUST_INVALID,
                status=500)

    if not zone:  # old code-path
        try:
            registration = RegisteredDevicePublicKey.objects.get(
                public_key=client_device.public_key)
            if not registration.is_used():
                registration.use()

            # Use the RegisteredDevicePublicKey, now that we've initialized the device and put it in its zone
            zone = registration.zone

        except RegisteredDevicePublicKey.DoesNotExist:
            try:
                # A redirect loop here is also possible, if a Device exists in the central server database
                # corresponding to the client_device, but no corresponding RegisteredDevicePublicKey exists
                device = Device.objects.get(
                    public_key=client_device.public_key)
                return JsonResponseMessageError(
                    "This device has already been registered",
                    code=EC.DEVICE_ALREADY_REGISTERED,
                    status=409)
            except Device.DoesNotExist:
                return JsonResponseMessageError(
                    "Device registration with public key not found; login and register first?",
                    code=EC.PUBLIC_KEY_UNREGISTERED,
                    status=404)

    client_device.save(imported=True)

    try:
        device_zone = DeviceZone.objects.get(device=client_device, zone=zone)
        device_zone.save(
        )  # re-save, to give it a central server signature that will be honored by old clients
    except DeviceZone.DoesNotExist:
        device_zone = DeviceZone(device=client_device, zone=zone)
        device_zone.save(
        )  # create the DeviceZone for the new device, with an 'upgraded' signature

    # return our local (server) Device, its Zone, and the newly created DeviceZone, to the client
    #   Note the order :)
    #
    # Addition: always back central server object--in case they didn't get it during install,
    #   they need it for software updating.
    return JsonResponse(
        serialize([
            Device.get_central_server(),
            Device.get_own_device(), zone, device_zone
        ],
                  dest_version=client_device.version,
                  ensure_ascii=False))
Ejemplo n.º 30
0
def force_sync(request):
    """
    """
    force_job("syncmodels")  # now launches asynchronously
    return JsonResponse({})