Structuring secret links in Rails 3 - ruby-on-rails-3

In a rails application I am building I have a resource called buckets.
I am now tasked with creating secret links for these buckets, for example -
http://myapp.dev/x/:secret_unique_hash_for_bucket_one/
- which maps to -
http://myapp.dev/buckets/1
- without exposing the the actual ID.
I also require all of the actions and subresources on this secret link to work such as -
http://myapp.dev/x/:secret_unique_hash_for_bucket_one/edit
http://myapp.dev/x/:secret_unique_hash_for_bucket_one/ideas/1
Finally I also require the url helpers (edit_bucket_path) to retain the secrecy of the resource ID.
How would one best structure this?

When a bucket is created, you could hash you Bucket model attributes (or just some of them, obviously not just the id) to a SHA256 and save it with your model.
Then in your model Bucket model override the to_param method.
def to_param
self.hashed
end
and then your routes will use your hashed attributes instead of defaulting to the id.

Related

What is the REST path naming convention to a nested resource that belongs to the authenticated user?

When an authenticated user wishes to access a resource which he exclusively owns, it seems redundant to specify the user id in the URL path.
Thus, in the following examples, which is the more appropriate way to name my API endpoint?
Example 1
User wants to change profile pic
PUT /users/{id}/profile-pic
or
PUT /profile/profile-pic
Example 2
User wants to add a hobby to his profile
POST /users/{id}/hobbies
or
POST /profile/hobbies
It's a joy to see people paying attention to their API design in terms of URI and responses. An API that is not well designed is going to quickly die since people will avoid using them.
Even if it's not going to be public and no one will use it aside from yourself or your team, think of your colleagues and your future self and take some time to think about how your URIs will look like.
Back to your question my friend, according to the hands-on restful API design patterns and best practices book, that I invite you to read,the REST API is composed of four unique archetypes, as
follows:
Document: The document is the base for a resource representation with a field and link-based structure.
https://api-test.​lufthansa.com/​v1/profiles
https:/​/api-test.lufthansa.​com/​v1/​profiles/​customers
Collection: A collection is also a resource, and it is a directory of resources managed by the API providers or servers.
https:/​/api-​test.​lufthansa.​com/​v1/​profiles/​customers/accountbalance
https:/​/api-​test.lufthansa.​com/​v1/​profiles/​customers/memberstatus
Stores: A store is a resource repository managed by the client. The store allows the API client to put resources in, choose URIs for the resources that get added, get them out, and delete them when it decides.
http:/​/api.example.com/cart-management/users/{id}/carts
http:/​/api.​example.​com/​song-​management/​users/​{id}/playlists
Controller: Controller resources are similar to executable methods, with parameters and return values. REST API relies on controller resources to perform application-specific actions that do not come under any of the CRUD methods.
POST /alerts/245245/resend
So, in your case, you can follow the API design of GitHub API. Look how they are retrieving the projects of an organisation. Yours would look this way:
PUT /users/{id}/profile-pic
POST /users/{id}/hobbies
I'm sorry for making it long, I wanted to base my perspective on something concrete.
When an authenticated user wishes to access a resource which he exclusively owns, it seems redundant to specify the user id in the URL path.
It shouldn't; the semantics of a resource identifier and the semantics of an Authorization header are different.
The fact that only Bob can get a copy of /profile/Bob is a matter of access policy, not message semantics.
Review Fielding's definition of resource. "Bob's profile" and "Alice's profile" are distinct nameable information (assuming for the moment that Bob and Alice are themselves distinct) and therefore should have different identifiers.
That's the "RESTful" answer.
In practice, HTTP has special rules about authentication, and the handling of authenticated requests means that you'll probably "get away with" treating the Authorization header as part of the identifier of the resource (particularly in the case where an authorized user is only allowed to access their own resource hierarchy).

Copy between S3 buckets using signed URL with boto? [duplicate]

