/*_############################################################################
  _##
  _##  SNMP4J-Agent 3 - AgentPolicyProfile.java
  _##
  _##  Copyright (C) 2005-2026  Frank Fock (SNMP4J.org)
  _##
  _##  Licensed under the Apache License, Version 2.0 (the "License");
  _##  you may not use this file except in compliance with the License.
  _##  You may obtain a copy of the License at
  _##
  _##      http://www.apache.org/licenses/LICENSE-2.0
  _##
  _##  Unless required by applicable law or agreed to in writing, software
  _##  distributed under the License is distributed on an "AS IS" BASIS,
  _##  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  _##  See the License for the specific language governing permissions and
  _##  limitations under the License.
  _##
  _##########################################################################*/

package org.snmp4j.agent.audit;

import org.snmp4j.security.SecurityLevel;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.OID;

import java.io.File;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * Represents a policy profile that defines various access rules and restrictions
 * for an agent, including file access, network access, managed object access,
 * and associated security levels.
 * @author Frank Fock
 * @since 3.12.0
 */
public class AgentPolicyProfile implements java.io.Serializable{

    private static final long serialVersionUID = 1L;

    public static final File[] DEFAULT_ALLOWED_FILES =
            new File[] { new File("snmp4j-agent.cfg"), new File("snmp4j-agent.bc") };

    public static final AgentPolicyProfile DEFAULT =
            new AgentPolicyProfile(ManagedObjectAccess.UNRESTRICTED,
                    NetworkAccess.REMOTE_NETWORK_UNLIMITED,
                    FileAccess.WORKING_DIRECTORY_READWRITE,
                    SecurityLevel.noAuthNoPriv,
                    DEFAULT_ALLOWED_FILES, null, null, null);

    public static final AgentPolicyProfile LOCALHOST_ONLY =
            new AgentPolicyProfile(ManagedObjectAccess.UNRESTRICTED,
                    NetworkAccess.LOCAL_NETWORK, FileAccess.WORKING_DIRECTORY_READWRITE, SecurityLevel.noAuthNoPriv);

    public static final AgentPolicyProfile CONFIDENTIAL =
            new AgentPolicyProfile(ManagedObjectAccess.UNRESTRICTED,
                    NetworkAccess.REMOTE_NETWORK_LIMITED, FileAccess.WORKING_DIRECTORY_READWRITE,
                    SecurityLevel.authPriv);

    public File[] getAllowedFiles() {
        return allowedFiles;
    }

    public enum ManagedObjectAccess {
        READ,
        READ_WRITE,
        READ_WRITE_CREATE,
        UNRESTRICTED
    }

    public enum NetworkAccess {
        LOCAL_NETWORK,
        REMOTE_NETWORK_LIMITED,
        REMOTE_NETWORK_UNLIMITED
    }

    /**
     * The {@code FileAccess} enumeration defines the levels of file access permissions
     * that can be assigned to an entity. It specifies the scope of file system
     * interactions allowed, ranging from no access to full access.
     *
     * <ul>
     *   <li>{@code NO_ACCESS}: Indicates that no file access is permitted.</li>
     *   <li>{@code WORKING_DIRECTORY_READONLY}: Grants read-only access to files.</li>
     *   <li>{@code WORKING_DIRECTORY_READWRITE}: Restricts read and write access to the working directory exclusively.</li>
     *   <li>{@code LIMITED_DIRECTORY_ACCESS}: Allows read-write access to a specified set of directories.</li>
     *   <li>{@code ALL_DIRECTORY_ACCESS}: Grants unrestricted access to all directories.</li>
     * </ul>
     *
     * This enumeration is intended to control and enforce file access policies
     * within a system to ensure data integrity and security.
     */
    public enum FileAccess {
        NO_ACCESS,
        WORKING_DIRECTORY_READONLY,
        WORKING_DIRECTORY_READWRITE,
        LIMITED_DIRECTORY_ACCESS,
        ALL_DIRECTORY_ACCESS
    }

    private final ManagedObjectAccess managedObjectAccess;
    private final NetworkAccess networkAccess;
    private final FileAccess fileAccess;
    private final SecurityLevel minSecurityLevel;

    private File[] allowedFiles;
    private File[] allowedDirectories;

    private Set<Address> allowedAddresses;
    private Map<OID, ManagedObjectAccess> allowedObjects;

    public AgentPolicyProfile(ManagedObjectAccess managedObjectAccess,
                              NetworkAccess networkAccess,
                              FileAccess fileAccess,
                              SecurityLevel minSecurityLevel) {
        this.managedObjectAccess = managedObjectAccess;
        this.networkAccess = networkAccess;
        this.fileAccess = fileAccess;
        this.minSecurityLevel = minSecurityLevel;
    }

