Spree Custom Roles Permissions - cancan

I am trying to give some custom roles within spree specific permissions.
Cant find this answer anywhere
role_ability.rb
class RoleAbility
include CanCan::Ability
def initialize(user)
user || User.new # for guest
if user.has_role? "admin"
can :manage, :all
elsif user.has_role? "retailer"
can :manage, Product
else
can :read, :all
end
end
end
I thought this might be a popular idea, of letting a user with role 'manager' manage only products and other certain Models...
if I change
elsif user.has_role? "retailer"
can :manage, Product
to
elsif user.has_role? "retailer"
can :manage, :all
It works as expected... I can access all of the admin area
I only want the "Retailer" to be able to :manage Products tho!! ;)
"admin" is only a role associated with a user, ie all roles are Users.
You can probably see where this is going, Retailers can sign up and sell items of their own.. well thats the goal.
Any pointers??

A quick fix to this problem would be to add a authorize_admin method to a Admin::ProductsController decorator.rb
app/controllers/admin_products_controller_decorator.rb
Admin::ProductsController.class_eval do
def authorize_admin
authorize! :admin, Product
authorize! params[:action].to_sym, Product
end
end
NOTE: This will override the one set in auth/app/controllers/admin_orders_controller_decorator.rb removing the ":admin, Object" requirement for this controller.
That means the role will have to have access to both the :admin AND :action for Product.. ie:
app/models/retailer_ability.rb
class RetailerAbility
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.has_role? "retailer"
can :read, Product
can :admin, Product
end
end
end
Should allow retailers to read products on the admin.
Also Dont forget to add this to an initializer:
config/initializers/spree.rb
Ability.register_ability(RetailerAbility)

There is a native way in spree_auth_devise to do this. It was not documented, but now is.
https://github.com/spree/spree_auth_devise Section: "Using in an existing Rails application"

Related

How to switch from Sqlite to Postgres while installing Warden on Sinatra on Heroku

This is partly a problem-solving question, partly a "I'm trying to understand what's going on" question. I hope that's allowed. Basically, I'm trying to get Warden user authentication to work with Ruby/Sinatra and Postgres on Heroku.
I got a lot of help from this handy (but oldish) tutorial.
From some Rails experience I am a bit familiar with Active Record. The tutorial didn't mention anything about creating a migration for the User class. I went ahead and made my own migration, with my own properties ("name", "email", "password"), only to discover later that, lo and behold, the properties I put in that migration weren't being used by (and in fact were rejected by) the actual model in use. When I examined the object instances in the database, I found that they had only the properties Warden provided for me ("username" and "password").
I'm just trying to understand what happened here. I migrated down my (apparently unnecessary and ignored) Users migration, and nothing happened. I mean that I was able to create User instances and log in using them just as before.
Then it occurred to me that this old Warden tutorial (from 2012) uses something called DataMapper, which does what Active Record would do today. Is that right? They are both "ORMs"? I'm still confused about why Sinatra completely ignored the User migration I did. Maybe it's just using a different database--I did notice wht might be a new db.sqlite database in my main file. Pretty sure the one I created for Active Record was db/madlibs.sqlite3.
Although it works on my local machine, I'm pretty sure it won't work on Heroku, since they don't support sqlite (pretty sure). That then means I'll have to go back to the Warden documentation and figure out how to get it to work with my Postgres database...right? Any pointers on how to get started with that? Since this will be my first project using any authentication library like Warden, it's pretty intimidating.
Here's what I have so far (repo):
app.rb:
require 'sinatra'
require 'sinatra/activerecord'
require 'sinatra/base'
require './config/environment'
require 'bundler'
Bundler.require
require './model'
enable :sessions
class Madlib < ActiveRecord::Base
end
class SinatraWardenExample < Sinatra::Base
register Sinatra::Flash
end
use Warden::Manager do |config|
config.serialize_into_session{|user| user.id }
config.serialize_from_session{|id| User.get(id) }
config.scope_defaults :default,
strategies: [:password],
action: 'auth/unauthenticated'
config.failure_app = self
end
Warden::Manager.before_failure do |env,opts|
env['REQUEST_METHOD'] = 'POST'
end
Warden::Strategies.add(:password) do
def valid?
params['user']['username'] && params['user']['password']
end
def authenticate!
user = User.first(username: params['user']['username'])
if user.nil?
fail!("The username you entered does not exist.")
elsif user.authenticate(params['user']['password'])
success!(user)
else
fail!("Could not log in")
end
end
end
...non authentication routes...
post '/auth/login' do
env['warden'].authenticate!
flash[:success] = env['warden'].message
if session[:return_to].nil?
redirect '/'
else
redirect session[:return_to]
end
end
get '/auth/logout' do
env['warden'].raw_session.inspect
env['warden'].logout
flash[:success] = 'Successfully logged out'
redirect '/'
end
post '/auth/unauthenticated' do
session[:return_to] = env['warden.options'][:attempted_path]
puts env['warden.options'][:attempted_path]
flash[:error] = env['warden'].message || "You must log in"
redirect '/auth/login'
end
get '/protected' do
env['warden'].authenticate!
#current_user = env['warden'].user
erb :protected
end
model.rb (just the User model):
require 'rubygems'
require 'data_mapper'
require 'dm-sqlite-adapter'
require 'bcrypt'
DataMapper.setup(:default, "sqlite://#{Dir.pwd}/db.sqlite")
class User
include DataMapper::Resource
include BCrypt
property :id, Serial, :key => true
property :username, String, :length => 3..50
property :password, BCryptHash
def authenticate(attempted_password)
if self.password == attempted_password
true
else
false
end
end
end
DataMapper.finalize
DataMapper.auto_upgrade!
It seems like this repo might have solved the problems I'm facing now. Should I study that? The Warden documentation itself is pretty forbidding for a relative beginner. For example, it says "Warden must be downstream of some kind of session middleware. It must have a failure application declared, and you should declare which strategies to use by default." I don't understand that. And then it gives some code...which I also don't quite understand. Advice?? (Should I be working with a teacher/mentor, maybe?)

