SecurityMapper.java

/*
 * Copyright (C) 2014, 2015, 2022 XStream Committers.
 * All rights reserved.
 *
 * Created on 08. January 2014 by Joerg Schaible
 */
package com.thoughtworks.xstream.mapper;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.thoughtworks.xstream.security.AnyTypePermission;
import com.thoughtworks.xstream.security.ForbiddenClassException;
import com.thoughtworks.xstream.security.NoTypePermission;
import com.thoughtworks.xstream.security.TypePermission;


/**
 * A Mapper implementation injecting a security layer based on permission rules for any type required in the
 * unmarshalling process.
 * 
 * @author Jörg Schaible
 * @since 1.4.7
 */
public class SecurityMapper extends MapperWrapper {

    private final List<TypePermission> permissions;

    /**
     * Construct a SecurityMapper.
     * 
     * @param wrapped the mapper chain
     * @since 1.4.7
     */
    public SecurityMapper(final Mapper wrapped) {
        this(wrapped, (TypePermission[])null);
    }

    /**
     * Construct a SecurityMapper.
     * 
     * @param wrapped the mapper chain
     * @param permissions the predefined permissions
     * @since 1.4.7
     */
    public SecurityMapper(final Mapper wrapped, final TypePermission... permissions) {
        super(wrapped);
        this.permissions = permissions == null //
            ? new ArrayList<>()
            : new ArrayList<>(Arrays.asList(permissions));
    }

    /**
     * Add a new permission.
     * <p>
     * Permissions are evaluated in the added sequence. An instance of {@link NoTypePermission} or
     * {@link AnyTypePermission} will implicitly wipe any existing permission.
     * </p>
     * 
     * @param permission the permission to add.
     * @since 1.4.7
     */
    public void addPermission(final TypePermission permission) {
        if (permission.equals(NoTypePermission.NONE) || permission.equals(AnyTypePermission.ANY)) {
            permissions.clear();
        }
        permissions.add(0, permission);
    }

    @Override
    public Class<?> realClass(final String elementName) {
        final Class<?> type = super.realClass(elementName);
        for (final TypePermission permission : permissions) {
            if (permission.allows(type)) {
                return type;
            }
        }
        throw new ForbiddenClassException(type);
    }
}