예제 #1
0
 def test_delete_asset_by_teacher(self, client, fake_auth, mock_asset,
                                  mock_category):
     """Authorized user can delete asset."""
     course = Course.find_by_id(mock_asset.course_id)
     instructors = list(filter(lambda u: is_teaching(u), course.users))
     fake_auth.login(instructors[0].id)
     self._verify_delete_asset(mock_asset.id, client)
예제 #2
0
 def test_teacher_view_asset(self, client, fake_auth, mock_asset):
     """Authorized user can view asset."""
     course = Course.find_by_id(mock_asset.course_id)
     instructors = list(filter(lambda u: is_teaching(u), course.users))
     fake_auth.login(instructors[0].id)
     asset = _api_get_asset(asset_id=mock_asset.id, client=client)
     assert asset['id'] == mock_asset.id
예제 #3
0
 def test_likes_multiple_users_increment_remove_likes_decrement(
         self, client, fake_auth, mock_asset):
     course_users = Course.find_by_id(mock_asset.course_id).users
     user_iterator = (user for user in course_users
                      if user not in mock_asset.users)
     different_user_1 = next(user_iterator)
     different_user_2 = next(user_iterator)
     fake_auth.login(different_user_1.id)
     response = self._api_like_asset(asset_id=mock_asset.id,
                                     client=client,
                                     expected_status_code=200)
     assert response.json['likes'] == 1
     assert response.json['liked'] is True
     fake_auth.login(different_user_2.id)
     response = self._api_like_asset(asset_id=mock_asset.id,
                                     client=client,
                                     expected_status_code=200)
     assert response.json['likes'] == 2
     assert response.json['liked'] is True
     fake_auth.login(different_user_1.id)
     response = self._api_remove_like_asset(asset_id=mock_asset.id,
                                            client=client,
                                            expected_status_code=200)
     assert response.json['likes'] == 1
     assert response.json['liked'] is False
     fake_auth.login(different_user_2.id)
     response = self._api_remove_like_asset(asset_id=mock_asset.id,
                                            client=client,
                                            expected_status_code=200)
     assert response.json['likes'] == 0
     assert response.json['liked'] is False
예제 #4
0
def mock_asset(app, db_session):
    course = Course.create(
        canvas_api_domain='bcourses.berkeley.edu',
        canvas_course_id=randrange(1000000),
    )
    category_hidden = Category.create(
        canvas_assignment_name='Just look into her false colored eyes',
        course_id=course.id,
        title='What a clown (visible=False)',
        canvas_assignment_id=98765,
        visible=False,
    )
    category_visible = Category.create(
        canvas_assignment_name='Just look into her false colored eyes',
        course_id=course.id,
        title='What a clown (visible=True)',
        canvas_assignment_id=98765,
        visible=True,
    )
    course = Course.query.order_by(Course.name).all()[0]
    canvas_user_id = str(randint(1000000, 9999999))
    user = User.create(
        canvas_course_role='Student',
        canvas_course_sections=[],
        canvas_email=f'{canvas_user_id}@berkeley.edu',
        canvas_enrollment_state='active',
        canvas_full_name=f'Student {canvas_user_id}',
        canvas_user_id=canvas_user_id,
        course_id=course.id,
    )
    unique_token = datetime.now().isoformat()
    asset = Asset.create(
        asset_type='link',
        categories=[category_hidden, category_visible],
        course_id=course.id,
        description=None,
        download_url=
        f"s3://{app.config['AWS_S3_BUCKET_FOR_ASSETS']}/asset/{course.id}_{canvas_user_id}_{unique_token}.pdf",
        title=f'Mock Asset created at {unique_token}',
        url=f'https://en.wikipedia.org/wiki/{unique_token}',
        users=[user],
    )
    for test_comment in _get_mock_comments():
        comment = Comment.create(asset=asset,
                                 body=test_comment['body'],
                                 user_id=user.id)
        for reply in test_comment.get('replies', []):
            reply = Comment.create(
                asset=asset,
                body=reply['body'],
                parent_id=comment.id,
                user_id=user.id,
            )
    std_commit(allow_test_environment=True)
    yield asset
    db_session.delete(asset)
    std_commit(allow_test_environment=True)
예제 #5
0
 def test_teacher_download(self, app, client, fake_auth, mock_asset):
     """Authorized user can download asset."""
     course = Course.find_by_id(mock_asset.course_id)
     instructors = list(filter(lambda u: is_teaching(u), course.users))
     fake_auth.login(instructors[0].id)
     # TODO: Mock S3 so authorized user actually gets download. For now, 404 oddly indicates success.
     self._api_download_asset(app,
                              asset_id=mock_asset.id,
                              client=client,
                              expected_status_code=404)
