From 6b08ea265295cb3442983bc333e1942f96716553 Mon Sep 17 00:00:00 2001 From: Kirill Merkushev Date: Thu, 6 Feb 2020 00:22:34 +0100 Subject: [PATCH 01/10] GRPC jwt based authentication --- build.gradle | 2 + plugins/grpc-transport-auth/build.gradle | 38 +++++++ .../transport/grpc/StaticRSAKeyProvider.java | 73 ++++++++++++ .../transport/grpc/config/GRPCAuthConfig.java | 106 ++++++++++++++++++ .../grpc/StaticRSAKeyProviderTest.java | 79 +++++++++++++ .../grpc/config/GRPCAuthConfigTest.java | 93 +++++++++++++++ 6 files changed, 391 insertions(+) create mode 100644 plugins/grpc-transport-auth/build.gradle create mode 100644 plugins/grpc-transport-auth/src/main/java/com/github/bsideup/liiklus/transport/grpc/StaticRSAKeyProvider.java create mode 100644 plugins/grpc-transport-auth/src/main/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfig.java create mode 100644 plugins/grpc-transport-auth/src/test/java/com/github/bsideup/liiklus/transport/grpc/StaticRSAKeyProviderTest.java create mode 100644 plugins/grpc-transport-auth/src/test/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfigTest.java diff --git a/build.gradle b/build.gradle index 9f355d24..22d6d890 100644 --- a/build.gradle +++ b/build.gradle @@ -109,6 +109,8 @@ configure(subprojects.findAll { !it.name.startsWith("examples/") }) { dependency 'org.apache.kafka:kafka-clients:2.3.1' dependency 'com.salesforce.servicelibs:reactor-grpc-stub:0.10.0' + dependency 'com.avast.grpc.jwt:grpc-java-jwt:0.2.0' + dependency 'com.auth0:java-jwt:3.8.1' dependency 'org.awaitility:awaitility:4.0.1' } diff --git a/plugins/grpc-transport-auth/build.gradle b/plugins/grpc-transport-auth/build.gradle new file mode 100644 index 00000000..21d0116e --- /dev/null +++ b/plugins/grpc-transport-auth/build.gradle @@ -0,0 +1,38 @@ +jar { + manifest { + attributes( + 'Plugin-Id': "${project.name}", + 'Plugin-Version': "${project.version}", + ) + } + + into('lib') { + from(configurations.compile - configurations.compileOnly) + } +} + +dependencies { + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + compileOnly 'com.google.auto.service:auto-service' + annotationProcessor 'com.google.auto.service:auto-service' + + compileOnly project(":app") + compile project(":grpc-transport") + compile 'com.auth0:java-jwt' + compile 'com.avast.grpc.jwt:grpc-java-jwt' + + compileOnly project(":api") + + testCompileOnly 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' + testCompile 'org.junit.jupiter:junit-jupiter-engine' + testCompile 'org.junit.jupiter:junit-jupiter-params' + testCompile project(":app") + testCompile project(":testing") + testCompile project(":client") + testCompile 'org.springframework.boot:spring-boot-starter-test' + testCompile 'org.springframework.boot:spring-boot-starter-validation' + testCompile 'com.fasterxml.jackson.core:jackson-databind' + +} diff --git a/plugins/grpc-transport-auth/src/main/java/com/github/bsideup/liiklus/transport/grpc/StaticRSAKeyProvider.java b/plugins/grpc-transport-auth/src/main/java/com/github/bsideup/liiklus/transport/grpc/StaticRSAKeyProvider.java new file mode 100644 index 00000000..a8540c1a --- /dev/null +++ b/plugins/grpc-transport-auth/src/main/java/com/github/bsideup/liiklus/transport/grpc/StaticRSAKeyProvider.java @@ -0,0 +1,73 @@ +package com.github.bsideup.liiklus.transport.grpc; + +import com.auth0.jwt.interfaces.RSAKeyProvider; +import lombok.SneakyThrows; + +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.stream.Collectors; + +public class StaticRSAKeyProvider implements RSAKeyProvider { + private Map keys; + + public StaticRSAKeyProvider(Map keys) { + this.keys = keys.entrySet() + .stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + key -> { + try { + return parsePubKey(key.getValue()); + } catch (InvalidKeySpecException e) { + throw new IllegalArgumentException(String.format("Invalid RSA pubkey with id %s", key.getKey()), e); + } + } + )); + } + + @Override + public RSAPublicKey getPublicKeyById(String keyId) { + if (!keys.containsKey(keyId)) { + throw new NoSuchElementException(String.format("KeyId %s is not defined to authorize GRPC requests", keyId)); + } + return keys.get(keyId); + } + + @Override + public RSAPrivateKey getPrivateKey() { + return null; // we don't sign anything + } + + @Override + public String getPrivateKeyId() { + return null; // we don't sign anything + } + + /** + * Standard "ssh-rsa AAAAB3Nza..." pubkey representation could be converted to a proper format with + * `ssh-keygen -f id_rsa.pub -e -m pkcs8` + * + * This method will work the same if you strip beginning, as well as line breaks on your own + * + * @param key X509 encoded (with -----BEGIN PUBLIC KEY----- lines) + * @return parsed string + */ + @SneakyThrows(NoSuchAlgorithmException.class) + static RSAPublicKey parsePubKey(String key) throws InvalidKeySpecException { + String keyContent = key.replaceAll("\\n", "") + .replace("-----BEGIN PUBLIC KEY-----", "") + .replace("-----END PUBLIC KEY-----", ""); + + byte[] byteKey = Base64.getDecoder().decode(keyContent); + var x509EncodedKeySpec = new X509EncodedKeySpec(byteKey); + + return (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec); + } +} diff --git a/plugins/grpc-transport-auth/src/main/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfig.java b/plugins/grpc-transport-auth/src/main/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfig.java new file mode 100644 index 00000000..392936e7 --- /dev/null +++ b/plugins/grpc-transport-auth/src/main/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfig.java @@ -0,0 +1,106 @@ +package com.github.bsideup.liiklus.transport.grpc.config; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.avast.grpc.jwt.server.JwtServerInterceptor; +import com.github.bsideup.liiklus.transport.grpc.GRPCLiiklusTransportConfigurer; +import com.github.bsideup.liiklus.transport.grpc.StaticRSAKeyProvider; +import com.github.bsideup.liiklus.util.PropertiesUtil; +import com.google.auto.service.AutoService; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.validator.group.GroupSequenceProvider; +import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.support.GenericApplicationContext; + +import javax.validation.constraints.NotEmpty; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Slf4j +@AutoService(ApplicationContextInitializer.class) +public class GRPCAuthConfig implements ApplicationContextInitializer { + + @Override + public void initialize(GenericApplicationContext applicationContext) { + var environment = applicationContext.getEnvironment(); + + var authProperties = PropertiesUtil.bind(environment, new GRPCAuthProperties()); + + if (authProperties.getAlg() == GRPCAuthProperties.Alg.NONE) { + return; + } + + log.info("GRPC Authorization ENABLED with algorithm {}", authProperties.getAlg()); + + JWTVerifier verifier = createVerifier(authProperties.getAlg(), authProperties); + + applicationContext.registerBean( + GRPCLiiklusTransportConfigurer.class, + () -> builder -> builder.intercept(new JwtServerInterceptor<>(verifier::verify)) + ); + } + + private JWTVerifier createVerifier(GRPCAuthProperties.Alg alg, GRPCAuthProperties properties) { + switch (alg) { + case HMAC512: + return JWT + .require(Algorithm.HMAC512(properties.getSecret())) + .acceptLeeway(2) + .build(); + case RSA512: + return JWT + .require(Algorithm.RSA512(new StaticRSAKeyProvider(properties.getKeys()))) + .acceptLeeway(2) + .build(); + default: + throw new IllegalStateException("Unsupported algorithm"); + } + } + + @ConfigurationProperties("grpc.auth") + @Data + @GroupSequenceProvider(GRPCAuthProperties.EnabledSequenceProvider.class) + static class GRPCAuthProperties { + + Alg alg = Alg.NONE; + + @NotEmpty(groups = Symmetric.class) + String secret; + + @NotEmpty(groups = Asymmetric.class) + Map keys = Map.of(); + + enum Alg { + NONE, + RSA512, + HMAC512, + } + + interface Symmetric { + } + + interface Asymmetric { + } + + public static class EnabledSequenceProvider implements DefaultGroupSequenceProvider { + + @Override + public List> getValidationGroups(GRPCAuthProperties object) { + var sequence = new ArrayList>(); + sequence.add(GRPCAuthProperties.class); + if (object != null && object.getAlg() == Alg.HMAC512) { + sequence.add(Symmetric.class); + } + if (object != null && object.getAlg() == Alg.RSA512) { + sequence.add(Asymmetric.class); + } + return sequence; + } + } + } +} \ No newline at end of file diff --git a/plugins/grpc-transport-auth/src/test/java/com/github/bsideup/liiklus/transport/grpc/StaticRSAKeyProviderTest.java b/plugins/grpc-transport-auth/src/test/java/com/github/bsideup/liiklus/transport/grpc/StaticRSAKeyProviderTest.java new file mode 100644 index 00000000..5b0e523a --- /dev/null +++ b/plugins/grpc-transport-auth/src/test/java/com/github/bsideup/liiklus/transport/grpc/StaticRSAKeyProviderTest.java @@ -0,0 +1,79 @@ +package com.github.bsideup.liiklus.transport.grpc; + + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.security.spec.InvalidKeySpecException; +import java.util.Map; +import java.util.NoSuchElementException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class StaticRSAKeyProviderTest { + + private static final String STRIPPED_4096 = "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApkuZHC50VVyc6mkqWMXl" + + "+fuhBmXhw8N8w6A0mlxRDHltdsPYvxE5n/Id4xUDCISZfjXIuSVyq/a7K3esbEqc" + + "fs6/dm6PQuLMsaxEzS3Gxn+QxELJ41IyKiT0DFAhorSoFChfzkkS7whHm+O8wVDI" + + "Z0Aj5TjY5t0/1CvU7wKeMDjVqOR3usEb37/5qu4ps0RbgQzBKjoJ3LSo/tt4tZw+" + + "V3dT2lEVCKCA9OA0I5UXFUwUyMH8NudSlEpExGcmNHM4sEW4NK4Y7RW9tyDT0RQR" + + "ydUIP8rXkjqyxMyHnwNUuzxJHqIXAdEhzw2xGLBSxr87wfmK09TEfSjmMemHfCfF" + + "Ht+esDSy7zRB68hCS/chyN57xyBWG3BeaKeJm34gLU6gt+9Bhvq90a0RXA7TXK7y" + + "QwhDQQwNPhUQshE036l/jCDxmgJZPNkvpweAeROsoEDf5o0TRaybXbyQh+jn+iJP" + + "ve7K2bTixmjlQKOWB4HZ+1YWyTzUabpdeuHVokKuVFzpKqi5oid3Bz17XU4fN36e" + + "M9CSV1urnlgdVwKwYttFwuerstwpB2rOT1UmamQhPwfDGy9x2d2vghSi+ELzKkKv" + + "yAlkIdeK/WLIi3l/R4pCFC1JfAGagXS+Jtvr9+PkiD3bG220HpW1ry68CZcsO91z" + + "7UCJcQMxXdt1gk3K+EbWaDUCAwEAAQ=="; + + private static final String FULL_2048 = "-----BEGIN PUBLIC KEY-----\n" + + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6b/6nQLIQQ8fHT4PcSyb\n" + + "hOLUE/237dgicbjsE7/Z/uPffuc36NTMJ122ppz6dWYnCrQ6CeTgAde4hlLE7Kvv\n" + + "aFiUbe5XKwSL8KV292XqrwRZhMI58TTTygcrBodYGzHy0Yytv703rz+9Qt5HO5BF\n" + + "02/+sM+Z0wlH6aXl3K3/2HfSOfitqnArBGaAs+PRNX2jlVKD1c9Cb7vo5L0X7q+6\n" + + "55uBErEoN7IHbj1u33qI/xEvPSycIiT2RXMGZkvDZH6mTsALel4aP4Qpp1NcE+kD\n" + + "itoBYAPTGgR4gBQveXZmD10yUVgJl2icINY3FvT9oJB6wgCY9+iTvufPppT1RPFH\n" + + "dQIDAQAB\n" + + "-----END PUBLIC KEY-----\n"; + + @ParameterizedTest + @ValueSource(strings = { + STRIPPED_4096, + FULL_2048 + }) + void shouldParseX509(String pubkey) throws InvalidKeySpecException { + var parsed = StaticRSAKeyProvider.parsePubKey(pubkey); + assertThat(parsed).isNotNull(); + assertThat(parsed.getAlgorithm()).isEqualTo("RSA"); + } + + @Test + void shouldThrowExceptionOnInvalid() { + assertThatThrownBy(() -> StaticRSAKeyProvider.parsePubKey("")).isInstanceOf(InvalidKeySpecException.class); + } + + @Test + void shouldCreateProviderInstance() { + new StaticRSAKeyProvider(Map.of( + "valid", STRIPPED_4096 + )); + } + + @Test + void shouldHandleValidAndInvalidWithExceptionInConstructor() { + assertThatThrownBy(() -> new StaticRSAKeyProvider(Map.of( + "valid", STRIPPED_4096, + "invalid", "" + ))) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void shouldComplainOnNotFoundKey() { + assertThatThrownBy(() -> new StaticRSAKeyProvider(Map.of( + "valid", STRIPPED_4096 + )).getPublicKeyById("unknown")) + .isInstanceOf(NoSuchElementException.class); + } +} diff --git a/plugins/grpc-transport-auth/src/test/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfigTest.java b/plugins/grpc-transport-auth/src/test/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfigTest.java new file mode 100644 index 00000000..cae06801 --- /dev/null +++ b/plugins/grpc-transport-auth/src/test/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfigTest.java @@ -0,0 +1,93 @@ +package com.github.bsideup.liiklus.transport.grpc.config; + +import com.github.bsideup.liiklus.transport.grpc.GRPCLiiklusTransportConfigurer; +import org.junit.jupiter.api.Test; +import org.springframework.beans.BeansException; +import org.springframework.boot.context.properties.bind.validation.BindValidationException; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.support.StaticApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; + +class GRPCAuthConfigTest { + private static final String FULL_2048 = "-----BEGIN PUBLIC KEY-----\n" + + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6b/6nQLIQQ8fHT4PcSyb\n" + + "hOLUE/237dgicbjsE7/Z/uPffuc36NTMJ122ppz6dWYnCrQ6CeTgAde4hlLE7Kvv\n" + + "aFiUbe5XKwSL8KV292XqrwRZhMI58TTTygcrBodYGzHy0Yytv703rz+9Qt5HO5BF\n" + + "02/+sM+Z0wlH6aXl3K3/2HfSOfitqnArBGaAs+PRNX2jlVKD1c9Cb7vo5L0X7q+6\n" + + "55uBErEoN7IHbj1u33qI/xEvPSycIiT2RXMGZkvDZH6mTsALel4aP4Qpp1NcE+kD\n" + + "itoBYAPTGgR4gBQveXZmD10yUVgJl2icINY3FvT9oJB6wgCY9+iTvufPppT1RPFH\n" + + "dQIDAQAB\n" + + "-----END PUBLIC KEY-----\n"; + + ApplicationContextRunner applicationContextRunner = new ApplicationContextRunner(() -> new StaticApplicationContext() { + @Override + public void refresh() throws BeansException, IllegalStateException { + } + }) + .withInitializer((ApplicationContextInitializer) new GRPCAuthConfig()); + + @Test + void shouldRequireAlg() { + applicationContextRunner = applicationContextRunner.withPropertyValues( + "spring.profiles.active: not_gateway", + "grpc.auth.alg: NONE" + ); + applicationContextRunner.run(context -> { + assertThat(context).doesNotHaveBean(GRPCLiiklusTransportConfigurer.class); + }); + } + + @Test + void shouldAddWithHmac512() { + applicationContextRunner = applicationContextRunner.withPropertyValues( + "spring.profiles.active: not_gateway", + "grpc.auth.alg: HMAC512", + "grpc.auth.secret: secret" + ); + applicationContextRunner.run(context -> { + assertThat(context) + .hasSingleBean(GRPCLiiklusTransportConfigurer.class); + }); + } + + @Test + void shouldAddWithRsa512() { + applicationContextRunner = applicationContextRunner.withPropertyValues( + "spring.profiles.active: not_gateway", + "grpc.auth.alg: RSA512", + "grpc.auth.keys.key: " + FULL_2048 + ); + applicationContextRunner.run(context -> { + assertThat(context) + .hasSingleBean(GRPCLiiklusTransportConfigurer.class); + }); + } + + @Test + void shouldValidateParametersHmac512() { + applicationContextRunner = applicationContextRunner.withPropertyValues( + "spring.profiles.active: not_gateway", + "grpc.auth.alg: HMAC512" + ); + applicationContextRunner.run(context -> { + assertThat(context) + .getFailure() + .hasCauseInstanceOf(BindValidationException.class); + }); + } + + @Test + void shouldValidateParametersRsa512() { + applicationContextRunner = applicationContextRunner.withPropertyValues( + "spring.profiles.active: not_gateway", + "grpc.auth.alg: RSA512" + ); + applicationContextRunner.run(context -> { + assertThat(context) + .getFailure() + .hasCauseInstanceOf(BindValidationException.class); + }); + } +} \ No newline at end of file From cd99a8f70f94f9a330ee0d2a3cf88aab9bfa643f Mon Sep 17 00:00:00 2001 From: Kirill Merkushev Date: Thu, 6 Feb 2020 11:10:22 +0100 Subject: [PATCH 02/10] cleanup deps --- build.gradle | 2 +- plugins/grpc-transport-auth/build.gradle | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 22d6d890..06ce6ef1 100644 --- a/build.gradle +++ b/build.gradle @@ -110,7 +110,7 @@ configure(subprojects.findAll { !it.name.startsWith("examples/") }) { dependency 'com.salesforce.servicelibs:reactor-grpc-stub:0.10.0' dependency 'com.avast.grpc.jwt:grpc-java-jwt:0.2.0' - dependency 'com.auth0:java-jwt:3.8.1' + dependency 'com.auth0:java-jwt:3.9.0' dependency 'org.awaitility:awaitility:4.0.1' } diff --git a/plugins/grpc-transport-auth/build.gradle b/plugins/grpc-transport-auth/build.gradle index 21d0116e..9e94e67a 100644 --- a/plugins/grpc-transport-auth/build.gradle +++ b/plugins/grpc-transport-auth/build.gradle @@ -18,21 +18,18 @@ dependencies { annotationProcessor 'com.google.auto.service:auto-service' compileOnly project(":app") - compile project(":grpc-transport") + compileOnly project(":grpc-transport") + compile 'com.auth0:java-jwt' compile 'com.avast.grpc.jwt:grpc-java-jwt' - compileOnly project(":api") - testCompileOnly 'org.projectlombok:lombok' testAnnotationProcessor 'org.projectlombok:lombok' testCompile 'org.junit.jupiter:junit-jupiter-engine' testCompile 'org.junit.jupiter:junit-jupiter-params' testCompile project(":app") testCompile project(":testing") - testCompile project(":client") + testCompile project(":grpc-transport") testCompile 'org.springframework.boot:spring-boot-starter-test' - testCompile 'org.springframework.boot:spring-boot-starter-validation' - testCompile 'com.fasterxml.jackson.core:jackson-databind' } From d64ae0ec2e834997a0cf409c7bd6a667ee26bce3 Mon Sep 17 00:00:00 2001 From: Kirill Merkushev Date: Fri, 7 Feb 2020 17:44:16 +0100 Subject: [PATCH 03/10] integration test --- app/build.gradle | 5 + .../github/bsideup/liiklus/GRPCAuthTest.java | 195 ++++++++++++++++++ .../keys/private_key_main_2048_pkcs8.pem | 28 +++ build.gradle | 2 +- gradle.properties | 1 + plugins/grpc-transport-auth/build.gradle | 1 + .../transport/grpc/config/GRPCAuthConfig.java | 17 +- 7 files changed, 246 insertions(+), 3 deletions(-) create mode 100644 app/src/test/java/com/github/bsideup/liiklus/GRPCAuthTest.java create mode 100644 app/src/test/resources/keys/private_key_main_2048_pkcs8.pem diff --git a/app/build.gradle b/app/build.gradle index 27b09c88..b43744a6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -29,6 +29,11 @@ dependencies { testCompile 'org.springframework.boot:spring-boot-starter-test' testCompile 'org.assertj:assertj-core' testCompile 'org.awaitility:awaitility' + + // to test JWT grpc auth + testCompile 'com.auth0:java-jwt' + testCompile 'com.avast.grpc.jwt:grpc-java-jwt' + testCompile project(":client") } diff --git a/app/src/test/java/com/github/bsideup/liiklus/GRPCAuthTest.java b/app/src/test/java/com/github/bsideup/liiklus/GRPCAuthTest.java new file mode 100644 index 00000000..3c703d96 --- /dev/null +++ b/app/src/test/java/com/github/bsideup/liiklus/GRPCAuthTest.java @@ -0,0 +1,195 @@ +package com.github.bsideup.liiklus; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.interfaces.RSAKeyProvider; +import com.avast.grpc.jwt.client.JwtCallCredentials; +import com.github.bsideup.liiklus.protocol.PublishRequest; +import com.google.protobuf.ByteString; +import io.grpc.CallOptions; +import io.grpc.Channel; +import io.grpc.ClientCall; +import io.grpc.ClientInterceptor; +import io.grpc.ManagedChannelBuilder; +import io.grpc.MethodDescriptor; +import io.grpc.Server; +import io.grpc.StatusRuntimeException; +import lombok.SneakyThrows; +import org.junit.Test; +import org.pf4j.PluginManager; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; +import reactor.core.publisher.Hooks; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.KeyFactory; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class GRPCAuthTest { + + static { + System.setProperty("plugins.dir", "../plugins"); + System.setProperty("plugins.pathMatcher", "*/build/libs/*.jar"); + + Hooks.onOperatorDebug(); + } + + static ConfigurableApplicationContext startLiiklus(List args) { + return Application.start(args.stream().map(it -> "--" + it).toArray(String[]::new)); + } + + @SneakyThrows + static int getGRPCPort(ApplicationContext ctx) { + var pluginManager = ctx.getBean(PluginManager.class); + + var classLoader = pluginManager.getPluginClassLoader("grpc-transport"); + var serverClazz = classLoader.loadClass(Server.class.getName()); + var getPortMethod = serverClazz.getDeclaredMethod("getPort"); + var server = ctx.getBean(serverClazz); + + return (int) getPortMethod.invoke(server); + } + + + @Test + public void shouldPublishOnlyWithAuthHmac512() { + var args = List.of( + "storage.positions.type=MEMORY", + "storage.records.type=MEMORY", + "rsocket.enabled=false", + "grpc.port=0", + "grpc.auth.alg=HMAC512", + "grpc.auth.secret=secret", + "server.port=0" + ); + var event = PublishRequest.newBuilder() + .setTopic("authorized") + .setValue(ByteString.copyFromUtf8("bar")) + .build(); + + try (var app = startLiiklus(args)) { + int port = getGRPCPort(app); + + var unauthClient = new GRPCLiiklusClient( + ManagedChannelBuilder + .forAddress("localhost", port) + .directExecutor() + .usePlaintext() + .build() + ); + + assertThatThrownBy(() -> unauthClient.publish(event).block()) + .isInstanceOf(StatusRuntimeException.class) + .hasMessageContaining("UNAUTHENTICATED: authorization header not found"); + + + var wrongAuthClient = new GRPCLiiklusClient( + ManagedChannelBuilder + .forAddress("localhost", port) + .directExecutor() + .usePlaintext() + .intercept(authInterceptor(Algorithm.HMAC256("wrong"))) + .build() + ); + + assertThatThrownBy(() -> wrongAuthClient.publish(event).block()) + .isInstanceOf(StatusRuntimeException.class) + .hasMessageContaining("UNAUTHENTICATED: authorization header validation failed: The provided Algorithm doesn't match the one defined in the JWT's Header"); + + + var authenticatedClient = new GRPCLiiklusClient( + ManagedChannelBuilder + .forAddress("localhost", port) + .directExecutor() + .usePlaintext() + .intercept(authInterceptor(Algorithm.HMAC512("secret"))) + .build() + ); + + authenticatedClient.publish(event).block(); + } + } + + @Test + public void shouldPublishWithAuthRsa512() { + var args = List.of( + "storage.positions.type=MEMORY", + "storage.records.type=MEMORY", + "rsocket.enabled=false", + "grpc.port=0", + "grpc.auth.alg=RSA512", + "grpc.auth.keys.main=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6b/6nQLIQQ8fHT4PcSyb" + + "hOLUE/237dgicbjsE7/Z/uPffuc36NTMJ122ppz6dWYnCrQ6CeTgAde4hlLE7Kvv" + + "aFiUbe5XKwSL8KV292XqrwRZhMI58TTTygcrBodYGzHy0Yytv703rz+9Qt5HO5BF" + + "02/+sM+Z0wlH6aXl3K3/2HfSOfitqnArBGaAs+PRNX2jlVKD1c9Cb7vo5L0X7q+6" + + "55uBErEoN7IHbj1u33qI/xEvPSycIiT2RXMGZkvDZH6mTsALel4aP4Qpp1NcE+kD" + + "itoBYAPTGgR4gBQveXZmD10yUVgJl2icINY3FvT9oJB6wgCY9+iTvufPppT1RPFH" + + "dQIDAQAB", + "server.port=0" + ); + var event = PublishRequest.newBuilder() + .setTopic("authorized") + .setValue(ByteString.copyFromUtf8("bar")) + .build(); + + try (var app = startLiiklus(args)) { + int port = getGRPCPort(app); + + var authenticatedClient = new GRPCLiiklusClient( + ManagedChannelBuilder + .forAddress("localhost", port) + .directExecutor() + .usePlaintext() + .intercept(authInterceptor(Algorithm.RSA512(new RSAKeyProvider() { + @Override + public RSAPublicKey getPublicKeyById(String keyId) { + return null; // not verifying + } + + @SneakyThrows + @Override + public RSAPrivateKey getPrivateKey() { + // you can convert openssh key format to a rsa with `ssh-keygen -p -m PEM -f private_openssh_key` + // and then from the rsa to a pkcs8 by `openssl pkcs8 -topk8 -nocrypt -in private_openssh_key` + String privateKeyContent = new String(Files.readAllBytes(Paths.get( + ClassLoader.getSystemResource("keys/private_key_main_2048_pkcs8.pem").toURI() + ))) + .replaceAll("\\n", "") + .replace("-----BEGIN PRIVATE KEY-----", "") + .replace("-----END PRIVATE KEY-----", ""); + + var keySpecPKCS8 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyContent)); + return (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(keySpecPKCS8); + } + + @Override + public String getPrivateKeyId() { + return "main"; // matches with grpc.auth.keys -> ["main"] + } + }))) + .build() + ); + + authenticatedClient.publish(event).block(); + } + } + + private ClientInterceptor authInterceptor(Algorithm alg) { + return new ClientInterceptor() { + @Override + public ClientCall interceptCall(MethodDescriptor call, CallOptions headers, Channel next) { + return next.newCall(call, headers.withCallCredentials(JwtCallCredentials.blocking(() -> JWT + .create() + .sign(alg) + ))); + } + }; + } +} diff --git a/app/src/test/resources/keys/private_key_main_2048_pkcs8.pem b/app/src/test/resources/keys/private_key_main_2048_pkcs8.pem new file mode 100644 index 00000000..1e6456e9 --- /dev/null +++ b/app/src/test/resources/keys/private_key_main_2048_pkcs8.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDpv/qdAshBDx8d +Pg9xLJuE4tQT/bft2CJxuOwTv9n+499+5zfo1MwnXbamnPp1ZicKtDoJ5OAB17iG +UsTsq+9oWJRt7lcrBIvwpXb3ZeqvBFmEwjnxNNPKBysGh1gbMfLRjK2/vTevP71C +3kc7kEXTb/6wz5nTCUfppeXcrf/Yd9I5+K2qcCsEZoCz49E1faOVUoPVz0Jvu+jk +vRfur7rnm4ESsSg3sgduPW7feoj/ES89LJwiJPZFcwZmS8NkfqZOwAt6Xho/hCmn +U1wT6QOK2gFgA9MaBHiAFC95dmYPXTJRWAmXaJwg1jcW9P2gkHrCAJj36JO+58+m +lPVE8Ud1AgMBAAECggEBAKeqljhDg6LvFtFh76+tYIxsK9V/G4yWiPZrv6LW7aZg +i7K6Zacz5JCkLtzKIvlM4dpStoLcUjXgJ7Lp8ekV1y9Qwn8sBAiORVbDSVdiGnmZ +tCB/NRKoYvY6OAmB0ZgINvVKZGLxddzV6orpZ8z4yq1EWzs2Xk87DAMzhXLKuIbq +5KCCoSlckDPCf4azfDgnxee3W5Tb3sH3CWi3msz7cu+2AzLT0OAjE4XYCTiR7vnT +MtpFNX1AR3HiwE1a/8YosOjqKFcjoMSwqjfOcShrS5ePvm4p2agXeJPz924PXB0J ++HXF3uS7D61HOQ5KGnnyN3+l1j3KTXN8jqC2Xzo/qu0CgYEA/ATiy1l4iIf2pue4 +kllv9/sPZqIkcu5QAecnQaYe29bdh8j7rT3wWMXzVDpBXi5xYVK356BuEtRtdw1A +pdjs1jWngvC8dVGnHBVLLy8NYOKd8uDJSFsysyAJ+Zirw+F3o9L0xoGIWgxEA+4m +d44UbcdJtvOBaZ+kremDpHA44p8CgYEA7XE3WwLF6AbZHw5COL72UCeVc9Ist0if +jw2+KP2U8JVj7E/A7dJVvlujJFoWVUmr+NIKzBLOpP/bjkTcGfYFWCxsKDpGoTJz +Vx5OSjAAaRtqiUniyNEI6UKsYLmozxU4sylUGlTeD67yvZUgqdnB+M8Y8J/xJeW4 +4/GoS2MiEWsCgYEAq7zYoCJUVRXyK0L1MCXqe16G3DXaCMgFlYZj5gTCOqVtST7Y +4vG2e4hJjTg1m2yiruOjlyBoYkSIY/yP9XSh0Ee34y8R/hCqhCSum3TA9Sj44a30 +/G3JWu+WXJSBWHapBOaZDzzuIg8BunvrksUrfrOztAy0P7oeirT6lHA1E5kCgYAt +LADP+7MS9VqRIfFPQmUx0pYINs/y/on8eSzYN4YCTyl3Z6TYmc9eK6jZ3ZmqGB3z +dGJBeMJ/eX2Xj1ogRkG2CJ16+bs+J47x3/4c9wzc8i5OeBQBCGOdnOWWcTvASdVD +oHUznTmx2iKsFpbkOV1BrISeIo+KGi4Wj37o+K8eiQKBgHQOPd/5G7ZEyiV+l8rB +Z1c7DpyqN1yj3cEO9k3Pw485e6GxYJOWHOuqYu7AWhiFi3zN2OuyGQfykjwAs5v3 +B6JIhIk2bGv17b54skr4KgZTzeseWwtN918G8iXz0LgZGaafdLywMkgTM1lyqO0i +DJbKe/M6FGmMmbz7j5NRdE/6 +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/build.gradle b/build.gradle index 06ce6ef1..4c26f9bf 100644 --- a/build.gradle +++ b/build.gradle @@ -78,7 +78,7 @@ configure(subprojects.findAll { !it.name.startsWith("examples/") }) { mavenBom 'org.junit:junit-bom:5.5.2' mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES mavenBom 'org.testcontainers:testcontainers-bom:1.12.3' - mavenBom 'io.grpc:grpc-bom:1.24.1' + mavenBom 'io.grpc:grpc-bom:1.23.0' mavenBom 'com.google.protobuf:protobuf-bom:3.10.0' } diff --git a/gradle.properties b/gradle.properties index 16089002..f5720526 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,2 @@ org.gradle.caching=true +version=0.0.1-SNAPSHOT \ No newline at end of file diff --git a/plugins/grpc-transport-auth/build.gradle b/plugins/grpc-transport-auth/build.gradle index 9e94e67a..94ae6cc4 100644 --- a/plugins/grpc-transport-auth/build.gradle +++ b/plugins/grpc-transport-auth/build.gradle @@ -3,6 +3,7 @@ jar { attributes( 'Plugin-Id': "${project.name}", 'Plugin-Version': "${project.version}", + 'Plugin-Dependencies': 'grpc-transport', ) } diff --git a/plugins/grpc-transport-auth/src/main/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfig.java b/plugins/grpc-transport-auth/src/main/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfig.java index 392936e7..469a8cc8 100644 --- a/plugins/grpc-transport-auth/src/main/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfig.java +++ b/plugins/grpc-transport-auth/src/main/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfig.java @@ -8,7 +8,9 @@ import com.github.bsideup.liiklus.transport.grpc.StaticRSAKeyProvider; import com.github.bsideup.liiklus.util.PropertiesUtil; import com.google.auto.service.AutoService; +import io.grpc.netty.NettyServerBuilder; import lombok.Data; +import lombok.Value; import lombok.extern.slf4j.Slf4j; import org.hibernate.validator.group.GroupSequenceProvider; import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; @@ -37,11 +39,12 @@ public void initialize(GenericApplicationContext applicationContext) { log.info("GRPC Authorization ENABLED with algorithm {}", authProperties.getAlg()); + // Init it early to check that everything is fine in config JWTVerifier verifier = createVerifier(authProperties.getAlg(), authProperties); applicationContext.registerBean( - GRPCLiiklusTransportConfigurer.class, - () -> builder -> builder.intercept(new JwtServerInterceptor<>(verifier::verify)) + JWTAuthGRPCTransportConfigurer.class, + () -> new JWTAuthGRPCTransportConfigurer(verifier) ); } @@ -62,6 +65,16 @@ private JWTVerifier createVerifier(GRPCAuthProperties.Alg alg, GRPCAuthPropertie } } + @Value + private static class JWTAuthGRPCTransportConfigurer implements GRPCLiiklusTransportConfigurer { + private JWTVerifier verifier; + + @Override + public void apply(NettyServerBuilder builder) { + builder.intercept(new JwtServerInterceptor<>(verifier::verify)); + } + } + @ConfigurationProperties("grpc.auth") @Data @GroupSequenceProvider(GRPCAuthProperties.EnabledSequenceProvider.class) From 5b611c9476d1838abae30e6e9c8f5e5ac7597511 Mon Sep 17 00:00:00 2001 From: Kirill Merkushev Date: Fri, 7 Feb 2020 17:51:11 +0100 Subject: [PATCH 04/10] fix side-effect of default version change --- .../main/java/com/github/bsideup/liiklus/ApplicationRunner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tck/src/main/java/com/github/bsideup/liiklus/ApplicationRunner.java b/tck/src/main/java/com/github/bsideup/liiklus/ApplicationRunner.java index c600d002..2ebdd617 100644 --- a/tck/src/main/java/com/github/bsideup/liiklus/ApplicationRunner.java +++ b/tck/src/main/java/com/github/bsideup/liiklus/ApplicationRunner.java @@ -42,7 +42,7 @@ public ConfigurableApplicationContext run() { var tempFile = Files.createTempFile("app", ".jar"); tempFile.toFile().deleteOnExit(); - try (var appJarStream = getClass().getClassLoader().getResourceAsStream("app-boot.jar")) { + try (var appJarStream = getClass().getClassLoader().getResourceAsStream("app-0.0.1-SNAPSHOT-boot.jar")) { Files.copy(appJarStream, tempFile, StandardCopyOption.REPLACE_EXISTING); } From 8b18f31ccd485eac865683d6826aadbbb9c99b74 Mon Sep 17 00:00:00 2001 From: Kirill Merkushev Date: Fri, 7 Feb 2020 18:14:57 +0100 Subject: [PATCH 05/10] fix side-effect of default version change --- .../plugins/example/support/AbstractIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/plugin/src/test/java/com/github/bsideup/liiklus/plugins/example/support/AbstractIntegrationTest.java b/examples/plugin/src/test/java/com/github/bsideup/liiklus/plugins/example/support/AbstractIntegrationTest.java index ba772dbf..cd1d160d 100644 --- a/examples/plugin/src/test/java/com/github/bsideup/liiklus/plugins/example/support/AbstractIntegrationTest.java +++ b/examples/plugin/src/test/java/com/github/bsideup/liiklus/plugins/example/support/AbstractIntegrationTest.java @@ -22,7 +22,7 @@ public abstract class AbstractIntegrationTest { static { LiiklusContainer liiklus = new LiiklusContainer("0.7.0") .withEnv("storage_records_type", "MEMORY") - .withClasspathResourceMapping("/example-plugin.jar", "/app/plugins/example-plugin.jar", BindMode.READ_ONLY) + .withClasspathResourceMapping("/example-plugin-0.0.1-SNAPSHOT.jar", "/app/plugins/example-plugin.jar", BindMode.READ_ONLY) .withLogConsumer(new ToStringConsumer() { @Override public void accept(OutputFrame outputFrame) { From 7cc32a398f8cc8f2ecaa9018ce13588ab57596b4 Mon Sep 17 00:00:00 2001 From: Kirill Merkushev Date: Fri, 7 Feb 2020 23:12:13 +0100 Subject: [PATCH 06/10] Update plugins/grpc-transport-auth/build.gradle Co-Authored-By: Sergei Egorov --- plugins/grpc-transport-auth/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/grpc-transport-auth/build.gradle b/plugins/grpc-transport-auth/build.gradle index 94ae6cc4..ec2a77af 100644 --- a/plugins/grpc-transport-auth/build.gradle +++ b/plugins/grpc-transport-auth/build.gradle @@ -3,7 +3,7 @@ jar { attributes( 'Plugin-Id': "${project.name}", 'Plugin-Version': "${project.version}", - 'Plugin-Dependencies': 'grpc-transport', + 'Plugin-Dependencies': project(":grpc-transport").name, ) } From 73209022d02cabc6d1577f3e74761be2d151522f Mon Sep 17 00:00:00 2001 From: Kirill Merkushev Date: Sat, 8 Feb 2020 00:02:12 +0100 Subject: [PATCH 07/10] reuse tck app runner to keep e2e test in module --- app/build.gradle | 5 -- plugins/grpc-transport-auth/build.gradle | 2 + .../liiklus/transport/grpc}/GRPCAuthTest.java | 63 +++++++------------ .../keys/private_key_main_2048_pkcs8.pem | 0 4 files changed, 25 insertions(+), 45 deletions(-) rename {app/src/test/java/com/github/bsideup/liiklus => plugins/grpc-transport-auth/src/test/java/com/github/bsideup/liiklus/transport/grpc}/GRPCAuthTest.java (83%) rename {app => plugins/grpc-transport-auth}/src/test/resources/keys/private_key_main_2048_pkcs8.pem (100%) diff --git a/app/build.gradle b/app/build.gradle index b43744a6..27b09c88 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -29,11 +29,6 @@ dependencies { testCompile 'org.springframework.boot:spring-boot-starter-test' testCompile 'org.assertj:assertj-core' testCompile 'org.awaitility:awaitility' - - // to test JWT grpc auth - testCompile 'com.auth0:java-jwt' - testCompile 'com.avast.grpc.jwt:grpc-java-jwt' - testCompile project(":client") } diff --git a/plugins/grpc-transport-auth/build.gradle b/plugins/grpc-transport-auth/build.gradle index ec2a77af..64a73cc1 100644 --- a/plugins/grpc-transport-auth/build.gradle +++ b/plugins/grpc-transport-auth/build.gradle @@ -31,6 +31,8 @@ dependencies { testCompile project(":app") testCompile project(":testing") testCompile project(":grpc-transport") + testCompile project(":tck") + testCompile project(":client") testCompile 'org.springframework.boot:spring-boot-starter-test' } diff --git a/app/src/test/java/com/github/bsideup/liiklus/GRPCAuthTest.java b/plugins/grpc-transport-auth/src/test/java/com/github/bsideup/liiklus/transport/grpc/GRPCAuthTest.java similarity index 83% rename from app/src/test/java/com/github/bsideup/liiklus/GRPCAuthTest.java rename to plugins/grpc-transport-auth/src/test/java/com/github/bsideup/liiklus/transport/grpc/GRPCAuthTest.java index 3c703d96..dbd9bd75 100644 --- a/app/src/test/java/com/github/bsideup/liiklus/GRPCAuthTest.java +++ b/plugins/grpc-transport-auth/src/test/java/com/github/bsideup/liiklus/transport/grpc/GRPCAuthTest.java @@ -1,9 +1,11 @@ -package com.github.bsideup.liiklus; +package com.github.bsideup.liiklus.transport.grpc; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.RSAKeyProvider; import com.avast.grpc.jwt.client.JwtCallCredentials; +import com.github.bsideup.liiklus.ApplicationRunner; +import com.github.bsideup.liiklus.GRPCLiiklusClient; import com.github.bsideup.liiklus.protocol.PublishRequest; import com.google.protobuf.ByteString; import io.grpc.CallOptions; @@ -18,8 +20,6 @@ import org.junit.Test; import org.pf4j.PluginManager; import org.springframework.context.ApplicationContext; -import org.springframework.context.ConfigurableApplicationContext; -import reactor.core.publisher.Hooks; import java.nio.file.Files; import java.nio.file.Paths; @@ -34,17 +34,6 @@ public class GRPCAuthTest { - static { - System.setProperty("plugins.dir", "../plugins"); - System.setProperty("plugins.pathMatcher", "*/build/libs/*.jar"); - - Hooks.onOperatorDebug(); - } - - static ConfigurableApplicationContext startLiiklus(List args) { - return Application.start(args.stream().map(it -> "--" + it).toArray(String[]::new)); - } - @SneakyThrows static int getGRPCPort(ApplicationContext ctx) { var pluginManager = ctx.getBean(PluginManager.class); @@ -60,21 +49,18 @@ static int getGRPCPort(ApplicationContext ctx) { @Test public void shouldPublishOnlyWithAuthHmac512() { - var args = List.of( - "storage.positions.type=MEMORY", - "storage.records.type=MEMORY", - "rsocket.enabled=false", - "grpc.port=0", - "grpc.auth.alg=HMAC512", - "grpc.auth.secret=secret", - "server.port=0" - ); var event = PublishRequest.newBuilder() .setTopic("authorized") .setValue(ByteString.copyFromUtf8("bar")) .build(); - try (var app = startLiiklus(args)) { + try (var app = new ApplicationRunner("MEMORY", "MEMORY") + .withProperty("grpc.enabled", true) + .withProperty("grpc.port", 0) + .withProperty("grpc.auth.alg", "HMAC512") + .withProperty("grpc.auth.secret", "secret") + .run() + ) { int port = getGRPCPort(app); var unauthClient = new GRPCLiiklusClient( @@ -119,27 +105,24 @@ public void shouldPublishOnlyWithAuthHmac512() { @Test public void shouldPublishWithAuthRsa512() { - var args = List.of( - "storage.positions.type=MEMORY", - "storage.records.type=MEMORY", - "rsocket.enabled=false", - "grpc.port=0", - "grpc.auth.alg=RSA512", - "grpc.auth.keys.main=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6b/6nQLIQQ8fHT4PcSyb" + - "hOLUE/237dgicbjsE7/Z/uPffuc36NTMJ122ppz6dWYnCrQ6CeTgAde4hlLE7Kvv" + - "aFiUbe5XKwSL8KV292XqrwRZhMI58TTTygcrBodYGzHy0Yytv703rz+9Qt5HO5BF" + - "02/+sM+Z0wlH6aXl3K3/2HfSOfitqnArBGaAs+PRNX2jlVKD1c9Cb7vo5L0X7q+6" + - "55uBErEoN7IHbj1u33qI/xEvPSycIiT2RXMGZkvDZH6mTsALel4aP4Qpp1NcE+kD" + - "itoBYAPTGgR4gBQveXZmD10yUVgJl2icINY3FvT9oJB6wgCY9+iTvufPppT1RPFH" + - "dQIDAQAB", - "server.port=0" - ); var event = PublishRequest.newBuilder() .setTopic("authorized") .setValue(ByteString.copyFromUtf8("bar")) .build(); - try (var app = startLiiklus(args)) { + try (var app = new ApplicationRunner("MEMORY", "MEMORY") + .withProperty("grpc.enabled", true) + .withProperty("grpc.port", 0) + .withProperty("grpc.auth.alg", "RSA512") + .withProperty("grpc.auth.keys.main", "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6b/6nQLIQQ8fHT4PcSyb" + + "hOLUE/237dgicbjsE7/Z/uPffuc36NTMJ122ppz6dWYnCrQ6CeTgAde4hlLE7Kvv" + + "aFiUbe5XKwSL8KV292XqrwRZhMI58TTTygcrBodYGzHy0Yytv703rz+9Qt5HO5BF" + + "02/+sM+Z0wlH6aXl3K3/2HfSOfitqnArBGaAs+PRNX2jlVKD1c9Cb7vo5L0X7q+6" + + "55uBErEoN7IHbj1u33qI/xEvPSycIiT2RXMGZkvDZH6mTsALel4aP4Qpp1NcE+kD" + + "itoBYAPTGgR4gBQveXZmD10yUVgJl2icINY3FvT9oJB6wgCY9+iTvufPppT1RPFH" + + "dQIDAQAB") + .run() + ) { int port = getGRPCPort(app); var authenticatedClient = new GRPCLiiklusClient( diff --git a/app/src/test/resources/keys/private_key_main_2048_pkcs8.pem b/plugins/grpc-transport-auth/src/test/resources/keys/private_key_main_2048_pkcs8.pem similarity index 100% rename from app/src/test/resources/keys/private_key_main_2048_pkcs8.pem rename to plugins/grpc-transport-auth/src/test/resources/keys/private_key_main_2048_pkcs8.pem From 757f20ea9f80cd25eff2f9dc98292634fb3eaf76 Mon Sep 17 00:00:00 2001 From: Kirill Merkushev Date: Sat, 8 Feb 2020 00:24:04 +0100 Subject: [PATCH 08/10] explicit dep on mem plugins --- plugins/grpc-transport-auth/build.gradle | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/grpc-transport-auth/build.gradle b/plugins/grpc-transport-auth/build.gradle index 64a73cc1..9f9ffb57 100644 --- a/plugins/grpc-transport-auth/build.gradle +++ b/plugins/grpc-transport-auth/build.gradle @@ -12,6 +12,16 @@ jar { } } +tasks.test.dependsOn( + jar, + rootProject.project(":inmemory-positions-storage").getTasksByName("jar", false) +) + +tasks.test.dependsOn( + jar, + rootProject.project(":inmemory-records-storage").getTasksByName("jar", false) +) + dependencies { compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' From 4b445bc2d61a42e855d267f48b701fcddb2ef53d Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Wed, 12 Feb 2020 22:27:46 +0100 Subject: [PATCH 09/10] cleanups --- .../liiklus/plugins/LiiklusPluginManager.java | 26 ++++++++++++++----- .../support/AbstractIntegrationTest.java | 2 +- gradle.properties | 1 - plugins/grpc-transport-auth/build.gradle | 22 ++++++---------- .../transport/grpc/config/GRPCAuthConfig.java | 2 +- .../grpc/config/GRPCAuthConfigTest.java | 8 +++--- .../bsideup/liiklus/ApplicationRunner.java | 2 +- 7 files changed, 35 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/com/github/bsideup/liiklus/plugins/LiiklusPluginManager.java b/app/src/main/java/com/github/bsideup/liiklus/plugins/LiiklusPluginManager.java index 43797e21..9cfae262 100644 --- a/app/src/main/java/com/github/bsideup/liiklus/plugins/LiiklusPluginManager.java +++ b/app/src/main/java/com/github/bsideup/liiklus/plugins/LiiklusPluginManager.java @@ -2,12 +2,8 @@ import lombok.Getter; import lombok.NonNull; -import org.pf4j.DefaultPluginManager; -import org.pf4j.ExtensionFinder; -import org.pf4j.ManifestPluginDescriptorFinder; -import org.pf4j.PluginDescriptorFinder; -import org.pf4j.PluginLoader; -import org.pf4j.PluginRepository; +import lombok.experimental.Delegate; +import org.pf4j.*; import java.nio.file.Path; @@ -21,6 +17,24 @@ public LiiklusPluginManager(@NonNull Path pluginsRoot, @NonNull String pluginsPa this.pluginsPathMatcher = pluginsPathMatcher; } + @Override + protected VersionManager createVersionManager() { + var versionManager = super.createVersionManager(); + + class DelegatingVersionManager implements VersionManager { + @Delegate + final VersionManager delegate = versionManager; + } + + return new DelegatingVersionManager() { + @Override + public boolean checkVersionConstraint(String version, String constraint) { + // TODO https://github.com/pf4j/pf4j/issues/367 + return "*".equals(constraint) || super.checkVersionConstraint(version, constraint); + } + }; + } + @Override protected PluginDescriptorFinder createPluginDescriptorFinder() { return new ManifestPluginDescriptorFinder(); diff --git a/examples/plugin/src/test/java/com/github/bsideup/liiklus/plugins/example/support/AbstractIntegrationTest.java b/examples/plugin/src/test/java/com/github/bsideup/liiklus/plugins/example/support/AbstractIntegrationTest.java index cd1d160d..ba772dbf 100644 --- a/examples/plugin/src/test/java/com/github/bsideup/liiklus/plugins/example/support/AbstractIntegrationTest.java +++ b/examples/plugin/src/test/java/com/github/bsideup/liiklus/plugins/example/support/AbstractIntegrationTest.java @@ -22,7 +22,7 @@ public abstract class AbstractIntegrationTest { static { LiiklusContainer liiklus = new LiiklusContainer("0.7.0") .withEnv("storage_records_type", "MEMORY") - .withClasspathResourceMapping("/example-plugin-0.0.1-SNAPSHOT.jar", "/app/plugins/example-plugin.jar", BindMode.READ_ONLY) + .withClasspathResourceMapping("/example-plugin.jar", "/app/plugins/example-plugin.jar", BindMode.READ_ONLY) .withLogConsumer(new ToStringConsumer() { @Override public void accept(OutputFrame outputFrame) { diff --git a/gradle.properties b/gradle.properties index f5720526..16089002 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1 @@ org.gradle.caching=true -version=0.0.1-SNAPSHOT \ No newline at end of file diff --git a/plugins/grpc-transport-auth/build.gradle b/plugins/grpc-transport-auth/build.gradle index 9f9ffb57..50e2db53 100644 --- a/plugins/grpc-transport-auth/build.gradle +++ b/plugins/grpc-transport-auth/build.gradle @@ -3,7 +3,9 @@ jar { attributes( 'Plugin-Id': "${project.name}", 'Plugin-Version': "${project.version}", - 'Plugin-Dependencies': project(":grpc-transport").name, + 'Plugin-Dependencies': [ + project(":grpc-transport").name + ].join(","), ) } @@ -12,14 +14,11 @@ jar { } } +tasks.test.dependsOn(jar) tasks.test.dependsOn( - jar, - rootProject.project(":inmemory-positions-storage").getTasksByName("jar", false) -) - -tasks.test.dependsOn( - jar, - rootProject.project(":inmemory-records-storage").getTasksByName("jar", false) + [":inmemory-records-storage", ":inmemory-positions-storage", ":grpc-transport"].collect { + project(it).getTasksByName("jar", true) + } ) dependencies { @@ -36,13 +35,8 @@ dependencies { testCompileOnly 'org.projectlombok:lombok' testAnnotationProcessor 'org.projectlombok:lombok' - testCompile 'org.junit.jupiter:junit-jupiter-engine' - testCompile 'org.junit.jupiter:junit-jupiter-params' - testCompile project(":app") - testCompile project(":testing") - testCompile project(":grpc-transport") testCompile project(":tck") testCompile project(":client") testCompile 'org.springframework.boot:spring-boot-starter-test' - + testRuntime project(":grpc-transport") } diff --git a/plugins/grpc-transport-auth/src/main/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfig.java b/plugins/grpc-transport-auth/src/main/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfig.java index 469a8cc8..612b1350 100644 --- a/plugins/grpc-transport-auth/src/main/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfig.java +++ b/plugins/grpc-transport-auth/src/main/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfig.java @@ -66,7 +66,7 @@ private JWTVerifier createVerifier(GRPCAuthProperties.Alg alg, GRPCAuthPropertie } @Value - private static class JWTAuthGRPCTransportConfigurer implements GRPCLiiklusTransportConfigurer { + static class JWTAuthGRPCTransportConfigurer implements GRPCLiiklusTransportConfigurer { private JWTVerifier verifier; @Override diff --git a/plugins/grpc-transport-auth/src/test/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfigTest.java b/plugins/grpc-transport-auth/src/test/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfigTest.java index cae06801..700e73f1 100644 --- a/plugins/grpc-transport-auth/src/test/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfigTest.java +++ b/plugins/grpc-transport-auth/src/test/java/com/github/bsideup/liiklus/transport/grpc/config/GRPCAuthConfigTest.java @@ -1,6 +1,6 @@ package com.github.bsideup.liiklus.transport.grpc.config; -import com.github.bsideup.liiklus.transport.grpc.GRPCLiiklusTransportConfigurer; +import com.github.bsideup.liiklus.transport.grpc.config.GRPCAuthConfig.JWTAuthGRPCTransportConfigurer; import org.junit.jupiter.api.Test; import org.springframework.beans.BeansException; import org.springframework.boot.context.properties.bind.validation.BindValidationException; @@ -35,7 +35,7 @@ void shouldRequireAlg() { "grpc.auth.alg: NONE" ); applicationContextRunner.run(context -> { - assertThat(context).doesNotHaveBean(GRPCLiiklusTransportConfigurer.class); + assertThat(context).doesNotHaveBean(JWTAuthGRPCTransportConfigurer.class); }); } @@ -48,7 +48,7 @@ void shouldAddWithHmac512() { ); applicationContextRunner.run(context -> { assertThat(context) - .hasSingleBean(GRPCLiiklusTransportConfigurer.class); + .hasSingleBean(JWTAuthGRPCTransportConfigurer.class); }); } @@ -61,7 +61,7 @@ void shouldAddWithRsa512() { ); applicationContextRunner.run(context -> { assertThat(context) - .hasSingleBean(GRPCLiiklusTransportConfigurer.class); + .hasSingleBean(JWTAuthGRPCTransportConfigurer.class); }); } diff --git a/tck/src/main/java/com/github/bsideup/liiklus/ApplicationRunner.java b/tck/src/main/java/com/github/bsideup/liiklus/ApplicationRunner.java index 2ebdd617..c600d002 100644 --- a/tck/src/main/java/com/github/bsideup/liiklus/ApplicationRunner.java +++ b/tck/src/main/java/com/github/bsideup/liiklus/ApplicationRunner.java @@ -42,7 +42,7 @@ public ConfigurableApplicationContext run() { var tempFile = Files.createTempFile("app", ".jar"); tempFile.toFile().deleteOnExit(); - try (var appJarStream = getClass().getClassLoader().getResourceAsStream("app-0.0.1-SNAPSHOT-boot.jar")) { + try (var appJarStream = getClass().getClassLoader().getResourceAsStream("app-boot.jar")) { Files.copy(appJarStream, tempFile, StandardCopyOption.REPLACE_EXISTING); } From fa4aec91aec335306afedb335cdd2a56ecdba47e Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Thu, 13 Feb 2020 09:29:54 +0100 Subject: [PATCH 10/10] pin the gRPC version and add the issue link --- build.gradle | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 4c26f9bf..25b57cb8 100644 --- a/build.gradle +++ b/build.gradle @@ -78,7 +78,9 @@ configure(subprojects.findAll { !it.name.startsWith("examples/") }) { mavenBom 'org.junit:junit-bom:5.5.2' mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES mavenBom 'org.testcontainers:testcontainers-bom:1.12.3' - mavenBom 'io.grpc:grpc-bom:1.23.0' + + // pinned to 1.23.1, see https://github.com/grpc/grpc-java/issues/6707 + mavenBom 'io.grpc:grpc-bom:1.23.1' mavenBom 'com.google.protobuf:protobuf-bom:3.10.0' }