Skip to content
This repository has been archived by the owner on Dec 8, 2017. It is now read-only.

Add guest user capability #3

Merged
merged 10 commits into from
Dec 14, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ private_data_path: _data/private
# team member images
team_img_dir: assets/images/team
missing_team_member_img: logo-18f.jpg
guest_user_img: logo-18f.jpg
2 changes: 1 addition & 1 deletion _data/private
4 changes: 2 additions & 2 deletions _layouts/bare.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
</head>
<body itemscope itemtype="http://schema.org/WebPage">
<section class="container bare">

<!--# include virtual="{{ site.baseurl }}/auth/$REMOTE_USER/index.html" -->
{% unless site.public %}
<!--# include virtual="{{ site.baseurl }}/auth/$http_x_forwarded_email/index.html" -->{% endunless %}

<div class="bare-logo" role="banner" itemscope itemtype="http://schema.org/WPHeader">
<a href="{{ site.baseurl }}/">
Expand Down
4 changes: 4 additions & 0 deletions _layouts/guest_user_auth_include.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div class="auth-include">
<span>{{ page.user.full_name }}</span>
<img src="{{ site.baseurl }}/{{ site.team_img_dir }}/{{ site.guest_user_img }}">
</div>
6 changes: 3 additions & 3 deletions _layouts/team_member_auth_include.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="team-member-auth-include">
<a href="{{ site.baseurl }}/team/{{ page.member.name }}"><span>{{ page.member.full_name }}</span>
<img class="team-member-thumb" src="{{ site.baseurl }}/{{ page.member.image }}"></a>
<div class="auth-include">
<a href="{{ site.baseurl }}/team/{{ page.user.name }}"><span>{{ page.user.full_name }}</span>
<img src="{{ site.baseurl }}/{{ page.user.image }}"></a>
</div>
65 changes: 51 additions & 14 deletions _plugins/auth.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,70 @@ class Auth
def self.generate_artifacts(site)
return if site.config['public']
team = site.data['team'].values.select {|i| i.member? 'email'}
return if team.empty?
team.each {|i| generate_team_authentication_include(site, i)}
generate_hub_authenticated_emails(site, team)
guests = site.data['guest_users'] || []
return if team.empty? and guests.empty?

groups = {
team => 'team_member_auth_include.html',
guests => 'guest_user_auth_include.html',
}

groups.each do |group, layout|
group.each do |user|
generate_user_authentication_include(site, user, layout)
end
end

generate_hub_authenticated_emails(site, team, guests)
end

private

# Generates the upper-right-corner divs used to identify the authenticated
# user. The divs are imported via a Server Side Include directive in
# _layouts/bare.html.
# user as html snippets under +_site/auth+, one snippet per authenticated
# user email address. The snippets are imported via a Server Side Include
# directive in +_layouts/bare.html+.
#
# In the specific case of the internal 18F Hub, the +google_auth_proxy+ is
# configured with +pass_basic_auth = true+, which passes the authenticated
# username as part of the HTTP Basic Auth +Authorization: Basic+ header,
# as well as the +X-Forwarded-User+ and +X-Forwarded-Email headers+:
# http://word.bitly.com/post/47548678256/google-auth-proxy
# See the +p.SetBasicAuth+ block in +ServeHTTP()+ from:
# https://github.com/bitly/google_auth_proxy/blob/master/oauthproxy.go
#
# Nginx, in turn, uses this information to set the values of the
# embedded variables +$remote_user+, +$http_x_forwarded_user+, and
# +$http_x_forwarded_email+, which are available to the SSI engine:
# http://nginx.com/resources/admin-guide/web-server/ (Variables section)
# http://nginx.org/en/docs/http/ngx_http_core_module.html#var_remote_user
# http://nginx.org/en/docs/http/ngx_http_core_module.html#var_http_
#
# More info on HTTP Basic Auth and the +$REMOTE_USER+ CGI variable:
# http://tools.ietf.org/html/rfc3875#section-4.1.11
# http://tools.ietf.org/html/rfc2617#section-2
#
# +site+:: Jekyll site object
# +member+:: team member hash
def self.generate_team_authentication_include(site, member)
username = member['email'].sub(/@.+$/, '')
page = Page.new(site, File.join('auth', username), 'index.html',
'team_member_auth_include.html',
"#{member['full_name']} Authentication Include")
page.data['member'] = member
# +user+:: user hash
# +layout+:: determines the layout of the HTML snippet
def self.generate_user_authentication_include(site, user, layout)
page = Page.new(site, File.join('auth', user['email']), 'index.html',
layout, "#{user['full_name']} Authentication Include")
page.data['user'] = user
site.pages << page
end

