init
This commit is contained in:
@@ -43,6 +43,8 @@ class ExplicitRelease(BaseModel):
|
|||||||
details_link: str | None
|
details_link: str | None
|
||||||
paid_features: str | None
|
paid_features: str | None
|
||||||
pay_link: str | None
|
pay_link: str | None
|
||||||
|
admin_only: bool = False
|
||||||
|
super_user_only: bool = False
|
||||||
|
|
||||||
def is_version_compatible(self):
|
def is_version_compatible(self):
|
||||||
return is_lnbits_version_ok(self.min_lnbits_version, self.max_lnbits_version)
|
return is_lnbits_version_ok(self.min_lnbits_version, self.max_lnbits_version)
|
||||||
@@ -83,6 +85,8 @@ class ExtensionConfig(BaseModel):
|
|||||||
warning: str | None = ""
|
warning: str | None = ""
|
||||||
min_lnbits_version: str | None
|
min_lnbits_version: str | None
|
||||||
max_lnbits_version: str | None
|
max_lnbits_version: str | None
|
||||||
|
admin_only: bool = False
|
||||||
|
super_user_only: bool = False
|
||||||
|
|
||||||
def is_version_compatible(self) -> bool:
|
def is_version_compatible(self) -> bool:
|
||||||
return is_lnbits_version_ok(self.min_lnbits_version, self.max_lnbits_version)
|
return is_lnbits_version_ok(self.min_lnbits_version, self.max_lnbits_version)
|
||||||
@@ -195,6 +199,8 @@ class ExtensionRelease(BaseModel):
|
|||||||
cost_sats: int | None = None
|
cost_sats: int | None = None
|
||||||
paid_sats: int | None = 0
|
paid_sats: int | None = 0
|
||||||
payment_hash: str | None = None
|
payment_hash: str | None = None
|
||||||
|
admin_only: bool = False
|
||||||
|
super_user_only: bool = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def archive_url(self) -> str:
|
def archive_url(self) -> str:
|
||||||
@@ -263,6 +269,8 @@ class ExtensionRelease(BaseModel):
|
|||||||
paid_features=e.paid_features,
|
paid_features=e.paid_features,
|
||||||
repo=e.repo,
|
repo=e.repo,
|
||||||
icon=e.icon,
|
icon=e.icon,
|
||||||
|
admin_only=e.admin_only,
|
||||||
|
super_user_only=e.super_user_only,
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -289,6 +297,8 @@ class ExtensionRelease(BaseModel):
|
|||||||
release.min_lnbits_version = config.min_lnbits_version
|
release.min_lnbits_version = config.min_lnbits_version
|
||||||
release.max_lnbits_version = config.max_lnbits_version
|
release.max_lnbits_version = config.max_lnbits_version
|
||||||
release.is_version_compatible = config.is_version_compatible()
|
release.is_version_compatible = config.is_version_compatible()
|
||||||
|
release.admin_only = config.admin_only
|
||||||
|
release.super_user_only = config.super_user_only
|
||||||
|
|
||||||
release.icon = icon_to_github_url(f"{org}/{repo}", config.tile)
|
release.icon = icon_to_github_url(f"{org}/{repo}", config.tile)
|
||||||
|
|
||||||
@@ -336,6 +346,8 @@ class ExtensionMeta(BaseModel):
|
|||||||
paid_features: str | None = None
|
paid_features: str | None = None
|
||||||
has_paid_release: bool = False
|
has_paid_release: bool = False
|
||||||
has_free_release: bool = False
|
has_free_release: bool = False
|
||||||
|
admin_only: bool = False
|
||||||
|
super_user_only: bool = False
|
||||||
|
|
||||||
|
|
||||||
class InstallableExtension(BaseModel):
|
class InstallableExtension(BaseModel):
|
||||||
@@ -399,6 +411,16 @@ class InstallableExtension(BaseModel):
|
|||||||
return False
|
return False
|
||||||
return self.meta.pay_to_enable.required is True
|
return self.meta.pay_to_enable.required is True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_admin_only(self) -> bool:
|
||||||
|
return settings.is_admin_extension(self.id) or bool(
|
||||||
|
self.meta and self.meta.admin_only
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_super_user_only(self) -> bool:
|
||||||
|
return bool(self.meta and self.meta.super_user_only)
|
||||||
|
|
||||||
async def download_archive(self):
|
async def download_archive(self):
|
||||||
logger.info(f"Downloading extension {self.name} ({self.installed_version}).")
|
logger.info(f"Downloading extension {self.name} ({self.installed_version}).")
|
||||||
ext_zip_file = self.zip_path
|
ext_zip_file = self.zip_path
|
||||||
@@ -454,6 +476,16 @@ class InstallableExtension(BaseModel):
|
|||||||
|
|
||||||
self.name = config_json.get("name")
|
self.name = config_json.get("name")
|
||||||
self.short_description = config_json.get("short_description")
|
self.short_description = config_json.get("short_description")
|
||||||
|
if self.meta:
|
||||||
|
self.meta.admin_only = config_json.get("admin_only", False)
|
||||||
|
self.meta.super_user_only = config_json.get("super_user_only", False)
|
||||||
|
if self.meta.installed_release:
|
||||||
|
self.meta.installed_release.admin_only = config_json.get(
|
||||||
|
"admin_only", False
|
||||||
|
)
|
||||||
|
self.meta.installed_release.super_user_only = config_json.get(
|
||||||
|
"super_user_only", False
|
||||||
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
self.meta
|
self.meta
|
||||||
@@ -496,6 +528,10 @@ class InstallableExtension(BaseModel):
|
|||||||
return
|
return
|
||||||
if not release.is_version_compatible:
|
if not release.is_version_compatible:
|
||||||
return
|
return
|
||||||
|
if not self.meta:
|
||||||
|
self.meta = ExtensionMeta()
|
||||||
|
self.meta.admin_only = release.admin_only
|
||||||
|
self.meta.super_user_only = release.super_user_only
|
||||||
if not self.meta or not self.meta.latest_release:
|
if not self.meta or not self.meta.latest_release:
|
||||||
meta = self.meta or ExtensionMeta()
|
meta = self.meta or ExtensionMeta()
|
||||||
meta.latest_release = release
|
meta.latest_release = release
|
||||||
@@ -569,6 +605,8 @@ class InstallableExtension(BaseModel):
|
|||||||
config.tile,
|
config.tile,
|
||||||
),
|
),
|
||||||
meta=ExtensionMeta(
|
meta=ExtensionMeta(
|
||||||
|
admin_only=config.admin_only,
|
||||||
|
super_user_only=config.super_user_only,
|
||||||
latest_release=ExtensionRelease.from_github_release(
|
latest_release=ExtensionRelease.from_github_release(
|
||||||
source_repo, latest_release
|
source_repo, latest_release
|
||||||
),
|
),
|
||||||
@@ -580,7 +618,12 @@ class InstallableExtension(BaseModel):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_explicit_release(cls, e: ExplicitRelease) -> InstallableExtension:
|
def from_explicit_release(cls, e: ExplicitRelease) -> InstallableExtension:
|
||||||
meta = ExtensionMeta(archive=e.archive, dependencies=e.dependencies)
|
meta = ExtensionMeta(
|
||||||
|
archive=e.archive,
|
||||||
|
dependencies=e.dependencies,
|
||||||
|
admin_only=e.admin_only,
|
||||||
|
super_user_only=e.super_user_only,
|
||||||
|
)
|
||||||
return InstallableExtension(
|
return InstallableExtension(
|
||||||
id=e.id,
|
id=e.id,
|
||||||
name=e.name,
|
name=e.name,
|
||||||
@@ -610,6 +653,8 @@ class InstallableExtension(BaseModel):
|
|||||||
short_description=config_json.get("short_description"),
|
short_description=config_json.get("short_description"),
|
||||||
icon=config_json.get("tile"),
|
icon=config_json.get("tile"),
|
||||||
meta=ExtensionMeta(
|
meta=ExtensionMeta(
|
||||||
|
admin_only=config_json.get("admin_only", False),
|
||||||
|
super_user_only=config_json.get("super_user_only", False),
|
||||||
installed_release=ExtensionRelease(
|
installed_release=ExtensionRelease(
|
||||||
name=ext_id,
|
name=ext_id,
|
||||||
version=version,
|
version=version,
|
||||||
@@ -617,6 +662,10 @@ class InstallableExtension(BaseModel):
|
|||||||
source_repo=f"{conf_path}",
|
source_repo=f"{conf_path}",
|
||||||
min_lnbits_version=config_json.get("min_lnbits_version"),
|
min_lnbits_version=config_json.get("min_lnbits_version"),
|
||||||
max_lnbits_version=config_json.get("max_lnbits_version"),
|
max_lnbits_version=config_json.get("max_lnbits_version"),
|
||||||
|
admin_only=config_json.get("admin_only", False),
|
||||||
|
super_user_only=config_json.get(
|
||||||
|
"super_user_only", False
|
||||||
|
),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -79,7 +79,11 @@ async def api_install_extension(data: CreateExtension):
|
|||||||
raise HTTPException(HTTPStatus.BAD_REQUEST, "Incompatible extension version.")
|
raise HTTPException(HTTPStatus.BAD_REQUEST, "Incompatible extension version.")
|
||||||
|
|
||||||
release.payment_hash = data.payment_hash
|
release.payment_hash = data.payment_hash
|
||||||
ext_meta = ExtensionMeta(installed_release=release)
|
ext_meta = ExtensionMeta(
|
||||||
|
installed_release=release,
|
||||||
|
admin_only=release.admin_only,
|
||||||
|
super_user_only=release.super_user_only,
|
||||||
|
)
|
||||||
ext_info = InstallableExtension(
|
ext_info = InstallableExtension(
|
||||||
id=data.ext_id,
|
id=data.ext_id,
|
||||||
name=data.ext_id,
|
name=data.ext_id,
|
||||||
@@ -191,6 +195,14 @@ async def api_enable_extension(
|
|||||||
raise ValueError(f"Extension '{ext_id}' is not installed.")
|
raise ValueError(f"Extension '{ext_id}' is not installed.")
|
||||||
if not ext.active:
|
if not ext.active:
|
||||||
raise ValueError(f"Extension '{ext_id}' is not activated.")
|
raise ValueError(f"Extension '{ext_id}' is not activated.")
|
||||||
|
if ext.is_super_user_only and not settings.is_super_user(account_id.id):
|
||||||
|
raise HTTPException(
|
||||||
|
HTTPStatus.FORBIDDEN, f"User not authorized for extension '{ext_id}'."
|
||||||
|
)
|
||||||
|
if ext.is_admin_only and not settings.is_admin_user(account_id.id):
|
||||||
|
raise HTTPException(
|
||||||
|
HTTPStatus.FORBIDDEN, f"User not authorized for extension '{ext_id}'."
|
||||||
|
)
|
||||||
|
|
||||||
user_ext = await get_user_extension(account_id.id, ext_id)
|
user_ext = await get_user_extension(account_id.id, ext_id)
|
||||||
if not user_ext:
|
if not user_ext:
|
||||||
@@ -464,6 +476,8 @@ async def get_extension_release(org: str, repo: str, tag_name: str):
|
|||||||
"min_lnbits_version": config.min_lnbits_version,
|
"min_lnbits_version": config.min_lnbits_version,
|
||||||
"is_version_compatible": config.is_version_compatible(),
|
"is_version_compatible": config.is_version_compatible(),
|
||||||
"warning": config.warning,
|
"warning": config.warning,
|
||||||
|
"admin_only": config.admin_only,
|
||||||
|
"super_user_only": config.super_user_only,
|
||||||
}
|
}
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -573,7 +587,8 @@ async def extensions(account_id: AccountId = Depends(check_account_id_exists)):
|
|||||||
(True for version in db_versions if version.db == ext.id), False
|
(True for version in db_versions if version.db == ext.id), False
|
||||||
),
|
),
|
||||||
"isAvailable": ext.id in all_ext_ids,
|
"isAvailable": ext.id in all_ext_ids,
|
||||||
"isAdminOnly": ext.id in settings.lnbits_admin_extensions,
|
"isAdminOnly": ext.is_admin_only,
|
||||||
|
"isSuperUserOnly": ext.is_super_user_only,
|
||||||
"isActive": ext.id not in inactive_extensions,
|
"isActive": ext.id not in inactive_extensions,
|
||||||
"latestRelease": (
|
"latestRelease": (
|
||||||
dict(ext.meta.latest_release)
|
dict(ext.meta.latest_release)
|
||||||
|
|||||||
+14
-1
@@ -14,6 +14,7 @@ from lnbits.core.crud import (
|
|||||||
get_account,
|
get_account,
|
||||||
get_account_by_email,
|
get_account_by_email,
|
||||||
get_account_by_username,
|
get_account_by_username,
|
||||||
|
get_installed_extension,
|
||||||
get_user_active_extensions_ids,
|
get_user_active_extensions_ids,
|
||||||
get_user_from_account,
|
get_user_from_account,
|
||||||
get_wallet_for_key,
|
get_wallet_for_key,
|
||||||
@@ -424,7 +425,19 @@ async def check_user_extension_access(
|
|||||||
Check if the user has access to a particular extension.
|
Check if the user has access to a particular extension.
|
||||||
Raises HTTP Forbidden if the user is not allowed.
|
Raises HTTP Forbidden if the user is not allowed.
|
||||||
"""
|
"""
|
||||||
if settings.is_admin_extension(ext_id) and not settings.is_admin_user(user_id):
|
ext = await get_installed_extension(ext_id, conn=conn)
|
||||||
|
is_admin_only = settings.is_admin_extension(ext_id) or bool(
|
||||||
|
ext and ext.meta and ext.meta.admin_only
|
||||||
|
)
|
||||||
|
is_super_user_only = bool(ext and ext.meta and ext.meta.super_user_only)
|
||||||
|
|
||||||
|
if is_super_user_only and not settings.is_super_user(user_id):
|
||||||
|
return SimpleStatus(
|
||||||
|
success=False,
|
||||||
|
message=f"User not authorized for extension '{ext_id}'.",
|
||||||
|
)
|
||||||
|
|
||||||
|
if is_admin_only and not settings.is_admin_user(user_id):
|
||||||
return SimpleStatus(
|
return SimpleStatus(
|
||||||
success=False, message=f"User not authorized for extension '{ext_id}'."
|
success=False, message=f"User not authorized for extension '{ext_id}'."
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -307,7 +307,13 @@
|
|||||||
:label="$t('disable')"
|
:label="$t('disable')"
|
||||||
></q-btn>
|
></q-btn>
|
||||||
<q-badge
|
<q-badge
|
||||||
v-if="extension.isAdminOnly && !g.user.admin"
|
v-if="extension.isSuperUserOnly && !g.user.super_user"
|
||||||
|
>
|
||||||
|
Super User Only
|
||||||
|
</q-badge>
|
||||||
|
|
||||||
|
<q-badge
|
||||||
|
v-else-if="extension.isAdminOnly && !g.user.admin"
|
||||||
v-text="$t('admin_only')"
|
v-text="$t('admin_only')"
|
||||||
>
|
>
|
||||||
</q-badge>
|
</q-badge>
|
||||||
@@ -316,7 +322,9 @@
|
|||||||
v-else-if="
|
v-else-if="
|
||||||
extension.isInstalled &&
|
extension.isInstalled &&
|
||||||
extension.isActive &&
|
extension.isActive &&
|
||||||
!g.user.extensions.includes(extension.id)
|
!g.user.extensions.includes(extension.id) &&
|
||||||
|
(!extension.isAdminOnly || g.user.admin) &&
|
||||||
|
(!extension.isSuperUserOnly || g.user.super_user)
|
||||||
"
|
"
|
||||||
flat
|
flat
|
||||||
color="primary"
|
color="primary"
|
||||||
|
|||||||
@@ -152,6 +152,8 @@ async def test_extension_api_install_details_and_release_endpoints(mocker):
|
|||||||
short_description="Config",
|
short_description="Config",
|
||||||
min_lnbits_version="0.1.0",
|
min_lnbits_version="0.1.0",
|
||||||
max_lnbits_version=None,
|
max_lnbits_version=None,
|
||||||
|
admin_only=True,
|
||||||
|
super_user_only=True,
|
||||||
)
|
)
|
||||||
mocker.patch.object(
|
mocker.patch.object(
|
||||||
ExtensionConfig,
|
ExtensionConfig,
|
||||||
@@ -160,6 +162,8 @@ async def test_extension_api_install_details_and_release_endpoints(mocker):
|
|||||||
)
|
)
|
||||||
release_info = await get_extension_release("org", ext_id, "v1.0.0")
|
release_info = await get_extension_release("org", ext_id, "v1.0.0")
|
||||||
assert release_info["is_version_compatible"] is True
|
assert release_info["is_version_compatible"] is True
|
||||||
|
assert release_info["admin_only"] is True
|
||||||
|
assert release_info["super_user_only"] is True
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.anyio
|
@pytest.mark.anyio
|
||||||
@@ -258,6 +262,8 @@ async def test_extension_api_pay_to_enable_and_catalog_views(mocker, admin_user)
|
|||||||
catalog_entry = make_installable_extension(
|
catalog_entry = make_installable_extension(
|
||||||
ext_id,
|
ext_id,
|
||||||
pay_to_enable=PayToEnableInfo(required=True, amount=21, wallet=admin_wallet.id),
|
pay_to_enable=PayToEnableInfo(required=True, amount=21, wallet=admin_wallet.id),
|
||||||
|
admin_only=True,
|
||||||
|
super_user_only=True,
|
||||||
)
|
)
|
||||||
mocker.patch.object(
|
mocker.patch.object(
|
||||||
InstallableExtension,
|
InstallableExtension,
|
||||||
@@ -267,6 +273,8 @@ async def test_extension_api_pay_to_enable_and_catalog_views(mocker, admin_user)
|
|||||||
catalog = await extensions(AccountId(id=regular_user.id))
|
catalog = await extensions(AccountId(id=regular_user.id))
|
||||||
catalog_item = next(item for item in catalog if item["id"] == ext_id)
|
catalog_item = next(item for item in catalog if item["id"] == ext_id)
|
||||||
assert catalog_item["payToEnable"]["wallet"] is None
|
assert catalog_item["payToEnable"]["wallet"] is None
|
||||||
|
assert catalog_item["isAdminOnly"] is True
|
||||||
|
assert catalog_item["isSuperUserOnly"] is True
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.anyio
|
@pytest.mark.anyio
|
||||||
@@ -428,3 +436,31 @@ async def test_extension_api_review_endpoints(mocker):
|
|||||||
CreateExtensionReview(tag=ext_id, name="Alice", rating=900, comment="Great")
|
CreateExtensionReview(tag=ext_id, name="Alice", rating=900, comment="Great")
|
||||||
)
|
)
|
||||||
assert payment_request.payment_hash.startswith("hash_")
|
assert payment_request.payment_hash.startswith("hash_")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.anyio
|
||||||
|
async def test_extension_api_enable_rejects_admin_and_super_only_extensions(
|
||||||
|
admin_user,
|
||||||
|
):
|
||||||
|
regular_user = await create_user_account(
|
||||||
|
Account(
|
||||||
|
id=uuid4().hex,
|
||||||
|
username=f"user_{uuid4().hex[:8]}",
|
||||||
|
email=f"user_{uuid4().hex[:8]}@lnbits.com",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
admin_only_ext = f"admin_{uuid4().hex[:8]}"
|
||||||
|
super_only_ext = f"super_{uuid4().hex[:8]}"
|
||||||
|
|
||||||
|
await create_installed_extension(
|
||||||
|
make_installable_extension(admin_only_ext, admin_only=True)
|
||||||
|
)
|
||||||
|
await create_installed_extension(
|
||||||
|
make_installable_extension(super_only_ext, super_user_only=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(HTTPException, match="User not authorized"):
|
||||||
|
await api_enable_extension(admin_only_ext, AccountId(id=regular_user.id))
|
||||||
|
|
||||||
|
with pytest.raises(HTTPException, match="User not authorized"):
|
||||||
|
await api_enable_extension(super_only_ext, AccountId(id=admin_user.id))
|
||||||
|
|||||||
@@ -141,9 +141,13 @@ def make_installable_extension(
|
|||||||
pay_to_enable: PayToEnableInfo | None = None,
|
pay_to_enable: PayToEnableInfo | None = None,
|
||||||
dependencies: list[str] | None = None,
|
dependencies: list[str] | None = None,
|
||||||
payments: list[ReleasePaymentInfo] | None = None,
|
payments: list[ReleasePaymentInfo] | None = None,
|
||||||
|
admin_only: bool = False,
|
||||||
|
super_user_only: bool = False,
|
||||||
) -> InstallableExtension:
|
) -> InstallableExtension:
|
||||||
release = make_extension_release(ext_id, version)
|
release = make_extension_release(ext_id, version)
|
||||||
release.is_version_compatible = compatible
|
release.is_version_compatible = compatible
|
||||||
|
release.admin_only = admin_only
|
||||||
|
release.super_user_only = super_user_only
|
||||||
return InstallableExtension(
|
return InstallableExtension(
|
||||||
id=ext_id,
|
id=ext_id,
|
||||||
name=f"Extension {ext_id}",
|
name=f"Extension {ext_id}",
|
||||||
@@ -156,6 +160,8 @@ def make_installable_extension(
|
|||||||
pay_to_enable=pay_to_enable,
|
pay_to_enable=pay_to_enable,
|
||||||
dependencies=dependencies or [],
|
dependencies=dependencies or [],
|
||||||
payments=payments or [],
|
payments=payments or [],
|
||||||
|
admin_only=admin_only,
|
||||||
|
super_user_only=super_user_only,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from fastapi.exceptions import HTTPException
|
|||||||
from httpx import AsyncClient
|
from httpx import AsyncClient
|
||||||
from pydantic.types import UUID4
|
from pydantic.types import UUID4
|
||||||
|
|
||||||
|
from lnbits.core.crud.extensions import create_installed_extension
|
||||||
from lnbits.core.crud.users import delete_account
|
from lnbits.core.crud.users import delete_account
|
||||||
from lnbits.core.models import User
|
from lnbits.core.models import User
|
||||||
from lnbits.core.models.users import AccessTokenPayload
|
from lnbits.core.models.users import AccessTokenPayload
|
||||||
@@ -18,10 +19,12 @@ from lnbits.decorators import (
|
|||||||
check_extension_builder,
|
check_extension_builder,
|
||||||
check_first_install,
|
check_first_install,
|
||||||
check_user_exists,
|
check_user_exists,
|
||||||
|
check_user_extension_access,
|
||||||
optional_user_id,
|
optional_user_id,
|
||||||
)
|
)
|
||||||
from lnbits.helpers import create_access_token
|
from lnbits.helpers import create_access_token
|
||||||
from lnbits.settings import AuthMethods, Settings, settings
|
from lnbits.settings import AuthMethods, Settings, settings
|
||||||
|
from tests.helpers import make_installable_extension
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.anyio
|
@pytest.mark.anyio
|
||||||
@@ -225,3 +228,35 @@ async def test_check_extension_builder_requires_admin_when_disabled_for_users(
|
|||||||
admin_user = user_alan.copy(deep=True)
|
admin_user = user_alan.copy(deep=True)
|
||||||
admin_user.admin = True
|
admin_user.admin = True
|
||||||
await check_extension_builder(admin_user)
|
await check_extension_builder(admin_user)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.anyio
|
||||||
|
async def test_check_user_extension_access_honors_extension_metadata(
|
||||||
|
settings: Settings, user_alan: User, admin_user: User
|
||||||
|
):
|
||||||
|
admin_only_ext = f"admin_{uuid4().hex[:8]}"
|
||||||
|
super_only_ext = f"super_{uuid4().hex[:8]}"
|
||||||
|
|
||||||
|
await create_installed_extension(
|
||||||
|
make_installable_extension(admin_only_ext, admin_only=True)
|
||||||
|
)
|
||||||
|
await create_installed_extension(
|
||||||
|
make_installable_extension(super_only_ext, super_user_only=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
regular_status = await check_user_extension_access(user_alan.id, admin_only_ext)
|
||||||
|
assert regular_status.success is False
|
||||||
|
|
||||||
|
admin_status = await check_user_extension_access(admin_user.id, admin_only_ext)
|
||||||
|
assert admin_status.success is True
|
||||||
|
|
||||||
|
admin_super_status = await check_user_extension_access(admin_user.id, super_only_ext)
|
||||||
|
assert admin_super_status.success is False
|
||||||
|
|
||||||
|
previous_super_user = settings.super_user
|
||||||
|
settings.super_user = admin_user.id
|
||||||
|
try:
|
||||||
|
super_status = await check_user_extension_access(admin_user.id, super_only_ext)
|
||||||
|
assert super_status.success is True
|
||||||
|
finally:
|
||||||
|
settings.super_user = previous_super_user
|
||||||
|
|||||||
Reference in New Issue
Block a user