From fa424b889bdf8294cd2615d79dfb0d7d3bff09f6 Mon Sep 17 00:00:00 2001 From: Oleksandr Mandryk <2678920+omandryk@users.noreply.github.com> Date: Tue, 11 Aug 2026 10:46:04 -0400 Subject: [PATCH 1/4] Revive 1.5 maintenance baseline --- .github/dependabot.yml | 12 +++ .github/workflows/ci.yml | 24 ++++++ .travis.yml | 9 --- CHANGELOG.md | 15 ++++ CONTRIBUTING.md | 13 +++ README.md | 81 +++++++++++-------- SECURITY.md | 5 ++ pom.xml | 58 ++++--------- .../woocommerce/oauth/OAuthConfig.java | 2 +- .../woocommerce/oauth/OAuthSignature.java | 18 +++-- .../integration/WooCommerceClientTest.java | 6 +- .../woocommerce/oauth/OAuthConfigTest.java | 26 ++++++ .../woocommerce/oauth/OAuthSignatureTest.java | 62 ++++++++++++++ 13 files changed, 234 insertions(+), 97 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md create mode 100644 src/test/java/com/icoderman/woocommerce/oauth/OAuthConfigTest.java create mode 100644 src/test/java/com/icoderman/woocommerce/oauth/OAuthSignatureTest.java diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..542edcf --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + - package-ecosystem: maven + directory: / + schedule: + interval: monthly + open-pull-requests-limit: 5 + - package-ecosystem: github-actions + directory: / + schedule: + interval: monthly + open-pull-requests-limit: 5 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..601b227 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,24 @@ +name: CI + +on: + push: + branches: [master] + pull_request: + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + java: ['8', '11', '17', '21'] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: ${{ matrix.java }} + cache: maven + - run: mvn --batch-mode --no-transfer-progress verify -Dgpg.skip=true diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2022698..0000000 --- a/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: java -jdk: oraclejdk8 -dist: trusty - -install: mvn install -Dgpg.skip - -cache: - directories: - - $HOME/.m2 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..da2adb1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog + +## Unreleased + +- Restored continuous integration across supported Java versions. +- Removed embedded WooCommerce credentials from integration tests. +- Updated maintained dependencies and build plugins. +- Added unit coverage for OAuth configuration and signing behavior. +- Corrected OAuth query and ISO-8601 date encoding, informed by community pull requests #37 and #49. +- Accepted store URLs with a trailing slash, based on community pull request #32. +- Updated project ownership, security, and contribution documentation. + +## 1.4 - 2019-11-05 + +- Latest historical Maven Central release before the project revival. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..04fa502 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,13 @@ +# Contributing + +Issues and focused pull requests are welcome. Before proposing a substantial API change, open an issue so the design and compatibility impact can be discussed. + +## Development + +Requirements: JDK 8 or newer and Maven 3.8 or newer. + +```bash +mvn verify -Dgpg.skip=true +``` + +Integration tests require a disposable WooCommerce store and credentials supplied through `WC_URL`, `WC_CONSUMER_KEY`, and `WC_CONSUMER_SECRET`. Never commit real credentials. diff --git a/README.md b/README.md index db19f97..8e9f3ea 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,56 @@ # WooCommerce API Java Wrapper -[![Build Status](https://travis-ci.org/icoderman/wc-api-java.svg?branch=master)](https://travis-ci.org/icoderman/wc-api-java) -Java wrapper for WooCommerce REST API. The library supports the latest versions of WooCommerce REST API only -with the OAuth 1.0a authentication over the HTTP protocol. +[![CI](https://github.com/omandryk/wc-api-java/actions/workflows/ci.yml/badge.svg)](https://github.com/omandryk/wc-api-java/actions/workflows/ci.yml) +[![Maven Central](https://img.shields.io/maven-central/v/com.icoderman/wc-api-java.svg)](https://central.sonatype.com/artifact/com.icoderman/wc-api-java) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) + +A small Java wrapper for the WooCommerce REST API, originally released in 2016 and distributed through Maven Central. + +> **Project revival:** version 1.4 is the latest published release. The 1.5 line restores maintenance, testing, and modern release infrastructure while preserving the existing API. Do not use unreleased snapshots in production. + +## Installation -## Setup -wc-api-java is available on maven central: ```xml - - com.icoderman - wc-api-java - 1.4 - + + com.icoderman + wc-api-java + 1.4 + ``` ## Usage ```java - public static void main(String[] args) { - // Setup client - OAuthConfig config = new OAuthConfig("http://woocommerce.com", "consumerKey", "consumerSecret"); - WooCommerce wooCommerce = new WooCommerceAPI(config, ApiVersionType.V3); - - // Prepare object for request - Map productInfo = new HashMap<>(); - productInfo.put("name", "Premium Quality"); - productInfo.put("type", "simple"); - productInfo.put("regular_price", "21.99"); - productInfo.put("description", "Pellentesque habitant morbi tristique senectus et netus"); - - // Make request and retrieve result - Map product = wooCommerce.create(EndpointBaseType.PRODUCTS.getValue(), productInfo); - - System.out.println(product.get("id")); - - // Get all with request parameters - Map params = new HashMap<>(); - params.put("per_page","100"); - params.put("offset","0"); - List products = wooCommerce.getAll(EndpointBaseType.PRODUCTS.getValue(), params); - - System.out.println(products.size()); - } +OAuthConfig config = new OAuthConfig( + "https://store.example.com", + System.getenv("WC_CONSUMER_KEY"), + System.getenv("WC_CONSUMER_SECRET") +); +WooCommerce wooCommerce = new WooCommerceAPI(config, ApiVersionType.V3); + +Map params = new HashMap<>(); +params.put("per_page", "100"); +List products = wooCommerce.getAll(EndpointBaseType.PRODUCTS.getValue(), params); ``` + +Never commit WooCommerce credentials. Supply them through a secret manager or environment variables. + +## Compatibility + +- Java 8 or newer +- WooCommerce REST API v2/v3 endpoints +- OAuth 1.0a request signing used by the historical 1.x API + +The compatibility matrix will be expanded as part of the 1.5 revival. HTTPS-first authentication and a modernized API are being evaluated separately for a future major release. + +## Development + +```bash +mvn verify -Dgpg.skip=true +``` + +See [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md) before opening an issue or pull request. + +## License + +MIT © Oleksandr Mandryk diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..f14d595 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,5 @@ +# Security policy + +The latest release is the only supported version. Please report suspected vulnerabilities privately through GitHub Security Advisories instead of opening a public issue. + +Never include WooCommerce consumer keys, consumer secrets, access tokens, or customer data in a report. Use redacted requests and a minimal reproduction whenever possible. diff --git a/pom.xml b/pom.xml index 9444510..b3e15f5 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ WooCommerce API Java Wrapper Java wrapper for WooCommerce REST API - https://github.com/icoderman/wc-api-java + https://github.com/omandryk/wc-api-java @@ -23,32 +23,24 @@ icoderman Oleksandr Mandryk icoderman@gmail.com - https://github.com/icoderman + https://github.com/omandryk - scm:git:git@github.com:icoderman/wc-api-java.git - scm:git:git@github.com:icoderman/wc-api-java.git - git@github.com/icoderman/wc-api-java.git + scm:git:https://github.com/omandryk/wc-api-java.git + scm:git:ssh://git@github.com/omandryk/wc-api-java.git + https://github.com/omandryk/wc-api-java HEAD - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - 2.10.0 - 4.5.10 - 4.12 + UTF-8 + 8 + 8 + 2.18.6 + 4.5.14 + 4.13.2 @@ -84,17 +76,13 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.0 - - 1.8 - 1.8 - + 3.14.1 org.apache.maven.plugins maven-source-plugin - 3.0.1 + 3.3.1 attach-sources @@ -108,7 +96,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.0.1 + 3.11.3 attach-javadocs @@ -122,7 +110,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.2.8 true @@ -140,22 +128,10 @@ org.apache.maven.plugins maven-release-plugin - 2.5.3 - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.8 - true - - ossrh - https://oss.sonatype.org/ - true - + 3.1.1 - \ No newline at end of file + diff --git a/src/main/java/com/icoderman/woocommerce/oauth/OAuthConfig.java b/src/main/java/com/icoderman/woocommerce/oauth/OAuthConfig.java index 5d9dd77..198f573 100644 --- a/src/main/java/com/icoderman/woocommerce/oauth/OAuthConfig.java +++ b/src/main/java/com/icoderman/woocommerce/oauth/OAuthConfig.java @@ -12,7 +12,7 @@ public OAuthConfig(String url, String consumerKey, String consumerSecret) { consumerSecret == null || consumerSecret.isEmpty()) { throw new IllegalArgumentException("All arguments are required"); } - this.url = url; + this.url = url.endsWith("/") ? url.substring(0, url.length() - 1) : url; this.consumerKey = consumerKey; this.consumerSecret = consumerSecret; } diff --git a/src/main/java/com/icoderman/woocommerce/oauth/OAuthSignature.java b/src/main/java/com/icoderman/woocommerce/oauth/OAuthSignature.java index 1cb5997..ecfd5d8 100644 --- a/src/main/java/com/icoderman/woocommerce/oauth/OAuthSignature.java +++ b/src/main/java/com/icoderman/woocommerce/oauth/OAuthSignature.java @@ -58,10 +58,9 @@ public static String getAsQueryString(OAuthConfig config, String endpoint, HttpM return ""; } Map oauthParameters = getAsMap(config, endpoint, httpMethod, params); - String encodedSignature = oauthParameters.get(OAuthHeader.OAUTH_SIGNATURE.getValue()) - .replace(SpecialSymbol.PLUS.getPlain(), SpecialSymbol.PLUS.getEncoded()); - oauthParameters.put(OAuthHeader.OAUTH_SIGNATURE.getValue(), encodedSignature); - return mapToString(oauthParameters, SpecialSymbol.EQUAL.getPlain(), SpecialSymbol.AMP.getPlain()); + Map encodedParameters = percentEncodeParameters(oauthParameters); + return mapToString(getSortedParameters(encodedParameters), + SpecialSymbol.EQUAL.getPlain(), SpecialSymbol.AMP.getPlain()); } public static String getAsQueryString(OAuthConfig config, String endpoint, HttpMethod httpMethod) { @@ -95,15 +94,18 @@ private static String urlEncode(String s) { } } - private static String getSignatureBaseString(String url, String method, Map parameters) { + static String getSignatureBaseString(String url, String method, Map parameters) { String requestURL = urlEncode(url); // 1. Percent encode every key and value that will be signed. Map encodedParameters = percentEncodeParameters(parameters); // 2. Sort the list of parameters alphabetically by encoded key. encodedParameters = getSortedParameters(encodedParameters); - String paramsString = mapToString(encodedParameters, SpecialSymbol.EQUAL.getEncoded(), SpecialSymbol.AMP.getEncoded()); - return String.format(BASE_SIGNATURE_FORMAT, method, requestURL, paramsString); + String normalizedParameters = mapToString(encodedParameters, + SpecialSymbol.EQUAL.getPlain(), SpecialSymbol.AMP.getPlain()); + + // 3. Percent encode the complete normalized parameter string for the signature base string. + return String.format(BASE_SIGNATURE_FORMAT, method, requestURL, percentEncode(normalizedParameters)); } private static String mapToString(Map paramsMap, String keyValueDelimiter, String paramsDelimiter) { @@ -128,7 +130,7 @@ private static String percentEncode(String s) { try { return URLEncoder.encode(s, UTF_8) // OAuth encodes some characters differently: - .replace(SpecialSymbol.PLUS.getPlain(), SpecialSymbol.PLUS.getEncoded()) + .replace(SpecialSymbol.PLUS.getPlain(), "%20") .replace(SpecialSymbol.STAR.getPlain(), SpecialSymbol.STAR.getEncoded()) .replace(SpecialSymbol.TILDE.getEncoded(), SpecialSymbol.TILDE.getPlain()); } catch (UnsupportedEncodingException e) { diff --git a/src/test/java/com/icoderman/woocommerce/integration/WooCommerceClientTest.java b/src/test/java/com/icoderman/woocommerce/integration/WooCommerceClientTest.java index be37195..5b450ad 100644 --- a/src/test/java/com/icoderman/woocommerce/integration/WooCommerceClientTest.java +++ b/src/test/java/com/icoderman/woocommerce/integration/WooCommerceClientTest.java @@ -18,9 +18,9 @@ public class WooCommerceClientTest { - private static final String CONSUMER_KEY = "ck_d35e7be7cc695d87f23490729dd80e173f88c8f5"; - private static final String CONSUMER_SECRET = "cs_53a835760712ebf0c8bcf2a21197af4b2323a052"; - private static final String WC_URL = "http://localhost/index.php"; + private static final String CONSUMER_KEY = System.getenv("WC_CONSUMER_KEY"); + private static final String CONSUMER_SECRET = System.getenv("WC_CONSUMER_SECRET"); + private static final String WC_URL = System.getenv("WC_URL"); private WooCommerce wooCommerce; diff --git a/src/test/java/com/icoderman/woocommerce/oauth/OAuthConfigTest.java b/src/test/java/com/icoderman/woocommerce/oauth/OAuthConfigTest.java new file mode 100644 index 0000000..f0bf27a --- /dev/null +++ b/src/test/java/com/icoderman/woocommerce/oauth/OAuthConfigTest.java @@ -0,0 +1,26 @@ +package com.icoderman.woocommerce.oauth; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class OAuthConfigTest { + @Test(expected = IllegalArgumentException.class) + public void rejectsMissingConsumerSecret() { + new OAuthConfig("https://example.com", "consumer-key", ""); + } + + @Test + public void retainsConfigurationValues() { + OAuthConfig config = new OAuthConfig("https://example.com", "consumer-key", "consumer-secret"); + assertEquals("https://example.com", config.getUrl()); + assertEquals("consumer-key", config.getConsumerKey()); + assertEquals("consumer-secret", config.getConsumerSecret()); + } + + @Test + public void removesTrailingSlashFromStoreUrl() { + OAuthConfig config = new OAuthConfig("https://example.com/", "consumer-key", "consumer-secret"); + assertEquals("https://example.com", config.getUrl()); + } +} diff --git a/src/test/java/com/icoderman/woocommerce/oauth/OAuthSignatureTest.java b/src/test/java/com/icoderman/woocommerce/oauth/OAuthSignatureTest.java new file mode 100644 index 0000000..417b5f5 --- /dev/null +++ b/src/test/java/com/icoderman/woocommerce/oauth/OAuthSignatureTest.java @@ -0,0 +1,62 @@ +package com.icoderman.woocommerce.oauth; + +import com.icoderman.woocommerce.HttpMethod; +import org.junit.Test; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class OAuthSignatureTest { + private final OAuthConfig config = new OAuthConfig("https://example.com", "consumer key", "consumer-secret"); + + @Test + public void returnsEmptyValuesForIncompleteInput() { + assertTrue(OAuthSignature.getAsMap(null, "https://example.com", HttpMethod.GET).isEmpty()); + assertEquals("", OAuthSignature.getAsQueryString(config, null, HttpMethod.GET)); + } + + @Test + public void includesRequiredOAuthParameters() { + Map values = OAuthSignature.getAsMap( + config, "https://example.com/wp-json/wc/v3/products", HttpMethod.GET); + assertEquals("consumer key", values.get("oauth_consumer_key")); + assertEquals("HMAC-SHA256", values.get("oauth_signature_method")); + assertTrue(values.containsKey("oauth_nonce")); + assertTrue(values.containsKey("oauth_timestamp")); + assertTrue(values.containsKey("oauth_signature")); + } + + @Test + public void percentEncodesQueryValues() { + Map params = new HashMap<>(); + params.put("search", "coffee & tea"); + String query = OAuthSignature.getAsQueryString( + config, "https://example.com/wp-json/wc/v3/products", HttpMethod.GET, params); + assertTrue(query.contains("search=coffee%20%26%20tea")); + assertFalse(query.contains("coffee+%26+tea")); + } + + @Test + public void deleteAddsWooCommerceForceParameter() { + Map values = OAuthSignature.getAsMap( + config, "https://example.com/wp-json/wc/v3/products/1", HttpMethod.DELETE, + Collections.emptyMap()); + assertEquals("true", values.get("force")); + } + + @Test + public void doubleEncodesIsoDateInsideSignatureBaseString() { + Map params = new HashMap<>(); + params.put("after", "2023-02-21T13:00:23"); + + String baseString = OAuthSignature.getSignatureBaseString( + "https://example.com/wp-json/wc/v3/orders", "GET", params); + + assertTrue(baseString.contains("after%3D2023-02-21T13%253A00%253A23")); + } +} From b5fd21ebb1ef45dc5f7f52416878ef11834362b0 Mon Sep 17 00:00:00 2001 From: Oleksandr Mandryk <2678920+omandryk@users.noreply.github.com> Date: Tue, 11 Aug 2026 10:56:00 -0400 Subject: [PATCH 2/4] Use branded project contact email --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b3e15f5..0ba8999 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ icoderman Oleksandr Mandryk - icoderman@gmail.com + hello@omandryk.com https://github.com/omandryk From 77c263c853cda24ef5ecfa99f20cd1e54c3d2c09 Mon Sep 17 00:00:00 2001 From: Oleksandr Mandryk <2678920+omandryk@users.noreply.github.com> Date: Tue, 11 Aug 2026 11:04:23 -0400 Subject: [PATCH 3/4] Keep revival changes outside core behavior --- CHANGELOG.md | 3 - CONTRIBUTING.md | 2 +- README.md | 6 +- .../woocommerce/oauth/OAuthConfig.java | 2 +- .../woocommerce/oauth/OAuthSignature.java | 18 +++--- .../woocommerce/oauth/OAuthConfigTest.java | 26 -------- .../woocommerce/oauth/OAuthSignatureTest.java | 62 ------------------- 7 files changed, 13 insertions(+), 106 deletions(-) delete mode 100644 src/test/java/com/icoderman/woocommerce/oauth/OAuthConfigTest.java delete mode 100644 src/test/java/com/icoderman/woocommerce/oauth/OAuthSignatureTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index da2adb1..01c0b72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,6 @@ - Restored continuous integration across supported Java versions. - Removed embedded WooCommerce credentials from integration tests. - Updated maintained dependencies and build plugins. -- Added unit coverage for OAuth configuration and signing behavior. -- Corrected OAuth query and ISO-8601 date encoding, informed by community pull requests #37 and #49. -- Accepted store URLs with a trailing slash, based on community pull request #32. - Updated project ownership, security, and contribution documentation. ## 1.4 - 2019-11-05 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 04fa502..f459180 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,4 +10,4 @@ Requirements: JDK 8 or newer and Maven 3.8 or newer. mvn verify -Dgpg.skip=true ``` -Integration tests require a disposable WooCommerce store and credentials supplied through `WC_URL`, `WC_CONSUMER_KEY`, and `WC_CONSUMER_SECRET`. Never commit real credentials. +The legacy live-store integration tests are currently disabled with JUnit's `@Ignore`. For manual testing, use a disposable WooCommerce store and supply `WC_URL`, `WC_CONSUMER_KEY`, and `WC_CONSUMER_SECRET` through environment variables. Never commit real credentials. diff --git a/README.md b/README.md index 8e9f3ea..2bb05b9 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ A small Java wrapper for the WooCommerce REST API, originally released in 2016 a ```java OAuthConfig config = new OAuthConfig( - "https://store.example.com", + "http://localhost", System.getenv("WC_CONSUMER_KEY"), System.getenv("WC_CONSUMER_SECRET") ); @@ -39,9 +39,9 @@ Never commit WooCommerce credentials. Supply them through a secret manager or en - Java 8 or newer - WooCommerce REST API v2/v3 endpoints -- OAuth 1.0a request signing used by the historical 1.x API +- Legacy OAuth 1.0a request signing over HTTP -The compatibility matrix will be expanded as part of the 1.5 revival. HTTPS-first authentication and a modernized API are being evaluated separately for a future major release. +The 1.x client does not implement WooCommerce HTTPS Basic Authentication. Use it only in a controlled environment until HTTPS-first authentication is available in a future major release. ## Development diff --git a/src/main/java/com/icoderman/woocommerce/oauth/OAuthConfig.java b/src/main/java/com/icoderman/woocommerce/oauth/OAuthConfig.java index 198f573..5d9dd77 100644 --- a/src/main/java/com/icoderman/woocommerce/oauth/OAuthConfig.java +++ b/src/main/java/com/icoderman/woocommerce/oauth/OAuthConfig.java @@ -12,7 +12,7 @@ public OAuthConfig(String url, String consumerKey, String consumerSecret) { consumerSecret == null || consumerSecret.isEmpty()) { throw new IllegalArgumentException("All arguments are required"); } - this.url = url.endsWith("/") ? url.substring(0, url.length() - 1) : url; + this.url = url; this.consumerKey = consumerKey; this.consumerSecret = consumerSecret; } diff --git a/src/main/java/com/icoderman/woocommerce/oauth/OAuthSignature.java b/src/main/java/com/icoderman/woocommerce/oauth/OAuthSignature.java index ecfd5d8..1cb5997 100644 --- a/src/main/java/com/icoderman/woocommerce/oauth/OAuthSignature.java +++ b/src/main/java/com/icoderman/woocommerce/oauth/OAuthSignature.java @@ -58,9 +58,10 @@ public static String getAsQueryString(OAuthConfig config, String endpoint, HttpM return ""; } Map oauthParameters = getAsMap(config, endpoint, httpMethod, params); - Map encodedParameters = percentEncodeParameters(oauthParameters); - return mapToString(getSortedParameters(encodedParameters), - SpecialSymbol.EQUAL.getPlain(), SpecialSymbol.AMP.getPlain()); + String encodedSignature = oauthParameters.get(OAuthHeader.OAUTH_SIGNATURE.getValue()) + .replace(SpecialSymbol.PLUS.getPlain(), SpecialSymbol.PLUS.getEncoded()); + oauthParameters.put(OAuthHeader.OAUTH_SIGNATURE.getValue(), encodedSignature); + return mapToString(oauthParameters, SpecialSymbol.EQUAL.getPlain(), SpecialSymbol.AMP.getPlain()); } public static String getAsQueryString(OAuthConfig config, String endpoint, HttpMethod httpMethod) { @@ -94,18 +95,15 @@ private static String urlEncode(String s) { } } - static String getSignatureBaseString(String url, String method, Map parameters) { + private static String getSignatureBaseString(String url, String method, Map parameters) { String requestURL = urlEncode(url); // 1. Percent encode every key and value that will be signed. Map encodedParameters = percentEncodeParameters(parameters); // 2. Sort the list of parameters alphabetically by encoded key. encodedParameters = getSortedParameters(encodedParameters); - String normalizedParameters = mapToString(encodedParameters, - SpecialSymbol.EQUAL.getPlain(), SpecialSymbol.AMP.getPlain()); - - // 3. Percent encode the complete normalized parameter string for the signature base string. - return String.format(BASE_SIGNATURE_FORMAT, method, requestURL, percentEncode(normalizedParameters)); + String paramsString = mapToString(encodedParameters, SpecialSymbol.EQUAL.getEncoded(), SpecialSymbol.AMP.getEncoded()); + return String.format(BASE_SIGNATURE_FORMAT, method, requestURL, paramsString); } private static String mapToString(Map paramsMap, String keyValueDelimiter, String paramsDelimiter) { @@ -130,7 +128,7 @@ private static String percentEncode(String s) { try { return URLEncoder.encode(s, UTF_8) // OAuth encodes some characters differently: - .replace(SpecialSymbol.PLUS.getPlain(), "%20") + .replace(SpecialSymbol.PLUS.getPlain(), SpecialSymbol.PLUS.getEncoded()) .replace(SpecialSymbol.STAR.getPlain(), SpecialSymbol.STAR.getEncoded()) .replace(SpecialSymbol.TILDE.getEncoded(), SpecialSymbol.TILDE.getPlain()); } catch (UnsupportedEncodingException e) { diff --git a/src/test/java/com/icoderman/woocommerce/oauth/OAuthConfigTest.java b/src/test/java/com/icoderman/woocommerce/oauth/OAuthConfigTest.java deleted file mode 100644 index f0bf27a..0000000 --- a/src/test/java/com/icoderman/woocommerce/oauth/OAuthConfigTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.icoderman.woocommerce.oauth; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class OAuthConfigTest { - @Test(expected = IllegalArgumentException.class) - public void rejectsMissingConsumerSecret() { - new OAuthConfig("https://example.com", "consumer-key", ""); - } - - @Test - public void retainsConfigurationValues() { - OAuthConfig config = new OAuthConfig("https://example.com", "consumer-key", "consumer-secret"); - assertEquals("https://example.com", config.getUrl()); - assertEquals("consumer-key", config.getConsumerKey()); - assertEquals("consumer-secret", config.getConsumerSecret()); - } - - @Test - public void removesTrailingSlashFromStoreUrl() { - OAuthConfig config = new OAuthConfig("https://example.com/", "consumer-key", "consumer-secret"); - assertEquals("https://example.com", config.getUrl()); - } -} diff --git a/src/test/java/com/icoderman/woocommerce/oauth/OAuthSignatureTest.java b/src/test/java/com/icoderman/woocommerce/oauth/OAuthSignatureTest.java deleted file mode 100644 index 417b5f5..0000000 --- a/src/test/java/com/icoderman/woocommerce/oauth/OAuthSignatureTest.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.icoderman.woocommerce.oauth; - -import com.icoderman.woocommerce.HttpMethod; -import org.junit.Test; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -public class OAuthSignatureTest { - private final OAuthConfig config = new OAuthConfig("https://example.com", "consumer key", "consumer-secret"); - - @Test - public void returnsEmptyValuesForIncompleteInput() { - assertTrue(OAuthSignature.getAsMap(null, "https://example.com", HttpMethod.GET).isEmpty()); - assertEquals("", OAuthSignature.getAsQueryString(config, null, HttpMethod.GET)); - } - - @Test - public void includesRequiredOAuthParameters() { - Map values = OAuthSignature.getAsMap( - config, "https://example.com/wp-json/wc/v3/products", HttpMethod.GET); - assertEquals("consumer key", values.get("oauth_consumer_key")); - assertEquals("HMAC-SHA256", values.get("oauth_signature_method")); - assertTrue(values.containsKey("oauth_nonce")); - assertTrue(values.containsKey("oauth_timestamp")); - assertTrue(values.containsKey("oauth_signature")); - } - - @Test - public void percentEncodesQueryValues() { - Map params = new HashMap<>(); - params.put("search", "coffee & tea"); - String query = OAuthSignature.getAsQueryString( - config, "https://example.com/wp-json/wc/v3/products", HttpMethod.GET, params); - assertTrue(query.contains("search=coffee%20%26%20tea")); - assertFalse(query.contains("coffee+%26+tea")); - } - - @Test - public void deleteAddsWooCommerceForceParameter() { - Map values = OAuthSignature.getAsMap( - config, "https://example.com/wp-json/wc/v3/products/1", HttpMethod.DELETE, - Collections.emptyMap()); - assertEquals("true", values.get("force")); - } - - @Test - public void doubleEncodesIsoDateInsideSignatureBaseString() { - Map params = new HashMap<>(); - params.put("after", "2023-02-21T13:00:23"); - - String baseString = OAuthSignature.getSignatureBaseString( - "https://example.com/wp-json/wc/v3/orders", "GET", params); - - assertTrue(baseString.contains("after%3D2023-02-21T13%253A00%253A23")); - } -} From 8afe0355137ada04b8a6399ffd3e6fa84eed2386 Mon Sep 17 00:00:00 2001 From: Oleksandr Mandryk <2678920+omandryk@users.noreply.github.com> Date: Tue, 11 Aug 2026 11:05:32 -0400 Subject: [PATCH 4/4] Use current GitHub Actions runtimes --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 601b227..deb2e18 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,8 +15,8 @@ jobs: matrix: java: ['8', '11', '17', '21'] steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-java@v5 with: distribution: temurin java-version: ${{ matrix.java }}