Hello all,>From what I''ve seen, this type of question doesn''t really seem to get ananswer on this list as most of the replies relate to failures of RSpec. If this is the case, where is the best place to go to get advice about best practices etc? I have a question about best practice. In some of my controllers only an admin user can perform edit, update, show etc. So I have a before filter in those controllers; ApplicationController#authorise_is_admin The ApplicationController#authorise_is_admin throws an AccessDenied exception and that is caught in ApplicationController#access_denied My question is, in the spec for the calling controller, let''s say ProductGroups, what should I spec? I have a context "user is admin" and that''s easy to spec, but the context "user is not admin" is where I''m stuck as no actions are performed in that controller but I would just like to cover that failure somehow. Any advice? Thanks in advance. -ants -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110117/5c32f33e/attachment-0001.html>
On Mon, Jan 17, 2011 at 9:48 AM, Ants Pants <antsmailinglist at gmail.com>wrote:> Hello all, > > From what I''ve seen, this type of question doesn''t really seem to get an > answer on this list as most of the replies relate to failures of RSpec. If > this is the case, where is the best place to go to get advice about best > practices etc? > > I have a question about best practice. In some of my controllers only an > admin user can perform edit, update, show etc. So I have a before filter in > those controllers; ApplicationController#authorise_is_admin > > The ApplicationController#authorise_is_admin throws an AccessDenied > exception and that is caught in ApplicationController#access_denied > > My question is, in the spec for the calling controller, let''s say > ProductGroups, what should I spec? > > I have a context "user is admin" and that''s easy to spec, but the context > "user is not admin" is where I''m stuck as no actions are performed in that > controller but I would just like to cover that failure somehow. > > Interesting question. I had the same dilemma and decided that it took toomuch effort and test code to test this at the controller level. What I do (and this may or may not work for you depending on your apps security needs), is to have an authorize method in the User model. It returns success or failure based on the controller and action passed. The model looks something like this: def authorize(controller_name, action_name) if self.role current_role = self.role.name else # guest user is empty user current_role = ''guest'' end case controller_name when ''activations'' if current_role != ''guest'' return set_autorize_failure_value("You are already logged in to the system. If you are activating a new user please log out first and try again.") end return authorize_success_message when ''feedback_supports'' if current_role == ''guest'' || current_role == ''sysadmin'' return set_autorize_failure_value(LOGIN_NOTICE) end return authorize_success_message ... end Then in the spec it is real easy: describe "user authorization - guest role" do it "is authorized to access certain pages only" do user = User.new user.authorize(''activations'', ''create'')[:success].should == true user.authorize(''home'', ''index'')[:success].should == false .... end end This might not be everyone''s cup of tea and I am sure I can refactor and make this less verbose, but what I like is having the ''dna'' of all my access rights app wide in one place.> Any advice? > > Thanks in advance. > > -ants > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110117/d99e75d0/attachment.html>
That behaviour exists in ApplicationController. Since ProductGroupsController inherits that behaviour from ApplicationController, the specs for ProductGroupsController should cover that behaviour. To keep your code DRY, create a shared example group that can be used in multiple controller specs. Eg: http://pastie.org/1470751 -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110117/72673e20/attachment.html>
On Jan 17, 2011, at 10:16 AM, David Kahn wrote:> On Mon, Jan 17, 2011 at 9:48 AM, Ants Pants <antsmailinglist at gmail.com> wrote: > Hello all, > > From what I''ve seen, this type of question doesn''t really seem to get an answer on this list as most of the replies relate to failures of RSpec. If this is the case, where is the best place to go to get advice about best practices etc? > > I have a question about best practice. In some of my controllers only an admin user can perform edit, update, show etc. So I have a before filter in those controllers; ApplicationController#authorise_is_admin > > The ApplicationController#authorise_is_admin throws an AccessDenied exception and that is caught in ApplicationController#access_denied > > My question is, in the spec for the calling controller, let''s say ProductGroups, what should I spec? > > I have a context "user is admin" and that''s easy to spec, but the context "user is not admin" is where I''m stuck as no actions are performed in that controller but I would just like to cover that failure somehow. > > Interesting question. I had the same dilemma and decided that it took too much effort and test code to test this at the controller level. What I do (and this may or may not work for you depending on your apps security needs), is to have an authorize method in the User model. It returns success or failure based on the controller and action passed. The model looks something like this: > > def authorize(controller_name, action_name) > if self.role > current_role = self.role.name > else > # guest user is empty user > current_role = ''guest'' > end > > > case controller_name > when ''activations'' > if current_role != ''guest'' > return set_autorize_failure_value("You are already logged in to the system. If you are activating a new user please log out first and try again.") > end > return authorize_success_message > > when ''feedback_supports'' > if current_role == ''guest'' || current_role == ''sysadmin'' > return set_autorize_failure_value(LOGIN_NOTICE) > end > return authorize_success_message > ... > > end > > Then in the spec it is real easy: > > describe "user authorization - guest role" do > it "is authorized to access certain pages only" do > user = User.new > user.authorize(''activations'', ''create'')[:success].should == true > user.authorize(''home'', ''index'')[:success].should == false > > .... > > end > end > > This might not be everyone''s cup of tea and I am sure I can refactor and make this less verbose, but what I like is having the ''dna'' of all my access rights app wide in one place.Definitely agree with the idea of keeping decisions in one place. I don''t really like the idea of ''controllers'' living inside a model, but change ''controller_name'' to ''resource_collection_name'' and that solves that problem. I would still want to specify that the controller asks the user for authorization. WDYT? -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110117/155f0003/attachment.html>
When I read the David Kahn''s response, I thought it strange to have controller code in the model and was going to query him on it so I''m glad that others think the same. As for Nick''s response, I will look into it_behaves_like (I remember seeing that in the book with a pizza example) It sounds good to me. Thanks for the replies. -ants On 17 January 2011 20:43, David Chelimsky <dchelimsky at gmail.com> wrote:> On Jan 17, 2011, at 10:16 AM, David Kahn wrote: > > On Mon, Jan 17, 2011 at 9:48 AM, Ants Pants <antsmailinglist at gmail.com>wrote: > >> Hello all, >> >> From what I''ve seen, this type of question doesn''t really seem to get an >> answer on this list as most of the replies relate to failures of RSpec. If >> this is the case, where is the best place to go to get advice about best >> practices etc? >> >> I have a question about best practice. In some of my controllers only an >> admin user can perform edit, update, show etc. So I have a before filter in >> those controllers; ApplicationController#authorise_is_admin >> >> The ApplicationController#authorise_is_admin throws an AccessDenied >> exception and that is caught in ApplicationController#access_denied >> >> My question is, in the spec for the calling controller, let''s say >> ProductGroups, what should I spec? >> >> I have a context "user is admin" and that''s easy to spec, but the context >> "user is not admin" is where I''m stuck as no actions are performed in that >> controller but I would just like to cover that failure somehow. >> >> Interesting question. I had the same dilemma and decided that it took too > much effort and test code to test this at the controller level. What I do > (and this may or may not work for you depending on your apps security > needs), is to have an authorize method in the User model. It returns success > or failure based on the controller and action passed. The model looks > something like this: > > def authorize(controller_name, action_name) > if self.role > current_role = self.role.name > else > # guest user is empty user > current_role = ''guest'' > end > > > case controller_name > when ''activations'' > if current_role != ''guest'' > return set_autorize_failure_value("You are already logged in to the > system. If you are activating a new user please log out first and try > again.") > end > return authorize_success_message > > when ''feedback_supports'' > if current_role == ''guest'' || current_role == ''sysadmin'' > return set_autorize_failure_value(LOGIN_NOTICE) > end > return authorize_success_message > ... > > end > > Then in the spec it is real easy: > > describe "user authorization - guest role" do > it "is authorized to access certain pages only" do > user = User.new > user.authorize(''activations'', ''create'')[:success].should == true > user.authorize(''home'', ''index'')[:success].should == false > > .... > > end > end > > This might not be everyone''s cup of tea and I am sure I can refactor and > make this less verbose, but what I like is having the ''dna'' of all my access > rights app wide in one place. > > > Definitely agree with the idea of keeping decisions in one place. I don''t > really like the idea of ''controllers'' living inside a model, but change > ''controller_name'' to ''resource_collection_name'' and that solves that > problem. > > I would still want to specify that the controller asks the user for > authorization. WDYT? > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110118/41652ee4/attachment.html>
> On 17 January 2011 20:43, David Chelimsky <dchelim... at gmail.com> wrote: > > > On Jan 17, 2011, at 10:16 AM, David Kahn wrote: > > > On Mon, Jan 17, 2011 at 9:48 AM, Ants Pants <antsmailingl... at gmail.com>wrote: > > >> Hello all, > > >> From what I''ve seen, this type of question doesn''t really seem to get an > >> answer on this list as most of the replies relate to failures of RSpec. If > >> this is the case, where is the best place to go to get advice about best > >> practices etc? > > >> I have a question about best practice. In some of my controllers only an > >> admin user can perform edit, update, show etc. So I have a before filter in > >> those controllers; ApplicationController#authorise_is_admin > > >> The ApplicationController#authorise_is_admin throws an AccessDenied > >> exception and that is caught in ApplicationController#access_denied > > >> My question is, in the spec for the calling controller, let''s say > >> ProductGroups, what should I spec? > > >> I have a context "user is admin" and that''s easy to spec, but the context > >> "user is not admin" is where I''m stuck as no actions are performed in that > >> controller but I would just like to cover that failure somehow. > > >> Interesting question. I had the same dilemma and decided that it took too > > much effort and test code to test this at the controller level. What I do > > (and this may or may not work for you depending on your apps security > > needs), is to have an authorize method in the User model. It returns success > > or failure based on the controller and action passed. The model looks > > something like this: > > > ? def authorize(controller_name, action_name) > > ? ? if self.role > > ? ? ? current_role = self.role.name > > ? ? else > > ? ? ? # guest user is empty user > > ? ? ? current_role = ''guest'' > > ? ? end > > > ? ? case controller_name > > ? ? when ''activations'' > > ? ? ? if current_role != ''guest'' > > ? ? ? ? return set_autorize_failure_value("You are already logged in to the > > system. If you are activating a new user please log out first and try > > again.") > > ? ? ? end > > ? ? ? return authorize_success_message > > > ? ? when ''feedback_supports'' > > ? ? ? if current_role == ''guest'' || current_role == ''sysadmin'' > > ? ? ? ? return set_autorize_failure_value(LOGIN_NOTICE) > > ? ? ? end > > ? ? ? return authorize_success_message > > ... > > > end > > > Then in the spec it is real easy: > > > ? describe "user authorization - guest role" do > > ? ? it "is authorized to access certain pages only" do > > ? ? ? user = User.new > > ? ? ? user.authorize(''activations'', ''create'')[:success].should == true > > ? ? ? user.authorize(''home'', ''index'')[:success].should == false > > > ? ? .... > > > ? ? end > > ? end > > > This might not be everyone''s cup of tea and I am sure I can refactor and > > make this less verbose, but what I like is having the ''dna'' of all my access > > rights app wide in one place. > > > Definitely agree with the idea of keeping decisions in one place. I don''t > > really like the idea of ''controllers'' living inside a model, but change > > ''controller_name'' to ''resource_collection_name'' and that solves that > > problem. > > > I would still want to specify that the controller asks the user for > > authorization. WDYT? >On Jan 18, 2:30 am, Ants Pants <antsmailingl... at gmail.com> wrote:> When I read the David Kahn''s response, I thought it strange to have > controller code in the model and was going to query him on it so I''m glad > that others think the same. > > As for Nick''s response, I will look into it_behaves_like (I remember seeing > that in the book with a pizza example) It sounds good to me. > > Thanks for the replies. > > -ants >One thing you might consider is to separate your admin functionality from your regular user functionality. This isn''t for everyone, but it might work out well for you. I used to do it the way you are currently doing it, and it was okay until I ran into some situations in which a regular user and an admin could edit a resource, but only the admin could edit certain attributes. The logic in my controller started putting on a bit of weight. My practice now is to separate appropriate functionality into namespaced controllers. All features available for users not logged in go in the default namespace, all features requiring the user to be logged in (but not administrative) go in the / user/ namespace, and all features that require administrative rights go into the /admin/ namespace. Then, in each namespace, I create a base controller that does the authorization/redirection and all other controllers in the namespace inherit from it. Something like: https://gist.github.com/784363 Doing it this way does create seeming duplication in the views, but partials can be used very easily to deal with that. This method also more or less demands a menu structure that completely separates the functionality of each role, but in the projects that I''ve been working on, that has been a benefit. To have all of the role-based functionality separated in controllers and menus has provided clear lines that are easy to follow. In short, it has worked out really well. But, as always, your own mileage may vary. Peace. Phillip
On 18 January 2011 12:28, Phillip Koebbe <phillipkoebbe at gmail.com> wrote:> > > > On 17 January 2011 20:43, David Chelimsky <dchelim... at gmail.com> wrote: > > > > > On Jan 17, 2011, at 10:16 AM, David Kahn wrote: > > > > > On Mon, Jan 17, 2011 at 9:48 AM, Ants Pants <antsmailingl... at gmail.com > >wrote: > > > > >> Hello all, > > > > >> From what I''ve seen, this type of question doesn''t really seem to get > an > > >> answer on this list as most of the replies relate to failures of > RSpec. If > > >> this is the case, where is the best place to go to get advice about > best > > >> practices etc? > > > > >> I have a question about best practice. In some of my controllers only > an > > >> admin user can perform edit, update, show etc. So I have a before > filter in > > >> those controllers; ApplicationController#authorise_is_admin > > > > >> The ApplicationController#authorise_is_admin throws an AccessDenied > > >> exception and that is caught in ApplicationController#access_denied > > > > >> My question is, in the spec for the calling controller, let''s say > > >> ProductGroups, what should I spec? > > > > >> I have a context "user is admin" and that''s easy to spec, but the > context > > >> "user is not admin" is where I''m stuck as no actions are performed in > that > > >> controller but I would just like to cover that failure somehow. > > > > >> Interesting question. I had the same dilemma and decided that it took > too > > > much effort and test code to test this at the controller level. What I > do > > > (and this may or may not work for you depending on your apps security > > > needs), is to have an authorize method in the User model. It returns > success > > > or failure based on the controller and action passed. The model looks > > > something like this: > > > > > def authorize(controller_name, action_name) > > > if self.role > > > current_role = self.role.name > > > else > > > # guest user is empty user > > > current_role = ''guest'' > > > end > > > > > case controller_name > > > when ''activations'' > > > if current_role != ''guest'' > > > return set_autorize_failure_value("You are already logged in to > the > > > system. If you are activating a new user please log out first and try > > > again.") > > > end > > > return authorize_success_message > > > > > when ''feedback_supports'' > > > if current_role == ''guest'' || current_role == ''sysadmin'' > > > return set_autorize_failure_value(LOGIN_NOTICE) > > > end > > > return authorize_success_message > > > ... > > > > > end > > > > > Then in the spec it is real easy: > > > > > describe "user authorization - guest role" do > > > it "is authorized to access certain pages only" do > > > user = User.new > > > user.authorize(''activations'', ''create'')[:success].should == true > > > user.authorize(''home'', ''index'')[:success].should == false > > > > > .... > > > > > end > > > end > > > > > This might not be everyone''s cup of tea and I am sure I can refactor > and > > > make this less verbose, but what I like is having the ''dna'' of all my > access > > > rights app wide in one place. > > > > > Definitely agree with the idea of keeping decisions in one place. I > don''t > > > really like the idea of ''controllers'' living inside a model, but change > > > ''controller_name'' to ''resource_collection_name'' and that solves that > > > problem. > > > > > I would still want to specify that the controller asks the user for > > > authorization. WDYT? > > > On Jan 18, 2:30 am, Ants Pants <antsmailingl... at gmail.com> wrote: > > When I read the David Kahn''s response, I thought it strange to have > > controller code in the model and was going to query him on it so I''m glad > > that others think the same. > > > > As for Nick''s response, I will look into it_behaves_like (I remember > seeing > > that in the book with a pizza example) It sounds good to me. > > > > Thanks for the replies. > > > > -ants > > > > One thing you might consider is to separate your admin functionality > from your regular user functionality. This isn''t for everyone, but it > might work out well for you. I used to do it the way you are currently > doing it, and it was okay until I ran into some situations in which a > regular user and an admin could edit a resource, but only the admin > could edit certain attributes. The logic in my controller started > putting on a bit of weight. My practice now is to separate appropriate > functionality into namespaced controllers. All features available for > users not logged in go in the default namespace, all features > requiring the user to be logged in (but not administrative) go in the / > user/ namespace, and all features that require administrative rights > go into the /admin/ namespace. Then, in each namespace, I create a > base controller that does the authorization/redirection and all other > controllers in the namespace inherit from it. Something like: > > https://gist.github.com/784363 > > Doing it this way does create seeming duplication in the views, but > partials can be used very easily to deal with that. This method also > more or less demands a menu structure that completely separates the > functionality of each role, but in the projects that I''ve been working > on, that has been a benefit. To have all of the role-based > functionality separated in controllers and menus has provided clear > lines that are easy to follow. In short, it has worked out really > well. But, as always, your own mileage may vary. > > Peace. > Phillip > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >+1 for the many controllers for one model pattern, you can really simplify controllers with this approach. Having a separate view hierarchy for each of these controllers also makes for simpler views, with less logic. However you have be very disciplined with your view partials, or the number of files can get a bit confusing All best Andrew -- ------------------------ Andrew Premdas blog.andrew.premdas.org -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110121/d368f1c5/attachment.html>
On Mon, Jan 17, 2011 at 1:43 PM, David Chelimsky <dchelimsky at gmail.com>wrote:> On Jan 17, 2011, at 10:16 AM, David Kahn wrote: > > On Mon, Jan 17, 2011 at 9:48 AM, Ants Pants <antsmailinglist at gmail.com>wrote: > >> Hello all, >> >> From what I''ve seen, this type of question doesn''t really seem to get an >> answer on this list as most of the replies relate to failures of RSpec. If >> this is the case, where is the best place to go to get advice about best >> practices etc? >> >> I have a question about best practice. In some of my controllers only an >> admin user can perform edit, update, show etc. So I have a before filter in >> those controllers; ApplicationController#authorise_is_admin >> >> The ApplicationController#authorise_is_admin throws an AccessDenied >> exception and that is caught in ApplicationController#access_denied >> >> My question is, in the spec for the calling controller, let''s say >> ProductGroups, what should I spec? >> >> I have a context "user is admin" and that''s easy to spec, but the context >> "user is not admin" is where I''m stuck as no actions are performed in that >> controller but I would just like to cover that failure somehow. >> >> Interesting question. I had the same dilemma and decided that it took too > much effort and test code to test this at the controller level. What I do > (and this may or may not work for you depending on your apps security > needs), is to have an authorize method in the User model. It returns success > or failure based on the controller and action passed. The model looks > something like this: > > def authorize(controller_name, action_name) > if self.role > current_role = self.role.name > else > # guest user is empty user > current_role = ''guest'' > end > > > case controller_name > when ''activations'' > if current_role != ''guest'' > return set_autorize_failure_value("You are already logged in to the > system. If you are activating a new user please log out first and try > again.") > end > return authorize_success_message > > when ''feedback_supports'' > if current_role == ''guest'' || current_role == ''sysadmin'' > return set_autorize_failure_value(LOGIN_NOTICE) > end > return authorize_success_message > ... > > end > > Then in the spec it is real easy: > > describe "user authorization - guest role" do > it "is authorized to access certain pages only" do > user = User.new > user.authorize(''activations'', ''create'')[:success].should == true > user.authorize(''home'', ''index'')[:success].should == false > > .... > > end > end > > This might not be everyone''s cup of tea and I am sure I can refactor and > make this less verbose, but what I like is having the ''dna'' of all my access > rights app wide in one place. > > > Definitely agree with the idea of keeping decisions in one place. I don''t > really like the idea of ''controllers'' living inside a model, but change > ''controller_name'' to ''resource_collection_name'' and that solves that > problem. > > I would still want to specify that the controller asks the user for > authorization. WDYT? >If I am following the thread right, I should clarify that I call ''authorize'' from the application controller -- should have included this in my first response. It is from there that the User model is invoked: class ApplicationController < ActionController::Base private def authorize if current_user return_value = current_user.authorize(controller_name, action_name) else return_value = User.new.authorize(controller_name, action_name) end if !return_value[:success] flash[:notice] = return_value[:failure_message] if current_user redirect_to home_path else redirect_to login_path end end end end I would not venture to say what I have is the best but just what came up from refactoring in search of simplifying my code and tests. I also do not like the idea of anything dependent like a session or params or something else from a controller getting coupled to a model, but in this case it feels to me that the model is just a switchboard in decision-making, which for me I would consider business logic.> > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110121/9abe6b18/attachment.html>
Yes, I like that response and I do do that to a limited fashion. Maybe I should think about using it more widely. -ants On 18 January 2011 13:28, Phillip Koebbe <phillipkoebbe at gmail.com> wrote:> > > > On 17 January 2011 20:43, David Chelimsky <dchelim... at gmail.com> wrote: > > > > > On Jan 17, 2011, at 10:16 AM, David Kahn wrote: > > > > > On Mon, Jan 17, 2011 at 9:48 AM, Ants Pants <antsmailingl... at gmail.com > >wrote: > > > > >> Hello all, > > > > >> From what I''ve seen, this type of question doesn''t really seem to get > an > > >> answer on this list as most of the replies relate to failures of > RSpec. If > > >> this is the case, where is the best place to go to get advice about > best > > >> practices etc? > > > > >> I have a question about best practice. In some of my controllers only > an > > >> admin user can perform edit, update, show etc. So I have a before > filter in > > >> those controllers; ApplicationController#authorise_is_admin > > > > >> The ApplicationController#authorise_is_admin throws an AccessDenied > > >> exception and that is caught in ApplicationController#access_denied > > > > >> My question is, in the spec for the calling controller, let''s say > > >> ProductGroups, what should I spec? > > > > >> I have a context "user is admin" and that''s easy to spec, but the > context > > >> "user is not admin" is where I''m stuck as no actions are performed in > that > > >> controller but I would just like to cover that failure somehow. > > > > >> Interesting question. I had the same dilemma and decided that it took > too > > > much effort and test code to test this at the controller level. What I > do > > > (and this may or may not work for you depending on your apps security > > > needs), is to have an authorize method in the User model. It returns > success > > > or failure based on the controller and action passed. The model looks > > > something like this: > > > > > def authorize(controller_name, action_name) > > > if self.role > > > current_role = self.role.name > > > else > > > # guest user is empty user > > > current_role = ''guest'' > > > end > > > > > case controller_name > > > when ''activations'' > > > if current_role != ''guest'' > > > return set_autorize_failure_value("You are already logged in to > the > > > system. If you are activating a new user please log out first and try > > > again.") > > > end > > > return authorize_success_message > > > > > when ''feedback_supports'' > > > if current_role == ''guest'' || current_role == ''sysadmin'' > > > return set_autorize_failure_value(LOGIN_NOTICE) > > > end > > > return authorize_success_message > > > ... > > > > > end > > > > > Then in the spec it is real easy: > > > > > describe "user authorization - guest role" do > > > it "is authorized to access certain pages only" do > > > user = User.new > > > user.authorize(''activations'', ''create'')[:success].should == true > > > user.authorize(''home'', ''index'')[:success].should == false > > > > > .... > > > > > end > > > end > > > > > This might not be everyone''s cup of tea and I am sure I can refactor > and > > > make this less verbose, but what I like is having the ''dna'' of all my > access > > > rights app wide in one place. > > > > > Definitely agree with the idea of keeping decisions in one place. I > don''t > > > really like the idea of ''controllers'' living inside a model, but change > > > ''controller_name'' to ''resource_collection_name'' and that solves that > > > problem. > > > > > I would still want to specify that the controller asks the user for > > > authorization. WDYT? > > > On Jan 18, 2:30 am, Ants Pants <antsmailingl... at gmail.com> wrote: > > When I read the David Kahn''s response, I thought it strange to have > > controller code in the model and was going to query him on it so I''m glad > > that others think the same. > > > > As for Nick''s response, I will look into it_behaves_like (I remember > seeing > > that in the book with a pizza example) It sounds good to me. > > > > Thanks for the replies. > > > > -ants > > > > One thing you might consider is to separate your admin functionality > from your regular user functionality. This isn''t for everyone, but it > might work out well for you. I used to do it the way you are currently > doing it, and it was okay until I ran into some situations in which a > regular user and an admin could edit a resource, but only the admin > could edit certain attributes. The logic in my controller started > putting on a bit of weight. My practice now is to separate appropriate > functionality into namespaced controllers. All features available for > users not logged in go in the default namespace, all features > requiring the user to be logged in (but not administrative) go in the / > user/ namespace, and all features that require administrative rights > go into the /admin/ namespace. Then, in each namespace, I create a > base controller that does the authorization/redirection and all other > controllers in the namespace inherit from it. Something like: > > https://gist.github.com/784363 > > Doing it this way does create seeming duplication in the views, but > partials can be used very easily to deal with that. This method also > more or less demands a menu structure that completely separates the > functionality of each role, but in the projects that I''ve been working > on, that has been a benefit. To have all of the role-based > functionality separated in controllers and menus has provided clear > lines that are easy to follow. In short, it has worked out really > well. But, as always, your own mileage may vary. > > Peace. > Phillip > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110122/c0c26097/attachment.html>
On 2011-01-21 10:25 PM, David Kahn wrote:> > > On Mon, Jan 17, 2011 at 1:43 PM, David Chelimsky <dchelimsky at gmail.com > <mailto:dchelimsky at gmail.com>> wrote: > > On Jan 17, 2011, at 10:16 AM, David Kahn wrote: > >> On Mon, Jan 17, 2011 at 9:48 AM, Ants Pants >> <antsmailinglist at gmail.com <mailto:antsmailinglist at gmail.com>> wrote: >> >> Hello all, >> >> From what I''ve seen, this type of question doesn''t really >> seem to get an answer on this list as most of the replies >> relate to failures of RSpec. If this is the case, where is >> the best place to go to get advice about best practices etc? >> >> I have a question about best practice. In some of my >> controllers only an admin user can perform edit, update, show >> etc. So I have a before filter in those controllers; >> ApplicationController#authorise_is_admin >> >> The ApplicationController#authorise_is_admin throws an >> AccessDenied exception and that is caught >> in ApplicationController#access_denied >> >> My question is, in the spec for the calling controller, let''s >> say ProductGroups, what should I spec? >> >> I have a context "user is admin" and that''s easy to spec, but >> the context "user is not admin" is where I''m stuck as no >> actions are performed in that controller but I would just >> like to cover that failure somehow. >> >> Interesting question. I had the same dilemma and decided that it >> took too much effort and test code to test this at the controller >> level. What I do (and this may or may not work for you depending >> on your apps security needs), is to have an authorize method in >> the User model. It returns success or failure based on the >> controller and action passed. The model looks something like this: >> >> def authorize(controller_name, action_name) >> if self.role >> current_role = self.role.name <http://self.role.name/> >> else >> # guest user is empty user >> current_role = ''guest'' >> end >> >> >> case controller_name >> when ''activations'' >> if current_role != ''guest'' >> return set_autorize_failure_value("You are already logged >> in to the system. If you are activating a new user please log out >> first and try again.") >> end >> return authorize_success_message >> >> when ''feedback_supports'' >> if current_role == ''guest'' || current_role == ''sysadmin'' >> return set_autorize_failure_value(LOGIN_NOTICE) >> end >> return authorize_success_message >> ... >> >> end >> >> Then in the spec it is real easy: >> >> describe "user authorization - guest role" do >> it "is authorized to access certain pages only" do >> user = User.new >> user.authorize(''activations'', ''create'')[:success].should =>> true >> user.authorize(''home'', ''index'')[:success].should == false >> >> .... >> >> end >> end >> >> This might not be everyone''s cup of tea and I am sure I can >> refactor and make this less verbose, but what I like is having >> the ''dna'' of all my access rights app wide in one place. > > Definitely agree with the idea of keeping decisions in one place. > I don''t really like the idea of ''controllers'' living inside a > model, but change ''controller_name'' to ''resource_collection_name'' > and that solves that problem. > > I would still want to specify that the controller asks the user > for authorization. WDYT? > > > If I am following the thread right, I should clarify that I call > ''authorize'' from the application controller -- should have included > this in my first response. It is from there that the User model is > invoked: > > class ApplicationController < ActionController::Base > > private > > def authorize > if current_user > return_value = current_user.authorize(controller_name, > action_name) > else > return_value = User.new.authorize(controller_name, action_name) > end > > if !return_value[:success] > flash[:notice] = return_value[:failure_message] > if current_user > redirect_to home_path > else > redirect_to login_path > end > end > end > > end > > > I would not venture to say what I have is the best but just what came > up from refactoring in search of simplifying my code and tests. I also > do not like the idea of anything dependent like a session or params or > something else from a controller getting coupled to a model, but in > this case it feels to me that the model is just a switchboard in > decision-making, which for me I would consider business logic. > >Keeping the "cup of tea" idea in mind, I can understand how you could arrive at this point. I did something similar a couple of years ago. I was passing something into the model that could come only from a controller, which was fine and dandy until I needed to use the model method in some way that proved difficult to get that information. It may have been a rake task, I can''t remember the exact details. But from that point, I started thinking about my models as separate from the particular application I was working on at the moment. When creating methods and passing in arguments, I started thinking about what would happen if I used this model outside of the Rails application. Would I still be able to pass that information in easily and would it make sense? The other thought that I had with this particular approach (passing controller name and action into the model) is that it seems the model specs would feel sort of fake. In order to unit test the authorize method, you have to test with real values. I may be overlooking something (just got up, so it''s very possible), but that doesn''t seem like it''s testing behavior so much as how the method handles specific values. If you add new controllers and/or actions, you must then go add them to your unit test to make sure the authorize method responds correctly. That doesn''t seem like the kind of test that I''d feel comfortable trusting. Now that I think about it, I actually had this same situation come up a couple of years ago. Before I started splitting role-based functionality into separate namespaced controllers, I was trying to control access to various actions via before_filters. Using the same concept of controller/action combination, I created a hash of those pairs and which roles could access them. The role names then turned into model methods that returned true/false. Something like: access_rules = { ''products'' => { ''show'' => [:clerk, :manager], ''new'' => [:manager], ''edit'' => [:manager] }, ''stores'' => { ''show'' => [:clerk, :manager], ''new'' => [:manager], ''edit'' => [:manager] } } roles = access_rules[controller_name][action_name] return true if roles.any? { |role| current_user.send("#{role}?") } return false Something like that anyway. I even went so far as to write a controller plugin so I could simplify it to allow_access(:clerk, :only => [:show]) allow_access(:manager, :only => [:show, :new, :edit]) verify_access This is obviously a contrived example, but it should suffice to get the point across. Bringing this back to RSpec, specs make more sense this way as you can check to make sure your role methods on the user model return true or false based on a database setting and you can easily mock/stub those methods in the controller specs to make sure that you don''t get to actions that you shouldn''t. By the way, I prefer English breakfast tea and Indian spiced chai. :) Peace. Phillip