🔍 Code Extractor

function manage_user_share_access_v2

Maturity: 63

Manages user access permissions to a FileCloud document share, creating shares if needed and setting read-only or write access for specified users.

File:
/tf/active/vicechatdev/CDocs/controllers/permission_startup_check.py
Lines:
66 - 297
Complexity:
complex

Purpose

This function provides comprehensive management of user access to document versions in FileCloud. It handles the complete workflow: checking if a share exists for the document version, creating a new share if necessary, verifying if the user already has access, adding the user to the share if needed, and setting appropriate permissions (read-only or write access). It includes extensive error handling and logging throughout the process, making it suitable for production environments where document access control is critical.

Source Code

def manage_user_share_access(
    document_version: DocumentVersion,
    user_id: str,
    grant_write_access: bool = False
) -> Dict[str, Any]:
    """
    Manage a user's access to a document share in FileCloud.
    
    This function ensures the user has appropriate access (read-only or edit)
    to a document share in FileCloud based on their role and document status.
    
    Args:
        document_version: The document version to share
        user_id: User's email address (used as FileCloud identifier)
        grant_write_access: Whether to grant write access (True) or read-only (False)
        
    Returns:
        Dict with result information
    """
    try:
        # Get FileCloud client - using direct FC API access
        fc_client = get_filecloud_client()
        if not fc_client:
            logger.error("Could not get FileCloud client")
            return {
                'success': False,
                'message': 'FileCloud connection not available'
            }
        
        # Check if share_id attribute exists, if not, initialize it
        if not hasattr(document_version, 'share_id') or not document_version.share_id:
            # Either attribute missing or it's None/empty - create a new share
            
            # Get document
            doc = document_version.document
            if not doc:
                logger.error("Could not get document from version")
                return {
                    'success': False,
                    'message': 'Document not found for version'
                }
            
            # Get file path in FileCloud
            file_path = get_filecloud_document_path(
                doc, 
                version=document_version.version_number
            )
            
            # Check if file exists - with error handling
            try:
                file_exists = fc_client.check_file_exists(file_path)
                if not file_exists:
                    logger.error(f"File not found at path: {file_path}")
                    return {
                        'success': False,
                        'message': f'File not found: {file_path}'
                    }
            except Exception as file_check_error:
                logger.error(f"Error checking if file exists: {str(file_check_error)}")
                # Continue anyway - the file might still exist even if the check fails
            
            # Create a new share directly using the FC API
            share_name = f"{doc.title} v{document_version.version_number}"
            
            # Add try/except specifically for add_share
            try:
                share_result = fc_client.add_share(
                    share_location=file_path,
                    share_name=share_name,
                    max_downloads=1000
                )
            except Exception as share_error:
                logger.error(f"Error creating share: {str(share_error)}")
                return {
                    'success': False,
                    'message': f"Error creating share: {str(share_error)}"
                }
            
            if not share_result.get('success', False):
                logger.error(f"Failed to create share: {share_result.get('message', 'Unknown error')}")
                return {
                    'success': False,
                    'message': f"Failed to create share: {share_result.get('message', 'Unknown error')}"
                }
            
            # Extract share ID and URL
            share_id = share_result.get('share_id')
            share_url = share_result.get('share_url')
            
            if not share_id:
                logger.error("No share ID returned from FileCloud API")
                return {
                    'success': False,
                    'message': 'Share creation failed: No share ID returned'
                }
            
            # Update document version with share information
            if not hasattr(document_version, 'share_id'):
                setattr(document_version, 'share_id', share_id)
            else:
                document_version.share_id = share_id
                
            if not hasattr(document_version, 'share_url'):
                setattr(document_version, 'share_url', share_url)
            else:
                document_version.share_url = share_url
                
            try:
                document_version.save()
                logger.info(f"Successfully saved share info for document version {document_version.uid}")
            except Exception as save_err:
                logger.warning(f"Failed to save document version with share info: {save_err}")
                # Continue despite this error
        
        # Get the share ID safely
        share_id = getattr(document_version, 'share_id', None)
        if not share_id:
            logger.error("No share ID available for document version")
            return {
                'success': False,
                'message': 'No share ID available for document'
            }
        
        # 1. First check if user is already in the share
        user_exists_in_share = False
        try:
            users_result = fc_client.get_users_for_share(share_id=share_id)
            
            if users_result.get('success', False):
                # Extract users from the result
                # Structure varies based on if there's a single user or multiple users
                try:
                    if 'data' in users_result:
                        # Handle different response structures
                        if 'users' in users_result['data']:
                            users_data = users_result['data']['users']
                            user_list = []
                            
                            # Users data can be a dict with a 'user' key, or directly a user list
                            if isinstance(users_data, dict) and 'user' in users_data:
                                user_data = users_data['user']
                                # User can be a single dict or a list of dicts
                                if isinstance(user_data, list):
                                    user_list = user_data
                                else:
                                    user_list = [user_data]
                            elif isinstance(users_data, list):
                                user_list = users_data
                            
                            # Check if user is in the list
                            for user in user_list:
                                # Check if user is a dictionary before calling get()
                                if isinstance(user, dict):
                                    if user.get('userid') == user_id or user.get('email') == user_id:
                                        user_exists_in_share = True
                                        logger.info(f"User {user_id} already exists in share {share_id}")
                                        break
                                elif isinstance(user, str) and (user == user_id):
                                    # If user is directly a string (username or email)
                                    user_exists_in_share = True
                                    logger.info(f"User {user_id} already exists in share {share_id}")
                                    break
                except Exception as parse_err:
                    logger.warning(f"Error parsing users response: {str(parse_err)}")
                    # Continue anyway, we'll try to add the user
        except Exception as users_err:
            logger.warning(f"Error getting users for share: {str(users_err)}")
            # Continue anyway, we'll try to add the user
        
        # 2. Add user to share if not already added
        if not user_exists_in_share:
            # Add user to share first - using the correct parameter names
            try:
                # Make sure we're using valid parameters for add_user_to_share
                logger.info(f"Adding user {user_id} to share {share_id}")
                add_result = fc_client.add_user_to_share(
                    user_id=user_id,
                    share_id=share_id
                )
                
                if not add_result.get('success', False):
                    # If user already added but not detected above, continue anyway
                    if "already added" in str(add_result.get('message', '')).lower():
                        logger.info(f"User {user_id} was already added to share {share_id}")
                        user_exists_in_share = True
                    else:
                        logger.error(f"Failed to add user to share: {add_result.get('message', 'Unknown error')}")
                        return {
                            'success': False, 
                            'message': f"Failed to add user to share: {add_result.get('message', 'Unknown error')}"
                        }
            except Exception as add_err:
                logger.error(f"Error adding user to share: {str(add_err)}")
                return {
                    'success': False,
                    'message': f"Error adding user to share: {str(add_err)}"
                }
        
        # 3. Set appropriate access rights
        # Use set_user_access_for_share method to grant appropriate permissions
        try:
            perm_result = fc_client.set_user_access_for_share(
                share_id=share_id,
                user_id=user_id,
                write=grant_write_access,    # Set write permission based on parameter
                download=True,               # Always allow download
                share=False,                 # Don't allow resharing by default
                sync=False                   # Don't allow sync
            )
            
            if not perm_result.get('success', False):
                logger.warning(f"Set user access permissions failed: {perm_result.get('message', 'Unknown error')}")
                # Continue despite permission setting issues - at least user has basic access
        except Exception as perm_err:
            logger.warning(f"Error setting user permissions: {str(perm_err)}")
            # Continue anyway, at least the user is added to the share
        
        return {
            'success': True,
            'message': f"User access {'with edit rights' if grant_write_access else 'read-only'} set successfully",
            'share_url': getattr(document_version, 'share_url', None),
            'share_id': share_id,
            'user_id': user_id,
            'permission': "write" if grant_write_access else "read"
        }
        
    except Exception as e:
        logger.error(f"Error managing user share access: {str(e)}")
        return {
            'success': False,
            'message': f"Error: {str(e)}"
        }

