Skip to content

Instantly share code, notes, and snippets.

@elricstorm
Created January 6, 2011 23:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elricstorm/768843 to your computer and use it in GitHub Desktop.
Save elricstorm/768843 to your computer and use it in GitHub Desktop.
# Create permissions to populate the permissions table with.
permissions = Forumpermission.create([
{:name => 'view', :description => 'view index', :bitfield => 1},
{:name => 'search', :description => 'search forum', :bitfield => 2},
{:name => 'viewtopics', :description => 'view topics', :bitfield => 4},
{:name => 'createtopics', :description => 'create topics', :bitfield => 8},
{:name => 'edittopic', :description => 'edit own topic', :bitfield => 16},
{:name => 'deletetopic', :description => 'delete own topic', :bitfield => 32},
{:name => 'editothertopics', :description => 'edit other topics', :bitfield => 64},
{:name => 'deleteothertopics', :description => 'delete other topics', :bitfield => 128},
{:name => 'locktopics', :description => 'lock topics', :bitfield => 256},
{:name => 'stickytopics', :description => 'sticky topics', :bitfield => 512},
{:name => 'createpost', :description => 'create a post', :bitfield => 1024},
{:name => 'editpost', :description => 'edit own post', :bitfield => 2048},
{:name => 'deletepost', :description => 'delete own post', :bitfield => 4096},
{:name => 'editotherposts', :description => 'edit other posts', :bitfield => 8192},
{:name => 'deleteotherposts', :description => 'delete other posts', :bitfield => 16384},
{:name => 'createpoll', :description => 'create a poll', :bitfield => 32768},
{:name => 'editownpoll', :description => 'edit own poll', :bitfield => 65536},
{:name => 'deleteownpoll', :description => 'delete own poll', :bitfield => 131072},
{:name => 'editotherpolls', :description => 'edit other polls', :bitfield => 262144},
{:name => 'deleteotherpolls', :description => 'delete other polls', :bitfield => 524288},
{:name => 'addattachments', :description => 'add own attachments', :bitfield => 1048576},
{:name => 'editattachments', :description => 'edit own attachments', :bitfield => 2097152},
{:name => 'deleteattachments', :description => 'delete own attachments', :bitfield => 4194304},
{:name => 'editotherattachments', :description => 'edit other attachments', :bitfield => 8388608},
{:name => 'deleteotherattachments', :description => 'delete other attachments', :bitfield => 16777216},
{:name => 'createforum', :description => 'create forums', :bitfield => 33554432},
{:name => 'editforum', :description => 'edit forums', :bitfield => 67108864},
{:name => 'deleteforum', :description => 'delete forum', :bitfield => 134217728}
])
# Example bitfields from Kirin Forum Software
# Author is Joel Dezenzio => Elricstorm => Alpha Blue
module ForumPermissions
# The BitPermissions class is used to read and compare
# bitfields between an object (user) and the currently
# allowed permissions stored in a model (Forum Permissions).
# It determines what actions the user object is allowed to
# performed based upon the comparative result.
class BitPermissions
def initialize(bitfield)
@bitfield = bitfield
end
def find_permissions
# Find a bit listing of all permissions
permissions_table = Forumpermission.find(:all)
# create an action array
action_permissions = []
permissions_table.each do |permission|
# compare each bitfield against the permissions bitfield
# and determine if it's greater than zero. If so, then
# the permission is added to action_permissions for the
# current user object.
if @bitfield & permission.bitfield > 0
action_permissions << permission.name
end
end
return action_permissions
end
end
# Authorize method checks the membergroupids of the currently logged in user
# and separates the comma-delimited IDs, subsequently searching for the
# bitfield forum permissions of each membergroup ID. It then passes the
# bitfield to the BitPermissions class in the custom lib which returns an
# array of all matching permission strings ['canview', 'canedittopics', 'etc.]
# Because there are multiple membergroups, the array is built from all groups
# returned, flattened and then only unique entries remain. This allows a
# user to be a member of say 8 different groups which might have different
# permissions, and these permissions would be combined to show the full set.
def authorize
if user_signed_in?
mybits = []
memberids = @current_user.forumuser.membergroupids
memberids.split(',').each do |perms|
mybits << forum_permissions(Usergroup.find(perms).forumpermissions)
end
@current_user[:bfpermissions] = mybits.flatten!.uniq
end
end
# Uses a custom lib class BitPermissions to compare permissions within a group
def forum_permissions(bitfield)
BitPermissions.new(bitfield).find_permissions
end
# This method is used for checking singleton actions that are not dependent on
# specific models or require user_id matching. It is mainly defined in views
# for accessing hidden functions.
def user_can?(action)
user_signed_in? && @current_user[:bfpermissions].include?(action) ? true : false
end
# This method is used for checking actions that are dependent on specific
# models and requires that the currently logged in user matches the model
# user_id. If the user has access to actions that affect others, no
# user_id matching is required. Examples of the latter are (i.e. editothertopics,
# editotherposts, etc.) Actions that affect others supercede singleton permissions.
def authorize_bits(action, model, other=false)
if user_signed_in? && other == true
@current_user[:bfpermissions].include?(action) ? true : false
elsif user_signed_in? && other == false
@current_user.id == model.user_id && @current_user[:bfpermissions].include?(action) == true ? true : false
else
return false
end
end
# This method determines if a user can perform a controller action based on bitfield permissions.
# If the bitfield permissions match the required accessible action, then the user
# permissions are authorized. Otherwise, the user is redirected to a url based on
# the access_to_action_denied method. It has some dependency on user_id matching.
# This method is used exclusively for controller actions and is not a valid helper method.
def can_user(action, otheraction, model)
unless authorize_bits(action, model) == true || authorize_bits(otheraction, model, true) == true
access_to_action_denied
end
end
# Used in the forum, topic, and post controllers if bitfield permissions
# and other criteria return false
def access_to_action_denied
redirect_to root_url, :notice => "You are not authorized to perform this action." and return false
end
end
@elricstorm
Copy link
Author

