How to judge if a #PathVariable exists in request URL? - spring

I am trying to write an api permission filter on gateway. Tokens that do not carry with the specific roles should be prohibited from accessing resources. All the requests have been filtered effectively, except for apis that contains #PathVariable params. For example, an api with the URI /api/v1/query/{id}, the param id might be a uuid in some cases, and may be a long value in other cases.
Are there any better ways except adding more and more Regex patterns? The overall goal of gateway is to consume as less time as possible.

I came up with a proper solution anyway. The #PathVariable in all the projects are located in the last or the last two parts in the URL. e.g. /api/v1/data/query/{uid}/{pid} or something like that. So we could eliminate that part using Apache Common's StringUtils#lastIndexOf() and StringUtils#substring().
To write the code for demonstration, import both Hutool and Commons-Lang3.
<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.5.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>
import cn.hutool.core.util.IdUtil;
import org.apache.commons.lang3.StringUtils;
public class StringDemo {
public static void main(String[] args) {
String url = "http://localhost:8080/api/v1/data/query/" + IdUtil.simpleUUID() + "/" + IdUtil.getSnowflake(1L, 16).nextId();
System.out.println(url);
int index = StringUtils.lastIndexOf(url, "/");
String subUrl = StringUtils.substring(url, 0, index);
System.out.println(subUrl);
int index2 = StringUtils.lastIndexOf(subUrl, "/");
String subOfSubUrl = StringUtils.substring(url, 0, index2);
System.out.println(subOfSubUrl);
}
}
The result is as follows:
http://localhost:8080/api/v1/data/query/19280769925f43d98b2af405579955ac/1356927788629626880
http://localhost:8080/api/v1/data/query/19280769925f43d98b2af405579955ac
http://localhost:8080/api/v1/data/query
By simplifying the uri to the simpliest, in my case is /api/v1/data/query, it is easy to write the related codes to check of roles.

Related

Number of items fetched per page gets reduced after some number of pages while fetching data from Azure cosmosDB using Spring boot

I have a Cosmos container containing about 24K documents. I am fetching these in pages of size 200 using spring boot. Here's the snippet for the same:
public void ingest() {
var page = itemProjectionHeimdallRepository.findAll(PageRequest.ofSize(200));
totalItems = (int) page.getTotalElements();
int pageCount = 0;
while (pageCount < totalPageCount) {
process(page);
pageCount++;
if (pageCount < totalPageCount) {
page = itemProjectionHeimdallRepository.findAll(page.nextPageable());
}
}
}
With this, I get proper number of records(200) per page upto 59 pages, but thereafter I keep getting only 25 records per page and the remaining records are lost. Which means after processing all the pages I don't have all the 24K records with me.
The source Cosmos container from where I am reading is provisioned with 3000 RU/s. Not sure what's going wrong.
Here's the dependency I am using in my POM for Spring data cosmos:
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>spring-data-cosmosdb</artifactId>
<version>2.3.0</version>
</dependency>

Read Full Query parameter String in Spring Boot

Is there a way for me to read the entire query string in the GET API? Since there can be a variable number of parameters I am already looking at using this
public void createUser(#RequestParam(required=false) Map<String,String> qparams) {
}
But I want to read the entire query string as well.
The reason being one of the parameters here is an HMAC which is calculated on the entire string. and we are using that HMAC for cross verification.
We have deep integration with third-party software. The issue here is that the third-party software can make a change to their API at any point in time.
Here's how you can do it.
#GetMapping("/test1")
void endpoint1(HttpServletRequest req) {
var qs = req.getQueryString() //returns the entire string
qs.split("&") //split to get the individual parameters
}

Istio + Jaeger tracing with gRPC calls using Spring Boot

