jekyll-auth 0.6.1 → 1.0.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 +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +15 -0
- data/Gemfile +3 -0
- data/README.md +149 -0
- data/Rakefile +18 -7
- data/bin/jekyll-auth +97 -109
- data/jekyll-auth.gemspec +31 -0
- data/lib/jekyll-auth.rb +31 -6
- data/lib/jekyll_auth/auth_site.rb +47 -0
- data/lib/jekyll_auth/commands.rb +73 -0
- data/lib/jekyll_auth/config_error.rb +8 -0
- data/lib/jekyll_auth/helpers.rb +18 -0
- data/lib/jekyll_auth/jekyll_site.rb +14 -0
- data/lib/jekyll_auth/sinatra/auth/github.rb +11 -0
- data/lib/{jekyll-auth → jekyll_auth}/version.rb +1 -1
- data/script/bootstrap +7 -0
- data/script/cibuild +8 -0
- data/script/console +1 -0
- data/script/release +38 -0
- data/script/server +3 -0
- data/script/setup +5 -0
- data/spec/jekyll_auth_auth_site_spec.rb +76 -0
- data/spec/jekyll_auth_bin_spec.rb +44 -0
- data/spec/jekyll_auth_commands_spec.rb +76 -0
- data/spec/jekyll_auth_helpers_spec.rb +62 -0
- data/spec/jekyll_auth_jekyll_site_spec.rb +44 -0
- data/spec/jekyll_auth_spec.rb +47 -0
- data/spec/spec_helper.rb +60 -0
- data/templates/.gitignore +3 -0
- data/templates/Rakefile +9 -0
- data/{config.ru → templates/config.ru} +0 -0
- data/templates/index.html +19 -0
- metadata +126 -25
- data/lib/jekyll-auth/auth-site.rb +0 -47
- data/lib/jekyll-auth/config.rb +0 -28
- data/lib/jekyll-auth/jekyll-site.rb +0 -7
data/jekyll-auth.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require './lib/jekyll_auth/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "jekyll-auth"
|
5
|
+
s.version = JekyllAuth::VERSION
|
6
|
+
s.summary = "A simple way to use Github OAuth to serve a protected jekyll site to your GitHub organization"
|
7
|
+
s.description = "A simple way to use Github Oauth to serve a protected jekyll site to your GitHub organization."
|
8
|
+
s.authors = "Ben Balter"
|
9
|
+
s.email = "ben@balter.com"
|
10
|
+
s.homepage = "https://github.com/benbalter/jekyll-auth"
|
11
|
+
s.license = "MIT"
|
12
|
+
s.files = `git ls-files`.split("\n")
|
13
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
15
|
+
s.require_paths = ["lib"]
|
16
|
+
|
17
|
+
s.add_dependency "jekyll", "~> 2.0"
|
18
|
+
s.add_dependency "sinatra-index", "~> 0.0"
|
19
|
+
s.add_dependency "sinatra_auth_github", "~> 1.1"
|
20
|
+
s.add_dependency "rack", "1.5.2"
|
21
|
+
s.add_dependency "dotenv", "~> 1.0"
|
22
|
+
s.add_dependency "rake", "~> 10.3"
|
23
|
+
s.add_dependency "rack-ssl-enforcer", "~> 0.2"
|
24
|
+
s.add_dependency "mercenary", "~> 0.3"
|
25
|
+
s.add_dependency 'safe_yaml', "~> 1.0"
|
26
|
+
s.add_dependency "colorator", "~> 0.1"
|
27
|
+
s.add_development_dependency "rspec", "~> 3.1"
|
28
|
+
s.add_development_dependency "rack-test", "~> 0.6"
|
29
|
+
s.add_development_dependency "webmock", "~> 1.2 "
|
30
|
+
s.add_development_dependency "pry", "~> 0.10"
|
31
|
+
end
|
data/lib/jekyll-auth.rb
CHANGED
@@ -1,13 +1,16 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require 'sinatra-index'
|
3
2
|
require 'sinatra_auth_github'
|
4
|
-
require 'rack'
|
5
3
|
require 'dotenv'
|
6
4
|
require 'safe_yaml'
|
7
|
-
require
|
8
|
-
require
|
9
|
-
|
10
|
-
|
5
|
+
require 'colorator'
|
6
|
+
require 'mkmf'
|
7
|
+
require_relative 'jekyll_auth/version'
|
8
|
+
require_relative 'jekyll_auth/helpers'
|
9
|
+
require_relative 'jekyll_auth/auth_site'
|
10
|
+
require_relative 'jekyll_auth/jekyll_site'
|
11
|
+
require_relative 'jekyll_auth/config_error'
|
12
|
+
require_relative 'jekyll_auth/commands'
|
13
|
+
|
11
14
|
Dotenv.load
|
12
15
|
|
13
16
|
class JekyllAuth
|
@@ -17,4 +20,26 @@ class JekyllAuth
|
|
17
20
|
run JekyllAuth::JekyllSite
|
18
21
|
end
|
19
22
|
end
|
23
|
+
|
24
|
+
def self.config_file
|
25
|
+
File.join(Dir.pwd, "_config.yml")
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.config
|
29
|
+
@config ||= begin
|
30
|
+
config = YAML.safe_load_file(config_file)
|
31
|
+
config["jekyll_auth"] || {}
|
32
|
+
rescue
|
33
|
+
{}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.whitelist
|
38
|
+
whitelist = JekyllAuth::config["whitelist"]
|
39
|
+
Regexp.new(whitelist.join("|")) unless whitelist.nil?
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.ssl?
|
43
|
+
!!JekyllAuth::config["ssl"]
|
44
|
+
end
|
20
45
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class JekyllAuth
|
2
|
+
class AuthSite < Sinatra::Base
|
3
|
+
|
4
|
+
configure :production do
|
5
|
+
require 'rack-ssl-enforcer'
|
6
|
+
use Rack::SslEnforcer if JekyllAuth.ssl?
|
7
|
+
end
|
8
|
+
|
9
|
+
use Rack::Session::Cookie, {
|
10
|
+
:http_only => true,
|
11
|
+
:secret => ENV['SESSION_SECRET'] || SecureRandom.hex
|
12
|
+
}
|
13
|
+
|
14
|
+
set :github_options, {
|
15
|
+
:scopes => 'read:org'
|
16
|
+
}
|
17
|
+
|
18
|
+
ENV['WARDEN_GITHUB_VERIFIER_SECRET'] ||= SecureRandom.hex
|
19
|
+
register Sinatra::Auth::Github
|
20
|
+
|
21
|
+
use Rack::Logger
|
22
|
+
|
23
|
+
include JekyllAuth::Helpers
|
24
|
+
|
25
|
+
before do
|
26
|
+
pass if whitelisted?
|
27
|
+
|
28
|
+
logger.info "Authentication strategy: #{authentication_strategy}"
|
29
|
+
|
30
|
+
case authentication_strategy
|
31
|
+
when :team
|
32
|
+
github_team_authenticate! ENV['GITHUB_TEAM_ID']
|
33
|
+
when :teams
|
34
|
+
github_teams_authenticate! ENV['GITHUB_TEAM_IDS'].split(",")
|
35
|
+
when :org
|
36
|
+
github_organization_authenticate! ENV['GITHUB_ORG_ID']
|
37
|
+
else
|
38
|
+
raise JekyllAuth::ConfigError
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
get '/logout' do
|
43
|
+
logout!
|
44
|
+
redirect '/'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
class JekyllAuth
|
2
|
+
class Commands
|
3
|
+
|
4
|
+
FILES = %w{Rakefile config.ru .gitignore .env}
|
5
|
+
VARS = %w{client_id client_secret team_id org_id}
|
6
|
+
|
7
|
+
def self.source
|
8
|
+
@source ||= File.expand_path( "../../templates", File.dirname(__FILE__) )
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.destination
|
12
|
+
@destination ||= Dir.pwd
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.changed?
|
16
|
+
execute_command("git", "status", destination, "--porcelain").length != 0
|
17
|
+
rescue
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.execute_command(*args)
|
22
|
+
output, status = Open3.capture2e(*args)
|
23
|
+
raise "Command `#{args.join(" ")}` failed: #{output}" if status != 0
|
24
|
+
output
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.copy_templates
|
28
|
+
FILES.each do |file|
|
29
|
+
if File.exist? "#{destination}/#{file}"
|
30
|
+
puts "* #{destination}/#{file} already exists... skipping."
|
31
|
+
else
|
32
|
+
puts "* creating #{destination}/#{file}"
|
33
|
+
FileUtils.cp "#{source}/#{file}", "#{destination}/#{file}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.team_id(org, team)
|
39
|
+
client = Octokit::Client.new :access_token => ENV["GITHUB_TOKEN"]
|
40
|
+
client.auto_paginate = true
|
41
|
+
teams = client.organization_teams org
|
42
|
+
found = teams.find { |t| t[:slug] == team }
|
43
|
+
found[:id] if found
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.env_var_set?(var)
|
47
|
+
!(ENV[var].to_s.blank?)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.init_repo
|
51
|
+
execute_command "git", "init", destination
|
52
|
+
FILES.each do |file|
|
53
|
+
next if file == ".env"
|
54
|
+
execute_command("git", "add", "--", "#{destination}/#{file}")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.initial_commit
|
59
|
+
execute_command "git", "commit", "-m", "'[Jekyll Auth] Initial setup'"
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.heroku_remote_set?
|
63
|
+
remotes = execute_command "git", "remote", "-v"
|
64
|
+
!!(remotes =~ /^heroku\s/)
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.configure_heroku(options)
|
68
|
+
VARS.each do |var|
|
69
|
+
execute_command "heroku", "config:set", "GITHUB_#{var.upcase}=#{options[var]}" if options[var]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class JekyllAuth
|
2
|
+
module Helpers
|
3
|
+
def whitelisted?
|
4
|
+
return true if request.path_info == "/logout"
|
5
|
+
!!(JekyllAuth.whitelist && JekyllAuth.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_ID'].to_s.blank?
|
14
|
+
:org
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class JekyllAuth
|
2
|
+
class JekyllSite < Sinatra::Base
|
3
|
+
|
4
|
+
register Sinatra::Index
|
5
|
+
set :public_folder, File.expand_path('_site', Dir.pwd)
|
6
|
+
use_static_index 'index.html'
|
7
|
+
|
8
|
+
not_found do
|
9
|
+
status 404
|
10
|
+
four_oh_four = File.expand_path('_site/404.html', Dir.pwd)
|
11
|
+
File.read(four_oh_four) if File.exists?(four_oh_four)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module Auth
|
3
|
+
module Github
|
4
|
+
# Like the native github_team_authenticate! but accepts an array of team ids
|
5
|
+
def github_teams_authenticate!(teams)
|
6
|
+
authenticate!
|
7
|
+
halt([401, "Unauthorized User"]) unless teams.any? { |team_id| github_team_access?(team_id) }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/script/bootstrap
ADDED
data/script/cibuild
ADDED
data/script/console
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
bundle exec pry -r "./lib/jekyll-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 jekyll-auth-*.gem
|
13
|
+
gem build -q jekyll-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 jekyll-auth-*.gem | sed 's/^jekyll-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 jekyll-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,76 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "logged in user" do
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
def app
|
7
|
+
JekyllAuth.site
|
8
|
+
end
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
setup_tmp_dir
|
12
|
+
@user = make_user('login' => 'benbaltertest')
|
13
|
+
login_as @user
|
14
|
+
|
15
|
+
ENV['GITHUB_ORG_ID'] = "balter-test-org"
|
16
|
+
|
17
|
+
stub_request(:get, "https://api.github.com/orgs/#{ENV["GITHUB_ORG_ID"]}/members/benbaltertest").
|
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
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "logged out user" do
|
39
|
+
|
40
|
+
include Rack::Test::Methods
|
41
|
+
|
42
|
+
def app
|
43
|
+
JekyllAuth.site
|
44
|
+
end
|
45
|
+
|
46
|
+
before do
|
47
|
+
ENV['GITHUB_ORG_ID'] = "balter-test-org"
|
48
|
+
end
|
49
|
+
|
50
|
+
it "doesn't let you view indexes" do
|
51
|
+
get "/"
|
52
|
+
expect(last_response.status).to eql(302)
|
53
|
+
expect(last_response.headers['Location']).to match(%r{^https://github\.com/login/oauth/authorize})
|
54
|
+
|
55
|
+
get "/some_dir"
|
56
|
+
expect(last_response.status).to eql(302)
|
57
|
+
expect(last_response.headers['Location']).to match(%r{^https://github\.com/login/oauth/authorize})
|
58
|
+
end
|
59
|
+
|
60
|
+
it "doesn't let you view files" do
|
61
|
+
get "/index.html"
|
62
|
+
expect(last_response.status).to eql(302)
|
63
|
+
expect(last_response.headers['Location']).to match(%r{^https://github\.com/login/oauth/authorize})
|
64
|
+
|
65
|
+
get "/some_dir/index.html"
|
66
|
+
expect(last_response.status).to eql(302)
|
67
|
+
expect(last_response.headers['Location']).to match(%r{^https://github\.com/login/oauth/authorize})
|
68
|
+
end
|
69
|
+
|
70
|
+
it "refuses to serve the site without an authentication strategy" do
|
71
|
+
ENV["GITHUB_ORG_ID"] = nil
|
72
|
+
ENV["GITHUB_TEAM_ID"] = nil
|
73
|
+
ENV["GITHUB_TEAMS_ID"] = nil
|
74
|
+
expect{get "/"}.to raise_error(JekyllAuth::ConfigError)
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,44 @@
|
|
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 jekyll site to your GitHub organization})
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "team id" do
|
15
|
+
|
16
|
+
it "errors if no token is given" do
|
17
|
+
env = { "GITHUB_TOKEN" => nil}
|
18
|
+
expect{execute_bin(env, "team_id", "--org", "balter-test-org", "--team", "1")}.to raise_error(RuntimeError).
|
19
|
+
with_message(/prefix the jekyll-auth command with GITHUB_TOKEN/)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "errors if no team_id or org_id is given" do
|
23
|
+
env = { "GITHUB_TOKEN" => "1234"}
|
24
|
+
expect{execute_bin(env, "team_id")}.to raise_error(RuntimeError).
|
25
|
+
with_message(/An org name and team ID are required/)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it "initializes a new site" do
|
30
|
+
`git init`
|
31
|
+
`git add .`
|
32
|
+
`git commit -m 'initial commit'`
|
33
|
+
execute_bin({"RACK_ENV" => "TEST"}, "new")
|
34
|
+
expect(File).to exist("#{tmp_dir}/config.ru")
|
35
|
+
expect(File).to exist("#{tmp_dir}/Rakefile")
|
36
|
+
expect(File).to exist("#{tmp_dir}/.gitignore")
|
37
|
+
expect(File).to exist("#{tmp_dir}/.env")
|
38
|
+
end
|
39
|
+
|
40
|
+
it "builds the site" do
|
41
|
+
execute_bin({}, "build")
|
42
|
+
expect(File).to exist("#{tmp_dir}/_site/index.html")
|
43
|
+
end
|
44
|
+
end
|