# Generates the list of email addresses permitted to access the Hub after
# passing through the google_auth_proxy. See deploy/README.md for details.
# +site+:: Jekyll site object
# +team+:: array of team member hashes
def self.generate_hub_authenticated_emails(site, team)
# +guests+ array of guest user hashes
def self.generate_hub_authenticated_emails(site, team, guests)
page = Page.new(site, 'auth', 'hub-authenticated-emails.txt',
'hub-authenticated-emails.txt', 'Authenticated Emails')
page.data['addrs'] = team.map {|i| i['email']}.sort!
page.data['addrs'] = team.map {|i| i['email']}
page.data['addrs'].concat(guests.map {|i| i['email']})
page.data['addrs'].sort!
site.pages << page
end
end
Expand Down
11 changes: 11 additions & 0 deletions _plugins/joiner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def self.join_data(site)

impl.join_snippet_data
impl.join_project_status
impl.import_guest_users
impl.filter_private_pages

site.data.delete 'public'
Expand Down Expand Up @@ -283,6 +284,16 @@ def join_project_status
@data['private'].delete 'project_status'
end

# Imports the guest_users list into the top-level site.data object.
def import_guest_users
private_data = site.data['private'] || {}
hub_data = private_data['hub'] || {}
if hub_data.member? 'guest_users'
site.data['guest_users'] = site.data['private']['hub']['guest_users']
site.data['private']['hub'].delete 'guest_users'
end
end

# Filters out private pages when generating the public Hub.
def filter_private_pages
if @public_mode
Expand Down
112 changes: 112 additions & 0 deletions _test/auth_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
require_relative "../_plugins/auth"
require_relative "page"

require "jekyll"
require "jekyll/site"
require "minitest/autorun"

module Hub
class AuthTest < ::Minitest::Test
def setup
@site = ::Jekyll::Site.new ::Jekyll::Configuration::DEFAULTS

@team = {
'mbland' => {
'email' => 'michael.bland@gsa.gov',
'full_name' => 'Mike Bland'},
'foobar' => {
'email' => 'foo.bar@notgsa.gov',
'full_name' => 'Foo Bar'},
}

@guests = [
{'email' => 'mbland@acm.org',
'full_name' => 'Mike Bland'},
{'email' => 'baz.quux@notgsa.gov',
'full_name' => 'Baz Quux'},
]
end

def auth_include_pages
unless @site.pages.empty?
@site.pages[0..-2]
else
[]
end
end

def auth_include_users
auth_include_pages.map {|i| i['user']}
end

def auth_include_files
auth_include_pages.map {|i| File.join(i.dir, i.name)}
end

def auth_emails
unless @site.pages.empty?
@site.pages.last['addrs']
else
[]
end
end

def user_include_files(user_ids)
user_ids.map {|i| File.join('', 'auth', i, 'index.html')}
end

def test_empty_team_and_guests
@site.data['team'] = {}
@site.data['guest_users'] = []
Auth.generate_artifacts @site

assert_empty auth_include_pages
assert_empty auth_emails
end

def test_no_artifacts_in_public_mode
@site.data['team'] = @team
@site.data['guest_users'] = @guests
@site.config['public'] = true
Auth.generate_artifacts @site

