def update_campaign(campaign_id): """ Updates an existing campaign. .. :quickref: Campaign; Updates an existing campaign. **Example request**: .. sourcecode:: http PUT /campaigns/1 HTTP/1.1 Host: 127.0.0.1 Content-Type: application/json { "name": "Derpsters", } **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "id": 1, "aliases": ["icanhaz"], "created_time": "Thu, 28 Feb 2019 17:10:44 GMT", "modified_time": "Thu, 28 Feb 2019 17:18:29 GMT", "name": "Derpsters" } :reqheader Authorization: Optional Apikey value :resheader Content-Type: application/json :status 200: Campaign updated :status 400: JSON does not match the schema :status 401: Invalid role to perform this action :status 404: Campaign ID not found :status 409: Campaign already exists """ data = request.get_json() # Verify the ID exists. campaign = Campaign.query.get(campaign_id) if not campaign: return error_response(404, 'Campaign ID not found') # Verify this name does not already exist. existing = Campaign.query.filter_by(name=data['name']).first() if existing: return error_response(409, 'Campaign already exists') else: campaign.name = data['name'] # Save the changes. db.session.commit() response = jsonify(campaign.to_dict()) return response
def decorated_function(*args, **kwargs): # Get the role of the function name in the config. required_role = 'admin' # Get the API key if there is one. # The header should look like: # Authorization: Apikey blah-blah-blah # So strip off the first 7 characters to get the actual key. authorization = request.headers.get('Authorization') apikey = None if authorization and 'apikey' in authorization.lower(): apikey = authorization[7:] # If there is an API key, look it up and get the user's roles. if apikey: user = db.session.query(User).filter_by(apikey=apikey).first() # If the user exists and they have the required role, return the function. if user: if user.active: if any(role.name.lower() == required_role for role in user.roles): return function(*args, **kwargs) else: return error_response(401, 'Insufficient privileges') else: return error_response(401, 'API user is not active') else: return error_response(401, 'API user does not exist') else: return error_response(401, 'Bad or missing API key')
def decorated_function(*args, **kwargs): try: if not request.get_json(): return error_response(400, 'Request must include valid JSON') except BadRequest: return error_response(400, 'Request must include valid JSON') return function(*args, **kwargs)
def validate_task(): data = request.get_json() or {} if 'message' not in data or 'schema_name' not in data: return error_response('bad data input, must include message and schema name') # ToDo recursive? schema = Schema.query.filter_by(name=data['schema_name']).first() if schema is None: return error_response(404) if schema.extended is not None: extension = Schema.query.filter_by(name=schema.extended).first() if extension is None: return error_response(404) extension = json.loads(json.dumps(ast.literal_eval(extension.schema))) instance = json.loads(json.dumps(data['message'])) try: jsonschema.validate(instance=instance, schema=extension) except (jsonschema.ValidationError, jsonschema.exceptions.SchemaError) as e: return error_response(400, e.message) # ToDo error handling schema = ast.literal_eval(schema.schema) schema = json.loads(json.dumps(schema)) instance = json.loads(json.dumps(data['message'])) try: jsonschema.validate(instance=instance, schema=schema) except (jsonschema.ValidationError, jsonschema.exceptions.SchemaError) as e: return bad_request(e.message) return make_response('', 204)
def decorated_function(*args, **kwargs): try: validate(request.json, schema) except SchemaError as e: return error_response(400, 'JSON schema is not valid: {}'.format(e.message)) except ValidationError as e: return error_response(400, 'Request JSON does not match schema: {}'.format(e.message)) return function(*args, **kwargs)
def update_tag(tag_id): """ Updates an existing tag. .. :quickref: Tag; Updates an existing tag. **Example request**: .. sourcecode:: http PUT /tags/1 HTTP/1.1 Host: 127.0.0.1 Content-Type: application/json { "value": "from_address", } **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "id": 1, "value": "from_address" } :reqheader Authorization: Optional Apikey value :resheader Content-Type: application/json :status 200: Tag updated :status 400: JSON does not match the schema :status 401: Invalid role to perform this action :status 404: Tag ID not found :status 409: Tag already exists """ data = request.get_json() # Verify the ID exists. tag = Tag.query.get(tag_id) if not tag: return error_response(404, 'Tag ID not found') # Verify this value does not already exist. existing = Tag.query.filter_by(value=data['value']).first() if existing: return error_response(409, 'Tag already exists') # Set the new value. tag.value = data['value'] db.session.commit() response = jsonify(tag.to_dict()) return response
def update_indicator_type(indicator_type_id): """ Updates an existing indicator type. .. :quickref: IndicatorType; Updates an existing indicator type. **Example request**: .. sourcecode:: http PUT /indicators/type/1 HTTP/1.1 Host: 127.0.0.1 Content-Type: application/json { "value": "URI - Domain Name", } **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "id": 1, "value": "URI - Domain Name" } :reqheader Authorization: Optional Apikey value :resheader Content-Type: application/json :status 200: Indicator type updated :status 400: JSON does not match the schema :status 401: Invalid role to perform this action :status 404: Indicator type ID not found :status 409: Indicator type already exists """ data = request.get_json() # Verify the ID exists. indicator_type = IndicatorType.query.get(indicator_type_id) if not indicator_type: return error_response(404, 'Indicator type ID not found') # Verify this value does not already exist. existing = IndicatorType.query.filter_by(value=data['value']).first() if existing: return error_response(409, 'Indicator type already exists') # Set the new value. indicator_type.value = data['value'] db.session.commit() response = jsonify(indicator_type.to_dict()) return response
def create_indicator_relationship(parent_id, child_id): """ Creates a parent/child relationship between two indicators. .. :quickref: Indicator; Creates a parent/child relationship between two indicators. **Example request**: .. sourcecode:: http POST /indicators/1/2/relationship HTTP/1.1 Host: 127.0.0.1 **Example response**: .. sourcecode:: http HTTP/1.1 204 No Content :reqheader Authorization: Optional Apikey value :resheader Content-Type: application/json :status 204: Relationship created :status 400: Cannot add an indicator to its own children :status 400: Child indicator already has a parent :status 400: JSON does not match the schema :status 401: Invalid role to perform this action :status 404: Indicator ID not found """ # Verify the parent ID exists. parent_indicator = Indicator.query.get(parent_id) if not parent_indicator: return error_response( 404, 'Parent indicator ID not found: {}'.format(parent_id)) # Verify the child ID exists. child_indicator = Indicator.query.get(child_id) if not child_indicator: return error_response( 404, 'Child indicator ID not found: {}'.format(child_id)) # Verify the parent and child are not the same. if parent_id == child_id: return error_response(400, 'Cannot add an indicator to its own children') # Try to create the relationship or error if it could not be created. result = parent_indicator.add_child(child_indicator) if result: db.session.commit() return '', 204 else: db.session.rollback() return error_response(400, 'Child indicator already has a parent')
def update_schema(): data = request.get_json() or {} if 'name' not in data or 'schema' not in data: return error_response( 422, 'bad data input, must include schema and schema name') schema = Schema.query.filter_by(name=data['name']).first() if schema is None: return error_response(404) schema.from_dict(data) db.session.commit() response = jsonify(schema.to_dict()) response.status_code = 201 return response
def create_indicator_equal(a_id, b_id): """ Creates an equal to relationship between two indicators. .. :quickref: Indicator; Creates an equal to relationship between two indicators. **Example request**: .. sourcecode:: http POST /indicators/1/2/equal HTTP/1.1 Host: 127.0.0.1 **Example response**: .. sourcecode:: http HTTP/1.1 204 No Content :reqheader Authorization: Optional Apikey value :resheader Content-Type: application/json :status 204: Relationship created :status 400: Cannot make indicator equal to itself :status 400: JSON does not match the schema :status 401: Invalid role to perform this action :status 404: Indicator ID not found :status 409: The indicators are already directly or indirectly equal """ # Verify the a_id exists. a_indicator = Indicator.query.get(a_id) if not a_indicator: return error_response(404, 'Indicator ID not found: {}'.format(a_id)) # Verify the b_id exists. b_indicator = Indicator.query.get(b_id) if not b_indicator: return error_response(404, 'Indicator ID not found: {}'.format(b_id)) # Verify the IDs are not the same. if a_id == b_id: return error_response(400, 'Cannot make indicator equal to itself') # Try to create the relationship or error if it could not be created. result = a_indicator.make_equal(b_indicator) if result: db.session.commit() return '', 204 else: db.session.rollback() return error_response(409, 'The indicators are already directly or indirectly equal')
def delete_indicator_relationship(parent_id, child_id): """ Deletes an indicator parent/child relationship. .. :quickref: Indicator; Deletes an equal to relationship between two indicators. **Example request**: .. sourcecode:: http DELETE /indicators/1/2/relationship HTTP/1.1 Host: 127.0.0.1 **Example response**: .. sourcecode:: http HTTP/1.1 204 No Content :reqheader Authorization: Optional Apikey value :status 204: Relationship deleted :status 400: Parent and child indicators must be different :status 401: Invalid role to perform this action :status 404: Indicator ID not found :status 404: Relationship does not exist """ # Verify the parent ID exists. parent_indicator = Indicator.query.get(parent_id) if not parent_indicator: return error_response( 404, 'Parent indicator ID not found: {}'.format(parent_id)) # Verify the child ID exists. child_indicator = Indicator.query.get(child_id) if not child_indicator: return error_response( 404, 'Child indicator ID not found: {}'.format(child_id)) # Verify the parent and child are not the same. if parent_id == child_id: return error_response(400, 'Parent and child indicators must be different') result = parent_indicator.remove_child(child_indicator) if result: db.session.commit() return '', 204 else: db.session.rollback() return error_response(404, 'Relationship does not exist')
def internal_error(error): if request.path.startswith('/api/'): db.session.rollback() return error_response(500, 'Internal server error') else: return DefaultView().render('errors/500.html'), 500
def create_role(): """ Creates a new role. Requires the admin role. .. :quickref: Role; Creates a new role. Requires the admin role. **Example request**: .. sourcecode:: http POST /roles HTTP/1.1 Host: 127.0.0.1 Content-Type: application/json { "name": "analyst", "description": "Users that create and process intel" } **Example response**: .. sourcecode:: http HTTP/1.1 201 Created Content-Type: application/json { "id": 1, "name": "analyst", "description": "Users that create and process intel" } :reqheader Authorization: Optional Apikey value :resheader Content-Type: application/json :status 201: Role created :status 400: JSON does not match the schema :status 401: Invalid role to perform this action :status 409: Role already exists """ data = request.get_json() # Verify this name does not already exist. existing = Role.query.filter_by(name=data['name']).first() if existing: return error_response(409, 'Role already exists') # Create and add the new value. role = Role(name=data['name']) # Add the description if one was given. if 'description' in data: role.description = data['description'] db.session.add(role) db.session.commit() response = jsonify(role.to_dict()) response.status_code = 201 response.headers['Location'] = url_for('api.read_role', role_id=role.id) return response
def delete_indicator_equal(a_id, b_id): """ Deletes an equal to relationship between two indicators. .. :quickref: Indicator; Deletes an equal to relationship between two indicators. **Example request**: .. sourcecode:: http DELETE /indicators/1/2/equal HTTP/1.1 Host: 127.0.0.1 **Example response**: .. sourcecode:: http HTTP/1.1 204 No Content :reqheader Authorization: Optional Apikey value :status 204: Relationship deleted :status 400: Indicator IDs must be different :status 401: Invalid role to perform this action :status 404: Indicator ID not found :status 404: Relationship does not exist or the indicators are not directly equal """ # Verify the a_id exists. a_indicator = Indicator.query.get(a_id) if not a_indicator: return error_response(404, 'Indicator ID not found: {}'.format(a_id)) # Verify the b_id exists. b_indicator = Indicator.query.get(b_id) if not b_indicator: return error_response(404, 'Indicator ID not found: {}'.format(b_id)) # Verify the IDs are not the same. if a_id == b_id: return error_response(400, 'Indicator IDs must be different') result = a_indicator.remove_equal(b_indicator) if result: db.session.commit() return '', 204 else: db.session.rollback() return error_response(404, 'Relationship does not exist or the indicators are not directly equal')
def add_schema(): data = request.get_json() or {} if 'name' not in data or 'schema' not in data: return error_response( 422, 'bad data input, must include schema and schema name') if Schema.query.filter_by(name=data['name']).first(): return error_response(409, 'schema name already saved') if 'extended' in data: if not Schema.query.filter_by(name=data['extended']).first(): return error_response(406, 'Extended schema not in the DB') schema = Schema() schema.from_dict(data) db.session.add(schema) db.session.commit() response = jsonify(schema.to_dict()) response.status_code = 201 return response
def remove_schema(id): schema = Schema.query.filter_by(id=id).first() if schema is None: return error_response(404) db.session.delete(schema) db.session.commit() response = jsonify({'message': 'schema removed'}) response.status_code = 201 return response
def get_schema_list(): schemas = Schema.query.all() if schemas is None or len(schemas) is 0: return error_response(404) schemas_dict = { k: v for k, v in ((schema.id, schema.name) for schema in schemas) } response = jsonify(schemas_dict) response.status_code = 200 return response
def create_indicator_type(): """ Creates a new indicator type. .. :quickref: IndicatorType; Creates a new indicator type. **Example request**: .. sourcecode:: http POST /indicators/type HTTP/1.1 Host: 127.0.0.1 Content-Type: application/json { "value": "Email - Address" } **Example response**: .. sourcecode:: http HTTP/1.1 201 Created Content-Type: application/json { "id": 1, "value": "Email - Address" } :reqheader Authorization: Optional Apikey value :resheader Content-Type: application/json :status 201: Indicator type created :status 400: JSON does not match the schema :status 401: Invalid role to perform this action :status 409: Indicator type already exists """ data = request.get_json() # Verify this value does not already exist. existing = IndicatorType.query.filter_by(value=data['value']).first() if existing: return error_response(409, 'Indicator type already exists') # Create and add the new value. indicator_type = IndicatorType(value=data['value']) db.session.add(indicator_type) db.session.commit() response = jsonify(indicator_type.to_dict()) response.status_code = 201 response.headers['Location'] = url_for('api.read_indicator_type', indicator_type_id=indicator_type.id) return response
def create_tag(): """ Creates a new tag. .. :quickref: Tag; Creates a new tag. **Example request**: .. sourcecode:: http POST /tags HTTP/1.1 Host: 127.0.0.1 Content-Type: application/json { "value": "phish" } **Example response**: .. sourcecode:: http HTTP/1.1 201 Created Content-Type: application/json { "id": 1, "value": "phish" } :reqheader Authorization: Optional Apikey value :resheader Content-Type: application/json :status 201: Tag created :status 400: JSON does not match the schema :status 401: Invalid role to perform this action :status 409: Tag already exists """ data = request.get_json() # Verify this value does not already exist. existing = Tag.query.filter_by(value=data['value']).first() if existing: return error_response(409, 'Tag already exists') # Create and add the new value. tag = Tag(value=data['value']) db.session.add(tag) db.session.commit() response = jsonify(tag.to_dict()) response.status_code = 201 response.headers['Location'] = url_for('api.read_tag', tag_id=tag.id) return response
def delete_campaign_alias(campaign_alias_id): """ Deletes a campaign alias. .. :quickref: CampaignAlias; Deletes an campaign alias. **Example request**: .. sourcecode:: http DELETE /campaigns/alias/1 HTTP/1.1 Host: 127.0.0.1 **Example response**: .. sourcecode:: http HTTP/1.1 204 No Content :reqheader Authorization: Optional Apikey value :status 204: Campaign alias deleted :status 401: Invalid role to perform this action :status 404: Campaign alias ID not found :status 409: Unable to delete campaign alias due to foreign key constraints """ campaign_alias = CampaignAlias.query.get(campaign_alias_id) if not campaign_alias: return error_response(404, 'Campaign alias ID not found') try: db.session.delete(campaign_alias) db.session.commit() except exc.IntegrityError: db.session.rollback() return error_response( 409, 'Unable to delete campaign alias due to foreign key constraints') return '', 204
def delete_intel_reference(intel_reference_id): """ Deletes an intel reference. .. :quickref: IntelReference; Deletes an intel reference. **Example request**: .. sourcecode:: http DELETE /intel/reference/1 HTTP/1.1 Host: 127.0.0.1 **Example response**: .. sourcecode:: http HTTP/1.1 204 No Content :reqheader Authorization: Optional Apikey value :status 204: Intel reference deleted :status 401: Invalid role to perform this action :status 404: Intel reference ID not found :status 409: Unable to delete intel reference due to foreign key constraints """ intel_reference = IntelReference.query.get(intel_reference_id) if not intel_reference: return error_response(404, 'Intel reference ID not found') try: db.session.delete(intel_reference) db.session.commit() except exc.IntegrityError: db.session.rollback() return error_response( 409, 'Unable to delete intel reference due to foreign key constraints') return '', 204
def delete_tag(tag_id): """ Deletes a tag. .. :quickref: Tag; Deletes a tag. **Example request**: .. sourcecode:: http DELETE /tags/1 HTTP/1.1 Host: 127.0.0.1 **Example response**: .. sourcecode:: http HTTP/1.1 204 No Content :reqheader Authorization: Optional Apikey value :status 204: Tag deleted :status 401: Invalid role to perform this action :status 404: Tag ID not found :status 409: Unable to delete tag due to foreign key constraints """ tag = Tag.query.get(tag_id) if not tag: return error_response(404, 'Tag ID not found') try: db.session.delete(tag) db.session.commit() except exc.IntegrityError: db.session.rollback() return error_response( 409, 'Unable to delete tag due to foreign key constraints') return '', 204
def delete_user(user_id): """ Deletes a user. Requires the admin role. .. :quickref: User; Deletes a user. Requires the admin role. **Example request**: .. sourcecode:: http DELETE /users/2 HTTP/1.1 Host: 127.0.0.1 **Example response**: .. sourcecode:: http HTTP/1.1 204 No Content :reqheader Authorization: Optional Apikey value :status 204: User deleted :status 401: Invalid role to perform this action :status 404: User ID not found :status 409: Unable to delete user due to foreign key constraints """ user = User.query.get(user_id) if not user: return error_response(404, 'User ID not found') try: db.session.delete(user) db.session.commit() except exc.IntegrityError: db.session.rollback() return error_response( 409, 'Unable to delete user due to foreign key constraints') return '', 204
def delete_role(role_id): """ Deletes a role. Requires the admin role. .. :quickref: Role; Deletes a role. Requires the admin role. **Example request**: .. sourcecode:: http DELETE /roles/1 HTTP/1.1 Host: 127.0.0.1 **Example response**: .. sourcecode:: http HTTP/1.1 204 No Content :reqheader Authorization: Optional Apikey value :status 204: Role deleted :status 401: Invalid role to perform this action :status 404: Role ID not found :status 409: Unable to delete role due to foreign key constraints """ role = Role.query.get(role_id) if not role: return error_response(404, 'Role ID not found') try: db.session.delete(role) db.session.commit() except exc.IntegrityError: db.session.rollback() return error_response( 409, 'Unable to delete role due to foreign key constraints') return '', 204
def read_user(user_id): """ Gets a single user given its ID. .. :quickref: User; Gets a single user given its ID. **Example request**: .. sourcecode:: http GET /users/2 HTTP/1.1 Host: 127.0.0.1 Accept: application/json **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "active": true, "email": "*****@*****.**", "first_name": "John", "id": 2, "last_name": "Doe", "roles": ["analyst"], "username": "******" } :reqheader Authorization: Optional Apikey value :resheader Content-Type: application/json :status 200: User found :status 401: Invalid role to perform this action :status 404: User ID not found """ user = User.query.get(user_id) if not user: return error_response(404, 'User ID not found') return jsonify(user.to_dict())
def read_campaign(campaign_id): """ Gets a single campaign given its ID. .. :quickref: Campaign; Gets a single campaign given its ID. **Example request**: .. sourcecode:: http GET /campaigns/1 HTTP/1.1 Host: 127.0.0.1 Accept: application/json **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "id": 1, "aliases": ["icanhaz"], "created_time": "Thu, 28 Feb 2019 17:10:44 GMT", "modified_time": "Thu, 28 Feb 2019 17:10:44 GMT", "name": "LOLcats" } :reqheader Authorization: Optional Apikey value :resheader Content-Type: application/json :status 200: Campaign found :status 401: Invalid role to perform this action :status 404: Campaign ID not found """ campaign = Campaign.query.get(campaign_id) if not campaign: return error_response(404, 'Campaign ID not found') return jsonify(campaign.to_dict())
def read_intel_reference(intel_reference_id): """ Gets a single intel reference given its ID. .. :quickref: IntelReference; Gets a single intel reference given its ID. **Example request**: .. sourcecode:: http GET /intel/reference/1 HTTP/1.1 Host: 127.0.0.1 Accept: application/json **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "id": 1, "reference": "http://yourwiki.com/page-for-the-event", "source": "Your company", "username": "******" } :reqheader Authorization: Optional Apikey value :resheader Content-Type: application/json :status 200: Intel source found :status 401: Invalid role to perform this action :status 404: Intel source ID not found """ intel_reference = IntelReference.query.get(intel_reference_id) if not intel_reference: return error_response(404, 'Intel reference ID not found') return jsonify(intel_reference.to_dict())
def read_role(role_id): """ Gets a single role given its ID. .. :quickref: Role; Gets a single role given its ID. **Example request**: .. sourcecode:: http GET /roles/1 HTTP/1.1 Host: 127.0.0.1 Accept: application/json **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "id": 1, "name": "analyst", "description": "Users that create and process intel" } :reqheader Authorization: Optional Apikey value :resheader Content-Type: application/json :status 200: Role found :status 401: Invalid role to perform this action :status 404: Role ID not found """ role = Role.query.get(role_id) if not role: return error_response(404, 'Role ID not found') return jsonify(role.to_dict())
def read_campaign_alias(campaign_alias_id): """ Gets a single campaign alias given its ID. .. :quickref: CampaignAlias; Gets a single campaign alias given its ID. **Example request**: .. sourcecode:: http GET /campaigns/alias/1 HTTP/1.1 Host: 127.0.0.1 Accept: application/json **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "id": 1, "alias": "icanhaz", "campaign": "LOLcats" } :reqheader Authorization: Optional Apikey value :resheader Content-Type: application/json :status 200: Campaign alias found :status 401: Invalid role to perform this action :status 404: Campaign alias ID not found """ campaign_alias = CampaignAlias.query.get(campaign_alias_id) if not campaign_alias: return error_response(404, 'Campaign alias ID not found') return jsonify(campaign_alias.to_dict())
def read_indicator_type(indicator_type_id): """ Gets a single indicator type given its ID. .. :quickref: IndicatorType; Gets a single indicator type given its ID. **Example request**: .. sourcecode:: http GET /indicators/type/1 HTTP/1.1 Host: 127.0.0.1 Accept: application/json **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "id": 1, "value": "Email - Address" } :reqheader Authorization: Optional Apikey value :resheader Content-Type: application/json :status 200: Indicator type found :status 401: Invalid role to perform this action :status 404: Indicator type ID not found """ indicator_type = IndicatorType.query.get(indicator_type_id) if not indicator_type: return error_response(404, 'Indicator type ID not found') return jsonify(indicator_type.to_dict())