I have a rest api using apache camel. When I hit a post request on a route, it should get a file from S3. I am sending json data(filename, bucketName, accesskey, secretkey, region) in order to extract the file from s3. Here is the code for that ->
public static class HelloRoute extends RouteBuilder {
#Override
public void configure() {
rest("/")
.post("file-from-s3")
.route()
.setHeader(AWS2S3Constants.KEY, constant("filename"))
.to("aws2-s3://bucketnameaccessKey=INSERT&secretKey=INSERT®ion=INSERT&operation=getObject")
.to("file:/tmp/")
The issue is that I don't want the .setHeader(AWS2S3Constants.KEY, constant("filename"))
part. Is there a way to remove that and put an alternate to that in the URI itself. I tried something like this ->
public static class HelloRoute extends RouteBuilder {
#Override
public void configure() {
rest("/")
.post("file-from-s3")
.route()
.to("aws2-s3://bucketnameaccessKey=INSERT&secretKey=INSERT®ion=INSERT&filename=hello.txt&operation=getObject")
.to("file:/tmp/")
But this is giving me an error java.lang.IllegalArgumentException: AWS S3 Key header missing.. Is there some other way to do this?
Sometimes build an AWS Request can be complex, because of multiple options. We introduce the possibility to use a POJO as body.
Take a look at: https://camel.apache.org/components/latest/aws2-s3-component.html#_using_a_pojo_as_body
Related
I have a rest api using apache camel. When I hit a post request on a route, it gets a file from S3. Here is the code for that ->
public static class HelloRoute extends RouteBuilder {
#Override
public void configure() {
rest("/")
.post("file-from-s3")
.route()
.setHeader(AWS2S3Constants.KEY, constant("filename"))
.to("aws2-s3://bucketname?accessKey=INSERT&secretKey=INSERT®ion=INSERT&operation=getObject")
.endRest();
}
}
This gives the content of the file in Postman. I want the response in a json format where the contents of the file will be in the content key of json. How to do this?
Make sure you have binding mode enabled (auto|json) in your REST configuration and the consumes/produces set on your route. Now write your processor to build your response object and set it in the body. Camel will handle the rest for you.
public static class HelloRoute extends RouteBuilder {
restConfiguration().component("netty-http").host("localhost").port(portNum).bindingMode(RestBindingMode.auto);
#Override
public void configure() {
rest("/")
.post("file-from-s3")
.consumes("application/json").type(YourRequest.class)
.produces("application/json").outType(YourResponse.class)
.route()
.setHeader(AWS2S3Constants.KEY, constant("filename"))
.to("aws2-s3://bucketname?accessKey=INSERT&secretKey=INSERT®ion=INSERT&operation=getObject")
//.process("responseBuilderProcessor")
.endRest();
}
}
I am using apache camel for this.I have an http server. When I do a get request on a url it should fetch a file from a bucket in aws s3 and then create a file in my local directory.
public static class HelloRoute extends RouteBuilder {
#Override
public void configure() {
rest("/")
.get("file-from-s3")
.route()
.from("aws2-s3://bucketname?accessKey=INSERT&secretKey=INSERT®ion=us-east-1&prefix=hello.txt")
.to("file:/tmp/")
.endRest();
}
I wrote the above code for this but it looks like it's ignoring the "from" statement and directly executing the "to" statement and hence creating an empty file in my tmp directory. Is there some other way to do this?
I have a lambda, written in Java, that accepts a Request Object of the structure
{
"id": "be1c320a-144f-464d-b32c-38ec7fb4445b",
"userId": "foobar"
}
When I call this Lambda through the test interface with such an object, it works fine.
I want to create an API where a GET request to
/users/foobar/items/be1c320a-144f-464d-b32c-38ec7fb4445b
i.e. of the form
/users/{userId}/items/{id}
calls this Lambda.
I have created the API resources /users, {userId}, items, and {id} appropriately.
And I have created the GET method (on /users/{userId}/items/{id})and associated it to the lambda.
When I test the API, it invokes the lambda, but with null values in the request. I can see it package the path as {"id":"be1c320a-144f-464d-b32c-38ec7fb4445b","userId": "foobar"} in the logs, but that's not being sent in the body.
I have tried creating a template map (and have tried RTFM), but cannot see how to map path parameters to a body.
How do I achieve this mapping?
I think your Request Object structure may not be properly configured. There may be a few ways to configure this. Here is some information that has helped me.
How to pass a querystring or route parameter to AWS Lambda from Amazon API Gateway - Demonstrates this mapping (albeit with python). However, taking the top response, if you enable "Use Lambda Proxy integration", you can similarily do this with Java as so:
#Override
public Object handleRequest(APIGatewayProxyRequestEvent input, Context context) {
Map<String, String> pathParameters = input.getPathParameters();
String id = pathParameters.get("id");
String userId = pathParameters.get("userId");
// Handle rest of request..
}
This is a tuturial using the serverless framework to create an Api with Java. This tutorial similarily accesses the pathParameters by parsing the input rather than using the APIGatewayProxyRequestEvent java class.
#Override
public Object handleRequest(Map<String, Object> input, Context context) {
try {
// get the 'pathParameters' from input
Map<String,String> pathParameters = (Map<String,String>)input.get("pathParameters");
String id = pathParameters.get("id");
String userId = pathParameters.get("userId");
} catch (Exception ex) {
logger.error("Error in retrieving product: " + ex);
}
}
Use a mapping template.
First, in the Method Request section, you should see userId and id as Request Paths
Then, in the Integration Request, do not choose Proxy Integration.
Then in the Mapping Templates section, add a new mapping template for application/json of the form
{
"id" : "$method.request.path.id",
"userId" : "$method.request.path.user_id"
}
I have have read other posts on the same topic, but I haven't really gotten a clear picture of how to best solve this:
I have a webservice, that is "stateless" when it comes to the authentication/session, meaning that the client will send two strings with every request (in the HTTP header), AuthToken and DeviceUUID.
These two strings are then compared to the storage, and if found, we know which user it is.
1)
Id like to use the [Authenticate] attribute for each service that I want to protect, and then a method should be executed where I check the two strings.
2)
If I add the [RequiredRole], a method should also be executed, where I have access to the HTTP headers (the two strings), so I can do my lookup.
I am unsure of how to do this in the easiest and cleanest manner possible. I do not want to create ServiceStack Session objects etc, I just want a method that, for each decorated services, runs a method to check authenticated state.
If you want to execute something else in addition when the [Authenticate] and [RequiredRole] attributes are used then it sounds like you want a custom [MyAuthenticate] Request Filter attribute which does both, i.e. validates that the request is Authenticated and executes your custom functionality, e.g:
public class MyAuthenticateAttribute : AuthenticateAttribute
{
public override async Task ExecuteAsync(IRequest req, IResponse res, object dto)
{
await base.ExecuteAsync(req, res, requestDto);
var authenticated = !res.IsClosed;
if (authenticated)
{
//...
}
}
}
Then use it instead of [Authenticate] in places where you need that extra functionality:
[MyAuthenticate]
public class MyServices { ... }
But I'd personally keep the logic in the attributes separated:
public class MyLogicPostAuthAttribute : RequestFilterAsyncAttribute
{
public override async Task ExecuteAsync(IRequest req, IResponse res, object dto)
{
//...
}
}
So they're explicit and can be layered independently of the [Authenticate] attribute, e.g:
[Authenticate]
[MyLogicPostAuth]
public class MyServices { ... }
Which can also be combined like:
[Authenticate, MyLogicPostAuth]
public class MyServices { ... }
There is a Spring Security configuration in my application, so all the rest interfaces need to pass some filters and authentication.
This is the security configuration I have:
#Configuration
#EnableWebSecurity
public class MyProgramSecurityConfiguration extends WebSecurityConfigurerAdapter {
....
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.addFilterBefore(restBasedAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.csrf().disable()
.authorizeRequests().anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
.and()
.logout().permitAll(false)
.addLogoutHandler(logoutHandler).logoutRequestMatcher(new AntPathRequestMatcher(LOGIN_PATH, HttpMethod.DELETE.toString()))
.logoutSuccessHandler(getLogoutSuccessHandler())
.and()
.sessionManagement().maximumSessions(5);
http.addFilterAfter(termsAndConditionsFilter, TermAndConditionFilter.class);
http.addFilterAfter(certificateValidationFilter, TermsAndConditionsFilter.class);
... // some other filters
}
....
}
But, as I described in the title of the question, I would like to have one endpoint /internal/application/info that can be called by anyone without any need of authentication, logging or whatever. I think that you can do it with regexMatcher, am I right?
I think you should to add one more method of HttpSecurity class, something like that:
....
.and()
.authorizeRequests()
.antMatchers("/internal/application/info")
.permitAll()
....