warden-github-rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/.gitignore +20 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +10 -0
  4. data/Gemfile +14 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +188 -0
  7. data/Rakefile +6 -0
  8. data/VERSION +1 -0
  9. data/lib/warden/github/rails.rb +42 -0
  10. data/lib/warden/github/rails/config.rb +52 -0
  11. data/lib/warden/github/rails/controller_helpers.rb +35 -0
  12. data/lib/warden/github/rails/railtie.rb +27 -0
  13. data/lib/warden/github/rails/routes.rb +83 -0
  14. data/lib/warden/github/rails/test_helpers.rb +28 -0
  15. data/lib/warden/github/rails/test_helpers/mock_user.rb +28 -0
  16. data/lib/warden/github/rails/version.rb +9 -0
  17. data/lib/warden/github/rails/view_helpers.rb +18 -0
  18. data/spec/integration/controller_helpers_spec.rb +95 -0
  19. data/spec/integration/membership_spec.rb +183 -0
  20. data/spec/integration/route_spec.rb +82 -0
  21. data/spec/integration/scope_spec.rb +33 -0
  22. data/spec/rails_app/app/controllers/scoped_controller.rb +28 -0
  23. data/spec/rails_app/app/controllers/unscoped_controller.rb +28 -0
  24. data/spec/rails_app/config.ru +2 -0
  25. data/spec/rails_app/config/application.rb +20 -0
  26. data/spec/rails_app/config/boot.rb +3 -0
  27. data/spec/rails_app/config/environment.rb +3 -0
  28. data/spec/rails_app/config/environments/development.rb +8 -0
  29. data/spec/rails_app/config/environments/production.rb +8 -0
  30. data/spec/rails_app/config/environments/test.rb +11 -0
  31. data/spec/rails_app/config/initializers/secret_token.rb +1 -0
  32. data/spec/rails_app/config/initializers/session_store.rb +1 -0
  33. data/spec/rails_app/config/initializers/warden_github_rails.rb +12 -0
  34. data/spec/rails_app/config/initializers/wrap_parameters.rb +4 -0
  35. data/spec/rails_app/config/routes.rb +49 -0
  36. data/spec/rails_app/script/rails +6 -0
  37. data/spec/spec_helper.rb +37 -0
  38. data/spec/unit/config_spec.rb +67 -0
  39. data/spec/unit/mock_user_spec.rb +20 -0
  40. data/spec/unit/rails_spec.rb +11 -0
  41. data/spec/unit/test_helpers_spec.rb +39 -0
  42. data/warden-github-rails.gemspec +25 -0
  43. metadata +229 -0
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ tags
19
+ bin/
20
+ log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ script: "bundle exec rspec"
3
+ rvm:
4
+ - 1.8.7
5
+ - 1.9.2
6
+ - 1.9.3
7
+ - ruby-head
8
+ matrix:
9
+ allow_failures:
10
+ - rvm: ruby-head
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ if ENV['EDGE']
6
+ gem 'warden-github', :github => 'atmos/warden-github'
7
+ end
8
+
9
+ group :development do
10
+ unless ENV['CI']
11
+ gem 'debugger', :platforms => :ruby_19, :require => false
12
+ gem 'ruby-debug', :platforms => :ruby_18, :require => false
13
+ end
14
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Philipe Fatio
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,188 @@
1
+ # warden-github-rails
2
+
3
+ [![Build Status](https://travis-ci.org/fphilipe/warden-github-rails.png)](https://travis-ci.org/fphilipe/warden-github-rails)
4
+ [![Gem Version](https://badge.fury.io/rb/warden-github-rails.png)](http://badge.fury.io/rb/warden-github-rails)
5
+ [![Dependency Status](https://gemnasium.com/fphilipe/warden-github-rails.png)](https://gemnasium.com/fphilipe/warden-github-rails)
6
+ [![Code Climate](https://codeclimate.com/github/fphilipe/warden-github-rails.png)](https://codeclimate.com/github/fphilipe/warden-github-rails)
7
+
8
+ A gem for rails that provides easy GitHub OAuth integration.
9
+ It is built on top of [warden-github](https://github.com/atmos/warden-github), which gives you an easy to use [warden](https://github.com/hassox/warden) strategy to authenticate GitHub users.
10
+
11
+ ## Motivation
12
+
13
+ **Wouldn't it be nice to**
14
+
15
+ - use your organization and it's teams for user access control?
16
+ - add a new employee to your GitHub organization or team in order to grant them access to your app's admin area?
17
+
18
+ The motivation for this gem was to provide a very easy authorization (not authentication) mechanism to existing rails apps for admins, especially in combination with organization and team memberships.
19
+ The provided routing helpers do exactly that.
20
+ They allow you to restrict access to members of your organization or a certain team.
21
+
22
+ This is how your rails `routes.rb` could look like:
23
+
24
+ ```ruby
25
+ constraints(:subdomain => 'admin') do
26
+ github_authenticate(:org => 'my_company_inc') do
27
+ resources :users
28
+ resources :projects
29
+
30
+ github_authenticated(:team => 'sysadmins') do
31
+ resource :infrastructure
32
+ end
33
+ end
34
+ end
35
+ ```
36
+
37
+ Of course, this gem can also be used for user registration and authentication.
38
+ Several helper methods are available in the controller to accomplish this:
39
+
40
+ ```ruby
41
+ class UsersController < ApplicationController
42
+ # ...
43
+
44
+ def new
45
+ github_authenticate! # Performs OAuth flow when not logged in.
46
+ @user = User.new(:name => github_user.name, :email => github_user.email)
47
+ end
48
+
49
+ def create
50
+ attrs = params.require(:user).permit(:name, :email).merge(:github_id => github_user.id)
51
+ @user = User.create(attrs)
52
+
53
+ if @user
54
+ redirect_to :show
55
+ else
56
+ render :new
57
+ end
58
+ end
59
+
60
+ # ...
61
+ end
62
+ ```
63
+
64
+ ## Usage
65
+
66
+ ### Configuration
67
+
68
+ First off, you might want to configure this gem by creating an initializer such as `config/initializers/warden_github_rails.rb`.
69
+ There you can define:
70
+
71
+ - various scopes and their configs (scopes are types of users with different configs)
72
+ - the default scope (which is `:user` by default)
73
+ - team aliases (GitHub teams are identified by a numerical ID; defining an alias for a team makes it easier to use)
74
+
75
+ Here's how such a config might look like:
76
+
77
+ ```ruby
78
+ Warden::GitHub::Rails.setup do |config|
79
+ config.add_scope :user, :client_id => 'foo',
80
+ :client_secret => 'bar',
81
+ :scope => 'user'
82
+
83
+ config.add_scope :admin, :client_id => 'abc',
84
+ :client_secret => 'xyz',
85
+ :redirect_uri => '/admin/login/callback',
86
+ :scope => 'repo'
87
+
88
+ config.default_scope = :admin
89
+
90
+ config.add_team :marketing, 456
91
+ end
92
+ ```
93
+
94
+ For a list of allowed config parameters to use in `#add_scope`, read the [warden-github documentation](https://github.com/atmos/warden-github#parameters).
95
+
96
+ ### Inside `routes.rb`
97
+
98
+ The available routing helpers are defined and documented in [lib/warden/github/rails/routes.rb](lib/warden/github/rails/routes.rb).
99
+ They all accept an optional scope that, when omitted, falls back to the default_scope configured in the initializer.
100
+
101
+ Examples:
102
+
103
+ ```ruby
104
+ # Performs login if not logged in already.
105
+ github_authenticate do
106
+ resource :profile
107
+ end
108
+
109
+ # Does not perform login when not logged in.
110
+ github_authenticated do
111
+ delete '/logout' => 'sessions#delete'
112
+ end
113
+
114
+ # Only matches when not logged in. Does not perform login.
115
+ github_unauthenticated do
116
+ resource :registration
117
+ end
118
+
119
+ # Only matches when member of the organization. Initiates login if not logged in.
120
+ github_authenticate(:org => 'my_company') do
121
+ resource :admin
122
+ end
123
+
124
+ # Only matches when member of the team. Does not initiate login if not logged in.
125
+ github_authenticated(:team => 'markting') do
126
+ get '/dashboard' => 'dashboard#show'
127
+ end
128
+
129
+ # Using dynamic membership values:
130
+ github_authenticate(:org => lambda { |req| r.params[:id] }) do
131
+ get '/orgs/:id' => 'orgs#show'
132
+ end
133
+ ```
134
+
135
+ ### Inside a Controller
136
+
137
+ The available controller helpers are defined and documented in [lib/warden/github/rails/controller_helpers.rb](lib/warden/github/rails/controller_helpers.rb).
138
+ They all accept an optional scope that, when omitted, falls back to the default_scope configured in the initializer.
139
+
140
+ ```ruby
141
+ class SomeController < ActionController::Base
142
+ def show
143
+ @is_admin = github_authenticated?(:admin)
144
+ end
145
+
146
+ def delete
147
+ github_logout
148
+ redirect_to '/'
149
+ end
150
+
151
+ def settings
152
+ github_authenticate!
153
+ @settings = UserSettings.find_by_github_user_id(github_user.id)
154
+ end
155
+
156
+ def finish_wizard
157
+ github_session[:wizard_completed] = true
158
+ end
159
+
160
+ def followers
161
+ @followers = github_user.api.followers
162
+ end
163
+ end
164
+ ```
165
+
166
+ ### Communicating with the GitHub API
167
+
168
+ Once a user is logged in, you'll have access to it in the controller using `github_user`. It is an instance of `Warden::GitHub::User` which is defined in the [warden-github](https://github.com/atmos/warden-github/blob/master/lib/warden/github/user.rb) gem. The instance has several methods to access user information such as `#name`, `#id`, `#email`, etc. It also features a method `#api` which returns a preconfigured [Octokit](https://github.com/pengwynn/octokit) client for that user.
169
+
170
+ ## Known Limitations
171
+
172
+ Currently, this gem does not play nicely with [Devise](https://github.com/plataformatec/devise). Devise is built on top of warden and performs some monkeypatching of warden which breaks this gem. Support will be added as soon as possible.
173
+
174
+ ## Additional Information
175
+
176
+ ### Dependencies
177
+
178
+ - [warden-github](https://github.com/atmos/warden-github)
179
+ - [warden](https://github.com/hassox/warden)
180
+ - [octokit](https://github.com/pengwynn/octokit)
181
+
182
+ ### Maintainers
183
+
184
+ - Philipe Fatio ([@fphilipe](https://github.com/fphilipe))
185
+
186
+ ### License
187
+
188
+ MIT License. Copyright 2013 Philipe Fatio
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,42 @@
1
+ require 'warden/github'
2
+
3
+ require 'warden/github/rails/version'
4
+ require 'warden/github/rails/routes'
5
+ require 'warden/github/rails/railtie'
6
+ require 'warden/github/rails/config'
7
+ require 'warden/github/rails/controller_helpers'
8
+ require 'warden/github/rails/view_helpers'
9
+
10
+ require 'forwardable'
11
+
12
+ module Warden
13
+ module GitHub
14
+ module Rails
15
+ extend SingleForwardable
16
+
17
+ def_delegators :config,
18
+ :default_scope,
19
+ :warden_config,
20
+ :warden_config=,
21
+ :scopes,
22
+ :team_id
23
+
24
+ @config = Config.new
25
+
26
+ def self.config
27
+ @config
28
+ end
29
+
30
+ # Use this method to setup this gem.
31
+ #
32
+ # @example
33
+ #
34
+ # Warden::GitHub::Rails.setup do |config|
35
+ # # ...
36
+ # end
37
+ def self.setup
38
+ yield config
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,52 @@
1
+ module Warden
2
+ module GitHub
3
+ module Rails
4
+ class Config
5
+ BadConfig = Class.new(StandardError)
6
+
7
+ # A cache of the warden config for the active manager.
8
+ attr_accessor :warden_config
9
+
10
+ # Default scope to use when not explicitly specified.
11
+ attr_accessor :default_scope
12
+
13
+ # The list of scopes and their configs. This is used to add custom
14
+ # configs to a specific scope. When using a scope that is not listed
15
+ # here, it will use the default configs from warden-github.
16
+ attr_reader :scopes
17
+
18
+ # A hash containing team alias names and their numeric id.
19
+ attr_reader :teams
20
+
21
+ def initialize
22
+ @default_scope = :user
23
+ @scopes = {}
24
+ @teams = {}
25
+ end
26
+
27
+ # Adds a scope with custom configurations to the list of scopes.
28
+ def add_scope(name, config={})
29
+ scopes[name] = config
30
+ end
31
+
32
+ # Maps a team id to a name in order to easier reference it.
33
+ def add_team(name, id)
34
+ teams[name.to_sym] = Integer(id)
35
+ end
36
+
37
+ # Gets the team id for a team id or alias.
38
+ def team_id(team)
39
+ # In ruby 1.8 doing a Integer(:symbol) returns an integer. Thus, test
40
+ # for symbol first.
41
+ if team.is_a? Symbol
42
+ teams.fetch(team)
43
+ else
44
+ Integer(team) rescue teams.fetch(team.to_sym)
45
+ end
46
+ rescue IndexError
47
+ fail BadConfig, "No team id defined for team #{team}."
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,35 @@
1
+ module Warden
2
+ module GitHub
3
+ module Rails
4
+ module ControllerHelpers
5
+ # Initiates the OAuth flow if not already authenticated for the
6
+ # specified scope.
7
+ def github_authenticate!(scope=Rails.default_scope)
8
+ request.env['warden'].authenticate!(:scope => scope)
9
+ end
10
+
11
+ # Logs out a user if currently logged in for the specified scope.
12
+ def github_logout(scope=Rails.default_scope)
13
+ request.env['warden'].logout(scope)
14
+ end
15
+
16
+ # Checks whether a user is logged in for the specified scope.
17
+ def github_authenticated?(scope=Rails.default_scope)
18
+ request.env['warden'].authenticated?(scope)
19
+ end
20
+
21
+ # Returns the currently signed in user for the specified scope. See the
22
+ # documentation for Warden::GitHub::User for available methods.
23
+ def github_user(scope=Rails.default_scope)
24
+ request.env['warden'].user(scope)
25
+ end
26
+
27
+ # Accessor for the currently signed in user's session. This will be
28
+ # cleared once logged out.
29
+ def github_session(scope=Rails.default_scope)
30
+ request.env['warden'].session(scope) if github_authenticated?(scope)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,27 @@
1
+ module Warden
2
+ module GitHub
3
+ module Rails
4
+ class Railtie < ::Rails::Railtie
5
+ config.app_middleware.use Warden::Manager do |config|
6
+ config.failure_app = lambda { |env| [403, {}, [env['warden'].message]] }
7
+ Rails.warden_config = config
8
+ Rails.scopes.each do |scope, scope_config|
9
+ config.scope_defaults scope, :strategies => [:github],
10
+ :config => scope_config
11
+ end
12
+ end
13
+
14
+ initializer 'warden-github-rails.controller_helpers' do
15
+ ActiveSupport.on_load(:action_controller) do
16
+ include ControllerHelpers
17
+ end
18
+
19
+ ActiveSupport.on_load(:action_view) do
20
+ include ViewHelpers
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,83 @@
1
+ module Warden
2
+ module GitHub
3
+ module Rails
4
+ module Routes
5
+ # Enforces an authenticated GitHub user for the routes. If not
6
+ # authenticated, it initiates the OAuth flow.
7
+ #
8
+ # Team and organization memberships can be checked by specifying a hash
9
+ # such as `:team => 'foobar'` or `:org => 'my_company'`.
10
+ def github_authenticate(scope=nil, options={}, &routes_block)
11
+ github_constraint(scope, options, routes_block) do |warden, scope|
12
+ warden.authenticate!(:scope => scope)
13
+ end
14
+ end
15
+
16
+ # The routes will only be visible to authenticated GitHub users. When
17
+ # not authenticated, it does not initiate the OAuth flow.
18
+ #
19
+ # Team and organization memberships can be checked by specifying a hash
20
+ # such as `:team => 'foobar'` or `:org => 'my_company'`.
21
+ def github_authenticated(scope=nil, options={}, &routes_block)
22
+ github_constraint(scope, options, routes_block) do |warden, scope|
23
+ warden.authenticated?(:scope => scope)
24
+ end
25
+ end
26
+
27
+ # The routes will only be visible to all but authenticated GitHub users.
28
+ #
29
+ # This constraint currently does not check for memberships since of its
30
+ # limited usage.
31
+ def github_unauthenticated(scope=nil, options={}, &routes_block)
32
+ github_constraint(scope, options, routes_block) do |warden, scope|
33
+ not warden.authenticated?(:scope => scope)
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def github_constraint(scope, options, routes_block, &block)
40
+ options, scope = scope, nil if scope.is_a? Hash
41
+ scope ||= Rails.default_scope
42
+
43
+ constraint = lambda do |request|
44
+ warden = request.env['warden']
45
+
46
+ if block.call(warden, scope)
47
+ if (user = warden.user(scope))
48
+ evaled_options = github_eval_options(options, request)
49
+ github_enforce_options(user, evaled_options)
50
+ else
51
+ true
52
+ end
53
+ end
54
+ end
55
+
56
+ constraints(constraint, &routes_block)
57
+ end
58
+
59
+ def github_enforce_options(user, options)
60
+ if (team = options[:team])
61
+ user.team_member?(Rails.team_id(team))
62
+ elsif (org = options[:org] || options[:organization])
63
+ user.organization_member?(org)
64
+ else
65
+ true
66
+ end
67
+ end
68
+
69
+ def github_eval_options(options, request)
70
+ Hash[options.map { |k,v|
71
+ if v.is_a?(Proc)
72
+ [k, v.call(request)]
73
+ else
74
+ [k, v]
75
+ end
76
+ }]
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ ActionDispatch::Routing::Mapper.send(:include, Warden::GitHub::Rails::Routes)