Merge remote-tracking branch 'upstream/develop' into fix/course-deletion
This commit is contained in:
46
lms/auth.py
46
lms/auth.py
@@ -1,3 +1,5 @@
|
||||
import json
|
||||
|
||||
import frappe
|
||||
|
||||
ALLOWED_PATHS = [
|
||||
@@ -12,6 +14,15 @@ ALLOWED_PATHS = [
|
||||
"/api/method/frappe.integrations.oauth2.authorize",
|
||||
"/api/method/frappe.integrations.oauth2.approve",
|
||||
"/api/method/frappe.integrations.oauth2.get_token",
|
||||
"/api/method/frappe.www.login.login_via_google",
|
||||
"/api/method/frappe.www.login.login_via_github",
|
||||
"/api/method/frappe.www.login.login_via_facebook",
|
||||
"/api/method/frappe.www.login.login_via_frappe",
|
||||
"/api/method/frappe.www.login.login_via_office365",
|
||||
"/api/method/frappe.www.login.login_via_salesforce",
|
||||
"/api/method/frappe.www.login.login_via_fairlogin",
|
||||
"/api/method/frappe.www.login.login_via_keycloak",
|
||||
"/api/method/frappe.www.login.custom",
|
||||
"/api/method/frappe.integrations.oauth2.openid_profile",
|
||||
"/api/method/frappe.website.doctype.web_page_view.web_page_view.make_view_log",
|
||||
"/api/method/upload_file",
|
||||
@@ -33,10 +44,14 @@ ALLOWED_PATHS = [
|
||||
"/api/method/frappe.utils.print_format.download_pdf",
|
||||
"/api/method/frappe.desk.search.search_link",
|
||||
"/api/method/frappe.core.doctype.communication.email.make",
|
||||
"/api/method/frappe.core.doctype.user.user.reset_password",
|
||||
]
|
||||
|
||||
|
||||
def authenticate():
|
||||
if not frappe.conf.get("block_endpoints"):
|
||||
return
|
||||
|
||||
if frappe.form_dict.cmd:
|
||||
path = f"/api/method/{frappe.form_dict.cmd}"
|
||||
else:
|
||||
@@ -48,10 +63,39 @@ def authenticate():
|
||||
|
||||
if not path.startswith("/api/"):
|
||||
return
|
||||
print("path", path)
|
||||
|
||||
if path.startswith("/lms") or path.startswith("/api/method/lms."):
|
||||
return
|
||||
|
||||
if is_server_script_path(path):
|
||||
return
|
||||
|
||||
if is_custom_app_endpoint(path):
|
||||
return
|
||||
|
||||
if path in ALLOWED_PATHS:
|
||||
return
|
||||
frappe.throw(f"Access not allowed for this URL: {path}", frappe.PermissionError)
|
||||
|
||||
|
||||
def is_server_script_path(path):
|
||||
endpoint = path.split("/api/method/")[-1]
|
||||
if frappe.db.exists("Server Script", {"script_type": "API", "api_method": endpoint, "disabled": 0}):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def is_custom_app_endpoint(path):
|
||||
allowed_custom_endpoints = frappe.conf.get("allowed_custom_endpoints", [])
|
||||
|
||||
if isinstance(allowed_custom_endpoints, str):
|
||||
try:
|
||||
parsed = json.loads(allowed_custom_endpoints)
|
||||
allowed_custom_endpoints = parsed if isinstance(parsed, list) else [allowed_custom_endpoints]
|
||||
except Exception:
|
||||
allowed_custom_endpoints = [allowed_custom_endpoints]
|
||||
|
||||
for endpoint in allowed_custom_endpoints:
|
||||
if endpoint in path:
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -257,12 +257,12 @@
|
||||
"length": 0,
|
||||
"link_filters": null,
|
||||
"mandatory_depends_on": null,
|
||||
"modified": "2025-12-24 12:56:32.110405",
|
||||
"modified": "2025-12-24 12:56:32.110406",
|
||||
"module": null,
|
||||
"name": "User-open_to",
|
||||
"no_copy": 0,
|
||||
"non_negative": 0,
|
||||
"options": "\nOpportunities\nHiring",
|
||||
"options": "\nWork\nHiring",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
|
||||
@@ -331,8 +331,8 @@ def get_certification_query(filters):
|
||||
)
|
||||
if field == "member_name":
|
||||
query = query.where(Certificate.member_name.like(value[1]))
|
||||
if field == "open_to_opportunities":
|
||||
query = query.where(User.open_to == "Opportunities")
|
||||
if field == "open_to_work":
|
||||
query = query.where(User.open_to == "Work")
|
||||
if field == "hiring":
|
||||
query = query.where(User.open_to == "Hiring")
|
||||
return query
|
||||
|
||||
@@ -3,14 +3,6 @@
|
||||
|
||||
frappe.ui.form.on("LMS Batch", {
|
||||
onload: function (frm) {
|
||||
frm.set_query("student", "students", function (doc) {
|
||||
return {
|
||||
filters: {
|
||||
ignore_user_type: 1,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("reference_doctype", "timetable", function () {
|
||||
let doctypes = ["Course Lesson", "LMS Quiz", "LMS Assignment"];
|
||||
return {
|
||||
|
||||
@@ -37,7 +37,7 @@ class LMSBatch(Document):
|
||||
|
||||
def on_update(self):
|
||||
if self.has_value_changed("published") and self.published:
|
||||
frappe.enqueue(send_notification_for_published_batch, batch=self, now=True)
|
||||
frappe.enqueue(send_notification_for_published_batch, batch=self)
|
||||
|
||||
def autoname(self):
|
||||
if not self.name:
|
||||
|
||||
@@ -166,14 +166,14 @@ class TestLMSUtils(BaseTestUtils):
|
||||
certified_participants_no_match = get_certified_participants(filters=filters)
|
||||
self.assertEqual(len(certified_participants_no_match), 0)
|
||||
|
||||
def test_certified_participants_with_open_to_opportunities(self):
|
||||
filters = {"open_to_opportunities": 1}
|
||||
certified_participants_open_to_oppo = get_certified_participants(filters=filters)
|
||||
self.assertEqual(len(certified_participants_open_to_oppo), 0)
|
||||
def test_certified_participants_with_open_to_work(self):
|
||||
filters = {"open_to_work": 1}
|
||||
certified_participants_open_to_work = get_certified_participants(filters=filters)
|
||||
self.assertEqual(len(certified_participants_open_to_work), 0)
|
||||
|
||||
frappe.db.set_value("User", self.student1.email, "open_to", "Opportunities")
|
||||
certified_participants_open_to_oppo = get_certified_participants(filters=filters)
|
||||
self.assertEqual(len(certified_participants_open_to_oppo), 1)
|
||||
frappe.db.set_value("User", self.student1.email, "open_to", "Work")
|
||||
certified_participants_open_to_work = get_certified_participants(filters=filters)
|
||||
self.assertEqual(len(certified_participants_open_to_work), 1)
|
||||
frappe.db.set_value("User", self.student1.email, "open_to", "")
|
||||
|
||||
def test_certified_participants_with_open_to_hiring(self):
|
||||
|
||||
821
lms/locale/ar.po
821
lms/locale/ar.po
File diff suppressed because it is too large
Load Diff
657
lms/locale/bs.po
657
lms/locale/bs.po
File diff suppressed because it is too large
Load Diff
629
lms/locale/cs.po
629
lms/locale/cs.po
File diff suppressed because it is too large
Load Diff
629
lms/locale/da.po
629
lms/locale/da.po
File diff suppressed because it is too large
Load Diff
645
lms/locale/de.po
645
lms/locale/de.po
File diff suppressed because it is too large
Load Diff
629
lms/locale/eo.po
629
lms/locale/eo.po
File diff suppressed because it is too large
Load Diff
675
lms/locale/es.po
675
lms/locale/es.po
File diff suppressed because it is too large
Load Diff
673
lms/locale/fa.po
673
lms/locale/fa.po
File diff suppressed because it is too large
Load Diff
669
lms/locale/fr.po
669
lms/locale/fr.po
File diff suppressed because it is too large
Load Diff
635
lms/locale/hr.po
635
lms/locale/hr.po
File diff suppressed because it is too large
Load Diff
643
lms/locale/hu.po
643
lms/locale/hu.po
File diff suppressed because it is too large
Load Diff
629
lms/locale/id.po
629
lms/locale/id.po
File diff suppressed because it is too large
Load Diff
655
lms/locale/it.po
655
lms/locale/it.po
File diff suppressed because it is too large
Load Diff
629
lms/locale/my.po
629
lms/locale/my.po
File diff suppressed because it is too large
Load Diff
685
lms/locale/nb.po
685
lms/locale/nb.po
File diff suppressed because it is too large
Load Diff
635
lms/locale/nl.po
635
lms/locale/nl.po
File diff suppressed because it is too large
Load Diff
661
lms/locale/pl.po
661
lms/locale/pl.po
File diff suppressed because it is too large
Load Diff
629
lms/locale/pt.po
629
lms/locale/pt.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
641
lms/locale/ru.po
641
lms/locale/ru.po
File diff suppressed because it is too large
Load Diff
629
lms/locale/sl.po
629
lms/locale/sl.po
File diff suppressed because it is too large
Load Diff
631
lms/locale/sr.po
631
lms/locale/sr.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
639
lms/locale/sv.po
639
lms/locale/sv.po
File diff suppressed because it is too large
Load Diff
629
lms/locale/ta.po
629
lms/locale/ta.po
File diff suppressed because it is too large
Load Diff
629
lms/locale/th.po
629
lms/locale/th.po
File diff suppressed because it is too large
Load Diff
679
lms/locale/tr.po
679
lms/locale/tr.po
File diff suppressed because it is too large
Load Diff
635
lms/locale/vi.po
635
lms/locale/vi.po
File diff suppressed because it is too large
Load Diff
641
lms/locale/zh.po
641
lms/locale/zh.po
File diff suppressed because it is too large
Load Diff
@@ -114,4 +114,5 @@ lms.patches.v2_0.count_in_program
|
||||
lms.patches.v2_0.fix_scorm_lesson_reference_idx #02-09-2025
|
||||
lms.patches.v2_0.certified_members_to_certifications #05-10-2025
|
||||
lms.patches.v2_0.fix_job_application_resume_urls
|
||||
lms.patches.v2_0.open_to_opportunities
|
||||
lms.patches.v2_0.open_to_opportunities
|
||||
lms.patches.v2_0.open_to_work
|
||||
12
lms/patches/v2_0/open_to_work.py
Normal file
12
lms/patches/v2_0/open_to_work.py
Normal file
@@ -0,0 +1,12 @@
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
open_to_field_exists = frappe.db.exists("Custom Field", {"dt": "User", "fieldname": "open_to"})
|
||||
|
||||
if not open_to_field_exists:
|
||||
return
|
||||
|
||||
open_to_opportunities = frappe.get_all("User", {"open_to": "Opportunities"}, ["name"])
|
||||
for user in open_to_opportunities:
|
||||
frappe.db.set_value("User", user.name, "open_to", "Work")
|
||||
@@ -11,24 +11,16 @@ class TestAuth(FrappeAPITestCase, BaseTestUtils):
|
||||
self.normal_user = self._create_user("normal-user@example.com", "Normal", "User", ["LMS Student"])
|
||||
|
||||
def test_allowed_path(self):
|
||||
site_url = frappe.utils.get_site_url(frappe.local.site)
|
||||
headers = {"Authorization": "Bearer set_test_example_user"}
|
||||
url = site_url + "/api/method/lms.lms.utils.get_courses"
|
||||
response = self.get(
|
||||
url,
|
||||
headers=headers,
|
||||
)
|
||||
self.assertNotEqual(response.json.get("exc_type"), "PermissionError")
|
||||
frappe.form_dict.cmd = "ping"
|
||||
frappe.session.user = self.normal_user.name
|
||||
authenticate()
|
||||
frappe.session.user = "Administrator"
|
||||
|
||||
def test_not_allowed_path(self):
|
||||
site_url = frappe.utils.get_site_url(frappe.local.site)
|
||||
headers = {"Authorization": "Bearer set_test_example_user"}
|
||||
url = site_url + "/api/method/frappe.auth.get_logged_user"
|
||||
response = self.get(
|
||||
url,
|
||||
headers=headers,
|
||||
)
|
||||
self.assertEqual(response.json.get("exc_type"), "PermissionError")
|
||||
frappe.form_dict.cmd = "frappe.auth.get_logged_user"
|
||||
frappe.session.user = self.normal_user.name
|
||||
self.assertRaises(frappe.PermissionError, authenticate)
|
||||
frappe.session.user = "Administrator"
|
||||
|
||||
def tearDown(self):
|
||||
BaseTestUtils.tearDown(self)
|
||||
|
||||
Reference in New Issue
Block a user