From 3deebe92486006d555b91be2190a0de7c4535a85 Mon Sep 17 00:00:00 2001 From: Filip Izydorczyk Date: Fri, 11 Sep 2020 13:15:22 +0200 Subject: [PATCH 1/7] Added CasValidator --- restservice/pom.xml | 6 +++ .../com/plannaplan/security/CasValidator.java | 42 +++++++++++++++++++ .../plannaplan/security/CasValidatorTest.java | 21 ++++++++++ 3 files changed, 69 insertions(+) create mode 100644 restservice/src/main/java/com/plannaplan/security/CasValidator.java create mode 100644 restservice/src/test/java/com/plannaplan/security/CasValidatorTest.java diff --git a/restservice/pom.xml b/restservice/pom.xml index 4d62b67..bd3a2dc 100644 --- a/restservice/pom.xml +++ b/restservice/pom.xml @@ -31,6 +31,12 @@ test + + org.apache.httpcomponents + httpclient + 4.5.10 + + org.springframework.boot spring-boot-starter-web 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..4af59b2 --- /dev/null +++ b/restservice/src/main/java/com/plannaplan/security/CasValidator.java @@ -0,0 +1,42 @@ +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 { + 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); + + } + String res = result.substring(result.indexOf('\n') + 1); + return res; + + } + + } +} 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..234a759 --- /dev/null +++ b/restservice/src/test/java/com/plannaplan/security/CasValidatorTest.java @@ -0,0 +1,21 @@ +package com.plannaplan.security; + +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) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } +} From c9f50c5b160a33ed8a5abcf206aad786c5119277 Mon Sep 17 00:00:00 2001 From: Filip Izydorczyk Date: Fri, 11 Sep 2020 14:12:13 +0200 Subject: [PATCH 2/7] Cas update --- .../controllers/TokenController.java | 21 +++++++++++++++++++ .../security/CasValidationExcepiton.java | 13 ++++++++++++ .../com/plannaplan/security/CasValidator.java | 7 +++++-- .../plannaplan/security/CasValidatorTest.java | 21 ++++++++++++++++++- 4 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 restservice/src/main/java/com/plannaplan/controllers/TokenController.java create mode 100644 restservice/src/main/java/com/plannaplan/security/CasValidationExcepiton.java 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..d23b931 --- /dev/null +++ b/restservice/src/main/java/com/plannaplan/controllers/TokenController.java @@ -0,0 +1,21 @@ +package com.plannaplan.controllers; + +import com.plannaplan.security.CasValidator; + +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"; + + // @GetMapping("/token") + // public ResponseEntity getToken(@RequestParam("ticket") final String ticket) { + // CasValidator validator = new CasValidator(SERVICE_URL,ticket); + // } +} 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 index 4af59b2..9813758 100644 --- a/restservice/src/main/java/com/plannaplan/security/CasValidator.java +++ b/restservice/src/main/java/com/plannaplan/security/CasValidator.java @@ -20,7 +20,7 @@ public class CasValidator { this.ticket = ticket; } - public String validate() throws Exception { + 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)) { @@ -31,8 +31,11 @@ public class CasValidator { 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/test/java/com/plannaplan/security/CasValidatorTest.java b/restservice/src/test/java/com/plannaplan/security/CasValidatorTest.java index 234a759..f825874 100644 --- a/restservice/src/test/java/com/plannaplan/security/CasValidatorTest.java +++ b/restservice/src/test/java/com/plannaplan/security/CasValidatorTest.java @@ -1,5 +1,7 @@ package com.plannaplan.security; +import static org.junit.jupiter.api.Assertions.assertTrue; + import org.junit.Ignore; import org.junit.Test; @@ -14,8 +16,25 @@ public class CasValidatorTest { try { System.out.println(validator.validate()); } catch (Exception e) { - // TODO Auto-generated catch block 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); } } } From 8c16b2f3d1da1efc4b5fcce260deb5d1ec0d052f Mon Sep 17 00:00:00 2001 From: Filip Izydorczyk Date: Fri, 11 Sep 2020 14:33:29 +0200 Subject: [PATCH 3/7] Added auth endpoint --- .../controllers/TokenController.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/restservice/src/main/java/com/plannaplan/controllers/TokenController.java b/restservice/src/main/java/com/plannaplan/controllers/TokenController.java index d23b931..09d4528 100644 --- a/restservice/src/main/java/com/plannaplan/controllers/TokenController.java +++ b/restservice/src/main/java/com/plannaplan/controllers/TokenController.java @@ -1,7 +1,9 @@ package com.plannaplan.controllers; +import com.plannaplan.security.CasValidationExcepiton; import com.plannaplan.security.CasValidator; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; @@ -14,8 +16,20 @@ public class TokenController { public static String SERVICE_URL = "http://localhost:3000"; - // @GetMapping("/token") - // public ResponseEntity getToken(@RequestParam("ticket") final String ticket) { - // CasValidator validator = new CasValidator(SERVICE_URL,ticket); - // } + @GetMapping("/token") + public ResponseEntity getToken(@RequestParam("ticket") final String ticket) { + CasValidator validator = new CasValidator(SERVICE_URL,ticket); + + try{ + String authority = validator.validate(); + return new ResponseEntity<>(authority,HttpStatus.OK); + } + catch(CasValidationExcepiton e){ + return new ResponseEntity<>("Wrong ticket",HttpStatus.UNAUTHORIZED); + } + catch(Exception e){ + return new ResponseEntity<>(e.getMessage(),HttpStatus.INTERNAL_SERVER_ERROR); + } + + } } From 11f362568410d5b3983b1658d33e7f11c36edad6 Mon Sep 17 00:00:00 2001 From: Filip Izydorczyk Date: Mon, 14 Sep 2020 12:39:25 +0200 Subject: [PATCH 4/7] User service added login and returning token --- .../java/com/plannaplan/entities/User.java | 26 ++++++++ .../exceptions/UserNotFoundException.java | 13 ++++ .../repositories/UserRepository.java | 5 +- .../com/plannaplan/services/UserService.java | 22 +++++++ .../java/com/plannaplan/types/UserRoles.java | 2 +- .../plannaplan/services/UserServiceTest.java | 63 +++++++++++++++++++ .../src/main/java/com/plannaplan/App.java | 23 ++++++- 7 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 buisnesslogic/src/main/java/com/plannaplan/exceptions/UserNotFoundException.java create mode 100644 buisnesslogic/src/test/java/com/plannaplan/services/UserServiceTest.java 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..a341ecb 100644 --- a/buisnesslogic/src/main/java/com/plannaplan/repositories/UserRepository.java +++ b/buisnesslogic/src/main/java/com/plannaplan/repositories/UserRepository.java @@ -3,9 +3,12 @@ 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); } \ 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..270aac2 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,22 @@ public class UserService extends EventWatcher { super(); } + public String login(String authority) throws UserNotFoundException { + User user = this.repo.getByAuthority(authority); + if (user == null) { + throw new UserNotFoundException("Can not find user with given authority"); + } + String token = UUID.randomUUID().toString(); + user.setToken(token); + return token; + } + + public void save(User user) { + this.repo.save(user); + } + + public User getUserByEmail(String email) { + return this.repo.getByAuthority(email); + } + } \ 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..e21bf45 --- /dev/null +++ b/buisnesslogic/src/test/java/com/plannaplan/services/UserServiceTest.java @@ -0,0 +1,63 @@ +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); + } 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/restservice/src/main/java/com/plannaplan/App.java b/restservice/src/main/java/com/plannaplan/App.java index 137a7bd..50d5f57 100644 --- a/restservice/src/main/java/com/plannaplan/App.java +++ b/restservice/src/main/java/com/plannaplan/App.java @@ -1,15 +1,36 @@ 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 { + @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("noteexisitingmail@notexistingdomain.com"); + // testUser.setName("Tom"); + // testUser.setSurname("Kovalsky"); + // testUser.setRole(UserRoles.TEST_USER); + // this.userService.save(testUser); + // } } From 478ea2748078bec238a03fa7917c6b333dec291d Mon Sep 17 00:00:00 2001 From: Filip Izydorczyk Date: Mon, 14 Sep 2020 12:55:47 +0200 Subject: [PATCH 5/7] Endpoint returnung token if valid data --- .../com/plannaplan/services/UserService.java | 5 ++-- .../plannaplan/services/UserServiceTest.java | 1 + .../src/main/java/com/plannaplan/App.java | 18 ++++++------- .../controllers/TokenController.java | 25 ++++++++++++------- 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/buisnesslogic/src/main/java/com/plannaplan/services/UserService.java b/buisnesslogic/src/main/java/com/plannaplan/services/UserService.java index 270aac2..3bdcfa7 100644 --- a/buisnesslogic/src/main/java/com/plannaplan/services/UserService.java +++ b/buisnesslogic/src/main/java/com/plannaplan/services/UserService.java @@ -20,12 +20,13 @@ public class UserService extends EventWatcher { } public String login(String authority) throws UserNotFoundException { - User user = this.repo.getByAuthority(authority); + 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; } @@ -34,7 +35,7 @@ public class UserService extends EventWatcher { } public User getUserByEmail(String email) { - return this.repo.getByAuthority(email); + return this.repo.getByAuthority(email.replace("\n", "").trim()); } } \ 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 index e21bf45..7a764f6 100644 --- a/buisnesslogic/src/test/java/com/plannaplan/services/UserServiceTest.java +++ b/buisnesslogic/src/test/java/com/plannaplan/services/UserServiceTest.java @@ -45,6 +45,7 @@ public class UserServiceTest { 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); diff --git a/restservice/src/main/java/com/plannaplan/App.java b/restservice/src/main/java/com/plannaplan/App.java index 50d5f57..ca8b681 100644 --- a/restservice/src/main/java/com/plannaplan/App.java +++ b/restservice/src/main/java/com/plannaplan/App.java @@ -24,13 +24,13 @@ public class App { SpringApplication.run(App.class, args); } - // @EventListener(ApplicationReadyEvent.class) - // public void importData() { - // User testUser = new User(); - // testUser.setEmail("noteexisitingmail@notexistingdomain.com"); - // testUser.setName("Tom"); - // testUser.setSurname("Kovalsky"); - // testUser.setRole(UserRoles.TEST_USER); - // this.userService.save(testUser); - // } + @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/TokenController.java b/restservice/src/main/java/com/plannaplan/controllers/TokenController.java index 09d4528..f6ef1d1 100644 --- a/restservice/src/main/java/com/plannaplan/controllers/TokenController.java +++ b/restservice/src/main/java/com/plannaplan/controllers/TokenController.java @@ -1,8 +1,11 @@ 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; @@ -16,19 +19,23 @@ 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); + CasValidator validator = new CasValidator(SERVICE_URL, ticket); - try{ + try { String authority = validator.validate(); - return new ResponseEntity<>(authority,HttpStatus.OK); - } - catch(CasValidationExcepiton e){ - return new ResponseEntity<>("Wrong ticket",HttpStatus.UNAUTHORIZED); - } - catch(Exception e){ - return new ResponseEntity<>(e.getMessage(),HttpStatus.INTERNAL_SERVER_ERROR); + 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); } } From 82c4c9d0fe09fed20e54fdee02f5ce4d0e05cd92 Mon Sep 17 00:00:00 2001 From: Filip Izydorczyk Date: Mon, 14 Sep 2020 14:02:05 +0200 Subject: [PATCH 6/7] Added forbiden urls and protected --- .../repositories/UserRepository.java | 3 + .../com/plannaplan/services/UserService.java | 4 + restservice/pom.xml | 12 +++ .../security/AuthenticationFilter.java | 49 ++++++++++ .../security/AuthenticationProvider.java | 91 +++++++++++++++++++ .../security/WebSecurityConfig.java | 66 ++++++++++++++ 6 files changed, 225 insertions(+) create mode 100644 restservice/src/main/java/com/plannaplan/security/AuthenticationFilter.java create mode 100644 restservice/src/main/java/com/plannaplan/security/AuthenticationProvider.java create mode 100644 restservice/src/main/java/com/plannaplan/security/WebSecurityConfig.java diff --git a/buisnesslogic/src/main/java/com/plannaplan/repositories/UserRepository.java b/buisnesslogic/src/main/java/com/plannaplan/repositories/UserRepository.java index a341ecb..8fbbbb4 100644 --- a/buisnesslogic/src/main/java/com/plannaplan/repositories/UserRepository.java +++ b/buisnesslogic/src/main/java/com/plannaplan/repositories/UserRepository.java @@ -11,4 +11,7 @@ import org.springframework.stereotype.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 3bdcfa7..0fec12f 100644 --- a/buisnesslogic/src/main/java/com/plannaplan/services/UserService.java +++ b/buisnesslogic/src/main/java/com/plannaplan/services/UserService.java @@ -38,4 +38,8 @@ public class UserService extends EventWatcher { 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/restservice/pom.xml b/restservice/pom.xml index bd3a2dc..d941ad1 100644 --- a/restservice/pom.xml +++ b/restservice/pom.xml @@ -42,6 +42,12 @@ spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-security + 2.3.3.RELEASE + + org.springframework.boot spring-boot-starter-test @@ -54,6 +60,12 @@ + + org.apache.commons + commons-lang3 + 3.8.1 + + buisnesslogic com.plannaplan 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/WebSecurityConfig.java b/restservice/src/main/java/com/plannaplan/security/WebSecurityConfig.java new file mode 100644 index 0000000..29850bc --- /dev/null +++ b/restservice/src/main/java/com/plannaplan/security/WebSecurityConfig.java @@ -0,0 +1,66 @@ +package com.plannaplan.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +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**"); + } + + @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() + .antMatchers(HttpMethod.GET, "/token**").permitAll().anyRequest().authenticated(); + + } + + @Bean + AuthenticationFilter authenticationFilter() throws Exception { + final AuthenticationFilter filter = new AuthenticationFilter(PROTECTED_URLS); + filter.setAuthenticationManager(authenticationManager()); + // filter.setAuthenticationSuccessHandler(successHandler()); + return filter; + } + + @Bean + AuthenticationEntryPoint forbiddenEntryPoint() { + return new HttpStatusEntryPoint(HttpStatus.FORBIDDEN); + } + +} From b951f1f93485217ebb11dd45283b65517389c18a Mon Sep 17 00:00:00 2001 From: Filip Izydorczyk Date: Tue, 15 Sep 2020 11:31:30 +0200 Subject: [PATCH 7/7] Added some endpoints not protected --- docs/api.md | 37 ++++++++++++++----- .../src/main/java/com/plannaplan/App.java | 2 + .../controllers/ConfigController.java | 3 ++ .../controllers/CoursesController.java | 31 +++++++++------- .../controllers/GroupController.java | 9 +++-- .../security/WebSecurityConfig.java | 8 ++-- 6 files changed, 59 insertions(+), 31 deletions(-) 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/src/main/java/com/plannaplan/App.java b/restservice/src/main/java/com/plannaplan/App.java index ca8b681..7f8c652 100644 --- a/restservice/src/main/java/com/plannaplan/App.java +++ b/restservice/src/main/java/com/plannaplan/App.java @@ -13,6 +13,8 @@ import org.springframework.context.event.EventListener; @SpringBootApplication public class App { + public final static String API_VERSION = "v1"; + @Autowired UserService userService; 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/security/WebSecurityConfig.java b/restservice/src/main/java/com/plannaplan/security/WebSecurityConfig.java index 29850bc..25628fb 100644 --- a/restservice/src/main/java/com/plannaplan/security/WebSecurityConfig.java +++ b/restservice/src/main/java/com/plannaplan/security/WebSecurityConfig.java @@ -2,7 +2,6 @@ package com.plannaplan.security; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpMethod; 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; @@ -37,7 +36,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override public void configure(final WebSecurity webSecurity) { - webSecurity.ignoring().antMatchers("/token**"); + webSecurity.ignoring().antMatchers("/token**").antMatchers("/api/v1/courses/getCourses") + .antMatchers("/api/v1/groups/getCourseGroups").antMatchers("/api/v1/courses/getCoursesWithGroups"); } @Override @@ -46,15 +46,13 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().exceptionHandling().and() .authenticationProvider(provider) .addFilterBefore(authenticationFilter(), AnonymousAuthenticationFilter.class).authorizeRequests() - .antMatchers(HttpMethod.GET, "/token**").permitAll().anyRequest().authenticated(); + .anyRequest().authenticated(); } - @Bean AuthenticationFilter authenticationFilter() throws Exception { final AuthenticationFilter filter = new AuthenticationFilter(PROTECTED_URLS); filter.setAuthenticationManager(authenticationManager()); - // filter.setAuthenticationSuccessHandler(successHandler()); return filter; }