Skip to content
This repository has been archived by the owner on Apr 16, 2024. It is now read-only.

Commit

Permalink
Merge branch 'develop' into feature/#582-unit-visibility-toggle-2
Browse files Browse the repository at this point in the history
  • Loading branch information
dboschm authored May 2, 2018
2 parents 2e3a21d + 641d06f commit c2a2c3f
Show file tree
Hide file tree
Showing 50 changed files with 1,024 additions and 1,313 deletions.
7 changes: 4 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ before_script:
- sh -e /etc/init.d/xvfb start
# required for MongoDB to have time to start up
- sleep 15
- npm install -g @angular/cli

install:
# install api and return back to root for following steps
Expand Down Expand Up @@ -75,9 +76,9 @@ script:
- cd ..
# run frontend-tests and return back to root for following steps
- cd app/webFrontend
- npm run lint # run linter
# - npm run test # run test - does not work properly
- npm run e2e # run end-to-end-tests
- ng lint # run linter
# - ng test --single-run true --watch false || true # run test - many tests are broken
- ng e2e # run end-to-end-tests
- sed -i 's/$TRAVIS_COMMIT/'$TRAVIS_COMMIT'/' src/app/shared/services/raven-error-handler.service.ts
- cd ../..
- travis_wait 30 .travis/build-fe.sh # build in prod mode
Expand Down
37 changes: 23 additions & 14 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,36 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [NEXT]
### Added
- Added an Box for Information on the Homescreen [#216] (https://github.com/h-da/geli/issues/216)
- Added account activation resend feature [#601] (https://github.com/h-da/geli/issues/601)
- Added the possibility to sort all courses alphabetically. [#567](https://github.com/h-da/geli/issues/567)
- Added a box for information on the homescreen. [#216](https://github.com/h-da/geli/issues/216)
- Added an account activation resend feature. [#601](https://github.com/h-da/geli/issues/601)
- Added `SnackBarService` as wrapper for `MatSnackBar`. [#574](https://github.com/h-da/geli/issues/574)
- Added new course & user API unit tests. [#654](https://github.com/h-da/geli/issues/654) [#691](https://github.com/h-da/geli/issues/691)
- Added details of courseAdmin and teacher to course detail view. on click profiles are shown.[#598] (https://github.com/h-da/geli/issues/598)

### Changed
- Refactored or slightly altered various course & user related APIs. [#654](https://github.com/h-da/geli/issues/654) [#691](https://github.com/h-da/geli/issues/691)

### Fixed
- wasteful course data usage #654 via specialized course model interfaces.
- a broken documentation link #583.
- Limited the first- and lastname to 64 characters in registration- and edit page. [#585](https://github.com/h-da/geli/issues/585)
- add correct E-Mail validator to edit-profile and register component [#564]
- Upload of profile pictures now prevents files with wrong extensions [#581] (https://github.com/h-da/geli/issues/581)
- the view of downloading a empty course [#659](https://github.com/h-da/geli/issues/659)
- Videos in the Course Section now get equally sized and cant grow too big in mobile views. [#534] (https://github.com/h-da/geli/issues/534)
- Background on password reset page is missing [#673] (https://github.com/h-da/geli/issues/673)
- Fix space in the topbar for notification icon for student. [#696] (https://github.com/h-da/geli/issues/696)
- Fixed wasteful course data usage via specialized course model interfaces. [#654](https://github.com/h-da/geli/issues/654)
- Fixed a broken documentation link. [#583](https://github.com/h-da/geli/issues/583)
- Limited the first and last name to 64 characters in the registration- and edit page. [#585](https://github.com/h-da/geli/issues/585)
- Added a correct email validator to the `user-edit` and `register` components. [#564](https://github.com/h-da/geli/issues/564)
- Upload of profile pictures now prevents files with forbidden extensions. [#581](https://github.com/h-da/geli/issues/581)
- Fixed empty course downloads. [#659](https://github.com/h-da/geli/issues/659)
- Videos in the course now get sized equally and can't grow too big in mobile views. [#534](https://github.com/h-da/geli/issues/534)
- Fixed missing background on the password reset page. [#673](https://github.com/h-da/geli/issues/673)
- Fixed notification icon spacing in the navbar for students. [#696](https://github.com/h-da/geli/issues/696)
- Repair Angular CLI code generation. [#701](https://github.com/h-da/geli/pull/701)
- Fixed `tsconfig.spec.ts` for `ng test`. [#656](https://github.com/h-da/geli/pull/656)

### Added
- Unit visibility toggle [#582](https://github.com/h-da/geli/issues/582)

### Security
- Fixed multiple severe course related security issues #594, #653.
- Updated the dependencies for security

- Fixed numerous severe user related security issues. [#691](https://github.com/h-da/geli/issues/691)
- Fixed multiple severe course related security issues. [#594](https://github.com/h-da/geli/issues/594) [#653](https://github.com/h-da/geli/issues/653) [#691](https://github.com/h-da/geli/issues/691)
- Updated the dependencies for security. [#661](https://github.com/h-da/geli/issues/661)

## [0.6.0] - 2018-03-31 - Introduces MediaManager and some minor changes
### Added
Expand Down
64 changes: 50 additions & 14 deletions api/src/config/errorCodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,6 @@
import config from '../config/main';

export const errorCodes = {
user : {
userNotFound: {
code: 'user not found',
text: 'User was not found.',
},
retryAfter: {
code: 'retry after',
text: 'You can only resend the activation every ' + config.timeTilNextActivationResendMin + ' minutes. You can resend again in ',
},
userAlreadyActive: {
code: 'user already active',
text: 'User is already activated.'
}
},
mail: {
duplicate: {
code: 'duplicate mail',
Expand Down Expand Up @@ -48,6 +34,56 @@ export const errorCodes = {
text: 'Incorrect or missing access key'
}
},
user: {
cantChangeOwnRole: {
code: 'cantChangeOwnRole',
text: 'You can\'t change your own role.'
},
emailAlreadyInUse: {
code: 'emailAlreadyInUse',
text: 'This email address is already in use.'
},
invalidOldUserRole: {
code: 'invalidOldUserRole',
text: 'Invalid old user role.'
},
invalidNewUserRole: {
code: 'invalidNewUserRole',
text: 'Invalid update role.'
},
cantChangeUserWithHigherRole: {
code: 'cantChangeUserWithHigherRole',
text: 'You don\'t have the authorization to change a user of this role.'
},
onlyAdminsCanChangeRoles: {
code: 'onlyAdminsCanChangeRoles',
text: 'Only users with admin privileges can change roles.'
},
onlyAdminsCanChangeUids: {
code: 'onlyAdminsCanChangeUids',
text: 'Only users with admin privileges can change uids.'
},
invalidPassword: {
code: 'invalidPassword',
text: 'Invalid current password!'
},
noOtherAdmins: {
code: 'noOtherAdmins',
text: 'There are no other users with admin privileges.'
},
userNotFound: {
code: 'user not found',
text: 'User was not found.',
},
retryAfter: {
code: 'retry after',
text: 'You can only resend the activation every ' + config.timeTilNextActivationResendMin + ' minutes. You can resend again in ',
},
userAlreadyActive: {
code: 'user already active',
text: 'User is already activated.'
}
},
whitelist: {
duplicateWhitelistUser: {
code: 'duplicate uid',
Expand Down
88 changes: 30 additions & 58 deletions api/src/controllers/CourseController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
UseBefore
} from 'routing-controllers';
import passportJwtMiddleware from '../security/passportJwtMiddleware';
import * as errorCodes from '../config/errorCodes';
import {errorCodes} from '../config/errorCodes';

import {ICourse} from '../../../shared/models/ICourse';
import {ICourseDashboard} from '../../../shared/models/ICourseDashboard';
Expand All @@ -29,6 +29,7 @@ const multer = require('multer');
import crypto = require('crypto');
import {API_NOTIFICATION_TYPE_ALL_CHANGES, NotificationSettings} from '../models/NotificationSettings';
import {IWhitelistUser} from '../../../shared/models/IWhitelistUser';
import {DocumentToObjectOptions} from 'mongoose';

const uploadOptions = {
storage: multer.diskStorage({
Expand Down Expand Up @@ -172,6 +173,8 @@ export class CourseController {
}

await course.populateLecturesFor(currentUser)
.populate('courseAdmin')
.populate('teachers')
.execPopulate();
await course.processLecturesFor(currentUser);
return course.forView();
Expand Down Expand Up @@ -336,7 +339,7 @@ export class CourseController {
.populate('whitelist')
.execPopulate();
await course.processLecturesFor(currentUser);
return course.toObject();
return course.toObject(<DocumentToObjectOptions>{currentUser});
}

private userReadConditions(currentUser: IUser) {
Expand Down Expand Up @@ -425,7 +428,7 @@ export class CourseController {
// If a strict version is deemed important, see mongoose Model.findOneAndUpdate for a potential approach.
const existingCourse = await Course.findOne({name: course.name});
if (existingCourse) {
throw new BadRequestError(errorCodes.errorCodes.course.duplicateName.code);
throw new BadRequestError(errorCodes.course.duplicateName.code);
}
course.courseAdmin = currentUser;
const newCourse = new Course(course);
Expand Down Expand Up @@ -479,38 +482,10 @@ export class CourseController {
* @apiParam {Object} data Data (with access key).
* @apiParam {IUser} currentUser Currently logged in user.
*
* @apiSuccess {Course} course Enrolled course.
* @apiSuccess {{}} result Empty object.
*
* @apiSuccessExample {json} Success-Response:
* {
* "_id": "5a037e6b60f72236d8e7c83d",
* "updatedAt": "2017-11-08T22:00:11.869Z",
* "createdAt": "2017-11-08T22:00:11.263Z",
* "name": "Introduction to web development",
* "description": "Whether you're just getting started with Web development or are just expanding your horizons...",
* "courseAdmin": {
* "_id": "5a037e6a60f72236d8e7c815",
* "updatedAt": "2017-11-08T22:00:10.898Z",
* "createdAt": "2017-11-08T22:00:10.898Z",
* "email": "teacher2@test.local",
* "isActive": true,
* "role": "teacher",
* "profile": {
* "firstName": "Ober",
* "lastName": "Lehrer"
* },
* "id": "5a037e6a60f72236d8e7c815"
* },
* "active": true,
* "__v": 1,
* "whitelist": [],
* "enrollType": "free",
* "lectures": [],
* "students": [],
* "teachers": [],
* "id": "5a037e6b60f72236d8e7c83d",
* "hasAccessKey": false
* }
* {}
*
* @apiError NotFoundError
* @apiError ForbiddenError Not allowed to join, you are not on whitelist.
Expand All @@ -519,32 +494,33 @@ export class CourseController {
@Authorized(['student'])
@Post('/:id/enroll')
async enrollStudent(@Param('id') id: string, @Body() data: any, @CurrentUser() currentUser: IUser) {
let course = await Course.findById(id);
const course = await Course.findById(id);
if (!course) {
throw new NotFoundError();
}
if (course.enrollType === 'whitelist') {
const wUsers: IWhitelistUser[] = await WhitelistUser.find().where({courseId: course._id});
if (wUsers.filter(e =>
e.firstName === currentUser.profile.firstName.toLowerCase()
&& e.lastName === currentUser.profile.lastName.toLowerCase()
&& e.uid === currentUser.uid).length <= 0) {
throw new ForbiddenError(errorCodes.errorCodes.course.notOnWhitelist.code);
e.firstName === currentUser.profile.firstName.toLowerCase()
&& e.lastName === currentUser.profile.lastName.toLowerCase()
&& e.uid === currentUser.uid).length <= 0) {
throw new ForbiddenError(errorCodes.course.notOnWhitelist.code);
}
} else if (course.accessKey && course.accessKey !== data.accessKey) {
throw new ForbiddenError(errorCodes.errorCodes.course.accessKey.code);
throw new ForbiddenError(errorCodes.course.accessKey.code);
}

if (course.students.indexOf(currentUser._id) < 0) {
course.students.push(currentUser);
await new NotificationSettings({
'user': currentUser, 'course': course,
'user': currentUser,
'course': course,
'notificationType': API_NOTIFICATION_TYPE_ALL_CHANGES,
'emailNotification': false
}).save();
course = await course.save();
await course.save();
}
return course.toObject();
return {};
}

/**
Expand All @@ -557,10 +533,10 @@ export class CourseController {
* @apiParam {Object} data Body.
* @apiParam {IUser} currentUser Currently logged in user.
*
* @apiSuccess {Course} course Left course.
* @apiSuccess {{}} result Empty object.
*
* @apiSuccessExample {json} Success-Response:
* {result: true}
* {}
*
* @apiError NotFoundError
* @apiError ForbiddenError
Expand All @@ -577,7 +553,7 @@ export class CourseController {
course.students.splice(index, 1);
await NotificationSettings.findOne({'user': currentUser, 'course': course}).remove();
await course.save();
return {result: true};
return {};
} else {
// This equals an implicit !course.checkPrivileges(currentUser).userIsCourseStudent check.
throw new ForbiddenError();
Expand All @@ -595,11 +571,10 @@ export class CourseController {
* @apiParam {String} id Course ID.
* @apiParam {Object} file Uploaded file.
*
* @apiSuccess {Course} course Updated course.
* @apiSuccess {Object} result Returns the new whitelist length.
*
* @apiSuccessExample {json} Success-Response:
* {
* success: true,
* newlength: 10
* }
*
Expand All @@ -615,7 +590,7 @@ export class CourseController {
@CurrentUser() currentUser: IUser) {
const name: string = file.originalname;
if (!name.endsWith('.csv')) {
throw new TypeError(errorCodes.errorCodes.upload.type.notCSV.code);
throw new TypeError(errorCodes.upload.type.notCSV.code);
}
const course = await Course.findById(id);
if (!course.checkPrivileges(currentUser).userCanEditCourse) {
Expand All @@ -628,7 +603,7 @@ export class CourseController {
const buffer = <string> await this.parser.parseFile(file);
await this.parser.updateCourseFromBuffer(buffer, course);
await course.save();
return {success: true, newlength: course.whitelist.length};
return {newlength: course.whitelist.length};
}

/**
Expand All @@ -642,13 +617,12 @@ export class CourseController {
* @apiParam {ICourse} course New course data.
* @apiParam {IUser} currentUser Currently logged in user.
*
* @apiSuccess {Course} course Updated course.
* @apiSuccess {Object} result ID and name of the course.
*
* @apiSuccessExample {json} Success-Response:
* {
* _id: "5a037e6b60f72236d8e7c83d",
* name: "Introduction to web development",
* success: true
* name: "Introduction to web development"
* }
*
* @apiError NotFoundError Can't find the course. (Includes implicit authorization check.)
Expand All @@ -665,7 +639,7 @@ export class CourseController {
}
const updatedCourse = await Course.findOneAndUpdate(conditions, course, {'new': true});
if (updatedCourse) {
return {_id: updatedCourse.id, name: updatedCourse.name, success: true};
return {_id: updatedCourse.id, name: updatedCourse.name};
} else {
throw new NotFoundError();
}
Expand All @@ -679,12 +653,10 @@ export class CourseController {
* @apiParam {String} id Course ID.
* @apiParam {IUser} currentUser Currently logged in user.
*
* @apiSuccess {Boolean} result Confirmation of deletion.
* @apiSuccess {{}} result Empty object.
*
* @apiSuccessExample {json} Success-Response:
* {
* "result": true
* }
* {}
*
* @apiError NotFoundError
* @apiError ForbiddenError
Expand All @@ -700,6 +672,6 @@ export class CourseController {
throw new ForbiddenError();
}
await course.remove();
return {result: true};
return {};
}
}
Loading

0 comments on commit c2a2c3f

Please sign in to comment.