2013-09-25 20:46:28 +00:00
|
|
|
import logging
|
2013-10-10 03:00:34 +00:00
|
|
|
import requests
|
2013-11-18 19:49:54 +00:00
|
|
|
import stripe
|
2013-09-25 20:46:28 +00:00
|
|
|
|
2013-10-14 21:50:07 +00:00
|
|
|
from flask import (abort, redirect, request, url_for, render_template,
|
2013-12-30 22:05:27 +00:00
|
|
|
make_response, Response, Blueprint)
|
2014-01-14 20:23:44 +00:00
|
|
|
from flask.ext.login import login_required, current_user
|
2013-11-19 00:17:58 +00:00
|
|
|
from urlparse import urlparse
|
2013-09-25 16:45:12 +00:00
|
|
|
|
|
|
|
from data import model
|
2013-12-27 23:01:44 +00:00
|
|
|
from app import app, mixpanel
|
|
|
|
from auth.permissions import AdministerOrganizationPermission
|
2013-11-18 19:49:54 +00:00
|
|
|
from util.invoice import renderInvoiceToPdf
|
2013-11-18 23:42:27 +00:00
|
|
|
from util.seo import render_snapshot
|
2014-01-02 23:01:34 +00:00
|
|
|
from util.cache import no_cache
|
2013-12-27 22:19:14 +00:00
|
|
|
from endpoints.api import get_route_data
|
|
|
|
from endpoints.common import common_login
|
2013-09-25 20:46:28 +00:00
|
|
|
|
2013-11-19 00:17:58 +00:00
|
|
|
|
2013-09-25 20:46:28 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2013-12-30 22:05:27 +00:00
|
|
|
web = Blueprint('web', __name__)
|
|
|
|
|
2013-09-25 20:46:28 +00:00
|
|
|
|
2014-01-15 18:55:57 +00:00
|
|
|
def render_page_template(name, **kwargs):
|
2014-01-23 19:51:43 +00:00
|
|
|
|
|
|
|
resp = make_response(render_template(name, route_data=get_route_data(),
|
2014-01-15 18:55:57 +00:00
|
|
|
**kwargs))
|
2013-12-28 18:28:52 +00:00
|
|
|
resp.headers['X-FRAME-OPTIONS'] = 'DENY'
|
|
|
|
return resp
|
2013-09-25 16:45:12 +00:00
|
|
|
|
|
|
|
|
2013-12-30 22:05:27 +00:00
|
|
|
@web.route('/', methods=['GET'], defaults={'path': ''})
|
|
|
|
@web.route('/repository/<path:path>', methods=['GET'])
|
|
|
|
@web.route('/organization/<path:path>', methods=['GET'])
|
2014-01-02 23:01:34 +00:00
|
|
|
@no_cache
|
2013-10-10 23:06:04 +00:00
|
|
|
def index(path):
|
2013-12-27 22:19:14 +00:00
|
|
|
return render_page_template('index.html')
|
2013-09-25 16:45:12 +00:00
|
|
|
|
|
|
|
|
2013-12-30 22:05:27 +00:00
|
|
|
@web.route('/snapshot', methods=['GET'])
|
|
|
|
@web.route('/snapshot/', methods=['GET'])
|
|
|
|
@web.route('/snapshot/<path:path>', methods=['GET'])
|
2013-11-18 22:11:06 +00:00
|
|
|
def snapshot(path = ''):
|
2013-11-19 00:17:58 +00:00
|
|
|
parsed = urlparse(request.url)
|
|
|
|
final_url = '%s://%s/%s' % (parsed.scheme, 'localhost', path)
|
|
|
|
result = render_snapshot(final_url)
|
2013-11-18 22:11:06 +00:00
|
|
|
if result:
|
|
|
|
return result
|
|
|
|
|
|
|
|
abort(404)
|
|
|
|
|
|
|
|
|
2013-12-30 22:05:27 +00:00
|
|
|
@web.route('/plans/')
|
2014-01-02 23:01:34 +00:00
|
|
|
@no_cache
|
2013-10-14 02:06:31 +00:00
|
|
|
def plans():
|
|
|
|
return index('')
|
|
|
|
|
|
|
|
|
2013-12-30 22:05:27 +00:00
|
|
|
@web.route('/guide/')
|
2014-01-02 23:01:34 +00:00
|
|
|
@no_cache
|
2013-10-14 02:06:31 +00:00
|
|
|
def guide():
|
|
|
|
return index('')
|
|
|
|
|
|
|
|
|
2013-12-30 22:05:27 +00:00
|
|
|
@web.route('/organizations/')
|
|
|
|
@web.route('/organizations/new/')
|
2014-01-02 23:01:34 +00:00
|
|
|
@no_cache
|
2013-11-07 06:48:58 +00:00
|
|
|
def organizations():
|
|
|
|
return index('')
|
|
|
|
|
2013-12-27 23:01:44 +00:00
|
|
|
|
2013-12-30 22:05:27 +00:00
|
|
|
@web.route('/user/')
|
2014-01-02 23:01:34 +00:00
|
|
|
@no_cache
|
2013-10-14 02:06:31 +00:00
|
|
|
def user():
|
|
|
|
return index('')
|
|
|
|
|
|
|
|
|
2013-12-30 22:05:27 +00:00
|
|
|
@web.route('/signin/')
|
2014-01-02 23:01:34 +00:00
|
|
|
@no_cache
|
2013-10-14 21:50:07 +00:00
|
|
|
def signin():
|
|
|
|
return index('')
|
|
|
|
|
|
|
|
|
2014-01-23 19:51:43 +00:00
|
|
|
@web.route('/contact/')
|
|
|
|
@no_cache
|
2013-12-17 22:02:37 +00:00
|
|
|
def contact():
|
|
|
|
return index('')
|
|
|
|
|
|
|
|
|
2013-12-30 22:05:27 +00:00
|
|
|
@web.route('/new/')
|
2014-01-02 23:01:34 +00:00
|
|
|
@no_cache
|
2013-10-24 21:41:55 +00:00
|
|
|
def new():
|
|
|
|
return index('')
|
|
|
|
|
|
|
|
|
2013-12-30 22:05:27 +00:00
|
|
|
@web.route('/repository/')
|
2014-01-02 23:01:34 +00:00
|
|
|
@no_cache
|
2013-10-16 01:50:14 +00:00
|
|
|
def repository():
|
|
|
|
return index('')
|
|
|
|
|
2013-11-22 20:54:23 +00:00
|
|
|
|
2013-12-30 22:05:27 +00:00
|
|
|
@web.route('/security/')
|
2014-01-02 23:01:34 +00:00
|
|
|
@no_cache
|
2013-11-22 20:54:23 +00:00
|
|
|
def security():
|
|
|
|
return index('')
|
|
|
|
|
|
|
|
|
2013-12-30 22:05:27 +00:00
|
|
|
@web.route('/v1')
|
|
|
|
@web.route('/v1/')
|
2014-01-02 23:01:34 +00:00
|
|
|
@no_cache
|
2013-10-17 21:45:08 +00:00
|
|
|
def v1():
|
|
|
|
return index('')
|
2013-10-16 01:50:14 +00:00
|
|
|
|
2013-11-22 20:54:23 +00:00
|
|
|
|
2013-12-30 22:05:27 +00:00
|
|
|
@web.route('/status', methods=['GET'])
|
2014-01-02 23:01:34 +00:00
|
|
|
@no_cache
|
2013-10-02 18:35:21 +00:00
|
|
|
def status():
|
|
|
|
return make_response('Healthy')
|
|
|
|
|
|
|
|
|
2013-12-30 22:05:27 +00:00
|
|
|
@web.route('/tos', methods=['GET'])
|
2014-01-02 23:01:34 +00:00
|
|
|
@no_cache
|
2013-10-01 21:44:13 +00:00
|
|
|
def tos():
|
2013-12-27 22:19:14 +00:00
|
|
|
return render_page_template('tos.html')
|
2013-10-01 21:44:13 +00:00
|
|
|
|
|
|
|
|
2013-12-30 22:05:27 +00:00
|
|
|
@web.route('/disclaimer', methods=['GET'])
|
2014-01-02 23:01:34 +00:00
|
|
|
@no_cache
|
2013-11-22 17:32:05 +00:00
|
|
|
def disclaimer():
|
2013-12-27 22:19:14 +00:00
|
|
|
return render_page_template('disclaimer.html')
|
2013-11-22 17:32:05 +00:00
|
|
|
|
|
|
|
|
2013-12-30 22:05:27 +00:00
|
|
|
@web.route('/privacy', methods=['GET'])
|
2014-01-02 23:01:34 +00:00
|
|
|
@no_cache
|
2013-10-01 21:44:13 +00:00
|
|
|
def privacy():
|
2013-12-27 22:19:14 +00:00
|
|
|
return render_page_template('privacy.html')
|
2013-10-01 21:44:13 +00:00
|
|
|
|
|
|
|
|
2013-12-30 22:05:27 +00:00
|
|
|
@web.route('/receipt', methods=['GET'])
|
2013-11-18 19:49:54 +00:00
|
|
|
def receipt():
|
2013-12-21 03:38:53 +00:00
|
|
|
if not current_user.is_authenticated():
|
|
|
|
abort(401)
|
|
|
|
return
|
|
|
|
|
2013-11-18 19:49:54 +00:00
|
|
|
id = request.args.get('id')
|
|
|
|
if id:
|
|
|
|
invoice = stripe.Invoice.retrieve(id)
|
|
|
|
if invoice:
|
2013-12-21 03:38:53 +00:00
|
|
|
user_or_org = model.get_user_or_org_by_customer_id(invoice.customer)
|
|
|
|
|
|
|
|
if user_or_org:
|
|
|
|
if user_or_org.organization:
|
|
|
|
admin_org = AdministerOrganizationPermission(user_or_org.username)
|
|
|
|
if not admin_org.can():
|
|
|
|
abort(404)
|
|
|
|
return
|
|
|
|
else:
|
|
|
|
if not user_or_org.username == current_user.db_user().username:
|
|
|
|
abort(404)
|
|
|
|
return
|
|
|
|
|
|
|
|
file_data = renderInvoiceToPdf(invoice, user_or_org)
|
|
|
|
return Response(file_data,
|
|
|
|
mimetype="application/pdf",
|
|
|
|
headers={"Content-Disposition": "attachment;filename=receipt.pdf"})
|
2013-11-18 19:49:54 +00:00
|
|
|
abort(404)
|
|
|
|
|
2013-09-27 23:55:04 +00:00
|
|
|
|
2014-01-14 20:23:44 +00:00
|
|
|
def exchange_github_code_for_token(code):
|
2013-10-10 03:00:34 +00:00
|
|
|
code = request.args.get('code')
|
|
|
|
payload = {
|
|
|
|
'client_id': app.config['GITHUB_CLIENT_ID'],
|
|
|
|
'client_secret': app.config['GITHUB_CLIENT_SECRET'],
|
|
|
|
'code': code,
|
|
|
|
}
|
|
|
|
headers = {
|
|
|
|
'Accept': 'application/json'
|
|
|
|
}
|
|
|
|
|
|
|
|
get_access_token = requests.post(app.config['GITHUB_TOKEN_URL'],
|
|
|
|
params=payload, headers=headers)
|
|
|
|
|
|
|
|
token = get_access_token.json()['access_token']
|
2014-01-14 20:23:44 +00:00
|
|
|
return token
|
|
|
|
|
2013-10-10 03:00:34 +00:00
|
|
|
|
2014-01-14 20:23:44 +00:00
|
|
|
def get_github_user(token):
|
2013-10-10 03:00:34 +00:00
|
|
|
token_param = {
|
|
|
|
'access_token': token,
|
|
|
|
}
|
|
|
|
get_user = requests.get(app.config['GITHUB_USER_URL'], params=token_param)
|
|
|
|
|
2014-01-14 20:23:44 +00:00
|
|
|
return get_user.json()
|
|
|
|
|
|
|
|
|
2014-01-23 19:51:43 +00:00
|
|
|
@web.route('/oauth2/github/callback', methods=['GET'])
|
2014-01-14 20:23:44 +00:00
|
|
|
def github_oauth_callback():
|
2014-01-15 18:55:57 +00:00
|
|
|
error = request.args.get('error', None)
|
|
|
|
if error:
|
|
|
|
return render_page_template('githuberror.html', error_message=error)
|
|
|
|
|
2014-01-14 20:23:44 +00:00
|
|
|
token = exchange_github_code_for_token(request.args.get('code'))
|
|
|
|
user_data = get_github_user(token)
|
|
|
|
|
2013-10-10 03:00:34 +00:00
|
|
|
username = user_data['login']
|
|
|
|
github_id = user_data['id']
|
|
|
|
|
|
|
|
v3_media_type = {
|
|
|
|
'Accept': 'application/vnd.github.v3'
|
|
|
|
}
|
2014-01-15 18:55:57 +00:00
|
|
|
|
|
|
|
token_param = {
|
|
|
|
'access_token': token,
|
|
|
|
}
|
2013-10-10 03:00:34 +00:00
|
|
|
get_email = requests.get(app.config['GITHUB_USER_EMAILS'],
|
|
|
|
params=token_param, headers=v3_media_type)
|
|
|
|
|
|
|
|
# We will accept any email, but we prefer the primary
|
|
|
|
found_email = None
|
|
|
|
for user_email in get_email.json():
|
|
|
|
found_email = user_email['email']
|
|
|
|
if user_email['primary']:
|
|
|
|
break
|
|
|
|
|
|
|
|
to_login = model.verify_federated_login('github', github_id)
|
|
|
|
if not to_login:
|
|
|
|
# try to create the user
|
2013-10-10 16:55:03 +00:00
|
|
|
try:
|
|
|
|
to_login = model.create_federated_user(username, found_email, 'github',
|
|
|
|
github_id)
|
2013-10-15 05:12:23 +00:00
|
|
|
|
|
|
|
# Success, tell mixpanel
|
|
|
|
mixpanel.track(to_login.username, 'register', {'service': 'github'})
|
|
|
|
|
|
|
|
state = request.args.get('state', None)
|
|
|
|
if state:
|
|
|
|
logger.debug('Aliasing with state: %s' % state)
|
|
|
|
mixpanel.alias(to_login.username, state)
|
|
|
|
|
2013-10-10 16:55:03 +00:00
|
|
|
except model.DataModelException, ex:
|
2013-12-27 22:19:14 +00:00
|
|
|
return render_page_template('githuberror.html', error_message=ex.message)
|
2013-10-10 03:00:34 +00:00
|
|
|
|
|
|
|
if common_login(to_login):
|
2014-01-30 22:23:14 +00:00
|
|
|
return redirect(url_for('web.index'))
|
2013-10-10 03:00:34 +00:00
|
|
|
|
2013-12-27 22:19:14 +00:00
|
|
|
return render_page_template('githuberror.html')
|
2013-09-25 16:45:12 +00:00
|
|
|
|
|
|
|
|
2014-01-23 19:51:43 +00:00
|
|
|
@web.route('/oauth2/github/callback/attach', methods=['GET'])
|
2014-01-14 20:23:44 +00:00
|
|
|
@login_required
|
|
|
|
def github_oauth_attach():
|
|
|
|
token = exchange_github_code_for_token(request.args.get('code'))
|
|
|
|
user_data = get_github_user(token)
|
|
|
|
github_id = user_data['id']
|
|
|
|
user_obj = current_user.db_user()
|
|
|
|
model.attach_federated_login(user_obj, 'github', github_id)
|
2014-01-30 22:23:14 +00:00
|
|
|
return redirect(url_for('web.user'))
|
2013-09-25 16:45:12 +00:00
|
|
|
|
|
|
|
|
2013-12-30 22:05:27 +00:00
|
|
|
@web.route('/confirm', methods=['GET'])
|
2013-09-27 23:29:01 +00:00
|
|
|
def confirm_email():
|
2013-09-27 23:55:04 +00:00
|
|
|
code = request.values['code']
|
2014-01-17 22:23:52 +00:00
|
|
|
user = None
|
|
|
|
new_email = None
|
2013-12-19 00:47:42 +00:00
|
|
|
|
|
|
|
try:
|
2014-01-17 22:23:52 +00:00
|
|
|
user, new_email = model.confirm_user_email(code)
|
2013-12-19 00:47:42 +00:00
|
|
|
except model.DataModelException as ex:
|
2014-01-17 22:23:52 +00:00
|
|
|
return render_page_template('confirmerror.html', error_message=ex.message)
|
2014-01-17 22:20:51 +00:00
|
|
|
|
2014-01-17 22:23:52 +00:00
|
|
|
common_login(user)
|
2013-09-27 23:55:04 +00:00
|
|
|
|
2014-01-30 22:23:14 +00:00
|
|
|
return redirect(url_for('web.user', tab='email')
|
|
|
|
if new_email else url_for('web.index'))
|
2013-09-27 23:29:01 +00:00
|
|
|
|
|
|
|
|
2013-12-30 22:05:27 +00:00
|
|
|
@web.route('/recovery', methods=['GET'])
|
2013-10-14 21:50:07 +00:00
|
|
|
def confirm_recovery():
|
|
|
|
code = request.values['code']
|
|
|
|
user = model.validate_reset_code(code)
|
2013-09-27 23:29:01 +00:00
|
|
|
|
2013-10-14 21:50:07 +00:00
|
|
|
if user:
|
|
|
|
common_login(user)
|
2014-01-30 22:23:14 +00:00
|
|
|
return redirect(url_for('web.user'))
|
2013-10-14 21:50:07 +00:00
|
|
|
else:
|
|
|
|
abort(403)
|