feat: Share-Links mit Berechtigungen (Lesen / Lesen+Schreiben)
Share-Links haben jetzt ein permission-Feld (read/write): - read (Standard): Nur Download erlaubt, kein Upload, kein Aendern - write: Download + Upload in Ordner erlaubt Backend-Absicherung: - POST /share/<token>/upload prueft permission == 'write', gibt 403 bei read-only Links zurueck - GET /share/<token>/info gibt permission + upload_allowed zurueck - ShareLink-Model hat neues permission-Feld (default: 'read') Frontend Share-Dialog: - Dropdown "Berechtigung" beim Erstellen von Links (Nur Lesen / Lesen+Hochladen) - Bestehende Links zeigen Berechtigungslevel an Frontend ShareView: - Upload-Zone nur sichtbar wenn upload_allowed == true - Bei read-only Links: kein Drag & Drop, kein Upload-Button Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -419,6 +419,10 @@ def create_share_link(file_id):
|
||||
password = data.get('password')
|
||||
expires_at = data.get('expires_at')
|
||||
max_downloads = data.get('max_downloads')
|
||||
permission = data.get('permission', 'read')
|
||||
|
||||
if permission not in ('read', 'write'):
|
||||
return jsonify({'error': 'Berechtigung muss "read" oder "write" sein'}), 400
|
||||
|
||||
token = secrets.token_urlsafe(32)
|
||||
password_hash = None
|
||||
@@ -435,6 +439,7 @@ def create_share_link(file_id):
|
||||
link = ShareLink(
|
||||
file_id=file_id,
|
||||
token=token,
|
||||
permission=permission,
|
||||
password_hash=password_hash,
|
||||
expires_at=exp_dt,
|
||||
created_by=user.id,
|
||||
@@ -446,6 +451,7 @@ def create_share_link(file_id):
|
||||
return jsonify({
|
||||
'token': token,
|
||||
'url': f'/share/{token}',
|
||||
'permission': permission,
|
||||
'expires_at': exp_dt.isoformat() if exp_dt else None,
|
||||
'has_password': bool(password),
|
||||
}), 201
|
||||
@@ -463,6 +469,7 @@ def list_share_links(file_id):
|
||||
return jsonify([{
|
||||
'id': l.id,
|
||||
'token': l.token,
|
||||
'permission': l.permission,
|
||||
'has_password': bool(l.password_hash),
|
||||
'expires_at': l.expires_at.isoformat() if l.expires_at else None,
|
||||
'download_count': l.download_count,
|
||||
@@ -490,7 +497,8 @@ def share_info(token):
|
||||
'size': f.size,
|
||||
'mime_type': f.mime_type,
|
||||
'has_password': bool(link.password_hash),
|
||||
'upload_allowed': f.is_folder,
|
||||
'permission': link.permission,
|
||||
'upload_allowed': f.is_folder and link.permission == 'write',
|
||||
}), 200
|
||||
|
||||
|
||||
@@ -560,7 +568,7 @@ def share_download(token):
|
||||
|
||||
@api_bp.route('/share/<token>/upload', methods=['POST'])
|
||||
def share_upload(token):
|
||||
"""Upload a file via a share link (only if the shared item is a folder)."""
|
||||
"""Upload a file via a share link (only if the shared item is a folder with write permission)."""
|
||||
link = ShareLink.query.filter_by(token=token).first()
|
||||
if not link:
|
||||
return jsonify({'error': 'Link nicht gefunden'}), 404
|
||||
@@ -568,6 +576,10 @@ def share_upload(token):
|
||||
if link.is_expired():
|
||||
return jsonify({'error': 'Link abgelaufen'}), 410
|
||||
|
||||
# Check write permission
|
||||
if link.permission != 'write':
|
||||
return jsonify({'error': 'Dieser Link erlaubt nur Lesen'}), 403
|
||||
|
||||
# Check password if set
|
||||
if link.password_hash:
|
||||
password = request.form.get('password', '') or request.headers.get('X-Share-Password', '')
|
||||
|
||||
Reference in New Issue
Block a user