def get_comments(asset_id): asset = Asset.find_by_id(asset_id=asset_id) if asset and can_view_asset(asset=asset, user=current_user): return tolerant_jsonify( _decorate_comments(Comment.get_comments(asset.id))) else: raise ResourceNotFoundError( 'Asset is either unavailable or non-existent.')
def _get_asset_for_like(asset_id): asset = Asset.find_by_id(asset_id=asset_id) if not asset or not can_view_asset(asset=asset, user=current_user): raise ResourceNotFoundError(f'No asset found with id: {asset_id}') elif current_user.user in asset.users: raise BadRequestError('You cannot like your own asset.') else: return asset
def delete_comment(comment_id): comment = Comment.find_by_id(comment_id=comment_id) if comment and can_delete_comment(comment=comment, user=current_user): Comment.delete(comment_id=comment_id) return tolerant_jsonify({'message': f'Comment {comment_id} deleted'}), 200 else: raise ResourceNotFoundError( 'Comment is either unavailable or non-existent.')
def get_asset(asset_id): asset = Asset.find_by_id(asset_id=asset_id) if asset and can_view_asset(asset=asset, user=current_user): if current_user.user not in asset.users: asset.increment_views(current_user.user) return tolerant_jsonify( asset.to_api_json(user_id=current_user.get_id())) else: raise ResourceNotFoundError(f'No asset found with id: {asset_id}')
def delete_asset(asset_id): asset = Asset.find_by_id(asset_id) if asset_id else None if not asset: raise ResourceNotFoundError('Asset not found.') if not can_update_asset(asset=asset, user=current_user): raise BadRequestError( 'To delete this asset you must own it or be a teacher in the course.' ) Asset.delete(asset_id=asset_id) return tolerant_jsonify({'message': f'Asset {asset_id} deleted'}), 200
def update_comment(comment_id): params = request.get_json() comment = Comment.find_by_id(comment_id=comment_id) if comment and can_update_comment(comment=comment, user=current_user): body = params.get('body', '').strip() if not body: raise BadRequestError('Comment body is required.') comment = Comment.update(body=body, comment_id=comment.id) return tolerant_jsonify(_decorate_comments([comment.to_api_json()])[0]) else: raise ResourceNotFoundError( 'Asset is either unavailable or non-existent.')
def dev_auth_login(): params = request.get_json() or {} if app.config['DEVELOPER_AUTH_ENABLED']: user_id = to_int(params.get('userId')) password = params.get('password') logger = app.logger if password != app.config['DEVELOPER_AUTH_PASSWORD']: logger.error('Dev auth: Wrong password') return tolerant_jsonify({'message': 'Invalid credentials'}, 401) return _login_user(user_id) else: raise ResourceNotFoundError('Unknown path')
def download(asset_id): asset = Asset.find_by_id(asset_id) s3_url = asset.download_url if asset and s3_url and can_view_asset(asset=asset, user=current_user): stream = stream_object(s3_url) if stream: now = datetime.now().strftime('%Y-%m-%d_%H-%M-%S') name = re.sub(r'[^a-zA-Z0-9]', '_', asset.title) extension = s3_url.rsplit('.', 1)[-1] return Response( stream, headers={ 'Content-disposition': f'attachment; filename="{name}_{now}.{extension}"', }, ) raise ResourceNotFoundError(f'Asset {asset_id} not found.')
def create_comment(): params = request.get_json() asset_id = params.get('assetId') asset = Asset.find_by_id(asset_id=asset_id) if asset and can_view_asset(asset=asset, user=current_user): body = params.get('body', '').strip() if not body: raise BadRequestError('Comment body is required.') parent_id = params.get('parentId') comment = Comment.create( asset=asset, user_id=current_user.user_id, body=body, parent_id=parent_id and int(parent_id), ) return tolerant_jsonify(_decorate_comments([comment.to_api_json()])[0]) else: raise ResourceNotFoundError( 'Asset is either unavailable or non-existent.')
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}'