audit-logs: introducing new audit logger for actions.
- api v2 - gathers more actions such as dump of user/action data
This commit is contained in:
parent
9ab3e7057a
commit
23f3d5fbdf
2 changed files with 143 additions and 1 deletions
138
rhodecode/lib/audit_logger.py
Normal file
138
rhodecode/lib/audit_logger.py
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2017-2017 RhodeCode GmbH
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License, version 3
|
||||
# (only), as published by the Free Software Foundation.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# This program is dual-licensed. If you wish to learn more about the
|
||||
# RhodeCode Enterprise Edition, including its added features, Support services,
|
||||
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
||||
|
||||
import logging
|
||||
import datetime
|
||||
|
||||
from rhodecode.model import meta
|
||||
from rhodecode.model.db import User, UserLog
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
ACTIONS = {
|
||||
'repo.add': {},
|
||||
'repo.edit': {},
|
||||
}
|
||||
|
||||
|
||||
class UserWrap(object):
|
||||
"""
|
||||
Fake object used to imitate AuthUser
|
||||
"""
|
||||
|
||||
def __init__(self, user_id=None, username=None, ip_addr=None):
|
||||
self.user_id = user_id
|
||||
self.username = username
|
||||
self.ip_addr = ip_addr
|
||||
|
||||
|
||||
def _store_log(action_name, action_data, user_id, username, user_data,
|
||||
ip_address, repository_id, repository_name):
|
||||
user_log = UserLog()
|
||||
user_log.version = UserLog.VERSION_2
|
||||
|
||||
user_log.action = action_name
|
||||
user_log.action_data = action_data
|
||||
|
||||
user_log.user_ip = ip_address
|
||||
|
||||
user_log.user_id = user_id
|
||||
user_log.username = username
|
||||
user_log.user_data = user_data
|
||||
|
||||
user_log.repository_id = repository_id
|
||||
user_log.repository_name = repository_name
|
||||
|
||||
user_log.action_date = datetime.datetime.now()
|
||||
|
||||
log.info('AUDIT: Logging action: `%s` by user:id:%s[%s] ip:%s',
|
||||
action_name, user_id, username, ip_address)
|
||||
|
||||
return user_log
|
||||
|
||||
|
||||
def store(
|
||||
action, user, action_data=None, user_data=None, ip_addr=None,
|
||||
repo=None, sa_session=None, commit=False):
|
||||
"""
|
||||
Audit logger for various actions made by users, typically this results in a call such::
|
||||
|
||||
from rhodecode.lib import audit_logger
|
||||
|
||||
audit_logger.store(action='repo.edit', user=self._rhodecode_user)
|
||||
audit_logger.store(action='repo.delete', user=audit_logger.UserWrap(username='itried-to-login', ip_addr='8.8.8.8'))
|
||||
|
||||
# without an user ?
|
||||
audit_user = audit_logger.UserWrap(
|
||||
username=self.request.params.get('username'),
|
||||
ip_addr=self.request.remote_addr)
|
||||
audit_logger.store(action='user.login.failure', user=audit_user)
|
||||
"""
|
||||
from rhodecode.lib.utils2 import safe_unicode
|
||||
from rhodecode.lib.auth import AuthUser
|
||||
|
||||
if action not in ACTIONS:
|
||||
raise ValueError('Action `{}` not in valid actions'.format(action))
|
||||
|
||||
if not sa_session:
|
||||
sa_session = meta.Session()
|
||||
|
||||
try:
|
||||
username = getattr(user, 'username', None)
|
||||
if not username:
|
||||
pass
|
||||
|
||||
user_id = getattr(user, 'user_id', None)
|
||||
if not user_id:
|
||||
# maybe we have username ? Try to figure user_id from username
|
||||
if username:
|
||||
user_id = getattr(
|
||||
User.get_by_username(username), 'user_id', None)
|
||||
|
||||
ip_addr = ip_addr or getattr(user, 'ip_addr', None)
|
||||
if not ip_addr:
|
||||
pass
|
||||
|
||||
if not user_data:
|
||||
# try to get this from the auth user
|
||||
if isinstance(user, AuthUser):
|
||||
user_data = {}
|
||||
|
||||
repository_id = getattr(repo, 'repo_id', None)
|
||||
repository_name = getattr(repo, 'repo_name', None)
|
||||
|
||||
user_log = _store_log(
|
||||
action_name=safe_unicode(action),
|
||||
action_data=action_data or {},
|
||||
user_id=user_id,
|
||||
username=username,
|
||||
user_data=user_data or {},
|
||||
ip_address=safe_unicode(ip_addr),
|
||||
repository_id=repository_id,
|
||||
repository_name=repository_name
|
||||
)
|
||||
sa_session.add(user_log)
|
||||
if commit:
|
||||
sa_session.commit()
|
||||
|
||||
except Exception:
|
||||
log.exception('AUDIT: failed to store audit log')
|
||||
|
|
@ -1098,6 +1098,10 @@ class UserLog(Base, BaseModel):
|
|||
{'extend_existing': True, 'mysql_engine': 'InnoDB',
|
||||
'mysql_charset': 'utf8', 'sqlite_autoincrement': True},
|
||||
)
|
||||
VERSION_1 = 'v1'
|
||||
VERSION_2 = 'v2'
|
||||
VERSIONS = [VERSION_1, VERSION_2]
|
||||
|
||||
user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
|
||||
user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
|
||||
username = Column("username", String(255), nullable=True, unique=None, default=None)
|
||||
|
|
@ -1107,7 +1111,7 @@ class UserLog(Base, BaseModel):
|
|||
action = Column("action", Text().with_variant(Text(1200000), 'mysql'), nullable=True, unique=None, default=None)
|
||||
action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
|
||||
|
||||
version = Column("version", String(255), nullable=True, default='v1')
|
||||
version = Column("version", String(255), nullable=True, default=VERSION_1)
|
||||
user_data = Column('user_data_json', MutationObj.as_mutable(JsonType(dialect_map=dict(mysql=UnicodeText(16384)))))
|
||||
action_data = Column('action_data_json', MutationObj.as_mutable(JsonType(dialect_map=dict(mysql=UnicodeText(16384)))))
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue