diff --git a/.gitea/workflows/back.yaml b/.gitea/workflows/back.yaml new file mode 100644 index 0000000..0e74e38 --- /dev/null +++ b/.gitea/workflows/back.yaml @@ -0,0 +1,19 @@ +name: Format + +on: [ push, pull_request ] + +jobs: + + formatting: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 # v2 minimum required + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' # See 'Supported distributions' for available options + java-version: '21' + - uses: axel-op/googlejavaformat-action@v3 + with: + args: "--replace --skip-sorting-imports --aosp" + - name: Print diffs + run: git --no-pager diff --exit-code \ No newline at end of file diff --git a/.gitignore b/.gitignore index 170ba0d..7117a86 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .idea keycloak/CAS/target docker-compose.yaml +postgres/data \ No newline at end of file diff --git a/Makefile b/Makefile index 6944ef4..26d5ccb 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ dev-front: clean vite prod: clean @cp config/prod.front.env front/MyINPulse-front/.env @cp config/prod.main.env .env - @cp config/frontdev.docker-compose.yaml docker-compose.yaml + @cp config/prod.docker-compose.yaml docker-compose.yaml @docker compose up -d --build diff --git a/MyINPulse-back/src/main/java/enseirb/myinpulse/MyinpulseApplication.java b/MyINPulse-back/src/main/java/enseirb/myinpulse/MyinpulseApplication.java index 23c2f28..196569d 100644 --- a/MyINPulse-back/src/main/java/enseirb/myinpulse/MyinpulseApplication.java +++ b/MyINPulse-back/src/main/java/enseirb/myinpulse/MyinpulseApplication.java @@ -1,29 +1,15 @@ package enseirb.myinpulse; -import enseirb.myinpulse.security.KeycloakJwtRolesConverter; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.core.convert.converter.Converter; -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.jwt.*; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import java.util.*; -import java.util.stream.Collectors; - -import static org.springframework.security.authorization.AuthorityAuthorizationManager.hasRole; @SpringBootApplication public class MyinpulseApplication { - public static void main(String[] args) { - SpringApplication.run(MyinpulseApplication.class, args); - } - - + public static void main(String[] args) { + SpringApplication.run(MyinpulseApplication.class, args); + } } diff --git a/MyINPulse-back/src/main/java/enseirb/myinpulse/api/GetUserInfo.java b/MyINPulse-back/src/main/java/enseirb/myinpulse/api/GetUserInfo.java index 50262e1..63aef48 100644 --- a/MyINPulse-back/src/main/java/enseirb/myinpulse/api/GetUserInfo.java +++ b/MyINPulse-back/src/main/java/enseirb/myinpulse/api/GetUserInfo.java @@ -22,21 +22,21 @@ public class GetUserInfo { @CrossOrigin(methods = {RequestMethod.GET, RequestMethod.OPTIONS}) @GetMapping("/random") - public boolean rand(){ + public boolean rand() { System.err.println("HELLO"); return Math.random() > 0.5; } @CrossOrigin(methods = {RequestMethod.GET, RequestMethod.OPTIONS}) @GetMapping("/random2") - public boolean rand2(){ + public boolean rand2() { System.err.println("HELLO2"); return Math.random() > 0.5; } @CrossOrigin(methods = {RequestMethod.GET, RequestMethod.OPTIONS}) @GetMapping("/random3") - public boolean rand3(){ + public boolean rand3() { System.err.println("HELLO"); return Math.random() > 0.5; } diff --git a/MyINPulse-back/src/main/java/enseirb/myinpulse/config/WebSecurityCustomConfiguration.java b/MyINPulse-back/src/main/java/enseirb/myinpulse/config/WebSecurityCustomConfiguration.java index 14c46b3..43a9889 100644 --- a/MyINPulse-back/src/main/java/enseirb/myinpulse/config/WebSecurityCustomConfiguration.java +++ b/MyINPulse-back/src/main/java/enseirb/myinpulse/config/WebSecurityCustomConfiguration.java @@ -23,10 +23,13 @@ public class WebSecurityCustomConfiguration { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(List.of("*")); configuration.setAllowedMethods(Arrays.asList("GET", "OPTIONS")); - configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type", - "x-auth-token")); // Do not remove, this fixes the CORS errors when unauthenticated - UrlBasedCorsConfigurationSource source = new - UrlBasedCorsConfigurationSource(); + configuration.setAllowedHeaders( + Arrays.asList( + "authorization", + "content-type", + "x-auth-token")); // Do not remove, this fixes the CORS errors when + // unauthenticated + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; @@ -34,17 +37,23 @@ public class WebSecurityCustomConfiguration { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - http - .authorizeHttpRequests(authorize -> authorize - .requestMatchers("/random2").access(hasRole("REALM_MyINPulse-entrepreneur")) - .requestMatchers("/random").access(hasRole("REALM_MyINPulse-admin")) - .requestMatchers("/random3").permitAll() - .anyRequest().authenticated() - ) - .oauth2ResourceServer(oauth2 -> oauth2 - .jwt(jwt -> jwt. - jwtAuthenticationConverter(new KeycloakJwtRolesConverter()))); + http.authorizeHttpRequests( + authorize -> + authorize + .requestMatchers("/random2") + .access(hasRole("REALM_MyINPulse-entrepreneur")) + .requestMatchers("/random") + .access(hasRole("REALM_MyINPulse-admin")) + .requestMatchers("/random3") + .permitAll() + .anyRequest() + .authenticated()) + .oauth2ResourceServer( + oauth2 -> + oauth2.jwt( + jwt -> + jwt.jwtAuthenticationConverter( + new KeycloakJwtRolesConverter()))); return http.build(); - } -} \ No newline at end of file +} diff --git a/MyINPulse-back/src/main/java/enseirb/myinpulse/security/KeycloakJwtRolesConverter.java b/MyINPulse-back/src/main/java/enseirb/myinpulse/security/KeycloakJwtRolesConverter.java index fafbef5..dba41cc 100644 --- a/MyINPulse-back/src/main/java/enseirb/myinpulse/security/KeycloakJwtRolesConverter.java +++ b/MyINPulse-back/src/main/java/enseirb/myinpulse/security/KeycloakJwtRolesConverter.java @@ -16,40 +16,35 @@ import java.util.stream.Stream; import static java.util.stream.Collectors.toSet; - public class KeycloakJwtRolesConverter implements Converter { - /** - * Prefix used for realm level roles. - */ + /** Prefix used for realm level roles. */ public static final String PREFIX_REALM_ROLE = "ROLE_REALM_"; - /** - * Prefix used in combination with the resource (client) name for resource level roles. - */ + + /** Prefix used in combination with the resource (client) name for resource level roles. */ public static final String PREFIX_RESOURCE_ROLE = "ROLE_"; - /** - * Name of the claim containing the realm level roles - */ + /** Name of the claim containing the realm level roles */ private static final String CLAIM_REALM_ACCESS = "realm_access"; - /** - * Name of the claim containing the resources (clients) the user has access to. - */ + + /** Name of the claim containing the resources (clients) the user has access to. */ private static final String CLAIM_RESOURCE_ACCESS = "resource_access"; - /** - * Name of the claim containing roles. (Applicable to realm and resource level.) - */ + + /** Name of the claim containing roles. (Applicable to realm and resource level.) */ private static final String CLAIM_ROLES = "roles"; @Override - public AbstractAuthenticationToken convert(Jwt source) - { - return new JwtAuthenticationToken(source, Stream.concat(new JwtGrantedAuthoritiesConverter().convert(source) - .stream(), TEMPORARNAME(source).stream()) - .collect(toSet())); + public AbstractAuthenticationToken convert(Jwt source) { + return new JwtAuthenticationToken( + source, + Stream.concat( + new JwtGrantedAuthoritiesConverter().convert(source).stream(), + TEMPORARNAME(source).stream()) + .collect(toSet())); } /** - * Extracts the realm and resource level roles from a JWT token distinguishing between them using prefixes. + * Extracts the realm and resource level roles from a JWT token distinguishing between them + * using prefixes. */ public Collection TEMPORARNAME(Jwt jwt) { // Collection that will hold the extracted roles @@ -66,33 +61,43 @@ public class KeycloakJwtRolesConverter implements Converter realmRoles = roles.stream() - // Prefix all realm roles with "ROLE_realm_" - .map(role -> new SimpleGrantedAuthority(PREFIX_REALM_ROLE + role)) - .collect(Collectors.toList()); + Collection realmRoles = + roles.stream() + // Prefix all realm roles with "ROLE_realm_" + .map(role -> new SimpleGrantedAuthority(PREFIX_REALM_ROLE + role)) + .collect(Collectors.toList()); grantedAuthorities.addAll(realmRoles); } } // Resource (client) roles - // A user might have access to multiple resources all containing their own roles. Therefore, it is a map of + // A user might have access to multiple resources all containing their own roles. Therefore, + // it is a map of // resource each possibly containing a "roles" property. - Map>> resourceAccess = jwt.getClaim(CLAIM_RESOURCE_ACCESS); + Map>> resourceAccess = + jwt.getClaim(CLAIM_RESOURCE_ACCESS); // Check if resources are assigned if (resourceAccess != null && !resourceAccess.isEmpty()) { // Iterate of all the resources - resourceAccess.forEach((resource, resourceClaims) -> { - // Iterate of the "roles" claim inside the resource claims - resourceClaims.get(CLAIM_ROLES).forEach( - // Add the role to the granted authority prefixed with ROLE_ and the name of the resource - role -> grantedAuthorities.add(new SimpleGrantedAuthority(PREFIX_RESOURCE_ROLE + resource + "_" + role)) - ); - }); + resourceAccess.forEach( + (resource, resourceClaims) -> { + // Iterate of the "roles" claim inside the resource claims + resourceClaims + .get(CLAIM_ROLES) + .forEach( + // Add the role to the granted authority prefixed with ROLE_ + // and the name of the resource + role -> + grantedAuthorities.add( + new SimpleGrantedAuthority( + PREFIX_RESOURCE_ROLE + + resource + + "_" + + role))); + }); } return grantedAuthorities; } - - } diff --git a/MyINPulse-back/src/test/java/enseirb/myinpulse/MyinpulseApplicationTests.java b/MyINPulse-back/src/test/java/enseirb/myinpulse/MyinpulseApplicationTests.java index dce5ab2..04669ca 100644 --- a/MyINPulse-back/src/test/java/enseirb/myinpulse/MyinpulseApplicationTests.java +++ b/MyINPulse-back/src/test/java/enseirb/myinpulse/MyinpulseApplicationTests.java @@ -6,8 +6,6 @@ import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class MyinpulseApplicationTests { - @Test - void contextLoads() { - } - + @Test + void contextLoads() {} } diff --git a/config/backdev.docker-compose.yaml b/config/backdev.docker-compose.yaml index 607fa49..25151a0 100644 --- a/config/backdev.docker-compose.yaml +++ b/config/backdev.docker-compose.yaml @@ -1,15 +1,14 @@ services: postgres: - image: postgres:latest + env_file: .env + build: + context: postgres/ + dockerfile: Dockerfile container_name: MyINPulse-DB ports: - - 5432:5432 + - 5433:5432 volumes: - - ./postgres:/var/lib/postgresql/data - environment: - POSTGRES_DB: ${POSTGRES_DB} - POSTGRES_USER: ${POSTGRES_USER} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + - ./postgres/data:/var/lib/postgresql/data keycloak: container_name: MyINPulse-keycloak diff --git a/config/backdev.main.env b/config/backdev.main.env index 6ddb261..2a597c3 100644 --- a/config/backdev.main.env +++ b/config/backdev.main.env @@ -1,10 +1,14 @@ -POSTGRES_DB=keycloak_db -POSTGRES_USER=keycloak_db_user -POSTGRES_PASSWORD=keycloak_db_user_password +POSTGRES_DB=postgres_db +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres_db_user_password + KEYCLOAK_ADMIN=admin KEYCLOAK_ADMIN_PASSWORD=admin KEYCLOAK_HOSTNAME=localhost +KEYCLOAK_DB=keycloak_db +KEYCLOAK_USER=keycloak_db_user +KEYCLOAK_PASSWORD=keycloak_db_user_password -MYINPULSE_DB=MyINPulse_db -MYINPULSE_DB_USER=MyINPulse_db_user -MYINPULSE_DB_PASS=MyINPulse_db_user_pass \ No newline at end of file +BACKEND_DB=backend_db +BACKEND_USER=backend_db_user +BACKEND_PASSWORD=backend_db_user_password \ No newline at end of file diff --git a/config/frontdev.docker-compose.yaml b/config/frontdev.docker-compose.yaml index aa6d0d0..529ac2c 100644 --- a/config/frontdev.docker-compose.yaml +++ b/config/frontdev.docker-compose.yaml @@ -1,15 +1,15 @@ services: postgres: - image: postgres:latest + env_file: .env + build: + context: postgres/ + dockerfile: Dockerfile container_name: MyINPulse-DB #ports: # - 5432:5432 volumes: - - ./postgres:/var/lib/postgresql/data - environment: - POSTGRES_DB: ${POSTGRES_DB} - POSTGRES_USER: ${POSTGRES_USER} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + - ./postgres/data:/var/lib/postgresql/data + keycloak: container_name: MyINPulse-keycloak diff --git a/config/frontdev.main.env b/config/frontdev.main.env index 6f32124..26c2803 100644 --- a/config/frontdev.main.env +++ b/config/frontdev.main.env @@ -1,6 +1,14 @@ -POSTGRES_DB=keycloak_db -POSTGRES_USER=keycloak_db_user -POSTGRES_PASSWORD=keycloak_db_user_password +POSTGRES_DB=postgres_db +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres_db_user_password + KEYCLOAK_ADMIN=admin KEYCLOAK_ADMIN_PASSWORD=admin -KEYCLOAK_HOSTNAME=localhost \ No newline at end of file +KEYCLOAK_HOSTNAME=localhost +KEYCLOAK_DB=keycloak_db +KEYCLOAK_USER=keycloak_db_user +KEYCLOAK_PASSWORD=keycloak_db_user_password + +BACKEND_DB=backend_db +BACKEND_USER=backend_db_user +BACKEND_PASSWORD=backend_db_user_password diff --git a/config/prod.docker-compose.yaml b/config/prod.docker-compose.yaml index fe2a3d7..51f2d76 100644 --- a/config/prod.docker-compose.yaml +++ b/config/prod.docker-compose.yaml @@ -1,11 +1,14 @@ services: postgres: - image: postgres:latest + env_file: .env + build: + context: postgres/ + dockerfile: Dockerfile container_name: MyINPulse-DB #ports: # - 5432:5432 volumes: - - ./postgres:/var/lib/postgresql/data + - ./postgres/data:/var/lib/postgresql/data environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} diff --git a/config/prod.main.env b/config/prod.main.env index e8db94b..179d92c 100644 --- a/config/prod.main.env +++ b/config/prod.main.env @@ -1,6 +1,14 @@ -POSTGRES_DB=keycloak_db -POSTGRES_USER=keycloak_db_user -POSTGRES_PASSWORD=keycloak_db_user_password +POSTGRES_DB=postgres_db +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres_db_user_password + KEYCLOAK_ADMIN=admin KEYCLOAK_ADMIN_PASSWORD=admin -KEYCLOAK_HOSTNAME=0549cd63f912d5dc9b31278d6f.eirb.fr \ No newline at end of file +KEYCLOAK_HOSTNAME=0549cd63f912d5dc9b31278d6f.eirb.fr +KEYCLOAK_DB=keycloak_db +KEYCLOAK_USER=keycloak_db_user +KEYCLOAK_PASSWORD=keycloak_db_user_password + +BACKEND_DB=backend_db +BACKEND_USER=backend_db_user +BACKEND_PASSWORD=backend_db_user_password diff --git a/postgres/Dockerfile b/postgres/Dockerfile new file mode 100644 index 0000000..da99686 --- /dev/null +++ b/postgres/Dockerfile @@ -0,0 +1,5 @@ +FROM postgres:latest + +# Custom initialization scripts +COPY ./create_user.sh /docker-entrypoint-initdb.d/10-create_user.sh +COPY ./create_db.sh /docker-entrypoint-initdb.d/20-create_db.sh diff --git a/postgres/create_db.sh b/postgres/create_db.sh new file mode 100644 index 0000000..c1728a0 --- /dev/null +++ b/postgres/create_db.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -e + +POSTGRES="psql --username ${POSTGRES_USER}" + +echo "Creating database: ${DB_NAME}" + +$POSTGRES <