218 lines
6.5 KiB
Python
218 lines
6.5 KiB
Python
import functools
|
|
import inspect
|
|
from collections import OrderedDict
|
|
from collections.abc import Callable
|
|
from typing import Any, Literal
|
|
|
|
import jingrow
|
|
from jingrow import TYPE_CHECKING, _
|
|
from jingrow.model.document import Document
|
|
from jingrow.query_builder.functions import Count
|
|
from jingrow.query_builder.terms import QueryBuilder
|
|
|
|
if TYPE_CHECKING:
|
|
from jcloude.jcloude.doctype.team.team import Team
|
|
|
|
from jcloude.utils import get_current_team
|
|
from jcloude.utils import user as utils_user
|
|
|
|
from .action import action_key
|
|
from .api import api_key
|
|
from .document import check as document_check
|
|
from .marketplace import check as marketplace_check
|
|
from .server_snapshot import check as server_snapshot_check
|
|
from .site_backup import check as site_backup_check
|
|
from .webhook import check as webhook_check
|
|
|
|
|
|
def api(scope: Literal["billing", "partner"]):
|
|
def wrapper(fn):
|
|
@functools.wraps(fn)
|
|
def inner(*args, **kwargs):
|
|
if (not roles_enabled()) or utils_user.is_system_manager():
|
|
return fn(*args, **kwargs)
|
|
key = api_key(scope)
|
|
if not key:
|
|
return fn(*args, **kwargs)
|
|
team: Team = get_current_team(get_pg=True)
|
|
if team.is_team_owner() or team.is_admin_user():
|
|
return fn(*args, **kwargs)
|
|
PressRole = jingrow.qb.DocType("Jcloude Role")
|
|
PressRoleUser = jingrow.qb.DocType("Jcloude Role User")
|
|
has_permission = (
|
|
jingrow.qb.from_(PressRole)
|
|
.inner_join(PressRoleUser)
|
|
.on(PressRoleUser.parent == PressRole.name)
|
|
.select(Count(PressRole.name).as_("count"))
|
|
.where(PressRole.team == team.name)
|
|
.where(PressRole[key] == 1)
|
|
.where(PressRoleUser.user == jingrow.session.user)
|
|
.run(as_dict=True)
|
|
.pop()
|
|
.get("count")
|
|
> 0
|
|
)
|
|
if not has_permission:
|
|
error_message = _("You do not have permission to perform the action.")
|
|
jingrow.throw(error_message, jingrow.PermissionError)
|
|
return fn(*args, **kwargs)
|
|
|
|
return inner
|
|
|
|
return wrapper
|
|
|
|
|
|
def action():
|
|
def wrapper(fn):
|
|
@functools.wraps(fn)
|
|
def inner(self: Document, *args, **kwargs):
|
|
if (not roles_enabled()) or utils_user.is_system_manager():
|
|
return fn(self, *args, **kwargs)
|
|
key = action_key(self)
|
|
if not key:
|
|
return fn(self, *args, **kwargs)
|
|
team: Team = get_current_team(get_pg=True)
|
|
if team.is_team_owner() or team.is_admin_user():
|
|
return fn(self, *args, **kwargs)
|
|
PressRole = jingrow.qb.DocType("Jcloude Role")
|
|
PressRoleUser = jingrow.qb.DocType("Jcloude Role User")
|
|
has_permission = (
|
|
jingrow.qb.from_(PressRole)
|
|
.inner_join(PressRoleUser)
|
|
.on(PressRoleUser.parent == PressRole.name)
|
|
.select(Count(PressRole.name).as_("count"))
|
|
.where(PressRole.team == team.name)
|
|
.where(PressRole[key] == 1)
|
|
.where(PressRoleUser.user == jingrow.session.user)
|
|
.run(as_dict=True)
|
|
.pop()
|
|
.get("count")
|
|
> 0
|
|
)
|
|
if not has_permission:
|
|
error_message = _("You do not have permission to perform the action.")
|
|
jingrow.throw(error_message, jingrow.PermissionError)
|
|
return fn(self, *args, **kwargs)
|
|
|
|
return inner
|
|
|
|
return wrapper
|
|
|
|
|
|
def document(
|
|
document_type: Callable[[OrderedDict], str],
|
|
document_name: Callable[[OrderedDict], str] = lambda _: "",
|
|
default_value: Callable[[OrderedDict], Any] | None = None,
|
|
should_throw: bool = True,
|
|
inject_values: bool = False,
|
|
injection_key: str | None = None,
|
|
):
|
|
"""
|
|
Check if the user has permission to access a specific document type and
|
|
name. This decorator can inject the result into the decorated function's
|
|
kwargs.
|
|
|
|
```python
|
|
@role_guard.document(
|
|
document_type=lambda _: "Release Group",
|
|
inject_values=True,
|
|
should_throw=False,
|
|
)
|
|
def example_function(release_groups: list[str]):
|
|
pass
|
|
```
|
|
|
|
:param document_type: Document type extractor function
|
|
:param document_name: Document name extractor function
|
|
:param default_value: Return a default value if permission check fails
|
|
:param should_throw: Whether to throw an error if permission check fails
|
|
:param inject_values: Whether to inject the result into the decorated function's kwargs
|
|
:param injection_key: Custom key for injected values in kwargs
|
|
"""
|
|
|
|
def wrapper(fn):
|
|
def gen_key(document_type: str) -> str:
|
|
return injection_key or document_type.lower().replace(" ", "_") + "s"
|
|
|
|
@functools.wraps(fn)
|
|
def inner(*args, **kwargs):
|
|
bound_args = inspect.signature(fn).bind(*args, **kwargs)
|
|
bound_args.apply_defaults()
|
|
t = document_type(bound_args.arguments)
|
|
n = document_name(bound_args.arguments)
|
|
r = (not roles_enabled()) or utils_user.is_system_manager() or check(t, n)
|
|
if not r and default_value:
|
|
return default_value(bound_args.arguments)
|
|
if not r and should_throw:
|
|
error_message = _("You do not have permission to access this {0}.").format(t)
|
|
jingrow.throw(error_message, jingrow.PermissionError)
|
|
if inject_values:
|
|
kwargs[gen_key(t)] = r
|
|
return fn(*args, **kwargs)
|
|
|
|
return inner
|
|
|
|
return wrapper
|
|
|
|
|
|
def base_query() -> QueryBuilder:
|
|
"""
|
|
Get a base query for Jcloude Role documents based on the current team context.
|
|
"""
|
|
PressRole = jingrow.qb.DocType("Jcloude Role")
|
|
PressRoleUser = jingrow.qb.DocType("Jcloude Role User")
|
|
return (
|
|
jingrow.qb.from_(PressRole)
|
|
.select(PressRole.name)
|
|
.left_join(PressRoleUser)
|
|
.on(PressRoleUser.parent == PressRole.name)
|
|
.where(PressRole.team == get_current_team())
|
|
.where(PressRoleUser.user == jingrow.session.user)
|
|
)
|
|
|
|
|
|
def check(document_type: str, document_name: str) -> bool | list[str]:
|
|
"""
|
|
Check if the user has permission to access a specific document type and name.
|
|
"""
|
|
team: Team = get_current_team(get_pg=True)
|
|
if team.is_team_owner() or team.is_admin_user():
|
|
return True
|
|
query = base_query()
|
|
match document_type:
|
|
case "Marketplace App":
|
|
return marketplace_check(query)
|
|
case "Jcloude Webhook":
|
|
return webhook_check(query)
|
|
case "Jcloude Webhook Attempt":
|
|
return webhook_check(query)
|
|
case "Jcloude Webhook Log":
|
|
return webhook_check(query)
|
|
case "Release Group":
|
|
return document_check(query, document_type, document_name)
|
|
case "Server":
|
|
return document_check(query, document_type, document_name)
|
|
case "Server Snapshot":
|
|
return server_snapshot_check(query, document_name)
|
|
case "Site":
|
|
return document_check(query, document_type, document_name)
|
|
case "Site Backup":
|
|
return site_backup_check(query, document_name)
|
|
case _:
|
|
return True
|
|
|
|
|
|
def roles_enabled() -> bool:
|
|
"""
|
|
Check if role-based access control is enabled for the current team. This is
|
|
done by checking if any roles exist for the team.
|
|
"""
|
|
return bool(
|
|
jingrow.db.exists(
|
|
{
|
|
"doctype": "Jcloude Role",
|
|
"team": get_current_team(),
|
|
}
|
|
)
|
|
)
|