[DEV] securized all the interfaces

This commit is contained in:
Edouard DUPIN 2022-05-23 23:25:35 +02:00
parent f45061097d
commit 1b71740b84
10 changed files with 113 additions and 81 deletions

View File

@ -19,15 +19,17 @@ import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
// https://stackoverflow.com/questions/26777083/best-practice-for-rest-token-based-authentication-with-jax-rs-and-jersey
// https://stackoverflow.com/questions/26777083/best-practice-for-rest-token-based-authentication-with-jax-rs-and-jersey/45814178#45814178
// https://stackoverflow.com/questions/32817210/how-to-access-jersey-resource-secured-by-rolesallowed
//@Provider
//@PreMatching
//@Secured
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
@ -42,11 +44,39 @@ public class AuthenticationFilter implements ContainerRequestFilter {
System.out.println("-----------------------------------------------------");
System.out.println("---- Check if have authorization ----");
System.out.println("-----------------------------------------------------");
System.out.println(" for:" + requestContext.getUriInfo().getPath());
System.out.println(" for:" + requestContext.getUriInfo().getPath());
Method method = resourceInfo.getResourceMethod();
// Access denied for all
if(method.isAnnotationPresent(DenyAll.class)) {
System.out.println(" ==> deny all " + requestContext.getUriInfo().getPath());
requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access blocked !!!").build());
return;
}
//Access allowed for all
if( method.isAnnotationPresent(PermitAll.class)) {
System.out.println(" ==> permit all " + requestContext.getUriInfo().getPath());
// no control ...
return;
}
// this is a security guard, all the API must define their access level:
if(!method.isAnnotationPresent(RolesAllowed.class)) {
System.out.println(" ==> missin @RolesAllowed " + requestContext.getUriInfo().getPath());
requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access ILLEGAL !!!").build());
return;
}
// Get the Authorization header from the request
String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
System.out.println("authorizationHeader: " + authorizationHeader);
if(authorizationHeader == null && method.isAnnotationPresent(PermitTokenInURI.class)) {
// TODO: ...
}
System.out.println(" -------------------------------");
// this get the parameters inside the pre-parsed element in the request ex: @Path("thumbnail/{id}") generate a map with "id"
MultivaluedMap<String, String> pathparam = requestContext.getUriInfo().getPathParameters();
@ -59,29 +89,19 @@ public class AuthenticationFilter implements ContainerRequestFilter {
System.out.println(" headers: " + item.getKey() + " ==>" + item.getValue());
}
System.out.println(" -------------------------------");
Method method = resourceInfo.getResourceMethod();
//Access denied for all
if(method.isAnnotationPresent(DenyAll.class)) {
requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access blocked for all users !!").build());
return;
}
//Access allowed for all
if( method.isAnnotationPresent(PermitAll.class)) {
// no control ...
return;
}
// Validate the Authorization header
// Validate the Authorization header data Model "Yota userId:token"
if (!isTokenBasedAuthentication(authorizationHeader)) {
System.out.println("REJECTED unauthorized: " + requestContext.getUriInfo().getPath());
abortWithUnauthorized(requestContext);
return;
}
// Extract the token from the Authorization header
// Extract the token from the Authorization header (Remove "Yota ")
String token = authorizationHeader.substring(AUTHENTICATION_SCHEME.length()).trim();
System.out.println("token: " + token);
User user = null;
try {
user = validateToken(token);
@ -91,56 +111,36 @@ public class AuthenticationFilter implements ContainerRequestFilter {
if (user == null) {
abortWithUnauthorized(requestContext);
}
// create the security context model:
String scheme = requestContext.getUriInfo().getRequestUri().getScheme();
requestContext.setSecurityContext(new MySecurityContext(user, scheme));
System.out.println("Get local user : " + user);
}
/*
//Verify user access
if(method.isAnnotationPresent(RolesAllowed.class))
{
MySecurityContext userContext = new MySecurityContext(user, scheme);
// retrieve the allowed right:
RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class);
Set<String> rolesSet = new HashSet<String>(Arrays.asList(rolesAnnotation.value()));
List<String> roles = Arrays.asList(rolesAnnotation.value());
// check if the user have the right:
boolean haveRight = false;
for (String role : roles) {
if (userContext.isUserInRole(role)) {
haveRight = true;
break;
}
}
//Is user valid?
if( ! isUserAllowed(username, password, rolesSet))
{
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED)
.entity("You cannot access this resource").build(););
if( ! haveRight) {
System.out.println("REJECTED not enought right : " + requestContext.getUriInfo().getPath() + " require: " + roles);
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("Not enought RIGHT !!!").build());
return;
}
requestContext.setSecurityContext(userContext);
System.out.println("Get local user : " + user);
}
}
}
private boolean isUserAllowed(final String username, final String password, final Set<String> rolesSet)
{
boolean isAllowed = false;
//Step 1. Fetch password from database and match with password in argument
//If both match then get the defined role for user from database and continue; else return isAllowed [false]
//Access the database and do this part yourself
//String userRole = userMgr.getUserRole(username);
if(username.equals("howtodoinjava") && password.equals("password"))
{
String userRole = "ADMIN";
//Step 2. Verify user role
if(rolesSet.contains(userRole))
{
isAllowed = true;
}
}
return isAllowed;
}
*/
private boolean isTokenBasedAuthentication(String authorizationHeader) {
// Check if the Authorization header is valid
// It must not be null and must be prefixed with "Bearer" plus a whitespace
// The authentication scheme comparison must be case-insensitive
return authorizationHeader != null && authorizationHeader.toLowerCase()
.startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " ");
return authorizationHeader != null && authorizationHeader.toLowerCase().startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " ");
}
private void abortWithUnauthorized(ContainerRequestContext requestContext) {
@ -150,7 +150,7 @@ return isAllowed;
requestContext.abortWith(
Response.status(Response.Status.UNAUTHORIZED)
.header(HttpHeaders.WWW_AUTHENTICATE,
AUTHENTICATION_SCHEME + " realm=\"" + REALM + "\"")
AUTHENTICATION_SCHEME + " 215:asdfglkjsqdfgsd4fg56sd4fg23d45fg6sd81fg35sdf4g6d53s4fg3s2d41fg")
.build());
}
@ -165,6 +165,7 @@ return isAllowed;
String token = value[1];
UserSmall userOAuth = UserDB.getUserOAuth(user, token);
System.out.println("Get local userOAuth : " + userOAuth);
// TODO: Set here the way of the default create user or need to have right to access on this website...
return UserDB.getUserOrCreate(userOAuth);
}
}

