refac
This commit is contained in:
@@ -815,6 +815,21 @@ app.state.config.ENABLE_USER_STATUS = ENABLE_USER_STATUS
|
|||||||
app.state.config.ENABLE_EVALUATION_ARENA_MODELS = ENABLE_EVALUATION_ARENA_MODELS
|
app.state.config.ENABLE_EVALUATION_ARENA_MODELS = ENABLE_EVALUATION_ARENA_MODELS
|
||||||
app.state.config.EVALUATION_ARENA_MODELS = EVALUATION_ARENA_MODELS
|
app.state.config.EVALUATION_ARENA_MODELS = EVALUATION_ARENA_MODELS
|
||||||
|
|
||||||
|
# Migrate legacy access_control → access_grants on boot
|
||||||
|
from open_webui.utils.access_control import migrate_access_control
|
||||||
|
|
||||||
|
connections = app.state.config.TOOL_SERVER_CONNECTIONS
|
||||||
|
if any("access_control" in c.get("config", {}) for c in connections):
|
||||||
|
for connection in connections:
|
||||||
|
migrate_access_control(connection.get("config", {}))
|
||||||
|
app.state.config.TOOL_SERVER_CONNECTIONS = connections
|
||||||
|
|
||||||
|
arena_models = app.state.config.EVALUATION_ARENA_MODELS
|
||||||
|
if any("access_control" in m.get("meta", {}) for m in arena_models):
|
||||||
|
for model in arena_models:
|
||||||
|
migrate_access_control(model.get("meta", {}))
|
||||||
|
app.state.config.EVALUATION_ARENA_MODELS = arena_models
|
||||||
|
|
||||||
app.state.config.OAUTH_USERNAME_CLAIM = OAUTH_USERNAME_CLAIM
|
app.state.config.OAUTH_USERNAME_CLAIM = OAUTH_USERNAME_CLAIM
|
||||||
app.state.config.OAUTH_PICTURE_CLAIM = OAUTH_PICTURE_CLAIM
|
app.state.config.OAUTH_PICTURE_CLAIM = OAUTH_PICTURE_CLAIM
|
||||||
app.state.config.OAUTH_EMAIL_CLAIM = OAUTH_EMAIL_CLAIM
|
app.state.config.OAUTH_EMAIL_CLAIM = OAUTH_EMAIL_CLAIM
|
||||||
|
|||||||
@@ -77,12 +77,21 @@ async def get_tools(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# OpenAPI Tool Servers
|
# OpenAPI Tool Servers
|
||||||
|
server_access_grants = {}
|
||||||
for server in await get_tool_servers(request):
|
for server in await get_tool_servers(request):
|
||||||
|
connection = request.app.state.config.TOOL_SERVER_CONNECTIONS[
|
||||||
|
server.get("idx", 0)
|
||||||
|
]
|
||||||
|
server_config = connection.get("config", {})
|
||||||
|
|
||||||
|
server_id = f"server:{server.get('id')}"
|
||||||
|
server_access_grants[server_id] = server_config.get("access_grants", [])
|
||||||
|
|
||||||
tools.append(
|
tools.append(
|
||||||
ToolUserResponse(
|
ToolUserResponse(
|
||||||
**{
|
**{
|
||||||
"id": f"server:{server.get('id')}",
|
"id": server_id,
|
||||||
"user_id": f"server:{server.get('id')}",
|
"user_id": server_id,
|
||||||
"name": server.get("openapi", {})
|
"name": server.get("openapi", {})
|
||||||
.get("info", {})
|
.get("info", {})
|
||||||
.get("title", "Tool Server"),
|
.get("title", "Tool Server"),
|
||||||
@@ -91,11 +100,6 @@ async def get_tools(
|
|||||||
.get("info", {})
|
.get("info", {})
|
||||||
.get("description", ""),
|
.get("description", ""),
|
||||||
},
|
},
|
||||||
"access_control": request.app.state.config.TOOL_SERVER_CONNECTIONS[
|
|
||||||
server.get("idx", 0)
|
|
||||||
]
|
|
||||||
.get("config", {})
|
|
||||||
.get("access_control", None),
|
|
||||||
"updated_at": int(time.time()),
|
"updated_at": int(time.time()),
|
||||||
"created_at": int(time.time()),
|
"created_at": int(time.time()),
|
||||||
}
|
}
|
||||||
@@ -119,20 +123,22 @@ async def get_tools(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
server_config = server.get("config", {})
|
||||||
|
|
||||||
|
tool_id = f"server:mcp:{server.get('info', {}).get('id')}"
|
||||||
|
server_access_grants[tool_id] = server_config.get("access_grants", [])
|
||||||
|
|
||||||
tools.append(
|
tools.append(
|
||||||
ToolUserResponse(
|
ToolUserResponse(
|
||||||
**{
|
**{
|
||||||
"id": f"server:mcp:{server.get('info', {}).get('id')}",
|
"id": tool_id,
|
||||||
"user_id": f"server:mcp:{server.get('info', {}).get('id')}",
|
"user_id": tool_id,
|
||||||
"name": server.get("info", {}).get("name", "MCP Tool Server"),
|
"name": server.get("info", {}).get("name", "MCP Tool Server"),
|
||||||
"meta": {
|
"meta": {
|
||||||
"description": server.get("info", {}).get(
|
"description": server.get("info", {}).get(
|
||||||
"description", ""
|
"description", ""
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
"access_control": server.get("config", {}).get(
|
|
||||||
"access_control", None
|
|
||||||
),
|
|
||||||
"updated_at": int(time.time()),
|
"updated_at": int(time.time()),
|
||||||
"created_at": int(time.time()),
|
"created_at": int(time.time()),
|
||||||
**(
|
**(
|
||||||
@@ -161,7 +167,7 @@ async def get_tools(
|
|||||||
has_access(
|
has_access(
|
||||||
user.id,
|
user.id,
|
||||||
"read",
|
"read",
|
||||||
getattr(tool, "access_control", None),
|
server_access_grants.get(str(tool.id), []),
|
||||||
user_group_ids,
|
user_group_ids,
|
||||||
db=db,
|
db=db,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -107,71 +107,79 @@ def has_permission(
|
|||||||
return get_permission(default_permissions, permission_hierarchy)
|
return get_permission(default_permissions, permission_hierarchy)
|
||||||
|
|
||||||
|
|
||||||
def get_permitted_group_and_user_ids(
|
|
||||||
type: str = "write", access_control: Optional[dict] = None
|
|
||||||
) -> Union[Dict[str, List[str]], None]:
|
|
||||||
if access_control is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
permission_access = access_control.get(type, {})
|
|
||||||
permitted_group_ids = permission_access.get("group_ids", [])
|
|
||||||
permitted_user_ids = permission_access.get("user_ids", [])
|
|
||||||
|
|
||||||
return {
|
|
||||||
"group_ids": permitted_group_ids,
|
|
||||||
"user_ids": permitted_user_ids,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def has_access(
|
def has_access(
|
||||||
user_id: str,
|
user_id: str,
|
||||||
type: str = "write",
|
permission: str = "read",
|
||||||
access_control: Optional[dict] = None,
|
access_grants: Optional[list] = None,
|
||||||
user_group_ids: Optional[Set[str]] = None,
|
user_group_ids: Optional[Set[str]] = None,
|
||||||
strict: bool = True,
|
|
||||||
db: Optional[Any] = None,
|
db: Optional[Any] = None,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
if access_control is None:
|
"""
|
||||||
if strict:
|
Check if a user has the specified permission using an in-memory access_grants list.
|
||||||
return type == "read"
|
|
||||||
else:
|
Used for config-driven resources (arena models, tool servers) that store
|
||||||
return True
|
access control as JSON in PersistentConfig rather than in the access_grant DB table.
|
||||||
|
|
||||||
|
Semantics:
|
||||||
|
- None or [] → private (owner-only, deny all)
|
||||||
|
- [{"principal_type": "user", "principal_id": "*", "permission": "read"}] → public read
|
||||||
|
- Specific grants → check user/group membership
|
||||||
|
"""
|
||||||
|
if not access_grants:
|
||||||
|
return False
|
||||||
|
|
||||||
if user_group_ids is None:
|
if user_group_ids is None:
|
||||||
user_groups = Groups.get_groups_by_member_id(user_id, db=db)
|
user_groups = Groups.get_groups_by_member_id(user_id, db=db)
|
||||||
user_group_ids = {group.id for group in user_groups}
|
user_group_ids = {group.id for group in user_groups}
|
||||||
|
|
||||||
permitted_ids = get_permitted_group_and_user_ids(type, access_control)
|
for grant in access_grants:
|
||||||
if permitted_ids is None:
|
if not isinstance(grant, dict):
|
||||||
return False
|
continue
|
||||||
|
if grant.get("permission") != permission:
|
||||||
permitted_group_ids = permitted_ids.get("group_ids", [])
|
continue
|
||||||
permitted_user_ids = permitted_ids.get("user_ids", [])
|
principal_type = grant.get("principal_type")
|
||||||
|
principal_id = grant.get("principal_id")
|
||||||
return user_id in permitted_user_ids or any(
|
if principal_type == "user" and (principal_id == "*" or principal_id == user_id):
|
||||||
group_id in permitted_group_ids for group_id in user_group_ids
|
return True
|
||||||
)
|
if principal_type == "group" and user_group_ids and principal_id in user_group_ids:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
# Get all users with access to a resource
|
return False
|
||||||
def get_users_with_access(
|
|
||||||
type: str = "write", access_control: Optional[dict] = None, db: Optional[Any] = None
|
|
||||||
) -> list[UserModel]:
|
|
||||||
if access_control is None:
|
|
||||||
result = Users.get_users(filter={"roles": ["!pending"]}, db=db)
|
|
||||||
return result.get("users", [])
|
|
||||||
|
|
||||||
permitted_ids = get_permitted_group_and_user_ids(type, access_control)
|
|
||||||
if permitted_ids is None:
|
|
||||||
return []
|
|
||||||
|
|
||||||
permitted_group_ids = permitted_ids.get("group_ids", [])
|
def migrate_access_control(data: dict, ac_key: str = "access_control", grants_key: str = "access_grants") -> None:
|
||||||
permitted_user_ids = permitted_ids.get("user_ids", [])
|
"""
|
||||||
|
Auto-migrate a config dict in-place from legacy access_control dict to access_grants list.
|
||||||
|
|
||||||
user_ids_with_access = set(permitted_user_ids)
|
If `grants_key` already exists, does nothing.
|
||||||
|
If `ac_key` exists (old format), converts it and stores as `grants_key`, then removes `ac_key`.
|
||||||
|
"""
|
||||||
|
if grants_key in data:
|
||||||
|
return
|
||||||
|
|
||||||
group_user_ids_map = Groups.get_group_user_ids_by_ids(permitted_group_ids, db=db)
|
access_control = data.get(ac_key)
|
||||||
for user_ids in group_user_ids_map.values():
|
if access_control is None and ac_key not in data:
|
||||||
user_ids_with_access.update(user_ids)
|
return
|
||||||
|
|
||||||
return Users.get_users_by_user_ids(list(user_ids_with_access), db=db)
|
grants: List[Dict[str, str]] = []
|
||||||
|
if access_control and isinstance(access_control, dict):
|
||||||
|
for perm in ["read", "write"]:
|
||||||
|
perm_data = access_control.get(perm, {})
|
||||||
|
if not perm_data:
|
||||||
|
continue
|
||||||
|
for group_id in perm_data.get("group_ids", []):
|
||||||
|
grants.append({
|
||||||
|
"principal_type": "group",
|
||||||
|
"principal_id": group_id,
|
||||||
|
"permission": perm,
|
||||||
|
})
|
||||||
|
for uid in perm_data.get("user_ids", []):
|
||||||
|
grants.append({
|
||||||
|
"principal_type": "user",
|
||||||
|
"principal_id": uid,
|
||||||
|
"permission": perm,
|
||||||
|
})
|
||||||
|
|
||||||
|
data[grants_key] = grants
|
||||||
|
data.pop(ac_key, None)
|
||||||
|
|||||||
@@ -1,124 +0,0 @@
|
|||||||
from pydantic import BaseModel, ConfigDict
|
|
||||||
from sqlalchemy import BigInteger, Boolean, Column, String, Text, JSON
|
|
||||||
from sqlalchemy.dialects.postgresql import JSONB
|
|
||||||
|
|
||||||
|
|
||||||
from sqlalchemy import or_, func, select, and_, text, cast, or_, and_, func
|
|
||||||
|
|
||||||
|
|
||||||
def has_permission(db, DocumentModel, query, filter: dict, permission: str = "read"):
|
|
||||||
group_ids = filter.get("group_ids", [])
|
|
||||||
user_id = filter.get("user_id")
|
|
||||||
dialect_name = db.bind.dialect.name
|
|
||||||
|
|
||||||
conditions = []
|
|
||||||
|
|
||||||
# Handle read_only permission separately
|
|
||||||
if permission == "read_only":
|
|
||||||
# For read_only, we want items where:
|
|
||||||
# 1. User has explicit read permission (via groups or user-level)
|
|
||||||
# 2. BUT does NOT have write permission
|
|
||||||
# 3. Public items are NOT considered read_only
|
|
||||||
|
|
||||||
read_conditions = []
|
|
||||||
|
|
||||||
# Group-level read permission
|
|
||||||
if group_ids:
|
|
||||||
group_read_conditions = []
|
|
||||||
for gid in group_ids:
|
|
||||||
if dialect_name == "sqlite":
|
|
||||||
group_read_conditions.append(
|
|
||||||
DocumentModel.access_control["read"]["group_ids"].contains(gid)
|
|
||||||
)
|
|
||||||
elif dialect_name == "postgresql":
|
|
||||||
group_read_conditions.append(
|
|
||||||
cast(
|
|
||||||
DocumentModel.access_control["read"]["group_ids"],
|
|
||||||
JSONB,
|
|
||||||
).contains([gid])
|
|
||||||
)
|
|
||||||
|
|
||||||
if group_read_conditions:
|
|
||||||
read_conditions.append(or_(*group_read_conditions))
|
|
||||||
|
|
||||||
# Combine read conditions
|
|
||||||
if read_conditions:
|
|
||||||
has_read = or_(*read_conditions)
|
|
||||||
else:
|
|
||||||
# If no read conditions, return empty result
|
|
||||||
return query.filter(False)
|
|
||||||
|
|
||||||
# Now exclude items where user has write permission
|
|
||||||
write_exclusions = []
|
|
||||||
|
|
||||||
# Exclude items owned by user (they have implicit write)
|
|
||||||
if user_id:
|
|
||||||
write_exclusions.append(DocumentModel.user_id != user_id)
|
|
||||||
|
|
||||||
# Exclude items where user has explicit write permission via groups
|
|
||||||
if group_ids:
|
|
||||||
group_write_conditions = []
|
|
||||||
for gid in group_ids:
|
|
||||||
if dialect_name == "sqlite":
|
|
||||||
group_write_conditions.append(
|
|
||||||
DocumentModel.access_control["write"]["group_ids"].contains(gid)
|
|
||||||
)
|
|
||||||
elif dialect_name == "postgresql":
|
|
||||||
group_write_conditions.append(
|
|
||||||
cast(
|
|
||||||
DocumentModel.access_control["write"]["group_ids"],
|
|
||||||
JSONB,
|
|
||||||
).contains([gid])
|
|
||||||
)
|
|
||||||
|
|
||||||
if group_write_conditions:
|
|
||||||
# User should NOT have write permission
|
|
||||||
write_exclusions.append(~or_(*group_write_conditions))
|
|
||||||
|
|
||||||
# Exclude public items (items without access_control)
|
|
||||||
write_exclusions.append(DocumentModel.access_control.isnot(None))
|
|
||||||
write_exclusions.append(cast(DocumentModel.access_control, String) != "null")
|
|
||||||
|
|
||||||
# Combine: has read AND does not have write AND not public
|
|
||||||
if write_exclusions:
|
|
||||||
query = query.filter(and_(has_read, *write_exclusions))
|
|
||||||
else:
|
|
||||||
query = query.filter(has_read)
|
|
||||||
|
|
||||||
return query
|
|
||||||
|
|
||||||
# Original logic for other permissions (read, write, etc.)
|
|
||||||
# Public access conditions
|
|
||||||
if group_ids or user_id:
|
|
||||||
conditions.extend(
|
|
||||||
[
|
|
||||||
DocumentModel.access_control.is_(None),
|
|
||||||
cast(DocumentModel.access_control, String) == "null",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
# User-level permission (owner has all permissions)
|
|
||||||
if user_id:
|
|
||||||
conditions.append(DocumentModel.user_id == user_id)
|
|
||||||
|
|
||||||
# Group-level permission
|
|
||||||
if group_ids:
|
|
||||||
group_conditions = []
|
|
||||||
for gid in group_ids:
|
|
||||||
if dialect_name == "sqlite":
|
|
||||||
group_conditions.append(
|
|
||||||
DocumentModel.access_control[permission]["group_ids"].contains(gid)
|
|
||||||
)
|
|
||||||
elif dialect_name == "postgresql":
|
|
||||||
group_conditions.append(
|
|
||||||
cast(
|
|
||||||
DocumentModel.access_control[permission]["group_ids"],
|
|
||||||
JSONB,
|
|
||||||
).contains([gid])
|
|
||||||
)
|
|
||||||
conditions.append(or_(*group_conditions))
|
|
||||||
|
|
||||||
if conditions:
|
|
||||||
query = query.filter(or_(*conditions))
|
|
||||||
|
|
||||||
return query
|
|
||||||
@@ -340,12 +340,12 @@ async def get_all_models(request, refresh: bool = False, user: UserModel = None)
|
|||||||
|
|
||||||
def check_model_access(user, model, db=None):
|
def check_model_access(user, model, db=None):
|
||||||
if model.get("arena"):
|
if model.get("arena"):
|
||||||
|
meta = model.get("info", {}).get("meta", {})
|
||||||
|
access_grants = meta.get("access_grants", [])
|
||||||
if not has_access(
|
if not has_access(
|
||||||
user.id,
|
user.id,
|
||||||
type="read",
|
permission="read",
|
||||||
access_control=model.get("info", {})
|
access_grants=access_grants,
|
||||||
.get("meta", {})
|
|
||||||
.get("access_control", {}),
|
|
||||||
db=db,
|
db=db,
|
||||||
):
|
):
|
||||||
raise Exception("Model not found")
|
raise Exception("Model not found")
|
||||||
@@ -384,12 +384,12 @@ def get_filtered_models(models, user, db=None):
|
|||||||
}
|
}
|
||||||
for model in models:
|
for model in models:
|
||||||
if model.get("arena"):
|
if model.get("arena"):
|
||||||
|
meta = model.get("info", {}).get("meta", {})
|
||||||
|
access_grants = meta.get("access_grants", [])
|
||||||
if has_access(
|
if has_access(
|
||||||
user.id,
|
user.id,
|
||||||
type="read",
|
permission="read",
|
||||||
access_control=model.get("info", {})
|
access_grants=access_grants,
|
||||||
.get("meta", {})
|
|
||||||
.get("access_control", {}),
|
|
||||||
user_group_ids=user_group_ids,
|
user_group_ids=user_group_ids,
|
||||||
):
|
):
|
||||||
filtered_models.append(model)
|
filtered_models.append(model)
|
||||||
|
|||||||
@@ -149,8 +149,9 @@ def has_tool_server_access(
|
|||||||
if user_group_ids is None:
|
if user_group_ids is None:
|
||||||
user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user.id)}
|
user_group_ids = {group.id for group in Groups.get_groups_by_member_id(user.id)}
|
||||||
|
|
||||||
access_control = server_connection.get("config", {}).get("access_control", None)
|
server_config = server_connection.get("config", {})
|
||||||
return has_access(user.id, "read", access_control, user_group_ids)
|
access_grants = server_config.get("access_grants", [])
|
||||||
|
return has_access(user.id, "read", access_grants, user_group_ids)
|
||||||
|
|
||||||
|
|
||||||
async def get_tools(
|
async def get_tools(
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
let headers = '';
|
let headers = '';
|
||||||
|
|
||||||
let functionNameFilterList = '';
|
let functionNameFilterList = '';
|
||||||
let accessControl = {};
|
let accessGrants = [];
|
||||||
|
|
||||||
let id = '';
|
let id = '';
|
||||||
let name = '';
|
let name = '';
|
||||||
@@ -149,7 +149,7 @@
|
|||||||
key,
|
key,
|
||||||
config: {
|
config: {
|
||||||
enable: enable,
|
enable: enable,
|
||||||
access_control: accessControl
|
access_grants: accessGrants
|
||||||
},
|
},
|
||||||
info: {
|
info: {
|
||||||
id,
|
id,
|
||||||
@@ -206,7 +206,7 @@
|
|||||||
|
|
||||||
if (data.config) {
|
if (data.config) {
|
||||||
enable = data.config.enable ?? true;
|
enable = data.config.enable ?? true;
|
||||||
accessControl = data.config.access_control ?? {};
|
accessGrants = data.config.access_grants ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
toast.success($i18n.t('Import successful'));
|
toast.success($i18n.t('Import successful'));
|
||||||
@@ -305,7 +305,7 @@
|
|||||||
config: {
|
config: {
|
||||||
enable: enable,
|
enable: enable,
|
||||||
function_name_filter_list: functionNameFilterList,
|
function_name_filter_list: functionNameFilterList,
|
||||||
access_control: accessControl
|
access_grants: accessGrants
|
||||||
},
|
},
|
||||||
info: {
|
info: {
|
||||||
id: id,
|
id: id,
|
||||||
@@ -339,7 +339,7 @@
|
|||||||
|
|
||||||
enable = true;
|
enable = true;
|
||||||
functionNameFilterList = '';
|
functionNameFilterList = '';
|
||||||
accessControl = null;
|
accessGrants = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
const init = () => {
|
const init = () => {
|
||||||
@@ -363,7 +363,7 @@
|
|||||||
|
|
||||||
enable = connection.config?.enable ?? true;
|
enable = connection.config?.enable ?? true;
|
||||||
functionNameFilterList = connection.config?.function_name_filter_list ?? '';
|
functionNameFilterList = connection.config?.function_name_filter_list ?? '';
|
||||||
accessControl = connection.config?.access_control ?? null;
|
accessGrants = connection.config?.access_grants ?? [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -819,7 +819,7 @@
|
|||||||
<hr class=" border-gray-100 dark:border-gray-700/10 my-2.5 w-full" />
|
<hr class=" border-gray-100 dark:border-gray-700/10 my-2.5 w-full" />
|
||||||
|
|
||||||
<div class="my-2">
|
<div class="my-2">
|
||||||
<AccessControl bind:accessControl />
|
<AccessControl bind:accessGrants />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
let modelIds = [];
|
let modelIds = [];
|
||||||
let filterMode = 'include';
|
let filterMode = 'include';
|
||||||
|
|
||||||
let accessControl = {};
|
let accessGrants = [];
|
||||||
|
|
||||||
let imageInputElement;
|
let imageInputElement;
|
||||||
let loading = false;
|
let loading = false;
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
description: description || null,
|
description: description || null,
|
||||||
model_ids: modelIds.length > 0 ? modelIds : null,
|
model_ids: modelIds.length > 0 ? modelIds : null,
|
||||||
filter_mode: modelIds.length > 0 ? (filterMode ? filterMode : null) : null,
|
filter_mode: modelIds.length > 0 ? (filterMode ? filterMode : null) : null,
|
||||||
access_control: accessControl
|
access_grants: accessGrants
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@
|
|||||||
description = model.meta.description;
|
description = model.meta.description;
|
||||||
modelIds = model.meta.model_ids || [];
|
modelIds = model.meta.model_ids || [];
|
||||||
filterMode = model.meta?.filter_mode ?? 'include';
|
filterMode = model.meta?.filter_mode ?? 'include';
|
||||||
accessControl = 'access_control' in model.meta ? model.meta.access_control : {};
|
accessGrants = model.meta.access_grants ?? [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -293,7 +293,7 @@
|
|||||||
<hr class=" border-gray-100 dark:border-gray-700/10 my-2.5 w-full" />
|
<hr class=" border-gray-100 dark:border-gray-700/10 my-2.5 w-full" />
|
||||||
|
|
||||||
<div class="my-2">
|
<div class="my-2">
|
||||||
<AccessControl bind:accessControl />
|
<AccessControl bind:accessGrants />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr class=" border-gray-100 dark:border-gray-700/10 my-2.5 w-full" />
|
<hr class=" border-gray-100 dark:border-gray-700/10 my-2.5 w-full" />
|
||||||
|
|||||||
Reference in New Issue
Block a user