jekyll-auth 0.6.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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 File.dirname(__FILE__) + '/jekyll-auth/version'
8
- require File.dirname(__FILE__) + '/jekyll-auth/config'
9
- require File.dirname(__FILE__) + '/jekyll-auth/auth-site'
10
- require File.dirname(__FILE__) + '/jekyll-auth/jekyll-site'
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,8 @@
1
+ class JekyllAuth
2
+ class ConfigError < SecurityError
3
+
4
+ def message
5
+ "Jekyll Auth is refusing to serve your site because your oauth credentials are not properly configured."
6
+ end
7
+ end
8
+ 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
@@ -1,3 +1,3 @@
1
1
  class JekyllAuth
2
- VERSION = '0.6.1'
2
+ VERSION = '1.0.0'
3
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,8 @@
1
+ #!/bin/sh
2
+ # Test that all dependencies resolve, and that the thing actually fires
3
+
4
+ set -e
5
+
6
+ bundle exec rake spec
7
+ bundle exec gem build jekyll-auth.gemspec
8
+ bundle exec jekyll-auth --version
@@ -0,0 +1 @@
1
+ bundle exec pry -r "./lib/jekyll-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 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"
@@ -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 jekyll-auth setup
@@ -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