Thinking Sphinx makes it wonderfully easy to search across multiple models:
ThinkingSphinx::Search.search 'Your query'
But what happens if your user is only allowed to see some of the data? On my current project, a signed-in user may see everything but a public user may only see part of the site. I need to restrict the search to what the user is allowed to see.
Fortunately what the user is allowed to see, in this case, depends on the class. For example a public user can see all news stories but no members' profiles. It would be harder were a public user allowed to see only some news stories or only some members' profiles.
My first solution was to restrict the classes which Thinking Sphinx searches, using the handy :classes option:
class SearchController < ApplicationController
def search
if params[:q].blank?
@results = []
else
@results = ThinkingSphinx::Search.search \\
params[:q],
:page => params[:page] || 1,
:classes => authorised_classes
end
end
private
def authorised_classes
[Forum, Member, Post, Review, Story, Team, Topic].select do |klass|
permitted_to? :read, klass.to_s.tableize.to_sym
end
end
end
The array of classes holds all the classes with indexes. (In reality my app has twice as many but I omitted some for legibility.)
The permitted_to? method comes from Steffen Bartsch’s excellent declarative_authorization plugin. There are many different ways to handle authorisation but this is my favourite. It has elegantly solved every authorisation requirement I have ever had; it just feels right.
As an aside, the authorisation rules are declared in config/authorization_rules.rb:
authorization do
role :guest do
has_permission_on :forums, :to => :read
has_permission_on :posts, :to => :read
# etc
end
role :member do
has_permission_on :forums, :to => :read
has_permission_on :members, :to => :read
has_permission_on :posts, :to => :read
# etc
end
end
privileges do
privilege :manage, :includes => [:create, :read, :update, :delete]
privilege :read, :includes => [:index, :show, :search]
privilege :create, :includes => :new
privilege :update, :includes => :edit
privilege :delete, :includes => :destroy
end
You can see that a guest may search forums and posts, but not members; a member can search all of them.
My search controller’s authorised_classes method worked perfectly, but I knew I was setting myself up for a fall in a few months' time: as and when I added a new class to the domain, I would probably forget to add it to this array of indexed classes.
Of course Thinking Sphinx knows which of my classes have indexes, so we can leave it to Thinking Sphinx:
def authorised_classes
ThinkingSphinx::indexed_models.select do |model|
permitted_to? :read, model.tableize.to_sym
end.map { |model| model.classify.constantize }
end
I think this is a pretty elegant way to integrate search and authorisation. Thanks to Pat Allan and Steffen Bartsch for writing high quality library code that makes my application code much easier to produce!
Thanks mate, just what I was looking for!
Alex Djioev • 16 March 2010