diff --git a/backend/app/src/main/java/com/ugent/pidgeon/config/JwtAuthenticationFilter.java b/backend/app/src/main/java/com/ugent/pidgeon/auth/JwtAuthenticationFilter.java similarity index 93% rename from backend/app/src/main/java/com/ugent/pidgeon/config/JwtAuthenticationFilter.java rename to backend/app/src/main/java/com/ugent/pidgeon/auth/JwtAuthenticationFilter.java index 4ef865e3..7f7c1581 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/config/JwtAuthenticationFilter.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/auth/JwtAuthenticationFilter.java @@ -1,4 +1,4 @@ -package com.ugent.pidgeon.config; +package com.ugent.pidgeon.auth; import com.auth0.jwk.Jwk; import com.auth0.jwk.JwkException; @@ -47,8 +47,8 @@ protected void doFilterInternal(jakarta.servlet.http.HttpServletRequest request, String token = bearerToken.substring(7); DecodedJWT jwt = JWT.decode(token); - Jwk jwk =null; - Algorithm algorithm=null; + Jwk jwk; + Algorithm algorithm; try { jwk = provider.get(jwt.getKeyId()); @@ -60,14 +60,13 @@ protected void doFilterInternal(jakarta.servlet.http.HttpServletRequest request, String firstName = jwt.getClaim("given_name").asString(); String lastName = jwt.getClaim("family_name").asString(); String email = jwt.getClaim("unique_name").asString(); - List groups = jwt.getClaim("groups").asList(String.class); String oid = jwt.getClaim("oid").asString(); // print full object - //logger.info(jwt.getClaims()); + // logger.info(jwt.getClaims()); - User user = new User(displayName, firstName,lastName, email, groups, oid); + User user = new User(displayName, firstName,lastName, email, oid); Auth authUser = new Auth(user, new ArrayList<>()); SecurityContextHolder.getContext().setAuthentication(authUser); diff --git a/backend/app/src/main/java/com/ugent/pidgeon/auth/Roles.java b/backend/app/src/main/java/com/ugent/pidgeon/auth/Roles.java new file mode 100644 index 00000000..6beea441 --- /dev/null +++ b/backend/app/src/main/java/com/ugent/pidgeon/auth/Roles.java @@ -0,0 +1,14 @@ +package com.ugent.pidgeon.auth; + +import com.ugent.pidgeon.postgre.models.types.UserRole; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Roles { + UserRole[] value(); +} \ No newline at end of file diff --git a/backend/app/src/main/java/com/ugent/pidgeon/auth/RolesInterceptor.java b/backend/app/src/main/java/com/ugent/pidgeon/auth/RolesInterceptor.java new file mode 100644 index 00000000..a0e687cb --- /dev/null +++ b/backend/app/src/main/java/com/ugent/pidgeon/auth/RolesInterceptor.java @@ -0,0 +1,58 @@ +package com.ugent.pidgeon.auth; + + +import com.ugent.pidgeon.model.Auth; +import com.ugent.pidgeon.postgre.models.UserEntity; +import com.ugent.pidgeon.postgre.models.types.UserRole; +import com.ugent.pidgeon.postgre.repository.UserRepository; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; + +import java.sql.Timestamp; +import java.util.Arrays; +import java.util.List; + +@Component +public class RolesInterceptor implements HandlerInterceptor { + + + private final UserRepository userRepository; + + @Autowired + public RolesInterceptor(UserRepository userRepository) { + this.userRepository = userRepository; + } + + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + if (handler instanceof HandlerMethod handlerMethod) { + Roles rolesAnnotation = handlerMethod.getMethodAnnotation(Roles.class); + if (rolesAnnotation != null) { + List requiredRoles = Arrays.asList(rolesAnnotation.value()); + Auth auth = (Auth) SecurityContextHolder.getContext().getAuthentication(); + UserEntity userEntity = userRepository.findUserByAzureId(auth.getOid()); + + if(userEntity == null) { + System.out.println("User does not exist, creating new one"); + userEntity = new UserEntity(auth.getUser().firstName,auth.getUser().lastName, auth.getEmail(), UserRole.student, auth.getOid()); + Timestamp now = new Timestamp(System.currentTimeMillis()); + userEntity.setCreatedAt(now); + userRepository.save(userEntity); + } + auth.setUserEntity(userEntity); + + if (!requiredRoles.contains(userEntity.getRole()) || userEntity.getRole() == UserRole.admin) { + response.sendError(HttpServletResponse.SC_FORBIDDEN, "User does not have required role"); + return false; + } + } + } + return true; + } +} diff --git a/backend/app/src/main/java/com/ugent/pidgeon/config/AuthConfig.java b/backend/app/src/main/java/com/ugent/pidgeon/config/AuthConfig.java index 280d62b6..3575aea8 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/config/AuthConfig.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/config/AuthConfig.java @@ -1,5 +1,6 @@ package com.ugent.pidgeon.config; +import com.ugent.pidgeon.auth.JwtAuthenticationFilter; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; @@ -22,7 +23,7 @@ public FilterRegistrationBean filterRegistrationBean() FilterRegistrationBean filter = new FilterRegistrationBean<>(); filter.setFilter(new JwtAuthenticationFilter(tenantId)); - filter.addUrlPatterns("/api/ietswatiknietwiltesten"); + filter.addUrlPatterns("/api/*"); return filter; } diff --git a/backend/app/src/main/java/com/ugent/pidgeon/config/WebConfig.java b/backend/app/src/main/java/com/ugent/pidgeon/config/WebConfig.java index 738ea1d2..0009ac5d 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/config/WebConfig.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/config/WebConfig.java @@ -1,13 +1,23 @@ package com.ugent.pidgeon.config; +import com.ugent.pidgeon.auth.RolesInterceptor; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { + + private final RolesInterceptor rolesInterceptor; + + @Autowired + public WebConfig(RolesInterceptor rolesInterceptor) { + this.rolesInterceptor = rolesInterceptor; + } + @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") @@ -15,4 +25,10 @@ public void addCorsMappings(CorsRegistry registry) { .allowedOrigins("*") .allowedHeaders("*"); } + + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(rolesInterceptor); + } } \ No newline at end of file diff --git a/backend/app/src/main/java/com/ugent/pidgeon/controllers/ApiRoutes.java b/backend/app/src/main/java/com/ugent/pidgeon/controllers/ApiRoutes.java new file mode 100644 index 00000000..9d6149a5 --- /dev/null +++ b/backend/app/src/main/java/com/ugent/pidgeon/controllers/ApiRoutes.java @@ -0,0 +1,6 @@ +package com.ugent.pidgeon.controllers; + +public final class ApiRoutes { + public static final String USER_BASE_PATH = "/api/user/"; + public static final String COURSE_BASE_PATH = "/api/course/"; +} diff --git a/backend/app/src/main/java/com/ugent/pidgeon/controllers/AuthTestController.java b/backend/app/src/main/java/com/ugent/pidgeon/controllers/AuthTestController.java index d15315b5..830756ea 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/controllers/AuthTestController.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/controllers/AuthTestController.java @@ -1,6 +1,8 @@ package com.ugent.pidgeon.controllers; +import com.ugent.pidgeon.auth.Roles; import com.ugent.pidgeon.model.Auth; import com.ugent.pidgeon.model.User; +import com.ugent.pidgeon.postgre.models.types.UserRole; import com.ugent.pidgeon.postgre.repository.UserRepository; import jakarta.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; @@ -13,7 +15,9 @@ public class AuthTestController { @Autowired private UserRepository userRepository; + @GetMapping("/api/test") + @Roles({UserRole.student, UserRole.teacher}) public User testApi(HttpServletRequest request, Auth auth) { return auth.getUser(); } diff --git a/backend/app/src/main/java/com/ugent/pidgeon/controllers/FilesubmissiontestController.java b/backend/app/src/main/java/com/ugent/pidgeon/controllers/FilesubmissiontestController.java index 0b7c396b..ef089f83 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/controllers/FilesubmissiontestController.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/controllers/FilesubmissiontestController.java @@ -1,14 +1,16 @@ package com.ugent.pidgeon.controllers; +import com.ugent.pidgeon.auth.Roles; +import com.ugent.pidgeon.model.Auth; import com.ugent.pidgeon.postgre.models.FileEntity; import com.ugent.pidgeon.postgre.models.SubmissionEntity; +import com.ugent.pidgeon.postgre.models.types.UserRole; import com.ugent.pidgeon.postgre.repository.FileRepository; import com.ugent.pidgeon.postgre.repository.GroupRepository; import com.ugent.pidgeon.postgre.repository.ProjectRepository; import com.ugent.pidgeon.postgre.repository.SubmissionRepository; import com.ugent.pidgeon.util.Filehandler; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -17,11 +19,8 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import java.io.FileInputStream; import java.nio.file.Path; import java.sql.Timestamp; -import java.util.logging.Logger; -import java.util.zip.ZipFile; @RestController public class FilesubmissiontestController { @@ -36,8 +35,9 @@ public class FilesubmissiontestController { private ProjectRepository projectRepository; @PostMapping("/project/{projectid}/submit") //Route to submit a file, it accepts a multiform with the file and submissionTime - public ResponseEntity submitFile(@RequestParam("file") MultipartFile file, @RequestParam("submissionTime") Timestamp time, @PathVariable("projectid") long projectid) { - long userId = 1L; //TODO: replace with id of current user + @Roles({UserRole.teacher, UserRole.student}) + public ResponseEntity submitFile(@RequestParam("file") MultipartFile file, @RequestParam("submissionTime") Timestamp time, @PathVariable("projectid") long projectid,Auth auth) { + long userId = auth.getUserEntity().getId(); Long groupId = groupRepository.groupIdByProjectAndUser(projectid, userId); if (!projectRepository.userPartOfProject(projectid, userId)) { @@ -70,13 +70,15 @@ public ResponseEntity submitFile(@RequestParam("file") MultipartFile fil } @GetMapping("submissions/{submissionid}") - public ResponseEntity getSubmission(@PathVariable("submissionid") long submissionid) { - long userId = 1L; //TODO: replace with id of current user + @Roles({UserRole.teacher, UserRole.student}) + public ResponseEntity getSubmission(@PathVariable("submissionid") long submissionid, Auth auth) { + long userId = auth.getUserEntity().getId(); // Get the submission entry from the database SubmissionEntity submission = submissionRepository.findById(submissionid).orElse(null); if (submission == null) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null); } + if (!groupRepository.userInGroup(submission.getGroupId(), userId)) { return ResponseEntity.status(HttpStatus.FORBIDDEN).body(null); } diff --git a/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaCourseController.java b/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaCourseController.java index 7991cf6f..29a0545f 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaCourseController.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaCourseController.java @@ -21,7 +21,7 @@ public class JpaCourseController { @Autowired private ProjectRepository projectRepository; - @GetMapping("/api/courses") + @GetMapping("/api/temp/courses") public String getCourses() { StringBuilder res = new StringBuilder(); for (CourseEntity course : courseRepository.findAll()) { diff --git a/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaGroupController.java b/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaGroupController.java index b3845a87..032b87ca 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaGroupController.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaGroupController.java @@ -26,7 +26,7 @@ public class JpaGroupController { @Autowired SubmissionRepository submissionRepository; - @GetMapping("/api/groups") + @GetMapping("/api/temp/groups") public List getGroups() { List res = new ArrayList<>(); for (GroupEntity group : groupRepository.findAll()) { diff --git a/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaProjectController.java b/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaProjectController.java index eb245d3d..a3cf9957 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaProjectController.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaProjectController.java @@ -21,7 +21,7 @@ public class JpaProjectController { @Autowired private TestRepository testRepository; - @GetMapping("/api/projects") + @GetMapping("/api/temp/projects") public List getProjects() { List res = new ArrayList<>(); for (ProjectEntity project : projectRepository.findAll()) { diff --git a/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaSubmissionController.java b/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaSubmissionController.java index 4fabad41..e63b7ed7 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaSubmissionController.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaSubmissionController.java @@ -20,7 +20,7 @@ public class JpaSubmissionController { @Autowired private FileRepository fileRepository; - @GetMapping("/api/submissions") + @GetMapping("/api/temp/submissions") public List getSubmissions() { List res = new ArrayList<>(); for (SubmissionEntity submission : submissionRepository.findAll()) { diff --git a/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaUserController.java b/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaUserController.java index 7619473c..5ae15360 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaUserController.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/controllers/JpaUserController.java @@ -16,7 +16,7 @@ public class JpaUserController { private UserRepository userRepository; Logger logger = LoggerFactory.getLogger(JpaUserController.class); - @GetMapping("/api/users") + @GetMapping("/api/temp/users") public String getUsers() { StringBuilder res = new StringBuilder(); for (UserEntity user : userRepository.findAll()) { diff --git a/backend/app/src/main/java/com/ugent/pidgeon/controllers/UserController.java b/backend/app/src/main/java/com/ugent/pidgeon/controllers/UserController.java new file mode 100644 index 00000000..4c4fb123 --- /dev/null +++ b/backend/app/src/main/java/com/ugent/pidgeon/controllers/UserController.java @@ -0,0 +1,40 @@ +package com.ugent.pidgeon.controllers; + +import com.ugent.pidgeon.auth.Roles; +import com.ugent.pidgeon.model.Auth; +import com.ugent.pidgeon.model.json.CourseWithRelationJson; +import com.ugent.pidgeon.model.json.UserJson; +import com.ugent.pidgeon.postgre.models.types.UserRole; +import com.ugent.pidgeon.postgre.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +public class UserController { + + @Autowired + private UserRepository userRepository; + + @GetMapping(ApiRoutes.USER_BASE_PATH + "{userid}") + @Roles({UserRole.student, UserRole.teacher}) + public UserJson getUserById(@PathVariable("userid") Long userid) { + UserJson res = userRepository.findById(userid).map(UserJson::new).orElse(null); + if (res == null) { + return null; + } + List courses = userRepository.findCourseIdsByUserId(userid); + + res.setCourses(courses.stream().map( + c -> new CourseWithRelationJson( + ApiRoutes.COURSE_BASE_PATH + c.getCourseId(), + c.getRelation() + )).toList()); + return res; + } + + +} diff --git a/backend/app/src/main/java/com/ugent/pidgeon/model/Auth.java b/backend/app/src/main/java/com/ugent/pidgeon/model/Auth.java index 492ebe41..4add2b68 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/model/Auth.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/model/Auth.java @@ -1,6 +1,8 @@ package com.ugent.pidgeon.model; +import com.ugent.pidgeon.postgre.models.UserEntity; import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.util.Assert; @@ -10,6 +12,7 @@ public class Auth extends AbstractAuthenticationToken { private static final long serialVersionUID = 620L; private final User user; + private UserEntity userEntity; @@ -31,12 +34,20 @@ public String getOid(){ return user.oid; } - public static org.springframework.security.authentication.UsernamePasswordAuthenticationToken unauthenticated(Object principal, Object credentials) { - return new org.springframework.security.authentication.UsernamePasswordAuthenticationToken(principal, credentials); + public void setUserEntity(UserEntity user){ + userEntity = user; } - public static org.springframework.security.authentication.UsernamePasswordAuthenticationToken authenticated(Object principal, Object credentials, Collection authorities) { - return new org.springframework.security.authentication.UsernamePasswordAuthenticationToken(principal, credentials, authorities); + public UserEntity getUserEntity(){ + return userEntity; + } + + public static UsernamePasswordAuthenticationToken unauthenticated(Object principal, Object credentials) { + return new UsernamePasswordAuthenticationToken(principal, credentials); + } + + public static UsernamePasswordAuthenticationToken authenticated(Object principal, Object credentials, Collection authorities) { + return new UsernamePasswordAuthenticationToken(principal, credentials, authorities); } public Object getCredentials() { diff --git a/backend/app/src/main/java/com/ugent/pidgeon/model/User.java b/backend/app/src/main/java/com/ugent/pidgeon/model/User.java index 006341ee..330c74e7 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/model/User.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/model/User.java @@ -8,13 +8,11 @@ public class User { public String firstName; public String lastName; public String email; - public List groups; public String oid; - public User (String name, String firstName, String lastName, String email, List groups, String oid) { + public User (String name, String firstName, String lastName, String email, String oid) { this.name = name; this.email = email; - this.groups = groups; this.oid = oid; this.firstName = firstName; this.lastName = lastName; diff --git a/backend/app/src/main/java/com/ugent/pidgeon/model/json/CourseWithRelationJson.java b/backend/app/src/main/java/com/ugent/pidgeon/model/json/CourseWithRelationJson.java new file mode 100644 index 00000000..00227d70 --- /dev/null +++ b/backend/app/src/main/java/com/ugent/pidgeon/model/json/CourseWithRelationJson.java @@ -0,0 +1,33 @@ +package com.ugent.pidgeon.model.json; + +import com.ugent.pidgeon.postgre.models.types.CourseRelation; + +public class CourseWithRelationJson { + private String url; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public CourseWithRelationJson() { + } + + public CourseWithRelationJson(String url, CourseRelation relation) { + this.url = url; + this.relation = relation; + } + + private CourseRelation relation; + + public CourseRelation getRelation() { + return relation; + } + + public void setRelation(CourseRelation relation) { + this.relation = relation; + } +} diff --git a/backend/app/src/main/java/com/ugent/pidgeon/model/json/UserJson.java b/backend/app/src/main/java/com/ugent/pidgeon/model/json/UserJson.java new file mode 100644 index 00000000..ba1044e3 --- /dev/null +++ b/backend/app/src/main/java/com/ugent/pidgeon/model/json/UserJson.java @@ -0,0 +1,106 @@ +package com.ugent.pidgeon.model.json; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ugent.pidgeon.postgre.models.UserEntity; +import com.ugent.pidgeon.postgre.models.types.UserRole; +import com.ugent.pidgeon.postgre.repository.UserRepository; +import org.codehaus.jackson.annotate.JsonProperty; + +import java.io.Serializable; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; + +public class UserJson { + + private long id; + private String name; + private String surname; + private String email; + private UserRole role; + + private Timestamp createdAt; + + private String oid; + + private List courses; + + public UserJson() { + } + + public UserJson(UserEntity entity) { + this.id = entity.getId(); + this.name = entity.getName(); + this.surname = entity.getSurname(); + this.email = entity.getEmail(); + this.role = entity.getRole(); + this.createdAt = entity.getCreatedAt(); + this.oid = entity.getMicrosoftToken(); + this.courses = new ArrayList<>(); + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSurname() { + return surname; + } + + public void setSurname(String surname) { + this.surname = surname; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public UserRole getRole() { + return role; + } + + public void setRole(UserRole role) { + this.role = role; + } + + public Timestamp getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Timestamp createdAt) { + this.createdAt = createdAt; + } + + public String getOid() { + return oid; + } + + public void setOid(String oid) { + this.oid = oid; + } + + + public List getCourses() { + return courses; + } + + public void setCourses(List courses) { + this.courses = courses; + } +} diff --git a/backend/app/src/main/java/com/ugent/pidgeon/postgre/models/UserEntity.java b/backend/app/src/main/java/com/ugent/pidgeon/postgre/models/UserEntity.java index 76d629ab..8382fc16 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/postgre/models/UserEntity.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/postgre/models/UserEntity.java @@ -28,18 +28,18 @@ public class UserEntity { @Enumerated(EnumType.STRING) private UserRole role; - @Column(name = "microsoft_token") - private String microsoftToken; + @Column(name = "azure_id") + private String azureId; @Column(name = "created_at") private Timestamp createdAt; - public UserEntity(String name, String surname, String email, UserRole role, String microsoftToken) { + public UserEntity(String name, String surname, String email, UserRole role, String azureId) { this.name = name; this.surname = surname; this.email = email; this.role = role; - this.microsoftToken = microsoftToken; + this.azureId = azureId; } public UserEntity() { @@ -86,11 +86,11 @@ public UserRole getRole() { } public String getMicrosoftToken() { - return microsoftToken; + return azureId; } public void setMicrosoftToken(String microsoftToken) { - this.microsoftToken = microsoftToken; + this.azureId = microsoftToken; } public Timestamp getCreatedAt() { diff --git a/backend/app/src/main/java/com/ugent/pidgeon/postgre/repository/UserRepository.java b/backend/app/src/main/java/com/ugent/pidgeon/postgre/repository/UserRepository.java index bc1b5d86..f4a09fb8 100644 --- a/backend/app/src/main/java/com/ugent/pidgeon/postgre/repository/UserRepository.java +++ b/backend/app/src/main/java/com/ugent/pidgeon/postgre/repository/UserRepository.java @@ -18,7 +18,18 @@ public interface CourseWithRelation { CourseRelation getRelation(); } + public interface CourseIdWithRelation { + Long getCourseId(); + CourseRelation getRelation(); + } + + @Query(value = "SELECT c.id as courseId, cu.relation as relation FROM CourseEntity c JOIN CourseUserEntity cu ON c.id = cu.courseId WHERE cu.userId = ?1") + List findCourseIdsByUserId(long id); + /* The 'as' is important here, as it is used to map the result to the CourseWithRelation interface */ @Query(value = "SELECT c as course, cu.relation as relation FROM CourseEntity c JOIN CourseUserEntity cu ON c.id = cu.courseId WHERE cu.userId = ?1") List findCoursesByUserId(long id); + + @Query(value = "SELECT * FROM users WHERE azure_id = ?1", nativeQuery = true) + public UserEntity findUserByAzureId(String id); } diff --git a/backend/app/src/main/resources/application.properties b/backend/app/src/main/resources/application.properties index 91309585..cbf13458 100644 --- a/backend/app/src/main/resources/application.properties +++ b/backend/app/src/main/resources/application.properties @@ -1,6 +1,10 @@ -#spring.datasource.url = jdbc:postgresql://localhost:5432/postgres -#spring.datasource.username = admin -#spring.datasource.password = root + +# Uncommenting these lines fixes wierd "Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured." error +spring.datasource.url = jdbc:postgresql://localhost:5432/postgres +spring.datasource.username = admin +spring.datasource.password = root + + #spring.jpa.show-sql=true #spring.jpa.database=postgresql # @@ -11,10 +15,12 @@ spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialec # Hibernate ddl auto (create, create-drop, validate, update) spring.jpa.hibernate.ddl-auto = update + server.port=8080 +#testing id: 39136cda-f02f-4305-9b08-45f132bab07e +azure.activedirectory.client-id=83616890-6249-4726-9e0c-87da4dde75b0 -azure.activedirectory.client-id=39136cda-f02f-4305-9b08-45f132bab07e -azure.activedirectory.b2c.client-secret=i1n8Q~57EDI.E2iLxzkW3Q.ixEtVIM4jwN7eDbxK +azure.activedirectory.b2c.client-secret=jE38Q~bU1qLdMKtL05d57nwXHSgrUSXm.vxLNb.n # For UGent auth: d7811cde-ecef-496c-8f91-a1786241b99c # Test auth: 62835335-e5c4-4d22-98f2-9d5b65a06d9d diff --git a/frontend/src/@types/types.d.ts b/frontend/src/@types/types.d.ts new file mode 100644 index 00000000..daec4a4b --- /dev/null +++ b/frontend/src/@types/types.d.ts @@ -0,0 +1,12 @@ + + + + +declare global { + interface Window { + + + } +} + +export {} \ No newline at end of file diff --git a/frontend/src/auth/AuthConfig.ts b/frontend/src/auth/AuthConfig.ts index c5479d52..0fa5ff93 100644 --- a/frontend/src/auth/AuthConfig.ts +++ b/frontend/src/auth/AuthConfig.ts @@ -1,28 +1,32 @@ -import { Configuration, PopupRequest } from "@azure/msal-browser"; +import { Configuration, PopupRequest } from "@azure/msal-browser" + +const hostedOnServer = false && window.location.hostname === "sel2-6.ugent.be" // For now, gotta change this when ugent auth is fixed // Config object to be passed to Msal on creation export const msalConfig: Configuration = { - auth: { - clientId: "39136cda-f02f-4305-9b08-45f132bab07e", - //For UGent auth: "https://login.microsoftonline.com/d7811cde-ecef-496c-8f91-a1786241b99c", - authority: "https://login.microsoftonline.com/d7811cde-ecef-496c-8f91-a1786241b99c", // "https://login.microsoftonline.com/62835335-e5c4-4d22-98f2-9d5b65a06d9d", - redirectUri: "/", - postLogoutRedirectUri: "/" - }, - system: { - allowNativeBroker: false - }, - cache: { - cacheLocation: "localStorage", - } + auth: { + // 39136cda-f02f-4305-9b08-45f132bab07e + clientId: hostedOnServer ? "83616890-6249-4726-9e0c-87da4dde75b0" : "39136cda-f02f-4305-9b08-45f132bab07e", + //For UGent auth: "https://login.microsoftonline.com/d7811cde-ecef-496c-8f91-a1786241b99c", + authority: "https://login.microsoftonline.com/d7811cde-ecef-496c-8f91-a1786241b99c", // "https://login.microsoftonline.com/62835335-e5c4-4d22-98f2-9d5b65a06d9d", + redirectUri: "/", + postLogoutRedirectUri: "/", + + }, + system: { + allowNativeBroker: false, + }, + cache: { + cacheLocation: "localStorage", + }, } // Add here scopes for id token to be used at MS Identity Platform endpoints. export const loginRequest: PopupRequest = { - scopes: ["User.Read"] -}; + scopes: ["User.Read"], +} // Add here the endpoints for MS Graph API services you would like to use. export const graphConfig = { - graphMeEndpoint: "https://graph.microsoft.com/v1.0/me" -}; \ No newline at end of file + graphMeEndpoint: "https://graph.microsoft.com/v1.0/me", +} diff --git a/frontend/src/components/layout/nav/UnauthNav.tsx b/frontend/src/components/layout/nav/UnauthNav.tsx index 84b01434..3d5687b7 100644 --- a/frontend/src/components/layout/nav/UnauthNav.tsx +++ b/frontend/src/components/layout/nav/UnauthNav.tsx @@ -6,7 +6,11 @@ const UnauthNav = () => { const { t } = useTranslation(); const handleLogin = async () => { try { - await msalInstance.loginPopup(); + await msalInstance.loginPopup({ + scopes: ['openid', 'profile', 'User.Read'], + }); + console.log(msalInstance); + } catch (error) { console.error(error) } diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 04d3a091..58fcb1eb 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -15,6 +15,8 @@ msalInstance.initialize().then(() => { msalInstance.setActiveAccount(accounts[0]) } + console.log(accounts); + msalInstance.addEventCallback((event: EventMessage) => { if (event.eventType === EventType.LOGIN_SUCCESS && event.payload) { const payload = event.payload as AuthenticationResult diff --git a/frontend/src/util/apiFetch.ts b/frontend/src/util/apiFetch.ts index d1808623..e9476119 100644 --- a/frontend/src/util/apiFetch.ts +++ b/frontend/src/util/apiFetch.ts @@ -2,6 +2,7 @@ import { ApiRoutes, GET_Responses, POST_Requests, POST_Responses, PUT_Requests } import axios, { AxiosResponse } from "axios"; import {msalInstance} from "../index"; import { AxiosRequestConfig } from "axios"; +import { msalConfig } from "../auth/AuthConfig"; const serverHost ="http://localhost:8080" // window.location.origin; @@ -32,7 +33,7 @@ async function apiFetch(method: "GET" | "POST" | "PUT" | "D if (!accessToken || !tokenExpiry || now >= tokenExpiry) { const response = await msalInstance.acquireTokenSilent({ - scopes: ["39136cda-f02f-4305-9b08-45f132bab07e/.default"], + scopes: [msalConfig.auth.clientId + "/.default"], account: account });