dbgate-api-premium 6.6.2 → 6.6.4
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/package.json +6 -6
- package/src/auth/authCommon.js +6 -0
- package/src/auth/authProvider.js +6 -1
- package/src/auth/storageAuthProvider.js +51 -6
- package/src/controllers/apps.js +342 -220
- package/src/controllers/auth.js +3 -1
- package/src/controllers/databaseConnections.js +1 -1
- package/src/controllers/files.js +6 -1
- package/src/controllers/serverConnections.js +2 -2
- package/src/controllers/sessions.js +17 -4
- package/src/controllers/storage.js +128 -4
- package/src/controllers/storageDb.js +331 -0
- package/src/controllers/teamFiles.js +250 -0
- package/src/controllers/uploads.js +66 -95
- package/src/currentVersion.js +2 -2
- package/src/main.js +3 -0
- package/src/proc/databaseConnectionProcess.js +0 -2
- package/src/storageModel.js +506 -37
- package/src/utility/hasPermission.js +51 -2
- package/src/gistSecret.js +0 -2
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// *** This file is part of DbGate Premium ***
|
|
2
|
+
|
|
3
|
+
const { loadPermissionsFromRequest, hasPermission } = require('../utility/hasPermission');
|
|
4
|
+
const {
|
|
5
|
+
storageGetExistingFile,
|
|
6
|
+
storageGetTeamFileRoleAccess,
|
|
7
|
+
storageGetTeamFileUserAccess,
|
|
8
|
+
storageUpdateTeamFile,
|
|
9
|
+
storageListTeamFilesForUser,
|
|
10
|
+
storageListTeamFilesForRole,
|
|
11
|
+
storageListAllTeamFiles,
|
|
12
|
+
storageCreateTeamFile,
|
|
13
|
+
storageGetExistingFileWithContent,
|
|
14
|
+
storageListAllAdminFiles,
|
|
15
|
+
storageUpdateTeamFileAdmin,
|
|
16
|
+
storageDeleteFile,
|
|
17
|
+
storageCopyFile,
|
|
18
|
+
} = require('./storageDb');
|
|
19
|
+
const { getBuiltinRoleIdFromRequest } = require('../auth/storageAuthProvider');
|
|
20
|
+
const socket = require('../utility/socket');
|
|
21
|
+
const fs = require('fs-extra');
|
|
22
|
+
const { getSqlFrontMatter, safeJsonParse, setSqlFrontMatter, removeSqlFrontMatter } = require('dbgate-tools');
|
|
23
|
+
const yaml = require('js-yaml');
|
|
24
|
+
const jwt = require('jsonwebtoken');
|
|
25
|
+
const { getStaticTokenSecret } = require('../auth/authCommon');
|
|
26
|
+
const crypto = require('crypto');
|
|
27
|
+
|
|
28
|
+
function extractFileMetadata(data, typeName) {
|
|
29
|
+
if (typeName == 'sql') {
|
|
30
|
+
const frontMatter = getSqlFrontMatter(data, yaml);
|
|
31
|
+
return frontMatter;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = {
|
|
37
|
+
createNew_meta: true,
|
|
38
|
+
async createNew({ fileType, file, data }, req) {
|
|
39
|
+
const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
40
|
+
if (!hasPermission(`all-team-files/create`, loadedPermissions)) {
|
|
41
|
+
throw new Error('No permission to create team files');
|
|
42
|
+
}
|
|
43
|
+
const userId = req?.user?.userId;
|
|
44
|
+
const metadata = extractFileMetadata(data, fileType);
|
|
45
|
+
const resp = await storageCreateTeamFile({ fileType, file, data, ownerUserId: userId, metadata });
|
|
46
|
+
socket.emitChanged('team-files-changed');
|
|
47
|
+
return resp;
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
update_meta: true,
|
|
51
|
+
async update({ teamFileId, data, name }, req) {
|
|
52
|
+
const existingFile = await storageGetExistingFile(teamFileId);
|
|
53
|
+
if (!existingFile) return false;
|
|
54
|
+
await this.checkWriteAccess(existingFile, req);
|
|
55
|
+
const metadata = extractFileMetadata(data, existingFile.type_name);
|
|
56
|
+
const resp = await storageUpdateTeamFile({ teamFileId, data, name, metadata });
|
|
57
|
+
socket.emitChanged('team-files-changed');
|
|
58
|
+
return resp;
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
list_meta: true,
|
|
62
|
+
async list(_params, req) {
|
|
63
|
+
const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
64
|
+
let res = [];
|
|
65
|
+
const readAll = hasPermission(`all-team-files/read`, loadedPermissions);
|
|
66
|
+
const writeAll = hasPermission(`all-team-files/write`, loadedPermissions);
|
|
67
|
+
const useAll = hasPermission(`all-team-files/use`, loadedPermissions);
|
|
68
|
+
if (readAll || writeAll || useAll) {
|
|
69
|
+
res = await storageListAllTeamFiles();
|
|
70
|
+
res = res?.map(item => ({ ...item, allow_read: readAll, allow_write: writeAll, allow_use: useAll }));
|
|
71
|
+
} else {
|
|
72
|
+
const userId = req?.user?.userId;
|
|
73
|
+
if (userId) {
|
|
74
|
+
res = await storageListTeamFilesForUser(userId);
|
|
75
|
+
} else {
|
|
76
|
+
res = await storageListTeamFilesForRole(getBuiltinRoleIdFromRequest(req));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return (
|
|
80
|
+
res?.map(item => {
|
|
81
|
+
const isOwner = item.owner_user_id && item.owner_user_id == req?.user?.userId;
|
|
82
|
+
return {
|
|
83
|
+
teamFileId: item.id,
|
|
84
|
+
file: item.file_name,
|
|
85
|
+
metadata: safeJsonParse(item.metadata),
|
|
86
|
+
folder: item.type_name,
|
|
87
|
+
allowRead: !!(item.allow_read || isOwner),
|
|
88
|
+
allowWrite: !!(item.allow_write || isOwner),
|
|
89
|
+
allowUse: !!(item.allow_use || isOwner),
|
|
90
|
+
};
|
|
91
|
+
}) || []
|
|
92
|
+
);
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
async checkAccessCore(existingFile, req, throwError, accessType, accessField) {
|
|
96
|
+
const userId = req?.user?.userId;
|
|
97
|
+
const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
98
|
+
|
|
99
|
+
if (hasPermission(`admin/team-files`, loadedPermissions)) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (hasPermission(`all-team-files/${accessType}`, loadedPermissions)) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
const roleId = getBuiltinRoleIdFromRequest(req);
|
|
107
|
+
const roleAccess = await storageGetTeamFileRoleAccess(existingFile.id, roleId);
|
|
108
|
+
if (roleAccess[accessField]) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
if (existingFile.owner_user_id == userId && userId) {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
if (existingFile.owner_user_id != userId && userId) {
|
|
115
|
+
const userAccess = await storageGetTeamFileUserAccess(existingFile.id, userId);
|
|
116
|
+
|
|
117
|
+
if (userAccess[accessField]) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!throwError) return false;
|
|
123
|
+
throw new Error(`No permission to ${accessType} this team file`);
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
async checkReadAccess(existingFile, req, throwError = true) {
|
|
127
|
+
return await this.checkAccessCore(existingFile, req, throwError, 'read', 'allowRead');
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
async checkWriteAccess(existingFile, req, throwError = true) {
|
|
131
|
+
return await this.checkAccessCore(existingFile, req, throwError, 'write', 'allowWrite');
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
async checkUseAccess(existingFile, req, throwError = true) {
|
|
135
|
+
return await this.checkAccessCore(existingFile, req, throwError, 'use', 'allowUse');
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
getContent_meta: true,
|
|
139
|
+
async getContent({ teamFileId }, req) {
|
|
140
|
+
const existingFile = await storageGetExistingFileWithContent(teamFileId);
|
|
141
|
+
if (!existingFile) return false;
|
|
142
|
+
let actualContent = existingFile.file_content;
|
|
143
|
+
let allowUse = false;
|
|
144
|
+
if (existingFile.type_name == 'sql') {
|
|
145
|
+
const frontMatter = getSqlFrontMatter(existingFile.file_content, yaml);
|
|
146
|
+
allowUse = await this.checkUseAccess(existingFile, req, false);
|
|
147
|
+
// TODO
|
|
148
|
+
// const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
149
|
+
// && !hasPermission('dbops/query', loadedPermissions)
|
|
150
|
+
if (allowUse) {
|
|
151
|
+
const useToken = jwt.sign(
|
|
152
|
+
{
|
|
153
|
+
contentHash: crypto.createHash('md5').update(removeSqlFrontMatter(existingFile.file_content)).digest('hex'),
|
|
154
|
+
},
|
|
155
|
+
getStaticTokenSecret()
|
|
156
|
+
// { expiresIn: '1h' }
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
actualContent = setSqlFrontMatter(actualContent, { ...frontMatter, useToken }, yaml);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (!allowUse) {
|
|
163
|
+
await this.checkReadAccess(existingFile, req);
|
|
164
|
+
}
|
|
165
|
+
return { content: actualContent, file: existingFile.file_name, metadata: existingFile.metadata };
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
copy_meta: true,
|
|
169
|
+
async copy({ teamFileId, newName }, req) {
|
|
170
|
+
const existingFile = await storageGetExistingFile(teamFileId);
|
|
171
|
+
if (!existingFile) return false;
|
|
172
|
+
await this.checkReadAccess(existingFile, req);
|
|
173
|
+
const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
174
|
+
if (!hasPermission(`all-team-files/create`, loadedPermissions)) {
|
|
175
|
+
throw new Error('No permission to create team files');
|
|
176
|
+
}
|
|
177
|
+
const userId = req?.user?.userId;
|
|
178
|
+
const resp = await storageCopyFile(teamFileId, newName, userId);
|
|
179
|
+
socket.emitChanged('team-files-changed');
|
|
180
|
+
return resp;
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
exportFile_meta: true,
|
|
184
|
+
async exportFile({ teamFileId, filePath }, req) {
|
|
185
|
+
const existingFile = await storageGetExistingFileWithContent(teamFileId);
|
|
186
|
+
if (!existingFile) return false;
|
|
187
|
+
await this.checkReadAccess(existingFile, req);
|
|
188
|
+
await fs.writeFile(filePath, existingFile.file_content);
|
|
189
|
+
return true;
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
delete_meta: true,
|
|
193
|
+
async delete({ teamFileId }, req) {
|
|
194
|
+
const existingFile = await storageGetExistingFile(teamFileId);
|
|
195
|
+
if (!existingFile) return false;
|
|
196
|
+
await this.checkWriteAccess(existingFile, req);
|
|
197
|
+
const resp = await storageDeleteFile(teamFileId);
|
|
198
|
+
socket.emitChanged('team-files-changed');
|
|
199
|
+
return resp;
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
listAdminFiles_meta: true,
|
|
203
|
+
async listAdminFiles({}, req) {
|
|
204
|
+
const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
205
|
+
if (!hasPermission(`admin/team-files`, loadedPermissions)) return [];
|
|
206
|
+
const res = await storageListAllAdminFiles();
|
|
207
|
+
return res.map(item => ({
|
|
208
|
+
id: item.id,
|
|
209
|
+
name: item.file_name,
|
|
210
|
+
ownerLogin: item.owner_login,
|
|
211
|
+
ownerEmail: item.owner_email,
|
|
212
|
+
typeName: item.type_name,
|
|
213
|
+
}));
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
getAdminFile_meta: true,
|
|
217
|
+
async getAdminFile({ id }, req) {
|
|
218
|
+
const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
219
|
+
if (!hasPermission(`admin/team-files`, loadedPermissions)) return false;
|
|
220
|
+
const existingFile = await storageGetExistingFileWithContent(id);
|
|
221
|
+
if (!existingFile) return false;
|
|
222
|
+
return {
|
|
223
|
+
content: existingFile.file_content,
|
|
224
|
+
name: existingFile.file_name,
|
|
225
|
+
metadata: existingFile.metadata,
|
|
226
|
+
id: existingFile.id,
|
|
227
|
+
ownerName: existingFile.owner_login,
|
|
228
|
+
ownerEmail: existingFile.owner_email,
|
|
229
|
+
typeName: existingFile.type_name,
|
|
230
|
+
};
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
setAdminFile_meta: true,
|
|
234
|
+
async setAdminFile({ id, name }, req) {
|
|
235
|
+
const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
236
|
+
if (!hasPermission(`admin/team-files`, loadedPermissions)) return false;
|
|
237
|
+
await storageUpdateTeamFileAdmin({ teamFileId: id, name });
|
|
238
|
+
socket.emitChanged('team-files-changed');
|
|
239
|
+
return true;
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
deleteAdminFile_meta: true,
|
|
243
|
+
async deleteAdminFile({ id }, req) {
|
|
244
|
+
const loadedPermissions = await loadPermissionsFromRequest(req);
|
|
245
|
+
if (!hasPermission(`admin/team-files`, loadedPermissions)) return false;
|
|
246
|
+
await storageDeleteFile(id);
|
|
247
|
+
socket.emitChanged('team-files-changed');
|
|
248
|
+
return true;
|
|
249
|
+
},
|
|
250
|
+
};
|
|
@@ -1,19 +1,8 @@
|
|
|
1
1
|
const crypto = require('crypto');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const { uploadsdir
|
|
4
|
-
const { getLogger
|
|
3
|
+
const { uploadsdir } = require('../utility/directories');
|
|
4
|
+
const { getLogger } = require('dbgate-tools');
|
|
5
5
|
const logger = getLogger('uploads');
|
|
6
|
-
const axios = require('axios');
|
|
7
|
-
const os = require('os');
|
|
8
|
-
const fs = require('fs/promises');
|
|
9
|
-
const { read } = require('./queryHistory');
|
|
10
|
-
const platformInfo = require('../utility/platformInfo');
|
|
11
|
-
const _ = require('lodash');
|
|
12
|
-
const serverConnections = require('./serverConnections');
|
|
13
|
-
const config = require('./config');
|
|
14
|
-
const gistSecret = require('../gistSecret');
|
|
15
|
-
const currentVersion = require('../currentVersion');
|
|
16
|
-
const socket = require('../utility/socket');
|
|
17
6
|
|
|
18
7
|
module.exports = {
|
|
19
8
|
upload_meta: {
|
|
@@ -51,88 +40,70 @@ module.exports = {
|
|
|
51
40
|
res.sendFile(path.join(uploadsdir(), req.query.file));
|
|
52
41
|
},
|
|
53
42
|
|
|
54
|
-
|
|
55
|
-
|
|
43
|
+
// uploadErrorToGist_meta: true,
|
|
44
|
+
// async uploadErrorToGist() {
|
|
45
|
+
// const logs = await fs.readFile(getLogsFilePath(), { encoding: 'utf-8' });
|
|
46
|
+
// const connections = await serverConnections.getOpenedConnectionReport();
|
|
47
|
+
// try {
|
|
48
|
+
// const response = await axios.default.post(
|
|
49
|
+
// 'https://api.github.com/gists',
|
|
50
|
+
// {
|
|
51
|
+
// description: `DbGate ${currentVersion.version} error report`,
|
|
52
|
+
// public: false,
|
|
53
|
+
// files: {
|
|
54
|
+
// 'logs.jsonl': {
|
|
55
|
+
// content: logs,
|
|
56
|
+
// },
|
|
57
|
+
// 'os.json': {
|
|
58
|
+
// content: JSON.stringify(
|
|
59
|
+
// {
|
|
60
|
+
// release: os.release(),
|
|
61
|
+
// arch: os.arch(),
|
|
62
|
+
// machine: os.machine(),
|
|
63
|
+
// platform: os.platform(),
|
|
64
|
+
// type: os.type(),
|
|
65
|
+
// },
|
|
66
|
+
// null,
|
|
67
|
+
// 2
|
|
68
|
+
// ),
|
|
69
|
+
// },
|
|
70
|
+
// 'platform.json': {
|
|
71
|
+
// content: JSON.stringify(
|
|
72
|
+
// _.omit(
|
|
73
|
+
// {
|
|
74
|
+
// ...platformInfo,
|
|
75
|
+
// },
|
|
76
|
+
// ['defaultKeyfile', 'sshAuthSock']
|
|
77
|
+
// ),
|
|
78
|
+
// null,
|
|
79
|
+
// 2
|
|
80
|
+
// ),
|
|
81
|
+
// },
|
|
82
|
+
// 'connections.json': {
|
|
83
|
+
// content: JSON.stringify(connections, null, 2),
|
|
84
|
+
// },
|
|
85
|
+
// 'version.json': {
|
|
86
|
+
// content: JSON.stringify(currentVersion, null, 2),
|
|
87
|
+
// },
|
|
88
|
+
// },
|
|
89
|
+
// },
|
|
90
|
+
// {
|
|
91
|
+
// headers: {
|
|
92
|
+
// Authorization: `token ${await this.getGistToken()}`,
|
|
93
|
+
// 'Content-Type': 'application/json',
|
|
94
|
+
// Accept: 'application/vnd.github.v3+json',
|
|
95
|
+
// },
|
|
96
|
+
// }
|
|
97
|
+
// );
|
|
56
98
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
uploadErrorToGist_meta: true,
|
|
61
|
-
async uploadErrorToGist() {
|
|
62
|
-
const logs = await fs.readFile(getLogsFilePath(), { encoding: 'utf-8' });
|
|
63
|
-
const connections = await serverConnections.getOpenedConnectionReport();
|
|
64
|
-
try {
|
|
65
|
-
const response = await axios.default.post(
|
|
66
|
-
'https://api.github.com/gists',
|
|
67
|
-
{
|
|
68
|
-
description: `DbGate ${currentVersion.version} error report`,
|
|
69
|
-
public: false,
|
|
70
|
-
files: {
|
|
71
|
-
'logs.jsonl': {
|
|
72
|
-
content: logs,
|
|
73
|
-
},
|
|
74
|
-
'os.json': {
|
|
75
|
-
content: JSON.stringify(
|
|
76
|
-
{
|
|
77
|
-
release: os.release(),
|
|
78
|
-
arch: os.arch(),
|
|
79
|
-
machine: os.machine(),
|
|
80
|
-
platform: os.platform(),
|
|
81
|
-
type: os.type(),
|
|
82
|
-
},
|
|
83
|
-
null,
|
|
84
|
-
2
|
|
85
|
-
),
|
|
86
|
-
},
|
|
87
|
-
'platform.json': {
|
|
88
|
-
content: JSON.stringify(
|
|
89
|
-
_.omit(
|
|
90
|
-
{
|
|
91
|
-
...platformInfo,
|
|
92
|
-
},
|
|
93
|
-
['defaultKeyfile', 'sshAuthSock']
|
|
94
|
-
),
|
|
95
|
-
null,
|
|
96
|
-
2
|
|
97
|
-
),
|
|
98
|
-
},
|
|
99
|
-
'connections.json': {
|
|
100
|
-
content: JSON.stringify(connections, null, 2),
|
|
101
|
-
},
|
|
102
|
-
'version.json': {
|
|
103
|
-
content: JSON.stringify(currentVersion, null, 2),
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
headers: {
|
|
109
|
-
Authorization: `token ${await this.getGistToken()}`,
|
|
110
|
-
'Content-Type': 'application/json',
|
|
111
|
-
Accept: 'application/vnd.github.v3+json',
|
|
112
|
-
},
|
|
113
|
-
}
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
return response.data;
|
|
117
|
-
} catch (err) {
|
|
118
|
-
logger.error(extractErrorLogData(err), 'DBGM-00148 Error uploading gist');
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
apiErrorMessage: err.message,
|
|
122
|
-
};
|
|
123
|
-
// console.error('Error creating gist:', error.response ? error.response.data : error.message);
|
|
124
|
-
}
|
|
125
|
-
},
|
|
99
|
+
// return response.data;
|
|
100
|
+
// } catch (err) {
|
|
101
|
+
// logger.error(extractErrorLogData(err), 'DBGM-00148 Error uploading gist');
|
|
126
102
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
Accept: 'application/vnd.github.v3+json',
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
return true;
|
|
137
|
-
},
|
|
103
|
+
// return {
|
|
104
|
+
// apiErrorMessage: err.message,
|
|
105
|
+
// };
|
|
106
|
+
// // console.error('Error creating gist:', error.response ? error.response.data : error.message);
|
|
107
|
+
// }
|
|
108
|
+
// },
|
|
138
109
|
};
|
package/src/currentVersion.js
CHANGED
package/src/main.js
CHANGED
|
@@ -29,6 +29,8 @@ const files = require('./controllers/files');
|
|
|
29
29
|
const scheduler = require('./controllers/scheduler');
|
|
30
30
|
const queryHistory = require('./controllers/queryHistory');
|
|
31
31
|
const cloud = require('./controllers/cloud');
|
|
32
|
+
const teamFiles = require('./controllers/teamFiles');
|
|
33
|
+
|
|
32
34
|
const onFinished = require('on-finished');
|
|
33
35
|
const processArgs = require('./utility/processArgs');
|
|
34
36
|
|
|
@@ -264,6 +266,7 @@ function useAllControllers(app, electron) {
|
|
|
264
266
|
useController(app, electron, '/apps', apps);
|
|
265
267
|
useController(app, electron, '/auth', auth);
|
|
266
268
|
useController(app, electron, '/cloud', cloud);
|
|
269
|
+
useController(app, electron, '/team-files', teamFiles);
|
|
267
270
|
}
|
|
268
271
|
|
|
269
272
|
function setElectronSender(electronSender) {
|