Skip to content

Commit 16cbbcf

Browse files
committed
Added initial version
1 parent 3aeaa41 commit 16cbbcf

29 files changed

+969
-1
lines changed

.gitignore

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/.bundle/
2+
/.yardoc
3+
/_yardoc/
4+
/coverage/
5+
/doc/
6+
/pkg/
7+
/spec/reports/
8+
/tmp/
9+
10+
# rspec failure tracking
11+
.rspec_status
12+
.rubocop-https*

.rspec

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
--format documentation
2+
--color
3+
--require spec_helper

.rubocop.yml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
inherit_from:
2+
- https://raw.githubusercontent.com/bloomon/style-guide/v0.1.0/ruby/rubocop.yml
3+
4+
Rails:
5+
Enabled: true
6+
7+
AllCops:
8+
TargetRubyVersion: 2.4

Gemfile

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# frozen_string_literal: true
2+
3+
source "https://rubygems.org"
4+
5+
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6+
7+
# Specify your gem's dependencies in gcp_iap_warden.gemspec
8+
gemspec

Gemfile.lock

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
PATH
2+
remote: .
3+
specs:
4+
gcp_iap_warden (0.1.0)
5+
jwt (~> 2.1.0)
6+
warden (~> 1.2.0)
7+
8+
GEM
9+
remote: https://rubygems.org/
10+
specs:
11+
abstract_type (0.0.7)
12+
adamantium (0.2.0)
13+
ice_nine (~> 0.11.0)
14+
memoizable (~> 0.4.0)
15+
addressable (2.5.2)
16+
public_suffix (>= 2.0.2, < 4.0)
17+
ast (2.3.0)
18+
binding_of_caller (0.8.0)
19+
debug_inspector (>= 0.0.1)
20+
coderay (1.1.2)
21+
concord (0.1.5)
22+
adamantium (~> 0.2.0)
23+
equalizer (~> 0.0.9)
24+
crack (0.4.3)
25+
safe_yaml (~> 1.0.0)
26+
debug_inspector (0.0.3)
27+
diff-lcs (1.3)
28+
equalizer (0.0.11)
29+
hashdiff (0.3.7)
30+
ice_nine (0.11.2)
31+
jwt (2.1.0)
32+
memoizable (0.4.2)
33+
thread_safe (~> 0.3, >= 0.3.1)
34+
method_source (0.9.0)
35+
parallel (1.12.1)
36+
parser (2.4.0.2)
37+
ast (~> 2.3)
38+
powerpack (0.1.1)
39+
proc_to_ast (0.1.0)
40+
coderay
41+
parser
42+
unparser
43+
procto (0.0.3)
44+
pry (0.11.3)
45+
coderay (~> 1.1.0)
46+
method_source (~> 0.9.0)
47+
public_suffix (3.0.1)
48+
rack (2.0.3)
49+
rack-test (0.8.2)
50+
rack (>= 1.0, < 3)
51+
rainbow (3.0.0)
52+
rake (10.5.0)
53+
rspec (3.7.0)
54+
rspec-core (~> 3.7.0)
55+
rspec-expectations (~> 3.7.0)
56+
rspec-mocks (~> 3.7.0)
57+
rspec-core (3.7.1)
58+
rspec-support (~> 3.7.0)
59+
rspec-expectations (3.7.0)
60+
diff-lcs (>= 1.2.0, < 2.0)
61+
rspec-support (~> 3.7.0)
62+
rspec-its (1.2.0)
63+
rspec-core (>= 3.0.0)
64+
rspec-expectations (>= 3.0.0)
65+
rspec-mocks (3.7.0)
66+
diff-lcs (>= 1.2.0, < 2.0)
67+
rspec-support (~> 3.7.0)
68+
rspec-parameterized (0.4.0)
69+
binding_of_caller
70+
parser
71+
proc_to_ast
72+
rspec (>= 2.13, < 4)
73+
unparser
74+
rspec-support (3.7.0)
75+
rubocop (0.52.1)
76+
parallel (~> 1.10)
77+
parser (>= 2.4.0.2, < 3.0)
78+
powerpack (~> 0.1)
79+
rainbow (>= 2.2.2, < 4.0)
80+
ruby-progressbar (~> 1.7)
81+
unicode-display_width (~> 1.0, >= 1.0.1)
82+
ruby-progressbar (1.9.0)
83+
safe_yaml (1.0.4)
84+
thread_safe (0.3.6)
85+
timecop (0.9.1)
86+
unicode-display_width (1.3.0)
87+
unparser (0.2.6)
88+
abstract_type (~> 0.0.7)
89+
adamantium (~> 0.2.0)
90+
concord (~> 0.1.5)
91+
diff-lcs (~> 1.3)
92+
equalizer (~> 0.0.9)
93+
parser (>= 2.3.1.2, < 2.5)
94+
procto (~> 0.0.2)
95+
vcr (4.0.0)
96+
warden (1.2.7)
97+
rack (>= 1.0)
98+
webmock (3.3.0)
99+
addressable (>= 2.3.6)
100+
crack (>= 0.3.2)
101+
hashdiff
102+
103+
PLATFORMS
104+
ruby
105+
106+
DEPENDENCIES
107+
bundler (~> 1.16)
108+
gcp_iap_warden!
109+
pry (~> 0.11.3)
110+
rack-test (~> 0.8.0)
111+
rake (~> 10.0)
112+
rspec (~> 3.0)
113+
rspec-its (~> 1.2.0)
114+
rspec-parameterized (~> 0.4.0)
115+
rubocop (~> 0.52.0)
116+
timecop (~> 0.9.0)
117+
vcr (~> 4.0.0)
118+
webmock (~> 3.3.0)
119+
120+
BUNDLED WITH
121+
1.16.1

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2018
3+
Copyright (c) 2018 Bloomon
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# GCP IAP Warden
2+
3+
Google Cloud [Cloud Identity-Aware Proxy](https://cloud.google.com/iap/)
4+
strategies for [Warden](https://github.com/hassox/warden)
5+
6+
## Usage
7+
8+
Below is just an example for ussage with rails.
9+
But you can easily reuse the code for you rack based app.
10+
11+
Read more about Warden [here](https://github.com/hassox/warden/wiki)
12+
13+
You may have use different strategies:
14+
`gcp_iap_google_jwt_header` or `gcp_iap_google_header`
15+
16+
Recommended is `gcp_iap_google_jwt_header` read more [here](https://cloud.google.com/iap/docs/signed-headers-howto)
17+
18+
Initialize the warden with something like
19+
20+
```
21+
# ./config/initializers/warden.rb
22+
23+
require "gcp_iap_warden"
24+
25+
GcpIapWarden::Strategy::GoogleJWTHeader.config(
26+
project: ENV.fetch("GCP_PROJECT_ID"),
27+
backend: ENV.fetch("GCP_BACKEND_ID")
28+
)
29+
30+
Rails.application.config.middleware.insert_after(
31+
ActionDispatch::Session::CookieStore, Warden::Manager
32+
) do |manager|
33+
manager.default_strategies :gcp_iap_google_jwt_header
34+
manager.failure_app = UnauthorizedController
35+
end
36+
```
37+
38+
Your `UnauthorizedController` may look like
39+
40+
```
41+
# app/controllers/unauthorized_controller.rb
42+
43+
class UnauthorizedController < ActionController::Metal
44+
def self.call(env)
45+
env["warden"].errors.each do |message|
46+
Rails.logger.warn("[unauthorized] reason: #{message}")
47+
end
48+
@respond ||= action(:respond)
49+
@respond.call(env)
50+
end
51+
52+
def respond
53+
self.response_body = "Unauthorized Action"
54+
self.status = :unauthorized
55+
end
56+
end
57+
```
58+
59+
## Development
60+
61+
Setup and run tests
62+
63+
```
64+
docker-compose run --rm app ./bin/setup
65+
```

Rakefile

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# frozen_string_literal: true
2+
3+
require "bundler/gem_tasks"
4+
require "rspec/core/rake_task"
5+
6+
RSpec::Core::RakeTask.new(:spec)
7+
8+
task default: :spec

bin/console

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/env ruby
2+
# frozen_string_literal: true
3+
4+
require "bundler/setup"
5+
require "gcp_iap_warden"
6+
7+
require "pry"
8+
Pry.start

bin/setup

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
IFS=$'\n\t'
4+
set -vx
5+
6+
bundle check || bundle install
7+
bundle exec rspec
8+
bundle exec rubocop

docker-compose.yml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
version: '2'
2+
services:
3+
app:
4+
image: ruby:2.4
5+
working_dir: /workdir
6+
environment:
7+
BUNDLE_PATH: /workdir/.bundle
8+
command: ./bin/setup
9+
volumes:
10+
- .:/workdir

gcp_iap_warden.gemspec

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# frozen_string_literal: true
2+
3+
lib = File.expand_path("../lib", __FILE__)
4+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5+
require "gcp_iap_warden/version"
6+
7+
Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
8+
spec.name = "gcp_iap_warden"
9+
spec.version = GcpIapWarden::VERSION
10+
spec.authors = ["Max Shytikov"]
11+
spec.email = ["mshytikov@gmail.com"]
12+
13+
spec.summary = "GCP Cloud IAP strategy for Warden"
14+
spec.description = "GCP Cloud IAP strategy for Warden"
15+
16+
# Prevent pushing this gem to RubyGems.org.
17+
# To allow pushes either set the 'allowed_push_host'
18+
# to allow pushing to a single host or delete this section
19+
# to allow pushing to any host.
20+
if spec.respond_to?(:metadata)
21+
spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
22+
else
23+
raise "RubyGems 2.0 or newer is required to protect against " \
24+
"public gem pushes."
25+
end
26+
27+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
28+
f.match(%r{^(test|spec|features)/})
29+
end
30+
spec.bindir = "exe"
31+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
32+
spec.require_paths = ["lib"]
33+
34+
spec.add_dependency "jwt", "~> 2.1.0"
35+
spec.add_dependency "warden", "~> 1.2.0"
36+
37+
spec.add_development_dependency "bundler", "~> 1.16"
38+
spec.add_development_dependency "pry", "~> 0.11.3"
39+
spec.add_development_dependency "rack-test", "~> 0.8.0"
40+
spec.add_development_dependency "rake", "~> 10.0"
41+
spec.add_development_dependency "rspec", "~> 3.0"
42+
spec.add_development_dependency "rspec-its", "~> 1.2.0"
43+
spec.add_development_dependency "rspec-parameterized", "~> 0.4.0"
44+
spec.add_development_dependency "rubocop", "~> 0.52.0"
45+
spec.add_development_dependency "timecop", "~> 0.9.0"
46+
spec.add_development_dependency "vcr", "~> 4.0.0"
47+
spec.add_development_dependency "webmock", "~> 3.3.0"
48+
end

lib/gcp_iap_warden.rb

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# frozen_string_literal: true
2+
3+
require "warden"
4+
5+
require "gcp_iap_warden/version"
6+
7+
module GcpIapWarden
8+
require_relative "gcp_iap_warden/utils"
9+
require_relative "gcp_iap_warden/key_store"
10+
require_relative "gcp_iap_warden/strategy"
11+
end

lib/gcp_iap_warden/key_store.rb

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# frozen_string_literal: true
2+
3+
require "open-uri"
4+
5+
module GcpIapWarden
6+
class KeyStore
7+
GOOGLE_PUBLIC_KEY_URL = "https://www.gstatic.com/iap/verify/public_key"
8+
9+
def initialize
10+
@keys = {}
11+
end
12+
13+
def fetch(key_id)
14+
return if key_id.nil?
15+
key = keys[key_id]
16+
return key if key
17+
18+
update_keys!(key_id)
19+
keys.fetch(key_id)
20+
end
21+
22+
private
23+
24+
attr_accessor :keys
25+
26+
def update_keys!(key_id)
27+
new_keys = load_keys
28+
raise "Can't find key with id: #{key_id}" unless new_keys.key?(key_id)
29+
self.keys = new_keys
30+
end
31+
32+
def load_keys
33+
::JSON.parse(open(GOOGLE_PUBLIC_KEY_URL).read)
34+
end
35+
end
36+
end

lib/gcp_iap_warden/strategy.rb

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# frozen_string_literal: true
2+
3+
module GcpIapWarden
4+
module Strategy
5+
require_relative "strategy/base"
6+
require_relative "strategy/google_header"
7+
require_relative "strategy/google_jwt_header"
8+
end
9+
end

0 commit comments

Comments
 (0)