Curl lets me GET but does not let me POST ... Spring JPA - spring

When I run the Spring Application and then try to comunicate with
the REST API it allows me to GET but not to POST.
So this works:
curl -u user:a75fd7ea-9a6e-4943-bc0c-3b0a96bda51b http://localhost:5000/activity/getall
This does not work:
curl -u user:a75fd7ea-9a6e-4943-bc0c-3b0a96bda51b
-H "Accept: application/json"
-X POST
-d '{
"name":"Sleep",
"criteria":"Sleep at least 8 hrs",
"ini":"2022-08-30",
"periodicity":"DAY",
"periodicityCount":"1"
}'
http://localhost:5000/activity/post
If you notice is the same Username and Password.
This is the response I get:
HTTP/1.1 403
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json
Transfer-Encoding: chunked
Date: Mon, 29 Aug 2022 19:25:27 GMT
Connection: close
{
"timestamp": "2022-08-29T19:25:27.510+00:00",
"status": 403,
"error": "Forbidden",
"path": "/activity/post"
}

The reason why your API calls fail is due to the CSRF protection you enabled in your Spring Security configuration.
The way this works is that for each non-GET request (= POST, PUT, PATCH or DELETE), you need to include a CSRF token.
To obtain a CSRF token, you first need to fire a GET request (eg. http://localhost:5000/activity/getall). In the response headers, you should see a Set-Cookie header containing an XSRF-TOKEN cookie. For example:
Set-Cookie: XSRF-TOKEN=098b732a-282a-11ed-a261-0242ac120002
Now you need to copy the value of the XSRF-TOKEN cookie (should contain a UUID), and set it as the value of the X-XSRF-TOKEN header:
curl \
-u user:a75fd7ea-9a6e-4943-bc0c-3b0a96bda51b
-H "Accept: application/json"
-H "X-XSRF-TOKEN: 098b732a-282a-11ed-a261-0242ac120002"
-X POST \
-d '{
"name":"Sleep",
"criteria":"Sleep at least 8 hrs",
"ini":"2022-08-30",
"periodicity":"DAY",
"periodicityCount":"1"
}'
http://localhost:5000/activity/post
After that, your request should succeed. Be aware, the response of this POST-request will contain a new CSRF token that you will have to copy to your next request.
Alternatively, you can disable CSRF protection by setting .csrf().disable() in your Spring Security configuration.

Related

Using Postman and Soapui I get a 400 when hitting a Spring Boot endpoint, but Swagger has no problem

