bunto-auth 2.1.0
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.
- 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
|