Repository navigation
Document provided ProtocolMapper implementations #47331
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| = Keycloak Admin API Guide | ||
|
|
||
| include::../attributes.adoc[] | ||
|
|
||
| <#list ctx.guides as guide> | ||
| :links_admin-api_${guide.id}_name: ${guide.title} | ||
| :links_admin-api_${guide.id}_url: #${guide.id} | ||
| </#list> | ||
|
|
||
| <#list ctx.guides as guide> | ||
| include::${guide.template}[leveloffset=+${guide.levelOffset}] | ||
| </#list> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| protocol-mappers |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| <#import "/templates/guide.adoc" as tmpl> | ||
|
|
||
| <@tmpl.guide | ||
| title="Protocol Mappers" | ||
| summary="Discover all built-in protocol mappers and how to use these to define token claims and assertion attributes."> | ||
|
|
||
| Protocol mappers provide a flexible way to define claims used in OAuth 2.0 tokens and endpoints, and attributes in SAML 2.0 assertions. For example adding user attributes or role mappings. | ||
|
|
||
| This page includes a list of all built-in protocol mappers, but {project_name} also supports defining custom protocol mappers through the `ProtocolMapper` SPI. | ||
|
|
||
| Protocol mappers can be created and managed via the {project_name} REST API using the | ||
| link:https://www.keycloak.org/docs-api/latest/rest-api/index.html#_post_adminrealmsrealmclient_scopesclient_scope_idprotocol_mappersmodels[create protocol mapper] endpoint. | ||
| When creating a `ProtocolMapperRepresentation`, the `config` field is a key-value map whose available entries | ||
| depend on the specific mapper type. Th 8000 is page serves as a reference for the expected configuration options | ||
| available in `ProtocolMapperRepresentation.config` for each `ProtocolMapper` implementation. | ||
|
|
||
| For example, to create a protocol mapper that maps a user attribute into an OIDC token claim, you would send a `ProtocolMapperRepresentation` using the following JSON: | ||
|
|
||
| [source,json] | ||
| ---- | ||
| { | ||
| "name": "my-user-attribute-mapper", | ||
| "protocol": "openid-connect", | ||
| "protocolMapper": "oidc-usermodel-attribute-mapper", | ||
| "config": { | ||
| "user.attribute": "phone_number", 8000 | ||
| "claim.name": "phone", | ||
| "jsonType.label": "String" | ||
| } | ||
| } | ||
| ---- | ||
|
|
||
| The `protocolMapper` field corresponds to the "ID" listed in the tables below, and the `config` entries are described in each mapper's configuration table. | ||
|
|
||
| The same mapper can be created programmatically using the {project_name} Java admin client: | ||
|
|
||
| [source,java] | ||
| ---- | ||
| ProtocolMapperRepresentation mapper = new ProtocolMapperRepresentation(); | ||
| mapper.setName("my-user-attribute-mapper"); | ||
| mapper.setProtocol("openid-connect"); | ||
| mapper.setProtocolMapper("oidc-usermodel-attribute-mapper"); | ||
| mapper.setConfig(Map.of( | ||
| "user.attribute", "phone_number", | ||
| "claim.name", "phone", | ||
| "jsonType.label", "String" | ||
| )); | ||
|
|
||
| Keycloak.getInstance(...) | ||
| .realm("my-realm") | ||
| .clientScopes() | ||
| .get("my-client-scope-id") | ||
| .getProtocolMappers() | ||
| .createMapper(mapper); | ||
| ---- | ||
|
|
||
| [discrete] | ||
| == Overview | ||
|
|
||
| The below table is an index of all available `ProtocolMapper` implementations provided by {project_name}, | ||
| grouped by the associated protocol. | ||
|
|
||
|
|
||
| [cols="1,2",options="header"] | ||
| |=== | ||
| |ID |Description | ||
| <#list ctx.protocolMappers.getMappersByProtocol(["openid-connect", "oid4vc", "saml", "docker-v2"]) as protocol, categoryMap> | ||
|
|
||
| 2+a|**${protocol}** | ||
| <#list categoryMap as category, mapperList> | ||
| <#list mapperList as mapper> | ||
| |<<${mapper.id()},${mapper.id()}>> | ||
| |${(mapper.helpText())!"_No description available._"} | ||
| </#list> | ||
| </#list> | ||
| </#list> | ||
|
|
||
| |=== | ||
|
|
||
| <#assign mappers = ctx.protocolMappers.getMappersByProtocol(["openid-connect", "oid4vc", "saml", "docker-v2"]) /> | ||
| <#list mappers as protocol, categoryMap> | ||
|
|
||
| == ${protocol} | ||
|
|
||
| The following section contains all `ProtocolMapper` implementations associated with the ${protocol} protocol. For each | ||
| implementation we provide the "ID" of the `ProtocolMapper` and a table describing the supported configuration properties. | ||
|
|
||
| <#list categoryMap as category, mapperList> | ||
| <#list mapperList as mapper> | ||
|
|
||
| [#${mapper.id()}] | ||
| === ${mapper.displayType()} | ||
|
|
||
| ${(mapper.helpText())!"_No description available._"} | ||
|
|
||
| *ID*: `${mapper.id()}` | ||
|
|
||
| <#if mapper.configProperties()?has_content> | ||
| [cols="1,1,1,1,2",options="header"] | ||
|
stianst marked this conversation as resolved.
Resolved
|
||
| |=== | ||
| |Name |Property |Type |Default |Description | ||
|
|
||
| <#list mapper.configProperties() as prop> | ||
| <#if prop.getName()??> | ||
| |${ctx.protocolMappers.resolveLabel(prop.getLabel()!prop.getName())} | ||
| |`${prop.getName()}` | ||
| |`${prop.getType()!"String"}` | ||
| |<#if prop.getDefaultValue()??>`${prop.getDefaultValue()?string}`<#else>_None_</#if> | ||
| |${ctx.protocolMappers.resolveTooltip((prop.getHelpText())!"")} | ||
|
|
||
| </#if> | ||
| </#list> | ||
| |=== | ||
| </#if> | ||
|
|
||
| </#list> | ||
| </#list> | ||
| </#list> | ||
|
|
||
| </@tmpl.guide> | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
101 changes: 101 additions & 0 deletions
101
docs/maven-plugin/src/main/java/org/keycloak/guides/maven/ProtocolMappers.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| package org.keycloak.guides.maven; | ||
|
|
||
| import java.io.IOException; | ||
| import java.io.Reader; | ||
| import java.nio.file.Files; | ||
| import java.nio.file.Path; | ||
| import java.util.Comparator; | ||
| import java.util.LinkedHashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Properties; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| import org.keycloak.protocol.ProtocolMapper; | ||
| import org.keycloak.provider.ProviderConfigProperty; | ||
| import org.keycloak.provider.ProviderManager; | ||
| import org.keycloak.quarkus.runtime.Providers; | ||
|
|
||
| public class ProtocolMappers { | ||
|
|
||
| private static final String MESSAGES_RELATIVE_PATH = "js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties"; | ||
|
|
||
| private final Map<String, Map<String, List<ProtocolMapperInfo>>> mappers; | ||
| private final Properties messages; | ||
|
|
||
| public ProtocolMappers(Path projectRootDir) { | ||
| messages = loadMessages(projectRootDir); | ||
| ProviderManager providerManager = Providers.getProviderManager(Thread.currentThread().getContextClassLoader()); | ||
|
|
||
| mappers = providerManager.loadSpis().stream() | ||
| .filter(spi -> spi.getName().equals("protocol-mapper")) | ||
| .findFirst() | ||
| .<Map<String, Map<String, List<ProtocolMapperInfo>>>>map(spi -> providerManager.load(spi).stream() | ||
| .map(ProtocolMapper.class::cast) | ||
| .sorted(Comparator.comparing(ProtocolMapper::getDisplayType)) | ||
| .map(mapper -> new ProtocolMapperInfo( | ||
| mapper.getId(), | ||
| mapper.getClass().getName(), | ||
| mapper.getProtocol(), | ||
| mapper.getDisplayType(), | ||
| mapper.getDisplayCategory(), | ||
| mapper.getHelpText(), | ||
| mapper.getPriority(), | ||
| mapper.getConfigProperties() | ||
| )) | ||
| .collect(Collectors.groupingBy( | ||
| ProtocolMapperInfo::protocol, | ||
| LinkedHashMap::new, | ||
| Collectors.groupingBy( | ||
| ProtocolMapperInfo::category, | ||
| LinkedHashMap::new, | ||
| Collectors.toList() | ||
| ) | ||
| ))) | ||
| .orElse(Map.of()); | ||
| } | ||
|
|
||
| public Map<String, Map<String, List<ProtocolMapperInfo>>> getMappersByProtocol(List<String> protocols) { | ||
| Map<String, Map<String, List<ProtocolMapperInfo>>> ordered = new LinkedHashMap<>(); | ||
| for (String protocol : protocols) { | ||
| Map<String, List<ProtocolMapperInfo>> categoryMap = mappers.get(protocol); | ||
| if (categoryMap != null) { | ||
| ordered.put(protocol, categoryMap); | ||
| } | ||
| } | ||
| return ordered; | ||
| } | ||
|
|
||
| public String resolveLabel(String label) { | ||
| if (label != null && label.endsWith(".label")) { | ||
| return messages.getProperty(label, label); | ||
| } | ||
| return label; | ||
| } | ||
|
|
||
| public String resolveTooltip(String tooltip) { | ||
| if (tooltip != null && tooltip.endsWith(".tooltip")) { | ||
| return messages.getProperty(tooltip, tooltip); | ||
| } | ||
| return tooltip; | ||
| } | ||
|
|
||
| private static Properties loadMessages(Path projectRootDir) { | ||
| Properties props = new Properties(); | ||
| Path messagesFile = projectRootDir.resolve(MESSAGES_RELATIVE_PATH); | ||
| if (Files.exists(messagesFile)) { | ||
| try (Reader reader = Files.newBufferedReader(messagesFile)) { | ||
| props.load(reader); | ||
| } catch (IOException e) { | ||
| throw new RuntimeException("Failed to load admin messages properties from " + messagesFile, e); | ||
| } | ||
| } | ||
| return props; | ||
| } | ||
|
|
||
| public record ProtocolMapperInfo(String id, String implementationClass, String protocol, String displayType, | ||
| String category, String helpText, int priority, | ||
| List<ProviderConfigProperty> configProperties) { | ||
|
|
||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be good to include an example here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to include two examples 1) raw JSON payload 2) Java admin client
ProtocolMapperRepresentation.