Skip to content

Commit e560fee

Browse files
committed
wip
1 parent 12c3b94 commit e560fee

File tree

1 file changed

+78
-45
lines changed

1 file changed

+78
-45
lines changed

authentik/root/storages.py

+78-45
Original file line numberDiff line numberDiff line change
@@ -296,24 +296,23 @@ def get_valid_name(self, name: str) -> str:
296296
return super().get_valid_name(name)
297297

298298
def _normalize_name(self, name: str) -> str:
299-
"""Create tenant-aware S3 key"""
300-
try:
301-
name = self.get_valid_name(name)
302-
normalized = safe_join(self.location, self.get_tenant_path(name))
303-
LOGGER.debug("Normalized S3 key", original=name, normalized=normalized)
304-
return normalized
305-
except ValueError as e:
306-
LOGGER.error(
307-
"Invalid S3 key path detected", name=name, tenant=self.tenant_prefix, error=str(e)
308-
)
309-
raise SuspiciousOperation(f"Invalid path: {name}") from e
299+
"""Normalize file path for S3 storage"""
300+
if ".." in name or name.startswith("/"):
301+
raise SuspiciousOperation(f"Suspicious path: {name}")
302+
normalized = self.get_tenant_path(name)
303+
LOGGER.debug(
304+
"Normalized S3 key",
305+
original=name,
306+
normalized=normalized,
307+
)
308+
return normalized
310309

311310
def _randomize_filename(self, filename: str) -> str:
312-
"""Generate random filename"""
313-
stem = uuid.uuid4().hex
314-
suffix = Path(filename).suffix.lower()
315-
tenant_hash = uuid.uuid5(uuid.NAMESPACE_DNS, self.tenant_prefix).hex[:8]
316-
randomized = f"{tenant_hash}_{stem}{suffix}"
311+
"""Generate a randomized filename while preserving extension"""
312+
name, ext = os.path.splitext(filename)
313+
tenant_hash = str(uuid.uuid5(uuid.NAMESPACE_DNS, self.tenant_prefix))[:8]
314+
random_uuid = str(uuid.uuid4())
315+
randomized = f"{tenant_hash}_{random_uuid}{ext.lower()}"
317316
LOGGER.debug(
318317
"Randomized filename",
319318
original=filename,
@@ -323,51 +322,57 @@ def _randomize_filename(self, filename: str) -> str:
323322
return randomized
324323

325324
def _save(self, name: str, content) -> str:
326-
"""Save file with secure random name and handle old file cleanup"""
327-
try:
328-
name = self.get_valid_name(name)
329-
randomized_name = self._randomize_filename(name)
330-
normalized_name = self._normalize_name(randomized_name)
331-
332-
self._file_mapping[name] = normalized_name
333-
334-
LOGGER.info(
335-
"Saving file to S3",
336-
original_name=name,
337-
randomized_name=randomized_name,
338-
normalized_name=normalized_name,
339-
tenant=self.tenant_prefix,
340-
)
325+
"""Save file to S3 with tenant isolation and random filename"""
326+
randomized_name = self._randomize_filename(name)
327+
normalized_name = self._normalize_name(randomized_name)
328+
329+
LOGGER.info(
330+
"Saving file to S3",
331+
original_name=name,
332+
randomized_name=randomized_name,
333+
normalized_name=normalized_name,
334+
tenant=self.tenant_prefix,
335+
)
341336

342-
result = super()._save(randomized_name, content)
337+
try:
338+
# Upload file to S3
339+
self.bucket.Object(normalized_name).upload_fileobj(content)
343340

341+
# Verify upload
344342
try:
345343
self.bucket.Object(normalized_name).load()
346-
LOGGER.debug(
347-
"File saved successfully to S3", key=normalized_name, tenant=self.tenant_prefix
348-
)
349344
except ClientError as e:
345+
error_code = e.response.get("Error", {}).get("Code", "Unknown")
346+
error_message = e.response.get("Error", {}).get("Message", "Unknown error")
350347
LOGGER.error(
351348
"Failed to verify S3 upload",
352349
key=normalized_name,
353-
error_code=e.response["Error"]["Code"],
354-
message=e.response["Error"]["Message"],
350+
error_code=error_code,
351+
message=error_message,
355352
tenant=self.tenant_prefix,
356353
)
357-
self._delete_file(normalized_name)
354+
# Clean up failed upload
355+
try:
356+
self.bucket.Object(normalized_name).delete()
357+
except Exception as cleanup_error:
358+
LOGGER.error(
359+
"Failed to clean up failed upload",
360+
key=normalized_name,
361+
error=str(cleanup_error),
362+
tenant=self.tenant_prefix,
363+
)
358364
raise
359365

360-
return result
366+
# Store mapping of original name to normalized name
367+
self._file_mapping[name] = normalized_name
361368

362-
except ClientError as e:
363-
LOGGER.error(
364-
"S3 upload failed",
365-
error_code=e.response["Error"]["Code"],
366-
message=e.response["Error"]["Message"],
369+
LOGGER.debug(
370+
"File saved successfully to S3",
367371
key=normalized_name,
368372
tenant=self.tenant_prefix,
369373
)
370-
raise
374+
return normalized_name
375+
371376
except Exception as e:
372377
LOGGER.error(
373378
"Unexpected error saving file to S3",
@@ -377,6 +382,34 @@ def _save(self, name: str, content) -> str:
377382
)
378383
raise
379384

385+
def delete(self, name: str) -> None:
386+
"""Delete file from S3"""
387+
try:
388+
# Get normalized name from mapping or normalize original name
389+
normalized_name = self._file_mapping.get(name, self._normalize_name(name))
390+
self.bucket.Object(normalized_name).delete()
391+
# Remove from mapping if exists
392+
self._file_mapping.pop(name, None)
393+
LOGGER.debug(
394+
"File deleted from S3",
395+
key=normalized_name,
396+
tenant=self.tenant_prefix,
397+
)
398+
except ClientError as e:
399+
if e.response.get("Error", {}).get("Code") != "404":
400+
LOGGER.error(
401+
"Failed to delete file from S3",
402+
name=name,
403+
error=str(e),
404+
tenant=self.tenant_prefix,
405+
)
406+
raise
407+
LOGGER.debug(
408+
"File not found during delete",
409+
name=name,
410+
tenant=self.tenant_prefix,
411+
)
412+
380413
def url(self, name: str, **kwargs) -> str:
381414
"""Generate URL without signing parameters when using custom domain"""
382415
try:

0 commit comments

Comments
 (0)