Add audit event attributes to workspace role model
This commit is contained in:
parent
ae3113f011
commit
3b6d745955
@ -1,6 +1,7 @@
|
|||||||
from sqlalchemy import String, Column, ForeignKey, inspect
|
from sqlalchemy import String, Column, ForeignKey, inspect
|
||||||
from sqlalchemy.dialects.postgresql import UUID
|
from sqlalchemy.dialects.postgresql import UUID, JSON
|
||||||
from sqlalchemy.orm import relationship
|
from sqlalchemy.orm import relationship
|
||||||
|
from atst.database import db
|
||||||
|
|
||||||
from atst.models import Base, types
|
from atst.models import Base, types
|
||||||
from atst.models.mixins.timestamps import TimestampsMixin
|
from atst.models.mixins.timestamps import TimestampsMixin
|
||||||
@ -20,11 +21,15 @@ class AuditEvent(Base, TimestampsMixin):
|
|||||||
request_id = Column(UUID(as_uuid=True), ForeignKey("requests.id"), index=True)
|
request_id = Column(UUID(as_uuid=True), ForeignKey("requests.id"), index=True)
|
||||||
request = relationship("Request", backref="audit_events")
|
request = relationship("Request", backref="audit_events")
|
||||||
|
|
||||||
|
changed_state = Column(JSON())
|
||||||
|
event_details = Column(JSON())
|
||||||
|
|
||||||
resource_type = Column(String(), nullable=False)
|
resource_type = Column(String(), nullable=False)
|
||||||
resource_id = Column(UUID(as_uuid=True), index=True, nullable=False)
|
resource_id = Column(UUID(as_uuid=True), index=True, nullable=False)
|
||||||
display_name = Column(String())
|
display_name = Column(String())
|
||||||
action = Column(String(), nullable=False)
|
action = Column(String(), nullable=False)
|
||||||
|
|
||||||
|
|
||||||
def save(self, connection):
|
def save(self, connection):
|
||||||
attrs = inspect(self).dict
|
attrs = inspect(self).dict
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@ from flask import g
|
|||||||
from sqlalchemy.orm import class_mapper
|
from sqlalchemy.orm import class_mapper
|
||||||
from sqlalchemy.orm.attributes import get_history
|
from sqlalchemy.orm.attributes import get_history
|
||||||
|
|
||||||
|
from atst.database import db
|
||||||
|
from atst.models.workspace_role import WorkspaceRole
|
||||||
from atst.models.audit_event import AuditEvent
|
from atst.models.audit_event import AuditEvent
|
||||||
from atst.utils import camel_to_snake, getattr_path
|
from atst.utils import camel_to_snake, getattr_path
|
||||||
|
|
||||||
@ -11,15 +13,48 @@ ACTION_UPDATE = "update"
|
|||||||
ACTION_DELETE = "delete"
|
ACTION_DELETE = "delete"
|
||||||
|
|
||||||
|
|
||||||
|
class AuditableWorkspaceRole(AuditableMixin):
|
||||||
|
target = target
|
||||||
|
changed_state = get_changed_state()
|
||||||
|
event_details = get_event_details()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_changed_state(self, resource):
|
||||||
|
previous_state = {}
|
||||||
|
inspr = inspect(target)
|
||||||
|
attrs = class_mapper(target.__class__).column_attrs
|
||||||
|
for attr in attrs:
|
||||||
|
history = getattr(inspr.attrs, attr.key).history
|
||||||
|
if history.has_changes():
|
||||||
|
previous_state[attr.key] = get_history(target, attr.key)[2].pop()
|
||||||
|
|
||||||
|
from_role = previous_role["role"]
|
||||||
|
to_role = target.role_displayname
|
||||||
|
return {"role": [from_role, to_role]}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_event_details(self):
|
||||||
|
# get user that is being updated
|
||||||
|
# does this need to query the db?
|
||||||
|
ws_role_id = target.auditable_request_id()
|
||||||
|
ws_role = (
|
||||||
|
db.session.query(WorkspaceRole)
|
||||||
|
.filter(WorkspaceRole.id == ws_role_id)
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
return {"user": ws_role.user_name}
|
||||||
|
|
||||||
|
|
||||||
class AuditableMixin(object):
|
class AuditableMixin(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_audit_event(connection, resource, action, previous_state=None):
|
def create_audit_event(connection, resource, action):
|
||||||
user_id = getattr_path(g, "current_user.id")
|
user_id = getattr_path(g, "current_user.id")
|
||||||
workspace_id = resource.auditable_workspace_id()
|
workspace_id = resource.auditable_workspace_id()
|
||||||
request_id = resource.auditable_request_id()
|
request_id = resource.auditable_request_id()
|
||||||
resource_type = resource.auditable_resource_type()
|
resource_type = resource.auditable_resource_type()
|
||||||
display_name = resource.auditable_displayname()
|
display_name = resource.auditable_displayname()
|
||||||
previous_role_id = previous_state["role_id"]
|
changed_state = resource.auditable_changed_state()
|
||||||
|
event_details = resource.auditable_event_details()
|
||||||
|
|
||||||
audit_event = AuditEvent(
|
audit_event = AuditEvent(
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
@ -29,7 +64,8 @@ class AuditableMixin(object):
|
|||||||
resource_id=resource.id,
|
resource_id=resource.id,
|
||||||
display_name=display_name,
|
display_name=display_name,
|
||||||
action=action,
|
action=action,
|
||||||
previous_role_id=previous_role_id,
|
changed_state=changed_state,
|
||||||
|
event_details=event_details,
|
||||||
)
|
)
|
||||||
|
|
||||||
audit_event.save(connection)
|
audit_event.save(connection)
|
||||||
@ -40,6 +76,17 @@ class AuditableMixin(object):
|
|||||||
event.listen(cls, "after_delete", cls.audit_delete)
|
event.listen(cls, "after_delete", cls.audit_delete)
|
||||||
event.listen(cls, "after_update", cls.audit_update)
|
event.listen(cls, "after_update", cls.audit_update)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_history(target):
|
||||||
|
previous_state = {}
|
||||||
|
inspr = inspect(target)
|
||||||
|
attrs = class_mapper(target.__class__).column_attrs
|
||||||
|
for attr in attrs:
|
||||||
|
history = getattr(inspr.attrs, attr.key).history
|
||||||
|
if history.has_changes():
|
||||||
|
previous_state[attr.key] = get_history(target, attr.key)[2].pop()
|
||||||
|
return previous_state
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def audit_insert(mapper, connection, target):
|
def audit_insert(mapper, connection, target):
|
||||||
"""Listen for the `after_insert` event and create an AuditLog entry"""
|
"""Listen for the `after_insert` event and create an AuditLog entry"""
|
||||||
@ -52,14 +99,13 @@ class AuditableMixin(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def audit_update(mapper, connection, target):
|
def audit_update(mapper, connection, target):
|
||||||
previous_state = {}
|
target.create_audit_event(connection, target, ACTION_UPDATE)
|
||||||
inspr = inspect(target)
|
|
||||||
attrs = class_mapper(target.__class__).column_attrs
|
def auditable_changed_state(self):
|
||||||
for attr in attrs:
|
return getattr_path(self, "history")
|
||||||
history = getattr(inspr.attrs, attr.key).history
|
|
||||||
if history.has_changes():
|
def auditable_event_details(self):
|
||||||
previous_state[attr.key] = get_history(target, attr.key)[2].pop()
|
return getattr_path(self, "auditable_event_details")
|
||||||
target.create_audit_event(connection, target, ACTION_UPDATE, previous_state)
|
|
||||||
|
|
||||||
def auditable_resource_type(self):
|
def auditable_resource_type(self):
|
||||||
return camel_to_snake(type(self).__name__)
|
return camel_to_snake(type(self).__name__)
|
||||||
|
@ -41,6 +41,17 @@ class WorkspaceRole(Base, mixins.TimestampsMixin, mixins.AuditableMixin):
|
|||||||
self.role.name, self.workspace.name, self.user_id, self.id
|
self.role.name, self.workspace.name, self.user_id, self.id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def history(self):
|
||||||
|
previous_state = AuditableMixin.get_history(self)
|
||||||
|
from_role = previous_role["role"]
|
||||||
|
to_role = self.role_displayname
|
||||||
|
return {"role": [from_role, to_role]}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def auditable_event_details(self):
|
||||||
|
return {"updated_user": self.user_name, "updated_user_id" = self.user_id}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def latest_invitation(self):
|
def latest_invitation(self):
|
||||||
if self.invitations:
|
if self.invitations:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user