def create_nonce(request): """ One of the few views that does not require a valid nonce. """ # Query strings: u = request.GET.get('u', None) # create nonce for a user if not u: raise Http404('Bad call') # FIXME: instead of returning 403, we should hand out a bogus nonce instead # >:-) But then we should do that below too.. # and what to use for publickey.key_type? user = get_object_or_403(User, username=u) # Prune old nonces.. might as well do that here. old = datetime.now() - timedelta(seconds=Nonce.MAX_AGE) Nonce.objects.filter(created__lt=old).delete() # Check whether this user has created enough nonces already. max_queued_nonces = 10 if Nonce.objects.filter(user=user).count() > max_queued_nonces: raise PermissionDenied() # Ok, create a new one. nonce = Nonce.objects.create(user=user) # Response: return EncryptedResponse(data=nonce.encrypted, enctype=user.publickey.key_type())
def update_properties(request, object_identifier): if len(request.GET) != 0: raise NotImplementedError('Unexpected GET args', request.GET) if len(request.POST) != 1: # only the nonce_b64 should be here raise NotImplementedError('Unexpected POST args', request.POST) # Decode object_identifier: object_identifier = urlunquote(object_identifier) # Check authorization and existence: if request.user.has_perm('pstore.view_any_object'): obj = get_object_or_404(Object, identifier=object_identifier) else: obj = get_object_or_403(Object, identifier=object_identifier) try: ObjectPerm.objects.get(object=obj, user=request.user, can_write=True) except ObjectPerm.DoesNotExist: raise PermissionDenied('No permissions for user', request.user) # Fetch users and do a few basic checks on them. Below, during the Property # create loop, we'll do some more checks. usernames = [] for property_name in request.FILES.keys(): for file in request.FILES.getlist(property_name): username = file.name usernames.append(username) usernames = set(usernames) users = dict([(i.username, i) for i in User.objects.filter(username__in=usernames)]) if len(users) != len(usernames): raise Exception('FIXME-EXCEPTION: user count invalid') # XXX: assert that user is in usernames! # TODO: do more assertions on the users here.. # TODO: add to audit log the attempted move of userset X to userset Y # Fetch files and re-set properties. Here we won't accept transparent # switches of public to shared properties. for property_name in request.FILES.keys(): # TODO: ensure that we have a file for every user for every key # TODO: check public/shared properties for swapperony Property.objects.filter(object=obj, name=property_name).delete() for file in request.FILES.getlist(property_name): # We only add new shared (encrypted) properties, we don't need to # touch the public ones. user = users[file.name] create_property(object=obj, property=property_name, file=file, user=user) # Update permissions. For now, we'll only use the can_write, since we don't # have a means of communicating that state yet. # TODO: (a) audit log here? # TODO: (b) don't delete permitted users and add a created column to # ObjectPerm? # TODO: (c) double check that there are no user-properties for disallowed # users left? currently_allowed = set([i.user for i in (ObjectPerm.objects.filter(object=obj) .select_related('user'))]) del currently_allowed # fixme.. use this # XXX: take currently_allowed, remove now-allowed: # delete the leftovers # add the not-in-currently_allowed ObjectPerm.objects.filter(object=obj).delete() for user in users.values(): ObjectPerm.objects.create(object=obj, user=user, can_write=True) return VoidResponse()
def get_object(request, object_identifier): """ Get a single object. You get verbose info. Note that if you want info about the properties for a different user, you'll need to redo the query with that user id. """ # Decode object_identifier. object_identifier = urlunquote(object_identifier) # Check authorization and existence: u = request.GET.get('u', None) if request.user.has_perm('object.view_any_object'): obj = get_object_or_404(Object, identifier=object_identifier) elif u != request.user.username: raise PermissionDenied() else: obj = get_object_or_403(Object, identifier=object_identifier) # Check if the user is allowed to view this. try: obj.allowed.get(user=request.user) except ObjectDoesNotExist: if not request.user.has_perm('object.view_any_object'): raise PermissionDenied() # Get lots of info for this object. result = {} # Get the allowed users. allowed = obj.allowed.select_related('user') result['users'] = dict([(i.user.username, {'can_write': i.can_write}) for i in allowed]) # Get a list of properties. propqs = Property.objects.filter(object=obj) if u: propqs = propqs.filter(Q(user__username=u) | Q(user=None)) else: propqs = propqs.filter(user=None) # BEWARE: For the SQLite3 backend, the LENGTH() is incorrect! propqs = propqs.extra(select={'size': 'LENGTH(value)'}) properties = set(propqs.values_list('id', 'name', 'type', 'size', 'user')) # Get the values, but only for small properties. small_property_ids = [i[0] for i in properties if i[3] <= 2048] # https://code.djangoproject.com/ticket/9619 # We cannot do values_list when using SQLite3. # #property_values = dict(Property.objects # # .filter(id__in=small_property_ids) # # .values_list('id', 'value')) property_qs = Property.objects.filter(id__in=small_property_ids) property_values = dict((i.id, i.value) for i in property_qs.only('id', 'value')) # Put properties in the results dictionary. result['properties'] = {} enctype, encuid = None, None # cache encryption type for property_id, name, type, size, user in properties: assert type in (Property.TYPE_PUBLIC, Property.TYPE_SHARED) # Cache the enctype so we don't have to look it up for every property. if user: if not enctype: encuid = user enctype = PublicKey.objects.get(user__id=user).key_type() assert enctype else: assert encuid == user, '%r == %r' % (encuid, user) # These are always returned. info = {'data': None, 'size': size, 'enctype': ('none', enctype)[bool(user)]} # If data is small enough, it is set too. if property_id in property_values: info['data'] = b64encode(property_values[property_id]) result['properties'][name] = info # If a superuser called and wants to know what properties there are, add # those. if not u: # Fetch property names for the properties. for name, type in (Property.objects.filter(object=obj) .exclude(user=None) .values_list('name', 'type').distinct()): assert type == Property.TYPE_SHARED # These are always returned, but this time we have no useful info # for the caller. info = {'data': None, 'size': None, 'enctype': None} result['properties'][name] = info return JsonResponse(request, result)