Example #1
0
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())
Example #2
0
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()
Example #3
0
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)