Merge pull request 'database' (#2) from database into master

Reviewed-on: #2
This commit is contained in:
v4l3n71n 2022-05-13 17:56:07 +00:00
commit 24152ec239
17 changed files with 368 additions and 496 deletions

12
pom.xml
View File

@ -48,6 +48,18 @@
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-jwt</artifactId> <artifactId>quarkus-smallrye-jwt</artifactId>
</dependency> </dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-security-jpa</artifactId>
</dependency>
<dependency> <dependency>
<groupId>io.quarkus</groupId> <groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId> <artifactId>quarkus-junit5</artifactId>

View File

@ -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...");
}
}

View File

@ -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 {
}

View File

@ -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);
}
}
}

View File

@ -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();
}
}

View File

@ -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> hello = Collections.synchronizedSet(new LinkedHashSet<>());
public HelloRessource(){
hello.add(new Hello("toto"));
}
@GET
public Response hello_json(){
return Response.ok(this.hello).build();
}
}

View File

@ -1,4 +1,4 @@
package com.covas; package com.covas.Json;
import io.quarkus.runtime.annotations.RegisterForReflection; import io.quarkus.runtime.annotations.RegisterForReflection;

View File

@ -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;
}
}

View File

@ -1,4 +1,4 @@
package com.covas; package com.covas.Resources;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.Path; import javax.ws.rs.Path;

View File

@ -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> 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();
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}

View File

@ -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 = "";
}
}

View File

@ -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;
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,14 @@
smallrye.jwt.sign.key.location=privateKey.pem smallrye.jwt.sign.key.location=privateKey.pem
mp.jwt.verify.publickey.location=publicKey.pem mp.jwt.verify.publickey.location=publicKey.pem
mp.jwt.verify.issuer=https://example.com/issuer 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

View File

@ -12,7 +12,7 @@ public class GreetingResourceTest {
@Test @Test
public void testHelloEndpoint() { public void testHelloEndpoint() {
given() given()
.when().get("/hello") .when().get("/api/hello")
.then() .then()
.statusCode(200) .statusCode(200)
.body(is("Hello RESTEasy")); .body(is("Hello RESTEasy"));