doorkeeper 0.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of doorkeeper might be problematic. Click here for more details.

Files changed (39) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +106 -0
  3. data/Rakefile +42 -0
  4. data/app/assets/javascripts/doorkeeper/application.js +9 -0
  5. data/app/assets/stylesheets/doorkeeper/application.css +7 -0
  6. data/app/assets/stylesheets/doorkeeper/form.css +8 -0
  7. data/app/controllers/doorkeeper/application_controller.rb +27 -0
  8. data/app/controllers/doorkeeper/applications_controller.rb +35 -0
  9. data/app/controllers/doorkeeper/authorizations_controller.rb +26 -0
  10. data/app/controllers/doorkeeper/tokens_controller.rb +15 -0
  11. data/app/helpers/doorkeeper/application_helper.rb +4 -0
  12. data/app/models/access_grant.rb +29 -0
  13. data/app/models/access_token.rb +35 -0
  14. data/app/models/application.rb +20 -0
  15. data/app/views/doorkeeper/applications/_form.html.erb +27 -0
  16. data/app/views/doorkeeper/applications/edit.html.erb +13 -0
  17. data/app/views/doorkeeper/applications/index.html.erb +29 -0
  18. data/app/views/doorkeeper/applications/new.html.erb +13 -0
  19. data/app/views/doorkeeper/applications/show.html.erb +23 -0
  20. data/app/views/doorkeeper/authorizations/error.html.erb +1 -0
  21. data/app/views/doorkeeper/authorizations/new.html.erb +18 -0
  22. data/app/views/layouts/doorkeeper/application.html.erb +25 -0
  23. data/config/initializers/form_errors.rb +14 -0
  24. data/config/routes.rb +8 -0
  25. data/lib/doorkeeper.rb +19 -0
  26. data/lib/doorkeeper/config.rb +42 -0
  27. data/lib/doorkeeper/doorkeeper_for.rb +42 -0
  28. data/lib/doorkeeper/engine.rb +9 -0
  29. data/lib/doorkeeper/oauth/access_token_request.rb +84 -0
  30. data/lib/doorkeeper/oauth/authorization_request.rb +95 -0
  31. data/lib/doorkeeper/oauth/random_string.rb +15 -0
  32. data/lib/doorkeeper/validations.rb +29 -0
  33. data/lib/doorkeeper/version.rb +3 -0
  34. data/lib/generators/doorkeeper/install_generator.rb +17 -0
  35. data/lib/generators/doorkeeper/templates/README +13 -0
  36. data/lib/generators/doorkeeper/templates/initializer.rb +23 -0
  37. data/lib/generators/doorkeeper/templates/migration.rb +29 -0
  38. data/lib/tasks/doorkeeper_tasks.rake +4 -0
  39. metadata +162 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2011 Applicake. http://applicake.com
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # Doorkeeper - awesome oauth provider for your Rails app.
2
+
3
+ [![Build Status](https://secure.travis-ci.org/applicake/doorkeeper.png)](http://travis-ci.org/applicake/doorkeeper)
4
+
5
+ Doorkeeper is a gem that makes it easy to introduce oauth2 provider
6
+ functionality to your application.
7
+
8
+ So far it supports only Authorization Code
9
+ flow, but we will gradually introduce other flows.
10
+
11
+ For more information about Oauth2 go to
12
+ [Oauth2 Specs (Draft)](http://tools.ietf.org/html/draft-ietf-oauth-v2-22)
13
+
14
+ ## Installation
15
+
16
+ Put this in your Gemfile:
17
+
18
+ gem 'doorkeeper'
19
+
20
+ Run the installation generator with:
21
+
22
+ rails generate doorkeeper:install
23
+
24
+ This will generate the doorkeeper initializer and the oauth tables migration. Don't forget to run the migration in your application:
25
+
26
+ rake db:migrate
27
+
28
+ ## Configuration
29
+
30
+ The installation will mount the Doorkeeper routes to your app like this:
31
+
32
+ Rails.application.routes.draw do
33
+ mount Doorkeeper::Engine => "/oauth"
34
+ # your routes
35
+ end
36
+
37
+ This will mount following routes:
38
+
39
+ GET /oauth/authorize
40
+ POST /oauth/authorize
41
+ DELETE /oauth/authorize
42
+ POST /oauth/token
43
+ resources /oauth/applications
44
+
45
+ You need to configure Doorkeeper in order to provide resource_owner model and authentication block `initializers/doorkeeper.rb`
46
+
47
+ Doorkeeper.configure do
48
+ resource_owner_authenticator do |routes|
49
+ current_user || redirect_to('/sign_in', :alert => "Needs sign in.") # returns nil if current_user is not logged in
50
+ end
51
+ end
52
+
53
+ If you use devise, you may want to use warden to authenticate the block:
54
+
55
+ resource_owner_authenticator do |routes|
56
+ current_user || warden.authenticate!(:scope => :user)
57
+ end
58
+
59
+ ## Protecting resources (a.k.a your API endpoint)
60
+
61
+ In the controller add the before_filter to authorize the token
62
+
63
+ class ProtectedResourcesController < ApplicationController
64
+ doorkeeper_for :all # For all actions
65
+ doorkeeper_for :only => :index # Require token only for index action
66
+ doorkeeper_for :except => :show # Require token for all actions except show
67
+ end
68
+
69
+ ## Creating and using client applications
70
+
71
+ To start using OAuth 2 as a client first fire up the server with `rails server`, go to `/oauth/applications` and create an application for your client.
72
+
73
+ Choose a name and a callback url for it. If you use oauth2 gem you can specify your just generated client as:
74
+
75
+ require 'oauth2'
76
+ client_id = '...' # your client's id
77
+ client_secret = '...' # your client's secret
78
+ redirect_uri = '...' # your client's redirect uri
79
+ client = OAuth2::Client.new(
80
+ client_id,
81
+ client_secret,
82
+ :site => "http://localhost:3000"
83
+ )
84
+
85
+ If you changed the default mount path `/oauth` in your `routes.rb` you need to specify it in the oauth client as `:authorize_url` and `:token_url`. For more information, check the oauth2 gem documentation.
86
+
87
+ After that you can try to request an authorization code with the oauth2 gem as follow:
88
+
89
+ client.auth_code.authorize_url(:redirect_uri => redirect_uri)
90
+ # => http://localhost:3000/oauth/authorize?response_type=code&client_id=...&redirect_uri=...
91
+
92
+ If you visit the returned url, you'll see a screen to authorize your app. Click on `authorize` and you'll be redirected to your client redirect url.
93
+
94
+ Grab the code from the redirect url and request a access token with the following:
95
+
96
+ token = client.auth_code.get_token(parms[:code], :redirect_uri => redirect_uri)
97
+
98
+ You now have an access token to access you protected resources.
99
+
100
+ ## Contributing/Development
101
+
102
+ Check our [contributing guidelines page in the wiki](https://github.com/applicake/doorkeeper/wiki/Contributing)
103
+
104
+ ## Supported ruby versions
105
+
106
+ All supported ruby versions are [listed here](https://github.com/applicake/doorkeeper/wiki/Supported-Ruby-versions)
data/Rakefile ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'Doorkeeper'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ require 'rspec/core/rake_task'
24
+
25
+ desc 'Default: run specs.'
26
+ task :default => :spec
27
+
28
+ desc "Run all specs"
29
+ RSpec::Core::RakeTask.new(:spec => "app:test:prepare")
30
+
31
+ namespace :doorkeeper do
32
+ desc "Install doorkeeper in dummy app"
33
+ task :install do
34
+ cd 'spec/dummy'
35
+ system 'bundle exec rails g doorkeeper:install --force'
36
+ end
37
+ end
38
+
39
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
40
+ load 'rails/tasks/engine.rake'
41
+
42
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,9 @@
1
+ // This is a manifest file that'll be compiled into including all the files listed below.
2
+ // Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
3
+ // be included in the compiled file accessible from http://example.com/assets/application.js
4
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
5
+ // the compiled file.
6
+ //
7
+ //= require jquery
8
+ //= require jquery_ujs
9
+ //= require_tree .
@@ -0,0 +1,7 @@
1
+ /*
2
+ * This is a manifest file that'll automatically include all the stylesheets available in this directory
3
+ * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
4
+ * the top of the compiled file, but it's generally better to create a new file per style scope.
5
+ *= require_self
6
+ *= require_tree .
7
+ */
@@ -0,0 +1,8 @@
1
+ .error input {
2
+ color: #B94A48;
3
+ border-color: #EE5F5B;
4
+ }
5
+
6
+ .error .help-inline {
7
+ color: #B94A48;
8
+ }
@@ -0,0 +1,27 @@
1
+ module Doorkeeper
2
+ class ApplicationController < ActionController::Base
3
+ private
4
+
5
+ def authenticate_resource_owner!
6
+ current_resource_owner
7
+ end
8
+
9
+ def current_resource_owner
10
+ instance_exec(main_app, &Doorkeeper.authenticate_resource_owner)
11
+ end
12
+
13
+ def authenticate_admin!
14
+ if block = Doorkeeper.authenticate_admin
15
+ instance_exec(main_app, &block)
16
+ end
17
+ end
18
+
19
+ def method_missing(method, *args, &block)
20
+ if method =~ /_(url|path)$/
21
+ raise "Your path has not been found. Didn't you mean to call routes.#{method} in doorkeeper configuration blocks?"
22
+ else
23
+ super
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,35 @@
1
+ module Doorkeeper
2
+ class ApplicationsController < ApplicationController
3
+ respond_to :html
4
+
5
+ before_filter :authenticate_admin!
6
+
7
+ def index
8
+ @applications = Application.all
9
+ end
10
+
11
+ def new
12
+ @application = Application.new
13
+ end
14
+
15
+ def create
16
+ @application = Application.new(params[:application])
17
+ flash[:notice] = "Application created" if @application.save
18
+ respond_with @application
19
+ end
20
+
21
+ def show
22
+ @application = Application.find(params[:id])
23
+ end
24
+
25
+ def edit
26
+ @application = Application.find(params[:id])
27
+ end
28
+
29
+ def update
30
+ @application = Application.find(params[:id])
31
+ flash[:notice] = "Application updated" if @application.update_attributes(params[:application])
32
+ respond_with @application
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,26 @@
1
+ class Doorkeeper::AuthorizationsController < Doorkeeper::ApplicationController
2
+ before_filter :authenticate_resource_owner!
3
+
4
+ def new
5
+ render :error unless authorization.valid?
6
+ end
7
+
8
+ def create
9
+ if authorization.authorize
10
+ redirect_to authorization.success_redirect_uri
11
+ else
12
+ render :error
13
+ end
14
+ end
15
+
16
+ def destroy
17
+ authorization.deny
18
+ redirect_to authorization.invalid_redirect_uri
19
+ end
20
+
21
+ private
22
+
23
+ def authorization
24
+ @authorization ||= Doorkeeper::OAuth::AuthorizationRequest.new(current_resource_owner, params)
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ class Doorkeeper::TokensController < Doorkeeper::ApplicationController
2
+ def create
3
+ if token.authorize
4
+ render :json => token.authorization
5
+ else
6
+ render :json => token.error_response
7
+ end
8
+ end
9
+
10
+ private
11
+
12
+ def token
13
+ @token ||= Doorkeeper::OAuth::AccessTokenRequest.new(params)
14
+ end
15
+ end
@@ -0,0 +1,4 @@
1
+ module Doorkeeper
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,29 @@
1
+ class AccessGrant < ActiveRecord::Base
2
+ include Doorkeeper::OAuth::RandomString
3
+
4
+ self.table_name = "oauth_access_grants"
5
+
6
+ belongs_to :application
7
+
8
+ validates :resource_owner_id, :application_id, :token, :expires_in, :redirect_uri, :presence => true
9
+
10
+ before_validation :generate_token, :on => :create
11
+
12
+ def expired?
13
+ expires_in.present? && Time.now > expired_time
14
+ end
15
+
16
+ def accessible?
17
+ !expired?
18
+ end
19
+
20
+ private
21
+
22
+ def expired_time
23
+ self.created_at + expires_in.seconds
24
+ end
25
+
26
+ def generate_token
27
+ self.token = unique_random_string_for(:token)
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ class AccessToken < ActiveRecord::Base
2
+ include Doorkeeper::OAuth::RandomString
3
+
4
+ self.table_name = "oauth_access_tokens"
5
+
6
+ belongs_to :application
7
+
8
+ scope :accessible, where(:revoked_at => nil)
9
+
10
+ validates :application_id, :resource_owner_id, :token, :presence => true
11
+
12
+ before_validation :generate_token, :on => :create
13
+
14
+ def revoke!
15
+ update_attribute :revoked_at, DateTime.now
16
+ end
17
+
18
+ def revoked?
19
+ self.revoked_at.present?
20
+ end
21
+
22
+ def expired?
23
+ self.expires_at.present? && DateTime.now > self.expires_at
24
+ end
25
+
26
+ def accessible?
27
+ !expired? && !revoked?
28
+ end
29
+
30
+ private
31
+
32
+ def generate_token
33
+ self.token = unique_random_string_for(:token)
34
+ end
35
+ end
@@ -0,0 +1,20 @@
1
+ class Application < ActiveRecord::Base
2
+ include Doorkeeper::OAuth::RandomString
3
+ set_table_name 'oauth_applications'
4
+
5
+ has_many :access_grants
6
+
7
+ validates :name, :secret, :redirect_uri, :presence => true
8
+ validates :uid, :presence => true, :uniqueness => true
9
+
10
+ before_validation :generate_uid, :generate_secret, :on => :create
11
+
12
+ private
13
+ def generate_uid
14
+ self.uid = unique_random_string_for(:uid)
15
+ end
16
+
17
+ def generate_secret
18
+ self.secret = random_string
19
+ end
20
+ end
@@ -0,0 +1,27 @@
1
+ <%= form_for(application) do |f| %>
2
+ <fieldset>
3
+ <% if @application.errors.any? %>
4
+ <div class="alert-message error" data-alert><a class="close" href="#">×</a><p>Whoops! Check your form for possible errors</p></div>
5
+ <% end %>
6
+
7
+ <div class="clearfix">
8
+ <%= f.label :name %>
9
+ <div class="input">
10
+ <%= f.text_field :name %>
11
+ </div>
12
+ </div>
13
+
14
+ <div class="clearfix">
15
+ <%= f.label :redirect_uri %>
16
+ <div class="input">
17
+ <%= f.text_field :redirect_uri %>
18
+ </div>
19
+ </div class="clearfix">
20
+
21
+ <div class="actions">
22
+ <%= f.submit :Submit, :class => "btn primary" %>
23
+ <%= link_to "Cancel", applications_path, :class => "btn" %>
24
+ </div>
25
+ </fieldset>
26
+ <% end %>
27
+
@@ -0,0 +1,13 @@
1
+ <div class="span16">
2
+ <header class="page-header"><h2>Edit application</h2></header>
3
+ </div>
4
+
5
+ <div class="span10">
6
+ <%= render 'form', :application => @application %>
7
+ </div>
8
+
9
+ <div class="span6">
10
+ <h3>Actions</h3>
11
+ <p><%= link_to 'Back to application list', applications_path %></p>
12
+ </div>
13
+
@@ -0,0 +1,29 @@
1
+ <div class="span16">
2
+ <header class="page-header">
3
+ <h2>Your applications</h2>
4
+ </header>
5
+
6
+ <p><%= link_to 'New Application', new_application_path %></p>
7
+
8
+ <table class="zebra-striped">
9
+ <thead>
10
+ <tr>
11
+ <th>Name</th>
12
+ <th>Callback url</th>
13
+ <th></th>
14
+ <th></th>
15
+ </tr>
16
+ </thead>
17
+ <tbody>
18
+ <% @applications.each do |application| %>
19
+ <tr>
20
+ <td><%= link_to application.name, application %></td>
21
+ <td><%= application.redirect_uri %></td>
22
+ <td><%= link_to 'Edit', edit_application_path(application) %></td>
23
+ <td><%= link_to 'Destroy', application, :confirm => 'Are you sure?', :method => :delete %></td>
24
+ </tr>
25
+ <% end %>
26
+ </tbody>
27
+ </table>
28
+
29
+ </div>
@@ -0,0 +1,13 @@
1
+ <div class="span16">
2
+ <header class="page-header"><h2>New application</h2></header>
3
+ </div>
4
+
5
+ <div class="span10">
6
+ <%= render 'form', :application => @application %>
7
+ </div>
8
+
9
+ <div class="span6">
10
+ <h3>Actions</h3>
11
+ <p><%= link_to 'Back to application list', applications_path %></p>
12
+ </div>
13
+
@@ -0,0 +1,23 @@
1
+ <div class="span16">
2
+ <header class="page-header">
3
+ <h1>Application: <%= @application.name %></h1>
4
+ </header>
5
+ </div>
6
+
7
+ <div class="span10">
8
+ <h4>Callback url:</h4>
9
+ <p><%= @application.redirect_uri %></p>
10
+
11
+ <h4>Application Id:</h4>
12
+ <p><code><%= @application.uid %></code></p>
13
+
14
+ <h4>Secret:</h4>
15
+ <p><code><%= @application.secret %></code></p>
16
+ </div>
17
+
18
+ <div class="span6">
19
+ <h3>Actions</h3>
20
+ <p><%= link_to 'List all', applications_path %></p>
21
+ <p><%= link_to 'Edit', edit_application_path(@application) %></p>
22
+ <p><%= link_to 'Remove', @application, :method => :delete, :confirm => "Are you sure?" %></p>
23
+ </div>
@@ -0,0 +1 @@
1
+ <p>An error has occurred</p>
@@ -0,0 +1,18 @@
1
+ <div class="span16">
2
+ <h2>Authorize <%= @authorization.client.name %> to use your account?</h2>
3
+ </div>
4
+
5
+ <div class="span16">
6
+ <%= form_tag authorization_path do %>
7
+ <%= hidden_field_tag :client_id, @authorization.client_id %>
8
+ <%= hidden_field_tag :redirect_uri, @authorization.redirect_uri %>
9
+ <%= hidden_field_tag :response_type, @authorization.response_type %>
10
+ <%= submit_tag "Authorize", :class => "btn primary" %> or
11
+ <% end %>
12
+ <%= form_tag authorization_path, :method => :delete do %>
13
+ <%= hidden_field_tag :client_id, @authorization.client_id %>
14
+ <%= hidden_field_tag :redirect_uri, @authorization.redirect_uri %>
15
+ <%= hidden_field_tag :response_type, @authorization.response_type %>
16
+ <%= button_tag "Deny", :class => "btn" %>
17
+ <% end %>
18
+ </div>
@@ -0,0 +1,25 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Doorkeeper</title>
5
+ <%= stylesheet_link_tag "http://twitter.github.com/bootstrap/1.4.0/bootstrap.min.css" %>
6
+ <%= stylesheet_link_tag "doorkeeper/application" %>
7
+ <%= javascript_include_tag "doorkeeper/application" %>
8
+ <%= csrf_meta_tags %>
9
+ </head>
10
+ <body>
11
+ <section id="main" class="container">
12
+ <div class="content">
13
+ <div class="row">
14
+ <% flash.each do |key, message| %>
15
+ <div class="span16">
16
+ <div class="alert-message <%= key %>" data-alert><a class="close" href="#">×</a><p><%= message %></p></div>
17
+ </div>
18
+ <% end %>
19
+
20
+ <%= yield %>
21
+ </div>
22
+ </div>
23
+ </section>
24
+ </body>
25
+ </html>
@@ -0,0 +1,14 @@
1
+ ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
2
+ if html_tag.match("input")
3
+ message = (error = instance.error_message).respond_to?(:join) ? error.join(",") : error
4
+ %(<span class="error">
5
+ #{html_tag}
6
+ <span class="help-inline">
7
+ #{message}
8
+ </span>
9
+ </span>
10
+ ).html_safe
11
+ else
12
+ html_tag
13
+ end
14
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,8 @@
1
+ Doorkeeper::Engine.routes.draw do
2
+ get 'authorize', :to => "authorizations#new", :as => :authorization
3
+ post 'authorize', :to => "authorizations#create", :as => :authorization
4
+ delete 'authorize', :to => "authorizations#destroy", :as => :authorization
5
+ post 'token', :to => "tokens#create", :as => :token
6
+
7
+ resources :applications
8
+ end
data/lib/doorkeeper.rb ADDED
@@ -0,0 +1,19 @@
1
+ require "doorkeeper/engine"
2
+ require "doorkeeper/config"
3
+ require "doorkeeper/doorkeeper_for"
4
+
5
+ module Doorkeeper
6
+ autoload :Validations, "doorkeeper/validations"
7
+
8
+ module OAuth
9
+ class MismatchRedirectURI < StandardError; end
10
+
11
+ autoload :RandomString, "doorkeeper/oauth/random_string"
12
+ autoload :AuthorizationRequest, "doorkeeper/oauth/authorization_request"
13
+ autoload :AccessTokenRequest, "doorkeeper/oauth/access_token_request"
14
+ end
15
+
16
+ def self.setup
17
+ yield self
18
+ end
19
+ end
@@ -0,0 +1,42 @@
1
+ module Doorkeeper
2
+ def self.configure(&block)
3
+ @@config = Config.new(&block)
4
+ end
5
+
6
+ class Config
7
+ module ConfigOptions
8
+ def register_config_option(name, attribute, receives_block = true)
9
+ define_method name do |*args, &block|
10
+ if receives_block
11
+ self.instance_variable_set(:"@#{attribute}", block)
12
+ else
13
+ self.instance_variable_set(:"@#{attribute}", args[0])
14
+ end
15
+ end
16
+
17
+ attr_reader attribute
18
+ public attribute
19
+
20
+ Doorkeeper.class_eval "
21
+ def self.#{attribute}
22
+ @@config.#{attribute}
23
+ end
24
+ "
25
+ end
26
+
27
+ def extended(base)
28
+ base.send(:private, :register_method)
29
+ end
30
+ end
31
+
32
+ extend ConfigOptions
33
+
34
+ register_config_option :resource_owner_authenticator, :authenticate_resource_owner
35
+ register_config_option :admin_authenticator, :authenticate_admin
36
+
37
+ def initialize(&block)
38
+ instance_eval &block
39
+ end
40
+ end
41
+ end
42
+
@@ -0,0 +1,42 @@
1
+ module Doorkeeper
2
+ module Controller
3
+ module ClassMethods
4
+ def doorkeeper_for(options)
5
+ raise "You have to specify some option for doorkeeper_for method" unless options.present?
6
+ options = nil if options == :all
7
+ if options
8
+ before_filter :doorkeeper_before_filter, options
9
+ else
10
+ before_filter :doorkeeper_before_filter
11
+ end
12
+ end
13
+ end
14
+
15
+ def self.included(base)
16
+ base.extend ClassMethods
17
+ base.send(:private, :doorkeeper_before_filter, :doorkeeper_token, :doorkeeper_valid_token, :get_doorkeeper_token)
18
+ end
19
+
20
+ def doorkeeper_before_filter
21
+ head :unauthorized unless doorkeeper_valid_token
22
+ end
23
+
24
+ def doorkeeper_valid_token
25
+ doorkeeper_token and doorkeeper_token.accessible?
26
+ end
27
+
28
+ def doorkeeper_token
29
+ @token ||= get_doorkeeper_token
30
+ end
31
+
32
+ def get_doorkeeper_token
33
+ token = params[:access_token] || params[:bearer_token] || request.env['HTTP_AUTHORIZATION']
34
+ if token
35
+ token.gsub!(/Bearer /, '')
36
+ end
37
+ AccessToken.find_by_token(token)
38
+ end
39
+ end
40
+ end
41
+
42
+ ActionController::Base.send(:include, Doorkeeper::Controller)
@@ -0,0 +1,9 @@
1
+ module Doorkeeper
2
+ class Engine < Rails::Engine
3
+ isolate_namespace Doorkeeper
4
+
5
+ config.generators do |g|
6
+ g.test_framework :rspec, :view_specs => false
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,84 @@
1
+ module Doorkeeper::OAuth
2
+ class AccessTokenRequest
3
+ include Doorkeeper::Validations
4
+
5
+ ATTRIBUTES = [
6
+ :client_id,
7
+ :client_secret,
8
+ :grant_type,
9
+ :code,
10
+ :redirect_uri,
11
+ ]
12
+
13
+ validate :attributes, :error => :invalid_request
14
+ validate :client, :error => :invalid_client
15
+ validate :grant, :error => :invalid_grant
16
+ validate :grant_type, :error => :unsupported_grant_type
17
+
18
+ attr_accessor *ATTRIBUTES
19
+
20
+ def initialize(attributes = {})
21
+ ATTRIBUTES.each { |attr| instance_variable_set("@#{attr}", attributes[attr]) }
22
+ validate
23
+ end
24
+
25
+ def authorize
26
+ create_access_token if valid?
27
+ end
28
+
29
+ def authorization
30
+ { 'access_token' => access_token,
31
+ 'token_type' => token_type
32
+ }
33
+ end
34
+
35
+ def valid?
36
+ self.error.nil?
37
+ end
38
+
39
+ def access_token
40
+ @access_token.token
41
+ end
42
+
43
+ def token_type
44
+ "bearer"
45
+ end
46
+
47
+ def error_response
48
+ { 'error' => error.to_s }
49
+ end
50
+
51
+ private
52
+
53
+ def grant
54
+ @grant ||= AccessGrant.find_by_token(@code)
55
+ end
56
+
57
+ def client
58
+ @client ||= Application.find_by_uid_and_secret(@client_id, @client_secret)
59
+ end
60
+
61
+ def create_access_token
62
+ @access_token = AccessToken.create!({
63
+ :application_id => client.uid,
64
+ :resource_owner_id => grant.resource_owner_id,
65
+ })
66
+ end
67
+
68
+ def validate_attributes
69
+ code.present? && grant_type.present? && redirect_uri.present?
70
+ end
71
+
72
+ def validate_client
73
+ !!client
74
+ end
75
+
76
+ def validate_grant
77
+ grant && grant.accessible? && grant.application_id == client.id && grant.redirect_uri == redirect_uri
78
+ end
79
+
80
+ def validate_grant_type
81
+ grant_type == "authorization_code"
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,95 @@
1
+ module Doorkeeper::OAuth
2
+ class AuthorizationRequest
3
+ include Doorkeeper::Validations
4
+
5
+ DEFAULT_EXPIRATION_TIME = 600
6
+
7
+ ATTRIBUTES = [
8
+ :response_type,
9
+ :client_id,
10
+ :redirect_uri,
11
+ :scope,
12
+ :state
13
+ ]
14
+
15
+ validate :attributes, :error => :invalid_request
16
+ validate :client, :error => :invalid_client
17
+ validate :redirect_uri, :error => :invalid_redirect_uri
18
+ validate :response_type, :error => :unsupported_response_type
19
+
20
+ attr_accessor *ATTRIBUTES
21
+ attr_accessor :resource_owner, :error
22
+
23
+ def initialize(resource_owner, attributes)
24
+ ATTRIBUTES.each { |attr| instance_variable_set("@#{attr}", attributes[attr]) }
25
+ @resource_owner = resource_owner
26
+ @grant = nil
27
+ validate
28
+ end
29
+
30
+ def authorize
31
+ create_authorization if valid?
32
+ end
33
+
34
+ def deny
35
+ self.error = :access_denied
36
+ end
37
+
38
+ def success_redirect_uri
39
+ build_uri do |uri|
40
+ query = "code=#{token}"
41
+ query << "&state=#{state}" if has_state?
42
+ uri.query = query
43
+ end
44
+ end
45
+
46
+ def invalid_redirect_uri
47
+ build_uri { |uri| uri.query = "error=#{error}" }
48
+ end
49
+
50
+ def client
51
+ @client ||= Application.find_by_uid(client_id)
52
+ end
53
+
54
+ private
55
+
56
+ def create_authorization
57
+ @grant = AccessGrant.create!(
58
+ :application_id => client.id,
59
+ :resource_owner_id => resource_owner.id,
60
+ :expires_in => DEFAULT_EXPIRATION_TIME,
61
+ :redirect_uri => redirect_uri
62
+ )
63
+ end
64
+
65
+ def has_state?
66
+ state.present?
67
+ end
68
+
69
+ def token
70
+ @grant.token
71
+ end
72
+
73
+ def build_uri
74
+ uri = URI.parse(client.redirect_uri)
75
+ yield uri
76
+ uri.to_s
77
+ end
78
+
79
+ def validate_attributes
80
+ %w(response_type client_id redirect_uri).all? { |attr| send(attr).present? }
81
+ end
82
+
83
+ def validate_client
84
+ !!client
85
+ end
86
+
87
+ def validate_redirect_uri
88
+ client.redirect_uri == redirect_uri
89
+ end
90
+
91
+ def validate_response_type
92
+ response_type == "code"
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,15 @@
1
+ module Doorkeeper::OAuth
2
+ module RandomString
3
+ def random_string
4
+ SecureRandom.hex(32)
5
+ end
6
+
7
+ def unique_random_string_for(attribute)
8
+ loop do
9
+ token = random_string
10
+ break token unless self.class.send("find_by_#{attribute}", token)
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,29 @@
1
+ module Doorkeeper
2
+ module Validations
3
+ extend ActiveSupport::Concern
4
+
5
+ attr_accessor :error
6
+
7
+ def validate
8
+ @error = nil
9
+ self.class.validations.each do |validation|
10
+ break if @error
11
+ @error = validation.last unless send("validate_#{validation.first}")
12
+ end
13
+ end
14
+
15
+ def valid?
16
+ @error.nil?
17
+ end
18
+
19
+ module ClassMethods
20
+ def validate(attribute, options = {})
21
+ validations << [attribute, options[:error]]
22
+ end
23
+
24
+ def validations
25
+ @validations ||= []
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module Doorkeeper
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,17 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ class Doorkeeper::InstallGenerator < Rails::Generators::Base
4
+ include Rails::Generators::Migration
5
+ source_root File.expand_path('../templates', __FILE__)
6
+
7
+ def install
8
+ migration_template 'migration.rb', 'db/migrate/create_doorkeeper_tables.rb'
9
+ template "initializer.rb", "config/initializers/doorkeeper.rb"
10
+ route "mount Doorkeeper::Engine => '/oauth'"
11
+ readme "README"
12
+ end
13
+
14
+ def self.next_migration_number(dirname)
15
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ ===============================================================================
2
+
3
+ There is a setup that you need to do before you can use doorkeeper.
4
+
5
+ Step 1.
6
+ Go to config/initializers/doorkeeper.rb and configure
7
+ resource_owner_authenticator block.
8
+
9
+ Step 2.
10
+ That's it, that's all. Enjoy!
11
+
12
+ ===============================================================================
13
+
@@ -0,0 +1,23 @@
1
+ Doorkeeper.configure do
2
+ # This block will be called to check whether the
3
+ # resource owner is authenticated or not
4
+ resource_owner_authenticator do |routes|
5
+ raise "Please configure doorkeeper resource_owner_authenticator block located in #{__FILE__}"
6
+ # Put your resource owner authentication logic here.
7
+ # If you want to use named routes from your app you need
8
+ # to call them on routes object eg.
9
+ # routes.new_user_session_path
10
+ # e.g. User.find_by_id(session[:user_id]) || redirect_to routes.new_user_seesion_path
11
+ end
12
+
13
+ # If you want to restrict the access to the web interface for
14
+ # adding oauth authorized applications you need to declare the
15
+ # block below
16
+ # admin_authenticator do |routes|
17
+ # # Put your admin authentication logic here.
18
+ # # If you want to use named routes from your app you need
19
+ # # to call them on routes object eg.
20
+ # # routes.new_admin_session_path
21
+ # Admin.find_by_id(session[:admin_id]) || redirect_to routes.new_admin_session_path
22
+ # end
23
+ end
@@ -0,0 +1,29 @@
1
+ class CreateDoorkeeperTables < ActiveRecord::Migration
2
+ def change
3
+ create_table :oauth_applications do |t|
4
+ t.string :name, :null => false
5
+ t.string :uid, :null => false
6
+ t.string :secret, :null => false
7
+ t.string :redirect_uri, :null => false
8
+ t.timestamps
9
+ end
10
+
11
+ create_table :oauth_access_grants do |t|
12
+ t.integer :resource_owner_id, :null => false
13
+ t.integer :application_id, :null => false
14
+ t.string :token, :null => false
15
+ t.integer :expires_in, :null => false
16
+ t.string :redirect_uri, :null => false
17
+ t.datetime :created_at, :null => false
18
+ end
19
+
20
+ create_table :oauth_access_tokens do |t|
21
+ t.integer :resource_owner_id, :null => false
22
+ t.integer :application_id, :null => false
23
+ t.string :token, :null => false
24
+ t.datetime :expires_at
25
+ t.datetime :revoked_at
26
+ t.datetime :created_at, :null => false
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :doorkeeper do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,162 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: doorkeeper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Felipe Elias Philipp
9
+ - Piotr Jakubowski
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2011-11-28 00:00:00.000000000Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rails
17
+ requirement: &70336834072240 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: 3.1.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *70336834072240
26
+ - !ruby/object:Gem::Dependency
27
+ name: sqlite3
28
+ requirement: &70336834071600 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: *70336834071600
37
+ - !ruby/object:Gem::Dependency
38
+ name: rspec-rails
39
+ requirement: &70336834070820 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *70336834070820
48
+ - !ruby/object:Gem::Dependency
49
+ name: capybara
50
+ requirement: &70336834070040 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: *70336834070040
59
+ - !ruby/object:Gem::Dependency
60
+ name: generator_spec
61
+ requirement: &70336834069340 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: *70336834069340
70
+ - !ruby/object:Gem::Dependency
71
+ name: factory_girl_rails
72
+ requirement: &70336834068500 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: *70336834068500
81
+ - !ruby/object:Gem::Dependency
82
+ name: timecop
83
+ requirement: &70336834068020 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: *70336834068020
92
+ description: Doorkeeper is an OAuth 2 provider for Rails.
93
+ email:
94
+ - felipe@applicake.com
95
+ - piotr.jakubowski@applicake.com
96
+ executables: []
97
+ extensions: []
98
+ extra_rdoc_files: []
99
+ files:
100
+ - app/assets/javascripts/doorkeeper/application.js
101
+ - app/assets/stylesheets/doorkeeper/application.css
102
+ - app/assets/stylesheets/doorkeeper/form.css
103
+ - app/controllers/doorkeeper/application_controller.rb
104
+ - app/controllers/doorkeeper/applications_controller.rb
105
+ - app/controllers/doorkeeper/authorizations_controller.rb
106
+ - app/controllers/doorkeeper/tokens_controller.rb
107
+ - app/helpers/doorkeeper/application_helper.rb
108
+ - app/models/access_grant.rb
109
+ - app/models/access_token.rb
110
+ - app/models/application.rb
111
+ - app/views/doorkeeper/applications/_form.html.erb
112
+ - app/views/doorkeeper/applications/edit.html.erb
113
+ - app/views/doorkeeper/applications/index.html.erb
114
+ - app/views/doorkeeper/applications/new.html.erb
115
+ - app/views/doorkeeper/applications/show.html.erb
116
+ - app/views/doorkeeper/authorizations/error.html.erb
117
+ - app/views/doorkeeper/authorizations/new.html.erb
118
+ - app/views/layouts/doorkeeper/application.html.erb
119
+ - config/initializers/form_errors.rb
120
+ - config/routes.rb
121
+ - lib/doorkeeper/config.rb
122
+ - lib/doorkeeper/doorkeeper_for.rb
123
+ - lib/doorkeeper/engine.rb
124
+ - lib/doorkeeper/oauth/access_token_request.rb
125
+ - lib/doorkeeper/oauth/authorization_request.rb
126
+ - lib/doorkeeper/oauth/random_string.rb
127
+ - lib/doorkeeper/validations.rb
128
+ - lib/doorkeeper/version.rb
129
+ - lib/doorkeeper.rb
130
+ - lib/generators/doorkeeper/install_generator.rb
131
+ - lib/generators/doorkeeper/templates/initializer.rb
132
+ - lib/generators/doorkeeper/templates/migration.rb
133
+ - lib/generators/doorkeeper/templates/README
134
+ - lib/tasks/doorkeeper_tasks.rake
135
+ - MIT-LICENSE
136
+ - Rakefile
137
+ - README.md
138
+ homepage: https://github.com/applicake/doorkeeper
139
+ licenses: []
140
+ post_install_message:
141
+ rdoc_options: []
142
+ require_paths:
143
+ - lib
144
+ required_ruby_version: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ none: false
152
+ requirements:
153
+ - - ! '>='
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ requirements: []
157
+ rubyforge_project:
158
+ rubygems_version: 1.8.10
159
+ signing_key:
160
+ specification_version: 3
161
+ summary: Doorkeeper is an OAuth 2 provider for Rails.
162
+ test_files: []