Entropic 2.3.8
Local-first agentic inference engine
Loading...
Searching...
No Matches
permission_persister.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
13
15
16#include <algorithm>
17#include <fstream>
18#include <sstream>
19#include <string>
20#include <vector>
21
22namespace entropic {
23
24namespace {
25auto logger = entropic::log::get("storage.permission_persister");
26} // anonymous namespace
27
35 const std::filesystem::path& config_dir)
36 : config_path_(config_dir / "config.local.yaml") {}
37
45static std::string read_file(const std::filesystem::path& path) {
46 std::ifstream in(path);
47 if (!in.is_open()) return "";
48 std::ostringstream ss;
49 ss << in.rdbuf();
50 return ss.str();
51}
52
61static bool write_file(const std::filesystem::path& path,
62 const std::string& content) {
63 std::filesystem::create_directories(path.parent_path());
64 std::ofstream out(path);
65 if (!out.is_open()) return false;
66 out << content;
67 return out.good();
68}
69
77static std::vector<std::string> split_lines(const std::string& content) {
78 std::vector<std::string> lines;
79 std::istringstream iss(content);
80 std::string line;
81 while (std::getline(iss, line)) {
82 lines.push_back(line);
83 }
84 return lines;
85}
86
96static bool list_contains(const std::vector<std::string>& lines,
97 size_t start, std::string_view pattern) {
98 std::string needle = "- " + std::string(pattern);
99 for (size_t i = start + 1; i < lines.size(); ++i) {
100 auto pos = lines[i].find_first_not_of(' ');
101 if (pos == std::string::npos) continue;
102 auto trimmed = lines[i].substr(pos);
103 if (!trimmed.starts_with("- ")) break;
104 if (trimmed == needle) return true;
105 }
106 return false;
107}
108
117static auto find_list_end(std::vector<std::string>& lines,
118 size_t list_start) {
119 auto ins = lines.begin()
120 + static_cast<long>(list_start) + 1;
121 while (ins != lines.end()) {
122 auto pos = ins->find_first_not_of(' ');
123 bool is_item = pos != std::string::npos
124 && ins->substr(pos).starts_with("- ");
125 if (!is_item) break;
126 ++ins;
127 }
128 return ins;
129}
130
139static auto find_section_end(std::vector<std::string>& lines,
140 std::vector<std::string>::iterator start) {
141 auto it = start + 1;
142 while (it != lines.end() && !it->empty()
143 && ((*it)[0] == ' ' || (*it)[0] == '\t')) {
144 ++it;
145 }
146 return it;
147}
148
156static std::string join_lines(const std::vector<std::string>& lines) {
157 std::ostringstream out;
158 for (size_t i = 0; i < lines.size(); ++i) {
159 out << lines[i];
160 if (i + 1 < lines.size()) out << '\n';
161 }
162 auto result = out.str();
163 if (!result.empty() && result.back() != '\n') {
164 result += '\n';
165 }
166 return result;
167}
168
185static bool insert_permission_item(std::vector<std::string>& lines,
186 std::string_view pattern,
187 const std::string& perms_key,
188 const std::string& list_line,
189 const std::string& item) {
190 auto perms_it = std::find(lines.begin(), lines.end(), perms_key);
191 if (perms_it == lines.end()) {
192 lines.push_back(perms_key);
193 lines.push_back(list_line);
194 lines.push_back(item);
195 } else if (auto list_it = std::find(perms_it, lines.end(), list_line);
196 list_it == lines.end()) {
197 auto pos = find_section_end(lines, perms_it);
198 lines.insert(pos, item);
199 lines.insert(pos, list_line);
200 } else {
201 size_t idx = static_cast<size_t>(
202 std::distance(lines.begin(), list_it));
203 if (list_contains(lines, idx, pattern)) {
204 return false;
205 }
206 lines.insert(find_list_end(lines, idx), item);
207 }
208 return true;
209}
210
219bool PermissionPersister::save_permission(std::string_view pattern,
220 bool allow) {
221 std::lock_guard lock(write_mutex_);
222
223 auto lines = split_lines(read_file(config_path_));
224 const char* list_key = allow ? "allow" : "deny";
225 std::string perms_key = "permissions:";
226 std::string list_line = std::string(" ") + list_key + ":";
227 std::string item = " - " + std::string(pattern);
228
229 if (!insert_permission_item(lines, pattern, perms_key, list_line, item)) {
230 return true; // already present — nothing to write
231 }
232
233 if (!write_file(config_path_, join_lines(lines))) {
234 logger->error("Failed to write permission to {}",
235 config_path_.string());
236 return false;
237 }
238
239 logger->info("Saved permission {}: {}",
240 allow ? "allow" : "deny", pattern);
241 return true;
242}
243
244} // namespace entropic
PermissionPersister(const std::filesystem::path &config_dir)
Construct with config directory path.
bool save_permission(std::string_view pattern, bool allow)
Save a permission pattern.
spdlog initialization and logger access.
ENTROPIC_EXPORT std::shared_ptr< spdlog::logger > get(const std::string &name)
Get or create a named logger.
Definition logging.cpp:211
Activate model on GPU (WARM → ACTIVE).
static bool insert_permission_item(std::vector< std::string > &lines, std::string_view pattern, const std::string &perms_key, const std::string &list_line, const std::string &item)
Insert a permission item into the parsed YAML lines.
static bool write_file(const std::filesystem::path &path, const std::string &content)
Write string to file.
static std::vector< std::string > split_lines(const std::string &content)
Split string into lines.
static auto find_section_end(std::vector< std::string > &lines, std::vector< std::string >::iterator start)
Find the end of a YAML section (indented block).
static bool list_contains(const std::vector< std::string > &lines, size_t start, std::string_view pattern)
Check if a YAML sequence already contains a pattern.
static auto find_list_end(std::vector< std::string > &lines, size_t list_start)
Find the end of a YAML sequence (last "- " item).
static std::string join_lines(const std::vector< std::string > &lines)
Join lines back into a string with trailing newline.
Persist permission allow/deny patterns to project config.