bunto-auth 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,7 @@
1
+ class BuntoAuth
2
+ class ConfigError < RuntimeError
3
+ def message
4
+ "Bunto Auth is refusing to serve your site because your oauth credentials are not properly configured."
5
+ end
6
+ end
7
+ 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
@@ -0,0 +1,3 @@
1
+ class BuntoAuth
2
+ VERSION = "2.1.0".freeze
3
+ end
@@ -0,0 +1,7 @@
1
+ #!/bin/sh
2
+
3
+ set -e
4
+
5
+ echo "Let's get set up here..."
6
+ bundle install
7
+ echo "Boom."
@@ -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
@@ -0,0 +1 @@
1
+ bundle exec pry -r "./lib/bunto-auth"
@@ -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"
@@ -0,0 +1,3 @@
1
+ #! /bin/sh
2
+
3
+ bundle exec rake site
@@ -0,0 +1,5 @@
1
+ #!/bin/sh
2
+
3
+ set -e
4
+
5
+ bundle exec bunto-auth setup
@@ -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