Spree: how to get the current order for a user without their session? - ruby-on-rails

I'm looking for a way to get the current order for a particular user within a webhook controller so that I can apply a promotion to it. Since it's a webhook controller, I won't have access to the session for the user whose order I need, so I won't be able to use the current_order method within Spree::Core::ControllerHelpers::Order . I'm using spree 2.2.

This logic may provide what you're looking for:
https://github.com/spree/spree/blob/11a24a823780dbc63708b9ba840f4e7696202dc2/core/config/initializers/user_class_extensions.rb#L22-L24
You could also pass the Spree::Order ID along to your webhook controller, and just use that to find the order.

Related

How can I allow only a specific user role to update an attribute to a specific value

Afternoon, got a bit of an issue I am not sure how to resolve.
I am trying to setup some rules that allows only certain types of user roles to update the status attribute on a model to a certain status.
So I looked into doing this with pundit as it seems to be an authorisation issue, however one problem with that is you cannot pass the params to the pundit policy which I would need access too (so I can see what attribute they are trying to change to), and it seems that its bad practise to pass params to a pundit policy.
The next option was to make it a callback in the model, however the problem here is I don’t have access to the current_user inside the callback and again it seems its bad practise to add the current_user helper into a model.
So I am left with perhaps doing it in the controller? Again does not seem the right place for it?
An example to make it a little easier to understand:
I want to allow a User with the role of admin to be allowed to change the status of a post to "resolved", no one else is allowed to change the status to "resolved"
Try this,
create a instance method in User model like bellow,
def is_admin?
self.has_role(:admin) # if you are using rolify
---OR---
self.status == "admin" # if you have status attribute in your user table
end
Then call this method on current_user in edit/update method of post controller. to check current_user is admin or not

Write rule in Cancan or model validation?

Update: After reading the answers, I think I should rephrase my question (as question 3)
From time to time I get confused as to where I should write a some conditional check: in Cancan ability or in ActiveRecord model validation?
As the first example: Say I have a folder model which can be nested. I want prevent deletion of a folder if it is the only child of the parent folder.
This should probably be model logic (as a before_destroy callback). However I would also want to hide the delete button(and block controller action), which seems like the realm of Cancan.
As the second example: I want to prevent deletion of a folder not owned by me.
This will need the use of current_user which is stored in the session. I have the impression that session related condition should not touch the model itself, so this is for Cancan. Is it correct?
Question 3:
If deleting a folder requires both:
current_user is owner check (written as Cancan ability)
folder is not the only child check (written in model as destroyable?())
Should the Cancan ability also call model.destroyable?(), or should I call model.destroyable?() separately (in view and in controller)?
IMO Cancan is about authorization: is the user allowed to delete a given resource based on who they are. Restricting resource deletion based on other criteria falls outside that purview.
This sounds like a combination of authorization and business logic. A view helper might check both if the user can? delete the resource, and that the resource is deletable?.
For the first example, I'd do both: put a before_destroy callback that guards against destroying the last record, and also show the button based on CanCan ability. Just hiding the button would not prevent a POST request being sent to your server and deleting the record.
For the second scenario, put the CanCan check on the controller action, so that the destroy action cannot be called without authorization. It does not belong in the model.

accessing user credentials/permissions

Is there an easy way to get a credential/permission for a particular user?
I've seen the hasCredential() method, but I'd like to dynamically check any user, not just the current user.
I know I can use sfContext::getInstance()->getUser(); to get the current user object, but is there a way to load in any user and get his/her credentials?
Thanks
It kind of depends on the "store" you're using. In Symfony the current user (the one provided with sfContext::getInstance()->getUser()) is more an abstraction of the session.
So the most used plugin for authentication, sfDoctrineGuard, has controllers (sfGuardAuth) which handle things like a a signin form, and once succesful, it will populate the sfUser accordingly.
So, if you want to check for the permissions of a user, you will have to check the underlying store. If you're using sfDoctrineGuard, you can retrieve a User model through sfGuardUserTable::getInstance()->find(...). On the sfGuardUser, you can call the hasPermission($name) function to check.
If you are using sfGuardPlugin or sfDoctrineGuardPlugin you can get all user permissions by calling getAllPermissions() inside actions with:
$this->getUser()->getGuardUser()->getAllPermissions();
or inside models with:
sfContext::getInstance()->getUser()->getGuardUser()->getAllPermissions();
either way you have to call getGuardUser.
if you are using the sfGuardPlugin, you have the sf_guard_user, sf_guard_permission and sf_guard_group tables in your databse. So you can query that tables to obtain the permission of an specific user doing something like:
$c = new Criteria();
$c->add('username',$specificName);
$userPeer = new sfGuardUserPeer();
$user = $userPeer->doSelect($c);
$credentials = $user->getPermissions();
that way in $credentials you'll have an array of all the permissions (permissions are the credentials in sfGuardPlugin) of the selected user.
Good luck

