fix: misc permission issues
This commit is contained in:
17
lms/hooks.py
17
lms/hooks.py
@@ -86,13 +86,16 @@ after_migrate = [
|
||||
# -----------
|
||||
# Permissions evaluated in scripted ways
|
||||
|
||||
# permission_query_conditions = {
|
||||
# "Event": "frappe.desk.doctype.event.event.get_permission_query_conditions",
|
||||
# }
|
||||
#
|
||||
# has_permission = {
|
||||
# "Event": "frappe.desk.doctype.event.event.has_permission",
|
||||
# }
|
||||
permission_query_conditions = {
|
||||
"LMS Certificate": "lms.lms.doctype.lms_certificate.lms_certificate.get_permission_query_conditions",
|
||||
}
|
||||
|
||||
has_permission = {
|
||||
"LMS Live Class": "lms.lms.doctype.lms_live_class.lms_live_class.has_permission",
|
||||
"LMS Batch": "lms.lms.doctype.lms_batch.lms_batch.has_permission",
|
||||
"LMS Program": "lms.lms.doctype.lms_program.lms_program.has_permission",
|
||||
"LMS Certificate": "lms.lms.doctype.lms_certificate.lms_certificate.has_permission",
|
||||
}
|
||||
|
||||
# DocType Class
|
||||
# ---------------
|
||||
|
||||
@@ -1722,7 +1722,7 @@ def get_profile_details(username: str):
|
||||
as_dict=True,
|
||||
)
|
||||
roles = frappe.get_roles(details.name)
|
||||
if not has_lms_role(roles):
|
||||
if not has_lms_role():
|
||||
frappe.throw(
|
||||
_("User does not have permission to access this users profile details."), frappe.PermissionError
|
||||
)
|
||||
@@ -1730,7 +1730,8 @@ def get_profile_details(username: str):
|
||||
return details
|
||||
|
||||
|
||||
def has_lms_role(roles: list):
|
||||
def has_lms_role():
|
||||
roles = frappe.get_roles()
|
||||
lms_roles = set(LMS_ROLES)
|
||||
user_roles = set(roles)
|
||||
return not lms_roles.isdisjoint(user_roles)
|
||||
@@ -2223,7 +2224,7 @@ def get_assessment_from_lesson(course: str, assessmentType: str):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_badges(member: str):
|
||||
if not has_lms_role(frappe.get_roles()):
|
||||
if not has_lms_role():
|
||||
frappe.throw(_("You do not have permission to access badges."), frappe.PermissionError)
|
||||
|
||||
badges = frappe.get_all(
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
"fieldname": "condition",
|
||||
"fieldtype": "Code",
|
||||
"label": "Condition",
|
||||
"mandatory_depends_on": "eval:doc.event == \"Manual Assignment\""
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.event == 'Value Change'",
|
||||
@@ -100,7 +100,7 @@
|
||||
"link_fieldname": "badge"
|
||||
}
|
||||
],
|
||||
"modified": "2026-02-19 15:05:49.719925",
|
||||
"modified": "2026-02-20 17:58:25.924109",
|
||||
"modified_by": "sayali@frappe.io",
|
||||
"module": "LMS",
|
||||
"name": "LMS Badge",
|
||||
|
||||
@@ -10,17 +10,17 @@ from lms.lms.doctype.lms_badge.lms_badge import eval_condition
|
||||
|
||||
class LMSBadgeAssignment(Document):
|
||||
def validate(self):
|
||||
self.validate_owner()
|
||||
self.validate_duplicate_badge_assignment()
|
||||
self.validate_badge_criteria()
|
||||
self.validate_owner()
|
||||
|
||||
def validate_owner(self):
|
||||
if self.owner == self.member:
|
||||
return
|
||||
|
||||
roles = frappe.get_roles(self.owner)
|
||||
if "Moderator" not in roles:
|
||||
frappe.throw(_("You must be a Moderator to assign badges to users."))
|
||||
event = frappe.db.get_value("LMS Badge", self.badge, "event")
|
||||
if event == "Manual Assignment":
|
||||
roles = frappe.get_roles(frappe.session.user)
|
||||
admins = ["Moderator", "Course Creator", "Batch Evaluator"]
|
||||
if not any(role in roles for role in admins):
|
||||
frappe.throw(_("You must be an Admin to assign badges to users."))
|
||||
|
||||
def validate_duplicate_badge_assignment(self):
|
||||
grant_only_once = frappe.db.get_value("LMS Badge", self.badge, "grant_only_once")
|
||||
@@ -40,25 +40,27 @@ class LMSBadgeAssignment(Document):
|
||||
"LMS Badge", self.badge, ["reference_doctype", "user_field", "condition", "enabled"], as_dict=True
|
||||
)
|
||||
|
||||
if badge_details:
|
||||
if badge_details.reference_doctype and badge_details.user_field and badge_details.condition:
|
||||
user_fieldname = frappe.db.get_value(
|
||||
"DocField",
|
||||
{"parent": badge_details.reference_doctype, "fieldname": badge_details.user_field},
|
||||
"fieldname",
|
||||
if not badge_details:
|
||||
return
|
||||
|
||||
if badge_details.reference_doctype and badge_details.user_field and badge_details.condition:
|
||||
user_fieldname = frappe.db.get_value(
|
||||
"DocField",
|
||||
{"parent": badge_details.reference_doctype, "fieldname": badge_details.user_field},
|
||||
"fieldname",
|
||||
)
|
||||
|
||||
documents = frappe.get_all(
|
||||
badge_details.reference_doctype,
|
||||
{user_fieldname: self.member},
|
||||
)
|
||||
|
||||
for document in documents:
|
||||
reference_value = eval_condition(
|
||||
frappe.get_doc(badge_details.reference_doctype, document.name),
|
||||
badge_details.condition,
|
||||
)
|
||||
if reference_value:
|
||||
return
|
||||
|
||||
documents = frappe.get_all(
|
||||
badge_details.reference_doctype,
|
||||
{user_fieldname: self.member},
|
||||
)
|
||||
|
||||
for document in documents:
|
||||
reference_value = eval_condition(
|
||||
frappe.get_doc(badge_details.reference_doctype, document.name),
|
||||
badge_details.condition,
|
||||
)
|
||||
if reference_value:
|
||||
return
|
||||
|
||||
frappe.throw(_("Member does not meet the criteria for the badge {0}.").format(self.badge))
|
||||
frappe.throw(_("Member does not meet the criteria for the badge {0}.").format(self.badge))
|
||||
|
||||
@@ -20,6 +20,7 @@ from lms.lms.utils import (
|
||||
get_lesson_url,
|
||||
get_lms_route,
|
||||
get_quiz_details,
|
||||
guest_access_allowed,
|
||||
update_payment_record,
|
||||
)
|
||||
|
||||
@@ -213,6 +214,10 @@ def create_live_class(
|
||||
auto_recording: str,
|
||||
description: str = None,
|
||||
):
|
||||
roles = frappe.get_roles()
|
||||
if not any(role in roles for role in ["Moderator", "Batch Evaluator"]):
|
||||
frappe.throw(_("You do not have permission to create a live class."))
|
||||
|
||||
payload = {
|
||||
"topic": title,
|
||||
"start_time": format_datetime(f"{date} {time}", "yyyy-MM-ddTHH:mm:ssZ"),
|
||||
@@ -391,3 +396,23 @@ def send_mail(batch, student):
|
||||
args=args,
|
||||
header=[_(f"Batch Start Reminder: {batch.title}"), "orange"],
|
||||
)
|
||||
|
||||
|
||||
def has_permission(doc, ptype="read", user=None):
|
||||
user = user or frappe.session.user
|
||||
if user == "Guest" and not guest_access_allowed():
|
||||
return False
|
||||
|
||||
roles = frappe.get_roles(user)
|
||||
if "Moderator" in roles or "Batch Evaluator" in roles:
|
||||
return True
|
||||
|
||||
is_enrolled = frappe.db.exists("LMS Batch Enrollment", {"batch": doc.name, "member": user})
|
||||
if is_enrolled:
|
||||
return True
|
||||
|
||||
is_batch_published = frappe.db.get_value("LMS Batch", doc.name, "published")
|
||||
if is_batch_published:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
"grid_page_length": 50,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2025-12-17 16:50:31.128747",
|
||||
"modified": "2026-02-20 17:32:34.580862",
|
||||
"modified_by": "sayali@frappe.io",
|
||||
"module": "LMS",
|
||||
"name": "LMS Certificate",
|
||||
@@ -153,27 +153,6 @@
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "LMS Student",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "LMS Student",
|
||||
"share": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
@@ -197,6 +176,15 @@
|
||||
"role": "Course Creator",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "LMS Student",
|
||||
"share": 1
|
||||
}
|
||||
],
|
||||
"row_format": "Dynamic",
|
||||
|
||||
@@ -12,6 +12,7 @@ from frappe.utils.telemetry import capture
|
||||
|
||||
class LMSCertificate(Document):
|
||||
def validate(self):
|
||||
self.validate_criteria()
|
||||
self.validate_duplicate_certificate()
|
||||
|
||||
def autoname(self):
|
||||
@@ -54,6 +55,43 @@ class LMSCertificate(Document):
|
||||
header=[subject, "green"],
|
||||
)
|
||||
|
||||
def validate_criteria(self):
|
||||
self.validate_role_of_owner()
|
||||
self.validate_batch_enrollment()
|
||||
self.validate_course_enrollment()
|
||||
|
||||
def validate_role_of_owner(self):
|
||||
roles = frappe.get_roles()
|
||||
is_admin = any(role in roles for role in ["Moderator", "Course Creator", "Batch Evaluator"])
|
||||
if not self.course and not self.batch_name and not is_admin:
|
||||
frappe.throw(_("Course or Batch is required to issue a certificate."))
|
||||
|
||||
def validate_batch_enrollment(self):
|
||||
if self.batch_name:
|
||||
is_enrolled = frappe.db.exists(
|
||||
"LMS Batch Enrollment", {"batch": self.batch_name, "member": self.member}
|
||||
)
|
||||
if not is_enrolled:
|
||||
frappe.throw(_("Certification cannot be issued as the member is not enrolled in this batch."))
|
||||
|
||||
def validate_course_enrollment(self):
|
||||
if self.course:
|
||||
is_enrolled = frappe.db.exists("LMS Enrollment", {"course": self.course, "member": self.member})
|
||||
if not is_enrolled:
|
||||
frappe.throw(
|
||||
_("Certification cannot be issued as the member is not enrolled in this course.")
|
||||
)
|
||||
|
||||
completion_certificate = frappe.db.get_value("LMS Course", self.course, "enable_certification")
|
||||
if completion_certificate:
|
||||
progress = frappe.db.get_value(
|
||||
"LMS Enrollment", {"course": self.course, "member": self.member}, "progress"
|
||||
)
|
||||
if progress < 100:
|
||||
frappe.throw(
|
||||
_("Certification cannot be issued as the member has not completed the course.")
|
||||
)
|
||||
|
||||
def validate_duplicate_certificate(self):
|
||||
self.validate_course_duplicates()
|
||||
self.validate_batch_duplicates()
|
||||
@@ -177,3 +215,19 @@ def validate_certification_eligibility(course):
|
||||
)
|
||||
if progress < 100:
|
||||
frappe.throw(_("You have not completed the course yet."))
|
||||
|
||||
|
||||
def has_permission(doc, ptype="read", user=None):
|
||||
user = user or frappe.session.user
|
||||
roles = frappe.get_roles(user)
|
||||
if "Moderator" in roles or "Course Creator" in roles or "Batch Evaluator" in roles:
|
||||
return True
|
||||
return doc.published
|
||||
|
||||
|
||||
def get_permission_query_conditions(user):
|
||||
user = user or frappe.session.user
|
||||
roles = frappe.get_roles(user)
|
||||
if "Moderator" in roles or "Course Creator" in roles or "Batch Evaluator" in roles:
|
||||
return None
|
||||
return """(`tabLMS Certificate`.published = 1)"""
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"grid_page_length": 50,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2026-01-29 16:10:47.787285",
|
||||
"modified": "2026-02-20 17:40:39.823017",
|
||||
"modified_by": "sayali@frappe.io",
|
||||
"module": "LMS",
|
||||
"name": "LMS Course Review",
|
||||
@@ -63,8 +63,8 @@
|
||||
"create": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "LMS Student",
|
||||
"share": 1,
|
||||
|
||||
@@ -169,3 +169,15 @@ def get_minutes(duration_in_seconds):
|
||||
if duration_in_seconds:
|
||||
return int(duration_in_seconds) // 60
|
||||
return 0
|
||||
|
||||
|
||||
def has_permission(doc, ptype="read", user=None):
|
||||
user = user or frappe.session.user
|
||||
roles = frappe.get_roles(user)
|
||||
if "Moderator" in roles or "Batch Evaluator" in roles:
|
||||
return True
|
||||
|
||||
return frappe.db.exists(
|
||||
"LMS Batch Enrollment",
|
||||
{"batch": doc.batch_name, "member": user},
|
||||
)
|
||||
|
||||
@@ -5,6 +5,8 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
|
||||
from lms.lms.utils import guest_access_allowed
|
||||
|
||||
|
||||
class LMSProgram(Document):
|
||||
def validate(self):
|
||||
@@ -41,3 +43,24 @@ class LMSProgram(Document):
|
||||
|
||||
if self.member_count != member_count:
|
||||
self.member_count = member_count
|
||||
|
||||
|
||||
def has_permission(doc, ptype="read", user=None):
|
||||
user = user or frappe.session.user
|
||||
|
||||
if user == "Guest" and not guest_access_allowed():
|
||||
return False
|
||||
|
||||
roles = frappe.get_roles(user)
|
||||
if "Moderator" in roles or "Course Creator" in roles:
|
||||
return True
|
||||
|
||||
is_enrolled = frappe.db.exists("LMS Program Member", {"parent": doc.name, "member": user})
|
||||
if is_enrolled:
|
||||
return True
|
||||
|
||||
is_program_published = frappe.db.get_value("LMS Program", doc.name, "published")
|
||||
if is_program_published:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
"grid_page_length": 50,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2025-06-24 14:42:08.288983",
|
||||
"modified": "2026-02-20 14:43:56.587110",
|
||||
"modified_by": "sayali@frappe.io",
|
||||
"module": "LMS",
|
||||
"name": "LMS Programming Exercise Submission",
|
||||
@@ -146,6 +146,7 @@
|
||||
"create": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
|
||||
Reference in New Issue
Block a user