• Stars
    star
    125
  • Rank 286,335 (Top 6 %)
  • Language
    Ruby
  • License
    MIT License
  • Created about 14 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Authentication protocol for use in your routing and model context

Shield

Shield

n. A solid piece of metal code used to protect your application.

Why another authentication library?

  1. Because most of the other libraries are too huge.
  2. Extending other libraries is a pain.
  3. Writing code is fun :-).

What shield is

  1. Simple (~ 110 lines of Ruby code).
  2. Doesn't get in the way.
  3. Treats you like a grown up.

What shield is not

  • is not a ready-made end-to-end authentication solution.
  • is not biased towards any kind of ORM.

Understanding Shield in 15 minutes

Shield::Model

Shield::Model is a very basic protocol for doing authentication against your model. It doesn't assume a lot, apart from the following:

  1. You will implement User.fetch which receives the login string.
  2. You have an attribute crypted_password which is able to store up to 192 characters.

And that's it.

In order to implement the model protocol, you start by including Shield::Model.

class User < Struct.new(:email, :crypted_password)
  include Shield::Model

  def self.fetch(email)
    user = new(email)
    user.password = "pass1234"

    return user
  end
end

By including Shield::Model, you get all the general methods needed in order to do authentication.

  1. You get User.authenticate which receives the login string and password as the two parameters.
  2. You get User#password= which automatically converts the clear text password into a hashed form and assigns it into #crypted_password.
u = User.new("[email protected]")

# A password accessor has been added which manages `crypted_password`.
u.password = "pass1234"

Shield::Password.check("pass1234", u.crypted_password)
# => true

# Since we've hard coded all passwords to pass1234
# we're able to authenticate properly.
nil == User.authenticate("[email protected]", "pass1234")
# => false

# If we try a different password on the other hand,
# we get `nil`.
nil == User.authenticate("[email protected]", "wrong")
# => true

Shield uses Armor for encrypting passwords. Armor is a pure ruby implementation of PBKDF2, a password-based key derivation function recommended for the protection of electronically-stored data.

To make Shield work with any ORM, make sure that an .[] method which fetches the user instance by id is implemented.

class User
  include Shield::Model

  # ...

  def self.[](id)
    get id
  end
end

Logging in with an email and username?

If your requirements dictate that you need to be able to support logging in using either username or email, then you can simply extend User.fetch a bit by doing:

# in Sequel (http://sequel.rubyforge.org)
class User < Sequel::Model
  include Shield::Model

  def self.fetch(identifier)
    filter(email: identifier).first || filter(username: identifier).first
  end
end

# in Ohm (http://ohm.keyvalue.org)
class User < Ohm::Model
  include Shield::Model

  attribute :email
  attribute :username

  unique :email
  unique :username

  def self.fetch(identifier)
    with(:email, identifier) || with(:username, identifier)
  end
end

If you want to allow case-insensitive logins for some reason, you can simply normalize the values to their lowercase form.

Shield::Helpers

As the name suggests, Shield::Helpers is out there to aid you a bit, but this time it aids you in the context of your Rack application.

Shield::Helpers assumes only the following:

  1. You have included in your application a Session handler, (e.g. Rack::Session::Cookie)
  2. You have an env method which returns the environment hash as was passed in Rack.

Note: As of this writing, Sinatra, Cuba & Rails adhere to having an env method in the handler / controller context. Shield also ships with tests for both Cuba and Sinatra.

require "sinatra"

# Satisfies assumption number 1 above.
use Rack::Session::Cookie

# Mixes `Shield::Helpers` into your routes context.
helpers Shield::Helpers

get "/private" do
  error(401) unless authenticated(User)

  "Private"
end

get "/login" do
  erb :login
end

post "/login" do
  if login(User, params[:login], params[:password])
    remember(authenticated(User)) if params[:remember_me]
    redirect(params[:return] || "/")
  else
    redirect "/login"
  end
end

get "/logout" do
  logout(User)
  redirect "/"
end

__END__

@@ login
<h1>Login</h1>

<form action='/login' method='post'>
<input type='text' name='login' placeholder='Email'>
<input type='password' name='password' placeholder='Password'>
<input type='submit' name='proceed' value='Login'>

Note for the reader: The redirect to params[:return] in the example is vulnerable to URL hijacking. You can whitelist redirectable urls, or simply make sure the URL matches the pattern /\A[\/a-z0-9\-]+\z/i.

