From 39e91a88b4306405c4fd363766cebb228f85685e Mon Sep 17 00:00:00 2001 From: tvallin Date: Fri, 3 Nov 2023 17:01:42 +0100 Subject: [PATCH 1/3] Fix security Signed-off-by: tvallin --- .../src/main/archetype/common/security.xml | 222 +--------- .../main/archetype/mp/custom/custom-mp.xml | 1 + .../custom}/files/application.google.yaml | 0 .../files/application.http-signature.yaml | 0 .../custom}/files/application.oidc.yaml | 0 .../java/__pkg__/JwtApplication.java.mustache | 17 + .../java/__pkg__/JwtResource.java.mustache | 24 ++ .../archetype/mp/custom/security-outputs.xml | 102 +++++ .../main/resources/application.yaml.mustache | 12 - .../main/archetype/se/custom/custom-se.xml | 3 +- .../custom}/files/application.jwt.yaml | 0 .../java/__pkg__/GoogleMain.java.mustache | 19 +- .../__pkg__/JwtOverrideService.java.mustache | 6 +- .../__pkg__/OutboundOverrideJwt.java.mustache | 17 +- .../java/__pkg__/SignatureMain.java.mustache | 21 +- .../src/main/resources/WEB/index-google.html | 68 +++ .../files/src/main/resources/signing-jwk.json | 0 .../src/main/resources/verifying-jwk.json | 2 +- .../OutboundOverrideJwtTest.java.mustache | 18 +- .../__pkg__/SignatureMainTest.java.mustache | 8 +- .../archetype/se/custom/security-outputs.xml | 395 ++++++++++++++++++ .../src/main/archetype/se/custom/security.xml | 48 --- 22 files changed, 654 insertions(+), 329 deletions(-) rename archetypes/helidon/src/main/archetype/{common => mp/custom}/files/application.google.yaml (100%) rename archetypes/helidon/src/main/archetype/{common => mp/custom}/files/application.http-signature.yaml (100%) rename archetypes/helidon/src/main/archetype/{common => mp/custom}/files/application.oidc.yaml (100%) create mode 100644 archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/JwtApplication.java.mustache create mode 100644 archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/JwtResource.java.mustache create mode 100644 archetypes/helidon/src/main/archetype/mp/custom/security-outputs.xml rename archetypes/helidon/src/main/archetype/{common => se/custom}/files/application.jwt.yaml (100%) create mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/src/main/resources/WEB/index-google.html rename archetypes/helidon/src/main/archetype/{common => se/custom}/files/src/main/resources/signing-jwk.json (100%) rename archetypes/helidon/src/main/archetype/{common => se/custom}/files/src/main/resources/verifying-jwk.json (89%) create mode 100644 archetypes/helidon/src/main/archetype/se/custom/security-outputs.xml delete mode 100644 archetypes/helidon/src/main/archetype/se/custom/security.xml diff --git a/archetypes/helidon/src/main/archetype/common/security.xml b/archetypes/helidon/src/main/archetype/common/security.xml index 1d162950275..232967c26a9 100644 --- a/archetypes/helidon/src/main/archetype/common/security.xml +++ b/archetypes/helidon/src/main/archetype/common/security.xml @@ -29,144 +29,11 @@ optional="true"> - + @@ -300,27 +105,6 @@ true true - - - io.helidon.microprofile - helidon-microprofile-security - - - io.helidon.webserver - helidon-webserver-security - - - - - - - - - - io.helidon.security.integration.common - io.helidon.webserver.security - diff --git a/archetypes/helidon/src/main/archetype/mp/custom/custom-mp.xml b/archetypes/helidon/src/main/archetype/mp/custom/custom-mp.xml index 485e97114de..0de454edf69 100644 --- a/archetypes/helidon/src/main/archetype/mp/custom/custom-mp.xml +++ b/archetypes/helidon/src/main/archetype/mp/custom/custom-mp.xml @@ -31,6 +31,7 @@ + true diff --git a/archetypes/helidon/src/main/archetype/common/files/application.google.yaml b/archetypes/helidon/src/main/archetype/mp/custom/files/application.google.yaml similarity index 100% rename from archetypes/helidon/src/main/archetype/common/files/application.google.yaml rename to archetypes/helidon/src/main/archetype/mp/custom/files/application.google.yaml diff --git a/archetypes/helidon/src/main/archetype/common/files/application.http-signature.yaml b/archetypes/helidon/src/main/archetype/mp/custom/files/application.http-signature.yaml similarity index 100% rename from archetypes/helidon/src/main/archetype/common/files/application.http-signature.yaml rename to archetypes/helidon/src/main/archetype/mp/custom/files/application.http-signature.yaml diff --git a/archetypes/helidon/src/main/archetype/common/files/application.oidc.yaml b/archetypes/helidon/src/main/archetype/mp/custom/files/application.oidc.yaml similarity index 100% rename from archetypes/helidon/src/main/archetype/common/files/application.oidc.yaml rename to archetypes/helidon/src/main/archetype/mp/custom/files/application.oidc.yaml diff --git a/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/JwtApplication.java.mustache b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/JwtApplication.java.mustache new file mode 100644 index 00000000000..443be088c12 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/JwtApplication.java.mustache @@ -0,0 +1,17 @@ +package {{package}}; + +import java.util.Set; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.ws.rs.core.Application; +import org.eclipse.microprofile.auth.LoginConfig; + +@LoginConfig(authMethod = "MP-JWT") +@ApplicationScoped +public class JwtApplication extends Application { + + @Override + public Set> getClasses() { + return Set.of(JwtResource.class); + } +} diff --git a/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/JwtResource.java.mustache b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/JwtResource.java.mustache new file mode 100644 index 00000000000..c19509029e8 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/JwtResource.java.mustache @@ -0,0 +1,24 @@ +package {{package}}; + +import java.util.Optional; + +import io.helidon.security.Principal; +import io.helidon.security.SecurityContext; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.Context; + +import static jakarta.ws.rs.core.MediaType.TEXT_PLAIN; + +@Path("/hello") +public class JwtResource { + + @GET + @Produces(TEXT_PLAIN) + public String hello(@Context SecurityContext context) { + Optional userPrincipal = context.userPrincipal(); + return "Hello, " + userPrincipal.get().getName() + "!"; + } +} diff --git a/archetypes/helidon/src/main/archetype/mp/custom/security-outputs.xml b/archetypes/helidon/src/main/archetype/mp/custom/security-outputs.xml new file mode 100644 index 00000000000..0b5c77facfa --- /dev/null +++ b/archetypes/helidon/src/main/archetype/mp/custom/security-outputs.xml @@ -0,0 +1,102 @@ + + + + + + + + + + io.helidon.microprofile + helidon-microprofile-oidc + + + + + + + + + + + + files + + **/JwtApplication.java.mustache + **/JwtResource.java.mustache + + + + + + io.helidon.microprofile.jwt + helidon-microprofile-jwt-auth + + + + mp.jwt.verify.issuer=https://{IssuerPublicDomain}/oauth2/default + mp.jwt.verify.publickey.location=${mp.jwt.verify.issuer}/v1/keys + + + io.helidon.microprofile.jwt.auth + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + io.helidon.microprofile + helidon-microprofile-security + + + + + + + + diff --git a/archetypes/helidon/src/main/archetype/se/common/files/src/main/resources/application.yaml.mustache b/archetypes/helidon/src/main/archetype/se/common/files/src/main/resources/application.yaml.mustache index 0957bafc6cb..f59a08c265c 100644 --- a/archetypes/helidon/src/main/archetype/se/common/files/src/main/resources/application.yaml.mustache +++ b/archetypes/helidon/src/main/archetype/se/common/files/src/main/resources/application.yaml.mustache @@ -11,16 +11,4 @@ server: {{.}} {{/application-yaml-entries}} -{{#security}} -security: - config.require-encryption: false - properties: -{{#security-properties}} -{{.}} -{{/security-properties}} - providers: -{{#providers-config-entries}} -{{.}} -{{/providers-config-entries}} -{{/security}} diff --git a/archetypes/helidon/src/main/archetype/se/custom/custom-se.xml b/archetypes/helidon/src/main/archetype/se/custom/custom-se.xml index 7d3105be343..988a0cc38c2 100644 --- a/archetypes/helidon/src/main/archetype/se/custom/custom-se.xml +++ b/archetypes/helidon/src/main/archetype/se/custom/custom-se.xml @@ -25,11 +25,12 @@ - + + diff --git a/archetypes/helidon/src/main/archetype/common/files/application.jwt.yaml b/archetypes/helidon/src/main/archetype/se/custom/files/application.jwt.yaml similarity index 100% rename from archetypes/helidon/src/main/archetype/common/files/application.jwt.yaml rename to archetypes/helidon/src/main/archetype/se/custom/files/application.jwt.yaml diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/GoogleMain.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/GoogleMain.java.mustache index cafa5efce59..193c1727a8c 100644 --- a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/GoogleMain.java.mustache +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/GoogleMain.java.mustache @@ -5,15 +5,15 @@ import java.util.concurrent.TimeUnit; import io.helidon.http.HttpMediaTypes; import io.helidon.logging.common.LogConfig; -import io.helidon.webserver.WebServer; -import io.helidon.webserver.WebServerConfig; -import io.helidon.webserver.context.ContextFeature; -import io.helidon.webserver.staticcontent.StaticContentService; import io.helidon.security.Security; import io.helidon.security.SecurityContext; import io.helidon.security.Subject; -import io.helidon.webserver.security.SecurityFeature; import io.helidon.security.providers.google.login.GoogleTokenProvider; +import io.helidon.webserver.WebServer; +import io.helidon.webserver.WebServerConfig; +import io.helidon.webserver.context.ContextFeature; +import io.helidon.webserver.security.SecurityFeature; +import io.helidon.webserver.staticcontent.StaticContentService; /** * Google login button example main class using builders. @@ -55,11 +55,14 @@ public final class GoogleMain { .addProvider(GoogleTokenProvider.builder() .clientId("your-client-id.apps.googleusercontent.com")) .build(); - server.routing(routing -> routing + server.featuresDiscoverServices(false) .addFeature(ContextFeature.create()) - .addFeature(SecurityFeature.create(security)) + .addFeature(SecurityFeature.builder() + .security(security) + .build()) + .routing(routing -> routing .get("/rest/profile", SecurityFeature.authenticate(), - (req, res) -> { + (req, res) -> { Optional securityContext = req.context().get(SecurityContext.class); res.headers().contentType(HttpMediaTypes.PLAINTEXT_UTF_8); res.send("Response from builder based service, you are: \n" + securityContext diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/JwtOverrideService.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/JwtOverrideService.java.mustache index cccdea39d02..5297e9d6de2 100644 --- a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/JwtOverrideService.java.mustache +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/JwtOverrideService.java.mustache @@ -1,5 +1,7 @@ package {{package}}; +import io.helidon.security.EndpointConfig; +import io.helidon.security.SecurityContext; import io.helidon.webclient.http1.Http1Client; import io.helidon.webclient.security.WebClientSecurity; import io.helidon.webserver.WebServer; @@ -7,8 +9,6 @@ import io.helidon.webserver.http.HttpRules; import io.helidon.webserver.http.HttpService; import io.helidon.webserver.http.ServerRequest; import io.helidon.webserver.http.ServerResponse; -import io.helidon.security.SecurityContext; -import io.helidon.security.providers.jwt.JwtProvider; final class JwtOverrideService implements HttpService { @@ -32,7 +32,7 @@ final class JwtOverrideService implements HttpService { .orElseThrow(() -> new RuntimeException("WebServer not found in context")); String result = client.get("http://localhost:" + server.port("backend") + "/hello") - .property(JwtProvider.EP_PROPERTY_OUTBOUND_USER, "jill") + .property(EndpointConfig.PROPERTY_OUTBOUND_ID, "jill") .requestEntity(String.class); res.send("You are: " + context.userName() + ", backend service returned: " + result); diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/OutboundOverrideJwt.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/OutboundOverrideJwt.java.mustache index 1dc258ca079..d522e37352b 100644 --- a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/OutboundOverrideJwt.java.mustache +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/OutboundOverrideJwt.java.mustache @@ -2,16 +2,15 @@ package {{package}}; import java.util.concurrent.TimeUnit; -import io.helidon.http.Header; import io.helidon.config.Config; import io.helidon.config.ConfigSources; -import io.helidon.webserver.WebServer; -import io.helidon.webserver.WebServerConfig; -import io.helidon.webserver.context.ContextFeature; +import io.helidon.http.HeaderNames; import io.helidon.security.Principal; import io.helidon.security.SecurityContext; import io.helidon.security.Subject; -import io.helidon.webserver.security.SecurityFeature; +import io.helidon.webserver.WebServer; +import io.helidon.webserver.WebServerConfig; +import io.helidon.webserver.security.SecurityHttpFeature; /** * Creates two services. @@ -72,19 +71,17 @@ public final class OutboundOverrideJwt { Config backendConfig = Config.create(ConfigSources.classpath("backend-service-jwt.yaml")); server.routing(routing -> routing - .addFeature(ContextFeature.create()) - .addFeature(SecurityFeature.create(clientConfig.get("security"))) + .addFeature(SecurityHttpFeature.create(clientConfig.get("security.web-server"))) .register(new JwtOverrideService())) // backend that prints the current user .putSocket("backend", socket -> socket .routing(routing -> routing - .addFeature(ContextFeature.create()) - .addFeature(SecurityFeature.create(backendConfig.get("security"))) + .addFeature(SecurityHttpFeature.create(backendConfig.get("security.web-server"))) .get("/hello", (req, res) -> { // This is the token. It should be bearer - req.headers().first(Header.AUTHORIZATION) + req.headers().first(HeaderNames.AUTHORIZATION) .ifPresent(System.out::println); String username = req.context() diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SignatureMain.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SignatureMain.java.mustache index 76a4614d00f..0dfa4204d45 100644 --- a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SignatureMain.java.mustache +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SignatureMain.java.mustache @@ -10,14 +10,9 @@ import java.util.concurrent.TimeUnit; import io.helidon.common.configurable.Resource; import io.helidon.common.pki.Keys; -import io.helidon.webserver.WebServer; -import io.helidon.webserver.WebServerConfig; -import io.helidon.webserver.context.ContextFeature; -import io.helidon.webserver.http.HttpRouting; import io.helidon.security.CompositeProviderFlag; import io.helidon.security.CompositeProviderSelectionPolicy; import io.helidon.security.Security; -import io.helidon.webserver.security.SecurityFeature; import io.helidon.security.providers.common.OutboundConfig; import io.helidon.security.providers.common.OutboundTarget; import io.helidon.security.providers.httpauth.HttpBasicAuthProvider; @@ -25,11 +20,15 @@ import io.helidon.security.providers.httpauth.SecureUserStore; import io.helidon.security.providers.httpsign.HttpSignProvider; import io.helidon.security.providers.httpsign.InboundClientDefinition; import io.helidon.security.providers.httpsign.OutboundTargetDefinition; +import io.helidon.webserver.WebServer; +import io.helidon.webserver.WebServerConfig; +import io.helidon.webserver.http.HttpRouting; +import io.helidon.webserver.security.SecurityFeature; +import io.helidon.webserver.security.SecurityHttpFeature; /** * Example of authentication of service with http signatures, using configuration file as much as possible. */ -@SuppressWarnings("DuplicatedCode") public class SignatureMain { private static final Map USERS = new HashMap<>(); @@ -110,20 +109,18 @@ public class SignatureMain { } private static void routing2(HttpRouting.Builder routing) { - SecurityFeature security = SecurityFeature.create(security2()) + SecurityHttpFeature security = SecurityHttpFeature.create(security2()) .securityDefaults(SecurityFeature.authenticate()); - routing.addFeature(ContextFeature.create()) - .addFeature(security) + routing.addFeature(security) .get("/service2*", SecurityFeature.rolesAllowed("user")) .register(new Service2()); } private static void routing1(HttpRouting.Builder routing) { - SecurityFeature security = SecurityFeature.create(security1()) + SecurityHttpFeature security = SecurityHttpFeature.create(security1()) .securityDefaults(SecurityFeature.authenticate()); - routing.addFeature(ContextFeature.create()) - .addFeature(security) + routing.addFeature(security) .get("/service1*", SecurityFeature.rolesAllowed("user")) .register(new Service1()); } diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/resources/WEB/index-google.html b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/resources/WEB/index-google.html new file mode 100644 index 00000000000..92ddcd61e89 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/resources/WEB/index-google.html @@ -0,0 +1,68 @@ + + + + Google Login Provider + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +

