Skip to content

Advanced Rails 3 Tutorial Custom Resource, Devise, and User Specific Routing

bryanrite edited this page Jan 28, 2012 · 15 revisions

In this advanced tutorial, we'll setup a Rails 3.1.x site that exposes user specific subdirectories with authentication through Devise.

There is a working example of this tutorial at: https://github.com/bryanrite/dav4rack-example-devise-subdirectories

Beginning

This is a step by step walkthrough based on this: sample app.

To start, please follow the basic Rails 3 setup tutorial: https://github.com/chrisroberts/dav4rack/wiki/Rails-3

This will leave leave you with a Rails application that exposes the Rails root with no authentication.

Custom Resource

Our first step is to create a custom resource for us to use. Within your app/models directory, create a model for your WebDAV resources. In this example, we'll call it MyResource:

# app/models/my_resource.rb
class MyResource < DAV4Rack::FileResource
end

Next, we want to use our new resource instead of the default Resource as were using in the Basic Rails 3 tutorial:

# config/routes.rb
mount DAV4Rack::Handler.new(
   :root => Rails.root.to_s,
   :root_uri_path => '/',
   :resource_class => MyAppName::MyResource
), :at => '/', :constraints => { :subdomain => "webdav" }

Notice we changed the :resource_class value to our new resource. If you start rails now (rails s) and goto webdav.lvh.me:3000 (*.lvh.me is a convenient CNAME of localhost, good for working with subdomains on your development machine) in your WebDAV client, you should see your rails root directory. Great, we are now using our custom resource. Let's customize it.

Authentication with Devise

We can add authentication with Devise quite simply. In this example, I'll work with the assumption that you're using Devise with usernames instead of email for its simplicity (http://railscasts.com/episodes/210-customizing-devise), but working with email should be fairly straight forward as well.

The use of the user variable here is on purpose as this variable implemented in the parent class and automatically propagated to child files and directories.

We override the authentication method and ask Devise about valid users:

# app/model/my_resource.rb
...
private
   def authenticate(username, password)
     self.user = User.find_by_username(username)
     user.try(:valid_password?, password)
   end

You should now be able to connect or be denied access to WebDAV depending on your username/password.

User Specific Routing and Subdirectories

A somewhat common setup is to host user files within named subdirectories. For example:

+ root_dir
|- username1
|- username2
|- username3
...

This also allows for chroot jails for ssh and ftp as well. So we want to expose this particular user's username as the WebDAV root, we can do this by overriding file root. Our updated resource file looks like:

# app/model/my_resource.rb
class MyResource < DAV4Rack::FileResource

   def root
      File.join(options[:root].to_s, user.username)
   end

   private
      def authenticate(username, password)
         self.user = User.find_by_username(username)
         user.try(:valid_password?, password)
      end
end

Now that we've chrooted the WebDAV client, we need to update our Rails routes with the proper root directory:

# config/routes.rb
mount DAV4Rack::Handler.new(
   :root => "/path/to/root_dir/",
   :root_uri_path => '/',
   :resource_class => MyAppName::MyResource
), :at => '/', :constraints => { :subdomain => "webdav" }

Voila! Now, when you load up your WebDAV client, the root you are presented with should be root_dir/username, securely segregating your client specific folders.

Thanks again to Chris for the great DAV4Rack!

Check out the sample app that has all of the above in a working Rails 3 app at: https://github.com/bryanrite/dav4rack-example-devise-subdirectories