Thoughts regarding model ids in rails routes and validation

I am new to RoR and started working on a typical 'has_many' association (ie. a user has many friends). I have everything working correctly, but I don't like having the ids exposed in the url. I find that I need to add extra validation in my controller to make sure the ids represent valid associations in case the user manually entered different ids.
Personally I would like to see the ids out of the url and passed via some other means but that is not always possible. Shallow nesting of resources will help reduce the number of ids I need to validate at least.
What is the RoR philosophy on this? I have not seen anything specific to this issue.
Thanks
the URL has parameters if it is a GET url.
Try using POST parameters, which means your url will no longer be cluttered. Note that a malicious user can still send a made-up POST request using curl.
My approach to this is implementing proper authorization. If the user requests information for an object he is not permitted to read, this should be handled by an authorization framework.
With CanCan or Declarative Authorization you can define rules that replace your "manual" (and error-prone) checks in controllers.
I like the IDs being in the URL. That is what REST is about. Getting information for specific Resources, which have to be identified with an ID.
You can use Friendly ID in order to replace the integer ID by a slug (e.g. users/tollbooth instead of users/42).
basically ror routes by default takes id as key to generate urls. If you are not fan of id based urls then you can always override urls by using to_param inside model.
def to_param
# make sure this field is always present & unique
username
end
then by default you will start seeing username instead of id inside urls
How to find object inside controller actions
User.find_by_username(params[:id])
If you dont want to do this manually make use of slug gems like friendly id

Steps to create my own authentication system, need some guidance

I want to learn how to create my own authentication system, please provide some guidance if am doing this wrong.
I will create a Module in my /lib folder /lib/auth.rb
I will require this module in my ApplicationController.
when a user enters their email + password, I will call a method that will do a lookup in the user's table for a user with the same email, I will then compare the passwords. (i'll add encryption with salt later).
If the user entered the correct credentials, I will create a row in the Sessions table, and then write the session GUID to a cookie.
Now whenever I need to check if the user is logged in, or I need the user object, I will check if the cookie exists, if it does, I will lookup the session table for a row with the same guid, if it exists, I will return the session row and then load the User object.
I realize there are many suggestions one can give, but in a nutshell does this sound like a workable solution?
Now to make this usable, I will have to make some helper methods in my ApplicationController right?
How will I access the current_user from within my views?
P.S I know of other authentication systems, I just want to learn how to create my own.
The basic logic you're following is correct. Of course you can always expand on this with features that you need. For instance, you'll need helper methods for things like "logged_in?" and "current_user". Also, you might want to add session expiry, or session retention as a "remember me" feature.
Go for it, you won't learn authentication systems better than building your own then figuring what's wrong with it.
You should really check out the authlogic gem on github.
http://github.com/binarylogic/authlogic
It also has great instructions on how to set up your users.
After Faisal said what I would say, I only give you answer to the last part of your question:
"How will I access the current_user from within my views?"
try something like this:
class User < ...
def self.current=(u)
#current = u
end
def self.current
#current
end
end
In your views (or any part of your code) you can call User.current. Your controller has to assign a validated user to User.current. Your filters can react to "if User.current.nil?" and so on.
If you want to be thread safe, you may use a thread variable instead of #current:
Thread.current[:current_user] = u

Resources