Create a secret url using spring boot - spring

I'm studying different technologies to create an API for a multi plateform application. This application has to give the possibility to users to share a file with a friend without authentication, but the URL has to be unguessable so the file keep secret. Juste like sharing picture feature in google photos.
Spring boot is one of the most interesting framework to create a multi plateform API, but I'm wondering if it's possible to create a secret and unguessable URL.
Thank you for your time.

To answer your question : you can organize your URLs path with some random hard-to-guess part (eg https://hostname/fileshare/Zak/myVideos/295223cb464d4e4794b93a09a1c730fd) UUIDs are 128 bits data and pretty much standard.
Another way would be to add a checksum token in the queryString :
https://hostname/fileshare/Zak/myVideos/lolcat.mp4?h=187515ZEDwhere the token is generated from the url path (and possibly even the queryString) with some secure algorithm (for exemple hmac256) and have your Controller (or better, a Filter) check if the h parameter is indeed equal to the hashed path.
EDIT : further explanation :
I'm assuming you've already got (or at least intend to have) a controller capable to serve content based on a file system directory. In my previous example, I assumed something of the likes /Zak/myVideos/. Spring controllers can easily return files in this directory by their filenames, but if the filename are easy to guess (eg video.mp4), I understand that /Zak/myVideos/video1.mp4 would be vulnerable. That's why I suggested to use UUIDs.
How to use UUIDS ?
If you can rename the files in /Zak/myVideos, simply rename them by random UUIDs and it will work transparently. The drawside to this is 1) the filenames won't mean anything anymore and 2) you're maybe not able to rename those files.
You can also have a DB table referencing filenames and UUIDs, and simply have your controller call a service to retrieve the correct filename from the correct UUID. The drawside to this is that you'd need to have a DB and write some code (and slow down API calls to query the DB).
That's why I also suggested to simply use a token. The url would still be the litteral path to your file, but require an additional parameter (the token) in the queryString. A servletRequestFilter could check whether the token is valid or not (with a simple hash + check algorithm) before granting access to the controller serving the file. This way, you won't need to rename your files nor create a DB.

Related

How can i access folder outside the root of codeigniter without passing sensitive information in the url?

