{"action":"created","comment":{"url":"https://api.github.com/repos/h-da/geli/pulls/comments/183422846","pull_request_review_id":114417839,"id":183422846,"diff_hunk":"@@ -138,129 +113,229 @@ export class CourseController {\n     }\n \n     const courses = await Course.find(conditions);\n-    return await Course.getSanitized(\n-      currentUser, courses,\n-      {\n-        all: {\n-          copy: [\n-            '_id',\n-            'active',\n-            'createdAt',\n-            'description',\n-            'enrollType',\n-            'hasAccessKey',\n-            'name',\n-            'updatedAt',\n-          ],\n-          onlyid: [\n-            'courseAdmin',\n-          ]\n-        },\n-        safe: {\n-          empty: [\n-            'teachers'\n-          ],\n-          selfid: [\n-            'students'\n-          ]\n-        },\n-        editor: {\n-          onlyid: [\n-            'teachers'\n-          ]\n-        }\n-      }\n-    );\n+    return await Promise.all(courses.map(async (course) => {\n+      return course.forDashboard(currentUser);\n+    }));\n   }\n \n   /**\n-   * @api {get} /api/courses/:id Request course with certain ID\n-   * @apiName GetCourse\n+   * @api {get} /api/courses/:id Request view information for a specific course\n+   * @apiName GetCourseView\n    * @apiGroup Course\n    *\n    * @apiParam {String} id Course ID.\n    * @apiParam {IUser} currentUser Currently logged in user.\n    *\n-   * @apiSuccess {Course} course Course.\n+   * @apiSuccess {ICourseView} course ICourseView object.\n    *\n    * @apiSuccessExample {json} Success-Response:\n    *     {\n-   *         \"_id\": \"5a037e6b60f72236d8e7c83d\",\n-   *         \"updatedAt\": \"2017-11-08T22:00:11.869Z\",\n-   *         \"createdAt\": \"2017-11-08T22:00:11.263Z\",\n-   *         \"name\": \"Introduction to web development\",\n-   *         \"description\": \"Whether you're just getting started with Web development or are just expanding your horizons...\",\n+   *         \"_id\": \"5ad0f9b56ff514268c5adc8d\",\n+   *         \"name\": \"Access key test\",\n+   *         \"description\": \"This course is used to test the access key course enroll type.\",\n+   *         \"lectures\": [\n+   *             {\n+   *                 \"units\": [\n+   *                     {\n+   *                         \"__t\": \"free-text\",\n+   *                         \"_id\": \"5ad0f9b56ff514268c5adc99\",\n+   *                         \"updatedAt\": \"2018-04-13T18:40:53.305Z\",\n+   *                         \"createdAt\": \"2018-04-13T18:40:53.305Z\",\n+   *                         \"name\": \"What is the purpose of this course fixture?\",\n+   *                         \"description\": \"\",\n+   *                         \"markdown\": \"To test the 'accesskey' enrollType.\",\n+   *                         \"_course\": \"5ad0f9b56ff514268c5adc8d\",\n+   *                         \"__v\": 0\n+   *                     }\n+   *                 ],\n+   *                 \"_id\": \"5ad0f9b56ff514268c5adc92\",\n+   *                 \"updatedAt\": \"2018-04-13T18:40:53.316Z\",\n+   *                 \"createdAt\": \"2018-04-13T18:40:53.284Z\",\n+   *                 \"name\": \"Documentation\",\n+   *                 \"description\": \"Documents the course fixture.\",\n+   *                 \"__v\": 1\n+   *             }\n+   *         ]\n+   *     }\n+   *\n+   * @apiError NotFoundError Includes implicit authorization check. (In getCourse helper method.)\n+   * @apiError ForbiddenError (Redundant) Authorization check.\n+   */\n+  @Get('/:id')\n+  async getCourseView(@Param('id') id: string, @CurrentUser() currentUser: IUser): Promise<ICourseView> {\n+    const course = await this.getCourse(id, currentUser);\n+\n+    // This is currently a redundant check, because userReadConditions in getCourse above already restricts access!\n+    // (I.e. just in case future changes break something.)\n+    if (!course.checkPrivileges(currentUser).userCanViewCourse) {\n+      throw new ForbiddenError();\n+    }\n+\n+    await course.populateLecturesFor(currentUser)\n+      .execPopulate();\n+    await course.processLecturesFor(currentUser);\n+    return course.forView();","path":"api/src/controllers/CourseController.ts","position":236,"original_position":236,"commit_id":"ac6ba7afb948599b8b96cd58e6c766a1f06afc3c","original_commit_id":"ac6ba7afb948599b8b96cd58e6c766a1f06afc3c","user":{"login":"grosspersky","id":11839276,"avatar_url":"https://avatars0.githubusercontent.com/u/11839276?v=4","gravatar_id":"","url":"https://api.github.com/users/grosspersky","html_url":"https://github.com/grosspersky","followers_url":"https://api.github.com/users/grosspersky/followers","following_url":"https://api.github.com/users/grosspersky/following{/other_user}","gists_url":"https://api.github.com/users/grosspersky/gists{/gist_id}","starred_url":"https://api.github.com/users/grosspersky/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/grosspersky/subscriptions","organizations_url":"https://api.github.com/users/grosspersky/orgs","repos_url":"https://api.github.com/users/grosspersky/repos","events_url":"https://api.github.com/users/grosspersky/events{/privacy}","received_events_url":"https://api.github.com/users/grosspersky/received_events","type":"User","site_admin":false},"body":"See previous comment.","created_at":"2018-04-23T14:49:00Z","updated_at":"2018-04-23T14:49:00Z","html_url":"https://github.com/h-da/geli/pull/664#discussion_r183422846","pull_request_url":"https://api.github.com/repos/h-da/geli/pulls/664","author_association":"COLLABORATOR","_links":{"self":{"href":"https://api.github.com/repos/h-da/geli/pulls/comments/183422846"},"html":{"href":"https://github.com/h-da/geli/pull/664#discussion_r183422846"},"pull_request":{"href":"https://api.github.com/repos/h-da/geli/pulls/664"}}},"pull_request":{"url":"https://api.github.com/repos/h-da/geli/pulls/664","id":183253050,"html_url":"https://github.com/h-da/geli/pull/664","diff_url":"https://github.com/h-da/geli/pull/664.diff","patch_url":"https://github.com/h-da/geli/pull/664.patch","issue_url":"https://api.github.com/repos/h-da/geli/issues/664","number":664,"state":"open","locked":false,"title":"Bugfix/#654 wasteful course data fix","user":{"login":"torss","id":37229901,"avatar_url":"https://avatars0.githubusercontent.com/u/37229901?v=4","gravatar_id":"","url":"https://api.github.com/users/torss","html_url":"https://github.com/torss","followers_url":"https://api.github.com/users/torss/followers","following_url":"https://api.github.com/users/torss/following{/other_user}","gists_url":"https://api.github.com/users/torss/gists{/gist_id}","starred_url":"https://api.github.com/users/torss/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/torss/subscriptions","organizations_url":"https://api.github.com/users/torss/orgs","repos_url":"https://api.github.com/users/torss/repos","events_url":"https://api.github.com/users/torss/events{/privacy}","received_events_url":"https://api.github.com/users/torss/received_events","type":"User","site_admin":false},"body":"## Description\r\n\r\nCloses #654\r\nCloses #653\r\n\r\nThis streamlines the `getCourses` and `getCourse` APIs to fix #654, also fixing the #653 `getCourse` leak in the process.\r\n\r\nA severe leak in `getCourses` was previously fixed in #594 via an overly complex `getSanitized` filter. Such a filter was necessary, because the dashboard frontend required various unnecessary data to function properly. (E.g. the course teacher IDs to check whether a teacher is part of a course or not.)\r\nThis has been streamlined via a new `ICourseDashboard` interface, which specifies all required data for the dashboard. That data includes some of the original `ICourse` fields, but also some flags from the (improved) `checkPrivileges` course method. The frontend & backend has been adjusted accordingly and the now obsolete `getSanitized` functionality is removed.\r\n\r\nMore importantly, another leak (#653) in `getCourse` is fixed, which is done implicitly via the streamlining (#654). That streamlining is facilitated by splitting `getCourse` into `getCourseView` and `getCourseEdit`.\r\n`getCourseEdit` continues to provide the \"unsafe\" (i.e. full) course information for editing purposes, but now only allows access for authorized users (i.e. admins or course teachers/admins).\r\n`getCourseView` provides a reduced (new) `ICourseView` object, similar to `ICourseDashboard`, but with lecture information instead of the `checkPrivileges` access flags.\r\nThe API tests have been adjusted and extended to test against unauthorized users.\r\n\r\nThere are also other minor improvements / additions, mostly to support the primary goals.\r\n\r\n## Improvements\r\n\r\n- Refactored `checkPrivileges` course method\r\n- New `ExtractId.ts` utility (to extract mongoose IDs)\r\n- `ExtractId.ts` utility tests\r\n- Simplified `getCourses` via `ICourseDashboard`\r\n- Split `getCourse` into `getCourseView` and `getCourseEdit`\r\n- Secured `getCourseEdit` via access control\r\n- Secured `getCourseView` via reduction to `ICourseView`\r\n- Moved some functionality from `getCourse(s)` to `Course.ts` / `Lecture.ts`\r\n- Removed obsolete `getSanitized` course method\r\n- Adjusted frontend to the streamlined / secured `getCourse(s)` system\r\n- Adjusted tests to the streamlined / secured `getCourse(s)` system\r\n- Minor typo fixed in `Access-key-test.json`\r\n- Import clean-up in `CourseController.ts`\r\n\r\n## Known Issues\r\n\r\nDue to the amount of central (course) system changes, some parts might clash with concurrent work.\r\n","created_at":"2018-04-22T03:13:40Z","updated_at":"2018-04-23T14:49:00Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a9344610e927f8ae240a830fd032fa9b28431ab9","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":721594812,"url":"https://api.github.com/repos/h-da/geli/labels/api","name":"api","color":"ba7f2c","default":false},{"id":560507897,"url":"https://api.github.com/repos/h-da/geli/labels/bug","name":"bug","color":"ee0701","default":true},{"id":560507899,"url":"https://api.github.com/repos/h-da/geli/labels/enhancement","name":"enhancement","color":"84b6eb","default":true},{"id":892672246,"url":"https://api.github.com/repos/h-da/geli/labels/refactoring","name":"refactoring","color":"eacf62","default":false},{"id":721595053,"url":"https://api.github.com/repos/h-da/geli/labels/web-frontend","name":"web-frontend","color":"d16159","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/h-da/geli/pulls/664/commits","review_comments_url":"https://api.github.com/repos/h-da/geli/pulls/664/comments","review_comment_url":"https://api.github.com/repos/h-da/geli/pulls/comments{/number}","comments_url":"https://api.github.com/repos/h-da/geli/issues/664/comments","statuses_url":"https://api.github.com/repos/h-da/geli/statuses/ac6ba7afb948599b8b96cd58e6c766a1f06afc3c","head":{"label":"h-da:bugfix/#654-wasteful-data-fix","ref":"bugfix/#654-wasteful-data-fix","sha":"ac6ba7afb948599b8b96cd58e6c766a1f06afc3c","user":{"login":"h-da","id":635522,"avatar_url":"https://avatars1.githubusercontent.com/u/635522?v=4","gravatar_id":"","url":"https://api.github.com/users/h-da","html_url":"https://github.com/h-da","followers_url":"https://api.github.com/users/h-da/followers","following_url":"https://api.github.com/users/h-da/following{/other_user}","gists_url":"https://api.github.com/users/h-da/gists{/gist_id}","starred_url":"https://api.github.com/users/h-da/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/h-da/subscriptions","organizations_url":"https://api.github.com/users/h-da/orgs","repos_url":"https://api.github.com/users/h-da/repos","events_url":"https://api.github.com/users/h-da/events{/privacy}","received_events_url":"https://api.github.com/users/h-da/received_events","type":"Organization","site_admin":false},"repo":{"id":84955069,"name":"geli","full_name":"h-da/geli","owner":{"login":"h-da","id":635522,"avatar_url":"https://avatars1.githubusercontent.com/u/635522?v=4","gravatar_id":"","url":"https://api.github.com/users/h-da","html_url":"https://github.com/h-da","followers_url":"https://api.github.com/users/h-da/followers","following_url":"https://api.github.com/users/h-da/following{/other_user}","gists_url":"https://api.github.com/users/h-da/gists{/gist_id}","starred_url":"https://api.github.com/users/h-da/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/h-da/subscriptions","organizations_url":"https://api.github.com/users/h-da/orgs","repos_url":"https://api.github.com/users/h-da/repos","events_url":"https://api.github.com/users/h-da/events{/privacy}","received_events_url":"https://api.github.com/users/h-da/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/h-da/geli","description":"geli is an open source e-learning platform","fork":false,"url":"https://api.github.com/repos/h-da/geli","forks_url":"https://api.github.com/repos/h-da/geli/forks","keys_url":"https://api.github.com/repos/h-da/geli/keys{/key_id}","collaborators_url":"https://api.github.com/repos/h-da/geli/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/h-da/geli/teams","hooks_url":"https://api.github.com/repos/h-da/geli/hooks","issue_events_url":"https://api.github.com/repos/h-da/geli/issues/events{/number}","events_url":"https://api.github.com/repos/h-da/geli/events","assignees_url":"https://api.github.com/repos/h-da/geli/assignees{/user}","branches_url":"https://api.github.com/repos/h-da/geli/branches{/branch}","tags_url":"https://api.github.com/repos/h-da/geli/tags","blobs_url":"https://api.github.com/repos/h-da/geli/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/h-da/geli/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/h-da/geli/git/refs{/sha}","trees_url":"https://api.github.com/repos/h-da/geli/git/trees{/sha}","statuses_url":"https://api.github.com/repos/h-da/geli/statuses/{sha}","languages_url":"https://api.github.com/repos/h-da/geli/languages","stargazers_url":"https://api.github.com/repos/h-da/geli/stargazers","contributors_url":"https://api.github.com/repos/h-da/geli/contributors","subscribers_url":"https://api.github.com/repos/h-da/geli/subscribers","subscription_url":"https://api.github.com/repos/h-da/geli/subscription","commits_url":"https://api.github.com/repos/h-da/geli/commits{/sha}","git_commits_url":"https://api.github.com/repos/h-da/geli/git/commits{/sha}","comments_url":"https://api.github.com/repos/h-da/geli/comments{/number}","issue_comment_url":"https://api.github.com/repos/h-da/geli/issues/comments{/number}","contents_url":"https://api.github.com/repos/h-da/geli/contents/{+path}","compare_url":"https://api.github.com/repos/h-da/geli/compare/{base}...{head}","merges_url":"https://api.github.com/repos/h-da/geli/merges","archive_url":"https://api.github.com/repos/h-da/geli/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/h-da/geli/downloads","issues_url":"https://api.github.com/repos/h-da/geli/issues{/number}","pulls_url":"https://api.github.com/repos/h-da/geli/pulls{/number}","milestones_url":"https://api.github.com/repos/h-da/geli/milestones{/number}","notifications_url":"https://api.github.com/repos/h-da/geli/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/h-da/geli/labels{/name}","releases_url":"https://api.github.com/repos/h-da/geli/releases{/id}","deployments_url":"https://api.github.com/repos/h-da/geli/deployments","created_at":"2017-03-14T13:56:14Z","updated_at":"2018-04-23T12:45:19Z","pushed_at":"2018-04-23T13:39:38Z","git_url":"git://github.com/h-da/geli.git","ssh_url":"git@github.com:h-da/geli.git","clone_url":"https://github.com/h-da/geli.git","svn_url":"https://github.com/h-da/geli","homepage":"","size":45039,"stargazers_count":29,"watchers_count":29,"language":"TypeScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":8,"mirror_url":null,"archived":false,"open_issues_count":115,"license":{"key":"gpl-3.0","name":"GNU General Public License v3.0","spdx_id":"GPL-3.0","url":"https://api.github.com/licenses/gpl-3.0"},"forks":8,"open_issues":115,"watchers":29,"default_branch":"develop"}},"base":{"label":"h-da:develop","ref":"develop","sha":"623face662c21fbc95937d522f4fd8897268b9a3","user":{"login":"h-da","id":635522,"avatar_url":"https://avatars1.githubusercontent.com/u/635522?v=4","gravatar_id":"","url":"https://api.github.com/users/h-da","html_url":"https://github.com/h-da","followers_url":"https://api.github.com/users/h-da/followers","following_url":"https://api.github.com/users/h-da/following{/other_user}","gists_url":"https://api.github.com/users/h-da/gists{/gist_id}","starred_url":"https://api.github.com/users/h-da/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/h-da/subscriptions","organizations_url":"https://api.github.com/users/h-da/orgs","repos_url":"https://api.github.com/users/h-da/repos","events_url":"https://api.github.com/users/h-da/events{/privacy}","received_events_url":"https://api.github.com/users/h-da/received_events","type":"Organization","site_admin":false},"repo":{"id":84955069,"name":"geli","full_name":"h-da/geli","owner":{"login":"h-da","id":635522,"avatar_url":"https://avatars1.githubusercontent.com/u/635522?v=4","gravatar_id":"","url":"https://api.github.com/users/h-da","html_url":"https://github.com/h-da","followers_url":"https://api.github.com/users/h-da/followers","following_url":"https://api.github.com/users/h-da/following{/other_user}","gists_url":"https://api.github.com/users/h-da/gists{/gist_id}","starred_url":"https://api.github.com/users/h-da/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/h-da/subscriptions","organizations_url":"https://api.github.com/users/h-da/orgs","repos_url":"https://api.github.com/users/h-da/repos","events_url":"https://api.github.com/users/h-da/events{/privacy}","received_events_url":"https://api.github.com/users/h-da/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/h-da/geli","description":"geli is an open source e-learning platform","fork":false,"url":"https://api.github.com/repos/h-da/geli","forks_url":"https://api.github.com/repos/h-da/geli/forks","keys_url":"https://api.github.com/repos/h-da/geli/keys{/key_id}","collaborators_url":"https://api.github.com/repos/h-da/geli/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/h-da/geli/teams","hooks_url":"https://api.github.com/repos/h-da/geli/hooks","issue_events_url":"https://api.github.com/repos/h-da/geli/issues/events{/number}","events_url":"https://api.github.com/repos/h-da/geli/events","assignees_url":"https://api.github.com/repos/h-da/geli/assignees{/user}","branches_url":"https://api.github.com/repos/h-da/geli/branches{/branch}","tags_url":"https://api.github.com/repos/h-da/geli/tags","blobs_url":"https://api.github.com/repos/h-da/geli/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/h-da/geli/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/h-da/geli/git/refs{/sha}","trees_url":"https://api.github.com/repos/h-da/geli/git/trees{/sha}","statuses_url":"https://api.github.com/repos/h-da/geli/statuses/{sha}","languages_url":"https://api.github.com/repos/h-da/geli/languages","stargazers_url":"https://api.github.com/repos/h-da/geli/stargazers","contributors_url":"https://api.github.com/repos/h-da/geli/contributors","subscribers_url":"https://api.github.com/repos/h-da/geli/subscribers","subscription_url":"https://api.github.com/repos/h-da/geli/subscription","commits_url":"https://api.github.com/repos/h-da/geli/commits{/sha}","git_commits_url":"https://api.github.com/repos/h-da/geli/git/commits{/sha}","comments_url":"https://api.github.com/repos/h-da/geli/comments{/number}","issue_comment_url":"https://api.github.com/repos/h-da/geli/issues/comments{/number}","contents_url":"https://api.github.com/repos/h-da/geli/contents/{+path}","compare_url":"https://api.github.com/repos/h-da/geli/compare/{base}...{head}","merges_url":"https://api.github.com/repos/h-da/geli/merges","archive_url":"https://api.github.com/repos/h-da/geli/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/h-da/geli/downloads","issues_url":"https://api.github.com/repos/h-da/geli/issues{/number}","pulls_url":"https://api.github.com/repos/h-da/geli/pulls{/number}","milestones_url":"https://api.github.com/repos/h-da/geli/milestones{/number}","notifications_url":"https://api.github.com/repos/h-da/geli/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/h-da/geli/labels{/name}","releases_url":"https://api.github.com/repos/h-da/geli/releases{/id}","deployments_url":"https://api.github.com/repos/h-da/geli/deployments","created_at":"2017-03-14T13:56:14Z","updated_at":"2018-04-23T12:45:19Z","pushed_at":"2018-04-23T13:39:38Z","git_url":"git://github.com/h-da/geli.git","ssh_url":"git@github.com:h-da/geli.git","clone_url":"https://github.com/h-da/geli.git","svn_url":"https://github.com/h-da/geli","homepage":"","size":45039,"stargazers_count":29,"watchers_count":29,"language":"TypeScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":8,"mirror_url":null,"archived":false,"open_issues_count":115,"license":{"key":"gpl-3.0","name":"GNU General Public License v3.0","spdx_id":"GPL-3.0","url":"https://api.github.com/licenses/gpl-3.0"},"forks":8,"open_issues":115,"watchers":29,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/h-da/geli/pulls/664"},"html":{"href":"https://github.com/h-da/geli/pull/664"},"issue":{"href":"https://api.github.com/repos/h-da/geli/issues/664"},"comments":{"href":"https://api.github.com/repos/h-da/geli/issues/664/comments"},"review_comments":{"href":"https://api.github.com/repos/h-da/geli/pulls/664/comments"},"review_comment":{"href":"https://api.github.com/repos/h-da/geli/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/h-da/geli/pulls/664/commits"},"statuses":{"href":"https://api.github.com/repos/h-da/geli/statuses/ac6ba7afb948599b8b96cd58e6c766a1f06afc3c"}},"author_association":"COLLABORATOR"}}
{"actor":{"display_login":"grosspersky"}}