I'm using a service that puts the data I need on S3 and gives me a list of presigned URLs to download (http://.s3.amazonaws.com/?AWSAccessKeyID=...&Signature=...&Expires=...).
I want to copy those files into my S3 bucket without having to download them and upload again.
I'm using the Ruby SDK (but willing to try something else if it works..) and couldn't write anything like this.
I was able to initialize the S3 object with my credentials (access_key and secret) that grants me access to my bucket, but how do I pass the "source-side" access_key_id, signature and expires parameters?
To make the problem a bit simpler - I can't even do a GET request to the object using the presigned parameters. (not with regular HTTP, I want to do it through the SDK API).
I found a lot of examples of how to create a presigned URL but nothing about how to authenticate using an already given parameters (I obviously don't have the secret_key of my data provider).
Thanks!
You can't do this with a signed url, but as has been mentioned, if you fetch and upload within EC2 in an appropriate region for the buckets in question, there's essentially no additional cost.
Also worth noting, both buckets do not have to be in the same account, but the aws key that you use to make the request have to have permission to put the target object and get the source object. Permissions can be granted across accounts... though in many cases, that's unlikely to be granted.
http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectCOPY.html
You actually can do a copy with a presigned URL. To do this, you need to create a presigned PUT request that also includes a header like x-amz-copy-source: /sourceBucket/sourceObject in order to specify where you are copying from. In addition, if you want the copied object to have new metadata, you will also need to add the header x-amz-metadata-directive: REPLACE. See the REST API documentation for more details.

define per user custom scope for cognito

How can I define custom scopes on a per user basis using cognito?
For example I have scope resource1.read, resource1.write
I want user A to have resource1.read and resource1.write while user B has resource1.read only.
This is just a high level example. We have tons of different resources and wants to allow customers to manage what resource each user has access to.
I havent found a way to associate scopes with each individual users but only at a per pool level.
Is there a way to achieve this using only cognito or cognito + some AWS manged service or do I have to implement another API to manage the scopes myself?
we couldn't find a way to make scope work on per user basis so we ended up using the custom attributes instead.
if you have less than 25 scopes (cognito max limit) then you can use one attribute per scope. P.S. just be aware you can't rename/remove the attribute once its in place unless you delete the whole pool and start over again.
For example your attributes might look like:
custom:resource1.read : "true"
custom:resource1.write : "false"
custom:resource2.read : "true"
custom:resource2.write : "true"
the idea is simple. instead of having all the scopes defined inside the scopes array we define it in regular custom attributes. When the code checks for scopes just loop thru all fields and find the one with correct prefix.
You could implement your own authorization service and call it with the Pre token generation Lambda trigger:
https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-token-generation.html
This only applies to the ID token though, just like Steve's answer.
You can use this AWS Lambda trigger to customize an identity token before Amazon Cognito generates it. You can use this trigger to add new claims, update claims, or suppress claims in the identity token.

Grails: How to get username of currently logged in user, and what imports does that require?

I simply need the name of the currently logged in user. The same that gets displayed in gsp with <sec:username/>. I'm at a loss as to what to do.
Here's previous answers and questions - they all seem to require some import and none of the solutions works for me:
Grails Spring Security (get current user)
How to get id of the current user logged in grails?
How to get current_user by using Spring Security Grails plugin in GSP
Grails and Spring Security: How do I get the authenticated user from within a controller?
Specifically, I want to access the username insides a controller and put it into a variable of a domain instance in order to persist it (alongsides other information) into the database for logging purposes. Insides that controller, there's nothing to be seen that even remotely refers to Person/User classes, spring security or any other thing that sounds like it might do what I intend.
You can access the domain object corresponding to the logged-in user using springSecurityService.currentUser, and then fetch the username (or whichever other properties you require) from there. Due to Groovy's dynamic nature you don't need to import the domain class or the SpringSecurityService class to do this, simply using an untyped dependency injection
def springSecurityService
and you can access springSecurityService.currentUser?.username (or whatever).
Though as Burt points out in a comment, if it's only the username you want then it is more efficient to use springSecurityService.authentication.principal.username as this does not need to load the user object from the database. You only need currentUser if you want other properties of the user object aside from the ID or username.
Just figured it out: In the controller, def springSecurityService comes just after the opening bracket of the controller class (that's class YourController {) and the variable assignment looks like this: def username = springSecurityService.authentication.principal.getUsername() - apparently playing around with some code posted here has helped me.

Rails 3: How Does One Restrict Access to IDs

This might be very simple; I don't know Rails very well.
I have a match myController/myAction/myID in my routes.rb that will direct hyperlinks to the proper page (using link_to). But here's the problem: I don't want people to be able to freely modify the id parameter, passing in via the URL whatever they like.
Is there a way to perhaps restrict access to routes to the link_to method only? Or maybe there's another way to go about this, using a passed in hidden variable param or something?
Users access you site via urls like: /controller/action/:id right? A user can change an id and must not view another non authorized resource. How to achieve this?, on your controller, return only those resources that user is allowed to access.
For example, suppose that you are using devise:
class AController < ApplicationController
def index
#resouces = current_user.find_all_by_id params[:id]
end
end
This way if the user tries to access something he does not have access to, he will get an error.
Hope this helps, if not please let me know and I'll try to elaborate.
About current_user, yes it is supposed to be the current logged in user, it doesn't have to be devise, you can implement your own session handling logic and then create a helper method to retrieve the currently logged in user.
About using devise, if you don't want to implement your own session handling logic, plus if you want features like:
remember me
already created views that you can fully customize
authentication
authorization
password encryption
many more (please look at the docs for further information)
Then devise is a good way to go.
Also, it is always a great idea, if possible and as a learning exercise, implement your own authentication and authorization layers, you won't regret.
Best regards
Emmanuel Delgado