Commit cffc26d0d9e198edb51c359a04b3b40eb26a6df3
- Diff rendering mode:
- inline
- side by side
README.md
(1 / 1)
  | |||
82 | 82 | ||
83 | 83 | * Clone the repository from <https://git.pantoto.org/sweet-web/sweet-web-engine> | |
84 | 84 | ||
85 | > `` $ git clone --recursive https://git.pantoto.org/sweet-web/sweet-web-engine.git `` | ||
85 | > `` $ git clone https://git.pantoto.org/sweet-web/sweet-web-engine.git `` | ||
86 | 86 | ||
87 | 87 | * It is recommended to do the installation inside a python virtual | |
88 | 88 | environment. |
runserver.py
(2 / 1)
  | |||
3 | 3 | ||
4 | 4 | # Script to run the application server in development mode | |
5 | 5 | ||
6 | import sys, os | ||
6 | import sys | ||
7 | import os | ||
7 | 8 | ||
8 | 9 | # Get the path to the base directory of the app | |
9 | 10 | BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__))) |
swtstore/application.py
(4 / 2)
  | |||
3 | 3 | __init__.py | |
4 | 4 | """ | |
5 | 5 | ||
6 | from flask import Flask, request, jsonify, render_template, make_response, g | ||
7 | 6 | import os | |
8 | 7 | import logging | |
9 | 8 | from logging.handlers import RotatingFileHandler | |
10 | 9 | ||
10 | from flask import Flask, request, jsonify, render_template, make_response | ||
11 | |||
11 | 12 | from classes.database import db | |
12 | 13 | from config import DefaultConfig | |
13 | 14 | from classes import views | |
… | … | ||
71 | 71 | db.app = app | |
72 | 72 | oauth.init_app(app) | |
73 | 73 | ||
74 | |||
74 | 75 | # return the current db instance | |
75 | 76 | # TODO: is this needed so much? | |
76 | 77 | def getDBInstance(): | |
… | … | ||
165 | 165 | log_handler = RotatingFileHandler(log_file, maxBytes=max_size, | |
166 | 166 | backupCount=10) | |
167 | 167 | ||
168 | if app.config.has_key('LOG_LEVEL'): | ||
168 | if 'LOG_LEVEL' in app.config: | ||
169 | 169 | log_level = app.config['LOG_LEVEL'] or 'ERROR' | |
170 | 170 | else: | |
171 | 171 | log_level = 'ERROR' |
swtstore/classes/__init__.py
(1 / 0)
  | |||
2 | 2 | """ | |
3 | 3 | __init__.py | |
4 | 4 | """ | |
5 | |||
5 | 6 | from database import db | |
6 | 7 | from oauth import oauth | |
7 | 8 | import models |
  | |||
3 | 3 | ||
4 | 4 | from sqlalchemy.exc import DontWrapMixin | |
5 | 5 | ||
6 | |||
6 | 7 | class AlreadyExistsError(Exception, DontWrapMixin): | |
7 | 8 | pass | |
8 | 9 | ||
10 | |||
9 | 11 | class InvalidPayload(Exception, DontWrapMixin): | |
12 | pass | ||
13 | |||
14 | |||
15 | class ContextDoNotExist(Exception, DontWrapMixin): | ||
10 | 16 | pass |
swtstore/classes/models/client.py
(19 / 18)
  | |||
36 | 36 | _redirect_uris = db.Column(db.Text) | |
37 | 37 | _default_scopes = db.Column(db.Text) | |
38 | 38 | ||
39 | |||
40 | 39 | @property | |
41 | 40 | def client_id(self): | |
42 | 41 | return self.id | |
… | … | ||
72 | 72 | def __str__(self): | |
73 | 73 | return '<Client: %s :: ID: %s>' % (self.name, self.id) | |
74 | 74 | ||
75 | |||
76 | 75 | # create and persist the client to the database | |
77 | 76 | def persist(self): | |
78 | 77 | db.session.add(self) | |
… | … | ||
153 | 153 | #TODO: find out how to better structure the following code | |
154 | 154 | ||
155 | 155 | # OAuthLib decorators used by OAuthLib in the OAuth flow | |
156 | |||
157 | 156 | @oauth.clientgetter | |
158 | 157 | def loadClient(client_id): | |
159 | 158 | current_app.logger.debug('@oauth.clientgetter') | |
160 | 159 | #return Client.query.filter_by(id=client_id).first() | |
161 | 160 | return Client.query.get(client_id) | |
162 | 161 | ||
162 | |||
163 | 163 | @oauth.grantgetter | |
164 | 164 | def loadGrant(client_id, code): | |
165 | 165 | current_app.logger.debug('@oauth.grantgetter') | |
166 | 166 | return Grant.query.filter_by(client_id=client_id, code=code).first() | |
167 | 167 | ||
168 | |||
168 | 169 | @oauth.grantsetter | |
169 | 170 | def saveGrant(client_id, code, request, *args, **kwargs): | |
170 | 171 | current_app.logger.debug('@oauth.grantsetter') | |
171 | 172 | expires = datetime.utcnow() + timedelta(seconds=100) | |
172 | 173 | grant = Grant( | |
173 | client_id = client_id, | ||
174 | code = code['code'], | ||
175 | redirect_uri = request.redirect_uri, | ||
176 | _scopes = ' '.join(request.scopes), | ||
177 | user = User.getCurrentUser(), | ||
178 | expires = expires | ||
174 | client_id=client_id, | ||
175 | code=code['code'], | ||
176 | redirect_uri=request.redirect_uri, | ||
177 | _scopes=' '.join(request.scopes), | ||
178 | user=User.getCurrentUser(), | ||
179 | expires=expires | ||
179 | 180 | ) | |
180 | 181 | db.session.add(grant) | |
181 | 182 | db.session.commit() | |
182 | 183 | return grant | |
183 | 184 | ||
185 | |||
184 | 186 | @oauth.tokengetter | |
185 | 187 | def loadToken(access_token=None, refresh_token=None): | |
186 | 188 | current_app.logger.debug('@oauth.tokengetter') | |
… | … | ||
191 | 191 | elif refresh_token: | |
192 | 192 | return Token.query.filter_by(refresh_token=refresh_token).first() | |
193 | 193 | ||
194 | |||
194 | 195 | @oauth.tokensetter | |
195 | 196 | def saveToken(token, request, *args, **kwargs): | |
196 | 197 | current_app.logger.debug('@oauth.tokensetter') | |
… | … | ||
206 | 206 | expires = datetime.utcnow() + timedelta(seconds=expires_in) | |
207 | 207 | ||
208 | 208 | tok = Token( | |
209 | access_token = token['access_token'], | ||
210 | refresh_token = token['refresh_token'], | ||
211 | token_type = token['token_type'], | ||
212 | _scopes = token['scope'], | ||
213 | expires = expires, | ||
214 | client_id = request.client.id, | ||
215 | user = request.user | ||
209 | access_token=token['access_token'], | ||
210 | refresh_token=token['refresh_token'], | ||
211 | token_type=token['token_type'], | ||
212 | _scopes=token['scope'], | ||
213 | expires=expires, | ||
214 | client_id=request.client.id, | ||
215 | user=request.user | ||
216 | 216 | ) | |
217 | 217 | db.session.add(tok) | |
218 | 218 | db.session.commit() | |
219 | 219 | return tok | |
220 | 220 | ||
221 | |||
221 | 222 | @oauth.usergetter | |
222 | 223 | def getUser(): | |
223 | 224 | return User.getCurrentUser() | |
224 | 225 | ||
225 | 226 | ||
226 | |||
227 | 227 | # Authorized Clients | |
228 | 228 | class AuthorizedClients(db.Model): | |
229 | 229 | """ | |
… | … | ||
258 | 258 | ||
259 | 259 | @staticmethod | |
260 | 260 | def getByUser(user): | |
261 | authorized_clients = [row.client for row in \ | ||
261 | authorized_clients = [row.client for row in | ||
262 | 262 | AuthorizedClients.query.filter_by(user_id=user.id).all()] | |
263 | 263 | ||
264 | 264 | current_app.logger.debug('authorized clients %s', authorized_clients) |
  | |||
5 | 5 | from datetime import datetime | |
6 | 6 | import json | |
7 | 7 | ||
8 | from sqlalchemy.exc import IntegrityError | ||
9 | |||
8 | 10 | from swtstore.classes import db | |
9 | 11 | from swtstore.classes.models.types import JSONType | |
10 | 12 | from swtstore.classes.exceptions import AlreadyExistsError |
swtstore/classes/models/sweet.py
(9 / 13)
  | |||
8 | 8 | from swtstore.classes.database import db | |
9 | 9 | # custom SQLAlchemy type JSONType | |
10 | 10 | from swtstore.classes.models.types import JSONType | |
11 | from swtstore.classes.utils import urlnorm # normalize URLs | ||
11 | from swtstore.classes.utils import urlnorm # normalize URLs | ||
12 | 12 | from swtstore.classes.models import Context, User | |
13 | from swtstore.classes.exceptions import InvalidPayload, ContextDoNotExist | ||
13 | 14 | ||
15 | |||
14 | 16 | class Sweet(db.Model): | |
15 | 17 | """ customary docstring """ | |
16 | 18 | ||
… | … | ||
32 | 32 | ||
33 | 33 | created = db.Column(db.DateTime, default=datetime.utcnow) | |
34 | 34 | ||
35 | |||
36 | 35 | def __init__(self, who, what, where, how): | |
37 | 36 | current_app.logger.info('initing sweet..') | |
38 | 37 | self.who = who | |
… | … | ||
39 | 39 | self.where = urlnorm(where) | |
40 | 40 | self.how = how | |
41 | 41 | ||
42 | |||
43 | 42 | def __repr__(self): | |
44 | 43 | return '[Sweet Object: <%s : @%s: #%s : %s>]' % (self.id, self.who, | |
45 | 44 | self.what, self.where) | |
… | … | ||
58 | 58 | ||
59 | 59 | return None | |
60 | 60 | ||
61 | |||
62 | 61 | # create multiple sweets from a list of JSON | |
63 | 62 | @staticmethod | |
64 | 63 | def createSweets(who, payload): | |
65 | 64 | # the payload has to be a list; a list of swts | |
66 | 65 | for each in payload: | |
67 | if 'what' not in each and 'where' not in\ | ||
68 | each and 'how' not in each: | ||
66 | if 'what' not in each and 'where' not in each and 'how' not in\ | ||
67 | each: | ||
69 | 68 | ||
70 | raise InvalidPayload('Invalid payload %s \n for creating\ | ||
69 | raise InvalidPayload('Invalid payload %s \n while creating\ | ||
71 | 70 | mutiple sweets' % (each)) | |
72 | 71 | return None | |
73 | 72 | ||
… | … | ||
77 | 77 | what = Context.getByName(each['what']) | |
78 | 78 | ||
79 | 79 | if what is None: | |
80 | current_app.logger.info('Context "%s" do not exist. Aborting', | ||
81 | what) | ||
82 | g.error = 'Context do not exist' | ||
83 | abort(400) # this context doesn't exist! | ||
80 | raise ContextDoNotExist('Context %s do not exist!' % | ||
81 | (each['what'])) | ||
84 | 82 | ||
85 | 83 | current_app.logger.debug('SWEET PAYLOAD\n---\n%s\n%s\n%s\n%s\n----', | |
86 | who, what, each['where'], each['how']) | ||
84 | who, what, each['where'], each['how']) | ||
87 | 85 | ||
88 | 86 | new_sweet = Sweet(who, what, each['where'], each['how']) | |
89 | 87 | ||
… | … | ||
125 | 125 | #'created': self.created.isoformat() | |
126 | 126 | 'created': self.created.strftime('%a, %d %b %Y, %I:%M %p UTC'), | |
127 | 127 | } | |
128 | |||
129 | 128 | ||
130 | 129 | # create and persist the sweet to the database | |
131 | 130 | def persist(self): |
  | |||
3 | 3 | # class:: Types | |
4 | 4 | # extend SQLAlchemy Types | |
5 | 5 | ||
6 | from datetime import datetime | ||
7 | 6 | import json | |
8 | 7 | ||
9 | 8 | from sqlalchemy import types | |
9 | |||
10 | 10 | ||
11 | 11 | class JSONType(types.TypeDecorator): | |
12 | 12 | """ |
swtstore/classes/views/api.py
(12 / 11)
  | |||
1 | from flask import Module, jsonify, request, make_response, abort, g, json,\ | ||
2 | current_app | ||
1 | from flask import Module, jsonify, request, make_response | ||
2 | from flask import abort, g, json, current_app | ||
3 | 3 | ||
4 | from swtstore.classes.models import Context | ||
5 | from swtstore.classes.models import Sweet | ||
6 | from swtstore.classes.exceptions import AlreadyExistsError, InvalidPayload | ||
7 | from swtstore.classes.utils import urlnorm # normalize URLs | ||
4 | from swtstore.classes.models import Context, Sweet | ||
5 | from swtstore.classes.exceptions import AlreadyExistsError, InvalidPayload,\ | ||
6 | ContextDoNotExist | ||
7 | from swtstore.classes.utils import urlnorm # normalize URLs | ||
8 | 8 | from swtstore.classes.utils.httputils import makeCORSHeaders | |
9 | 9 | from swtstore.classes import oauth | |
10 | 10 | ||
… | … | ||
67 | 67 | payload = request.json or request.data | |
68 | 68 | if not payload: | |
69 | 69 | current_app.logger.error('data not found in payload!') | |
70 | g.error= 'data not found in payload!' | ||
70 | g.error = 'data not found in payload!' | ||
71 | 71 | abort(400) | |
72 | 72 | ||
73 | 73 | current_app.logger.debug('new sweet payload recvd.. %s', payload) | |
… | … | ||
78 | 78 | ||
79 | 79 | try: | |
80 | 80 | swts = Sweet.createSweets(who, payload) | |
81 | except InvalidPayload(msg): | ||
82 | current_app.logger.error('Invalid Payload in request') | ||
81 | except (InvalidPayload, ContextDoNotExist) as e: | ||
82 | current_app.logger.error('Error creating sweets. Error: %s', e) | ||
83 | 83 | abort(400) | |
84 | 84 | ||
85 | 85 | response.status_code = 200 | |
… | … | ||
99 | 99 | response = makeCORSHeaders(response, origin) | |
100 | 100 | ||
101 | 101 | if request.method == 'OPTIONS': | |
102 | reponse.status_code = 200 | ||
102 | response.status_code = 200 | ||
103 | 103 | return response | |
104 | 104 | ||
105 | 105 | args = request.args | |
… | … | ||
146 | 146 | current_app.logger.debug('getContextByName : %s', context) | |
147 | 147 | return jsonify(context.to_dict()) | |
148 | 148 | ||
149 | |||
149 | 150 | # Get a specific context with its definition; based on id | |
150 | 151 | @api.route('/contexts/<int:id>', methods=['GET']) | |
151 | 152 | def getContextById(id): | |
… | … | ||
194 | 194 | current_app.logger.debug('new context created: %s', new_context) | |
195 | 195 | ||
196 | 196 | # all ok. save the new context | |
197 | res = new_context.persist() | ||
197 | new_context.persist() | ||
198 | 198 | ||
199 | 199 | response.status_code = 200 | |
200 | 200 | return response |
swtstore/classes/views/app.py
(11 / 12)
  | |||
1 | 1 | # -*- coding utf-8 -*- | |
2 | 2 | # classes/views/apps.py | |
3 | 3 | ||
4 | from flask import Module, jsonify, request, render_template, redirect,\ | ||
5 | url_for, flash, abort | ||
4 | from flask import Module, request, render_template, redirect,\ | ||
5 | url_for, abort | ||
6 | 6 | ||
7 | from hashlib import md5 | ||
8 | 7 | from werkzeug.security import gen_salt | |
9 | 8 | ||
10 | 9 | from swtstore.classes.models import Client, User | |
… | … | ||
29 | 29 | abort(404) | |
30 | 30 | ||
31 | 31 | new_app = Client( | |
32 | id = gen_salt(40), | ||
33 | client_secret = gen_salt(50), | ||
34 | name = request.form.get('name'), | ||
35 | description = request.form.get('desc'), | ||
36 | user_id = current_user.id, | ||
37 | _host_url = request.form.get('host_url'), | ||
38 | _redirect_uris = urlnorm(request.form.get('redirect_uris')), | ||
39 | _default_scopes = ' '.join(request.form.get('scopes').split(',')), | ||
40 | _is_private = False | ||
32 | id=gen_salt(40), | ||
33 | client_secret=gen_salt(50), | ||
34 | name=request.form.get('name'), | ||
35 | description=request.form.get('desc'), | ||
36 | user_id=current_user.id, | ||
37 | _host_url=request.form.get('host_url'), | ||
38 | _redirect_uris=urlnorm(request.form.get('redirect_uris')), | ||
39 | _default_scopes=' '.join(request.form.get('scopes').split(',')), | ||
40 | _is_private=False | ||
41 | 41 | ) | |
42 | 42 | new_app.persist() | |
43 | 43 |
  | |||
1 | 1 | # -*- coding utf-8 -*- | |
2 | 2 | # classes/views/context.py | |
3 | 3 | ||
4 | from flask import Module, jsonify, request, render_template, redirect,\ | ||
5 | url_for, json, current_app | ||
4 | from flask import Module, request, render_template, redirect,\ | ||
5 | url_for, json, current_app, abort | ||
6 | 6 | ||
7 | 7 | from swtstore.classes.models import Context, User | |
8 | 8 | ||
9 | 9 | ||
10 | 10 | context = Module(__name__) | |
11 | 11 | ||
12 | |||
12 | 13 | @context.route('/register', methods=['GET', 'POST']) | |
13 | 14 | def register(): | |
14 | 15 | current_user = User.getCurrentUser() | |
… | … | ||
20 | 20 | return render_template('context/register.html') | |
21 | 21 | ||
22 | 22 | if request.method == 'POST': | |
23 | if not request.form.get('name') or not request.form.get('defn'): | ||
24 | abort(400) | ||
23 | if not request.form.get('name') or not request.form.get('defn'): | ||
24 | abort(400) | ||
25 | 25 | ||
26 | 26 | current_app.logger.debug('New Context: defn: %s ', | |
27 | 27 | request.form.get('defn')) | |
… | … | ||
29 | 29 | current_app.logger.debug('Resulting json_ld %s', json_ld) | |
30 | 30 | ||
31 | 31 | new_context = Context( | |
32 | name = request.form.get('name'), | ||
33 | definition = json_ld, | ||
34 | user_id = current_user.id | ||
32 | name=request.form.get('name'), | ||
33 | definition=json_ld, | ||
34 | user_id=current_user.id | ||
35 | 35 | ) | |
36 | 36 | current_app.logger.debug('New Context created: %s', new_context) | |
37 | 37 | new_context.persist() |
  | |||
2 | 2 | # classes/views/frontend.py | |
3 | 3 | ||
4 | 4 | ||
5 | from flask import Module, jsonify, request, render_template, redirect,\ | ||
6 | url_for, g, current_app | ||
5 | from flask import Module, render_template | ||
7 | 6 | ||
8 | from swtstore.classes.models import Sweet, User | ||
7 | from swtstore.classes.models import Sweet | ||
9 | 8 | ||
10 | 9 | ||
11 | 10 | frontend = Module(__name__) | |
11 | |||
12 | 12 | ||
13 | 13 | @frontend.route('/', methods=['GET']) | |
14 | 14 | def index(): |
  | |||
1 | 1 | # -*- coding utf-8 -*- | |
2 | 2 | # classes/views/oauth.py | |
3 | 3 | ||
4 | from flask import Module, jsonify, request, render_template, redirect,\ | ||
5 | url_for, current_app | ||
6 | import requests | ||
4 | from flask import Module, jsonify, request, render_template, current_app | ||
7 | 5 | ||
8 | 6 | from swtstore.classes import oauth | |
9 | 7 | from swtstore.classes.models import Client, AuthorizedClients, User | |
… | … | ||
33 | 33 | else: | |
34 | 34 | return render_template('oauth/authorize.html', **kwargs) | |
35 | 35 | ||
36 | |||
37 | 36 | confirm = request.form.get('confirm', 'no') | |
38 | 37 | authorized = request.form.get('authorized', 'no') | |
39 | 38 | current_app.logger.debug('confirm authorize from user: %s', confirm) | |
… | … | ||
55 | 55 | #print request.form | |
56 | 56 | current_app.logger.debug('access token touched..') | |
57 | 57 | return None | |
58 | |||
58 | 59 | ||
59 | 60 | @Oauth.route('/errors') | |
60 | 61 | def error(): |
  | |||
2 | 2 | # classes/views/sweet.py | |
3 | 3 | ||
4 | 4 | ||
5 | from flask import Module, jsonify, request, render_template, redirect,\ | ||
6 | url_for, abort, json | ||
5 | from flask import Module, render_template, abort | ||
7 | 6 | ||
8 | from swtstore.classes.models import Context, Sweet, User | ||
7 | from swtstore.classes.models import Sweet | ||
9 | 8 | ||
10 | 9 | ||
11 | 10 | sweet = Module(__name__) | |
11 | |||
12 | 12 | ||
13 | 13 | @sweet.route('/<int:id>', methods=['GET']) | |
14 | 14 | def showSweet(id): |
  | |||
4 | 4 | import requests | |
5 | 5 | ||
6 | 6 | # flask imports | |
7 | from flask import Module, jsonify, request, render_template, session,\ | ||
8 | make_response, url_for, redirect, json, current_app | ||
7 | from flask import Module, request, render_template, session,\ | ||
8 | make_response, url_for, redirect, json, current_app | ||
9 | 9 | ||
10 | 10 | # swtstore imports | |
11 | 11 | from swtstore.classes.models import User, Sweet, Context, Client,\ | |
12 | AuthorizedClients | ||
12 | AuthorizedClients | ||
13 | 13 | ||
14 | from swtstore.classes.utils.httputils import makeCORSHeaders | ||
15 | 14 | from swtstore.config import DefaultConfig | |
16 | 15 | ||
17 | 16 | ||
… | … | ||
18 | 18 | ||
19 | 19 | user = Module(__name__) | |
20 | 20 | ||
21 | |||
21 | 22 | @user.route('/login', methods=['POST']) | |
22 | 23 | def login(): | |
23 | 24 | ||
… | … | ||
68 | 68 | response.status_code = 500 | |
69 | 69 | return response | |
70 | 70 | ||
71 | |||
71 | 72 | @user.route('/logout', methods=['POST']) | |
72 | 73 | def logout(): | |
73 | 74 | ||
… | … | ||
82 | 82 | response.status_code = 200 | |
83 | 83 | return response | |
84 | 84 | ||
85 | |||
85 | 86 | @user.route('/me', methods=['GET', 'POST']) | |
86 | 87 | def profile(): | |
87 | 88 | ||
… | … | ||
138 | 138 | apps = Client.getClientsByCreator(user.id) | |
139 | 139 | return render_template('user/apps.html', apps=apps) | |
140 | 140 | ||
141 | |||
141 | 142 | @user.route('/me/authorized_apps', methods=['GET', 'POST']) | |
142 | 143 | def authorizedApps(): | |
143 | 144 | ||
… | … | ||
149 | 149 | if request.method == 'GET': | |
150 | 150 | authorized_clients = AuthorizedClients.getByUser(user) | |
151 | 151 | return render_template('user/authorized_apps.html', | |
152 | authorized_clients=authorized_clients) | ||
152 | authorized_clients=authorized_clients) | ||
153 | 153 | ||
154 | 154 | # else POST request | |
155 | 155 | client_id = request.form.get('revoke-id', '') |
swtstore/sample_config.py
(2 / 2)
  | |||
1 | 1 | ||
2 | |||
2 | 3 | class DefaultConfig(): | |
3 | 4 | ||
4 | 5 | """ | |
… | … | ||
21 | 21 | # been done prior to editing this line. | |
22 | 22 | # Refer https://wiki.debian.org/PostgreSql#User_access for creating users | |
23 | 23 | # in postgresql. | |
24 | SQLALCHEMY_DATABASE_URI =\ | ||
25 | 'dialect+driver://username:password@host:port/database' | ||
24 | SQLALCHEMY_DATABASE_URI = 'dialect+driver://username:password@host:port/database' | ||
26 | 25 | ||
27 | 26 | # Log level for the application | |
28 | 27 | LOG_LEVEL = 'ERROR' |