├── Gemfile ├── README.rdoc ├── app ├── controllers │ ├── oauth_clients_controller.rb │ └── oauth_controller.rb ├── models │ ├── access_token.rb │ ├── client_application.rb │ ├── oauth2_token.rb │ ├── oauth2_verifier.rb │ ├── oauth_nonce.rb │ ├── oauth_token.rb │ └── request_token.rb └── views │ ├── oauth │ ├── authorize.html.erb │ ├── authorize_failure.html.erb │ ├── authorize_success.html.erb │ └── oauth2_authorize.html.erb │ └── oauth_clients │ ├── _form.html.erb │ ├── edit.html.erb │ ├── index.html.erb │ ├── new.html.erb │ └── show.html.erb ├── assets └── stylesheets │ └── application.css ├── config ├── locales │ ├── en.yml │ ├── ja.yml │ └── zh.yml └── routes.rb ├── db └── migrate │ └── 001_create_oauth_tables.rb ├── init.rb ├── lib └── oauth_provider_user_patch.rb └── test ├── fixtures ├── client_applications.yml ├── oauth_nonces.yml └── oauth_tokens.yml ├── test_helper.rb └── unit ├── client_application_test.rb ├── oauth_nonce_test.rb └── oauth_token_test.rb /Gemfile: -------------------------------------------------------------------------------- 1 | gem "oauth-plugin", "~> 0.5.1" 2 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Redmine OAuth Provider 2 | 3 | == Requirements 4 | 5 | Redmine 2.3 or later 6 | 7 | == Installation 8 | 9 | Type below commands: 10 | 11 | $ cd $RAILS_ROOT/plugins 12 | $ git clone git://github.com/suer/redmine_oauth_provider.git 13 | 14 | Create database tables: 15 | rake redmine:plugins:migrate RAILS_ENV=production 16 | 17 | Then, restart your Redmine. 18 | 19 | == Manage your client applications 20 | 21 | Access to 22 | 23 | http://your.redmine.host/oauth_clients/ 24 | 25 | And register your applications. 26 | 27 | == OAuth URL 28 | 29 | * Request Token URL: 30 | 31 | http://your.redmine.host/oauth/request_token 32 | 33 | * Access Token URL: 34 | 35 | http://your.redmine.host/oauth/access_token 36 | 37 | * Authorize URL: 38 | 39 | http://your.redmine.host/oauth/authorize 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/controllers/oauth_clients_controller.rb: -------------------------------------------------------------------------------- 1 | class OauthClientsController < ApplicationController 2 | unloadable 3 | 4 | before_filter :get_user 5 | before_filter :get_client_application, :only => [:show, :edit, :update, :destroy] 6 | 7 | def index 8 | @client_applications = @user.client_applications 9 | @tokens = @user.tokens.where('oauth_tokens.invalidated_at is null and oauth_tokens.authorized_at is not null') 10 | end 11 | 12 | def new 13 | @client_application = ClientApplication.new 14 | end 15 | 16 | def create 17 | @client_application = @user.client_applications.build(params[:client_application]) 18 | if @client_application.save 19 | flash[:notice] = l("Registered the information successfully") 20 | redirect_to :action => "show", :id => @client_application.id 21 | else 22 | render :action => "new" 23 | end 24 | end 25 | 26 | def show 27 | end 28 | 29 | def edit 30 | end 31 | 32 | def update 33 | if @client_application.update_attributes(params[:client_application]) 34 | flash[:notice] = l("Updated the client information successfully") 35 | redirect_to :action => "show", :id => @client_application.id 36 | else 37 | render :action => "edit" 38 | end 39 | end 40 | 41 | def destroy 42 | @client_application.destroy 43 | flash[:notice] = l("Destroyed the client application registration") 44 | redirect_to :action => "index" 45 | end 46 | 47 | private 48 | 49 | def get_user 50 | render_403 unless User.current.logged? 51 | 52 | if params[:user_id] && params[:user_id] != User.current.id.to_s 53 | if User.current.admin? 54 | @user = User.find(params[:user_id]) 55 | else 56 | render_403 57 | end 58 | else 59 | @user = User.current 60 | end 61 | end 62 | 63 | def get_client_application 64 | unless @client_application = @user.client_applications.find(params[:id]) 65 | flash.now[:error] = l("Wrong application id") 66 | raise ActiveRecord::RecordNotFound 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /app/controllers/oauth_controller.rb: -------------------------------------------------------------------------------- 1 | require 'oauth/controllers/provider_controller' 2 | class OauthController < ApplicationController 3 | unloadable 4 | skip_filter :check_if_login_required 5 | include OAuth::Controllers::ProviderController 6 | 7 | before_filter :login_or_oauth_required, :only => [:user_info] 8 | 9 | def logged_in? 10 | User.current.logged? 11 | end 12 | 13 | def login_required 14 | raise Unauthorized unless User.current.logged? 15 | end 16 | 17 | def user_info 18 | user_hash = { :user => {} } 19 | user = User.find(session[:user_id]) 20 | if user 21 | hash = user.attributes 22 | hash.delete(:hashed_password) 23 | hash.delete(:salt) 24 | hash.merge!(:mail => user.mail) 25 | user_hash = { :user => hash } 26 | end 27 | respond_to do |format| 28 | format.json { render :json => user_hash } 29 | end 30 | end 31 | 32 | def current_user 33 | User.find(session[:user_id]) 34 | end 35 | 36 | def current_user=(user) 37 | start_user_session(user) 38 | end 39 | 40 | def authorize_with_allow 41 | params[:authorize] = '1' if params[:allow] 42 | authorize_without_allow 43 | end 44 | alias_method_chain :authorize, :allow 45 | end 46 | -------------------------------------------------------------------------------- /app/models/access_token.rb: -------------------------------------------------------------------------------- 1 | class AccessToken < OauthToken 2 | unloadable 3 | 4 | validates_presence_of :user, :secret 5 | before_create :set_authorized_at 6 | attr_accessible :user 7 | 8 | # Implement this to return a hash or array of the capabilities the access token has 9 | # This is particularly useful if you have implemented user defined permissions. 10 | # def capabilities 11 | # {:invalidate=>"/oauth/invalidate",:capabilities=>"/oauth/capabilities"} 12 | # end 13 | 14 | protected 15 | 16 | def set_authorized_at 17 | self.authorized_at = Time.now 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/models/client_application.rb: -------------------------------------------------------------------------------- 1 | require 'oauth' 2 | class ClientApplication < ActiveRecord::Base 3 | unloadable 4 | 5 | belongs_to :user 6 | has_many :tokens, :class_name => "OauthToken" 7 | has_many :access_tokens 8 | has_many :oauth2_verifiers 9 | has_many :oauth_tokens 10 | validates_presence_of :name, :url, :key, :secret 11 | validates_uniqueness_of :key 12 | before_validation :generate_keys, :on => :create 13 | attr_accessible :name, :url, :callback_url, :support_url 14 | 15 | validates_format_of :url, :with => /\Ahttp(s?):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/i 16 | validates_format_of :support_url, :with => /\Ahttp(s?):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/i, :allow_blank=>true 17 | validates_format_of :callback_url, :with => /\Ahttp(s?):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/i, :allow_blank=>true 18 | 19 | attr_accessor :token_callback_url 20 | 21 | def self.find_token(token_key) 22 | token = OauthToken.find_by_token(token_key, :include => :client_application) 23 | if token && token.authorized? 24 | token 25 | else 26 | nil 27 | end 28 | end 29 | 30 | def self.verify_request(request, options = {}, &block) 31 | begin 32 | signature = OAuth::Signature.build(request, options, &block) 33 | return false unless OauthNonce.remember(signature.request.nonce, signature.request.timestamp) 34 | value = signature.verify 35 | value 36 | rescue OAuth::Signature::UnknownSignatureMethod => e 37 | false 38 | end 39 | end 40 | 41 | def oauth_server 42 | @oauth_server ||= OAuth::Server.new("http://your.site") 43 | end 44 | 45 | def credentials 46 | @oauth_client ||= OAuth::Consumer.new(key, secret) 47 | end 48 | 49 | # If your application requires passing in extra parameters handle it here 50 | def create_request_token(params={}) 51 | RequestToken.create :client_application => self, :callback_url=>self.token_callback_url 52 | end 53 | 54 | protected 55 | 56 | def generate_keys 57 | self.key = OAuth::Helper.generate_key(40)[0,40] 58 | self.secret = OAuth::Helper.generate_key(40)[0,40] 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /app/models/oauth2_token.rb: -------------------------------------------------------------------------------- 1 | class Oauth2Token < AccessToken 2 | unloadable 3 | 4 | attr_accessor :state 5 | def as_json(options={}) 6 | d = {:access_token=>token, :token_type => 'bearer'} 7 | d[:expires_in] = expires_in if expires_at 8 | d 9 | end 10 | 11 | def to_query 12 | q = "access_token=#{token}&token_type=bearer" 13 | q << "&state=#{URI.escape(state)}" if @state 14 | q << "&expires_in=#{expires_in}" if expires_at 15 | q << "&scope=#{URI.escape(scope)}" if scope 16 | q 17 | end 18 | 19 | def expires_in 20 | expires_at.to_i - Time.now.to_i 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/models/oauth2_verifier.rb: -------------------------------------------------------------------------------- 1 | class Oauth2Verifier < OauthToken 2 | unloadable 3 | 4 | validates_presence_of :user 5 | attr_accessor :state 6 | 7 | def exchange!(params={}) 8 | OauthToken.transaction do 9 | token = Oauth2Token.create! :user=>user,:client_application=>client_application, :scope => scope 10 | invalidate! 11 | token 12 | end 13 | end 14 | 15 | def code 16 | token 17 | end 18 | 19 | def redirect_url 20 | callback_url 21 | end 22 | 23 | def to_query 24 | q = "code=#{token}" 25 | q << "&state=#{URI.escape(state)}" if @state 26 | q 27 | end 28 | 29 | protected 30 | 31 | def generate_keys 32 | self.token = OAuth::Helper.generate_key(20)[0,20] 33 | self.expires_at = 10.minutes.from_now 34 | self.authorized_at = Time.now 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /app/models/oauth_nonce.rb: -------------------------------------------------------------------------------- 1 | # Simple store of nonces. The OAuth Spec requires that any given pair of nonce and timestamps are unique. 2 | # Thus you can use the same nonce with a different timestamp and viceversa. 3 | class OauthNonce < ActiveRecord::Base 4 | unloadable 5 | 6 | attr_accessible :nonce, :timestamp 7 | validates_presence_of :nonce, :timestamp 8 | validates_uniqueness_of :nonce, :scope => :timestamp 9 | 10 | # Remembers a nonce and it's associated timestamp. It returns false if it has already been used 11 | def self.remember(nonce, timestamp) 12 | oauth_nonce = OauthNonce.create(:nonce => nonce, :timestamp => timestamp) 13 | return false if oauth_nonce.new_record? 14 | oauth_nonce 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/models/oauth_token.rb: -------------------------------------------------------------------------------- 1 | class OauthToken < ActiveRecord::Base 2 | unloadable 3 | 4 | belongs_to :client_application 5 | belongs_to :user 6 | validates_uniqueness_of :token 7 | validates_presence_of :client_application, :token 8 | before_validation :generate_keys, :on => :create 9 | attr_accessible :client_application, :callback_url 10 | 11 | def invalidated? 12 | invalidated_at != nil 13 | end 14 | 15 | def invalidate! 16 | update_attribute(:invalidated_at, Time.now) 17 | end 18 | 19 | def authorized? 20 | authorized_at != nil && !invalidated? 21 | end 22 | 23 | def to_query 24 | "oauth_token=#{token}&oauth_token_secret=#{secret}" 25 | end 26 | 27 | protected 28 | 29 | def generate_keys 30 | self.token = OAuth::Helper.generate_key(40)[0,40] 31 | self.secret = OAuth::Helper.generate_key(40)[0,40] 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /app/models/request_token.rb: -------------------------------------------------------------------------------- 1 | class RequestToken < OauthToken 2 | unloadable 3 | 4 | attr_accessor :provided_oauth_verifier 5 | 6 | def authorize!(user) 7 | return false if authorized? 8 | self.user = user 9 | self.authorized_at = Time.now 10 | self.verifier=OAuth::Helper.generate_key(20)[0,20] unless oauth10? 11 | self.save 12 | end 13 | 14 | def exchange! 15 | return false unless authorized? 16 | return false unless oauth10? || verifier==provided_oauth_verifier 17 | 18 | RequestToken.transaction do 19 | access_token = AccessToken.create(:user => user, :client_application => client_application) 20 | invalidate! 21 | access_token 22 | end 23 | end 24 | 25 | def to_query 26 | if oauth10? 27 | super 28 | else 29 | "#{super}&oauth_callback_confirmed=true" 30 | end 31 | end 32 | 33 | def oob? 34 | callback_url.nil? || callback_url.downcase == 'oob' 35 | end 36 | 37 | def oauth10? 38 | (defined? OAUTH_10_SUPPORT) && OAUTH_10_SUPPORT && self.callback_url.blank? 39 | end 40 | 41 | end 42 | -------------------------------------------------------------------------------- /app/views/oauth/authorize.html.erb: -------------------------------------------------------------------------------- 1 |