Login to Google.

+
+
+
+ + +
+
+
+ + + diff --git a/archetypes/helidon/src/main/archetype/common/files/src/main/resources/signing-jwk.json b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/resources/signing-jwk.json similarity index 100% rename from archetypes/helidon/src/main/archetype/common/files/src/main/resources/signing-jwk.json rename to archetypes/helidon/src/main/archetype/se/custom/files/src/main/resources/signing-jwk.json diff --git a/archetypes/helidon/src/main/archetype/common/files/src/main/resources/verifying-jwk.json b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/resources/verifying-jwk.json similarity index 89% rename from archetypes/helidon/src/main/archetype/common/files/src/main/resources/verifying-jwk.json rename to archetypes/helidon/src/main/archetype/se/custom/files/src/main/resources/verifying-jwk.json index 09df45ed5b0..5bfc903024a 100644 --- a/archetypes/helidon/src/main/archetype/common/files/src/main/resources/verifying-jwk.json +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/resources/verifying-jwk.json @@ -2,7 +2,7 @@ "keys": [ { "kty": "oct", - "kid": "example", + "kid": "helidon", "alg": "HS256", "key_ops": [ "sign", diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/OutboundOverrideJwtTest.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/OutboundOverrideJwtTest.java.mustache index 72b95d1a19b..5742a2af785 100644 --- a/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/OutboundOverrideJwtTest.java.mustache +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/OutboundOverrideJwtTest.java.mustache @@ -2,16 +2,16 @@ package {{package}}; import java.net.URI; -import io.helidon.webserver.testing.junit5.ServerTest; -import io.helidon.webserver.testing.junit5.SetUpServer; +import io.helidon.security.EndpointConfig; +import io.helidon.security.Security; +import io.helidon.security.providers.httpauth.HttpBasicAuthProvider; import io.helidon.webclient.http1.Http1Client; -import io.helidon.webclient.http1.Http1ClientRequest; import io.helidon.webclient.http1.Http1ClientResponse; import io.helidon.webclient.security.WebClientSecurity; import io.helidon.webserver.WebServer; import io.helidon.webserver.WebServerConfig; -import io.helidon.security.Security; -import io.helidon.security.providers.httpauth.HttpBasicAuthProvider; +import io.helidon.webserver.testing.junit5.ServerTest; +import io.helidon.webserver.testing.junit5.SetUpServer; import org.junit.jupiter.api.Test; @@ -46,8 +46,8 @@ public class OutboundOverrideJwtTest { public void testOverrideExample() { try (Http1ClientResponse response = client.get() .path("/override") - .property(HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_USER, "jack") - .property(HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_PASSWORD, "password") + .property(EndpointConfig.PROPERTY_OUTBOUND_ID, "jack") + .property(EndpointConfig.PROPERTY_OUTBOUND_SECRET, "password") .request()) { assertThat(response.status().code(), is(200)); @@ -61,8 +61,8 @@ public class OutboundOverrideJwtTest { public void testPropagateExample() { try (Http1ClientResponse response = client.get() .path("/propagate") - .property(HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_USER, "jack") - .property(HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_PASSWORD, "password") + .property(EndpointConfig.PROPERTY_OUTBOUND_ID, "jack") + .property(EndpointConfig.PROPERTY_OUTBOUND_SECRET, "password") .request()) { assertThat(response.status().code(), is(200)); diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/SignatureMainTest.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/SignatureMainTest.java.mustache index 2f03ecf9751..bdd4e56be1d 100644 --- a/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/SignatureMainTest.java.mustache +++ b/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/SignatureMainTest.java.mustache @@ -13,8 +13,8 @@ import io.helidon.security.providers.httpauth.HttpBasicAuthProvider; import org.junit.jupiter.api.Test; -import static io.helidon.security.providers.httpauth.HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_PASSWORD; -import static io.helidon.security.providers.httpauth.HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_USER; +import static io.helidon.security.EndpointConfig.PROPERTY_OUTBOUND_SECRET; +import static io.helidon.security.EndpointConfig.PROPERTY_OUTBOUND_ID; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.is; @@ -50,8 +50,8 @@ public abstract class SignatureMainTest { private void test(String uri, Set expectedRoles, Set invalidRoles, String service) { try (Http1ClientResponse response = client.get(uri) - .property(EP_PROPERTY_OUTBOUND_USER, "jack") - .property(EP_PROPERTY_OUTBOUND_PASSWORD, "password") + .property(PROPERTY_OUTBOUND_ID, "jack") + .property(PROPERTY_OUTBOUND_SECRET, "password") .request()) { assertThat(response.status().code(), is(200)); diff --git a/archetypes/helidon/src/main/archetype/se/custom/security-outputs.xml b/archetypes/helidon/src/main/archetype/se/custom/security-outputs.xml new file mode 100644 index 00000000000..fc134386516 --- /dev/null +++ b/archetypes/helidon/src/main/archetype/se/custom/security-outputs.xml @@ -0,0 +1,395 @@ + + + + + + + + + + + + + + + + io.helidon.security.providers + helidon-security-providers-oidc + + + io.helidon.security.providers + helidon-security-providers-idcs-mapper + + + + java.util.Optional + + + io.helidon.common.context.Contexts + io.helidon.http.HttpMediaTypes + io.helidon.security.Security + io.helidon.security.SecurityContext + io.helidon.security.Subject + io.helidon.security.providers.oidc.OidcFeature + + + + + + + + + { + Optional securityContext = req.context().get(SecurityContext.class); + res.headers().contentType(HttpMediaTypes.PLAINTEXT_UTF_8); + res.send("Response from config based service, you are: \n" + securityContext + .flatMap(SecurityContext::user) + .map(Subject::toString) + .orElse("Security context is null")); + }) + .get("/loggedout", (req, res) -> res.send("You have been logged out"))]]> + + + io.helidon.security + io.helidon.security.providers.oidc + + + .identity.oraclecloud.com + 2. idcs-client-id : This is obtained from your IDCS application in the IDCS console + 3. idcs-client-secret : This is obtained from your IDCS application in the IDCS console + 4. frontend-uri : This is the base URL of your application when run, e.g. `http://localhost:7987` + 5. proxy-host : Your proxy server if needed + 6. scope-audience : This is the scope audience which MUST match the primary audience in the IDCS resource, recommendation is not to have a trailing slash (/) + +## Try the application + +Build and run the application and then try the endpoints: + +1. Open http://localhost:7987/rest/profile in your browser. This should present + you with a response highlighting your logged in role (null) correctly as you are not logged in +2. Open `http://localhost:7987/oidc/logout` in your browser. This will log you out from your IDCS and Helidon sessions +]]> + + + + + + + + files + + src/*/java/**/GoogleMain.java.mustache + src/*/java/**/GoogleMainTest.java.mustache + + + + files + + src/*/resources/**/static.js/google-app.js + + + + + + + io.helidon.bundles + helidon-bundles-config + + + io.helidon.webserver + helidon-webserver-static-content + + + + + + + + + + + + + + + + + + files + + src/*/java/**/OutboundOverrideJwt.java.mustache + src/*/java/**/OutboundOverrideJwtTest.java.mustache + src/*/java/**/JwtOverrideService.java.mustache + + + + files + + src/*/resources/**/backend-service-jwt.yaml + src/*/resources/**/client-service-jwt.yaml + src/*/resources/**/signing-jwk.json + src/*/resources/**/verifying-jwk.json + + + + + + io.helidon.webclient + helidon-webclient-security + + + io.helidon.security.providers + helidon-security-providers-http-auth + + + io.helidon.security.providers + helidon-security-providers-jwt + + + io.helidon.bundles + helidon-bundles-config + + + + + + + io.helidon.webclient.security + io.helidon.security.providers.jwt + io.helidon.webclient.http1 + + + + + + + + + + + files + + src/*/java/**/Service1.java.mustache + src/*/java/**/Service2.java.mustache + src/*/java/**/SignatureMain.java.mustache + src/*/java/**/SignatureMainTest.java.mustache + + + + + + io.helidon.webclient + helidon-webclient-security + + + io.helidon.bundles + helidon-bundles-security + + + io.helidon.config + helidon-config-hocon + + + + io.helidon.webclient.http1 + io.helidon.common.pki + io.helidon.webserver.context + io.helidon.security.providers.common + io.helidon.security.providers.httpauth + io.helidon.security.providers.httpsign + + + + + + + + + + + + + io.helidon.security.abac + helidon-security-abac-scope + + + io.helidon.security.abac + helidon-security-abac-role + + + + + + + + + + + + + + + + io.helidon.webserver + helidon-webserver-security + + + + + + + io.helidon.security.integration.common + io.helidon.webserver.security + + + + + + + diff --git a/archetypes/helidon/src/main/archetype/se/custom/security.xml b/archetypes/helidon/src/main/archetype/se/custom/security.xml deleted file mode 100644 index 7bccd44854a..00000000000 --- a/archetypes/helidon/src/main/archetype/se/custom/security.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - files - - src/*/java/**/OutboundOverrideJwtExample.java.mustache - src/*/java/**/OutboundOverrideJwtExampleTest.java.mustache - src/*/java/**/JwtOverrideService.java.mustache - src/*/java/**/GoogleMain.java.mustache - src/*/java/**/GoogleMainTest.java.mustache - src/*/java/**/Service1.java.mustache - src/*/java/**/Service2.java.mustache - src/*/java/**/SignatureMain.java.mustache - src/*/java/**/SignatureMainTest.java.mustache - /**/trick-to-avoid-empty-tag - - - - files - - src/*/resources/**/backend-service-jwt.yaml - src/*/resources/**/client-service-jwt.yaml - src/*/resources/**/google.js/google-app.js - /**/trick-to-avoid-empty-tag - - - - From 73fbcc3b356d5b624420c71bc80ffe3886a49a3f Mon Sep 17 00:00:00 2001 From: tvallin Date: Wed, 8 Nov 2023 11:43:07 +0100 Subject: [PATCH 2/3] improve OIDC generated code Signed-off-by: tvallin --- .../mp/custom/files/application.oidc.yaml | 29 ++++++++++--------- .../java/__pkg__/OidcResource.java.mustache | 27 +++++++++++++++++ .../archetype/mp/custom/security-outputs.xml | 20 +++++++++++++ 3 files changed, 62 insertions(+), 14 deletions(-) create mode 100644 archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/OidcResource.java.mustache diff --git a/archetypes/helidon/src/main/archetype/mp/custom/files/application.oidc.yaml b/archetypes/helidon/src/main/archetype/mp/custom/files/application.oidc.yaml index cfd8a1b9e3a..5f3f2aff3e1 100644 --- a/archetypes/helidon/src/main/archetype/mp/custom/files/application.oidc.yaml +++ b/archetypes/helidon/src/main/archetype/mp/custom/files/application.oidc.yaml @@ -1,15 +1,16 @@ - oidc: - client-id: "client-id-of-this-service" - # See [EncryptionFilter](https://helidon.io/docs/latest/apidocs/io.helidon.config.encryption/io/helidon/config/encryption/EncryptionFilter.html) for details about encrypting passwords in configuration files. - client-secret: "client-secret-of-this-service" - identity-uri: "http://your-tenant.identity-server.com" - frontend-uri: "http://my-service:8080" - audience: "http://my-service" - cors: - allow-origins: ["http://foo.com", "http://there.com"] - allow-methods: ["PUT", "DELETE"] - outbound: - - name: "internal-services" - hosts: ["*.example.org"] - outbound-token: - header: "X-Internal-Auth" + # use a custom name, so it does not clash with other examples + cookie-name: "OIDC_EXAMPLE_COOKIE" + # support for "Authorization" header with bearer token + header-use: true + # the default redirect-uri, where the webserver listens on redirects from identity server + redirect-uri: "/oidc/redirect" + issuer: "https://tenant.some-server.com/oauth2/default" + audience: "configured audience" + client-id: "some client id" + client-secret: "some client secret" + identity-uri: "https://tenant.some-server.com/oauth2/default" + frontend-uri: "http://localhost:7987" + server-type: "@default" + # We want to redirect to login page (and token can be received either through cookie or header) + redirect: true diff --git a/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/OidcResource.java.mustache b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/OidcResource.java.mustache new file mode 100644 index 00000000000..0d132a18ded --- /dev/null +++ b/archetypes/helidon/src/main/archetype/mp/custom/files/src/main/java/__pkg__/OidcResource.java.mustache @@ -0,0 +1,27 @@ +package {{package}}; + +import io.helidon.security.SecurityContext; +import io.helidon.security.annotations.Authenticated; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Context; + +/** + * A simple JAX-RS resource with a single GET method. + */ +@Path("/test") +public class OidcResource { + + /** + * Hello world using security context. + * @param securityContext context as established during login + * @return a string with current username + */ + @Authenticated + @GET + public String getIt(@Context SecurityContext securityContext) { + return "Hello " + securityContext.userName(); + } + +} diff --git a/archetypes/helidon/src/main/archetype/mp/custom/security-outputs.xml b/archetypes/helidon/src/main/archetype/mp/custom/security-outputs.xml index 0b5c77facfa..877d7d94869 100644 --- a/archetypes/helidon/src/main/archetype/mp/custom/security-outputs.xml +++ b/archetypes/helidon/src/main/archetype/mp/custom/security-outputs.xml @@ -22,6 +22,12 @@ + + files + + **/OidcResource.java.mustache + + @@ -32,6 +38,20 @@ + + io.helidon.security + io.helidon.security.annotations + + + + From 765157016c73cf58082bb288bcfd3898bbd5f927 Mon Sep 17 00:00:00 2001 From: tvallin Date: Fri, 10 Nov 2023 10:18:59 +0100 Subject: [PATCH 3/3] remove main security main class + rename google index.html Signed-off-by: tvallin --- .../common/files/application.abac.yaml | 34 -- .../src/main/archetype/common/security.xml | 3 - .../mp/custom/files/application.abac.yaml | 3 + .../mp/custom/files/application.oidc.yaml | 32 +- .../archetype/mp/custom/security-outputs.xml | 10 + .../main/archetype/se/common/common-se.xml | 7 + .../java/__pkg__/GoogleMain.java.mustache | 76 ---- .../__pkg__/OutboundOverrideJwt.java.mustache | 97 ------ .../java/__pkg__/SignatureMain.java.mustache | 218 ------------ .../{index-google.html => google-login.html} | 0 .../java/__pkg__/GoogleMainTest.java.mustache | 44 --- .../archetype/se/custom/security-outputs.xml | 324 ++++++++++++++++-- 12 files changed, 327 insertions(+), 521 deletions(-) delete mode 100644 archetypes/helidon/src/main/archetype/common/files/application.abac.yaml create mode 100644 archetypes/helidon/src/main/archetype/mp/custom/files/application.abac.yaml delete mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/GoogleMain.java.mustache delete mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/OutboundOverrideJwt.java.mustache delete mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SignatureMain.java.mustache rename archetypes/helidon/src/main/archetype/se/custom/files/src/main/resources/WEB/{index-google.html => google-login.html} (100%) delete mode 100644 archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/GoogleMainTest.java.mustache diff --git a/archetypes/helidon/src/main/archetype/common/files/application.abac.yaml b/archetypes/helidon/src/main/archetype/common/files/application.abac.yaml deleted file mode 100644 index 432b05d3d9f..00000000000 --- a/archetypes/helidon/src/main/archetype/common/files/application.abac.yaml +++ /dev/null @@ -1,34 +0,0 @@ - - abac: - # prepares environment - # executes attribute validations - # validates that attributes were processed - # grants/denies access to resource - # - #### - # Combinations: - # # Will fail if any attribute is not validated and if any has failed validation - # fail-on-unvalidated: true - # fail-if-none-validated: true - # - # # Will fail if there is one or more attributes present and NONE of them is validated or if any has failed validation - # # Will NOT fail if there is at least one validated attribute and any number of not validated attributes (and NONE failed) - # fail-on-unvalidated: false - # fail-if-none-validated: true - # - # # Will fail if there is any attribute that failed validation - # # Will NOT fail if there are no failed validation or if there are NONE validated - # fail-on-unvalidated: false - # fail-if-none-validated: false - #### - # fail if an attribute was not validated (e.g. we do not know, whether it is valid or not) - # defaults to true - fail-on-unvalidated: true - # fail if none of the attributes were validated - # defaults to true - fail-if-none-validated: true -# policy-validator: -# validators: -# - class: "io.helidon.security.abac.policy.DefaultPolicyValidator" -# my-custom-policy-engine: -# some-key: "some value" -# another-key: "another value" diff --git a/archetypes/helidon/src/main/archetype/common/security.xml b/archetypes/helidon/src/main/archetype/common/security.xml index 232967c26a9..24eadb99f62 100644 --- a/archetypes/helidon/src/main/archetype/common/security.xml +++ b/archetypes/helidon/src/main/archetype/common/security.xml @@ -84,9 +84,6 @@ + diff --git a/archetypes/helidon/src/main/archetype/se/common/common-se.xml b/archetypes/helidon/src/main/archetype/se/common/common-se.xml index ad391521df8..8aa5442fb25 100644 --- a/archetypes/helidon/src/main/archetype/se/common/common-se.xml +++ b/archetypes/helidon/src/main/archetype/se/common/common-se.xml @@ -125,7 +125,14 @@ .build() .start(); + {{#Main-after-server}} + {{.}} + {{/Main-after-server}} + System.out.println("WEB server is up! http://localhost:" + server.port() + "/simple-greet"); + {{#Main-logging}} + {{.}} + {{/Main-logging}} ]]>
diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/GoogleMain.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/GoogleMain.java.mustache deleted file mode 100644 index 193c1727a8c..00000000000 --- a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/GoogleMain.java.mustache +++ /dev/null @@ -1,76 +0,0 @@ -package {{package}}; - -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import io.helidon.http.HttpMediaTypes; -import io.helidon.logging.common.LogConfig; -import io.helidon.security.Security; -import io.helidon.security.SecurityContext; -import io.helidon.security.Subject; -import io.helidon.security.providers.google.login.GoogleTokenProvider; -import io.helidon.webserver.WebServer; -import io.helidon.webserver.WebServerConfig; -import io.helidon.webserver.context.ContextFeature; -import io.helidon.webserver.security.SecurityFeature; -import io.helidon.webserver.staticcontent.StaticContentService; - -/** - * Google login button example main class using builders. - */ -@SuppressWarnings({"SpellCheckingInspection", "DuplicatedCode"}) -public final class GoogleMain { - - private GoogleMain() { - } - - /** - * Start the example. - * - * @param args ignored - */ - public static void main(String[] args) { - LogConfig.configureRuntime(); - WebServerConfig.Builder builder = WebServerConfig.builder(); - setup(builder); - WebServer server = builder.build(); - - long t = System.nanoTime(); - server.start(); - long time = System.nanoTime() - t; - - System.out.printf(""" - Server started in %d ms - Started server on localhost: %2$d - You can access this example at http://localhost:%2$d/index.html - - Check application.yaml in case you are behind a proxy to configure it - """, - TimeUnit.MILLISECONDS.convert(time, TimeUnit.NANOSECONDS), - server.port()); - } - - static void setup(WebServerConfig.Builder server) { - Security security = Security.builder() - .addProvider(GoogleTokenProvider.builder() - .clientId("your-client-id.apps.googleusercontent.com")) - .build(); - server.featuresDiscoverServices(false) - .addFeature(ContextFeature.create()) - .addFeature(SecurityFeature.builder() - .security(security) - .build()) - .routing(routing -> routing - .get("/rest/profile", SecurityFeature.authenticate(), - (req, res) -> { - Optional securityContext = req.context().get(SecurityContext.class); - res.headers().contentType(HttpMediaTypes.PLAINTEXT_UTF_8); - res.send("Response from builder based service, you are: \n" + securityContext - .flatMap(SecurityContext::user) - .map(Subject::toString) - .orElse("Security context is null")); - res.next(); - }) - .register(StaticContentService.create("/WEB"))); - } -} diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/OutboundOverrideJwt.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/OutboundOverrideJwt.java.mustache deleted file mode 100644 index d522e37352b..00000000000 --- a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/OutboundOverrideJwt.java.mustache +++ /dev/null @@ -1,97 +0,0 @@ -package {{package}}; - -import java.util.concurrent.TimeUnit; - -import io.helidon.config.Config; -import io.helidon.config.ConfigSources; -import io.helidon.http.HeaderNames; -import io.helidon.security.Principal; -import io.helidon.security.SecurityContext; -import io.helidon.security.Subject; -import io.helidon.webserver.WebServer; -import io.helidon.webserver.WebServerConfig; -import io.helidon.webserver.security.SecurityHttpFeature; - -/** - * Creates two services. - * First service invokes the second with outbound security. - * There are two endpoints: - *
    - *
  • One that does simple identity propagation and one that uses an explicit username
  • - *
  • One that uses basic authentication to authenticate users and JWT to propagate identity
  • - *
- one that does simple identity propagation and one that uses an explicit username. - *

- * The difference between this example and basic authentication example: - *

    - *
  • Configuration files (this example uses ones with -jwt.yaml suffix)
  • - *
  • Client property used to override username
  • - *
- */ -public final class OutboundOverrideJwt { - - private OutboundOverrideJwt() { - } - - /** - * Example that propagates identity and on one endpoint explicitly sets the username and password. - * - * @param args ignored - */ - public static void main(String[] args) { - WebServerConfig.Builder builder = WebServer.builder(); - setup(builder); - WebServer server = builder.build(); - - long t = System.nanoTime(); - server.start(); - long time = System.nanoTime() - t; - - server.context().register(server); - - System.out.printf(""" - Server started in %3$d ms - - *********************** - ** Endpoints: ** - *********************** - - http://localhost:%1$d/propagate - http://localhost:%1$d/override - - Backend service started on: http://localhost:%2$d/hello - - """, - server.port(), - server.port("backend"), - TimeUnit.MILLISECONDS.convert(time, TimeUnit.NANOSECONDS)); - } - - static void setup(WebServerConfig.Builder server) { - Config clientConfig = Config.create(ConfigSources.classpath("client-service-jwt.yaml")); - Config backendConfig = Config.create(ConfigSources.classpath("backend-service-jwt.yaml")); - - server.routing(routing -> routing - .addFeature(SecurityHttpFeature.create(clientConfig.get("security.web-server"))) - .register(new JwtOverrideService())) - - // backend that prints the current user - .putSocket("backend", socket -> socket - .routing(routing -> routing - .addFeature(SecurityHttpFeature.create(backendConfig.get("security.web-server"))) - .get("/hello", (req, res) -> { - - // This is the token. It should be bearer - req.headers().first(HeaderNames.AUTHORIZATION) - .ifPresent(System.out::println); - - String username = req.context() - .get(SecurityContext.class) - .flatMap(SecurityContext::user) - .map(Subject::principal) - .map(Principal::getName) - .orElse("Anonymous"); - - res.send(username); - }))); - } -} diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SignatureMain.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SignatureMain.java.mustache deleted file mode 100644 index 0dfa4204d45..00000000000 --- a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/java/__pkg__/SignatureMain.java.mustache +++ /dev/null @@ -1,218 +0,0 @@ -package {{package}}; - -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import io.helidon.common.configurable.Resource; -import io.helidon.common.pki.Keys; -import io.helidon.security.CompositeProviderFlag; -import io.helidon.security.CompositeProviderSelectionPolicy; -import io.helidon.security.Security; -import io.helidon.security.providers.common.OutboundConfig; -import io.helidon.security.providers.common.OutboundTarget; -import io.helidon.security.providers.httpauth.HttpBasicAuthProvider; -import io.helidon.security.providers.httpauth.SecureUserStore; -import io.helidon.security.providers.httpsign.HttpSignProvider; -import io.helidon.security.providers.httpsign.InboundClientDefinition; -import io.helidon.security.providers.httpsign.OutboundTargetDefinition; -import io.helidon.webserver.WebServer; -import io.helidon.webserver.WebServerConfig; -import io.helidon.webserver.http.HttpRouting; -import io.helidon.webserver.security.SecurityFeature; -import io.helidon.webserver.security.SecurityHttpFeature; - -/** - * Example of authentication of service with http signatures, using configuration file as much as possible. - */ -public class SignatureMain { - - private static final Map USERS = new HashMap<>(); - - static { - addUser("jack", "password", List.of("user", "admin")); - addUser("jill", "password", List.of("user")); - addUser("john", "password", List.of()); - } - - private SignatureMain() { - } - - private static void addUser(String user, String password, List roles) { - USERS.put(user, new SecureUserStore.User() { - @Override - public String login() { - return user; - } - - char[] password() { - return password.toCharArray(); - } - - @Override - public boolean isPasswordValid(char[] password) { - return Arrays.equals(password(), password); - } - - @Override - public Collection roles() { - return roles; - } - }); - } - - /** - * Starts this example. - * - * @param args ignored - */ - public static void main(String[] args) { - WebServerConfig.Builder builder = WebServer.builder(); - setup(builder); - WebServer server = builder.build(); - server.context().register(server); - - long t = System.nanoTime(); - server.start(); - long time = System.nanoTime() - t; - - System.out.printf(""" - Server started in %1d ms - - Signature example: from builder - - Users: - jack/password in roles: user, admin - jill/password in roles: user - john/password in no roles - - *********************** - ** Endpoints: ** - *********************** - - Basic authentication, user role required, will use symmetric signatures for outbound: - http://localhost:%2$d/service1 - Basic authentication, user role required, will use asymmetric signatures for outbound: - http://localhost:%3$d/service1-rsa - - """, TimeUnit.MILLISECONDS.convert(time, TimeUnit.NANOSECONDS), server.port(), server.port("service2")); - } - - static void setup(WebServerConfig.Builder server) { - server.routing(SignatureMain::routing1) - .putSocket("service2", socket -> socket - .routing(SignatureMain::routing2)); - } - - private static void routing2(HttpRouting.Builder routing) { - SecurityHttpFeature security = SecurityHttpFeature.create(security2()) - .securityDefaults(SecurityFeature.authenticate()); - - routing.addFeature(security) - .get("/service2*", SecurityFeature.rolesAllowed("user")) - .register(new Service2()); - } - - private static void routing1(HttpRouting.Builder routing) { - SecurityHttpFeature security = SecurityHttpFeature.create(security1()) - .securityDefaults(SecurityFeature.authenticate()); - routing.addFeature(security) - .get("/service1*", SecurityFeature.rolesAllowed("user")) - .register(new Service1()); - } - - private static Security security2() { - return Security.builder() - .providerSelectionPolicy(CompositeProviderSelectionPolicy - .builder() - .addAuthenticationProvider("http-signatures", CompositeProviderFlag.OPTIONAL) - .addAuthenticationProvider("basic-auth") - .build()) - .addProvider(HttpBasicAuthProvider - .builder() - .realm("mic") - .userStore(users()), - "basic-auth") - .addProvider(HttpSignProvider.builder() - .addInbound(InboundClientDefinition - .builder("service1-hmac") - .principalName("Service1 - HMAC signature") - .hmacSecret("somePasswordForHmacShouldBeEncrypted") - .build()) - .addInbound(InboundClientDefinition - .builder("service1-rsa") - .principalName("Service1 - RSA signature") - .publicKeyConfig(Keys.builder() - .keystore(k -> k - .keystore(Resource.create("keystore.p12")) - .passphrase("password") - .certAlias("service_cert") - .build()) - .build()) - .build()), - "http-signatures") - .build(); - } - - private static Security security1() { - return Security.builder() - .providerSelectionPolicy(CompositeProviderSelectionPolicy - .builder() - .addOutboundProvider("basic-auth") - .addOutboundProvider("http-signatures") - .build()) - .addProvider(HttpBasicAuthProvider - .builder() - .realm("mic") - .userStore(users()) - .addOutboundTarget(OutboundTarget.builder("propagate-all").build()), - "basic-auth") - .addProvider(HttpSignProvider - .builder() - .outbound(OutboundConfig - .builder() - .addTarget(hmacTarget()) - .addTarget(rsaTarget()) - .build()), - "http-signatures") - .build(); - } - - private static OutboundTarget rsaTarget() { - return OutboundTarget.builder("service2-rsa") - .addHost("localhost") - .addPath("/service2-rsa.*") - .customObject(OutboundTargetDefinition.class, - OutboundTargetDefinition.builder("service1-rsa") - .privateKeyConfig(Keys.builder() - .keystore(k -> k - .keystore(Resource.create("keystore.p12")) - .passphrase("password") - .keyAlias("myPrivateKey") - .build()) - .build()) - .build()) - .build(); - } - - private static OutboundTarget hmacTarget() { - return OutboundTarget.builder("service2") - .addHost("localhost") - .addPath("/service2") - .customObject( - OutboundTargetDefinition.class, - OutboundTargetDefinition - .builder("service1-hmac") - .hmacSecret("somePasswordForHmacShouldBeEncrypted") - .build()) - .build(); - } - - private static SecureUserStore users() { - return login -> Optional.ofNullable(USERS.get(login)); - } -} diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/main/resources/WEB/index-google.html b/archetypes/helidon/src/main/archetype/se/custom/files/src/main/resources/WEB/google-login.html similarity index 100% rename from archetypes/helidon/src/main/archetype/se/custom/files/src/main/resources/WEB/index-google.html rename to archetypes/helidon/src/main/archetype/se/custom/files/src/main/resources/WEB/google-login.html diff --git a/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/GoogleMainTest.java.mustache b/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/GoogleMainTest.java.mustache deleted file mode 100644 index 38071538e0f..00000000000 --- a/archetypes/helidon/src/main/archetype/se/custom/files/src/test/java/__pkg__/GoogleMainTest.java.mustache +++ /dev/null @@ -1,44 +0,0 @@ - -package {{package}}; - -import io.helidon.http.HeaderNames; -import io.helidon.http.Status; -import io.helidon.webserver.testing.junit5.ServerTest; -import io.helidon.webserver.testing.junit5.SetUpServer; -import io.helidon.webclient.http1.Http1Client; -import io.helidon.webclient.http1.Http1ClientResponse; -import io.helidon.webserver.WebServerConfig; - -import org.junit.jupiter.api.Test; - -import static io.helidon.common.testing.junit5.OptionalMatcher.optionalValue; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; - -/** - * Unit test for {@link GoogleBuilderMain}. - */ -@ServerTest -public class GoogleMainTest { - - private final Http1Client client; - - GoogleMainTest(Http1Client client) { - this.client = client; - } - - @SetUpServer - public static void setup(WebServerConfig.Builder server) { - GoogleMain.setup(server); - } - - @Test - public void testEndpoint() { - try (Http1ClientResponse response = client.get("/rest/profile").request()) { - - assertThat(response.status(), is(Status.UNAUTHORIZED_401)); - assertThat(response.headers().first(HeaderNames.WWW_AUTHENTICATE), - optionalValue(is("Bearer realm=\"helidon\",scope=\"openid profile email\""))); - } - } -} diff --git a/archetypes/helidon/src/main/archetype/se/custom/security-outputs.xml b/archetypes/helidon/src/main/archetype/se/custom/security-outputs.xml index fc134386516..ea13511f7da 100644 --- a/archetypes/helidon/src/main/archetype/se/custom/security-outputs.xml +++ b/archetypes/helidon/src/main/archetype/se/custom/security-outputs.xml @@ -141,20 +141,13 @@ Build and run the application and then try the endpoints: - - files - - src/*/java/**/GoogleMain.java.mustache - src/*/java/**/GoogleMainTest.java.mustache - - files src/*/resources/**/static.js/google-app.js - + @@ -166,6 +159,55 @@ Build and run the application and then try the endpoints: helidon-webserver-static-content + + java.util.Optional + + + io.helidon.http.HttpMediaTypes + io.helidon.security.Security + io.helidon.security.SecurityContext + io.helidon.security.Subject + io.helidon.security.providers.google.login.GoogleTokenProvider + io.helidon.webserver.WebServer + io.helidon.webserver.WebServerConfig + io.helidon.webserver.context.ContextFeature + io.helidon.webserver.security.SecurityFeature + io.helidon.webserver.staticcontent.StaticContentService + + + + + + + + + { + Optional securityContext = req.context().get(SecurityContext.class); + res.headers().contentType(HttpMediaTypes.PLAINTEXT_UTF_8); + res.send("Response from builder based service, you are: \n" + securityContext + .flatMap(SecurityContext::user) + .map(Subject::toString) + .orElse("Security context is null")); + res.next(); + }) + .register(StaticContentService.create("/WEB")) +]]> + + + + @@ -203,11 +245,6 @@ Update the following files with your client id (it should support http://localho 1. src/main/resources/application.yaml - set security.properties.google-client-id or override it in a file in ~/helidon/examples.yaml 2. src/main/resources/WEB/index.html - update the meta tag in header with name "google-signin-client_id" 3. src/main/java/io/helidon/security/examples/google/GoogleMain.java - update the client id in builder of provider - -## Exercise Google example -```bash -java -cp target/{{artifactId}}.jar {{package}}.GoogleMain -``` ]]> @@ -218,8 +255,6 @@ java -cp target/{{artifactId}}.jar {{package}}.GoogleMain files - src/*/java/**/OutboundOverrideJwt.java.mustache - src/*/java/**/OutboundOverrideJwtTest.java.mustache src/*/java/**/JwtOverrideService.java.mustache @@ -251,8 +286,57 @@ java -cp target/{{artifactId}}.jar {{package}}.GoogleMain helidon-bundles-config
- - + + io.helidon.config.Config + io.helidon.config.ConfigSources + io.helidon.http.HeaderNames + io.helidon.security.Principal + io.helidon.security.SecurityContext + io.helidon.security.Subject + io.helidon.webserver.WebServer + io.helidon.webserver.WebServerConfig + io.helidon.webserver.security.SecurityHttpFeature + + + + + + + + + socket + .routing(routing -> routing + .addFeature(SecurityHttpFeature.create(backendConfig.get("security.web-server"))) + .get("/hello", (req, res) -> { + + // This is the token. It should be bearer + req.headers().first(HeaderNames.AUTHORIZATION) + .ifPresent(System.out::println); + + String username = req.context() + .get(SecurityContext.class) + .flatMap(SecurityContext::user) + .map(Subject::principal) + .map(Principal::getName) + .orElse("Anonymous"); + + res.send(username); + }))) +]]> + + + + + + io.helidon.webclient.security @@ -263,12 +347,6 @@ java -cp target/{{artifactId}}.jar {{package}}.GoogleMain src/*/java/**/Service1.java.mustache src/*/java/**/Service2.java.mustache - src/*/java/**/SignatureMain.java.mustache - src/*/java/**/SignatureMainTest.java.mustache @@ -306,6 +382,186 @@ curl -u "jill:anotherPassword" http://localhost:8080/override helidon-config-hocon + + java.util.Arrays + java.util.Collection + java.util.HashMap + java.util.List + java.util.Map + java.util.Optional + + + io.helidon.common.configurable.Resource + io.helidon.common.pki.Keys + io.helidon.security.CompositeProviderFlag + io.helidon.security.CompositeProviderSelectionPolicy + io.helidon.security.Security + io.helidon.security.providers.common.OutboundConfig + io.helidon.security.providers.common.OutboundTarget + io.helidon.security.providers.httpauth.HttpBasicAuthProvider + io.helidon.security.providers.httpauth.SecureUserStore + io.helidon.security.providers.httpsign.HttpSignProvider + io.helidon.security.providers.httpsign.InboundClientDefinition + io.helidon.security.providers.httpsign.OutboundTargetDefinition + io.helidon.webserver.security.SecurityFeature + io.helidon.webserver.security.SecurityHttpFeature + + + USERS = new HashMap<>(); + + static { + addUser("jack", "password", List.of("user", "admin")); + addUser("jill", "password", List.of("user")); + addUser("john", "password", List.of()); + } + + private static void addUser(String user, String password, List roles) { + USERS.put(user, new SecureUserStore.User() { + @Override + public String login() { + return user; + } + + char[] password() { + return password.toCharArray(); + } + + @Override + public boolean isPasswordValid(char[] password) { + return Arrays.equals(password(), password); + } + + @Override + public Collection roles() { + return roles; + } + }); + } +]]> + + + + + + + + + k + .keystore(Resource.create("keystore.p12")) + .passphrase("password") + .certAlias("service_cert") + .build()) + .build()) + .build()), + "http-signatures") + .build(); + } + + private static Security security1() { + return Security.builder() + .providerSelectionPolicy(CompositeProviderSelectionPolicy + .builder() + .addOutboundProvider("basic-auth") + .addOutboundProvider("http-signatures") + .build()) + .addProvider(HttpBasicAuthProvider + .builder() + .realm("mic") + .userStore(users()) + .addOutboundTarget(OutboundTarget.builder("propagate-all").build()), + "basic-auth") + .addProvider(HttpSignProvider + .builder() + .outbound(OutboundConfig + .builder() + .addTarget(hmacTarget()) + .addTarget(rsaTarget()) + .build()), + "http-signatures") + .build(); + } + + private static OutboundTarget rsaTarget() { + return OutboundTarget.builder("service2-rsa") + .addHost("localhost") + .addPath("/service2-rsa.*") + .customObject(OutboundTargetDefinition.class, + OutboundTargetDefinition.builder("service1-rsa") + .privateKeyConfig(Keys.builder() + .keystore(k -> k + .keystore(Resource.create("keystore.p12")) + .passphrase("password") + .keyAlias("myPrivateKey") + .build()) + .build()) + .build()) + .build(); + } + + private static OutboundTarget hmacTarget() { + return OutboundTarget.builder("service2") + .addHost("localhost") + .addPath("/service2") + .customObject( + OutboundTargetDefinition.class, + OutboundTargetDefinition + .builder("service1-hmac") + .hmacSecret("somePasswordForHmacShouldBeEncrypted") + .build()) + .build(); + } + + private static SecureUserStore users() { + return login -> Optional.ofNullable(USERS.get(login)); + } +]]> + + + socket + .routing(Main::routing2))]]> + + + + io.helidon.webclient.http1 io.helidon.common.pki @@ -318,12 +574,6 @@ curl -u "jill:anotherPassword" http://localhost:8080/override helidon-security-abac-role + + + + - @@ -374,7 +631,8 @@ security: io.helidon.security.integration.common io.helidon.webserver.security - +