    public AgentPolicyProfile(ManagedObjectAccess managedObjectAccess,
                              NetworkAccess networkAccess,
                              FileAccess fileAccess,
                              SecurityLevel minSecurityLevel,
                              File[] allowedFiles,
                              File[] allowedDirectories,
                              Set<Address> allowedAddresses,
                              Map<OID, ManagedObjectAccess> allowedObjects) {
        this(managedObjectAccess, networkAccess, fileAccess, minSecurityLevel);
        this.allowedFiles = allowedFiles;
        this.allowedDirectories = allowedDirectories;
        this.allowedAddresses = allowedAddresses;
        this.allowedObjects = allowedObjects;
    }

    /**
     * Constructs a copy of the specified {@code AgentPolicyProfile}, adding the specified resource restrictions.
     *
     * @param other
     *        The existing {@code AgentPolicyProfile} to copy the base configuration from.
     * @param allowedFiles
     *        An array of {@code File} objects representing the files that are permissible for access in addition
     *        to the base configuration's allowed files.
     * @param allowedDirectories
     *        An array of {@code File} objects representing the directories that are permissible for access in addition
     *        to the base configuration's allowed files.
     * @param allowedAddresses
     *        A {@code Set} of {@code Address} objects representing the network addresses that are permissible
     *        for communication in addition to the base configuration's allowed files.
     * @param allowedObjects
     *        A {@code Map} containing {@code OID} keys and {@code ManagedObjectAccess} values, representing
     *        the objects that are permissible for management access in addition to the base configuration's
     *        allowed files.
     */
    public AgentPolicyProfile(AgentPolicyProfile other,
                              File[] allowedFiles,
                              File[] allowedDirectories,
                              Set<Address> allowedAddresses,
                              Map<OID, ManagedObjectAccess> allowedObjects) {
        this.managedObjectAccess = other.managedObjectAccess;
        this.networkAccess = other.networkAccess;
        this.fileAccess = other.fileAccess;
        this.minSecurityLevel = other.minSecurityLevel;
        this.allowedFiles = other.allowedFiles;
        this.allowedDirectories = other.allowedDirectories;
        this.allowedAddresses = other.allowedAddresses;
        this.allowedObjects = other.allowedObjects;
        if (allowedFiles != null) {
            if (this.allowedFiles == null) {
                this.allowedFiles = allowedFiles;
            }
            else {
                File[] newAllowedFiles = new File[this.allowedFiles.length + allowedFiles.length];
                System.arraycopy(this.allowedFiles, 0, newAllowedFiles, 0, this.allowedFiles.length);
                System.arraycopy(allowedFiles, 0, newAllowedFiles, this.allowedFiles.length, allowedFiles.length);
                this.allowedFiles = newAllowedFiles;
            }
        }
        if (allowedDirectories != null) {
            if (this.allowedDirectories == null) {
                this.allowedDirectories = allowedDirectories;
            }
            else {
                File[] newAllowedDirectories = new File[this.allowedDirectories.length + allowedDirectories.length];
                System.arraycopy(this.allowedDirectories, 0, newAllowedDirectories, 0, this.allowedDirectories.length);
                System.arraycopy(allowedDirectories, 0, newAllowedDirectories, this.allowedDirectories.length, allowedDirectories.length);
                this.allowedDirectories = newAllowedDirectories;
            }
        }
        if (allowedAddresses != null) {
            if (this.allowedAddresses == null) {
                this.allowedAddresses = allowedAddresses;
            }
            else {
                this.allowedAddresses.addAll(allowedAddresses);
            }
        }
        if (allowedObjects != null) {
            if (this.allowedObjects == null) {
                this.allowedObjects = allowedObjects;
            }
            else {
                this.allowedObjects.putAll(allowedObjects);
            }
        }
        if (this.allowedAddresses == null && other.allowedAddresses != null) {
            this.allowedAddresses = new HashSet<>();
        }
        if (this.allowedObjects == null && other.allowedObjects != null) {
            this.allowedObjects = new java.util.HashMap<>();
        }
        if (other.allowedAddresses != null) {
            this.allowedAddresses.addAll(other.allowedAddresses);
        }
        if (other.allowedObjects != null) {
            this.allowedObjects.putAll(other.allowedObjects);
        }
    }

    public ManagedObjectAccess getManagedObjectAccess() {
        return managedObjectAccess;
    }

    public NetworkAccess getNetworkAccess() {
        return networkAccess;
    }

    public FileAccess getFileAccess() {
        return fileAccess;
    }

    public SecurityLevel getMinSecurityLevel() {
        return minSecurityLevel;
    }

    public File[] getAllowedDirectories() {
        return allowedDirectories;
    }

    public Set<Address> getAllowedAddresses() {
        return allowedAddresses;
    }

    public Map<OID, ManagedObjectAccess> getAllowedObjects() {
        return allowedObjects;
    }
}
