Beispiel #1
0
    def _post_save(self, instance, created, *args, **kwargs):
        Role_ = utils.get_current_apps().get_model('main', 'Role')
        ContentType_ = utils.get_current_apps().get_model('contenttypes', 'ContentType')
        ct_id = ContentType_.objects.get_for_model(instance).id

        Model = utils.get_current_apps().get_model('main', instance.__class__.__name__)
        latest_instance = Model.objects.get(pk=instance.pk)

        with batch_role_ancestor_rebuilding():
            # Create any missing role objects
            missing_roles = []
            for implicit_role_field in getattr(latest_instance.__class__, '__implicit_role_fields'):
                cur_role = getattr(latest_instance, implicit_role_field.name, None)
                if cur_role is None:
                    missing_roles.append(Role_(role_field=implicit_role_field.name, content_type_id=ct_id, object_id=latest_instance.id))

            if len(missing_roles) > 0:
                Role_.objects.bulk_create(missing_roles)
                updates = {}
                role_ids = []
                for role in Role_.objects.filter(content_type_id=ct_id, object_id=latest_instance.id):
                    setattr(latest_instance, role.role_field, role)
                    updates[role.role_field] = role.id
                    role_ids.append(role.id)
                type(latest_instance).objects.filter(pk=latest_instance.pk).update(**updates)
                Role.rebuild_role_ancestor_list(role_ids, [])

            update_role_parentage_for_instance(latest_instance)
            instance.refresh_from_db()
Beispiel #2
0
    def _post_delete(self, instance, *args, **kwargs):
        role_ids = []
        for implicit_role_field in getattr(instance.__class__, '__implicit_role_fields'):
            role_ids.append(getattr(instance, implicit_role_field.name + '_id'))

        Role_ = utils.get_current_apps().get_model('main', 'Role')
        child_ids = [x for x in Role_.parents.through.objects.filter(to_role_id__in=role_ids).distinct().values_list('from_role_id', flat=True)]
        Role_.objects.filter(id__in=role_ids).delete()
        Role.rebuild_role_ancestor_list([], child_ids)
Beispiel #3
0
def rebuild_role_hierarchy(apps, schema_editor):
    logger.info('Computing role roots..')
    start = time()
    roots = Role.objects \
                .all() \
                .values_list('id', flat=True)
    stop = time()
    logger.info('Found %d roots in %f seconds, rebuilding ancestry map' %
                (len(roots), stop - start))
    start = time()
    Role.rebuild_role_ancestor_list(roots, [])
    stop = time()
    logger.info('Rebuild completed in %f seconds' % (stop - start))
    logger.info('Done.')
Beispiel #4
0
def rebuild_role_hierarchy(apps, schema_editor):
    """
    This should be called in any migration when ownerships are changed.
    Ex. I remove a user from the admin_role of a credential.
    Ancestors are cached from parents for performance, this re-computes ancestors.
    """
    logger.info('Computing role roots..')
    start = time()
    roots = Role.objects.all().values_list('id', flat=True)
    stop = time()
    logger.info('Found %d roots in %f seconds, rebuilding ancestry map' % (len(roots), stop - start))
    start = time()
    Role.rebuild_role_ancestor_list(roots, [])
    stop = time()
    logger.info('Rebuild ancestors completed in %f seconds' % (stop - start))
    logger.info('Done.')
