Fine grained clients (#2702)
This commit is contained in:
parent
be000ff08c
commit
5b559bcdbd
29 changed files with 153 additions and 75 deletions
|
@ -56,6 +56,7 @@ export const AdvancedTab = ({
|
||||||
protocol,
|
protocol,
|
||||||
authenticationFlowBindingOverrides,
|
authenticationFlowBindingOverrides,
|
||||||
adminUrl,
|
adminUrl,
|
||||||
|
access,
|
||||||
},
|
},
|
||||||
}: AdvancedProps) => {
|
}: AdvancedProps) => {
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
|
@ -197,7 +198,11 @@ export const AdvancedTab = ({
|
||||||
tab
|
tab
|
||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
<FormAccess role="manage-clients" isHorizontal>
|
<FormAccess
|
||||||
|
role="manage-clients"
|
||||||
|
fineGrainedAccess={access?.configure}
|
||||||
|
isHorizontal
|
||||||
|
>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("notBefore")}
|
label={t("notBefore")}
|
||||||
fieldId="kc-not-before"
|
fieldId="kc-not-before"
|
||||||
|
@ -258,7 +263,11 @@ export const AdvancedTab = ({
|
||||||
)}
|
)}
|
||||||
{publicClient && (
|
{publicClient && (
|
||||||
<>
|
<>
|
||||||
<FormAccess role="manage-clients" isHorizontal>
|
<FormAccess
|
||||||
|
role="manage-clients"
|
||||||
|
fineGrainedAccess={access?.configure}
|
||||||
|
isHorizontal
|
||||||
|
>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("nodeReRegistrationTimeout")}
|
label={t("nodeReRegistrationTimeout")}
|
||||||
fieldId="kc-node-reregistration-timeout"
|
fieldId="kc-node-reregistration-timeout"
|
||||||
|
@ -393,6 +402,7 @@ export const AdvancedTab = ({
|
||||||
setValue(`attributes.${key}`, value)
|
setValue(`attributes.${key}`, value)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
hasConfigureAccess={access?.configure}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -424,6 +434,7 @@ export const AdvancedTab = ({
|
||||||
reset={() =>
|
reset={() =>
|
||||||
resetFields(["exclude.session.state.from.auth.response"])
|
resetFields(["exclude.session.state.from.auth.response"])
|
||||||
}
|
}
|
||||||
|
hasConfigureAccess={access?.configure}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -434,6 +445,7 @@ export const AdvancedTab = ({
|
||||||
<AdvancedSettings
|
<AdvancedSettings
|
||||||
protocol={protocol}
|
protocol={protocol}
|
||||||
control={control}
|
control={control}
|
||||||
|
hasConfigureAccess={access?.configure}
|
||||||
save={() => save()}
|
save={() => save()}
|
||||||
reset={() => {
|
reset={() => {
|
||||||
resetFields([
|
resetFields([
|
||||||
|
@ -463,6 +475,7 @@ export const AdvancedTab = ({
|
||||||
authenticationFlowBindingOverrides?.direct_grant
|
authenticationFlowBindingOverrides?.direct_grant
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
hasConfigureAccess={access?.configure}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
</ScrollForm>
|
</ScrollForm>
|
||||||
|
|
|
@ -11,9 +11,13 @@ import { KeycloakTextArea } from "../components/keycloak-text-area/KeycloakTextA
|
||||||
|
|
||||||
type ClientDescriptionProps = {
|
type ClientDescriptionProps = {
|
||||||
protocol?: string;
|
protocol?: string;
|
||||||
|
hasConfigureAccess?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ClientDescription = ({ protocol }: ClientDescriptionProps) => {
|
export const ClientDescription = ({
|
||||||
|
protocol,
|
||||||
|
hasConfigureAccess: configure,
|
||||||
|
}: ClientDescriptionProps) => {
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
|
@ -21,7 +25,7 @@ export const ClientDescription = ({ protocol }: ClientDescriptionProps) => {
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
} = useFormContext<ClientRepresentation>();
|
} = useFormContext<ClientRepresentation>();
|
||||||
return (
|
return (
|
||||||
<FormAccess role="manage-clients" unWrap>
|
<FormAccess role="manage-clients" fineGrainedAccess={configure} unWrap>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
labelIcon={
|
labelIcon={
|
||||||
<HelpItem helpText="clients-help:clientId" fieldLabelId="clientId" />
|
<HelpItem helpText="clients-help:clientId" fieldLabelId="clientId" />
|
||||||
|
|
|
@ -128,7 +128,7 @@ const ClientDetailHeader = ({
|
||||||
}, [client, t]);
|
}, [client, t]);
|
||||||
|
|
||||||
const { hasAccess } = useAccess();
|
const { hasAccess } = useAccess();
|
||||||
const isManager = hasAccess("manage-clients");
|
const isManager = hasAccess("manage-clients") || client.access?.configure;
|
||||||
|
|
||||||
const dropdownItems = [
|
const dropdownItems = [
|
||||||
<DropdownItem key="download" onClick={toggleDownloadDialog}>
|
<DropdownItem key="download" onClick={toggleDownloadDialog}>
|
||||||
|
@ -189,12 +189,11 @@ export default function ClientDetails() {
|
||||||
const { profileInfo } = useServerInfo();
|
const { profileInfo } = useServerInfo();
|
||||||
|
|
||||||
const { hasAccess } = useAccess();
|
const { hasAccess } = useAccess();
|
||||||
const isManager = hasAccess("manage-clients");
|
const permissionsEnabled =
|
||||||
const canViewPermissions = hasAccess(
|
!profileInfo?.disabledFeatures?.includes("ADMIN_FINE_GRAINED_AUTHZ") &&
|
||||||
"manage-authorization",
|
hasAccess("manage-authorization");
|
||||||
"manage-clients"
|
const hasManageClients = hasAccess("manage-clients");
|
||||||
);
|
const hasViewUsers = hasAccess("view-users");
|
||||||
const canViewServiceAccountRoles = hasAccess("view-users");
|
|
||||||
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
|
@ -432,14 +431,20 @@ export default function ClientDetails() {
|
||||||
{...route("keys")}
|
{...route("keys")}
|
||||||
>
|
>
|
||||||
{client.protocol === "openid-connect" && (
|
{client.protocol === "openid-connect" && (
|
||||||
<Keys clientId={clientId} save={save} />
|
<Keys
|
||||||
|
clientId={clientId}
|
||||||
|
save={save}
|
||||||
|
hasConfigureAccess={client.access?.configure}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{client.protocol === "saml" && (
|
{client.protocol === "saml" && (
|
||||||
<SamlKeys clientId={clientId} save={save} />
|
<SamlKeys clientId={clientId} save={save} />
|
||||||
)}
|
)}
|
||||||
</Tab>
|
</Tab>
|
||||||
)}
|
)}
|
||||||
{!client.publicClient && !isRealmClient(client) && (
|
{!client.publicClient &&
|
||||||
|
!isRealmClient(client) &&
|
||||||
|
(hasManageClients || client.access?.configure) && (
|
||||||
<Tab
|
<Tab
|
||||||
id="credentials"
|
id="credentials"
|
||||||
title={<TabTitleText>{t("credentials")}</TabTitleText>}
|
title={<TabTitleText>{t("credentials")}</TabTitleText>}
|
||||||
|
@ -463,7 +468,7 @@ export default function ClientDetails() {
|
||||||
loader={loader}
|
loader={loader}
|
||||||
paginated={false}
|
paginated={false}
|
||||||
messageBundle="clients"
|
messageBundle="clients"
|
||||||
isReadOnly={!isManager}
|
isReadOnly={!(hasManageClients || client.access?.configure)}
|
||||||
/>
|
/>
|
||||||
</Tab>
|
</Tab>
|
||||||
{!isRealmClient(client) && !client.bearerOnly && (
|
{!isRealmClient(client) && !client.bearerOnly && (
|
||||||
|
@ -496,6 +501,7 @@ export default function ClientDetails() {
|
||||||
clientName={client.clientId!}
|
clientName={client.clientId!}
|
||||||
clientId={clientId}
|
clientId={clientId}
|
||||||
protocol={client!.protocol!}
|
protocol={client!.protocol!}
|
||||||
|
fineGrainedAccess={client!.access?.manage}
|
||||||
/>
|
/>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab
|
<Tab
|
||||||
|
@ -595,7 +601,7 @@ export default function ClientDetails() {
|
||||||
</RoutableTabs>
|
</RoutableTabs>
|
||||||
</Tab>
|
</Tab>
|
||||||
)}
|
)}
|
||||||
{client!.serviceAccountsEnabled && canViewServiceAccountRoles && (
|
{client!.serviceAccountsEnabled && hasViewUsers && (
|
||||||
<Tab
|
<Tab
|
||||||
id="serviceAccount"
|
id="serviceAccount"
|
||||||
data-testid="serviceAccountTab"
|
data-testid="serviceAccountTab"
|
||||||
|
@ -605,10 +611,7 @@ export default function ClientDetails() {
|
||||||
<ServiceAccount client={client} />
|
<ServiceAccount client={client} />
|
||||||
</Tab>
|
</Tab>
|
||||||
)}
|
)}
|
||||||
{!profileInfo?.disabledFeatures?.includes(
|
{permissionsEnabled && (hasManageClients || client.access?.manage) && (
|
||||||
"ADMIN_FINE_GRAINED_AUTHZ"
|
|
||||||
) &&
|
|
||||||
canViewPermissions && (
|
|
||||||
<Tab
|
<Tab
|
||||||
id="permissions"
|
id="permissions"
|
||||||
data-testid="permissionsTab"
|
data-testid="permissionsTab"
|
||||||
|
|
|
@ -49,7 +49,7 @@ export const ClientSettings = ({
|
||||||
const { realm } = useRealm();
|
const { realm } = useRealm();
|
||||||
|
|
||||||
const { hasAccess } = useAccess();
|
const { hasAccess } = useAccess();
|
||||||
const isManager = hasAccess("manage-clients");
|
const isManager = hasAccess("manage-clients") || client.access?.configure;
|
||||||
|
|
||||||
const [loginThemeOpen, setLoginThemeOpen] = useState(false);
|
const [loginThemeOpen, setLoginThemeOpen] = useState(false);
|
||||||
const loginThemes = useServerInfo().themes!["login"];
|
const loginThemes = useServerInfo().themes!["login"];
|
||||||
|
@ -88,7 +88,10 @@ export const ClientSettings = ({
|
||||||
sections={sections.map((section) => t(section))}
|
sections={sections.map((section) => t(section))}
|
||||||
>
|
>
|
||||||
<Form isHorizontal>
|
<Form isHorizontal>
|
||||||
<ClientDescription protocol={client.protocol} />
|
<ClientDescription
|
||||||
|
protocol={client.protocol}
|
||||||
|
hasConfigureAccess={client.access?.configure}
|
||||||
|
/>
|
||||||
</Form>
|
</Form>
|
||||||
{protocol === "saml" ? (
|
{protocol === "saml" ? (
|
||||||
<SamlConfig />
|
<SamlConfig />
|
||||||
|
@ -96,7 +99,11 @@ export const ClientSettings = ({
|
||||||
!client.bearerOnly && <CapabilityConfig />
|
!client.bearerOnly && <CapabilityConfig />
|
||||||
)}
|
)}
|
||||||
{protocol === "saml" && <SamlSignature />}
|
{protocol === "saml" && <SamlSignature />}
|
||||||
<FormAccess isHorizontal role="manage-clients">
|
<FormAccess
|
||||||
|
isHorizontal
|
||||||
|
role="manage-clients"
|
||||||
|
fineGrainedAccess={client.access?.configure}
|
||||||
|
>
|
||||||
{!client.bearerOnly && (
|
{!client.bearerOnly && (
|
||||||
<>
|
<>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
|
@ -259,7 +266,11 @@ export const ClientSettings = ({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</FormAccess>
|
</FormAccess>
|
||||||
<FormAccess isHorizontal role="manage-clients">
|
<FormAccess
|
||||||
|
isHorizontal
|
||||||
|
role="manage-clients"
|
||||||
|
fineGrainedAccess={client.access?.configure}
|
||||||
|
>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("loginTheme")}
|
label={t("loginTheme")}
|
||||||
labelIcon={
|
labelIcon={
|
||||||
|
@ -383,7 +394,11 @@ export const ClientSettings = ({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</FormAccess>
|
</FormAccess>
|
||||||
<FormAccess isHorizontal role="manage-clients">
|
<FormAccess
|
||||||
|
isHorizontal
|
||||||
|
role="manage-clients"
|
||||||
|
fineGrainedAccess={client.access?.configure}
|
||||||
|
>
|
||||||
{protocol === "openid-connect" && (
|
{protocol === "openid-connect" && (
|
||||||
<>
|
<>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
|
|
|
@ -161,7 +161,10 @@ export default function ClientsSection() {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!isRealmClient(client) && isManager) {
|
if (
|
||||||
|
!isRealmClient(client) &&
|
||||||
|
(isManager || client.access?.configure)
|
||||||
|
) {
|
||||||
actions.push({
|
actions.push({
|
||||||
title: t("common:delete"),
|
title: t("common:delete"),
|
||||||
onClick() {
|
onClick() {
|
||||||
|
|
|
@ -68,7 +68,7 @@ export const GeneralSettings = () => {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<ClientDescription />
|
<ClientDescription hasConfigureAccess />
|
||||||
</FormAccess>
|
</FormAccess>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,6 +23,7 @@ type AdvancedSettingsProps = {
|
||||||
save: () => void;
|
save: () => void;
|
||||||
reset: () => void;
|
reset: () => void;
|
||||||
protocol?: string;
|
protocol?: string;
|
||||||
|
hasConfigureAccess?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AdvancedSettings = ({
|
export const AdvancedSettings = ({
|
||||||
|
@ -30,11 +31,16 @@ export const AdvancedSettings = ({
|
||||||
save,
|
save,
|
||||||
reset,
|
reset,
|
||||||
protocol,
|
protocol,
|
||||||
|
hasConfigureAccess,
|
||||||
}: AdvancedSettingsProps) => {
|
}: AdvancedSettingsProps) => {
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
return (
|
return (
|
||||||
<FormAccess role="manage-realm" isHorizontal>
|
<FormAccess
|
||||||
|
role="manage-realm"
|
||||||
|
fineGrainedAccess={hasConfigureAccess}
|
||||||
|
isHorizontal
|
||||||
|
>
|
||||||
{protocol !== "openid-connect" && (
|
{protocol !== "openid-connect" && (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("assertionLifespan")}
|
label={t("assertionLifespan")}
|
||||||
|
|
|
@ -20,6 +20,7 @@ type AuthenticationOverridesProps = {
|
||||||
save: () => void;
|
save: () => void;
|
||||||
reset: () => void;
|
reset: () => void;
|
||||||
protocol?: string;
|
protocol?: string;
|
||||||
|
hasConfigureAccess?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AuthenticationOverrides = ({
|
export const AuthenticationOverrides = ({
|
||||||
|
@ -27,6 +28,7 @@ export const AuthenticationOverrides = ({
|
||||||
control,
|
control,
|
||||||
save,
|
save,
|
||||||
reset,
|
reset,
|
||||||
|
hasConfigureAccess,
|
||||||
}: AuthenticationOverridesProps) => {
|
}: AuthenticationOverridesProps) => {
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
|
@ -56,7 +58,11 @@ export const AuthenticationOverrides = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormAccess role="manage-clients" isHorizontal>
|
<FormAccess
|
||||||
|
role="manage-clients"
|
||||||
|
fineGrainedAccess={hasConfigureAccess}
|
||||||
|
isHorizontal
|
||||||
|
>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("browserFlow")}
|
label={t("browserFlow")}
|
||||||
fieldId="browserFlow"
|
fieldId="browserFlow"
|
||||||
|
|
|
@ -20,11 +20,13 @@ import { KeycloakTextInput } from "../../components/keycloak-text-input/Keycloak
|
||||||
type FineGrainOpenIdConnectProps = {
|
type FineGrainOpenIdConnectProps = {
|
||||||
save: () => void;
|
save: () => void;
|
||||||
reset: () => void;
|
reset: () => void;
|
||||||
|
hasConfigureAccess?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FineGrainOpenIdConnect = ({
|
export const FineGrainOpenIdConnect = ({
|
||||||
save,
|
save,
|
||||||
reset,
|
reset,
|
||||||
|
hasConfigureAccess,
|
||||||
}: FineGrainOpenIdConnectProps) => {
|
}: FineGrainOpenIdConnectProps) => {
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
const providers = useServerInfo().providers;
|
const providers = useServerInfo().providers;
|
||||||
|
@ -141,7 +143,11 @@ export const FineGrainOpenIdConnect = ({
|
||||||
));
|
));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormAccess role="manage-clients" isHorizontal>
|
<FormAccess
|
||||||
|
role="manage-clients"
|
||||||
|
fineGrainedAccess={hasConfigureAccess}
|
||||||
|
isHorizontal
|
||||||
|
>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("logoUrl")}
|
label={t("logoUrl")}
|
||||||
fieldId="logoUrl"
|
fieldId="logoUrl"
|
||||||
|
|
|
@ -10,16 +10,22 @@ type OpenIdConnectCompatibilityModesProps = {
|
||||||
control: Control<Record<string, any>>;
|
control: Control<Record<string, any>>;
|
||||||
save: () => void;
|
save: () => void;
|
||||||
reset: () => void;
|
reset: () => void;
|
||||||
|
hasConfigureAccess?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const OpenIdConnectCompatibilityModes = ({
|
export const OpenIdConnectCompatibilityModes = ({
|
||||||
control,
|
control,
|
||||||
save,
|
save,
|
||||||
reset,
|
reset,
|
||||||
|
hasConfigureAccess,
|
||||||
}: OpenIdConnectCompatibilityModesProps) => {
|
}: OpenIdConnectCompatibilityModesProps) => {
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
return (
|
return (
|
||||||
<FormAccess role="manage-realm" isHorizontal>
|
<FormAccess
|
||||||
|
role="manage-realm"
|
||||||
|
fineGrainedAccess={hasConfigureAccess}
|
||||||
|
isHorizontal
|
||||||
|
>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("excludeSessionStateFromAuthenticationResponse")}
|
label={t("excludeSessionStateFromAuthenticationResponse")}
|
||||||
fieldId="excludeSessionStateFromAuthenticationResponse"
|
fieldId="excludeSessionStateFromAuthenticationResponse"
|
||||||
|
|
|
@ -347,7 +347,7 @@ export const AuthorizationEvaluate = ({ client }: Props) => {
|
||||||
>
|
>
|
||||||
<FormAccess
|
<FormAccess
|
||||||
isHorizontal
|
isHorizontal
|
||||||
role="manage-clients"
|
role="view-clients"
|
||||||
onSubmit={form.handleSubmit(evaluate)}
|
onSubmit={form.handleSubmit(evaluate)}
|
||||||
>
|
>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
|
@ -487,7 +487,7 @@ export const AuthorizationEvaluate = ({ client }: Props) => {
|
||||||
</FormAccess>
|
</FormAccess>
|
||||||
</FormPanel>
|
</FormPanel>
|
||||||
<FormPanel className="kc-permissions" title={t("common:permissions")}>
|
<FormPanel className="kc-permissions" title={t("common:permissions")}>
|
||||||
<FormAccess isHorizontal role="manage-clients">
|
<FormAccess isHorizontal role="view-clients">
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("applyToResourceType")}
|
label={t("applyToResourceType")}
|
||||||
fieldId="applyToResourceType"
|
fieldId="applyToResourceType"
|
||||||
|
|
|
@ -65,7 +65,7 @@ export const AuthorizationExport = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageSection>
|
<PageSection>
|
||||||
<FormAccess isHorizontal role="manage-realm" className="pf-u-mt-lg">
|
<FormAccess isHorizontal role="view-realm" className="pf-u-mt-lg">
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={t("authDetails")}
|
label={t("authDetails")}
|
||||||
labelIcon={
|
labelIcon={
|
||||||
|
|
|
@ -194,7 +194,7 @@ export default function PermissionDetails() {
|
||||||
<PageSection variant="light">
|
<PageSection variant="light">
|
||||||
<FormAccess
|
<FormAccess
|
||||||
isHorizontal
|
isHorizontal
|
||||||
role="manage-clients"
|
role="view-clients"
|
||||||
onSubmit={handleSubmit(save)}
|
onSubmit={handleSubmit(save)}
|
||||||
>
|
>
|
||||||
<FormProvider {...form}>
|
<FormProvider {...form}>
|
||||||
|
|
|
@ -179,7 +179,7 @@ export default function ResourceDetails() {
|
||||||
<FormProvider {...form}>
|
<FormProvider {...form}>
|
||||||
<FormAccess
|
<FormAccess
|
||||||
isHorizontal
|
isHorizontal
|
||||||
role="manage-clients"
|
role="view-clients"
|
||||||
className="keycloak__resource-details__form"
|
className="keycloak__resource-details__form"
|
||||||
onSubmit={handleSubmit(save)}
|
onSubmit={handleSubmit(save)}
|
||||||
>
|
>
|
||||||
|
|
|
@ -128,7 +128,7 @@ export default function ScopeDetails() {
|
||||||
<PageSection variant="light">
|
<PageSection variant="light">
|
||||||
<FormAccess
|
<FormAccess
|
||||||
isHorizontal
|
isHorizontal
|
||||||
role="manage-clients"
|
role="view-clients"
|
||||||
onSubmit={handleSubmit(save)}
|
onSubmit={handleSubmit(save)}
|
||||||
>
|
>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
|
|
|
@ -192,7 +192,7 @@ export default function PolicyDetails() {
|
||||||
<FormAccess
|
<FormAccess
|
||||||
isHorizontal
|
isHorizontal
|
||||||
onSubmit={handleSubmit(save)}
|
onSubmit={handleSubmit(save)}
|
||||||
role="manage-clients"
|
role="view-clients"
|
||||||
>
|
>
|
||||||
<FormProvider {...form}>
|
<FormProvider {...form}>
|
||||||
<NameDescription prefix="policy" />
|
<NameDescription prefix="policy" />
|
||||||
|
|
|
@ -75,7 +75,7 @@ export default function ImportForm() {
|
||||||
>
|
>
|
||||||
<FormProvider {...form}>
|
<FormProvider {...form}>
|
||||||
<JsonFileUpload id="realm-file" onChange={handleFileChange} />
|
<JsonFileUpload id="realm-file" onChange={handleFileChange} />
|
||||||
<ClientDescription />
|
<ClientDescription hasConfigureAccess />
|
||||||
<FormGroup label={t("common:type")} fieldId="kc-type">
|
<FormGroup label={t("common:type")} fieldId="kc-type">
|
||||||
<KeycloakTextInput
|
<KeycloakTextInput
|
||||||
type="text"
|
type="text"
|
||||||
|
|
|
@ -33,11 +33,12 @@ import { Certificate } from "./Certificate";
|
||||||
type KeysProps = {
|
type KeysProps = {
|
||||||
save: () => void;
|
save: () => void;
|
||||||
clientId: string;
|
clientId: string;
|
||||||
|
hasConfigureAccess?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const attr = "jwt.credential";
|
const attr = "jwt.credential";
|
||||||
|
|
||||||
export const Keys = ({ clientId, save }: KeysProps) => {
|
export const Keys = ({ clientId, save, hasConfigureAccess }: KeysProps) => {
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
|
@ -125,7 +126,11 @@ export const Keys = ({ clientId, save }: KeysProps) => {
|
||||||
</TextContent>
|
</TextContent>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<FormAccess role="manage-clients" isHorizontal>
|
<FormAccess
|
||||||
|
role="manage-clients"
|
||||||
|
fineGrainedAccess={hasConfigureAccess}
|
||||||
|
isHorizontal
|
||||||
|
>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
hasNoPaddingTop
|
hasNoPaddingTop
|
||||||
label={t("useJwksUrl")}
|
label={t("useJwksUrl")}
|
||||||
|
|
|
@ -15,7 +15,7 @@ export const DedicatedScopeDetailsRoute: RouteDef = {
|
||||||
path: "/:realm/clients/:clientId/clientScopes/dedicated/:tab?",
|
path: "/:realm/clients/:clientId/clientScopes/dedicated/:tab?",
|
||||||
component: lazy(() => import("../scopes/DedicatedScopes")),
|
component: lazy(() => import("../scopes/DedicatedScopes")),
|
||||||
breadcrumb: (t) => t("clients:dedicatedScopes"),
|
breadcrumb: (t) => t("clients:dedicatedScopes"),
|
||||||
access: "manage-clients",
|
access: "view-clients",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toDedicatedScope = (
|
export const toDedicatedScope = (
|
||||||
|
|
|
@ -16,7 +16,7 @@ export const NewPermissionRoute: RouteDef = {
|
||||||
path: "/:realm/clients/:id/authorization/permission/new/:permissionType/:selectedId?",
|
path: "/:realm/clients/:id/authorization/permission/new/:permissionType/:selectedId?",
|
||||||
component: lazy(() => import("../authorization/PermissionDetails")),
|
component: lazy(() => import("../authorization/PermissionDetails")),
|
||||||
breadcrumb: (t) => t("clients:createPermission"),
|
breadcrumb: (t) => t("clients:createPermission"),
|
||||||
access: "manage-clients",
|
access: "view-clients",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toNewPermission = (
|
export const toNewPermission = (
|
||||||
|
|
|
@ -9,7 +9,7 @@ export const NewPolicyRoute: RouteDef = {
|
||||||
path: "/:realm/clients/:id/authorization/policy/new/:policyType",
|
path: "/:realm/clients/:id/authorization/policy/new/:policyType",
|
||||||
component: lazy(() => import("../authorization/policy/PolicyDetails")),
|
component: lazy(() => import("../authorization/policy/PolicyDetails")),
|
||||||
breadcrumb: (t) => t("clients:createPolicy"),
|
breadcrumb: (t) => t("clients:createPolicy"),
|
||||||
access: "manage-clients",
|
access: "view-clients",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toCreatePolicy = (
|
export const toCreatePolicy = (
|
||||||
|
|
|
@ -9,7 +9,7 @@ export const NewResourceRoute: RouteDef = {
|
||||||
path: "/:realm/clients/:id/authorization/resource/new",
|
path: "/:realm/clients/:id/authorization/resource/new",
|
||||||
component: lazy(() => import("../authorization/ResourceDetails")),
|
component: lazy(() => import("../authorization/ResourceDetails")),
|
||||||
breadcrumb: (t) => t("clients:createResource"),
|
breadcrumb: (t) => t("clients:createResource"),
|
||||||
access: "manage-clients",
|
access: "view-clients",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toCreateResource = (
|
export const toCreateResource = (
|
||||||
|
|
|
@ -9,7 +9,7 @@ export const NewScopeRoute: RouteDef = {
|
||||||
path: "/:realm/clients/:id/authorization/scope/new",
|
path: "/:realm/clients/:id/authorization/scope/new",
|
||||||
component: lazy(() => import("../authorization/ScopeDetails")),
|
component: lazy(() => import("../authorization/ScopeDetails")),
|
||||||
breadcrumb: (t) => t("clients:createAuthorizationScope"),
|
breadcrumb: (t) => t("clients:createAuthorizationScope"),
|
||||||
access: "manage-clients",
|
access: "view-clients",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toNewScope = (
|
export const toNewScope = (
|
||||||
|
|
|
@ -15,7 +15,7 @@ export const PermissionDetailsRoute: RouteDef = {
|
||||||
path: "/:realm/clients/:id/authorization/permission/:permissionType/:permissionId",
|
path: "/:realm/clients/:id/authorization/permission/:permissionType/:permissionId",
|
||||||
component: lazy(() => import("../authorization/PermissionDetails")),
|
component: lazy(() => import("../authorization/PermissionDetails")),
|
||||||
breadcrumb: (t) => t("clients:permissionDetails"),
|
breadcrumb: (t) => t("clients:permissionDetails"),
|
||||||
access: "manage-clients",
|
access: "view-clients",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toPermissionDetails = (
|
export const toPermissionDetails = (
|
||||||
|
|
|
@ -14,7 +14,7 @@ export const PolicyDetailsRoute: RouteDef = {
|
||||||
path: "/:realm/clients/:id/authorization/policy/:policyId/:policyType",
|
path: "/:realm/clients/:id/authorization/policy/:policyId/:policyType",
|
||||||
component: lazy(() => import("../authorization/policy/PolicyDetails")),
|
component: lazy(() => import("../authorization/policy/PolicyDetails")),
|
||||||
breadcrumb: (t) => t("clients:createPolicy"),
|
breadcrumb: (t) => t("clients:createPolicy"),
|
||||||
access: "manage-clients",
|
access: "view-clients",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toPolicyDetails = (
|
export const toPolicyDetails = (
|
||||||
|
|
|
@ -13,7 +13,7 @@ export const ResourceDetailsRoute: RouteDef = {
|
||||||
path: "/:realm/clients/:id/authorization/resource/:resourceId?",
|
path: "/:realm/clients/:id/authorization/resource/:resourceId?",
|
||||||
component: lazy(() => import("../authorization/ResourceDetails")),
|
component: lazy(() => import("../authorization/ResourceDetails")),
|
||||||
breadcrumb: (t) => t("clients:createResource"),
|
breadcrumb: (t) => t("clients:createResource"),
|
||||||
access: "manage-clients",
|
access: "view-clients",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toResourceDetails = (
|
export const toResourceDetails = (
|
||||||
|
|
|
@ -46,6 +46,7 @@ export type ClientScopesProps = {
|
||||||
clientId: string;
|
clientId: string;
|
||||||
protocol: string;
|
protocol: string;
|
||||||
clientName: string;
|
clientName: string;
|
||||||
|
fineGrainedAccess?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Row = ClientScopeRepresentation & {
|
export type Row = ClientScopeRepresentation & {
|
||||||
|
@ -59,6 +60,7 @@ export const ClientScopes = ({
|
||||||
clientId,
|
clientId,
|
||||||
protocol,
|
protocol,
|
||||||
clientName,
|
clientName,
|
||||||
|
fineGrainedAccess,
|
||||||
}: ClientScopesProps) => {
|
}: ClientScopesProps) => {
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
|
@ -83,7 +85,7 @@ export const ClientScopes = ({
|
||||||
const isDedicatedRow = (value: Row) => value.id === DEDICATED_ROW;
|
const isDedicatedRow = (value: Row) => value.id === DEDICATED_ROW;
|
||||||
|
|
||||||
const { hasAccess } = useAccess();
|
const { hasAccess } = useAccess();
|
||||||
const isManager = hasAccess("manage-clients");
|
const isManager = hasAccess("manage-clients") || fineGrainedAccess;
|
||||||
|
|
||||||
const loader = async (first?: number, max?: number, search?: string) => {
|
const loader = async (first?: number, max?: number, search?: string) => {
|
||||||
const defaultClientScopes =
|
const defaultClientScopes =
|
||||||
|
@ -130,7 +132,7 @@ export const ClientScopes = ({
|
||||||
firstNum,
|
firstNum,
|
||||||
firstNum + Number(max)
|
firstNum + Number(max)
|
||||||
);
|
);
|
||||||
if (firstNum === 0) {
|
if (firstNum === 0 && isManager) {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
id: DEDICATED_ROW,
|
id: DEDICATED_ROW,
|
||||||
|
|
|
@ -20,6 +20,7 @@ import {
|
||||||
} from "../../components/role-mapping/RoleMapping";
|
} from "../../components/role-mapping/RoleMapping";
|
||||||
import type { RoleMappingPayload } from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
|
import type { RoleMappingPayload } from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
|
||||||
import useToggle from "../../utils/useToggle";
|
import useToggle from "../../utils/useToggle";
|
||||||
|
import { useAccess } from "../../context/access/Access";
|
||||||
|
|
||||||
type DedicatedScopeProps = {
|
type DedicatedScopeProps = {
|
||||||
client: ClientRepresentation;
|
client: ClientRepresentation;
|
||||||
|
@ -35,6 +36,9 @@ export const DedicatedScope = ({
|
||||||
const [client, setClient] = useState<ClientRepresentation>(initialClient);
|
const [client, setClient] = useState<ClientRepresentation>(initialClient);
|
||||||
const [hide, toggle] = useToggle();
|
const [hide, toggle] = useToggle();
|
||||||
|
|
||||||
|
const { hasAccess } = useAccess();
|
||||||
|
const isManager = hasAccess("manage-clients") || client.access?.manage;
|
||||||
|
|
||||||
const loader = async () => {
|
const loader = async () => {
|
||||||
const [clients, assignedRoles, effectiveRoles] = await Promise.all([
|
const [clients, assignedRoles, effectiveRoles] = await Promise.all([
|
||||||
adminClient.clients.find(),
|
adminClient.clients.find(),
|
||||||
|
@ -118,7 +122,11 @@ export const DedicatedScope = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageSection>
|
<PageSection>
|
||||||
<FormAccess role="manage-clients" isHorizontal>
|
<FormAccess
|
||||||
|
role="manage-clients"
|
||||||
|
fineGrainedAccess={client.access?.manage}
|
||||||
|
isHorizontal
|
||||||
|
>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
hasNoPaddingTop
|
hasNoPaddingTop
|
||||||
label={t("fullScopeAllowed")}
|
label={t("fullScopeAllowed")}
|
||||||
|
@ -149,6 +157,7 @@ export const DedicatedScope = ({
|
||||||
loader={loader}
|
loader={loader}
|
||||||
save={assignRoles}
|
save={assignRoles}
|
||||||
onHideRolesToggle={toggle}
|
onHideRolesToggle={toggle}
|
||||||
|
isManager={isManager}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -35,7 +35,7 @@ export const ServiceAccount = ({ client }: ServiceAccountProps) => {
|
||||||
const [serviceAccount, setServiceAccount] = useState<UserRepresentation>();
|
const [serviceAccount, setServiceAccount] = useState<UserRepresentation>();
|
||||||
|
|
||||||
const { hasAccess } = useAccess();
|
const { hasAccess } = useAccess();
|
||||||
const isManager = hasAccess("manage-clients");
|
const hasManageClients = hasAccess("manage-clients");
|
||||||
|
|
||||||
useFetch(
|
useFetch(
|
||||||
() =>
|
() =>
|
||||||
|
@ -128,7 +128,7 @@ export const ServiceAccount = ({ client }: ServiceAccountProps) => {
|
||||||
name={client.clientId!}
|
name={client.clientId!}
|
||||||
id={serviceAccount.id!}
|
id={serviceAccount.id!}
|
||||||
type="users"
|
type="users"
|
||||||
isManager={isManager}
|
isManager={hasManageClients || client.access?.configure}
|
||||||
loader={loader}
|
loader={loader}
|
||||||
save={assignRoles}
|
save={assignRoles}
|
||||||
onHideRolesToggle={() => setHide(!hide)}
|
onHideRolesToggle={() => setHide(!hide)}
|
||||||
|
|
Loading…
Reference in a new issue