/*_############################################################################
  _## 
  _##  SNMP4J-Agent-DB 3 - SampleAgentTest.java  
  _## 
  _##  Copyright (C) 2017-2025 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.db.sample;

import jetbrains.exodus.env.Environments;
import org.junit.Test;
import org.snmp4j.SNMP4JSettings;
import org.snmp4j.agent.DefaultMOServer;
import org.snmp4j.agent.MOServer;
import org.snmp4j.agent.audit.AgentAuditLevel;
import org.snmp4j.agent.audit.AgentAuditPolicy;
import org.snmp4j.agent.audit.AgentPolicyProfile;
import org.snmp4j.agent.audit.DefaultAgentAuditPolicy;
import org.snmp4j.agent.db.MOXodusPersistence;
import org.snmp4j.agent.io.ImportMode;
import org.snmp4j.agent.io.MOInputFactory;
import org.snmp4j.agent.mo.snmp.UsmMIB;
import org.snmp4j.security.*;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.util.ArgumentParser;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.*;

import static org.junit.Assert.*;
import static org.snmp4j.agent.db.sample.SampleAgent.DEFAULT_SAMPLE_AGENT_DB_POLICY;

public class SampleAgentTest {

    public static final String AUTH_12345678 = "auth12345678";
    public static final String AUTH_SHA256AES128AuthPassword = "SHA256AES128AuthPassword";
    public static final String PRIV_9876543210 = "priv9876543210";
    public static final String PRIV_SHA256AES128PrivPassword = "SHA256AES128PrivPassword";

    static {
        SNMP4JSettings.setSecretLoggingEnabled(true);
    }

    private static final String[] ARGS = { "-c", "-c SampleAgentTest.cfg", "udp:127.0.0.1/4700"};


    private SampleAgent sampleAgent;

    @org.junit.Before
    public void setUp() throws Exception {
        SecurityProtocols.getInstance().addDefaultProtocols();
        DefaultMOServer server = new DefaultMOServer().setDeadlockPreventionEnabled(true);
        MOServer[] moServers = new MOServer[]{server};
        FileAttribute<Set<PosixFilePermission>> attrs =
                PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------"));
        Path tempDir = Files.createTempDirectory("snmp4j-agent-db_unit-test", attrs);
        MOXodusPersistence moXodusPersistence =
                new MOXodusPersistence(moServers, Environments.newInstance(tempDir.toFile()));
        AgentAuditPolicy auditPolicy = new DefaultAgentAuditPolicy(
                new AgentPolicyProfile(
                        AgentPolicyProfile.ManagedObjectAccess.UNRESTRICTED,
                        AgentPolicyProfile.NetworkAccess.SITE_LOCAL_NETWORK,
                        AgentPolicyProfile.FileAccess.LIMITED_DIRECTORY_ACCESS,
                        SecurityLevel.authNoPriv,
                        null, new String[] { tempDir.toAbsolutePath().toString() },
                        null, null
                ), AgentAuditLevel.RESTRICT);
        ArgumentParser parser =
                new ArgumentParser(SampleAgent.COMMAND_LINE_OPTIONS, SampleAgent.COMMAND_LINE_PARAMS);
        Map<String, List<Object>> commandLineParameters;
        commandLineParameters = parser.parse(ARGS);

        Properties tableSizeLimits = SampleAgent.getTableSizeLimitsProperties(commandLineParameters, auditPolicy);

        sampleAgent = new SampleAgent(commandLineParameters,
                moServers, moXodusPersistence, tableSizeLimits, ImportMode.replaceCreate,
                auditPolicy);
    }

    @org.junit.After
    public void tearDown() throws Exception {
        sampleAgent.agent.shutdown();
    }


    @Test
    public void resetConfig() {
        try {
            Files.deleteIfExists(new File("SampleAgentTest.cfg").toPath());
        } catch (IOException e) {
            e.printStackTrace();
        }
        sampleAgent.run();
        byte[] localEngineID = sampleAgent.agent.getSnmpTargetMIB().getLocalEngineID();

        USM usm = sampleAgent.agent.getUsm();
        UsmMIB usmMIB = sampleAgent.agent.getUsmMIB();

        OctetString localEngineIDString = new OctetString(localEngineID);

        OctetString userNameAuthPriv = new OctetString("SHA256AES128");
        OctetString userNameAuthNoPriv = new OctetString("userAuthNoPriv");
        OctetString userName = new OctetString("user");

        AuthenticationProtocol authenticationProtocol = new AuthHMAC192SHA256();

        byte[] keyAuth = authenticationProtocol.passwordToKey(new OctetString(AUTH_12345678), localEngineID);
        byte[] keyPriv = authenticationProtocol.passwordToKey(new OctetString(PRIV_9876543210), localEngineID);
        UsmUser usmUserAuthPriv = new UsmUser(userNameAuthPriv, AuthHMAC192SHA256.ID, new OctetString(keyAuth),
                PrivAES128.ID, new OctetString(keyPriv), localEngineIDString);
        UsmUser usmUserAuthNoPriv = new UsmUser(userNameAuthNoPriv, AuthHMAC128SHA224.ID, new OctetString(keyAuth),
                null, null, localEngineIDString);
        UsmUser usmUserNoAuthNoPriv =
                new UsmUser(userName, null, null, null, null, localEngineIDString);

        usm.addUser(usmUserAuthNoPriv);
        usm.addUser(usmUserAuthPriv);
        usm.addUser(usmUserNoAuthNoPriv);

        // Indexes Changed
        OID indexUsmUserAuthNoPriv =
                new OID(new OctetString(localEngineID).toSubIndex(false).getValue(),
                        userNameAuthNoPriv.toSubIndex(false).getValue());
        OID indexUsmUserAuthPriv =
                new OID(new OctetString(localEngineID).toSubIndex(false).getValue(),
                        userNameAuthPriv.toSubIndex(false).getValue());
        byte[] keyAuthProperties = SecurityProtocols.getInstance().passwordToKey(authenticationProtocol.getID(),
                new OctetString(AUTH_SHA256AES128AuthPassword), localEngineID);
        byte[] keyPrivProperties = SecurityProtocols.getInstance().passwordToKey(PrivAES128.ID,
                authenticationProtocol.getID(), new OctetString(PRIV_SHA256AES128PrivPassword), localEngineID);

        sampleAgent.agent.saveState();

        // Check changed
        UsmMIB.UsmTableRow usmTableRowNew = usmMIB.getUsmUserEntry().getModel().getRow(indexUsmUserAuthPriv);
        assertNotNull(usmTableRowNew);
        assertArrayEquals(keyAuth, ((OctetString)usmTableRowNew.getValue(UsmMIB.colUsmUserAuthKey)).getValue());
        assertArrayEquals(keyPriv, ((OctetString)usmTableRowNew.getValue(UsmMIB.colUsmUserPrivKey)).getValue());
        assertEquals(userNameAuthPriv, usmTableRowNew.getValue(UsmMIB.colUsmUserSecurityName));

        usmTableRowNew = usmMIB.getUsmUserEntry().getModel().getRow(indexUsmUserAuthNoPriv);
        assertNotNull(usmTableRowNew);
        assertArrayEquals(keyAuth, ((OctetString)usmTableRowNew.getValue(UsmMIB.colUsmUserAuthKey)).getValue());
        assertEquals(userNameAuthNoPriv, ((OctetString)usmTableRowNew.getValue(UsmMIB.colUsmUserSecurityName)));

        sampleAgent.agent.configure();

        usmTableRowNew = usmMIB.getUsmUserEntry().getModel().getRow(indexUsmUserAuthPriv);
        assertNotNull(usmTableRowNew);
        assertArrayEquals(keyAuthProperties, ((OctetString)usmTableRowNew.getValue(UsmMIB.colUsmUserAuthKey)).getValue());
        assertArrayEquals(keyPrivProperties, ((OctetString)usmTableRowNew.getValue(UsmMIB.colUsmUserPrivKey)).getValue());
        assertEquals(userNameAuthPriv, usmTableRowNew.getValue(UsmMIB.colUsmUserSecurityName));

        usmTableRowNew = usmMIB.getUsmUserEntry().getModel().getRow(indexUsmUserAuthNoPriv);
        assertNull(usmTableRowNew);

    }
}