diff --git a/buisnesslogic/src/main/java/com/plannaplan/entities/User.java b/buisnesslogic/src/main/java/com/plannaplan/entities/User.java index c40d984..e675415 100644 --- a/buisnesslogic/src/main/java/com/plannaplan/entities/User.java +++ b/buisnesslogic/src/main/java/com/plannaplan/entities/User.java @@ -1,5 +1,7 @@ package com.plannaplan.entities; +import java.sql.Date; + import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; @@ -14,11 +16,35 @@ public class User { private Long id; private String name; private String surname; + private String email; private UserRoles role; + private String token; + private Date tokenCreatedDate; public User() { } + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public Date getTokenCreatedDate() { + return tokenCreatedDate; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.tokenCreatedDate = new Date(System.currentTimeMillis()); + this.token = token; + } + public String getName() { return name; } diff --git a/buisnesslogic/src/main/java/com/plannaplan/exceptions/UserNotFoundException.java b/buisnesslogic/src/main/java/com/plannaplan/exceptions/UserNotFoundException.java new file mode 100644 index 0000000..d56283f --- /dev/null +++ b/buisnesslogic/src/main/java/com/plannaplan/exceptions/UserNotFoundException.java @@ -0,0 +1,13 @@ +package com.plannaplan.exceptions; + +public class UserNotFoundException extends Exception { + /** + * + */ + private static final long serialVersionUID = 1L; + + public UserNotFoundException(String message) { + super(message); + } + +} diff --git a/buisnesslogic/src/main/java/com/plannaplan/repositories/UserRepository.java b/buisnesslogic/src/main/java/com/plannaplan/repositories/UserRepository.java index 19f1ac6..8fbbbb4 100644 --- a/buisnesslogic/src/main/java/com/plannaplan/repositories/UserRepository.java +++ b/buisnesslogic/src/main/java/com/plannaplan/repositories/UserRepository.java @@ -3,9 +3,15 @@ package com.plannaplan.repositories; import com.plannaplan.entities.User; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository public interface UserRepository extends JpaRepository { + @Query("FROM User WHERE email = ?1") + User getByAuthority(@Param("authority") String authority); + @Query("FROM User WHERE token = ?1") + User getByToken(@Param("token") String token); } \ No newline at end of file diff --git a/buisnesslogic/src/main/java/com/plannaplan/services/UserService.java b/buisnesslogic/src/main/java/com/plannaplan/services/UserService.java index ffa133a..0fec12f 100644 --- a/buisnesslogic/src/main/java/com/plannaplan/services/UserService.java +++ b/buisnesslogic/src/main/java/com/plannaplan/services/UserService.java @@ -1,6 +1,10 @@ package com.plannaplan.services; +import java.util.UUID; + import com.plannaplan.abstracts.EventWatcher; +import com.plannaplan.entities.User; +import com.plannaplan.exceptions.UserNotFoundException; import com.plannaplan.repositories.UserRepository; import org.springframework.beans.factory.annotation.Autowired; @@ -15,4 +19,27 @@ public class UserService extends EventWatcher { super(); } + public String login(String authority) throws UserNotFoundException { + User user = this.repo.getByAuthority(authority.replace("\n", "").trim()); + if (user == null) { + throw new UserNotFoundException("Can not find user with given authority"); + } + String token = UUID.randomUUID().toString(); + user.setToken(token); + this.repo.save(user); + return token; + } + + public void save(User user) { + this.repo.save(user); + } + + public User getUserByEmail(String email) { + return this.repo.getByAuthority(email.replace("\n", "").trim()); + } + + public User getByToken(String token) { + return this.repo.getByToken(token); + } + } \ No newline at end of file diff --git a/buisnesslogic/src/main/java/com/plannaplan/types/UserRoles.java b/buisnesslogic/src/main/java/com/plannaplan/types/UserRoles.java index f56d2c5..845fddc 100644 --- a/buisnesslogic/src/main/java/com/plannaplan/types/UserRoles.java +++ b/buisnesslogic/src/main/java/com/plannaplan/types/UserRoles.java @@ -1,5 +1,5 @@ package com.plannaplan.types; public enum UserRoles { - STUDENT, DEANERY, ADMIN + STUDENT, DEANERY, ADMIN, TEST_USER } \ No newline at end of file diff --git a/buisnesslogic/src/test/java/com/plannaplan/services/UserServiceTest.java b/buisnesslogic/src/test/java/com/plannaplan/services/UserServiceTest.java new file mode 100644 index 0000000..7a764f6 --- /dev/null +++ b/buisnesslogic/src/test/java/com/plannaplan/services/UserServiceTest.java @@ -0,0 +1,64 @@ +package com.plannaplan.services; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.plannaplan.entities.User; +import com.plannaplan.exceptions.UserNotFoundException; +import com.plannaplan.types.UserRoles; + +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.runner.RunWith; + +@RunWith(SpringRunner.class) +@SpringBootTest +@ContextConfiguration +@TestMethodOrder(OrderAnnotation.class) +public class UserServiceTest { + public static String TEST_USER_MAIL = "noteexisitingmail@notexistingdomain.com"; + public static String TEST_USER_NAME = "Tom"; + public static String TEST_USER_SUERNAME = "Kovalsky"; + + @Autowired + UserService userService; + + @Before + public void initialize() { + User testUser = new User(); + testUser.setEmail(TEST_USER_MAIL); + testUser.setName(TEST_USER_NAME); + testUser.setSurname(TEST_USER_SUERNAME); + testUser.setRole(UserRoles.TEST_USER); + this.userService.save(testUser); + } + + @Test + public void shouldReturnToken() { + try { + String token = this.userService.login(TEST_USER_MAIL); + System.out.println("Returned token: " + token); + assertTrue(token != null); + assertTrue(this.userService.getUserByEmail(TEST_USER_MAIL).getToken() != null); + } catch (UserNotFoundException e) { + e.printStackTrace(); + assertTrue(false); + } + } + + @Test + public void shouldThrowException() { + try { + this.userService.login("thiseamilisnotindatabase@gmail.com"); + assertTrue(false); + } catch (UserNotFoundException e) { + assertTrue(true); + } + } +} diff --git a/docs/api.md b/docs/api.md index c3f30b4..6556d62 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,11 +1,12 @@ # Dokumetacja API -| Api | Zadania endpointa | -| ---------------------------------------------- | ---------------------------------------------------------- | -| [/config](#config) | Załadowanie konfiguracji startowej do aplikacji PlanNaPlan | -| [/getCoursesWithGroups](#getcourseswithgroups) | Zwrócenie wszytskich kursów razem z grupami | -| [/getCourseGroups](#getcoursegroups) | Zwrócenie grup dla danego kursu | -| [/getCourses](#getcourses) | Zwrócenie wszystkich kursów | +| Api | Zadania endpointa | +| ------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| [/api/v1/configurator/config](#config) | Załadowanie konfiguracji startowej do aplikacji PlanNaPlan | +| [/api/v1/courses/getCoursesWithGroups](#getcourseswithgroups) | Zwrócenie wszytskich kursów razem z grupami | +| [/api/v1/groups/getCourseGroups](#getcoursegroups) | Zwrócenie grup dla danego kursu | +| [/api/v1/courses/getCourses](#getcourses) | Zwrócenie wszystkich kursów | +| [/token](#token) | Wymienia ticket z CAS-a na token ktorym beda autoryzowane chronione requesty | ## config @@ -30,7 +31,7 @@ Endpoint konfigurujacy caly system i importujacy dane do bazy. Source code: [link](../restservice/src/main/java/com/plannaplan/controllers/getCoursesWithGroups.java) ``` -GET /getCoursesWithGroups +GET /api/v1/courses/getCoursesWithGroups ``` #### Opis @@ -42,7 +43,7 @@ Zwraca wszystkie dostepne kursy wraz z listą grup. Source code: [link](../restservice/src/main/java/com/plannaplan/controllers/GroupController.java) ``` -GET /getCourseGroups +GET /api/v1/groups/getCourseGroups ``` #### Opis @@ -61,9 +62,27 @@ Zwraca wszytskie grupy dla danego kursu. Source code: [link](../restservice/src/main/java/com/plannaplan/controllers/CoursesController.java) ``` -GET /getCourses +GET /api/v1/courses/getCourses ``` #### Opis Zwraca wszystkie dostepne kursy. + +## token + +Source code: [link](../restservice/src/main/java/com/plannaplan/controllers/TokenController.java) + +``` +GET /token?ticket=ST-668405-W0gfvSVDRBdMUWLweKzv-cas.amu.edu.pl +``` + +#### Opis + +Po odpytaniu tego endpointa z podanym ticketem system zrobi nma nim validate i dostanie uzytkownika dla ktorego zostal on wygenerowany. System utworzy dla niego access token i go zwroci w odpowiedzi + +#### Parametry + +| Type | Name | Consumes | Opis | Type | +| ----------- | ------------------------- | -------- | --------------------------------------- | ------ | +| Query Param | **ticket**
required | - | ticket uzyskany z logowania poprzez CAS | string | diff --git a/restservice/pom.xml b/restservice/pom.xml index 4d62b67..d941ad1 100644 --- a/restservice/pom.xml +++ b/restservice/pom.xml @@ -31,11 +31,23 @@ test + + org.apache.httpcomponents + httpclient + 4.5.10 + + org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-security + 2.3.3.RELEASE + + org.springframework.boot spring-boot-starter-test @@ -48,6 +60,12 @@ + + org.apache.commons + commons-lang3 + 3.8.1 + + buisnesslogic com.plannaplan diff --git a/restservice/src/main/java/com/plannaplan/App.java b/restservice/src/main/java/com/plannaplan/App.java index 137a7bd..7f8c652 100644 --- a/restservice/src/main/java/com/plannaplan/App.java +++ b/restservice/src/main/java/com/plannaplan/App.java @@ -1,15 +1,38 @@ package com.plannaplan; +import com.plannaplan.entities.User; +import com.plannaplan.services.UserService; +import com.plannaplan.types.UserRoles; + +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; @SpringBootApplication public class App { + public final static String API_VERSION = "v1"; + + @Autowired + UserService userService; + public static void main(String[] args) { Logo logo = new Logo("beta"); System.out.println(logo.getLogo()); - System.out.println("|=============================================================================================|"); + System.out.println( + "|=============================================================================================|"); SpringApplication.run(App.class, args); } + + @EventListener(ApplicationReadyEvent.class) + public void importData() { + User testUser = new User(); + testUser.setEmail("filizy@st.amu.edu.pl"); + testUser.setName("Filip"); + testUser.setSurname("Izydorczyk"); + testUser.setRole(UserRoles.STUDENT); + this.userService.save(testUser); + } } diff --git a/restservice/src/main/java/com/plannaplan/controllers/ConfigController.java b/restservice/src/main/java/com/plannaplan/controllers/ConfigController.java index 7ec7f32..6438c74 100644 --- a/restservice/src/main/java/com/plannaplan/controllers/ConfigController.java +++ b/restservice/src/main/java/com/plannaplan/controllers/ConfigController.java @@ -5,6 +5,7 @@ import org.springframework.web.bind.annotation.RestController; import java.io.IOException; +import com.plannaplan.App; import com.plannaplan.Controller; import com.plannaplan.models.ConfigData; @@ -12,11 +13,13 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile; @RestController @CrossOrigin +@RequestMapping("/api/" + App.API_VERSION + "/configurator") public class ConfigController { @Autowired diff --git a/restservice/src/main/java/com/plannaplan/controllers/CoursesController.java b/restservice/src/main/java/com/plannaplan/controllers/CoursesController.java index f6e2d38..e56e294 100644 --- a/restservice/src/main/java/com/plannaplan/controllers/CoursesController.java +++ b/restservice/src/main/java/com/plannaplan/controllers/CoursesController.java @@ -5,6 +5,7 @@ import java.util.Dictionary; import java.util.Hashtable; import java.util.List; +import com.plannaplan.App; import com.plannaplan.entities.Course; import com.plannaplan.entities.Groups; import com.plannaplan.services.CourseService; @@ -15,38 +16,40 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; @RestController @CrossOrigin +@RequestMapping("/api/" + App.API_VERSION + "/courses") public class CoursesController { @Autowired private CourseService courseService; @GetMapping("/getCourses") - public ResponseEntity>> getMethodName() { + public ResponseEntity>> getMethodName() { List courses = this.courseService.getAllCourses(); - List> response = new ArrayList<>(); - for(Course c : courses){ + List> response = new ArrayList<>(); + for (Course c : courses) { Dictionary element = new Hashtable<>(); element.put("id", c.getId()); - element.put("name",c.getName()); + element.put("name", c.getName()); response.add(element); } - + return new ResponseEntity<>(response, HttpStatus.OK); } @GetMapping("/getCoursesWithGroups") - public ResponseEntity>> getCoursesWithGroups() { + public ResponseEntity>> getCoursesWithGroups() { List courses = this.courseService.getAllCourses(); - List> response = new ArrayList<>(); - for(Course c : courses){ + List> response = new ArrayList<>(); + for (Course c : courses) { Dictionary element = new Hashtable<>(); element.put("id", c.getId()); - element.put("name",c.getName()); - List> groups = new ArrayList<>(); - for(Groups g : c.getGroups()){ - Dictionary group = new Hashtable<>(); + element.put("name", c.getName()); + List> groups = new ArrayList<>(); + for (Groups g : c.getGroups()) { + Dictionary group = new Hashtable<>(); group.put("id", g.getId()); group.put("day", g.getDay().label); group.put("time", g.getTimeString()); @@ -55,11 +58,11 @@ public class CoursesController { group.put("type", g.getType()); groups.add(group); } - + element.put("groups", groups); response.add(element); } - + return new ResponseEntity<>(response, HttpStatus.OK); } diff --git a/restservice/src/main/java/com/plannaplan/controllers/GroupController.java b/restservice/src/main/java/com/plannaplan/controllers/GroupController.java index a84a5b3..2b56024 100644 --- a/restservice/src/main/java/com/plannaplan/controllers/GroupController.java +++ b/restservice/src/main/java/com/plannaplan/controllers/GroupController.java @@ -5,6 +5,7 @@ import java.util.Dictionary; import java.util.Hashtable; import java.util.List; +import com.plannaplan.App; import com.plannaplan.entities.Groups; import com.plannaplan.services.GroupService; @@ -13,21 +14,23 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @CrossOrigin +@RequestMapping("/api/" + App.API_VERSION + "/groups") public class GroupController { @Autowired private GroupService groupService; @GetMapping("/getCourseGroups") - public ResponseEntity>> getCourses(@RequestParam("id") Long id, @RequestParam(name="capacity", defaultValue="true") Boolean capacity){ + public ResponseEntity>> getCourses(@RequestParam("id") Long id, + @RequestParam(name = "capacity", defaultValue = "true") Boolean capacity) { List groups = this.groupService.getGroupsByCourse(id); List> response = new ArrayList<>(); - for (Groups g : groups) { Dictionary group = new Hashtable<>(); group.put("id", g.getId()); @@ -37,7 +40,7 @@ public class GroupController { group.put("room", g.getRoom()); if (capacity) { group.put("capacity", g.getCapacity()); - } + } group.put("type", g.getType()); response.add(group); diff --git a/restservice/src/main/java/com/plannaplan/controllers/TokenController.java b/restservice/src/main/java/com/plannaplan/controllers/TokenController.java new file mode 100644 index 0000000..f6ef1d1 --- /dev/null +++ b/restservice/src/main/java/com/plannaplan/controllers/TokenController.java @@ -0,0 +1,42 @@ +package com.plannaplan.controllers; + +import com.plannaplan.exceptions.UserNotFoundException; +import com.plannaplan.security.CasValidationExcepiton; +import com.plannaplan.security.CasValidator; +import com.plannaplan.services.UserService; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@CrossOrigin +public class TokenController { + + public static String SERVICE_URL = "http://localhost:3000"; + + @Autowired + private UserService userService; + + @GetMapping("/token") + public ResponseEntity getToken(@RequestParam("ticket") final String ticket) { + CasValidator validator = new CasValidator(SERVICE_URL, ticket); + + try { + String authority = validator.validate(); + String token = this.userService.login(authority); + return new ResponseEntity<>(token, HttpStatus.OK); + } catch (CasValidationExcepiton e) { + return new ResponseEntity<>("Wrong ticket", HttpStatus.UNAUTHORIZED); + } catch (UserNotFoundException e) { + return new ResponseEntity<>("User not found", HttpStatus.NOT_FOUND); + } catch (Exception e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } + + } +} diff --git a/restservice/src/main/java/com/plannaplan/security/AuthenticationFilter.java b/restservice/src/main/java/com/plannaplan/security/AuthenticationFilter.java new file mode 100644 index 0000000..3ebfe13 --- /dev/null +++ b/restservice/src/main/java/com/plannaplan/security/AuthenticationFilter.java @@ -0,0 +1,49 @@ +package com.plannaplan.security; + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; +import org.springframework.security.web.util.matcher.RequestMatcher; + +import static org.springframework.http.HttpHeaders.AUTHORIZATION; + +public class AuthenticationFilter extends AbstractAuthenticationProcessingFilter { + + AuthenticationFilter(final RequestMatcher requiresAuth) { + super(requiresAuth); + } + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) + throws AuthenticationException, IOException, ServletException { + + String token = request.getHeader(AUTHORIZATION); + if (token == null) { + token = ""; + } else { + token = StringUtils.removeStart(token, "Bearer").trim(); + } + Authentication requestAuthentication = new UsernamePasswordAuthenticationToken(token, token); + return getAuthenticationManager().authenticate(requestAuthentication); + + } + + @Override + protected void successfulAuthentication(final HttpServletRequest request, final HttpServletResponse response, + final FilterChain chain, final Authentication authResult) throws IOException, ServletException { + SecurityContextHolder.getContext().setAuthentication(authResult); + + chain.doFilter(request, response); + } + +} \ No newline at end of file diff --git a/restservice/src/main/java/com/plannaplan/security/AuthenticationProvider.java b/restservice/src/main/java/com/plannaplan/security/AuthenticationProvider.java new file mode 100644 index 0000000..aa9ff3d --- /dev/null +++ b/restservice/src/main/java/com/plannaplan/security/AuthenticationProvider.java @@ -0,0 +1,91 @@ +package com.plannaplan.security; + +import java.util.Collection; + +import com.plannaplan.entities.User; +import com.plannaplan.services.UserService; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Component; + +@Component +public class AuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { + + @Autowired + private UserService userService; + + @Override + protected void additionalAuthenticationChecks(UserDetails userDetails, + UsernamePasswordAuthenticationToken authentication) throws AuthenticationException { + // TODO Auto-generated method stub + + } + + @Override + protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) + throws AuthenticationException { + + String token = authentication.getCredentials().toString(); + + User user = this.userService.getByToken(token); + + if (user == null) { + throw new UsernameNotFoundException("Cannot find user with authentication token=" + token); + } + + UserDetails response = new UserDetails() { + + /** + * + */ + private static final long serialVersionUID = 1L; + + @Override + public Collection getAuthorities() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getPassword() { + return null; + } + + @Override + public String getUsername() { + return user.getName() + " " + user.getSurname(); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + // TODO Auto-generated method stub + return true; + } + + @Override + public boolean isEnabled() { + return true; + } + + }; + + return response; + } + +} diff --git a/restservice/src/main/java/com/plannaplan/security/CasValidationExcepiton.java b/restservice/src/main/java/com/plannaplan/security/CasValidationExcepiton.java new file mode 100644 index 0000000..88f3f5b --- /dev/null +++ b/restservice/src/main/java/com/plannaplan/security/CasValidationExcepiton.java @@ -0,0 +1,13 @@ +package com.plannaplan.security; + +public class CasValidationExcepiton extends RuntimeException { + /** + * + */ + private static final long serialVersionUID = 1L; + + public CasValidationExcepiton(String s) { + super(s); + } + +} diff --git a/restservice/src/main/java/com/plannaplan/security/CasValidator.java b/restservice/src/main/java/com/plannaplan/security/CasValidator.java new file mode 100644 index 0000000..9813758 --- /dev/null +++ b/restservice/src/main/java/com/plannaplan/security/CasValidator.java @@ -0,0 +1,45 @@ +package com.plannaplan.security; + +import java.net.URLEncoder; + +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; + +public class CasValidator { + private static String CAS_URL = "https://cas.amu.edu.pl/cas"; + private final CloseableHttpClient httpClient = HttpClients.createDefault(); + private String service; + private String ticket; + + public CasValidator(String service, String ticket) { + this.service = service; + this.ticket = ticket; + } + + public String validate() throws Exception, CasValidationExcepiton{ + HttpGet request = new HttpGet(CasValidator.CAS_URL + "/validate?service=" + + URLEncoder.encode(this.service, "UTF-8") + "&ticket=" + URLEncoder.encode(this.ticket, "UTF-8")); + try (CloseableHttpResponse response = httpClient.execute(request)) { + + HttpEntity entity = response.getEntity(); + + String result = null; + if (entity != null) { + // return it as a String + result = EntityUtils.toString(entity); + if(result.replace("\n", "").trim().equals("no")){ + throw new CasValidationExcepiton("Validation failed"); + } + } + + String res = result.substring(result.indexOf('\n') + 1); + return res; + + } + + } +} diff --git a/restservice/src/main/java/com/plannaplan/security/WebSecurityConfig.java b/restservice/src/main/java/com/plannaplan/security/WebSecurityConfig.java new file mode 100644 index 0000000..25628fb --- /dev/null +++ b/restservice/src/main/java/com/plannaplan/security/WebSecurityConfig.java @@ -0,0 +1,64 @@ +package com.plannaplan.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.OrRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.http.HttpStatus; +import org.springframework.security.web.authentication.AnonymousAuthenticationFilter; +import org.springframework.security.web.authentication.HttpStatusEntryPoint; + +@Configuration +@EnableWebSecurity +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + + private static final RequestMatcher PROTECTED_URLS = new OrRequestMatcher(new AntPathRequestMatcher("/api/**")); + + AuthenticationProvider provider; + + public WebSecurityConfig(final AuthenticationProvider authenticationProvider) { + super(); + this.provider = authenticationProvider; + } + + @Override + protected void configure(final AuthenticationManagerBuilder auth) { + auth.authenticationProvider(provider); + } + + @Override + public void configure(final WebSecurity webSecurity) { + webSecurity.ignoring().antMatchers("/token**").antMatchers("/api/v1/courses/getCourses") + .antMatchers("/api/v1/groups/getCourseGroups").antMatchers("/api/v1/courses/getCoursesWithGroups"); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.csrf().disable().formLogin().disable().httpBasic().disable().logout().disable().sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().exceptionHandling().and() + .authenticationProvider(provider) + .addFilterBefore(authenticationFilter(), AnonymousAuthenticationFilter.class).authorizeRequests() + .anyRequest().authenticated(); + + } + + AuthenticationFilter authenticationFilter() throws Exception { + final AuthenticationFilter filter = new AuthenticationFilter(PROTECTED_URLS); + filter.setAuthenticationManager(authenticationManager()); + return filter; + } + + @Bean + AuthenticationEntryPoint forbiddenEntryPoint() { + return new HttpStatusEntryPoint(HttpStatus.FORBIDDEN); + } + +} diff --git a/restservice/src/test/java/com/plannaplan/security/CasValidatorTest.java b/restservice/src/test/java/com/plannaplan/security/CasValidatorTest.java new file mode 100644 index 0000000..f825874 --- /dev/null +++ b/restservice/src/test/java/com/plannaplan/security/CasValidatorTest.java @@ -0,0 +1,40 @@ +package com.plannaplan.security; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.Ignore; +import org.junit.Test; + +public class CasValidatorTest { + + @Test + @Ignore + public void shouldValidateTicket() { + //you need to privide fresh ticket to make this test pass that's why it is marked as ignored + CasValidator validator = new CasValidator("http://localhost:3000", + "ST-572267-cbgKrcJLd0tdCubeLqdW-cas.amu.edu.pl"); + try { + System.out.println(validator.validate()); + } catch (Exception e) { + e.printStackTrace(); + assertTrue(false); + } + } + + @Test + public void shouldNotValidateTicket() { + //you need to privide fresh ticket to make this test pass that's why it is marked as ignored + CasValidator validator = new CasValidator("http://localhost:3000", + "notticket"); + try { + assertTrue(validator.validate().trim().equals("")); + } + catch (CasValidationExcepiton e){ + assertTrue(true); + } + catch (Exception e) { + e.printStackTrace(); + assertTrue(false); + } + } +}