The endpoint is in a RestController with this signature:
#PostMapping(value = "/unclaim")
#Operation(summary = "Unclaim Tasks ")
public BaseResponse<String> claimTasks(
#RequestParam(required = true, name = "taskIds") Long taskIds[]
)
{
If I use Soap ui to the correct URL ( I know I got that right, b/c if I append another character to it, I get a 404 ) I send this payload:
{
taskIds: [ 444, 34, 55 ]
}
Doing this in SoapUI and Postman both give 400s and no explanation :
HTTP/1.1 400
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Set-Cookie: JSESSIONID=C18701F799961FEECF967457574EB914; Path=/tlmapi; HttpOnly
Content-Length: 0
Date: Fri, 02 Sep 2022 16:11:44 GMT
Connection: close
But going to the swagger-ui.html page for this controller lets me construct a request that works :
So what's the difference ? Or is there a way to see the payload that swagger is sending?
Turns out that I accidentally was using the #RequestParam annotation on the list, instead of #RequestBody. Further, the param was Required, so spring rejected it and ignored my Json payload from SoapUI and Postman.
Hope this helps someone not waste a couple of hours...

Username and password have been treated as anonymous in Spring-security-oauth2 password mode

I'm using Spring Boot and Spring Security OAuth2 to issue tokens to the front-end.
Postman
When I use postman to test, everything works fine.
.
Browser
But when I sent a same request on browser using vue.js and axios, it didn't work as expected. The status code was 401.
Gerneral:
Request URL: http://localhost:8080/oauth/token
Request Method: POST
Status Code: 401
Remote Address: [::1]:8080
Referrer Policy: no-referrer-when-downgrade
Response Headers:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://localhost:8081
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Type: application/json;charset=UTF-8
Date: Sun, 17 Mar 2019 02:20:54 GMT
Expires: 0
Pragma: no-cache
Transfer-Encoding: chunked
Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers
WWW-Authenticate: Basic realm="oauth2/client"
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Request Headers:
Provisional headers are shown
Accept: application/json, text/plain, */*
Content-Type: application/x-www-form-urlencoded
Origin: http://localhost:8081
Referer: http://localhost:8081/login
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36
Form Data:
{"grant_type":"password","scope":"all","username":"admin","password":"888","client_id":"wellcell","client_secret":"wellcell"}:
Difference in server console log
I made a picture of side-by-side:
On the left side is the server console log of postman request.
And server console log of browser request is on the right.
After "ClientCredentialsTokenEndpointFilter", postman request went to "DaoAuthenticationProvider" to be authenticated.
But the browser request went to "BasicAuthencationFilter" and the "username" and "password" was ignored and an anonymous user was returned. Then, access is denied with an anonymous user.
Anybody had this kind of problem before?
I think problem with Content-Type: application/x-www-form-urlencoded. If you send json, you need to use Content-Type: application/json.
Simple use of axios with post of json:
axios.post("http://localhost:8080/oauth/token", {
"grant_type": "password",
"scope": "all",
"username": "admin",
"password": "888",
"client_id": "wellcell",
"client_secret": "wellcell"
}).then((response) => {
console.log(response.data);
});

Ambari api POST complaining CSRF protection

I am trying to set hbase property through Ambari API using following command
curl -u "admin:admin" -i -X POST -d '{"type": "hbase-site", "tag": "version3", "properties" : {"hbase.regionserver.global.memstore.size" : "0.6"}}' https://abct.net/api/v1/clusters/xyz/configurations
But keep getting following error
HTTP/1.1 400 Bad Request
Content-Length: 107
Content-Type: text/plain
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Server: Microsoft-IIS/8.5
x-ms-hdi-active: 10.8.18.29
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
User: admin
X-Powered-By: ARR/3.0
Set-Cookie: AMBARISESSIONID=2e8ortl32j1p7zdjatigdgvg;Path=/;HttpOnly; path=/; secure
X-Powered-By: ASP.NET
Date: Mon, 12 Sep 2016 18:19:38 GMT
{
"status" : 400,
"message" : "CSRF protection is turned on. X-Requested-By HTTP header is required."
}
What am missing here ?
Turns out you have to add the request header to the request for anything other than a GET request.
You can add the header with
curl --header "X-Requested-By: my_computer_name"
Or
You can disable this feature.
I had same problem in c# Rest client. Using Brig's answer fixed it:
HttpClientHandler handler = new HttpClientHandler
{
Credentials = new System.Net.NetworkCredential("xxxx", "yyyyy"),
};
using (var httpClient = new HttpClient(handler))
{
//"X-Requested-By: my_computer_name"
httpClient.DefaultRequestHeaders.Add("X-Requested-By","my_computer_name");

Spring MVC Restful 406 Not Acceptable Error

I know this question has been asked before, and I've checked every message of Spring MVC with the 406 error message and I am beside myself not knowing how to fix this problem since I've tried just about everything put fourth by these previous answers.
This one web-service takes an email address, and returns back a user object in JSON. I can tell you the unit test works great. And I know we pass the Spring Security, we are executing the servlet ... the only problem now is the JSON output ... I don't get that, I get this error message.
So, to be exact:
Spring Core: 4.2.4.RELEASE
Spring Security: 4.0.3.RELEASE
Jackson Faster XML: 2.6.5 (core, databind, annotations)
2.7.0 doesn't seem to work yet ...
Here is the springmvc-servlet.xml:
<context:annotation-config />
<context:component-scan base-package="com.agmednet.server.controller" />
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="com.agmednet.server.HibernateAwareObjectMapper">
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat">
<constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss.SSS"></constructor-arg>
</bean>
</property>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
Here is the controller:
#Controller
#RequestMapping("/users")
public class UserAccountController
{
#Autowired
private UserAccountService userAccountService;
#RequestMapping(value = "/email/{email:.*}", method = RequestMethod.GET)
public #ResponseBody UserAccountEntity getByEmailAddress(#PathVariable("email") String email)
{
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
User user = null;
if (principal instanceof User)
{
user = ((User) principal)
}
UserAccountEntity userAccount = userAccountService.getByEmailAddress(email);
return userAccount;
}
}
As you can see, I have the Accept header setup, I have the #ResponseBody annotation.
I have a SimpleCORSFilter Code on top of this:
#Component
public class SimpleCORSFilter implements Filter
{
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException
{
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, Content-Type, Accept, If-Modified-Since, openam_token");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig){}
public void destroy(){}
}
As you can see, this accepts my custom header for an "openam_token", but is has
"Accept" and "Content-Type" as well.
I am calling this from a linux box behind a firewall, so I have to SSH into the machine, and execute this curl statement:
curl -v -i -X GET -H "openam_token: AQIC5wM2LY4Sfcxfw-GSBSndg-4DMEyrEqcBgiTE4b4e3aE.*AAJTSQACMDE.*" -H "Content-Type: application/json, text/html" -H "Accept: application/json, text/html" backend.spring.mycompany.net:8080/services/api/users/email/user4252002#mycompany.com
So, here is what I am sending via curl in the request:
* Trying 10.0.4.107...
* Connected to backend.spring.agmednet.net (10.0.4.107) port 8080 (#0)
> GET /services/api/users/email/user4252002#agmednet.com HTTP/1.1
> User-Agent: curl/7.40.0
> Host: backend.spring.agmednet.net:8080
> openam_token: AQIC5wM2LY4Sfcxfw-GSBSndg-4DMEyrEqcBgiTE4b4e3aE.*AAJTSQACMDE.*
> Content-Type: application/json, text/html
> Accept: application/json, text/html
And here is the response:
< HTTP/1.1 406 Not Acceptable
HTTP/1.1 406 Not Acceptable
< Server: Apache-Coyote/1.1
Server: Apache-Coyote/1.1
< Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS, DELETE
Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS, DELETE
< Access-Control-Max-Age: 3600
Access-Control-Max-Age: 3600
< Access-Control-Allow-Headers: x-requested-with, Content-Type, Accept, If-Modified-Since, openam_token
Access-Control-Allow-Headers: x-requested-with, Content-Type, Accept, If-Modified-Since, openam_token
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
Pragma: no-cache
< Expires: 0
Expires: 0
< X-XSS-Protection: 1; mode=block
X-XSS-Protection: 1; mode=block
< X-Frame-Options: DENY
X-Frame-Options: DENY
< X-Content-Type-Options: nosniff
X-Content-Type-Options: nosniff
< Set-Cookie: JSESSIONID=76C4B28DD0B5D88EB9341944BDBCD045; Path=/services/; HttpOnly
Set-Cookie: JSESSIONID=76C4B28DD0B5D88EB9341944BDBCD045; Path=/services/; HttpOnly
< Content-Type: text/html;charset=utf-8
Content-Type: text/html;charset=utf-8
< Content-Language: en
Content-Language: en
< Content-Length: 1110
Content-Length: 1110
< Date: Wed, 20 Jan 2016 14:30:28 GMT
Date: Wed, 20 Jan 2016 14:30:28 GMT
<!DOCTYPE html>
<html><head>
<title>Apache Tomcat/8.0.30 - Error report</title>
</head>
<body>
<h1>HTTP Status 406 - </h1>
<div class="line"></div><p><b>type</b> Status report</p>
<p><b>message</b> <u></u></p>
<p><b>description</b>
<u>The resource identified by this request is only capable of generating
responses with characteristics not acceptable according to the request*
Connection #0 to host backend.spring.agmednet.net left intact
"accept" headers.</u></p>
<hr class="line"><h3>Apache Tomcat/8.0.30</h3></body></html>
Ultimately, this is the error message listed above:
The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request* Connection #0 to host backend.spring.mycompany.net left intact
"accept" headers.
So, I am using #ResponseBody, I am using the latest Jackson 2.x version. I am following all the suggestions from previous questions and I still can't get this to work.
Any help would be much appreciated.
The #EnableWebMVC wasn't the problem, and everything you see in the question is 100% correct. I have two other web-services that worked fine. The problem ultimately was with the curl statement itself:
curl -v -i -X GET -H /
"openam_token: AQIC5wM2LY4Sfcxfw-GSBSndg-4DMEyrEqcBgiTE4b4e3aE.*AAJTSQACMDE.*" /
-H "Content-Type: application/json, text/html" /
-H "Accept: application/json, text/html" /
backend.spring.mycompany.net:8080/services/api/users/email/user4252002#mycompany.com
If you notice, the URL and the path variable is not in quotes.
if you notice, the email address has an # symbol in it, which I found out means something to curl. So, what I did was change my curl to the following.
curl -v -i -X GET -H /
"openam_token: AQIC5wM2LY4Sfcxfw-GSBSndg-4DMEyrEqcBgiTE4b4e3aE.*AAJTSQACMDE.*" /
-H "Content-Type: */*" /
-H "Accept: */*" /
"backend.spring.mycompany.net:8080/services/api/users/email/user4252002%40mycompany%2Ecom"
1) I encoded the email address myself # to %40 and period-dot to %2E
You can use whatever tool you meant to encode the path variable.
2) Even though I am returning a java object transformed into JSON, I changed headers "Accept" and "Content-Type" to "/".
That was it. This worked and the 406 error message went away.

