Rails – set restriction and permission on attributes at model level based on role system (using restricted_attributes plugin / gem) – validate params at model level

RestrictedAttributes  Plugin / Gem:

This restricted_attributes plugin provides the capabilities to restrict attributes (fields) of db table’s while add or update a record. It validates your attributes values before your validation stuff.

Features :

  • Provides four different ways of restriction on fields (i.e read_only, create_only, update_only and hidden)
  • Restrict to add/modify values of particular attributes at model level while creating/updating a record.
  • Restrict functionality perform at before validation.
  • Able to set restriction on instance variables also.
  • OPTIONAL: It can also works on the basis of declarative authorization roles system.
  • So, able to set role wise restriction/permission on various users to change the value of an attributes.

More Information and download links :

Plugin :-  https://github.com/gkathare/restricted_attributes

Gem    :- http://rubygems.org/gems/restricted_attributes

Installation and Configuration :

1. Install restricted attributes plugin / gem :

- Install Plugin:

Download restricted_attributes plugin from above plugin link and add it your projects vendor directory.

- Install Gem :

You can install restricted_attributes gem, just add  following line in your project’s gemfile.

     gem “restricted_attributes”

2. Requirements (If you want to use :declarative tag feature) :

 - declarative_authorization plugin/gem.

3.  permissions.yml

Create permissions.yml file in your project/config  folder. like,

Format : /config/permissions.yml
    _____________________________________________________________
    |:default:
    |  :readonly: [:attribute1, :attribute2]
    |  :hiddenonly: [:attribute1, :attribute2]
    |  :createonly: [:attribute1, :attribute2]
    |  :updateonly: [:attribute1, :attribute2]
    |:role_1:
    |  :class_name_1:
    |    :readonly: [:attribute1, :attribute2]
    |    :hiddenonly: [:attribute1, :attribute2]
    |    :createonly: [:attribute1, :attribute2]
    |    :updateonly: [:attribute1, :attribute2]
    |    :permit_to: [:attribute1, :attribute2]
    |  :class_name_2:
    |    :permit_to: [:attribute1, :attribute2]
    |:role_2:
    |  :class_name_1:
    |    :permit_to: [:attribute1, :attribute2]
    |  :class_name_2:
    |    :permit_to: [:attribute1, :attribute2]
    |
    |
    |___________________________________________________________

where :

  •  role_1, role_2 are the roles of user.
  • class_name_1, class_name_2 are the class name or Model name
  • readonly, hiddenonly, createonly, updateonly - To set restriction on specific role.
  • permit_to: [] feature to set permission to change value of added attributes.
  • default: provides common restricted attributes for all classes of all roles like id, create_at, updated_at.

======================= Examples ============================

# Example 1 Simple one (without use of :declarative tag feature)
================================================================

    class Post < ActiveRecord::Base
        has_restricted_attributes :read_only => [:status],
                            :create_only => [:title, :publish],
                            :update_only => [:tags],
			    :hidden_only => [:activated],
                            :read_only_message => "is a read only attribute",
                            :create_only_message => "can't update, its permitted to create only.",
                            :update_only_message => "can't add, its permitted to update only."
    end

So, the restricted attributes will be as shown in following table.

# Post Model :

Can-> Read/Hidden Only):status/:activated (Create Only):title,:publish (Update Only):tags
Create Update Create Update Create Update
 Any User NO NO YES NO NO YES

Console Output :
———————–

    >> post = Post.new(:status => true, :title => "New Title", :tags => "new, topic")
    >> post.save
    => false

    >> post.errors
    => #<OrderedHash {:status => ["is a read only attribute"], :tags=>["can't add, its permitted to update only."]}>

    # for hidden attributes
    >> post = Post.new(:activated => true)
    >> post.save
    => false

    >> post.errors
    => #<OrderedHash {:status => ["is a hidden attribute"]}>
    OR
    >> post.is_restricted?(:read, :activated)  # To check :activated field is restricted to read.
    => true

===============================================================

# Example 2 : (with :declarative => true tag feature)
=====================================================

Step 1 :

    class User < ActiveRecord::Base
        has_restricted_attributes :read_only => [:logged_in],
                            :create_only => [:login, :email],
                            :update_only => [:bio],
                            :declarative => true

    end

    class Post < ActiveRecord::Base
        has_restricted_attributes :read_only => [:status],
                            :create_only => [:title, :publish],
                            :update_only => [:tags],
			    :hidden_only => [:activated],
                            :declarative => true,
                            :read_only_message => "is a read only attribute",
                            :create_only_message => "can't update, its permitted to create only.",
                            :update_only_message => "can't add, its permitted to update only."
    end