Beispiel #5
0
    def _post_save(self, instance, created, *args, **kwargs):
        Role_ = utils.get_current_apps().get_model('main', 'Role')
        ContentType_ = utils.get_current_apps().get_model(
            'contenttypes', 'ContentType')
        ct_id = ContentType_.objects.get_for_model(instance).id
        with batch_role_ancestor_rebuilding():
            # Create any missing role objects
            missing_roles = []
            for implicit_role_field in getattr(instance.__class__,
                                               '__implicit_role_fields'):
                cur_role = getattr(instance, implicit_role_field.name, None)
                if cur_role is None:
                    missing_roles.append(
                        Role_(role_field=implicit_role_field.name,
                              content_type_id=ct_id,
                              object_id=instance.id))
            if len(missing_roles) > 0:
                Role_.objects.bulk_create(missing_roles)
                updates = {}
                role_ids = []
                for role in Role_.objects.filter(content_type_id=ct_id,
                                                 object_id=instance.id):
                    setattr(instance, role.role_field, role)
                    updates[role.role_field] = role.id
                    role_ids.append(role.id)
                type(instance).objects.filter(pk=instance.pk).update(**updates)
                Role.rebuild_role_ancestor_list(role_ids, [])

            # Update parentage if necessary
            for implicit_role_field in getattr(instance.__class__,
                                               '__implicit_role_fields'):
                cur_role = getattr(instance, implicit_role_field.name)
                original_parents = set(json.loads(cur_role.implicit_parents))
                new_parents = implicit_role_field._resolve_parent_roles(
                    instance)
                cur_role.parents.remove(*list(original_parents - new_parents))
                cur_role.parents.add(*list(new_parents - original_parents))
                new_parents_list = list(new_parents)
                new_parents_list.sort()
                new_parents_json = json.dumps(new_parents_list)
                if cur_role.implicit_parents != new_parents_json:
                    cur_role.implicit_parents = new_parents_json
                    cur_role.save()
Beispiel #6
0
def rebuild_role_parentage(apps, schema_editor, models=None):
    """
    This should be called in any migration when any parent_role entry
    is modified so that the cached parent fields will be updated. Ex:
        foo_role = ImplicitRoleField(
            parent_role=['bar_role']  # change to parent_role=['admin_role']
        )

    This is like rebuild_role_hierarchy, but that method updates ancestors,
    whereas this method updates parents.
    """
    start = time()
    seen_models = set()
    model_ct = 0
    noop_ct = 0
    ContentType = apps.get_model('contenttypes', "ContentType")
    additions = set()
    removals = set()

    role_qs = Role.objects
    if models:
        # update_role_parentage_for_instance is expensive
        # if the models have been downselected, ignore those which are not in the list
        ct_ids = list(
            ContentType.objects.filter(
                model__in=[name.lower()
                           for name in models]).values_list('id', flat=True))
        role_qs = role_qs.filter(content_type__in=ct_ids)

    for role in role_qs.iterator():
        if not role.object_id:
            continue
        model_tuple = (role.content_type_id, role.object_id)
        if model_tuple in seen_models:
            continue
        seen_models.add(model_tuple)

        # The GenericForeignKey does not work right in migrations
        # with the usage as role.content_object
        # so we do the lookup ourselves with current migration models
        ct = role.content_type
        app = ct.app_label
        ct_model = apps.get_model(app, ct.model)
        content_object = ct_model.objects.get(pk=role.object_id)

        parents_added, parents_removed = update_role_parentage_for_instance(
            content_object)
        additions.update(parents_added)
        removals.update(parents_removed)
        if parents_added:
            model_ct += 1
            logger.debug('Added to parents of roles {} of {}'.format(
                parents_added, content_object))
        if parents_removed:
            model_ct += 1
            logger.debug('Removed from parents of roles {} of {}'.format(
                parents_removed, content_object))
        else:
            noop_ct += 1

    logger.debug('No changes to role parents for {} resources'.format(noop_ct))
    logger.debug('Added parents to {} roles'.format(len(additions)))
    logger.debug('Removed parents from {} roles'.format(len(removals)))
    if model_ct:
        logger.info(
            'Updated implicit parents of {} resources'.format(model_ct))

    logger.info('Rebuild parentage completed in %f seconds' % (time() - start))

    # this is ran because the ordinary signals for
    # Role.parents.add and Role.parents.remove not called in migration
    Role.rebuild_role_ancestor_list(list(additions), list(removals))