dn-scribe commited on
Commit
a2bea38
·
2 Parent(s): 1e177930d049ac

Merge branch 'main' into policy-results

Browse files
Files changed (2) hide show
  1. api/evidence.py +57 -2
  2. storage.py +3 -1
api/evidence.py CHANGED
@@ -3,6 +3,7 @@ import json
3
  from flask import Blueprint, request, jsonify, send_file, current_app
4
  from flask_jwt_extended import get_jwt_identity
5
  from werkzeug.utils import secure_filename
 
6
  from models import Attestation, UploadSession, User, Webhook, webhook_type_attestation_finalized
7
  from app import db
8
  from auth import jwt_required_with_logging, get_current_user_safe
@@ -195,7 +196,7 @@ def list_evidence():
195
 
196
  # Apply filters from AllContext data
197
  for key, value in data.items():
198
- if key == 'product_key':
199
  query = query.filter(Attestation.product_key == value)
200
  elif key == 'product_version':
201
  query = query.filter(Attestation.product_version == value)
@@ -213,7 +214,12 @@ def list_evidence():
213
  # Filter by labels in context
214
  for label in value:
215
  query = query.filter(Attestation.context['labels'].astext.contains(label))
216
-
 
 
 
 
 
217
  # Get all matching results
218
  attestations = query.all()
219
 
@@ -409,6 +415,55 @@ def upload_file(file_id):
409
  )
410
  return jsonify({'error': str(e)}), 500
411
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
412
  @evidence_bp.route('/evidence/finish', methods=['POST'])
413
  @jwt_required_with_logging
414
  def finish_attestation():
 
3
  from flask import Blueprint, request, jsonify, send_file, current_app
4
  from flask_jwt_extended import get_jwt_identity
5
  from werkzeug.utils import secure_filename
6
+ from sqlalchemy import func
7
  from models import Attestation, UploadSession, User, Webhook, webhook_type_attestation_finalized
8
  from app import db
9
  from auth import jwt_required_with_logging, get_current_user_safe
 
196
 
197
  # Apply filters from AllContext data
198
  for key, value in data.items():
199
+ if key == 'name' or key == 'product_key':
200
  query = query.filter(Attestation.product_key == value)
201
  elif key == 'product_version':
202
  query = query.filter(Attestation.product_version == value)
 
214
  # Filter by labels in context
215
  for label in value:
216
  query = query.filter(Attestation.context['labels'].astext.contains(label))
217
+ elif value is not None and value is not '' and value is not False:
218
+ if db.engine.dialect.name == 'sqlite':
219
+ query = query.filter(func.json_extract(Attestation.context, f'$.{key}') == str(value))
220
+ else: # PostgreSQL
221
+ query = query.filter(Attestation.context[key].astext == str(value))
222
+
223
  # Get all matching results
224
  attestations = query.all()
225
 
 
415
  )
416
  return jsonify({'error': str(e)}), 500
417
 
418
+ @evidence_bp.route('/evidence/download/<path:storage_path>', methods=['GET'])
419
+ def evidence_download(storage_path):
420
+ """Direct file download for local storage - API access to attestation files
421
+ This endpoint is public (no auth required) similar to a pre-signed URL"""
422
+ try:
423
+ config = current_app.config.get('STORAGE_CONFIG')
424
+
425
+ if config.STORAGE_TYPE != 'FILE_MOUNT':
426
+ # For non-local storage, redirect to presigned URL
427
+ storage = get_storage_backend()
428
+ download_url = storage.generate_download_url(storage_path)
429
+ return jsonify({'download_url': download_url}), 302
430
+
431
+ # For local storage, serve the file directly
432
+ file_path = os.path.join(config.FILE_STORAGE_PATH, storage_path)
433
+
434
+ if not os.path.exists(file_path):
435
+ return jsonify({'error': 'File not found'}), 404
436
+
437
+ log_event(
438
+ severity=6,
439
+ message=f"API file downloaded: {storage_path}",
440
+ event_type="api_file_downloaded"
441
+ )
442
+
443
+ # Determine content type based on file extension
444
+ content_type = None
445
+ if file_path.endswith(".json"):
446
+ content_type = "application/json"
447
+ elif file_path.endswith(".bin"):
448
+ # Try to infer from path or default to octet-stream
449
+ content_type = "application/octet-stream"
450
+
451
+ # Serve the file directly
452
+ return send_file(
453
+ file_path,
454
+ mimetype=content_type,
455
+ as_attachment=True,
456
+ download_name=os.path.basename(file_path)
457
+ )
458
+
459
+ except Exception as e:
460
+ log_event(
461
+ severity=3,
462
+ message=f"API download error: {str(e)}",
463
+ event_type="api_download_error"
464
+ )
465
+ return jsonify({'error': str(e)}), 500
466
+
467
  @evidence_bp.route('/evidence/finish', methods=['POST'])
468
  @jwt_required_with_logging
469
  def finish_attestation():
storage.py CHANGED
@@ -55,7 +55,9 @@ class FileStorageBackend(StorageBackend):
55
 
56
  def generate_download_url(self, storage_path):
57
  """Generate download URL for local files"""
58
- return urljoin(self.base_url, storage_path)
 
 
59
 
60
  def delete_file(self, storage_path):
61
  """Delete local file"""
 
55
 
56
  def generate_download_url(self, storage_path):
57
  """Generate download URL for local files"""
58
+ # For API access, prepend 'evidence/download/' to the storage path
59
+ api_path = f"evidence/download/{storage_path}"
60
+ return urljoin(self.base_url, api_path)
61
 
62
  def delete_file(self, storage_path):
63
  """Delete local file"""