Parameters

Name Type Default Kind
document_version DocumentVersion - positional_or_keyword
user_id str - positional_or_keyword
grant_write_access bool False positional_or_keyword

Parameter Details

document_version: A DocumentVersion model instance representing the specific version of a document to be shared. Must have attributes like 'document', 'version_number', and optionally 'share_id' and 'share_url'. The function will create and populate share_id/share_url if they don't exist.

user_id: String identifier for the user, typically their email address, used as the FileCloud user identifier. This is used to add the user to the share and set their permissions.

grant_write_access: Boolean flag determining permission level. When True, grants write/edit access to the document. When False (default), grants read-only access with download capability. Defaults to False for security.

Return Value

Type: Dict[str, Any]

Returns a dictionary with keys: 'success' (bool indicating operation success), 'message' (str describing the result or error), 'share_url' (str URL to access the share, if successful), 'share_id' (str FileCloud share identifier), 'user_id' (str echoing the input user_id), and 'permission' (str either 'write' or 'read' indicating granted access level). On failure, only 'success' and 'message' keys are guaranteed to be present.

Dependencies

  • logging
  • typing

Required Imports

import logging
from typing import Dict, Any
from CDocs.models.document import DocumentVersion
from CDocs.controllers.filecloud_controller import get_filecloud_client, get_filecloud_document_path

