Just users with admin role should be able to make a request at "users/all", but the basic users are able too.
This is my security config:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/users/**").permitAll()
.antMatchers( "/users/all").authenticated().anyRequest().hasAnyRole("ADMIN")
.antMatchers("/users/me").authenticated().anyRequest().hasAnyRole("ADMIN", "BASIC_USER")
.and()
.formLogin().disable()
.httpBasic();
}
This is the request from Postman
Why i am able to make the request with the basic user?
if i put them in this order:
.antMatchers( "/users/all").authenticated().anyRequest().hasAnyRole("ADMIN")
.antMatchers("/users/me").authenticated().anyRequest().hasAnyRole("ADMIN", "BASIC_USER")
.antMatchers(HttpMethod.POST, "/users/**").permitAll()
It's the same thing, but the /me request don't work anymore.
/all request
/me request
Now /me request response is 403.
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.user.getRoles().stream().map(role -> new SimpleGrantedAuthority(String.format("ROLE_%s", role.getRole()))).collect(Collectors.toList());
}
i changed role to role.getRole()
Can you change the lines, that admin restriction is upper?
Related
This service receives the multipart request from mobile client and passes on the request to downstream service for uploading the image. I am seeing 415 Unsupported Media Type in my downstream service
private void makeRequest(HttpRequest<Buffer> httpRequest,
Promise<Object> future,
RequestContext requestContext,
RoutingContext routingContext,
Entry entry) {
MultipartForm multipartForm = MultipartForm.create();
MultiMap attributes = routingContext.request()
.formAttributes();
attributes.forEach(attribute -> {
multipartForm.attribute(attribute.getKey(), attribute.getValue());
});
routingContext.fileUploads()
.forEach(fileUpload -> {
multipartForm.binaryFileUpload(fileUpload.name(), fileUpload.fileName(),
fileUpload.uploadedFileName(), fileUpload.contentType());
});
httpRequest.sendMultipartForm(multipartForm, response -> {
handleResponse(routingContext, future, response, requestContext, entry);
});
}
Getting the below exception
javax.ws.rs.NotSupportedException: HTTP 415 Unsupported Media Type
at org.glassfish.jersey.server.internal.routing.MethodSelectingRouter.getMethodRouter(MethodSelectingRouter.java:478)
at org.glassfish.jersey.server.internal.routing.MethodSelectingRouter.access$000(MethodSelectingRouter.java:94)
at org.glassfish.jersey.server.internal.routing.MethodSelectingRouter$4.apply(MethodSelectingRouter.java:779)
at org.glassfish.jersey.server.internal.routing.MethodSelectingRouter.apply(MethodSelectingRouter.java:371)
API signature of my downstream service
#POST
#Timed
#Path("{userId}/{scope}/upload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.APPLICATION_JSON)
#ApiOperation("Multipart upload of an image")
Can someone please guide what is wrong in my code snippet or is there any setting which needs to be enabled in vertx server or vertx web client?
Thanks,
Nitish Goyal
I was able to resolve this by explicitly setting the header
.putHeader("content-type", "multipart/form-data")
I have an apache camel application that requires sending log files to an endpoint and this requires Basic Authentication. I was able to pass the authMethod, authusername and authPassword to the url as specified in the camel documentation but the challange I'm having is that I keep getting null response from the endpoint after starting the application.
However, the same endpoint returns response code and response body using postman.
Below is my code:
from("{{routes.feeds.working.directory}}?idempotent=true")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
multipartEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
String fileName = exchange.getIn().getHeader(Exchange.FILE_NAME, String.class);
File file = exchange.getIn().getBody(File.class);
multipartEntityBuilder.addPart("file",
new FileBody(file, ContentType.MULTIPART_FORM_DATA, fileName));
exchange.getOut().setBody(multipartEntityBuilder.build());
Message out = exchange.getOut();
int responseCode = out.getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class);
log.info("response code "+responseCode);
}
})
.setHeader(Exchange.HTTP_QUERY,
constant("authMethod=Basic&authUsername="+username+"&authPassword="+password+""))
.to(TARGET_WITH_AUTH +"/"+uuid+"/files")
.log(LoggingLevel.DEBUG, "response code >>>>"+Exchange.HTTP_RESPONSE_CODE)
.log(LoggingLevel.INFO, "RESPONSE BODY ${body}")
.end();
Kindly help review and advise further
For HTTP basic authentication I use this before sending a request
<setHeader headerName="Authorization">
<constant>Basic cm9vdDpyb290</constant>
</setHeader>
cm9vdDpyb290 - Encoded Base64 root:root(username and password) string
This was fixed by using httpClient to send my requests with Basic Authentication. Apparently, authMethod in apache camel doesn't send the credentials along with the Post Request and that's why I was getting the initial 401 response code.
Thank y'all for your contributions.
Integration Tests (production code works well) fail while requesting REST endpoints secured with #RolesAllowed.
Following error is thrown:
[5/20/19 8:44:21:363 CEST] 00000109 com.ibm.ws.security.jaspi.JaspiServiceImpl I CWWKS1652A: Authentication failed with status AuthStatus.SEND_FAILUR for the web request
/banking/users/bed6109f-ef8a-47ec-8fa4-e57c71415a10. The user defined Java Authentication SPI for Containers (JASPIC) service null has determined that the authentication data is not valid.
Project is based on OpenLiberty with JWT. The difference is in the UI part. My UI is based on Angular, so for authentication (JWT issuing) following REST Endpoint is used:
#RequestScoped
#Path("/tokens")
#PermitAll
public class AuthResource {
#Inject
private SecurityContext securityContext;
#Inject
private AuthService authService;
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getJwt() {
if (securityContext.isCallerInRole("USER") || securityContext.isCallerInRole("ADMIN")) {
String name = securityContext.getCallerPrincipal().getName();
AuthPojo authPojo = authService.createJwt(name);
return Response.ok(authPojo).build();
}
return Response.status(Response.Status.UNAUTHORIZED).build();
}
}
So:
UI (Angular) calls https://localhost:5051/tokens with Header "Authorization: Basic ENCODED_USERNAME_PASSWORD"
Backend responds with newly generated JWT Token in body and Header "Set-Cookie: LtpaToken2=SOME_TOKEN; Path=/; HttpOnly"
UI uses this token for all other requests against REST Endpoints annotated with "#RolesAllowed({"ADMIN", "USER" })"
Once again, in production code, all this schema works well, but Integration Tests fail.
Here is Integration Test code:
public class MyResourceIT {
private static final String URL = "https://localhost:" +
System.getProperty("liberty.test.ssl.port") + "/users/" + USER_ID1;
private String authHeader;
#Before
public void setup() throws Exception {
authHeader = "Bearer " + new JwtVerifier().createAdminJwt(USER_NAME1);
}
#Test
public void getUserAndAccounts() {
Response response = HttpClientHelper.processRequest(URL, "GET", null, authHeader);
System.out.println("My URL: " + URL);
System.out.println("My Header: " + authHeader);
assertThat("HTTP GET failed", response.getStatus(), is(Response.Status.OK.getStatusCode()));
}
}
Looks like the problem why 401 instead 200 is returned is LtpaToken2 Cookie which is not set in Test. Instead Header "Authorization: Bearer JWT_TOKEN" is used, but this doesn't work.
I Expect that Endpoint secured with "#RolesAllowed" should respond with 200 when header "Authorization: Bearer JWT_TOKEN" is provided. Are there some tricks that should be done with a cookie?
UPDATE 2019-05-23
This is the whole project.
Example test is located here. The failing test is ignored
#Test
public void getUserAndAccounts_withJwt_authorized() throws IOException {
Response response = HttpClientHelper.processRequest(URL, "GET", null, authHeader, null);
assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
}
JWT token is created within following class in the #Before annotated method:
private String authHeader;
#Before
public void setup() throws Exception {
authHeader = "Bearer " + new JwtVerifier().createAdminJwt(USER_NAME1);
}
One thing to notice, that project is based on the following project.
Since the CWWKS1652A message was issued without a provider name, this indicates that appSecurity-3.0 is set and that at least a JSR-375 (a.k.a. Java EE Security API Specification) HttpAuthenticationMechanism is configured for the application, either via annotation or bean implementation. This causes an internal JASPIC provider to be created, therefore the null in the CWWKS1652A message, and this provider invokes the configured HttpAuthenticationMechanism that returns a AuthStatus.SEND_FAILURE status.
Please ensure that you intend to use an HttpAuthenticationMechanism and that valid authentication credentials are passed when challenged by this mechanism.
If it is determined that there is no HttpAuthenticationMechanism configured, then determine if there is an external JASPIC provider factory (AuthConfigFactory implementation) set via the authconfigprovider.factory property. In either case, it is the provider that responds with the AuthStatus.SEND_FAILURE seen in the message.
I have a CXF JAX-RS service and a GWT MVP4G presenter.
I call the service with the RequestBuilder and set Content-Type header to application/json.
But in the server side REST method do not call .
REST code is :
class PlayerService{
#POST
#Path("addplayer")
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
String createOrSaveNewPLayerInfo(PlayerType playerType);
}
GWT code:
RequestBuilder rq = new RequestBuilder(RequestBuilder.POST, url)
rq.setHeader("Content-Type", "application/json");
rq.sendRequest(s, new RequestCallback() {
#Override
public void onResponseReceived(Request request, Response response) {
LOGGER.info(">" + response.getStatusCode() + "<");
}
#Override
public void onError(Request request, Throwable exception) {
LOGGER.info(">>" + exception.getMessage() + "<<");
}
});
I assume, that your GWT application is running on the Jetty server and your service on a Tomcat server. In this case you have two different ports: 8080 & 8888. Calling the service on 8080 will be blocked by the Same Origin Policy.
To solve this, you can switch off the policy (look for CORS). Bad idea.
Instead run your GWT application inside a Tomcat. In this case you will not have any problems with the SOP.
To set up a external server with GWT take a look here.
I have created an SSL Endpoint with heroku. I have a test environment and a live environment. I have a REST call that generates a 303. Since Heroku handles the SSL in it's router, I'm not sure how I can detect if my SEE OTHER URL should create an HTTP or HTTPS based URI. Here's some sample code:
#GET
#Path( "/job/{jobId}" )
public Response getCallStatus( #PathParam( "jobId" ) Long jobId, #Context UriInfo uriInfo ) throws Exception {
if ( !jobService.isDone( jobId ) )
return build( Response.ok( POLLING_FREQUENCY ) );
URI jobLocation = uriInfo.getAbsolutePathBuilder().path( "result" ).build();
return build( Response.seeOther( jobLocation ) );
}
Because my server isn't handling the SSL (heroku is) the absolute path for the REST call will use HTTP instead of HTTPS. If I hard code HTTPS I will break my unit tests or other environment that do not require the HTTPS protocol.
Any thoughts? Or am I misunderstanding how heroku is doing this?
Okay, so here's the answer. Heroku does NOT forward the request as HTTPS. Because of this you need to look into the x-fowarded-proto header to decide what the 303 location is that you should send back to your client. The above code sample would change to something like this:
#GET
#Path( "/job/{jobId}" )
public Response getCallStatus( #PathParam( "jobId" ) Long jobId, #Context UriInfo uriInfo, #Context HttpHeaders headers ) throws Exception {
if ( !jobService.isDone( jobId ) )
return build( Response.ok( POLLING_FREQUENCY ) );
UriBuilder builder = uriInfo.getAbsolutePathBuilder().path( "result" );
String scheme = headers.getHeaderString( "x-forwarded-proto" );
if ( scheme != null )
builder.scheme( scheme );
return build( Response.seeOther( builder.build() ) );
}
That's basically it.
But a better way to handle it that would not require ANY changes in the coded REST METHOD would be to add a container request filter like this:
#PreMatching
public class HerokuContainerRequestFilter implements ContainerRequestFilter {
#Override
public void filter( ContainerRequestContext ctx ) throws IOException {
List<String> schemes = ctx.getHeaders().get( "x-forwarded-proto" );
if ( schemes != null && !schemes.isEmpty() ) {
String scheme = schemes.get( 0 );
UriBuilder builder = ctx.getUriInfo().getRequestUriBuilder();
ctx.setRequestUri( builder.scheme( scheme ).build() );
}
}
}
Then you just register this filter with your RestConfig like this:
public class RestApplication extends ResourceConfig {
public RestApplication() {
packages( "com.myapp.rest.service" );
// along with any other providers, etc that you register
register( HerokuContainerRequestFilter.class );
}
}
While user1888440 answer is totally working I'd rather configure https forwarding at server level.
For example, if you are using an embedded jetty as you're heroku web server you could use jetty built-in org.eclipse.jetty.server.ForwardedRequestCustomizer :
Customize Requests for Proxy Forwarding.
This customizer looks at at HTTP request for headers that indicate it has been forwarded by one or more proxies. Specifically handled are:
X-Forwarded-Host
X-Forwarded-Server
X-Forwarded-For
X-Forwarded-Proto
If these headers are present, then the Request object is updated so that the proxy is not seen as the other end point of the connection on which the request came
So instead of starting your server with :
Server server = new Server(port);
You could use :
Server server = new Server();
HttpConfiguration httpConfiguration = new HttpConfiguration();
httpConfiguration.addCustomizer(new ForwardedRequestCustomizer());
ServerConnector serverConnector = new ServerConnector(server, new HttpConnectionFactory(httpConfiguration));
serverConnector.setPort(port);
server.addConnector(serverConnector);