Commit 3e533eefbbc9c3470508be11fcd0c69fc6d10c78

Authored by Thomas Ziemer
1 parent 32cb9414

feature: SecurityInterceptor eingebaut

@@ -14,7 +14,8 @@ @@ -14,7 +14,8 @@
14 <mvn-compiler-plugin-version>3.3</mvn-compiler-plugin-version> 14 <mvn-compiler-plugin-version>3.3</mvn-compiler-plugin-version>
15 <jee-version>7.0</jee-version> 15 <jee-version>7.0</jee-version>
16 <java-version>1.8</java-version> 16 <java-version>1.8</java-version>
17 - <jaxrs-version>2.0</jaxrs-version> 17 + <jaxrs-version>2.0.1</jaxrs-version>
  18 + <resteasy-version>3.1.3.Final</resteasy-version>
18 <mysql-version>5.1.41</mysql-version> 19 <mysql-version>5.1.41</mysql-version>
19 <hibernate-version>5.2.1.Final</hibernate-version> 20 <hibernate-version>5.2.1.Final</hibernate-version>
20 <junit-version>4.12</junit-version> 21 <junit-version>4.12</junit-version>
@@ -45,6 +46,12 @@ @@ -45,6 +46,12 @@
45 <version>${jaxrs-version}</version> 46 <version>${jaxrs-version}</version>
46 <scope>provided</scope> 47 <scope>provided</scope>
47 </dependency> 48 </dependency>
  49 + <dependency>
  50 + <groupId>org.jboss.resteasy</groupId>
  51 + <artifactId>resteasy-jaxrs</artifactId>
  52 + <version>${resteasy-version}</version>
  53 + <scope>provided</scope>
  54 + </dependency>
48 <dependency> 55 <dependency>
49 <groupId>junit</groupId> 56 <groupId>junit</groupId>
50 <artifactId>junit</artifactId> 57 <artifactId>junit</artifactId>
src/main/java/net/ziemers/swxercise/lg/user/service/UserService.java
1 package net.ziemers.swxercise.lg.user.service; 1 package net.ziemers.swxercise.lg.user.service;
2 2
3 import java.util.Collection; 3 import java.util.Collection;
  4 +import java.util.Set;