예제 #6
0
 def test_errant_remove_like_does_not_decrement(self, client, fake_auth,
                                                mock_asset):
     course_users = Course.find_by_id(mock_asset.course_id).users
     different_user = next(user for user in course_users
                           if user not in mock_asset.users)
     fake_auth.login(different_user.id)
     asset = _api_get_asset(asset_id=mock_asset.id, client=client)
     assert asset['likes'] == 0
     assert asset['liked'] is False
     response = self._api_remove_like_asset(asset_id=mock_asset.id,
                                            client=client,
                                            expected_status_code=200)
     assert response.json['likes'] == 0
     assert response.json['liked'] is False
예제 #7
0
 def test_likes_same_user_does_not_increment(self, client, fake_auth,
                                             mock_asset):
     course_users = Course.find_by_id(mock_asset.course_id).users
     different_user = next(user for user in course_users
                           if user not in mock_asset.users)
     fake_auth.login(different_user.id)
     response = self._api_like_asset(asset_id=mock_asset.id,
                                     client=client,
                                     expected_status_code=200)
     assert response.json['likes'] == 1
     response = self._api_like_asset(asset_id=mock_asset.id,
                                     client=client,
                                     expected_status_code=200)
     assert response.json['likes'] == 1
예제 #8
0
 def test_like_asset_by_course_user(self, client, fake_auth, mock_asset):
     """Another user in the same court can like an asset."""
     course_users = Course.find_by_id(mock_asset.course_id).users
     different_user = next(user for user in course_users
                           if user not in mock_asset.users)
     fake_auth.login(different_user.id)
     asset = _api_get_asset(asset_id=mock_asset.id, client=client)
     assert asset['likes'] == 0
     assert asset['liked'] is False
     response = self._api_like_asset(asset_id=mock_asset.id,
                                     client=client,
                                     expected_status_code=200)
     assert response.json['likes'] == 1
     assert response.json['liked'] is True
     asset = _api_get_asset(asset_id=mock_asset.id, client=client)
     assert asset['likes'] == 1
     assert asset['liked'] is True
예제 #9
0
 def test_like_asset_by_non_course_user(self, client, fake_auth,
                                        mock_asset):
     """A user in a different course can't like an asset."""
     second_course = Course.create(
         canvas_api_domain='bcourses.berkeley.edu',
         canvas_course_id=randrange(1000000),
     )
     second_course_user = User.create(
         canvas_course_role='Student',
         canvas_enrollment_state='active',
         canvas_full_name='Doug Yule',
         canvas_user_id=randrange(1000000),
         course_id=second_course.id,
     )
     fake_auth.login(second_course_user.id)
     self._api_like_asset(asset_id=mock_asset.id,
                          client=client,
                          expected_status_code=404)
예제 #10
0
 def test_increment_asset_view_count(self, client, fake_auth, mock_asset):
     course = Course.find_by_id(mock_asset.course_id)
     instructors = list(filter(lambda u: is_teaching(u), course.users))
     # Instructor 1 increments view count.
     fake_auth.login(instructors[0].id)
     asset = _api_get_asset(asset_id=mock_asset.id, client=client)
     assert asset['views'] == 1
     # Instructor 2 increments view count.
     fake_auth.login(instructors[1].id)
     asset = _api_get_asset(asset_id=mock_asset.id, client=client)
     assert asset['views'] == 2
     # Repeat views do not increment,
     fake_auth.login(instructors[0].id)
     asset = _api_get_asset(asset_id=mock_asset.id, client=client)
     assert asset['views'] == 2
     # Views by asset owners do not increment.
     fake_auth.login(mock_asset.users[0].id)
     asset = _api_get_asset(asset_id=mock_asset.id, client=client)
     assert asset['views'] == 2
예제 #11
0
def _create_courses():
    for c in _test_canvas:
        canvas = Canvas(
            canvas_api_domain=c['canvas_api_domain'],
            api_key=c['api_key'],
            lti_key=c['lti_key'],
            lti_secret=c['lti_secret'],
            name=c['name'],
        )
        db.session.add(canvas)
    std_commit(allow_test_environment=True)

    courses = []
    for c in _test_courses:
        course = Course(
            active=c['active'],
            canvas_api_domain=c['canvas_api_domain'],
            canvas_course_id=c['canvas_course_id'],
        )
        db.session.add(course)
        courses.append(course)
    std_commit(allow_test_environment=True)
    return courses
