permissions.py
Source: src/sunholo/tools/permissions.py
Functions
permitted_tools(current_user: 'Optional[Dict[str, str]]', requested_tools: 'List[str]', permission_rules: 'List[Dict[str, Any]] | None' = None, default_permissions: 'Dict[str, Any] | None' = None, tool_configs: 'Optional[Dict[str, Any]]' = None, vac_name: 'str' = '', use_cache: 'bool' = True) -> 'Tuple[List[str], Dict[str, Any]]'
Validate and filter tools based on user permissions.
Performs security-critical permission validation:
- Checks user email against permission rules
- Filters requested tools to allowed set
- Merges user-provided configs with defaults
- Handles wildcard ("*") config values
Args: current_user: Dict with "email" key, or None for anonymous. requested_tools: List of tool IDs the user wants to use. permission_rules: List of permission rule dicts, each with:
- "email" or "domain": Pattern to match
- "tools": List of allowed tool IDs
- "toolConfigs": Dict of tool-specific configs default_permissions: Default permissions dict with:
- "tools": Default allowed tools
- "toolConfigs": Default tool configs tool_configs: User-provided tool configs (overrides defaults where wildcard "*" values are present). vac_name: VAC name for cache key generation. use_cache: Whether to use permission cache.
Returns: Tuple of (filtered_tools, merged_configs).
- filtered_tools: Only tools the user is allowed to use
- merged_configs: Tool configs with user values applied to wildcards
batch_permitted_tools(requests: 'List[Tuple[Optional[Dict[str, str]], List[str], Optional[Dict], str]]', permission_rules: 'List[Dict[str, Any]] | None' = None, default_permissions: 'Dict[str, Any] | None' = None) -> 'List[Tuple[List[str], Dict[str, Any]]]'
Process multiple permission requests efficiently.
Groups requests to minimize redundant config loading.
Args: requests: List of (current_user, requested_tools, tool_configs, vac_name). permission_rules: Shared permission rules. default_permissions: Shared default permissions.
Returns: List of (filtered_tools, merged_configs) results.
check_tag_permissions(user_email: 'Optional[str]', tags: 'List[str]', tag_permissions: 'Dict[str, Dict[str, Any]]', owner_email: 'str' = '') -> 'bool'
Check if a user has permission to access resources with given tags.
Access types:
- "public": Anyone can access
- "private": Only owner
- "domain": Same domain as owner
- "domains": User's domain in allowed list
- "specific": User's email in allowed list
- "group": User in allowed groups
Args: user_email: User's email (None for anonymous). tags: Tags on the resource. tag_permissions: Permission rules per tag. owner_email: Resource owner's email.
Returns: True if user has access to at least one tag.
create_permission_cache_key(user_email: 'str', tools: 'List[str]', config: 'Dict[str, Any] | None' = None, vac_name: 'str' = '') -> 'str'
Create a stable cache key from permission request parameters.
Args: user_email: User email. tools: Requested tools list. config: Tool configs dict. vac_name: VAC name.
Returns: MD5 hash string.
extract_domain(email: 'str') -> 'Optional[str]'
Extract domain from an email address.
Args: email: Email address string.
Returns: Lowercase domain, or None if invalid.
filter_tools_by_tags(user_email: 'Optional[str]', requested_tools: 'List[str]', tool_tags: 'Dict[str, List[str]]', tag_permissions: 'Dict[str, Dict[str, Any]]', owner_email: 'str' = '') -> 'List[str]'
Filter tools based on tag-level permissions.
Args: user_email: User's email. requested_tools: Tools the user wants to use. tool_tags: Mapping of tool_id -> list of tags. tag_permissions: Permission rules per tag. owner_email: Owner email for private/domain checks.
Returns: Filtered list of tools the user can access.
Classes
PermissionCache
Time-based cache for permission results.
Prevents redundant permission computation for repeated requests within the TTL window.
Args: max_size: Maximum cache entries. ttl_seconds: Time-to-live for cache entries in seconds.
-
init(self, max_size: 'int' = 500, ttl_seconds: 'int' = 300)
- Initialize self. See help(type(self)) for accurate signature.
-
_cleanup_expired(self) -> 'None'
- No docstring available.
-
_is_expired(self, key: 'str') -> 'bool'
- No docstring available.
-
clear(self) -> 'None'
- Clear all cached values.
-
get(self, key: 'str') -> 'Any'
- Get cached value, or None if expired/missing.
-
set(self, key: 'str', value: 'Any') -> 'None'
- Set a cached value with current timestamp.
