refactor: don't use impersonate directly, use similar logic

This will allow impersonating as well

Signed-off-by: Akhil Narang <me@akhilnarang.dev>
This commit is contained in:
Akhil Narang 2025-01-23 13:03:39 +05:30
parent 1dc767f671
commit 15065a93e3
No known key found for this signature in database
GPG key ID: 9DCC61E211BF645F
5 changed files with 36 additions and 21 deletions

View file

@ -163,12 +163,12 @@ class LoginManager:
frappe.form_dict.pop("pwd", None) frappe.form_dict.pop("pwd", None)
self.post_login() 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") self.run_trigger("on_login")
validate_ip_address(self.user) validate_ip_address(self.user)
self.validate_hour() self.validate_hour()
self.get_user_info() self.get_user_info()
self.make_session() self.make_session(duration=duration, audit_user=audit_user)
self.setup_boot_cache() self.setup_boot_cache()
self.set_user_info() self.set_user_info()
@ -216,13 +216,15 @@ class LoginManager:
def clear_preferred_language(self): def clear_preferred_language(self):
frappe.local.cookie_manager.delete_cookie("preferred_language") 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 # start session
frappe.local.session_obj = Session( frappe.local.session_obj = Session(
user=self.user, user=self.user,
resume=resume, resume=resume,
full_name=self.full_name, full_name=self.full_name,
user_type=self.user_type, user_type=self.user_type,
duration=duration,
audit_user=audit_user,
) )
# reset user if changed to Guest # reset user if changed to Guest
@ -342,13 +344,14 @@ class LoginManager:
"""login as guest""" """login as guest"""
self.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.user = user
self.post_login() self.post_login(duration, audit_user)
def impersonate(self, user): def impersonate(self, user):
current_user = frappe.session.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. # Flag this session as impersonated session, so other code can log this.
frappe.local.session_obj.set_impersonated(current_user) frappe.local.session_obj.set_impersonated(current_user)

View file

@ -1200,9 +1200,6 @@ def browse(
frappe.init(site) frappe.init(site)
frappe.connect() frappe.connect()
frappe.flags.session_duration = duration
frappe.flags.audit_user = user_for_audit
sid = "" sid = ""
if user: if user:
if not frappe.db.exists("User", user): if not frappe.db.exists("User", user):
@ -1213,7 +1210,7 @@ def browse(
frappe.utils.set_request(path="/") frappe.utils.set_request(path="/")
frappe.local.cookie_manager = CookieManager() frappe.local.cookie_manager = CookieManager()
frappe.local.login_manager = LoginManager() 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}" sid = f"/app?sid={frappe.session.sid}"
else: else:
click.echo("Please enable developer mode to login as a user") click.echo("Please enable developer mode to login as a user")

View file

@ -37,6 +37,9 @@ class Version(Document):
if impersonator := frappe.session.data.get("impersonated_by"): if impersonator := frappe.session.data.get("impersonated_by"):
data["impersonated_by"] = impersonator 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: def set_diff(self, old: Document, new: Document) -> bool:
"""Set the data property with the diff of the docs if present""" """Set the data property with the diff of the docs if present"""
diff = get_diff(old, new) diff = get_diff(old, new)

View file

@ -246,6 +246,12 @@ function get_version_timeline_content(version_doc, frm) {
const impersonated_msg = __("Impersonated by {0}", [get_user_link(impersonated_by)]); const impersonated_msg = __("Impersonated by {0}", [get_user_link(impersonated_by)]);
out = out.map((message) => `${message} · ${impersonated_msg.bold()}`); 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; return out;
} }

View file

@ -205,7 +205,15 @@ def generate_csrf_token():
class Session: class Session:
__slots__ = ("_update_in_cache", "data", "full_name", "sid", "time_diff", "user", "user_type") __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( self.sid = cstr(
frappe.form_dict.pop("sid", None) or unquote(frappe.request.cookies.get("sid", "Guest")) frappe.form_dict.pop("sid", None) or unquote(frappe.request.cookies.get("sid", "Guest"))
) )
@ -225,7 +233,7 @@ class Session:
else: else:
if self.user: if self.user:
self.validate_user() self.validate_user()
self.start() self.start(duration, audit_user)
def validate_user(self): def validate_user(self):
if not frappe.get_cached_value("User", self.user, "enabled"): if not frappe.get_cached_value("User", self.user, "enabled"):
@ -234,7 +242,7 @@ class Session:
frappe.ValidationError, frappe.ValidationError,
) )
def start(self): def start(self, duration: str | None = None, audit_user: str | None = None):
"""start a new session""" """start a new session"""
# generate sid # generate sid
if self.user == "Guest": if self.user == "Guest":
@ -246,17 +254,17 @@ class Session:
self.sid = self.data.sid = sid self.sid = self.data.sid = sid
self.data.data.user = self.user self.data.data.user = self.user
self.data.data.session_ip = frappe.local.request_ip self.data.data.session_ip = frappe.local.request_ip
if frappe.flags.session_duration: if duration:
self.data.data.fixed_duration = True self.data.data.fixed_duration = True
if frappe.flags.audit_user: if audit_user:
self.data.data.impersonated_by = frappe.flags.audit_user self.data.data.audit_user = audit_user
if self.user != "Guest": if self.user != "Guest":
self.data.data.update( self.data.data.update(
{ {
"last_updated": frappe.utils.now(), "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, "full_name": self.full_name,
"user_type": self.user_type, "user_type": self.user_type,
} }
@ -392,9 +400,6 @@ class Session:
if frappe.session.user == "Guest": if frappe.session.user == "Guest":
return return
if self.data.data.fixed_duration:
return
now = frappe.utils.now_datetime() now = frappe.utils.now_datetime()
# update session in db # update session in db
@ -406,7 +411,8 @@ class Session:
if ( if (
force or (time_diff is None) or (time_diff > 600) or self._update_in_cache force or (time_diff is None) or (time_diff > 600) or self._update_in_cache
) and not frappe.flags.read_only: ) 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) self.data.data.lang = str(frappe.lang)
Sessions = frappe.qb.DocType("Sessions") Sessions = frappe.qb.DocType("Sessions")