Moodle, how to update $USER content after updating user role through data base

A user logs in to the server, current role of the user in a course context is editing teacher. I want to change the role to student, and test if attempt capability exists or not for the user in course context?
To change the role of user to student for a given context, I manually update the role_assignment table's role column from editing teacher role to student. Changes done directly show no effect unless the user logs out and logs in again.
To test the capability of the user I use $USER global variable.
Even after manually changing the role of the user to student $USER is not updated: $USER shows the changes only after user logs out and logs in again.
I think issue is related to browser cache, session.
How can I update $USER directly as soon as I change role?
Code used to change role of user:
$index = $context->depth-1;
$array = explode("/",$context->path);
$context_id = $array[$index];
// get logged in user id
$userId = $USER->id;
$user = $DB->get_record('role_assignments', array('contextid'=>$array[$index], 'userid'=> $userId));
$user->roleid = 5;
$roles = get_user_roles($context, $userId);
$user->timemodified = time();
$DB->update_record('role_assignments', $user, $bulk=false);
I think the code you want looks something a bit more like this:
$coursecontext = $context->get_course_context();
$teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher']);
$studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student']);
role_assign($studentroleid, $USER->id, $coursecontext->id);
role_unassign($teacherroleid, $USER->id, $coursecontext->id);
I wasn't quite sure what you were trying to do by exploding the context path, so I assumed you were trying to get the course context (which I've done above). If you just wanted the id of the current context, then $context->id would have done.
It's not a good idea to hard code roleids, 5 is the student role in most clean installs, but looking it up is more reliable.
Using the proper Moodle functions to assign/unassign roles is much more reliable than manually messing around with database entries (if you want, you can take a look in lib/accesslib.php and see exactly what those functions are doing internally).

Customizing Surf Platform Root-Scoped API

