Skip to content

Commit

Permalink
more code
Browse files Browse the repository at this point in the history
  • Loading branch information
sangupta committed Nov 4, 2020
1 parent 57d8a6c commit 06b3634
Show file tree
Hide file tree
Showing 12 changed files with 307 additions and 86 deletions.
6 changes: 6 additions & 0 deletions migration.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
* Moved jerry.ds.bitarray to jerry.lang
* Moved jerry.ds.refresh to jerry.refresh
* Moved jerry.ds.mutable to jerry.lang
* Moved jerry.ds.bound to jerry.lang
* Remove DumpUtils in favor of GsonUtils.toJson()
*
34 changes: 34 additions & 0 deletions src/main/java/com/sangupta/jerry/http/TenantDetectionStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.sangupta.jerry.http;

/**
* Enumeration for different strategies that help define how the tenant for an
* incoming request needs to be identified.
*
* @author sangupta
*
*/
public enum TenantDetectionStrategy {

/**
* HTTP request parameter defines the tenant
*/
REQUEST_PARAM,

/**
* HTTP request header defines the tenant
*/
REQUEST_HTTP_HEADER,

/**
* A path variable that shall be used to detect the tenant
*/
PATH_VARIABLE,

/**
* In this case the user authentication system needs to detect
* and set the tenant based on user's entitlements.
*
*/
USER_AUTH;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package com.sangupta.jerry.http.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import com.sangupta.jerry.http.TenantDetectionStrategy;
import com.sangupta.jerry.security.SecurityContext;
import com.sangupta.jerry.util.AssertUtils;

/**
* Servlet filter that helps identify the incoming tenant.
*
* @author sangupta
*
*/
public class TenantDetectionServletFilter implements Filter {

protected TenantDetectionStrategy tenantDetectionStrategy = TenantDetectionStrategy.REQUEST_PARAM;

protected String tenancyRequestParam = "tenantID";

protected String tenancyHttpHeader = "X-Tenant-ID";

public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest hsr = (HttpServletRequest) request;

// clear existing tenant
SecurityContext.clear();

// check if we have a strategy to find out tenant
if (this.tenantDetectionStrategy == null) {
// nothing to do
chain.doFilter(request, response);
return;
}

// detect tenant based on strategy
switch (this.tenantDetectionStrategy) {
case PATH_VARIABLE:
break;

case REQUEST_HTTP_HEADER:
this.setTenantFromHttpHeader(hsr);
break;

case REQUEST_PARAM:
this.setTenantFromRequestParam(hsr);
break;

case USER_AUTH:
break;

default:
break;
}

// complete the request
try {
chain.doFilter(request, response);
} finally {
// clean the tenant again
SecurityContext.clear();
}
}

/**
* Find the tenant using chosen request http header.
*
* @param hsr the {@link HttpServletRequest}
*/
protected void setTenantFromHttpHeader(HttpServletRequest hsr) {
String value = hsr.getHeader(this.tenancyHttpHeader);
if (AssertUtils.isEmpty(value)) {
// do nothing
return;
}

SecurityContext.setTenant(value);
}

/**
* Find the tenant using chosen request parameter.
*
* @param hsr the {@link HttpServletRequest}
*/
protected void setTenantFromRequestParam(HttpServletRequest hsr) {
String value = hsr.getParameter(this.tenancyRequestParam);
if (AssertUtils.isEmpty(value)) {
// do nothing
return;
}

SecurityContext.setTenant(value);
}

@Override
public void destroy() {

}

/**
*
* @param tenantDetectionStrategy
*/
public void setTenantDetectionStrategy(TenantDetectionStrategy tenantDetectionStrategy) {
this.tenantDetectionStrategy = tenantDetectionStrategy;
}

/**
*
* @param tenancyHttpHeader
*/
public void setTenancyHttpHeader(String tenancyHttpHeader) {
this.tenancyHttpHeader = tenancyHttpHeader;
}

/**
*
* @param tenancyRequestParam
*/
public void setTenancyRequestParam(String tenancyRequestParam) {
this.tenancyRequestParam = tenancyRequestParam;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@
import java.util.Arrays;

import com.sangupta.jerry.consume.GenericConsumer;
import com.sangupta.jerry.util.CheckUtils;
import com.sangupta.jerry.util.Check;
import com.sangupta.jerry.util.IOUtils;

/**
* A simple file reader that read a given file chunk by chunk and
* A simple file reader that reads a given file chunk by chunk and
* pass it to a {@link GenericConsumer} of bytes so that the file
* can be consumed.
*
Expand All @@ -48,7 +48,7 @@ public class FileByteChunkConsumer {
private final GenericConsumer<byte[]> consumer;

public FileByteChunkConsumer(File file, GenericConsumer<byte[]> consumer) {
CheckUtils.checkReadableFile(file);
Check.checkReadableFile(file);

if(consumer == null) {
throw new IllegalArgumentException("Consumer cannot be null");
Expand Down
73 changes: 31 additions & 42 deletions src/main/java/com/sangupta/jerry/security/SecurityContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,15 @@ public class SecurityContext {
* Thread local instance to store the principal per thread
*/
private static final ThreadLocal<UserAwarePrincipal> PRINCIPAL_HOLDER = new ThreadLocal<>();

private static final ThreadLocal<String> TOKEN_HOLDER = new ThreadLocal<>();

/**
* Thread local instance to store the tenant per thread
*/
private static final ThreadLocal<String> TENANT_HOLDER = new ThreadLocal<>();

/**
* The anonymous user account
*/
private static UserAwarePrincipal ANONYMOUS_USER_PRINCIPAL = null;

/**
* Method that sets up the anonymous user account. If no user is assigned to the
* request, the anonymous user account will be returned.
*
* @param principal the {@link Principal} instance to use for anonymous users
*/
public static void setupAnonymousUserAccount(UserAwarePrincipal principal) {
ANONYMOUS_USER_PRINCIPAL = principal;
}
private static Class<? extends UserAwarePrincipal> userClass = null;

/**
* Setup a principal in this context.
Expand All @@ -81,6 +70,10 @@ public static void setPrincipal(UserAwarePrincipal principal) {
public static void setTenant(String tenant) {
TENANT_HOLDER.set(tenant);
}

public static void setUserClass(Class<? extends UserAwarePrincipal> userClass) {
SecurityContext.userClass = userClass;
}

/**
* Return the currently set principal
Expand All @@ -89,12 +82,29 @@ public static void setTenant(String tenant) {
*
*/
public static UserAwarePrincipal getPrincipal() {
UserAwarePrincipal principal = PRINCIPAL_HOLDER.get();
if (principal != null) {
return principal;
}

return ANONYMOUS_USER_PRINCIPAL;
return PRINCIPAL_HOLDER.get();
}

@SuppressWarnings("unchecked")
public static <T> T getUser() {
if(SecurityContext.userClass == null) {
throw new IllegalStateException("Must set the user class via SecurityContext.setUserClass()");
}

UserAwarePrincipal principal = getPrincipal();
if(principal == null) {
return null;
}

return (T) SecurityContext.userClass.cast(principal);
}

public static String getUserToken() {
return TOKEN_HOLDER.get();
}

public static void setUserToken(String token) {
TOKEN_HOLDER.set(token);
}

/**
Expand Down Expand Up @@ -126,12 +136,6 @@ public static boolean isAnonymousUser() {
return true;
}

// the following == check is intentional for we want to compare
// object instances here, and not object values
if (principal == ANONYMOUS_USER_PRINCIPAL) {
return true;
}

return false;
}

Expand All @@ -155,28 +159,13 @@ public static boolean isSameTenant(String tenant) {
return current.equals(tenant);
}

/**
* Clear the security context of set principal value.
*
*/
public static void clearPrincipal() {
PRINCIPAL_HOLDER.remove();
}

/**
* Clear the security context of the set tenant value.
*
*/
public static void clearTenant() {
TENANT_HOLDER.remove();
}

/**
* Clear the security context of the set principal and tenant values.
*
*/
public static void clear() {
PRINCIPAL_HOLDER.remove();
TENANT_HOLDER.remove();
TOKEN_HOLDER.remove();
}
}
24 changes: 12 additions & 12 deletions src/main/java/com/sangupta/jerry/util/BitUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ protected BitUtils() throws InstantiationException {
* @return <code>true</code> if bit is set, <code>false</code> otherwise
*/
public static boolean isBitSet(int value, int bit) {
CheckUtils.checkArgument(bit >= LEAST_BIT, "Bit to set cannot be less than zero");
CheckUtils.checkArgument(bit <= MAX_BIT_INT, "Bit to set cannot be greater than " + MAX_BIT_INT);
Check.checkArgument(bit >= LEAST_BIT, "Bit to set cannot be less than zero");
Check.checkArgument(bit <= MAX_BIT_INT, "Bit to set cannot be greater than " + MAX_BIT_INT);
int mask = 1 << bit;
return (value & mask) != 0;
}
Expand All @@ -70,8 +70,8 @@ public static boolean isBitSet(int value, int bit) {
* @return the updated value after setting the bit
*/
public static int setBit(int value, int bit) {
CheckUtils.checkArgument(bit >= LEAST_BIT, "Bit to set cannot be less than zero");
CheckUtils.checkArgument(bit <= MAX_BIT_INT, "Bit to set cannot be greater than " + MAX_BIT_INT);
Check.checkArgument(bit >= LEAST_BIT, "Bit to set cannot be less than zero");
Check.checkArgument(bit <= MAX_BIT_INT, "Bit to set cannot be greater than " + MAX_BIT_INT);
int mask = 1 << bit;
return value | mask;
}
Expand All @@ -88,8 +88,8 @@ public static int setBit(int value, int bit) {
* @return the updated value after setting the bit
*/
public static int clearBit(int value, int bit) {
CheckUtils.checkArgument(bit >= LEAST_BIT, "Bit to set cannot be less than zero");
CheckUtils.checkArgument(bit <= MAX_BIT_INT, "Bit to set cannot be greater than " + MAX_BIT_INT);
Check.checkArgument(bit >= LEAST_BIT, "Bit to set cannot be less than zero");
Check.checkArgument(bit <= MAX_BIT_INT, "Bit to set cannot be greater than " + MAX_BIT_INT);
int mask = ~setBit(0, bit);
return value & mask;
}
Expand All @@ -107,8 +107,8 @@ public static int clearBit(int value, int bit) {
* otherwise
*/
public static boolean isBitSet(long value, int bit) {
CheckUtils.checkArgument(bit >= LEAST_BIT, "Bit to set cannot be less than zero");
CheckUtils.checkArgument(bit <= MAX_BIT_LONG, "Bit to set cannot be greater than " + MAX_BIT_LONG);
Check.checkArgument(bit >= LEAST_BIT, "Bit to set cannot be less than zero");
Check.checkArgument(bit <= MAX_BIT_LONG, "Bit to set cannot be greater than " + MAX_BIT_LONG);
long mask = 1l << bit;
return ((long) (value & mask)) != 0l;
}
Expand All @@ -125,8 +125,8 @@ public static boolean isBitSet(long value, int bit) {
* @return the updated value after setting the bit
*/
public static long setBit(long value, int bit) {
CheckUtils.checkArgument(bit >= LEAST_BIT, "Bit to set cannot be less than zero");
CheckUtils.checkArgument(bit <= MAX_BIT_LONG, "Bit to set cannot be greater than " + MAX_BIT_LONG);
Check.checkArgument(bit >= LEAST_BIT, "Bit to set cannot be less than zero");
Check.checkArgument(bit <= MAX_BIT_LONG, "Bit to set cannot be greater than " + MAX_BIT_LONG);
long mask = 1L << bit;
return value | mask;
}
Expand All @@ -143,8 +143,8 @@ public static long setBit(long value, int bit) {
* @return the updated value after setting the bit
*/
public static long clearBit(long value, int bit) {
CheckUtils.checkArgument(bit >= LEAST_BIT, "Bit to set cannot be less than zero");
CheckUtils.checkArgument(bit <= MAX_BIT_LONG, "Bit to set cannot be greater than " + MAX_BIT_LONG);
Check.checkArgument(bit >= LEAST_BIT, "Bit to set cannot be less than zero");
Check.checkArgument(bit <= MAX_BIT_LONG, "Bit to set cannot be greater than " + MAX_BIT_LONG);
long mask = ~setBit(0L, bit);
return value & mask;
}
Expand Down
Loading

0 comments on commit 06b3634

Please sign in to comment.