I'm trying to check callers to a method in Google Cloud Endpoint against a
whitelist. How to get the remote address of a client? (And how to get the request object?)
UPDATE: Thanks to #ikerlasaga:
#ApiMethod(name = "echo")
public Message echo(HttpServletRequest req, Message message, #Named("n") #Nullable Integer n) {
String remote = req.getRemoteAddr();
return doEcho(message, n);
}
From this post: Getting raw HTTP Data (Headers, Cookies, etc) in Google Cloud Endpoints
#ApiMethod(name = "echo")
public Message echo(HttpServletRequest req, Message message, #Named("n") #Nullable Integer n) {
String remote = req.getRemoteAddr();
return doEcho(message, n);
}
Thanks to #ikerlasaga
Related
I implement rate limiting in Spring Cloud Gateway (SCG). I get client IP address with below code
#Component
public class RemoteAddressKeyResolver implements KeyResolver {
#Override
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
}
My SCG is behind a proxy so it gets address of proxy not real client address. How can I get real client address?
I found a solution!. There is an implementation of RemoteAddressResolver is XForwardedRemoteAddressResolver. Just use it, don't need to implement logic again.
#Component
public class RemoteAddressKeyResolver implements KeyResolver {
#Override
public Mono<String> resolve(ServerWebExchange exchange) {
XForwardedRemoteAddressResolver resolver = XForwardedRemoteAddressResolver.maxTrustedIndex(1);
InetSocketAddress inetSocketAddress = resolver.resolve(exchange);
return Mono.just(inetSocketAddress.getAddress().getHostAddress());
}
}
That's all, so simple!
you could check your request headers key, like X-Forwarded-For (depends on your proxy settings)
The X-Forwarded-For (XFF) header is a de-facto standard header for identifying the originating IP address of a client connecting to a web server through an HTTP proxy or a load balancer.
getFirst will return the origin ip
exchange.getRequest().getHeaders().getFirst("X-Forwarded-For")
return exchange -> {
// String origin = exchange.getRequest().getHeaders().getFirst("X-Forwarded-For");
// if (origin == null)
// origin = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
// return Mono.just(origin);
XForwardedRemoteAddressResolver resolver = XForwardedRemoteAddressResolver.maxTrustedIndex(1);
InetSocketAddress inetSocketAddress = resolver.resolve(exchange);
logger.trace("inetSocketAddress {}", inetSocketAddress);
logger.trace(".getHostName() {}", inetSocketAddress.getHostName());
return Mono.just(inetSocketAddress.getHostName());
};
We have a Daemon application that uses the EWS API to access office365/Exchange server with basic authentication. I am trying to implement the Oauth2. There are a lot of documents. However, they are often out of date and caused more confusion. I followed this document https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-daemon-overview, which seems up-to-date. I did the following steps:
Register App
Document: https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-daemon-app-registration
- Registered a secret with application password in Azure AD, i.e. certificate is used. The generated secret is recorded.
- selected the “Accounts in this organizational directory only”.
- Requested API Permission for Application permissions for Exchange full_access_as_app and Mail.Read. Admin consent is granted.
Get Token
Document: https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-daemon-acquire-token?tabs=java
I prototyped to use Protocol to get token
POST /{tenant}/oauth2/v2.0/token HTTP/1.1
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id={myAppClientId}
&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
&client_secret={myAppSecret}
&grant_type=client_credentials
I got token with
{
"token_type": "Bearer",
"expires_in": 3599,
"ext_expires_in": 3599,
"access_token": "……thetoken…"
}
Call EWS API in my App
My App works with the Basic Authentication. I modified it by adding the Authorization header ("Authorization", "Bearer " + accessToken); Basially the prepareWebRequest() function is overriden by adding the Authorization header. Compared with a Basic Authentication case, the request has the additional Authorization header with the Bearer token.
For the same EWS API call that the Basic Authorization had worked, the response is 401 with
x-ms-diagnostics
2000003;reason="The audience claim value is invalid for current resource. Audience claim is 'https://graph.microsoft.com', request url is 'https://outlook.office365.com/EWS/Exchange.asmx' and resource type is 'Exchange'.";error_category="invalid_resource"
Researched in stackoverflow, people suggested to use the following as scope value to get token in step 2:
https://outlook.office365.com/full_access_as_app
https://outlook.office.com/Mail.Read
I tried and both returned “invalid_scope” error. It seems both worked before but not anymore. Following the working scope value format, I tried to use https://outlook.office.com/.default as scope value. I was able to get a token! However, when I use this token in EWS API to access the mailbox, I got 500 error instead of the 401.
What are the right things to do to make it work? What is the right Scope to access an office365 mail box?
More Code Snippets
This is a new class added for oauth2
package microsoft.exchange.webservices.data;
import java.util.Map;
public final class BearerTokenCredentials extends ExchangeCredentials {
private static final String BEARER_TOKEN_FORMAT_REGEX = "^[-._~+/A-Za-z0-9]+=*$";
private static final String AUTHORIZATION = "Authorization";
private static final String BEARER_AUTH_PREAMBLE = "Bearer ";
private String token;
public String getToken() {
return token;
}
public BearerTokenCredentials(String bearerToken) {
if (bearerToken == null) {
throw new IllegalArgumentException("Bearer token can not be null");
}
this.validateToken(bearerToken);
this.token = bearerToken;
}
protected void validateToken(String bearerToken) throws IllegalArgumentException {
if (!bearerToken.matches(BEARER_TOKEN_FORMAT_REGEX)) {
throw new IllegalArgumentException("Bearer token format is invalid.");
}
}
#Override
public void prepareWebRequest(HttpWebRequest request) {
Map<String, String> headersMap = request.getHeaders();
String bearerValue = BEARER_AUTH_PREAMBLE + token;
headersMap.put(AUTHORIZATION, bearerValue);
//headersMap.put("X-AnchorMailbox","esj_office365_imap#genesyslab.onmicrosoft.com");
request.setHeaders(headersMap);
}
}
Use the token to acceess EWS/Exchange ews-java-api 2.0-patched
ExchangeService service = new
ExchangeService(ExchangeVersion.Exchange2010_SP2); //version is
Exchange2010_SP2
service.setTraceEnabled(true);
BearerTokenCredentials credentials = new BearerTokenCredentials("thetoken");
service.setCredentials(credentials);
service.setUrl(new
URI(host));//https://outloook.office365.com/EWS/Exchange.asmx
try{
Folder.bind(service, WellKnownFolderName.Inbox);
}catch(Exception e)
{
//The remote server returned an error: (500)Internal Server Error
}
The code you use to connect to the Office365 Mailbox still needs to use EWS Impersonation eg
service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, mailboxName);
Where MailboxName is the Mailbox you want to connect to.
I have Jsoup code and successfully send request.Also this code work fine in hide/change 'X-Forwarded-For' Header data, but i cant hide/change Remote/System Ip Address.
Client Side Code:
Document doc = Jsoup.connect("http://192.168.XX.XX:XXXX/microFin/XXXX")
.header("X-Forwarded-For", "192.168.0.1").get();
Server Side Code:
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String authCredentials = request.getHeader("Authorization");
String pathInfo = request.getServletPath();/////api/auth
String ip = request.getHeader("X-Forwarded-For");
String ip11 = request.getRemoteAddr();
if (ip == null) {
ip = request.getRemoteAddr();
}
System.out.println("IP-ADDRESS::" + ip);//192.168.0.1
System.out.println("IP-ADDRESS::" + ip11);//actual ip ???
If any solution for change System Ip then please help me.
You can use a VPN service to hide the IP address of the client machine. There are several software ranging from premium to paid.
My software of preference is: TunnelBear Link
I use Spring integration for Web service message handling. Unfortunately the Message does not contains the sender IP Address. How can I get this information?
#Bean
public SimpleWebServiceInboundGateway myInboundGateway() {
SimpleWebServiceInboundGateway simpleWebServiceInboundGateway = new SimpleWebServiceInboundGateway();
simpleWebServiceInboundGateway.setRequestChannelName("testChannel");
simpleWebServiceInboundGateway.setReplyChannelName("testResponseChannel");
return simpleWebServiceInboundGateway;
}
#ServiceActivator(inputChannel = "testChannel", outputChannel = "testResponseChannel")
public Message getHeaders(Message message) {
// how can I reach the sender IP address here
return message;
}
The SimpleWebServiceInboundGateway doesn't map transport headers by default.
See DefaultSoapHeaderMapper.
Of course you can implement your own, but that really might be enough for you to use:
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest()
.getRemoteAddr();
in that your target #ServiceActivator.
Of course that will work if you don't shift message to a different thread before the service activator. The RequestContextHolder.currentRequestAttributes() is tied with ThreadLocal.
You can retrieve it from HttpServletRequest, using getRemoteAddr() to get access to user IP address and getHeader() to get header value. This is assuming you can modify your controller class.
Perhaps this will help:
#Controller
public class MyController {
#RequestMapping(value="/do-something")
public void doSomething(HttpServletRequest request) {
final String userIpAddress = request.getRemoteAddr();
final String userAgent = request.getHeader("user-agent");
System.out.println (userIpAddress);
}
}
I've a RESTlet server running on my android device. It is configured fine. Now I'm jUnit testing it and this particular test is failing for no reason.
I've this URL:
http://10.17.1.72:8080/contacts?order=ASC&limit=10&offset=1 which is correct, I receive a 200 response as expected.
But then if I misspell the parameters in the URL I should get a 404 response. Using postman extension for Chrome if I hit http://10.17.1.72:8080/contacts?oooooooorder=ASC&limit=10&offset=1 (note that "order" is misspelled) I receive a 404 as I should. Until here everything is fine.
The problem comes when I create a RESTlet client to make that GET request on my jUnit test, it receives a 200 response.
Here is my jUnit test method:
public void testGoodRequest() // Success, receives a 200 code.
{
// Create the client resource
ClientResource resource = new ClientResource("http://10.17.1.72:8080/contacts?order=ASC&limit=10&offset=1");
Response response = resource.getResponse();
Log.d(TAG, "Good: " + response.getStatus().getCode());
assertTrue(response.getStatus().getCode() == 200);
}
Adn this one should receive a 404 but receives a 200, eventho the same get request using Chrome's postman receives a 404:
public void testBadRequestWithOrderMisspelled()
{
// Create the client resource
ClientResource resource = new ClientResource("http://10.17.1.72:8080/contacts?oofdgrder=ASC&limit=10&offset=1");
Response response = resource.getResponse();
Log.d(TAG, "BadRequestWithOrderMisspelled: " + response.getStatus().getCode());
assertTrue(response.getStatus().getCode() == 404); // Assert fails, receives 200 instead of 404
}
And here is my Restlet handle method:
#Override
public void handle(Request request, Response response) {
//final ContactList contactList = new ContactList(mContext);
String type = request.getMethod().getName();
String order = request.getResourceRef().getQueryAsForm().getFirstValue("order");
String limit = request.getResourceRef().getQueryAsForm().getFirstValue("limit");
String offset = request.getResourceRef().getQueryAsForm().getFirstValue("offset");
String query = request.getResourceRef().getQueryAsForm().getFirstValue("query");
if(!"order".equals(order) || !"limit".equals(limit) || !"offset".equals(offset) || !"query".equals(query))
{
// Show error
response.setStatus(new Status(Status.CLIENT_ERROR_NOT_FOUND, "Badly formatted URL."));
return;
}
(...)
}
404 is for resource http://10.17.1.72:8080/contacts not found
WIKIPEDIA: 404 Not Found
The requested resource could not be found but may be available again in the future. Subsequent requests by the client are permissible.
if resource is there and proper input is not give for process
it will not give 404! Read More
plus as per following code, there are 2 things
you misspelled limite
you are comparing received values to Strings like order offset
amd limit
i do not think that is what you want to achieve, you should be comparing it with some variables rather
if(!"order".equals(order) || !"limite".equals(limit) || !"offset".equals(offset) || !"query".equals(query))
{
// Show error
response.setStatus(new Status(Status.CLIENT_ERROR_NOT_FOUND, "Badly formatted URL."));
return;
}
For some reason that I don't know RESTlet client was probably the reason for my problem.
I switched over to an android async http client and things are running smoothly.
Here is my code using async http client:
SyncHttpClient client = new SyncHttpClient();
// Should be success
client.get("http://10.17.1.72:8080/contacts", new AsyncHttpResponseHandler() {
#Override
public void onSuccess(String response) {
Log.d(TAG, "GET - ID: 1 - Success expected -> Got success");
auxArray.add("1");
}
#Override
public void onFailure(int arg0, Header[] arg1, byte[] arg2, Throwable arg3) {
super.onFailure(arg0, arg1, arg2, arg3);
Log.d(TAG, "GET - ID: 1 - Success expected -> Got failure");
}
});
Have you tracked the HTTP request using tools and does the request reaches your handler before throwing the error?