Lockdown version 0.8.0 has been released! New DSL for defining permissions, better documentation and more specs on the gem, all good stuff.
Next up: Model level security! woohoo!
Lockdown version 0.8.0 has been released! New DSL for defining permissions, better documentation and more specs on the gem, all good stuff.
Next up: Model level security! woohoo!
Posted in lockdown | Comments Off
While working on a project with Rein Henrichs he showed me a nice way to format code. I’m sharing.
Let’s pretend you have a method that can take many parameters, it happens.
def some_useful_method
my_method :parameter_one, :parameter_two, :parameter_three, :parameter_four
end
I’ve typically just kept the first parameter on the same line as the method call and tabbed my way to aligned glory.
def some_useful_method
my_method :parameter_one,
:parameter_two,
:parameter_three,
:parameter_four
end
That’s not bad, but we can do better!
def some_useful_method
my_method \
:parameter_one,
:parameter_two,
:parameter_three,
:parameter_four
end
Doesn’t that look better? Of course it does! The backslash can be used in any event you want the parser to continue on to the next line.
Why should you know this? Well, I’ve heard from a few people that keeping to an 80 column width is a good practice. For some projects, I’ve heard it’s required as part of the patch approval project. I never really understood the fuss until I implemented the rule myself.
Keeping within 80 columns will make your code more readable. Please note, this isn’t a draconian rule, there are exceptions.
Even though you can tag the conditional to the end of your statement like:
create(:name => "My Record", :type => Model.type_a) if create_child?
This is simple and it’s not terrible to read, but this is easier:
if create_child?
create(:name => "My Record", :type => Model.type_a)
end
If you’re not a perfect coder and need to put a debug statement in this conditional you can do so without refactoring your code.
Just give the 80 column rule a shot and you should find yourself writing cleaner code.
Posted in Code Style | 1 Comment »
So, I’ve been struggling with the best way to define rules for Lockdown. I wanted something that was intuitive and readable. I think I have it now…
Note: This functionality is not currently in Lockdown, but will be soon.
Here’s a simple example that will create a permission called ‘Manage Users’ that is defined as all methods on the UsersController.
set_permission(:manage_users).
with_controller(:users)
Now let’s create a permission that only allows access to certain methods on the UsersController, like ‘My Account’. This permission is restricted to the :show and :update methods.
set_permission(:my_account).
with_controller(:users).
only_methods(:show, :update)
But wait! You say that isn’t good enough? Want add some model knowledge…like say, a user can only access these methods if the current_user_id matches the id of the user being shown/edited? Well, here you go:
set_permission(:my_account).
with_controller(:users).
only_methods(:show, :update).
to_model(:user).
where(:current_user_id).
equals(:id)
For the resources that may have multiple editor access like timesheets, you could define a rule like:
set_permission(:manage_timesheet).
with_controller(:timesheets).
only_methods(:show, :update).
to_model(:timesheet).
where(:current_user_id).
is_in(:editor_ids)
This expects an :editor_ids method on the timesheet model that will return an array of ids. If the current_user_id is in that array, the user will have access.
To summarize:
For the controller you will have:
.with_controller(:controller_name) # defines which controller we're talking about
.only_methods(:meth1, :meth2) # only these methods on the controller
.except_methods(:meth1, :meth2) # all controller methods except these
# :with_controller defaults to all_methods
# can chain them together:
# with_controller(:users).
# and_with_controller(:user_groups).
#
# and_with_controller is just an alias to with_controller ... reads more better :)
For the model:
.to_model(:model_name) # defines which model we're talking about
.where(:data_method) # data_method must be available to the controller
.equals(:value_method) # model_name.value_method must equal data_method
.is_in(:values_method) # model_name.values_method.include?(data_method)
Let me know how you’d like to use it…what’s your use case?
Posted in lockdown | 1 Comment »
For the multitude who don’t know and could have possibly gotten here by mistake:
Lockdown is a authentication/authorization system for RubyOnRails (ver 2.x)
Please take a look at the news release and to find out more, check out the wiki.
For the honored few that do know and use Lockdown, this is a big step for the project. A lot more polish has been placed on the code, a lot more simplification.
Still work to do, features to add, but I’m pleased with the progress. Hope you are too.
Posted in lockdown | Comments Off
Now that the holidays are almost over, I’ve started work on a new version of Lockdown. I’ve created a ‘future’ branch on github for this purpose.
I have a couple of technologies I’m switching to:
1. Mr Bones: I like the clean design of Mr. Bones.
2. Templater: I’m going to simplify how Lockdown is mixed in to your application.
3. Compatibility with Resource Controller will be tackled as well.
I would like to know what you would like. If there is a feature that’s missing that has kept you from using Lockdown, what is it? I’m going to work hard to make Lockdown the security system of choice for Rails applications. I need your help to let me know what you want.
Of course, if you’d like to help out and contribute some code, that would be great!
As I’m posting this in a few places, please tweet your suggestions to me. [http://twitter.com/stonean]
If it’s a longer suggestion, just email it to andy at stonean. com, this way I won’t miss it.
thanks,
stonean
Update:
After some discussions with Dr. Nic, I’m going to take a step back and re-review Rubigen. It may be best to help out the Rubigen project.
Posted in lockdown | 4 Comments »
I’ve always reasoned that Haml went against the principles of an MVC framework because the designers would not (typically) be able to work with views written in Haml. I’ve recently come to the conclusion that, in practice, Haml is better for your project.
When I took a step back to analyze what impact a switch to Haml would have on our team I realized something interesting. After we receive HTML from our designers, we break it up into partials, attach unobtrusive javascript methods and make other various tweaks that require the designer to pair with a developer. The notion of keeping the responsibility of view maintenance with the designers was just proven incorrect in practice.
The succinct syntax and close connection to your stylesheets give your developers a more efficient environment. Isn’t this what we’re all after?
Speed concerns? With version 2.0, Haml is now slight faster than Erb and with that goes my last excuse for not switching.
Haml, I will now get to know you better.
Posted in Haml | 2 Comments »
Here’s the link to the news release on stonean.com.
btw, if you’d like to get this type of information from twitter, you can.
Posted in lockdown | Comments Off
Inject is a powerfully cool method provided by the Enumerable module. At first I was a little confused by the name. I didn’t know what it meant. Here’s a simple example:
an_array = [1, 2, 3, 4, 5, 6]
def x10(v)
v * 10
end
an_array.inject([]) do |sum, num|
sum << x10(num)
end
=> [10, 20, 30, 40, 50, 60]
So, by passing [] into the inject method, the variable sum is initialized as an empty array. Now the inject method will iterate over the array an add the value return by x10 to the new array (sum).
Calling inject on a hash:
def convert_hash(hsh)
hsh.inject({}) do |sum, aray|
sum.merge( { aray[0] => some_conversion_method(aray[1]) } )
end
end
**Notice that inject when called on a hash will pass the key and value in one array: [key, value]. Hence the aray[0] and aray[1] calls.
Of course, since you are creating an entirely new hash, you could change the key if you wanted. This was just a simple example.
Anyway…just a short update. It’s been a while since I shared. :)
Posted in Ruby | Comments Off
I recently deployed a Rails application that, for various reasons, required caching dynamic content. All dynamic content is represented by a core “Content” model that contains a url attribute. In order for my app to show these pages I have the following as my last route:
map.dynamic ':p1',
:controller => "contents",
:action => "show",
:requirements => {:p1 => /[\w|-]*/}
As you can see, the contents/show is my catchall action. If I can find a content url matching :p1, I show the content.
Another feature of the application is that you can restrict access to this content to one or many user groups via my Lockdown gem. At a basic level, Lockdown works by testing for access in a before_filter it adds to all controllers. To ensure I don’t expose protected content, I needed to use action caching to have my before filters execute before showing the page.
Action caching typically creates a cache file that corresponds to the action. However, in this case, the show action represents multiple urls. Here’s how you get around that:
class ContentsController < ApplicationController
caches_action :show,
:cache_path => proc{|c| c.send(:cached_page, c.params[:p1], c.params[:page]) }
def show
#logic to render multiple content types
end
private
# This method will return my cache name
def cached_page(url, page)
page ||= "1"
"#{url}_#{page}"
end
end
If the url for the contents was “/about-us”, the cache would be “about-us”. If the content url was “/reunion-pictures?page=2″, the cache would be “reunion-pictures_2″. So forth and so on.
I was going setup a route that would turn “/reunion-pictures?page=2″ to “/reunion-pictures/2″, but for some reason, adding the route to do this broke the display of the page links in will_paginate. I’m not sure why and the url wasn’t that important to me. The value provided by will_paginate was more important to me than pretty routes in this instance. I may revisit this issue later…but I need to get back on point.
So…I’m caching now, cool. Now I just have to account for changing content, enter the sweepers:
class ImageSweeper < ActionController::Caching::Sweeper
observe Image
def after_create(post)
expire_cache_for(post)
end
def after_update(post)
expire_cache_for(post)
end
def after_destroy(post)
expire_cache_for(post)
end
private
def expire_cache_for(record)
record.galleries.each do |gal|
for i in 1..((gal.artworks.length / 10) + 1)
expire_action("#{gal.url}_#{i}")
end
end
end
One type of dynamic content is a gallery that can contain multiple images. In order to make sure my galleries are kept up-to-date, I needed this sweeper to expire the caches of the galleries that contained the images I was creating/updating/deleting.
In order for this ImageSweeper to clean house, I needed to add the following to the ImagesController:
cache_sweeper :image_sweeper,
:only => [:create, :update, :destroy]
Now my galleries will stay current and I won’t be locked into showing cached galleries that represent old/incorrect information.
The cached pages are ridiculously faster…and yes, that’s a technical term similar to “ludicrous speed”.
Posted in RubyOnRails | Comments Off
I don’t frequently have the need to create anything more than the basic “map.resources :model” route. As things go, this means I don’t remember how to anything other than the basic routes and forget the difference between :collection and :member and how that translates to urls and helper methods. So, I’m creating this page as a reference for myself so I don’t have to google and piece together bits of information.
Basic:
map.resources :users
# Which gives you the following helpers:
users_path
users_url
new_user_path
new_user_url
user_path(@user)
user_url(@user)
edit_user_path(@user)
edit_user_url(@user)
# Accessed via:
/users
/users/new
/users/1
/users/1/edit
Nested:
map.resources :posts do |posts|
posts.resources :comments
end
# Which gives you the following helpers:
post_comments_path
post_comments_url
new_post_comment_path
new_post_comment_url
post_comment_path(@post,@comment)
post_comment_url(@post,@comment)
edit_post_comment_path(@post,@comment)
edit_post_comment_url(@post,@comment)
# Accessed via:
post/1/comments
post/1/comments/new
post/1/comments/1
post/1/comments/1/edit
Adding methods outside the standard REST methods.
map.resources :users, :collection => {:active => :get}
# Which gives you the following helpers:
active_users_path
active_users_url
# Accessed via:
/users/active
The member option means you’re going to access a ‘member’ of the collection. Why this didn’t stick the first time, I have no idea. I’m just slow I guess.
map.resources :users, :member => {:activate => :post}
# Which gives you the following helpers:
activate_user_path(@user)
activate_user_url(@user)
#Accessed via:
/users/1/activate
There will be more added as I run across more usage needs. Let me know if I messed anything up here.
Posted in RubyOnRails | 1 Comment »