Step 2 :
Create permissions.yml file in your project/config/  folder,  and add roles and permissions for the user as shown in following table.

* Here you can set permission to change the value of restricted attributes on the basis of role system.

Example:
    ## /config/permissions.yml
     ___________________________________________________________
    |:default:
    |  :readonly: [:created_at, :updated_at]
    |:global_admin:
    |  :user:
    |    :permit_to: [:logged_in, :login, :email, :bio]
    |  :post:
    |    :permit_to: [:title, :status, :publish, :tags]
    |:member:
    |  :user:
    |    :permit_to: [:email]
    |  :post:
    |    :permit_to: [:publish]
    |
    |
    |
    |
    |___________________________________________________________

asdfasdf

# where in that,

  • :global_admin , :member are the roles of user. [ROLE]
  • :user , :post are the class names [CLASS/MODEL]
  • :permit_to: [] here you can add those attributes which will be permitted for appropriate User [ATTRIBUTES]
  • :default: you can add common attributes to restrict the access for all classes.

Result :
———

So, the permissions on restricted attributes for global_admin and member user will be as shown in following table.

#Post Model :

        Can-> (Read Only)
:status
(Create Only)
:title,:publish
(Update Only)
:tags
Create Update Create Update Create Update

User
(global_admin)

YES YES YES YES YES YES

User
(member)

NO NO YES NO-:title YES-:publish NO YES

Console Output :
———————–

    
    # for member user
    >> Authorization.current_user = User.find_by_login("member")
    >> post = Post.new(:status => true, :title => "New Title", :tags => "new, topic")
    >> post.publish = true
    >> post.save
    => false
    >> post.errors
    => <OrderedHash {:status => ["is a read only attribute"], :tags => ["is a update only attribute"]}>

    >> post = Post.find(123)
    >> post.title = "My new title"
    >> post.publish = false
    >> post.save
    => false

    >> post.errors
    => <OrderedHash {:title => ["is a create only attribute"]}>

    # for hidden attributes
    >> post = Post.new(:activated => true)
    >> post.save
    => false

    >> post.errors
    => #<OrderedHash {:status => ["is a hidden attribute"]}>
    OR
    >> post.is_restricted?(:read, :activated)  # To check :activated field is restricted to read.
    => true

    ### For Global Admin

    >> Authorization.current_user = User.find_by_login("global_admin")
    >> post = Post.new(:status => true, :title => "New Title", :tags => "new, topic")
    >> post.save
    => true

    >> post.errors
    => #<OrderedHash {}>

======================= End Examples ============================

More information & more examples refer in it’s Part 2  (Coming soon)

Cheers!!! :)

Thank you
Ganesh K

 

Here is one another post on rspec 2.5 with jruby 1.5.6 + rails 3

Installation steps :

First of all I consider you have setup environment with jruby 1.5.6 + rails 3.0.6.

You can download jruby 1.5.6 zip file from following link

http://jruby.org.s3.amazonaws.com/downloads/1.5.6/jruby-bin-1.5.6.zip

After environment  setup and configuration.

Step 1 -  To install rspec gem just add following lines in your Gemfile of your project.\

group :development, :test do
  gem "rspec-rails", ">= 2.0.1"
end

Step 2 - Try bundle install at project root

> bundle install

Step 3 -  Once you installed, then you need to install RSpec

> jruby -S  rails  g  rspec:install

Output :

exist  lib
create  lib/tasks/rspec.rake
exist  config/initializers
create  config/initializers/rspec_generator.rb
exist  spec
create  spec/spec_helper.rb

Step 4 – install binstubs in your project.

> bundle install --binstubs

This will create bin folder in your project directory ( like  urproject/bin/ )

and will auto generate all rspec bin stubs into that directory.

Like..

autospec
cdiff
cucumber
decolor
edit_json.rb
erubis
htmldiff
image_voodoo
ldiff
memcached_top
mongrel_rpm
newrelic
newrelic_cmd
nokogiri
prettify_json.rb
rackup
rails
rake
rake2thor
rprotoc
rspec
ruby_parse
thor
tilt
tt

Step – 5  - Create test database

> rake db:test:clone_structure

So this is all about rspec installation and configuration for your project.

 

Step – 6Unit testing ( Model testing)

# spec/models/user_spec.rb

require File.dirname(__FILE__) + '/../spec_helper'