<%= l(:oauth_authorize_tilte) %>

2 |

<%= l(:oauth_authorize_comment) %>

3 |

4 | <%= link_to(@token.client_application.name, @token.client_application.url) %> 5 | (<%= link_to(@token.client_application.url, @token.client_application.url) %>) 6 |

7 | <%= form_tag authorize_url do %> 8 | <%= hidden_field_tag "oauth_token", @token.token %> 9 | <%- if params[:oauth_callback] -%> 10 | <%= hidden_field_tag "oauth_callback", params[:oauth_callback] %> 11 | <%- end -%> 12 |

13 | <%= submit_tag(l(:oauth_allow), :name => :allow, :autofocus => true) %> 14 | <%= submit_tag(l(:oauth_deny), :name => :deny) %> 15 |

16 | <% end %> 17 | -------------------------------------------------------------------------------- /app/views/oauth/authorize_failure.html.erb: -------------------------------------------------------------------------------- 1 |

<%= l(:oauth_deny_message) %>

2 | -------------------------------------------------------------------------------- /app/views/oauth/authorize_success.html.erb: -------------------------------------------------------------------------------- 1 |

You have allowed this request

-------------------------------------------------------------------------------- /app/views/oauth/oauth2_authorize.html.erb: -------------------------------------------------------------------------------- 1 |

Authorize access to your account

2 |

Would you like to authorize <%= link_to @token.client_application.name,@token.client_application.url %> (<%= link_to @token.client_application.url,@token.client_application.url %>) to access your account?

3 | <% form_tag authorize_url do %> 4 | <%= hidden_field_tag "response_type", params[:response_type]%> 5 | <%= hidden_field_tag "client_id", params[:client_id]%> 6 | <%= hidden_field_tag "redirect_uri", params[:redirect_uri]%> 7 | <%= hidden_field_tag "state", params[:state]%> 8 | <%= hidden_field_tag "scope", params[:scope]%> 9 | 10 |

11 | <%= check_box_tag 'authorize' %> authorize access 12 |

13 |

14 | <%= submit_tag %> 15 |

16 | <% end %> 17 | -------------------------------------------------------------------------------- /app/views/oauth_clients/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= error_messages_for @client_application %> 2 |
3 |
4 |
5 | <%= f.text_field :name %> 6 |
7 |
8 |
9 | <%= f.text_field :url %> 10 |
11 |
12 |
13 | <%= f.text_field :callback_url %> 14 |
15 |
16 |
17 | <%= f.text_field :support_url %> 18 |
19 |
20 | -------------------------------------------------------------------------------- /app/views/oauth_clients/edit.html.erb: -------------------------------------------------------------------------------- 1 |