I want to customize Surf Platform Root-Scoped API specifically user object. That means add new property or method to user object to check the user is in certain group in header.inc.ftl [in share] like `<#if user.isAdmin>
How can I implement this?
Is Alfresco Root Scoped Objects can be used as Surf Platform Root-Scoped object?
I have no idea of customizing surf platform root object. Can anyone help me???
Not quite sure what you are trying to accomplish, but the role security model is hardcoded in spring-surf/spring webscripts. There is guest, user and admin. If what you want is another analogous role you'll have to hack the spring-surf libaries, namely:
org/springframework/extensions/surf/mvc/PageView.java
org/springframework/extensions/webscripts/ScriptUser.java
org/springframework/extensions/webscripts/Description.java
org/springframework/extensions/webscripts/connector/User.java
This is what I had to do to implement user.isEmployee. This approach allows you to literally treat your new role just as the others.
you can use
<authentication>employee</authentication>
in page descriptors or
<item type="link" permission="employee" id="people">/people-finder</item>
on the navigation.
Just checking whether the user is in a certain group in a certain webscript is a whole diffrent story and does not provide the same functionality.
If what you want is the latter, you should make a call to
/alfresco/service/api/groups/{shortName}
miss
and works through the response.
Update: The item permission attribute requires a little more tweaking.
In header.get.js, propagate the new role to it gets processed properly in header.inc.ftl:
model.permissions =
{
guest: user.isGuest,
admin: user.isAdmin,
employee : user.isEmployee
};
you could try (in JavaScript I managed something like) this:
user = Application.getCurrentUser(context);
String userName = user.getUserName();
user.isAdmin() >>> result return true if user logining is admin
or in JSP:
#{NavigationBean.currentUser.admin == true}
Sorry, i noticed now you was talking about Surf Platform root objects, but the link you put there, is deprecated for Alfresco versions above 3.3. You still use something so old?
If you manage to use JavaScript API's you could use "person" root object, with boolean isAdmin().

Cannot access devise current_admin_user in AcitveAdmin form definition

I need to display some form fields in ActiveAdmin form only to specific users.
But when I try to check user status with this code:
ActiveAdmin.register Store do
# ...
form do |f|
f.inputs "Basic" do
if current_admin_user.super_admin?
f.input :admin_user
end
# ...
end
end
end
I get
undefined local variable or method `current_admin_user' for #<ActiveAdmin::DSL:0xdb8e798>
CanCan methods also don't work in the ActiveAdmin form definition.
Generally my question is: how can I manage admin interface display, based on current user type?
Particularly, how can I get current devise user object from within ActiveAdmin definitions?
It is a matter of scope. You could try accessing the helper method using the f.template object like so:
ActiveAdmin.register Store do
# ...
form do |f|
f.inputs "Basic" do
if f.template.current_admin_user.super_admin?
f.input :admin_user
end
# ...
end
end
end
Good luck.
I've found a workaround for this issue. In /app/admin/stores.rb:
ActiveAdmin.register Store do
# ...
form :partial => 'form'
# ...
end
and then in /app/views/admin/stores/_form.html.haml:
= semantic_form_for [:admin, #store] do |f|
= f.inputs "Basic" do
- if current_admin_user.super_admin?
=f.input :admin_user
It's not convenient at all, but works.
I know this thread is a little old but I just found a nice little fix for this. I'm not using devise, I'm using the Twitter API for logging in my users
In active_admin.rb, look for config.current_user_method and change the default value to current_user from current_admin_user
config.current_user_method = :current_user
Also change the logout_link_path to in your routes to include as: :destroy_user_session

Zend_Acl modular class system

Hey guys, I'm starting with Zend, and trying to understand the way it works (getting there), and with Acl classes, people seem to declare all the roles and resources in one file. Now to me this seems a bit of a waste of system resources if the person is only logging in as a basic user, or even just a guest/visitor to the site. So I was thinking is it possible to have different classes which set the roles dependant on the role/resource of the current user.
My current idea is to use switch/case on the role in the bootstrap, and load individual Acl classes based on the role name (for example sake, 'visitor' => 'Model_VisitorAcl', 'users' => 'Model_UserAcl' and 'admin' => 'Model_AdminAcl'), each with a corresponding class file. Then within the class files do something for each one. This is working, and currently I could define all my 'admin' acls in one (backtracking through the rights heirarchy of admin-user-visitor), then in 'user' have 'user'-'visitor' roles and resources.
However this doesn't seem the best way, as if I wanted to change the role permissions for 'user' I'd need to change it for 'user' and 'admin'. This is obv not a problem with 2 roles, but on bigger systems it obviously is.
So I tried something like
class Model_AdminAcl extends Zend_Acl{
function __construct(){
$d = new Model_UserAcl();
//defining the admin role and permissions, with inheritance from the roles
//set in Model_UserAcl
}
}
To try and get the Model_UserAcl construct function to run, thus setting the role 'user', however this does not seem to work, throwing "Uncaught exception 'Zend_Acl_Role_Registry_Exception' with message 'Parent Role id 'users' does not exist'"
error.
So whats the next step? Having individual include files for each module, and have the required classes include them as needed?
Or am I just making mountain out of molehills with this issue?
Thanks,
Psy
Ok, solved this, not sure why I didn't think to try this in the first place (Friday brain frazzle I think)
I just put the contents of the constructor into its own function, and then called that function in the constructor, and make the classes inherit from the class below it.
//Model/VisitorAcl.php
class Model_VisitorAcl extends Zend_Acl{
function __construct(){
$this->addVisitorRules();
}
function addVisitorRules(){
//adding the role and permissions
}
}
//Model/UserAcl.php
class Model_UserAcl extends Model_VisitorAcl{
function __construct(){
$this->addVisitorRules();
$this->addUserRules();
}
function addUserRules(){
//adding the role and permissions
}
}
//Model/AdminAcl.php
class Model_AdminAcl extends Model_AdminAcl{
function __construct(){
$this->addVisitorRules();
$this->addUserRules();
$this->addAdminRules();
}
function addAdminRules(){
//adding the role and permissions
}
}
If this is not the way I should go about it, then by all means please let me know.
Psy