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}