Ejemplo n.º 1
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)
Ejemplo n.º 2
0
def _create_users(courses):
    course = courses[0]
    users = []
    for test_user in _test_users:
        user = User.create(
            canvas_course_role=test_user['canvas_course_role'],
            canvas_course_sections=[],
            canvas_email=test_user['canvas_email'],
            canvas_enrollment_state=test_user['canvas_enrollment_state'],
            canvas_full_name=test_user['canvas_full_name'],
            canvas_user_id=test_user['canvas_user_id'],
            course_id=course.id,
        )
        users.append(user)
    std_commit(allow_test_environment=True)
    return users
Ejemplo n.º 3
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)
Ejemplo n.º 4
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}'
Ejemplo n.º 5
0
    def poll_users(self, db_course, api_course):  # noqa C901
        db_users_by_canvas_id = {u.canvas_user_id: u for u in db_course.users}

        api_sections = list(api_course.get_sections(include=['students']))
        app.logger.info(f'Retrieved {len(api_sections)} sections from Canvas: {_format_course(db_course)}')
        api_sections_by_user_id = {}
        for s in api_sections:
            for u in (s.students or []):
                user_id = u['id']
                if api_sections_by_user_id.get(user_id):
                    api_sections_by_user_id[user_id].append(s.name)
                else:
                    api_sections_by_user_id[user_id] = [s.name]

        api_users = list(api_course.get_users(include=['enrollments', 'avatar_url', 'email']))
        app.logger.info(f'Retrieved {len(api_users)} users from Canvas: {_format_course(db_course)}')
        api_user_ids = set()
        for u in api_users:
            api_user_ids.add(u.id)
            enrollment_state = 'active'
            course_role = 'Student'
            enrollment = next((e for e in u.enrollments if e['course_id'] == db_course.canvas_course_id), None)
            if not enrollment:
                enrollment_state = 'completed'
            else:
                if enrollment['enrollment_state'] in ['active', 'completed', 'inactive', 'invited', 'rejected']:
                    enrollment_state = enrollment['enrollment_state']
                if enrollment['role'] in ['Adv Designer', 'DesignerEnrollment', 'TaEnrollment', 'TeacherEnrollment']:
                    course_role = 'urn:lti:role:ims/lis/Instructor'

            user_attributes = {
                'canvas_course_role': course_role,
                'canvas_enrollment_state': enrollment_state,
                'canvas_full_name': u.name,
                'canvas_user_id': u.id,
                'course_id': db_course.id,
                'canvas_course_sections': api_sections_by_user_id.get(u.id, []),
                'canvas_email': getattr(u, 'email', None),
                'canvas_image': getattr(u, 'avatar_url', None),
            }
            db_user = db_users_by_canvas_id.get(u.id)
            if not db_user:
                app.logger.debug(f'Adding new user {u.id}: {_format_course(db_course)}')
                db_users_by_canvas_id[u.id] = User.create(**user_attributes)
            else:
                updated = False
                for key, value in user_attributes.items():
                    if getattr(db_user, key, None) != value:
                        setattr(db_user, key, value)
                        updated = True
                if updated:
                    app.logger.debug(f'Updating info for user {db_user.canvas_user_id}: {_format_course(db_course)}')
                    db.session.add(db_user)
                    std_commit()

        for db_user in db_users_by_canvas_id.values():
            if db_user.canvas_user_id not in api_user_ids and db_user.canvas_enrollment_state != 'inactive':
                app.logger.debug(f'Marking user {db_user.canvas_user_id} as inactive: {_format_course(db_course)}')
                db_user.canvas_enrollment_state = 'inactive'
                db.session.add(db_user)
        std_commit()
        return db_users_by_canvas_id