##// END OF EJS Templates
caches: use repo.lru based Dict cache. This LRUDict uses Timing Algo to not have to use locking...
caches: use repo.lru based Dict cache. This LRUDict uses Timing Algo to not have to use locking for the LRU implementation, this it's safer to use for dogpile. We used it before with beaker, so it's generally more stable.

File last commit:

r2930:a5198975 default
r2945:ec5716e4 default
Show More
audit_logger.py
279 lines | 9.0 KiB | text/x-python | PythonLexer
audit-logs: introducing new audit logger for actions....
r1694 # -*- coding: utf-8 -*-
release: update copyright year to 2018
r2487 # Copyright (C) 2017-2018 RhodeCode GmbH
audit-logs: introducing new audit logger for actions....
r1694 #
# 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
audit-logger: use raw JSON with empty data to control unicode decode warnings....
r2184 from rhodecode.lib.jsonalchemy import JsonRaw
audit-logs: introducing new audit logger for actions....
r1694 from rhodecode.model import meta
audit-logs: add push/pull actions to audit logs.
r1736 from rhodecode.model.db import User, UserLog, Repository
audit-logs: introducing new audit logger for actions....
r1694
log = logging.getLogger(__name__)
audit-logs: added audit-logs on user actions....
r1801 # action as key, and expected action_data as value
audit-logs: implemented pull request and comment events.
r1807 ACTIONS_V1 = {
audit-logs: fill in some default values for the expected action data.
r1802 'user.login.success': {'user_agent': ''},
'user.login.failure': {'user_agent': ''},
'user.logout': {'user_agent': ''},
audit-logs: added user.register audit log entry.
r2384 'user.register': {},
audit-logs: use new v2 api on login/logout/password reset views.
r1697 'user.password.reset_request': {},
audit-logs: fill in some default values for the expected action data.
r1802 'user.push': {'user_agent': '', 'commit_ids': []},
'user.pull': {'user_agent': ''},
audit-logs: use new v2 api on login/logout/password reset views.
r1697
audit-logs: added audit-logs on user actions....
r1801 'user.create': {'data': {}},
'user.delete': {'old_data': {}},
'user.edit': {'old_data': {}},
'user.edit.permissions': {},
audit-logs: updated action data attrbiutes.
r1823 'user.edit.ip.add': {'ip': {}, 'user': {}},
'user.edit.ip.delete': {'ip': {}, 'user': {}},
'user.edit.token.add': {'token': {}, 'user': {}},
'user.edit.token.delete': {'token': {}, 'user': {}},
'user.edit.email.add': {'email': ''},
'user.edit.email.delete': {'email': ''},
users: added SSH key management for user admin pages
r1993 'user.edit.ssh_key.add': {'token': {}, 'user': {}},
'user.edit.ssh_key.delete': {'token': {}, 'user': {}},
audit-logs: added audit-logs on user actions....
r1801 'user.edit.password_reset.enabled': {},
'user.edit.password_reset.disabled': {},
audit-logs: added audit logs on user groups admin page.
r1805 'user_group.create': {'data': {}},
'user_group.delete': {'old_data': {}},
'user_group.edit': {'old_data': {}},
'user_group.edit.permissions': {},
audit-logs: implemented full audit logs across application....
r1829 'user_group.edit.member.add': {'user': {}},
'user_group.edit.member.delete': {'user': {}},
audit-logs: added audit logs on user groups admin page.
r1805
audit-logs: fill in some default values for the expected action data.
r1802 'repo.create': {'data': {}},
audit-logs: moved async tasks from old deprecated action_logger.
r1803 'repo.fork': {'data': {}},
audit-logs: fill in some default values for the expected action data.
r1802 'repo.edit': {'old_data': {}},
repo-permissions: moved permissions into pyramid....
r1734 'repo.edit.permissions': {},
audit-logs: fill in some default values for the expected action data.
r1802 'repo.delete': {'old_data': {}},
audit-logs: updated action data attrbiutes.
r1823 'repo.commit.strip': {'commit_id': ''},
'repo.archive.download': {'user_agent': '', 'archive_name': '',
'archive_spec': '', 'archive_cached': ''},
audit-logs: implemented pull request and comment events.
r1807 'repo.pull_request.create': '',
'repo.pull_request.edit': '',
'repo.pull_request.delete': '',
'repo.pull_request.close': '',
'repo.pull_request.merge': '',
'repo.pull_request.vote': '',
'repo.pull_request.comment.create': '',
'repo.pull_request.comment.delete': '',
'repo.pull_request.reviewer.add': '',
'repo.pull_request.reviewer.delete': '',
audit-logs: implemented full audit logs across application....
r1829 'repo.commit.comment.create': {'data': {}},
'repo.commit.comment.delete': {'data': {}},
audit-logs: implemented pull request and comment events.
r1807 'repo.commit.vote': '',
audit-logs: fill in some default values for the expected action data.
r1802 'repo_group.create': {'data': {}},
'repo_group.edit': {'old_data': {}},
audit-logs: added action logs for repository groups.
r1799 'repo_group.edit.permissions': {},
audit-logs: fill in some default values for the expected action data.
r1802 'repo_group.delete': {'old_data': {}},
audit-logs: introducing new audit logger for actions....
r1694 }
audit-logs: implemented pull request and comment events.
r1807 ACTIONS = ACTIONS_V1
audit-logs: introducing new audit logger for actions....
r1694
audit-logs: implement enum sources that should be re-used.
r1798 SOURCE_WEB = 'source_web'
SOURCE_API = 'source_api'
audit-logs: introducing new audit logger for actions....
r1694
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
audit-logs: add push/pull actions to audit logs.
r1736 class RepoWrap(object):
"""
Fake object used to imitate RepoObject that audit logger requires
"""
def __init__(self, repo_id=None, repo_name=None):
self.repo_id = repo_id
self.repo_name = repo_name
audit-logs: introducing new audit logger for actions....
r1694 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
audit-logger: use raw JSON with empty data to control unicode decode warnings....
r2184 user_log.action_data = action_data or JsonRaw(u'{}')
audit-logs: introducing new audit logger for actions....
r1694
user_log.user_ip = ip_address
user_log.user_id = user_id
user_log.username = username
audit-logger: use raw JSON with empty data to control unicode decode warnings....
r2184 user_log.user_data = user_data or JsonRaw(u'{}')
audit-logs: introducing new audit logger for actions....
r1694
user_log.repository_id = repository_id
user_log.repository_name = repository_name
user_log.action_date = datetime.datetime.now()
return user_log
audit-logger: added convinience wrappers to store web or api action.
r1800 def store_web(*args, **kwargs):
if 'action_data' not in kwargs:
kwargs['action_data'] = {}
kwargs['action_data'].update({
'source': SOURCE_WEB
})
return store(*args, **kwargs)
def store_api(*args, **kwargs):
if 'action_data' not in kwargs:
kwargs['action_data'] = {}
kwargs['action_data'].update({
'source': SOURCE_API
})
return store(*args, **kwargs)
audit-logs: added audit-logs on user actions....
r1801 def store(action, user, action_data=None, user_data=None, ip_addr=None,
repo=None, sa_session=None, commit=False):
audit-logs: introducing new audit logger for actions....
r1694 """
audit-logger: added convinience wrappers to store web or api action.
r1800 Audit logger for various actions made by users, typically this
results in a call such::
audit-logs: introducing new audit logger for actions....
r1694
from rhodecode.lib import audit_logger
audit-logger: added entry for archive download.
r1743 audit_logger.store(
audit-logs: implemented full audit logs across application....
r1829 'repo.edit', user=self._rhodecode_user)
audit-logger: added entry for archive download.
r1743 audit_logger.store(
audit-logs: implemented full audit logs across application....
r1829 'repo.delete', action_data={'data': repo_data},
audit-logger: added entry for archive download.
r1743 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'))
# repo action
audit_logger.store(
audit-logs: implemented full audit logs across application....
r1829 'repo.delete',
audit-logger: added entry for archive download.
r1743 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'),
repo=audit_logger.RepoWrap(repo_name='some-repo'))
# repo action, when we know and have the repository object already
audit_logger.store(
audit-logs: implemented full audit logs across application....
r1829 'repo.delete', action_data={'source': audit_logger.SOURCE_WEB, },
audit-logs: implement enum sources that should be re-used.
r1798 user=self._rhodecode_user,
audit-logger: added entry for archive download.
r1743 repo=repo_object)
audit-logs: introducing new audit logger for actions....
r1694
audit-logger: added convinience wrappers to store web or api action.
r1800 # alternative wrapper to the above
audit_logger.store_web(
audit-logs: implemented full audit logs across application....
r1829 'repo.delete', action_data={},
audit-logger: added convinience wrappers to store web or api action.
r1800 user=self._rhodecode_user,
repo=repo_object)
audit-logs: introducing new audit logger for actions....
r1694 # without an user ?
audit-logger: added entry for archive download.
r1743 audit_logger.store(
audit-logs: implemented full audit logs across application....
r1829 'user.login.failure',
audit-logger: added entry for archive download.
r1743 user=audit_logger.UserWrap(
username=self.request.params.get('username'),
ip_addr=self.request.remote_addr))
audit-logs: introducing new audit logger for actions....
r1694 """
from rhodecode.lib.utils2 import safe_unicode
from rhodecode.lib.auth import AuthUser
audit-logs: fill in some default values for the expected action data.
r1802 action_spec = ACTIONS.get(action, None)
if action_spec is None:
raise ValueError('Action `{}` is not supported'.format(action))
audit-logs: introducing new audit logger for actions....
r1694
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):
audit-logger: store some user details on we have proper object of auth-user.
r1701 user_data = {
'username': user.username,
'email': user.email,
}
audit-logs: introducing new audit logger for actions....
r1694
audit-logs: add push/pull actions to audit logs.
r1736 repository_name = getattr(repo, 'repo_name', None)
audit-logs: introducing new audit logger for actions....
r1694 repository_id = getattr(repo, 'repo_id', None)
audit-logs: add push/pull actions to audit logs.
r1736 if not repository_id:
# maybe we have repo_name ? Try to figure repo_id from repo_name
if repository_name:
repository_id = getattr(
Repository.get_by_repo_name(repository_name), 'repo_id', None)
audit-logs: introducing new audit logger for actions....
r1694
audit-logs: allow showing individual entries for audit log.
r2110 action_name = safe_unicode(action)
ip_address = safe_unicode(ip_addr)
security: update lastactivity when on audit logs....
r2930 with sa_session.no_autoflush:
update_user_last_activity(sa_session, user_id)
audit-logs: allow showing individual entries for audit log.
r2110
security: update lastactivity when on audit logs....
r2930 user_log = _store_log(
action_name=action_name,
action_data=action_data or {},
user_id=user_id,
username=username,
user_data=user_data or {},
ip_address=ip_address,
repository_id=repository_id,
repository_name=repository_name
)
sa_session.add(user_log)
if commit:
sa_session.commit()
audit-logs: introducing new audit logger for actions....
r1694
audit-logs: allow showing individual entries for audit log.
r2110 entry_id = user_log.entry_id or ''
log.info('AUDIT[%s]: Logging action: `%s` by user:id:%s[%s] ip:%s',
entry_id, action_name, user_id, username, ip_address)
audit-logs: introducing new audit logger for actions....
r1694 except Exception:
log.exception('AUDIT: failed to store audit log')
security: update lastactivity when on audit logs....
r2930
def update_user_last_activity(sa_session, user_id):
_last_activity = datetime.datetime.now()
try:
sa_session.query(User).filter(User.user_id == user_id).update(
{"last_activity": _last_activity})
log.debug(
'updated user `%s` last activity to:%s', user_id, _last_activity)
except Exception:
log.exception("Failed last activity update")