View File

@ -28,7 +28,8 @@ class MySecurityContext implements SecurityContext {
return contextPrincipale.user.admin == true;
}
if (role.contentEquals("USER")) {
return contextPrincipale.user.admin == false;
// if not an admin, this is a user...
return true; //contextPrincipale.user.admin == false;
}
return false;
}

View File

@ -10,6 +10,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@NameBinding
@Retention(RUNTIME)
@Target({TYPE, METHOD})
public @interface Secured {
@Target({METHOD})
public @interface PermitTokenInURI {
}

View File

@ -4,13 +4,12 @@ import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.karideo.ConfigVariable;
import org.kar.karideo.GenericContext;
import org.kar.karideo.Secured;
import org.kar.karideo.PermitTokenInURI;
import org.kar.karideo.WebLauncher;
import org.kar.karideo.db.DBEntry;
import org.kar.karideo.model.Data;
import org.kar.karideo.model.DataSmall;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.imageio.ImageIO;
import javax.ws.rs.*;
@ -39,7 +38,6 @@ import java.util.Date;
// https://gist.github.com/aitoroses/4f7a2b197b732a6a691d
@Path("/data")
@PermitAll
@Produces({MediaType.APPLICATION_JSON})
public class DataResource {
private final static int CHUNK_SIZE = 1024 * 1024; // 1MB chunks
@ -325,11 +323,10 @@ public class DataResource {
return null;
}
//@Secured
@POST
@Path("/upload/")
@Consumes({MediaType.MULTIPART_FORM_DATA})
//@RolesAllowed("USER")
@RolesAllowed("ADMIN")
public Response uploadFile(@Context SecurityContext sc, @FormDataParam("file") InputStream fileInputStream, @FormDataParam("file") FormDataContentDisposition fileMetaData) {
GenericContext gc = (GenericContext) sc.getUserPrincipal();
System.out.println("===================================================");
@ -348,10 +345,10 @@ public class DataResource {
//return null;
}
//@Secured
@GET
@Path("{id}")
//@RolesAllowed("USER")
@PermitTokenInURI
@RolesAllowed("USER")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response retriveDataId(@Context SecurityContext sc, @HeaderParam("Range") String range, @PathParam("id") Long id) throws Exception {
GenericContext gc = (GenericContext) sc.getUserPrincipal();
@ -367,10 +364,11 @@ public class DataResource {
}
return buildStream(ConfigVariable.getMediaDataFolder() + File.separator + id + File.separator + "data", range, value.mimeType);
}
@Secured
@GET
@Path("thumbnail/{id}")
@RolesAllowed("USER")
@PermitTokenInURI
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response retriveDataThumbnailId(@Context SecurityContext sc, @HeaderParam("Range") String range, @PathParam("id") Long id) throws Exception {
GenericContext gc = (GenericContext) sc.getUserPrincipal();
@ -417,7 +415,8 @@ public class DataResource {
//@Secured
@GET
@Path("{id}/{name}")
//@RolesAllowed("USER")
@PermitTokenInURI
@RolesAllowed("USER")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response retriveDataFull(@Context SecurityContext sc, @HeaderParam("Range") String range, @PathParam("id") Long id, @PathParam("name") String name) throws Exception {
GenericContext gc = (GenericContext) sc.getUserPrincipal();

View File

@ -5,6 +5,7 @@ import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.karideo.model.NodeSmall;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@ -12,13 +13,13 @@ import java.io.InputStream;
import java.util.List;
@Path("/season")
@PermitAll
@Produces({MediaType.APPLICATION_JSON})
public class SeasonResource {
private static final String typeInNode = "SEASON";
@GET
@Path("{id}")
@RolesAllowed("USER")
public static NodeSmall getWithId(@PathParam("id") Long id) {
return NodeInterface.getWithId(typeInNode, id);
}
@ -32,12 +33,14 @@ public class SeasonResource {
}
@GET
@RolesAllowed("USER")
public List<NodeSmall> get() {
return NodeInterface.get(typeInNode);
}
@PUT
@Path("{id}")
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Response put(@PathParam("id") Long id, String jsonRequest) {
return NodeInterface.put(typeInNode, id, jsonRequest);
@ -45,12 +48,14 @@ public class SeasonResource {
@DELETE
@Path("{id}")
@RolesAllowed("ADMIN")
public Response delete(@PathParam("id") Long id) {
return NodeInterface.delete(typeInNode, id);
}
@POST
@Path("{id}/add_cover")
@RolesAllowed("ADMIN")
@Consumes({MediaType.MULTIPART_FORM_DATA})
public Response uploadCover(@PathParam("id") Long id,
@FormDataParam("fileName") String fileName,
@ -61,6 +66,7 @@ public class SeasonResource {
}
@GET
@Path("{id}/rm_cover/{cover_id}")
@RolesAllowed("ADMIN")
public Response removeCover(@PathParam("id") Long nodeId, @PathParam("cover_id") Long coverId) {
return NodeInterface.removeCover(typeInNode, nodeId, coverId);
}

View File

@ -5,6 +5,7 @@ import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.karideo.model.NodeSmall;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@ -12,13 +13,13 @@ import java.io.InputStream;
import java.util.List;
@Path("/series")
@PermitAll
@Produces({MediaType.APPLICATION_JSON})
public class SeriesResource {
private static final String typeInNode = "SERIES";
@GET
@Path("{id}")
@RolesAllowed("USER")
public static NodeSmall getWithId(@PathParam("id") Long id) {
return NodeInterface.getWithId(typeInNode, id);
}
@ -33,12 +34,14 @@ public class SeriesResource {
}
@GET
@RolesAllowed("USER")
public List<NodeSmall> get() {
return NodeInterface.get(typeInNode);
}
@PUT
@Path("{id}")
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Response put(@PathParam("id") Long id, String jsonRequest) {
return NodeInterface.put(typeInNode, id, jsonRequest);
@ -46,12 +49,14 @@ public class SeriesResource {
@DELETE
@Path("{id}")
@RolesAllowed("ADMIN")
public Response delete(@PathParam("id") Long id) {
return NodeInterface.delete(typeInNode, id);
}
@POST
@Path("{id}/add_cover")
@RolesAllowed("ADMIN")
@Consumes({MediaType.MULTIPART_FORM_DATA})
public Response uploadCover(@PathParam("id") Long id,
@FormDataParam("fileName") String fileName,
@ -63,6 +68,7 @@ public class SeriesResource {
@GET
@Path("{id}/rm_cover/{coverId}")
@RolesAllowed("ADMIN")
public Response removeCover(@PathParam("id") Long nodeId, @PathParam("coverId") Long coverId) {
return NodeInterface.removeCover(typeInNode, nodeId, coverId);
}

View File

@ -5,6 +5,7 @@ import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.karideo.model.NodeSmall;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@ -12,13 +13,13 @@ import java.io.InputStream;
import java.util.List;
@Path("/type")
@PermitAll
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public class TypeResource {
private static final String typeInNode = "TYPE";
@GET
@Path("{id}")
@RolesAllowed("USER")
public static NodeSmall getWithId(@PathParam("id") Long id) {
return NodeInterface.getWithId(typeInNode, id);
}
@ -28,13 +29,14 @@ public class TypeResource {
}
@GET
@PermitAll
@RolesAllowed("USER")
public List<NodeSmall> get() {
return NodeInterface.get(typeInNode);
}
@PUT
@Path("{id}")
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Response put(@PathParam("id") Long id, String jsonRequest) {
return NodeInterface.put(typeInNode, id, jsonRequest);
@ -42,12 +44,14 @@ public class TypeResource {
@DELETE
@Path("{id}")
@RolesAllowed("ADMIN")
public Response delete(@PathParam("id") Long id) {
return NodeInterface.delete(typeInNode, id);
}
@POST
@Path("{id}/add_cover")
@RolesAllowed("ADMIN")
@Consumes({MediaType.MULTIPART_FORM_DATA})
public Response uploadCover(@PathParam("id") Long id,
@FormDataParam("file_name") String file_name,
@ -58,6 +62,7 @@ public class TypeResource {
}
@GET
@Path("{id}/rm_cover/{cover_id}")
@RolesAllowed("ADMIN")
public Response removeCover(@PathParam("id") Long nodeId, @PathParam("cover_id") Long coverId) {
return NodeInterface.removeCover(typeInNode, nodeId, coverId);
}

View File

@ -5,6 +5,7 @@ import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.karideo.model.NodeSmall;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@ -12,13 +13,13 @@ import java.io.InputStream;
import java.util.List;
@Path("/universe")
@PermitAll
@Produces({MediaType.APPLICATION_JSON})
public class UniverseResource {
private static final String typeInNode = "UNIVERSE";
@GET
@Path("{id}")
@RolesAllowed("USER")
public static NodeSmall getWithId(@PathParam("id") Long id) {
return NodeInterface.getWithId(typeInNode, id);
}
@ -32,12 +33,14 @@ public class UniverseResource {
}
@GET
@RolesAllowed("USER")
public List<NodeSmall> get() {
return NodeInterface.get(typeInNode);
}
@PUT
@Path("{id}")
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Response put(@PathParam("id") Long id, String jsonRequest) {
return NodeInterface.put(typeInNode, id, jsonRequest);
@ -45,12 +48,14 @@ public class UniverseResource {
@DELETE
@Path("{id}")
@RolesAllowed("ADMIN")
public Response delete(@PathParam("id") Long id) {
return NodeInterface.delete(typeInNode, id);
}
@POST
@Path("{id}/add_cover")
@RolesAllowed("ADMIN")
@Consumes({MediaType.MULTIPART_FORM_DATA})
public Response uploadCover(@PathParam("id") Long id,
@FormDataParam("fileName") String fileName,
@ -61,6 +66,7 @@ public class UniverseResource {
}
@GET
@Path("{id}/rm_cover/{cover_id}")
@RolesAllowed("ADMIN")
public Response removeCover(@PathParam("id") Long nodeId, @PathParam("cover_id") Long coverId) {
return NodeInterface.removeCover(typeInNode, nodeId, coverId);
}

View File

@ -1,7 +1,6 @@
package org.kar.karideo.api;
import org.kar.karideo.GenericContext;
import org.kar.karideo.Secured;
import org.kar.karideo.UserDB;
import org.kar.karideo.WebLauncher;
import org.kar.karideo.db.DBEntry;
@ -29,7 +28,6 @@ import java.util.Random;
@Path("/users")
@PermitAll
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public class UserResource {
@ -62,6 +60,7 @@ public class UserResource {
*/
// curl http://localhost:9993/api/users
@GET
@RolesAllowed("ADMIN")
public List<UserExtern> getUsers() {
System.out.println("getUsers");
DBEntry entry = new DBEntry(WebLauncher.dbConfig);
@ -94,10 +93,9 @@ public class UserResource {
}
*/
// curl http://localhost:9993/api/users/3
@Secured
@GET
@Path("{id}")
@RolesAllowed("USER")
@RolesAllowed("ADMIN")
public UserExtern getUsers(@Context SecurityContext sc, @PathParam("id") long userId) {
System.out.println("getUser " + userId);
GenericContext gc = (GenericContext) sc.getUserPrincipal();
@ -118,7 +116,6 @@ public class UserResource {
}
*/
// curl http://localhost:9993/api/users/3
@Secured
@GET
@Path("me")
@RolesAllowed("USER")
@ -133,6 +130,7 @@ public class UserResource {
// curl -d '{"id":3,"login":"HeeroYui","password":"bouloued","email":"yui.heero@gmail.com","emailValidate":0,"newEmail":null,"authorisationLevel":"ADMIN"}' -H "Content-Type: application/json" -X POST http://localhost:9993/api/users
@POST
@RolesAllowed("ADMIN")
public Response createUser(User user) {
System.out.println("getUser " + user);
/*
@ -160,6 +158,7 @@ public class UserResource {
@GET
@Path("/check_login")
@PermitAll
public Response checkLogin(@QueryParam("login") String login) {
System.out.println("checkLogin: " + login);
@ -184,8 +183,10 @@ public class UserResource {
return Response.status(520).build();
}
// TODO: for more security we need to hash the email when sended... or remove thios API !!!
@GET
@Path("/check_email")
@PermitAll
public Response checkEmail(@QueryParam("email") String email) {
System.out.println("checkEmail: " + email);

View File

@ -11,6 +11,7 @@ import org.kar.karideo.model.MediaSmall;
import org.kar.karideo.model.NodeSmall;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@ -24,7 +25,6 @@ import java.util.ArrayList;
import java.util.List;
@Path("/video")
@PermitAll
@Produces({MediaType.APPLICATION_JSON})
public class VideoResource {
// UPDATE `node` SET `type` = "SEASON" WHERE `type` = "SAISON"
@ -32,6 +32,7 @@ public class VideoResource {
// UPDATE `node` SET `type` = "SERIES" WHERE `type` = "SERIE"
@GET
@RolesAllowed("USER")
public List<MediaSmall> get() {
System.out.println("VIDEO get");
DBEntry entry = new DBEntry(WebLauncher.dbConfig);
@ -74,6 +75,7 @@ public class VideoResource {
@GET
@Path("{id}")
@RolesAllowed("USER")
public MediaSmall get(@PathParam("id") Long id) {
System.out.println("VIDEO get " + id);
DBEntry entry = new DBEntry(WebLauncher.dbConfig);
@ -120,6 +122,7 @@ public class VideoResource {
@PUT
@Path("{id}")
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Response put(@PathParam("id") Long id, String jsonRequest) {
ObjectMapper mapper = new ObjectMapper();
@ -261,6 +264,7 @@ public class VideoResource {
@POST
@Path("/upload/")
@RolesAllowed("ADMIN")
@Consumes({MediaType.MULTIPART_FORM_DATA})
public Response uploadFile(@FormDataParam("fileName") String fileName,
@FormDataParam("universe") String universe,
@ -431,6 +435,7 @@ public class VideoResource {
}
@POST
@Path("{id}/add_cover")
@RolesAllowed("ADMIN")
@Consumes({MediaType.MULTIPART_FORM_DATA})
public Response uploadCover(@PathParam("id") Long id,
@FormDataParam("fileName") String fileName,
@ -527,6 +532,7 @@ public class VideoResource {
}
@GET
@Path("{id}/rm_cover/{cover_id}")
@RolesAllowed("ADMIN")
public Response removeCover(@PathParam("id") Long mediaId, @PathParam("cover_id") Long coverId) {
DBEntry entry = new DBEntry(WebLauncher.dbConfig);
String query = "UPDATE `cover_link_media` SET `modify_date`=now(3), `deleted`=true WHERE `media_id` = ? AND `data_id` = ?";
@ -547,6 +553,7 @@ public class VideoResource {
@DELETE
@Path("{id}")
@RolesAllowed("ADMIN")
public Response delete(@PathParam("id") Long id) {
DBEntry entry = new DBEntry(WebLauncher.dbConfig);
String query = "UPDATE `media` SET `modify_date`=now(3), `deleted`=true WHERE `id` = ? and `deleted` = false ";