upload file for storage (#86)
* upload file for storage accept uploaded yaml file for velociraptor connector authentication * file upload for velo api creds * precommit passing
taylor_socfortress committed
Jul 31, 2023 at 16:46 UTC
ebec2cabd52c053772cc87988c719ffef3eecf70
4 files changed
+84
.gitignore
+1
@@ -41,3 +41,4 @@ wheels/
41
.env
42
*.sqbpro
43
site/
44
+backend/app/file-store/api.config.yaml
backend/app/routes/connectors.py
+18
@@ -76,3 +76,21 @@ def update_connector_route(id: str):
76
return validate_and_update_connector(id, request_data, service)
77
else:
78
return jsonify(connector), 404
79
+
80
+
81
+@bp.route("/connectors/upload", methods=["POST"])
82
+def upload_file():
83
+ logger.info("Received request to upload a file")
84
+ # check if the post request has the file part
85
+ if "file" not in request.files:
86
+ logger.error("No file part in the request")
87
+ return {"message": "No file part in the request", "success": False}, 400
88
+ file = request.files["file"]
89
+ # if user does not select file, browser also
90
+ # submit an empty part without filename
91
+ if file.filename == "":
92
+ logger.error("No selected file")
93
+ return {"message": "No selected file", "success": False}, 400
94
+
95
+ service = ConnectorService(db)
96
+ return service.save_file(file)
backend/app/services/connectors/connectors.py
+23
@@ -1,7 +1,10 @@
1
# from app.models.connectors import ConnectorFactory
2
+import os
3
+
4
from flask import current_app
5
from loguru import logger
6
from sqlalchemy.exc import SQLAlchemyError
7
+from werkzeug.utils import secure_filename
8
9
from app.models.connectors import Connector
10
from app.models.connectors import connector_factory
@@ -10,6 +13,10 @@ from app.models.models import Connectors
13
# from app.models.models import ConnectorsAvailable
14
# from app.models.models import connectors_schema
15
16
+UPLOAD_FOLDER = "file-store"
17
+UPLOAD_FOLDER = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), UPLOAD_FOLDER)
18
+ALLOWED_EXTENSIONS = set(["yaml"]) # replace with your allowed file extensions
19
+
20
21
class ConnectorService:
22
"""
@@ -234,3 +241,19 @@ class ConnectorService:
241
"message": "Request data is invalid. Ensure connector_url and connector_api_key are present",
242
"success": False,
243
}
244
+
245
+ @staticmethod
246
+ def allowed_file(filename):
247
+ return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS
248
+
249
+ def save_file(self, file):
250
+ if file and self.allowed_file(file.filename):
251
+ filename = secure_filename(file.filename)
252
+ file.save(os.path.join(UPLOAD_FOLDER, filename))
253
+ connector = self.db.session.query(Connectors).filter_by(connector_name="Velociraptor").first()
254
+ # Write location to `connectors` table
255
+ connector.connector_api_key = os.path.join(UPLOAD_FOLDER, filename)
256
+ self.db.session.commit()
257
+ return {"message": "File successfully uploaded", "success": True}
258
+ else:
259
+ return {"message": "File type not allowed", "success": False}, 400
backend/app/static/swagger.json
+42
@@ -354,6 +354,48 @@
354
}
355
}
356
},
357
+ "/connectors/upload": {
358
+ "post": {
359
+ "tags": ["Connectors"],
360
+ "summary": "Upload a file",
361
+ "description": "Endpoint to upload a file to the server.",
362
+ "requestBody": {
363
+ "content": {
364
+ "multipart/form-data": {
365
+ "schema": {
366
+ "type": "object",
367
+ "properties": {
368
+ "file": {
369
+ "type": "string",
370
+ "format": "binary"
371
+ }
372
+ }
373
+ }
374
+ }
375
+ }
376
+ },
377
+ "responses": {
378
+ "200": {
379
+ "description": "A JSON response containing the details of the uploaded file.",
380
+ "content": {
381
+ "application/json": {
382
+ "schema": {
383
+ "type": "object",
384
+ "properties": {
385
+ "message": {
386
+ "type": "string"
387
+ },
388
+ "success": {
389
+ "type": "boolean"
390
+ }
391
+ }
392
+ }
393
+ }
394
+ }
395
+ }
396
+ }
397
+ }
398
+ },
399
"/customers/create": {
400
"post": {
401
"tags": ["Customers"],