Skip to content

Commit

Permalink
Merge branch 'main' into patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
dokterbob authored Nov 6, 2024
2 parents f69a569 + 2ba2c59 commit 51d790f
Show file tree
Hide file tree
Showing 14 changed files with 177 additions and 57 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ on:
workflow_call:
workflow_dispatch:
pull_request:
branches: [main, dev]
branches: [main, dev, 'release/**']
push:
branches: [main, dev]
branches: [main, dev, 'release/**']

permissions: read-all

Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ jobs:
name: Install Python, poetry and Python dependencies
with:
poetry-working-directory: ${{ env.BACKEND_DIR }}
- name: Build Python distribution
run: poetry self add poetry-plugin-ignore-build-script && poetry build --ignore-build-script
working-directory: ${{ env.BACKEND_DIR }}
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
Expand Down
134 changes: 104 additions & 30 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,110 @@ All notable changes to Chainlit will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [1.3.1] - 2024-10-25

### Security Advisory

- **IMPORTANT**: This release temporarily reverts the file access security improvements from 1.3.0 to restore element functionality. The element feature currently has a known security vulnerability that could allow unauthorized access to files. We strongly recommend against using elements in production environments until the next release.
- A comprehensive security fix using HTTP-only cookie authentication will be implemented in an upcoming release.

### Changed

