Skip to content

Commit 748c41a

Browse files
authored
Merge branch 'dev' into quiz-question-files
2 parents ee117a5 + 97c37a8 commit 748c41a

20 files changed

Lines changed: 459 additions & 185 deletions

Makefile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ endif
88
start:
99
docker compose -f docker-compose.nginx.yml up
1010

11+
12+
create-migrations:
13+
make clean-cache
14+
docker compose -f docker-compose.nginx.yml run php php bin/console doctrine:migrations:diff
15+
1116
# set up the database
1217
migrate:
1318
docker compose -f docker-compose.nginx.yml run php php bin/console doctrine:migrations:migrate
@@ -16,6 +21,10 @@ migrate:
1621
down:
1722
docker compose -f docker-compose.nginx.yml down
1823

24+
# rebuild the containers from the ground up
25+
build:
26+
docker compose -f docker-compose.nginx.yml up --build
27+
1928
# clear the Symfony cache
2029
clean-cache:
2130
docker compose -f docker-compose.nginx.yml run php bin/console cache:clear

assets/js/Components/ReviewFilesPage.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ export default function ReviewFilesPage({
617617
contentUrl: contentUrl,
618618
contentId: contentId,
619619
contentType: contentType,
620-
sectionIds: sectionIds?.length > 0 ? sectionIds : []
620+
sectionIds: sectionIds?.length > 0 ? sectionIds : [],
621621
}
622622

623623
return contentItemOption
@@ -660,11 +660,11 @@ const getSectionPostOptions = (newFile, sectionReferences) => {
660660
return postSectionOptions
661661
}
662662

663-
const updateAndScanContent = async (postContentItemOptions, postSectionItemOption) => {
663+
const updateAndScanContent = async (postContentItemOptions, postSectionItemOption, fileId) => {
664664
const responseStatus = []
665665
try{
666666
let api = new Api(settings)
667-
const responseStr = await api.updateContent(postContentItemOptions, postSectionItemOption)
667+
const responseStr = await api.updateContent(postContentItemOptions, postSectionItemOption, fileId)
668668
const response = await responseStr.json()
669669
if (response.errors && response.errors.length > 0) {
670670
response.errors.forEach((error) => {
@@ -748,7 +748,7 @@ const getSectionPostOptions = (newFile, sectionReferences) => {
748748
const postSectionOptions = getSectionPostOptions(updatedFileData, sectionReferences)
749749

750750
if((postContentItemOptions && postContentItemOptions.length > 0) || (postSectionOptions && postSectionOptions.length > 0)){
751-
const responseStatus = await updateAndScanContent(postContentItemOptions, postSectionOptions)
751+
const responseStatus = await updateAndScanContent(postContentItemOptions, postSectionOptions, updatedFileData.id)
752752
if(responseStatus && responseStatus[0]?.type == "error"){
753753
responseStatus.forEach((err) => addMessage({message: err.message, severity: 'error', visible:true}))
754754
updateActiveSessionFile(tempFile.id, settings.ISSUE_STATE.ERROR)
@@ -835,7 +835,7 @@ const getSectionPostOptions = (newFile, sectionReferences) => {
835835
const postSectionOptions = getSectionPostOptions(activeFile, sectionReferences)
836836

837837
if((postContentItemOptions && postContentItemOptions.length > 0) || (postSectionOptions && postSectionOptions.length > 0)){
838-
const responseStatus = await updateAndScanContent(postContentItemOptions, postSectionOptions)
838+
const responseStatus = await updateAndScanContent(postContentItemOptions, postSectionOptions, activeFile.id)
839839
if(responseStatus && responseStatus[0]?.type == "error"){
840840
responseStatus.forEach((err) => addMessage({message: err.message, severity: 'error', visible:true}))
841841
updateActiveSessionFile(tempFile.id, settings.ISSUE_STATE.ERROR)

assets/js/Components/Widgets/MessageTray.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ export default function MessageTray ({
1616
const containerRef = useRef(null)
1717

1818
const createMessage = (newMessage) => {
19-
if(!newMessage.message) {
19+
20+
if(!newMessage?.message) {
2021
return
2122
}
2223
const id = Date.now() + Math.random().toString().slice(2)

assets/js/Services/Api.js

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,13 @@ export default class Api {
1212
reviewFile: '/api/files/{file}/review',
1313
postFile: '/api/files/{file}/post',
1414
deleteFile: '/api/files/{file}/delete',
15-
updateContent: '/api/content',
15+
updateContent: '/api/{file}/content',
1616
reportPdf: '/download/courses/{course}/reports/pdf',
1717
adminCourses: '/api/admin/courses/account/{account}/term/{term}',
1818
scanContent: '/api/sync/content/{contentItem}?report={getReport}',
1919
scanCourse: '/api/sync/{course}',
2020
scanLmsCourse: '/api/admin/sync/lms/{lmsCourseId}',
2121
fullRescan: '/api/sync/rescan/{course}',
22-
scanIssue: '/api/issues/{issue}/scan',
2322
adminReport: '/api/admin/courses/{course}/reports/latest',
2423
adminCourseReport: '/api/admin/courses/{course}/reports/full',
2524
adminReportHistory: '/api/admin/reports/account/{account}/term/{term}',
@@ -184,9 +183,10 @@ export default class Api {
184183

185184
}
186185

187-
updateContent(contentOptions, sectionOptions){
186+
updateContent(contentOptions, sectionOptions, fileId){
188187
const authToken = this.getAuthToken()
189188
let url = `${this.apiUrl}${this.endpoints.updateContent}`
189+
url = url.replace('{file}', fileId)
190190

191191
return fetch(url, {
192192
method: 'POST',
@@ -351,21 +351,6 @@ export default class Api {
351351
})
352352
}
353353

354-
scanIssue(issueId)
355-
{
356-
const authToken = this.getAuthToken()
357-
let url = `${this.apiUrl}${this.endpoints.scanIssue}`
358-
url = url.replace('{issue}', issueId)
359-
360-
return fetch(url, {
361-
method: 'GET',
362-
headers: {
363-
'Content-Type': 'application/json',
364-
'X-AUTH-TOKEN': authToken,
365-
},
366-
})
367-
}
368-
369354
getIssueContent(issueId) {
370355
const authToken = this.getAuthToken()
371356
let url = `${this.apiUrl}${this.endpoints.getIssueContent}`

src/Controller/AdminController.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ public function index(
5454
if (!$user) {
5555
$this->util->exitWithMessage('User authentication failed.');
5656
}
57-
if (!$lmsUser->validateApiKey($user)) {
57+
$apiStatus = $lmsUser->validateApiKey($user);
58+
if (!$apiStatus['success']) {
5859
$this->session->set('destination', 'admin');
5960
return $this->redirectToRoute('authorize', ['auth_token' => $this->session->getUuid()]);
6061
}

src/Controller/ApiController.php

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,47 @@
33
namespace App\Controller;
44

55
use App\Entity\Course;
6+
use App\Services\SessionService;
67
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
8+
use Symfony\Component\Console\Output\ConsoleOutput;
79

810
abstract class ApiController extends AbstractController
911
{
10-
public function userHasCourseAccess(Course $course) : bool {
11-
// Check if course belongs to user's institution
12+
protected function userHasCourseAccess(Course $course, SessionService $sessionService) : bool {
13+
$output = new ConsoleOutput();
14+
15+
// Check if user actually exists
1216
/** @var \App\Entity\User */
1317
$user = $this->getUser();
18+
if (!$user) {
19+
return false;
20+
}
21+
22+
// Check if we can get a session
23+
$userSession = $sessionService->getSession();
24+
if (!$userSession) {
25+
return false;
26+
}
27+
28+
// Check if course belongs to user's institution
1429
$userInstitutionId = $user?->getInstitution()?->getId();
1530
$resourceInstitutionId = $course->getInstitution()->getId();
16-
return $resourceInstitutionId === $userInstitutionId;
31+
32+
if ($resourceInstitutionId !== $userInstitutionId) {
33+
return false;
34+
}
35+
36+
$userCourseId = $userSession->get('lms_course_id');
37+
38+
// No course ID found in the session
39+
if (!$userCourseId) {
40+
return false;
41+
}
42+
43+
if ($course->getLmsCourseId() !== $userCourseId) {
44+
return false;
45+
}
46+
47+
return true;
1748
}
1849
}

src/Controller/DashboardController.php

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ public function index(
4848
if (!$user) {
4949
$this->util->exitWithMessage('User authentication failed.');
5050
}
51-
if (!$this->isUiDevelopment() && !$lmsUser->validateApiKey($user)) {
51+
$apiStatus = $lmsUser->validateApiKey($user);
52+
if (!$apiStatus['success']) {
5253
if ($this->session->get('oauthAttempted', false)) {
5354
$this->util->exitWithMessage('API authentication failed. Contact your administrator.');
5455
}
@@ -57,9 +58,6 @@ public function index(
5758
}
5859

5960
$lmsCourseId = $this->session->get('lms_course_id');
60-
if($this->isUiDevelopment() && !isset($lmsCourseId)) {
61-
$lmsCourseId = 616;
62-
}
6361
if (!$lmsCourseId) {
6462
$this->util->exitWithMessage('Missing LMS course ID.');
6563
}
@@ -145,10 +143,4 @@ protected function createCourse(Institution $institution, $lmsCourseId)
145143

146144
return $course;
147145
}
148-
149-
private function isUiDevelopment()
150-
{
151-
return $this->getParameter('app.use_development_auth') == 'YES';
152-
}
153-
154146
}

src/Controller/FileItemsController.php

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use App\Response\ApiResponse;
88
use App\Services\LmsPostService;
99
use App\Services\LmsFetchService;
10+
use App\Services\SessionService;
1011
use App\Services\UtilityService;
1112
use Doctrine\Persistence\ManagerRegistry;
1213
use Symfony\Component\HttpFoundation\JsonResponse;
@@ -24,7 +25,7 @@ public function __construct(ManagerRegistry $doctrine)
2425
}
2526

2627
#[Route('/api/files/{file}/review', name: 'review_file')]
27-
public function reviewFile(FileItem $file, Request $request, UtilityService $util)
28+
public function reviewFile(SessionService $sessionService, Request $request, UtilityService $util, FileItem $file)
2829
{
2930
$apiResponse = new ApiResponse();
3031
$user = $this->getUser();
@@ -33,14 +34,13 @@ public function reviewFile(FileItem $file, Request $request, UtilityService $uti
3334
try {
3435
// Check if user has access to course
3536
$course = $file->getCourse();
36-
if (!$this->userHasCourseAccess($course)) {
37+
if (!$this->userHasCourseAccess($course, $sessionService)) {
3738
throw new \Exception("You do not have permission to access this issue.");
3839
}
3940

4041
$updates = \json_decode($request->getContent(), true);
4142
$file->setReviewed($updates['reviewed']);
4243
if ($updates['replacement']){
43-
$output->writeln("Triggers");
4444
$file->removeReplacementFile();
4545
}
4646
$file->setReviewedBy($user);
@@ -71,17 +71,16 @@ public function reviewFile(FileItem $file, Request $request, UtilityService $uti
7171
}
7272

7373
#[Route('/api/files/{file}/post', methods: ['POST'], name: 'file_post')]
74-
public function postFile(FileItem $file, Request $request, UtilityService $util, LmsPostService $lmsPost, LmsFetchService $lmsFetch)
74+
public function postFile(SessionService $sessionService, Request $request, UtilityService $util, LmsPostService $lmsPost, LmsFetchService $lmsFetch, FileItem $file)
7575
{
7676
$output = new ConsoleOutput();
7777
$apiResponse = new ApiResponse();
7878
$user = $this->getUser();
79-
$output->writeln("Getting here on the backend");
8079

8180
try {
8281
// Check if user has access to course
8382
$course = $file->getCourse();
84-
if (!$this->userHasCourseAccess($course)) {
83+
if (!$this->userHasCourseAccess($course, $sessionService)) {
8584
throw new \Exception("You do not have permission to access this issue.");
8685
}
8786

@@ -134,8 +133,8 @@ public function postFile(FileItem $file, Request $request, UtilityService $util,
134133
}
135134

136135
// This route is created here as files are the primary items using this route
137-
#[Route('/api/content', methods: ['POST'], name: 'upload_content')]
138-
public function uploadContent(Request $request, UtilityService $util, LmsPostService $lmsPost, LmsFetchService $lmsFetch){
136+
#[Route('/api/{file}/content', methods: ['POST'], name: 'upload_content')]
137+
public function uploadContent(SessionService $sessionService, Request $request, UtilityService $util, LmsPostService $lmsPost, LmsFetchService $lmsFetch, FileItem $file){
139138
$output = new ConsoleOutput();
140139
$apiResponse = new ApiResponse();
141140
$user = $this->getUser();
@@ -145,6 +144,15 @@ public function uploadContent(Request $request, UtilityService $util, LmsPostSer
145144
$contentOptions = $content['content'];
146145
$sectionOptions = $content['section'];
147146

147+
if(empty($contentOptions) && empty($sectionOptions)){
148+
throw new \Exception("Tried to update content without any content avaliable");
149+
}
150+
151+
$course = $file->getCourse();
152+
if (!$this->userHasCourseAccess($course, $sessionService)) {
153+
throw new \Exception("You do not have permission to access this issue.");
154+
}
155+
148156
$lmsContent = $lmsPost->uploadContentToLms($contentOptions, $sectionOptions, $user);
149157
if(!$lmsContent){
150158
throw new \Exception("Failed to change references in canvas");
@@ -165,12 +173,17 @@ public function uploadContent(Request $request, UtilityService $util, LmsPostSer
165173
}
166174

167175
#[Route('/api/files/{file}/delete', methods: ['DELETE'], name: 'delete_file')]
168-
public function deleteFile(FileItem $file, UtilityService $util, LmsPostService $lmsPost, LmsFetchService $lmsFetch){
176+
public function deleteFile(SessionService $sessionService, FileItem $file, UtilityService $util, LmsPostService $lmsPost, LmsFetchService $lmsFetch){
169177
$output = new ConsoleOutput();
170178
$apiResponse = new ApiResponse();
171179
$user = $this->getUser();
172180

173181
try{
182+
$course = $file->getCourse();
183+
if (!$this->userHasCourseAccess($course, $sessionService)) {
184+
throw new \Exception("You do not have permission to access this issue.");
185+
}
186+
174187
$fileDeletionResponse = $lmsPost->deleteFileFromLms($file, $user);
175188
if(!$fileDeletionResponse || isset($fileDeletionResponse->error)){
176189
throw new \Exception("Failed to delete file!");

0 commit comments

Comments
 (0)