mirror of
https://github.com/quay/quay.git
synced 2025-04-18 10:44:06 +03:00
logs: Audit export logs requests (PROJQUAY-7679) (#3146)
* logs: Audit export logs requests (PROJQUAY-7679)) We add the ability to audit export logs requests that were previously not tracked. * Add UI elements to properly render new audit log * Truncate date/time column on exterme zooms * Add initdb.py entries * Fix migration and add test db data * Add test database and fix migration paths * Changed logging mechanism to grab raised exceptions * Fix improper import * Add date/time timestamp to saved metadata * Change message on export logs screen in UI * Changed message in old UI as well * Change log description in new UI too * Simplify call logic and add additonal information to logged errors
This commit is contained in:
parent
212cb80741
commit
77bc70a637
@ -0,0 +1,36 @@
|
||||
"""Add export logs auditing
|
||||
|
||||
Revision ID: 8e97c2cfee57
|
||||
Revises: 9085e82074f2
|
||||
Create Date: 2024-08-19 13:56:50.063519
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "8e97c2cfee57"
|
||||
down_revision = "9085e82074f2"
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade(op, tables, tester):
|
||||
op.bulk_insert(
|
||||
tables.logentrykind,
|
||||
[
|
||||
{"name": "export_logs_success"},
|
||||
{"name": "export_logs_failure"},
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def downgrade(op, tables, tester):
|
||||
op.execute(
|
||||
tables.logentrykind.delete().where(
|
||||
tables.logentrykind.c.name == op.inline_literal("export_logs_success")
|
||||
)
|
||||
)
|
||||
op.execute(
|
||||
tables.logentrykind.delete().where(
|
||||
tables.logentrykind.c.name == op.inline_literal("export_logs_failure")
|
||||
)
|
||||
)
|
@ -20,6 +20,7 @@ from endpoints.api import (
|
||||
allow_if_global_readonly_superuser,
|
||||
allow_if_superuser,
|
||||
format_date,
|
||||
log_action,
|
||||
nickname,
|
||||
page_support,
|
||||
parse_args,
|
||||
@ -335,7 +336,7 @@ def _queue_logs_export(start_time, end_time, options, namespace_name, repository
|
||||
|
||||
(start_time, end_time) = _validate_logs_arguments(start_time, end_time)
|
||||
if end_time < start_time:
|
||||
abort(400)
|
||||
raise InvalidLogsDateRangeError("Invalid time span selected")
|
||||
export_id = logs_model.queue_logs_export(
|
||||
start_time,
|
||||
end_time,
|
||||
@ -351,6 +352,45 @@ def _queue_logs_export(start_time, end_time, options, namespace_name, repository
|
||||
return export_id
|
||||
|
||||
|
||||
def _log_export_success(user_or_org_name, export_id, request, repository=None):
|
||||
|
||||
metadata = {
|
||||
"date/time": datetime.utcnow(),
|
||||
"export_id": export_id,
|
||||
"message": "queued for export",
|
||||
"url": request.get_json().get("callback_url") or None,
|
||||
"email": request.get_json().get("callback_email") or None,
|
||||
}
|
||||
|
||||
if repository:
|
||||
metadata["repo"] = repository
|
||||
|
||||
log_action(
|
||||
"export_logs_success",
|
||||
user_or_org_name,
|
||||
metadata,
|
||||
)
|
||||
|
||||
|
||||
def _log_export_failure(user_or_org_name, request, ex, repository=None):
|
||||
|
||||
metadata = {
|
||||
"date/time": datetime.utcnow(),
|
||||
"error": ex,
|
||||
"url": request.get_json().get("callback_url") or None,
|
||||
"email": request.get_json().get("callback_email") or None,
|
||||
}
|
||||
|
||||
if repository:
|
||||
metadata["repo"] = repository
|
||||
|
||||
log_action(
|
||||
"export_logs_failure",
|
||||
user_or_org_name,
|
||||
metadata,
|
||||
)
|
||||
|
||||
|
||||
@resource("/v1/repository/<apirepopath:repository>/exportlogs")
|
||||
@show_if(features.LOG_EXPORT)
|
||||
@path_param("repository", "The full path of the repository. e.g. namespace/name")
|
||||
@ -372,13 +412,21 @@ class ExportRepositoryLogs(RepositoryParamResource):
|
||||
Queues an export of the logs for the specified repository.
|
||||
"""
|
||||
if registry_model.lookup_repository(namespace, repository) is None:
|
||||
_log_export_failure(namespace, request, "non-existent repository", repository)
|
||||
raise NotFound()
|
||||
|
||||
start_time = parsed_args["starttime"]
|
||||
end_time = parsed_args["endtime"]
|
||||
export_id = _queue_logs_export(
|
||||
start_time, end_time, request.get_json(), namespace, repository_name=repository
|
||||
)
|
||||
try:
|
||||
export_id = _queue_logs_export(
|
||||
start_time, end_time, request.get_json(), namespace, repository_name=repository
|
||||
)
|
||||
except (InvalidRequest, InvalidLogsDateRangeError) as ex:
|
||||
_log_export_failure(namespace, request, ex, repository)
|
||||
abort(400, ex)
|
||||
|
||||
_log_export_success(namespace, export_id, request, repository)
|
||||
|
||||
return {
|
||||
"export_id": export_id,
|
||||
}
|
||||
@ -403,11 +451,19 @@ class ExportUserLogs(ApiResource):
|
||||
"""
|
||||
Returns the aggregated logs for the current user.
|
||||
"""
|
||||
user = get_authenticated_user()
|
||||
|
||||
start_time = parsed_args["starttime"]
|
||||
end_time = parsed_args["endtime"]
|
||||
|
||||
user = get_authenticated_user()
|
||||
export_id = _queue_logs_export(start_time, end_time, request.get_json(), user.username)
|
||||
try:
|
||||
export_id = _queue_logs_export(start_time, end_time, request.get_json(), user.username)
|
||||
except (InvalidRequest, InvalidLogsDateRangeError) as ex:
|
||||
_log_export_failure(user.username, request, ex, None)
|
||||
abort(400, ex)
|
||||
|
||||
_log_export_success(user.username, export_id, request, None)
|
||||
|
||||
return {
|
||||
"export_id": export_id,
|
||||
}
|
||||
@ -439,9 +495,17 @@ class ExportOrgLogs(ApiResource):
|
||||
start_time = parsed_args["starttime"]
|
||||
end_time = parsed_args["endtime"]
|
||||
|
||||
export_id = _queue_logs_export(start_time, end_time, request.get_json(), orgname)
|
||||
try:
|
||||
export_id = _queue_logs_export(start_time, end_time, request.get_json(), orgname)
|
||||
except (InvalidRequest, InvalidLogsDateRangeError) as ex:
|
||||
_log_export_failure(orgname, request, ex, None)
|
||||
abort(400, ex)
|
||||
|
||||
_log_export_success(orgname, export_id, request, None)
|
||||
|
||||
return {
|
||||
"export_id": export_id,
|
||||
}
|
||||
|
||||
_log_export_failure(orgname, request, "unauthorized", None)
|
||||
raise Unauthorized()
|
||||
|
@ -483,6 +483,9 @@ def initialize_database():
|
||||
LogEntryKind.create(name="oauth_token_assigned")
|
||||
LogEntryKind.create(name="oauth_token_revoked")
|
||||
|
||||
LogEntryKind.create(name="export_logs_success")
|
||||
LogEntryKind.create(name="export_logs_failure")
|
||||
|
||||
ImageStorageLocation.create(name="local_eu")
|
||||
ImageStorageLocation.create(name="local_us")
|
||||
|
||||
|
@ -608,6 +608,28 @@ angular.module('quay').directive('logsView', function () {
|
||||
'oauth_token_assigned': function (metadata) {
|
||||
return 'OAuth token assigned to user {assigned_user} by {assigning_user} for application {application}[[with client id {client_id}]]';
|
||||
},
|
||||
export_logs_success: function (metadata) {
|
||||
if (metadata.repo) {
|
||||
if (metadata.url) {
|
||||
return `Logs export queued for delivery: id ${metadata.export_id}, url: ${metadata.url}, repository: ${metadata.repo}`;
|
||||
} else if (metadata.email) {
|
||||
return `Logs export queued for delivery: id ${metadata.export_id}, email: ${obfuscate_email(metadata.email)}, repository: ${metadata.repo}`;
|
||||
} else {
|
||||
return `Logs export queued for delivery: id ${metadata.export_id}, url: ${metadata.url}, email: ${obfuscate_email(metadata.email)}, repository: ${metadata.repo}`;
|
||||
}
|
||||
} else {
|
||||
if (metadata.url) {
|
||||
return `User/organization logs export queued for delivery: id ${metadata.export_id}, url: ${metadata.url}`;
|
||||
} else if (metadata.email) {
|
||||
return `User/organization logs export queued for delivery: id ${metadata.export_id}, email: ${obfuscate_email(metadata.email)}`;
|
||||
} else {
|
||||
return `User/organization logs export queued for delivery: id ${metadata.export_id}, url: ${metadata.url}, email: ${obfuscate_email(metadata.email)}`;
|
||||
}
|
||||
}
|
||||
},
|
||||
export_logs_failure: function (metadata) {
|
||||
return `Export logs failure: ${metadata.error}, ${metadata.repo ? `requested repository: ${metadata.repo}` : ''}`;
|
||||
},
|
||||
};
|
||||
|
||||
var logKinds = {
|
||||
@ -711,6 +733,8 @@ angular.module('quay').directive('logsView', function () {
|
||||
'login_failure': 'Login failure',
|
||||
'autoprune_tag_delete': 'Autoprune worker tag deletion',
|
||||
'oauth_token_assigned': 'OAuth token assigned',
|
||||
'export_logs_success': 'Export logs queued for delivery',
|
||||
'export_logs_failure': 'Export logs failure',
|
||||
|
||||
// Note: these are deprecated.
|
||||
'add_repo_webhook': 'Add webhook',
|
||||
@ -892,3 +916,8 @@ angular.module('quay').directive('logsView', function () {
|
||||
|
||||
return directiveDefinitionObject;
|
||||
});
|
||||
|
||||
function obfuscate_email(email) {
|
||||
const email_array = email.split('@');
|
||||
return email_array[0].substring(0, 2)+'*'.repeat(email_array[0].length - 2)+'@'+email_array[1];
|
||||
}
|
||||
|
@ -5742,7 +5742,7 @@ COPY public.accesstokenkind (id, name) FROM stdin;
|
||||
--
|
||||
|
||||
COPY public.alembic_version (version_num) FROM stdin;
|
||||
ba263f9be4a6
|
||||
8e97c2cfee57
|
||||
\.
|
||||
|
||||
|
||||
@ -6424,6 +6424,11 @@ COPY public.logentrykind (id, name) FROM stdin;
|
||||
113 oauth_token_assigned
|
||||
114 oauth_token_revoked
|
||||
115 change_tag_immutability
|
||||
116 create_robot_federation
|
||||
117 delete_robot_federation
|
||||
118 federated_robot_token_exchange
|
||||
119 export_logs_success
|
||||
120 export_logs_failure
|
||||
\.
|
||||
|
||||
|
||||
@ -6448,18 +6453,18 @@ COPY public.loginservice (id, name) FROM stdin;
|
||||
--
|
||||
|
||||
COPY public.manifest (id, repository_id, digest, media_type_id, manifest_bytes, config_media_type, layers_compressed_size, subject, subject_backfilled, artifact_type, artifact_type_backfilled) FROM stdin;
|
||||
1 1 sha256:f54a58bc1aac5ea1a25d796ae155dc228b3f0e11d046ae276b39c4bf2f13d8c4 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1469,\n "digest": "sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 2479,\n "digest": "sha256:2db29710123e3e53a794f2694094b9b4338aa9ee5c40b930cb8063a1be392c54"\n }\n ]\n} application/vnd.docker.container.image.v1+json 2479 \N \N \N \N
|
||||
2 1 sha256:7b8b7289d0536a08eabdf71c20246e23f7116641db7e1d278592236ea4dcb30c 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1482,\n "digest": "sha256:c0218de6585df06a66d67b25237bdda42137c727c367373a32639710c7a9fa94"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 3684,\n "digest": "sha256:b921b04d0447ddcd82a9220d887cd146f6ef39e20a938ee5e19a90fc3323e030"\n }\n ]\n} application/vnd.docker.container.image.v1+json 3684 \N \N \N \N
|
||||
3 1 sha256:f130bd2d67e6e9280ac6d0a6c83857bfaf70234e8ef4236876eccfbd30973b1c 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1482,\n "digest": "sha256:1ec996c686eb87d8f091080ec29dd1862b39b5822ddfd8f9a1e2c9288fad89fe"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 2993,\n "digest": "sha256:9b157615502ddff86482f7fe2fa7a074db74a62fce12b4e8507827ac8f08d0ce"\n }\n ]\n} application/vnd.docker.container.image.v1+json 2993 \N \N \N \N
|
||||
4 1 sha256:432f982638b3aefab73cc58ab28f5c16e96fdb504e8c134fc58dff4bae8bf338 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1485,\n "digest": "sha256:46331d942d6350436f64e614d75725f6de3bb5c63e266e236e04389820a234c4"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 3208,\n "digest": "sha256:7050e35b49f5e348c4809f5eff915842962cb813f32062d3bbdd35c750dd7d01"\n }\n ]\n} application/vnd.docker.container.image.v1+json 3208 \N \N \N \N
|
||||
5 1 sha256:995efde2e81b21d1ea7066aa77a59298a62a9e9fbb4b77f36c189774ec9b1089 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1468,\n "digest": "sha256:36d89aa75357c8f99e359f8cabc0aae667d47d8f25ed51cbe66e148e3a77e19c"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 2736,\n "digest": "sha256:7f0d4fad461d1ac69488092b5914b5ec642133c0fb884539045de33fbcd2eadb"\n }\n ]\n} application/vnd.docker.container.image.v1+json 2736 \N \N \N \N
|
||||
6 1 sha256:eb11b1a194ff8e236a01eff392c4e1296a53b0fb4780d8b0382f7996a15d5392 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1473,\n "digest": "sha256:5004e9d559e7a75f42249ddeca4d5764fa4db05592a7a9a641e4ac37cc619ba1"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 4092,\n "digest": "sha256:bbc6052697e5fdcd1b311e0b3f65189ffbe354cf8ae97e7a55d588e855097174"\n }\n ]\n} application/vnd.docker.container.image.v1+json 4092 \N \N \N \N
|
||||
7 1 sha256:b836bb24a270b9cc935962d8228517fde0f16990e88893d935efcb1b14c0017a 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1471,\n "digest": "sha256:61fff98d5ca765a4351964c8f4b5fb1a0d2c48458026f5452a389eb52d146fe8"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 3929,\n "digest": "sha256:33450689bfb495ed64ead935c9933f1d6b3e42fe369b8de9680cf4ff9d89ce5c"\n }\n ]\n} application/vnd.docker.container.image.v1+json 3929 \N \N \N \N
|
||||
8 1 sha256:98c9722322be649df94780d3fbe594fce7996234b259f27eac9428b84050c849 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1471,\n "digest": "sha256:b3593dab05491cdf5ee88c29bee36603c0df0bc34798eed5067f6e1335a9d391"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 3000,\n "digest": "sha256:3caa6dc69d0b73f21d29bfa75356395f2695a7abad34f010656740e90ddce399"\n }\n ]\n} application/vnd.docker.container.image.v1+json 3000 \N \N \N \N
|
||||
9 1 sha256:c7b6944911848ce39b44ed660d95fb54d69bbd531de724c7ce6fc9f743c0b861 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1469,\n "digest": "sha256:df5477cea5582b0ae6a31de2d1c9bbacb506091f42a3b0fe77a209006f409fd8"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 3276,\n "digest": "sha256:abc70fcc95b2f52b325d69cc5c259dd9babb40a9df152e88b286fada1d3248bd"\n }\n ]\n} application/vnd.docker.container.image.v1+json 3276 \N \N \N \N
|
||||
10 1 sha256:7693efac53eb85ff1afb03f7f2560015c57ac2175707f1f141f31161634c9dba 15 {"manifests":[{"digest":"sha256:f54a58bc1aac5ea1a25d796ae155dc228b3f0e11d046ae276b39c4bf2f13d8c4","mediaType":"application\\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"amd64","os":"linux"},"size":525},{"digest":"sha256:7b8b7289d0536a08eabdf71c20246e23f7116641db7e1d278592236ea4dcb30c","mediaType":"application\\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"arm","os":"linux","variant":"v5"},"size":525},{"digest":"sha256:f130bd2d67e6e9280ac6d0a6c83857bfaf70234e8ef4236876eccfbd30973b1c","mediaType":"application\\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"arm","os":"linux","variant":"v7"},"size":525},{"digest":"sha256:432f982638b3aefab73cc58ab28f5c16e96fdb504e8c134fc58dff4bae8bf338","mediaType":"application\\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"arm64","os":"linux","variant":"v8"},"size":525},{"digest":"sha256:995efde2e81b21d1ea7066aa77a59298a62a9e9fbb4b77f36c189774ec9b1089","mediaType":"application\\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"386","os":"linux"},"size":525},{"digest":"sha256:eb11b1a194ff8e236a01eff392c4e1296a53b0fb4780d8b0382f7996a15d5392","mediaType":"application\\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"mips64le","os":"linux"},"size":525},{"digest":"sha256:b836bb24a270b9cc935962d8228517fde0f16990e88893d935efcb1b14c0017a","mediaType":"application\\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"ppc64le","os":"linux"},"size":525},{"digest":"sha256:98c9722322be649df94780d3fbe594fce7996234b259f27eac9428b84050c849","mediaType":"application\\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"riscv64","os":"linux"},"size":525},{"digest":"sha256:c7b6944911848ce39b44ed660d95fb54d69bbd531de724c7ce6fc9f743c0b861","mediaType":"application\\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"s390x","os":"linux"},"size":525}],"mediaType":"application\\/vnd.docker.distribution.manifest.list.v2+json","schemaVersion":2} \N 0 \N \N \N \N
|
||||
11 155 sha256:f54a58bc1aac5ea1a25d796ae155dc228b3f0e11d046ae276b39c4bf2f13d8c4 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1469,\n "digest": "sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 2479,\n "digest": "sha256:2db29710123e3e53a794f2694094b9b4338aa9ee5c40b930cb8063a1be392c54"\n }\n ]\n} application/vnd.docker.container.image.v1+json 2479 \N \N \N \N
|
||||
12 1 sha256:7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1470,\n "digest": "sha256:9c7a54a9a43cca047013b82af109fe963fde787f63f9e016fdc3384500c2823d"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 2457,\n "digest": "sha256:719385e32844401d57ecfd3eacab360bf551a1491c05b85806ed8f1b08d792f6"\n }\n ]\n} application/vnd.docker.container.image.v1+json 2457 \N \N \N \N
|
||||
2 1 sha256:7b8b7289d0536a08eabdf71c20246e23f7116641db7e1d278592236ea4dcb30c 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1482,\n "digest": "sha256:c0218de6585df06a66d67b25237bdda42137c727c367373a32639710c7a9fa94"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 3684,\n "digest": "sha256:b921b04d0447ddcd82a9220d887cd146f6ef39e20a938ee5e19a90fc3323e030"\n }\n ]\n} application/vnd.docker.container.image.v1+json 3684 \N t \N t
|
||||
4 1 sha256:432f982638b3aefab73cc58ab28f5c16e96fdb504e8c134fc58dff4bae8bf338 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1485,\n "digest": "sha256:46331d942d6350436f64e614d75725f6de3bb5c63e266e236e04389820a234c4"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 3208,\n "digest": "sha256:7050e35b49f5e348c4809f5eff915842962cb813f32062d3bbdd35c750dd7d01"\n }\n ]\n} application/vnd.docker.container.image.v1+json 3208 \N t \N t
|
||||
5 1 sha256:995efde2e81b21d1ea7066aa77a59298a62a9e9fbb4b77f36c189774ec9b1089 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1468,\n "digest": "sha256:36d89aa75357c8f99e359f8cabc0aae667d47d8f25ed51cbe66e148e3a77e19c"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 2736,\n "digest": "sha256:7f0d4fad461d1ac69488092b5914b5ec642133c0fb884539045de33fbcd2eadb"\n }\n ]\n} application/vnd.docker.container.image.v1+json 2736 \N t \N t
|
||||
6 1 sha256:eb11b1a194ff8e236a01eff392c4e1296a53b0fb4780d8b0382f7996a15d5392 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1473,\n "digest": "sha256:5004e9d559e7a75f42249ddeca4d5764fa4db05592a7a9a641e4ac37cc619ba1"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 4092,\n "digest": "sha256:bbc6052697e5fdcd1b311e0b3f65189ffbe354cf8ae97e7a55d588e855097174"\n }\n ]\n} application/vnd.docker.container.image.v1+json 4092 \N t \N t
|
||||
7 1 sha256:b836bb24a270b9cc935962d8228517fde0f16990e88893d935efcb1b14c0017a 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1471,\n "digest": "sha256:61fff98d5ca765a4351964c8f4b5fb1a0d2c48458026f5452a389eb52d146fe8"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 3929,\n "digest": "sha256:33450689bfb495ed64ead935c9933f1d6b3e42fe369b8de9680cf4ff9d89ce5c"\n }\n ]\n} application/vnd.docker.container.image.v1+json 3929 \N t \N t
|
||||
8 1 sha256:98c9722322be649df94780d3fbe594fce7996234b259f27eac9428b84050c849 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1471,\n "digest": "sha256:b3593dab05491cdf5ee88c29bee36603c0df0bc34798eed5067f6e1335a9d391"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 3000,\n "digest": "sha256:3caa6dc69d0b73f21d29bfa75356395f2695a7abad34f010656740e90ddce399"\n }\n ]\n} application/vnd.docker.container.image.v1+json 3000 \N t \N t
|
||||
9 1 sha256:c7b6944911848ce39b44ed660d95fb54d69bbd531de724c7ce6fc9f743c0b861 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1469,\n "digest": "sha256:df5477cea5582b0ae6a31de2d1c9bbacb506091f42a3b0fe77a209006f409fd8"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 3276,\n "digest": "sha256:abc70fcc95b2f52b325d69cc5c259dd9babb40a9df152e88b286fada1d3248bd"\n }\n ]\n} application/vnd.docker.container.image.v1+json 3276 \N t \N t
|
||||
10 1 sha256:7693efac53eb85ff1afb03f7f2560015c57ac2175707f1f141f31161634c9dba 15 {"manifests":[{"digest":"sha256:f54a58bc1aac5ea1a25d796ae155dc228b3f0e11d046ae276b39c4bf2f13d8c4","mediaType":"application\\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"amd64","os":"linux"},"size":525},{"digest":"sha256:7b8b7289d0536a08eabdf71c20246e23f7116641db7e1d278592236ea4dcb30c","mediaType":"application\\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"arm","os":"linux","variant":"v5"},"size":525},{"digest":"sha256:f130bd2d67e6e9280ac6d0a6c83857bfaf70234e8ef4236876eccfbd30973b1c","mediaType":"application\\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"arm","os":"linux","variant":"v7"},"size":525},{"digest":"sha256:432f982638b3aefab73cc58ab28f5c16e96fdb504e8c134fc58dff4bae8bf338","mediaType":"application\\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"arm64","os":"linux","variant":"v8"},"size":525},{"digest":"sha256:995efde2e81b21d1ea7066aa77a59298a62a9e9fbb4b77f36c189774ec9b1089","mediaType":"application\\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"386","os":"linux"},"size":525},{"digest":"sha256:eb11b1a194ff8e236a01eff392c4e1296a53b0fb4780d8b0382f7996a15d5392","mediaType":"application\\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"mips64le","os":"linux"},"size":525},{"digest":"sha256:b836bb24a270b9cc935962d8228517fde0f16990e88893d935efcb1b14c0017a","mediaType":"application\\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"ppc64le","os":"linux"},"size":525},{"digest":"sha256:98c9722322be649df94780d3fbe594fce7996234b259f27eac9428b84050c849","mediaType":"application\\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"riscv64","os":"linux"},"size":525},{"digest":"sha256:c7b6944911848ce39b44ed660d95fb54d69bbd531de724c7ce6fc9f743c0b861","mediaType":"application\\/vnd.docker.distribution.manifest.v2+json","platform":{"architecture":"s390x","os":"linux"},"size":525}],"mediaType":"application\\/vnd.docker.distribution.manifest.list.v2+json","schemaVersion":2} \N 0 \N t \N t
|
||||
11 155 sha256:f54a58bc1aac5ea1a25d796ae155dc228b3f0e11d046ae276b39c4bf2f13d8c4 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1469,\n "digest": "sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 2479,\n "digest": "sha256:2db29710123e3e53a794f2694094b9b4338aa9ee5c40b930cb8063a1be392c54"\n }\n ]\n} application/vnd.docker.container.image.v1+json 2479 \N t \N t
|
||||
1 1 sha256:f54a58bc1aac5ea1a25d796ae155dc228b3f0e11d046ae276b39c4bf2f13d8c4 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1469,\n "digest": "sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 2479,\n "digest": "sha256:2db29710123e3e53a794f2694094b9b4338aa9ee5c40b930cb8063a1be392c54"\n }\n ]\n} application/vnd.docker.container.image.v1+json 2479 \N t \N t
|
||||
3 1 sha256:f130bd2d67e6e9280ac6d0a6c83857bfaf70234e8ef4236876eccfbd30973b1c 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1482,\n "digest": "sha256:1ec996c686eb87d8f091080ec29dd1862b39b5822ddfd8f9a1e2c9288fad89fe"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 2993,\n "digest": "sha256:9b157615502ddff86482f7fe2fa7a074db74a62fce12b4e8507827ac8f08d0ce"\n }\n ]\n} application/vnd.docker.container.image.v1+json 2993 \N t \N t
|
||||
12 1 sha256:7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3 16 {\n "schemaVersion": 2,\n "mediaType": "application/vnd.docker.distribution.manifest.v2+json",\n "config": {\n "mediaType": "application/vnd.docker.container.image.v1+json",\n "size": 1470,\n "digest": "sha256:9c7a54a9a43cca047013b82af109fe963fde787f63f9e016fdc3384500c2823d"\n },\n "layers": [\n {\n "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",\n "size": 2457,\n "digest": "sha256:719385e32844401d57ecfd3eacab360bf551a1491c05b85806ed8f1b08d792f6"\n }\n ]\n} application/vnd.docker.container.image.v1+json 2457 \N t \N t
|
||||
\.
|
||||
|
||||
|
||||
@ -6710,8 +6715,8 @@ COPY public.quayservice (id, name) FROM stdin;
|
||||
--
|
||||
|
||||
COPY public.queueitem (id, queue_name, body, available_after, available, processing_expires, retries_remaining, state_id) FROM stdin;
|
||||
1 namespacegc/2/ {"marker_id": 1, "original_username": "quay"} 2024-06-07 15:55:41.812038 t 2024-06-07 18:50:41.733122 5 348c6b78-b9a0-4db1-8fa5-f67e2e42d485
|
||||
2 namespacegc/3/ {"marker_id": 2, "original_username": "clair"} 2024-06-07 15:55:46.859942 t 2024-06-07 18:50:46.830201 5 81d862c7-29b5-426d-86cf-55241ed696a6
|
||||
2 namespacegc/3/ {"marker_id": 2, "original_username": "clair"} 2024-09-30 17:51:28.055159 t 2024-09-30 20:46:28.041972 5 245c3f97-bac6-4742-93eb-2b61f948c6b9
|
||||
1 namespacegc/2/ {"marker_id": 1, "original_username": "quay"} 2024-09-30 17:51:33.074743 t 2024-09-30 20:46:33.062514 5 daa3897c-92bf-4678-a49a-8dbf4efed18c
|
||||
\.
|
||||
|
||||
|
||||
@ -7735,23 +7740,23 @@ COPY public.star (id, user_id, repository_id, created) FROM stdin;
|
||||
-- Data for Name: tag; Type: TABLE DATA; Schema: public; Owner: quay
|
||||
--
|
||||
|
||||
COPY public.tag (id, name, repository_id, manifest_id, lifetime_start_ms, lifetime_end_ms, hidden, reversion, tag_kind_id, linked_tag_id) FROM stdin;
|
||||
2 $temp-14b356fd-6475-4036-8a51-6222469ec319 1 2 1667589325995 1667592925995 t f 1 \N
|
||||
3 $temp-4a1787d7-7f4c-4bb8-84d7-23e518be2e07 1 3 1667589327671 1667592927671 t f 1 \N
|
||||
4 $temp-9f0521e7-4e36-4934-b467-0358b158faad 1 4 1667589329223 1667592929223 t f 1 \N
|
||||
5 $temp-aa3c515a-80ce-47ab-b21f-1285e17d03ba 1 5 1667589330949 1667592930949 t f 1 \N
|
||||
6 $temp-2eb4b1d2-65da-4161-97cf-762d9bb4fe93 1 6 1667589332577 1667592932577 t f 1 \N
|
||||
7 $temp-4ddefd63-fdcb-43d6-8740-be334338e846 1 7 1667589334341 1667592934341 t f 1 \N
|
||||
8 $temp-7c67ad17-2b65-49b2-a9ce-e018689ce7f2 1 8 1667589335850 1667592935850 t f 1 \N
|
||||
9 $temp-64f33f3a-8551-49da-b36c-e4ab3a47c247 1 9 1667589337525 1667592937525 t f 1 \N
|
||||
10 manifestlist 1 10 1667589337924 \N f f 1 \N
|
||||
11 latest 155 11 1687976145896 \N f f 1 \N
|
||||
1 latest 1 1 1667589234345 1690479044462 f f 1 \N
|
||||
12 $temp-224d812a-ca19-4ccb-92c2-9a04a21924a7 1 1 1690479048789 1690479348789 t f 1 \N
|
||||
13 latest 1 1 1690479048814 1690479058287 f t 1 \N
|
||||
15 $temp-cfb6beff-645a-479d-be37-73ce2631b3c7 1 1 1690479070649 1690479370649 t f 1 \N
|
||||
14 latest 1 12 1690479058287 1690479070667 f f 1 \N
|
||||
16 latest 1 1 1690479070667 \N f t 1 \N
|
||||
COPY public.tag (id, name, repository_id, manifest_id, lifetime_start_ms, lifetime_end_ms, hidden, reversion, tag_kind_id, linked_tag_id, immutable) FROM stdin;
|
||||
2 $temp-14b356fd-6475-4036-8a51-6222469ec319 1 2 1667589325995 1667592925995 t f 1 \N f
|
||||
3 $temp-4a1787d7-7f4c-4bb8-84d7-23e518be2e07 1 3 1667589327671 1667592927671 t f 1 \N f
|
||||
4 $temp-9f0521e7-4e36-4934-b467-0358b158faad 1 4 1667589329223 1667592929223 t f 1 \N f
|
||||
5 $temp-aa3c515a-80ce-47ab-b21f-1285e17d03ba 1 5 1667589330949 1667592930949 t f 1 \N f
|
||||
6 $temp-2eb4b1d2-65da-4161-97cf-762d9bb4fe93 1 6 1667589332577 1667592932577 t f 1 \N f
|
||||
7 $temp-4ddefd63-fdcb-43d6-8740-be334338e846 1 7 1667589334341 1667592934341 t f 1 \N f
|
||||
8 $temp-7c67ad17-2b65-49b2-a9ce-e018689ce7f2 1 8 1667589335850 1667592935850 t f 1 \N f
|
||||
9 $temp-64f33f3a-8551-49da-b36c-e4ab3a47c247 1 9 1667589337525 1667592937525 t f 1 \N f
|
||||
10 manifestlist 1 10 1667589337924 \N f f 1 \N f
|
||||
11 latest 155 11 1687976145896 \N f f 1 \N f
|
||||
1 latest 1 1 1667589234345 1690479044462 f f 1 \N f
|
||||
12 $temp-224d812a-ca19-4ccb-92c2-9a04a21924a7 1 1 1690479048789 1690479348789 t f 1 \N f
|
||||
13 latest 1 1 1690479048814 1690479058287 f t 1 \N f
|
||||
15 $temp-cfb6beff-645a-479d-be37-73ce2631b3c7 1 1 1690479070649 1690479370649 t f 1 \N f
|
||||
14 latest 1 12 1690479058287 1690479070667 f f 1 \N f
|
||||
16 latest 1 1 1690479070667 \N f t 1 \N f
|
||||
\.
|
||||
|
||||
|
||||
@ -8295,7 +8300,7 @@ SELECT pg_catalog.setval('public.logentry_id_seq', 1, false);
|
||||
-- Name: logentrykind_id_seq; Type: SEQUENCE SET; Schema: public; Owner: quay
|
||||
--
|
||||
|
||||
SELECT pg_catalog.setval('public.logentrykind_id_seq', 115, true);
|
||||
SELECT pg_catalog.setval('public.logentrykind_id_seq', 120, true);
|
||||
|
||||
|
||||
--
|
||||
|
@ -632,7 +632,53 @@ export function useLogDescriptions() {
|
||||
oauth_token_assigned: function (metadata) {
|
||||
return `OAuth token assigned to user ${metadata.assigned_user} by ${metadata.assigning_user} for application ${metadata.application}`;
|
||||
},
|
||||
export_logs_success: function (metadata: Metadata) {
|
||||
if (metadata.repo) {
|
||||
if (metadata.url) {
|
||||
return `Logs export queued for delivery: id ${metadata.export_id}, url: ${metadata.url}, repository: ${metadata.repo}`;
|
||||
} else if (metadata.email) {
|
||||
return `Logs export queued for delivery: id ${
|
||||
metadata.export_id
|
||||
}, email: ${obfuscate_email(metadata.email)}, repository: ${
|
||||
metadata.repo
|
||||
}`;
|
||||
} else {
|
||||
return `Logs export queued for delivery: id ${
|
||||
metadata.export_id
|
||||
}, url: ${metadata.url}, email: ${obfuscate_email(
|
||||
metadata.email,
|
||||
)}, repository: ${metadata.repo}`;
|
||||
}
|
||||
} else {
|
||||
if (metadata.url) {
|
||||
return `User/organization logs export queued for delivery: id ${metadata.export_id}, url: ${metadata.url}`;
|
||||
} else if (metadata.email) {
|
||||
return `User/organization logs export queued for delivery: id ${
|
||||
metadata.export_id
|
||||
}, email: ${obfuscate_email(metadata.email)}`;
|
||||
} else {
|
||||
return `User/organization logs export queued for delivery: id ${
|
||||
metadata.export_id
|
||||
}, url: ${metadata.url}, email: ${obfuscate_email(metadata.email)}`;
|
||||
}
|
||||
}
|
||||
},
|
||||
export_logs_failure: function (metadata: Metadata) {
|
||||
return `Export logs failure: ${metadata.error}, ${
|
||||
metadata.repo ? `requested repository: ${metadata.repo}` : ''
|
||||
}`;
|
||||
},
|
||||
};
|
||||
|
||||
return descriptions;
|
||||
}
|
||||
|
||||
function obfuscate_email(email: Array) {
|
||||
const email_array = email.split('@');
|
||||
return (
|
||||
email_array[0].substring(0, 2) +
|
||||
'*'.repeat(email_array[0].length - 2) +
|
||||
'@' +
|
||||
email_array[1]
|
||||
);
|
||||
}
|
||||
|
@ -243,4 +243,6 @@ export const logKinds = {
|
||||
oauth_token_assigned: 'OAuth token assigned',
|
||||
enable_team_sync: 'Enable Team Sync',
|
||||
disable_team_sync: 'Disable Team Sync',
|
||||
export_logs_success: 'Export logs queued for delivery',
|
||||
export_logs_failure: 'Export logs failure',
|
||||
};
|
||||
|
@ -7,7 +7,15 @@ import {
|
||||
Split,
|
||||
SplitItem,
|
||||
} from '@patternfly/react-core';
|
||||
import {Table, Tbody, Td, Th, Thead, Tr} from '@patternfly/react-table';
|
||||
import {
|
||||
Table,
|
||||
TableText,
|
||||
Tbody,
|
||||
Td,
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
} from '@patternfly/react-table';
|
||||
import {useInfiniteQuery} from '@tanstack/react-query';
|
||||
import RequestError from 'src/components/errors/RequestError';
|
||||
import {getLogs} from 'src/hooks/UseUsageLogs';
|
||||
@ -92,17 +100,21 @@ export function UsageLogsTable(props: UsageLogsTableProps) {
|
||||
<Table variant="compact" borders={false} style={{margin: '20px'}}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>Date & Time</Th>
|
||||
<Th width={15}>Date & Time</Th>
|
||||
<Th>Description</Th>
|
||||
<Th>Performed by</Th>
|
||||
<Th>IP address</Th>
|
||||
<Th>IP Address</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{logs.pages.map((logPage: any) =>
|
||||
logPage.logs.map((log: any, index: number) => (
|
||||
<Tr key={index}>
|
||||
<Td>{new Date(log.datetime).toLocaleString()}</Td>
|
||||
<Td>
|
||||
<TableText wrapModifier="truncate">
|
||||
{new Date(log.datetime).toLocaleString()}
|
||||
</TableText>
|
||||
</Td>
|
||||
<Td>
|
||||
{logDescriptions[log.kind]
|
||||
? logDescriptions[log.kind](log.metadata)
|
||||
|
Loading…
x
Reference in New Issue
Block a user