mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-09 15:02:05 -03:30
New Identity Provider condition for client policies
Closes #44442 Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
parent
a9c1bcc9bd
commit
ae7e7ba084
@ -2848,6 +2848,7 @@ searchScope=Search scope
|
||||
dateFrom=Date(from)
|
||||
importAdded_one=One record added.
|
||||
clientAccessType=It uses the client's access type (confidential, public, bearer-only) to determine whether the policy is applied. Condition is checked during most of OpenID Connect requests (Authorization requests, token requests, introspection endpoint request, etc.). Confidential client has enabled client authentication when public client has disabled client authentication. Bearer-only is a deprecated client type.
|
||||
identityProviderAlias=Condition that checks the Identity Provider that is involved in the client request. Only applies to operations in which an IdP is involved (for example JWT Authorization grant).
|
||||
firstName=First name
|
||||
emptySecondaryAction=Configure a new mapper
|
||||
defaultGroupAdded_one=New group added to the default groups
|
||||
|
||||
@ -1,9 +1,5 @@
|
||||
import type ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation";
|
||||
import {
|
||||
HelpItem,
|
||||
SelectControl,
|
||||
useFetch,
|
||||
} from "@keycloak/keycloak-ui-shared";
|
||||
import { HelpItem, SelectControl } from "@keycloak/keycloak-ui-shared";
|
||||
import {
|
||||
Checkbox,
|
||||
FormGroup,
|
||||
@ -20,13 +16,8 @@ import { FormAccess } from "../../components/form/FormAccess";
|
||||
import { convertAttributeNameToForm } from "../../util";
|
||||
import useIsFeatureEnabled, { Feature } from "../../utils/useIsFeatureEnabled";
|
||||
import { FormFields } from "../ClientDetails";
|
||||
import { MultiValuedListComponent } from "../../components/dynamic/MultivaluedListComponent";
|
||||
import IdentityProviderRepresentation, {
|
||||
IdentityProviderType,
|
||||
} from "@keycloak/keycloak-admin-client/lib/defs/identityProviderRepresentation";
|
||||
import { useAdminClient } from "../../admin-client";
|
||||
import { useState } from "react";
|
||||
import { IdentityProvidersQuery } from "@keycloak/keycloak-admin-client/lib/resources/identityProviders";
|
||||
import { IdentityProviderSelect } from "../../components/identity-provider/IdentityProviderSelect";
|
||||
import { IdentityProviderType } from "@keycloak/keycloak-admin-client/lib/defs/identityProviderRepresentation";
|
||||
import { useAccess } from "../../context/access/Access";
|
||||
|
||||
type CapabilityConfigProps = {
|
||||
@ -38,7 +29,6 @@ export const CapabilityConfig = ({
|
||||
unWrap,
|
||||
protocol: type,
|
||||
}: CapabilityConfigProps) => {
|
||||
const { adminClient } = useAdminClient();
|
||||
const { t } = useTranslation();
|
||||
const { control, watch, setValue } = useFormContext<FormFields>();
|
||||
const protocol = type || watch("protocol");
|
||||
@ -51,28 +41,8 @@ export const CapabilityConfig = ({
|
||||
false,
|
||||
);
|
||||
const isFeatureEnabled = useIsFeatureEnabled();
|
||||
const [idps, setIdps] = useState<IdentityProviderRepresentation[]>([]);
|
||||
const [search, setSearch] = useState("");
|
||||
const { hasSomeAccess } = useAccess();
|
||||
const showIdentityProviders = hasSomeAccess("view-identity-providers");
|
||||
useFetch(
|
||||
async () => {
|
||||
if (!showIdentityProviders) {
|
||||
return [];
|
||||
}
|
||||
const params: IdentityProvidersQuery = {
|
||||
max: 20,
|
||||
realmOnly: true,
|
||||
};
|
||||
if (search) {
|
||||
params.search = search;
|
||||
}
|
||||
params.type = IdentityProviderType.JWT_AUTHORIZATION_GRANT;
|
||||
return await adminClient.identityProviders.find(params);
|
||||
},
|
||||
setIdps,
|
||||
[search],
|
||||
);
|
||||
return (
|
||||
<FormAccess
|
||||
isHorizontal
|
||||
@ -436,17 +406,19 @@ export const CapabilityConfig = ({
|
||||
{isFeatureEnabled(Feature.JWTAuthorizationGrant) &&
|
||||
showIdentityProviders &&
|
||||
jwtAuthorizationGrantEnabled.toString() === "true" && (
|
||||
<MultiValuedListComponent
|
||||
<IdentityProviderSelect
|
||||
name={convertAttributeNameToForm<FormFields>(
|
||||
"attributes.oauth2.jwt.authorization.grant.idp",
|
||||
)}
|
||||
label={t("jwtAuthorizationGrantIdp")}
|
||||
helpText={t("jwtAuthorizationGrantIdpHelp")}
|
||||
convertToName={convertAttributeNameToForm}
|
||||
stringify
|
||||
identityProviderType={
|
||||
IdentityProviderType.JWT_AUTHORIZATION_GRANT
|
||||
}
|
||||
isDisabled={clientAuthentication}
|
||||
options={idps.map(({ alias }) => alias ?? "")}
|
||||
onSearch={setSearch}
|
||||
realmOnly
|
||||
stringify
|
||||
/>
|
||||
)}
|
||||
{isFeatureEnabled(Feature.DPoP) && (
|
||||
|
||||
@ -0,0 +1,10 @@
|
||||
import type { ComponentProps } from "./components";
|
||||
import { IdentityProviderSelect } from "../identity-provider/IdentityProviderSelect";
|
||||
|
||||
export const IdentityProviderMultiSelectComponent = (props: ComponentProps) => (
|
||||
<IdentityProviderSelect
|
||||
{...props}
|
||||
convertToName={props.convertToName}
|
||||
name={props.name!}
|
||||
/>
|
||||
);
|
||||
@ -1,4 +1,5 @@
|
||||
import {
|
||||
FormErrorText,
|
||||
HelpItem,
|
||||
KeycloakSelect,
|
||||
SelectVariant,
|
||||
@ -17,6 +18,10 @@ function toStringValue(formValue: string[]): string {
|
||||
return formValue.join("##");
|
||||
}
|
||||
|
||||
type MultiValuedListComponentProps = ComponentProps & {
|
||||
variant?: `${SelectVariant}`;
|
||||
};
|
||||
|
||||
export const MultiValuedListComponent = ({
|
||||
name,
|
||||
label,
|
||||
@ -28,9 +33,13 @@ export const MultiValuedListComponent = ({
|
||||
required,
|
||||
convertToName,
|
||||
onSearch,
|
||||
}: ComponentProps) => {
|
||||
variant = SelectVariant.typeaheadMulti,
|
||||
}: MultiValuedListComponentProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { control } = useFormContext();
|
||||
const {
|
||||
control,
|
||||
formState: { errors },
|
||||
} = useFormContext();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
function setSearch(value: string) {
|
||||
@ -39,6 +48,14 @@ export const MultiValuedListComponent = ({
|
||||
}
|
||||
}
|
||||
|
||||
const convertedName = convertToName(name!);
|
||||
|
||||
const getError = () => {
|
||||
return convertedName
|
||||
.split(".")
|
||||
.reduce((record: any, key) => record?.[key], errors);
|
||||
};
|
||||
|
||||
return (
|
||||
<FormGroup
|
||||
label={t(label!)}
|
||||
@ -47,53 +64,78 @@ export const MultiValuedListComponent = ({
|
||||
isRequired={required}
|
||||
>
|
||||
<Controller
|
||||
name={convertToName(name!)}
|
||||
name={convertedName}
|
||||
control={control}
|
||||
defaultValue={
|
||||
stringify ? defaultValue || "" : defaultValue ? [defaultValue] : []
|
||||
stringify || variant !== SelectVariant.typeaheadMulti
|
||||
? defaultValue || ""
|
||||
: defaultValue
|
||||
? [defaultValue]
|
||||
: []
|
||||
}
|
||||
rules={{
|
||||
required: { value: required || false, message: t("required") },
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<KeycloakSelect
|
||||
toggleId={name}
|
||||
data-testid={name}
|
||||
isDisabled={isDisabled}
|
||||
chipGroupProps={{
|
||||
numChips: 3,
|
||||
expandedText: t("hide"),
|
||||
collapsedText: t("showRemaining"),
|
||||
}}
|
||||
variant={SelectVariant.typeaheadMulti}
|
||||
typeAheadAriaLabel="Select"
|
||||
onToggle={(isOpen) => setOpen(isOpen)}
|
||||
selections={
|
||||
stringify ? stringToMultiline(field.value) : field.value
|
||||
}
|
||||
onSelect={(v) => {
|
||||
const option = v.toString();
|
||||
const values = stringify
|
||||
? stringToMultiline(field.value)
|
||||
: field.value;
|
||||
let newValue;
|
||||
if (values.includes(option)) {
|
||||
newValue = values.filter((item: string) => item !== option);
|
||||
} else {
|
||||
newValue = [...values, option];
|
||||
<>
|
||||
<KeycloakSelect
|
||||
toggleId={name}
|
||||
data-testid={name}
|
||||
isDisabled={isDisabled}
|
||||
chipGroupProps={{
|
||||
numChips: 3,
|
||||
expandedText: t("hide"),
|
||||
collapsedText: t("showRemaining"),
|
||||
}}
|
||||
variant={variant}
|
||||
typeAheadAriaLabel={t("choose")}
|
||||
onToggle={setOpen}
|
||||
selections={
|
||||
stringify && variant === SelectVariant.typeaheadMulti
|
||||
? stringToMultiline(field.value)
|
||||
: field.value
|
||||
}
|
||||
field.onChange(stringify ? toStringValue(newValue) : newValue);
|
||||
}}
|
||||
onClear={() => {
|
||||
field.onChange(stringify ? "" : []);
|
||||
}}
|
||||
onFilter={(value) => setSearch(value)}
|
||||
isOpen={open}
|
||||
aria-label={t(label!)}
|
||||
>
|
||||
{options?.map((option) => (
|
||||
<SelectOption key={option} value={option}>
|
||||
{option}
|
||||
</SelectOption>
|
||||
))}
|
||||
</KeycloakSelect>
|
||||
onSelect={(v) => {
|
||||
const option = v.toString();
|
||||
if (variant === SelectVariant.typeaheadMulti) {
|
||||
const values = stringify
|
||||
? stringToMultiline(field.value)
|
||||
: field.value;
|
||||
let newValue;
|
||||
if (values.includes(option)) {
|
||||
newValue = values.filter((item: string) => item !== option);
|
||||
} else if (option !== "") {
|
||||
newValue = [...values, option];
|
||||
} else {
|
||||
newValue = values;
|
||||
}
|
||||
field.onChange(
|
||||
stringify && variant === SelectVariant.typeaheadMulti
|
||||
? toStringValue(newValue)
|
||||
: newValue,
|
||||
);
|
||||
} else {
|
||||
field.onChange(option);
|
||||
}
|
||||
}}
|
||||
onClear={() => {
|
||||
field.onChange(
|
||||
stringify || variant !== SelectVariant.typeaheadMulti
|
||||
? ""
|
||||
: [],
|
||||
);
|
||||
}}
|
||||
onFilter={setSearch}
|
||||
isOpen={open}
|
||||
>
|
||||
{options?.map((option) => (
|
||||
<SelectOption key={option} value={option}>
|
||||
{option}
|
||||
</SelectOption>
|
||||
))}
|
||||
</KeycloakSelect>
|
||||
{getError() && <FormErrorText message={getError().message} />}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
@ -3,6 +3,7 @@ import { FunctionComponent } from "react";
|
||||
|
||||
import { BooleanComponent } from "./BooleanComponent";
|
||||
import { ClientSelectComponent } from "./ClientSelectComponent";
|
||||
import { IdentityProviderMultiSelectComponent } from "./IdentityProviderMultiSelectComponent";
|
||||
import { FileComponent } from "./FileComponent";
|
||||
import { GroupComponent } from "./GroupComponent";
|
||||
import { ListComponent } from "./ListComponent";
|
||||
@ -45,6 +46,7 @@ type ComponentType =
|
||||
| "Group"
|
||||
| "MultivaluedList"
|
||||
| "ClientList"
|
||||
| "IdentityProviderMultiList"
|
||||
| "UserProfileAttributeList"
|
||||
| "MultivaluedString"
|
||||
| "File"
|
||||
@ -65,6 +67,7 @@ export const COMPONENTS: {
|
||||
Map: MapComponent,
|
||||
Group: GroupComponent,
|
||||
ClientList: ClientSelectComponent,
|
||||
IdentityProviderMultiList: IdentityProviderMultiSelectComponent,
|
||||
UserProfileAttributeList: UserProfileAttributeListComponent,
|
||||
MultivaluedList: MultiValuedListComponent,
|
||||
MultivaluedString: MultiValuedStringComponent,
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
import type IdentityProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderRepresentation";
|
||||
import { IdentityProviderType } from "@keycloak/keycloak-admin-client/lib/defs/identityProviderRepresentation";
|
||||
import type { IdentityProvidersQuery } from "@keycloak/keycloak-admin-client/lib/resources/identityProviders";
|
||||
import { SelectVariant, useFetch } from "@keycloak/keycloak-ui-shared";
|
||||
import { useState } from "react";
|
||||
import { useAdminClient } from "../../admin-client";
|
||||
import type { ComponentProps } from "../dynamic/components";
|
||||
import { MultiValuedListComponent } from "../dynamic/MultivaluedListComponent";
|
||||
|
||||
type IdentityProviderSelectProps = ComponentProps & {
|
||||
variant?: `${SelectVariant}`;
|
||||
identityProviderType?: IdentityProviderType;
|
||||
realmOnly?: boolean;
|
||||
};
|
||||
|
||||
export const IdentityProviderSelect = ({
|
||||
identityProviderType = IdentityProviderType.ANY,
|
||||
realmOnly = false,
|
||||
...props
|
||||
}: IdentityProviderSelectProps) => {
|
||||
const { adminClient } = useAdminClient();
|
||||
|
||||
const [identityProviders, setIdentityProviders] = useState<
|
||||
IdentityProviderRepresentation[]
|
||||
>([]);
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
useFetch(
|
||||
() => {
|
||||
const params: IdentityProvidersQuery = {
|
||||
max: 20,
|
||||
type: identityProviderType,
|
||||
realmOnly: realmOnly,
|
||||
};
|
||||
if (search) {
|
||||
params.search = search;
|
||||
}
|
||||
return adminClient.identityProviders.find(params);
|
||||
},
|
||||
(identityProviders) => setIdentityProviders(identityProviders),
|
||||
[search],
|
||||
);
|
||||
|
||||
return (
|
||||
<MultiValuedListComponent
|
||||
{...props}
|
||||
onSearch={setSearch}
|
||||
options={identityProviders.map(({ alias }) => alias!)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -84,6 +84,8 @@ public class ProviderConfigProperty {
|
||||
*/
|
||||
public static final String URL_TYPE ="Url";
|
||||
|
||||
public static final String IDENTITY_PROVIDER_MULTI_LIST_TYPE="IdentityProviderMultiList"; // only in admin console, not in themes
|
||||
|
||||
protected String name;
|
||||
protected String label;
|
||||
protected String helpText;
|
||||
|
||||
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2025 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.services.clientpolicy.condition;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.representations.idm.ClientPolicyConditionConfigurationRepresentation;
|
||||
import org.keycloak.services.clientpolicy.ClientPolicyContext;
|
||||
import org.keycloak.services.clientpolicy.ClientPolicyException;
|
||||
import org.keycloak.services.clientpolicy.ClientPolicyVote;
|
||||
import org.keycloak.services.clientpolicy.context.JWTAuthorizationGrantContext;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author rmartinc
|
||||
*/
|
||||
public class IdentityProviderCondition extends AbstractClientPolicyConditionProvider<IdentityProviderCondition.Configuration> {
|
||||
|
||||
public static class Configuration extends ClientPolicyConditionConfigurationRepresentation {
|
||||
|
||||
@JsonProperty(IdentityProviderConditionFactory.IDENTITY_PROVIDERS_ALIASES)
|
||||
protected List<String> identityProviderAliases;
|
||||
|
||||
public List<String> getIdentityProviderAliases() {
|
||||
return identityProviderAliases;
|
||||
}
|
||||
|
||||
public void setIdentityProviderAliases(List<String> identityProviderAliases) {
|
||||
this.identityProviderAliases = identityProviderAliases;
|
||||
}
|
||||
}
|
||||
|
||||
public IdentityProviderCondition(KeycloakSession session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<Configuration> getConditionConfigurationClass() {
|
||||
return Configuration.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProviderId() {
|
||||
return IdentityProviderConditionFactory.PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientPolicyVote applyPolicy(ClientPolicyContext context) throws ClientPolicyException {
|
||||
return switch (context.getEvent()) {
|
||||
case JWT_AUTHORIZATION_GRANT -> isIdentityProvider(((JWTAuthorizationGrantContext) context).getIdentityProvider().getAlias())
|
||||
? ClientPolicyVote.YES
|
||||
: ClientPolicyVote.NO;
|
||||
default -> ClientPolicyVote.ABSTAIN;
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isIdentityProvider(String identityProviderAlias) throws ClientPolicyException {
|
||||
return configuration.identityProviderAliases.contains(identityProviderAlias);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2025 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.services.clientpolicy.condition;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||
|
||||
/**
|
||||
* <p>Condition that defines a list of Identity Provider aliases and checks if the
|
||||
* alias in the client policy context is (or is not) part of that list.</p>
|
||||
*
|
||||
* @author rmartinc
|
||||
*/
|
||||
public class IdentityProviderConditionFactory extends AbstractClientPolicyConditionProviderFactory {
|
||||
|
||||
public static final String PROVIDER_ID = "identity-provider-alias";
|
||||
public static final String IDENTITY_PROVIDERS_ALIASES = "identity_provider_aliases";
|
||||
|
||||
@Override
|
||||
public ClientPolicyConditionProvider create(KeycloakSession session) {
|
||||
return new IdentityProviderCondition(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return """
|
||||
Condition that checks the Identity Provider that is involved in the client request.
|
||||
Only applies to operations in which an IdP is involved (for example JWT Authorization grant).
|
||||
""";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
List<ProviderConfigProperty> properties = ProviderConfigurationBuilder.create()
|
||||
.property()
|
||||
.name(IDENTITY_PROVIDERS_ALIASES)
|
||||
.type(ProviderConfigProperty.IDENTITY_PROVIDER_MULTI_LIST_TYPE)
|
||||
.label("Identity provider aliases")
|
||||
.helpText("List of Identity Provider aliases to take into consideration for the condition.")
|
||||
.required(Boolean.TRUE)
|
||||
.add()
|
||||
.build();
|
||||
addCommonConfigProperties(properties);
|
||||
return properties;
|
||||
}
|
||||
|
||||
}
|
||||
@ -10,3 +10,4 @@ org.keycloak.services.clientpolicy.condition.ClientProtocolConditionFactory
|
||||
org.keycloak.services.clientpolicy.condition.ClientAttributesConditionFactory
|
||||
org.keycloak.services.clientpolicy.condition.AcrConditionFactory
|
||||
org.keycloak.services.clientpolicy.condition.GrantTypeConditionFactory
|
||||
org.keycloak.services.clientpolicy.condition.IdentityProviderConditionFactory
|
||||
|
||||
@ -8,6 +8,7 @@ import org.keycloak.representations.idm.ClientPolicyConditionConfigurationRepres
|
||||
import org.keycloak.representations.idm.ClientPolicyConditionRepresentation;
|
||||
import org.keycloak.representations.idm.ClientPolicyRepresentation;
|
||||
import org.keycloak.services.clientpolicy.condition.GrantTypeCondition;
|
||||
import org.keycloak.services.clientpolicy.condition.IdentityProviderCondition;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
@ -30,14 +31,24 @@ public class ClientPolicyBuilder {
|
||||
return new ClientPolicyBuilder(rep);
|
||||
}
|
||||
|
||||
public static GrantTypeCondition.Configuration grantTypeConditionConfiguration(String... types) {
|
||||
public static GrantTypeCondition.Configuration grantTypeConditionConfiguration(boolean negativeLogic, String... types) {
|
||||
GrantTypeCondition.Configuration config = new GrantTypeCondition.Configuration();
|
||||
config.setNegativeLogic(negativeLogic);
|
||||
if (types != null && types.length > 0) {
|
||||
config.setGrantTypes(List.of(types));
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
public static IdentityProviderCondition.Configuration identityProviderConditionConfiguration(boolean negativeLogic, String... aliases) {
|
||||
IdentityProviderCondition.Configuration config = new IdentityProviderCondition.Configuration();
|
||||
config.setNegativeLogic(negativeLogic);
|
||||
if (aliases != null && aliases.length > 0) {
|
||||
config.setIdentityProviderAliases(List.of(aliases));
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
public static ClientPolicyBuilder update(ClientPolicyRepresentation rep) {
|
||||
return new ClientPolicyBuilder(rep);
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ public class JWTAuthorizationGrantDownscopeClientPoliciesTest extends BaseAbstra
|
||||
.name("policy")
|
||||
.description("description of policy")
|
||||
.condition(GrantTypeConditionFactory.PROVIDER_ID, ClientPolicyBuilder.grantTypeConditionConfiguration(
|
||||
OAuth2Constants.JWT_AUTHORIZATION_GRANT))
|
||||
false, OAuth2Constants.JWT_AUTHORIZATION_GRANT))
|
||||
.profile("executor")
|
||||
.build());
|
||||
|
||||
|
||||
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2025 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.tests.oauth;
|
||||
|
||||
import org.keycloak.services.clientpolicy.condition.IdentityProviderConditionFactory;
|
||||
import org.keycloak.services.clientpolicy.executor.DownscopeAssertionGrantEnforcerExecutorFactory;
|
||||
import org.keycloak.testframework.annotations.InjectRealm;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.realm.ClientPolicyBuilder;
|
||||
import org.keycloak.testframework.realm.ClientProfileBuilder;
|
||||
import org.keycloak.testframework.realm.ManagedRealm;
|
||||
import org.keycloak.testframework.realm.RealmConfigBuilder;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author rmartinc
|
||||
*/
|
||||
@KeycloakIntegrationTest(config = JWTAuthorizationGrantTest.JWTAuthorizationGrantServerConfig.class)
|
||||
public class JWTIdentityProviderConditionDownscopeClientPoliciesTest extends JWTAuthorizationGrantDownscopeClientPoliciesTest {
|
||||
|
||||
@InjectRealm(config = JWTAuthorizationGranthRealmConfig.class)
|
||||
protected ManagedRealm realm;
|
||||
|
||||
public static class JWTAuthorizationGranthRealmConfig extends OIDCIdentityProviderJWTAuthorizationGrantTest.JWTAuthorizationGrantRealmConfig {
|
||||
|
||||
@Override
|
||||
public RealmConfigBuilder configure(RealmConfigBuilder realm) {
|
||||
super.configure(realm);
|
||||
|
||||
realm.clientProfile(ClientProfileBuilder.create()
|
||||
.name("executor")
|
||||
.description("executor description")
|
||||
.executor(DownscopeAssertionGrantEnforcerExecutorFactory.PROVIDER_ID, null)
|
||||
.build());
|
||||
|
||||
realm.clientPolicy(ClientPolicyBuilder.create()
|
||||
.name("policy")
|
||||
.description("description of policy")
|
||||
.condition(IdentityProviderConditionFactory.PROVIDER_ID, ClientPolicyBuilder.identityProviderConditionConfiguration(
|
||||
false, "other", IDP_ALIAS))
|
||||
.profile("executor")
|
||||
.build());
|
||||
|
||||
return realm;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2025 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.tests.oauth;
|
||||
|
||||
import org.keycloak.services.clientpolicy.condition.IdentityProviderConditionFactory;
|
||||
import org.keycloak.services.clientpolicy.executor.DownscopeAssertionGrantEnforcerExecutorFactory;
|
||||
import org.keycloak.testframework.annotations.InjectRealm;
|
||||
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.testframework.realm.ClientPolicyBuilder;
|
||||
import org.keycloak.testframework.realm.ClientProfileBuilder;
|
||||
import org.keycloak.testframework.realm.ManagedRealm;
|
||||
import org.keycloak.testframework.realm.RealmConfigBuilder;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author rmartinc
|
||||
*/
|
||||
@KeycloakIntegrationTest(config = JWTAuthorizationGrantTest.JWTAuthorizationGrantServerConfig.class)
|
||||
public class JWTNegativeIdentityProviderConditionDownscopeClientPoliciesTest extends JWTAuthorizationGrantDownscopeClientPoliciesTest {
|
||||
|
||||
@InjectRealm(config = JWTAuthorizationGranthRealmConfig.class)
|
||||
protected ManagedRealm realm;
|
||||
|
||||
public static class JWTAuthorizationGranthRealmConfig extends OIDCIdentityProviderJWTAuthorizationGrantTest.JWTAuthorizationGrantRealmConfig {
|
||||
|
||||
@Override
|
||||
public RealmConfigBuilder configure(RealmConfigBuilder realm) {
|
||||
super.configure(realm);
|
||||
|
||||
realm.clientProfile(ClientProfileBuilder.create()
|
||||
.name("executor")
|
||||
.description("executor description")
|
||||
.executor(DownscopeAssertionGrantEnforcerExecutorFactory.PROVIDER_ID, null)
|
||||
.build());
|
||||
|
||||
realm.clientPolicy(ClientPolicyBuilder.create()
|
||||
.name("policy")
|
||||
.description("description of policy")
|
||||
.condition(IdentityProviderConditionFactory.PROVIDER_ID, ClientPolicyBuilder.identityProviderConditionConfiguration(
|
||||
true, "other"))
|
||||
.profile("executor")
|
||||
.build());
|
||||
|
||||
return realm;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user