diff --git a/frappe/auth.py b/frappe/auth.py index c7dc0d17a5..2a50c75cac 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -163,12 +163,12 @@ class LoginManager: frappe.form_dict.pop("pwd", None) self.post_login() - def post_login(self): + def post_login(self, duration: str | None = None, audit_user: str | None = None): self.run_trigger("on_login") validate_ip_address(self.user) self.validate_hour() self.get_user_info() - self.make_session() + self.make_session(duration=duration, audit_user=audit_user) self.setup_boot_cache() self.set_user_info() @@ -216,13 +216,15 @@ class LoginManager: def clear_preferred_language(self): frappe.local.cookie_manager.delete_cookie("preferred_language") - def make_session(self, resume=False): + def make_session(self, resume: bool = False, duration: str | None = None, audit_user: str | None = None): # start session frappe.local.session_obj = Session( user=self.user, resume=resume, full_name=self.full_name, user_type=self.user_type, + duration=duration, + audit_user=audit_user, ) # reset user if changed to Guest @@ -342,13 +344,14 @@ class LoginManager: """login as guest""" self.login_as("Guest") - def login_as(self, user): + def login_as(self, user: str, duration: str | None = None, audit_user: str | None = None): self.user = user - self.post_login() + self.post_login(duration, audit_user) def impersonate(self, user): current_user = frappe.session.user - self.login_as(user) + session_data = frappe.local.session_obj.data.data + self.login_as(user, duration=session_data.duration, audit_user=session_data.audit_user) # Flag this session as impersonated session, so other code can log this. frappe.local.session_obj.set_impersonated(current_user) diff --git a/frappe/commands/site.py b/frappe/commands/site.py index 66f2d5e244..0c748d038a 100644 --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -1200,9 +1200,6 @@ def browse( frappe.init(site) frappe.connect() - frappe.flags.session_duration = duration - frappe.flags.audit_user = user_for_audit - sid = "" if user: if not frappe.db.exists("User", user): @@ -1213,7 +1210,7 @@ def browse( frappe.utils.set_request(path="/") frappe.local.cookie_manager = CookieManager() frappe.local.login_manager = LoginManager() - frappe.local.login_manager.login_as(user) + frappe.local.login_manager.login_as(user, duration, user_for_audit) sid = f"/app?sid={frappe.session.sid}" else: click.echo("Please enable developer mode to login as a user") diff --git a/frappe/core/doctype/version/version.py b/frappe/core/doctype/version/version.py index 39eff56a75..3ad33021c7 100644 --- a/frappe/core/doctype/version/version.py +++ b/frappe/core/doctype/version/version.py @@ -37,6 +37,9 @@ class Version(Document): if impersonator := frappe.session.data.get("impersonated_by"): data["impersonated_by"] = impersonator + if audit_user := frappe.session.data.get("audit_user"): + data["audit_user"] = audit_user + def set_diff(self, old: Document, new: Document) -> bool: """Set the data property with the diff of the docs if present""" diff = get_diff(old, new) diff --git a/frappe/public/js/frappe/form/footer/version_timeline_content_builder.js b/frappe/public/js/frappe/form/footer/version_timeline_content_builder.js index 43d55fe5da..7e6a5526c7 100644 --- a/frappe/public/js/frappe/form/footer/version_timeline_content_builder.js +++ b/frappe/public/js/frappe/form/footer/version_timeline_content_builder.js @@ -246,6 +246,12 @@ function get_version_timeline_content(version_doc, frm) { const impersonated_msg = __("Impersonated by {0}", [get_user_link(impersonated_by)]); out = out.map((message) => `${message} · ${impersonated_msg.bold()}`); } + + const audit_user = data.audit_user; + if (audit_user) { + const audit_msg = __("[Action taken by {0}]", [audit_user]); + out = out.map((message) => `${message} · ${audit_msg.bold()}`); + } return out; } diff --git a/frappe/sessions.py b/frappe/sessions.py index 51df41bff1..1b5d04f8b5 100644 --- a/frappe/sessions.py +++ b/frappe/sessions.py @@ -205,7 +205,15 @@ def generate_csrf_token(): class Session: __slots__ = ("_update_in_cache", "data", "full_name", "sid", "time_diff", "user", "user_type") - def __init__(self, user, resume=False, full_name=None, user_type=None): + def __init__( + self, + user: str, + resume: bool = False, + full_name: str | None = None, + user_type: str | None = None, + duration: str | None = None, + audit_user: str | None = None, + ): self.sid = cstr( frappe.form_dict.pop("sid", None) or unquote(frappe.request.cookies.get("sid", "Guest")) ) @@ -225,7 +233,7 @@ class Session: else: if self.user: self.validate_user() - self.start() + self.start(duration, audit_user) def validate_user(self): if not frappe.get_cached_value("User", self.user, "enabled"): @@ -234,7 +242,7 @@ class Session: frappe.ValidationError, ) - def start(self): + def start(self, duration: str | None = None, audit_user: str | None = None): """start a new session""" # generate sid if self.user == "Guest": @@ -246,17 +254,17 @@ class Session: self.sid = self.data.sid = sid self.data.data.user = self.user self.data.data.session_ip = frappe.local.request_ip - if frappe.flags.session_duration: + if duration: self.data.data.fixed_duration = True - if frappe.flags.audit_user: - self.data.data.impersonated_by = frappe.flags.audit_user + if audit_user: + self.data.data.audit_user = audit_user if self.user != "Guest": self.data.data.update( { "last_updated": frappe.utils.now(), - "session_expiry": frappe.flags.session_duration or get_expiry_period(), + "session_expiry": duration or get_expiry_period(), "full_name": self.full_name, "user_type": self.user_type, } @@ -392,9 +400,6 @@ class Session: if frappe.session.user == "Guest": return - if self.data.data.fixed_duration: - return - now = frappe.utils.now_datetime() # update session in db @@ -406,7 +411,8 @@ class Session: if ( force or (time_diff is None) or (time_diff > 600) or self._update_in_cache ) and not frappe.flags.read_only: - self.data.data.last_updated = now + if not self.data.data.fixed_duration: + self.data.data.last_updated = now self.data.data.lang = str(frappe.lang) Sessions = frappe.qb.DocType("Sessions")