<%= l(:oauth_edit_application) %>

2 | <%= form_for @client_application, :url => oauth_client_path(@client_application), :html => {:method => :put} do |f| %> 3 | <%= render :partial => "form", :locals => { :f => f } %> 4 | <%= submit_tag l(:button_edit) %> 5 | <% end %> 6 | <%= link_to l(:button_show), oauth_client_path(@client_application) %> | 7 | <%= link_to l(:button_back), oauth_clients_path %> 8 | -------------------------------------------------------------------------------- /app/views/oauth_clients/index.html.erb: -------------------------------------------------------------------------------- 1 |

<%= l(:oauth_client_applications) %>

2 | <% unless @client_applications.empty? %> 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | <% @client_applications.each do |client|%> 13 | 14 | 15 | 16 | 17 | 18 | <% end %> 19 | 20 |
<%= l(:field_name) %>
<%= link_to client.name, oauth_client_path(client) %><%= link_to l(:button_edit), edit_oauth_client_path(client), :class => "icon icon-edit" %><%= link_to l(:button_delete), oauth_client_path(client), :confirm => l(:text_are_you_sure), :method => :delete, :class => "icon icon-del" %>
21 | <% end %> 22 | <%= link_to l(:oauth_register_application), { :action => :new }, :class => "icon icon-add" %> 23 | 24 | <%= stylesheet_link_tag 'application.css', :plugin => 'redmine_oauth_provider' %> 25 | -------------------------------------------------------------------------------- /app/views/oauth_clients/new.html.erb: -------------------------------------------------------------------------------- 1 |

