/*_############################################################################
  _##
  _##  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.*;

/**
 * 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 String[] DEFAULT_ALLOWED_FILES =
            new String[] { "snmp4j-agent.cfg", "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 =
            new AgentPolicyProfile(ManagedObjectAccess.UNRESTRICTED,
                    NetworkAccess.LOOPBACK, FileAccess.WORKING_DIRECTORY_READWRITE, SecurityLevel.noAuthNoPriv);

    public static final AgentPolicyProfile SITE_LOCAL =
            new AgentPolicyProfile(ManagedObjectAccess.UNRESTRICTED,
                    NetworkAccess.SITE_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 List<String> getAllowedFiles() {
        return allowedFiles;
    }

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

    public enum NetworkAccess {
        LOOPBACK,
        SITE_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 ArrayList<String> allowedFiles = new ArrayList<>(DEFAULT_ALLOWED_FILES.length);
    private ArrayList<String> allowedDirectories = new ArrayList<>();

    private ArrayList<String> allowedAddresses = new ArrayList<>();
    private Map<String, ManagedObjectAccess> allowedObjects = new java.util.HashMap<>();

    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,
                              String[] allowedFiles,
                              String[] allowedDirectories,
                              String[] allowedAddresses,
                              Map<String, ManagedObjectAccess> allowedObjects) {
        this(managedObjectAccess, networkAccess, fileAccess, minSecurityLevel);
        if (allowedFiles != null) {
            this.allowedFiles.addAll(Arrays.asList(allowedFiles));
        }
        if (allowedDirectories != null) {
            this.allowedDirectories.addAll(Arrays.asList(allowedDirectories));
        }
        if (allowedAddresses != null) {
            this.allowedAddresses.addAll(Arrays.asList(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,
                              String[] allowedFiles,
                              String[] allowedDirectories,
                              String[] allowedAddresses,
                              Map<String, 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) {
            this.allowedFiles.addAll(Arrays.asList(allowedFiles));
        }
        if (allowedDirectories != null) {
            this.allowedDirectories.addAll(Arrays.asList(allowedDirectories));
        }
        if (allowedAddresses != null) {
            this.allowedAddresses.addAll(Arrays.asList(allowedAddresses));
        }
        if (allowedObjects != null) {
            this.allowedObjects.putAll(allowedObjects);
        }
    }

    public ManagedObjectAccess getManagedObjectAccess() {
        return managedObjectAccess;
    }

    public NetworkAccess getNetworkAccess() {
        return networkAccess;
    }

    public FileAccess getFileAccess() {
        return fileAccess;
    }

    public SecurityLevel getMinSecurityLevel() {
        return minSecurityLevel;
    }

    public List<String> getAllowedDirectories() {
        return allowedDirectories;
    }

    public List<String> getAllowedAddresses() {
        return Collections.unmodifiableList(allowedAddresses);
    }

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