warden-github-rails 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.travis.yml +10 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +22 -0
- data/README.md +188 -0
- data/Rakefile +6 -0
- data/VERSION +1 -0
- data/lib/warden/github/rails.rb +42 -0
- data/lib/warden/github/rails/config.rb +52 -0
- data/lib/warden/github/rails/controller_helpers.rb +35 -0
- data/lib/warden/github/rails/railtie.rb +27 -0
- data/lib/warden/github/rails/routes.rb +83 -0
- data/lib/warden/github/rails/test_helpers.rb +28 -0
- data/lib/warden/github/rails/test_helpers/mock_user.rb +28 -0
- data/lib/warden/github/rails/version.rb +9 -0
- data/lib/warden/github/rails/view_helpers.rb +18 -0
- data/spec/integration/controller_helpers_spec.rb +95 -0
- data/spec/integration/membership_spec.rb +183 -0
- data/spec/integration/route_spec.rb +82 -0
- data/spec/integration/scope_spec.rb +33 -0
- data/spec/rails_app/app/controllers/scoped_controller.rb +28 -0
- data/spec/rails_app/app/controllers/unscoped_controller.rb +28 -0
- data/spec/rails_app/config.ru +2 -0
- data/spec/rails_app/config/application.rb +20 -0
- data/spec/rails_app/config/boot.rb +3 -0
- data/spec/rails_app/config/environment.rb +3 -0
- data/spec/rails_app/config/environments/development.rb +8 -0
- data/spec/rails_app/config/environments/production.rb +8 -0
- data/spec/rails_app/config/environments/test.rb +11 -0
- data/spec/rails_app/config/initializers/secret_token.rb +1 -0
- data/spec/rails_app/config/initializers/session_store.rb +1 -0
- data/spec/rails_app/config/initializers/warden_github_rails.rb +12 -0
- data/spec/rails_app/config/initializers/wrap_parameters.rb +4 -0
- data/spec/rails_app/config/routes.rb +49 -0
- data/spec/rails_app/script/rails +6 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/unit/config_spec.rb +67 -0
- data/spec/unit/mock_user_spec.rb +20 -0
- data/spec/unit/rails_spec.rb +11 -0
- data/spec/unit/test_helpers_spec.rb +39 -0
- data/warden-github-rails.gemspec +25 -0
- metadata +229 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
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
|
+
[](https://travis-ci.org/fphilipe/warden-github-rails)
|
4
|
+
[](http://badge.fury.io/rb/warden-github-rails)
|
5
|
+
[](https://gemnasium.com/fphilipe/warden-github-rails)
|
6
|
+
[](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
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)
|