diff --git a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties index 7c97b5a0085..8613b774ec0 100644 --- a/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties +++ b/js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties @@ -3360,13 +3360,12 @@ resourceScope=Resource scope resourceScopeHelpText=Specifies the scope of the resource. This is used to determine the type of resource that the permission is granted to. allClients=All clients specificClients=Specific clients -allUsers=All users -specificUsers=Specific users +allResourceType=All {{resourceType}} +specificResourceType=Specific {{resourceType}} assignedPolicies=Assigned policies assignExistingPolicies=Assign existing policies requiredPolicies=Please add at least one policy. createNewPolicy=Create new policy -createPermissionPolicy=Create policy policy=Policy policyType=Policy type policyTypeHelpText=Specifies the type of policy. This is used to determine the type of policy that the permission is granted to. @@ -3383,7 +3382,6 @@ authorizationScopeDetailsTitle=Authorization scope details authorizationScopeDetailsSubtitle=Authorization scope defines the actions that can be performed on a resource. authorizationScopeDetailsName=Name authorizationScopeDetailsDescription=Description -authorizationScopeDetailsDescriptionText=Lorem ipsum allResources=All resources currentRealm=Current realm recentlyUsed=Recently used @@ -3391,8 +3389,9 @@ viewAll=View all currentRealmExplain=This realm is selected removeInvalidUsers=Remove invalid users during searches removeInvalidUsersHelp=Remove users from the local database if they are not available from the user storage when executing searches. If this is true, users no longer available from their corresponding user storage will be deleted from the local database whenever trying to look up users. If false, then users previously imported from the user storage will be kept in the local database, as read-only and disabled, even if that user is no longer available from the user storage. For example, user was deleted directly from LDAP or the `Users DN` is invalid. Note that this behavior will only happen when the user is not yet cached. -applyPermissionTo=Enforce access to -applyPermissionToHelpText=Specifies the resource that the permission is applied to. +createPermissionPolicy=Create policy +enforceAccessTo=Enforce access to +enforceAccessToHelpText=Specifies the resource that the permission is applied to. emptyPermissionPoliciesInstructions=No policies exist in this realm. noPermissionSearchResultsInstructions=No permissions matched your filters. deleteAdminPermissionConfirm=If you delete permission {{ permission }}, administrators cannot perform the actions on resources that were defined by the permission. @@ -3414,4 +3413,9 @@ authorizationScope.Groups.manage-members=Manages group members authorizationScope.Groups.manage-membership=Adds or removes group members authorizationScope.Groups.view=Views this group authorizationScope.Groups.view-members=Views group members -authorizationScope.IdentityProviders.token-exchange=Allows clients to exchange tokens for tokens issued by this identity provider \ No newline at end of file +authorizationScope.IdentityProviders.token-exchange=Allows clients to exchange tokens for tokens issued by this identity provider +usersResources=Users +clientsResources=Clients +groupsResources=Groups +resourceTypeHelpText=Specifies which {{ resourceType }} are allowed by this permission. +evaluation=Evaluation \ No newline at end of file diff --git a/js/apps/admin-ui/src/clients/authorization/ScopePicker.tsx b/js/apps/admin-ui/src/clients/authorization/ScopePicker.tsx index dad3bf46655..22f504da8a5 100644 --- a/js/apps/admin-ui/src/clients/authorization/ScopePicker.tsx +++ b/js/apps/admin-ui/src/clients/authorization/ScopePicker.tsx @@ -1,5 +1,6 @@ import type ScopeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/scopeRepresentation"; import { + FormErrorText, HelpItem, KeycloakSelect, SelectVariant, @@ -29,7 +30,10 @@ export const ScopePicker = ({ }: ScopePickerProps) => { const { adminClient } = useAdminClient(); const { t } = useTranslation(); - const { control } = useFormContext(); + const { + control, + formState: { errors }, + } = useFormContext(); const [open, setOpen] = useState(false); const [scopes, setScopes] = useState(); const [search, setSearch] = useState(""); @@ -77,44 +81,50 @@ export const ScopePicker = ({ name="scopes" defaultValue={[]} control={control} + rules={isAdminPermissionsClient ? { required: t("requiredField") } : {}} render={({ field }) => { const selectedValues = field.value.map((o: Scope) => o.name); return ( - setOpen(val)} - isOpen={open} - selections={selectedValues} - onFilter={(value) => { - setSearch(value); - return renderScopes(allScopes || []); - }} - onSelect={(selectedValue) => { - const option = - typeof selectedValue === "string" - ? selectedValue - : (selectedValue as Scope).name; - const changedValue = field.value.find( - (o: Scope) => o.name === option, - ) - ? field.value.filter((item: Scope) => item.name !== option) - : [...field.value, { name: option }]; - field.onChange(changedValue); - }} - onClear={() => { - setSearch(""); - field.onChange([]); - }} - typeAheadAriaLabel={t("authorizationScopes")} - > - {renderScopes(allScopes || [])} - + <> + setOpen(val)} + isOpen={open} + selections={selectedValues} + onFilter={(value) => { + setSearch(value); + return renderScopes(allScopes || []); + }} + onSelect={(selectedValue) => { + const option = + typeof selectedValue === "string" + ? selectedValue + : (selectedValue as Scope).name; + const changedValue = field.value.find( + (o: Scope) => o.name === option, + ) + ? field.value.filter((item: Scope) => item.name !== option) + : [...field.value, { name: option }]; + field.onChange(changedValue); + }} + onClear={() => { + setSearch(""); + field.onChange([]); + }} + typeAheadAriaLabel={t("authorizationScopes")} + > + {renderScopes(allScopes || [])} + + {isAdminPermissionsClient && errors.scopes && ( + + )} + ); }} /> diff --git a/js/apps/admin-ui/src/clients/authorization/SearchDropdown.tsx b/js/apps/admin-ui/src/clients/authorization/SearchDropdown.tsx index a3901392006..2c94874b4a5 100644 --- a/js/apps/admin-ui/src/clients/authorization/SearchDropdown.tsx +++ b/js/apps/admin-ui/src/clients/authorization/SearchDropdown.tsx @@ -23,11 +23,9 @@ export type SearchForm = { uri?: string; owner?: string; resourceType?: string; - policyId?: string; }; type SearchDropdownProps = { - policies?: PolicyRepresentation[]; resources?: UserRepresentation[]; types?: PolicyRepresentation[]; search: SearchForm; @@ -36,7 +34,6 @@ type SearchDropdownProps = { }; export const SearchDropdown = ({ - policies, resources, types, search, @@ -57,9 +54,6 @@ export const SearchDropdown = ({ const [open, toggle] = useToggle(); const [resourceScopes, setResourceScopes] = useState([]); - const [localPolicies, setLocalPolicies] = useState( - policies || [], - ); const selectedType = useWatch({ control: form.control, name: "type" }); const [key, setKey] = useState(0); @@ -71,11 +65,7 @@ export const SearchDropdown = ({ useEffect(() => { const type = types?.find((item) => item.type === selectedType); setResourceScopes(type?.scopes || []); - - if (policies?.length) { - setLocalPolicies(policies); - } - }, [selectedType, types, policies]); + }, [selectedType, types]); useEffect(() => { reset(search); @@ -125,7 +115,7 @@ export const SearchDropdown = ({ )} {type !== "resource" && ( )} - {type === "adminPermission" && ( - ({ - key: id!, - value: name!, - })) - : [] - } - /> - )}