Shield::Middleware

If you have a keen eye you might have noticed that instead of redirecting away to the login URL in the example above, we instead chose to do a 401 Unauthorized. In strict HTTP Status code terms, this is the proper approach. The redirection is simply the user experience pattern that has emerged in web applications.

But don't despair! If you want to do redirects simply add Shield::Middleware to your middleware stack like so:

# taken from example above
use Shield::Middleware, "/login"
use Rack::Session::Cookie

# rest of code follows here
# ...

Now when your application responds with a 401, Shield::Middleware will be responsible for doing the redirect to /login.

If you try and do a curl --head http://localhost:4567/private with Shield::Middleware, you'll get a response similar to the following:

HTTP/1.1 302 Found
Location: http://localhost:4567/login?return=%2Fprivate
Content-Type: text/html

Notice that it specifies /private as the return URL.

Installation

You can install it using rubygems:

gem install shield

More Repositories

1

requests

a port of python's requests
Ruby
55
star
2

dep

Ruby
48
star
3

nobi

Ruby
33
star
4

malone

Simple ruby mailing solution which always delivers.
Ruby
27
star
5

armor

A pure ruby PBKDF2 implementation
Ruby
21
star
6

cuba-contrib

A collection of useful modules for Cuba
Ruby
19
star
7

nobi.js

Javascript port of python's itsdangerous
JavaScript
15
star
8

rprov

Redis Provisioning
Ruby
12
star
9

cookie.lua

basic cookie building / parsing for lua
Lua
9
star
10

dotfiles

Vim Script
6
star
11

orchestra

REST based modules with tokyo underneath
Ruby
6
star
12

crudo

Ruby
6
star
13

imagery

Simple image resizing
Ruby
6
star
14

ip_to_country

Gives you the 2 letter iso of the country given an ip address (based off of rubyquiz, just made it a plugin)
Ruby
6
star
15

chimp

A lightweight mailchimp client
Ruby
5
star
16

acts_as_solr_fu

Acts as Solr enhancements such as the PaginatedCollectionProxy, and :include => behavior changes
Ruby
5
star
17

osx_strings_file_parser

OS X Strings file parser for localizations
Ruby
4
star
18

peons

A safe worker queue on top of Redis
Ruby
4
star
19

shield-contrib

Ruby
4
star
20

blog

My Personal Blog
Ruby
3
star
21

frankrb

A short demonstration of building a basic sinatra-ish clone
Ruby
3
star
22

scraper

TODO
Ruby
3
star
23

redic.js

JavaScript
3
star
24

an

AN: A thin Authorize.NET client
Ruby
3
star
25

uri.lua

basic uri parsing / building for lua
Lua
2
star
26

redis-replicated

A very experimental approach to handling replicated redis
Ruby
2
star
27

redis-lua-playground

Lua
2
star
28

ntable

Ruby
2
star
29

cyrildavid.com

JavaScript
2
star
30

tell

(T)hin Ruby Secure Sh(ell)
Ruby
2
star
31

theia

Goddess of Forms
Ruby
2
star
32

ohm-redux

Ruby
2
star
33

scrivener.lua

Lua
1
star
34

rails-templates

Ruby
1
star
35

qs.lua

Lua
1
star
36

scrivener.js

JavaScript
1
star
37

s3env

Go
1
star
38

rspec_expectation_matchers

Ruby
1
star
39

draft

Ruby
1
star
40

product.com

Ruby
1
star
41

aiakos

Ohm-specific authentication solution.
Ruby
1
star
42

resume

Cyril David's Resume
1
star
43

mote-debug

Ruby
1
star
44

zones

Lua
1
star
45

batcher

minimal count / time based batching for flushing payloads
Go
1
star
46

profileapp

1
star
47

greene

Golang connection recycler
Go
1
star
48

empty

1
star
49

solr_query

TODO
Ruby
1
star
50

sarama-cluster-tester

Go
1
star
51

sss

Ruby
1
star
52

acts_as_dropdown

gem'ified version of DeLynn Berry's original plugin
Ruby
1
star
53

cyx.github.com

github page
1
star
54

twitter_fu

quick and short lib for consuming rss of a twitter user
Ruby
1
star
55

exco

Ruby
1
star