class CommunityRequestsResource(MethodView): """Resource to list community membership requests.""" post_args = { 'response': fields.Raw(location='json', required=True, validate=[validate.OneOf(['accept', 'decline'])]), 'role': fields.Raw(location='json', required=False, validate=[validate.OneOf(['M', 'A', 'C'])]) } @pass_community def get(self, comid=None, community=None, outgoing_only=False, incoming_only=False, page_size=20, page=0): """List all the community membership requests.""" admin_ids = \ [admin.user.id for admin in CommunityMember.get_admins( community.id)] if int(current_user.get_id()) not in admin_ids: abort(404) response_object = {} if not outgoing_only: count, requests = \ MembershipRequestAPI.get_community_incoming_requests( community.id, page_size=page_size) response_object['inc_count'] = count response_object['inc_requests'] = [] for req in requests: response_object['inc_requests'].append({ 'email': req.email, 'req_id': req.id }) if not incoming_only: count, requests = \ MembershipRequestAPI.get_community_outgoing_requests( community.id, page_size=page_size) response_object['out_count'] = count response_object['out_requests'] = [] for req in requests: response_object['out_requests'].append({ 'email': req.email, 'req_id': req.id, 'role': str(req.role.title) }) return jsonify(response_object), 200
class BucketResource(ContentNegotiatedMethodView): """"Bucket item resource.""" get_args = { 'versions': fields.Raw( location='query', missing=False, ), 'uploads': fields.Raw( location='query', missing=False, ) } @need_permissions(lambda self, bucket: bucket, 'bucket-listmultiparts') def multipart_listuploads(self, bucket): """List objects in a bucket.""" return self.make_response( data=MultipartObject.query_by_bucket(bucket).limit(1000).all(), context={ 'class': MultipartObject, 'bucket': bucket, 'many': True, }) @need_permissions( lambda self, bucket, versions: bucket, 'bucket-read', ) def listobjects(self, bucket, versions): """List objects in a bucket.""" if versions is not False: check_permission(current_permission_factory( bucket, 'bucket-read-versions'), hidden=False) return self.make_response(data=ObjectVersion.get_by_bucket( bucket.id, versions=versions is not False).limit(1000).all(), context={ 'class': ObjectVersion, 'bucket': bucket, 'many': True, }) @use_kwargs(get_args) @pass_bucket def get(self, bucket=None, versions=None, uploads=None): """Get list of objects in the bucket.""" if uploads is not False: return self.multipart_listuploads(bucket) else: return self.listobjects(bucket, versions) @pass_bucket @need_bucket_permission('bucket-read') def head(self, bucket=None, **kwargs): """Check the existence of the bucket."""
def post(self): """Uploads a file""" file_obj = FlaskParser.parse_files(self, request, "upfile", field=fields.Raw()) # pylint: disable=maybe-no-member if isinstance(file_obj, type( marshmallow.missing)): # werkzeug.datastructures.FileStorage return {"message": "File missing"}, 400 # Bad request size = Upload.get_size(file_obj) if size > app.config["MAX_UPLOAD"]: return { "message": "You exceded file limit of {}".format(app.config["MAX_UPLOAD"]) }, 400 if file_obj.mimetype != Upload.mimetype: return { "message": "File mimetype is {} instead of {}".format( str(file_obj.mimetype), Upload.mimetype) }, 415 # 415 Unsupported Media Type file_path = os.path.join(app.config['UPLOAD_FOLDER'], file_obj.filename) file_obj.save(file_path) return jsonify( message="Saved file:{} with size {} bytes".format(file_path, size))
class Main(Resource): args = { "query": fields.Raw(required=True, validate=validate.Length(1, max=None)), "count": fields.Int(required=False, validate=validate.Range(1, max=None)) } def __init__(self, indexer: Indexer): self.__indexer = indexer @use_args(args) def get(self, args): queryResult = self.__indexer.query(args["query"]) if "count" in args: return queryResult.jsonify(count=args["count"]) else: return queryResult.jsonify()
class BucketResource(ContentNegotiatedMethodView): """Bucket item resource.""" get_args = { 'versions': fields.Raw( location='query', ), 'uploads': fields.Raw( location='query', ) } def __init__(self, *args, **kwargs): """Instantiate content negotiated view.""" super(BucketResource, self).__init__(*args, **kwargs) @need_permissions(lambda self, bucket: bucket, 'bucket-listmultiparts') def multipart_listuploads(self, bucket): """List objects in a bucket. :param bucket: A :class:`invenio_files_rest.models.Bucket` instance. :returns: The Flask response. """ return self.make_response( data=MultipartObject.query_by_bucket(bucket).limit(1000).all(), context={ 'class': MultipartObject, 'bucket': bucket, 'many': True, } ) @need_permissions( lambda self, bucket, versions: bucket, 'bucket-read', ) def listobjects(self, bucket, versions): """List objects in a bucket. :param bucket: A :class:`invenio_files_rest.models.Bucket` instance. :returns: The Flask response. """ if versions is not missing: check_permission( current_permission_factory(bucket, 'bucket-read-versions'), hidden=False ) return self.make_response( data=ObjectVersion.get_by_bucket( bucket.id, versions=versions is not missing).limit(1000).all(), context={ 'class': ObjectVersion, 'bucket': bucket, 'many': True, } ) @use_kwargs(get_args) @pass_bucket def get(self, bucket=None, versions=missing, uploads=missing): """Get list of objects in the bucket. :param bucket: A :class:`invenio_files_rest.models.Bucket` instance. :returns: The Flask response. """ if uploads is not missing: return self.multipart_listuploads(bucket) else: return self.listobjects(bucket, versions) @pass_bucket @need_bucket_permission('bucket-read') def head(self, bucket=None, **kwargs): """Check the existence of the bucket."""
@use_kwargs({ 'part_number': fields.Int( load_from='_chunkNumber', location='form', required=True, ), 'content_length': fields.Int( load_from='_currentChunkSize', location='form', required=True, validate=minsize_validator, ), 'uploaded_file': fields.Raw( load_from='file', location='files', required=True, ), }) def ngfileupload_partfactory(part_number=None, content_length=None, uploaded_file=None): """Part factory for ng-file-upload. :param part_number: The part number. (Default: ``None``) :param content_length: The content length. (Default: ``None``) :param uploaded_file: The upload request. (Default: ``None``) :returns: The content length, part number, stream, HTTP Content-Type header. """ return content_length, part_number, uploaded_file.stream, \ uploaded_file.headers.get('Content-Type'), None, None
class ObjectResource(ContentNegotiatedMethodView): """"Object item resource.""" get_args = { 'version_id': fields.UUID( location='query', load_from='versionId', missing=None, ), 'upload_id': fields.UUID( location='query', load_from='uploadId', missing=None, ) } delete_args = get_args post_args = { 'uploads': fields.Raw( location='query', missing=False, ), 'upload_id': fields.UUID( location='query', load_from='uploadId', missing=None, ) } put_args = { 'upload_id': fields.UUID( location='query', load_from='uploadId', missing=None, ), } upload_headers = { 'content_md5': fields.Str( load_from='Content-MD5', location='headers', missing=None, ), 'content_length': fields.Int( load_from='Content-Length', location='headers', required=True, validate=minsize_validator, ) } multipart_init_args = { 'size': fields.Int( locations=('query', 'json'), required=True, ), 'part_size': fields.Int( locations=('query', 'json'), required=True, ), } # # ObjectVersion helpers # @staticmethod def get_object(bucket, key, version_id): """Retrieve object and abort if it doesn't exists.""" obj = ObjectVersion.get(bucket, key, version_id=version_id) if not obj: abort(404, 'Object does not exists.') check_permission(current_permission_factory(obj, 'object-read')) if not obj.is_head: check_permission(current_permission_factory( obj, 'object-read-version'), hidden=False) return obj @staticmethod @use_kwargs(upload_headers) def create_object(bucket, key, uploaded_file=None, content_md5=None, content_length=None): """Create a new object.""" # Initial validation of size based on Content-Length. # User can tamper with Content-Length, so this is just an initial up # front check. The storage subsystem must validate the size limit as # well. size_limit = bucket.size_limit if size_limit and content_length > size_limit: desc = 'File size limit exceeded.' \ if isinstance(size_limit, int) else size_limit.reason raise FileSizeError(description=desc) with db.session.begin_nested(): obj = ObjectVersion.create(bucket, key) obj.set_contents(request.stream, size=content_length, size_limit=size_limit) db.session.commit() return obj @need_permissions( lambda self, bucket, obj, *args: obj, 'object-delete', hidden=False, # Because get_object permission check has already run ) def delete_object(self, bucket, obj, version_id): """Delete an existing object.""" if version_id is None: # Create a delete marker. with db.session.begin_nested(): ObjectVersion.delete(bucket, obj.key) db.session.commit() else: # Permanently delete specific object version. check_permission( current_permission_factory(bucket, 'object-delete-version'), hidden=False, ) obj.remove() db.session.commit() if obj.file_id: remove_file_data.delay(str(obj.file_id)) return self.make_response('', 204) @staticmethod def send_object(bucket, obj, expected_chksum=None, logger_data=None, restricted=True): """Send an object for a given bucket.""" if not obj.is_head: check_permission(current_permission_factory( obj, 'object-read-version'), hidden=False) if expected_chksum and obj.file.checksum != expected_chksum: current_app.logger.warning('File checksum mismatch detected.', extra=logger_data) file_downloaded.send(current_app._get_current_object(), obj=obj) return obj.send_file(restricted=restricted) # # MultipartObject helpers # @pass_multipart(with_completed=True) @need_permissions(lambda self, multipart: multipart, 'multipart-read') def multipart_listparts(self, multipart): """Get parts of a multpart upload.""" return self.make_response( data=Part.query_by_multipart(multipart).order_by( Part.part_number).limit(1000).all(), context={ 'class': Part, 'multipart': multipart, 'many': True, }) @use_kwargs(multipart_init_args) def multipart_init(self, bucket, key, size=None, part_size=None): """Initiate a multipart upload.""" multipart = MultipartObject.create(bucket, key, size, part_size) db.session.commit() return self.make_response(data=multipart, context={ 'class': MultipartObject, 'bucket': bucket, }) @use_kwargs(upload_headers) @pass_multipart(with_completed=True) def multipart_uploadpart(self, multipart, content_md5=None, content_length=None): """Upload a part.""" if content_length != multipart.chunk_size: raise MultipartInvalidChunkSize() # Extract part number from request. data = None for schema in current_files_rest.uploadparts_schema_factory: try: data = parser.parse(schema) if data: break except UnprocessableEntity: pass if not data or data.get('part_number') is None: raise MultipartInvalidPartNumber() part_number = data['part_number'] # Create part try: p = Part.get_or_create(multipart, part_number) p.set_contents(request.stream) db.session.commit() except Exception: # We remove the Part since incomplete data may have been written to # disk (e.g. client closed connection etc.) db.session.rollback() Part.delete(multipart, part_number) raise return self.make_response(data=p, context={ 'class': Part, }, etag=p.checksum) @pass_multipart(with_completed=True) def multipart_complete(self, multipart): """Complete a multipart upload.""" multipart.complete() db.session.commit() merge_multipartobject.delay(str(multipart.upload_id)) return self.make_response(data=multipart, context={ 'class': MultipartObject, 'bucket': multipart.bucket, }) @pass_multipart() @need_permissions( lambda self, multipart: multipart, 'multipart-delete', ) def multipart_delete(self, multipart): """Abort a multipart upload.""" multipart.delete() db.session.commit() if multipart.file_id: remove_file_data.delay(str(multipart.file_id)) return self.make_response('', 204) # # HTTP methods implementations # @use_kwargs(get_args) @pass_bucket def get(self, bucket=None, key=None, version_id=None, upload_id=None): """Get object or list parts of a multpart upload.""" if upload_id: return self.multipart_listparts(bucket, key, upload_id) else: obj = self.get_object(bucket, key, version_id) return self.send_object(bucket, obj) @use_kwargs(post_args) @pass_bucket @need_bucket_permission('bucket-update') def post(self, bucket=None, key=None, uploads=None, upload_id=None): """Upload a new object or start/complete a multipart upload.""" if uploads is not False: return self.multipart_init(bucket, key) elif upload_id is not None: return self.multipart_complete(bucket, key, upload_id) abort(403) @use_kwargs(put_args) @pass_bucket @need_bucket_permission('bucket-update') def put(self, bucket=None, key=None, upload_id=None): """Update a new object or upload a part of a multipart upload.""" if upload_id is not None: return self.multipart_uploadpart(bucket, key, upload_id) else: return self.create_object(bucket, key) @use_kwargs(delete_args) @pass_bucket def delete(self, bucket=None, key=None, version_id=None, upload_id=None): """Delete an object or abort a multipart upload.""" if upload_id is not None: return self.multipart_delete(bucket, key, upload_id) else: obj = self.get_object(bucket, key, version_id) return self.delete_object(bucket, obj, version_id)
data_key='_chunkNumber', location='form', required=True, ), 'content_length': fields.Int( load_from='_currentChunkSize', data_key='_currentChunkSize', location='form', required=True, validate=minsize_validator, ), 'uploaded_file': fields.Raw( load_from='file', data_key='file', location='files', required=True, ), }) def ngfileupload_partfactory(part_number=None, content_length=None, uploaded_file=None): """Part factory for ng-file-upload. :param part_number: The part number. (Default: ``None``) :param content_length: The content length. (Default: ``None``) :param uploaded_file: The upload request. (Default: ``None``) :returns: The content length, part number, stream, HTTP Content-Type header. """ return content_length, part_number, uploaded_file.stream, \
class SuccessListSchema(ma.Schema): results = fields.List(fields.Raw()) total = fields.Number()
class CommunityMembersResource(MethodView): """Resource for creating, listing and removing community memberships.""" post_args = { 'role': fields.Raw(location='json', required=False, validate=[validate.OneOf(['M', 'A', 'C'])]), 'email': fields.Email(location='json', required=False), 'request_type': fields.Raw(location='json', required=True, validate=[validate.OneOf(['invitation', 'request'])]) } put_args = { 'role': fields.Raw(location='json', required=False, validate=[validate.OneOf(['M', 'A', 'C'])]), 'user_id': fields.Raw(location='json', required=False), 'email': fields.Email(location='json', required=False), } delete_args = { 'user_id': fields.Raw(location='query', required=False), } @use_kwargs(post_args) @pass_community def post(self, comid=None, community=None, email=None, role=None, request_type=None, **kwargs): """Join a community or invite a user to it.""" if request_type == 'invitation': admin_ids = \ [admin.user.id for admin in CommunityMember.get_admins( community.id)] if int(current_user.get_id()) not in admin_ids: abort(404) existing_membership_req = MembershipRequest.query.filter_by( comm_id=community.id, email=email).one_or_none() if existing_membership_req: abort(400, 'This is an already existing relationship.') CommunityMembersAPI.invite_member(community, email, role) elif request_type == 'request': user_id = int(current_user.get_id()) email = current_user.email CommunityMembersAPI.join_community(user_id, email, community) db.session.commit() return 'Succesfully Invited', 200 @pass_community def get(self, comid=None, community=None): """List the community members.""" admin_ids = \ [admin.user.id for admin in CommunityMember.get_admins( community.id)] if int(current_user.get_id()) not in admin_ids: abort(404) ui_members = [] for member in CommunityMembersAPI.get_members(community.id).all(): add_member = {} add_member['user_id'] = str(member.user_id) add_member['email'] = member.user.email add_member['role'] = str(member.role.title) ui_members.append(add_member) return jsonify(ui_members), 200 @use_kwargs(delete_args) @pass_community def delete(self, comid=None, community=None, user_id=None): """Remove a member from a community.""" admin_ids = \ [admin.user.id for admin in CommunityMember.get_admins( community.id)] if int(current_user.get_id()) not in admin_ids: abort(404) CommunityMembersAPI.delete_member(community.id, user_id) db.session.commit() return 'Succesfully removed', 204
class MembershipRequestResource(MethodView): """Resource to view and handle membership requests.""" post_args = { 'response': fields.Raw(location='json', required=True, validate=[validate.OneOf(['accept', 'decline'])]), 'role': fields.Raw(location='json', required=False, validate=[validate.OneOf(['M', 'A', 'C'])]) } put_args = { 'role': fields.Raw(location='json', required=True, validate=[validate.OneOf(['M', 'A', 'C'])]) } def get(self, membership_request_id): """Get the information for a membership request.""" member_request, community = MembershipRequestAPI.get_invitation( membership_request_id) response_object = {} response_object['community_name'] = community.json['title'] response_object['community_id'] = community.json['id'] response_object['role'] = str(member_request.role.title) return jsonify(response_object), 200 @use_kwargs(post_args) def post(self, membership_request_id, response=None, role=None): """Accept or reject a membership request.""" if not current_user.is_authenticated: abort(404) user_id = int(current_user.get_id()) member_request = MembershipRequest.query.get(membership_request_id) if not member_request.is_invite: # and current_user.email == request.email): community_admins = [ admin.user.id for admin in CommunityMember.get_admins(member_request.comm_id) ] if not (user_id in community_admins and not member_request.is_invite): abort(404) if response == 'accept': MembershipRequestAPI.accept_invitation(request, role, user_id) db.session.commit() return 'Succesfully accepted.', 200 else: MembershipRequestAPI.decline_or_cancel_invitation( member_request.id) db.session.commit() return 'Succesfully rejected.', 200 @use_kwargs(put_args) def put(self, membership_request_id, role=None): """Modify a membership request.""" if not current_user.is_authenticated: abort(404) member_request = MembershipRequest.query.get(membership_request_id) community_admins = [ admin.user.id for admin in CommunityMember.get_admins(member_request.comm_id) ] if not (int(current_user.get_id()) in community_admins and member_request.is_invite): abort(404) member_request.role = role db.session.commit() return 'Succesfully modified invitaion.', 200 def delete(self, membership_request_id): """Cancel(remove) a membership request.""" if not current_user.is_authenticated: abort(404) user_id = int(current_user.get_id()) member_request = MembershipRequest.query.get(membership_request_id) if not (current_user.email == member_request.email and not member_request.is_invite): community_admins = [ admin.user.id for admin in CommunityMember.get_admins(member_request.comm_id) ] if not (user_id in community_admins and member_request.is_invite): abort(404) MembershipRequestAPI.decline_or_cancel_invitation(member_request.id) db.session.commit() return 'Succesfully cancelled invitation.', 204
@login_required @use_kwargs(projekty_edit_args) def projekty_edit(project_id): project = Project.get_by_id(project_id) return render_template('frontend/projekty-edit.html', project=project) save_project_args = { 'project_id': fields.Int(required=True), 'title_pl': fields.Str(required=True), 'title_en': fields.Str(required=True), 'description_pl': fields.Str(required=True), 'description_en': fields.Str(required=True), 'content_pl': fields.Str(required=True), 'content_en': fields.Str(required=True), 'data': fields.Raw(location='files', missing=None), } @frontend.route('/edit/submit', methods=['POST']) @login_required @use_kwargs(save_project_args) def save_project(project_id, title_pl, title_en, description_pl, description_en, content_pl, content_en, data): project = Project.get_by_id(project_id) project.title_pl = title_pl project.title_en = title_en project.description_pl = description_pl project.description_en = description_en project.content_pl = content_pl project.content_en = content_en