invenio-audit-logs 1.0.0.dev0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. invenio_audit_logs/__init__.py +18 -0
  2. invenio_audit_logs/alembic/1743073617_create_audit_logs_branch.py +27 -0
  3. invenio_audit_logs/alembic/1743073720_create_audit_logs_table.py +57 -0
  4. invenio_audit_logs/config.py +42 -0
  5. invenio_audit_logs/ext.py +47 -0
  6. invenio_audit_logs/proxies.py +16 -0
  7. invenio_audit_logs/records/__init__.py +12 -0
  8. invenio_audit_logs/records/api.py +51 -0
  9. invenio_audit_logs/records/models.py +27 -0
  10. invenio_audit_logs/records/templates/__init__.py +10 -0
  11. invenio_audit_logs/records/templates/os-v1/__init__.py +8 -0
  12. invenio_audit_logs/records/templates/os-v1/datastream-auditlog-v1.0.0.json +73 -0
  13. invenio_audit_logs/records/templates/os-v2/__init__.py +8 -0
  14. invenio_audit_logs/records/templates/os-v2/datastream-auditlog-v1.0.0.json +73 -0
  15. invenio_audit_logs/resources/__init__.py +17 -0
  16. invenio_audit_logs/resources/config.py +57 -0
  17. invenio_audit_logs/resources/resource.py +68 -0
  18. invenio_audit_logs/services/__init__.py +18 -0
  19. invenio_audit_logs/services/config.py +109 -0
  20. invenio_audit_logs/services/generators.py +24 -0
  21. invenio_audit_logs/services/permissions.py +24 -0
  22. invenio_audit_logs/services/results.py +113 -0
  23. invenio_audit_logs/services/schema.py +140 -0
  24. invenio_audit_logs/services/service.py +88 -0
  25. invenio_audit_logs/services/uow.py +31 -0
  26. invenio_audit_logs/views.py +18 -0
  27. invenio_audit_logs-1.0.0.dev0.dist-info/METADATA +78 -0
  28. invenio_audit_logs-1.0.0.dev0.dist-info/RECORD +33 -0
  29. invenio_audit_logs-1.0.0.dev0.dist-info/WHEEL +6 -0
  30. invenio_audit_logs-1.0.0.dev0.dist-info/entry_points.txt +18 -0
  31. invenio_audit_logs-1.0.0.dev0.dist-info/licenses/AUTHORS.rst +13 -0
  32. invenio_audit_logs-1.0.0.dev0.dist-info/licenses/LICENSE +21 -0
  33. invenio_audit_logs-1.0.0.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,18 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # This file is part of Invenio.