So, basically what this does is if you want to decide what actions a user has, you can create a members group, decide the actions, add the bits, and assign the bitpermissions to that group. This allows you to use it as a comparison to determine if the user within the group is authorized for "x" controller action.

@elricstorm
Copy link
Author

The can_user method has action and otheraction arguments. To make it simpler to understand, the idea behind this method is if a user is allowed to editother, deleteother, viewother, stickyother, etc. etc. then this meant the user was a part of a "super" group that supercedes the default commands of editown, deleteown, viewown, stickyown, etc. If you can do something to "other" it is assumed you can do something for yourself.

This is a barebones example of how bitfield permissions work but you can realistically assign bits to "any" object, not just a user.

@elricstorm
Copy link
Author

So, looking at say a usergroup, a registered member might have a bitpermission assignment of 3101. In a controller I can do the following for an edit command:

def edit
@topic = @forum.topics.find(params[:id])
# can_user is a method located in lib/forum_permissions.rb
can_user('edittopic', 'editothertopics', @topic)
end

Which will call authorize_bits from the can_user method to see if "edittopic" or "editothertopics" is part of the actions assigned to :bfpermissions. So how does this work exactly? Open up IRB and find out.

irb
=> 3101 & 16 > 0
=> true

16 is the bits assigned to edittopic and the action is located within 3101. Now then, what if the user only had view, search, viewtopics, createtopics? Add them up.

irb
=> 15 & 16 > 0
=> false

@elricstorm
Copy link
Author

This makes it so that you can do a lot of things with bitpermissions. You could feasibly create bitfield permission tables for different categories. By the time you finished, you could have 5,000 actionable permissions and it wouldn't be difficult to administrate because all you are doing is deciding exactly what each object is authorized to do. Authorization at extreme levels. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment