diff --git a/.gitignore b/.gitignore index df6cd6e..db75fcc 100644 --- a/.gitignore +++ b/.gitignore @@ -172,3 +172,4 @@ buildNumber.properties .classpath # End of https://www.toptal.com/developers/gitignore/api/maven,intellij+all,eclipse +/patch-tool/.work/ diff --git a/patch-tool/README.md b/patch-tool/README.md new file mode 100644 index 0000000..50d13ef --- /dev/null +++ b/patch-tool/README.md @@ -0,0 +1,116 @@ +# Keycloak Git Branch Diff & Patch Tool + +This script is designed to work **specifically with the [Keycloak Git repository](https://github.com/keycloak/keycloak)**. It compares selected Java files between two branches of the Keycloak repo, generates a patch, and (optionally) applies that patch to a target directory. + +## Features + +- Clones the Keycloak repository into a temporary **.work** directory +- Extracts only files listed in `keycloak-files.txt` (tailored for Keycloak source paths) +- Compares two branches and creates a **unified diff patch** +- Cleans up file paths and replaces package names in the patch for project-specific needs +- Optionally applies the patch to the **parent directory** of the current folder + +## Requirements + +- **Bash** (Unix/Linux/macOS environment) +- **git** installed and configured +- Access to the Keycloak repository (SSH key if private) +- A `keycloak-files.txt` file in the current directory containing relative file paths from the Keycloak repo + +## Usage + +```bash +./patch.sh [--apply] OLD_BRANCH NEW_BRANCH +```` + +### Arguments + +* `OLD_BRANCH` – The base branch in the Keycloak repo (e.g., `origin/release/26.0`) +* `NEW_BRANCH` – The comparison branch in the Keycloak repo (e.g., `origin/archive/release/26.1`) +* `--apply` – Optional flag to **apply** the generated patch to the parent directory + +### Examples + +Generate patch without applying: + +```bash +./patch.sh origin/release/26.0 origin/archive/release/26.1 +``` + +Generate and apply patch: + +```bash +./patch.sh --apply origin/release/26.0 origin/archive/release/26.1 +``` + +## How It Works + +```plaintext ++-----------------------------+ +| Start script | ++-----------------------------+ + | + v ++-----------------------------+ +| Validate keycloak-files.txt | ++-----------------------------+ + | + v ++-----------------------------+ +| Clone Keycloak repository | +| into .work/repo | ++-----------------------------+ + | + v ++-----------------------------+ +| Validate branches | ++-----------------------------+ + | + v ++--------------------------------------+ +| Export listed files from OLD_BRANCH | +| to .work/files/old | ++--------------------------------------+ + | + v ++--------------------------------------+ +| Export listed files from NEW_BRANCH | +| to .work/files/new | ++--------------------------------------+ + | + v ++-----------------------------+ +| Generate patch file | +| .work/patch.patch | ++-----------------------------+ + | + v ++--------------------------------+ +| Post-process patch (clean paths,| +| replace package names) | ++--------------------------------+ + | + v ++-----------------------------------------+ +| If --apply flag set: | +| Apply patch to parent directory | ++-----------------------------------------+ + | + v ++-----------------------------+ +| Script ends with patch ready| ++-----------------------------+ +``` + +## Output + +* Generated patch file: `.work/patch.patch` +* Temporary exported files in `.work/files/old` and `.work/files/new` + +## Notes + +* The `.work` directory is deleted and recreated on each run to ensure a clean workspace. +* The script exits immediately on any error (`set -e`). +* Missing files on either branch will be reported but won’t stop the script. +* Patch is applied **only** if the `--apply` option is specified. + diff --git a/patch-tool/keycloak-files.txt b/patch-tool/keycloak-files.txt new file mode 100644 index 0000000..5229975 --- /dev/null +++ b/patch-tool/keycloak-files.txt @@ -0,0 +1,51 @@ +services/src/main/java/org/keycloak/authentication/authenticators/broker/util/SerializedBrokeredIdentityContext.java +services/src/main/java/org/keycloak/broker/saml/mappers/AdvancedAttributeToRoleMapper.java +services/src/main/java/org/keycloak/broker/saml/mappers/AttributeToRoleMapper.java +services/src/main/java/org/keycloak/broker/saml/mappers/UserAttributeMapper.java +services/src/main/java/org/keycloak/broker/saml/mappers/UsernameTemplateMapper.java +services/src/main/java/org/keycloak/broker/saml/SAMLDataMarshaller.java +services/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java +services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java +services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java +services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java +saml-core-api/src/main/java/org/keycloak/dom/saml/v2/assertion/AssertionType.java +saml-core-api/src/main/java/org/keycloak/dom/saml/v2/assertion/AttributeStatementType.java +saml-core-api/src/main/java/org/keycloak/dom/saml/v2/metadata/AttributeConsumingServiceType.java +saml-core-api/src/main/java/org/keycloak/dom/saml/v2/metadata/EntitiesDescriptorType.java +saml-core-api/src/main/java/org/keycloak/dom/saml/v2/metadata/EntityDescriptorType.java +saml-core-api/src/main/java/org/keycloak/dom/saml/v2/metadata/SPSSODescriptorType.java +saml-core-api/src/main/java/org/keycloak/dom/saml/v2/protocol/ResponseType.java +services/src/main/java/org/keycloak/protocol/saml/mappers/SamlMetadataDescriptorUpdater.java +services/src/main/java/org/keycloak/protocol/saml/JaxrsSAML2BindingBuilder.java +services/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java +saml-core-api/src/main/java/org/keycloak/saml/common/constants/JBossSAMLConstants.java +saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/request/SAML2Request.java +saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/request/SecurityActions.java +saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/response/SAML2Response.java +saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/sig/SAML2Signature.java +saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/AbstractStaxSamlAssertionParser.java +saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAssertionParser.java +saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAssertionQNames.java +saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAttributeParser.java +saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAttributeStatementParser.java +saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/assertion/SAMLAttributeValueParser.java +saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLAttributeConsumingServiceParser.java +saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLEntitiesDescriptorParser.java +saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLEntityDescriptorParser.java +saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/metadata/SAMLSPSSODescriptorParser.java +saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLArtifactResponseParser.java +saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/protocol/SAMLResponseParser.java +saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParser.java +saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/factories/JBossSAMLAuthnResponseFactory.java +saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/factories/SAMLAssertionFactory.java +saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java +saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/SAMLMetadataUtil.java +saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/BaseWriter.java +saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLAssertionWriter.java +saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLMetadataWriter.java +saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLRequestWriter.java +saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLResponseWriter.java +saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java +saml-core/src/main/java/org/keycloak/saml/SAML2AuthnRequestBuilder.java +saml-core/src/main/java/org/keycloak/saml/SAMLRequestParser.java +services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java diff --git a/patch-tool/patch.sh b/patch-tool/patch.sh new file mode 100755 index 0000000..70d402c --- /dev/null +++ b/patch-tool/patch.sh @@ -0,0 +1,109 @@ +#!/bin/bash + +set -e + +APPLY_PATCH=false + +usage() { + echo "Usage: $0 [--apply] OLD_BRANCH NEW_BRANCH" + echo " --apply Apply patch to parent directory after generation" + exit 1 +} + +# Parse options +while [[ "$1" == --* ]]; do + case "$1" in + --apply) + APPLY_PATCH=true + shift + ;; + *) + usage + ;; + esac +done + +if [ $# -ne 2 ]; then + usage +fi + +OLD_BRANCH="$1" +NEW_BRANCH="$2" + +GIT_REPO_URL="git@github.com:keycloak/keycloak.git" +WORK_DIR="$(pwd)/.work" +REPO_DIR="$WORK_DIR/repo" +OLD_DIR="$WORK_DIR/files/old" +NEW_DIR="$WORK_DIR/files/new" +PATCH_FILE="$WORK_DIR/patch.patch" +FILES_LIST="$(pwd)/keycloak-files.txt" + +echo "=== Git Diff Generator ===" +echo "Repository : $GIT_REPO_URL" +echo "Old branch : $OLD_BRANCH" +echo "New branch : $NEW_BRANCH" +echo "Work dir : $WORK_DIR" +echo "Apply patch: $APPLY_PATCH" + +# Ensure keycloak-files.txt exists +if [ ! -f "$FILES_LIST" ]; then + echo "Error: $FILES_LIST not found in $(pwd)" + exit 1 +fi + +# Read file list into array +FILES=() +while IFS= read -r line; do + [[ "$line" =~ ^#.*$ || -z "$line" ]] && continue + FILES+=("$line") +done < "$FILES_LIST" + +# Prepare work directory +rm -rf "$WORK_DIR" +mkdir -p "$WORK_DIR" "$OLD_DIR" "$NEW_DIR" + +echo "Cloning repository..." +git clone "$GIT_REPO_URL" "$REPO_DIR" + +# Validate branches exist +for BRANCH in "$OLD_BRANCH" "$NEW_BRANCH"; do + if ! git -C "$REPO_DIR" ls-remote --exit-code --heads origin "$BRANCH" > /dev/null; then + echo "Error: Branch '$BRANCH' does not exist in remote repository." + exit 1 + fi +done + +echo "Exporting files..." +for FILE in "${FILES[@]}"; do + mkdir -p "$OLD_DIR/$(dirname "$FILE")" + git -C "$REPO_DIR" show "$OLD_BRANCH:$FILE" > "$OLD_DIR/$FILE" 2>/dev/null || echo "Missing in $OLD_BRANCH: $FILE" + + mkdir -p "$NEW_DIR/$(dirname "$FILE")" + git -C "$REPO_DIR" show "$NEW_BRANCH:$FILE" > "$NEW_DIR/$FILE" 2>/dev/null || echo "Missing in $NEW_BRANCH: $FILE" +done + +echo "File count (.java only):" +echo " $OLD_BRANCH: $(find "$OLD_DIR" -type f -name '*.java' | wc -l)" +echo " $NEW_BRANCH: $(find "$NEW_DIR" -type f -name '*.java' | wc -l)" + +echo "Creating patch..." +diff --color=never -ruN "$OLD_DIR" "$NEW_DIR" > "$PATCH_FILE" || true + +# Post-process diff +safe_sed() { + sed "$1" "$2" > "$2.tmp" && mv "$2.tmp" "$2" +} + +safe_sed "s|$OLD_DIR/saml-core/||g" "$PATCH_FILE" +safe_sed "s|$NEW_DIR/saml-core/||g" "$PATCH_FILE" +safe_sed "s|$OLD_DIR/services/||g" "$PATCH_FILE" +safe_sed "s|$NEW_DIR/services/||g" "$PATCH_FILE" +safe_sed 's|org/keycloak/|nl/first8/keycloak/|g' "$PATCH_FILE" + +echo "Patch created at: $PATCH_FILE" + +if [ "$APPLY_PATCH" = true ]; then + echo "Applying patch to parent directory: $(dirname "$(pwd)")" + patch -d "$(dirname "$(pwd)")" < "$PATCH_FILE" + echo "Patch applied successfully." +fi diff --git a/pom.xml b/pom.xml index 64e3125..fabe70b 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ UTF-8 UTF-8 - 26.0.0 + 26.1.5 4.8.3.1 3.4.1 @@ -383,4 +383,4 @@ - \ No newline at end of file + diff --git a/src/main/java/nl/first8/keycloak/protocol/saml/SamlProtocolUtils.java b/src/main/java/nl/first8/keycloak/protocol/saml/SamlProtocolUtils.java index 11edd99..415f171 100644 --- a/src/main/java/nl/first8/keycloak/protocol/saml/SamlProtocolUtils.java +++ b/src/main/java/nl/first8/keycloak/protocol/saml/SamlProtocolUtils.java @@ -3,7 +3,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.net.URI; - +import java.nio.charset.StandardCharsets; import java.security.PublicKey; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; @@ -151,7 +151,7 @@ public static void verifyRedirectSignature(SAMLDocumentHolder documentHolder, Ke String decodedAlgorithm = RedirectBindingUtil.urlDecode(encodedParams.getFirst(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY)); SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.getFromXmlMethod(decodedAlgorithm); if (!RedirectBindingSignatureUtil.validateRedirectBindingSignature(signatureAlgorithm, - rawQuery.getBytes("UTF-8"), decodedSignature, locator, keyId)) { + rawQuery.getBytes(StandardCharsets.UTF_8), decodedSignature, locator, keyId)) { throw new VerificationException("Invalid query param signature"); } } catch (Exception e) { diff --git a/src/main/java/nl/first8/keycloak/services/resources/IdentityBrokerService.java b/src/main/java/nl/first8/keycloak/services/resources/IdentityBrokerService.java index 6d04761..21998ab 100644 --- a/src/main/java/nl/first8/keycloak/services/resources/IdentityBrokerService.java +++ b/src/main/java/nl/first8/keycloak/services/resources/IdentityBrokerService.java @@ -353,6 +353,8 @@ public Response performLogin(@PathParam("provider_alias") String providerAlias, } return response; } + } catch (WebApplicationException e) { + return e.getResponse(); } catch (IdentityBrokerException e) { return redirectToErrorPage(Response.Status.BAD_GATEWAY, Messages.COULD_NOT_SEND_AUTHENTICATION_REQUEST, e, providerAlias); } catch (Exception e) { @@ -1123,7 +1125,7 @@ private AuthenticationSessionModel parseSessionCode(String code, String clientId private Response checkAccountManagementFailedLinking(AuthenticationSessionModel authSession, String error, Object... parameters) { UserSessionModel userSession = new AuthenticationSessionManager(session).getUserSession(authSession); - if (userSession != null && authSession.getClient() != null && authSession.getClient().getClientId().equals(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID)) { + if (userSession != null && authSession.getClient() != null) { this.event.event(EventType.FEDERATED_IDENTITY_LINK); UserModel user = userSession.getUser(); @@ -1154,7 +1156,7 @@ private Response checkPassiveLoginError(AuthenticationSessionModel authSession, .setHttpHeaders(headers) .setUriInfo(session.getContext().getUri()) .setEventBuilder(event); - return protocol.sendError(authSession, error); + return protocol.sendError(authSession, error, null); } return null; }