Skip to content

Commit 4ba4916

Browse files
committed
feature: add GDPR compliance
1 parent 389de84 commit 4ba4916

19 files changed

+4265
-41
lines changed

GDPR_COMPLIANCE_GUIDE.md

Lines changed: 879 additions & 0 deletions
Large diffs are not rendered by default.

spec/AuditLogAdapter.spec.js

Lines changed: 465 additions & 0 deletions
Large diffs are not rendered by default.

spec/AuditLogController.spec.js

Lines changed: 491 additions & 0 deletions
Large diffs are not rendered by default.

spec/AuditLogSchemas.spec.js

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
'use strict';
2+
3+
const fs = require('fs');
4+
const path = require('path');
5+
6+
describe('Audit Logging - Schema Operations', () => {
7+
const testLogFolder = path.join(__dirname, 'temp-audit-logs-schema');
8+
9+
beforeEach(async () => {
10+
if (fs.existsSync(testLogFolder)) {
11+
fs.rmSync(testLogFolder, { recursive: true, force: true });
12+
}
13+
14+
await reconfigureServer({
15+
auditLog: {
16+
auditLogFolder: testLogFolder,
17+
},
18+
});
19+
});
20+
21+
afterEach(async () => {
22+
if (fs.existsSync(testLogFolder)) {
23+
fs.rmSync(testLogFolder, { recursive: true, force: true });
24+
}
25+
});
26+
27+
it('should log schema creation', async () => {
28+
const schema = {
29+
className: 'AuditSchemaTest',
30+
fields: {
31+
testField: { type: 'String' },
32+
},
33+
};
34+
35+
await request({
36+
method: 'POST',
37+
url: Parse.serverURL + '/schemas',
38+
body: schema,
39+
headers: {
40+
'X-Parse-Application-Id': Parse.applicationId,
41+
'X-Parse-Master-Key': Parse.masterKey,
42+
},
43+
});
44+
45+
await new Promise(resolve => setTimeout(resolve, 200));
46+
47+
const logFiles = fs.readdirSync(testLogFolder);
48+
expect(logFiles.length).toBeGreaterThan(0);
49+
50+
const logFile = path.join(testLogFolder, logFiles[0]);
51+
const logContent = fs.readFileSync(logFile, 'utf8');
52+
53+
expect(logContent).toContain('SCHEMA_MODIFY');
54+
expect(logContent).toContain('AuditSchemaTest');
55+
expect(logContent).toContain('create');
56+
});
57+
58+
it('should log schema update', async () => {
59+
const schema = {
60+
className: 'AuditSchemaUpdate',
61+
fields: {
62+
field1: { type: 'String' },
63+
},
64+
};
65+
66+
await request({
67+
method: 'POST',
68+
url: Parse.serverURL + '/schemas',
69+
body: schema,
70+
headers: {
71+
'X-Parse-Application-Id': Parse.applicationId,
72+
'X-Parse-Master-Key': Parse.masterKey,
73+
},
74+
});
75+
76+
await new Promise(resolve => setTimeout(resolve, 200));
77+
const logFiles1 = fs.readdirSync(testLogFolder);
78+
if (logFiles1.length > 0) {
79+
fs.unlinkSync(path.join(testLogFolder, logFiles1[0]));
80+
}
81+
82+
await request({
83+
method: 'PUT',
84+
url: Parse.serverURL + '/schemas/AuditSchemaUpdate',
85+
body: {
86+
fields: {
87+
field2: { type: 'Number' },
88+
},
89+
},
90+
headers: {
91+
'X-Parse-Application-Id': Parse.applicationId,
92+
'X-Parse-Master-Key': Parse.masterKey,
93+
},
94+
});
95+
96+
await new Promise(resolve => setTimeout(resolve, 200));
97+
98+
const logFiles = fs.readdirSync(testLogFolder);
99+
expect(logFiles.length).toBeGreaterThan(0);
100+
101+
const logFile = path.join(testLogFolder, logFiles[0]);
102+
const logContent = fs.readFileSync(logFile, 'utf8');
103+
104+
expect(logContent).toContain('SCHEMA_MODIFY');
105+
expect(logContent).toContain('AuditSchemaUpdate');
106+
expect(logContent).toContain('update');
107+
});
108+
109+
it('should log schema deletion', async () => {
110+
const schema = {
111+
className: 'AuditSchemaDelete',
112+
fields: {
113+
field1: { type: 'String' },
114+
},
115+
};
116+
117+
await request({
118+
method: 'POST',
119+
url: Parse.serverURL + '/schemas',
120+
body: schema,
121+
headers: {
122+
'X-Parse-Application-Id': Parse.applicationId,
123+
'X-Parse-Master-Key': Parse.masterKey,
124+
},
125+
});
126+
127+
await new Promise(resolve => setTimeout(resolve, 200));
128+
const logFiles1 = fs.readdirSync(testLogFolder);
129+
if (logFiles1.length > 0) {
130+
fs.unlinkSync(path.join(testLogFolder, logFiles1[0]));
131+
}
132+
133+
await request({
134+
method: 'DELETE',
135+
url: Parse.serverURL + '/schemas/AuditSchemaDelete',
136+
headers: {
137+
'X-Parse-Application-Id': Parse.applicationId,
138+
'X-Parse-Master-Key': Parse.masterKey,
139+
},
140+
});
141+
142+
await new Promise(resolve => setTimeout(resolve, 200));
143+
144+
const logFiles = fs.readdirSync(testLogFolder);
145+
expect(logFiles.length).toBeGreaterThan(0);
146+
147+
const logFile = path.join(testLogFolder, logFiles[0]);
148+
const logContent = fs.readFileSync(logFile, 'utf8');
149+
150+
expect(logContent).toContain('SCHEMA_MODIFY');
151+
expect(logContent).toContain('AuditSchemaDelete');
152+
expect(logContent).toContain('delete');
153+
});
154+
155+
it('should log failed schema creation', async () => {
156+
const schema = {
157+
className: '_InvalidClassName',
158+
fields: {
159+
testField: { type: 'String' },
160+
},
161+
};
162+
163+
try {
164+
await request({
165+
method: 'POST',
166+
url: Parse.serverURL + '/schemas',
167+
body: schema,
168+
headers: {
169+
'X-Parse-Application-Id': Parse.applicationId,
170+
'X-Parse-Master-Key': Parse.masterKey,
171+
},
172+
});
173+
} catch (error) {
174+
// Expected to fail
175+
}
176+
177+
await new Promise(resolve => setTimeout(resolve, 200));
178+
179+
const logFiles = fs.readdirSync(testLogFolder);
180+
if (logFiles.length > 0) {
181+
const logFile = path.join(testLogFolder, logFiles[0]);
182+
const logContent = fs.readFileSync(logFile, 'utf8');
183+
184+
expect(logContent).toContain('SCHEMA_MODIFY');
185+
expect(logContent).toContain('"success":false');
186+
}
187+
});
188+
189+
it('should capture user context in schema logs', async () => {
190+
const schema = {
191+
className: 'AuditSchemaUser',
192+
fields: {
193+
field1: { type: 'String' },
194+
},
195+
};
196+
197+
await request({
198+
method: 'POST',
199+
url: Parse.serverURL + '/schemas',
200+
body: schema,
201+
headers: {
202+
'X-Parse-Application-Id': Parse.applicationId,
203+
'X-Parse-Master-Key': Parse.masterKey,
204+
},
205+
});
206+
207+
await new Promise(resolve => setTimeout(resolve, 200));
208+
209+
const logFiles = fs.readdirSync(testLogFolder);
210+
const logFile = path.join(testLogFolder, logFiles[0]);
211+
const logContent = fs.readFileSync(logFile, 'utf8');
212+
213+
expect(logContent).toContain('SCHEMA_MODIFY');
214+
});
215+
216+
it('should log schema changes with field details', async () => {
217+
const schema = {
218+
className: 'AuditSchemaFields',
219+
fields: {
220+
stringField: { type: 'String' },
221+
numberField: { type: 'Number' },
222+
pointerField: { type: 'Pointer', targetClass: '_User' },
223+
},
224+
};
225+
226+
await request({
227+
method: 'POST',
228+
url: Parse.serverURL + '/schemas',
229+
body: schema,
230+
headers: {
231+
'X-Parse-Application-Id': Parse.applicationId,
232+
'X-Parse-Master-Key': Parse.masterKey,
233+
},
234+
});
235+
236+
await new Promise(resolve => setTimeout(resolve, 200));
237+
238+
const logFiles = fs.readdirSync(testLogFolder);
239+
const logFile = path.join(testLogFolder, logFiles[0]);
240+
const logContent = fs.readFileSync(logFile, 'utf8');
241+
242+
expect(logContent).toContain('SCHEMA_MODIFY');
243+
expect(logContent).toContain('stringField');
244+
expect(logContent).toContain('numberField');
245+
});
246+
});

0 commit comments

Comments
 (0)