I'm using this library (grpc-spring-boot-starter) so I can have gRPC capabilities in a Spring Boot app.
I want to know how to properly integrate this with Istio + Jaeger tracing.
I need to know what are the needed dependencies for this to happen.
I have two (2) apps, one serves as the gRPC client and one serves as the gRPC server,
The expectation is that the trace between the gRPC client and the gRPC server must be reflected in Jaeger. But it's not happening.
I am inside a Kubernetes cluster that has Istio.
What really happens is HTTP request -> Envoy sidecar -> gRPC Client's Spring Boot #RestController -> get the Headers from HTTP request -> copy those to gRPC call before making the call -> gRPC Service.
How can I make the gRPC client <-> gRPC Service trace shown to Jaeger?
Are there any dependencies that needs to be imported?
Right now I have:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!-- Used together with Sleuth to be able to trace gRPC calls. -->
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-grpc</artifactId>
</dependency>
<!-- Add'l dependency for Istio + Jaeger gRPC tracing -->
<dependency>
<groupId>io.jaegertracing</groupId>
<artifactId>jaeger-client</artifactId>
</dependency>
I also have done something like this to "propagate the headers":
private void propagateHeaders(HttpHeaders headers) {
// TODO: adding these string values to a final / constant.
// get these tracing headers from HTTP request, (coming from Envoy)
String xRequestIdFromHttpHeader = headers.getFirst("x-request-id");
String xB3TraceIdFromHttpHeader = headers.getFirst("x-b3-traceid");
String xB3SpanIdFromHttpHeader = headers.getFirst("x-b3-spanid");
String xB3ParentSpanIdFromHttpHeader = headers.getFirst("x-b3-parentspanid");
String xB3SampledFromHttpHeader = headers.getFirst("x-b3-sampled");
String xB3FlagsFromHttpHeader = headers.getFirst("x-b3-flags");
String xOtSpanContextFromHttpHeader = headers.getFirst("x-ot-span-context");
// create a custom gRPC header, they call it Metadata
Metadata xRequestIdMetadata = new Metadata();
Metadata xB3TraceIdMetadata = new Metadata();
Metadata xB3SpanIdMetadata = new Metadata();
Metadata xB3ParentSpanIdMetadata = new Metadata();
Metadata xB3SampledMetadata = new Metadata();
// TODO: refactor. putting to List<> and using foreach to attach this one.
// assign value of the x-request-id Metadata to the value of the HTTP Header
xRequestIdMetadata.put(Metadata.Key.of("x-request-id", Metadata.ASCII_STRING_MARSHALLER), xRequestIdFromHttpHeader);
xB3TraceIdMetadata.put(Metadata.Key.of("x-b3-traceid", Metadata.ASCII_STRING_MARSHALLER), xB3TraceIdFromHttpHeader);
xB3SpanIdMetadata.put(Metadata.Key.of("x-b3-spanid", Metadata.ASCII_STRING_MARSHALLER), xB3SpanIdFromHttpHeader);
xB3ParentSpanIdMetadata.put(Metadata.Key.of("x-b3-parentspanid", Metadata.ASCII_STRING_MARSHALLER), xB3ParentSpanIdFromHttpHeader);
xB3SampledMetadata.put(Metadata.Key.of("x-b3-sampled", Metadata.ASCII_STRING_MARSHALLER), xB3SampledFromHttpHeader);
// TODO: refactor. putting to List<> and using foreach to attach this one.
// use MetadataUtils to attach that new Metadata to our stub before requesting to our gRPC Server via gRPC
greetingServiceStub = MetadataUtils.attachHeaders(greetingServiceStub, xRequestIdMetadata);
greetingServiceStub = MetadataUtils.attachHeaders(greetingServiceStub, xB3TraceIdMetadata);
greetingServiceStub = MetadataUtils.attachHeaders(greetingServiceStub, xB3SpanIdMetadata);
greetingServiceStub = MetadataUtils.attachHeaders(greetingServiceStub, xB3ParentSpanIdMetadata);
greetingServiceStub = MetadataUtils.attachHeaders(greetingServiceStub, xB3SampledMetadata);
}
But it doesn't seem to work..
What you did is for http 1.x, and it doesn't work for http2/grpc. Please dive into grpc impl in springboot doc.

ADAL invalid redirect uri

