@bniladridas/cursor 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.clang-tidy +28 -0
- package/.dockerignore +56 -0
- package/.env.example +29 -0
- package/.github/CODEOWNERS +2 -0
- package/.github/ISSUE_TEMPLATE/blank.md +27 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
- package/.github/SECURITY.md +24 -0
- package/.github/codeql/codeql-config.yml +8 -0
- package/.github/dependabot.yml +14 -0
- package/.github/labeler.yml +50 -0
- package/.github/packaging/brand-cursor.png +0 -0
- package/.github/packaging/database/init.sql +48 -0
- package/.github/packaging/docker/Dockerfile +111 -0
- package/.github/packaging/docker/docker-compose.yml +56 -0
- package/.github/packaging/scripts/preflight.sh +413 -0
- package/.github/packaging/scripts/prepare-release.sh +141 -0
- package/.github/packaging/scripts/release.sh +22 -0
- package/.github/packaging/scripts/setup-git-hooks.sh +73 -0
- package/.github/pull_request_template.md +31 -0
- package/.github/signed.json +9 -0
- package/.github/workflows/README.md +23 -0
- package/.github/workflows/ci.yml +181 -0
- package/.github/workflows/cla.yml +33 -0
- package/.github/workflows/formula-sha.yml +63 -0
- package/.github/workflows/issue-response.yml +44 -0
- package/.github/workflows/labeler.yml +42 -0
- package/.github/workflows/pr-body.yml +49 -0
- package/.github/workflows/release.yml +176 -0
- package/.github/workflows/security.yml +94 -0
- package/.github/workflows/stale.yml +38 -0
- package/AGENTS.md +49 -0
- package/CHANGELOG.md +3 -0
- package/CMakeLists.txt +646 -0
- package/Formula/cursor.rb +46 -0
- package/LICENSE +201 -0
- package/Makefile +28 -0
- package/README.md +46 -0
- package/cli.js +16 -0
- package/include/agent.h +86 -0
- package/include/agent_mode.h +17 -0
- package/include/memory_manager.h +102 -0
- package/include/services/ai_service.h +31 -0
- package/include/services/auth_service.h +87 -0
- package/include/services/checkpoint_service.h +69 -0
- package/include/services/codebase_service.h +38 -0
- package/include/services/command_service.h +23 -0
- package/include/services/context_service.h +74 -0
- package/include/services/database_service.h +56 -0
- package/include/services/error_service.h +106 -0
- package/include/services/file_service.h +51 -0
- package/include/services/git_service.h +29 -0
- package/include/services/github_service.h +85 -0
- package/include/services/mcp_service.h +85 -0
- package/include/services/multi_file_service.h +93 -0
- package/include/services/sandbox_service.h +96 -0
- package/include/services/theme_service.h +67 -0
- package/include/services/web_service.h +52 -0
- package/include/utils/config.h +68 -0
- package/include/utils/memory_utils.h +79 -0
- package/include/utils/platform.h +56 -0
- package/include/utils/ui.h +43 -0
- package/include/utils/validation.h +63 -0
- package/include/utils/version.h.in +17 -0
- package/install.js +49 -0
- package/package.json +16 -0
- package/release/checksums.txt +3 -0
- package/release/cursor-linux/cursor_v0.1.7_linux_amd64.tar.gz +0 -0
- package/release/cursor-macos/cursor_v0.1.7_darwin_arm64.tar.gz +0 -0
- package/release/cursor-windows/cursor__windows_amd64.zip +0 -0
- package/src/agent.cpp +2026 -0
- package/src/main.cpp +97 -0
- package/src/memory_manager.cpp +814 -0
- package/src/services/ai_service.cpp +366 -0
- package/src/services/auth_service.cpp +779 -0
- package/src/services/checkpoint_service.cpp +465 -0
- package/src/services/codebase_service.cpp +233 -0
- package/src/services/command_service.cpp +82 -0
- package/src/services/context_service.cpp +348 -0
- package/src/services/database_service.cpp +148 -0
- package/src/services/error_service.cpp +438 -0
- package/src/services/file_service.cpp +349 -0
- package/src/services/git_service.cpp +148 -0
- package/src/services/github_service.cpp +435 -0
- package/src/services/mcp_service.cpp +481 -0
- package/src/services/multi_file_service.cpp +591 -0
- package/src/services/sandbox_service.cpp +678 -0
- package/src/services/theme_service.cpp +429 -0
- package/src/services/web_service.cpp +532 -0
- package/src/utils/config.cpp +77 -0
- package/src/utils/memory_utils.cpp +93 -0
- package/src/utils/ui.cpp +307 -0
- package/src/utils/validation.cpp +306 -0
- package/src/utils/version.cpp +175 -0
- package/tests/e2e/docker-compose.yml +195 -0
- package/tests/e2e/run_e2e_tests.sh +70 -0
- package/tests/e2e/run_tests_in_docker.sh +115 -0
- package/tests/main_test.cpp +16 -0
- package/tests/mocks/mock_ollama.py +98 -0
- package/tests/mocks/start_nginx.sh +64 -0
|
@@ -0,0 +1,779 @@
|
|
|
1
|
+
#include "services/auth_service.h"
|
|
2
|
+
#include <cstdlib>
|
|
3
|
+
#include <filesystem>
|
|
4
|
+
#include <fstream>
|
|
5
|
+
#include <iomanip>
|
|
6
|
+
#include <iostream>
|
|
7
|
+
#include <nlohmann/json.hpp>
|
|
8
|
+
#include <openssl/err.h>
|
|
9
|
+
#include <openssl/evp.h>
|
|
10
|
+
#include <openssl/kdf.h>
|
|
11
|
+
#include <openssl/rand.h>
|
|
12
|
+
#include <random>
|
|
13
|
+
#include <sstream>
|
|
14
|
+
#ifdef _WIN32
|
|
15
|
+
#include <Lmcons.h> // for UNLEN and GetUserName
|
|
16
|
+
#include <windows.h>
|
|
17
|
+
#else
|
|
18
|
+
#include <pwd.h>
|
|
19
|
+
#include <unistd.h> // for getlogin_r
|
|
20
|
+
#endif
|
|
21
|
+
|
|
22
|
+
struct EvpCipherCtx {
|
|
23
|
+
EVP_CIPHER_CTX *ctx;
|
|
24
|
+
EvpCipherCtx() : ctx(EVP_CIPHER_CTX_new()) {}
|
|
25
|
+
~EvpCipherCtx() {
|
|
26
|
+
if (ctx)
|
|
27
|
+
EVP_CIPHER_CTX_free(ctx);
|
|
28
|
+
}
|
|
29
|
+
EvpCipherCtx(const EvpCipherCtx &) = delete;
|
|
30
|
+
EvpCipherCtx &operator=(const EvpCipherCtx &) = delete;
|
|
31
|
+
EVP_CIPHER_CTX *get() const { return ctx; }
|
|
32
|
+
explicit operator bool() const { return ctx != nullptr; }
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Initialize static members
|
|
36
|
+
namespace Services {
|
|
37
|
+
std::map<std::string, AuthProvider> AuthService::providers;
|
|
38
|
+
std::string AuthService::active_provider = "together";
|
|
39
|
+
std::vector<unsigned char> AuthService::encryption_key;
|
|
40
|
+
std::string AuthService::key_file_path = "data/encryption_key.bin";
|
|
41
|
+
} // namespace Services
|
|
42
|
+
|
|
43
|
+
std::string Services::AuthService::get_auth_config_path() {
|
|
44
|
+
return "data/auth_config.json";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
void Services::AuthService::ensure_auth_directory() {
|
|
48
|
+
std::filesystem::create_directories("data");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
std::vector<unsigned char>
|
|
52
|
+
Services::AuthService::generate_random_bytes(size_t length) {
|
|
53
|
+
std::vector<unsigned char> buffer(length);
|
|
54
|
+
if (RAND_bytes(buffer.data(), static_cast<int>(length)) != 1) {
|
|
55
|
+
throw std::runtime_error("Failed to generate random bytes");
|
|
56
|
+
}
|
|
57
|
+
return buffer;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
std::string
|
|
61
|
+
Services::AuthService::bytes_to_hex(const std::vector<unsigned char> &bytes) {
|
|
62
|
+
std::ostringstream oss;
|
|
63
|
+
oss << std::hex << std::setfill('0');
|
|
64
|
+
for (unsigned char byte : bytes) {
|
|
65
|
+
oss << std::setw(2) << static_cast<int>(byte);
|
|
66
|
+
}
|
|
67
|
+
return oss.str();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
std::vector<unsigned char>
|
|
71
|
+
Services::AuthService::hex_to_bytes(const std::string &hex) {
|
|
72
|
+
std::vector<unsigned char> bytes;
|
|
73
|
+
for (size_t i = 0; i < hex.length(); i += 2) {
|
|
74
|
+
std::string byte_string = hex.substr(i, 2);
|
|
75
|
+
auto byte =
|
|
76
|
+
static_cast<unsigned char>(strtol(byte_string.c_str(), nullptr, 16));
|
|
77
|
+
bytes.push_back(byte);
|
|
78
|
+
}
|
|
79
|
+
return bytes;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
bool Services::AuthService::derive_key(const std::string &password,
|
|
83
|
+
const std::vector<unsigned char> &salt,
|
|
84
|
+
std::vector<unsigned char> &key) {
|
|
85
|
+
key.resize(AuthService::key_size);
|
|
86
|
+
return PKCS5_PBKDF2_HMAC(
|
|
87
|
+
password.c_str(), static_cast<int>(password.length()), salt.data(),
|
|
88
|
+
static_cast<int>(salt.size()), ITERATIONS, EVP_sha256(),
|
|
89
|
+
AuthService::key_size, key.data()) == 1;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
bool Services::AuthService::save_encryption_key() {
|
|
93
|
+
ensure_auth_directory();
|
|
94
|
+
std::ofstream key_file(key_file_path, std::ios::binary);
|
|
95
|
+
if (!key_file)
|
|
96
|
+
return false;
|
|
97
|
+
|
|
98
|
+
// Generate a random salt
|
|
99
|
+
auto salt = generate_random_bytes(SALT_SIZE);
|
|
100
|
+
|
|
101
|
+
// Derive a key from the system's username and salt
|
|
102
|
+
std::vector<unsigned char> derived_key;
|
|
103
|
+
char username[256] = {0};
|
|
104
|
+
|
|
105
|
+
// Get username in a cross-platform way
|
|
106
|
+
#ifdef _WIN32
|
|
107
|
+
DWORD username_len = UNLEN + 1;
|
|
108
|
+
if (!GetUserNameA(username, &username_len)) {
|
|
109
|
+
throw std::runtime_error("Failed to get Windows username");
|
|
110
|
+
}
|
|
111
|
+
#else
|
|
112
|
+
if (getlogin_r(username, sizeof(username) - 1) != 0) {
|
|
113
|
+
// Try getpwuid if getlogin_r fails
|
|
114
|
+
struct passwd *pwd = getpwuid(geteuid());
|
|
115
|
+
if (!pwd || !pwd->pw_name) {
|
|
116
|
+
throw std::runtime_error("Failed to get system username");
|
|
117
|
+
}
|
|
118
|
+
strncpy(username, pwd->pw_name, sizeof(username) - 1);
|
|
119
|
+
username[sizeof(username) - 1] = '\0';
|
|
120
|
+
}
|
|
121
|
+
#endif
|
|
122
|
+
|
|
123
|
+
if (!derive_key(username, salt, derived_key)) {
|
|
124
|
+
throw std::runtime_error("Key derivation failed");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Encrypt the master key with the derived key
|
|
128
|
+
std::vector<unsigned char> iv = generate_random_bytes(AuthService::iv_size);
|
|
129
|
+
std::vector<unsigned char> tag(AuthService::tag_size);
|
|
130
|
+
std::vector<unsigned char> ciphertext(encryption_key.size() +
|
|
131
|
+
EVP_MAX_BLOCK_LENGTH);
|
|
132
|
+
|
|
133
|
+
int len = 0;
|
|
134
|
+
int ciphertext_len = 0;
|
|
135
|
+
|
|
136
|
+
EvpCipherCtx ctx;
|
|
137
|
+
if (!ctx) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_gcm(), nullptr,
|
|
142
|
+
derived_key.data(), iv.data()) != 1) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (EVP_EncryptUpdate(ctx.get(), ciphertext.data(), &len,
|
|
147
|
+
encryption_key.data(),
|
|
148
|
+
static_cast<int>(encryption_key.size())) != 1) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
ciphertext_len = len;
|
|
152
|
+
|
|
153
|
+
if (EVP_EncryptFinal_ex(ctx.get(), ciphertext.data() + len, &len) != 1) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
ciphertext_len += len;
|
|
157
|
+
|
|
158
|
+
if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG,
|
|
159
|
+
AuthService::tag_size, tag.data()) != 1) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Write salt, IV, tag, and ciphertext to file
|
|
164
|
+
key_file.write(reinterpret_cast<const char *>(salt.data()), salt.size());
|
|
165
|
+
key_file.write(reinterpret_cast<const char *>(iv.data()), iv.size());
|
|
166
|
+
key_file.write(reinterpret_cast<const char *>(tag.data()), tag.size());
|
|
167
|
+
key_file.write(reinterpret_cast<const char *>(ciphertext.data()),
|
|
168
|
+
ciphertext_len);
|
|
169
|
+
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
bool Services::AuthService::load_or_generate_key() {
|
|
174
|
+
// Try to load existing key
|
|
175
|
+
std::ifstream key_file(key_file_path, std::ios::binary);
|
|
176
|
+
if (key_file) {
|
|
177
|
+
// Read salt, IV, tag, and ciphertext
|
|
178
|
+
std::vector<unsigned char> salt(SALT_SIZE);
|
|
179
|
+
std::vector<unsigned char> iv(AuthService::iv_size);
|
|
180
|
+
std::vector<unsigned char> tag(tag_size);
|
|
181
|
+
|
|
182
|
+
key_file.read(reinterpret_cast<char *>(salt.data()), SALT_SIZE);
|
|
183
|
+
key_file.read(reinterpret_cast<char *>(iv.data()), iv_size);
|
|
184
|
+
key_file.read(reinterpret_cast<char *>(tag.data()), tag_size);
|
|
185
|
+
|
|
186
|
+
// Read the rest as ciphertext
|
|
187
|
+
std::vector<unsigned char> ciphertext(
|
|
188
|
+
(std::istreambuf_iterator<char>(key_file)),
|
|
189
|
+
std::istreambuf_iterator<char>());
|
|
190
|
+
|
|
191
|
+
// Derive key from username and salt
|
|
192
|
+
char username[256] = {0};
|
|
193
|
+
#ifdef _WIN32
|
|
194
|
+
DWORD username_len = UNLEN + 1;
|
|
195
|
+
if (!GetUserNameA(username, &username_len)) {
|
|
196
|
+
throw std::runtime_error("Failed to get Windows username");
|
|
197
|
+
}
|
|
198
|
+
#else
|
|
199
|
+
if (getlogin_r(username, sizeof(username) - 1) != 0) {
|
|
200
|
+
// Try getpwuid if getlogin_r fails
|
|
201
|
+
struct passwd *pwd = getpwuid(geteuid());
|
|
202
|
+
if (!pwd || !pwd->pw_name) {
|
|
203
|
+
throw std::runtime_error("Failed to get system username");
|
|
204
|
+
}
|
|
205
|
+
strncpy(username, pwd->pw_name, sizeof(username) - 1);
|
|
206
|
+
username[sizeof(username) - 1] = '\0';
|
|
207
|
+
}
|
|
208
|
+
#endif
|
|
209
|
+
|
|
210
|
+
std::vector<unsigned char> derived_key;
|
|
211
|
+
if (!derive_key(username, salt, derived_key)) {
|
|
212
|
+
throw std::runtime_error("Key derivation failed");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Decrypt the master key
|
|
216
|
+
std::vector<unsigned char> plaintext(ciphertext.size());
|
|
217
|
+
int len;
|
|
218
|
+
int plaintext_len;
|
|
219
|
+
|
|
220
|
+
EvpCipherCtx ctx;
|
|
221
|
+
if (!ctx)
|
|
222
|
+
return false;
|
|
223
|
+
|
|
224
|
+
if (EVP_DecryptInit_ex(ctx.get(), EVP_aes_256_gcm(), nullptr,
|
|
225
|
+
derived_key.data(), iv.data()) != 1) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (EVP_DecryptUpdate(ctx.get(), plaintext.data(), &len, ciphertext.data(),
|
|
230
|
+
static_cast<int>(ciphertext.size())) != 1) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
plaintext_len = len;
|
|
234
|
+
|
|
235
|
+
// Set expected tag value
|
|
236
|
+
if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, tag_size,
|
|
237
|
+
tag.data()) != 1) {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Finalize decryption and check authentication tag
|
|
242
|
+
int ret = EVP_DecryptFinal_ex(ctx.get(), plaintext.data() + len, &len);
|
|
243
|
+
|
|
244
|
+
if (ret <= 0) {
|
|
245
|
+
// Authentication failed
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
plaintext_len += len;
|
|
250
|
+
plaintext.resize(plaintext_len);
|
|
251
|
+
encryption_key = plaintext;
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// No existing key, generate a new one
|
|
256
|
+
encryption_key = generate_random_bytes(key_size);
|
|
257
|
+
return save_encryption_key();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
bool Services::AuthService::initialize_encryption_key() {
|
|
261
|
+
try {
|
|
262
|
+
return load_or_generate_key();
|
|
263
|
+
} catch (const std::exception &e) {
|
|
264
|
+
std::cerr << "Encryption key initialization failed: " << e.what()
|
|
265
|
+
<< std::endl;
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
std::string
|
|
271
|
+
Services::AuthService::encrypt_credential(const std::string &credential) {
|
|
272
|
+
if (encryption_key.empty() && !initialize_encryption_key()) {
|
|
273
|
+
throw std::runtime_error("Failed to initialize encryption key");
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Generate random IV
|
|
277
|
+
std::vector<unsigned char> iv = generate_random_bytes(AuthService::iv_size);
|
|
278
|
+
std::vector<unsigned char> tag(AuthService::tag_size);
|
|
279
|
+
std::vector<unsigned char> ciphertext(credential.size() +
|
|
280
|
+
EVP_MAX_BLOCK_LENGTH);
|
|
281
|
+
|
|
282
|
+
int len;
|
|
283
|
+
int ciphertext_len;
|
|
284
|
+
|
|
285
|
+
// Create and initialize the encryption context
|
|
286
|
+
EvpCipherCtx ctx;
|
|
287
|
+
if (!ctx)
|
|
288
|
+
throw std::runtime_error("Failed to create encryption context");
|
|
289
|
+
|
|
290
|
+
// Initialize the encryption operation with AES-256-GCM
|
|
291
|
+
if (EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_gcm(), nullptr,
|
|
292
|
+
encryption_key.data(), iv.data()) != 1) {
|
|
293
|
+
throw std::runtime_error("Encryption initialization failed");
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Encrypt the plaintext
|
|
297
|
+
if (EVP_EncryptUpdate(
|
|
298
|
+
ctx.get(), ciphertext.data(), &len,
|
|
299
|
+
reinterpret_cast<const unsigned char *>(credential.c_str()),
|
|
300
|
+
static_cast<int>(credential.length())) != 1) {
|
|
301
|
+
throw std::runtime_error("Encryption update failed");
|
|
302
|
+
}
|
|
303
|
+
ciphertext_len = len;
|
|
304
|
+
|
|
305
|
+
// Finalize the encryption
|
|
306
|
+
if (EVP_EncryptFinal_ex(ctx.get(), ciphertext.data() + len, &len) != 1) {
|
|
307
|
+
throw std::runtime_error("Encryption finalization failed");
|
|
308
|
+
}
|
|
309
|
+
ciphertext_len += len;
|
|
310
|
+
|
|
311
|
+
// Get the authentication tag
|
|
312
|
+
if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG,
|
|
313
|
+
AuthService::tag_size, tag.data()) != 1) {
|
|
314
|
+
throw std::runtime_error("Failed to get authentication tag");
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Resize the ciphertext to actual size
|
|
318
|
+
ciphertext.resize(ciphertext_len);
|
|
319
|
+
|
|
320
|
+
// Combine IV + tag + ciphertext and encode as hex
|
|
321
|
+
std::vector<unsigned char> combined;
|
|
322
|
+
combined.reserve(iv.size() + tag.size() + ciphertext.size());
|
|
323
|
+
combined.insert(combined.end(), iv.begin(), iv.end());
|
|
324
|
+
combined.insert(combined.end(), tag.begin(), tag.end());
|
|
325
|
+
combined.insert(combined.end(), ciphertext.begin(), ciphertext.end());
|
|
326
|
+
|
|
327
|
+
return bytes_to_hex(combined);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
std::string
|
|
331
|
+
Services::AuthService::decrypt_credential(const std::string &encrypted_hex) {
|
|
332
|
+
if (encryption_key.empty() && !initialize_encryption_key()) {
|
|
333
|
+
throw std::runtime_error("Failed to initialize encryption key");
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Convert hex string back to bytes
|
|
337
|
+
std::vector<unsigned char> combined = hex_to_bytes(encrypted_hex);
|
|
338
|
+
|
|
339
|
+
// Extract IV, tag, and ciphertext
|
|
340
|
+
if (combined.size() < iv_size + tag_size) {
|
|
341
|
+
throw std::runtime_error("Invalid encrypted data format");
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
std::vector<unsigned char> iv(combined.begin(), combined.begin() + iv_size);
|
|
345
|
+
std::vector<unsigned char> tag(combined.begin() + iv_size,
|
|
346
|
+
combined.begin() + iv_size + tag_size);
|
|
347
|
+
std::vector<unsigned char> ciphertext(combined.begin() + iv_size + tag_size,
|
|
348
|
+
combined.end());
|
|
349
|
+
|
|
350
|
+
// Decrypt the ciphertext
|
|
351
|
+
std::vector<unsigned char> plaintext(ciphertext.size() +
|
|
352
|
+
EVP_MAX_BLOCK_LENGTH);
|
|
353
|
+
int len;
|
|
354
|
+
int plaintext_len;
|
|
355
|
+
|
|
356
|
+
// Create and initialize the decryption context
|
|
357
|
+
EvpCipherCtx ctx;
|
|
358
|
+
if (!ctx)
|
|
359
|
+
throw std::runtime_error("Failed to create decryption context");
|
|
360
|
+
|
|
361
|
+
// Initialize the decryption operation with AES-256-GCM
|
|
362
|
+
if (EVP_DecryptInit_ex(ctx.get(), EVP_aes_256_gcm(), nullptr,
|
|
363
|
+
encryption_key.data(), iv.data()) != 1) {
|
|
364
|
+
throw std::runtime_error("Decryption initialization failed");
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Provide the message to be decrypted and obtain the plaintext output
|
|
368
|
+
if (EVP_DecryptUpdate(ctx.get(), plaintext.data(), &len, ciphertext.data(),
|
|
369
|
+
static_cast<int>(ciphertext.size())) != 1) {
|
|
370
|
+
throw std::runtime_error("Decryption update failed");
|
|
371
|
+
}
|
|
372
|
+
plaintext_len = len;
|
|
373
|
+
|
|
374
|
+
// Set the expected tag value
|
|
375
|
+
if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, tag_size,
|
|
376
|
+
tag.data()) != 1) {
|
|
377
|
+
throw std::runtime_error("Failed to set authentication tag");
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Finalize the decryption and check the authentication tag
|
|
381
|
+
int ret = EVP_DecryptFinal_ex(ctx.get(), plaintext.data() + len, &len);
|
|
382
|
+
|
|
383
|
+
if (ret <= 0) {
|
|
384
|
+
// Authentication failed
|
|
385
|
+
throw std::runtime_error(
|
|
386
|
+
"Authentication failed - invalid key or corrupted data");
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
plaintext_len += len;
|
|
390
|
+
plaintext.resize(plaintext_len);
|
|
391
|
+
|
|
392
|
+
return std::string(plaintext.begin(), plaintext.end());
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
void Services::AuthService::initialize_default_providers() {
|
|
396
|
+
// Together AI
|
|
397
|
+
AuthProvider together;
|
|
398
|
+
together.name = "together";
|
|
399
|
+
together.display_name = "Together AI";
|
|
400
|
+
together.base_url = "https://api.together.xyz/v1";
|
|
401
|
+
together.model = "meta-llama/Llama-3.2-3B-Instruct-Turbo";
|
|
402
|
+
together.is_active = true;
|
|
403
|
+
providers["together"] = together;
|
|
404
|
+
|
|
405
|
+
// Ollama (local)
|
|
406
|
+
AuthProvider ollama;
|
|
407
|
+
ollama.name = "ollama";
|
|
408
|
+
ollama.display_name = "Ollama (Local)";
|
|
409
|
+
ollama.base_url = "http://localhost:11434/v1";
|
|
410
|
+
ollama.model = "llama3.2:3b";
|
|
411
|
+
ollama.api_key = "ollama"; // Placeholder for local
|
|
412
|
+
providers["ollama"] = ollama;
|
|
413
|
+
|
|
414
|
+
// OpenAI
|
|
415
|
+
AuthProvider openai;
|
|
416
|
+
openai.name = "openai";
|
|
417
|
+
openai.display_name = "OpenAI";
|
|
418
|
+
openai.base_url = "https://api.openai.com/v1";
|
|
419
|
+
openai.model = "gpt-4";
|
|
420
|
+
providers["openai"] = openai;
|
|
421
|
+
|
|
422
|
+
// Anthropic
|
|
423
|
+
AuthProvider anthropic;
|
|
424
|
+
anthropic.name = "anthropic";
|
|
425
|
+
anthropic.display_name = "Anthropic Claude";
|
|
426
|
+
anthropic.base_url = "https://api.anthropic.com/v1";
|
|
427
|
+
anthropic.model = "claude-3-sonnet-20240229";
|
|
428
|
+
providers["anthropic"] = anthropic;
|
|
429
|
+
|
|
430
|
+
// Cerebras
|
|
431
|
+
AuthProvider cerebras;
|
|
432
|
+
cerebras.name = "cerebras";
|
|
433
|
+
cerebras.display_name = "Cerebras";
|
|
434
|
+
cerebras.base_url = "https://api.cerebras.ai/v1";
|
|
435
|
+
cerebras.model = "llama3.1-8b";
|
|
436
|
+
providers["cerebras"] = cerebras;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
void Services::AuthService::initialize() {
|
|
440
|
+
// Initialize encryption first
|
|
441
|
+
if (!initialize_encryption_key()) {
|
|
442
|
+
throw std::runtime_error("Failed to initialize encryption");
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
initialize_default_providers();
|
|
446
|
+
load_auth_config();
|
|
447
|
+
|
|
448
|
+
// Try to load API keys from environment variables
|
|
449
|
+
for (auto &[name, provider] : providers) {
|
|
450
|
+
std::string env_var = name + "_API_KEY";
|
|
451
|
+
std::transform(env_var.begin(), env_var.end(), env_var.begin(), ::toupper);
|
|
452
|
+
|
|
453
|
+
const char *env_key = std::getenv(env_var.c_str());
|
|
454
|
+
if (env_key && provider.api_key.empty()) {
|
|
455
|
+
try {
|
|
456
|
+
// Encrypt the API key before storing it
|
|
457
|
+
provider.api_key = encrypt_credential(env_key);
|
|
458
|
+
provider.is_valid = true;
|
|
459
|
+
// Save the encrypted key to config
|
|
460
|
+
save_auth_config();
|
|
461
|
+
} catch (const std::exception &e) {
|
|
462
|
+
std::cerr << "Failed to encrypt API key for " << name << ": "
|
|
463
|
+
<< e.what() << std::endl;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
bool Services::AuthService::add_provider(const AuthProvider &provider) {
|
|
470
|
+
providers[provider.name] = provider;
|
|
471
|
+
return save_auth_config();
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
bool Services::AuthService::remove_provider(const std::string &provider_name) {
|
|
475
|
+
auto it = providers.find(provider_name);
|
|
476
|
+
if (it == providers.end()) {
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Don't allow removal of built-in providers
|
|
481
|
+
if (provider_name == "together" || provider_name == "ollama" ||
|
|
482
|
+
provider_name == "openai" || provider_name == "anthropic" ||
|
|
483
|
+
provider_name == "cerebras") {
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
providers.erase(it);
|
|
488
|
+
|
|
489
|
+
// If we're removing the active provider, switch to together
|
|
490
|
+
if (active_provider == provider_name) {
|
|
491
|
+
active_provider = "together";
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
return save_auth_config();
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
bool Services::AuthService::set_active_provider(
|
|
498
|
+
const std::string &provider_name) {
|
|
499
|
+
auto it = providers.find(provider_name);
|
|
500
|
+
if (it == providers.end()) {
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Deactivate all providers
|
|
505
|
+
for (auto &[name, provider] : providers) {
|
|
506
|
+
provider.is_active = false;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Activate the selected provider
|
|
510
|
+
it->second.is_active = true;
|
|
511
|
+
active_provider = provider_name;
|
|
512
|
+
|
|
513
|
+
return save_auth_config();
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
std::string Services::AuthService::get_active_provider() {
|
|
517
|
+
return active_provider;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
std::vector<std::string> Services::AuthService::list_providers() {
|
|
521
|
+
std::vector<std::string> provider_names;
|
|
522
|
+
for (const auto &[name, provider] : providers) {
|
|
523
|
+
provider_names.push_back(name);
|
|
524
|
+
}
|
|
525
|
+
return provider_names;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
Services::AuthProvider
|
|
529
|
+
Services::AuthService::get_provider_info(const std::string &provider_name) {
|
|
530
|
+
auto it = providers.find(provider_name);
|
|
531
|
+
if (it != providers.end()) {
|
|
532
|
+
return it->second;
|
|
533
|
+
}
|
|
534
|
+
return AuthProvider{}; // Return empty provider if not found
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
bool Services::AuthService::set_api_key(const std::string &provider_name,
|
|
538
|
+
const std::string &api_key) {
|
|
539
|
+
auto it = providers.find(provider_name);
|
|
540
|
+
if (it == providers.end()) {
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
it->second.api_key = api_key;
|
|
545
|
+
it->second.is_valid = !api_key.empty();
|
|
546
|
+
|
|
547
|
+
return save_auth_config();
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
std::string
|
|
551
|
+
Services::AuthService::get_api_key(const std::string &provider_name) {
|
|
552
|
+
std::string target_provider =
|
|
553
|
+
provider_name.empty() ? active_provider : provider_name;
|
|
554
|
+
|
|
555
|
+
auto it = providers.find(target_provider);
|
|
556
|
+
if (it != providers.end()) {
|
|
557
|
+
return it->second.api_key;
|
|
558
|
+
}
|
|
559
|
+
return "";
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
bool Services::AuthService::validate_credentials(
|
|
563
|
+
const std::string &provider_name) {
|
|
564
|
+
auto it = providers.find(provider_name);
|
|
565
|
+
if (it == providers.end()) {
|
|
566
|
+
return false;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// For local providers like Ollama, always consider valid
|
|
570
|
+
if (provider_name == "ollama") {
|
|
571
|
+
it->second.is_valid = true;
|
|
572
|
+
return true;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// For API-based providers, check if API key exists
|
|
576
|
+
bool is_valid = !it->second.api_key.empty();
|
|
577
|
+
it->second.is_valid = is_valid;
|
|
578
|
+
|
|
579
|
+
return is_valid;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
void Services::AuthService::clear_credentials(
|
|
583
|
+
const std::string &provider_name) {
|
|
584
|
+
auto it = providers.find(provider_name);
|
|
585
|
+
if (it != providers.end()) {
|
|
586
|
+
it->second.api_key.clear();
|
|
587
|
+
it->second.is_valid = false;
|
|
588
|
+
save_auth_config();
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
bool Services::AuthService::save_auth_config() {
|
|
593
|
+
try {
|
|
594
|
+
ensure_auth_directory();
|
|
595
|
+
|
|
596
|
+
nlohmann::json config;
|
|
597
|
+
config["active_provider"] = active_provider;
|
|
598
|
+
config["providers"] = nlohmann::json::object();
|
|
599
|
+
|
|
600
|
+
for (const auto &[name, provider] : providers) {
|
|
601
|
+
nlohmann::json provider_json;
|
|
602
|
+
provider_json["name"] = provider.name;
|
|
603
|
+
provider_json["display_name"] = provider.display_name;
|
|
604
|
+
provider_json["base_url"] = provider.base_url;
|
|
605
|
+
provider_json["model"] = provider.model;
|
|
606
|
+
provider_json["is_active"] = provider.is_active;
|
|
607
|
+
provider_json["is_valid"] = provider.is_valid;
|
|
608
|
+
|
|
609
|
+
// Encrypt API key before saving
|
|
610
|
+
if (!provider.api_key.empty()) {
|
|
611
|
+
provider_json["api_key"] = encrypt_credential(provider.api_key);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
if (!provider.additional_config.empty()) {
|
|
615
|
+
provider_json["additional_config"] = provider.additional_config;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
config["providers"][name] = provider_json;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
std::ofstream file(get_auth_config_path());
|
|
622
|
+
file << config.dump(2);
|
|
623
|
+
|
|
624
|
+
return true;
|
|
625
|
+
|
|
626
|
+
} catch (const std::exception &e) {
|
|
627
|
+
std::cerr << "Failed to save auth config: " << e.what() << std::endl;
|
|
628
|
+
return false;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
bool Services::AuthService::load_auth_config() {
|
|
633
|
+
try {
|
|
634
|
+
std::string config_path = get_auth_config_path();
|
|
635
|
+
if (!std::filesystem::exists(config_path)) {
|
|
636
|
+
return true; // Use defaults
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
std::ifstream file(config_path);
|
|
640
|
+
nlohmann::json config;
|
|
641
|
+
file >> config;
|
|
642
|
+
|
|
643
|
+
if (config.contains("active_provider")) {
|
|
644
|
+
active_provider = config["active_provider"];
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
if (config.contains("providers")) {
|
|
648
|
+
for (const auto &[name, provider_json] : config["providers"].items()) {
|
|
649
|
+
auto it = providers.find(name);
|
|
650
|
+
if (it != providers.end()) {
|
|
651
|
+
// Update existing provider
|
|
652
|
+
it->second.is_active = provider_json.value("is_active", false);
|
|
653
|
+
it->second.is_valid = provider_json.value("is_valid", false);
|
|
654
|
+
|
|
655
|
+
if (provider_json.contains("api_key")) {
|
|
656
|
+
it->second.api_key = decrypt_credential(provider_json["api_key"]);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
if (provider_json.contains("additional_config")) {
|
|
660
|
+
it->second.additional_config = provider_json["additional_config"];
|
|
661
|
+
}
|
|
662
|
+
} else {
|
|
663
|
+
// Add new custom provider
|
|
664
|
+
AuthProvider provider;
|
|
665
|
+
provider.name = provider_json.value("name", name);
|
|
666
|
+
provider.display_name = provider_json.value("display_name", name);
|
|
667
|
+
provider.base_url = provider_json.value("base_url", "");
|
|
668
|
+
provider.model = provider_json.value("model", "");
|
|
669
|
+
provider.is_active = provider_json.value("is_active", false);
|
|
670
|
+
provider.is_valid = provider_json.value("is_valid", false);
|
|
671
|
+
|
|
672
|
+
if (provider_json.contains("api_key")) {
|
|
673
|
+
provider.api_key = decrypt_credential(provider_json["api_key"]);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
if (provider_json.contains("additional_config")) {
|
|
677
|
+
provider.additional_config = provider_json["additional_config"];
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
providers[name] = provider;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
return true;
|
|
686
|
+
|
|
687
|
+
} catch (const std::exception &e) {
|
|
688
|
+
std::cerr << "Failed to load auth config: " << e.what() << std::endl;
|
|
689
|
+
return false;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
bool Services::AuthService::update_provider_config(
|
|
694
|
+
const std::string &provider_name,
|
|
695
|
+
const std::map<std::string, std::string> &config) {
|
|
696
|
+
auto it = providers.find(provider_name);
|
|
697
|
+
if (it == providers.end()) {
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
for (const auto &[key, value] : config) {
|
|
702
|
+
it->second.additional_config[key] = value;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
return save_auth_config();
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
bool Services::AuthService::test_provider_connection(
|
|
709
|
+
const std::string &provider_name) {
|
|
710
|
+
auto it = providers.find(provider_name);
|
|
711
|
+
if (it == providers.end()) {
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// For local providers like Ollama, always return true for now
|
|
716
|
+
if (provider_name == "ollama") {
|
|
717
|
+
return true;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// For API providers, check if we have credentials
|
|
721
|
+
return !it->second.api_key.empty();
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
std::string
|
|
725
|
+
Services::AuthService::get_provider_status(const std::string &provider_name) {
|
|
726
|
+
auto it = providers.find(provider_name);
|
|
727
|
+
if (it == providers.end()) {
|
|
728
|
+
return "Not Found";
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
if (!it->second.is_valid) {
|
|
732
|
+
return "Invalid Credentials";
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
if (test_provider_connection(provider_name)) {
|
|
736
|
+
return "Connected";
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
return "Disconnected";
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
void Services::AuthService::refresh_all_provider_status() {
|
|
743
|
+
for (auto &[name, provider] : providers) {
|
|
744
|
+
provider.is_valid = validate_credentials(name);
|
|
745
|
+
}
|
|
746
|
+
save_auth_config();
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
bool Services::AuthService::is_credential_secure() {
|
|
750
|
+
// Check if we have proper encryption setup
|
|
751
|
+
return true; // For now, assume secure
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
void Services::AuthService::generate_encryption_key() {
|
|
755
|
+
// In a real implementation, generate a proper encryption key
|
|
756
|
+
// For now, this is a placeholder
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
bool Services::AuthService::backup_credentials(const std::string &backup_path) {
|
|
760
|
+
try {
|
|
761
|
+
std::filesystem::copy_file(get_auth_config_path(), backup_path);
|
|
762
|
+
return true;
|
|
763
|
+
} catch (const std::exception &e) {
|
|
764
|
+
std::cerr << "Failed to backup credentials: " << e.what() << std::endl;
|
|
765
|
+
return false;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
bool Services::AuthService::restore_credentials(
|
|
770
|
+
const std::string &backup_path) {
|
|
771
|
+
try {
|
|
772
|
+
std::filesystem::copy_file(backup_path, get_auth_config_path());
|
|
773
|
+
load_auth_config();
|
|
774
|
+
return true;
|
|
775
|
+
} catch (const std::exception &e) {
|
|
776
|
+
std::cerr << "Failed to restore credentials: " << e.what() << std::endl;
|
|
777
|
+
return false;
|
|
778
|
+
}
|
|
779
|
+
}
|