- Reverted authentication requirements for file access endpoints to restore element functionality (#1474)

### Development

- Work in progress on implementing HTTP-only cookie authentication for proper security (#1472)

## [1.3.0] - 2024-10-22

### Security

- Fixed critical endpoint security vulnerabilities (#1441)
- Enhanced authentication for file-related endpoints (#1431)
- Upgraded frontend and backend dependencies to address security issues (#1431)

### Added

- SQLite support in SQLAlchemy integration (#1319)
- Support for IETF BCP 47 language tags, enabling localized languages like es-419 (#1399)
- Environment variables `OAUTH_<PROVIDER>_PROMPT` and `OAUTH_PROMPT` to
override oauth prompt parameter. Enabling users to explicitly enable login/consent prompts for oauth, e.g. `OAUTH_PROMPT=consent` to prevent automatic re-login. (#1362, #1456).
- Added `get_element()` method to SQLAlchemyDataLayer (#1346)

### Changed

- Bumped LiteralAI dependency to version 0.0.625 (#1376)
- Optimized LiteralDataLayer for improved performance and consistency (#1376)
- Refactored context handling in SQLAlchemy data layer (#1319)
- Updated package metadata with correct authors, license, and documentation links (#1413)
- Enhanced GitHub Actions workflow with restricted permissions (#1349)

### Fixed

- Resolved dialog boxes extending beyond window bounds (#1446)
- Fixed tasklist functionality when Chainlit is submounted (#1433)
- Corrected handling of `display_name` in PersistentUser during authentication (#1425)
- Fixed SQLAlchemy identifier quoting (#1395)
- Improved spaces handling in avatar filenames (#1418)

### Development

- Implemented extensive test coverage for LiteralDataLayer and SQLAlchemyDataLayer
- Added comprehensive unit tests for file-related endpoints
- Enhanced code organization and import structure
- Improved Python code style and linting (#1353)
- Resolved various small text and documentation issues (#1347, #1348)

## [2.0.dev2] - 2024-10-25

### Security Advisory

- **IMPORTANT**: This release temporarily reverts the file access security improvements from 2.0.dev1 to restore element functionality. The element feature currently has a known security vulnerability that could allow unauthorized access to files. We strongly recommend against using elements in production environments until the next release.
- A comprehensive security fix using HTTP-only cookie authentication will be implemented in an upcoming release.

### Changed

- Reverted authentication requirements for file access endpoints to restore element functionality (#1474)

### Development

- Work in progress on implementing HTTP-only cookie authentication for proper security (#1472)

## [2.0.dev1] - 2024-10-22

### Added

- Interactive DataFrame display component using MUI Data Grid (#1373)
- Optional websocket connection in react-client (#1379)
- Current URL in message payload (#1403)
- Improved image interaction - clicking opens popup with download option (#1402)
- Configurable user session timeout (#1032)

### Security

- Fixed file access vulnerability in get_file and upload_file endpoints (#1441)
- Added authentication to /project/file endpoint (#1441)
- Addressed security vulnerabilities in frontend dependencies (#1431, #1414)

### Fixed

- Dialog boxes extending beyond window (#1446)
- Allow empty chat input when submitting attachments (#1261)
- Tasklist when Chainlit is submounted (#1433)
- Spaces in avatar filenames (#1418)
- Step argument input and concurrency issues (#1409)
- Display_name copying to PersistentUser during authentication (#1425)

### Development

- Refactored storage clients into separate modules (#1363)
- Support for IETF BCP 47 language tags (#1399)
- Improved GitHub Actions workflows and build process (#1445)
- Direct installation from GitHub support (#1423)
- Extended package metadata with homepage and documentation links (#1413)
- Various backend fixes and code cleanup (#1432)

## [2.0.dev0] - 2024-10-08

### Breaking Changes
Expand Down Expand Up @@ -40,36 +144,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Added new wavtools directory with various audio processing utilities
- Implemented new AudioWorklet processors for more efficient audio handling

## [1.3.0rc0] - 2024-10-02

### Added

- SQLite support in SQLAlchemy integration (#1137)
- Extensive test coverage for LiteralDataLayer and SQLAlchemyDataLayer
- `get_element()` method to SQLAlchemyDataLayer (#1346)

### Changed

- Bumped LiteralAI dependency to version 0.0.625 (#1376)
- Refactored LiteralDataLayer for improved performance and consistency
- Refactored context handling in SQLAlchemy data layer (#1319)
- Enhanced GitHub Actions workflow with restricted permissions (#1349)

### Fixed

- Resolved issues with SQLite database support (#1137)
- Addressed automatic OAuth login after logout (#1362)
- Various code style and linting improvements (#1353, #1348, #1347)

### Development

- Implemented LiteralToChainlitConverter class for handling conversions
- Added comprehensive unit tests for data layer components
- Improved import structure and removed unused imports
- Updated README with latest project information (#1351)

We encourage users to thoroughly test this release candidate, particularly the LiteralAI integration and history features, and provide feedback before the final 1.3.0 release.

## [1.2.0] - 2024-09-16

### Security
Expand Down
4 changes: 2 additions & 2 deletions backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Chainlit is an open-source async Python framework which allows developers to bui

Full documentation is available [here](https://docs.chainlit.io). You can ask Chainlit related questions to [Chainlit Help](https://help.chainlit.io/), an app built using Chainlit!

> [!NOTE]
> [!NOTE]
> Check out [Literal AI](https://literalai.com), our product to monitor and evaluate LLM applications! It works with any Python or TypeScript applications and [seamlessly](https://docs.chainlit.io/data-persistence/overview) with Chainlit by adding a `LITERAL_API_KEY` in your project.
>
> Chainlit is developed and maintained by the Literal AI team, which is currently focused on expanding the capabilities of Literal AI. While we continue to support and maintain Chainlit, we are also committed to enabling the community to contribute, particularly in areas like integrations and data layers.
Expand All @@ -43,7 +43,7 @@ If this opens the `hello app` in your browser, you're all set!
The latest in-development version can be installed straight from GitHub with:

```sh
pip install git+https://github.com/Chainlit/chainlit.git@dokterbob/build_frontend_on_poetry_build#subdirectory=backend/
pip install git+https://github.com/Chainlit/chainlit.git#subdirectory=backend/
```

(Requires Node and pnpm installed on the system.)
Expand Down
4 changes: 3 additions & 1 deletion backend/chainlit/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ def create_jwt(data: User) -> str:
to_encode: Dict[str, Any] = data.to_dict()
to_encode.update(
{
"exp": datetime.utcnow() + timedelta(minutes=60 * 24 * 15), # 15 days
"exp": datetime.utcnow() + timedelta(
seconds=config.project.user_session_timeout
),
}
)
encoded_jwt = jwt.encode(to_encode, get_jwt_secret(), algorithm="HS256")
Expand Down
5 changes: 5 additions & 0 deletions backend/chainlit/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@
# Duration (in seconds) during which the session is saved when the connection is lost
session_timeout = 3600
# Duration (in seconds) of the user session expiry
user_session_timeout = 1296000 # 15 days
# Enable third parties caching (e.g LangChain cache)
cache = false
Expand Down Expand Up @@ -306,6 +309,8 @@ class ProjectSettings(DataClassJsonMixin):
# Path to the local chat db
# Duration (in seconds) during which the session is saved when the connection is lost
session_timeout: int = 3600
# Duration (in seconds) of the user session expiry
user_session_timeout: int = 1296000 # 15 days
# Enable third parties caching (e.g LangChain cache)
cache: bool = False
# Follow symlink for asset mount (see https://github.com/Chainlit/chainlit/issues/317)
Expand Down
52 changes: 43 additions & 9 deletions backend/chainlit/oauth_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class OAuthProvider:
client_secret: str
authorize_url: str
authorize_params: Dict[str, str]
default_prompt: Optional[str] = None

def is_configured(self):
return all([os.environ.get(env) for env in self.env])
Expand All @@ -26,6 +27,21 @@ async def get_token(self, code: str, url: str) -> str:
async def get_user_info(self, token: str) -> Tuple[Dict[str, str], User]:
raise NotImplementedError()

def get_env_prefix(self) -> str:
"""Return environment prefix, like AZURE_AD."""

return self.id.replace("-", "_").upper()

def get_prompt(self) -> Optional[str]:
"""Return OAuth prompt param."""
if prompt := os.environ.get(f"OAUTH_{self.get_env_prefix()}_PROMPT"):
return prompt

if prompt := os.environ.get("OAUTH_PROMPT"):
return prompt

return self.default_prompt


class GithubOAuthProvider(OAuthProvider):
id = "github"
Expand All @@ -37,9 +53,11 @@ def __init__(self):
self.client_secret = os.environ.get("OAUTH_GITHUB_CLIENT_SECRET")
self.authorize_params = {
"scope": "user:email",
"prompt": "consent",
}

if prompt := self.get_prompt():
self.authorize_params["prompt"] = prompt

async def get_token(self, code: str, url: str):
payload = {
"client_id": self.client_id,
Expand Down Expand Up @@ -96,9 +114,11 @@ def __init__(self):
"scope": "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
"response_type": "code",
"access_type": "offline",
"prompt": "login",
}

if prompt := self.get_prompt():
self.authorize_params["prompt"] = prompt

async def get_token(self, code: str, url: str):
payload = {
"client_id": self.client_id,
Expand Down Expand Up @@ -164,9 +184,11 @@ def __init__(self):
"response_type": "code",
"scope": "https://graph.microsoft.com/User.Read",
"response_mode": "query",
"prompt": "login",
}

if prompt := self.get_prompt():
self.authorize_params["prompt"] = prompt

async def get_token(self, code: str, url: str):
payload = {
"client_id": self.client_id,
Expand Down Expand Up @@ -249,9 +271,11 @@ def __init__(self):
"scope": "https://graph.microsoft.com/User.Read https://graph.microsoft.com/openid",
"response_mode": "form_post",
"nonce": nonce,
"prompt": "login",
}

if prompt := self.get_prompt():
self.authorize_params["prompt"] = prompt

async def get_token(self, code: str, url: str):
payload = {
"client_id": self.client_id,
Expand Down Expand Up @@ -329,9 +353,11 @@ def __init__(self):
"response_type": "code",
"scope": "openid profile email",
"response_mode": "query",
"prompt": "login",
}

if prompt := self.get_prompt():
self.authorize_params["prompt"] = prompt

def get_authorization_server_path(self):
if not self.authorization_server_id:
return "/default"
Expand Down Expand Up @@ -401,9 +427,11 @@ def __init__(self):
"response_type": "code",
"scope": "openid profile email",
"audience": f"{self.original_domain}/userinfo",
"prompt": "login",
}

if prompt := self.get_prompt():
self.authorize_params["prompt"] = prompt

async def get_token(self, code: str, url: str):
payload = {
"client_id": self.client_id,
Expand Down Expand Up @@ -459,9 +487,11 @@ def __init__(self):
"response_type": "code",
"scope": "openid profile email",
"audience": f"{self.domain}/userinfo",
"prompt": "login",
}

if prompt := self.get_prompt():
self.authorize_params["prompt"] = prompt

async def get_token(self, code: str, url: str):
payload = {
"client_id": self.client_id,
Expand Down Expand Up @@ -518,9 +548,11 @@ def __init__(self):
"response_type": "code",
"client_id": self.client_id,
"scope": "openid profile email",
"prompt": "login",
}

if prompt := self.get_prompt():
self.authorize_params["prompt"] = prompt

async def get_token(self, code: str, url: str):
payload = {
"client_id": self.client_id,
Expand Down Expand Up @@ -587,9 +619,11 @@ def __init__(self):
self.authorize_params = {
"scope": "openid profile email",
"response_type": "code",
"prompt": "login",
}

if prompt := self.get_prompt():
self.authorize_params["prompt"] = prompt

async def get_token(self, code: str, url: str):
payload = {
"client_id": self.client_id,
Expand Down
15 changes: 8 additions & 7 deletions backend/chainlit/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -881,7 +881,7 @@ async def upload_file(
async def get_file(
file_id: str,
session_id: str,
current_user: Annotated[Union[User, PersistedUser], Depends(get_current_user)],
# current_user: Annotated[Union[User, PersistedUser], Depends(get_current_user)], #TODO: Causes 401 error. See https://github.com/Chainlit/chainlit/issues/1472
):
"""Get a file from the session files directory."""

Expand All @@ -895,12 +895,13 @@ async def get_file(
detail="Unauthorized",
)

if current_user:
if not session.user or session.user.identifier != current_user.identifier:
raise HTTPException(
status_code=401,
detail="You are not authorized to download files from this session",
)
#TODO: Causes 401 error. See https://github.com/Chainlit/chainlit/issues/1472
# if current_user:
# if not session.user or session.user.identifier != current_user.identifier:
# raise HTTPException(
# status_code=401,
# detail="You are not authorized to download files from this session",
# )

if file_id in session.files:
file = session.files[file_id]
Expand Down
Loading

0 comments on commit 51d790f

Please sign in to comment.