├── .autotest ├── .gitignore ├── .rspec ├── .watchr.rb ├── CHANGELOG.rdoc ├── Gemfile ├── Gemfile.lock ├── README.rdoc ├── Rakefile ├── VERSION ├── app ├── controllers │ ├── accesses_controller.rb │ ├── application_controller.rb │ ├── clients_controller.rb │ ├── oauth │ │ ├── oauth_authorize_controller.rb │ │ └── oauth_token_controller.rb │ ├── pastas_controller.rb │ ├── pizzas_controller.rb │ ├── scopes_controller.rb │ ├── sessions_controller.rb │ └── users_controller.rb ├── helpers │ ├── accesses_helper.rb │ ├── application_helper.rb │ ├── clients_helper.rb │ ├── oauth_accesses_helper.rb │ ├── oauth_helper.rb │ ├── scopes_helper.rb │ ├── sessions_helper.rb │ └── users_helper.rb ├── models │ ├── client.rb │ ├── oauth │ │ ├── oauth_access.rb │ │ ├── oauth_authorization.rb │ │ ├── oauth_daily_request.rb │ │ ├── oauth_refresh_token.rb │ │ └── oauth_token.rb │ ├── scope.rb │ └── user.rb └── views │ ├── accesses │ ├── index.html.erb │ └── show.html.erb │ ├── clients │ ├── _form.html.erb │ ├── edit.html.erb │ ├── index.html.erb │ ├── new.html.erb │ └── show.html.erb │ ├── layouts │ └── application.html.erb │ ├── oauth │ ├── authorize.html.erb │ └── token.json.erb │ ├── scopes │ ├── _form.html.erb │ ├── edit.html.erb │ ├── index.html.erb │ ├── new.html.erb │ └── show.html.erb │ ├── sessions │ └── new.html.erb │ ├── shared │ ├── 403.json.erb │ ├── 404.json.erb │ ├── 422.json.erb │ ├── 500.json.erb │ └── html │ │ ├── 404.html.erb │ │ └── 422.html.erb │ └── users │ ├── _form.html.erb │ ├── edit.html.erb │ ├── index.html.erb │ ├── new.html.erb │ └── show.html.erb ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cucumber.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── backtrace_silencers.rb │ ├── capybara_headers_hack.rb │ ├── inflections.rb │ ├── lely_array_normalize.rb │ ├── lely_base_document.rb │ ├── lely_rescue_helper.rb │ ├── mime_types.rb │ ├── oauth_scope.rb │ ├── oauth_settings.rb │ ├── secret_token.rb │ └── session_store.rb ├── locales │ └── en.yml ├── mongoid.yml ├── oauth.yml └── routes.rb ├── db └── seeds.rb ├── doc └── README_FOR_APP ├── lib └── tasks │ ├── .gitkeep │ ├── cucumber.rake │ └── watchr.rake ├── public ├── 404.html ├── 422.html ├── 500.html ├── favicon.ico ├── images │ ├── help-us.png │ ├── help-us.psd │ └── screenshots │ │ ├── access.png │ │ ├── admin-dashboard.png │ │ ├── all-scope.png │ │ ├── authorization.png │ │ ├── block-clients.png │ │ ├── client-show.png │ │ ├── first-user-creation.png │ │ ├── pizzas-scope.png │ │ └── scopes.png ├── javascripts │ ├── application.js │ ├── highcharts.js │ ├── jquery.js │ ├── jquery.min.js │ ├── jquery.tagsinput.js │ └── rails.js ├── robots.txt └── stylesheets │ ├── .gitkeep │ ├── gh-buttons.css │ ├── gh-icons.png │ ├── jquery.tagsinput.css │ ├── reset.css │ └── template.css ├── script ├── cucumber └── rails ├── spec ├── acceptance │ ├── acceptance_helper.rb │ ├── accesses_controller_spec.rb │ ├── clients_controller_spec.rb │ ├── oauth │ │ ├── oauth_authorize_controller_spec.rb │ │ └── oauth_token_controller_spec.rb │ ├── resource_controller_spec.rb │ ├── scopes_controller_spec.rb │ ├── support │ │ ├── helpers.rb │ │ ├── paths.rb │ │ └── view_helpers.rb │ └── users_controller_spec.rb ├── controllers │ ├── pastas_controller_spec.rb │ └── pizzas_controller_spec.rb ├── extras │ └── scope_spec.rb ├── factories │ └── oauth.rb ├── models │ ├── oauth │ │ ├── client_spec.rb │ │ ├── oauth_access_spec.rb │ │ ├── oauth_authorization_spec.rb │ │ ├── oauth_daily_request_spec.rb │ │ ├── oauth_refresh_token_spec.rb │ │ └── oauth_token_spec.rb │ ├── scope_spec.rb │ └── user_spec.rb ├── spec_helper.rb └── support │ └── settings_helper.rb └── vendor └── plugins └── .gitkeep /.autotest: -------------------------------------------------------------------------------- 1 | require "autotest/growl" 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle 2 | db/*.sqlite3 3 | log/*.log 4 | tmp/**/* 5 | config/database.yml 6 | *.DS_Store 7 | *.swp 8 | .rvmrc 9 | 10 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format Fuubar 2 | --colour 3 | -------------------------------------------------------------------------------- /.watchr.rb: -------------------------------------------------------------------------------- 1 | # -------------- 2 | # Running Spec 3 | # -------------- 4 | 5 | def run_spec(file) 6 | unless File.exist?(file) 7 | puts "#{file} does not exist" 8 | return 9 | end 10 | 11 | run_spec_cmd(file) 12 | end 13 | 14 | def run_spec_cmd(cmd) 15 | puts "Running #{cmd}" 16 | system "bundle exec rspec #{cmd}" 17 | end 18 | 19 | # -------------- 20 | # Autodetection 21 | # -------------- 22 | 23 | # Spec tests 24 | watch("spec/.*/*_spec\.rb") do |match| 25 | run_spec match[0] 26 | end 27 | 28 | # Models tests 29 | watch("app/models/(.*/.*)\.rb") do |match| 30 | run_spec %{spec/models/#{match[1]}_spec.rb} 31 | end 32 | 33 | # Acceptance tests for every controller (not the 34 | # best solution, but it works pretty well) 35 | watch("app/controllers/(.*/.*)\.rb") do |match| 36 | exclusions = ["controllers/application_controller"] 37 | unless exclusions.include? match[1] 38 | run_spec %{spec/acceptance/#{match[1]}_spec.rb} 39 | end 40 | end 41 | 42 | # ---------------- 43 | # Signal Handling 44 | # ---------------- 45 | 46 | @second_int = false 47 | 48 | # Run acceptance tests (Ctrl-\) 49 | Signal.trap('QUIT') do 50 | run_spec "spec/acceptance/" 51 | end 52 | 53 | # Run all tests (Ctrl-c) 54 | Signal.trap 'INT' do 55 | check_exit # exit (double Ctrl-c) 56 | #@second_int = true 57 | run_spec "spec/" 58 | end 59 | 60 | def check_exit 61 | exit if @second_int 62 | end 63 | -------------------------------------------------------------------------------- /CHANGELOG.rdoc: -------------------------------------------------------------------------------- 1 | = Changelog 2 | 3 | 4 | == Release v0.3.1 (2011/04/22) 5 | 6 | * Added documentation with screenshots 7 | * Added tag system on scope definition 8 | * Added basic graph with access token stats 9 | 10 | 11 | == Release v0.3.0 (2011/04/21) 12 | 13 | * Added refined UI for the dashboard section 14 | * Updated documentation 15 | 16 | 17 | == Release v0.2.3 (2011/04/19) 18 | 19 | * Added redirect to last URI before logging in 20 | * Added functionality to block the access of a user resource in 21 | behalf of a specific client 22 | * Added client access and stats to every user in the dashboard 23 | * Added functionality to block a client (only admin can do this) 24 | * Added UI to automatically set admin the first user which 25 | register into the OAuth Server 26 | * Added autoupdate of scopes to all clients when one of them 27 | changes 28 | 29 | 30 | == Release v0.2.2 (2011/04/18) 31 | 32 | * Added documentation on scope definition, action authorization, testing 33 | framework and oauth flow testing code. 34 | 35 | 36 | == Release v0.2.1 (2011/04/15) 37 | 38 | * Added bearer token protection simply adding the filter 39 | before_filter :oauth_authorized on the conrtoller 40 | * Added dynamic scope setting. Now is possible to set the 41 | scopes directly in the admin interface, and easily protect 42 | every method inside a controller. 43 | * Added dashboard for admin to add scopes and monitor all 44 | registered users 45 | * Added dashboard to control and create clients 46 | * Added dashboard to edit user info 47 | 48 | 49 | == Release v0.2.0 (2011/04/06) 50 | 51 | * Added refresh token mechanisms 52 | * Removed unuseful gems 53 | * Changed denied message from access=denied to error=access_denied 54 | * Introduced test to block of single token (idea of logout) 55 | * Changed oauth/authorize with /oauth/authorization to make sistem more REST 56 | * Redefinition of all documentation, more simple and complete 57 | 58 | 59 | == Release v0.1.1 (2011/03/29) 60 | 61 | * Added documentation explaining the authorization flows 62 | 63 | 64 | == Release v0.1.0 (2011/03/29) 65 | 66 | * Added documentation explaining the existing flows 67 | * Added OAuth2 server with acceptance tests 68 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | gem 'rails', '3.0.5' 4 | gem 'mongoid', '2.0.0.beta.20' 5 | gem 'bson_ext' 6 | gem 'yajl-ruby' 7 | gem 'will_paginate' 8 | gem 'rack-ssl' 9 | gem "bcrypt-ruby", :require => "bcrypt" 10 | gem 'validate_url', '0.1.6' 11 | gem 'email_validator' 12 | gem 'chronic' 13 | gem 'jquery-rails' 14 | 15 | group :development, :test do 16 | gem 'debugger' 17 | gem 'rspec-rails', '2.4.1' 18 | gem 'steak' 19 | gem 'capybara' 20 | gem 'selenium-client' 21 | gem 'selenium-webdriver' 22 | gem 'launchy' 23 | gem 'shoulda' 24 | gem 'factory_girl_rails', '1.1.beta1' 25 | gem 'webrat' 26 | gem 'autotest' 27 | gem 'autotest-growl' 28 | gem 'database_cleaner' 29 | gem 'fuubar' 30 | gem 'watchr' 31 | gem 'delorean' 32 | gem 'rspec-set' 33 | end 34 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | ZenTest (4.5.0) 5 | abstract (1.0.0) 6 | actionmailer (3.0.5) 7 | actionpack (= 3.0.5) 8 | mail (~> 2.2.15) 9 | actionpack (3.0.5) 10 | activemodel (= 3.0.5) 11 | activesupport (= 3.0.5) 12 | builder (~> 2.1.2) 13 | erubis (~> 2.6.6) 14 | i18n (~> 0.4) 15 | rack (~> 1.2.1) 16 | rack-mount (~> 0.6.13) 17 | rack-test (~> 0.5.7) 18 | tzinfo (~> 0.3.23) 19 | activemodel (3.0.5) 20 | activesupport (= 3.0.5) 21 | builder (~> 2.1.2) 22 | i18n (~> 0.4) 23 | activerecord (3.0.5) 24 | activemodel (= 3.0.5) 25 | activesupport (= 3.0.5) 26 | arel (~> 2.0.2) 27 | tzinfo (~> 0.3.23) 28 | activeresource (3.0.5) 29 | activemodel (= 3.0.5) 30 | activesupport (= 3.0.5) 31 | activesupport (3.0.5) 32 | arel (2.0.9) 33 | autotest (4.4.6) 34 | ZenTest (>= 4.4.1) 35 | autotest-growl (0.2.9) 36 | bcrypt-ruby (2.1.4) 37 | bson (1.3.0) 38 | bson_ext (1.3.0) 39 | builder (2.1.2) 40 | capybara (0.4.1.2) 41 | celerity (>= 0.7.9) 42 | culerity (>= 0.2.4) 43 | mime-types (>= 1.16) 44 | nokogiri (>= 1.3.3) 45 | rack (>= 1.0.0) 46 | rack-test (>= 0.5.4) 47 | selenium-webdriver (>= 0.0.27) 48 | xpath (~> 0.1.3) 49 | celerity (0.8.9) 50 | chalofa_ruby-progressbar (0.0.9.1) 51 | childprocess (0.1.8) 52 | ffi (~> 1.0.6) 53 | chronic (0.3.0) 54 | columnize (0.3.6) 55 | configuration (1.2.0) 56 | culerity (0.2.15) 57 | database_cleaner (0.6.6) 58 | debugger (1.1.2) 59 | columnize (>= 0.3.1) 60 | debugger-linecache (~> 1.1) 61 | debugger-ruby_core_source (~> 1.1) 62 | debugger-linecache (1.1.1) 63 | debugger-ruby_core_source (>= 1.1.1) 64 | debugger-ruby_core_source (1.1.2) 65 | delorean (1.0.0) 66 | chronic 67 | diff-lcs (1.1.3) 68 | email_validator (1.0.0) 69 | erubis (2.6.6) 70 | abstract (>= 1.0.0) 71 | factory_girl (2.0.0.beta2) 72 | factory_girl_rails (1.1.beta1) 73 | factory_girl (~> 2.0.0.beta) 74 | rails (>= 3.0.0) 75 | ffi (1.0.11) 76 | fuubar (0.0.4) 77 | chalofa_ruby-progressbar (~> 0.0.9) 78 | rspec (~> 2.0) 79 | rspec-instafail (~> 0.1.4) 80 | i18n (0.5.0) 81 | jquery-rails (0.2.7) 82 | rails (~> 3.0) 83 | thor (~> 0.14.4) 84 | json_pure (1.5.1) 85 | launchy (0.4.0) 86 | configuration (>= 0.0.5) 87 | rake (>= 0.8.1) 88 | mail (2.2.15) 89 | activesupport (>= 2.3.6) 90 | i18n (>= 0.4.0) 91 | mime-types (~> 1.16) 92 | treetop (~> 1.4.8) 93 | mime-types (1.16) 94 | mongo (1.3.0) 95 | bson (>= 1.3.0) 96 | mongoid (2.0.0.beta.20) 97 | activemodel (~> 3.0) 98 | mongo (~> 1.1) 99 | tzinfo (~> 0.3.22) 100 | will_paginate (~> 3.0.pre) 101 | nokogiri (1.5.2) 102 | polyglot (0.3.1) 103 | rack (1.2.5) 104 | rack-mount (0.6.14) 105 | rack (>= 1.0.0) 106 | rack-ssl (1.3.2) 107 | rack 108 | rack-test (0.5.7) 109 | rack (>= 1.0) 110 | rails (3.0.5) 111 | actionmailer (= 3.0.5) 112 | actionpack (= 3.0.5) 113 | activerecord (= 3.0.5) 114 | activeresource (= 3.0.5) 115 | activesupport (= 3.0.5) 116 | bundler (~> 1.0) 117 | railties (= 3.0.5) 118 | railties (3.0.5) 119 | actionpack (= 3.0.5) 120 | activesupport (= 3.0.5) 121 | rake (>= 0.8.7) 122 | thor (~> 0.14.4) 123 | rake (0.9.2.2) 124 | rspec (2.4.0) 125 | rspec-core (~> 2.4.0) 126 | rspec-expectations (~> 2.4.0) 127 | rspec-mocks (~> 2.4.0) 128 | rspec-core (2.4.0) 129 | rspec-expectations (2.4.0) 130 | diff-lcs (~> 1.1.2) 131 | rspec-instafail (0.1.7) 132 | rspec-mocks (2.4.0) 133 | rspec-rails (2.4.1) 134 | actionpack (~> 3.0) 135 | activesupport (~> 3.0) 136 | railties (~> 3.0) 137 | rspec (~> 2.4.0) 138 | rspec-set (0.0.1) 139 | rspec (>= 2) 140 | rubyzip (0.9.4) 141 | selenium-client (1.2.18) 142 | selenium-webdriver (0.1.4) 143 | childprocess (>= 0.1.7) 144 | ffi (>= 1.0.7) 145 | json_pure 146 | rubyzip 147 | shoulda (2.11.3) 148 | steak (1.1.0) 149 | rspec (>= 1.3) 150 | thor (0.14.6) 151 | treetop (1.4.9) 152 | polyglot (>= 0.3.1) 153 | tzinfo (0.3.26) 154 | validate_url (0.1.6) 155 | activemodel (>= 3.0.0) 156 | watchr (0.7) 157 | webrat (0.7.3) 158 | nokogiri (>= 1.2.0) 159 | rack (>= 1.0) 160 | rack-test (>= 0.5.3) 161 | will_paginate (3.0.pre2) 162 | xpath (0.1.3) 163 | nokogiri (~> 1.3) 164 | yajl-ruby (0.8.2) 165 | 166 | PLATFORMS 167 | ruby 168 | 169 | DEPENDENCIES 170 | autotest 171 | autotest-growl 172 | bcrypt-ruby 173 | bson_ext 174 | capybara 175 | chronic 176 | database_cleaner 177 | debugger 178 | delorean 179 | email_validator 180 | factory_girl_rails (= 1.1.beta1) 181 | fuubar 182 | jquery-rails 183 | launchy 184 | mongoid (= 2.0.0.beta.20) 185 | rack-ssl 186 | rails (= 3.0.5) 187 | rspec-rails (= 2.4.1) 188 | rspec-set 189 | selenium-client 190 | selenium-webdriver 191 | shoulda 192 | steak 193 | validate_url (= 0.1.6) 194 | watchr 195 | webrat 196 | will_paginate 197 | yajl-ruby 198 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | require 'rake' 6 | 7 | Lelylan::Application.load_tasks 8 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | v0.3.1 2 | -------------------------------------------------------------------------------- /app/controllers/accesses_controller.rb: -------------------------------------------------------------------------------- 1 | class AccessesController < ApplicationController 2 | 3 | before_filter :find_accesses 4 | before_filter :find_access, except: "index" 5 | 6 | def index 7 | end 8 | 9 | def show 10 | end 11 | 12 | def block 13 | @access.block! 14 | redirect_to accesses_url 15 | end 16 | 17 | def unblock 18 | @access.unblock! 19 | redirect_to accesses_url 20 | end 21 | 22 | 23 | private 24 | 25 | def find_accesses 26 | @accesses = OauthAccess.where(resource_owner_uri: current_user.uri) 27 | end 28 | 29 | def find_access 30 | @access = @accesses.id(params[:id]).first 31 | unless @access 32 | redirect_to root_path, alert: "Resource not found." 33 | end 34 | end 35 | 36 | # TODO: change this behavior with a simple redirect 37 | def resource_not_found 38 | flash.now.alert = "notifications.document.not_found" 39 | @info = { id: params[:id] } 40 | render "shared/html/404" and return 41 | end 42 | 43 | end 44 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | include Lelylan::Rescue::Helpers 3 | 4 | protect_from_forgery 5 | 6 | before_filter :authenticate 7 | helper_method :current_user 8 | helper_method :admin_does_not_exist 9 | 10 | rescue_from BSON::InvalidObjectId, with: :bson_invalid_object_id 11 | rescue_from JSON::ParserError, with: :json_parse_error 12 | rescue_from Mongoid::Errors::InvalidType, with: :mongoid_errors_invalid_type 13 | 14 | protected 15 | 16 | def json_body 17 | @body = HashWithIndifferentAccess.new(JSON.parse(request.body.read.to_s)) 18 | end 19 | 20 | def authenticate 21 | if api_request 22 | # oauth_authorized # uncomment to make all json API protected 23 | else 24 | session_auth 25 | end 26 | end 27 | 28 | def api_request 29 | json? 30 | end 31 | 32 | def json? 33 | request.format == "application/json" 34 | end 35 | 36 | def session_auth 37 | @current_user ||= User.criteria.id(session[:user_id]).first if session[:user_id] 38 | unless current_user 39 | session[:back] = request.url 40 | redirect_to(log_in_path) and return false 41 | end 42 | return @current_user 43 | end 44 | 45 | def current_user 46 | @current_user 47 | end 48 | 49 | def oauth_authorized 50 | action = params[:controller] + "/" + params[:action] 51 | normalize_token 52 | @token = OauthToken.where(token: params[:token]).all_in(scope: [action]).first 53 | if @token.nil? or @token.blocked? 54 | render text: "Unauthorized access.", status: 401 55 | return false 56 | else 57 | access = OauthAccess.where(client_uri: @token.client_uri , resource_owner_uri: @token.resource_owner_uri).first 58 | access.accessed! 59 | end 60 | end 61 | 62 | def normalize_token 63 | # Token in the body 64 | if (json_body and @body[:token]) 65 | params[:token] = @body[:token] 66 | end 67 | # Token in the header 68 | if request.env["Authorization"] 69 | params[:token] = request.env["Authorization"].split(" ").last 70 | end 71 | end 72 | 73 | def admin_does_not_exist 74 | User.where(admin: true).first.nil? 75 | end 76 | 77 | end 78 | -------------------------------------------------------------------------------- /app/controllers/clients_controller.rb: -------------------------------------------------------------------------------- 1 | class ClientsController < ApplicationController 2 | before_filter :find_clients 3 | before_filter :find_client, only: ["show", "edit", "update", "destroy", "block", "unblock"] 4 | before_filter :normalize_scope, only: ["create", "update"] 5 | before_filter :admin?, only: ["block", "unblock"] 6 | 7 | def index 8 | end 9 | 10 | def show 11 | end 12 | 13 | def new 14 | @client = Client.new 15 | @client.scope = ["all"] 16 | end 17 | 18 | def create 19 | @client = Client.new(params[:client]) 20 | @client.created_from = current_user.uri 21 | @client.uri = @client.base_uri(request) 22 | @client.scope_values = Oauth.normalize_scope(params[:client][:scope].clone) 23 | 24 | if @client.save 25 | redirect_to @client, notice: "Resource was successfully created." 26 | else 27 | render "new" 28 | end 29 | end 30 | 31 | def edit 32 | end 33 | 34 | def update 35 | @client.scope = params[:client][:scope] 36 | @client.scope_values = Oauth.normalize_scope(params[:client][:scope].clone) 37 | 38 | if @client.update_attributes(params[:client]) 39 | flash.now.notice = "Resource was successfully updated." 40 | render "show" 41 | else 42 | render action: "edit" 43 | end 44 | end 45 | 46 | def destroy 47 | @client.destroy 48 | redirect_to(clients_url, notice: "Resource was successfully destroyed.") 49 | end 50 | 51 | # TODO: this is not REST way 52 | def block 53 | @client.block! 54 | redirect_to clients_url 55 | end 56 | 57 | def unblock 58 | @client.unblock! 59 | redirect_to clients_url 60 | end 61 | 62 | 63 | private 64 | 65 | def find_clients 66 | if current_user.admin? 67 | @clients = Client.criteria 68 | else 69 | @clients = Client.where(created_from: current_user.uri) 70 | end 71 | end 72 | 73 | def find_client 74 | @client = @clients.id(params[:id]).first 75 | unless @client 76 | redirect_to root_path, alert: "Resource not found." 77 | end 78 | end 79 | 80 | def normalize_scope 81 | params[:client][:scope] = params[:client][:scope].split(Oauth.settings["scope_separator"]) 82 | end 83 | 84 | def admin? 85 | unless current_user.admin? 86 | flash.alert = "Unauthorized access." 87 | redirect_to root_path 88 | return false 89 | end 90 | end 91 | 92 | end 93 | -------------------------------------------------------------------------------- /app/controllers/oauth/oauth_authorize_controller.rb: -------------------------------------------------------------------------------- 1 | class Oauth::OauthAuthorizeController < ApplicationController 2 | 3 | before_filter :authenticate 4 | before_filter :normalize_scope 5 | before_filter :find_client 6 | before_filter :check_scope # check if the access is authorized 7 | before_filter :client_blocked? # check if the client is blocked 8 | before_filter :access_blocked? # check if user has blocked the client 9 | 10 | before_filter :token_blocked?, only: :show # check for an existing token 11 | before_filter :refresh_token, only: :show # create a new token 12 | 13 | 14 | def show 15 | render "/oauth/authorize" and return 16 | end 17 | 18 | def create 19 | @client.granted! 20 | 21 | # section 4.1.1 - authorization code flow 22 | if params[:response_type] == "code" 23 | @authorization = OauthAuthorization.create(client_uri: @client.uri, resource_owner_uri: current_user.uri, scope: params[:scope]) 24 | redirect_to authorization_redirect_uri(@client, @authorization, params[:state]) 25 | end 26 | 27 | # section 4.2.1 - implicit grant flow 28 | if params[:response_type] == "token" 29 | @token = OauthToken.create(client_uri: @client.uri, resource_owner_uri: current_user.uri, scope: params[:scope]) 30 | redirect_to implicit_redirect_uri(@client, @token, params[:state]) 31 | end 32 | end 33 | 34 | def destroy 35 | @client.revoked! 36 | redirect_to deny_redirect_uri(params[:response_type], params[:state]) 37 | end 38 | 39 | 40 | private 41 | 42 | def normalize_scope 43 | params[:scope] = Oauth.normalize_scope(params[:scope]) 44 | end 45 | 46 | 47 | def find_client 48 | @client = Client.where_uri(params[:client_id], params[:redirect_uri]) 49 | client_not_found unless @client.first 50 | end 51 | 52 | def check_scope 53 | @client = @client.where_scope(params[:scope]).first 54 | scope_not_valid unless @client 55 | end 56 | 57 | def client_blocked? 58 | client_blocked if @client.blocked? 59 | end 60 | 61 | def access_blocked? 62 | access = OauthAccess.find_or_create_by(:client_uri => @client.uri, resource_owner_uri: current_user.uri) 63 | access_blocked if access.blocked? 64 | end 65 | 66 | def token_blocked? 67 | if params[:response_type] == "token" 68 | @token = OauthToken.exist(@client.uri, current_user.uri, params[:scope]).first 69 | token_blocked if @token and @token.blocked? 70 | end 71 | end 72 | 73 | # @only refresh token for implicit flow 74 | def refresh_token 75 | if @token 76 | @token = OauthToken.create(client_uri: @client.uri, resource_owner_uri: current_user.uri, scope: params[:scope]) 77 | redirect_to implicit_redirect_uri(@client, @token, params[:state]) and return 78 | end 79 | end 80 | 81 | 82 | # helper methods 83 | 84 | def client_not_found 85 | flash.now.alert = I18n.t "notifications.oauth.client.not_found" 86 | @info = { client_id: params[:client_id], redirect_uri: params[:redirect_uri] } 87 | render "oauth/authorize" and return 88 | end 89 | 90 | def scope_not_valid 91 | flash.now.alert = I18n.t "notifications.oauth.client.not_authorized" 92 | @info = { scope: params[:scope] } 93 | render "oauth/authorize" and return 94 | end 95 | 96 | def client_blocked 97 | flash.now.alert = I18n.t "notifications.oauth.client.blocked" 98 | @info = { client_id: params[:client_id] } 99 | render "oauth/authorize" and return 100 | end 101 | 102 | def access_blocked 103 | flash.now.alert = I18n.t "notifications.oauth.resource_owner.blocked_client" 104 | @info = { client_id: params[:client_id] } 105 | render "oauth/authorize" and return 106 | end 107 | 108 | def token_blocked 109 | flash.now.alert = I18n.t "notifications.oauth.token.blocked_token" 110 | @info = { client_id: params[:client_id], token: @token.token } 111 | render "oauth/authorize" and return 112 | end 113 | 114 | def authorization_redirect_uri(client, authorization, state) 115 | uri = client.redirect_uri 116 | uri += "?code=" + authorization.code 117 | uri += "&state=" + state if state 118 | return uri 119 | end 120 | 121 | def implicit_redirect_uri(client, token, state) 122 | uri = client.redirect_uri 123 | uri += "#token=" + token.token 124 | uri += "&expires_in=" + Oauth.settings["token_expires_in"] 125 | uri += "&state=" + state if state 126 | return uri 127 | end 128 | 129 | def deny_redirect_uri(response_type, state) 130 | uri = @client.redirect_uri 131 | uri += (response_type == "code") ? "?" : "#" 132 | uri += "error=access_denied" 133 | uri += "&state=" + state if state 134 | return uri 135 | end 136 | 137 | end 138 | -------------------------------------------------------------------------------- /app/controllers/oauth/oauth_token_controller.rb: -------------------------------------------------------------------------------- 1 | class Oauth::OauthTokenController < ApplicationController 2 | include ActionView::Helpers::DateHelper 3 | 4 | skip_before_filter :authenticate 5 | before_filter :json_body 6 | 7 | # authorization code flow 8 | before_filter :client_where_secret_and_redirect 9 | before_filter :find_authorization 10 | before_filter :find_authorization_expired 11 | 12 | # password credential flow 13 | before_filter :normalize_scope 14 | before_filter :client_where_secret 15 | before_filter :check_scope 16 | before_filter :find_resource_owner 17 | 18 | # refresh token flow 19 | before_filter :find_refresh_token 20 | before_filter :find_expired_token 21 | before_filter :token_blocked? 22 | 23 | before_filter :client_blocked?, only: "create" # check if the client is blocked 24 | before_filter :access_blocked?, only: "create" # check if user has blocked the client 25 | 26 | 27 | def create 28 | # section 4.1.3 - authorization code flow 29 | if @body[:grant_type] == "authorization_code" 30 | @token = OauthToken.create(client_uri: @client.uri, resource_owner_uri: @authorization.resource_owner_uri, scope: @authorization.scope) 31 | @refresh_token = OauthRefreshToken.create(access_token: @token.token) 32 | render "/oauth/token" and return 33 | end 34 | 35 | # section 4.3.1 (password credentials flow) 36 | if @body[:grant_type] == "password" 37 | @token = OauthToken.create(client_uri: @client.uri, resource_owner_uri: @resource_owner.uri, scope: @body[:scope]) 38 | @refresh_token = OauthRefreshToken.create(access_token: @token.token) 39 | render "/oauth/token" and return 40 | end 41 | 42 | # section 6.0 (refresh token) 43 | if @body[:grant_type] == "refresh_token" 44 | @token = OauthToken.create(client_uri: @expired_token.client_uri, resource_owner_uri: @expired_token.resource_owner_uri, scope: @expired_token.scope) 45 | render "/oauth/token" and return 46 | end 47 | end 48 | 49 | # simulate a logout blocking the token 50 | # TODO: refactoring 51 | def destroy 52 | token = OauthToken.where(token: params[:id]).first 53 | if token 54 | token.block! 55 | return head 200 56 | else 57 | return head 404 58 | end 59 | end 60 | 61 | 62 | private 63 | 64 | # filters for section 4.1.3 - authorization code flow 65 | def client_where_secret_and_redirect 66 | if @body[:grant_type] == "authorization_code" 67 | @client = Client.where_secret(@body[:client_secret], @body[:client_id]).where(redirect_uri: @body[:redirect_uri]).first 68 | message = "notifications.oauth.client.not_found" 69 | info = { client_secret: @body[:client_secret], client_id: @body[:client_id], redirect_uri: @body[:redirect_uri] } 70 | render_422 message, info unless @client 71 | end 72 | end 73 | 74 | def find_authorization 75 | if @body[:grant_type] == "authorization_code" 76 | @authorization = OauthAuthorization.where_code_and_client_uri(@body[:code], @client.uri).first 77 | @resource_owner_uri = @authorization.resource_owner_uri if @authorization 78 | message = "notifications.oauth.authorization.not_found" 79 | info = { code: @body[:code], client_id: @client.uri } 80 | render_422 message, info unless @authorization 81 | end 82 | end 83 | 84 | def find_authorization_expired 85 | if @body[:grant_type] == "authorization_code" 86 | message = "notifications.oauth.authorization.expired" 87 | info = { expired_at: @authorization.expire_at, description: distance_of_time_in_words(@authorization.expire_at, Time.now, true) } 88 | render_422 message, info if @authorization.expired? 89 | end 90 | end 91 | 92 | 93 | # filters for section 4.3.1 (password credentials flow) 94 | def normalize_scope 95 | if @body[:grant_type] == "password" 96 | @body[:scope] ||= "" 97 | @body[:scope] = Oauth.normalize_scope(@body[:scope]) 98 | end 99 | end 100 | 101 | def client_where_secret 102 | if @body[:grant_type] == "password" or @body[:grant_type] == "refresh_token" 103 | @client = Client.where_secret(@body[:client_secret], @body[:client_id]) 104 | message = "notifications.oauth.client.not_found" 105 | info = { client_secret: @body[:client_secret], client_id: @body[:client_id] } 106 | render_422 message, info unless @client.first 107 | end 108 | end 109 | 110 | def check_scope 111 | if @body[:grant_type] == "password" 112 | @client = @client.where_scope(@body[:scope]).first 113 | message = "notifications.oauth.client.not_authorized" 114 | info = { scope: @body[:scope] } 115 | render_422 message, info unless @client 116 | end 117 | end 118 | 119 | def find_resource_owner 120 | if @body[:grant_type] == "password" 121 | @resource_owner = User.authenticate(@body[:username], @body[:password]) 122 | @resource_owner_uri = @resource_owner.uri if @resource_owner 123 | message = "notifications.oauth.resource_owner.not_found" 124 | info = { username: @body[:username] } 125 | render_422 message, info unless @resource_owner 126 | end 127 | end 128 | 129 | 130 | # filters for refresh token (section 6.0) 131 | def find_refresh_token 132 | if @body[:grant_type] == "refresh_token" 133 | @client = @client.first 134 | @refresh_token = OauthRefreshToken.where(refresh_token: @body[:refresh_token]).first 135 | message = "notifications.oauth.refresh_token.not_found" 136 | info = { refresh_token: @body[:refresh_token] } 137 | render_422 message, info unless @refresh_token 138 | end 139 | end 140 | 141 | def find_expired_token 142 | if @body[:grant_type] == "refresh_token" 143 | @expired_token = OauthToken.where(token: @refresh_token.access_token).first 144 | @resource_owner_uri = @expired_token.resource_owner_uri 145 | message = "notifications.oauth.token.not_found" 146 | info = { token: @refresh_token.access_token } 147 | render_422 message, info unless @refresh_token 148 | end 149 | end 150 | 151 | def token_blocked? 152 | if @body[:grant_type] == "refresh_token" 153 | message = "notifications.oauth.token.blocked_token" 154 | info = { token: @refresh_token.access_token } 155 | render_422 message, info if @expired_token.blocked? 156 | end 157 | end 158 | 159 | 160 | # shared 161 | def client_blocked? 162 | message = "notifications.oauth.client.blocked" 163 | info = { client_id: @body[:client_id] } 164 | render_422 message, info if @client.blocked? 165 | end 166 | 167 | def access_blocked? 168 | access = OauthAccess.find_or_create_by(:client_uri => @client.uri, resource_owner_uri: @resource_owner_uri) 169 | message = "notifications.oauth.resource_owner.blocked_client" 170 | info = { client_id: @body[:client_id] } 171 | render_422 message, info if access.blocked 172 | end 173 | 174 | # visualization 175 | def render_404(message, info) 176 | @message = I18n.t message 177 | @info = info.to_s 178 | render "shared/404", status: 404 and return 179 | end 180 | 181 | def render_422(message, info) 182 | @message = I18n.t message 183 | @info = info.to_json 184 | render "shared/422", status: 422 and return 185 | end 186 | 187 | end 188 | -------------------------------------------------------------------------------- /app/controllers/pastas_controller.rb: -------------------------------------------------------------------------------- 1 | class PastasController < ApplicationController 2 | before_filter :oauth_authorized 3 | 4 | def index 5 | render json: {action: :index} 6 | end 7 | 8 | def show 9 | render json: {action: :show} 10 | end 11 | 12 | def create 13 | render json: {action: :create} 14 | end 15 | 16 | def update 17 | render json: {action: :update} 18 | end 19 | 20 | def destroy 21 | render json: {action: :destroy} 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/controllers/pizzas_controller.rb: -------------------------------------------------------------------------------- 1 | class PizzasController < ApplicationController 2 | before_filter :oauth_authorized 3 | 4 | def index 5 | render json: {action: :index} 6 | end 7 | 8 | def show 9 | render json: {action: :show} 10 | end 11 | 12 | def create 13 | render json: {action: :create} 14 | end 15 | 16 | def update 17 | render json: {action: :update} 18 | end 19 | 20 | def destroy 21 | render json: {action: :destroy} 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/controllers/scopes_controller.rb: -------------------------------------------------------------------------------- 1 | class ScopesController < ApplicationController 2 | 3 | before_filter :admin? 4 | before_filter :find_resource, only: ["show", "edit", "update", "destroy"] 5 | after_filter :sync_existing_scopes, only: ["update", "destroy"] 6 | 7 | def index 8 | @scopes = Scope.all 9 | end 10 | 11 | def show 12 | end 13 | 14 | def new 15 | @scope = Scope.new 16 | end 17 | 18 | def create 19 | @scope = Scope.new(params[:scope]) 20 | @scope.uri = @scope.base_uri(request) 21 | @scope.values = @scope.normalize(params[:scope][:values]) 22 | 23 | if @scope.save 24 | redirect_to(@scope, notice: "Resource was successfully created.") 25 | else 26 | render action: "new" 27 | end 28 | end 29 | 30 | def edit 31 | end 32 | 33 | def update 34 | @scope.values = @scope.normalize(params[:scope][:values]) 35 | 36 | if @scope.update_attributes(params[:scope]) 37 | render("show", notice: "Resource was successfully updated.") 38 | else 39 | render action: "edit" 40 | end 41 | end 42 | 43 | def destroy 44 | @scope.destroy 45 | redirect_to(scopes_url, notice: "Resource was successfully destroyed.") 46 | end 47 | 48 | 49 | private 50 | 51 | def find_resource 52 | @scope = Scope.criteria.id(params[:id]).first 53 | unless @scope 54 | redirect_to root_path, alert: "Resource not found." 55 | end 56 | end 57 | 58 | # TODO: put into a background process 59 | def sync_existing_scopes 60 | Client.sync_clients_with_scope(@scope.name) 61 | end 62 | 63 | def admin? 64 | unless current_user.admin? 65 | flash.alert = "Unauthorized access." 66 | redirect_to root_path 67 | return false 68 | end 69 | end 70 | 71 | end 72 | -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | 3 | skip_before_filter :authenticate 4 | 5 | def new 6 | end 7 | 8 | def create 9 | user = User.authenticate(params[:email], params[:password]) 10 | if user 11 | session[:user_id] = user.id 12 | session[:back] ||= user_path(user) 13 | redirect_to session[:back], notice: "Logged in!" 14 | session[:back] = nil 15 | else 16 | flash.now.alert = "Invalid email or password" 17 | render "new" 18 | end 19 | end 20 | 21 | def destroy 22 | session[:user_id] = nil 23 | redirect_to root_url, :notice => "Logged out!" 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | 3 | skip_before_filter :authenticate, only: ["new", "create"] 4 | before_filter :admin?, only: ["index"] 5 | before_filter :find_user, only: ["show", "edit", "update", "destroy"] 6 | 7 | def index 8 | @users = User.all 9 | end 10 | 11 | def show 12 | end 13 | 14 | def new 15 | @user = User.new 16 | end 17 | 18 | def create 19 | @user = User.new(params[:user]) 20 | @user.uri = @user.base_uri(request) 21 | @user.admin = true if admin_does_not_exist 22 | if @user.save 23 | redirect_to root_url, :notice => "Signed up!" 24 | else 25 | render "new" 26 | end 27 | end 28 | 29 | def edit 30 | end 31 | 32 | def update 33 | params[:user].delete_if { |key, value| key == "password" and value.empty? } 34 | if @user.update_attributes(params[:user]) 35 | render "show" 36 | else 37 | render action: "edit" 38 | end 39 | end 40 | 41 | 42 | private 43 | 44 | def find_user 45 | @user = current_user.admin? ? User.criteria : User.where(uri: current_user.uri) 46 | @user = @user.id(params[:id]).first 47 | unless @user 48 | redirect_to root_path, alert: "Resource not found." 49 | end 50 | end 51 | 52 | def admin? 53 | unless current_user.admin? 54 | flash.alert = "Unauthorized access." 55 | redirect_to root_path 56 | return false 57 | end 58 | end 59 | 60 | end 61 | -------------------------------------------------------------------------------- /app/helpers/accesses_helper.rb: -------------------------------------------------------------------------------- 1 | module AccessesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | 3 | end 4 | 5 | -------------------------------------------------------------------------------- /app/helpers/clients_helper.rb: -------------------------------------------------------------------------------- 1 | module ClientsHelper 2 | def authorization_uri(client, scope) 3 | "/oauth/authorization?response_type=code&scope=#{scope}&client_id=#{client.uri}&redirect_uri=#{client.redirect_uri}" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/helpers/oauth_accesses_helper.rb: -------------------------------------------------------------------------------- 1 | module OauthAccessesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/oauth_helper.rb: -------------------------------------------------------------------------------- 1 | module OauthHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/scopes_helper.rb: -------------------------------------------------------------------------------- 1 | module ScopesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/sessions_helper.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/users_helper.rb: -------------------------------------------------------------------------------- 1 | module UsersHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/models/client.rb: -------------------------------------------------------------------------------- 1 | # Application making protected resource requests on behalf of 2 | # the resource owner and with its authorization 3 | 4 | class Client 5 | include Mongoid::Document 6 | include Mongoid::Timestamps 7 | include Lelylan::Document::Base 8 | 9 | field :uri # client identifier (internal) 10 | field :name # client name 11 | field :created_from # user who created the client 12 | field :secret # client secret 13 | field :site_uri # client website 14 | field :redirect_uri # page called after authorization 15 | field :scope, type: Array, default: [] # raw scope with keywords 16 | field :scope_values, type: Array, default: [] # scope parsed as array of allowed actions 17 | field :info # client additional info 18 | field :granted_times, type: Integer, default: 0 # tokens granted in the authorization step 19 | field :revoked_times, type: Integer, default: 0 # tokens revoked in the authorization step 20 | field :blocked, type: Time, default: nil # blocks any request from the client 21 | 22 | attr_accessible :name, :site_uri, :redirect_uri, :info, :scope 23 | 24 | before_create :random_secret 25 | before_destroy :clean 26 | 27 | validates :name, presence: true 28 | validates :uri, presence: true, url: true 29 | validates :created_from, presence: true, url: true 30 | validates :redirect_uri, presence: true, url: true 31 | 32 | 33 | # Block the client 34 | def block! 35 | self.blocked = Time.now 36 | self.save 37 | OauthToken.block_client!(self.uri) 38 | OauthAuthorization.block_client!(self.uri) 39 | end 40 | 41 | # Unblock the client 42 | def unblock! 43 | self.blocked = nil 44 | self.save 45 | end 46 | 47 | # Check if the status is or is not blocked 48 | def blocked? 49 | !self.blocked.nil? 50 | end 51 | 52 | # Increase the counter of resource owners granting the access 53 | # to the client 54 | def granted! 55 | self.granted_times += 1 56 | self.save 57 | end 58 | 59 | # Increase the counter of resource owners revoking the access 60 | # to the client 61 | def revoked! 62 | self.revoked_times += 1 63 | self.save 64 | end 65 | 66 | def scope_pretty 67 | separator = Oauth.settings["scope_separator"] 68 | scope.join(separator) 69 | end 70 | 71 | def scope_values_pretty 72 | separator = Oauth.settings["scope_separator"] 73 | scope_values.join(separator) 74 | end 75 | 76 | class << self 77 | 78 | # Filter to the client uri (internal identifier) and the 79 | # redirect uri 80 | def where_uri(client_uri, redirect_uri) 81 | where(uri: client_uri, redirect_uri: redirect_uri) 82 | end 83 | 84 | # Filter to the client secret and the redirect uri 85 | def where_secret(secret, client_uri) 86 | where(secret: secret, uri: client_uri) 87 | end 88 | 89 | # Filter to the client scope 90 | def where_scope(scope) 91 | all_in(scope_values: scope) 92 | end 93 | 94 | # Sync all clients with the correct exploded scope when a 95 | # scope is modified (changed or removed) 96 | def sync_clients_with_scope(scope) 97 | Client.all.each do |client| 98 | scope_string = client.scope.join(Oauth.settings["scope_separator"]) 99 | client.scope_values = Oauth.normalize_scope(scope_string) 100 | client.save 101 | end 102 | end 103 | end 104 | 105 | 106 | private 107 | 108 | # TODO: use atomic updates 109 | # https://github.com/mongoid/mongoid/commit/aa2c388c71529bf4d987b286acfd861eaac530ce 110 | def block_tokens! 111 | OauthToken.where(client_uri: uri).map(&:block!) 112 | end 113 | 114 | def block_authorizations! 115 | OauthAuthorization.where(client_uri: uri).map(&:block!) 116 | end 117 | 118 | def random_secret 119 | self.secret = ActiveSupport::SecureRandom.hex(Oauth.settings["random_length"]) 120 | end 121 | 122 | def clean 123 | OauthToken.where(client_uri: uri).destroy_all 124 | OauthAuthorization.where(client_uri: uri).destroy_all 125 | end 126 | 127 | end 128 | -------------------------------------------------------------------------------- /app/models/oauth/oauth_access.rb: -------------------------------------------------------------------------------- 1 | # Access info related to a resource owner using a specific 2 | # client (block and statistics) 3 | 4 | class OauthAccess 5 | include Mongoid::Document 6 | include Mongoid::Timestamps 7 | 8 | field :client_uri # client identifier (internal) 9 | field :resource_owner_uri # resource owner identifier 10 | field :blocked, type: Time, default: nil # authorization block (a user block a single client) 11 | 12 | embeds_many :oauth_daily_requests # daily requests (one record per day) 13 | 14 | validates :client_uri, presence: true 15 | validates :resource_owner_uri, presence: true 16 | 17 | 18 | # Block the resource owner delegation to a specific client 19 | def block! 20 | self.blocked = Time.now 21 | self.save 22 | OauthToken.block_access!(client_uri, resource_owner_uri) 23 | OauthAuthorization.block_access!(client_uri, resource_owner_uri) 24 | end 25 | 26 | # Unblock the resource owner delegation to a specific client 27 | def unblock! 28 | self.blocked = nil 29 | self.save 30 | end 31 | 32 | # Check if the status is or is not blocked 33 | def blocked? 34 | !self.blocked.nil? 35 | end 36 | 37 | # Increment the daily accesses 38 | def accessed! 39 | daily_requests.increment! 40 | end 41 | 42 | # A daily requests record (there is one per day) 43 | # 44 | # @params [String] time we want to find the requests record 45 | # @return [OauthDailyRequest] requests record 46 | def daily_requests(time = Time.now) 47 | find_or_create_daily_requests(time) 48 | end 49 | 50 | # Give back the last days in a friendly format.It is used to 51 | # generate graph for statistics 52 | def chart_days 53 | daily_requests = self.oauth_daily_requests.limit(10) 54 | days = daily_requests.map(&:created_at) 55 | days.map { |d| d.strftime("%b %e") } 56 | end 57 | 58 | # Give the number of accesses for the last days. It is used 59 | # to generate graph for statistics 60 | def chart_times 61 | access_times = self.oauth_daily_requests.limit(10) 62 | access_times.map(&:times) 63 | end 64 | 65 | 66 | private 67 | 68 | def find_or_create_daily_requests(time) 69 | daily_requests = oauth_daily_requests.find_day(time).first 70 | daily_requests = oauth_daily_requests.create(created_at: time) unless daily_requests 71 | return daily_requests 72 | end 73 | 74 | def daily_id(time) 75 | time.year + time.month + time.day 76 | end 77 | 78 | end 79 | -------------------------------------------------------------------------------- /app/models/oauth/oauth_authorization.rb: -------------------------------------------------------------------------------- 1 | # Authorization grant which represents the authorization 2 | # provided by the resource owner 3 | 4 | class OauthAuthorization 5 | include Mongoid::Document 6 | include Mongoid::Timestamps 7 | 8 | field :client_uri # client identifier 9 | field :resource_owner_uri # resource owner identifier 10 | field :code # authorization code 11 | field :scope, type: Array # scope accessible with request 12 | field :expire_at, type: Time # authorization expiration (security reasons) 13 | field :blocked, type: Time, default: nil # authorization block (if client is blocked) 14 | 15 | validates :client_uri, presence: true, url: true 16 | validates :resource_owner_uri, presence: true, url: true 17 | 18 | before_create :random_code 19 | before_create :create_expiration 20 | 21 | # Block the authorization (when resource owner blocks a client) 22 | def block! 23 | self.blocked = Time.now 24 | self.save 25 | end 26 | 27 | # Block tokens used from a client 28 | def self.block_client!(client_uri) 29 | self.where(client_uri: client_uri).map(&:block!) 30 | end 31 | 32 | # Block tokens used from a client in behalf of a resource owner 33 | def self.block_access!(client_uri, resource_owner_uri) 34 | self.where(client_uri: client_uri, resource_owner_uri: resource_owner_uri).map(&:block!) 35 | end 36 | 37 | # Check if the status is or is not blocked 38 | def blocked? 39 | !self.blocked.nil? 40 | end 41 | 42 | # Check if the authorization is expired 43 | def expired? 44 | self.expire_at < Time.now 45 | end 46 | 47 | # Find the authorization based on the client uri and the 48 | # authorization code 49 | class << self 50 | def where_code_and_client_uri(code, client_id) 51 | where(code: code).where(client_uri: client_id) 52 | end 53 | end 54 | 55 | 56 | private 57 | 58 | # random authorization code 59 | def random_code 60 | self.code = ActiveSupport::SecureRandom.hex(Oauth.settings["random_length"]) 61 | end 62 | 63 | # expiration time 64 | def create_expiration 65 | self.expire_at = Chronic.parse("in #{Oauth.settings["authorization_expires_in"]} seconds") 66 | end 67 | 68 | end 69 | -------------------------------------------------------------------------------- /app/models/oauth/oauth_daily_request.rb: -------------------------------------------------------------------------------- 1 | # Daily requests of a Resource Owner on a specific client 2 | 3 | class OauthDailyRequest 4 | 5 | include Mongoid::Document 6 | 7 | field :created_at, type: Time # creation time 8 | field :time_id # unique key for the day 9 | field :day # request day 10 | field :month # request month 11 | field :year # request year 12 | field :times, type: Integer, default: 0 # daily request times 13 | 14 | # resource owner's client access 15 | embedded_in :oauth_access, inverse_of: :oauth_daily_requests 16 | 17 | after_create :init_times 18 | 19 | # Increment the times counter that track the number of 20 | # requests a client have made in behalf of a resource 21 | # owner in a specific day 22 | def increment! 23 | self.times += 1 24 | self.save 25 | end 26 | 27 | class << self 28 | 29 | # Find a daily requests record 30 | def find_day(time) 31 | time_id = time_id(time) 32 | where(time_id: time_id) 33 | end 34 | 35 | # Define an identifier for a specific day 36 | def time_id(time) 37 | time.strftime("%Y%m%d") 38 | end 39 | end 40 | 41 | private 42 | 43 | # Add statistical informations 44 | def init_times 45 | self.day = self.created_at.strftime("%d") 46 | self.month = self.created_at.strftime("%m") 47 | self.year = self.created_at.strftime("%Y") 48 | self.time_id = self.class.time_id(created_at) 49 | self.save 50 | end 51 | 52 | end 53 | -------------------------------------------------------------------------------- /app/models/oauth/oauth_refresh_token.rb: -------------------------------------------------------------------------------- 1 | class OauthRefreshToken 2 | include Mongoid::Document 3 | include Mongoid::Timestamps 4 | 5 | field :refresh_token 6 | field :access_token 7 | 8 | validates :access_token, presence: true 9 | 10 | before_create :random_refresh_token 11 | 12 | private 13 | 14 | def random_refresh_token 15 | self.refresh_token = ActiveSupport::SecureRandom.hex(Oauth.settings["random_length"]) 16 | end 17 | 18 | end 19 | -------------------------------------------------------------------------------- /app/models/oauth/oauth_token.rb: -------------------------------------------------------------------------------- 1 | # Access token used from the client to request resource 2 | # owner resouces 3 | 4 | class OauthToken 5 | include Mongoid::Document 6 | include Mongoid::Timestamps 7 | 8 | field :client_uri # client identifier (internal) 9 | field :resource_owner_uri # resource owner identifier 10 | field :token # access token 11 | field :refresh_token # refresh token 12 | field :scope, type: Array # scope accessible with token 13 | field :expire_at, type: Time, default: nil # token expiration 14 | field :blocked, type: Time, default: nil # access token block (if client is blocked) 15 | 16 | before_create :random_token 17 | before_create :random_refresh_token 18 | before_create :create_expiration 19 | 20 | validates :client_uri, presence: true, url: true 21 | validates :resource_owner_uri, presence: true, url: true 22 | 23 | 24 | # Block the resource owner delegation to a specific client 25 | def block! 26 | self.blocked = Time.now 27 | self.save 28 | end 29 | 30 | # Block tokens used from a client 31 | def self.block_client!(client_uri) 32 | self.where(client_uri: client_uri).map(&:block!) 33 | end 34 | 35 | # Block tokens used from a client in behalf of a resource owner 36 | def self.block_access!(client_uri, resource_owner_uri) 37 | self.where(client_uri: client_uri, resource_owner_uri: resource_owner_uri).map(&:block!) 38 | end 39 | 40 | def self.exist(client_uri, resource_owner_uri, scope) 41 | self.where(client_uri: client_uri). 42 | where(resource_owner_uri: resource_owner_uri). 43 | all_in(scope: scope) 44 | end 45 | 46 | # Check if the status is or is not blocked 47 | def blocked? 48 | !self.blocked.nil? 49 | end 50 | 51 | # Last time the resource owner have used the token 52 | def last_access 53 | self.updated_at 54 | end 55 | 56 | # Token is expired or not 57 | def expired? 58 | self.expire_at < Time.now 59 | end 60 | 61 | 62 | private 63 | 64 | def random_token 65 | self.token = ActiveSupport::SecureRandom.hex(Oauth.settings["random_length"]) 66 | end 67 | 68 | def random_refresh_token 69 | self.refresh_token = ActiveSupport::SecureRandom.hex(Oauth.settings["random_length"]) 70 | end 71 | 72 | def create_expiration 73 | self.expire_at = Chronic.parse("in #{Oauth.settings["token_expires_in"]} seconds") 74 | end 75 | 76 | end 77 | -------------------------------------------------------------------------------- /app/models/scope.rb: -------------------------------------------------------------------------------- 1 | class Scope 2 | include Mongoid::Document 3 | include Mongoid::Timestamps 4 | include Lelylan::Document::Base 5 | 6 | field :name 7 | field :uri 8 | field :values, type: Array, default: [] 9 | 10 | attr_accessible :name 11 | 12 | validates :name, presence: true 13 | validates :values, presence: true 14 | validates :uri, url: true 15 | 16 | def normalize(val) 17 | separator = Oauth.settings["scope_separator"] 18 | val = val.split(separator) 19 | end 20 | 21 | def values_pretty 22 | separator = Oauth.settings["scope_separator"] 23 | values.join(separator) 24 | end 25 | 26 | class << self 27 | # Sync all scopes with the correct exploded scope when a 28 | # scope is modified (changed or removed) 29 | def sync_scopes_with_scope(scope) 30 | scopes_to_sync = any_in(scope: [scope]) 31 | scopes_to_sync.each do |client| 32 | scope.values = Oauth.normalize_scope(scope.values) 33 | scope.save 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User 2 | include Mongoid::Document 3 | include Mongoid::Timestamps 4 | include Lelylan::Document::Base 5 | 6 | field :uri 7 | field :email 8 | field :name 9 | field :password_hash 10 | field :password_salt 11 | field :admin, type: Boolean, default: false 12 | 13 | attr_accessible :email, :name, :password 14 | 15 | attr_accessor :password 16 | before_save :encrypt_password 17 | 18 | validates :password, presence: true, on: :create 19 | # TODO: add password length 20 | #validates :password, length: {min: 6}, empty: true 21 | validates :email, presence: true 22 | validates :email, uniqueness: true 23 | validates :email, email: true 24 | 25 | def self.authenticate(email, password) 26 | user = where(email: email).first 27 | user.verify(password) if user 28 | end 29 | 30 | def verify(password) 31 | if password_hash == BCrypt::Engine.hash_secret(password, password_salt) 32 | self 33 | else 34 | nil 35 | end 36 | end 37 | 38 | def encrypt_password 39 | if password.present? 40 | self.password_salt = BCrypt::Engine.generate_salt 41 | self.password_hash = BCrypt::Engine.hash_secret(password, password_salt) 42 | end 43 | end 44 | 45 | def admin? 46 | self.admin 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /app/views/accesses/index.html.erb: -------------------------------------------------------------------------------- 1 |

Show Accesses

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <% @accesses.each do |access| %> 11 | 12 | 13 | 14 | 15 | <% if access.blocked? %> 16 | 17 | <% else %> 18 | 19 | <% end %> 20 | 21 | 22 | <% end %> 23 |
Client URI
<%= access.client_uri %><%= link_to 'Show stats', access_path(access), class: "button icon settings" %><%= link_to 'Unblock!', unblock_access_path(access), method: :put, class: "button danger" %><%= link_to 'Block!', block_access_path(access), method: :put, class: "button danger" %>
24 | 25 |
26 | -------------------------------------------------------------------------------- /app/views/accesses/show.html.erb: -------------------------------------------------------------------------------- 1 |

Show Access

2 | 3 |
4 | Client URI: 5 | <%= @access.client_uri %> 6 |
7 | 8 |
9 | Today requests: 10 | <%= @access.daily_requests.times %> 11 |
12 | 13 |
14 | 15 |