diff --git a/api/server/services/GraphApiService.js b/api/server/services/GraphApiService.js index 48ecbad5ac..82fa245d58 100644 --- a/api/server/services/GraphApiService.js +++ b/api/server/services/GraphApiService.js @@ -188,7 +188,10 @@ const getUserOwnedEntraGroups = async (accessToken, sub) => { try { const graphClient = await createGraphClient(accessToken, sub); - const groupsResponse = await graphClient.api('/me/ownedObjects/microsoft.graph.group').select('id').get(); + const groupsResponse = await graphClient + .api('/me/ownedObjects/microsoft.graph.group') + .select('id') + .get(); return (groupsResponse.value || []).map((group) => group.id); } catch (error) { diff --git a/api/server/services/GraphApiService.spec.js b/api/server/services/GraphApiService.spec.js index 5c1484d8b5..5d8dd62cf5 100644 --- a/api/server/services/GraphApiService.spec.js +++ b/api/server/services/GraphApiService.spec.js @@ -60,7 +60,7 @@ describe('GraphApiService', () => { beforeEach(async () => { jest.clearAllMocks(); await mongoose.connection.dropDatabase(); - + // Set up environment variable for People.Read scope process.env.OPENID_GRAPH_SCOPES = 'User.Read,People.Read,Group.Read.All'; diff --git a/api/server/services/PermissionService.js b/api/server/services/PermissionService.js index ba16ca16be..85a263d455 100644 --- a/api/server/services/PermissionService.js +++ b/api/server/services/PermissionService.js @@ -484,7 +484,7 @@ const hasPublicPermission = async ({ resourceType, resourceId, requiredPermissio // Use public principal to check permissions const publicPrincipal = [{ principalType: 'public' }]; - + const entries = await findEntriesByPrincipalsAndResource( publicPrincipal, resourceType, @@ -492,7 +492,7 @@ const hasPublicPermission = async ({ resourceType, resourceId, requiredPermissio ); // Check if any entry has the required permission bits - return entries.some(entry => (entry.permBits & requiredPermissions) === requiredPermissions); + return entries.some((entry) => (entry.permBits & requiredPermissions) === requiredPermissions); } catch (error) { logger.error(`[PermissionService.hasPublicPermission] Error: ${error.message}`); // Re-throw validation errors diff --git a/client/src/components/SidePanel/Agents/Sharing/GrantAccessDialog.tsx b/client/src/components/SidePanel/Agents/Sharing/GrantAccessDialog.tsx index 18e8662ce3..bef9131e62 100644 --- a/client/src/components/SidePanel/Agents/Sharing/GrantAccessDialog.tsx +++ b/client/src/components/SidePanel/Agents/Sharing/GrantAccessDialog.tsx @@ -41,8 +41,8 @@ export default function GrantAccessDialog({ const { data: permissionsData, - isLoading: isLoadingPermissions, - error: permissionsError, + // isLoading: isLoadingPermissions, + // error: permissionsError, } = useGetResourcePermissionsQuery(resourceType, agentDbId!, { enabled: !!agentDbId, }); diff --git a/client/src/components/SidePanel/Agents/Sharing/ManagePermissionsDialog.tsx b/client/src/components/SidePanel/Agents/Sharing/ManagePermissionsDialog.tsx index c70b22d229..34e77444db 100644 --- a/client/src/components/SidePanel/Agents/Sharing/ManagePermissionsDialog.tsx +++ b/client/src/components/SidePanel/Agents/Sharing/ManagePermissionsDialog.tsx @@ -8,7 +8,6 @@ import { OGDialogClose, OGDialogContent, OGDialogTrigger, - Badge, } from '~/components/ui'; import { cn, removeFocusOutlines } from '~/utils'; import { useToastContext } from '~/Providers'; @@ -43,7 +42,10 @@ export default function ManagePermissionsDialog({ } = useGetResourcePermissionsQuery(resourceType, agentDbId, { enabled: !!agentDbId, }); - const { data: accessRoles, isLoading: rolesLoading } = useGetAccessRolesQuery(resourceType); + const { + data: accessRoles, + // isLoading, + } = useGetAccessRolesQuery(resourceType); const updatePermissionsMutation = useUpdateResourcePermissionsMutation(); @@ -165,11 +167,31 @@ export default function ManagePermissionsDialog({ const totalShares = managedShares.length + (managedIsPublic ? 1 : 0); const originalTotalShares = currentShares.length + (isPublic ? 1 : 0); - // Check if there's at least one owner (user, group, or public with owner role) + /** Check if there's at least one owner (user, group, or public with owner role) */ const hasAtLeastOneOwner = managedShares.some((share) => share.accessRoleId === ACCESS_ROLE_IDS.AGENT_OWNER) || (managedIsPublic && managedPublicRole === ACCESS_ROLE_IDS.AGENT_OWNER); + let peopleLabel = localize('com_ui_people'); + if (managedShares.length === 1) { + peopleLabel = localize('com_ui_person'); + } + + let buttonAriaLabel = localize('com_ui_manage_permissions_for') + ' agent'; + if (agentName != null && agentName !== '') { + buttonAriaLabel = localize('com_ui_manage_permissions_for') + ` "${agentName}"`; + } + + let dialogTitle = localize('com_ui_manage_permissions_for') + ' Agent'; + if (agentName != null && agentName !== '') { + dialogTitle = localize('com_ui_manage_permissions_for') + ` "${agentName}"`; + } + + let publicSuffix = ''; + if (managedIsPublic) { + publicSuffix = localize('com_ui_and_public'); + } + return ( @@ -178,11 +200,7 @@ export default function ManagePermissionsDialog({ 'btn btn-neutral border-token-border-light relative h-9 rounded-lg font-medium', removeFocusOutlines, )} - aria-label={ - agentName != null && agentName !== '' - ? localize('com_ui_manage_permissions_for') + ` "${agentName}"` - : localize('com_ui_manage_permissions_for') + ' agent' - } + aria-label={buttonAriaLabel} type="button" >
@@ -197,9 +215,7 @@ export default function ManagePermissionsDialog({
- {agentName != null && agentName !== '' - ? localize('com_ui_manage_permissions_for') + ` "${agentName}"` - : localize('com_ui_manage_permissions_for') + ' Agent'} + {dialogTitle}
@@ -211,16 +227,16 @@ export default function ManagePermissionsDialog({ {localize('com_ui_current_access')}

- {totalShares === 0 - ? localize('com_ui_no_users_groups_access') - : localize('com_ui_shared_with_count', { - 0: managedShares.length, - 1: - managedShares.length === 1 - ? localize('com_ui_person') - : localize('com_ui_people'), - 2: managedIsPublic ? localize('com_ui_and_public') : '', - })} + {(() => { + if (totalShares === 0) { + return localize('com_ui_no_users_groups_access'); + } + return localize('com_ui_shared_with_count', { + 0: managedShares.length, + 1: peopleLabel, + 2: publicSuffix, + }); + })()}

{(managedShares.length > 0 || managedIsPublic) && ( @@ -237,34 +253,44 @@ export default function ManagePermissionsDialog({ - {isLoadingPermissions ? ( -
- - - {localize('com_ui_loading_permissions')} - -
- ) : managedShares.length > 0 ? ( -
-

- - {localize('com_ui_user_group_permissions')} ({managedShares.length}) -

- handleRoleChange(id, newRole)} - /> -
- ) : ( -
- -

- {localize('com_ui_no_individual_access')} -

-
- )} + {(() => { + if (isLoadingPermissions) { + return ( +
+ + + {localize('com_ui_loading_permissions')} + +
+ ); + } + + if (managedShares.length > 0) { + return ( +
+

+ + {localize('com_ui_user_group_permissions')} ({managedShares.length}) +

+ handleRoleChange(id, newRole)} + /> +
+ ); + } + + return ( +
+ +

+ {localize('com_ui_no_individual_access')} +

+
+ ); + })()}

diff --git a/client/src/components/ui/SearchPicker.tsx b/client/src/components/ui/SearchPicker.tsx index 973009f484..5c4d878b18 100644 --- a/client/src/components/ui/SearchPicker.tsx +++ b/client/src/components/ui/SearchPicker.tsx @@ -6,7 +6,6 @@ import { Search, X } from 'lucide-react'; import { cn } from '~/utils'; import { Spinner } from '~/components/svg'; import { Skeleton } from '~/components/ui'; -import { useLocation } from 'react-router-dom'; import { useLocalize } from '~/hooks'; type SearchPickerProps = { @@ -31,7 +30,6 @@ export function SearchPicker({ onQueryChange, query, label, - inputClassName, isSmallScreen = false, placeholder, resetValueOnHide = false, @@ -39,8 +37,7 @@ export function SearchPicker({ minQueryLengthForNoResults = 2, }: SearchPickerProps) { const localize = useLocalize(); - const location = useLocation(); - const [open, setOpen] = React.useState(false); + const [_open, setOpen] = React.useState(false); const inputRef = React.useRef(null); const combobox = Ariakit.useComboboxStore({ resetValueOnHide, @@ -96,7 +93,7 @@ export function SearchPicker({ value={query} // autoSelect placeholder={placeholder || localize('com_ui_select_options')} - className="m-0 mr-0 w-full rounded-md border-none bg-surface-secondary bg-transparent p-0 py-2 pl-7 pl-9 pr-3 text-sm leading-tight text-text-primary placeholder-text-secondary placeholder-opacity-100 focus:outline-none focus-visible:outline-none group-focus-within:placeholder-text-primary group-hover:placeholder-text-primary" + className="m-0 mr-0 w-full rounded-md border-none bg-transparent p-0 py-2 pl-9 pr-3 text-sm leading-tight text-text-primary placeholder-text-secondary placeholder-opacity-100 focus:outline-none focus-visible:outline-none group-focus-within:placeholder-text-primary group-hover:placeholder-text-primary" />