4 5
5 import javax.ejb.Stateless; 6 import javax.ejb.Stateless;
6 import javax.inject.Inject; 7 import javax.inject.Inject;
@@ -22,23 +23,50 @@ public class UserService { @@ -22,23 +23,50 @@ public class UserService {
22 @Inject 23 @Inject
23 private SessionContext sessionContext; 24 private SessionContext sessionContext;
24 25
  26 + /**
  27 + * Meldet den Benutzer im SessionContext an.
  28 + *
  29 + * @param dto das {@link UserDto} enthält die Eigenschaften des anzumeldenden Benutzers
  30 + * @return <code>true</code>, wenn die Anmeldung erfolgreich war.
  31 + */
25 public boolean loginUser(UserDto dto) { 32 public boolean loginUser(UserDto dto) {
26 final User user = dao.findByUsername(dto.getUsername()); 33 final User user = dao.findByUsername(dto.getUsername());
27 return user != null && user.getProfile().isValidPassword(dto.getPassword()) && sessionContext.login(user); 34 return user != null && user.getProfile().isValidPassword(dto.getPassword()) && sessionContext.login(user);
28 } 35 }
29 36
  37 + /**
  38 + * Meldet den Benutzer vom SessionContext ab.
  39 + *
  40 + * @return <code>true</code>, wenn die Abmeldung erfolgreich war.
  41 + */
30 public boolean logoutUser() { 42 public boolean logoutUser() {
31 - return sessionContext.logout(); 43 + return sessionContext.logout();
32 } 44 }
33 45
  46 + /**
  47 + * Findet den Benutzer mit der übergebenen Id.
  48 + *
  49 + * @param id die Id des gesuchten Benutzes.
  50 + * @return den gesuchten Benutzer, oder <code>null</code>, falls ein solcher nicht existiert.
  51 + */
34 public User findUser(final Long id) { 52 public User findUser(final Long id) {
35 return dao.findById(id); 53 return dao.findById(id);
36 } 54 }
37 55
  56 + /**
  57 + * Findet alle existierenden Benutzer.
  58 + * @return alle Benutzer, oder eine leere Collection, falls keine existieren.
  59 + */
38 public Collection<User> findAllUsers() { 60 public Collection<User> findAllUsers() {
39 return dao.findAll(User.class); 61 return dao.findAll(User.class);
40 } 62 }
41 63
  64 + /**
  65 + * Erstellt einen neuen Benutzer, sofern noch keiner mit dem selben Benutzernamen existiert.
  66 + *
  67 + * @param userDto das {@link UserDto} enthält die Eigenschaften des zu erstellenden Benutzers
  68 + * @return die Id des neuen Benutzers, wenn die Erstellung erfolgreich war.
  69 + */
42 public Long createUser(final UserDto userDto) { 70 public Long createUser(final UserDto userDto) {
43 User user = dao.findByUsername(userDto.getUsername()); 71 User user = dao.findByUsername(userDto.getUsername());
44 if (user == null) { 72 if (user == null) {
@@ -53,21 +81,48 @@ public class UserService { @@ -53,21 +81,48 @@ public class UserService {
53 return null; 81 return null;
54 } 82 }
55 83
56 - public void deleteUser(final Long id) {  
57 - dao.remove(User.class, id); 84 + /**
  85 + * Löscht den Benutzer mit der übergebenen Id.
  86 + *
  87 + * @param id die Id des zu löschenden Benutzers
  88 + */
  89 + public User deleteUser(final Long id) {
  90 + return dao.remove(User.class, id);
58 } 91 }
59 92
  93 + /**
  94 + * Löscht den zurzeit angemeldeten Benutzer.
  95 + *
  96 + * @return <code>true</code>, wenn das Löschen des angemeldeten Benutzers erfolgreich war.
  97 + */
60 public boolean deleteUser() { 98 public boolean deleteUser() {
61 // ist zurzeit ein Benutzer angemeldet, können wir ihn löschen 99 // ist zurzeit ein Benutzer angemeldet, können wir ihn löschen
62 final User user = sessionContext.getUser(); 100 final User user = sessionContext.getUser();
63 - if (user != null) {  
64 - return dao.remove(User.class, user.getId()) != null;  
65 - }  
66 - return false; 101 + return user != null && dao.remove(User.class, user.getId()) != null;
67 } 102 }
68 103
  104 + /**
  105 + * Liefert das {@link User}-Objekt des zurzeit angemeldeten Benutzers zurück.
  106 + * @return das User-Objekt des zurzeit angemeldeten Benutzers.
  107 + */
69 public User getSessionUser() { 108 public User getSessionUser() {
70 return sessionContext.getUser(); 109 return sessionContext.getUser();
71 } 110 }
72 111
  112 + /**
  113 + * Prüft, ob der zurzeit angemeldete Benutzer eine Rolle aus der Liste der übergebenen Rollen besitzt.
  114 + *
  115 + * @param rolesSet die Rollenliste
  116 + * @return <code>true</code>, falls der zurzeit angemeldete Benutzer eine der übergebenen Rollen besitzt.
  117 + */
  118 + public boolean isUserAllowed(final Set<String> rolesSet) {
  119 + final User user = getSessionUser();
  120 +
  121 + // ist überhaupt ein Benutzer angemeldet?
  122 + if (user != null) {
  123 + // TODO muss noch implementiert werden
  124 + return true;
  125 + }
  126 + return false;
  127 + }
73 } 128 }
src/main/java/net/ziemers/swxercise/ui/SecurityInterceptor.java 0 → 100644
  1 +package net.ziemers.swxercise.ui;
  2 +
  3 +import javax.annotation.PostConstruct;
  4 +import javax.annotation.security.DenyAll;
  5 +import javax.annotation.security.PermitAll;
  6 +import javax.annotation.security.RolesAllowed;
  7 +import javax.inject.Inject;
  8 +import javax.ws.rs.container.ContainerRequestContext;
  9 +import javax.ws.rs.container.ContainerRequestFilter;
  10 +import javax.ws.rs.ext.Provider;
  11 +import java.lang.reflect.AnnotatedElement;
  12 +import java.util.Arrays;
  13 +import java.util.HashSet;
  14 +import java.util.Set;
  15 +
  16 +import net.ziemers.swxercise.lg.user.service.UserService;
  17 +import org.jboss.resteasy.core.Headers;
  18 +import org.jboss.resteasy.core.ResourceMethodInvoker;
  19 +import org.jboss.resteasy.core.ServerResponse;
  20 +import org.slf4j.Logger;
  21 +import org.slf4j.LoggerFactory;
  22 +
  23 +/**
  24 + * Dies ist ein Interceptor für die Annotationen @PermitAll, @DenyAll und @RolesAllowed. Er stellt sicher,
  25 + * dass die REST-Methoden, welche mit den genannten Annotationen versehen wurden, nur von authorisierten
  26 + * Benutzern aufgerufen werden können.
  27 + */
  28 +@Provider
  29 +public class SecurityInterceptor implements ContainerRequestFilter {
  30 +
  31 + private static final String PROPERTY_RMI = "org.jboss.resteasy.core.ResourceMethodInvoker";
  32 +
  33 + // 401: The request has not been applied because it lacks valid authentication credentials for the target resource.
  34 + private static final ServerResponse ACCESS_DENIED = new ServerResponse("Access denied for this rest method", 401, new Headers<>());
  35 +
  36 + // 403: The server understood the request but refuses to authorize it.
  37 + private static final ServerResponse ACCESS_FORBIDDEN = new ServerResponse("Nobody can access this rest method", 403, new Headers<>());
  38 +
  39 + private Logger logger;
  40 +
  41 + @Inject
  42 + private UserService userService;
  43 +
  44 + @PostConstruct
  45 + public void initialize() {
  46 + logger = LoggerFactory.getLogger(SecurityInterceptor.class);
  47 + }
  48 +
  49 + @Override
  50 + public void filter(ContainerRequestContext requestContext) {
  51 + final ResourceMethodInvoker methodInvoker = (ResourceMethodInvoker) requestContext.getProperty(PROPERTY_RMI);
  52 + final AnnotatedElement element = methodInvoker.getMethod();
  53 +
  54 + // Zugriff auf die Methode ist für alle erlaubt (obsolet)
  55 + if (element.isAnnotationPresent(PermitAll.class)) {
  56 + logger.debug("@PermitAll");
  57 + }
  58 +
  59 + // Zugriff auf die Methode ist für niemanden erlaubt
  60 + else if (element.isAnnotationPresent(DenyAll.class)) {
  61 + requestContext.abortWith(ACCESS_FORBIDDEN);
  62 + logger.debug("@DenyAll");
  63 + }
  64 +
  65 + // Zugriff auf die Methode muss anhand ihrer Rollen geprüft werden
  66 + else if (element.isAnnotationPresent(RolesAllowed.class)) {
  67 + final RolesAllowed rolesAllowed = element.getAnnotation(RolesAllowed.class);
  68 + final Set<String> rolesSet = new HashSet<>(Arrays.asList(rolesAllowed.value()));
  69 +
  70 + if (!userService.isUserAllowed(rolesSet)) {
  71 + requestContext.abortWith(ACCESS_DENIED);
  72 + }
  73 + logger.debug("@RolesAllowed({})", rolesSet.toString());
  74 + }
  75 + }
  76 +
  77 +}
src/main/java/net/ziemers/swxercise/ui/UserViewController.java
@@ -2,6 +2,7 @@ package net.ziemers.swxercise.ui; @@ -2,6 +2,7 @@ package net.ziemers.swxercise.ui;
2 2
3 import java.util.Collection; 3 import java.util.Collection;
4 4
  5 +import javax.annotation.security.RolesAllowed;
5 import javax.enterprise.context.ApplicationScoped; 6 import javax.enterprise.context.ApplicationScoped;
6 import javax.inject.Inject; 7 import javax.inject.Inject;
7 import javax.ws.rs.*; 8 import javax.ws.rs.*;
@@ -70,6 +71,7 @@ public class UserViewController { @@ -70,6 +71,7 @@ public class UserViewController {
70 @Path("v1/user") 71 @Path("v1/user")
71 @Consumes(MediaType.APPLICATION_JSON) 72 @Consumes(MediaType.APPLICATION_JSON)
72 @Produces(MediaType.APPLICATION_JSON) 73 @Produces(MediaType.APPLICATION_JSON)
  74 + @RolesAllowed("ADMIN")
73 public RestResponse createUser(UserDto dto) { 75 public RestResponse createUser(UserDto dto) {
74 final Long id = userService.createUser(dto); 76 final Long id = userService.createUser(dto);
75 if (id != null) { 77 if (id != null) {
@@ -94,6 +96,7 @@ public class UserViewController { @@ -94,6 +96,7 @@ public class UserViewController {
94 @Path("v1/user/{id}") 96 @Path("v1/user/{id}")
95 @Consumes(MediaType.APPLICATION_JSON) 97 @Consumes(MediaType.APPLICATION_JSON)
96 @Produces(MediaType.APPLICATION_JSON) 98 @Produces(MediaType.APPLICATION_JSON)
  99 + @RolesAllowed("ADMIN")
97 public RestResponse updateUser(@PathParam("id") Long id, UserDto dto) { 100 public RestResponse updateUser(@PathParam("id") Long id, UserDto dto) {
98 // TODO noch zu implementieren 101 // TODO noch zu implementieren
99 return new RestResponse(ResponseState.FAILED); 102 return new RestResponse(ResponseState.FAILED);
@@ -111,6 +114,7 @@ public class UserViewController { @@ -111,6 +114,7 @@ public class UserViewController {
111 @DELETE 114 @DELETE
112 @Path("v1/user/{id}") 115 @Path("v1/user/{id}")
113 @Produces(MediaType.APPLICATION_JSON) 116 @Produces(MediaType.APPLICATION_JSON)
  117 + @RolesAllowed("ADMIN")
114 public RestResponse deleteUser(@PathParam("id") Long id) { 118 public RestResponse deleteUser(@PathParam("id") Long id) {
115 userService.deleteUser(id); 119 userService.deleteUser(id);
116 return new RestResponse(); 120 return new RestResponse();