Skip to content
This repository was archived by the owner on Nov 1, 2023. It is now read-only.

Commit bde9522

Browse files
randallagordonDhaulagiri
authored andcommitted
Basic Auth Configuration (#45)
* Basic Basic Auth * Allow username and password to be passed in via env vars * Add test spec for basic auth returning 401 * Update README with Basic Auth config info * Add .htpasswd to test fixture * Set env vars in test * Request the file that actually exists, maybe? * Is context the key? * Perhaps app.run needs to come to the party too? * Move `auth_basic` from `location` to `server` * Set `basic_auth` as true if env `basic_auth_username` exists * Fix htpasswd generation when basic_auth is false * Append env password instead truncating * Fix typo * Fix config example in README
1 parent 2d643c5 commit bde9522

File tree

9 files changed

+83
-0
lines changed

9 files changed

+83
-0
lines changed

README.md

+14
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,20 @@ You can redirect all HTTP requests to HTTPS.
150150
}
151151
```
152152

153+
#### Basic Authentication
154+
155+
You can enable Basic Authentication so all requests require authentication.
156+
157+
```
158+
{
159+
"basic_auth": true
160+
}
161+
```
162+
163+
This will generate `.htpasswd` using environment variables `BASIC_AUTH_USERNAME` and `BASIC_AUTH_PASSWORD` if they are present. Otherwise it will use a standard `.htpasswd` file present in the `app` directory.
164+
165+
Passwords set via `BASIC_AUTH_PASSWORD` can be generated using OpenSSL or Apache Utils. For instance: `openssl passwd -apr1`.
166+
153167
#### Proxy Backends
154168
For single page web applications like Ember, it's common to back the application with another app that's hosted on Heroku. The down side of separating out these two applications is that now you have to deal with CORS. To get around this (but at the cost of some latency) you can have the static buildpack proxy apps to your backend at a mountpoint. For instance, we can have all the api requests live at `/api/` which actually are just requests to our API server.
155169

scripts/boot

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ esac
1717

1818
"${HERE}/config/make-config"
1919

20+
# Create .htpasswd if BASIC_AUTH_USERNAME and BASIC_AUTH_PASSWORD are provided
21+
"${HERE}/config/make-htpasswd"
22+
2023
# make a shared pipe; we'll write the name of the process that exits to it once
2124
# that happens, and wait for that event below this particular call works on
2225
# Linux and Mac OS (will create a literal ".XXXXXX" on Mac, but that doesn't

scripts/config/lib/nginx_config.rb

+6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ class NginxConfig
88
encoding: "UTF-8",
99
clean_urls: false,
1010
https_only: false,
11+
basic_auth: false,
12+
basic_auth_htpasswd_path: "/app/.htpasswd",
1113
worker_connections: 512,
1214
resolver: "8.8.8.8",
1315
logging: {
@@ -45,6 +47,10 @@ def initialize(json_file)
4547
json["clean_urls"] ||= DEFAULT[:clean_urls]
4648
json["https_only"] ||= DEFAULT[:https_only]
4749

50+
json["basic_auth"] = true unless ENV['BASIC_AUTH_USERNAME'].nil?
51+
json["basic_auth"] ||= DEFAULT[:basic_auth]
52+
json["basic_auth_htpasswd_path"] ||= DEFAULT[:basic_auth_htpasswd_path]
53+
4854
json["routes"] ||= {}
4955
json["routes"] = NginxConfigUtil.parse_routes(json["routes"])
5056

scripts/config/make-htpasswd

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/usr/bin/env ruby
2+
3+
require 'json'
4+
5+
USER_CONFIG = "/app/static.json"
6+
7+
config = {}
8+
config = JSON.parse(File.read(USER_CONFIG)) if File.exist?(USER_CONFIG)
9+
10+
HTPASSWD = config["basic_auth_htpasswd_path"] || '/app/.htpasswd'
11+
USERNAME = ENV["BASIC_AUTH_USERNAME"]
12+
PASSWORD = ENV["BASIC_AUTH_PASSWORD"]
13+
14+
htpasswd = "#{USERNAME}:#{PASSWORD}" unless (USERNAME.nil? || PASSWORD.nil?)
15+
16+
File.open(HTPASSWD, 'a') { |file| file.puts(htpasswd) } if !htpasswd.nil?

scripts/config/templates/nginx.conf.erb

+5
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ http {
5050
resolver <%= resolver %>;
5151
<% end %>
5252

53+
<% if basic_auth %>
54+
auth_basic "Restricted";
55+
auth_basic_user_file <%= basic_auth_htpasswd_path %>;
56+
<% end %>
57+
5358
location / {
5459
mruby_post_read_handler /app/bin/config/lib/ngx_mruby/headers.rb cache;
5560
mruby_set $fallback /app/bin/config/lib/ngx_mruby/routes_fallback.rb cache;

spec/fixtures/basic_auth/.htpasswd

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test:$apr1$Dnavu2z9$ZFxQn/mXVQoeYGD.tA2bW/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
foobar

spec/fixtures/basic_auth/static.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"basic_auth": true
3+
}

spec/simple_spec.rb

+34
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,40 @@
182182
end
183183
end
184184

185+
describe "basic_auth" do
186+
context "static.json without basic_auth key" do
187+
let(:name) { "hello_world" }
188+
189+
let(:env) {
190+
{
191+
"BASIC_AUTH_USERNAME" => "test",
192+
"BASIC_AUTH_PASSWORD" => "$apr1$Dnavu2z9$ZFxQn/mXVQoeYGD.tA2bW/"
193+
}
194+
}
195+
196+
it "should require authentication" do
197+
response = app.get("/index.html")
198+
expect(response.code).to eq("401")
199+
end
200+
end
201+
202+
context "static.json with basic_auth key and .htpasswd" do
203+
let(:name) { "basic_auth" }
204+
205+
let(:env) {
206+
{
207+
"BASIC_AUTH_USERNAME" => "test",
208+
"BASIC_AUTH_PASSWORD" => "$apr1$/pb2/xQR$cn7UPcTOLymIH1ZMe.NfO."
209+
}
210+
}
211+
212+
it "should require authentication" do
213+
response = app.get("/foo.html")
214+
expect(response.code).to eq("401")
215+
end
216+
end
217+
end
218+
185219
describe "custom error pages" do
186220
let(:name) { "custom_error_pages" }
187221

0 commit comments

Comments
 (0)