import os with open('addons/education_lms/tests/test_education_lms.py', 'a') as f: f.write(''' # ========================================== # PHASE H SECURITY TESTS # ========================================== # 37. Faculty A cannot read/modify Faculty B courses/content def test_37_faculty_isolation(self): # Faculty B course course_b = self.env['education.lms.course'].with_user(self.user_faculty_b).create({ 'title': 'Course B', 'faculty_id': self.user_faculty_b.id, 'program_id': self.program.id, 'academic_year_id': self.academic_year.id, }) # Faculty A tries to read -> AccessError with self.assertRaises(AccessError): course_b.with_user(self.user_faculty_a).read(['title']) # Faculty A tries to modify with self.assertRaises(AccessError): course_b.with_user(self.user_faculty_a).write({'title': 'Hacked'}) # Faculty A tries to delete with self.assertRaises(AccessError): course_b.with_user(self.user_faculty_a).unlink() # Faculty A tries to create section under Course B -> AccessError with self.assertRaises(AccessError): self.env['education.lms.section'].with_user(self.user_faculty_a).create({ 'title': 'Sec B', 'course_id': course_b.id }) # 38. Student Isolation (cannot read/modify other student submission) def test_38_student_isolation(self): self.course_faculty_a.state = 'published' assignment = self.env['education.lms.assignment'].with_user(self.user_faculty_a).create({ 'title': 'Assign H', 'course_id': self.course_faculty_a.id, 'due_date': fields.Date.today() + timedelta(days=5), 'state': 'published' }) submission_a = self.env['education.lms.submission'].with_user(self.user_student_a).create({ 'assignment_id': assignment.id, 'student_id': self.student_a.id, 'text_submission': 'Student A Work', }) # Student B tries to read with self.assertRaises(AccessError): submission_a.with_user(self.user_student_b).read(['text_submission']) # Student B tries to write with self.assertRaises(AccessError): submission_a.with_user(self.user_student_b).write({'text_submission': 'Hacked'}) # Student B tries to create submission for Student A with self.assertRaises(AccessError): self.env['education.lms.submission'].with_user(self.user_student_b).create({ 'assignment_id': assignment.id, 'student_id': self.student_a.id, 'text_submission': 'Student B Work', }) # 39. Course Eligibility Security def test_39_course_eligibility(self): # Course A uses Program, Year, Term A, Batch A. Student A matches all. # Course B uses no Term -> Student A matches. course_b = self.env['education.lms.course'].with_user(self.user_faculty_a).create({ 'title': 'Course No Term', 'faculty_id': self.user_faculty_a.id, 'program_id': self.program.id, 'academic_year_id': self.academic_year.id, 'state': 'published' }) # Student A can read course_b res = course_b.with_user(self.user_student_a).read(['title']) self.assertTrue(len(res) > 0) # Course C uses Term B -> Student A does not match. term_b = self.env['education.academic.term'].create({ 'name': 'Term B', 'code': 'TB', 'academic_year_id': self.academic_year.id, }) course_c = self.env['education.lms.course'].with_user(self.user_faculty_a).create({ 'title': 'Course Diff Term', 'faculty_id': self.user_faculty_a.id, 'program_id': self.program.id, 'academic_year_id': self.academic_year.id, 'term_id': term_b.id, 'state': 'published' }) # Wait, the rule should block Student A with self.assertRaises(AccessError): course_c.with_user(self.user_student_a).read(['title']) # 40. Assignment access and states def test_40_assignment_access(self): # Draft assignment -> Student A cannot read self.course_faculty_a.state = 'published' assignment = self.env['education.lms.assignment'].with_user(self.user_faculty_a).create({ 'title': 'Assign Draft', 'course_id': self.course_faculty_a.id, 'due_date': fields.Date.today() + timedelta(days=5), 'state': 'draft' }) with self.assertRaises(AccessError): assignment.with_user(self.user_student_a).read(['title']) # Published -> Student A can read assignment.with_user(self.user_faculty_a).action_publish() res = assignment.with_user(self.user_student_a).read(['title']) self.assertTrue(len(res) > 0) # Closed -> Student A can read, but cannot submit assignment.with_user(self.user_faculty_a).action_close() res = assignment.with_user(self.user_student_a).read(['title']) self.assertTrue(len(res) > 0) with self.assertRaises(ValidationError): self.env['education.lms.submission'].with_user(self.user_student_a).create({ 'assignment_id': assignment.id, 'student_id': self.student_a.id, 'text_submission': 'Student A Work', }) # 41. Submission edge cases def test_41_submission_security_edge_cases(self): self.course_faculty_a.state = 'published' assignment = self.env['education.lms.assignment'].with_user(self.user_faculty_a).create({ 'title': 'Assign H2', 'course_id': self.course_faculty_a.id, 'due_date': fields.Date.today() + timedelta(days=5), 'state': 'published' }) submission = self.env['education.lms.submission'].with_user(self.user_student_a).create({ 'assignment_id': assignment.id, 'student_id': self.student_a.id, 'text_submission': 'Student A Work', }) # change student_id -> AccessError/ValidationError? with self.assertRaises(AccessError): submission.with_user(self.user_student_a).write({'student_id': self.student_b.id}) # change assignment_id assignment_2 = self.env['education.lms.assignment'].with_user(self.user_faculty_a).create({ 'title': 'Assign H3', 'course_id': self.course_faculty_a.id, 'due_date': fields.Date.today() + timedelta(days=5), 'state': 'published' }) with self.assertRaises(AccessError): # student a might not have access to change assignment if it violates unique rule or is blocked. # actually students can edit assignment_id if in draft, but let's test. # In Phase F we blocked changing assignment if submission is not draft. But it is draft here. # wait, they can change it if it's draft and they own the new assignment's course. # But the prompt says "assignment_id changed after submission -> REJECT". pass submission.with_user(self.user_student_a).action_submit() with self.assertRaises(AccessError): submission.with_user(self.user_student_a).write({'assignment_id': assignment_2.id}) # change marks with self.assertRaises(AccessError): submission.with_user(self.user_student_a).write({'marks': 100}) # change feedback with self.assertRaises(AccessError): submission.with_user(self.user_student_a).write({'feedback': 'Good job me'}) # 42. Attachment Security Direct Test import base64 def test_42_attachment_security(self): self.course_faculty_a.state = 'published' assignment = self.env['education.lms.assignment'].with_user(self.user_faculty_a).create({ 'title': 'Assign H Attachments', 'course_id': self.course_faculty_a.id, 'due_date': fields.Date.today() + timedelta(days=5), 'state': 'published' }) # Student A submission sub_a = self.env['education.lms.submission'].with_user(self.user_student_a).create({ 'assignment_id': assignment.id, 'student_id': self.student_a.id, 'text_submission': 'A', }) attach_a = self.env['ir.attachment'].with_user(self.user_student_a).create({ 'name': 'file_a.txt', 'datas': base64.b64encode(b'student A file'), 'res_model': 'education.lms.submission', 'res_id': sub_a.id, }) sub_b = self.env['education.lms.submission'].with_user(self.user_student_b).create({ 'assignment_id': assignment.id, 'student_id': self.student_b.id, 'text_submission': 'B', }) attach_b = self.env['ir.attachment'].with_user(self.user_student_b).create({ 'name': 'file_b.txt', 'datas': base64.b64encode(b'student B file'), 'res_model': 'education.lms.submission', 'res_id': sub_b.id, }) # Verify A can read attach A self.assertTrue(attach_a.with_user(self.user_student_a).read(['name'])) # Verify A cannot read attach B with self.assertRaises(AccessError): attach_b.with_user(self.user_student_a).read(['name']) # Verify Faculty A can read both self.assertTrue(attach_a.with_user(self.user_faculty_a).read(['name'])) self.assertTrue(attach_b.with_user(self.user_faculty_a).read(['name'])) # Verify Faculty B cannot read either with self.assertRaises(AccessError): attach_a.with_user(self.user_faculty_b).read(['name']) # 43. Admin override def test_43_admin_override(self): user_admin = self.env['res.users'].create({ 'name': 'Admin User 2', 'login': 'lms_admin2', 'groups_id': [(4, self.group_admin.id)] }) # Admin can read faculty B's course course_b = self.env['education.lms.course'].with_user(self.user_faculty_b).create({ 'title': 'Course B', 'faculty_id': self.user_faculty_b.id, 'program_id': self.program.id, 'academic_year_id': self.academic_year.id, }) res = course_b.with_user(user_admin).read(['title']) self.assertTrue(len(res) > 0) course_b.with_user(user_admin).write({'title': 'Admin changed'}) self.assertEqual(course_b.title, 'Admin changed') # 44. Portal User Mapping def test_44_portal_user_mapping(self): # A user without student_id should not see Student A's courses/submissions in LMS Context user_no_student = self.env['res.users'].create({ 'name': 'No Student', 'login': 'no_student', 'groups_id': [(4, self.group_student.id)] }) self.course_faculty_a.state = 'published' with self.assertRaises(AccessError): self.course_faculty_a.with_user(user_no_student).read(['title']) ''')