<%= l(:oauth_register_application) %>

2 | <%= form_for :client_application, :url => { :action => :create } do |f| %> 3 | <%= render :partial => "form", :locals => { :f => f } %> 4 | <%= submit_tag l(:label_register) %> 5 | <% end %> 6 | -------------------------------------------------------------------------------- /app/views/oauth_clients/show.html.erb: -------------------------------------------------------------------------------- 1 |

<%= l(:oauth_detail, :name => @client_application.name) %>

2 |

3 | <%= l(:oauth_consumer_key) %>: 4 | <%=@client_application.key %> 5 |

6 |

7 | <%= l(:oauth_consumer_secret) %>: 8 | <%=@client_application.secret %> 9 |

10 |

11 | <%= l(:oauth_request_token_url) %> 12 | http<%='s' if request.ssl? %>://<%= request.host_with_port %><%= request_token_path %> 13 |

14 |

15 | <%= l(:oauth_access_token_url) %> 16 | http<%='s' if request.ssl? %>://<%= request.host_with_port %><%= access_token_path %> 17 |

18 |

19 | <%= l(:oauth_authorize_url) %> 20 | http<%='s' if request.ssl? %>://<%= request.host_with_port %><%= authorize_path %> 21 |

22 | 23 | <%= link_to l(:button_edit), edit_oauth_client_path(@client_application) %> | 24 | <%= link_to l(:button_back), oauth_clients_path %> 25 | -------------------------------------------------------------------------------- /assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | .list.oauth-clients { 2 | width: 400px; 3 | } 4 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # English strings go here for Rails i18n 2 | en: 3 | field_support_url: Support URL 4 | field_callback_url: Callback URL 5 | oauth_allow: "Allow" 6 | oauth_deny: "Deny" 7 | oauth_authorize_tilte: Authorize access to your account 8 | oauth_authorize_comment: Would you like to authorize a following application to access your account? 9 | oauth_deny_message: You have disallowed this request 10 | oauth_client_applications: OAuth Client Applications 11 | oauth_main_application_url: Main Application URL 12 | oauth_callback_url: Callback URL 13 | oauth_support_url: Support URL 14 | oauth_edit_application: Edit your application 15 | oauth_register_application: Register a new application 16 | oauth_detail: OAuth details for %{name} 17 | oauth_consumer_key: Consumer Key 18 | oauth_consumer_secret: Consumer Secret 19 | oauth_request_token_url: Request Token URL 20 | oauth_access_token_url: Access Token URL 21 | oauth_authorize_url: Authorize URL 22 | 23 | "Registered the information successfully": "Registered the information successfully" 24 | "Updated the client information successfully": "Updated the client information successfully" 25 | "Destroyed the client application registration": "Destroyed the client application registration" 26 | "Wrong application id": "Wrong application id" 27 | -------------------------------------------------------------------------------- /config/locales/ja.yml: -------------------------------------------------------------------------------- 1 | ja: 2 | field_support_url: サポート URL 3 | field_callback_url: コールバック URL 4 | oauth_allow: 許可 5 | oauth_deny: 許可しない 6 | oauth_authorize_tilte: 認証 7 | oauth_authorize_comment: 以下のアプリケーションへのアクセスを許可しますか? 8 | oauth_deny_message: リクエストは許可されませんでした 9 | oauth_client_applications: OAuth クライアントアプリケーション 10 | oauth_main_application_url: メインアプリケーション URL 11 | oauth_callback_url: コールバック URL 12 | oauth_support_url: サポート URL 13 | oauth_edit_application: アプリケーションの編集 14 | oauth_register_application: アプリケーションの登録 15 | oauth_detail: OAuth %{name} の詳細 16 | oauth_consumer_key: Consumer Key 17 | oauth_consumer_secret: Consumer Secret 18 | oauth_request_token_url: Request Token URL 19 | oauth_access_token_url: Access Token URL 20 | oauth_authorize_url: Authorize URL 21 | 22 | "Registered the information successfully": 登録に成功しました 23 | "Updated the client information successfully": 更新に成功しました 24 | "Destroyed the client application registration": クライアントアプリケーションを削除しました 25 | "Wrong application id": アプリケーション ID が誤っています 26 | -------------------------------------------------------------------------------- /config/locales/zh.yml: -------------------------------------------------------------------------------- 1 | # English strings go here for Rails i18n 2 | zh: 3 | oauth_allow: "允許" 4 | oauth_deny: "拒絕" 5 | oauth_authorize_tilte: 帳號存取驗證 6 | oauth_authorize_comment: 您是否同意下列應用程式存取您的帳號? 7 | oauth_deny_message: 您已拒絕此要求 8 | oauth_client_applications: OAuth客戶端應用程式 9 | oauth_main_application_url: 應用程式URL 10 | oauth_callback_url: Callback URL 11 | oauth_support_url: Support URL 12 | oauth_edit_application: 編輯 13 | oauth_register_application: 註冊新應用程式 14 | oauth_detail: OAuth %{name} 的詳細內容 15 | oauth_consumer_key: Consumer Key 16 | oauth_consumer_secret: Consumer Secret 17 | oauth_request_token_url: Request Token URL 18 | oauth_access_token_url: Access Token URL 19 | oauth_authorize_url: Authorize URL 20 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | resources :oauth_clients 2 | match 'oauth/test_request', :to => 'oauth#test_request', :as => :test_request, :via => [:get, :post] 3 | match 'oauth/token', :to => 'oauth#token', :as => :token, :via => [:get, :post] 4 | match 'oauth/access_token', :to => 'oauth#access_token', :as => :access_token, :via => [:get, :post] 5 | match 'oauth/request_token', :to => 'oauth#request_token', :as => :request_token, :via => [:get, :post] 6 | match 'oauth/authorize', :to => 'oauth#authorize', :as => :authorize, :via => [:get, :post] 7 | match 'oauth/revoke', :to => 'oauth#revoke', :as => :revoke, :via => [:get, :post] 8 | match 'oauth', :to => 'oauth#index', :as => :oauth, :via => [:get, :post] 9 | match 'oauth/current_user', :to => 'oauth#current_user', :as => :current_user, :via => [:get, :post] 10 | match 'oauth/user_info', :to => 'oauth#user_info', :as => :user_info, :via => [:get, :post] 11 | -------------------------------------------------------------------------------- /db/migrate/001_create_oauth_tables.rb: -------------------------------------------------------------------------------- 1 | class CreateOauthTables < ActiveRecord::Migration 2 | def self.up 3 | create_table :client_applications do |t| 4 | t.string :name 5 | t.string :url 6 | t.string :support_url 7 | t.string :callback_url 8 | t.string :key, :limit => 40 9 | t.string :secret, :limit => 40 10 | t.integer :user_id 11 | 12 | t.timestamps 13 | end 14 | add_index :client_applications, :key, :unique => true 15 | 16 | create_table :oauth_tokens do |t| 17 | t.integer :user_id 18 | t.string :type, :limit => 20 19 | t.integer :client_application_id 20 | t.string :token, :limit => 40 21 | t.string :secret, :limit => 40 22 | t.string :callback_url 23 | t.string :verifier, :limit => 20 24 | t.string :scope 25 | t.timestamp :authorized_at, :invalidated_at, :expires_at 26 | t.timestamps 27 | end 28 | 29 | add_index :oauth_tokens, :token, :unique => true 30 | 31 | create_table :oauth_nonces do |t| 32 | t.string :nonce 33 | t.integer :timestamp 34 | 35 | t.timestamps 36 | end 37 | add_index :oauth_nonces,[:nonce, :timestamp], :unique => true 38 | 39 | end 40 | 41 | def self.down 42 | drop_table :client_applications 43 | drop_table :oauth_tokens 44 | drop_table :oauth_nonces 45 | end 46 | 47 | end 48 | -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding : utf-8 -*- 2 | require 'redmine' 3 | 4 | module RedmineApp 5 | class Application < Rails::Application 6 | require 'oauth/rack/oauth_filter' 7 | config.middleware.use OAuth::Rack::OAuthFilter 8 | end 9 | end 10 | 11 | # Patches to the Redmine core. 12 | Rails.configuration.to_prepare do 13 | require_dependency 'project' 14 | require_dependency 'user' 15 | 16 | User.send(:include, OauthProviderUserPatch) 17 | end 18 | 19 | Redmine::Plugin.register :redmine_oauth_provider do 20 | name 'Redmine Oauth Provider plugin' 21 | author 'Jana Dvořáková' 22 | description 'Oauth Provider plugin for Redmine' 23 | version '0.0.5' 24 | url 'https://github.com/Virtualmaster/redmine-oauth-provider' 25 | author_url 'http://www.jana4u.net/' 26 | end 27 | -------------------------------------------------------------------------------- /lib/oauth_provider_user_patch.rb: -------------------------------------------------------------------------------- 1 | # Patches Redmine's Users dynamically. 2 | module OauthProviderUserPatch 3 | def self.included(base) # :nodoc: 4 | base.class_eval do 5 | unloadable 6 | 7 | has_many :client_applications 8 | has_many :tokens, -> {includes(:client_application).order("authorized_at desc")}, {:class_name => "OauthToken"} 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/fixtures/client_applications.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html 2 | one: 3 | id: 1 4 | name: MyString 5 | url: http://test.com 6 | support_url: http://test.com/support 7 | callback_url: http://test.com/callback 8 | key: one_key 9 | secret: MyString 10 | user_id: 1 11 | created_at: 2007-11-17 16:56:51 12 | updated_at: 2007-11-17 16:56:51 13 | two: 14 | id: 2 15 | name: MyString 16 | url: http://test.com 17 | support_url: http://test.com/support 18 | callback_url: http://test.com/callback 19 | key: two_key 20 | secret: MyString 21 | user_id: 1 22 | created_at: 2007-11-17 16:56:51 23 | updated_at: 2007-11-17 16:56:51 24 | -------------------------------------------------------------------------------- /test/fixtures/oauth_nonces.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html 2 | one: 3 | id: 1 4 | nonce: a_nonce 5 | timestamp: 1 6 | created_at: 2007-11-25 17:27:04 7 | updated_at: 2007-11-25 17:27:04 8 | two: 9 | id: 2 10 | nonce: b_nonce 11 | timestamp: 2 12 | created_at: 2007-11-25 17:27:04 13 | updated_at: 2007-11-25 17:27:04 14 | -------------------------------------------------------------------------------- /test/fixtures/oauth_tokens.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html 2 | one: 3 | id: 1 4 | user_id: 1 5 | client_application_id: 1 6 | token: one 7 | secret: MyString 8 | created_at: 2007-11-19 07:31:46 9 | updated_at: 2007-11-19 07:31:46 10 | two: 11 | id: 2 12 | user_id: 1 13 | client_application_id: 1 14 | token: two 15 | secret: MyString 16 | created_at: 2007-11-19 07:31:46 17 | updated_at: 2007-11-19 07:31:46 18 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # Load the normal Rails helper 2 | require File.expand_path(File.dirname(__FILE__) + '/../../../../test/test_helper') 3 | 4 | # Ensure that we are using the temporary fixture path 5 | Engines::Testing.set_fixture_path 6 | -------------------------------------------------------------------------------- /test/unit/client_application_test.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.join(File.dirname(__FILE__), '/../test_helper')) 2 | module OAuthHelpers 3 | 4 | def create_consumer 5 | @consumer=OAuth::Consumer.new(@application.key,@application.secret, 6 | { 7 | :site=>@application.oauth_server.base_url 8 | }) 9 | end 10 | 11 | end 12 | 13 | class ClientApplicationTest < ActiveSupport::TestCase 14 | include OAuthHelpers 15 | fixtures :users,:client_applications,:oauth_tokens 16 | 17 | def setup 18 | @application = ClientApplication.create :name => "Agree2", :url => "http://agree2.com", :user => users(:users_001) 19 | create_consumer 20 | end 21 | 22 | def test_should_be_valid 23 | assert @application.valid? 24 | end 25 | 26 | 27 | def test_should_not_have_errors 28 | assert_equal [], @application.errors.full_messages 29 | end 30 | 31 | def test_should_have_key_and_secret 32 | assert_not_nil @application.key 33 | assert_not_nil @application.secret 34 | end 35 | 36 | def test_should_have_credentials 37 | assert_not_nil @application.credentials 38 | assert_equal @application.key, @application.credentials.key 39 | assert_equal @application.secret, @application.credentials.secret 40 | end 41 | 42 | end -------------------------------------------------------------------------------- /test/unit/oauth_nonce_test.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.join(File.dirname(__FILE__), '/../test_helper')) 2 | require 'oauth/helper' 3 | 4 | class ClientNoneTest < ActiveSupport::TestCase 5 | include OAuth::Helper 6 | 7 | def setup 8 | @oauth_nonce = OauthNonce.remember(generate_key,Time.now.to_i) 9 | end 10 | 11 | def test_should_be_valid 12 | assert @oauth_nonce.valid? 13 | end 14 | 15 | def test_should_not_have_errors 16 | assert_equal [], @oauth_nonce.errors.full_messages 17 | end 18 | 19 | def test_should_not_be_a_new_record 20 | assert !@oauth_nonce.new_record? 21 | end 22 | 23 | def test_shuold_not_allow_a_second_one_with_the_same_values 24 | assert_equal false, OauthNonce.remember(@oauth_nonce.nonce, @oauth_nonce.timestamp) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /test/unit/oauth_token_test.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.join(File.dirname(__FILE__), '/../test_helper')) 2 | 3 | class RequestTokenTest < ActiveSupport::TestCase 4 | 5 | fixtures :client_applications, :users, :oauth_tokens 6 | 7 | def setup 8 | @token = RequestToken.create :client_application=>client_applications(:one) 9 | end 10 | 11 | def test_should_be_valid 12 | assert @token.valid? 13 | end 14 | 15 | def test_should_not_have_errors 16 | assert @token.errors.empty? 17 | end 18 | 19 | def test_should_have_a_token 20 | assert_not_nil @token.token 21 | end 22 | 23 | def test_should_have_a_secret 24 | assert_not_nil @token.secret 25 | end 26 | 27 | def test_should_not_be_authorized 28 | assert !@token.authorized? 29 | end 30 | 31 | def test_should_not_be_invalidated 32 | assert !@token.invalidated? 33 | end 34 | 35 | def test_should_authorize_request 36 | @token.authorize!(users(:users_001)) 37 | assert @token.authorized? 38 | assert_not_nil @token.authorized_at 39 | assert_equal users(:users_001), @token.user 40 | end 41 | 42 | def test_should_not_exchange_without_approval 43 | assert_equal false, @token.exchange! 44 | assert_equal false, @token.invalidated? 45 | end 46 | 47 | def test_should_exchange_with_approval 48 | @token.authorize!(users(:users_001)) 49 | @token.provided_oauth_verifier = @token.verifier 50 | @access = @token.exchange! 51 | assert_not_equal false, @access 52 | assert @token.invalidated? 53 | 54 | assert_equal users(:users_001), @access.user 55 | assert @access.authorized? 56 | end 57 | 58 | end 59 | --------------------------------------------------------------------------------