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.
@@ -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