4
+ # Copyright (C) 2025 CERN.
5
+ #
6
+ # Invenio-Audit-Logs is free software; you can redistribute it and/or modify
7
+ # it under the terms of the MIT License; see LICENSE file for more details.
8
+
9
+ """Module providing audit logging features for Invenio.."""
10
+
11
+ from .ext import InvenioAuditLogs
12
+
13
+ __version__ = "1.0.0.dev0"
14
+
15
+ __all__ = (
16
+ "__version__",
17
+ "InvenioAuditLogs",
18
+ )
@@ -0,0 +1,27 @@
1
+ #
2
+ # This file is part of Invenio.
3
+ # Copyright (C) 2016-2018 CERN.
4
+ #
5
+ # Invenio-Audit-Logs is free software; you can redistribute it and/or modify it
6
+ # under the terms of the MIT License; see LICENSE file for more details.
7
+
8
+ """Create Audit logs branch."""
9
+
10
+ import sqlalchemy as sa
11
+ from alembic import op
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision = "1743073617"
15
+ down_revision = None
16
+ branch_labels = ("invenio_audit_logs",)
17
+ depends_on = "dbdbc1b19cf2"
18
+
19
+
20
+ def upgrade():
21
+ """Upgrade database."""
22
+ pass
23
+
24
+
25
+ def downgrade():
26
+ """Downgrade database."""
27
+ pass
@@ -0,0 +1,57 @@
1
+ #
2
+ # This file is part of Invenio.
3
+ # Copyright (C) 2016-2018 CERN.
4
+ #
5
+ # Invenio-Audit-Logs is free software; you can redistribute it and/or modify it
6
+ # under the terms of the MIT License; see LICENSE file for more details.
7
+
8
+ """Create Audit Logs table."""
9
+
10
+ import sqlalchemy as sa
11
+ import sqlalchemy_utils
12
+ from alembic import op
13
+ from sqlalchemy.dialects import mysql, postgresql
14
+
15
+ # revision identifiers, used by Alembic.
16
+ revision = "1743073720"
17
+ down_revision = "1743073617"
18
+ branch_labels = ()
19
+ depends_on = None
20
+
21
+
22
+ def upgrade():
23
+ """Upgrade database."""
24
+ op.create_table(
25
+ "audit_logs_metadata",
26
+ sa.Column("id", sqlalchemy_utils.types.uuid.UUIDType(), nullable=False),
27
+ sa.Column(
28
+ "created",
29
+ sa.DateTime().with_variant(mysql.DATETIME(fsp=6), "mysql"),
30
+ nullable=False,
31
+ ),
32
+ sa.Column(
33
+ "updated",
34
+ sa.DateTime().with_variant(mysql.DATETIME(fsp=6), "mysql"),
35
+ nullable=False,
36
+ ),
37
+ sa.Column("action", sa.String(length=255), nullable=False),
38
+ sa.Column("resource_type", sa.String(length=255), nullable=False),
39
+ sa.Column("user_id", sa.String(length=255), nullable=False),
40
+ sa.Column(
41
+ "json",
42
+ sa.JSON()
43
+ .with_variant(sqlalchemy_utils.types.json.JSONType(), "mysql")
44
+ .with_variant(
45
+ postgresql.JSONB(none_as_null=True, astext_type=sa.Text()), "postgresql"
46
+ )
47
+ .with_variant(sqlalchemy_utils.types.json.JSONType(), "sqlite"),
48
+ nullable=True,
49
+ ),
50
+ sa.Column("version_id", sa.Integer(), nullable=False),
51
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_audit_logs_metadata")),
52
+ )
53
+
54
+
55
+ def downgrade():
56
+ """Downgrade database."""
57
+ op.drop_table("audit_logs_metadata")
@@ -0,0 +1,42 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2025 CERN.
4
+ #
5
+ # Invenio-Audit-Logs is free software; you can redistribute it and/or modify
6
+ # it under the terms of the MIT License; see LICENSE file for more details.
7
+
8
+ """Configuration for invenio-audit-logs."""
9
+
10
+ from invenio_records_resources.services.records.facets import TermsFacet
11
+
12
+ AUDIT_LOGS_SEARCH = {
13
+ "facets": ["resource"],
14
+ "sort": [
15
+ "bestmatch",
16
+ "newest",
17
+ "oldest",
18
+ ],
19
+ # query_parser_cls
20
+ }
21
+ """Search configuration for audit logs."""
22
+
23
+ AUDIT_LOGS_FACETS = {
24
+ "resource": dict(
25
+ facet=TermsFacet(
26
+ field="resource_type",
27
+ label="Resource",
28
+ value_labels={
29
+ "record": "Record",
30
+ "community": "Community",
31
+ }, # Add user later
32
+ ),
33
+ ui=dict(field="resource_type"),
34
+ ),
35
+ }
36
+
37
+ AUDIT_LOGS_SORT_OPTIONS = {
38
+ "bestmatch": dict(title="Best match", fields=["_score"]),
39
+ "newest": dict(title="Newest", fields=["-@timestamp"]),
40
+ "oldest": dict(title="Oldest", fields=["@timestamp"]),
41
+ }
42
+ """Sort options for audit logs."""
@@ -0,0 +1,47 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2025 CERN.
4
+ #
5
+ # Invenio-Audit-Logs is free software; you can redistribute it and/or modify
6
+ # it under the terms of the MIT License; see LICENSE file for more details.
7
+
8
+ """Module providing audit logging features for Invenio.."""
9
+
10
+ from . import config
11
+ from .resources import AuditLogResource, AuditLogResourceConfig
12
+ from .services import AuditLogService, AuditLogServiceConfig
13
+
14
+
15
+ class InvenioAuditLogs(object):
16
+ """Invenio-Audit-Logs extension."""
17
+
18
+ def __init__(self, app=None):
19
+ """Extension initialization."""
20
+ if app:
21
+ self.init_app(app)
22
+
23
+ def init_app(self, app):
24
+ """Flask application initialization."""
25
+ self.init_config(app)
26
+ self.init_services(app)
27
+ self.init_resources(app)
28
+ app.extensions["invenio-audit-logs"] = self
29
+
30
+ def init_config(self, app):
31
+ """Initialize configuration."""
32
+ for k in dir(config):
33
+ if k.startswith("AUDIT_LOGS_"):
34
+ app.config.setdefault(k, getattr(config, k))
35
+
36
+ def init_services(self, app):
37
+ """Initialize services."""
38
+ self.audit_log_service = AuditLogService(
39
+ config=AuditLogServiceConfig.build(app),
40
+ )
41
+
42
+ def init_resources(self, app):
43
+ """Init resources."""
44
+ self.audit_log_resource = AuditLogResource(
45
+ service=self.audit_log_service,
46
+ config=AuditLogResourceConfig.build(app),
47
+ )
@@ -0,0 +1,16 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2025 CERN.
4
+ #
5
+ # Invenio-Jobs is free software; you can redistribute it and/or modify it
6
+ # under the terms of the MIT License; see LICENSE file for more details.
7
+
8
+ """Proxies."""
9
+
10
+ from flask import current_app
11
+ from werkzeug.local import LocalProxy
12
+
13
+ current_audit_logs_service = LocalProxy(
14
+ lambda: current_app.extensions["invenio-audit-logs"].audit_log_service
15
+ )
16
+ """Proxy to an instance of ``AuditLogs`` service."""
@@ -0,0 +1,12 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2025 CERN.
4
+ #
5
+ # Invenio-Audit-Logs is free software; you can redistribute it and/or modify it
6
+ # under the terms of the MIT License; see LICENSE file for more details.
7
+
8
+ """Audit Log data layer definitions."""
9
+
10
+ from .api import AuditLog
11
+
12
+ __all__ = ("AuditLog",)
@@ -0,0 +1,51 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # This file is part of Invenio.
4
+ # Copyright (C) 2025 CERN.
5
+ #
6
+ # Invenio-Audit-Logs is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+
9
+ """API classes for audit log event."""
10
+
11
+ from datetime import datetime
12
+ from uuid import UUID
13
+
14
+ from invenio_records.dumpers import SearchDumper
15
+ from invenio_records.systemfields import DictField, ModelField
16
+ from invenio_records_resources.records.api import Record
17
+ from invenio_records_resources.records.systemfields import IndexField
18
+
19
+ from . import models
20
+
21
+
22
+ class AuditLog(Record):
23
+ """API class to represent a structured audit-log event."""
24
+
25
+ model_cls = models.AuditLog
26
+ """The model class for the log."""
27
+
28
+ dumper = SearchDumper(
29
+ model_fields={
30
+ "id": ("id", UUID),
31
+ "created": ("@timestamp", datetime),
32
+ },
33
+ )
34
+ """Search dumper with configured dump keys."""
35
+
36
+ index = IndexField("auditlog-audit-log-v1.0.0", search_alias="auditlog")
37
+ """The search engine index to use."""
38
+
39
+ id = ModelField("id", dump_type=UUID)
40
+
41
+ created = ModelField("created", dump_type=datetime, dump_key="@timestamp")
42
+
43
+ action = ModelField("action", dump_type=str)
44
+
45
+ user_id = ModelField("user_id", dump=False, dump_type=str)
46
+
47
+ user = DictField("user")
48
+
49
+ resource_type = ModelField("resource_type", dump=False, dump_type=str)
50
+
51
+ resource = DictField("resource")
@@ -0,0 +1,27 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2025 CERN.
4
+ #
5
+ # Invenio-Audit-Logs is free software; you can redistribute it and/or modify it
6
+ # under the terms of the MIT License; see LICENSE file for more details.
7
+
8
+ """Base model classes for Audit Logs in Invenio."""
9
+
10
+
11
+ from invenio_db import db
12
+ from invenio_records.models import RecordMetadataBase
13
+ from sqlalchemy.types import String
14
+
15
+
16
+ class AuditLog(db.Model, RecordMetadataBase):
17
+ """Model class for Audit Log."""
18
+
19
+ __tablename__ = "audit_logs_metadata"
20
+
21
+ encoder = None
22
+
23
+ action = db.Column(String(255), nullable=False)
24
+
25
+ resource_type = db.Column(String(255), nullable=False)
26
+
27
+ user_id = db.Column(String(255), nullable=False)
@@ -0,0 +1,10 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # This file is part of Invenio.
4
+ # Copyright (C) 2025 CERN.
5
+ #
6
+ # Invenio-Audit-Logs is free software; you can redistribute it and/or modify it
7
+ # under the terms of the MIT License; see LICENSE file for more details.
8
+
9
+
10
+ """Audit Logs datastream index templates."""
@@ -0,0 +1,8 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2025 CERN.
4
+ #
5
+ # Invenio-Audit-Logs is free software; you can redistribute it and/or modify
6
+ # it under the terms of the MIT License; see LICENSE file for more details.
7
+
8
+ """OpenSearch V1 template mappings."""
@@ -0,0 +1,73 @@
1
+ {
2
+ "index_patterns": ["__SEARCH_INDEX_PREFIX__auditlog*"],
3
+ "data_stream": {},
4
+ "template": {
5
+ "settings": {
6
+ "analysis": {
7
+ "filter": {
8
+ "email_domains": {
9
+ "type": "pattern_capture",
10
+ "preserve_original": false,
11
+ "patterns": [
12
+ "@(.+)"
13
+ ]
14
+ }
15
+ },
16
+ "analyzer": {
17
+ "email": {
18
+ "tokenizer": "uax_url_email",
19
+ "filter": [
20
+ "email_domains",
21
+ "lowercase",
22
+ "unique"
23
+ ]
24
+ }
25
+ }
26
+ }
27
+ },
28
+ "mappings": {
29
+ "dynamic": "strict",
30
+ "numeric_detection": false,
31
+ "properties": {
32
+ "id": { "type": "keyword" },
33
+ "uuid": { "type": "keyword" },
34
+ "version_id": { "type": "integer" },
35
+ "action": { "type": "keyword" },
36
+ "resource": {
37
+ "properties": {
38
+ "id": { "type": "keyword" },
39
+ "type": { "type": "keyword" }
40
+ }
41
+ },
42
+ "message": { "type": "text" },
43
+ "user": {
44
+ "properties": {
45
+ "id": { "type": "keyword" },
46
+ "name": { "type": "keyword" },
47
+ "email": {
48
+ "type": "text",
49
+ "analyzer": "email",
50
+ "fielddata": true,
51
+ "fields": {
52
+ "domain": {
53
+ "type": "text",
54
+ "analyzer": "email",
55
+ "fielddata": true
56
+ }
57
+ }
58
+ }
59
+ }
60
+ },
61
+ "metadata": {
62
+ "type": "object",
63
+ "dynamic": true
64
+ },
65
+ "updated": { "type": "date" }
66
+ }
67
+ },
68
+ "aliases": {
69
+ "__SEARCH_INDEX_PREFIX__auditlog": {},
70
+ "__SEARCH_INDEX_PREFIX__audit-log": {}
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,8 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2025 CERN.
4
+ #
5
+ # Invenio-Audit-Logs is free software; you can redistribute it and/or modify
6
+ # it under the terms of the MIT License; see LICENSE file for more details.
7
+
8
+ """OpenSearch V1 template mappings."""
@@ -0,0 +1,73 @@
1
+ {
2
+ "index_patterns": ["__SEARCH_INDEX_PREFIX__auditlog*"],
3
+ "data_stream": {},
4
+ "template": {
5
+ "settings": {
6
+ "analysis": {
7
+ "filter": {
8
+ "email_domains": {
9
+ "type": "pattern_capture",
10
+ "preserve_original": false,
11
+ "patterns": [
12
+ "@(.+)"
13
+ ]
14
+ }
15
+ },
16
+ "analyzer": {
17
+ "email": {
18
+ "tokenizer": "uax_url_email",
19
+ "filter": [
20
+ "email_domains",
21
+ "lowercase",
22
+ "unique"
23
+ ]
24
+ }
25
+ }
26
+ }
27
+ },
28
+ "mappings": {
29
+ "dynamic": "strict",
30
+ "numeric_detection": false,
31
+ "properties": {
32
+ "id": { "type": "keyword" },
33
+ "uuid": { "type": "keyword" },
34
+ "version_id": { "type": "integer" },
35
+ "action": { "type": "keyword" },
36
+ "resource": {
37
+ "properties": {
38
+ "id": { "type": "keyword" },
39
+ "type": { "type": "keyword" }
40
+ }
41
+ },
42
+ "message": { "type": "text" },
43
+ "user": {
44
+ "properties": {
45
+ "id": { "type": "keyword" },
46
+ "name": { "type": "keyword" },
47
+ "email": {
48
+ "type": "text",
49
+ "analyzer": "email",
50
+ "fielddata": true,
51
+ "fields": {
52
+ "domain": {
53
+ "type": "text",
54
+ "analyzer": "email",
55
+ "fielddata": true
56
+ }
57
+ }
58
+ }
59
+ }
60
+ },
61
+ "metadata": {
62
+ "type": "object",
63
+ "dynamic": true
64
+ },
65
+ "updated": { "type": "date" }
66
+ }
67
+ },
68
+ "aliases": {
69
+ "__SEARCH_INDEX_PREFIX__auditlog": {},
70
+ "__SEARCH_INDEX_PREFIX__audit-log": {}
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,17 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2025 CERN.
4
+ #
5
+ # Invenio-Audit-Logs is free software; you can redistribute it and/or
6
+ # modify it under the terms of the MIT License; see LICENSE file for more
7
+ # details.
8
+
9
+ """Resources module."""
10
+
11
+ from .config import AuditLogResourceConfig
12
+ from .resource import AuditLogResource
13
+
14
+ __all__ = (
15
+ "AuditLogResource",
16
+ "AuditLogResourceConfig",
17
+ )
@@ -0,0 +1,57 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2025 CERN.
4
+ #
5
+ # Invenio-Audit-Logs is free software; you can redistribute it and/or
6
+ # modify it under the terms of the MIT License; see LICENSE file for more
7
+ # details.
8
+
9
+ """Audit logs resource config."""
10
+
11
+ from invenio_records_resources.resources import (
12
+ RecordResourceConfig,
13
+ SearchRequestArgsSchema,
14
+ )
15
+ from invenio_records_resources.services.base.config import ConfiguratorMixin
16
+ from marshmallow import fields
17
+
18
+
19
+ #
20
+ # Request args
21
+ #
22
+ class AuditLogSearchRequestArgsSchema(SearchRequestArgsSchema):
23
+ """Search parameters for audit-logs."""
24
+
25
+ id = fields.UUID()
26
+ resource_id = fields.String()
27
+ resource_type = fields.String()
28
+ user_id = fields.String()
29
+ action = fields.String()
30
+
31
+
32
+ #
33
+ # Resource config
34
+ #
35
+ class AuditLogResourceConfig(RecordResourceConfig, ConfiguratorMixin):
36
+ """Audit-Logs resource configuration."""
37
+
38
+ blueprint_name = "audit_logs"
39
+ url_prefix = "/audit-logs"
40
+
41
+ routes = {
42
+ "list": "/",
43
+ "item": "/<id>",
44
+ }
45
+
46
+ request_view_args = {
47
+ "id": fields.UUID(),
48
+ }
49
+
50
+ request_search_args = AuditLogSearchRequestArgsSchema
51
+
52
+ response_handlers = {
53
+ "application/vnd.inveniordm.v1+json": RecordResourceConfig.response_handlers[
54
+ "application/json"
55
+ ],
56
+ **RecordResourceConfig.response_handlers,
57
+ }
@@ -0,0 +1,68 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2025 CERN.
4
+ #
5
+ # Invenio-Audit-Logs is free software; you can redistribute it and/or
6
+ # modify it under the terms of the MIT License; see LICENSE file for more
7
+ # details.
8
+
9
+ """Logs resource."""
10
+
11
+ from flask import g
12
+ from flask_resources import resource_requestctx, response_handler, route
13
+ from invenio_records_resources.resources.records.resource import (
14
+ RecordResource,
15
+ request_extra_args,
16
+ request_search_args,
17
+ request_view_args,
18
+ )
19
+ from invenio_records_resources.resources.records.utils import search_preference
20
+
21
+
22
+ #
23
+ # Resource
24
+ #
25
+ class AuditLogResource(RecordResource):
26
+ """Resource layer for audit-logs."""
27
+
28
+ def create_blueprint(self, **options):
29
+ """Create the blueprint."""
30
+ options["url_prefix"] = ""
31
+ return super().create_blueprint(**options)
32
+
33
+ def create_url_rules(self):
34
+ """Create the URL rules for the log resource."""
35
+ routes = self.config.routes
36
+
37
+ def p(route):
38
+ """Prefix a route with the URL prefix."""
39
+ return f"{self.config.url_prefix}{route}"
40
+
41
+ return [
42
+ route("GET", p(routes["list"]), self.search),
43
+ route("GET", p(routes["item"]), self.read),
44
+ ]
45
+
46
+ @request_extra_args
47
+ @request_search_args
48
+ @request_view_args
49
+ @response_handler(many=True)
50
+ def search(self):
51
+ """Perform a search over the logs."""
52
+ hits = self.service.search(
53
+ identity=g.identity,
54
+ params=resource_requestctx.args,
55
+ search_preference=search_preference(),
56
+ )
57
+ return hits.to_dict(), 200
58
+
59
+ @request_extra_args
60
+ @request_view_args
61
+ @response_handler()
62
+ def read(self):
63
+ """Read a specific log entry."""
64
+ item = self.service.read(
65
+ id_=resource_requestctx.view_args["id"],
66
+ identity=g.identity,
67
+ )
68
+ return item.to_dict(), 200
@@ -0,0 +1,18 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2025 CERN.
4
+ #
5
+ # Invenio-Audit-Logs is free software; you can redistribute it and/or modify it
6
+ # under the terms of the MIT License; see LICENSE file for more details.
7
+
8
+ """Audit Log Services."""
9
+
10
+ from .config import AuditLogServiceConfig
11
+ from .schema import AuditLogSchema
12
+ from .service import AuditLogService
13
+
14
+ __all__ = (
15
+ "AuditLogService",
16
+ "AuditLogSchema",
17
+ "AuditLogServiceConfig",
18
+ )