예제 #12
0
def _lti_launch_authentication(tool_id):
    is_asset_library = tool_id == TOOL_ID_ASSET_LIBRARY
    is_engagement_index = tool_id == TOOL_ID_ENGAGEMENT_INDEX
    if not is_asset_library and not is_engagement_index:
        raise BadRequestError(f'Missing or invalid tool_id: {tool_id}')

    def _alpha_num(s):
        value = _str_strip(s)
        return value if value.isalnum() else None

    args = request.form
    lti_params = {}
    validation = {
        'custom_canvas_api_domain': _str_strip,
        'custom_canvas_course_id': _str_strip,
        'custom_canvas_user_id': _str_strip,
        'custom_external_tool_url': _canvas_external_tool_url,
        'lis_person_name_full': _str_strip,
        'oauth_consumer_key': _alpha_num,
        'oauth_nonce': _alpha_num,
        'oauth_signature_method': _str_strip,
        'oauth_timestamp': _str_strip,
        'oauth_version': _str_strip,
        'roles': _str_strip,
    }

    def _fetch(key):
        value = args.get(key)
        validate = validation[key]
        pass_headers = validate is _canvas_external_tool_url
        validated_value = validate(value, request.headers) if pass_headers else validate(value)
        if validated_value:
            lti_params[key] = validated_value
            return lti_params[key]
        else:
            app.logger.warning(f'Invalid \'{key}\' parameter in LTI launch: {value}')

    if all(_fetch(key) for key in validation.keys()):
        app.logger.info(f'LTI launch params passed basic validation: {lti_params}')
        canvas_api_domain = lti_params['custom_canvas_api_domain']
        canvas = Canvas.find_by_domain(canvas_api_domain)

        if not canvas:
            raise ResourceNotFoundError(f'Failed \'canvas\' lookup where canvas_api_domain = {canvas_api_domain}')

        if canvas.lti_key != lti_params['oauth_consumer_key']:
            raise BadRequestError(f'oauth_consumer_key does not match {canvas_api_domain} lti_key in squiggy db.')

        tool_provider = FlaskToolProvider.from_flask_request(
            request=request,
            secret=canvas.lti_secret,
        )
        valid_request = tool_provider.is_valid_request(LtiRequestValidator(canvas))

        if valid_request or app.config['TESTING']:
            # TODO: We do not want app.config['TESTING'] in this conditional. It is here because our tests are failing
            #       on HMAC-signature verification. Let's fix after we get a successful LTI launch.
            app.logger.info(f'FlaskToolProvider validated {canvas_api_domain} LTI launch request.')
        else:
            raise BadRequestError(f'LTI oauth failed in {canvas_api_domain} request')

        external_tool_url = lti_params['custom_external_tool_url']
        canvas_course_id = lti_params['custom_canvas_course_id']
        course = Course.find_by_canvas_course_id(
            canvas_api_domain=canvas_api_domain,
            canvas_course_id=canvas_course_id,
        )
        if course:
            course = Course.update(
                asset_library_url=external_tool_url if is_asset_library else course.asset_library_url,
                course_id=course.id,
                engagement_index_url=external_tool_url if is_engagement_index else course.engagement_index_url,
            )
            app.logger.info(f'Updated course during LTI launch: {course.to_api_json()}')
        else:
            course = Course.create(
                asset_library_url=external_tool_url if is_asset_library else None,
                canvas_api_domain=canvas_api_domain,
                canvas_course_id=canvas_course_id,
                engagement_index_url=external_tool_url if is_engagement_index else None,
                name=args.get('context_title'),
            )
            app.logger.info(f'Created course via LTI launch: {course.to_api_json()}')

        canvas_user_id = lti_params['custom_canvas_user_id']
        user = User.find_by_course_id(canvas_user_id=canvas_user_id, course_id=course.id)
        if user:
            app.logger.info(f'Found user during LTI launch: canvas_user_id={canvas_user_id}, course_id={course.id}')
        else:
            user = User.create(
                course_id=course.id,
                canvas_user_id=canvas_user_id,
                canvas_course_role=str(lti_params['roles']),
                canvas_enrollment_state=args.get('custom_canvas_enrollment_state') or 'active',
                canvas_full_name=lti_params['lis_person_name_full'],
                canvas_image=args.get('user_image'),  # TODO: Verify user_image.
                canvas_email=args.get('lis_person_contact_email_primary'),
                canvas_course_sections=None,  # TODO: Set by poller?
            )
            app.logger.info(f'Created user during LTI launch: canvas_user_id={canvas_user_id}')

        path = '/assets' if is_asset_library else '/engage'
        params = f'canvasApiDomain={canvas_api_domain}&canvasCourseId={canvas_course_id}'
        app.logger.info(f'LTI launch redirect: {path}?{params}')
        return user, f'{path}?{params}'