bunto-auth 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +24 -0
- data/.travis.yml +29 -0
- data/Gemfile +3 -0
- data/README.md +177 -0
- data/Rakefile +20 -0
- data/bin/bunto-auth +134 -0
- data/bunto-auth.gemspec +33 -0
- data/lib/bunto-auth.rb +25 -0
- data/lib/bunto_auth/auth_site.rb +42 -0
- data/lib/bunto_auth/bunto_site.rb +13 -0
- data/lib/bunto_auth/commands.rb +72 -0
- data/lib/bunto_auth/config.rb +23 -0
- data/lib/bunto_auth/config_error.rb +7 -0
- data/lib/bunto_auth/helpers.rb +18 -0
- data/lib/bunto_auth/sinatra/auth/github.rb +13 -0
- data/lib/bunto_auth/version.rb +3 -0
- data/script/bootstrap +7 -0
- data/script/cibuild +13 -0
- data/script/console +1 -0
- data/script/release +38 -0
- data/script/server +3 -0
- data/script/setup +5 -0
- data/spec/bunto_auth_auth_site_spec.rb +74 -0
- data/spec/bunto_auth_bin_spec.rb +43 -0
- data/spec/bunto_auth_bunto_site_spec.rb +43 -0
- data/spec/bunto_auth_commands_spec.rb +75 -0
- data/spec/bunto_auth_helpers_spec.rb +61 -0
- data/spec/bunto_auth_spec.rb +46 -0
- data/spec/spec_helper.rb +62 -0
- data/templates/.gitignore +3 -0
- data/templates/Rakefile +9 -0
- data/templates/config.ru +5 -0
- data/templates/index.html +19 -0
- metadata +307 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
class BuntoAuth
|
2
|
+
class AuthSite < Sinatra::Base
|
3
|
+
configure :production do
|
4
|
+
require "rack-ssl-enforcer"
|
5
|
+
use Rack::SslEnforcer if BuntoAuth.ssl?
|
6
|
+
end
|
7
|
+
|
8
|
+
use Rack::Session::Cookie, :http_only => true,
|
9
|
+
:secret => ENV["SESSION_SECRET"] || SecureRandom.hex
|
10
|
+
|
11
|
+
set :github_options, :scopes => "read:org"
|
12
|
+
|
13
|
+
ENV["WARDEN_GITHUB_VERIFIER_SECRET"] ||= SecureRandom.hex
|
14
|
+
register Sinatra::Auth::Github
|
15
|
+
|
16
|
+
use Rack::Logger
|
17
|
+
|
18
|
+
include BuntoAuth::Helpers
|
19
|
+
|
20
|
+
before do
|
21
|
+
pass if whitelisted?
|
22
|
+
|
23
|
+
logger.info "Authentication strategy: #{authentication_strategy}"
|
24
|
+
|
25
|
+
case authentication_strategy
|
26
|
+
when :team
|
27
|
+
github_team_authenticate! ENV["GITHUB_TEAM_ID"]
|
28
|
+
when :teams
|
29
|
+
github_teams_authenticate! ENV["GITHUB_TEAM_IDS"].split(",")
|
30
|
+
when :org
|
31
|
+
github_organization_authenticate! ENV["GITHUB_ORG_NAME"]
|
32
|
+
else
|
33
|
+
raise BuntoAuth::ConfigError
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
get "/logout" do
|
38
|
+
logout!
|
39
|
+
redirect "/"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class BuntoAuth
|
2
|
+
class BuntoSite < Sinatra::Base
|
3
|
+
register Sinatra::Index
|
4
|
+
set :public_folder, File.expand_path("_site", Dir.pwd)
|
5
|
+
use_static_index "index.html"
|
6
|
+
|
7
|
+
not_found do
|
8
|
+
status 404
|
9
|
+
four_oh_four = File.expand_path("_site/404.html", Dir.pwd)
|
10
|
+
File.read(four_oh_four) if File.exist?(four_oh_four)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
class BuntoAuth
|
2
|
+
class Commands
|
3
|
+
FILES = %w(Rakefile config.ru .gitignore .env).freeze
|
4
|
+
VARS = %w(client_id client_secret team_id org_name).freeze
|
5
|
+
|
6
|
+
def self.source
|
7
|
+
@source ||= File.expand_path("../../templates", File.dirname(__FILE__))
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.destination
|
11
|
+
@destination ||= Dir.pwd
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.changed?
|
15
|
+
!execute_command("git", "status", destination, "--porcelain").empty?
|
16
|
+
rescue
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.execute_command(*args)
|
21
|
+
output, status = Open3.capture2e(*args)
|
22
|
+
raise "Command `#{args.join(" ")}` failed: #{output}" unless status.exitstatus.zero?
|
23
|
+
output
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.copy_templates
|
27
|
+
FILES.each do |file|
|
28
|
+
if File.exist? "#{destination}/#{file}"
|
29
|
+
puts "* #{destination}/#{file} already exists... skipping."
|
30
|
+
else
|
31
|
+
puts "* creating #{destination}/#{file}"
|
32
|
+
FileUtils.cp "#{source}/#{file}", "#{destination}/#{file}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.team_id(org, team)
|
38
|
+
client = Octokit::Client.new :access_token => ENV["GITHUB_TOKEN"]
|
39
|
+
client.auto_paginate = true
|
40
|
+
teams = client.organization_teams org
|
41
|
+
found = teams.find { |t| t[:slug] == team }
|
42
|
+
found[:id] if found
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.env_var_set?(var)
|
46
|
+
!ENV[var].to_s.blank?
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.init_repo
|
50
|
+
execute_command "git", "init", destination
|
51
|
+
FILES.each do |file|
|
52
|
+
next if file == ".env"
|
53
|
+
execute_command("git", "add", "--", "#{destination}/#{file}")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.initial_commit
|
58
|
+
execute_command "git", "commit", "-m", "'[Bunto Auth] Initial setup'"
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.heroku_remote_set?
|
62
|
+
remotes = execute_command "git", "remote", "-v"
|
63
|
+
!!(remotes =~ %r!^heroku\s!)
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.configure_heroku(options)
|
67
|
+
VARS.each do |var|
|
68
|
+
execute_command "heroku", "config:set", "GITHUB_#{var.upcase}=#{options[var]}" if options[var]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class BuntoAuth
|
2
|
+
def self.config_file
|
3
|
+
File.join(Dir.pwd, "_config.yml")
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.config
|
7
|
+
@config ||= begin
|
8
|
+
config = YAML.safe_load_file(config_file)
|
9
|
+
config["bunto_auth"] || {}
|
10
|
+
rescue
|
11
|
+
{}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.whitelist
|
16
|
+
whitelist = BuntoAuth.config["whitelist"]
|
17
|
+
Regexp.new(whitelist.join("|")) unless whitelist.nil?
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.ssl?
|
21
|
+
!!BuntoAuth.config["ssl"]
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class BuntoAuth
|
2
|
+
module Helpers
|
3
|
+
def whitelisted?
|
4
|
+
return true if request.path_info == "/logout"
|
5
|
+
!!(BuntoAuth.whitelist && BuntoAuth.whitelist.match(request.path_info))
|
6
|
+
end
|
7
|
+
|
8
|
+
def authentication_strategy
|
9
|
+
if !ENV["GITHUB_TEAM_ID"].to_s.blank?
|
10
|
+
:team
|
11
|
+
elsif !ENV["GITHUB_TEAM_IDS"].to_s.blank?
|
12
|
+
:teams
|
13
|
+
elsif !ENV["GITHUB_ORG_NAME"].to_s.blank?
|
14
|
+
:org
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module Auth
|
3
|
+
module Github
|
4
|
+
module Helpers
|
5
|
+
# Like the native github_team_authenticate! but accepts an array of team ids
|
6
|
+
def github_teams_authenticate!(teams)
|
7
|
+
authenticate!
|
8
|
+
halt([401, "Unauthorized User"]) unless teams.any? { |team_id| github_team_access?(team_id) }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/script/bootstrap
ADDED
data/script/cibuild
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
# Test that all dependencies resolve, and that the thing actually fires
|
3
|
+
|
4
|
+
set -e
|
5
|
+
|
6
|
+
export GITHUB_CLIENT_ID=FOO
|
7
|
+
export GITHUB_CLIENT_SECRET=BAR
|
8
|
+
export GITHUB_ORG_NAME="bunto"
|
9
|
+
|
10
|
+
bundle exec rake spec
|
11
|
+
bundle exec rubocop
|
12
|
+
bundle exec gem build bunto-auth.gemspec
|
13
|
+
bundle exec bunto-auth --version
|
data/script/console
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
bundle exec pry -r "./lib/bunto-auth"
|
data/script/release
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
# Tag and push a release.
|
3
|
+
|
4
|
+
set -e
|
5
|
+
|
6
|
+
# Make sure we're in the project root.
|
7
|
+
|
8
|
+
cd $(dirname "$0")/..
|
9
|
+
|
10
|
+
# Build a new gem archive.
|
11
|
+
|
12
|
+
rm -rf bunto-auth-*.gem
|
13
|
+
gem build -q bunto-auth.gemspec
|
14
|
+
|
15
|
+
# Make sure we're on the master branch.
|
16
|
+
|
17
|
+
(git branch | grep -q '* master') || {
|
18
|
+
echo "Only release from the master branch."
|
19
|
+
exit 1
|
20
|
+
}
|
21
|
+
|
22
|
+
# Figure out what version we're releasing.
|
23
|
+
|
24
|
+
tag=v`ls bunto-auth-*.gem | sed 's/^bunto-auth-\(.*\)\.gem$/\1/'`
|
25
|
+
|
26
|
+
# Make sure we haven't released this version before.
|
27
|
+
|
28
|
+
git fetch -t origin
|
29
|
+
|
30
|
+
(git tag -l | grep -q "$tag") && {
|
31
|
+
echo "Whoops, there's already a '${tag}' tag."
|
32
|
+
exit 1
|
33
|
+
}
|
34
|
+
|
35
|
+
# Tag it and bag it.
|
36
|
+
|
37
|
+
gem push bunto-auth-*.gem && git tag "$tag" &&
|
38
|
+
git push origin master && git push origin "$tag"
|
data/script/server
ADDED
data/script/setup
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "logged in user" do
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
def app
|
7
|
+
BuntoAuth.site
|
8
|
+
end
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
setup_tmp_dir
|
12
|
+
@user = make_user("login" => "buntotest")
|
13
|
+
login_as @user
|
14
|
+
|
15
|
+
ENV["GITHUB_ORG_NAME"] = "balter-test-org"
|
16
|
+
|
17
|
+
stub_request(:get, "https://api.github.com/orgs/#{ENV["GITHUB_ORG_NAME"]}/members/buntotest")
|
18
|
+
.to_return(:status => 200)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "shows the securocat when github returns an oauth error" do
|
22
|
+
get "/auth/github/callback?error=redirect_uri_mismatch"
|
23
|
+
expect(last_response.body).to match(%r!securocat\.png!)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "logs the user out" do
|
27
|
+
get "/logout"
|
28
|
+
expect(last_response.status).to eql(302)
|
29
|
+
expect(last_response.headers["Location"]).to eql("http://example.org/")
|
30
|
+
|
31
|
+
get "/"
|
32
|
+
expect(last_response.status).to eql(302)
|
33
|
+
expect(last_response.headers["Location"]).to match(%r!^https://github\.com/login/oauth/authorize!)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "logged out user" do
|
38
|
+
include Rack::Test::Methods
|
39
|
+
|
40
|
+
def app
|
41
|
+
BuntoAuth.site
|
42
|
+
end
|
43
|
+
|
44
|
+
before do
|
45
|
+
ENV["GITHUB_ORG_NAME"] = "balter-test-org"
|
46
|
+
end
|
47
|
+
|
48
|
+
it "doesn't let you view indexes" do
|
49
|
+
get "/"
|
50
|
+
expect(last_response.status).to eql(302)
|
51
|
+
expect(last_response.headers["Location"]).to match(%r!^https://github\.com/login/oauth/authorize!)
|
52
|
+
|
53
|
+
get "/some_dir"
|
54
|
+
expect(last_response.status).to eql(302)
|
55
|
+
expect(last_response.headers["Location"]).to match(%r!^https://github\.com/login/oauth/authorize!)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "doesn't let you view files" do
|
59
|
+
get "/index.html"
|
60
|
+
expect(last_response.status).to eql(302)
|
61
|
+
expect(last_response.headers["Location"]).to match(%r!^https://github\.com/login/oauth/authorize!)
|
62
|
+
|
63
|
+
get "/some_dir/index.html"
|
64
|
+
expect(last_response.status).to eql(302)
|
65
|
+
expect(last_response.headers["Location"]).to match(%r!^https://github\.com/login/oauth/authorize!)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "refuses to serve the site without an authentication strategy" do
|
69
|
+
ENV["GITHUB_ORG_NAME"] = nil
|
70
|
+
ENV["GITHUB_TEAM_ID"] = nil
|
71
|
+
ENV["GITHUB_TEAMS_ID"] = nil
|
72
|
+
expect { get "/" }.to raise_error(BuntoAuth::ConfigError)
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "bin" do
|
4
|
+
before(:each) do
|
5
|
+
setup_tmp_dir
|
6
|
+
end
|
7
|
+
|
8
|
+
it "spits out the help do" do
|
9
|
+
env = { "GITHUB_TOKEN" => nil }
|
10
|
+
output = execute_bin(env, "--help")
|
11
|
+
expect(output).to match(%r!A simple way to use Github OAuth to serve a protected bunto site to your GitHub organization!)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "team id" do
|
15
|
+
it "errors if no token is given" do
|
16
|
+
env = { "GITHUB_TOKEN" => nil }
|
17
|
+
expect { execute_bin(env, "team_id", "--org", "balter-test-org", "--team", "1") }.to raise_error(RuntimeError)
|
18
|
+
.with_message(%r!prefix the bunto-auth command with GITHUB_TOKEN!)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "errors if no team_id or org_name is given" do
|
22
|
+
env = { "GITHUB_TOKEN" => "1234" }
|
23
|
+
expect { execute_bin(env, "team_id") }.to raise_error(RuntimeError)
|
24
|
+
.with_message(%r!An org name and team ID are required!)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "initializes a new site" do
|
29
|
+
`git init`
|
30
|
+
`git add .`
|
31
|
+
`git commit -m 'initial commit'`
|
32
|
+
execute_bin({ "RACK_ENV" => "TEST" }, "new")
|
33
|
+
expect(File).to exist("#{tmp_dir}/config.ru")
|
34
|
+
expect(File).to exist("#{tmp_dir}/Rakefile")
|
35
|
+
expect(File).to exist("#{tmp_dir}/.gitignore")
|
36
|
+
expect(File).to exist("#{tmp_dir}/.env")
|
37
|
+
end
|
38
|
+
|
39
|
+
it "builds the site" do
|
40
|
+
execute_bin({}, "build")
|
41
|
+
expect(File).to exist("#{tmp_dir}/_site/index.html")
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "bunto site" do
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
def app
|
7
|
+
BuntoAuth::BuntoSite
|
8
|
+
end
|
9
|
+
|
10
|
+
before do
|
11
|
+
setup_tmp_dir
|
12
|
+
File.write File.expand_path("_config.yml", tmp_dir), "foo: bar"
|
13
|
+
`bundle exec bunto build`
|
14
|
+
end
|
15
|
+
|
16
|
+
it "serves the index" do
|
17
|
+
get "/"
|
18
|
+
expect(last_response.body).to eql("My awesome site")
|
19
|
+
end
|
20
|
+
|
21
|
+
it "serves a page" do
|
22
|
+
get "/index.html"
|
23
|
+
expect(last_response.body).to eql("My awesome site")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "serves a directory index" do
|
27
|
+
get "/some_dir"
|
28
|
+
expect(last_response.body).to eql("My awesome directory")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "serves the default 404" do
|
32
|
+
get "/a-bad-path"
|
33
|
+
expect(last_response.status).to eql(404)
|
34
|
+
expect(last_response.body).to eql("<h1>Not Found</h1>")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "serves a custom 404" do
|
38
|
+
File.write File.expand_path("_site/404.html", tmp_dir), "My custom 404"
|
39
|
+
get "/a-bad-path"
|
40
|
+
expect(last_response.status).to eql(404)
|
41
|
+
expect(last_response.body).to eql("My custom 404")
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "commands" do
|
4
|
+
before do
|
5
|
+
setup_tmp_dir
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should find the template directory" do
|
9
|
+
expect(File.directory?(BuntoAuth::Commands.source)).to eql(true)
|
10
|
+
expect(File).to exist("#{BuntoAuth::Commands.source}/config.ru")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should know the destination directory" do
|
14
|
+
expect(BuntoAuth::Commands.destination).to eql(tmp_dir)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should execute a command" do
|
18
|
+
expect(BuntoAuth::Commands.execute_command("ls")).to match(%r!index\.html!)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should retrieve a team's ID" do
|
22
|
+
stub_request(:get, "https://api.github.com/orgs/batler-test-org/teams?per_page=100")
|
23
|
+
.to_return(:status => 204, :body => [{ :slug => "test-team", :id => 1 }])
|
24
|
+
expect(BuntoAuth::Commands.team_id("batler-test-org", "test-team")).to eql(1)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should copy the template files" do
|
28
|
+
expect(File).to_not exist("#{tmp_dir}/config.ru")
|
29
|
+
BuntoAuth::Commands.copy_templates
|
30
|
+
expect(File).to exist("#{tmp_dir}/config.ru")
|
31
|
+
expect(File).to exist("#{tmp_dir}/Rakefile")
|
32
|
+
expect(File).to exist("#{tmp_dir}/.gitignore")
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should know when a directory's changed" do
|
36
|
+
`git init`
|
37
|
+
`git add .`
|
38
|
+
`git commit -m 'initial commit'`
|
39
|
+
expect(BuntoAuth::Commands.changed?).to eql(false)
|
40
|
+
`touch config.ru`
|
41
|
+
expect(BuntoAuth::Commands.changed?).to eql(true)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "knows when env vars are set" do
|
45
|
+
var = "SOME_ENV_VAR"
|
46
|
+
|
47
|
+
ENV.delete(var)
|
48
|
+
expect(BuntoAuth::Commands.env_var_set?(var)).to eql(false)
|
49
|
+
|
50
|
+
ENV[var] = "bar"
|
51
|
+
expect(BuntoAuth::Commands.env_var_set?(var)).to eql(true)
|
52
|
+
|
53
|
+
ENV[var] = ""
|
54
|
+
expect(BuntoAuth::Commands.env_var_set?(var)).to eql(false)
|
55
|
+
|
56
|
+
ENV[var] = nil
|
57
|
+
expect(BuntoAuth::Commands.env_var_set?(var)).to eql(false)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "knows when there's a heroku remote" do
|
61
|
+
`git init`
|
62
|
+
expect(BuntoAuth::Commands.heroku_remote_set?).to eql(false)
|
63
|
+
`git remote add heroku https://example.com`
|
64
|
+
expect(BuntoAuth::Commands.heroku_remote_set?).to eql(true)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should make an initial commit" do
|
68
|
+
`git init`
|
69
|
+
`touch foo.md`
|
70
|
+
`git add foo.md`
|
71
|
+
BuntoAuth::Commands.initial_commit
|
72
|
+
output = BuntoAuth::Commands.execute_command "git", "log"
|
73
|
+
expect(output).to match(%r!\[Bunto Auth\] Initial setup!)
|
74
|
+
end
|
75
|
+
end
|