Usage Example

from CDocs.models.document import DocumentVersion
from your_module import manage_user_share_access

# Get a document version instance (from database query)
doc_version = DocumentVersion.objects.get(uid='some-uuid')

# Grant read-only access to a user
result = manage_user_share_access(
    document_version=doc_version,
    user_id='user@example.com',
    grant_write_access=False
)

if result['success']:
    print(f"User granted {result['permission']} access")
    print(f"Share URL: {result['share_url']}")
else:
    print(f"Error: {result['message']}")

# Grant write access to another user
write_result = manage_user_share_access(
    document_version=doc_version,
    user_id='editor@example.com',
    grant_write_access=True
)

if write_result['success']:
    print(f"Editor granted write access at {write_result['share_url']}")

Best Practices

  • Always check the 'success' key in the returned dictionary before accessing other keys like 'share_url' or 'share_id'
  • The function modifies the document_version object by adding/updating share_id and share_url attributes - ensure the object is from a valid database session
  • Use grant_write_access=False by default for security; only grant write access when explicitly needed
  • The function includes extensive error handling and will continue operation even if some steps fail (e.g., permission setting), so check logs for warnings
  • User IDs should be email addresses that exist in the FileCloud system
  • The function is idempotent - calling it multiple times with the same parameters will not create duplicate shares or user access entries
  • Ensure proper logging configuration to capture detailed error messages and warnings for troubleshooting
  • The FileCloud client connection must be established before calling this function; verify get_filecloud_client() returns a valid client
  • Document version objects should have their associated document relationship properly loaded to avoid database query errors

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function manage_user_share_access 97.6% similar

    Manages user access permissions (read-only or edit) to a specific document version share in FileCloud, creating the share if it doesn't exist.

    From: /tf/active/vicechatdev/CDocs/controllers/filecloud_helper.py
  • function manage_user_share_access_v1 97.2% similar

    Manages user access permissions to a FileCloud document share, creating the share if needed and setting read-only or write access for a specified user.

    From: /tf/active/vicechatdev/CDocs/controllers/filecloud_controller.py
  • function remove_user_access 78.5% similar

    Removes a user's access permissions from a specific document version share in FileCloud by delegating to the FileCloud client's remove_user_from_share method.

    From: /tf/active/vicechatdev/CDocs/controllers/share_controller.py
  • function get_user_access_url 76.9% similar

    Retrieves a share URL for a document version and determines the appropriate access level (read/write) for a specific user based on their role (owner, author, reviewer, approver) and the document's current status.

    From: /tf/active/vicechatdev/CDocs/controllers/share_controller.py
  • function manage_document_permissions 76.3% similar

    Comprehensive function to manage document sharing and user permissions. This function: 1. Creates a share only if needed for active users 2. Adds/updates users with appropriate permissions based on their roles 3. Removes users who shouldn't have access anymore 4. Cleans up shares that are no longer needed 5. Manages ACL entries for write permissions on the document's folder Args: document: The document to manage permissions for Returns: Dict: Result of permission updates with detailed information

    From: /tf/active/vicechatdev/CDocs/controllers/share_controller.py
← Back to Browse