Google OAuth 2: response_type error on token request

I am trying to return an OAuth 2 response code for an OAuth response token. However, my request returns the following error which has zero results in google. I tried changing response_type to "token" instead of "code" but that didn't work either.
OAuth 2 parameters can only have a single value: response_type
Request Details:
scope=https://www.googleapis.com/auth/userinfo.email
client_secret=_____
response_type=code
grant_type=authorization_code
redirect_uri=http://localhost/folder/
client_id=____.apps.googleusercontent.com
code=_____
I'm sending this second-step payload to POST https://accounts.google.com/o/oauth2/auth
What is wrong with my request?
Edit
I just realized that there is the https://accounts.google.com/o/oauth2/token URL that should be used for this request. However, changing to that URL now gives:
HTTP/1.0 400 Bad Request
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: Fri, 01 Jan 1990 00:00:00 GMT
Date: Fri, 27 Jul 2012 22:44:35 GMT
Content-Type: application/json
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Server: GSE
{
"error" : "invalid_request"
}
Edit 2
Removing the response_type and changing the URL like above solved this.
After receiving the authorization code you have to ask '/o/oauth2/token' for the access token. This request takes no 'scope' and no 'response_type' parameters. See the Google documentation for more details.
After trying out a couple of methods, the required parameters to make the OAUTH2 call are
redirect_uri, response_type, scope, client_id. I kept on debugging the oauth call based on the error report I received.

Resources