This repository has been archived by the owner on Jul 14, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e207272
commit c904a2b
Showing
14 changed files
with
492 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from flask import Flask | ||
from flask.ext.sqlalchemy import SQLAlchemy | ||
|
||
from coreapi import CoreAPI | ||
|
||
#from flaskext.mysql import MySQL | ||
|
||
app = Flask(__name__) | ||
app.config.from_object('config') | ||
|
||
CoreAPI.check_app(app) | ||
coreapi = CoreAPI(app) | ||
|
||
db = SQLAlchemy(app) | ||
|
||
from app import controllers, models |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
from binascii import unhexlify | ||
from hashlib import sha256 | ||
from datetime import datetime | ||
|
||
from braveapi.client import API | ||
from ecdsa.keys import SigningKey, VerifyingKey | ||
from ecdsa.curves import NIST256p | ||
|
||
from flask import render_template, flash, redirect, url_for, abort, request, jsonify, session, g | ||
from app import app, coreapi, db, models | ||
from .forms import ConfirmStanding, SearchStanding | ||
from .coreapi import CoreAPI, CoreSession | ||
|
||
import cgi | ||
|
||
|
||
CHECK_LOGIN_EXCLUDE_ENDPOINTS = ['authorize', 'authorized'] | ||
@app.before_request | ||
def check_login(): | ||
if request.endpoint not in CHECK_LOGIN_EXCLUDE_ENDPOINTS: | ||
if 'token' in session: | ||
core_session = coreapi.get_session(session['token']) | ||
# TODO: check perms here | ||
g.core_session = core_session | ||
g.user = core_session.get_user() | ||
else: | ||
return redirect('/authorize') | ||
|
||
@app.route('/session_info') | ||
def session_info(): | ||
if g.core_session: | ||
return jsonify(g.core_session.info) | ||
return "No active session" | ||
|
||
@app.route('/perm_info') | ||
def perm_info(): | ||
return "View: {0}<br/>Edit: {1}<br/>core.application.create: {2}" \ | ||
.format(g.core_session.has_view_perm(), g.core_session.has_edit_perm(), g.core_session.has_perm('core.application.create')) | ||
|
||
|
||
@app.route('/') | ||
@app.route('/standings') | ||
def index(): | ||
standings = models.Standing.query.all() | ||
|
||
return render_template("standings.html", | ||
title='Standings', | ||
user=g.user, | ||
standings=standings) | ||
|
||
@app.route('/add_standing', methods=['GET', 'POST']) | ||
def add_standing(): | ||
|
||
form = SearchStanding() | ||
if form.validate_on_submit(): | ||
|
||
if form.entity_type.data == 'alliance': | ||
flash('alliance') | ||
else: | ||
flash('corp') | ||
preview_form = ConfirmStanding() | ||
return render_template('standing_add_preview.html', | ||
form = preview_form, | ||
entity_type = form.entity_type.data, | ||
standing = form.standing.data, | ||
entity_id = 'id', | ||
corporation_name = 'corp name', | ||
corporation_ticker = 'corp ticker', | ||
alliance_name = 'alliance name', | ||
alliance_ticker = 'alliance ticker') | ||
if form.errors: | ||
for field_name, field_errors in form.errors.items(): | ||
flash(u"Error in %s: %s" % (form[field_name].label.text, "<br>".join(field_errors))) | ||
return render_template('standing_add_search.html', | ||
title='Add Standing', | ||
form=form) | ||
|
||
|
||
@app.route('/do_edit_standing', methods=['GET', 'POST']) | ||
def do_edit_standing(): | ||
form = ConfirmStanding() | ||
if form.validate_on_submit(): | ||
if form.entity_type == 'alliance': | ||
flash('alliance') | ||
models.Standing.query.filter_by() | ||
else: | ||
flash('corp') | ||
|
||
|
||
if form.errors: | ||
for field_name, field_errors in form.errors.items(): | ||
flash(u"Error in %s: %s" % (form[field_name].label.text, "<br>".join(field_errors))) | ||
return redirect('/add_standing') | ||
|
||
# Perform the initial API call and direct the user. | ||
@app.route('/authorize') | ||
def authorize(): | ||
api = coreapi.get_api() | ||
|
||
# Build Success/Failure Redirect URLs | ||
success = str("http://"+app.config['SERVER_NAME']+url_for('authorized')) | ||
failure = str("http://"+app.config['SERVER_NAME']+url_for('fail')) | ||
|
||
# Make the authentication call to the CORE Service | ||
result = api.core.authorize(success=success, failure=failure) | ||
|
||
print result.__repr__() | ||
|
||
if 'location' in result: | ||
# Redirect based on the authentication request validity | ||
return redirect(result.location) | ||
else: | ||
return 'Error authorizing app: {0}'.format(result.message) | ||
|
||
|
||
# Root URI | ||
@app.route('/authorized') | ||
def authorized(): | ||
# Perform the initial API call and direct the user. | ||
|
||
api = coreapi.get_api() | ||
|
||
# Build Success/Failure Redirect URLs | ||
token = request.args.get('token', '') | ||
|
||
if not token: | ||
abort(401) | ||
|
||
session['token'] = token | ||
|
||
return redirect('/session_info') | ||
|
||
|
||
# Root URI | ||
@app.route('/fail') | ||
def fail(): | ||
abort(401) | ||
|
||
@app.route('/logout') | ||
@app.route('/ciao') | ||
def logout(): | ||
session.pop('token', None) | ||
return "Logged out" | ||
|
||
@app.route('/posts') | ||
def posts(): | ||
user = {'nickname': 'Miguel'} # fake user | ||
posts = [ # fake array of posts | ||
{ | ||
'author': {'nickname': 'John'}, | ||
'body': 'Beautiful day in Portland!' | ||
}, | ||
{ | ||
'author': {'nickname': 'Susan'}, | ||
'body': 'The Avengers movie was so cool!' | ||
} | ||
] | ||
return render_template("index.html", | ||
title='Home', | ||
user=user, | ||
posts=posts) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
# CORE_ENDPOINT: Url of the CORE API server your connecting to. Must not have a trailing '/', usually points to /api on the deployed domain | ||
# CORE_APP_ID: The ID of your registered application on the application management page in Core | ||
# CORE_PRIVATE_KEY: Your applications Private ECDSA Key as generated in the check_app(app) function | ||
# CORE_CORE_PUBLIC_KEY: The Core API server's Public ECDSA Key, printed in HEX on the application management page. | ||
|
||
import textwrap | ||
|
||
from binascii import unhexlify | ||
from hashlib import sha256 | ||
|
||
from ecdsa.keys import SigningKey, VerifyingKey | ||
from ecdsa.curves import NIST256p | ||
|
||
from braveapi.client import API | ||
|
||
class CoreAPI: | ||
required_config_variables_ = ['CORE_ENDPOINT', 'CORE_APP_ID', 'CORE_PRIVATE_KEY', \ | ||
'CORE_CORE_PUBLIC_KEY', 'CORE_VIEW_PERMISSION', 'CORE_EDIT_PERMISSION'] | ||
|
||
def __init__(self, app): | ||
self.endpoint = app.config['CORE_ENDPOINT'] | ||
self.app_id = app.config['CORE_APP_ID'] | ||
self.private_key_string = app.config['CORE_PRIVATE_KEY'] | ||
self.core_public_key_string = app.config['CORE_CORE_PUBLIC_KEY'] | ||
|
||
self.private_key = SigningKey.from_string(unhexlify(self.private_key_string), curve=NIST256p, hashfunc=sha256) | ||
self.core_public_key = VerifyingKey.from_string(unhexlify(self.core_public_key_string), curve=NIST256p, hashfunc=sha256) | ||
|
||
self.view_perm = app.config['CORE_VIEW_PERMISSION'] | ||
self.edit_perm = app.config['CORE_EDIT_PERMISSION'] | ||
|
||
@staticmethod | ||
def check_app(app): | ||
try: | ||
for var in CoreAPI.required_config_variables_: | ||
app.config[var] | ||
except KeyError as e: | ||
private = SigningKey.generate(NIST256p, hashfunc=sha256) | ||
|
||
error_message = "\n================================================================================\n" | ||
|
||
error_message += "Core Service API identity, public, or private key missing.\n\n" | ||
|
||
error_message += "Here's a new private key; update the api.private setting to reflect this.\n" + \ | ||
"%s \n\n" % private.to_string().encode('hex') | ||
|
||
error_message += "Here's that key's public key; this is what you register with Core.\n" + \ | ||
"%s \n\n" % private.get_verifying_key().to_string().encode('hex') | ||
|
||
error_message += textwrap.fill("After registering, make sure to populate all of the following in config.py: {0}" \ | ||
.format(", ".join(CoreAPI.required_config_variables_))) | ||
|
||
error_message += "\n================================================================================\n\n" | ||
|
||
print error_message | ||
raise | ||
|
||
def get_api(self): | ||
return API(self.endpoint, self.app_id, self.private_key, self.core_public_key) | ||
|
||
def get_session(self, token): | ||
info = self.get_api().core.info(token=token) | ||
return CoreSession(self, info) | ||
|
||
def check_logged_in(self): | ||
return | ||
|
||
|
||
class CoreSession: | ||
def __init__(self, coreapi, info): | ||
self.info = info | ||
self.coreapi = coreapi | ||
|
||
def has_perm(self, perm): | ||
return perm in self.info.perms | ||
|
||
def has_view_perm(self): | ||
return self.has_perm(self.coreapi.view_perm) | ||
|
||
def has_edit_perm(self): | ||
return self.has_perm(self.coreapi.edit_perm) | ||
|
||
def get_user(self): | ||
alliance = None | ||
if 'alliance' in self.info: | ||
alliance = self.info.alliance.name | ||
return User(self.info.character.name, | ||
self.info.corporation.name, | ||
alliance) | ||
|
||
class User: | ||
def __init__(self, character, corporation, alliance): | ||
self.character = character | ||
self.corporation = corporation | ||
self.alliance = alliance |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from flask.ext.wtf import Form | ||
from wtforms import StringField, BooleanField, RadioField, SubmitField, HiddenField | ||
from wtforms.validators import DataRequired | ||
|
||
class SearchStanding(Form): | ||
entity_type = RadioField('Entity Type', | ||
choices=[('alliance', 'Alliance'), ('corporation', 'Corporation')], | ||
default='alliance', | ||
validators=[DataRequired()]) | ||
search_text = StringField('Search', validators=[DataRequired()]) | ||
standing = RadioField('Standing', | ||
choices=[('+10', '+10'), ('+5', '+5'), ('+2.5', '+2.5'), ('+1.1', '+1.1'), ('0', '0'), ('-5', '-5'), ('-10', '-10')], | ||
default='+2.5', | ||
validators=[DataRequired()]) | ||
add_by_id = SubmitField('Add By Id') | ||
add_by_name = SubmitField('Add By Name') | ||
add_by_ticker = SubmitField('Add By Ticker') | ||
|
||
class ConfirmStanding(Form): | ||
entity_type = HiddenField('Entity Type', validators=[DataRequired()]) | ||
standing = HiddenField('Search', validators=[DataRequired()]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
from app import db | ||
|
||
class Character(db.Model): | ||
id = db.Column(db.Integer, primary_key=True) | ||
name = db.Column(db.String(1024), index=True, unique=True) | ||
corporation_id = db.Column(db.Integer, db.ForeignKey('corporation.id')) | ||
alliance_id = db.Column(db.Integer, db.ForeignKey('alliance.id')) | ||
|
||
class Corporation(db.Model): | ||
id = db.Column(db.Integer, primary_key=True) | ||
name = db.Column(db.String(1024), index=True, unique=True) | ||
ticker = db.Column(db.String(1024), index=True, unique=True) | ||
alliance_id = db.Column(db.Integer, db.ForeignKey('alliance.id')) | ||
characters = db.relationship('Character', backref='corporation', lazy='dynamic') | ||
standings = db.relationship('Standing', backref='corporation', lazy='dynamic') | ||
|
||
class Alliance(db.Model): | ||
id = db.Column(db.Integer, primary_key=True) | ||
name = db.Column(db.String(1024), index=True, unique=True) | ||
ticker = db.Column(db.String(1024), index=True, unique=True) | ||
characters = db.relationship('Character', backref='alliance', lazy='dynamic') | ||
corporations = db.relationship('Corporation', backref='alliance', lazy='dynamic') | ||
standings = db.relationship('Standing', backref='alliance', lazy='dynamic') | ||
|
||
class Standing(db.Model): | ||
id = db.Column(db.Integer, primary_key=True) | ||
standing = db.Column(db.String(16), index=True) | ||
corporation_id = db.Column(db.Integer, db.ForeignKey('corporation.id')) | ||
alliance_id = db.Column(db.Integer, db.ForeignKey('alliance.id')) | ||
creator_id = db.Column(db.Integer, db.ForeignKey('character.id')) | ||
editor_id = db.Column(db.Integer, db.ForeignKey('character.id')) | ||
|
||
class StandingChange(db.Model): | ||
id = db.Column(db.Integer, primary_key=True) | ||
corporation_id = db.Column(db.Integer, db.ForeignKey('corporation.id')) | ||
alliance_id = db.Column(db.Integer, db.ForeignKey('alliance.id')) | ||
date = db.Column(db.DateTime) | ||
character_name = db.Column(db.String(1024)) | ||
character_corporation_name = db.Column(db.String(1024)) | ||
character_corporation_ticker = db.Column(db.String(1024)) | ||
character_alliance_name = db.Column(db.String(1024)) | ||
character_alliance_ticker = db.Column(db.String(1024)) | ||
|
||
def find_or_create_corporation_standing(corporation_eve_id, character_eve_id): | ||
standing = self.query.filter_by(corporation_id = corpororation_eve_id).first() | ||
if not standing: | ||
standing = self.__class__() | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
<html> | ||
<head> | ||
{% if title %} | ||
<title>{{ title }} - standings</title> | ||
{% else %} | ||
<title>standings</title> | ||
{% endif %} | ||
|
||
<link href="/static/css/bootstrap.min.css" rel="stylesheet" media="screen"> | ||
<link href="/static/css/bootstrap-responsive.min.css" rel="stylesheet"> | ||
<script src="http://code.jquery.com/jquery-latest.js"></script> | ||
<script src="/static/js/bootstrap.min.js"></script> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<link href="/static/css/base.css" rel="stylesheet"> | ||
</head> | ||
<body> | ||
<nav class="navbar navbar-inverse navbar-fixed-top"> | ||
<div class="container"> | ||
<div class="navbar-header"> | ||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse"> | ||
<span class="sr-only">Toggle navigation</span> | ||
<span class="icon-bar"></span> | ||
<span class="icon-bar"></span> | ||
<span class="icon-bar"></span> | ||
</button> | ||
<a class="navbar-brand" href="/"> | ||
<strong>HERO</strong> | ||
<small>Standings</small> | ||
</a> | ||
</div> | ||
<div class="collapse navbar-collapse"> | ||
<ul class="nav navbar-nav"> | ||
<li><a href="/">Home</a></li> | ||
<li><a href="/add_standing">Add</a></li> | ||
</ul> | ||
<ul class="nav navbar-nav navbar-right"> | ||
<li><a href="/logout">Log out</a></li> | ||
</ul> | ||
</div> | ||
</div> | ||
</nav> | ||
<div class="container"> | ||
{% include 'flash.html' %} | ||
{% block content %}{% endblock %} | ||
</div> | ||
</body> | ||
</html> |
Oops, something went wrong.