001/*
002 * Copyright (C) 2014 XStream Committers.
003 * All rights reserved.
004 *
005 * Created on 08. January 2014 by Joerg Schaible
006 */
007package com.thoughtworks.xstream.mapper;
008
009import java.util.ArrayList;
010import java.util.Arrays;
011import java.util.List;
012
013import com.thoughtworks.xstream.security.AnyTypePermission;
014import com.thoughtworks.xstream.security.ForbiddenClassException;
015import com.thoughtworks.xstream.security.NoTypePermission;
016import com.thoughtworks.xstream.security.TypePermission;
017
018
019/**
020 * A Mapper implementation injecting a security layer based on permission rules for any type required in the
021 * unmarshalling process.
022 * 
023 * @author Jörg Schaible
024 * @since 1.4.7
025 */
026public class SecurityMapper extends MapperWrapper {
027
028    private final List permissions;
029
030    /**
031     * Construct a SecurityMapper.
032     * 
033     * @param wrapped the mapper chain
034     * @since 1.4.7
035     */
036    public SecurityMapper(final Mapper wrapped) {
037        this(wrapped, (TypePermission[])null);
038    }
039
040    /**
041     * Construct a SecurityMapper.
042     * 
043     * @param wrapped the mapper chain
044     * @param permissions the predefined permissions
045     * @since 1.4.7
046     */
047    public SecurityMapper(final Mapper wrapped, final TypePermission[] permissions) {
048        super(wrapped);
049        this.permissions = permissions == null //
050            ? new ArrayList()
051            : new ArrayList(Arrays.asList(permissions));
052    }
053
054    /**
055     * Add a new permission.
056     * <p>
057     * Permissions are evaluated in the added sequence. An instance of {@link NoTypePermission} or
058     * {@link AnyTypePermission} will implicitly wipe any existing permission.
059     * </p>
060     * 
061     * @param permission the permission to add.
062     * @since 1.4.7
063     */
064    public void addPermission(final TypePermission permission) {
065        if (permission.equals(NoTypePermission.NONE) || permission.equals(AnyTypePermission.ANY))
066            permissions.clear();
067        permissions.add(0, permission);
068    }
069
070    public Class realClass(final String elementName) {
071        final Class type = super.realClass(elementName);
072        for (int i = 0; i < permissions.size(); ++i) {
073            final TypePermission permission = (TypePermission)permissions.get(i);
074            if (permission.allows(type))
075                return type;
076        }
077        throw new ForbiddenClassException(type);
078    }
079}