Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d8c3f3b
Added roles model
cableman Apr 14, 2025
2222fae
Added migration for roles table
cableman Apr 14, 2025
4193720
Added default roles to database
cableman Apr 15, 2025
d75be6b
Added roles routes
cableman Apr 15, 2025
a38cc2c
Added support for adding new roles
cableman Apr 15, 2025
6c754a3
Added support for deleting role
cableman Apr 15, 2025
488bc03
Ensured dynamic role assignment is used in oauth
cableman Apr 15, 2025
5e8925d
Added roles apis to frontend
cableman Apr 15, 2025
d93cb88
Added support for dynamic roles in frontend
cableman Apr 22, 2025
8318cbf
Added support for dynamic roles in user add
cableman Apr 22, 2025
59b9360
Started on moving permissions into database
cableman Apr 22, 2025
a76f8b8
Set default permission in database based on config
cableman Apr 23, 2025
70409a0
Added support for adding new permission
cableman Apr 23, 2025
0425618
Added support for updateing permission
cableman Apr 24, 2025
bd02edd
Clean up permission routes and database
cableman Apr 24, 2025
7739400
Added relationship berween permissions and roles
cableman Apr 28, 2025
ad71a10
Changed roles API end-point to use role names
cableman Apr 29, 2025
1dd3b5a
Adding support for roles API in frontend
cableman Apr 29, 2025
8054aac
Updated default permission API to include all permissions
cableman Apr 30, 2025
351f1ce
Adding support for new permission in UI
cableman Apr 30, 2025
0b1292e
Moved permission into own end-point
cableman May 1, 2025
b276fe1
Using same shared component for permissions
cableman May 1, 2025
415ab9d
Added label to permisison and overview to UI
cableman May 1, 2025
a355de7
Added categroies as selector in permission UI
cableman May 2, 2025
218cd2a
Resovled merge conflict in users
cableman May 2, 2025
e9b362c
Fixed default permission for groups
cableman May 5, 2025
fe4cfb4
Resovled merge with permission components with core
cableman May 5, 2025
725025c
Ensured ENABLE_PERSISTENT_CONFIG is used for permissions
cableman May 5, 2025
7daad70
Fixed database migration conflict
cableman May 5, 2025
8e83aaa
Code clean up in roles and permissions
cableman May 5, 2025
cd786e1
Resovled merge conflict in role update
cableman May 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 39 additions & 5 deletions backend/open_webui/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
)
from open_webui.internal.db import Base, get_db
from open_webui.utils.redis import get_redis_connection
from open_webui.models.permissions import Permissions


class EndpointFilter(logging.Filter):
Expand Down Expand Up @@ -1130,7 +1131,6 @@ def oidc_oauth_register(client):
os.environ.get("USER_PERMISSIONS_FEATURES_NOTES", "True").lower() == "true"
)


DEFAULT_USER_PERMISSIONS = {
"workspace": {
"models": USER_PERMISSIONS_WORKSPACE_MODELS_ACCESS,
Expand Down Expand Up @@ -1167,10 +1167,44 @@ def oidc_oauth_register(client):
},
}

USER_PERMISSIONS = PersistentConfig(
"USER_PERMISSIONS",
"user.permissions",
DEFAULT_USER_PERMISSIONS,
DEFAULT_USER_PERMISSIONS_LABELS = {
"workspace": {
"models": "Models Access",
"knowledge": "Knowledge Access",
"prompts": "Prompts Access",
"tools": "Tools Access",
},
"sharing": {
"public_models": "Models Public Sharing",
"public_knowledge": "Knowledge Public Sharing",
"public_prompts": "Prompts Public Sharing",
"public_tools": "Tools Public Sharing",
},
"chat": {
"controls": "Allow Chat Controls",
"file_upload": "Allow File Upload",
"delete": "Allow Chat Delete",
"edit": "Allow Chat Edit",
"share": "Allow Chat Share",
"export": "Allow Chat Export",
"stt": "Allow Speech to Text",
"tts": "Allow Text to Speech",
"call": "Allow Call",
"multiple_models": "Allow Multiple Models in Chat",
"temporary": "Allow Temporary Chat",
"temporary_enforced": "Enforce Temporary Chat",
},
"features": {
"direct_tool_servers": "Direct Tool Servers",
"web_search": "Web Search",
"image_generation": "Image Generation",
"code_interpreter": "Code Interpreter",
"notes": "Notes",
},
}

USER_PERMISSIONS = Permissions.set_initial_permissions(
DEFAULT_USER_PERMISSIONS, DEFAULT_USER_PERMISSIONS_LABELS
)

ENABLE_CHANNELS = PersistentConfig(
Expand Down
16 changes: 16 additions & 0 deletions backend/open_webui/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,22 @@ def __str__(self) -> str:
)
FILE_NOT_PROCESSED = "Extracted content is not available for this file. Please ensure that the file is processed before proceeding."

PERMISSION_FETCH_FAILED = "Permissions fetch failed. Please try again later."
PERMISSION_NOT_FOUND = (
lambda name="", category="": f"Permission '{name}' with category '{category}' was not found"
)

ROLE_ALREADY_EXISTS = (
lambda role="": f"A role with the name '{role}' already exists"
)
ROLE_DO_NOT_EXISTS = "A role does not exist."
ROLE_ALREADY_ASSIGNED_TO_GROUP = ()
ROLE_DELETE_ERROR = "Oops! Something went wrong. We encountered an issue while trying to delete the role. Please give it another shot."
ROLE_NOT_FOUND = (
"This role does not exist. Please check the role name and try again."
)
ROLE_ERROR = "Oops! Something went wrong. Please try again later."


class TASKS(str, Enum):
def __str__(self) -> str:
Expand Down
10 changes: 9 additions & 1 deletion backend/open_webui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@
tools,
users,
utils,
roles,
permissions,
)

