forked from shaba/openuds
Merge remote-tracking branch 'origin/v3.6'
This commit is contained in:
commit
2908b99435
@ -31,7 +31,7 @@ Author: Adolfo Gómez, dkmaster at dkmon dot com
|
|||||||
"""
|
"""
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
from uds.models import Log, getSqlDatetime
|
from uds import models
|
||||||
from uds.core.util.log import (
|
from uds.core.util.log import (
|
||||||
REST,
|
REST,
|
||||||
OWNER_TYPE_AUDIT,
|
OWNER_TYPE_AUDIT,
|
||||||
@ -40,31 +40,68 @@ from uds.core.util.log import (
|
|||||||
WARNING,
|
WARNING,
|
||||||
ERROR,
|
ERROR,
|
||||||
CRITICAL,
|
CRITICAL,
|
||||||
# These are legacy support until full removal
|
|
||||||
WARN,
|
|
||||||
FATAL,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from .handlers import Handler
|
from .handlers import Handler
|
||||||
|
|
||||||
|
# This structct allows us to perform the following:
|
||||||
|
# If path has ".../providers/[uuid]/..." we will replace uuid with "provider nanme" sourrounded by []
|
||||||
|
# If path has ".../services/[uuid]/..." we will replace uuid with "service name" sourrounded by []
|
||||||
|
# If path has ".../users/[uuid]/..." we will replace uuid with "user name" sourrounded by []
|
||||||
|
# If path has ".../groups/[uuid]/..." we will replace uuid with "group name" sourrounded by []
|
||||||
|
UUID_REPLACER = (
|
||||||
|
('providers', models.Provider),
|
||||||
|
('services', models.Service),
|
||||||
|
('users', models.User),
|
||||||
|
('groups', models.Group),
|
||||||
|
)
|
||||||
|
|
||||||
def log_operation(handler: typing.Optional['Handler'], response_code: int, level: int = INFO):
|
|
||||||
|
def replacePath(path: str) -> str:
|
||||||
|
"""Replaces uuids in path with names
|
||||||
|
All paths are in the form .../type/uuid/...
|
||||||
|
"""
|
||||||
|
for type, model in UUID_REPLACER:
|
||||||
|
if f'/{type}/' in path:
|
||||||
|
try:
|
||||||
|
uuid = path.split(f'/{type}/')[1].split('/')[0]
|
||||||
|
name = model.objects.get(uuid=uuid).name # type: ignore
|
||||||
|
path = path.replace(uuid, f'[{name}]')
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def log_operation(
|
||||||
|
handler: typing.Optional['Handler'], response_code: int, level: int = INFO
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Logs a request
|
Logs a request
|
||||||
"""
|
"""
|
||||||
if not handler:
|
if not handler:
|
||||||
return # Nothing to log
|
return # Nothing to log
|
||||||
|
|
||||||
|
path = handler._request.path
|
||||||
|
|
||||||
|
# If a common request, and no error, we don't log it because it's useless and a waste of resources
|
||||||
|
if response_code < 400 and any(
|
||||||
|
x in path for x in ('overview', 'tableinfo', 'gui', 'types', 'system')
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
path = replacePath(path)
|
||||||
|
|
||||||
username = handler._request.user.pretty_name if handler._request.user else 'Unknown'
|
username = handler._request.user.pretty_name if handler._request.user else 'Unknown'
|
||||||
# Global log is used without owner nor type
|
# Global log is used without owner nor type
|
||||||
Log.objects.create(
|
models.Log.objects.create(
|
||||||
owner_id=0,
|
owner_id=0,
|
||||||
owner_type=OWNER_TYPE_AUDIT,
|
owner_type=OWNER_TYPE_AUDIT,
|
||||||
created=getSqlDatetime(),
|
created=models.getSqlDatetime(),
|
||||||
level=level,
|
level=level,
|
||||||
source=REST,
|
source=REST,
|
||||||
data=f'{username}: [{handler._request.method}/{response_code}] {handler._request.path}'[
|
data=f'{handler._request.ip} {username}: [{handler._request.method}/{response_code}] {path}'[
|
||||||
:255
|
:255
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -163,7 +163,7 @@ class gui:
|
|||||||
# Helper to convert an item to a dict
|
# Helper to convert an item to a dict
|
||||||
def choiceFromValue(val: typing.Union[str, typing.Dict[str, str]]) -> typing.Dict[str, str]:
|
def choiceFromValue(val: typing.Union[str, typing.Dict[str, str]]) -> typing.Dict[str, str]:
|
||||||
if isinstance(val, str):
|
if isinstance(val, str):
|
||||||
return {'id': val, 'name': val}
|
return {'id': val, 'text': val}
|
||||||
# Return a deepcopy
|
# Return a deepcopy
|
||||||
return copy.deepcopy(val)
|
return copy.deepcopy(val)
|
||||||
|
|
||||||
|
@ -227,6 +227,7 @@ class SMSMFA(mfas.MFA):
|
|||||||
cls.networks.setValues([
|
cls.networks.setValues([
|
||||||
gui.choiceItem(v.uuid, v.name)
|
gui.choiceItem(v.uuid, v.name)
|
||||||
for v in models.Network.objects.all().order_by('name')
|
for v in models.Network.objects.all().order_by('name')
|
||||||
|
if v.uuid
|
||||||
])
|
])
|
||||||
|
|
||||||
def composeSmsUrl(self, userId: str, userName: str, code: str, phone: str) -> str:
|
def composeSmsUrl(self, userId: str, userName: str, code: str, phone: str) -> str:
|
||||||
|
@ -80,7 +80,7 @@ class ListReportAuditCSV(ListReport):
|
|||||||
def genData(self) -> typing.Generator[typing.Tuple, None, None]:
|
def genData(self) -> typing.Generator[typing.Tuple, None, None]:
|
||||||
# Xtract user method, response_code and request from data
|
# Xtract user method, response_code and request from data
|
||||||
# the format is "user: [method/response_code] request"
|
# the format is "user: [method/response_code] request"
|
||||||
rx = re.compile(r'(?P<user>[^:]*): \[(?P<method>[^/]*)/(?P<response_code>[^\]]*)\] (?P<request>.*)')
|
rx = re.compile(r'(?P<ip>[^ ]*) (?P<user>[^:]*): \[(?P<method>[^/]*)/(?P<response_code>[^\]]*)\] (?P<request>.*)')
|
||||||
|
|
||||||
start = self.startDate.datetime().replace(hour=0, minute=0, second=0, microsecond=0)
|
start = self.startDate.datetime().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
end = self.endDate.datetime().replace(hour=23, minute=59, second=59, microsecond=999999)
|
end = self.endDate.datetime().replace(hour=23, minute=59, second=59, microsecond=999999)
|
||||||
@ -91,10 +91,6 @@ class ListReportAuditCSV(ListReport):
|
|||||||
m = rx.match(i.data)
|
m = rx.match(i.data)
|
||||||
|
|
||||||
if m is not None:
|
if m is not None:
|
||||||
# Skip fields with 200 and one of this words on the request
|
|
||||||
# overview, tableinfo, gui, types, system
|
|
||||||
if m.group('response_code') == '200' and any(x in m.group('request') for x in ('overview', 'tableinfo', 'gui', 'types', 'system')):
|
|
||||||
continue
|
|
||||||
# Convert response code to an string if 200, else, to an error
|
# Convert response code to an string if 200, else, to an error
|
||||||
response_code = {
|
response_code = {
|
||||||
'200': 'OK',
|
'200': 'OK',
|
||||||
@ -105,10 +101,11 @@ class ListReportAuditCSV(ListReport):
|
|||||||
'405': 'Method Not Allowed',
|
'405': 'Method Not Allowed',
|
||||||
'500': 'Internal Server Error',
|
'500': 'Internal Server Error',
|
||||||
'501': 'Not Implemented',
|
'501': 'Not Implemented',
|
||||||
}.get(m.group('response_code'), 'CODE: ' + m.group('response_code'))
|
}.get(m.group('response_code'), 'Code: ' + m.group('response_code'))
|
||||||
yield (
|
yield (
|
||||||
i.created,
|
i.created,
|
||||||
i.level_str,
|
i.level_str,
|
||||||
|
m.group('ip'),
|
||||||
m.group('user'),
|
m.group('user'),
|
||||||
m.group('method'),
|
m.group('method'),
|
||||||
response_code,
|
response_code,
|
||||||
@ -120,7 +117,7 @@ class ListReportAuditCSV(ListReport):
|
|||||||
writer = csv.writer(output)
|
writer = csv.writer(output)
|
||||||
|
|
||||||
writer.writerow(
|
writer.writerow(
|
||||||
[ugettext('Date'), ugettext('Level'), ugettext('User'), ugettext('Method'), ugettext('Response code'), ugettext('Request')]
|
[ugettext('Date'), ugettext('Level'), ugettext('IP'), ugettext('User'), ugettext('Method'), ugettext('Response code'), ugettext('Request')]
|
||||||
)
|
)
|
||||||
|
|
||||||
for l in self.genData():
|
for l in self.genData():
|
||||||
|
33
server/src/uds/templates/uds/reports/lists/audit.html
Normal file
33
server/src/uds/templates/uds/reports/lists/audit.html
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{% load l10n i18n %}
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Users Report</title>
|
||||||
|
<meta name="author" content="UDS">
|
||||||
|
<meta name="description" content="List of UDS audit logs">
|
||||||
|
<meta name="keywords" content="uds,report,audit,log,list">
|
||||||
|
<meta name="generator" content="UDS Reporting">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<table style="width: 100%; font-size: 0.8em;">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 25%">{% trans 'Date' %}</th>
|
||||||
|
<th style="width: 15%">{% trans 'Level' %}</th>
|
||||||
|
<th style="width: 60%">{% trans 'Message' %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for log in logs %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ log.date }}</td>
|
||||||
|
<td>{{ log.level }}</td>
|
||||||
|
<td>{{ log.message }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<!-- <p style="page-break-before: always">
|
||||||
|
This is a new page
|
||||||
|
</p> -->
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
x
Reference in New Issue
Block a user