mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-07-23 14:59:36 +02:00
feat: Enhance collection sharing and location management features
- Implemented unsharing functionality in CollectionViewSet, including removal of user-owned locations from collections. - Refactored ContentImageViewSet to support multiple content types and improved permission checks for image uploads. - Added user ownership checks in LocationViewSet for delete operations. - Enhanced collection management in the frontend to display both owned and shared collections separately. - Updated Immich integration to handle access control based on location visibility and user permissions. - Improved UI components to show creator information and manage collection links more effectively. - Added loading states and error handling in collection fetching logic.
This commit is contained in:
parent
7f80dad94b
commit
ba162175fe
19 changed files with 641 additions and 245 deletions
|
@ -270,48 +270,93 @@ class ImmichIntegrationView(viewsets.ViewSet):
|
|||
integration = get_object_or_404(ImmichIntegration, id=integration_id)
|
||||
owner_id = integration.user
|
||||
|
||||
# Try to find the image entry with collections and sharing information
|
||||
image_entry = (
|
||||
# Get all images for this immich_id and user
|
||||
image_entries = list(
|
||||
ContentImage.objects
|
||||
.filter(immich_id=imageid, user=owner_id)
|
||||
.select_related('location')
|
||||
.prefetch_related('location__collections', 'location__collections__shared_with')
|
||||
.order_by('-location__is_public') # Public locations first
|
||||
.first()
|
||||
.select_related('content_type')
|
||||
)
|
||||
|
||||
# Sort by access level priority and find the best match
|
||||
def get_access_priority(image_entry):
|
||||
"""Return priority score for access control (lower = higher priority)"""
|
||||
content_obj = image_entry.content_object
|
||||
|
||||
# Only handle Location objects for now (can be extended for other types)
|
||||
if not hasattr(content_obj, 'is_public'):
|
||||
return 999 # Low priority for non-location objects
|
||||
|
||||
# For Location objects, check access levels
|
||||
if content_obj.is_public:
|
||||
return 0 # Highest priority - public location
|
||||
|
||||
# Check if location is in any public collection
|
||||
if hasattr(content_obj, 'collections'):
|
||||
collections = content_obj.collections.all()
|
||||
if any(collection.is_public for collection in collections):
|
||||
return 1 # Second priority - private location in public collection
|
||||
|
||||
# Check for shared collections (if user is authenticated)
|
||||
if (request.user.is_authenticated and
|
||||
any(collection.shared_with.filter(id=request.user.id).exists()
|
||||
for collection in collections)):
|
||||
return 2 # Third priority - shared collection access
|
||||
|
||||
return 3 # Lowest priority - private location, owner access only
|
||||
|
||||
# Sort image entries by access priority
|
||||
image_entries.sort(key=get_access_priority)
|
||||
image_entry = image_entries[0] if image_entries else None
|
||||
|
||||
# Access control
|
||||
if image_entry:
|
||||
location = image_entry.location
|
||||
collections = location.collections.all()
|
||||
|
||||
# Determine access level
|
||||
is_authorized = False
|
||||
|
||||
# Level 1: Public location (highest priority)
|
||||
if location.is_public:
|
||||
is_authorized = True
|
||||
|
||||
# Level 2: Private location in any public collection
|
||||
elif any(collection.is_public for collection in collections):
|
||||
is_authorized = True
|
||||
|
||||
# Level 3: Owner access
|
||||
elif request.user.is_authenticated and request.user == owner_id:
|
||||
is_authorized = True
|
||||
|
||||
# Level 4: Shared collection access - check if user has access to any collection
|
||||
elif (request.user.is_authenticated and
|
||||
any(collection.shared_with.filter(id=request.user.id).exists()
|
||||
for collection in collections)):
|
||||
is_authorized = True
|
||||
content_obj = image_entry.content_object
|
||||
|
||||
if not is_authorized:
|
||||
return Response({
|
||||
'message': 'This image belongs to a private location and you are not authorized.',
|
||||
'error': True,
|
||||
'code': 'immich.permission_denied'
|
||||
}, status=status.HTTP_403_FORBIDDEN)
|
||||
# Only apply access control to Location objects
|
||||
if hasattr(content_obj, 'is_public'):
|
||||
location = content_obj
|
||||
|
||||
# Determine access level
|
||||
is_authorized = False
|
||||
|
||||
# Level 1: Public location (highest priority)
|
||||
if location.is_public:
|
||||
is_authorized = True
|
||||
|
||||
# Level 2: Private location in any public collection
|
||||
elif hasattr(location, 'collections'):
|
||||
collections = location.collections.all()
|
||||
if any(collection.is_public for collection in collections):
|
||||
is_authorized = True
|
||||
|
||||
# Level 3: Owner access
|
||||
elif request.user.is_authenticated and request.user == owner_id:
|
||||
is_authorized = True
|
||||
|
||||
# Level 4: Shared collection access
|
||||
elif (request.user.is_authenticated and
|
||||
any(collection.shared_with.filter(id=request.user.id).exists()
|
||||
for collection in collections)):
|
||||
is_authorized = True
|
||||
else:
|
||||
# Location without collections - owner access only
|
||||
if request.user.is_authenticated and request.user == owner_id:
|
||||
is_authorized = True
|
||||
|
||||
if not is_authorized:
|
||||
return Response({
|
||||
'message': 'This image belongs to a private location and you are not authorized.',
|
||||
'error': True,
|
||||
'code': 'immich.permission_denied'
|
||||
}, status=status.HTTP_403_FORBIDDEN)
|
||||
else:
|
||||
# For non-Location objects, allow only owner access for now
|
||||
if not request.user.is_authenticated or request.user != owner_id:
|
||||
return Response({
|
||||
'message': 'This image is not publicly accessible and you are not the owner.',
|
||||
'error': True,
|
||||
'code': 'immich.permission_denied'
|
||||
}, status=status.HTTP_403_FORBIDDEN)
|
||||
else:
|
||||
# No ContentImage exists; allow only the integration owner
|
||||
if not request.user.is_authenticated or request.user != owner_id:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue