You've already forked oncall
mirror of
https://github.com/linkedin/oncall.git
synced 2025-11-27 23:18:38 +02:00
Add GET API for audit log
This commit is contained in:
committed by
Joe Gillotti
parent
c2fa89f6d8
commit
06ad13ae62
@@ -8,16 +8,13 @@ from oncall.constants import (EVENT_CREATED, EVENT_EDITED, EVENT_SWAPPED, EVENT_
|
||||
TEAM_CREATED, TEAM_EDITED, TEAM_DELETED, ROSTER_EDITED, ROSTER_USER_ADDED,
|
||||
ROSTER_CREATED, ROSTER_USER_EDITED, ROSTER_USER_DELETED, ROSTER_DELETED, ADMIN_DELETED,
|
||||
ADMIN_CREATED)
|
||||
from oncall import db
|
||||
|
||||
def get_audit_log(start, end):
|
||||
connection = db.connect()
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('SELECT action_name FROM audit WHERE timestamp BETWEEN %s AND %s', (int(start), int(end) + 1))
|
||||
ret = {row[0] for row in cursor}
|
||||
cursor.close()
|
||||
connection.close()
|
||||
return ret
|
||||
re = requests.get(api_v0('audit?start=%s&end=%s&' % (start, end)))
|
||||
assert re.status_code == 200
|
||||
data = re.json()
|
||||
actions = set(audit['action'] for audit in data)
|
||||
return actions
|
||||
|
||||
|
||||
@prefix('test_audit')
|
||||
@@ -28,7 +25,7 @@ def test_audit(team, user, role, roster, event):
|
||||
team_name = team.create()
|
||||
|
||||
# test team actions
|
||||
start = time.time()
|
||||
start = int(time.time())
|
||||
team_name_2 = team.create()
|
||||
requests.put(api_v0('teams/'+team_name_2), json={'email': 'foo', 'slack_channel': 'bar'})
|
||||
requests.delete(api_v0('teams/%s' % team_name_2))
|
||||
@@ -37,7 +34,7 @@ def test_audit(team, user, role, roster, event):
|
||||
assert {TEAM_CREATED, TEAM_DELETED, TEAM_EDITED} <= audit
|
||||
|
||||
# test roster actions
|
||||
start = time.time()
|
||||
start = int(time.time())
|
||||
roster_name = roster.create(team_name)
|
||||
requests.put(api_v0('teams/%s/rosters/%s' % (team_name, roster_name)), json={'name': 'foo'})
|
||||
requests.put(api_v0('teams/%s/rosters/foo' % team_name), json={'name': roster_name})
|
||||
@@ -55,7 +52,7 @@ def test_audit(team, user, role, roster, event):
|
||||
ROSTER_EDITED, ROSTER_USER_EDITED} <= audit
|
||||
|
||||
# test event actions
|
||||
start = time.time()
|
||||
start = int(time.time())
|
||||
ev_start, ev_end = int(time.time()) + 100, int(time.time()) + 36000
|
||||
user.add_to_team(user_name, team_name)
|
||||
user.add_to_team(user_name_2, team_name)
|
||||
@@ -94,7 +91,7 @@ def test_audit(team, user, role, roster, event):
|
||||
<= audit
|
||||
|
||||
# add/delete admin
|
||||
start = time.time()
|
||||
start = int(time.time())
|
||||
requests.post(api_v0('teams/%s/admins' % team_name), json={'name': user_name})
|
||||
requests.delete(api_v0('teams/%s/admins/%s' % (team_name, user_name)))
|
||||
end = time.time()
|
||||
|
||||
@@ -74,6 +74,9 @@ def init(application, config):
|
||||
from . import search
|
||||
application.add_route('/api/v0/search', search)
|
||||
|
||||
from . import audit
|
||||
application.add_route('/api/v0/audit', audit)
|
||||
|
||||
from . import upcoming_shifts
|
||||
application.add_route('/api/v0/users/{user_name}/upcoming', upcoming_shifts)
|
||||
|
||||
|
||||
89
src/oncall/api/v0/audit.py
Normal file
89
src/oncall/api/v0/audit.py
Normal file
@@ -0,0 +1,89 @@
|
||||
# Copyright (c) LinkedIn Corporation. All rights reserved. Licensed under the BSD-2 Clause license.
|
||||
# See LICENSE in the project root for license information.
|
||||
|
||||
from ujson import dumps as json_dumps
|
||||
from ... import db
|
||||
|
||||
|
||||
filters = {'owner': '`owner_name` = %(owner)s',
|
||||
'team': '`team_name` = %(team)s',
|
||||
'action': '`action_name` IN %(action)s',
|
||||
'start': '`timestamp` >= %(start)s',
|
||||
'end': '`timestamp` <= %(end)s'}
|
||||
|
||||
|
||||
def on_get(req, resp):
|
||||
'''
|
||||
Search audit log. Allows filtering based on a number of parameters,
|
||||
detailed below. Returns an entry in the audit log, including the name
|
||||
of the associated team, action owner, and action type, as well as a
|
||||
timestamp and the action context. The context tracks different data
|
||||
based on the action, which may be useful in investigating.
|
||||
Audit logs are tracked for the following actions:
|
||||
|
||||
* admin_created
|
||||
* event_created
|
||||
* event_edited
|
||||
* roster_created
|
||||
* roster_edited
|
||||
* roster_user_added
|
||||
* roster_user_deleted
|
||||
* team_created
|
||||
* team_edited
|
||||
* event_deleted
|
||||
* event_swapped
|
||||
* roster_user_edited
|
||||
* team_deleted
|
||||
* admin_deleted
|
||||
* roster_deleted
|
||||
* event_substituted
|
||||
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /api/v0/audit?team=foo-sre&end=1487466146&action=event_created HTTP/1.1
|
||||
Host: example.com
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
[
|
||||
{
|
||||
"context":"{"new_event_id":441072,"request_body":{"start":1518422400,"end":1518595200,"role":"primary","user":jdoe","team":"foo-sre"}}"
|
||||
"timestamp": 1488441600,
|
||||
"team_name": "foo-sre",
|
||||
"owner_name": "jdoe"
|
||||
"action_name: "event_created"
|
||||
}
|
||||
]
|
||||
|
||||
:query team_name: team name
|
||||
:query owner_name: action owner name
|
||||
:query action_name: name of action taken. If provided multiple action names,
|
||||
:query id: id of the event
|
||||
:query start: lower bound for audit entry's timestamp (unix timestamp)
|
||||
:query end: upper bound for audit entry's timestamp (unix timestamp)
|
||||
'''
|
||||
connection = db.connect()
|
||||
cursor = connection.cursor(db.DictCursor)
|
||||
if 'action' in req.params:
|
||||
req.params['action'] = req.get_param_as_list('action')
|
||||
|
||||
query = '''SELECT `owner_name` AS `owner`, `team_name` AS `team`,
|
||||
`action_name` AS `action`, `timestamp`, `context`
|
||||
FROM `audit`'''
|
||||
where = ' AND '.join(filters[field] for field in req.params if field in filters)
|
||||
if where:
|
||||
query = '%s WHERE %s' % (query, where)
|
||||
|
||||
cursor.execute(query, req.params)
|
||||
results = cursor.fetchall()
|
||||
cursor.close()
|
||||
connection.close()
|
||||
resp.body = json_dumps(results)
|
||||
Reference in New Issue
Block a user