85 lines
2.2 KiB
Python
85 lines
2.2 KiB
Python
"""Board sharing functionality."""
|
|
|
|
import secrets
|
|
import string
|
|
from datetime import datetime, timezone
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.database.models.share_link import ShareLink
|
|
|
|
|
|
def generate_secure_token(length: int = 64) -> str:
|
|
"""
|
|
Generate a cryptographically secure random token for share links.
|
|
|
|
Args:
|
|
length: Length of the token (default 64 characters)
|
|
|
|
Returns:
|
|
URL-safe random string
|
|
"""
|
|
# Use URL-safe characters (alphanumeric + - and _)
|
|
alphabet = string.ascii_letters + string.digits + "-_"
|
|
return "".join(secrets.choice(alphabet) for _ in range(length))
|
|
|
|
|
|
def validate_share_link_token(token: str, db: Session) -> ShareLink | None:
|
|
"""
|
|
Validate a share link token and return the share link if valid.
|
|
|
|
A share link is valid if:
|
|
- Token exists
|
|
- Not revoked
|
|
- Not expired (if expires_at is set)
|
|
|
|
Args:
|
|
token: The share link token
|
|
db: Database session
|
|
|
|
Returns:
|
|
ShareLink if valid, None otherwise
|
|
"""
|
|
share_link = (
|
|
db.query(ShareLink)
|
|
.filter(
|
|
ShareLink.token == token,
|
|
ShareLink.is_revoked == False, # noqa: E712
|
|
)
|
|
.first()
|
|
)
|
|
|
|
if share_link is None:
|
|
return None
|
|
|
|
# Check expiration
|
|
if share_link.expires_at and share_link.expires_at < datetime.now(timezone.utc):
|
|
return None
|
|
|
|
# Update access tracking
|
|
share_link.access_count += 1
|
|
share_link.last_accessed_at = datetime.now(timezone.utc)
|
|
db.commit()
|
|
|
|
return share_link
|
|
|
|
|
|
def check_permission(share_link: ShareLink, required_permission: str) -> bool:
|
|
"""
|
|
Check if a share link has the required permission level.
|
|
|
|
Args:
|
|
share_link: The share link to check
|
|
required_permission: Required permission level ('view-only' or 'view-comment')
|
|
|
|
Returns:
|
|
True if permission granted, False otherwise
|
|
"""
|
|
if required_permission == "view-only":
|
|
# Both view-only and view-comment can view
|
|
return share_link.permission_level in ("view-only", "view-comment")
|
|
elif required_permission == "view-comment":
|
|
# Only view-comment can comment
|
|
return share_link.permission_level == "view-comment"
|
|
return False
|