function get_user_access_url
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.
/tf/active/vicechatdev/CDocs/controllers/share_controller.py
864 - 1041
complex
Purpose
This function serves as a centralized access control mechanism for document versions. It evaluates user roles and document workflow states (draft, in review, in approval) to determine whether a user should have write access or read-only access. It handles complex scenarios like sequential reviews, active reviewer assignments, and completed review cycles. The function is read-only and does not modify permissions, only retrieves and returns access information.
Source Code
def get_user_access_url(document_version: DocumentVersion, user_uid: str) -> Dict[str, Any]:
"""
Get a share URL for a user and determine appropriate access level based on roles.
This function only retrieves information and does not modify permissions.
Args:
document_version: The document version to access
user_uid: UID of the user requesting access
Returns:
Dict: Access result with URL and permission information if successful
"""
try:
# Get document
document = document_version.document
if not document:
return {
'success': False,
'message': 'Document not found for version'
}
# Determine user role and permissions
is_owner = False
is_reviewer = False
is_approver = False
should_have_write_access = False
# Check if user is the owner
if hasattr(document, 'owner') and document.owner:
is_owner = (user_uid == document.owner.UID)
# Check if user is an author (not owner)
is_author = False
if hasattr(document, 'authors'):
for author in document.authors:
if author and user_uid == author.UID and (not is_owner):
is_author = True
break
# Check review status
if document.status == STATUS_IN_REVIEW:
# Use the direct database query approach from permission_startup_check.py
from CDocs.models.review import ReviewCycle
from CDocs.db import db_operations
# Get active review cycles for this document version
review_cycles = []
review_query = db_operations.run_query(
"""
MATCH (v:DocumentVersion {UID: $version_uid})<-[:FOR_REVIEW]-(r:ReviewCycle)
WHERE r.status IN ['PENDING', 'IN_PROGRESS']
RETURN r.UID as review_uid
""",
{"version_uid": document_version.uid}
)
if review_query:
review_cycles = [ReviewCycle(uid=record["review_uid"]) for record in review_query]
# Check if user is an active reviewer in any of these cycles
for review_cycle in review_cycles:
if review_cycle and review_cycle.status in ['PENDING', 'IN_PROGRESS']:
reviewer_assignments = review_cycle.get_reviewer_assignments()
for assignment in reviewer_assignments:
if assignment and assignment.reviewer_uid == user_uid:
is_reviewer = True
# For sequential reviews, only the active reviewer should have write access
#logger.info("checking if review is sequential based on status", review_cycle.sequential)
if review_cycle.sequential:
is_reviewer_active = assignment.status == 'ACTIVE'
else:
is_reviewer_active = assignment.status in ['ACTIVE', 'PENDING']
# Set write access flag if reviewer is active
if is_reviewer_active:
should_have_write_access = True
break
# Check approval status
if document.status == STATUS_IN_APPROVAL:
# Use the direct database query approach from permission_startup_check.py
from CDocs.models.approval import ApprovalCycle
from CDocs.db import db_operations
# Get approval cycles associated with this document version
approval_cycles = []
approval_query = db_operations.run_query(
"""
MATCH (v:DocumentVersion {UID: $version_uid})<-[:FOR_APPROVAL]-(a:ApprovalCycle)
WHERE a.status IN ['PENDING', 'IN_PROGRESS', 'IN_APPROVAL']
RETURN a.UID as approval_uid
""",
{"version_uid": document_version.uid}
)
if approval_query:
approval_cycles = [ApprovalCycle(uid=record["approval_uid"]) for record in approval_query]
# Check if user is an active approver in any of these cycles
for approval_cycle in approval_cycles:
if approval_cycle and approval_cycle.status not in ['COMPLETED', 'REJECTED', 'CANCELLED']:
approver_assignments = approval_cycle.get_approver_assignments()
for assignment in approver_assignments:
if assignment and not assignment.removal_date and assignment.approver_uid == user_uid:
is_approver = True
# Approvers always get read-only access
break
# Grant write access to owner and authors only in draft status
if (is_owner or is_author) and document.status == STATUS_DRAFT:
should_have_write_access = True
# NEW CONDITION: Grant write access to owner and authors if document is IN_REVIEW
# but all review cycles are completed or cancelled
if (is_owner or is_author) and document.status == STATUS_IN_REVIEW:
# Find if there are any active reviews
has_active_reviews = False
# Query for review cycles related to this document version
from CDocs.db import db_operations as db
review_query = db.run_query(
"""
MATCH (v:DocumentVersion {UID: $version_uid})<-[:FOR_REVIEW]-(r:ReviewCycle)
WHERE not(r.status IN ['COMPLETED', 'CANCELED'])
RETURN count(r) as active_count
""",
{"version_uid": document_version.uid}
)
if review_query and review_query[0].get('active_count', 0) > 0:
has_active_reviews = True
# If no active reviews, grant write access
if not has_active_reviews:
should_have_write_access = True
logger.debug(f"Granting write access to {user_uid} for IN_REVIEW document with no active reviews")
# Get share URL without modifying anything
share_url = None
if is_owner:
logger.info("Owner detected, generating share URL")
from CDocs.controllers.document_controller import get_document_edit_url
share_url=get_document_edit_url(document_uid=document.UID)
if share_url['success']:
share_url = share_url['edit_url']
else:
share_url = None
else:
share_url = document_version.share_url
if not share_url:
logger.warning(f"Document version {document_version.uid} has no share URL")
return {
'success': False,
'message': 'Document has no share URL'
}
# Return the access information without modifying permissions
return {
'success': True,
'share_url': share_url,
'write_access': should_have_write_access,
'is_owner': is_owner,
'is_author': is_author,
'is_reviewer': is_reviewer,
'is_approver': is_approver,
'current_status': document_version.status
}
except Exception as e:
logger.error(f"Error getting user access URL: {str(e)}")
return {
'success': False,
'message': f'Error getting access URL: {str(e)}'
}
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
document_version |
DocumentVersion | - | positional_or_keyword |
user_uid |
str | - | positional_or_keyword |
Parameter Details
document_version: A DocumentVersion object representing the specific version of a document for which access is being requested. Must have a valid 'document' relationship and 'uid' attribute. The object should contain status information and relationships to review/approval cycles.
user_uid: A string representing the unique identifier (UID) of the user requesting access to the document. This UID is used to match against document owners, authors, reviewers, and approvers to determine the user's role and access level.
Return Value
Type: Dict[str, Any]
Returns a dictionary with access information. On success: {'success': True, 'share_url': str (URL to access document), 'write_access': bool (whether user can edit), 'is_owner': bool, 'is_author': bool, 'is_reviewer': bool, 'is_approver': bool, 'current_status': str (document status)}. On failure: {'success': False, 'message': str (error description)}. Write access is granted to owners/authors in DRAFT status, active reviewers in IN_REVIEW status (considering sequential vs parallel reviews), and owners/authors when all review cycles are completed/cancelled.
Dependencies
CDocs.models.documentCDocs.models.user_extensionsCDocs.models.reviewCDocs.models.approvalCDocs.models.document_statusCDocs.controllers.filecloud_controllerCDocs.controllers.document_controllerCDocs.db.db_operationsloggingtyping
Required Imports
from typing import Dict, Any
from CDocs.models.document import DocumentVersion
import logging
Conditional/Optional Imports
These imports are only needed under specific conditions:
from CDocs.models.review import ReviewCycle
Condition: only when document status is STATUS_IN_REVIEW
Required (conditional)from CDocs.db import db_operations
Condition: only when document status is STATUS_IN_REVIEW or STATUS_IN_APPROVAL
Required (conditional)from CDocs.models.approval import ApprovalCycle
Condition: only when document status is STATUS_IN_APPROVAL
Required (conditional)from CDocs.controllers.document_controller import get_document_edit_url
Condition: only when user is the document owner
Required (conditional)Usage Example
from CDocs.models.document import DocumentVersion
from typing import Dict, Any
import logging
logger = logging.getLogger(__name__)
# Assume document_version and user_uid are already obtained
document_version = DocumentVersion(uid='doc_version_123')
user_uid = 'user_456'
# Get access information for the user
access_info = get_user_access_url(document_version, user_uid)
if access_info['success']:
print(f"Share URL: {access_info['share_url']}")
print(f"Write Access: {access_info['write_access']}")
print(f"User Roles - Owner: {access_info['is_owner']}, Author: {access_info['is_author']}")
print(f"User Roles - Reviewer: {access_info['is_reviewer']}, Approver: {access_info['is_approver']}")
print(f"Document Status: {access_info['current_status']}")
# Use the share URL to redirect user or display access link
if access_info['write_access']:
print("User can edit the document")
else:
print("User has read-only access")
else:
print(f"Error: {access_info['message']}")
Best Practices
- This function is read-only and does not modify permissions or database state - use it for access checks only
- Ensure the DocumentVersion object is fully loaded with its document relationship before calling this function
- The function handles multiple edge cases: sequential vs parallel reviews, completed review cycles, and multiple approval cycles
- Write access logic is status-dependent: DRAFT (owner/author), IN_REVIEW (active reviewers or owner/author if no active reviews), IN_APPROVAL (no write access)
- For sequential reviews, only the reviewer with 'ACTIVE' status gets write access; for parallel reviews, both 'ACTIVE' and 'PENDING' reviewers get access
- Always check the 'success' field in the returned dictionary before accessing other fields
- The function uses direct Cypher queries for performance when checking review and approval cycles
- Owners receive special treatment with a different URL generation method (get_document_edit_url)
- Consider caching the result if calling this function multiple times for the same user and document version within a short time period
- The function logs warnings and errors - ensure logging is properly configured to capture these messages for debugging
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function manage_user_share_access 77.5% similar
-
function manage_user_share_access_v2 76.9% similar
-
function get_document_access 75.8% similar
-
function manage_user_share_access_v1 75.3% similar
-
function remove_user_access 70.0% similar