Skip to content
This repository has been archived by the owner on Oct 1, 2024. It is now read-only.

Commit

Permalink
initial commit (template)
Browse files Browse the repository at this point in the history
  • Loading branch information
pkage committed Feb 2, 2020
0 parents commit 26022c9
Show file tree
Hide file tree
Showing 15 changed files with 284 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
**/__pycache__
**/env_ccauth
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
__pycache__/
instance/secret.json
env_*
*.pyc
.DS_Store
11 changes: 11 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM python:alpine3.7

COPY . /app
WORKDIR /app

RUN apk add make

RUN pip install -r requirements.txt

EXPOSE 5000
CMD make prod
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# compsoc committee sso

## what?

This is a tool to allow compsoc committee members to sign into internal applications, using our existing google admin platform.

## why?

This lets us greatly reduce the overhead of launching new applications, as we can shift account management up to the long-suffering administrator.

## how?

This is just a demo application, intended as a starting off point for creating new applications. You'll need to create a project on (ideally CompSoc's) [GCP](https://console.cloud.google.com/), issue a client ID and secret for a web oauth application, and properly configure the callback urls.

More verbosely:

1) Log into [GCP](https://console.cloud.google.com), and create a new project by clicking th project header on the title bar and clicking "New Project." Ideally this should be created under the "comp-soc.com" domain.

2) Once you've created the project, go to the sidebar > APIs & Services > Credentials. You'll need to add routes like so:

![credentials](/docs/credentials.png?raw=true)
![routes](/docs/routes.png?raw=true)

3) You'll also need to enable access to the People API, which is used to retrieve a profile photo and other information. This can be done through the sidebar > APIs & Services > Library portal.

Once you've done that, you'll need to create your json configuration file in `instance/secret.json`:

```json
{
"client_id": "YOUR_GOOGLE_CLIENT_ID",
"client_secret": "YOUR_GOOGLE_CLIENT_SECRET",
"app_secret_key": "SOME_RANDOM_STRING"
}
```

Then you should be good to go! Start the server with:

```
$ make run
```

# who?

This was written in a fit of procrastination by [@pkage](//kage.dev).
77 changes: 77 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from flask import Flask, redirect, url_for, render_template
from flask_dance.contrib.google import make_google_blueprint, google
from oauthlib.oauth2.rfc6749.errors import TokenExpiredError
import requests
import json

secrets = json.load(open('instance/secret.json', 'r'))

app = Flask(__name__)
app.secret_key = secrets['app_secret_key']
blueprint = make_google_blueprint(
client_id=secrets['client_id'],
client_secret=secrets['client_secret'],
scope=['profile', 'email']
)
app.register_blueprint(blueprint, url_prefix='/login')

# magic happens here
def email_valid(email):
if email.endswith('@comp-soc.com') or email.endswith('@hacktheburgh.com') or email.endswith('@sigint.mx'):
return True
return False

@app.route('/')
def index():
if google.authorized:
return redirect(url_for('profile'))
return render_template('login.html')

@app.route('/logout')
def logout():
# retrieve token
token = blueprint.token["access_token"]

try:
# revoke permission from Google's API
resp = google.post(
"https://accounts.google.com/o/oauth2/revoke",
params={"token": token},
headers={"Content-Type": "application/x-www-form-urlencoded"}
)
assert resp.ok, resp.text
except TokenExpiredError as e:
print('token expired, ignoring')
del blueprint.token # Delete OAuth token from storage
return redirect(url_for('index'))

@app.route('/profile')
def profile():
if not google.authorized:
return redirect(url_for('google.login'))
# print(dir(google.get))
# print('google token: {}'.format(google.token[u'access_token']))
resp = requests.get(
'https://people.googleapis.com/v1/people/me',
params={
'personFields': 'names,emailAddresses,photos'
},
headers={
'Authorization': 'Bearer {}'.format(google.token[u'access_token'])
})

person_info = resp.json()

profile = {
'email': person_info[u'emailAddresses'][0][u'value'],
'name': person_info[u'names'][0][u'displayName'],
'image': person_info[u'photos'][0][u'url'].split('=')[0] # remove the 100px limit (ends with =s100)
}

return render_template('profile.html',
profile=profile,
valid=email_valid(profile['email'])
)

if __name__ == '__main__':
app.run()
Empty file added docker-compose.yml
Empty file.
Binary file added docs/credentials.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/routes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
all: run


run:
OAUTHLIB_INSECURE_TRANSPORT=1 OAUTHLIB_RELAX_TOKEN_SCOPE=1 FLASK_DEBUG=1 flask run

prod:
FLASK_DEBUG=0 flask run
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
flask
Flask-Dance
requests
Binary file added static/compsoc-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
81 changes: 81 additions & 0 deletions static/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
@import url('https://fonts.googleapis.com/css?family=Merriweather&display=swap');

body {
margin: 0;
background-color: #725752;

font-family: 'Merriweather', serif;
}

main {
min-height: 100vh;

display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

.row {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}

/* -- LOGIN PAGE -- */

.login__bg {
}
.login {
color: white;
opacity: 0.7;

transition: opacity 0.25s cubic-bezier(0,0,0.3,1);
text-decoration: none;
}
.login > span {
margin-right: 0.5em;
}
.login:hover {
opacity: 1;
}

/* -- PROFILE -- */

.profile__bg {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;


color: white;
min-height: 100vh;
}

.profile__block {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

.profile__img {
width: 300px;
height: 300px;
margin-bottom: 1em;
}

.profile__status {
margin: 1em 0 1em 0;
}

.profile__status--invalid {
color: #ee6c4d;
}

.profile__status--valid {
color: #98af57;
}

14 changes: 14 additions & 0 deletions templates/base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>

<html>
<head>
<title>compsoc sso demo</title>
<link href="{{ url_for('static', filename='index.css') }}" rel="stylesheet" type="text/css">
<link href="{{ url_for('static', filename='compsoc-icon.png') }}" type="image/png" rel="icon">

<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
13 changes: 13 additions & 0 deletions templates/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{% extends "base.html" %}

{% block content %}

<main class="login__bg">
<a href="{{ url_for('profile') }}" class="login row">
<span>login </span>
<i class="fas fa-arrow-right"></i>
</a>
</main>

{% endblock %}

26 changes: 26 additions & 0 deletions templates/profile.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{% extends "base.html" %}

{% block content %}

<div class="profile__bg {{ 'profile__bg--valid' if valid else ''}}">
<div class="profile__block">
<img src="{{ profile.image }}" alt="{{ profile.name }}" class="profile__img">
<div class="profile__name">{{ profile.name }}</div>
<div class="profile__status {{ 'profile__status--valid' if valid else 'profile__status--invalid' }}">
{% if valid %}
<i class="fas fa-check"></i>
<span> verified compsoc committee member!</span>
{% else %}
<i class="fas fa-times"></i>
<span> not a compsoc committee member.</span>
{% endif %}
</div>

<a href="{{ url_for('logout') }}" class="login row">
<span>logout </span>
<i class="fas fa-arrow-right"></i>
</a>
</div>
</div>

{% endblock %}

0 comments on commit 26022c9

Please sign in to comment.