## Overview
This PR strengthens our permission system by introducing more granular
role-based access control across the platform.
## Changes
### New Permissions Added
- **Applications** - Control who can install and manage applications
- **Layouts** - Control who can customize page layouts and UI structure
- **AI** - Control access to AI features and agents
- **Upload File** - Separate permission for file uploads
- **Download File** - Separate permission for file downloads (frontend
visibility)
### Security Enhancements
- Implemented whitelist-based validation for workspace field updates
- Added explicit permission guards to core entity resolvers
- Enhanced ESLint rule to enforce permission checks on all mutations
- Created `CustomPermissionGuard` and `NoPermissionGuard` for better
code documentation
### Affected Components
- Core entity resolvers: webhooks, files, domains, applications,
layouts, postgres credentials
- Workspace update mutations now use whitelist validation
- Settings UI updated with new permission controls
### Developer Experience
- ESLint now catches missing permission guards during development
- Explicit guard markers make permission requirements clear in code
review
- Comprehensive test coverage for new permission logic
## Testing
- ✅ All TypeScript type checks pass
- ✅ ESLint validation passes
- ✅ New permission guards properly enforced
- ✅ Frontend UI displays new permissions correctly
## Migration Notes
Existing workspaces will need to assign the new permissions to roles as
needed. By default, all new permissions are set to `false` for non-admin
roles.
**Before**
- any user with workpace_members permission was able to remove a user
from their workspace. This triggered the deletion of workspaceMember +
of userWorkspace, but did not delete the user (even if they had no
workspace left) nor the roleTarget (acts as junction between role and
userWorkspace) which was left with a userWorkspaceId pointing to
nothing. This is because roleTarget points to userWorkspaceId but the
foreign key constraint was not implemented
- any user could delete their own account. This triggered the deletion
of all their workspaceMembers, but not of their userWorkspace nor their
user nor the roleTarget --> we have orphaned userWorkspace, not
technically but product wise - a userWorkspace without a workspaceMember
does not make sense
So the problems are
- we have some roleTargets pointing to non-existing userWorkspaceId
(which caused https://github.com/twentyhq/twenty/issues/14608 )
- we have userWorkspaces that should not exist and that have no
workspaceMember counterpart
- it is not possible for a user to leave a workspace by themselves, they
can only leave all workspaces at once, except if they are being removed
from the workspace by another user
**Now**
- if a user has multiple workspaces, they are given the possibility to
leave one workspace while remaining in the others (we show two buttons:
Leave workspace and Delete account buttons). if a user has just one
workspace, they only see Delete account
- when a user leaves a workspace, we delete their workspaceMember,
userWorkspace and roleTarget. If they don't belong to any other
workspace we also soft-delete their user
- soft-deleted users get hard deleted after 30 days thanks to a cron
- we have two commands to clean the orphans roleTarget and userWorkspace
(TODO: query db to see how many must be run)
**Next**
- once the commands have been run, we can implement and introduce the
foreign key constraint on roleTarget
Fixes https://github.com/twentyhq/twenty/issues/14608
Fixes https://github.com/twentyhq/private-issues/issues/361
There's room for more improvement here -
triggerInitialRecordTableDataLoad does a lot of things, should not be
triggered so much. actually even
RecordTableVirtualizedInitialDataLoadEffect should not be triggered when
there's just a metadata field change (if we trust the name
initialDataLoad)
As title
- adds decorators in twenty-sdk
- update twenty-cli load-manifest to it gets @FieldMetadata infos +
testing
- update twenty-server so it CRUD fields properly, using
universalIdentifier
- Fix UI so we can update managed objects records
- move FieldMetadata items from twenty-server to twenty-shared
Here is what the PR does:
- Surface password state in validatePasswordResetToken, returning
hasPassword so the client can tell whether a user is setting or changing
their password.
- Consume that flag throughout the front end (mock data, stories,
GraphQL types) and update the Reset/Set Password modal to swap the
heading/button label and success toast accordingly.
- After a successful password set/reset, immediately update the
logged-in user’s hasPassword flag so the Settings screen reflects the
new state without a reload.
Modal has two states now - reset password modal uses change password
state since it made intuitive sense.
<p align="center">
<img width="404" height="397" alt="image"
src="https://github.com/user-attachments/assets/c54cc581-1248-4395-833d-0202758e1947"
/>
</p>
<p align="center">
<img width="403" height="393" alt="image"
src="https://github.com/user-attachments/assets/d8a39a95-27e6-4037-86f2-1f74176002ba"
/>
</p>
---------
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
## Summary
Reverts commits afc518a, ae22e64, and 800b5b5 that refactored
`useAgentChat` to address umbrella hook pattern feedback.
## Issues Introduced by Refactoring
The refactoring broke several critical functionalities:
1. **Streaming fails on thread switch** - Messages don't stream properly
when switching between threads
2. **Messages lost on tab close** - When the Ask AI tab is closed, the
request is lost instead of continuing in the background
3. **Blank chat requiring force-reload** - Chats often appear blank and
require switching to another chat to force a reload (closes
[#1771](https://github.com/twentyhq/core-team-issues/issues/1771))
## Root Cause
After extensive debugging, it appears **multiple instances of `useChat`
don't work well together**. The refactored architecture inadvertently
created scenarios where multiple `useChat` instances interfere with each
other.
## Resolution
Reverting to restore functionality. The umbrella hook pattern
optimization needs a different architectural approach that doesn't rely
on multiple `useChat` instances.
## Follow-up
While the umbrella hook feedback is valid, we need to rethink the
implementation strategy:
- Find an alternative to multiple `useChat` instances
- Possibly consolidate chat state management differently
groupMode should be undefined if no groupby is set on the secondary
axis!
This default value is also the reason -- all the logic -- where
groupMode was checked for conditional rendering -- for eg
, negative data labels when there is no groupby -- the labels should
appear below wasn't happening -- since there was always a default to
groupMode!
changes -
- getting rid of the default value on the bar chart DTO for groupMode
- on front -- make sure groupMode is only set when the secondary axis
gets groupBy id and proper cleanup
Prevent users from creating v2 chart types via the api.
Only created unit tests and not integration tests (since it's not that
important, and the v2 will be released soon), but tested via the api
playground.
# Introduction
When creating a view with v2 flag activated in production result in race
condition due to request being slow and //.
That's why we're introducing a batch create on view field here
closing https://github.com/twentyhq/core-team-issues/issues/1836
## In v2
- batch create view field endpoint is available
- frontend will target the new endpoint
## In v1
- batch create view field endpoint is not available
- frontend will stick to old fake batch view field creation loop
## Polish
- use persist view field is quite verbose as contains two data model we
could aim to create an update many view fields in order to standardize
new pattern
changes -
- make iframe side panel to match others -- ie use SidePanelHeader for
title
- make url optional in configuration to match that of the other widgets
(allow partial saves) - render No data status when error or no url
- split widget sizes into two -- graph widget sizes and widget sizes
(graph widgets are a subset of chart widgets)
- on draft creation, do not fetch the full version. Avoid the fetch of
steps and trigger
- on activation, we were performing 8 queries/mutations synchronously +
2 additional for automated triggers. I refacto the call it it gets
reduced to 4 queries/mutations + 2 additional for automated triggers