def post_register(): query = json.loads(request.data) email = query['email'].lower() username = query['username'] password = query['password'] try: user = register_user( username=username, password=password, email=email, ) except RegisterUserException as ex: return make_fail_response( ex.message, error_code=ex.error_code, ), 400 g.user = user access_token = user.generate_access_token() refresh_token = user.generate_refresh_token() user_obj = user.to_dict() user_obj['hash_password'] = '' return make_success_response({ 'access_token': access_token.decode('ascii'), 'refresh_token': refresh_token.decode('ascii'), 'user': user_obj, })
def post_search_nodes(collection): query = json.loads(request.data) app.logger.debug(request.data) query['user_id'] = to_object_id(g.user._id) virtual_collection = query.pop('virtual_collection', None) if len(query.keys() - PAGINATION_QUERY_KEYS): return make_fail_response( 'Unknown keys: `{}`'.format(query.keys() - PAGINATION_QUERY_KEYS)), 400 if collection == 'in_hubs': hub = query.pop('hub') res = hub_manager.kind_to_hub_class[hub].search( plynx.base.hub.Query(**query)) else: if virtual_collection == NodeVirtualCollection.OPERATIONS: query['node_kinds'] = list( operation_manager.kind_to_operation_dict.keys()) elif virtual_collection == NodeVirtualCollection.WORKFLOWS: query['node_kinds'] = list( workflow_manager.kind_to_workflow_dict.keys()) res = node_collection_managers[collection].get_db_objects(**query) return make_success_response({ 'items': res['list'], 'total_count': res['metadata'][0]['total'] if res['metadata'] else 0, 'plugins_dict': PLUGINS_DICT, })
def get_graph_node_action(graph_id, action): graph_dict = graph_collection_manager.get_db_graph(graph_id, g.user._id) if not graph_dict: return make_fail_response('Graph was not found'), 404 if action == GraphNodePostAction.LIST_NODES: return make_success_response(nodes=graph_dict['nodes']) else: return make_fail_response('Unknown action `{}`. Try POST method.'.format(action)), 400
def worker_states(): try: return make_success_response({ 'items': get_worker_states(), 'plugins_dict': PLUGINS_DICT, }) except Exception as e: app.logger.error(e) return make_fail_response('Internal error: "{}"'.format(str(e)))
def get_auth_token(): access_token = g.user.generate_access_token() refresh_token = g.user.generate_refresh_token() user_obj = g.user.to_dict() user_obj['hash_password'] = '' return make_success_response({ 'access_token': access_token.decode('ascii'), 'refresh_token': refresh_token.decode('ascii'), 'user': user_obj, })
def get_user(username): user = UserCollectionManager.find_user_by_name(username) if not user: return make_fail_response('User not found'), 404 user_obj = user.to_dict() is_admin = IAMPolicies.IS_ADMIN in g.user.policies user_obj['_is_admin'] = is_admin user_obj['_readonly'] = user._id != g.user._id and not is_admin del user_obj['password_hash'] return make_success_response({ 'user': user_obj, })
def post_user(): data = json.loads(request.data) app.logger.warn(data) action = data.get('action', '') old_password = data.get('old_password', '') new_password = data.get('new_password', '') if action == UserPostAction.MODIFY: posted_user = User.from_dict(data['user']) existing_user = UserCollectionManager.find_user_by_name( posted_user.username) if not existing_user: return make_fail_response('User not found'), 404 if g.user.username != posted_user.username and IAMPolicies.IS_ADMIN not in g.user.policies: return make_fail_response( 'You don`t have permission to modify this user'), 401 if set(posted_user.policies) != set(existing_user.policies): if IAMPolicies.IS_ADMIN not in g.user.policies: return make_fail_response( 'You don`t have permission to modify policies'), 401 existing_user.policies = posted_user.policies if new_password: if not existing_user.verify_password(old_password): return make_fail_response('Incorrect password'), 401 existing_user.hash_password(new_password) existing_user.settings = posted_user.settings existing_user.save() if g.user.username == posted_user.username: g.user = posted_user is_admin = IAMPolicies.IS_ADMIN in g.user.policies user_obj = existing_user.to_dict() user_obj['_is_admin'] = is_admin user_obj[ '_readonly'] = existing_user._id != g.user._id and not is_admin del user_obj['password_hash'] return make_success_response({ 'user': user_obj, }) else: raise Exception('Unknown action: `{}`'.format(action)) raise NotImplementedError("Nothing is to return")
def post_group(): app.logger.debug(request.data) data = json.loads(request.data) group = Group.from_dict(data['group']) group.author = g.user._id db_group = node_collection_managers[Collections.GROUPS].get_db_object( group._id, g.user._id) action = data['action'] if db_group and db_group['_readonly']: return make_fail_response('Permission denied'), 403 if action == NodePostAction.SAVE: group.save(force=True) return make_success_response({ 'message': 'Group(_id=`{}`) successfully updated'.format(str(group._id)) })
def upload_file(): assert len(request.files) == 1 title = request.form.get('title', '{title}') description = request.form.get('description', '{description}') file_type = request.form.get('file_type', FILE_KIND) node_kind = request.form.get('node_kind', 'basic-file') app.logger.debug(request) if file_type not in RESOURCE_TYPES: app.logger.debug(file_type) app.logger.debug(RESOURCE_TYPES) return make_fail_response('Unknown file type `{}`'.format(file_type)), 400 resource_id = upload_file_stream(request.files['data']) file = plynx.db.node.Node.from_dict({ 'title': title, 'description': description, 'kind': node_kind, 'node_running_status': NodeRunningStatus.STATIC, 'node_status': NodeStatus.READY, }) file.outputs.append( plynx.db.node.Output.from_dict({ 'name': 'file', 'file_type': file_type, 'values': [resource_id], }) ) file.author = g.user._id file.save() return make_success_response({ 'resource_id': resource_id, 'node': file.to_dict(), })
def get_nodes(collection, node_link=None): user_id = to_object_id(g.user._id) can_view_others_operations = g.user.check_role( IAMPolicies.CAN_VIEW_OTHERS_OPERATIONS) can_view_others_workflows = g.user.check_role( IAMPolicies.CAN_VIEW_OTHERS_WORKFLOWS) can_view_operations = g.user.check_role(IAMPolicies.CAN_VIEW_OPERATIONS) can_view_workflows = g.user.check_role(IAMPolicies.CAN_VIEW_WORKFLOWS) can_create_operations = g.user.check_role( IAMPolicies.CAN_CREATE_OPERATIONS) can_create_workflows = g.user.check_role(IAMPolicies.CAN_CREATE_WORKFLOWS) if node_link in executor_manager.kind_to_executor_class and collection == Collections.TEMPLATES: # if node_link is a base node # i.e. /templates/basic-bash kind = node_link if kind in workflow_manager.kind_to_workflow_dict and ( not can_view_workflows or not can_create_workflows): return make_permission_denied() if kind in operation_manager.kind_to_operation_dict and ( not can_view_operations or not can_create_operations): return make_permission_denied() node = executor_manager.kind_to_executor_class[kind].get_default_node( is_workflow=kind in workflow_manager.kind_to_workflow_dict) if isinstance(node, tuple): data = node[0].to_dict() tour_steps = node[1] else: data = node.to_dict() tour_steps = [] data['kind'] = kind return make_success_response({ 'node': data, 'tour_steps': tour_steps, 'plugins_dict': PLUGINS_DICT, }) elif node_link in workflow_manager.kind_to_workflow_dict and collection == Collections.GROUPS: # TODO move group to a separate class group_dict = Group().to_dict() group_dict['kind'] = node_link return make_success_response({ 'group': group_dict, 'plugins_dict': PLUGINS_DICT, }) else: # when node_link is an id of the object try: node_id = to_object_id(node_link) except Exception: return make_fail_response('Invalid ID'), 404 if collection == Collections.GROUPS: # TODO move group to a separate class group = node_collection_managers[collection].get_db_object( node_id, user_id) if group: return make_success_response({ 'group': group, 'plugins_dict': PLUGINS_DICT, }) else: make_fail_response( 'Group `{}` was not found'.format(node_link)), 404 node = node_collection_managers[collection].get_db_node( node_id, user_id) app.logger.debug(node) if node: is_owner = node['author'] == user_id kind = node['kind'] if kind in workflow_manager.kind_to_workflow_dict and not can_view_workflows: return make_permission_denied() if kind in operation_manager.kind_to_operation_dict and not can_view_operations: return make_permission_denied() if kind in workflow_manager.kind_to_workflow_dict and not can_view_others_workflows and not is_owner: return make_permission_denied() if kind in operation_manager.kind_to_operation_dict and not can_view_others_operations and not is_owner: return make_permission_denied() return make_success_response({ 'node': node, 'plugins_dict': PLUGINS_DICT, }) else: return make_fail_response( 'Node `{}` was not found'.format(node_link)), 404
def post_node(collection): app.logger.debug(request.data) data = json.loads(request.data) node = Node.from_dict(data['node']) node.starred = False action = data['action'] db_node = node_collection_managers[collection].get_db_node( node._id, g.user._id) if db_node: if not node.author: node.author = db_node['author'] if node.author != db_node['author']: raise Exception( "Author of the node does not match the one in the database") is_author = db_node['author'] == g.user._id else: # assign the author node.author = g.user._id is_author = True is_admin = g.user.check_role(IAMPolicies.IS_ADMIN) is_workflow = node.kind in workflow_manager.kind_to_workflow_dict can_create_operations = g.user.check_role( IAMPolicies.CAN_CREATE_OPERATIONS) can_create_workflows = g.user.check_role(IAMPolicies.CAN_CREATE_WORKFLOWS) can_modify_others_workflows = g.user.check_role( IAMPolicies.CAN_MODIFY_OTHERS_WORKFLOWS) can_run_workflows = g.user.check_role(IAMPolicies.CAN_RUN_WORKFLOWS) if action == NodePostAction.SAVE: if (is_workflow and not can_create_workflows) or ( not is_workflow and not can_create_operations): return make_permission_denied( 'You do not have permission to save this object') if node.node_status != NodeStatus.CREATED: return make_fail_response( 'Cannot save node with status `{}`'.format(node.node_status)) if is_author or is_admin or (is_workflow and can_modify_others_workflows): node.save(force=True) else: return make_permission_denied( 'Only the owners or users with CAN_MODIFY_OTHERS_WORKFLOWS role can save it' ) elif action == NodePostAction.APPROVE: if is_workflow: return make_fail_response('Invalid action for a workflow'), 400 if node.node_status != NodeStatus.CREATED: return make_fail_response( 'Node status `{}` expected. Found `{}`'.format( NodeStatus.CREATED, node.node_status)) validation_error = executor_manager.kind_to_executor_class[node.kind]( node).validate() if validation_error: return make_success_response({ 'status': NodePostStatus.VALIDATION_FAILED, 'message': 'Node validation failed', 'validation_error': validation_error.to_dict() }) node.node_status = NodeStatus.READY if is_author or is_admin: node.save(force=True) else: return make_permission_denied() elif action == NodePostAction.CREATE_RUN: if not is_workflow: return make_fail_response('Invalid action for an operation'), 400 if node.node_status != NodeStatus.CREATED: return make_fail_response( 'Node status `{}` expected. Found `{}`'.format( NodeStatus.CREATED, node.node_status)) validation_error = executor_manager.kind_to_executor_class[node.kind]( node).validate() if validation_error: return make_success_response({ 'status': NodePostStatus.VALIDATION_FAILED, 'message': 'Node validation failed', 'validation_error': validation_error.to_dict() }) node = node.clone(NodeClonePolicy.NODE_TO_RUN) node.author = g.user._id if is_admin or can_run_workflows: node.save(collection=Collections.RUNS) else: return make_permission_denied( 'You do not have CAN_RUN_WORKFLOWS role') return make_success_response({ 'status': NodePostStatus.SUCCESS, 'message': 'Run(_id=`{}`) successfully created'.format(str(node._id)), 'run_id': str(node._id), 'url': '/{}/{}'.format(Collections.RUNS, node._id), }) elif action == NodePostAction.CLONE: if (is_workflow and not can_create_workflows) or ( not is_workflow and not can_create_operations): return make_permission_denied( 'You do not have the role to create an object') node_clone_policy = None if collection == Collections.TEMPLATES: node_clone_policy = NodeClonePolicy.NODE_TO_NODE elif collection == Collections.RUNS: node_clone_policy = NodeClonePolicy.RUN_TO_NODE node = node.clone(node_clone_policy) node.save(collection=Collections.TEMPLATES) return make_success_response({ 'message': 'Node(_id=`{}`) successfully created'.format(str(node._id)), 'node_id': str(node._id), 'url': '/{}/{}'.format(Collections.TEMPLATES, node._id), }) elif action == NodePostAction.VALIDATE: validation_error = executor_manager.kind_to_executor_class[node.kind]( node).validate() if validation_error: return make_success_response({ 'status': NodePostStatus.VALIDATION_FAILED, 'message': 'Node validation failed', 'validation_error': validation_error.to_dict() }) elif action == NodePostAction.DEPRECATE: if node.node_status == NodeStatus.CREATED: return make_fail_response('Node status `{}` not expected.'.format( node.node_status)) node.node_status = NodeStatus.DEPRECATED if is_author or is_admin: node.save(force=True) else: return make_permission_denied( 'You are not an author to deprecate it') elif action == NodePostAction.MANDATORY_DEPRECATE: if node.node_status == NodeStatus.CREATED: return make_fail_response('Node status `{}` not expected.'.format( node.node_status)) node.node_status = NodeStatus.MANDATORY_DEPRECATED if is_author or is_admin: node.save(force=True) else: return make_permission_denied( 'You are not an author to deprecate it') elif action == NodePostAction.PREVIEW_CMD: return make_success_response({ 'message': 'Successfully created preview', 'preview_text': executor_manager.kind_to_executor_class[node.kind](node).run( preview=True) }) elif action == NodePostAction.REARRANGE_NODES: node.arrange_auto_layout() return make_success_response({ 'message': 'Successfully created preview', 'node': node.to_dict(), }) elif action == NodePostAction.UPGRADE_NODES: upd = node_collection_managers[collection].upgrade_sub_nodes(node) return make_success_response({ 'message': 'Successfully updated nodes', 'node': node.to_dict(), 'upgraded_nodes_count': upd, }) elif action == NodePostAction.CANCEL: if is_author or is_admin: run_cancellation_manager.cancel_run(node._id) else: return make_permission_denied( 'You are not an author to cancel the run') elif action == NodePostAction.GENERATE_CODE: raise NotImplementedError() else: return make_fail_response('Unknown action `{}`'.format(action)) return make_success_response({ 'message': 'Node(_id=`{}`) successfully updated'.format(str(node._id)) })
def post_graph_node_action(graph_id, action): graph_dict = graph_collection_manager.get_db_graph(graph_id, g.user._id) if not graph_dict: return make_fail_response('Graph was not found'), 404 if graph_dict['_readonly']: return make_fail_response('Permission denied'), 403 if not request.data: return make_fail_response('Empty body'), 400 data = json.loads(request.data) graph = Graph.from_dict(graph_dict) if action == GraphNodePostAction.INSERT_NODE: node_id = data.get('node_id', None) x, y = int(data.get('x', 0)), int(data.get('y', 0)) node_dict = node_collection_manager.get_db_node(node_id) if not node_dict: return make_fail_response('Node was not found'), 404 node = Node.from_dict(node_dict) node.x, node.y = x, y node.parent_node = node._id node._id = ObjectId() graph.nodes.append(node) graph.save() return make_success_response(node=node.to_dict()) elif action == GraphNodePostAction.REMOVE_NODE: node_id = ObjectId(data.get('node_id', None)) node_index = -1 for index, node in enumerate(graph.nodes): for input in node.inputs: input.values = [value for value in input.values if ObjectId(value.node_id) != node_id] if ObjectId(node._id) == node_id: node_index = index if node_index < 0: return make_fail_response('Node was not found'), 404 del graph.nodes[node_index] graph.save() return make_success_response('Node removed') elif action == GraphNodePostAction.CHANGE_PARAMETER: node_id = data.get('node_id', None) parameter_name = data.get('parameter_name', None) parameter_value = data.get('parameter_value', None) if parameter_name is None: return make_fail_response('No parameter name'), 400 if parameter_value is None: return make_fail_response('No parameter value'), 400 node, = _find_nodes(graph, node_id) if not node: return make_fail_response('Node was not found'), 404 for parameter in node.parameters: if parameter.name == parameter_name: parameter_dict = parameter.to_dict() parameter_dict['value'] = parameter_value parameter.value = Parameter(obj_dict=parameter_dict).value graph.save() return make_success_response('Parameter updated') elif action in (GraphNodePostAction.CREATE_LINK, GraphNodePostAction.REMOVE_LINK): for field in ['from', 'to']: for sub_field in ['node_id', 'resource']: if field not in data: return make_fail_response('`{}` is missing'.format(field)), 400 if sub_field not in data[field]: return make_fail_response('`{}.{}` is missing'.format(field, sub_field)), 400 from_node_id = data['from']['node_id'] from_resource = data['from']['resource'] to_node_id = data['to']['node_id'] to_resource = data['to']['resource'] from_node, to_node = _find_nodes(graph, from_node_id, to_node_id) if not from_node or not to_node: return make_fail_response('Node was not found'), 404 from_output = None to_input = None for output in from_node.outputs: if output.name == from_resource: from_output = output break for input in to_node.inputs: if input.name == to_resource: to_input = input break if not from_output or not to_input: return make_fail_response('Input or output not found'), 404 if action == GraphNodePostAction.CREATE_LINK: # TODO graph.validate() it if from_output.file_type not in to_input.file_types and 'file' not in to_input.file_types: return make_fail_response('Incompatible types'), 400 # TODO graph.validate() it if to_input.max_count > 0 and len(to_input.values) >= to_input.max_count: return make_fail_response('Number of inputs reached the limit'), 400 new_input_value = InputValue() new_input_value.node_id = from_node_id new_input_value.output_id = from_resource # TODO graph.validate() it for value in to_input.values: if value.node_id == from_node_id and value.output_id == from_resource: return make_fail_response('Link already exists'), 400 to_input.values.append(new_input_value) elif action == GraphNodePostAction.REMOVE_LINK: rm_index = -1 # TODO graph.validate() it for index, value in enumerate(to_input.values): if value.node_id == from_node_id and value.output_id == from_resource: rm_index = index break if rm_index < 0: return make_fail_response('Link not found'), 404 del to_input.values[rm_index] graph.save() return make_success_response('Completed') else: return make_fail_response('Unknown action `{}`'.format(action)), 400 return 'ok'
def post_resource(): resource_id = upload_file_stream(request.files['data']) return make_success_response({ 'resource_id': resource_id })