--- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ * Clone the repository from - > `` $ git clone --recursive https://git.pantoto.org/sweet-web/sweet-web-engine.git `` + > `` $ git clone https://git.pantoto.org/sweet-web/sweet-web-engine.git `` * It is recommended to do the installation inside a python virtual environment. --- a/runserver.py +++ b/runserver.py @@ -3,7 +3,8 @@ # Script to run the application server in development mode -import sys, os +import sys +import os # Get the path to the base directory of the app BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__))) --- a/swtstore/application.py +++ b/swtstore/application.py @@ -3,11 +3,12 @@ __init__.py """ -from flask import Flask, request, jsonify, render_template, make_response, g import os import logging from logging.handlers import RotatingFileHandler +from flask import Flask, request, jsonify, render_template, make_response + from classes.database import db from config import DefaultConfig from classes import views @@ -70,6 +71,7 @@ db.app = app oauth.init_app(app) + # return the current db instance # TODO: is this needed so much? def getDBInstance(): @@ -163,7 +165,7 @@ log_handler = RotatingFileHandler(log_file, maxBytes=max_size, backupCount=10) - if app.config.has_key('LOG_LEVEL'): + if 'LOG_LEVEL' in app.config: log_level = app.config['LOG_LEVEL'] or 'ERROR' else: log_level = 'ERROR' --- a/swtstore/classes/__init__.py +++ b/swtstore/classes/__init__.py @@ -2,6 +2,7 @@ """ __init__.py """ + from database import db from oauth import oauth import models --- a/swtstore/classes/exceptions.py +++ b/swtstore/classes/exceptions.py @@ -3,9 +3,15 @@ from sqlalchemy.exc import DontWrapMixin + class AlreadyExistsError(Exception, DontWrapMixin): pass + class InvalidPayload(Exception, DontWrapMixin): + pass + + +class ContextDoNotExist(Exception, DontWrapMixin): pass --- a/swtstore/classes/models/client.py +++ b/swtstore/classes/models/client.py @@ -36,7 +36,6 @@ _redirect_uris = db.Column(db.Text) _default_scopes = db.Column(db.Text) - @property def client_id(self): return self.id @@ -73,7 +72,6 @@ def __str__(self): return '' % (self.name, self.id) - # create and persist the client to the database def persist(self): db.session.add(self) @@ -155,34 +153,36 @@ #TODO: find out how to better structure the following code # OAuthLib decorators used by OAuthLib in the OAuth flow - @oauth.clientgetter def loadClient(client_id): current_app.logger.debug('@oauth.clientgetter') #return Client.query.filter_by(id=client_id).first() return Client.query.get(client_id) + @oauth.grantgetter def loadGrant(client_id, code): current_app.logger.debug('@oauth.grantgetter') return Grant.query.filter_by(client_id=client_id, code=code).first() + @oauth.grantsetter def saveGrant(client_id, code, request, *args, **kwargs): current_app.logger.debug('@oauth.grantsetter') expires = datetime.utcnow() + timedelta(seconds=100) grant = Grant( - client_id = client_id, - code = code['code'], - redirect_uri = request.redirect_uri, - _scopes = ' '.join(request.scopes), - user = User.getCurrentUser(), - expires = expires + client_id=client_id, + code=code['code'], + redirect_uri=request.redirect_uri, + _scopes=' '.join(request.scopes), + user=User.getCurrentUser(), + expires=expires ) db.session.add(grant) db.session.commit() return grant + @oauth.tokengetter def loadToken(access_token=None, refresh_token=None): current_app.logger.debug('@oauth.tokengetter') @@ -191,6 +191,7 @@ elif refresh_token: return Token.query.filter_by(refresh_token=refresh_token).first() + @oauth.tokensetter def saveToken(token, request, *args, **kwargs): current_app.logger.debug('@oauth.tokensetter') @@ -205,24 +206,24 @@ expires = datetime.utcnow() + timedelta(seconds=expires_in) tok = Token( - access_token = token['access_token'], - refresh_token = token['refresh_token'], - token_type = token['token_type'], - _scopes = token['scope'], - expires = expires, - client_id = request.client.id, - user = request.user + access_token=token['access_token'], + refresh_token=token['refresh_token'], + token_type=token['token_type'], + _scopes=token['scope'], + expires=expires, + client_id=request.client.id, + user=request.user ) db.session.add(tok) db.session.commit() return tok + @oauth.usergetter def getUser(): return User.getCurrentUser() - # Authorized Clients class AuthorizedClients(db.Model): """ @@ -257,7 +258,7 @@ @staticmethod def getByUser(user): - authorized_clients = [row.client for row in \ + authorized_clients = [row.client for row in AuthorizedClients.query.filter_by(user_id=user.id).all()] current_app.logger.debug('authorized clients %s', authorized_clients) --- a/swtstore/classes/models/context.py +++ b/swtstore/classes/models/context.py @@ -5,6 +5,8 @@ from datetime import datetime import json +from sqlalchemy.exc import IntegrityError + from swtstore.classes import db from swtstore.classes.models.types import JSONType from swtstore.classes.exceptions import AlreadyExistsError --- a/swtstore/classes/models/sweet.py +++ b/swtstore/classes/models/sweet.py @@ -8,9 +8,11 @@ from swtstore.classes.database import db # custom SQLAlchemy type JSONType from swtstore.classes.models.types import JSONType -from swtstore.classes.utils import urlnorm # normalize URLs +from swtstore.classes.utils import urlnorm # normalize URLs from swtstore.classes.models import Context, User +from swtstore.classes.exceptions import InvalidPayload, ContextDoNotExist + class Sweet(db.Model): """ customary docstring """ @@ -30,7 +32,6 @@ created = db.Column(db.DateTime, default=datetime.utcnow) - def __init__(self, who, what, where, how): current_app.logger.info('initing sweet..') self.who = who @@ -38,7 +39,6 @@ self.where = urlnorm(where) self.how = how - def __repr__(self): return '[Sweet Object: <%s : @%s: #%s : %s>]' % (self.id, self.who, self.what, self.where) @@ -58,16 +58,15 @@ return None - # create multiple sweets from a list of JSON @staticmethod def createSweets(who, payload): # the payload has to be a list; a list of swts for each in payload: - if 'what' not in each and 'where' not in\ - each and 'how' not in each: + if 'what' not in each and 'where' not in each and 'how' not in\ + each: - raise InvalidPayload('Invalid payload %s \n for creating\ + raise InvalidPayload('Invalid payload %s \n while creating\ mutiple sweets' % (each)) return None @@ -78,13 +77,11 @@ what = Context.getByName(each['what']) if what is None: - current_app.logger.info('Context "%s" do not exist. Aborting', - what) - g.error = 'Context do not exist' - abort(400) # this context doesn't exist! + raise ContextDoNotExist('Context %s do not exist!' % + (each['what'])) current_app.logger.debug('SWEET PAYLOAD\n---\n%s\n%s\n%s\n%s\n----', - who, what, each['where'], each['how']) + who, what, each['where'], each['how']) new_sweet = Sweet(who, what, each['where'], each['how']) @@ -128,7 +125,6 @@ #'created': self.created.isoformat() 'created': self.created.strftime('%a, %d %b %Y, %I:%M %p UTC'), } - # create and persist the sweet to the database def persist(self): --- a/swtstore/classes/models/types.py +++ b/swtstore/classes/models/types.py @@ -3,10 +3,10 @@ # class:: Types # extend SQLAlchemy Types -from datetime import datetime import json from sqlalchemy import types + class JSONType(types.TypeDecorator): """ --- a/swtstore/classes/views/api.py +++ b/swtstore/classes/views/api.py @@ -1,10 +1,10 @@ -from flask import Module, jsonify, request, make_response, abort, g, json,\ - current_app +from flask import Module, jsonify, request, make_response +from flask import abort, g, json, current_app -from swtstore.classes.models import Context -from swtstore.classes.models import Sweet -from swtstore.classes.exceptions import AlreadyExistsError, InvalidPayload -from swtstore.classes.utils import urlnorm # normalize URLs +from swtstore.classes.models import Context, Sweet +from swtstore.classes.exceptions import AlreadyExistsError, InvalidPayload,\ + ContextDoNotExist +from swtstore.classes.utils import urlnorm # normalize URLs from swtstore.classes.utils.httputils import makeCORSHeaders from swtstore.classes import oauth @@ -67,7 +67,7 @@ payload = request.json or request.data if not payload: current_app.logger.error('data not found in payload!') - g.error= 'data not found in payload!' + g.error = 'data not found in payload!' abort(400) current_app.logger.debug('new sweet payload recvd.. %s', payload) @@ -78,8 +78,8 @@ try: swts = Sweet.createSweets(who, payload) - except InvalidPayload(msg): - current_app.logger.error('Invalid Payload in request') + except (InvalidPayload, ContextDoNotExist) as e: + current_app.logger.error('Error creating sweets. Error: %s', e) abort(400) response.status_code = 200 @@ -99,7 +99,7 @@ response = makeCORSHeaders(response, origin) if request.method == 'OPTIONS': - reponse.status_code = 200 + response.status_code = 200 return response args = request.args @@ -146,6 +146,7 @@ current_app.logger.debug('getContextByName : %s', context) return jsonify(context.to_dict()) + # Get a specific context with its definition; based on id @api.route('/contexts/', methods=['GET']) def getContextById(id): @@ -193,7 +194,7 @@ current_app.logger.debug('new context created: %s', new_context) # all ok. save the new context - res = new_context.persist() + new_context.persist() response.status_code = 200 return response --- a/swtstore/classes/views/app.py +++ b/swtstore/classes/views/app.py @@ -1,10 +1,9 @@ # -*- coding utf-8 -*- # classes/views/apps.py -from flask import Module, jsonify, request, render_template, redirect,\ - url_for, flash, abort +from flask import Module, request, render_template, redirect,\ + url_for, abort -from hashlib import md5 from werkzeug.security import gen_salt from swtstore.classes.models import Client, User @@ -30,15 +29,15 @@ abort(404) new_app = Client( - id = gen_salt(40), - client_secret = gen_salt(50), - name = request.form.get('name'), - description = request.form.get('desc'), - user_id = current_user.id, - _host_url = request.form.get('host_url'), - _redirect_uris = urlnorm(request.form.get('redirect_uris')), - _default_scopes = ' '.join(request.form.get('scopes').split(',')), - _is_private = False + id=gen_salt(40), + client_secret=gen_salt(50), + name=request.form.get('name'), + description=request.form.get('desc'), + user_id=current_user.id, + _host_url=request.form.get('host_url'), + _redirect_uris=urlnorm(request.form.get('redirect_uris')), + _default_scopes=' '.join(request.form.get('scopes').split(',')), + _is_private=False ) new_app.persist() --- a/swtstore/classes/views/context.py +++ b/swtstore/classes/views/context.py @@ -1,14 +1,15 @@ # -*- coding utf-8 -*- # classes/views/context.py -from flask import Module, jsonify, request, render_template, redirect,\ - url_for, json, current_app +from flask import Module, request, render_template, redirect,\ + url_for, json, current_app, abort from swtstore.classes.models import Context, User context = Module(__name__) + @context.route('/register', methods=['GET', 'POST']) def register(): current_user = User.getCurrentUser() @@ -19,8 +20,8 @@ return render_template('context/register.html') if request.method == 'POST': - if not request.form.get('name') or not request.form.get('defn'): - abort(400) + if not request.form.get('name') or not request.form.get('defn'): + abort(400) current_app.logger.debug('New Context: defn: %s ', request.form.get('defn')) @@ -28,9 +29,9 @@ current_app.logger.debug('Resulting json_ld %s', json_ld) new_context = Context( - name = request.form.get('name'), - definition = json_ld, - user_id = current_user.id + name=request.form.get('name'), + definition=json_ld, + user_id=current_user.id ) current_app.logger.debug('New Context created: %s', new_context) new_context.persist() --- a/swtstore/classes/views/frontend.py +++ b/swtstore/classes/views/frontend.py @@ -2,13 +2,13 @@ # classes/views/frontend.py -from flask import Module, jsonify, request, render_template, redirect,\ - url_for, g, current_app +from flask import Module, render_template -from swtstore.classes.models import Sweet, User +from swtstore.classes.models import Sweet frontend = Module(__name__) + @frontend.route('/', methods=['GET']) def index(): --- a/swtstore/classes/views/oauth.py +++ b/swtstore/classes/views/oauth.py @@ -1,9 +1,7 @@ # -*- coding utf-8 -*- # classes/views/oauth.py -from flask import Module, jsonify, request, render_template, redirect,\ - url_for, current_app -import requests +from flask import Module, jsonify, request, render_template, current_app from swtstore.classes import oauth from swtstore.classes.models import Client, AuthorizedClients, User @@ -35,7 +33,6 @@ else: return render_template('oauth/authorize.html', **kwargs) - confirm = request.form.get('confirm', 'no') authorized = request.form.get('authorized', 'no') current_app.logger.debug('confirm authorize from user: %s', confirm) @@ -58,6 +55,7 @@ #print request.form current_app.logger.debug('access token touched..') return None + @Oauth.route('/errors') def error(): --- a/swtstore/classes/views/sweet.py +++ b/swtstore/classes/views/sweet.py @@ -2,13 +2,13 @@ # classes/views/sweet.py -from flask import Module, jsonify, request, render_template, redirect,\ - url_for, abort, json +from flask import Module, render_template, abort -from swtstore.classes.models import Context, Sweet, User +from swtstore.classes.models import Sweet sweet = Module(__name__) + @sweet.route('/', methods=['GET']) def showSweet(id): --- a/swtstore/classes/views/user.py +++ b/swtstore/classes/views/user.py @@ -4,14 +4,13 @@ import requests # flask imports -from flask import Module, jsonify, request, render_template, session,\ - make_response, url_for, redirect, json, current_app +from flask import Module, request, render_template, session,\ + make_response, url_for, redirect, json, current_app # swtstore imports from swtstore.classes.models import User, Sweet, Context, Client,\ - AuthorizedClients + AuthorizedClients -from swtstore.classes.utils.httputils import makeCORSHeaders from swtstore.config import DefaultConfig @@ -19,6 +18,7 @@ user = Module(__name__) + @user.route('/login', methods=['POST']) def login(): @@ -68,6 +68,7 @@ response.status_code = 500 return response + @user.route('/logout', methods=['POST']) def logout(): @@ -81,6 +82,7 @@ response.status_code = 200 return response + @user.route('/me', methods=['GET', 'POST']) def profile(): @@ -136,6 +138,7 @@ apps = Client.getClientsByCreator(user.id) return render_template('user/apps.html', apps=apps) + @user.route('/me/authorized_apps', methods=['GET', 'POST']) def authorizedApps(): @@ -146,7 +149,7 @@ if request.method == 'GET': authorized_clients = AuthorizedClients.getByUser(user) return render_template('user/authorized_apps.html', - authorized_clients=authorized_clients) + authorized_clients=authorized_clients) # else POST request client_id = request.form.get('revoke-id', '') --- a/swtstore/sample_config.py +++ b/swtstore/sample_config.py @@ -1,4 +1,5 @@ + class DefaultConfig(): """ @@ -20,8 +21,7 @@ # been done prior to editing this line. # Refer https://wiki.debian.org/PostgreSql#User_access for creating users # in postgresql. - SQLALCHEMY_DATABASE_URI =\ - 'dialect+driver://username:password@host:port/database' + SQLALCHEMY_DATABASE_URI = 'dialect+driver://username:password@host:port/database' # Log level for the application LOG_LEVEL = 'ERROR'