Merge remote-tracking branch 'origin/v3.6'

This commit is contained in:
Adolfo Gómez García 2022-10-05 23:56:54 +02:00
commit 2908b99435
No known key found for this signature in database
GPG Key ID: DD1ABF20724CDA23
5 changed files with 85 additions and 17 deletions

View File

@ -31,7 +31,7 @@ Author: Adolfo Gómez, dkmaster at dkmon dot com
"""
import typing
from uds.models import Log, getSqlDatetime
from uds import models
from uds.core.util.log import (
REST,
OWNER_TYPE_AUDIT,
@ -40,31 +40,68 @@ from uds.core.util.log import (
WARNING,
ERROR,
CRITICAL,
# These are legacy support until full removal
WARN,
FATAL,
)
if typing.TYPE_CHECKING:
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
"""
if not handler:
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'
# Global log is used without owner nor type
Log.objects.create(
models.Log.objects.create(
owner_id=0,
owner_type=OWNER_TYPE_AUDIT,
created=getSqlDatetime(),
created=models.getSqlDatetime(),
level=level,
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
],
)

View File

@ -163,7 +163,7 @@ class gui:
# Helper to convert an item to a dict
def choiceFromValue(val: typing.Union[str, typing.Dict[str, str]]) -> typing.Dict[str, str]:
if isinstance(val, str):
return {'id': val, 'name': val}
return {'id': val, 'text': val}
# Return a deepcopy
return copy.deepcopy(val)

View File

@ -227,6 +227,7 @@ class SMSMFA(mfas.MFA):
cls.networks.setValues([
gui.choiceItem(v.uuid, v.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:

View File

@ -80,7 +80,7 @@ class ListReportAuditCSV(ListReport):
def genData(self) -> typing.Generator[typing.Tuple, None, None]:
# Xtract user method, response_code and request from data
# 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)
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)
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
response_code = {
'200': 'OK',
@ -105,10 +101,11 @@ class ListReportAuditCSV(ListReport):
'405': 'Method Not Allowed',
'500': 'Internal Server Error',
'501': 'Not Implemented',
}.get(m.group('response_code'), 'CODE: ' + m.group('response_code'))
}.get(m.group('response_code'), 'Code: ' + m.group('response_code'))
yield (
i.created,
i.level_str,
m.group('ip'),
m.group('user'),
m.group('method'),
response_code,
@ -120,7 +117,7 @@ class ListReportAuditCSV(ListReport):
writer = csv.writer(output)
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():

View 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>