##// END OF EJS Templates
fix(caching): fixed problems with Cache query for users....
fix(caching): fixed problems with Cache query for users. The old way of querying caused the user get query to be always cached, and returning old results even in 2fa forms. The new limited query doesn't cache the user object resolving issues

File last commit:

r5095:aa627a5f default
r5365:ae8a165b default
Show More
audit_logger.py
303 lines | 9.7 KiB | text/x-python | PythonLexer
copyrights: updated for 2023
r5088 # Copyright (C) 2017-2023 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
core: multiple fixes to unicode vs str usage...
r5065 from rhodecode.lib.str_utils import safe_str
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: added entries for branch permissions
r3025 'repo.edit.permissions.branch': {},
repositories: added option to archive repositories instead of deleting them....
r3090 'repo.archive': {'old_data': {}},
audit-logs: fill in some default values for the expected action data.
r1802 'repo.delete': {'old_data': {}},
audit-logs: added entries for branch permissions
r3025
audit-logs: updated action data attrbiutes.
r1823 'repo.archive.download': {'user_agent': '', 'archive_name': '',
'archive_spec': '', 'archive_cached': ''},
audit-logs: added entries for branch permissions
r3025
'repo.permissions.branch_rule.create': {},
'repo.permissions.branch_rule.edit': {},
'repo.permissions.branch_rule.delete': {},
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': '',
comments: edit functionality added
r4401 'repo.pull_request.comment.edit': '',
audit-logs: implemented pull request and comment events.
r1807 'repo.pull_request.comment.delete': '',
'repo.pull_request.reviewer.add': '',
'repo.pull_request.reviewer.delete': '',
reviewers: added observers as another way to define reviewers....
r4500 'repo.pull_request.observer.add': '',
'repo.pull_request.observer.delete': '',
audit-logs: added entries for branch permissions
r3025 'repo.commit.strip': {'commit_id': ''},
audit-logs: implemented full audit logs across application....
r1829 'repo.commit.comment.create': {'data': {}},
'repo.commit.comment.delete': {'data': {}},
comments: edit functionality added
r4401 'repo.commit.comment.edit': {'data': {}},
audit-logs: implemented pull request and comment events.
r1807 'repo.commit.vote': '',
artifacts: added reading of metadata and define basic audit-log entries for add/delete artifact
r3679 'repo.artifact.add': '',
'repo.artifact.delete': '',
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: added entries for branch permissions
r3025
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
python3: fixed various code issues...
r4973 user_log.action_data = action_data or JsonRaw('{}')
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
python3: fixed various code issues...
r4973 user_log.user_data = user_data or JsonRaw('{}')
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):
audit-logger: use copy of params we later modify to prevent from modification by the store function of parameters that we only use for reading.
r4195 action_data = {}
org_action_data = kwargs.pop('action_data', {})
action_data.update(org_action_data)
action_data['source'] = SOURCE_WEB
kwargs['action_data'] = action_data
audit-logger: added convinience wrappers to store web or api action.
r1800 return store(*args, **kwargs)
def store_api(*args, **kwargs):
audit-logger: use copy of params we later modify to prevent from modification by the store function of parameters that we only use for reading.
r4195 action_data = {}
org_action_data = kwargs.pop('action_data', {})
action_data.update(org_action_data)
action_data['source'] = SOURCE_API
kwargs['action_data'] = action_data
audit-logger: added convinience wrappers to store web or api action.
r1800 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.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:
modernize: updates for python3
r5095 raise ValueError(f'Action `{action}` is not supported')
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
core: multiple fixes to unicode vs str usage...
r5065 action_name = safe_str(action)
ip_address = safe_str(ip_addr)
audit-logs: allow showing individual entries for audit log.
r2110
security: update lastactivity when on audit logs....
r2930 with sa_session.no_autoflush:
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)
pull-requests: optimize db transaction logic....
r4712 if commit:
sa_session.commit()
entry_id = user_log.entry_id or ''
update_user_last_activity(sa_session, user_id)
security: update lastactivity when on audit logs....
r2930
if commit:
sa_session.commit()
audit-logs: introducing new audit logger for actions....
r1694
audit-logs: allow showing individual entries for audit log.
r2110 log.info('AUDIT[%s]: Logging action: `%s` by user:id:%s[%s] ip:%s',
logging: expose extra metadata to various important logs for loki
r4816 entry_id, action_name, user_id, username, ip_address,
extra={"entry_id": entry_id, "action": action_name,
"user_id": user_id, "ip": ip_address})
audit-logs: allow showing individual entries for audit log.
r2110
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:
pull-requests: optimize db transaction logic....
r4712 log.exception("Failed last activity update for user_id: %s", user_id)
sa_session.rollback()