My code (view) So far everything is working well when am accessing a folder(uploads) withing the root, but i would like to access a folder located in a different location withing the same server without showing sensitive information in the url when the image loads.
<img src="<?php echo base_url('/uploads/'.$popular_car['img_path'])?>" class="card-img-top"
style="height: 150px;"></div>
There's many alternatives. Most will involve some sort of database use. This is what I do (greatly summarized)
Every file that will need to be accessed has a record on a table. The record has a primary id, a secret random token and the path to the file. The table is indexed by both the primary ID and the token.
On the URL I get something like base_url('controller/file_access/).$id.'/'.$token. Upon receiving the request, I'll check the files table, if there's a match for both parameters, I'll stream the file to the browser. This way of doing things, albeit a little bit more complicated, has two main benefits:
1.- it prevents a user to just try different numeric IDs and see what is displayed. Since IDs are numeric and autoincrementing, all you'd need to do is looping from 1 to 100000 and download all files. Adding the token and querying the table with both parameters greatly reduces the risk of someone getting a file he/she's not intended to.
2.- it obscures the real location of the file, as streaming it to the browser in this way looks (in the eyes of the browser) as being located in example.com/controller/file_access/id/token but the real or relative path to the image remains hidden and non-accessible from the web.
Using this as a base you can add a lot of logic on top of this depending on your needs. You could also have an "allowed_user" field in the table if files are private and/or user-specific so that you don't stream the file if the user is not allowed to see it (even if he has the correct ID/token combination).
This is just a rough description of what I do on a couple of sites. Take it as the theoretical foundation you can build on.

how to implement Multi-Tenant functionality in asp.net-core

I have an Asp.net Core application I want to be able to allow multiple/ different Tenant(Client)to access the same application but using different url's. I have common database for all tenant(client).
So It is the main part I want to host my application in a domain say... www.myapplication.com then allow different Tenant(client) to access the same application using
1.www.TenantOne.myapplication.com
2.www.TenanatTwo.myapplication.com.
3.www.{TENANCY_NAME}.myapplication.com
I can't find any info on how to do this and I'm stuck.
How to do it? Please provide the code. Thanks.
As Saravanan suggested these types of questions don't belong here on SO. To get you started, I suggest you start looking if there are any frameworks such as SaaSKit available to add a multi tenancy layer to the pipeline.
The essential part is to know where each request comes from. Using subdomains is a good way to achieve that and middleware is a good place to 'identify' your tenant. You could have a database to persist the tenants but the implementation is entirely up to you. I also wrote a little article on the subject. Although it isn't ASP.NET Core, the principles still apply.
The approach I believe you are looking for is similar to the article at the url below.
https://dotnetthoughts.net/building-multi-tenant-web-apps-with-aspnet-core/
In it, the author splits the requesting URL into an array of strings delimited by the dot in the address. The variable 'subdomain' is then set to the first element of that array. In your question, it looks like you may want to use the second element in the array, but you get the idea.
var fullAddress = actionExecutingContext.HttpContext?.Request?
.Headers?["Host"].ToString()?.Split('.');
var subdomain = fullAddress[0];
//do something, get something, return something
How you use this data is up to you. The author of the article created a filter attribute, but there are many possibilities such as passing the tenant name as a parameter to a service function.
Sorry,you have to get something to start with and then come back for the people to help you with.
I would say that this is all of a domain based wild card mapping and change in your authentication logic to get the tenant id from the URL. Once you identified the tenant, you just login and then take it forward. Like you might be having a database with the tenant details like
tenant1 | tenant1.company.com | guid-ofthe-tenant | etc...
Once you get the URL, you lookup in the above table and get the tenant code and then you choose the login mode and then proceed.
In case you have tried something yet, we would be happy to point you if it does not work yet.

Multiple authentication methods for Apiary

I'm just getting started with Apiary and I can't tell if this is a limitation of the product or just me not understanding what to do.
I'm documenting an API which authenticates the user as part of every request. Sometimes the authentication is part of the path (a request for the user's profile would have the user id in the path), other times just as parameters (?user_id=1&auth=secret), and for POST requests, part of the incoming body as JSON.
Also, there are 3 methods of authentication in the app. You can log in with a Facebook UID, email address, or using the unique id of the device you're using. The result is something that looks like this:
##User [/user/{facebook_uid}{?access_token}, /user/{email}{?device_id}, /users/{device_auth_id}{?device_id}]
This works fine, and displays in the API as I'd expect:
But this introduces 2 issues:
1) If I wanted to add a set of parameters shared by all authentication methods, I would need to add it to all 3 like this:
## User [/user/{facebook_uid}{?access_token, extra_thing, this_too},
/user/{email}{?device_id, extra_thing, this_too},
/users/{device_auth_id}{?device_id, extra_thing, this_too}]
This seems a bit messy, it'd be much nicer to apply shared parameters at the end of the path array so they apply to all, something like this:
## User [/user/{facebook_uid}{?access_token}, /user/{email}{?device_id}, /users/{device_auth_id}{?device_id}]{&extra_thing, this_too}
But this doesn't work. Is there a way to do this? The documentation wasn't very helpful with more complicated stuff like this.
Also, would there be a way to create some kind of template which I could apply to all my methods? In the case where the authentication is part of the path its a bit unavoidable, but for other requests it would be nice to just do something like include: authentication and have it pull the unique_id/auth combo from a defined template somewhere.
Thanks!
First, there isn't really support for having a single model with multiple resource representations. It is an unusual thing to do and is actually good food for thought.
Second, using multiple URIs in [path segment] is probably going to confuse Apiary's mock server and make it unusable.
In my opinion, I'd split this into three models: Facebook User, E-mail User and Device User, with slightly different documentation (how are they created? Can you really create all of them through api? etc. etc.)
It also depends on how you want to document this. As path segments are not validated (it would be strange to have different resources based on the type of the arguments), you can just have (and I'd personally do just that)
## User [/user/{id}{?access_token, extra_thing, this_too}]
+ Parameters
+ id (required, string, `test#example.com`)...id of the user. Can be either user's e-mail, facebook id or device id from where user was created.
As for reusable parts, this is currently being implemented with authentication being part of that.

Generating i18n property files dynamically from db

I'd like to set up a simple server with a DB containing keys and translations for different messages. My different other applications would request this server, which would give them only the list of messages concerning them in, let's say, JSON. The different apps would then "compile" this list into several classic i18n .properties files they could use.
The advantage is that translation service is decorrelated from the different applications nofile has to be re-deployed or even updated on the applications servers. I could even set up a GUI to siplify operations on the messages.
Moreover, I found in the different projects I worked on that the manual maintenance of properties files can get really messy overtime (labels missing for one locale and it's really hard to find which one when the files get big, we used to use a central excel file to manage and generate .properties files but it's just a giant mess).
Anyway, do you think it's a good / feasable idea? Any insight on how to do it? How can I transform JSON into .properties easily? (is there tools for that?) (or should I use another format than JSON?). Thanks for any comment :)
To be blunt, I don't know why you want to reinvent the wheel and make it a bit more square-like.
Is it feasible? Yes it is.
Should you do it? No.
That is, you may serve translations with JSON, there is nothing wrong with that. It often makes sense.
What does not, is to put all the translations in a database. If you do that, I wish the localizers all the best.
It is just way easier to fix the translation in the standard text file (especially if it is UTF-8 encoded...) than in the database.
Talking about translations, you may want to (or be forced) to hire Translation Service Providers. These guys have there own Translation Memory systems as well as Machine Translation systems and guess what? They don't care about your database, nor GUI. They want to handle translations the standard way, that is using typical file formats. Java properties files happens to be one of them. Database scripts are not...
Now, on how to convert Java properties files to JSON. Assuming you are using Java, it's fairly straightforward. What you need first is to read your properties file:
// you somehow need to detect a Locale
ResourceBundle messages = ResourceBundle.getBundle("messages", locale);
The way you detect a Locale depends on the context. For instance, you may want to assign one in a user profile (allowing users to change their regional preferences), or you may want to create a web service that take language tag as a parameter. In both cases it would probably go down to this:
Locale locale = Locale.forLanguageTag(langTag);
Where langTag is a String containing your desired language tag (either read from user profile, which is recommended, or read as a parameter of a call to the web service).
When you have your resource file read, all you need to do is to take a desired portion and put it to the map:
Map<String, String> keyToTranslation = new HashMap<>();
for (String key : messages.keySet()) {
// filter the key somehow, for example:
// if (key.startsWith(moduleName))
keyToTranslation.put(key, messages.getString(key));
}
Then you can use Jackson to convert the map into JSON. Actually, any JAX-RS compatible library like RESTEasy would use it internally to perform the conversion.
It is of course possible to do this kind of conversion using completely different technology stack, i.e. using Node.js. However, there are few things to keep in mind:
Users may have a list of their preferred languages, which they advertise in a HTTP Accept-Language header. It is desirable to return a translation based on this list.
There is something called resource loading fallback. For instance, you may have no translations for Canadian version of French, but you may have regular French translations, which you may want to return to a user. ResourceBundle handles this for you, if you want to use something else, it is good to keep this in a back of your head.
You can use the i18n Maven Plugin to handle internationalization. It's very simple to configure your project. You render different output format files.
JSON
XML
Properties
Check out the sample i18n resource files.

Combining GET and POST in Sinatra (Ruby)

I am trying to make a RESTful api and have some function which needs credentials. For example say I'm writing a function which finds all nearby places within a certain radius, but only authorised users can use it.
One way to do it is to send it all using GET like so:
http://myapi.heroku.com/getNearbyPlaces?lon=12.343523&lat=56.123533&radius=30&username=john&password=blabla123
but obviously that's the worst possible way to do it.
Is it possible to instead move the username and password fields and embed them as POST variables over SSL, so the URL will only look like so:
https://myapi.heroku.com/getNearbyPlaces?lon=12.343523&lat=56.123533&radius=30
and the credentials will be sent encrypted.
How would I then in Sinatra and Ruby properly get at the GET and POST variables? Is this The Right Way To Do It? If not why not?
If you are really trying to create a restful API instead if some URL endpoints which happen to speak some HTTP dialect, you should stick to GET. It's even again in your path, so you seem to be pretty sure it's a get.
Instead of trying to hide the username and password in GET or POST parameters, you should instead use Basic authentication, which was invented especially for that purpose and is universally available in clients (and is available using convenience methods in Sinatra).
Also, if you are trying to use REST, you should embrace the concept of resources and resoiurce collections (which is implied by the R and E of REST). So you have a single URL like http://myapi.heroku.com/NearbyPlaces. If you GET there, you gather information about that resource, if you POST, you create a new resource, if you PUT yopu update n existing resource and if you DELETE, well, you delete it. What you should do before is th structure your object space into these resources and design your API around it.
Possibly, you could have a resource collection at http://myapi.heroku.com/places. Each place as a resource has a unique URL like http://myapi.heroku.com/places/123. New polaces can be created by POSTing to http://myapi.heroku.com/places. And nearby places could be gathered by GETing http://myapi.heroku.com/places/nearby?lon=12.343523&lat=56.123533&radius=30. hat call could return an Array or URLs to nearby places, e.g.
[
"http://myapi.heroku.com/places/123",
"http://myapi.heroku.com/places/17",
"http://myapi.heroku.com/places/42"
]
If you want to be truly discoverable, you might also embrace HATEOAS which constraints REST smentics in a way to allows API clients to "browse" through the API as a user with a browser would do. To allow this, you use Hyperlink inside your API which point to other resources, kind of like in the example above.
The params that are part of the url (namely lon, lat and radius) are known as query parameters, the user and password information that you want to send in your form are known as form parameters. In Sinatra both of these type of parameters are made available in the params hash of a controller.
So in Sinatra you would be able to access your lon parameter as params[:lon] and the user parameter as params[:user].
I suggest using basic or digest authentication and a plain GET request. In other words, your request should be "GET /places?lat=x&lon=x&radius=x" and you should let HTTP handle the authentication. If I understand your situation correctly, this is the ideal approach and will certainly be the most RESTful solution.
As an aside, your URI could be improved. Having verbs ("get") and query-like adjectives ("nearby") in your resource names is not really appropriate. In general, resources should be nouns (ie. "places", "person", "books"). See the example request I wrote above; "get" is redundant because you are using a GET request and "nearby" is redundant because you are already querying by location.

Resources