def setUp_fake_device(self): """ Fake the install process, to (quickly) make a key and set up the own_device() """ # Could be a fixture, but safer to simply hard-code. Settings.set("public_key", u'MIIBCgKCAQEAlbIPLnQH2dORFBK8i9x7/3E0DR571S01aP7M0TJD8vJf8OrgL8pnru3o2Jaoni1XasCZgizvM4GNImk9geqPP/sFkj0cf/MXSLr1VDKo1SoflST9yTbOi7tzVuhTeL4P3LJL6PO6iNiWkjAdqp9QX3mE/DHh/Q40G68meRw6dPDI4z8pyUcshOpxAHTSh2YQ39LJAxP7YS26yjDX/+9UNhetFxemMrBZO0UKtJYggPYRlMZmlTZLBU4ODMmK6MT26fB4DC4ChA3BD4OFiGDHI/iSy+iYJlcWaldbZtc+YfZlGhvsLnJlrp4WYykJSH5qeBuI7nZLWjYWWvMrDepXowIDAQAB') Settings.set("private_key", u'-----BEGIN RSA PRIVATE KEY-----\nMIIEqQIBAAKCAQEAlbIPLnQH2dORFBK8i9x7/3E0DR571S01aP7M0TJD8vJf8Org\nL8pnru3o2Jaoni1XasCZgizvM4GNImk9geqPP/sFkj0cf/MXSLr1VDKo1SoflST9\nyTbOi7tzVuhTeL4P3LJL6PO6iNiWkjAdqp9QX3mE/DHh/Q40G68meRw6dPDI4z8p\nyUcshOpxAHTSh2YQ39LJAxP7YS26yjDX/+9UNhetFxemMrBZO0UKtJYggPYRlMZm\nlTZLBU4ODMmK6MT26fB4DC4ChA3BD4OFiGDHI/iSy+iYJlcWaldbZtc+YfZlGhvs\nLnJlrp4WYykJSH5qeBuI7nZLWjYWWvMrDepXowIDAQABAoIBAD8S/a6XGU/BA1ov\n4t4TkvO44TO96nOSTvTkl6x1v4e4dJBwhvHcGP/uIrRQFtA/TpwedxAQmuFa7vrW\n2SHKkX1l6Z0Kvt1yshblH8XQaq8WxqPzKDQGMdVSsHCoB7PScaCOR8nqGGjcyeTi\n/T0NT7JK46vX4N7dgttrE+WixOrtDOUJLX92tGSp8bZgg284fV053nJqYHHROpmZ\nCibM5HK8B/19ULCpglGQCUVmJPtRzNK1bE9OlB8P5aZzdEd82oC8TKfSGmByO1TI\nCat6x8e0hYVIDElYGdcW5CDAr6rbU0CXOxxQAz3gJFDe1/RbbGJEdjO3IulYbR4H\nBK+nGxECgYkA424wFuQGRAwig//SNasv5aIqz2qnczL5ob3MXCFR4aOMowS94qvd\neRG9McxgRwbEApOTMVMAUYlBMkKE//sBM9XWQ4q8igJ+TUlV8RRQ8AP6hMUhSXX0\nNeEECcauP4vI6hhsnTsG/OQ4pr/4bEewsyXFwPSGkh2v3O+fuc6A8RywQ3u6icc+\n9wJ5AKiACZmpSskyJgp+3X0jpYixb7pQ9gU6QpJmP9Z2DdUNnm0Y5tDjnaCd/Bvy\nmNuCWqNbYdlEYH32B3sCshzFCqQwkgSMOa84cHQHx4Nx7SG2fUp9w1ExvnMRzrnw\n3sjB3ptbNhk1yrkzhFbd6ZG4fsL5Mb0EurAFtQKBiFCUVc2GdQHfGsuR9DS3tnyx\n/GEI9NNIGFJKIQHzfENp4wZPQ8fwBMREmLfwJZyEtSYEi35KXi6FZugb0WuwzzhC\nZ2v+19Y+E+nmNeD4xcSEZFpuTeDtPd1pIDkmf85cBI+Mn88FfvBTHA9YrPgQXnba\nxzoaaSOUCR9Kd1kp5V2IQJtoVytBwPkCeFIDD6kkxuuqZu2Q1gkEgptHkZPjt/rP\nYnuTHNsrVowuNr/u8NkXEC+O9Zg8ub2NcsQzxCpVp4lnaDitFTf/h7Bmm4tvHNx1\n4fX3m1oU51ATXGQXVit8xK+JKU9DN4wLIGgJOwmGLwd5VZ5aIEb2v2vykgzn8l2e\nSQKBiQC7CJVToYSUWnDGwCRsF+fY9jUculveAQpVWj03nYBtBdTh2EWcvfoSjjfl\nmpzrraojpQlUhrbUf5MU1fD9+i6swrCCvfjXITG3c1bkkB5AaQW7NiPHvDRMuDpc\nHIQ+vqzdn4iUlt7KB5ChpnZMmgiOdCBM0vQsZlVCbp0ZNLqVYhFASQnWl6V9\n-----END RSA PRIVATE KEY-----\n') Device.initialize_own_device()
def zone_management(request, zone_id, org_id=None): context = control_panel_context(request, org_id=org_id, zone_id=zone_id) own_device = Device.get_own_device() if not context["zone"] and (zone_id != "None" or Zone.objects.count() != 0 or settings.CENTRAL_SERVER): raise Http404() # on distributed server, we can make due if they're not registered. # Accumulate device data device_data = OrderedDict() if context["zone"]: devices = Device.objects.filter(devicezone__zone=context["zone"]) else: devices = Device.objects.filter(devicemetadata__is_trusted=False) for device in list(devices.order_by("devicemetadata__is_demo_device", "name")): user_activity = UserLogSummary.objects.filter(device=device) sync_sessions = SyncSession.objects.filter(client_device=device) if not settings.CENTRAL_SERVER and device.id != own_device.id: # Non-local sync sessions unavailable on distributed server sync_sessions = None exercise_activity = ExerciseLog.objects.filter(signed_by=device) device_data[device.id] = { "name": device.name or device.id, "num_times_synced": sync_sessions.count() if sync_sessions is not None else None, "last_time_synced": sync_sessions.aggregate(Max("timestamp"))["timestamp__max"] if sync_sessions is not None else None, "is_demo_device": device.get_metadata().is_demo_device, "last_time_used": exercise_activity.order_by("-completion_timestamp")[0:1] if user_activity.count() == 0 else user_activity.order_by("-last_activity_datetime", "-end_datetime")[0], "counter": device.get_counter_position(), } # Accumulate facility data facility_data = OrderedDict() if context["zone"]: facilities = Facility.objects.by_zone(context["zone"]) else: facilities = Facility.objects.all() for facility in list(facilities.order_by("name")): user_activity = UserLogSummary.objects.filter(user__facility=facility) exercise_activity = ExerciseLog.objects.filter(user__facility=facility) facility_data[facility.id] = { "name": facility.name, "num_users": FacilityUser.objects.filter(facility=facility).count(), "num_groups": FacilityGroup.objects.filter(facility=facility).count(), "id": facility.id, "last_time_used": exercise_activity.order_by("-completion_timestamp")[0:1] if user_activity.count() == 0 else user_activity.order_by("-last_activity_datetime", "-end_datetime")[0], } context.update({ "facilities": facility_data, "devices": device_data, "upload_form": UploadFileForm(), "own_device_is_trusted": Device.get_own_device().get_metadata().is_trusted, }) return context
def update_context(request): device = Device.get_own_device() zone = device.get_zone() context = { "registered": Device.get_own_device().is_registered(), "zone_id": zone.id if zone else None, "device_id": device.id, } return context
def test_description_exclusion_regression_bug_2470(self): """FacilityGroup.__init__ used to append "description" to _unhashable_fields, which affected other classes as well. This test ensures that the description is not being excluded from Device._hashable_representation, even after instantiating a FacilityGroup.""" d = Device(name="Test", description="Test") possibly_bad_serialization = d._hashable_representation() self.assertIn("description=Test", possibly_bad_serialization, "Hashable representation of Device did not include description") g = FacilityGroup() possibly_worse_serialization = d._hashable_representation() self.assertIn("description=Test", possibly_worse_serialization, "Instantiating a FacilityGroup changed hashable representation of Device")
def test_valid_trusted(self): """ Chain of trust: 1. Zone created by this device 2. Another device joins (no central server) through an invitation """ own_device = Device.get_own_device() zone = Zone(name="test_zone") zone.save() new_device = Device(name="new_device") # make a new device new_device.set_key(Key()) new_device.save() # get an ID new_device.get_metadata().save() # Now create an invitation, and claim that invitation for the new device. invitation = ZoneInvitation.generate(zone=zone, invited_by=own_device) invitation.claim(used_by=new_device) self.assertEqual(invitation.used_by, new_device, "Invitation should now be used by device %s" % new_device) self.assertEqual(DeviceZone.objects.filter(device=new_device).count(), 1, "There should be a DeviceZone for device %s" % new_device) self.assertEqual(DeviceZone.objects.get(device=new_device).zone, zone, "DeviceZone for device %s should be zone %s" % (new_device, zone)) # Now get a chain of trust establishing the new device on the zone chain = ChainOfTrust(zone=zone, device=new_device) self.assertTrue(chain.verify(), "Chain of trust should verify.")
def zone_management(request, zone_id, org_id=None): org = get_object_or_None(Organization, pk=org_id) if org_id else None zone = get_object_or_404(Zone, pk=zone_id)# if zone_id != "new" else None own_device = Device.get_own_device() # Accumulate device data device_data = dict() for device in Device.objects.filter(devicezone__zone=zone).order_by("devicemetadata__is_demo_device", "name"): user_activity = UserLogSummary.objects.filter(device=device) sync_sessions = SyncSession.objects.filter(client_device=device) if not settings.CENTRAL_SERVER and device.id != own_device.id: # Non-local sync sessions unavailable on distributed server sync_sessions = None exercise_activity = ExerciseLog.objects.filter(signed_by=device) device_data[device.id] = { "name": device.name or device.id, "is_demo_device": device.devicemetadata.is_demo_device, "num_times_synced": sync_sessions.count() if sync_sessions is not None else None, "last_time_synced": sync_sessions.aggregate(Max("timestamp"))["timestamp__max"] if sync_sessions is not None else None, "is_demo_device": device.get_metadata().is_demo_device, "last_time_used": exercise_activity.order_by("-completion_timestamp")[0:1] if user_activity.count() == 0 else user_activity.order_by("-end_datetime")[0], "counter": device.get_counter(), } # Accumulate facility data facility_data = dict() for facility in Facility.objects.by_zone(zone): user_activity = UserLogSummary.objects.filter(user__facility=facility) exercise_activity = ExerciseLog.objects.filter(user__facility=facility) facility_data[facility.id] = { "name": facility.name, "num_users": FacilityUser.objects.filter(facility=facility).count(), "num_groups": FacilityGroup.objects.filter(facility=facility).count(), "id": facility.id, "last_time_used": exercise_activity.order_by("-completion_timestamp")[0:1] if user_activity.count() == 0 else user_activity.order_by("-end_datetime")[0], } return { "org": org, "zone": zone, "facilities": facility_data, "devices": device_data, "upload_form": UploadFileForm(), "own_device_is_trusted": Device.get_own_device().get_metadata().is_trusted, }
def handle(self, *args, **options): if DeviceMetadata.objects.filter(is_own_device=True).count() > 0: self.stderr.write("Error: This device has already been initialized; aborting.\n") return if len(args) >= 1 and args[0]: name = args[0] else: name = get_host_name() if len(args) >= 2 and args[1]: description = args[1] else: description = "" Device.initialize_own_device(name=name, description=description) self.stdout.write("Device '%s'%s has been successfully initialized.\n" % (name, description and (" ('%s')" % description) or ""))
def zone_redirect(request): """ Dummy view to generate a helpful dynamic redirect to interface with 'control_panel' app """ device = Device.get_own_device() zone = device.get_zone() return HttpResponseRedirect(reverse("zone_management", kwargs={"zone_id": (zone and zone.pk) or "None"}))
def check_setup_status_wrapper_fn(request, *args, **kwargs): if "registered" not in request.session: logging.error("Key 'registered' not defined in session, but should be by now.") if User.objects.exists(): request.has_superuser = True # next line is for testing # User.objects.all().delete() if request.is_admin: # TODO(bcipolli): move this to the client side? if not request.session.get("registered", True) and BaseClient().test_connection() == "success": # Being able to register is more rare, so prioritize. messages.warning(request, mark_safe("Please <a href='%s'>follow the directions to register your device</a>, so that it can synchronize with the central server." % reverse("register_public_key"))) elif not request.session["facility_exists"]: zone_id = (Zone.objects.all() and Zone.objects.all()[0].id) or "None" messages.warning(request, mark_safe("Please <a href='%s'>create a facility</a> now. Users will not be able to sign up for accounts until you have made a facility." % reverse("add_facility", kwargs={"zone_id": zone_id}))) elif not request.is_logged_in: if not request.session.get("registered", True) and BaseClient().test_connection() == "success": # Being able to register is more rare, so prioritize. redirect_url = reverse("register_public_key") elif not request.session["facility_exists"]: zone = Device.get_own_device().get_zone() zone_id = "None" if not zone else zone.id redirect_url = reverse("add_facility", kwargs={"zone_id": zone_id}) else: redirect_url = None if redirect_url: messages.warning(request, mark_safe( "Please login with the account you created while running the installation script, \ to complete the setup.")) return handler(request, *args, **kwargs)
def handle(self, *args, **options): if DeviceMetadata.objects.filter(is_own_device=True).count() > 0: raise CommandError("Error: This device has already been initialized; aborting.\n") name = args[0] if (len(args) >= 1 and args[0]) else get_host_name() description = args[1] if (len(args) >= 2 and args[1]) else "" data_file = args[2] if (len(args) >= 3 and args[2]) else self.data_json_file own_device = Device.initialize_own_device(name=name, description=description) self.stdout.write("Device '%s'%s has been successfully initialized.\n" % (name, description and (" ('%s')" % description) or "")) # Nothing to do with a central server if settings.CENTRAL_SERVER: return # Now we're definitely not central server, so ... go for it! # Import a zone (for machines sharing zones), and join if it works! invitation = None if not os.path.exists(data_file): sys.stderr.write("Could not find resource file %s. This may cause warnings to appear when updating your KA Lite version.\n" % data_file) else: try: invitation = load_data_for_offline_install(in_file=data_file) self.stdout.write("Successfully imported offline data from %s\n" % data_file) # Doesn't hurt to keep data around. #if not settings.DEBUG: # os.remove(data_file) except Exception as e: raise CommandError("Error importing offline data from %s: %s\n" % (data_file, str(e))) confirm_or_generate_zone(invitation) initialize_facility(options["facility_name"])
def confirm_or_generate_zone(invitation=None, device_zone=None): invitation = invitation or get_object_or_None(ZoneInvitation, used_by=Device.get_own_device()) device_zone = device_zone or get_object_or_None(DeviceZone, device=Device.get_own_device()) if invitation: sys.stdout.write("Confirmed existing sharing network %s, using invitation %s.\n" % (invitation.zone, invitation)) elif device_zone: sys.stdout.write("Confirmed existing sharing network %s, using device_zone %s.\n" % (device_zone.zone, device_zone)) else: # Sorry dude, you weren't invited to the party. You'll have to have your own! # Generate a zone (for stand-alone machines) call_command("generate_zone") sys.stdout.write("Successfully generated a sharing network, and joined!.\n") initialize_registration() # would try to sync
def add_log_to_summary(cls, user_log, device=None): """Adds total_time to the appropriate user/device/activity's summary log.""" assert user_log.end_datetime, "all log items must have an end_datetime to be saved here." assert user_log.total_seconds >= 0, "all log items must have a non-negative total_seconds to be saved here." device = device or Device.get_own_device() # Must be done here, or install fails # Check for an existing object log_summary = cls.objects.filter( device=device, user=user_log.user, activity_type=user_log.activity_type, start_datetime__lte=user_log.end_datetime, end_datetime__gte=user_log.end_datetime, ) assert log_summary.count() <= 1, "There should never be multiple summaries in the same time period/device/user/type combo" # Get (or create) the log item log_summary = log_summary[0] if log_summary.count() else cls( device=device, user=user_log.user, activity_type=user_log.activity_type, start_datetime=cls.get_period_start_datetime(user_log.end_datetime, settings.USER_LOG_SUMMARY_FREQUENCY), end_datetime=cls.get_period_end_datetime(user_log.end_datetime, settings.USER_LOG_SUMMARY_FREQUENCY), total_seconds=0, count=0, ) logging.debug("Adding %d seconds for %s/%s/%d, period %s to %s" % (user_log.total_seconds, device.name, user_log.user.username, user_log.activity_type, log_summary.start_datetime, log_summary.end_datetime)) # Add the latest info log_summary.total_seconds += user_log.total_seconds log_summary.count += 1 log_summary.save()
def _do_fake_registration(self): # Create a Zone and DeviceZone to fool the Device into thinking it's registered zone = Zone(name="The Danger Zone", description="Welcome to it.") zone.save() device = Device.get_own_device() deviceZone = DeviceZone(device=device, zone=zone) deviceZone.save()
def device_management(request, device_id, zone_id=None, per_page=None, cur_page=None): context = control_panel_context(request, zone_id=zone_id, device_id=device_id) #Get pagination details cur_page = cur_page or request.REQUEST.get("cur_page", "1") per_page = per_page or request.REQUEST.get("per_page", "10") # Retrieve sync sessions all_sessions = SyncSession.objects.filter(client_device=context["device"]) total_sessions = all_sessions.count() shown_sessions = list(all_sessions.order_by("-timestamp").values("timestamp", "ip", "models_uploaded", "models_downloaded", "errors")) session_pages, page_urls = paginate_data(request, shown_sessions, page=cur_page, per_page=per_page) context.update({ "session_pages": session_pages, "page_urls": page_urls, "total_sessions": total_sessions, "device_version": total_sessions and all_sessions[0].client_version or None, "device_os": total_sessions and all_sessions[0].client_os or None, "is_own_device": not settings.CENTRAL_SERVER and device_id == Device.get_own_device().id, }) # If local (and, for security purposes, a distributed server), get device metadata if context["is_own_device"]: context.update(local_install_context(request)) return context
def register_public_key_client(request): if Device.get_own_device().get_zone(): set_as_registered() return {"already_registered": True} client = SyncClient() if client.test_connection() != "success": return {"no_internet": True} reg_response = client.register() reg_status = reg_response.get("code") if reg_status == "registered": set_as_registered() return {"newly_registered": True} if reg_status == "device_already_registered": set_as_registered() return {"already_registered": True} if reg_status == "public_key_unregistered": return { "unregistered": True, "registration_url": client.path_to_url( "/securesync/register/?" + urllib.quote(crypto.serialize_public_key())), } error_msg = reg_response.get("error", "") if error_msg: return {"error_msg": error_msg} return HttpResponse("Registration status: " + reg_status)
def register_public_key_client(request): own_device = Device.get_own_device() if own_device.get_zone(): set_as_registered() return {"already_registered": True} client = SyncClient() if client.test_connection() != "success": return {"no_internet": True} reg_response = client.register() reg_status = reg_response.get("code") if reg_status == "registered": set_as_registered() return {"newly_registered": True} if reg_status == "device_already_registered": set_as_registered() return {"already_registered": True} if reg_status == "public_key_unregistered": return { "unregistered": True, "registration_url": client.path_to_url( reverse("register_public_key") + "?" + urllib.quote(own_device.public_key) ), "central_login_url": "%s://%s/accounts/login" % (settings.SECURESYNC_PROTOCOL, settings.CENTRAL_SERVER_HOST), "callback_url": request.build_absolute_uri(reverse("register_public_key")), } error_msg = reg_response.get("error", "") if error_msg: return {"error_msg": error_msg} return HttpResponse(_("Registration status: ") + reg_status)
def test_facility_group_save(self): # only perform test if we are ourselves a trusted (i.e. central server) device if Device.get_own_device().is_trusted(): group = FacilityGroup(name="MyGroup", facility=self.facility) group.save() assert group.zone_fallback is not None, "Centrally created FacilityGroup was not assigned a zone."
def device_redirect(request): """ Dummy view to generate a helpful dynamic redirect to interface with 'control_panel' app """ device = Device.get_own_device() zone = device.get_zone() return HttpResponseRedirect(reverse("device_management", kwargs={"zone_id": (zone and zone.pk) or None, "device_id": device.pk}))
def test_facility_user_save(self): # only perform test if we are ourselves a trusted (i.e. central server) device if Device.get_own_device().is_trusted(): user = FacilityUser(username="******", facility=self.facility) user.set_password("blahblah") user.save() assert user.zone_fallback is not None, "Centrally created FacilityUser was not assigned a zone."
def test_description_exclusion_regression_bug_2470(self): """FacilityGroup.__init__ used to append "description" to _unhashable_fields, which affected other classes as well. This test ensures that the description is not being excluded from Device._hashable_representation, even after instantiating a FacilityGroup.""" d = Device(name="Test", description="Test") possibly_bad_serialization = d._hashable_representation() self.assertIn( "description=Test", possibly_bad_serialization, "Hashable representation of Device did not include description") g = FacilityGroup() possibly_worse_serialization = d._hashable_representation() self.assertIn( "description=Test", possibly_worse_serialization, "Instantiating a FacilityGroup changed hashable representation of Device" )
def test_not_redirected_when_offline(self): self.assertFalse(Device.get_own_device().is_registered(), "The device should be unregistered!") self.assertFalse(am_i_online(url=settings.CENTRAL_SERVER_URL), "Central server should be unreachable!") updated_videos_url = self.reverse("update_videos") response = self.client.get(updated_videos_url, follow=True) redirect_chain = response.redirect_chain # Will be the empty list if there are no redirects self.assertFalse(redirect_chain, "Should not be redirected when the central server is not reachable! " "Redirect chain: {0}".format(redirect_chain))
def easy_admin(request): context = { "wiki_url" : settings.CENTRAL_WIKI_URL, "central_server_host" : settings.CENTRAL_SERVER_HOST, "in_a_zone": Device.get_own_device().get_zone() is not None, } return context
def easy_admin(request): context = { "wiki_url" : settings.CENTRAL_WIKI_URL, "central_server_host" : settings.CENTRAL_SERVER_HOST, "in_a_zone": Device.get_own_device().get_zone() is not None, } return context
def test_facility_user_save(self): # only perform test if we are ourselves a trusted (i.e. central server) device if Device.get_own_device().is_trusted(): user = FacilityUser(username="******", facility=self.facility) user.set_password("blahblah") user.save() assert user.zone_fallback is not None, "Centrally created FacilityUser was not assigned a zone."
def test_distributed_server_cannot_overwrite_central_device(self): with self.get_distributed_server() as d: self.register(d) sync_results = d.sync() results = d.runcode( "from securesync.models import Device; d = Device(id='%s', name='Hahaha'); d.save()" % Device.get_own_device().id) with self.assertRaises(Exception): sync_results = d.sync() self.assertNotEqual( Device.get_own_device().name, "Hahaha", "Eek! Distributed server overwrote central server Device.")
def create_default_archive_filename(options=dict()): """Generate a filename for the archive""" out_file = "kalite" out_file += "-%s" % options['platform'] if options['platform'] else "" out_file += "-%s" % options['locale'] if options['locale'] else "" out_file += "-%s" % options['server_type'] if options['server_type'] else "" out_file += "-v%s.zip" % Device.get_own_device().get_version() return out_file
def easy_admin(request): context = { "wiki_url" : settings.CENTRAL_WIKI_URL, "central_server_host" : settings.CENTRAL_SERVER_HOST, "am_i_online": am_i_online(settings.CENTRAL_WIKI_URL, allow_redirects=False), "in_a_zone": Device.get_own_device().get_zone() is not None, } return context
def create_default_archive_filename(options=dict()): """Generate a filename for the archive""" out_file = "kalite" out_file += "-%s" % options['platform'] if options['platform'] else "" out_file += "-%s" % options['locale'] if options['locale'] else "" out_file += "-%s" % options['server_type'] if options['server_type'] else "" out_file += "-v%s.zip" % Device.get_own_device().get_version() return out_file
def save(self, *args, **kwargs): if not kwargs.get("imported", False): self.full_clean() already_complete = self.complete self.complete = (self.points >= 750) if not already_complete and self.complete: self.completion_timestamp = datetime.now() self.completion_counter = Device.get_own_device().get_counter() super(VideoLog, self).save(*args, **kwargs)
def save(self, *args, **kwargs): if not kwargs.get("imported", False): self.full_clean() already_complete = self.complete self.complete = (self.points >= 100) if not already_complete and self.complete: self.completion_timestamp = datetime.now() self.completion_counter = Device.get_own_device().get_counter() super(VideoLog, self).save(*args, **kwargs)
def update_context(request): device = Device.get_own_device() zone = device.get_zone() try: repo = git.Repo(os.path.dirname(__file__)) software_version = repo.git.describe() is_git_repo = bool(repo) except git.exc.InvalidGitRepositoryError: is_git_repo = False software_version = None context = { "registered": Device.get_own_device().is_registered(), "zone_id": zone.id if zone else None, "device_id": device.id, "is_git_repo": str(is_git_repo).lower(), # lower to make it look like JS syntax "git_software_version": software_version, } return context
def easy_admin(request): context = { "wiki_url" : settings.CENTRAL_WIKI_URL, "central_server_host" : settings.CENTRAL_SERVER_HOST, "in_a_zone": Device.get_own_device().get_zone() is not None, "clock_set": settings.ENABLE_CLOCK_SET, "ips": get_ip_addresses(include_loopback=False), "port": request.META.get("SERVER_PORT") or settings.user_facing_port(), } return context
def handle(self, *args, **options): if DeviceMetadata.objects.filter(is_own_device=True).count() > 0: raise CommandError( "Error: This device has already been initialized; aborting.\n") if len(args) >= 1 and args[0]: name = args[0] else: name = get_host_name() if len(args) >= 2 and args[1]: description = args[1] else: description = "" Device.initialize_own_device(name=name, description=description) self.stdout.write( "Device '%s'%s has been successfully initialized.\n" % (name, description and (" ('%s')" % description) or ""))
def device_redirect(request): """ Dummy view to generate a helpful dynamic redirect to interface with 'control_panel' app """ device = Device.get_own_device() zone = device.get_zone() if zone: return HttpResponseRedirect(reverse("device_management", kwargs={"zone_id": zone.pk, "device_id": device.pk})) else: raise Http404(_("This device is not on any zone."))
def wrapper_fn(request, *args, **kwargs): if kwargs.get("facility_id", None): # avoid using blank # Facility passed in directly facility = get_object_or_None(Facility, pk=kwargs["facility_id"]) elif "facility" in request.GET: # Facility from querystring facility = get_object_or_None(Facility, pk=request.GET["facility"]) if "set_default" in request.GET and request.is_admin and facility: Settings.set("default_facility", facility.id) elif settings.CENTRAL_SERVER: # following options are distributed-only facility = None elif "facility_user" in request.session: # Facility from currently logged-in facility user facility = request.session["facility_user"].facility elif request.session["facility_count"] == 1: # There's only one facility facility = Facility.objects.all()[0] elif request.session["facility_count"] > 0: if Settings.get("default_facility"): # There are multiple facilities--try to grab the default facility = get_object_or_None( Facility, pk=Settings.get("default_facility")) elif Facility.objects.filter( Q(signed_by__isnull=True) | Q(signed_by=Device.get_own_device())).count() == 1: # Default to a locally created facility (if there are multiple, and none are specified) facility = Facility.objects.filter( Q(signed_by__isnull=True) | Q(signed_by=Device.get_own_device()))[0] else: facility = None else: # There's nothing; don't bother even hitting the DB facility = None return handler(request, *args, facility=facility, **kwargs)
def do_fake_registration(): """ Register the device, in case some feature being tested depends on it. Will be undone by the database teardown. """ # Create a Zone and DeviceZone to fool the Device into thinking it's registered zone = Zone(name="The Danger Zone", description="Welcome to it.") zone.save() device = Device.get_own_device() device_zone = DeviceZone(device=device, zone=zone) device_zone.save()
def update_context(request): device = Device.get_own_device() zone = device.get_zone() context = { "is_registered": device.is_registered(), "zone_id": zone.id if zone else None, "device_id": device.id, } return context
def easy_admin(request): context = { "wiki_url" : settings.CENTRAL_WIKI_URL, "central_server_host" : settings.CENTRAL_SERVER_HOST, "in_a_zone": Device.get_own_device().get_zone() is not None, "clock_set": settings.ENABLE_CLOCK_SET, "ips": get_ip_addresses(include_loopback=False), "port": request.META.get("SERVER_PORT") or settings.user_facing_port(), } return context
def step_impl(context): url = reverse("coach_reports", kwargs={"zone_id": getattr(Device.get_own_device().get_zone(), "id", "None")}) context.browser.get(build_url(context, url)) # TODO(benjaoming) : This takes an awful lot of time to load the first # time it's built because of /api/coachreports/summary/?facility_id # being super slow try: find_id_with_wait(context, "summary_mainview", wait_time=60) except TimeoutException: raise RuntimeError("Could not find element, this was the DOM:\n\n" + context.browser.execute_script("return document.documentElement.outerHTML"))
def do_fake_registration(): """ Register the device, in case some feature being tested depends on it. Will be undone by the database teardown. """ # Create a Zone and DeviceZone to fool the Device into thinking it's registered zone = Zone(name="The Danger Zone", description="Welcome to it.") zone.save() device = Device.get_own_device() device_zone = DeviceZone(device=device, zone=zone) device_zone.save()
def register_public_key_client(request): own_device = Device.get_own_device() if own_device.is_registered(): initialize_registration() if request.next: return HttpResponseRedirect(request.next) else: return {"already_registered": True} client = RegistrationClient() if client.test_connection() != "success": return {"no_internet": True} reg_response = client.register() reg_status = reg_response.get("code") if reg_status == "registered": initialize_registration() if request.next: return HttpResponseRedirect(request.next) else: return {"newly_registered": True} elif reg_status == "device_already_registered": initialize_registration() if request.next: return HttpResponseRedirect(request.next) else: return {"already_registered": True} elif reg_status == "public_key_unregistered": # Callback url used to redirect to the distributed server url # after successful registration on the central server. base_registration_url = client.path_to_url( set_query_params(reverse("register_public_key"), { "device_key": urllib.quote(own_device.public_key), })) return { "unregistered": True, "auto_registration_url": set_query_params(base_registration_url, {"auto": True}), "classic_registration_url": set_query_params( base_registration_url, { "callback_url": request.build_absolute_uri(reverse("register_public_key")), }), "central_login_url": "%s://%s/accounts/login" % (settings.SECURESYNC_PROTOCOL, settings.CENTRAL_SERVER_HOST), } error_msg = reg_response.get("error", "") if error_msg: return {"error_msg": error_msg} return HttpResponse(_("Registration status: ") + reg_status)
def add_log_to_summary(cls, user_log, device=None): """Adds total_time to the appropriate user/device/activity's summary log.""" assert user_log.end_datetime, "all log items must have an end_datetime to be saved here." assert user_log.total_seconds >= 0, "all log items must have a non-negative total_seconds to be saved here." device = device or Device.get_own_device( ) # Must be done here, or install fails # Check for an existing object log_summary = cls.objects.filter( device=device, user=user_log.user, activity_type=user_log.activity_type, start_datetime__lt=user_log.end_datetime, end_datetime__gte=user_log.end_datetime, ) # Delete overlapping summaries because we know no better action. They # are not supposed to be there, but there are no database constraints. # # Added behavior in 0.16.7: We accumulate their counts and total_seconds # into the remaining log. overlapping_counts = 0 overlapping_total_seconds = 0 if log_summary.count() > 1: for log in log_summary[1:]: overlapping_counts += log.count overlapping_total_seconds += log.total_seconds log.soft_delete() # Get (or create) the log item log_summary = log_summary[0] if log_summary.count() else cls( device=device, user=user_log.user, activity_type=user_log.activity_type, language=user_log.language, start_datetime=cls.get_period_start_datetime( user_log.start_datetime, settings.USER_LOG_SUMMARY_FREQUENCY), end_datetime=cls.get_period_end_datetime( user_log.end_datetime, settings.USER_LOG_SUMMARY_FREQUENCY), total_seconds=0, count=0, ) logging.debug( "Adding %d seconds for %s/%s/%d/%s, period %s to %s" % (user_log.total_seconds, device.name, user_log.user.username, user_log.activity_type, user_log.language, log_summary.start_datetime, log_summary.end_datetime)) # Add the latest info log_summary.total_seconds += overlapping_total_seconds + user_log.total_seconds log_summary.count += overlapping_counts + 1 log_summary.last_activity_datetime = user_log.last_active_datetime log_summary.save()
def create_signature_file(inner_zip_file): signature_file = os.path.splitext(inner_zip_file)[0] + "_signature.txt" logging.debug("Generating signature; saving to %s" % signature_file) if settings.DEBUG or not os.path.exists(signature_file): # always regenerate in debug mode key = Device.get_own_device().get_key() chunk_size = int(2E5) #200kb chunks signature = key.sign_large_file(inner_zip_file, chunk_size=chunk_size) with open(signature_file, "w") as fp: fp.write("%d\n" % chunk_size) fp.write(signature) return signature_file
def test_not_redirected_when_offline(self): self.assertFalse(Device.get_own_device().is_registered(), "The device should be unregistered!") self.assertFalse(am_i_online(url=settings.CENTRAL_SERVER_URL), "Central server should be unreachable!") updated_videos_url = self.reverse("update_videos") response = self.client.get(updated_videos_url, follow=True) redirect_chain = response.redirect_chain # Will be the empty list if there are no redirects self.assertFalse( redirect_chain, "Should not be redirected when the central server is not reachable! " "Redirect chain: {0}".format(redirect_chain))
def check_setup_status_wrapper_fn(request, *args, **kwargs): if "registered" not in request.session: logging.error("Key 'registered' not defined in session, but should be by now.") if request.is_admin: # TODO(bcipolli): move this to the client side? if not request.session.get("registered", True) and BaseClient().test_connection() == "success": # Being able to register is more rare, so prioritize. messages.warning(request, mark_safe(_("Please <a href='%s'>follow the directions to register your device</a>, so that it can synchronize with the central server.") % reverse("register_public_key"))) elif not request.session["facility_exists"]: zone_id = (Zone.objects.all() and Zone.objects.all()[0].id) or "None" messages.warning(request, mark_safe(_("Please <a href='%s'>create a facility</a> now. Users will not be able to sign up for accounts until you have made a facility.") % reverse("add_facility", kwargs={"zone_id": zone_id}))) elif not request.is_logged_in: if not request.session.get("registered", True) and BaseClient().test_connection() == "success": # Being able to register is more rare, so prioritize. redirect_url = reverse("register_public_key") elif not request.session["facility_exists"]: zone = Device.get_own_device().get_zone() zone_id = "None" if not zone else zone.id redirect_url = reverse("add_facility", kwargs={"zone_id": zone_id}) else: redirect_url = None if redirect_url: messages.warning(request, mark_safe( _("Please login with the admin account you created, then create your facility and register this device to complete the setup."))) if get_installed_language_packs()['en']['language_pack_version'] == 0: alert_msg = "<p>{}</p>".format(_( "Dear Admin, you need to download a full version of the English " "language pack for KA Lite to work." )) + "<p><a href=\"{url}\">{msg}</a></p>".format( url=reverse("update_languages"), msg=_("Go to Language Management") ) alert_msg = mark_safe(alert_msg) messages.warning( request, alert_msg ) else: outdated_langpack_list = list(outdated_langpacks()) if outdated_langpack_list: pretty_lang_names = " --- ".join(lang.get("name", "") for lang in outdated_langpack_list) messages.warning( request, _( "Dear Admin, please log in and upgrade the following " "languages as soon as possible: {}" ).format(pretty_lang_names) ) return handler(request, *args, **kwargs)
def get_diagnostics(): # Key, value store for diagnostics # Not using OrderedDict because of python 2.6 diagnostics = [] diag = lambda x, y: diagnostics.append((x, y)) diag("KA Lite version", kalite.__version__) diag("python", sys.version) diag("platform", platform.platform()) status_code, urls = get_urls() for addr in urls: diag("server address", addr) for addr in get_urls_proxy(): diag("server proxy", addr) diag("server status", status.codes[status_code]) settings_imported = True # Diagnostics from settings try: from django.conf import settings from django.template.defaultfilters import filesizeformat except: settings_imported = False diag("Settings failure", traceback.format_exc()) if settings_imported: diag("installed in", os.path.dirname(kalite.__file__)) diag("content root", settings.CONTENT_ROOT) diag("content size", filesizeformat(get_size(settings.CONTENT_ROOT))) diag("user database", settings.DATABASES['default']['NAME']) try: from securesync.models import Device device = Device.get_own_device() sync_sessions = device.client_sessions.all() zone = device.get_zone() diag("device name", str(device.name)) diag("device ID", str(device.id)) diag("device registered", str(device.is_registered())) diag( "synced", str( sync_sessions.latest('timestamp'). timestamp if sync_sessions.exists() else "Never")) diag("sync result", ("OK" if sync_sessions.latest('timestamp').errors == 0 else "Error") if sync_sessions.exists() else "-") diag("zone ID", str(zone.id) if zone else "Unset") except: diag("Device failure", traceback.format_exc()) return diagnostics
def device_management(request, device_id, zone_id=None, per_page=None, cur_page=None): context = control_panel_context(request, zone_id=zone_id, device_id=device_id) #Get pagination details cur_page = cur_page or request.REQUEST.get("cur_page", "1") per_page = per_page or request.REQUEST.get("per_page", "10") # Retrieve sync sessions all_sessions = SyncSession.objects.filter(client_device=context["device"]) total_sessions = all_sessions.count() shown_sessions = list( all_sessions.order_by("-timestamp").values("timestamp", "ip", "models_uploaded", "models_downloaded", "errors")) session_pages, page_urls = paginate_data(request, shown_sessions, page=cur_page, per_page=per_page) sync_job = get_object_or_None(Job, command="syncmodels") context.update({ "session_pages": session_pages, "page_urls": page_urls, "total_sessions": total_sessions, "device_version": total_sessions and all_sessions[0].client_version or None, "device_os": total_sessions and all_sessions[0].client_os or None, "is_own_device": not settings.CENTRAL_SERVER and device_id == Device.get_own_device().id, "sync_job": sync_job, }) # If local (and, for security purposes, a distributed server), get device metadata if context["is_own_device"]: context.update(local_install_context(request)) return context
def step_impl(context): url = reverse("coach_reports", kwargs={ "zone_id": getattr(Device.get_own_device().get_zone(), "id", "None") }) context.browser.get(build_url(context, url)) try: find_id_with_wait(context, "summary_mainview") except TimeoutException: raise RuntimeError("Could not find element, this was the DOM:\n\n" + context.browser.execute_script( "return document.documentElement.outerHTML"))
def save(self, *args, **kwargs): if not kwargs.get("imported", False): self.full_clean() if self.attempts > 20 and not self.complete: self.struggling = True already_complete = self.complete self.complete = (self.streak_progress >= 100) if not already_complete and self.complete: self.struggling = False self.completion_timestamp = datetime.now() self.completion_counter = Device.get_own_device().get_counter() self.attempts_before_completion = self.attempts super(ExerciseLog, self).save(*args, **kwargs)
def create_json_file(include_data): central_server = Device.get_central_server() if not zone_id: models = [central_server] if central_server else [] else: # Get a chain of trust to the zone owner. # Because we're on the central server, this will # simply be the central server, but in the future # this would return an actual chain. logging.debug("Generating a zone invitation...") zone = Zone.objects.get(id=zone_id) chain = ChainOfTrust(zone=zone) assert chain.validate() new_invitation = ZoneInvitation.generate( zone=zone, invited_by=Device.get_own_device()) new_invitation.save( ) # keep a record of the invitation, for future revocation. Also, signs the thing # This ordering of objects is a bit be hokey, but OK--invitation usually must be # inserted before devicezones--but because it's not pointing to any devices, # it's OK to be at the end. # Note that the central server will always be at the front of the chain of trust, # so no need to explicitly include. models = chain.objects() + [new_invitation] # if include_data: logging.debug("Serializing entire dataset...") devices = Device.objects.by_zone(zone) devicezones = DeviceZone.objects.filter(zone=zone) models += list(devices) + list(devicezones) models += engine.get_models( zone=zone, limit=None) # get all models on this zone models_file = tempfile.mkstemp()[1] with open(models_file, "w") as fp: fp.write(engine.serialize(models)) return models_file
def easy_admin(request): context = { "wiki_url": settings.CENTRAL_WIKI_URL, "central_server_host": settings.CENTRAL_SERVER_HOST, "am_i_online": am_i_online(settings.CENTRAL_WIKI_URL, allow_redirects=False), "in_a_zone": Device.get_own_device().get_zone() is not None, } return context
def generate_status(self, request, **kwargs): #Build a list of messages to pass to the user. # Iterating over the messages removes them from the # session storage, thus they only appear once. message_dicts = get_messages_for_api_calls(request) # Default data data = { "is_logged_in": request.is_logged_in, "registered": request.session.get("registered", True), "is_admin": request.is_admin, "is_django_user": request.is_django_user, "points": 0, "current_language": request.session.get(settings.LANGUAGE_COOKIE_NAME), "messages": message_dicts, "status_timestamp": datetime.datetime.now(tzlocal()), "version": version.VERSION, "facilities": facility_list(), "simplified_login": settings.SIMPLIFIED_LOGIN, "docs_exist": getattr(settings, "DOCS_EXIST", False), "zone_id": getattr(Device.get_own_device().get_zone(), "id", "None"), "has_superuser": User.objects.filter(is_superuser=True).exists(), } # Override properties using facility data if "facility_user" in request.session: # Facility user user = request.session["facility_user"] data["is_logged_in"] = True data["username"] = user.get_name() # TODO-BLOCKER(jamalex): re-enable this conditional once tastypie endpoints invalidate cached session value # if "points" not in request.session: request.session["points"] = compute_total_points(user) data["points"] = request.session["points"] if request.session[ "points"] else 0 data["user_id"] = user.id data["user_uri"] = reverse("api_dispatch_detail", kwargs={ "resource_name": "user", "pk": user.id }) data["facility_id"] = user.facility.id # Override data using django data if request.user.is_authenticated(): # Django user data["is_logged_in"] = True data["username"] = request.user.username return data
def confirm_or_generate_zone(invitation=None, device_zone=None): invitation = invitation or get_object_or_None( ZoneInvitation, used_by=Device.get_own_device()) device_zone = device_zone or get_object_or_None( DeviceZone, device=Device.get_own_device()) if invitation: sys.stdout.write( "Confirmed existing sharing network %s, using invitation %s.\n" % (invitation.zone, invitation)) elif device_zone: sys.stdout.write( "Confirmed existing sharing network %s, using device_zone %s.\n" % (device_zone.zone, device_zone)) else: # Sorry dude, you weren't invited to the party. You'll have to have your own! # Generate a zone (for stand-alone machines) call_command("generate_zone") sys.stdout.write( "Successfully generated a sharing network, and joined!.\n") initialize_registration() # would try to sync
def create_signature_file(inner_zip_file): signature_file = os.path.splitext( inner_zip_file)[0] + "_signature.txt" logging.debug("Generating signature; saving to %s" % signature_file) if settings.DEBUG or not os.path.exists( signature_file): # always regenerate in debug mode key = Device.get_own_device().get_key() chunk_size = int(2E5) #200kb chunks signature = key.sign_large_file(inner_zip_file, chunk_size=chunk_size) with open(signature_file, "w") as fp: fp.write("%d\n" % chunk_size) fp.write(signature) return signature_file
def add_log_to_summary(cls, user_log, device=None): """Adds total_time to the appropriate user/device/activity's summary log.""" assert user_log.end_datetime, "all log items must have an end_datetime to be saved here." assert user_log.total_seconds >= 0, "all log items must have a non-negative total_seconds to be saved here." device = device or Device.get_own_device( ) # Must be done here, or install fails # Check for an existing object log_summary = cls.objects.filter( device=device, user=user_log.user, activity_type=user_log.activity_type, start_datetime__lte=user_log.end_datetime, end_datetime__gte=user_log.end_datetime, ) # TODO(anuragkanungo): Figure out and fix the issue for duplicate entries and enable assert check with if condition removed. #assert log_summary.count() <= 1, "There should never be multiple summaries in the same time period/device/user/type combo" if log_summary.count() > 1: for log in log_summary[1:]: log.soft_delete() # Get (or create) the log item log_summary = log_summary[0] if log_summary.count() else cls( device=device, user=user_log.user, activity_type=user_log.activity_type, language=user_log.language, start_datetime=cls.get_period_start_datetime( user_log.end_datetime, settings.USER_LOG_SUMMARY_FREQUENCY), end_datetime=cls.get_period_end_datetime( user_log.end_datetime, settings.USER_LOG_SUMMARY_FREQUENCY), total_seconds=0, count=0, ) logging.debug( "Adding %d seconds for %s/%s/%d/%s, period %s to %s" % (user_log.total_seconds, device.name, user_log.user.username, user_log.activity_type, user_log.language, log_summary.start_datetime, log_summary.end_datetime)) # Add the latest info log_summary.total_seconds += user_log.total_seconds log_summary.count += 1 log_summary.last_activity_datetime = user_log.last_active_datetime log_summary.save()
def step_impl(context): url = reverse("coach_reports", kwargs={ "zone_id": getattr(Device.get_own_device().get_zone(), "id", "None") }) context.browser.get(build_url(context, url)) # TODO(benjaoming) : This takes an awful lot of time to load the first # time it's built because of /api/coachreports/summary/?facility_id # being super slow try: find_id_with_wait(context, "summary_mainview", wait_time=60) except TimeoutException: raise RuntimeError("Could not find element, this was the DOM:\n\n" + context.browser.execute_script( "return document.documentElement.outerHTML"))
def test_cannot_delete_full_zone(self): # Save zone info, but without adding self.devicezone = DeviceZone(device=Device.get_own_device(), zone=self.zone) self.devicezone.save() # Check on the org management page self.browser_login_user(self.USER_EMAIL, self.USER_PASSWORD) with self.assertRaises(NoSuchElementException): self.assertEqual(self.browser.find_element_by_css_selector(".zone-delete-link"), None, "Make sure 'delete' link is gone.") # Follow the link, and confirm on the zone management page. zone_url = self.browser.find_element_by_css_selector(".zone-manage-link").get_attribute("href") self.browse_to(zone_url) self.assertEqual(self.browser.current_url, zone_url, "Expect link to go to zone management page") with self.assertRaises(NoSuchElementException): self.assertEqual(self.browser.find_element_by_css_selector(".zone-delete-link"), None, "Make sure 'delete' link is gone.")
def load_data_for_offline_install(in_file): """ Receives a serialized file for import. Import the file--nothing more! File should contain: * Central server object and, optionally * Zone object * Device and DeviceZone / ZoneInvitation objects (chain of trust) Essentially duplicates code from securesync.device.api_client:RegistrationClient """ assert os.path.exists(in_file), "in_file must exist." with open(in_file, "r") as fp: models = engine.deserialize( fp.read()) # all must be in a consistent version # First object should be the central server. try: central_server = models.next().object except Exception as e: logging.debug("Exception loading central server object: %s" % e) return logging.debug("Saving object %s" % central_server) assert isinstance(central_server, Device) central_server.save(imported=True, is_trusted=True) # Everything else, import as is. invitation = None for model in models: try: logging.debug("Saving object %s" % model.object) model.object.save(imported=True) if isinstance(model.object, ZoneInvitation): # Zone info existed in the data blob we received. Use it to join the zone! invitation = model.object if invitation.used_by is None: invitation.claim(used_by=Device.get_own_device()) except ValidationError as e: # Happens when there's duplication of data, sometimes. # Shouldn't happen, but keeping this here to make things # a bit more robust. logging.error("Failed to import model %s" % model) return invitation