from open_webui.routers.retrieval import (
Expand Down Expand Up @@ -588,6 +590,7 @@ async def lifespan(app: FastAPI):
app.state.config.RESPONSE_WATERMARK = RESPONSE_WATERMARK

app.state.config.USER_PERMISSIONS = USER_PERMISSIONS

app.state.config.WEBHOOK_URL = WEBHOOK_URL
app.state.config.BANNERS = WEBUI_BANNERS
app.state.config.MODEL_ORDER_LIST = MODEL_ORDER_LIST
Expand Down Expand Up @@ -1052,6 +1055,11 @@ async def inspect_websocket(request: Request, call_next):
)
app.include_router(utils.router, prefix="/api/v1/utils", tags=["utils"])

app.include_router(roles.router, prefix="/api/v1/roles", tags=["roles"])
app.include_router(
permissions.router, prefix="/api/v1/permissions", tags=["permissions"]
)


try:
audit_level = AuditLevel(AUDIT_LOG_LEVEL)
Expand Down Expand Up @@ -1405,7 +1413,7 @@ async def get_app_config(request: Request):
"max_size": app.state.config.FILE_MAX_SIZE,
"max_count": app.state.config.FILE_MAX_COUNT,
},
"permissions": {**app.state.config.USER_PERMISSIONS},
"permissions": USER_PERMISSIONS,
"google_drive": {
"client_id": GOOGLE_DRIVE_CLIENT_ID.value,
"api_key": GOOGLE_DRIVE_API_KEY.value,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""Added permissions to the database

Revision ID: 04c6df61a317
Revises: 262aff902ca3
Create Date: 2025-04-22 14:55:58.948054

"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa
from sqlalchemy import Enum


# revision identifiers, used by Alembic.
revision: str = "04c6df61a317"
down_revision: Union[str, None] = "262aff902ca3"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade():
# Create a permissions table.
op.create_table(
"permissions",
sa.Column("id", sa.Integer(), primary_key=True, autoincrement=True),
sa.Column("name", sa.String(), nullable=False),
sa.Column("label", sa.String(), nullable=False),
sa.Column(
"category",
Enum("workspace", "sharing", "chat", "features", name="permissioncategory"),
nullable=False,
),
sa.Column("description", sa.String()),
)

# Create a role_permissions join table to allow many-to-many relationships between roles and permissions.
op.create_table(
"role_permissions",
sa.Column("role_id", sa.Integer(), nullable=False),
sa.Column("permission_id", sa.Integer(), nullable=False),
sa.Column("value", sa.Boolean(), default=False), # Added value column here
sa.ForeignKeyConstraint(["role_id"], ["roles.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(
["permission_id"], ["permissions.id"], ondelete="CASCADE"
),
sa.PrimaryKeyConstraint("role_id", "permission_id"),
)


def downgrade():
op.drop_table("role_permissions")
op.drop_table("permissions")
op.execute("DROP TYPE permissioncategory")
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""Added roles tabel

Revision ID: 262aff902ca3
Revises: 9f0c9cd09105
Create Date: 2025-04-14 14:25:33.528446

"""

import time

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision: str = "262aff902ca3"
down_revision: Union[str, None] = "9f0c9cd09105"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade():
op.create_table(
"roles",
sa.Column("id", sa.Integer, primary_key=True),
sa.Column("name", sa.String()),
sa.Column("created_at", sa.BigInteger(), nullable=True),
sa.Column("updated_at", sa.BigInteger(), nullable=True),
)

# Insert default roles into the database.
current_time = int(time.time())

connection = op.get_bind()
connection.execute(
sa.text(
f"""
INSERT INTO roles (name, created_at, updated_at) VALUES
('pending', {current_time}, {current_time}),
('admin', {current_time}, {current_time}),
('user', {current_time}, {current_time})
"""
)
)


def downgrade():
op.drop_table("roles")
Loading