Topic: Devise, CanCan and roles.

My application is getting large and in need of a serious authorization overhaul. Its a hosted competition management application for multiple sports orgs to manage their team sports comps.

The structure is

sports orgs, super_users

sports,  members, admin_users

leagues

games,  teams


Additionally, there will be a players table linking members and teams through players.

Currently I rolled my own auth as described in agile web development, super users have access to everything while admin users only have access to everything in their sports org.

There is also a public section with game result reports using skip_before_filter :authorize.

Now I want to allow visitors to register as members, edit their own profile and nominate as a player for a team. The registrations will be approved by an admin user who will also have CRUD access to the users table and can associate them with teams as player or team contact.

A user may also query fees due as a player or as a team contact and pay them with active_merchant and paypal.

Effectively I need at least 5 roles.

1. super users: me

2. admin_users: limited only by sports_org_id

3. team_contacts: users nominated as team contacts (field in teams) who can enter game results and pay game fees.

4. users: registered users as described above with crud access over their own profile.

5. public: able to view public reports without any login. bots welcome.

Can Cancan handle a role structure like this? I assume I will refactor Members to Users for Devise.

The current auth structure restricts access by controller but cancan appears to do it by model. Does this mean that attempts to get a url which interacts with a model will redirect them to the login url if they don't have the correct model access permissions?

If I made a self method in User to get fees owing, can I call that from any controller if the user has access to the users model only? Could they then pay the fee owing?

Last edited by markhorrocks (2011-09-04 07:00:12)

Re: Devise, CanCan and roles.

what about STI for the user model
that way your controllers can work with the correct/expected implementation and you can keep your business logic for each role in each implementation of the user model.

One thing I would suggest is that you split admin out into a seperate app/url. that way the server does not have the overhead/memory footprint of having to load all the admin stuff for every visitor which can be quite expensive in terms of server resources needed for every visitor. Basically makes your app much more scaleable

I would always suggest that you roll your own logic as that gives you complete control

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: Devise, CanCan and roles.

The separate app had occurred to me and now seems like the easiest way.

All I need Devise for is to allow users to edit their own user profile which I plan to expand beyond the default Devise users. They could also select a team to associate with. In this way i would not need cancan.

I see a problem here already and that is that if a user is deleted I want to remove any user_id from other dependent tables. I would then need to duplicate Models across both apps?

Would I need at least a sub domain or can I run a separate Rails app from my_domain.com/other_stuff?

Last edited by markhorrocks (2011-09-04 10:58:24)

Re: Devise, CanCan and roles.

yeah, user deletion is a problem for any app. Indeed, most of my apps end up not deleting anything at all, just archiving stuff.
I would have a sub domain.

There are ways to make a route to a subdomain look like part of the same app but I'm not familiar with them.

Model duplication is an issue! This does need a lot of careful thought and planning. A couple of other options I can think of are
1) Write your models as gems - Tried this myself and I never felt very comfortable with this approach
2) maybe write rails metal server - An approach I'm thinking of using for a similar scenario.
3) Maybe even a mountable engine as per this Railscast http://railscasts.com/episodes/277-mountable-engines

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: Devise, CanCan and roles.

I don't really need to make the new app like like part of the same app. There are only 2 models beside users that I would need to interact with so that may be the answer. In @user.destroy I would just write some code that removes the player dependent records and team.user_id. Can I just add in the 2 extra models and do that?

Re: Devise, CanCan and roles.

In that case I would use modules - something like this maybe

module Shared
  module User
    module ClassMethods
      #All your public class methods here

      protected
      #all your protected class methods here
      private
      #all your private class methods here
    end

    #All your instance methods here

#   extend host class with class methods when we're included
    def self.included(host_class)
      host_class.extend(ClassMethods)
    end

  end

end

Then whenever you make a change to the module make sure you copy it into both apps or make it into a gem for version control. Either way you get to share the logic across both apps

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: Devise, CanCan and roles.

Yes, thanks for that. I have already been using modules extensively and they are great for eliminating code repetition. That was very hard to achieve in Delphi.

Re: Devise, CanCan and roles.

Yeah, modules bring a form of multiple inheritance which Delphi does not have. However much I love Delphi and will defend it to the last agree, sharing code in this way is difficult. Having said that I typically create all my own controls and use inheritance extensively when using Delphi giving me an object hierarchy which is quite easily extensible but as you say, sharing code between different base classes is not easy and has to be done in libraries.
Good luck, I'd love to know how you get on and which route you choose

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: Devise, CanCan and roles.

I have been thinking about the user model a lot.

1. The application is a hosted comp management app and has many distinct client organisations.

2. I want members (often over a thousand per organisation) to be able to register and edit their profiles online with devise. Ideally, I would not like them to be able to delete their profiles but it seems only fair to allow them to do that. Also gets rid of unneeded entries.

3. The members (users) are used for a variety of purposes within the app. They may be a team captain and able to record results or, as ordinary players, the number of games they play during a season may be counted to ensure they are qualified to play in the finals (usually 3 times). As such, it would play havoc for the system if members could delete their records ad hoc (or at all).