context "User" do
  before(:each) do
   @user1 = User.new(:name => "Ganesh")
   @user2 = User.create(:name => "Ganesh K", :city => "Mumbai", :country => "India")
  end

  specify "should save" do
    lambda {
      @user1.city = "Navi Mumbai"
      @user1.country = "India"
      @user1.save.should be_true
    }.should change(User, :count).by(1)
  end
  specify "should not save" do
    lambda {
      @user1.save.should be_false
      @user1.errors.should_not be_empty
    }.should change(User, :count).by(0)
  end

  specify "should update" do
   lambda {
      @user2.city = "Pune"
      @user2.save.should be_true
      @user2.city.should == "Pune"
    }.should change(User, :count).by(0)
  end  

  specify "should destroy" do
    lambda {
      @user2.destroy.should be_true
    }.should change(User, :count).by(-1)
  end
end

 

Step – 7  – Functional Testing ( Controller Testing)

# spec/controllers/users_controller_spec.rb

require File.dirname(__FILE__) + '/../spec_helper'

describe UsersController do
  render_views

  before(:each) do
   @user1 = User.new(:name => "Ganesh")
   @user2 = User.create(:name => "Ganesh K", :city => "Mumbai", :country => "India")
  end

  specify "should be show index" do
    get :index
     response.should be_success
     assigns[:users].should_not be_blank
    end
  end

  specify "should create user." do
   lambda {
     post :create, {:user => {:name => "Ganesh", :city => "Latur", :country => "India"} }
     assigns[:user].should_not be_blank
     response.should redirect_to(users_path)
   }.should change(User, :count).by(1)
  end

  specify "should update user." do
   lambda {
     put :update, {:id => @user2.id, :user => {:city => "Pune"}}
     assigns[:user].should_not be_blank
     assigns[:user].city.should == "Pune"
     response.should redirect_to(users_path)
   }.should change(User, :count).by(0)
  end
  specify "should destroy user." do
    lambda {
     post :destroy, {:id => @user2.id}
     response.should redirect_to(users_path)
    }.should change(User, :count).by(-1)
  end
end

======================================

You can refer following few rspec command to test your model / controller files are like.

To test model

> jruby -S bin/rspec spec/models/user_spec.rb

To  test controller

> jruby -S bin/rspec spec/controllers/users_controller_spec.rb

To test all model spec files with single command.

> jruby -S bin/rspec spec/models

To test all controller spec files with single command.

> jruby -S bin/rspec spec/controllers

Cheersss!!!!

-Ganesh K

Rails 3 named_scope

Slit difference named_scope syntax between Rails 2 and Rails 3

Rails 2.X :

class User < ActiveRecord::Base

named_scope :newest_user, :conditions => ["created_at >= ?", (Time.now.utc - 1.hour)]

end

Rails 3.x :

class User < ActiveRecord::Base

scope :newest_user, :conditions => ["created_at >= ?", (Time.now.utc - 1.hour)]

end

So, to avoid deprecation warning of “named_scope” in Rails 3.x just use “scope”.

Good Luck ;)

SMS Integration :
For my project, I integrated this service for Mobile number Verification. In desinged module user can verify his mobile no by verification code. I got Paid service to send SMS from my application(service provider www.znisms.com)

Required Parameters to integrate this service are:
1 – userId
2-API key
3-senderID
4-sendto
5-message In that first

3 parameters will get from service provider..

After that, In an action you have to create url with this parameters & related values
like ## /controller/profiles_controller.rb

def send_sms
  @receiver = params[:users][:reciever]
  @msg = params[:users][:message]
  @mu_sms_url = "http://api.znisms.com/post/smsv3.asp? userid=GaneshK&apikey=xxx&message=#{@msg}& senderid=9123123456&sendto=#{@receiver}"
  Kernal.open(@mu_sms_url) do |f|
    @result = f.write
  end
  flash[:notice] = @result
  redirect_to @user
 end

So, by using above code you can send request, Purpose behind this API key is unique key, and that is used to validate authorized request access.

Hope this code will help for your application.

Regards,
-Ganesh Kathare.

Rails 3  after_commit callback:

In rails 3 after_commit gem’s all functionality is now natively available in its library. There is some syntax variation in Rails 2 and Rails 3 as follow

Syntax in Rails 3 :

# commit after every save
after_commit :my_method

# commit after on save record
after_commit :my_method : on => :create

# commit after on update record
after_commit :my_method : on => :update

# commit after on delete record
after_commit :my_method : on => :destroy

Syntax in Rails 2 :

# commit after every save
after_commit :my_method

# commit after on save record
after_commit_on_create :my_method

# commit after on update record
after_commit_on_update :my_method

# commit after on delete record
after_commit_on_destroy :my_method

 

So, for rails 3 now no need to install any gem or plugin of after_commit.

Good Luck ;)