diff --git a/pom.xml b/pom.xml index e7fe8c7..b188d1d 100644 --- a/pom.xml +++ b/pom.xml @@ -48,6 +48,18 @@ io.quarkus quarkus-smallrye-jwt + + io.quarkus + quarkus-jdbc-postgresql + + + io.quarkus + quarkus-hibernate-orm-panache + + + io.quarkus + quarkus-security-jpa + io.quarkus quarkus-junit5 diff --git a/src/main/java/com/covas/ApplicationScoped/ApplicationLifeCycle.java b/src/main/java/com/covas/ApplicationScoped/ApplicationLifeCycle.java new file mode 100644 index 0000000..ecd1721 --- /dev/null +++ b/src/main/java/com/covas/ApplicationScoped/ApplicationLifeCycle.java @@ -0,0 +1,47 @@ +package com.covas.ApplicationScoped; + + +import org.jboss.logging.Logger; + +import io.quarkus.runtime.ShutdownEvent; +import io.quarkus.runtime.StartupEvent; + +import java.time.LocalDate; +import java.time.Month; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.Observes; +import javax.inject.Inject; +import javax.transaction.Transactional; + +import com.covas.Entity.UsersEntity; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + + +@ApplicationScoped +public class ApplicationLifeCycle { + @Inject + @ConfigProperty(name = "covas.schema.create", defaultValue = "true") + boolean schemaCreate; + + private static final Logger LOGGER = Logger.getLogger(ApplicationLifeCycle.class); + + @Transactional + void onStart(@Observes StartupEvent ev) { + LOGGER.info("The application has started"); + if (schemaCreate){ + UsersEntity.deleteAll(); + LOGGER.info("Robert80 user is created"); + UsersEntity.add("robert80", "robert80@gmail.com", "titi", "robert", LocalDate.of(1990, Month.JANUARY, 23), "toto", "User"); + LOGGER.info("Peter93 user is created"); + UsersEntity.add("peter93", "peter93gmail.com", "yollo", "peter", LocalDate.of(1993, Month.FEBRUARY, 26), "toto", "Admin"); + } else { + LOGGER.info("DB init wassn't created"); + } + } + void onStop(@Observes ShutdownEvent ev) { + LOGGER.info("The application is stopping..."); + } + +} diff --git a/src/main/java/com/covas/ApplicationScoped/ApplicationRoot.java b/src/main/java/com/covas/ApplicationScoped/ApplicationRoot.java new file mode 100644 index 0000000..732f414 --- /dev/null +++ b/src/main/java/com/covas/ApplicationScoped/ApplicationRoot.java @@ -0,0 +1,9 @@ +package com.covas.ApplicationScoped; + +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.core.Application; + +@ApplicationPath("/api") +public class ApplicationRoot extends Application { + +} diff --git a/src/main/java/com/covas/Classes/Hash.java b/src/main/java/com/covas/Classes/Hash.java new file mode 100644 index 0000000..a6a1a29 --- /dev/null +++ b/src/main/java/com/covas/Classes/Hash.java @@ -0,0 +1,42 @@ +package com.covas.Classes; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class Hash { + + + public static String encryptSHA512(String input) + { + try { + // getInstance() method is called with algorithm SHA-512 + MessageDigest md = MessageDigest.getInstance("SHA-512"); + + // digest() method is called + // to calculate message digest of the input string + // returned as array of byte + byte[] messageDigest = md.digest(input.getBytes()); + + // Convert byte array into signum representation + BigInteger no = new BigInteger(1, messageDigest); + + // Convert message digest into hex value + String hashtext = no.toString(16); + + // Add preceding 0s to make it 32 bit + while (hashtext.length() < 32) { + hashtext = "0" + hashtext; + } + + // return the HashText + return hashtext; + } + + // For specifying wrong message digest algorithms + catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/covas/Entity/UsersEntity.java b/src/main/java/com/covas/Entity/UsersEntity.java new file mode 100644 index 0000000..0d17bc8 --- /dev/null +++ b/src/main/java/com/covas/Entity/UsersEntity.java @@ -0,0 +1,64 @@ +package com.covas.Entity; + +import java.time.LocalDate; +import java.util.UUID; + +import javax.annotation.Generated; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import com.covas.Classes.Hash; + +import org.hibernate.annotations.ColumnDefault; +import org.hibernate.annotations.GenericGenerator; + +import io.quarkus.hibernate.orm.panache.PanacheEntityBase; + + +@Entity +@Table(name = "users") +public class UsersEntity extends PanacheEntityBase { + @Id + @Column(name = "id") + @GeneratedValue(generator = "UUID") + @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator") + public UUID id; + + @Column(nullable = false, unique = true) + public String pseudo; + @Column(nullable = false, unique = true) + public String email; + @Column(nullable = false) + public String name; + @Column(nullable = false) + public String firstName; + @Column(nullable = false) + public LocalDate birth; + @ColumnDefault("false") + public Boolean status; + @Column(nullable = false) + public String password; + public String roles; + + public static UsersEntity findByPseudo(String pseudo){ + return find("pseudo", pseudo).firstResult(); + } + + public static void add(String pseudo, String email, String name, String firstName, LocalDate birth, String password, String roles){ + + UsersEntity users = new UsersEntity(); + users.pseudo = pseudo; + users.email = email; + users.name = name; + users.firstName = firstName; + users.birth = birth; + users.status = false; + users.password = Hash.encryptSHA512(password); + users.roles = roles; + users.persist(); + } +} \ No newline at end of file diff --git a/src/main/java/com/covas/HelloRessource.java b/src/main/java/com/covas/HelloRessource.java deleted file mode 100644 index 4b7e99b..0000000 --- a/src/main/java/com/covas/HelloRessource.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.covas; - -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; - -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -@Consumes(MediaType.APPLICATION_JSON) -@Produces(MediaType.APPLICATION_JSON) -@Path("/json") -public class HelloRessource { - Set hello = Collections.synchronizedSet(new LinkedHashSet<>()); - - public HelloRessource(){ - hello.add(new Hello("toto")); - } - - @GET - public Response hello_json(){ - return Response.ok(this.hello).build(); - } -} diff --git a/src/main/java/com/covas/Hello.java b/src/main/java/com/covas/Json/Hello.java similarity index 91% rename from src/main/java/com/covas/Hello.java rename to src/main/java/com/covas/Json/Hello.java index 1250c08..6a5a749 100644 --- a/src/main/java/com/covas/Hello.java +++ b/src/main/java/com/covas/Json/Hello.java @@ -1,4 +1,4 @@ -package com.covas; +package com.covas.Json; import io.quarkus.runtime.annotations.RegisterForReflection; diff --git a/src/main/java/com/covas/Jwt2.java b/src/main/java/com/covas/Jwt2.java deleted file mode 100644 index 7e98745..0000000 --- a/src/main/java/com/covas/Jwt2.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.covas; - -import io.quarkus.runtime.annotations.RegisterForReflection; - -@RegisterForReflection -public class Jwt2 { - - public String name; - public Boolean status; - public String message; - - public Jwt2(){ - name = ""; - status = true; - message = ""; - } - - public Jwt2(String name){ - this.name = name; - status = true; - message = ""; - } - - public Jwt2(String name, String message){ - this.name = name; - this.message = message; - status = true; - } - - public Jwt2(String name, Boolean status, String message){ - this.name = name; - this.status = status; - this.message = message; - } - -} diff --git a/src/main/java/com/covas/GreetingResource.java b/src/main/java/com/covas/Resources/GreetingResource.java similarity index 90% rename from src/main/java/com/covas/GreetingResource.java rename to src/main/java/com/covas/Resources/GreetingResource.java index e40ba8f..312ddc1 100644 --- a/src/main/java/com/covas/GreetingResource.java +++ b/src/main/java/com/covas/Resources/GreetingResource.java @@ -1,4 +1,4 @@ -package com.covas; +package com.covas.Resources; import javax.ws.rs.GET; import javax.ws.rs.Path; diff --git a/src/main/java/com/covas/Resources/HelloRessource.java b/src/main/java/com/covas/Resources/HelloRessource.java new file mode 100644 index 0000000..fe4bc5d --- /dev/null +++ b/src/main/java/com/covas/Resources/HelloRessource.java @@ -0,0 +1,56 @@ +package com.covas.Resources; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +import javax.annotation.security.PermitAll; +import javax.annotation.security.RolesAllowed; +import javax.inject.Inject; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import com.covas.Json.Hello; + +import org.eclipse.microprofile.jwt.JsonWebToken; + +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +@Path("/json") +public class HelloRessource { + @Inject + JsonWebToken jwt; + + Set hello = Collections.synchronizedSet(new LinkedHashSet<>()); + + public HelloRessource(){ + hello.add(new Hello("toto")); + } + + @GET + @PermitAll + @Produces(MediaType.APPLICATION_JSON) + public Response hello_json(){ + return Response.ok(this.hello).build(); + } + + @GET + @Path("/user") + @RolesAllowed({"User"}) + @Produces(MediaType.APPLICATION_JSON) + public Response hello_user(){ + return Response.ok(new Hello(String.format("Hello %s", jwt.getName()))).build(); + } + + @GET + @Path("/admin") + @RolesAllowed({"Admin"}) + @Produces(MediaType.APPLICATION_JSON) + public Response hello_admin(){ + return Response.ok(new Hello(String.format("Hello admin %s", jwt.getName()))).build(); + } +} diff --git a/src/main/java/com/covas/Resources/TokenRessource.java b/src/main/java/com/covas/Resources/TokenRessource.java new file mode 100644 index 0000000..f1864f4 --- /dev/null +++ b/src/main/java/com/covas/Resources/TokenRessource.java @@ -0,0 +1,77 @@ +package com.covas.Resources; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.HashSet; + + +import javax.inject.Inject; +import javax.ws.rs.CookieParam; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.NewCookie; +import javax.ws.rs.core.Response; + +import com.covas.Classes.Hash; +import com.covas.Entity.UsersEntity; + +import io.smallrye.jwt.auth.principal.JWTParser; +import io.smallrye.jwt.auth.principal.ParseException; +import io.smallrye.jwt.build.Jwt; + +import org.eclipse.microprofile.jwt.JsonWebToken; +import org.jboss.resteasy.annotations.jaxrs.HeaderParam; +import org.postgresql.shaded.com.ongres.scram.common.bouncycastle.base64.Base64; + +@Path("/api") +public class TokenRessource { + @Inject + JsonWebToken jwt; + + + @Inject JWTParser parser; + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("token") + public Response getUserName(@HeaderParam("Authorization") String auth, @CookieParam("jwt") String jwtCookie) { + String name = "anonymous"; + String password = ""; + + if (jwtCookie == null) { + String[] hash = new String(Base64.decode(auth.split(" ")[1]), StandardCharsets.UTF_8).split(":"); + name = hash[0]; + password = Hash.encryptSHA512(hash[1]); + + UsersEntity users = UsersEntity.findByPseudo(name); + if(users != null){ + + if(password.equals(users.password)){ + // Create a JWT token signed using the 'HS256' algorithm + String newJwtCookie = Jwt.issuer("https://example.com/issuer").upn(name).groups(new HashSet<>(Arrays.asList(users.roles))).sign(); + // or create a JWT token encrypted using the 'A256KW' algorithm + // Jwt.upn("alice").encryptWithSecret(secret); + return Response.status(Response.Status.CREATED).cookie(new NewCookie("jwt", newJwtCookie)).build(); + } else { + return Response.status(Response.Status.FORBIDDEN).build(); + } + + + } + return Response.status(Response.Status.NOT_FOUND).build(); + + } else { + // All mp.jwt and smallrye.jwt properties are still effective, only the verification key is customized. + try { + jwt = parser.parse(jwtCookie); + } + catch(ParseException p){ + return Response.status(Response.Status.UNAUTHORIZED).build(); + } + // or jwt = parser.decrypt(jwtCookie, secret); + return Response.status(Response.Status.OK).build(); + } + } +} diff --git a/src/main/java/com/covas/Resources/UsersRessources.java b/src/main/java/com/covas/Resources/UsersRessources.java new file mode 100644 index 0000000..ade1086 --- /dev/null +++ b/src/main/java/com/covas/Resources/UsersRessources.java @@ -0,0 +1,48 @@ +package com.covas.Resources; + +import java.util.UUID; + +import javax.annotation.security.RolesAllowed; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import com.covas.Entity.UsersEntity; + +import org.jboss.logging.Logger; + + +@Produces(MediaType.APPLICATION_JSON) +@Path("users") +public class UsersRessources { + private static final Logger LOGGER = Logger.getLogger(UsersRessources.class); + @GET + @RolesAllowed({"Admin"}) + public Response getUsers(){ + return Response.ok(UsersEntity.listAll()).build(); + } + + @GET + @RolesAllowed({"Admin"}) + @Path("{id}") + public Response getSingleUser(@PathParam("id") String id){ + UUID uid = UUID.fromString(id); + UsersEntity users = UsersEntity.findById(uid); + if(users == null){ + return Response.status(Response.Status.NOT_FOUND).build(); + } + return Response.ok(users).build(); + } + + @GET + @RolesAllowed({"User"}) + @Path("info") + public Response getInfoUser(){ + return Response.ok().build(); + } + + +} diff --git a/src/main/java/com/covas/Token.java b/src/main/java/com/covas/Token.java deleted file mode 100644 index fe6eec7..0000000 --- a/src/main/java/com/covas/Token.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.covas; - -import io.quarkus.runtime.annotations.RegisterForReflection; - -@RegisterForReflection -public class Token { - - public String name; - public Boolean isHttps; - public String authScheme; - public Boolean hasJwt; - public String birthday; - public String role = ""; - - public Token(){ - this.name = "anonymous"; - this.isHttps = false; - this.authScheme = ""; - this.hasJwt = false; - this.birthday = ""; - this.role = ""; - } - - public Token(String name, Boolean isHttps, String authScheme, Boolean hasJwt){ - this.name = name; - this.isHttps = isHttps; - this.authScheme = authScheme; - this.hasJwt = hasJwt; - this.birthday = ""; - this.role = ""; - } - - - - -} diff --git a/src/main/java/com/covas/TokenRessource.java b/src/main/java/com/covas/TokenRessource.java deleted file mode 100644 index 2ecd335..0000000 --- a/src/main/java/com/covas/TokenRessource.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.covas; - -import java.util.Arrays; -import java.util.HashSet; - -import javax.annotation.security.PermitAll; -import javax.annotation.security.RolesAllowed; -import javax.inject.Inject; -import javax.ws.rs.CookieParam; -import javax.ws.rs.GET; -import javax.ws.rs.InternalServerErrorException; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.NewCookie; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.SecurityContext; - -import io.smallrye.jwt.auth.principal.JWTParser; -import io.smallrye.jwt.auth.principal.ParseException; -import io.smallrye.jwt.build.Jwt; - -import org.eclipse.microprofile.jwt.JsonWebToken; - - -@Path("/token") -public class TokenRessource { - - - @Inject - JsonWebToken jwt; - - @Inject JWTParser parser; - - - @GET - @Path("authentificate") - @Produces(MediaType.APPLICATION_JSON) - public Response getUserName(@CookieParam("jwt") String jwtCookie) { - if (jwtCookie == null) { - // Create a JWT token signed using the 'HS256' algorithm - String newJwtCookie = Jwt.issuer("https://example.com/issuer").upn("Alice").groups(new HashSet<>(Arrays.asList("User"))).sign(); - // or create a JWT token encrypted using the 'A256KW' algorithm - // Jwt.upn("alice").encryptWithSecret(secret); - - return Response.status(Response.Status.CREATED).entity(new Jwt2("Alice")).cookie(new NewCookie("jwt", newJwtCookie)).build(); - } else { - // All mp.jwt and smallrye.jwt properties are still effective, only the verification key is customized. - try { - jwt = parser.parse(jwtCookie); - } - catch(ParseException p){ - return Response.status(Response.Status.NOT_ACCEPTABLE).entity(new Jwt2("Alice", false, p.getMessage())).build(); - } - // or jwt = parser.decrypt(jwtCookie, secret); - return Response.status(Response.Status.OK).entity(new Jwt2(jwt.getName())).build(); - } - } - - @GET - @Path("permit-all") - @PermitAll - @Produces(MediaType.APPLICATION_JSON) - public Token hello(@Context SecurityContext ctx) { - return getResponseString(ctx); - } - - @GET - @Path("roles-allowed") - @RolesAllowed({"Admin" }) - @Produces(MediaType.APPLICATION_JSON) - public Token helloRolesAllowed(@Context SecurityContext ctx) { - Token token = getResponseString(ctx); - token.name = jwt.getName().toString(); - token.role = "Admin"; - return token; - } - - @GET - @Path("roles-user") - @RolesAllowed({"Toto"}) - @Produces(MediaType.APPLICATION_JSON) - public Token helloRolesUser(@Context SecurityContext ctx) { - Token token = getResponseString(ctx); - token.name = jwt.getName().toString(); - token.role = "User"; - return token; - } - - - private Token getResponseString(SecurityContext ctx) { - String name; - if (ctx.getUserPrincipal() == null) { - name = "anonymous"; - } else if (!ctx.getUserPrincipal().getName().equals(jwt.getName())) { - throw new InternalServerErrorException("Principal and JsonWebToken names do not match"); - } else { - name = ctx.getUserPrincipal().getName(); - } - return new Token(name, ctx.isSecure(), ctx.getAuthenticationScheme(), hasJwt()); - } - - private boolean hasJwt() { - return jwt.getClaimNames() != null; - } - -} diff --git a/src/main/resources/META-INF/resources/index.html b/src/main/resources/META-INF/resources/index.html deleted file mode 100644 index 781d7d2..0000000 --- a/src/main/resources/META-INF/resources/index.html +++ /dev/null @@ -1,282 +0,0 @@ - - - - - covas-quarkus - 1.0 - - - -
-
-
- - - - - quarkus_logo_horizontal_rgb_1280px_reverse - - - - - - - - - - - - - - - - - - -
-
-
- -
-
-
-

You just made a Quarkus application.

-

This page is served by Quarkus.

- Visit the Dev UI -

This page: src/main/resources/META-INF/resources/index.html

-

App configuration: src/main/resources/application.properties

-

Static assets: src/main/resources/META-INF/resources/

-

Code: src/main/java

-

Generated starter code:

-
    -
  • - RESTEasy JAX-RS Easily start your RESTful Web Services -
    @Path: /hello -
    Related guide -
  • - -
-
-
-

Selected extensions

-
    -
  • RESTEasy Jackson
  • -
-
Documentation
-

Practical step-by-step guides to help you achieve a specific goal. Use them to help get your work - done.

-
Set up your IDE
-

Everyone has a favorite IDE they like to use to code. Learn how to configure yours to maximize your - Quarkus productivity.

-
-
-
- - diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 3e147b7..2d02a15 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,6 +1,14 @@ smallrye.jwt.sign.key.location=privateKey.pem - mp.jwt.verify.publickey.location=publicKey.pem -mp.jwt.verify.issuer=https://example.com/issuer \ No newline at end of file +mp.jwt.verify.issuer=https://example.com/issuer + +quarkus.datasource.db-kind = postgresql +quarkus.datasource.username = toto +quarkus.datasource.password = toto +quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/toto +# drop and create the database at startup (use `update` to only update the schema) +quarkus.hibernate-orm.database.generation = drop-and-create + +covas.schema.create = true \ No newline at end of file diff --git a/src/test/java/com/covas/GreetingResourceTest.java b/src/test/java/com/covas/GreetingResourceTest.java index 7fb3ed3..6ee0982 100644 --- a/src/test/java/com/covas/GreetingResourceTest.java +++ b/src/test/java/com/covas/GreetingResourceTest.java @@ -12,7 +12,7 @@ public class GreetingResourceTest { @Test public void testHelloEndpoint() { given() - .when().get("/hello") + .when().get("/api/hello") .then() .statusCode(200) .body(is("Hello RESTEasy"));