001/*
002 * Copyright 2025 devteam@scivics-lab.com
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing,
011 * software distributed under the License is distributed on an
012 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013 * either express or implied.  See the License for the
014 * specific language governing permissions and limitations
015 * under the License.
016 */
017
018package com.scivicslab.actoriac;
019
020import java.io.BufferedReader;
021import java.io.IOException;
022import java.io.InputStream;
023import java.io.InputStreamReader;
024import java.util.HashMap;
025import java.util.Map;
026
027/**
028 * Parser for vault-config.ini files.
029 * Supports global, group-specific, and host-specific Vault path configurations.
030 *
031 * @author devteam@scivics-lab.com
032 */
033public class VaultConfigParser {
034
035    /**
036     * Parses a vault-config.ini file.
037     *
038     * @param input InputStream of the vault-config.ini file
039     * @return VaultPaths object containing all Vault path configurations
040     * @throws IOException if file reading fails
041     */
042    public static VaultPaths parse(InputStream input) throws IOException {
043        VaultPaths vaultPaths = new VaultPaths();
044
045        try (BufferedReader reader = new BufferedReader(new InputStreamReader(input))) {
046            String line;
047            String currentSection = null;
048
049            while ((line = reader.readLine()) != null) {
050                line = line.trim();
051
052                // Skip empty lines and comments
053                if (line.isEmpty() || line.startsWith("#") || line.startsWith(";")) {
054                    continue;
055                }
056
057                // Section header
058                if (line.startsWith("[") && line.endsWith("]")) {
059                    currentSection = line.substring(1, line.length() - 1).trim();
060                    continue;
061                }
062
063                // Key-value pair
064                int equalsIndex = line.indexOf('=');
065                if (equalsIndex > 0 && currentSection != null) {
066                    String key = line.substring(0, equalsIndex).trim();
067                    String value = line.substring(equalsIndex + 1).trim();
068
069                    if (currentSection.equals("vault:all")) {
070                        vaultPaths.addGlobalPath(key, value);
071                    } else if (currentSection.startsWith("vault:host:")) {
072                        String hostname = currentSection.substring("vault:host:".length());
073                        vaultPaths.addHostPath(hostname, key, value);
074                    } else if (currentSection.startsWith("vault:")) {
075                        String groupName = currentSection.substring("vault:".length());
076                        vaultPaths.addGroupPath(groupName, key, value);
077                    }
078                }
079            }
080        }
081
082        return vaultPaths;
083    }
084
085    /**
086     * Container for Vault path configurations.
087     */
088    public static class VaultPaths {
089        private final Map<String, String> globalPaths = new HashMap<>();
090        private final Map<String, Map<String, String>> groupPaths = new HashMap<>();
091        private final Map<String, Map<String, String>> hostPaths = new HashMap<>();
092
093        /**
094         * Adds a global Vault path.
095         *
096         * @param key Path key (e.g., "ssh_key_path")
097         * @param value Vault path (e.g., "secret/data/ssh/iacuser/private_key")
098         */
099        public void addGlobalPath(String key, String value) {
100            globalPaths.put(key, value);
101        }
102
103        /**
104         * Adds a group-specific Vault path.
105         *
106         * @param groupName Group name
107         * @param key Path key
108         * @param value Vault path
109         */
110        public void addGroupPath(String groupName, String key, String value) {
111            groupPaths.computeIfAbsent(groupName, k -> new HashMap<>()).put(key, value);
112        }
113
114        /**
115         * Adds a host-specific Vault path.
116         *
117         * @param hostname Hostname
118         * @param key Path key
119         * @param value Vault path
120         */
121        public void addHostPath(String hostname, String key, String value) {
122            hostPaths.computeIfAbsent(hostname, k -> new HashMap<>()).put(key, value);
123        }
124
125        /**
126         * Gets Vault paths for a specific host, applying priority rules.
127         * Priority: host-specific > group-specific > global
128         *
129         * @param hostname Hostname
130         * @param groupNames Group names this host belongs to
131         * @return Map of Vault paths for this host
132         */
133        public Map<String, String> getPathsForHost(String hostname, String... groupNames) {
134            Map<String, String> result = new HashMap<>(globalPaths);
135
136            // Apply group paths (later groups override earlier ones)
137            for (String groupName : groupNames) {
138                Map<String, String> groupPathMap = groupPaths.get(groupName);
139                if (groupPathMap != null) {
140                    result.putAll(groupPathMap);
141                }
142            }
143
144            // Apply host-specific paths (highest priority)
145            Map<String, String> hostPathMap = hostPaths.get(hostname);
146            if (hostPathMap != null) {
147                result.putAll(hostPathMap);
148            }
149
150            return result;
151        }
152
153        /**
154         * Gets global Vault paths.
155         *
156         * @return Map of global paths
157         */
158        public Map<String, String> getGlobalPaths() {
159            return new HashMap<>(globalPaths);
160        }
161
162        /**
163         * Gets group-specific Vault paths.
164         *
165         * @param groupName Group name
166         * @return Map of group paths, or empty map if group not found
167         */
168        public Map<String, String> getGroupPaths(String groupName) {
169            return new HashMap<>(groupPaths.getOrDefault(groupName, new HashMap<>()));
170        }
171
172        /**
173         * Gets host-specific Vault paths.
174         *
175         * @param hostname Hostname
176         * @return Map of host paths, or empty map if host not found
177         */
178        public Map<String, String> getHostPaths(String hostname) {
179            return new HashMap<>(hostPaths.getOrDefault(hostname, new HashMap<>()));
180        }
181    }
182}