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..3b4f04a 100644 --- a/MyINPulse-back/src/main/java/enseirb/myinpulse/api/GetUserInfo.java +++ b/MyINPulse-back/src/main/java/enseirb/myinpulse/api/GetUserInfo.java @@ -2,13 +2,13 @@ package enseirb.myinpulse.api; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.*; +import javax.management.relation.RoleNotFoundException; import java.security.Principal; + + @SpringBootApplication @RestController public class GetUserInfo { @@ -22,7 +22,8 @@ public class GetUserInfo { @CrossOrigin(methods = {RequestMethod.GET, RequestMethod.OPTIONS}) @GetMapping("/random") - public boolean rand(){ + public boolean rand(@RequestHeader("Authorization") String token) throws RoleNotFoundException { + System.err.println(token); System.err.println("HELLO"); return Math.random() > 0.5; } diff --git a/MyINPulse-back/src/main/java/enseirb/myinpulse/exceptions/RoleNotFoudException.java b/MyINPulse-back/src/main/java/enseirb/myinpulse/exceptions/RoleNotFoudException.java new file mode 100644 index 0000000..22e4c23 --- /dev/null +++ b/MyINPulse-back/src/main/java/enseirb/myinpulse/exceptions/RoleNotFoudException.java @@ -0,0 +1,7 @@ +package enseirb.myinpulse.exceptions; + +public class RoleNotFoudException extends RuntimeException { + public RoleNotFoudException(String message) { + super(message); + } +} diff --git a/MyINPulse-back/src/main/java/enseirb/myinpulse/exceptions/UserNotFoundException.java b/MyINPulse-back/src/main/java/enseirb/myinpulse/exceptions/UserNotFoundException.java new file mode 100644 index 0000000..983e766 --- /dev/null +++ b/MyINPulse-back/src/main/java/enseirb/myinpulse/exceptions/UserNotFoundException.java @@ -0,0 +1,7 @@ +package enseirb.myinpulse.exceptions; + +public class UserNotFoundException extends RuntimeException { + public UserNotFoundException(String message) { + super(message); + } +} 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..08c5cab 100644 --- a/MyINPulse-back/src/main/java/enseirb/myinpulse/security/KeycloakJwtRolesConverter.java +++ b/MyINPulse-back/src/main/java/enseirb/myinpulse/security/KeycloakJwtRolesConverter.java @@ -1,3 +1,7 @@ +/* + * Source: https://github.com/ChristianHuff-DEV/secure-spring-rest-api-using-keycloak/blob/main/src/main/java/io/betweendata/RestApi/security/oauth2/KeycloakJwtRolesConverter.java + * edited by Pierre Tellier + */ package enseirb.myinpulse.security; import org.springframework.core.convert.converter.Converter; @@ -41,17 +45,16 @@ public class KeycloakJwtRolesConverter implements Converter TEMPORARNAME(Jwt jwt) { + public Collection tokenRolesExtractor(Jwt jwt) { // Collection that will hold the extracted roles Collection grantedAuthorities = new ArrayList<>(); diff --git a/MyINPulse-back/src/main/java/enseirb/myinpulse/utils/KeycloakApi.java b/MyINPulse-back/src/main/java/enseirb/myinpulse/utils/KeycloakApi.java new file mode 100644 index 0000000..e91e9e1 --- /dev/null +++ b/MyINPulse-back/src/main/java/enseirb/myinpulse/utils/KeycloakApi.java @@ -0,0 +1,77 @@ +package enseirb.myinpulse.utils; + +import enseirb.myinpulse.exceptions.UserNotFoundException; +import org.springframework.web.client.RestClient; + +import javax.management.relation.RoleNotFoundException; + +import static org.springframework.http.MediaType.APPLICATION_JSON; + +public class KeycloakApi { + + static final String keycloakUrl = "http://localhost:7080"; + static final String realmName = "test"; + + /** + * Uses Keycloak API to retrieve a role representation of a role by its name + * @param roleName name of the role + * @param bearer authorization header used by the client to authenticate to keycloak + */ + static public RoleRepresentation getRoleRepresentationByName(String roleName, String bearer) throws RoleNotFoundException { + RoleRepresentation[] response = RestClient.builder().baseUrl(keycloakUrl) + .defaultHeader("Authorization", bearer) + .build() + .get() + .uri("/admin/realms/{realmName}/roles/{roleName}", realmName, roleName) + .retrieve() + .body(RoleRepresentation[].class); + + if (response == null || response.length == 0) { + throw new RoleNotFoundException("Role not found"); + } + return response[0]; + } + + static public String getUserIdByName(String username, String bearer) throws UserNotFoundException { + UserRepresentation[] response = RestClient.builder().baseUrl(keycloakUrl) + .defaultHeader("Authorization", bearer) + .build() + .get() + .uri("/admin/realms/{realmName}/users?username={username}", realmName, username) + .retrieve() + .body(UserRepresentation[].class); + + if (response == null || response.length == 0) { + throw new UserNotFoundException("User not found"); + } + return response[0].id; + } + + static public void setRoleToUser(String username, String roleName, String bearer) throws RoleNotFoundException, UserNotFoundException { + RoleRepresentation roleRepresentation = getRoleRepresentationByName(roleName, bearer); + String userId = getUserIdByName(username, bearer); + + + RestClient.builder().baseUrl(keycloakUrl) + .defaultHeader("Authorization", bearer) + .build() + .post() + .uri("/admin/realms/${realmName}/users/${userId}/role-mappings/realm", realmName, userId) + .body(roleRepresentation) + .contentType(APPLICATION_JSON) + .retrieve(); + } +} + + +class RoleRepresentation { + public String id; + public String name; + public String description; +} + +class UserRepresentation { + public String id; + public String name; +} + diff --git a/front/MyINPulse-front/src/services/api.ts b/front/MyINPulse-front/src/services/api.ts index 091455c..5c4fc7b 100644 --- a/front/MyINPulse-front/src/services/api.ts +++ b/front/MyINPulse-front/src/services/api.ts @@ -14,7 +14,8 @@ axiosInstance.interceptors.response.use( async (error) => { const originalRequest = error.config; if ( - error.response.status === 401 && + ((error.response && error.response.status === 401) || + error.code == "ERR_NETWORK") && !originalRequest._retry && store.authenticated ) { diff --git a/front/MyINPulse-front/src/views/test.vue b/front/MyINPulse-front/src/views/test.vue new file mode 100644 index 0000000..9d98958 --- /dev/null +++ b/front/MyINPulse-front/src/views/test.vue @@ -0,0 +1,73 @@ + + + + +