Skip to content

Commit

Permalink
Merge branch 'siem_audit_settings' into 'master'
Browse files Browse the repository at this point in the history
feat(ui): Integration of audit logs with SIEM systems

See merge request postgres-ai/database-lab!947
  • Loading branch information
Bogdan Tsechoev committed Dec 26, 2024
2 parents dc881a6 + 77c3404 commit 8f54b18
Show file tree
Hide file tree
Showing 11 changed files with 974 additions and 18 deletions.
3 changes: 2 additions & 1 deletion ui/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@
"sparql",
"SPARQL",
"subtransactions",
"mbox"
"mbox",
"SIEM"
]
}
114 changes: 113 additions & 1 deletion ui/packages/platform/src/actions/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const Actions = Reflux.createActions([{
updateOrg: ASYNC_ACTION,
createOrg: ASYNC_ACTION,
updateAiBotSettings: ASYNC_ACTION,
updateAuditSettings: ASYNC_ACTION,
inviteUser: ASYNC_ACTION,
useDemoData: ASYNC_ACTION,
setReportsProject: {},
Expand Down Expand Up @@ -114,7 +115,9 @@ const Actions = Reflux.createActions([{
downloadDblabSessionArtifact: ASYNC_ACTION,
sendUserCode: ASYNC_ACTION,
confirmUserEmail: ASYNC_ACTION,
confirmTosAgreement: ASYNC_ACTION
confirmTosAgreement: ASYNC_ACTION,
testSiemServiceConnection: ASYNC_ACTION,
getAuditEvents: ASYNC_ACTION
}]);

function timeoutPromise(ms, promise) {
Expand Down Expand Up @@ -654,6 +657,42 @@ Actions.updateAiBotSettings.listen(function (token, orgId, orgData) {
});
});

Actions.updateAuditSettings.listen(function (token, orgId, orgData) {
let action = this;

if (!api) {
settings.init(function () {
api = new Api(settings);
});
}

action.progressed({ orgId } + orgData);
timeoutPromise(REQUEST_TIMEOUT, api.updateAuditSettings(token, orgId, orgData))

.then(result => {
result.json()
.then(json => {
if (json) {
action.completed(json);
} else {
action.failed(new Error('wrong_reply'));
}
})
.catch(err => {
console.error(err);
action.failed(new Error('wrong_reply'));
});
})
.catch(err => {
console.error(err);
if (err && err.message && err.message === 'timeout') {
action.failed(new Error('failed_fetch'));
} else {
action.failed(new Error('wrong_reply'));
}
});
});

Actions.createOrg.listen(function (token, orgData) {
let action = this;

Expand Down Expand Up @@ -1571,4 +1610,77 @@ Actions.confirmTosAgreement.listen(function (token) {
);
});


Actions.testSiemServiceConnection.listen(function (token, data) {
let action = this;

if (!api) {
settings.init(function () {
api = new Api(settings);
});
}

action.progressed(data);
timeoutPromise(REQUEST_TIMEOUT, api.testSiemServiceConnection(token, data))

.then(result => {
result.json()
.then(json => {
if (json) {
action.completed(json);
} else {
action.failed(new Error('wrong_reply'));
}
})
.catch(err => {
console.error(err);
action.failed(new Error('wrong_reply'));
});
})
.catch(err => {
console.error(err);
if (err && err.message && err.message === 'timeout') {
action.failed(new Error('failed_fetch'));
} else {
action.failed(new Error('wrong_reply'));
}
});
});

Actions.getAuditEvents.listen(function (token) {
let action = this;

if (!api) {
settings.init(function () {
api = new Api(settings);
});
}

action.progressed();

timeoutPromise(REQUEST_TIMEOUT, api.getAuditEvents(token))
.then(result => {
result.json()
.then(json => {
if (json) {
action.completed({ data: json });
} else {
action.failed(new Error('wrong_reply'));
}
})
.catch(err => {
console.error(err);
action.failed(new Error('wrong_reply'));
});
})
.catch(err => {
console.error(err);
if (err && err.message && err.message === 'timeout') {
action.failed(new Error('failed_fetch'));
} else {
action.failed(new Error('wrong_reply'));
}
});
});

export default Actions;
76 changes: 76 additions & 0 deletions ui/packages/platform/src/api/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,71 @@ class Api {
});
}

updateAuditSettings(token, orgId, orgData) {
let params = {};
let headers = {
Authorization: 'Bearer ' + token,
prefer: 'return=representation'
};

if (typeof orgData.enableSiemIntegration !== 'undefined') {
params.siem_integration_enabled = orgData.enableSiemIntegration;
}

if (typeof orgData.urlSchema !== 'undefined') {
params.siem_integration_url = orgData.urlSchema;
}

if (typeof orgData.auditEvents !== "undefined") {
params.audit_events_to_log = orgData.auditEvents.map((item) => item.event_name)
}

if (typeof orgData.headers !== 'undefined' && Array.isArray(orgData.headers)) {
orgData.headers = orgData.headers.filter(item => item.key && item.value);
if (Object.keys(orgData.headers).length > 0) {
params.siem_integration_request_headers = orgData.headers.reduce((acc, item) => {
acc[item.key] = item.value;
return acc;
}, {});
} else {
params.siem_integration_request_headers = null
}
}

return this.patch(`${this.apiServer}/orgs?id=eq.` + orgId, params, {
headers: headers
});
}

testSiemServiceConnection(token, data) {
let params = {};
let headers = {
Accept: 'application/vnd.pgrst.object+json',
Authorization: 'Bearer ' + token,
prefer: 'return=representation'
};

if (typeof data.urlSchema !== 'undefined') {
params.api_url = data.urlSchema;
}

if (typeof data.headers !== 'undefined' && Array.isArray(data.headers)) {
data.headers = data.headers.filter(item => item.key && item.value);
if (Object.keys(data.headers).length > 0) {
params.http_headers_extra = data.headers.reduce((acc, item) => {
acc[item.key] = item.value;
return acc;
}, {});
} else {
params.http_headers_extra = null
}
}

return this.post(`${this.apiServer}/rpc/test_siem_connection`, params, {
headers: headers
});
}

inviteUser(token, orgId, email) {
let headers = {
Authorization: 'Bearer ' + token
Expand Down Expand Up @@ -992,6 +1057,17 @@ class Api {
{ headers }
);
}

getAuditEvents(token) {
let params = {};
let headers = {
Authorization: 'Bearer ' + token
};

return this.get(`${this.apiServer}/audit_events`, params, {
headers: headers
});
}
}

export default Api;
12 changes: 6 additions & 6 deletions ui/packages/platform/src/components/Audit/Audit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export interface AuditLogData {
action: string
actor: string
action_data: {
processed_rows_count: number
processed_row_count: number
}
created_at: string
table_name: string
Expand Down Expand Up @@ -155,11 +155,11 @@ class Audit extends Component<AuditWithStylesProps, AuditState> {
actorSrc = ' (changed directly in database) '
}

if (r.action_data && r.action_data.processed_rows_count) {
if (r.action_data && r.action_data.processed_row_count) {
rows =
r.action_data.processed_rows_count +
r.action_data.processed_row_count +
' ' +
(r.action_data.processed_rows_count > 1 ? 'rows' : 'row')
(r.action_data.processed_row_count > 1 ? 'rows' : 'row')
}

switch (r.action) {
Expand Down Expand Up @@ -197,8 +197,8 @@ class Audit extends Component<AuditWithStylesProps, AuditState> {
? r.data_before?.length
: r.data_after?.length
const objCount =
r.action_data && r.action_data.processed_rows_count
? r.action_data.processed_rows_count
r.action_data && r.action_data.processed_row_count
? r.action_data.processed_row_count
: null

if (displayedCount && (objCount as number) > displayedCount) {
Expand Down
Loading

0 comments on commit 8f54b18

Please sign in to comment.