def update_all_distributed_callback(request): """ """ if request.method != "POST": raise PermissionDenied("Only POST allowed to this URL endpoint.") videos = json.loads(request.POST["video_logs"]) exercises = json.loads(request.POST["exercise_logs"]) user = FacilityUser.objects.get(id=request.POST["user_id"]) node_ids = [node.get("id") for node in get_content_items()] # Save videos n_videos_uploaded = 0 for video in videos: video_id = video['video_id'] youtube_id = video['youtube_id'] # Only save video logs for videos that we recognize. if video_id not in node_ids: logging.warn("Skipping unknown video %s" % video_id) continue try: (vl, _) = VideoLog.get_or_initialize(user=user, video_id=video_id) # has to be that video_id, could be any youtube_id for key,val in video.iteritems(): setattr(vl, key, val) logging.debug("Saving video log for %s: %s" % (video_id, vl)) vl.save() n_videos_uploaded += 1 except KeyError: # logging.error("Could not save video log for data with missing values: %s" % video) except Exception as e: error_message = _("Unexpected error importing videos: %(err_msg)s") % {"err_msg": e} return JsonResponseMessageError(error_message, status=500) # Save exercises n_exercises_uploaded = 0 for exercise in exercises: # Only save video logs for videos that we recognize. if exercise['exercise_id'] not in node_ids: logging.warn("Skipping unknown video %s" % exercise['exercise_id']) continue try: (el, _) = ExerciseLog.get_or_initialize(user=user, exercise_id=exercise["exercise_id"]) for key,val in exercise.iteritems(): setattr(el, key, val) logging.debug("Saving exercise log for %s: %s" % (exercise['exercise_id'], el)) el.save() n_exercises_uploaded += 1 except KeyError: logging.error("Could not save exercise log for data with missing values: %s" % exercise) except Exception as e: error_message = _("Unexpected error importing exercises: %(err_msg)s") % {"err_msg": e} return JsonResponseMessageError(error_message, status=500) return JsonResponseMessageSuccess(_("Uploaded %(num_exercises)d exercises and %(num_videos)d videos") % { "num_exercises": n_exercises_uploaded, "num_videos": n_videos_uploaded, })
def time_set(request): """ Receives a date-time string and sets the system date-time RPi only. """ if not settings.ENABLE_CLOCK_SET: return JsonResponseMessageError( _("Time reset can only be done on Raspberry Pi systems."), status=403) # Form does all the data validation - including ensuring that the data passed is a proper date time. # This is necessary to prevent arbitrary code being run on the system. form = DateTimeForm(data=simplejson.loads(request.body)) if not form.is_valid(): return JsonResponseMessageError( _("Could not read date and time: Unrecognized input data format."), status=400) try: if os.system('sudo date +%%F%%T -s "%s"' % form.data["date_time"]): raise PermissionDenied except PermissionDenied as e: return JsonResponseMessageError(_( "System permissions prevented time setting, please run with root permissions." ), status=500) now = datetime.datetime.now().isoformat(" ").split(".")[0] return JsonResponseMessage(_("System time was reset successfully."))
def wrapper_fn_pfr(request, *args, **kwargs): if request.GET.get("process_id"): # Get by ID--direct! if not isnumeric(request.GET["process_id"]): return JsonResponseMessageError(_("process_id is not numeric."), status=400) else: process_log = get_object_or_404(UpdateProgressLog, id=request.GET["process_id"]) elif request.GET.get("process_name"): process_name = request.GET["process_name"] if "start_time" not in request.GET: start_time = datetime.datetime.now() else: start_time = make_naive(dateutil.parser.parse(request.GET["start_time"]), get_current_timezone()) try: # Get the latest one of a particular name--indirect process_log = UpdateProgressLog.get_active_log(process_name=process_name, create_new=False) if not process_log: # Still waiting; get the very latest, at least. logs = UpdateProgressLog.objects \ .filter(process_name=process_name, completed=True, end_time__gt=start_time) \ .order_by("-end_time") if logs: process_log = logs[0] except Exception as e: # The process finished before we started checking, or it's been deleted. # Best to complete silently, but for debugging purposes, will make noise for now. return JsonResponseMessageError(unicode(e), status=500) else: return JsonResponseMessageError(_("Must specify process_id or process_name"), status=400) return handler(request, process_log, *args, **kwargs)
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"})
def handler_403(request, *args, **kwargs): context = RequestContext(request) #message = None # Need to retrieve, but can't figure it out yet. if request.is_ajax(): return JsonResponseMessageError(_("You must be logged in with an account authorized to view this page (API)."), status=403) else: messages.error(request, mark_safe(_("You must be logged in with an account authorized to view this page."))) return HttpResponseRedirect(set_query_params(reverse("homepage"), {"next": request.get_full_path(), "login": True}))
def delete_zone(request, zone_id): zone = Zone.objects.get(id=zone_id) if zone.has_dependencies(passable_classes=["Organization"]): return JsonResponseMessageError( _("You cannot delete Zone '%(zone_name)s' because it is syncing data with with %(num_devices)d device(s)" ) % { "zone_name": zone.name, "num_devices": zone.devicezone_set.count(), }) else: zone.delete() return JsonResponseMessageSuccess( _("You have successfully deleted Zone %(zone_name)s") % {"zone_name": zone.name})
def require_sync_session_wrapper_fn(request): if request.body: data = simplejson.loads(request.body) else: data = request.GET try: if "client_nonce" not in data: return JsonResponseMessageError( "Client nonce must be specified.", status=400) session = SyncSession.objects.get( client_nonce=data["client_nonce"]) if not session.verified: return JsonResponseMessageError( "Session has not yet been verified.", status=401) if session.closed: return JsonResponseMessageError("Session is already closed.", status=401) except SyncSession.DoesNotExist: return JsonResponseMessageError( "Session with specified client nonce could not be found.", status=403) response = handler(data, session) session.save() return response
def delete_organization(request, org_id): org = Organization.objects.get(pk=org_id) num_zones = org.get_zones().count() if num_zones > 0: return JsonResponseMessageError( _("You cannot delete Organization '%(org_name)s' because it has %(num_zones)s sharing network(s) associated with it." ) % { "org_name": org.name, "num_zones": num_zones, }) else: org.delete() return JsonResponseMessageSuccess( _("You have successfully deleted Organization %(org_name)s.") % {"org_name": org.name})
def facility_delete(request, facility_id=None): if not request.is_django_user: raise PermissionDenied("Teachers cannot delete facilities.") if request.method != 'POST': return JsonResponseMessageError(_("Method is not allowed."), status=405) facility_id = facility_id or simplejson.loads(request.body or "{}").get("facility_id") fac = get_object_or_404(Facility, id=facility_id) fac.soft_delete() return JsonResponseMessageSuccess( _("Deleted facility %(facility_name)s successfully.") % {"facility_name": fac.name})
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)
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)
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)
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)
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))
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(), })