Compare commits
No commits in common. 'docs' and 'secret' have entirely different histories.
@ -1,13 +0,0 @@ |
|||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE |
|
||||||
Version 458.835.16.92, May 2018 |
|
||||||
|
|
||||||
Copyright (C) 2018 Charles Reid <charles@charlesreid1.com> |
|
||||||
|
|
||||||
Everyone is permitted to copy and distribute verbatim or modified |
|
||||||
copies of this license document, and changing it is allowed as long |
|
||||||
as the name is changed. |
|
||||||
|
|
||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE |
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION |
|
||||||
|
|
||||||
0. You just DO WHAT THE FUCK YOU WANT TO. |
|
@ -1,58 +0,0 @@ |
|||||||
# github-heroku-attack-rabbits |
|
||||||
|
|
||||||
## What's this business all about, then? |
|
||||||
|
|
||||||
This repository helps you protect your secret pages by (deep breath): |
|
||||||
|
|
||||||
hosting your secret page of static and/or dynamic content using a free Heroku app |
|
||||||
running a Python Flask server that uses Flask-Dance to authenticate visitors |
|
||||||
with Github which allows you fine-grained access control over your pages based on |
|
||||||
user attributes like organization or team membership or even things like how many |
|
||||||
repositories a user has or how many vowels are in their username. |
|
||||||
|
|
||||||
Also, did I mention the attack rabbits? |
|
||||||
|
|
||||||
![warning: attack rabbits ahead](docs/img/warning.png) |
|
||||||
|
|
||||||
|
|
||||||
## Where is everything? |
|
||||||
|
|
||||||
Final pages: |
|
||||||
|
|
||||||
* The finished product (pages on Heroku protected by attack rabbits) |
|
||||||
is at [github-heroku-attack-rabbits.herokuapp.com](https://github-heroku-attack-rabbits.herokuapp.com) |
|
||||||
|
|
||||||
* The documentation is at [pages.charlesreid1.com/github-heroku-attack-rabbits](https://pages.charlesreid1.com/github-heroku-attack-rabbits) |
|
||||||
|
|
||||||
Two branches in this repo compose the github-heroku-attack-rabbits documentation: |
|
||||||
|
|
||||||
* (**YOU ARE HERE**) The [docs](https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/docs) branch |
|
||||||
contains the files needed to generate the |
|
||||||
[github-heroku-attack-rabbits documentation site](https://pages.charlesreid1.com/github-heroku-attack-rabbits). |
|
||||||
|
|
||||||
* The [gh-pages](https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/gh-pages) branch |
|
||||||
contains the static files generated from the documentation. |
|
||||||
The contents of this branch compose the |
|
||||||
[github-heroku-attack-rabbits documentation site](https://pages.charlesreid1.com/github-heroku-attack-rabbits). |
|
||||||
|
|
||||||
Two branches illustrate github-heroku-attack-rabbits in practice: |
|
||||||
|
|
||||||
* The [secret](https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/secret) branch contains the files needed to create the secret page. |
|
||||||
This repository is public, so obviously these aren't *actually* secret, |
|
||||||
but in practice this would be in a protected repository. |
|
||||||
|
|
||||||
* The [heroku-pages](https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/heroku-pages) branch |
|
||||||
contains the content that is actually pushed to Heroku - that is, |
|
||||||
the final Flask app. |
|
||||||
|
|
||||||
|
|
||||||
## Where do I start? |
|
||||||
|
|
||||||
See the [documentation](https://pages.charlesreid1.com/github-heroku-attack-rabbits) |
|
||||||
or [docs/index.md](docs/index.md). |
|
||||||
|
|
||||||
|
|
||||||
## License |
|
||||||
|
|
||||||
This is released under the [WTFPL](LICENSE). |
|
||||||
|
|
@ -0,0 +1,16 @@ |
|||||||
|
# github-heroku-attack-rabbits: secret branch |
||||||
|
|
||||||
|
The secret branch contains the mkdocs source files |
||||||
|
needed to generate the final static content for the |
||||||
|
secret site. |
||||||
|
|
||||||
|
The `secret` branch contains the source files, |
||||||
|
the `heroku-pages` branch contains the finished product. |
||||||
|
|
||||||
|
The `heroku-pages` branch contains the site that is |
||||||
|
actually deployed to Heroku at the Github-Heroku attack |
||||||
|
sheep app: |
||||||
|
|
||||||
|
* [https://github-heroku-attack-rabbits.herokuapp.com](https://github-heroku-attack-rabbits.herokuapp.com). |
||||||
|
|
||||||
|
Lost? Visit the [docs](https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits) branch. |
@ -1,10 +0,0 @@ |
|||||||
body { |
|
||||||
background-color: #efefef; |
|
||||||
} |
|
||||||
div.body { |
|
||||||
background-color: #efefef; |
|
||||||
} |
|
||||||
.md-typeset pre { |
|
||||||
background-color: #ccc; |
|
||||||
} |
|
||||||
|
|
@ -1,33 +0,0 @@ |
|||||||
# Custom Domains |
|
||||||
|
|
||||||
If you want to use a custom domain: |
|
||||||
|
|
||||||
* Set up custom domain using Heroku command line interface |
|
||||||
* This will set up a DNS subdomain specifically for your app |
|
||||||
* Point your DNS records to the Heroku DNS subdomain |
|
||||||
|
|
||||||
This also introduces complications with HTTP vs HTTPS: |
|
||||||
OAuth must happen over HTTPS (required by protocol). |
|
||||||
But you the user can control your domain and create |
|
||||||
SSL certificates for it, but Heroku is hosting the app. |
|
||||||
|
|
||||||
You have two options: the free option, and the pay option. |
|
||||||
|
|
||||||
**The pay option:** For $7/mo you can upgrade to hobby nodes, |
|
||||||
which allows you to give Heroku permission to create an SSL |
|
||||||
certificate for your domain. This is the easiest solution |
|
||||||
and requires zero setup, zero certificate management. |
|
||||||
|
|
||||||
**The free option:** You can have your domain (HTTP only) |
|
||||||
forward to Heroku (HTTPS can't be forwarded - that's ***key***). |
|
||||||
When you hit the Heroku domain, it will log the user in to Github. |
|
||||||
When the user logs in successfully, Github will redirect them to |
|
||||||
the callback URL. This callback URL ***MUST*** be HTTPS, so it cannot |
|
||||||
redirect back to your (HTTP-only) custom domain. |
|
||||||
|
|
||||||
That means the userr will, after authenticating with Github, |
|
||||||
always be redirected to `https://my-cool-app.herokuapp.com` |
|
||||||
and never `http://my-cool-custom-domain-that-cannot-be-used-as-a-callback-because-it-is-https-only.com`. |
|
||||||
|
|
||||||
The paid option is much, much simpler in the end |
|
||||||
and will save you $7/mo in setup time alone. |
|
@ -1,174 +0,0 @@ |
|||||||
# Create a Flask App using Flask-Dance |
|
||||||
|
|
||||||
This is the heart of the method. |
|
||||||
|
|
||||||
The best thing to do here is just to walk you through the script. |
|
||||||
|
|
||||||
Import statements: |
|
||||||
|
|
||||||
``` |
|
||||||
import os, json |
|
||||||
from os.path import join, isfile, isdir |
|
||||||
from werkzeug.contrib.fixers import ProxyFix |
|
||||||
from flask import Flask, redirect, url_for, send_from_directory |
|
||||||
from flask_dance.contrib.github import make_github_blueprint, github |
|
||||||
``` |
|
||||||
|
|
||||||
Note that flask-dance adds an OAuth login/callback route |
|
||||||
to your Flask app by creating a `/login` blueprint, |
|
||||||
meaning all the OAuth stuff is just magically available |
|
||||||
via `/login`. |
|
||||||
|
|
||||||
Set paths for static content: |
|
||||||
|
|
||||||
``` |
|
||||||
PROJECT_ROOT = os.path.dirname(os.path.realpath(__file__)) |
|
||||||
STATIC_PATH = 'content' |
|
||||||
``` |
|
||||||
|
|
||||||
Create and configure app. This requires confidential information |
|
||||||
for the Github application you created, specifically the client |
|
||||||
ID and client secret. These are at the very top of the page when |
|
||||||
you visit your app's settings page. |
|
||||||
|
|
||||||
To find this, after you log in, click your profile photo in the |
|
||||||
upper right > Settings > Developer Settings > OAuth Apps > click the |
|
||||||
name for your OAuth app. |
|
||||||
|
|
||||||
``` |
|
||||||
app = Flask(__name__) |
|
||||||
|
|
||||||
# this worked locally, but not on heroku |
|
||||||
app.wsgi_app = ProxyFix(app.wsgi_app) |
|
||||||
|
|
||||||
app.secret_key = os.environ.get("FLASK_SECRET_KEY", "9502861d41e8729c5cae3225920b1b46") |
|
||||||
|
|
||||||
app.config["RESULT_STATIC_PATH"] = STATIC_PATH #os.path.join(PROJECT_ROOT,STATIC_PATH) |
|
||||||
app.config["GITHUB_OAUTH_CLIENT_ID"] = os.environ.get("GITHUB_OAUTH_CLIENT_ID") |
|
||||||
app.config["GITHUB_OAUTH_CLIENT_SECRET"] = os.environ.get("GITHUB_OAUTH_CLIENT_SECRET") |
|
||||||
``` |
|
||||||
|
|
||||||
Now the magic happens: we use flask-dance to create a blueprint |
|
||||||
that has methods and settings all ready to go for us to do the |
|
||||||
OAuth dance. |
|
||||||
|
|
||||||
`make_github_blueprint()` is part of the contrib module of flask-dance. |
|
||||||
There are several similar methods to generate blueprints for |
|
||||||
authenticating with other APIs. |
|
||||||
|
|
||||||
``` |
|
||||||
github_bp = make_github_blueprint( |
|
||||||
client_id = os.environ.get('GITHUB_OAUTH_CLIENT_ID'), |
|
||||||
client_secret = os.environ.get('GITHUB_OAUTH_CLIENT_SECRET'), |
|
||||||
scope='read:org') |
|
||||||
|
|
||||||
app.register_blueprint(github_bp, url_prefix="/login") |
|
||||||
|
|
||||||
contents404 = "<html><body><h1>Status: Error 404 Page Not Found</h1></body></html>" |
|
||||||
contents403 = "<html><body><h1>Status: Error 403 Access Denied</h1></body></html>" |
|
||||||
contents200 = "<html><body><h1>Status: OK 200</h1></body></html>" |
|
||||||
``` |
|
||||||
|
|
||||||
Deal with the `/` route first: |
|
||||||
|
|
||||||
* Check if authorized, if not, redirect them to the login URL |
|
||||||
(magical URLs taken care of magically by our `make_github_blueprint` |
|
||||||
function above) |
|
||||||
* If user is authorized (i.e., if they have gone through the OAuth |
|
||||||
process and given their passsword to Github and been redirected |
|
||||||
to your app with an OAuth token), then the next step is to |
|
||||||
find out some information about them. |
|
||||||
* Use the `github` object to call the Github API directly. |
|
||||||
* Decide what to do from there. |
|
||||||
|
|
||||||
``` |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
if not github.authorized: |
|
||||||
return redirect(url_for("github.login")) |
|
||||||
|
|
||||||
resp = github.get("/user/orgs") |
|
||||||
if resp.ok: |
|
||||||
|
|
||||||
all_orgs = resp.json() |
|
||||||
for org in all_orgs: |
|
||||||
if org['login']=='rainbow-mind-machine': |
|
||||||
``` |
|
||||||
|
|
||||||
The next line is important to how the server works: |
|
||||||
if all of the criteria above have been met, we return |
|
||||||
a static file: |
|
||||||
|
|
||||||
``` |
|
||||||
return send_from_directory(STATIC_PATH, 'index.html') |
|
||||||
``` |
|
||||||
|
|
||||||
This is normally "bad practice," and numerous type A people |
|
||||||
on the internet will tell you Flask should not be used for |
|
||||||
serving static files, and that you should use nginx etc., |
|
||||||
but these fail to igonore the following: |
|
||||||
|
|
||||||
* Heroku does not let you run or configure nginx |
|
||||||
* For crying out loud this example is about attack rabbits |
|
||||||
stop taking everything so seriously |
|
||||||
|
|
||||||
Now that we've got that out of the way... |
|
||||||
|
|
||||||
Here's how we serve up static files. This is a total hack, |
|
||||||
but it works. It takes any arbitrary path supplied by the |
|
||||||
user, and attempts to find a corresponding file to serve up |
|
||||||
on disk. |
|
||||||
|
|
||||||
If the user passes a file, then that file is served up. |
|
||||||
|
|
||||||
If the user passes a directory, then `index.html` is served up. |
|
||||||
|
|
||||||
If the user asks for a non-existent file, a 404 error is shown. |
|
||||||
|
|
||||||
If the user is not allowed to view the content, they will face |
|
||||||
the bowel-emptying terrors of the 403 error. |
|
||||||
|
|
||||||
``` |
|
||||||
@app.route('/<path:path>') |
|
||||||
def catch_all(path): |
|
||||||
|
|
||||||
if not github.authorized: |
|
||||||
return redirect(url_for("github.login")) |
|
||||||
|
|
||||||
username = github.get("/user").json()['login'] |
|
||||||
|
|
||||||
rsp = app.config["RESULT_STATIC_PATH"] |
|
||||||
resp = github.get("/user/orgs") |
|
||||||
if resp.ok: |
|
||||||
|
|
||||||
all_orgs = resp.json() |
|
||||||
for org in all_orgs: |
|
||||||
if org['login']=='dcppc': |
|
||||||
|
|
||||||
if(path==''): |
|
||||||
return send_from_directory(rsp, 'index.html') |
|
||||||
|
|
||||||
elif(isdir(join(rsp,path))): |
|
||||||
return send_from_directory(join(rsp,path),'index.html') |
|
||||||
|
|
||||||
elif(isfile(join(rsp,path))): |
|
||||||
return send_from_directory(rsp, path) |
|
||||||
|
|
||||||
else: |
|
||||||
return contents404 |
|
||||||
|
|
||||||
return contents403 |
|
||||||
``` |
|
||||||
|
|
||||||
Last, set a default 404 handler, and run the app: |
|
||||||
|
|
||||||
``` |
|
||||||
@app.errorhandler(404) |
|
||||||
def oops(e): |
|
||||||
return contents404 |
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__": |
|
||||||
app.run() |
|
||||||
``` |
|
||||||
|
|
@ -1,25 +0,0 @@ |
|||||||
# Authenticate Users on Github Membership |
|
||||||
|
|
||||||
For the sake of simplicity, will just demonstrate how this |
|
||||||
works for a single route, but you can combine different |
|
||||||
rules into multiple routes to provide access to different |
|
||||||
people on different parts of a site. |
|
||||||
|
|
||||||
Here is the relevant method that serves up `index.html` |
|
||||||
if the user is authenticated: |
|
||||||
|
|
||||||
``` |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
if not github.authorized: |
|
||||||
return redirect(url_for("github.login")) |
|
||||||
|
|
||||||
resp = github.get("/user") |
|
||||||
|
|
||||||
if resp.ok: |
|
||||||
return send_from_directory(STATIC_PATH, 'index.html') |
|
||||||
|
|
||||||
return contents403 |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
@ -1,22 +0,0 @@ |
|||||||
# Authenticate Users on Organization or Team Membership |
|
||||||
|
|
||||||
Here is how we can make access to a given page or route |
|
||||||
conditional on membership in an organization (in this |
|
||||||
example, membership in the `rainbow-mind-machine` organization |
|
||||||
is required for access): |
|
||||||
|
|
||||||
``` |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
if not github.authorized: |
|
||||||
return redirect(url_for("github.login")) |
|
||||||
|
|
||||||
resp = github.get("/user/orgs") |
|
||||||
if resp.ok: |
|
||||||
|
|
||||||
all_orgs = resp.json() |
|
||||||
for org in all_orgs: |
|
||||||
if org['login']=='rainbow-mind-machine': |
|
||||||
return send_from_directory(STATIC_PATH, 'index.html') |
|
||||||
``` |
|
||||||
|
|
@ -1,27 +0,0 @@ |
|||||||
# Authenticate Users on Other Criteria |
|
||||||
|
|
||||||
Suppose we wanted to do something silly like restrict |
|
||||||
access to a page to users with Github handles that were |
|
||||||
between 5 and 7 letters. |
|
||||||
|
|
||||||
Here is the relevant method that serves up `index.html` |
|
||||||
if the user's Github handle is 5-7 letters long: |
|
||||||
|
|
||||||
``` |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
if not github.authorized: |
|
||||||
return redirect(url_for("github.login")) |
|
||||||
|
|
||||||
resp = github.get("/user") |
|
||||||
|
|
||||||
if resp.ok: |
|
||||||
username = resp.json()['login'] |
|
||||||
if len(username)>=5 and len(username)<=7: |
|
||||||
return send_from_directory(STATIC_PATH, 'index.html') |
|
||||||
|
|
||||||
return contents403 |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,64 +0,0 @@ |
|||||||
# Authenticate Different Users on Different Portions of Site |
|
||||||
|
|
||||||
**NOTE: if you are authenticating using membership on a team, you will need the |
|
||||||
team id of the team you are interested in authenticating against.** |
|
||||||
|
|
||||||
For this example, let's expand the routes we're looking at |
|
||||||
a bit more. |
|
||||||
|
|
||||||
Suppose we have two folders, `team_only` and `org_only`. |
|
||||||
|
|
||||||
The `team_only` folder should only be accessible to your team, `Team Gold`. |
|
||||||
|
|
||||||
The `org_only` folder should only be accessible to your organization, `Colorful Colors`. |
|
||||||
|
|
||||||
The main site (i.e., all other files) should be publicly accessible. |
|
||||||
|
|
||||||
``` |
|
||||||
@app.route('/') |
|
||||||
def index(): |
|
||||||
return send_from_directory(STATIC_PATH, 'index.html') |
|
||||||
|
|
||||||
|
|
||||||
@app.route('/team_only/<path:path>') |
|
||||||
def team_gold(path): |
|
||||||
|
|
||||||
if not github.authorized: |
|
||||||
return redirect(url_for("github.login")) |
|
||||||
|
|
||||||
rsp = app.config["RESULT_STATIC_PATH"] |
|
||||||
|
|
||||||
resp = github.get("/user") |
|
||||||
if resp.ok: |
|
||||||
username = resp.json()['login'] |
|
||||||
|
|
||||||
team_id = 'XXXXX' |
|
||||||
|
|
||||||
resp = github.get("/teams/%s/members/%s"%( team_id, username )) |
|
||||||
if resp.code==204: |
|
||||||
team_gold_dir = os.path.join(STATIC_PATH, 'team_only') |
|
||||||
return send_from_directory(team_gold_dir, 'index.html') |
|
||||||
return contents403 |
|
||||||
|
|
||||||
|
|
||||||
@app.route('/org_only/<path:path>') |
|
||||||
def team_gold(path): |
|
||||||
|
|
||||||
if not github.authorized: |
|
||||||
return redirect(url_for("github.login")) |
|
||||||
|
|
||||||
rsp = app.config["RESULT_STATIC_PATH"] |
|
||||||
|
|
||||||
resp = github.get("/user") |
|
||||||
if resp.ok: |
|
||||||
|
|
||||||
my_org = 'XXXXXXXX' |
|
||||||
|
|
||||||
all_orgs = resp.json() |
|
||||||
for org in all_orgs: |
|
||||||
if org['login']==my_org: |
|
||||||
color_org_dir = os.path.join(STATIC_PATH, 'org_only') |
|
||||||
return send_from_directory(color_org_dir, 'index.html') |
|
||||||
return contents403 |
|
||||||
``` |
|
||||||
|
|
@ -1,98 +0,0 @@ |
|||||||
# Deploying to Heroku |
|
||||||
|
|
||||||
Once we have debugged the Flask app and we are happy with it, |
|
||||||
we are ready to deploy it to Heroku. |
|
||||||
|
|
||||||
|
|
||||||
## Repository Setup |
|
||||||
|
|
||||||
To do this, you should set up your repo as follows: |
|
||||||
|
|
||||||
Clone the repo: |
|
||||||
|
|
||||||
``` |
|
||||||
$ git clone https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits.git |
|
||||||
``` |
|
||||||
|
|
||||||
The repo has the following structure: |
|
||||||
|
|
||||||
``` |
|
||||||
github-heroku-attack-rabbits/ |
|
||||||
LICENSE |
|
||||||
README.md |
|
||||||
mkdocs.yml |
|
||||||
docs/ |
|
||||||
index.md |
|
||||||
... |
|
||||||
mkdocs-material/ |
|
||||||
... |
|
||||||
``` |
|
||||||
|
|
||||||
Now, inside the repo, clone the repo again, |
|
||||||
but this time clone the `heroku-pages` branch |
|
||||||
to the `site/` directory: |
|
||||||
|
|
||||||
``` |
|
||||||
$ cd github-heroku-attack-rabbits/ |
|
||||||
$ git clone -b heroku-pages https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits.git site |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
## Heroku Deploy Process |
|
||||||
|
|
||||||
To deploy content to Heroku, we add our Heroku project as a git remote |
|
||||||
(see the [heroku](heroku.md) page for how to do that) and then push to |
|
||||||
to the master branch of the heroku remote git repo. Changes are pulled |
|
||||||
in by Heroku and the app is restarted each time you run `git push`. |
|
||||||
|
|
||||||
We walk through the steps below. |
|
||||||
|
|
||||||
|
|
||||||
## Heroku Login |
|
||||||
|
|
||||||
From the `site/` directory containing the contents of the `heroku-pages` branch, |
|
||||||
that is, containing the Python flask app, log in to Heroku: |
|
||||||
|
|
||||||
``` |
|
||||||
$ heroku login |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
## Add Heroku Remote to `heroku-pages` Branch |
|
||||||
|
|
||||||
Now have Heroku add the proper git remote address: |
|
||||||
|
|
||||||
``` |
|
||||||
$ heroku git:remote -a <heroku-app-name> |
|
||||||
``` |
|
||||||
|
|
||||||
Now you're ready to deploy to Heroku. |
|
||||||
|
|
||||||
|
|
||||||
## Deploy to Heroku |
|
||||||
|
|
||||||
Double check your app is ready, then deploy: |
|
||||||
|
|
||||||
``` |
|
||||||
$ git push heroku heroku-pages:master |
|
||||||
``` |
|
||||||
|
|
||||||
This will push the local branch `heroku-pages` to |
|
||||||
the remote branch `master` on the `heroku` remote. |
|
||||||
This should begin a pre-commit hook where Heroku |
|
||||||
compiles your Python app. You should get the green |
|
||||||
light, if you tested your app locally and everything |
|
||||||
was good to go. |
|
||||||
|
|
||||||
|
|
||||||
## Check Your Heroku App |
|
||||||
|
|
||||||
Your Heroku app will be available at |
|
||||||
|
|
||||||
``` |
|
||||||
https://<heroku-app-name>.herokuapp.com |
|
||||||
``` |
|
||||||
|
|
||||||
You should see your Python flask app |
|
||||||
show up shortly. |
|
||||||
|
|
@ -1,42 +0,0 @@ |
|||||||
# Testing Flask App Locally |
|
||||||
|
|
||||||
We set up the Github App to use a callback of `https://localhost:5000/login/github/authorized` |
|
||||||
so that we could test the app locally. Now it is time to test the app locally. |
|
||||||
|
|
||||||
The application needs access to your Github app id and token. Those are provided |
|
||||||
via the `GITHUB_OAUTH_CLIENT_{ID,SECRET}` environment variables. Set these |
|
||||||
when you run the actual python command to run the server: |
|
||||||
|
|
||||||
``` |
|
||||||
$ GITHUB_OAUTH_CLIENT_ID="xxxxxxx" \ |
|
||||||
GITHUB_OAUTH_CLIENT_SECRET="xxxxxxx" \ |
|
||||||
OAUTHLIB_INSECURE_TRANSPORT=true \ |
|
||||||
python github.py |
|
||||||
``` |
|
||||||
|
|
||||||
This runs the Flask server on port 5000, where it will wait for a visitor. |
|
||||||
The way we have our application written in this example, the main `/` route |
|
||||||
will redirect the user to a Github login screen immediately, but you could |
|
||||||
also present the user with a friendly welcome page when they go to `/`, |
|
||||||
and only redirect them to the Github login prompt when they visit a |
|
||||||
URL like `/login` or `/auth`. |
|
||||||
|
|
||||||
Once you run the above command, open the following URL in your browser: |
|
||||||
|
|
||||||
``` |
|
||||||
http://localhost:5000/ |
|
||||||
``` |
|
||||||
|
|
||||||
**NOTE: Make sure you are logged out of Github and that you clear your cookies |
|
||||||
if you are already logged in as one user and wish to authenticate as another. |
|
||||||
The login is _very_ persistent so you may need to close and re-open your browser.** |
|
||||||
|
|
||||||
Visiting the address above will result in your being redirected to a Github |
|
||||||
login page. Once you login, Github will redirect the user back to the |
|
||||||
github-heroku-attack-rabbits application with a token that the application |
|
||||||
can use to perform actions on behalf of the user. |
|
||||||
|
|
||||||
## Next Step? |
|
||||||
|
|
||||||
If the app works, the next step is to deploy to Heroku. |
|
||||||
|
|
@ -1,57 +0,0 @@ |
|||||||
## get started with github |
|
||||||
|
|
||||||
We mentioned on the [heroku](heroku.md) page that heroku |
|
||||||
creates a remote git repository to hold the files you |
|
||||||
want to host. |
|
||||||
|
|
||||||
To use the contents of a Github repository on Heroku, |
|
||||||
just treat it like another git remote, no special setup |
|
||||||
is needed. |
|
||||||
|
|
||||||
However, to set up your Github-Heroku attack rabbits to |
|
||||||
authenticate a user via Github, and mercilessly attack |
|
||||||
all intruders, you must create a Github OAuth App. |
|
||||||
|
|
||||||
### Creating Github OAuth App |
|
||||||
|
|
||||||
Log into Github |
|
||||||
|
|
||||||
Go to Settings |
|
||||||
|
|
||||||
Click "Developer Settings" on the left side |
|
||||||
|
|
||||||
Click "New OAuth App" button in upper right |
|
||||||
|
|
||||||
**What do these settings mean?** |
|
||||||
|
|
||||||
* **Application name** is what will be shown to users when they visit |
|
||||||
a page protected by the attack rabbits and are prompted for |
|
||||||
their password by Github. |
|
||||||
|
|
||||||
* **Homepage URL/Application description** are for users who want to know more |
|
||||||
about your killer attack rabbit Github app |
|
||||||
|
|
||||||
* **Authorization callback URL** is the URL that the users will be sent to |
|
||||||
once they authenticate with Github and they are granted an OAuth token. |
|
||||||
This is the magic ingredient that allows you to take actions on behalf |
|
||||||
of the account logging in. |
|
||||||
|
|
||||||
In this guide we'll cover the case of checking membership in organizations or teams, |
|
||||||
but what your attack rabbits end up doing to determine if a user is allowed to |
|
||||||
access your secret pages is up to you. |
|
||||||
|
|
||||||
## Values to use |
|
||||||
|
|
||||||
You should set your own values for the **name** and **description** fields. |
|
||||||
|
|
||||||
The **home URL** is not actually necessary - it is simply provided for users to |
|
||||||
get more information about your app. |
|
||||||
|
|
||||||
The most important is the **callback URL**, which should be set to: |
|
||||||
|
|
||||||
http://localhost:5000/login/github/authorized |
|
||||||
|
|
||||||
This is for testing locally *only*. |
|
||||||
|
|
||||||
Don't use HTTPS in the callback URL! |
|
||||||
|
|
@ -1,67 +0,0 @@ |
|||||||
# create heroku app |
|
||||||
|
|
||||||
|
|
||||||
### Heroku toolbelt |
|
||||||
|
|
||||||
Heroku offers a really nice command line interface tool called |
|
||||||
Heroku Toolbelt. It is available through Homebrew and Aptitude: |
|
||||||
|
|
||||||
``` |
|
||||||
$ brew install heroku # from Mac |
|
||||||
|
|
||||||
$ apt-get install heroku # from Ubuntu |
|
||||||
``` |
|
||||||
|
|
||||||
It is then available on the command line as `heroku`. |
|
||||||
|
|
||||||
The first thing you should do is authenticate with |
|
||||||
your Heroku account by running |
|
||||||
|
|
||||||
``` |
|
||||||
$ heroku login |
|
||||||
``` |
|
||||||
|
|
||||||
We will use this command line application for the following tasks: |
|
||||||
|
|
||||||
* Create a git remote to point to the right Heroku git remote location |
|
||||||
* Set environment variables (for e.g. secret keys) on the remote Heroku instance |
|
||||||
* Get information (logs, status, etc.) about your Heroku app |
|
||||||
|
|
||||||
We will cover these commands as they come up. |
|
||||||
|
|
||||||
|
|
||||||
### Create heroku app |
|
||||||
|
|
||||||
Start by creating a heroku app. |
|
||||||
|
|
||||||
* Each heroku app must have a unique name |
|
||||||
* Each heroku app creates a remote git repo |
|
||||||
* Master branch is what Heroku deploys publicly on herokuapps.com |
|
||||||
* You will also need heroku CLI to link your github repo to your heroku app |
|
||||||
|
|
||||||
|
|
||||||
### Where heroku app lives |
|
||||||
|
|
||||||
Suppose you are creating an app called `my-cool-app` |
|
||||||
on heroku. Then your application will be hosted by |
|
||||||
Heroku and will be available at the URL: |
|
||||||
|
|
||||||
``` |
|
||||||
https://my-cool-app.herokuapp.com |
|
||||||
``` |
|
||||||
|
|
||||||
### How heroku apps works |
|
||||||
|
|
||||||
If you have used Github Pages before, Heroku uses a similar |
|
||||||
model (live hosting one particular branch of a git repository). |
|
||||||
However, Heroku is different because you can run dynamic scripts |
|
||||||
using Python, Ruby, PHP, etc. |
|
||||||
|
|
||||||
To change the content of your Heroku app, just change the contents |
|
||||||
of the repository, and push to master (push to the master branch of |
|
||||||
the remote Heroku repository). |
|
||||||
|
|
||||||
You will need to structure your repository carefully. |
|
||||||
That's what this page is here to help you do. |
|
||||||
Heroku can figure out the rest from there. |
|
||||||
|
|
Before Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 46 KiB |
@ -1,92 +0,0 @@ |
|||||||
# github-heroku-attack-rabbits |
|
||||||
|
|
||||||
## What's this business all about, then? |
|
||||||
|
|
||||||
This repository helps you put access control into place to protect your secret pages by (deep breath): |
|
||||||
|
|
||||||
hosting your secret page of static and/or dynamic content by using a free Heroku app |
|
||||||
running a Python Flask server that uses Flask-Dance to authenticate visitors |
|
||||||
with Github using OAuth which allows you fine-grained access control for your pages |
|
||||||
using user attributes like organization or team membership or even things like how many |
|
||||||
vowels a user has in their username. |
|
||||||
|
|
||||||
Also, did I mention the attack rabbits? |
|
||||||
|
|
||||||
![warning: attack rabbits ahead](img/warning.png) |
|
||||||
|
|
||||||
|
|
||||||
## Where is everything? |
|
||||||
|
|
||||||
Final pages: |
|
||||||
|
|
||||||
* The finished product (pages on Heroku protected by attack rabbits) |
|
||||||
is at [github-heroku-attack-rabbits.herokuapp.com](https://github-heroku-attack-rabbits.herokuapp.com) |
|
||||||
|
|
||||||
* The documentation is at [pages.charlesreid1.com/github-heroku-attack-rabbits](https://pages.charlesreid1.com/github-heroku-attack-rabbits) |
|
||||||
|
|
||||||
Two branches in this repo compose the github-heroku-attack-rabbits documentation: |
|
||||||
|
|
||||||
* (**YOU ARE HERE**) The [docs](https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/docs) branch |
|
||||||
contains the files needed to generate the |
|
||||||
[github-heroku-attack-rabbits documentation site](https://pages.charlesreid1.com/github-heroku-attack-rabbits). |
|
||||||
|
|
||||||
* The [gh-pages](https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/gh-pages) branch |
|
||||||
contains the static files generated from the documentation. |
|
||||||
The contents of this branch compose the |
|
||||||
[github-heroku-attack-rabbits documentation site](https://pages.charlesreid1.com/github-heroku-attack-rabbits). |
|
||||||
|
|
||||||
Two branches illustrate github-heroku-attack-rabbits in practice: |
|
||||||
|
|
||||||
* The [secret](https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/secret) branch contains the files needed to create the secret page. |
|
||||||
This repository is public, so obviously these aren't *actually* secret, |
|
||||||
but in practice this would be in a protected repository. |
|
||||||
|
|
||||||
* The [heroku-pages](https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/heroku-pages) branch |
|
||||||
contains the content that is actually pushed to Heroku - that is, |
|
||||||
the final Flask app. |
|
||||||
|
|
||||||
|
|
||||||
## Contents |
|
||||||
|
|
||||||
An overview of the steps: |
|
||||||
|
|
||||||
[Get Started with Heroku](heroku.md) |
|
||||||
|
|
||||||
[Get Started with Github](github.md) |
|
||||||
|
|
||||||
[Initialize Repository: Branches](repo.md) |
|
||||||
|
|
||||||
[Create a Flask App using Flask-Dance](flask.md) |
|
||||||
|
|
||||||
* [Authenticate users based on Github membership only](flask_auth_github.md) |
|
||||||
* [Authenticate users based on organization or team membership](flask_auth_org.md) |
|
||||||
* [Authenticate users based on some other criteria](flask_auth_other.md) |
|
||||||
* [Protection portions of the site](flask_auth_portions.md) |
|
||||||
|
|
||||||
[Test Flask App Locally](flask_local.md) |
|
||||||
|
|
||||||
[Deploying Flask App to Heroku](flask_heroku.md) |
|
||||||
|
|
||||||
[Custom Domains](custom_domains.md) |
|
||||||
|
|
||||||
|
|
||||||
## Links |
|
||||||
|
|
||||||
Python software used: |
|
||||||
|
|
||||||
* [Flask](http://flask.pocoo.org/) |
|
||||||
* [Flask-dance](https://github.com/singingwolfboy/flask-dance) |
|
||||||
* [Flask-dance-github](https://github.com/singingwolfboy/flask-dance-github) |
|
||||||
* [mkdocs-material (documentation theme)](https://github.com/squidfunk/mkdocs-material) |
|
||||||
* [mkdocs (documentation)](http://www.mkdocs.org/) |
|
||||||
|
|
||||||
Commercial services: |
|
||||||
|
|
||||||
* [Heroku](https://heroku.com) |
|
||||||
* [Github](https://github.com) |
|
||||||
|
|
||||||
|
|
||||||
## License |
|
||||||
|
|
||||||
This is released under the [WTFPL](https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/docs/LICENSE). |
|
||||||
|
|
@ -1,110 +0,0 @@ |
|||||||
# Initialize Git Repository |
|
||||||
|
|
||||||
Let's talk through how a repository should be laid out |
|
||||||
if we're going to be hosting a Flask app on Heroku. |
|
||||||
|
|
||||||
## Branches |
|
||||||
|
|
||||||
We will need a minimum of two branches. Here we specify |
|
||||||
the names that these branches will have in your Github repo, |
|
||||||
**which is different from the names of the branches on Heroku**: |
|
||||||
|
|
||||||
Branches on Github: |
|
||||||
|
|
||||||
* `heroku-pages` - this branch contains the content that Heroku will host. |
|
||||||
Specifically, it contains the Flask application in a `.py` file, |
|
||||||
and a few other files to help Heroku determine how to run the app |
|
||||||
and what to install. |
|
||||||
|
|
||||||
* `master` - this branch contains the content used to generate the documentation |
|
||||||
and page content that is being hosted behind the Heroku attack sheep. |
|
||||||
The documentation you are reading right now is from the master branch, |
|
||||||
and was made with `mkdocs`. |
|
||||||
|
|
||||||
On Heroku, we only have a single branch: |
|
||||||
|
|
||||||
* `master` (Heroku) maps to `heroku-pages` (Github) |
|
||||||
|
|
||||||
## Repo Layout |
|
||||||
|
|
||||||
Let's talk about the layout of the repository. |
|
||||||
|
|
||||||
If you wish to build the site in order to deploy it to Heroku, |
|
||||||
you should clone the `master` branch (preparing to make the |
|
||||||
content for your attack sheep-protected page): |
|
||||||
|
|
||||||
``` |
|
||||||
$ git clone -b master https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits.git |
|
||||||
$ cd github-heroku-attack-rabbits |
|
||||||
``` |
|
||||||
|
|
||||||
Once you are *inside* the master branch, clone the repo again, |
|
||||||
but this time clone the `heroku-pages` branch, and clone it |
|
||||||
to the `site/` folder: |
|
||||||
|
|
||||||
``` |
|
||||||
$ git clone -b heroku-pages https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits.git site |
|
||||||
$ cd site |
|
||||||
``` |
|
||||||
|
|
||||||
Now you will want to set up the Heroku remote: |
|
||||||
|
|
||||||
``` |
|
||||||
$ heroku git:remote -a my-cool-project |
|
||||||
``` |
|
||||||
|
|
||||||
The layout should now be: |
|
||||||
|
|
||||||
``` |
|
||||||
my-cool-project-repo/ <-- my-cool-project repo pointing to master branch |
|
||||||
|
|
||||||
docs/ \ |
|
||||||
index.md | |
|
||||||
heroku.md | <-- mkdocs files |
|
||||||
... | (can use any static content generator: |
|
||||||
| pelican, sphinx, etc.) |
|
||||||
mkdocs.yml / |
|
||||||
|
|
||||||
site/ <-- my-cool-project repo pointing to heroku-pages branch |
|
||||||
|
|
||||||
Procfile \ |
|
||||||
github.py | <-- heroku python app files |
|
||||||
requirements.txt | (can also use ruby, php, js, etc.) |
|
||||||
runtime.txt | (can also use ruby, php, js, etc.) |
|
||||||
... / |
|
||||||
|
|
||||||
content/ \ |
|
||||||
index.html | <-- static content hosted by Flask |
|
||||||
sitemap.xml | |
|
||||||
... / |
|
||||||
``` |
|
||||||
|
|
||||||
|
|
||||||
## Workflow |
|
||||||
|
|
||||||
Once you have things set up according to the instructions and diagram above, |
|
||||||
you're ready to run the push-to-deploy workflow and start running your secret |
|
||||||
site on Heroku. |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,11 @@ |
|||||||
|
.md-typeset h1 { font-weight: 600; } |
||||||
|
.md-typeset h2 { font-weight: 600; } |
||||||
|
.md-typeset h3 { font-weight: 600; } |
||||||
|
.md-typeset h4 { font-weight: 600; } |
||||||
|
|
||||||
|
body { |
||||||
|
background-color: #f2f9f7; |
||||||
|
} |
||||||
|
div.body { |
||||||
|
background-color: #f2f9f7; |
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
# The Secret Society of the Fish Slapping Dance |
||||||
|
|
||||||
|
Welcome, and congratulations on having an even number of vowels in your Github handle. |
||||||
|
|
||||||
|
And now for something completely expected: |
||||||
|
|
||||||
|
[Fish-Slapping Dance](https://www.youtube.com/watch?v=T8XeDvKqI4E) |
||||||
|
|
After Width: | Height: | Size: 598 KiB |
After Width: | Height: | Size: 621 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 34 KiB |
@ -0,0 +1,16 @@ |
|||||||
|
# Secret Index |
||||||
|
|
||||||
|
This is an index of secret pages. Note that this page is not protected. |
||||||
|
|
||||||
|
You will be asked to authenticate when you click the links below. |
||||||
|
|
||||||
|
If your username has an **even number of vowels**, you are a member |
||||||
|
of the [Secret Society of the Fish Slappers](fishslap.md). |
||||||
|
Click the link to log in and proceed, or else be attacked by |
||||||
|
the Github-Heroku attack rabbits. |
||||||
|
|
||||||
|
If your username has an **odd number of vowels**, you are a member |
||||||
|
of the [Secret Ministerial Department for Theoretical Silly Walk Studies](sillywalk.md). |
||||||
|
Click the link to log in and proceed, or else be attacked by |
||||||
|
the Github-Heroku attack rabbits. |
||||||
|
|