Note: i'm using an experimental pre-release of microsoft's latest adal
I'm trying to get my identity providers to work on the mobile applications. So far I've been able to load my identity providers and managed to get the login page to show (except for facebook).
The problem is that whenever i actually try to login i'm getting some error in the form off "invalid redirect uri".
Google, for instance, will say: "The redirect URI in the request: https://login.microsoftonline.com/... did not match a registered redirect URI.
Facebook will show: "Given URL is not allowed by the application configuration: One or more of the given URLs is not allowed by the App's settings. It must match the website URL or Canvas URL, or the domain must be a subdomain of one of the App's domains."
As far as I understand you don't actually need to register the mobile application anymore with the different identity providers because Azure sits in between you and them. Azure handles the connection, gets your token and uses it to identify you. It should then return a set of "azure tokens" to you.
To my knowledge the used redirect URI is registered on the portal since I'm able to load the identity providers in the first place?
Not to mention it seems to be a default URL that's used by many applications: urn:ietf:wg:oauth:2.0:oob which simply tells it to return it to some none-browser based application?
This is the code i'm using to actually do the login/signup:
private static String AUTHORITY_URL = "https://login.microsoftonline.com/<directory>/oauth2/authorize/";
private static String CLIENT_ID = "my_client_id";
private static String[] SCOPES = { "my_client_id" };
private static String[] ADDITIONAL_SCOPES = { "" };
private static String REDIRECT_URL = "urn:ietf:wg:oauth:2.0:oob";
private static String CORRELATION_ID = "";
private static String USER_HINT = "";
private static String EXTRA_QP = "nux=1";
private static String FB_POLICY = "B2C_1_<your policy>";
private static String EMAIL_SIGNIN_POLICY = "B2C_1_SignIn";
private static String EMAIL_SIGNUP_POLICY = "B2C_1_SignUp";
public async Task<AuthenticationResult> Login(IPlatformParameters parameters, bool isSignIn)
{
var authContext = new AuthenticationContext(AUTHORITY_URL, new TokenCache());
if (CORRELATION_ID != null &&
CORRELATION_ID.Trim().Length != 0)
{
authContext.CorrelationId = Guid.Parse(CORRELATION_ID);
}
String policy = "";
if (isSignIn)
policy = EMAIL_SIGNIN_POLICY;
else
policy = EMAIL_SIGNUP_POLICY;
return await authContext.AcquireTokenAsync(SCOPES, ADDITIONAL_SCOPES, CLIENT_ID, new Uri(REDIRECT_URL), parameters, UserIdentifier.AnyUser, EXTRA_QP, policy);
}
microsoft's documentation isn't really helping because most are either empty (they're literally not yet typed out) or it's some help topic from over a year ago. This stuff is pretty new so documentation seems to be hard to come by.
So, dear people of stackoverflow, what am I missing? Why is it saying that the redirect urI is invalid when it's been registered on the azure web portal? And if the redirect URI is invalid why can I retrieve the identity providers in the first place?
why is it that i can't seem to find solutions after hours of searching, yet when i post a question here i somehow find the answer within minutes...
It was quite a stupid mistake at that, one of my collegues had sent me the wrong authority url.
The funny thing is that it was correct "enough" to load the identity providers we had installed on the portal but not correct enough to handle actually signing in or up.
I initially used:
https://login.microsoftonline.com/<tenant_id>/oauth2/authorize/
where it should have been:
https://login.microsoftonline.com/<tenant_id>/oauth2/v2.0/authorize
You see that little "v2.0"? yeah that little bastard is what caused all the pain...

Jersey Response with JSONP

I am creating a Jersey API service to be called from browsers and java clients. The code works when called from the same domain, but when called from some other domain it did not work, so I created a tried to wrap the responseJson String with JSONWithPadding. It is still sending the normal response back and not the response I am looking. The implementation of service is Jersey ( Maven Path Below:
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-servlet</artifactId>
<version>${jersey.version}</version>
</dependency>
Sample Code for
#Path("/v1/sample")
#Produces({"application/json"})
public class SampleService
{
#GET
#Path("/service1")
#Produces({"application/json"})
public Response notWorking(#Context UriInfo uriInfo)
{
String result = "{\"name\":\"John Johnson\",\"street\":\"Oslo West 16\",\"phone\":\"555 1234567\"}";
String callbackStr = (String)uriInfo.getQueryParameters().getFirst("callback");
System.out.println("callbackStr ="+callbackStr);
JSONWithPadding paddedJson = new JSONWithPadding(result, callbackStr);
return Response.status(200).entity(paddedJson).build();
}
}
call 1: http://localhost:8080/myapi/v1/sample/service1
Response: {"name":"John Johnson","street":"Oslo West 16","phone":"555 1234567"}
call 2: http://localhost:8080/myapi/v1/sample/service1?callback=?
Response: {"name":"John Johnson","street":"Oslo West 16","phone":"555 1234567"}
In call 2: Response I am looking for is
?({"name":"John Johnson","street":"Oslo West 16","phone":"555 1234567"})
Surely I am missing something, but could not figure out.
There was several things to work on in your example.
JSONP needs one of the following response contentTypes: "application/javascript", "application/x-javascript", "application/ecmascript", "text/javascript", "text/x-javascript", "text/ecmascript", "text/jscript". It doesn't work with "application/json".
You have to make sure that the name of the QueryParam recieved is called "callbackStr". It is usually called "callback".
You can return directly the JSONWithPadding object.
#GET
#Path("/service1")
#Produces({"application/javascript"})
public JSONWithPadding nowItWorks(#QueryParam("callbackStr") String callbackStr, #Context UriInfo uriInfo)
{
String result = "{\"name\":\"John Johnson\",\"street\":\"Oslo West 16\",\"phone\":\"555 1234567\"}";
System.out.println("callbackStr ="+callbackStr);
return new JSONWithPadding(result, callbackStr);
}
Hope this helps.

Resources