2016-03-08 09:24:57 +01:00
|
|
|
import logging
|
2015-04-07 12:42:50 -03:00
|
|
|
import os
|
2016-05-03 11:22:54 +02:00
|
|
|
import tempfile
|
2016-01-26 15:34:56 +01:00
|
|
|
from bson import ObjectId
|
|
|
|
from datetime import datetime
|
2015-10-20 23:52:18 +02:00
|
|
|
import bugsnag
|
2016-04-04 14:59:11 +02:00
|
|
|
import bugsnag.flask
|
|
|
|
import bugsnag.handlers
|
2016-02-22 16:48:53 +01:00
|
|
|
from zencoder import Zencoder
|
2015-05-18 11:42:17 -03:00
|
|
|
from flask import g
|
|
|
|
from flask import request
|
2015-10-11 22:20:18 +02:00
|
|
|
from flask import abort
|
2016-01-26 15:34:56 +01:00
|
|
|
from eve import Eve
|
2016-02-22 16:48:53 +01:00
|
|
|
|
2016-01-26 18:26:18 +01:00
|
|
|
from eve.auth import TokenAuth
|
2016-01-26 15:34:56 +01:00
|
|
|
from eve.io.mongo import Validator
|
2015-05-20 12:14:38 -03:00
|
|
|
|
2015-04-14 12:04:50 -03:00
|
|
|
RFC1123_DATE_FORMAT = '%a, %d %b %Y %H:%M:%S GMT'
|
|
|
|
|
2016-03-04 14:49:48 +01:00
|
|
|
|
2016-01-26 18:26:18 +01:00
|
|
|
class NewAuth(TokenAuth):
|
|
|
|
def check_auth(self, token, allowed_roles, resource, method):
|
2016-03-04 14:49:48 +01:00
|
|
|
return validate_token()
|
|
|
|
|
2016-01-26 18:26:18 +01:00
|
|
|
|
2015-03-10 11:38:57 +01:00
|
|
|
class ValidateCustomFields(Validator):
|
2015-10-10 16:27:02 +02:00
|
|
|
def convert_properties(self, properties, node_schema):
|
2015-10-08 09:24:34 +02:00
|
|
|
for prop in node_schema:
|
|
|
|
if not prop in properties:
|
|
|
|
continue
|
|
|
|
schema_prop = node_schema[prop]
|
|
|
|
prop_type = schema_prop['type']
|
|
|
|
if prop_type == 'dict':
|
2015-10-10 16:27:02 +02:00
|
|
|
properties[prop] = self.convert_properties(
|
2015-10-08 09:24:34 +02:00
|
|
|
properties[prop], schema_prop['schema'])
|
|
|
|
if prop_type == 'list':
|
|
|
|
if properties[prop] in ['', '[]']:
|
|
|
|
properties[prop] = []
|
|
|
|
for k, val in enumerate(properties[prop]):
|
|
|
|
if not 'schema' in schema_prop:
|
|
|
|
continue
|
|
|
|
item_schema = {'item': schema_prop['schema']}
|
|
|
|
item_prop = {'item': properties[prop][k]}
|
2015-10-10 16:27:02 +02:00
|
|
|
properties[prop][k] = self.convert_properties(
|
2015-10-08 09:24:34 +02:00
|
|
|
item_prop, item_schema)['item']
|
|
|
|
# Convert datetime string to RFC1123 datetime
|
|
|
|
elif prop_type == 'datetime':
|
|
|
|
prop_val = properties[prop]
|
|
|
|
properties[prop] = datetime.strptime(prop_val, RFC1123_DATE_FORMAT)
|
|
|
|
elif prop_type == 'objectid':
|
|
|
|
prop_val = properties[prop]
|
|
|
|
if prop_val:
|
|
|
|
properties[prop] = ObjectId(prop_val)
|
|
|
|
else:
|
|
|
|
properties[prop] = None
|
|
|
|
|
|
|
|
return properties
|
|
|
|
|
2015-03-11 16:03:19 +01:00
|
|
|
def _validate_valid_properties(self, valid_properties, field, value):
|
2016-01-25 16:32:50 +01:00
|
|
|
projects_collection = app.data.driver.db['projects']
|
|
|
|
lookup = {'_id': ObjectId(self.document['project'])}
|
|
|
|
project = projects_collection.find_one(lookup)
|
|
|
|
node_type = next(
|
|
|
|
(item for item in project['node_types'] if item.get('name') \
|
2016-03-21 12:25:07 +01:00
|
|
|
and item['name'] == self.document['node_type']), None)
|
2015-04-15 10:25:31 -03:00
|
|
|
try:
|
2015-10-08 09:24:34 +02:00
|
|
|
value = self.convert_properties(value, node_type['dyn_schema'])
|
2015-04-15 11:51:55 -03:00
|
|
|
except Exception, e:
|
|
|
|
print ("Error converting: {0}".format(e))
|
2015-04-14 12:04:50 -03:00
|
|
|
|
2015-03-11 16:03:19 +01:00
|
|
|
v = Validator(node_type['dyn_schema'])
|
|
|
|
val = v.validate(value)
|
2015-04-15 10:25:31 -03:00
|
|
|
|
2015-03-11 16:03:19 +01:00
|
|
|
if val:
|
|
|
|
return True
|
|
|
|
else:
|
2015-04-15 10:25:31 -03:00
|
|
|
try:
|
|
|
|
print (val.errors)
|
|
|
|
except:
|
|
|
|
pass
|
2015-03-27 15:42:28 +01:00
|
|
|
self._error(
|
|
|
|
field, "Error validating properties")
|
2015-03-11 16:03:19 +01:00
|
|
|
|
2016-03-21 12:25:07 +01:00
|
|
|
|
2015-10-08 10:26:22 +02:00
|
|
|
# We specify a settings.py file because when running on wsgi we can't detect it
|
2016-03-14 14:51:46 +01:00
|
|
|
# automatically. The default path (which works in Docker) can be overridden with
|
2015-10-08 10:26:22 +02:00
|
|
|
# an env variable.
|
2016-03-14 12:13:49 +01:00
|
|
|
settings_path = os.environ.get(
|
|
|
|
'EVE_SETTINGS', '/data/git/pillar/pillar/settings.py')
|
2015-10-11 22:20:18 +02:00
|
|
|
app = Eve(settings=settings_path, validator=ValidateCustomFields, auth=NewAuth)
|
2015-05-20 12:14:38 -03:00
|
|
|
|
2016-04-04 14:59:11 +02:00
|
|
|
# Load configuration from three different sources, to make it easy to override
|
|
|
|
# settings with secrets, as well as for development & testing.
|
|
|
|
app_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
app.config.from_pyfile(os.path.join(app_root, 'config.py'), silent=False)
|
|
|
|
app.config.from_pyfile(os.path.join(app_root, 'config_local.py'), silent=True)
|
|
|
|
from_envvar = os.environ.get('PILLAR_CONFIG')
|
|
|
|
if from_envvar:
|
|
|
|
# Don't use from_envvar, as we want different behaviour. If the envvar
|
|
|
|
# is not set, it's fine (i.e. silent=True), but if it is set and the
|
|
|
|
# configfile doesn't exist, it should error out (i.e. silent=False).
|
|
|
|
app.config.from_pyfile(from_envvar, silent=False)
|
2015-09-11 15:04:25 +02:00
|
|
|
|
2016-05-03 11:22:54 +02:00
|
|
|
# Set the TMP environment variable to manage where uploads are stored.
|
|
|
|
# These are all used by tempfile.mkstemp(), but we don't knwow in whic
|
|
|
|
# order. As such, we remove all used variables but the one we set.
|
|
|
|
tempfile.tempdir = app.config['STORAGE_DIR']
|
|
|
|
os.environ['TMP'] = app.config['STORAGE_DIR']
|
|
|
|
os.environ.pop('TEMP', None)
|
|
|
|
os.environ.pop('TMPDIR', None)
|
|
|
|
|
2016-05-02 19:07:21 +02:00
|
|
|
|
2016-03-08 09:24:57 +01:00
|
|
|
# Configure logging
|
2016-03-15 10:29:59 +01:00
|
|
|
logging.basicConfig(
|
|
|
|
level=logging.WARNING,
|
|
|
|
format='%(asctime)-15s %(levelname)8s %(name)s %(message)s')
|
|
|
|
|
|
|
|
logging.getLogger('werkzeug').setLevel(logging.INFO)
|
|
|
|
|
2016-03-08 09:24:57 +01:00
|
|
|
log = logging.getLogger(__name__)
|
2016-03-08 15:27:12 +01:00
|
|
|
log.setLevel(logging.DEBUG if app.config['DEBUG'] else logging.INFO)
|
2016-04-01 13:29:22 +02:00
|
|
|
if app.config['DEBUG']:
|
|
|
|
log.info('Pillar starting, debug=%s', app.config['DEBUG'])
|
2016-03-08 09:24:57 +01:00
|
|
|
|
2016-04-04 14:59:11 +02:00
|
|
|
# Configure Bugsnag
|
|
|
|
if not app.config.get('TESTING'):
|
|
|
|
bugsnag.configure(
|
|
|
|
api_key=app.config['BUGSNAG_API_KEY'],
|
|
|
|
project_root="/data/git/pillar/pillar",
|
|
|
|
)
|
|
|
|
bugsnag.flask.handle_exceptions(app)
|
|
|
|
|
|
|
|
bs_handler = bugsnag.handlers.BugsnagHandler()
|
|
|
|
bs_handler.setLevel(logging.ERROR)
|
|
|
|
log.addHandler(bs_handler)
|
2016-01-07 20:06:25 +01:00
|
|
|
|
2016-03-14 17:02:12 +01:00
|
|
|
# Google Cloud project
|
2016-03-14 14:51:46 +01:00
|
|
|
try:
|
|
|
|
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = \
|
2016-03-14 17:02:12 +01:00
|
|
|
app.config['GCLOUD_APP_CREDENTIALS']
|
|
|
|
except KeyError:
|
2016-03-15 10:20:28 +01:00
|
|
|
raise SystemExit('GCLOUD_APP_CREDENTIALS configuration is missing')
|
2016-03-14 17:02:12 +01:00
|
|
|
|
|
|
|
# Storage backend (GCS)
|
|
|
|
try:
|
2016-03-15 10:20:28 +01:00
|
|
|
os.environ['GCLOUD_PROJECT'] = app.config['GCLOUD_PROJECT']
|
2016-03-14 14:51:46 +01:00
|
|
|
except KeyError:
|
2016-03-14 17:02:12 +01:00
|
|
|
raise SystemExit('GCLOUD_PROJECT configuration value is missing')
|
2016-03-14 14:51:46 +01:00
|
|
|
|
2016-02-10 16:13:07 +01:00
|
|
|
# Algolia search
|
|
|
|
if 'ALGOLIA_USER' in app.config:
|
2016-04-25 16:58:53 +02:00
|
|
|
from algoliasearch import algoliasearch
|
|
|
|
|
2016-02-10 16:13:07 +01:00
|
|
|
client = algoliasearch.Client(
|
2016-03-21 12:25:07 +01:00
|
|
|
app.config['ALGOLIA_USER'],
|
|
|
|
app.config['ALGOLIA_API_KEY'])
|
2016-02-10 16:13:07 +01:00
|
|
|
algolia_index_users = client.init_index(app.config['ALGOLIA_INDEX_USERS'])
|
2016-02-26 16:17:38 +01:00
|
|
|
algolia_index_nodes = client.init_index(app.config['ALGOLIA_INDEX_NODES'])
|
2016-02-10 16:13:07 +01:00
|
|
|
else:
|
|
|
|
algolia_index_users = None
|
2016-02-26 16:17:38 +01:00
|
|
|
algolia_index_nodes = None
|
2016-02-10 16:13:07 +01:00
|
|
|
|
2016-02-22 16:48:53 +01:00
|
|
|
# Encoding backend
|
|
|
|
if app.config['ENCODING_BACKEND'] == 'zencoder':
|
|
|
|
encoding_service_client = Zencoder(app.config['ZENCODER_API_KEY'])
|
|
|
|
else:
|
|
|
|
encoding_service_client = None
|
|
|
|
|
2016-03-14 14:51:46 +01:00
|
|
|
from utils.authentication import validate_token
|
|
|
|
from utils.authorization import check_permissions
|
|
|
|
from utils.activities import notification_parse
|
2016-03-24 15:13:48 +01:00
|
|
|
from modules.projects import before_inserting_projects
|
|
|
|
from modules.projects import after_inserting_projects
|
2015-10-11 22:20:18 +02:00
|
|
|
|
|
|
|
|
2016-03-31 11:10:01 +02:00
|
|
|
@app.before_request
|
|
|
|
def validate_token_at_every_request():
|
|
|
|
validate_token()
|
|
|
|
|
|
|
|
|
2016-03-09 16:55:59 +01:00
|
|
|
def before_returning_item_notifications(response):
|
|
|
|
if request.args.get('parse'):
|
|
|
|
notification_parse(response)
|
|
|
|
|
2016-03-15 16:17:25 +01:00
|
|
|
|
2016-03-09 16:55:59 +01:00
|
|
|
def before_returning_resource_notifications(response):
|
|
|
|
for item in response['_items']:
|
|
|
|
if request.args.get('parse'):
|
|
|
|
notification_parse(item)
|
2016-01-26 18:26:18 +01:00
|
|
|
|
2016-03-21 12:25:07 +01:00
|
|
|
|
2016-03-09 16:55:59 +01:00
|
|
|
app.on_fetched_item_notifications += before_returning_item_notifications
|
|
|
|
app.on_fetched_resource_notifications += before_returning_resource_notifications
|
2015-10-11 22:20:18 +02:00
|
|
|
|
2016-03-21 12:25:07 +01:00
|
|
|
|
2016-02-22 16:48:53 +01:00
|
|
|
# The encoding module (receive notification and report progress)
|
|
|
|
from modules.encoding import encoding
|
2016-04-08 18:45:35 +02:00
|
|
|
from modules.blender_id import blender_id
|
2016-04-19 16:00:32 +02:00
|
|
|
from modules import projects
|
2016-04-26 12:33:48 +02:00
|
|
|
from modules import local_auth
|
2016-05-02 11:35:17 +02:00
|
|
|
from modules import file_storage
|
2016-04-25 16:58:53 +02:00
|
|
|
from modules import users
|
2016-05-02 11:35:17 +02:00
|
|
|
from modules import nodes
|
2016-05-02 19:07:21 +02:00
|
|
|
from modules import latest
|
2016-03-21 12:25:07 +01:00
|
|
|
|
2016-02-22 16:48:53 +01:00
|
|
|
app.register_blueprint(encoding, url_prefix='/encoding')
|
2016-04-08 18:45:35 +02:00
|
|
|
app.register_blueprint(blender_id, url_prefix='/blender_id')
|
2016-04-19 16:00:32 +02:00
|
|
|
projects.setup_app(app, url_prefix='/p')
|
2016-04-26 12:33:48 +02:00
|
|
|
local_auth.setup_app(app, url_prefix='/auth')
|
2016-05-02 11:35:17 +02:00
|
|
|
file_storage.setup_app(app, url_prefix='/storage')
|
2016-05-02 19:07:21 +02:00
|
|
|
latest.setup_app(app, url_prefix='/latest')
|
2016-04-25 16:58:53 +02:00
|
|
|
users.setup_app(app)
|
2016-05-02 11:35:17 +02:00
|
|
|
nodes.setup_app(app)
|