raqeb-cli 1.0.0
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/README.md +134 -0
- package/bin/raqeb.js +212 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# raqeb-cli
|
|
2
|
+
|
|
3
|
+
Official Raqeb CLI - Command-line tool for Database PAM and Developer Secrets Management.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### Via npm (Recommended)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g raqeb-cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Via yarn
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
yarn global add raqeb-cli
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
### 1. Login
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
raqeb login --api-key sa_your_api_key_here
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 2. Get Secret
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
raqeb secrets get <secret-id>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 3. Get Database Credentials
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
raqeb db connect <database-id> --ttl 4 --access-level read-only
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 4. Revoke Credentials
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
raqeb db revoke <lease-id>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Commands
|
|
46
|
+
|
|
47
|
+
### Authentication
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
raqeb login --api-key <key>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Secrets Management
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Get secret value
|
|
57
|
+
raqeb secrets get <secret-id>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Database Access
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Get temporary credentials
|
|
64
|
+
raqeb db connect <database-id> [options]
|
|
65
|
+
--ttl <hours> Time to live (default: 4)
|
|
66
|
+
--access-level <level> read-only, read-write, or admin (default: read-only)
|
|
67
|
+
|
|
68
|
+
# Revoke credentials
|
|
69
|
+
raqeb db revoke <lease-id>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### API Keys
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# List API keys
|
|
76
|
+
raqeb keys list
|
|
77
|
+
|
|
78
|
+
# Create new API key
|
|
79
|
+
raqeb keys create <name> [--scopes <scopes>]
|
|
80
|
+
|
|
81
|
+
# Delete API key
|
|
82
|
+
raqeb keys delete <key-id>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Configuration
|
|
86
|
+
|
|
87
|
+
Configuration is stored in `~/.raqeb/config.json`
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"base_url": "https://app.raqeb.cloud/api/v1",
|
|
92
|
+
"api_key": "sa_your_api_key"
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Examples
|
|
97
|
+
|
|
98
|
+
### CI/CD Integration
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
#!/bin/bash
|
|
102
|
+
# Get database credentials for deployment
|
|
103
|
+
|
|
104
|
+
# Login (use environment variable)
|
|
105
|
+
raqeb login --api-key $RAQEB_API_KEY
|
|
106
|
+
|
|
107
|
+
# Get temporary credentials
|
|
108
|
+
raqeb db connect prod-mysql-db --ttl 1 --access-level read-write
|
|
109
|
+
|
|
110
|
+
# ... your deployment script ...
|
|
111
|
+
|
|
112
|
+
# Revoke when done
|
|
113
|
+
raqeb db revoke $LEASE_ID
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Retrieve Application Secrets
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# Get API key from secrets vault
|
|
120
|
+
raqeb secrets get api-key-prod
|
|
121
|
+
|
|
122
|
+
# Use in your application
|
|
123
|
+
export EXTERNAL_API_KEY=$(raqeb secrets get api-key-prod | grep "Value:" | cut -d' ' -f2)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
MIT
|
|
129
|
+
|
|
130
|
+
## Support
|
|
131
|
+
|
|
132
|
+
- Documentation: https://docs.raqeb.cloud
|
|
133
|
+
- Email: support@raqeb.cloud
|
|
134
|
+
- GitHub: https://github.com/raqeb/cli
|
package/bin/raqeb.js
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { program } = require('commander');
|
|
4
|
+
const axios = require('axios');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
const chalk = require('chalk');
|
|
9
|
+
|
|
10
|
+
const CONFIG_DIR = path.join(os.homedir(), '.raqeb');
|
|
11
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
12
|
+
|
|
13
|
+
// Ensure config directory exists
|
|
14
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
15
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Load config
|
|
19
|
+
function loadConfig() {
|
|
20
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
21
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
22
|
+
}
|
|
23
|
+
return { base_url: 'https://app.raqeb.cloud/api/v1' };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Save config
|
|
27
|
+
function saveConfig(config) {
|
|
28
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
29
|
+
fs.chmodSync(CONFIG_FILE, 0o600);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Get API client
|
|
33
|
+
function getClient() {
|
|
34
|
+
const config = loadConfig();
|
|
35
|
+
if (!config.api_key) {
|
|
36
|
+
console.error(chalk.red('❌ Not authenticated. Run: raqeb login --api-key <key>'));
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
return axios.create({
|
|
40
|
+
baseURL: config.base_url,
|
|
41
|
+
headers: {
|
|
42
|
+
'Authorization': `Bearer ${config.api_key}`,
|
|
43
|
+
'Content-Type': 'application/json'
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Login command
|
|
49
|
+
program
|
|
50
|
+
.command('login')
|
|
51
|
+
.description('Authenticate with API key')
|
|
52
|
+
.requiredOption('--api-key <key>', 'Your API key')
|
|
53
|
+
.action((options) => {
|
|
54
|
+
const config = loadConfig();
|
|
55
|
+
config.api_key = options.apiKey;
|
|
56
|
+
saveConfig(config);
|
|
57
|
+
console.log(chalk.green('✅ Successfully authenticated!'));
|
|
58
|
+
console.log(chalk.gray(`Config saved to: ${CONFIG_FILE}`));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Secrets commands
|
|
62
|
+
const secrets = program.command('secrets').description('Manage secrets');
|
|
63
|
+
|
|
64
|
+
secrets
|
|
65
|
+
.command('get <secret-id>')
|
|
66
|
+
.description('Get secret value')
|
|
67
|
+
.action(async (secretId) => {
|
|
68
|
+
try {
|
|
69
|
+
const client = getClient();
|
|
70
|
+
const response = await client.post('/service-accounts/secrets/retrieve', null, {
|
|
71
|
+
params: { secret_id: secretId }
|
|
72
|
+
});
|
|
73
|
+
const data = response.data;
|
|
74
|
+
console.log(chalk.cyan(`\n🔐 Secret: ${data.name}`));
|
|
75
|
+
console.log(chalk.white(`Value: ${data.value}`));
|
|
76
|
+
console.log(chalk.gray(`Retrieved at: ${data.retrieved_at}`));
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error(chalk.red(`❌ Error: ${error.response?.data?.detail || error.message}`));
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Database commands
|
|
84
|
+
const db = program.command('db').description('Manage database access');
|
|
85
|
+
|
|
86
|
+
db
|
|
87
|
+
.command('connect <database-id>')
|
|
88
|
+
.description('Get temporary database credentials')
|
|
89
|
+
.option('--ttl <hours>', 'Time to live in hours', '4')
|
|
90
|
+
.option('--access-level <level>', 'Access level (read-only, read-write, admin)', 'read-only')
|
|
91
|
+
.action(async (databaseId, options) => {
|
|
92
|
+
try {
|
|
93
|
+
const client = getClient();
|
|
94
|
+
const response = await client.post('/service-accounts/databases/dynamic-credentials', {
|
|
95
|
+
database_id: databaseId,
|
|
96
|
+
ttl_hours: parseInt(options.ttl),
|
|
97
|
+
access_level: options.accessLevel
|
|
98
|
+
});
|
|
99
|
+
const data = response.data;
|
|
100
|
+
|
|
101
|
+
console.log(chalk.cyan('\n🔑 Temporary Database Credentials'));
|
|
102
|
+
console.log(chalk.gray('━'.repeat(40)));
|
|
103
|
+
console.log(chalk.white(`Username: ${data.username}`));
|
|
104
|
+
console.log(chalk.white(`Password: ${data.password}`));
|
|
105
|
+
console.log(chalk.white(`Access Level: ${data.access_level}`));
|
|
106
|
+
console.log(chalk.white(`Expires: ${data.expires_at}`));
|
|
107
|
+
console.log(chalk.white(`Lease ID: ${data.lease_id}`));
|
|
108
|
+
console.log(chalk.yellow(`\n⚠️ These credentials will expire in ${options.ttl} hours`));
|
|
109
|
+
console.log(chalk.gray(`To revoke early: raqeb db revoke ${data.lease_id}`));
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error(chalk.red(`❌ Error: ${error.response?.data?.detail || error.message}`));
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
db
|
|
117
|
+
.command('revoke <lease-id>')
|
|
118
|
+
.description('Revoke database credentials')
|
|
119
|
+
.action(async (leaseId) => {
|
|
120
|
+
try {
|
|
121
|
+
const client = getClient();
|
|
122
|
+
await client.post(`/service-accounts/leases/${leaseId}/revoke`);
|
|
123
|
+
console.log(chalk.green(`✅ Lease ${leaseId} revoked successfully`));
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error(chalk.red(`❌ Error: ${error.response?.data?.detail || error.message}`));
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// API Keys commands
|
|
131
|
+
const keys = program.command('keys').description('Manage API keys');
|
|
132
|
+
|
|
133
|
+
keys
|
|
134
|
+
.command('list')
|
|
135
|
+
.description('List API keys')
|
|
136
|
+
.action(async () => {
|
|
137
|
+
try {
|
|
138
|
+
const client = getClient();
|
|
139
|
+
const response = await client.get('/service-accounts/api-keys');
|
|
140
|
+
const apiKeys = response.data;
|
|
141
|
+
|
|
142
|
+
if (apiKeys.length === 0) {
|
|
143
|
+
console.log(chalk.yellow('No API keys found'));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
console.log(chalk.cyan(`\n📋 API Keys (${apiKeys.length})`));
|
|
148
|
+
console.log(chalk.gray('━'.repeat(40)));
|
|
149
|
+
|
|
150
|
+
apiKeys.forEach(key => {
|
|
151
|
+
const status = key.is_active ? chalk.green('🟢 Active') : chalk.red('🔴 Inactive');
|
|
152
|
+
console.log(chalk.white(`\n${key.name}`));
|
|
153
|
+
console.log(chalk.gray(` ID: ${key.id}`));
|
|
154
|
+
console.log(chalk.gray(` Prefix: ${key.key_prefix}...`));
|
|
155
|
+
console.log(` Status: ${status}`);
|
|
156
|
+
console.log(chalk.gray(` Created: ${key.created_at}`));
|
|
157
|
+
if (key.last_used_at) {
|
|
158
|
+
console.log(chalk.gray(` Last Used: ${key.last_used_at}`));
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
} catch (error) {
|
|
162
|
+
console.error(chalk.red(`❌ Error: ${error.response?.data?.detail || error.message}`));
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
keys
|
|
168
|
+
.command('create <name>')
|
|
169
|
+
.description('Create new API key')
|
|
170
|
+
.option('--scopes <scopes>', 'Comma-separated scopes', 'secrets:read,databases:read')
|
|
171
|
+
.action(async (name, options) => {
|
|
172
|
+
try {
|
|
173
|
+
const client = getClient();
|
|
174
|
+
const response = await client.post('/service-accounts/api-keys', {
|
|
175
|
+
name: name,
|
|
176
|
+
scopes: options.scopes.split(',')
|
|
177
|
+
});
|
|
178
|
+
const data = response.data;
|
|
179
|
+
|
|
180
|
+
console.log(chalk.green('\n✅ API Key Created!'));
|
|
181
|
+
console.log(chalk.gray('━'.repeat(40)));
|
|
182
|
+
console.log(chalk.white(`Name: ${data.name}`));
|
|
183
|
+
console.log(chalk.white(`API Key: ${data.api_key}`));
|
|
184
|
+
console.log(chalk.yellow('\n⚠️ IMPORTANT: Save this key now! It won\'t be shown again.'));
|
|
185
|
+
console.log(chalk.gray(`\nTo use this key:`));
|
|
186
|
+
console.log(chalk.gray(` raqeb login --api-key ${data.api_key}`));
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.error(chalk.red(`❌ Error: ${error.response?.data?.detail || error.message}`));
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
keys
|
|
194
|
+
.command('delete <key-id>')
|
|
195
|
+
.description('Delete API key')
|
|
196
|
+
.action(async (keyId) => {
|
|
197
|
+
try {
|
|
198
|
+
const client = getClient();
|
|
199
|
+
await client.delete(`/service-accounts/api-keys/${keyId}`);
|
|
200
|
+
console.log(chalk.green(`✅ API key ${keyId} deleted successfully`));
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.error(chalk.red(`❌ Error: ${error.response?.data?.detail || error.message}`));
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
program
|
|
208
|
+
.name('raqeb')
|
|
209
|
+
.description('Raqeb CLI - Database PAM and Secrets Management')
|
|
210
|
+
.version('1.0.0');
|
|
211
|
+
|
|
212
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "raqeb-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Raqeb CLI - Command-line tool for Database PAM and Developer Secrets Management",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"raqeb": "bin/raqeb.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"raqeb",
|
|
14
|
+
"pam",
|
|
15
|
+
"database",
|
|
16
|
+
"secrets",
|
|
17
|
+
"cli",
|
|
18
|
+
"security",
|
|
19
|
+
"credentials"
|
|
20
|
+
],
|
|
21
|
+
"author": "Raqeb",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/raqeb/cli"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"axios": "^1.6.0",
|
|
29
|
+
"commander": "^11.0.0",
|
|
30
|
+
"chalk": "^4.1.2",
|
|
31
|
+
"ora": "^5.4.1",
|
|
32
|
+
"inquirer": "^8.2.5"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=14.0.0"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"bin",
|
|
39
|
+
"lib",
|
|
40
|
+
"README.md",
|
|
41
|
+
"LICENSE"
|
|
42
|
+
]
|
|
43
|
+
}
|