assert_empty auth_include_pages
assert_empty auth_emails
end

def test_team_artifacts_generated
@site.data['team'] = @team
Auth.generate_artifacts @site
assert_equal([@team['mbland'], @team['foobar']], auth_include_users)

expected_emails = ['michael.bland@gsa.gov', 'foo.bar@notgsa.gov']
assert_equal(user_include_files(expected_emails), auth_include_files)
assert_equal(expected_emails.sort!, auth_emails)
end

def test_guest_artifacts_generated
@site.data['team'] = {}
@site.data['guest_users'] = @guests
Auth.generate_artifacts @site
assert_equal([@guests[0], @guests[1]], auth_include_users)

expected_emails = ['mbland@acm.org', 'baz.quux@notgsa.gov']
assert_equal(user_include_files(expected_emails), auth_include_files)
assert_equal(expected_emails.sort!, auth_emails)
end

def test_team_and_guest_artifacts_generated
@site.data['team'] = @team
@site.data['guest_users'] = @guests
Auth.generate_artifacts @site
assert_equal([@team['mbland'], @team['foobar'], @guests[0], @guests[1]],
auth_include_users)

expected_emails = [
'michael.bland@gsa.gov','foo.bar@notgsa.gov',
'mbland@acm.org', 'baz.quux@notgsa.gov']
assert_equal(user_include_files(expected_emails), auth_include_files)
assert_equal(expected_emails.sort!, auth_emails)
end
end
end
45 changes: 35 additions & 10 deletions _test/joiner_test.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,45 @@
require_relative "../_plugins/joiner"
require_relative "page"

require "jekyll"
require "jekyll/page"
require "jekyll/site"
require_relative "../_plugins/joiner"
require "test/unit"
require "minitest/autorun"

module Hub
class DummyTestPage < ::Jekyll::Page
def initialize(site, dir, filename)
@site = site
@base = 'fake_test_basedir'
@dir = dir
@name = filename
class ImportGuestUsers < ::Minitest::Test
def setup
@site = ::Jekyll::Site.new ::Jekyll::Configuration::DEFAULTS
end

def test_no_private_data
assert_nil JoinerImpl.new(@site).import_guest_users
end

def test_no_hub_data
@site.data['private'] = {}
assert_nil JoinerImpl.new(@site).import_guest_users
assert_nil @site.data['guest_users']
end

def test_no_guest_users
@site.data['private'] = {'hub' => {}}
assert_nil JoinerImpl.new(@site).import_guest_users
assert_nil @site.data['guest_users']
end

def test_guest_users_moved_to_top_level
guests = [
{'email' => 'michael.bland@gsa.gov',
'full_name' => 'Mike Bland'},
]
@site.data['private'] = {'hub' => {'guest_users' => guests}}
assert_equal guests, JoinerImpl.new(@site).import_guest_users
assert_equal guests, @site.data['guest_users']
assert_nil @site.data['private']['hub']['guest_users']
end
end

class TestFilterPrivatePages < ::Test::Unit::TestCase
class FilterPrivatePagesTest < ::Minitest::Test
def setup
@site = ::Jekyll::Site.new ::Jekyll::Configuration::DEFAULTS
@all_page_names = []
Expand Down
13 changes: 13 additions & 0 deletions _test/page.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require "jekyll"
require "jekyll/page"

module Hub
class DummyTestPage < ::Jekyll::Page
def initialize(site, dir, filename)
@site = site
@base = 'fake_test_basedir'
@dir = dir
@name = filename
end
end
end
5 changes: 4 additions & 1 deletion assets/_sass/_hub.scss
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
margin-right: 5px;
}

.team-member-auth-include {
.auth-include {
position: absolute;
top: 0;
right: 0;
Expand All @@ -86,5 +86,8 @@
}
img {
vertical-align: middle;
width: 50px;
height: auto;
margin-right: 5px;
}
}