Would it be reasonable to retain the current Members table for this purpose and then have a Users table with a 1 to 1 link to Members. I could then use the Users profile data for the Member. There need not be a User to match each Member but the process of creating a User (devise registerable) will also create a Member with the User id and stub  data like the name and email address. I would still over ride the Member email with the User email but not the Member Name.

This kind of database design with two similar tables for Users and Members and 1 to 1 relationship seems anathema to me but it seems to be the only workable solution.

I would have a completely different application authorized by Devise handling the Users with a Users model in the admin application as well. Does this plan seem reasonable?

Re: Devise, CanCan and roles.

1. Fair enough

2. Why with devise? you have way more control rolling your own profile html erb smile Deleting - maybe could just be setting a visible flag false with an eMail to admin review and remove?

3. Simple enough, have roles of team captain and player and define rules appropriately smile As far as deleting is concerned. If  player is deleted they won''t be eligible for the finals anyway would they? If they were then just change the way the deelte works for each STI model

4)

Would it be reasonable to retain the current Members table for this purpose and then have a Users table with a 1 to 1 link to Members. I could then use the Users profile data for the Member. There need not be a User to match each Member but the process of creating a User (devise registerable) will also create a Member with the User id and stub  data like the name and email address. I would still over ride the Member email with the User email but not the Member Name.

I suggest you stop thinking about devise and start thinking about writing (must take all of a day) your own profile solution as per AWDWR or the roll your own as per http://railscasts.com/episodes/250-auth … om-scratch which is almost identical to AWDWR plus http://railscasts.com/episodes/235-omniauth-part-1 (and part 2). you will be able to do what you want smile

This kind of database design with two similar tables for Users and Members and 1 to 1 relationship seems anathema to me but it seems to be the only workable solution.

Me too! An STI solution I think would solve this smile Once you have the right solution, you will know! Things will just fit and you will start thinking "Well all I have to do is add some logic to this class and all will be good without affecting any other class"

p.s. - Do I know you? The name seems familiar and you have a Delphi background!

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: Devise, CanCan and roles.

The main issue for me is the deletes. Everything would work just fine if they couldn't delete the user they created and can edit. The problem for deletion is player statistics, fees owing and all that.

Currently, I have Members, not users but am just beginning the player integration so refactoring members to users would be a minor headache.

Options:

1. Keep Members, roll my own authorization and keep it all in the same app. There is no need for STI as Users and members are just the same. Some are players, some are team captains and some are just members. Somehow this option seems the most attractive. Maybe I could refactor Members to Users before I start for no other reason than I have some compatibility potential with Devise and Cancan down the road. Also code snippets are easier to use; we all know that programming is just cut / paste / edit / test / go_to_lunch, right?

You may know me from my Delphi days from around 2000 to 2006. I used to lurk and ask a lot in the isapi web broker forum then. In the end it got too hard to maintain and upgrade my app in Delphi.

1. I had to buy a new version (very expensive) every couple of years because they kept changing the technology like dumping com. My app still runs on win2003.

2. Hardly anybody really used it for web apps. They had a js framework called InternetExpress which used an xml dataset to load, navigate and edit multiple records with only a single trip back to the server to update. Sadly, the js relied on ie extensions and wouldn't work in other browsers, plus it had to be hacked to be usable and wasn't maintained.

In the end it wasn't too hard a decision to just change to RoR and Ubuntu, except that my app had been funded by an Aust. govt. grant and got pretty big.  Many of the queries i used in Delphi I simply can't figure out how to convert to ActiveRecord. I rely a lot on find_by_sql.

2. Refactor Members to Users. Make a separate app for users because my current auth system could be a headache to integrate with Devise. Override or hack the Devise destroy code to just mark the user as inactive. This is a technique i use with teams. Teams can't be destroyed if they have games with results. Problem : I have to hack Devise code to add a foreign key to users to restrict them to a sports org. Super admins don't have a sports org so complete integration could be problematic. Advantage: somebody else wrote it and is developing it.

Last edited by markhorrocks (2011-09-06 23:09:07)

Re: Devise, CanCan and roles.

maybe member could descend from user giving you your first STI implementation. Just a thought!
You mention using Devise but really I wouldn't.  you won't get the flexibility you need.
I'm wondering what you think Devise will give you that you can't do for yourself? Perhaps I can clear something up!

If you need a hand sorting out your find_by sql stuff I might be able to help with that!

If you roll your own solution then it is simple enough to add delete logic to the relevant STI class to warn the member that they can't delete their account until fees have been paid. Maybe a can_delete? method then an adjustment to the delete method on the model to archive rather than delete once business logic passes validations?

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: Devise, CanCan and roles.

Hi Mark,
We have a similar architecture that of yours
viz. Customers, Users, other models storing datas.
Users belongs to Customers.

Analyzing how to handle 'super admin' using devise and cancan.

Same as yours 'Super admin' is me

Can you tell me how to create super admin and how to authenticate using devise.

The problem is I have a customer_id associated with every user record.