Commit f867edb11e372a8dc06e4236e6829f76f62bf818

Implementing OAuth 2.0 enabled APIs

  - APIs: GET /api/sweets/q, POST /api/sweets, GET /api/users/me are now OAuth
    2.0 enabled. That means registered clients can use these APIs to sweet to
swtstore and retrieve swts from the swtstore.
  - Implement CORS headers in APIs and Persona login URLS, so that swtstore can
    support cross-domain, cross-origin requests.
  - README changes to better explain how to install and run this application.
  • Diff rendering mode:
  • inline
  • side by side

README.md

8------------8------------
99
10This is the sweet store application.10This is the sweet store application.
11
12The store for the decentralized, semantic, social web tweets a.k.a SWeeTs!11The store for the decentralized, semantic, social web tweets a.k.a SWeeTs!
1312
13This README is about installing, configuring and running/deploying this
14application. If you want to know more about SWeeTs, please go to
15[wiki.janastu.org/Sweet_Web](http://janastu.org/technoscience/index.php/Sweet_Web) and
16[trac.pantoto.org/sweet-web](http://trac.pantoto.org/sweet-web).
17
14This application acts as the repository store for all the SWeeTs that are18This application acts as the repository store for all the SWeeTs that are
15generated from the clients registered with the sweet store. It also provides
19generated from the clients registered with the sweet store. It provides
16query APIs to query the SWeeTs.20query APIs to query the SWeeTs.
1721
22It also provides user management to manage the users of the swtstore.
23
24All the APIs of the swtstore are accessed by using an access token which is
25generated by the swtstore when an user authorizes a third-party application
26through OAuth.
27
18Sweet store provides the following APIs:28Sweet store provides the following APIs:
1929
20 - [GET] /api/sweets/<id>: Get a specific SWeeT by its id.30 - [GET] /api/sweets/<id>: Get a specific SWeeT by its id.
2131
32 - [GET] /api/sweets/q?who=<username>&what=<contextname>&where=<URL> :
33 This API is for querying sweet based on the who, what and where
34 parameters.
35 This API do not support querying based on parameters mentioned in how,
36 but will be supported in future. Right now, the client can get sweets
37 based on the above mentioned three parameters, and as the 'how' part is
38 a JSON, it is trivial to do further filtering based on parameters of
39 'how' by the client.
40
22 - [POST] /api/sweets : Post a SWeeT to this swtstore with the data in the41 - [POST] /api/sweets : Post a SWeeT to this swtstore with the data in the
23 body of the request. Only registered applications can sweet to sweet store.
42 body of the request. The data or payload is a list of sweets. Even if you
43 are sending one sweet, make sure that the sweet is in a list.
2444
25 - [POST] /api/context : Create a new context on the swtstore.
45 - [GET] /api/users/me : Get a JSON details of the current user logged in.
2646
2747
28Any client side application can communicate with the sweet store using these
29APIs.
48Any third-party client side application can communicate with the swtstore
49using these APIs, provided they have a valid access token.
3050
3151
32Installing52Installing
62For more information on supported databases see62For more information on supported databases see
63[here](http://docs.sqlalchemy.org/en/rel_0_9/dialects/index.html).63[here](http://docs.sqlalchemy.org/en/rel_0_9/dialects/index.html).
6464
65_Important:_
66__So once you are sure you have Python and a relational database (like
67MySQL/Postgresql etc.) installed. You can go ahead and follow these steps:__
65___ Once you are sure you have Python and a relational database (like
66MySQL/Postgresql etc.) installed. You can go ahead and follow these steps:___
6867
69* Clone the repository from [https://git.pantoto.org/sweet-web/sweet-web-engine]
70 (https://git.pantoto.org/sweet-web/sweet-web-engine) OR you can download the
68* Clone the repository from [https://git.pantoto.org/sweet-web/sweet-web-engine](https://git.pantoto.org/sweet-web/sweet-web-engine)
69 OR you can download the
71 code from the same link.70 code from the same link.
7271
73* Initialize a python virtual environment using virtualenv in the same place
74 where you cloned the reposiory in the above step. Now, activate the
75 environment ``$ source <path/to/your/current-virtual-env>/bin/activate ``
72* It is strongly recommended to do the installation inside a virtual environment.
73 If you think, you know what you are doing and don't need the virtual
74 environment, you can skip to the next step.
75 Initialize a python virtual environment using `virtualenv` in the same directory
76 where you cloned the repository in the above step. Now, activate the
77 environment
7678
77 See
78 [http://www.virtualenv.org/en/latest/virtualenv.html]
79 (http://www.virtualenv.org/en/latest/virtualenv.html) for more details.
79 > ``$ source <path/to/your/current-virtual-env>/bin/activate ``
8080
81 See [http://www.virtualenv.org/en/latest/virtualenv.html](http://www.virtualenv.org/en/latest/virtualenv.html) for more details about `virtualenv`.
82
81* Run the setup.py script to install `` python setup.py install ``83* Run the setup.py script to install `` python setup.py install ``
8284
83You're done installing swtstore. Now you need to configure it to run.85You're done installing swtstore. Now you need to configure it to run.
91* Copy the contents of ``sample_config.py`` inside the ``swtstore`` directory91* Copy the contents of ``sample_config.py`` inside the ``swtstore`` directory
92 into ``config.py`` inside ``swtstore`` directory itself.92 into ``config.py`` inside ``swtstore`` directory itself.
9393
94 Assuming you are using a Unix based system, and you are in the root directory
94 Assuming you are using a *-nix based system, and you are in the root directory
95 of the codebase,95 of the codebase,
9696
97 `` $ cp swtstore/sample_config.py swtstore/config.py``97 `` $ cp swtstore/sample_config.py swtstore/config.py``
9898
99* Edit the config.py file, and change the values accordingly.99* Edit the config.py file, and change the values accordingly.
100100
101* Now, you have to setup the database for the swtstore application. But
102 fortunately, the creation of database and tables have also been scripted, so
103 all you need to do is run the ``dbsetup.py`` script.
104 `` $ python dbsetup.py ``
101105
106**NOTE:** Please remember that all these configuration step is necessary and is
107required wether you are running the application locally or deploying it on a
108server.
102109
110
111
103Running the server locally112Running the server locally
104--------------------------113--------------------------
105114
106Run the runserver.py script to run the server locally,115Run the runserver.py script to run the server locally,
107116
108`` python runserver.py ``
117> `` $ python runserver.py ``
109118
110This runs the application locally, on port 5001
119This runs the application locally, on port 5001. So you can direct your browser
120to http://localhost:5001
111121
112122
113123
114Deploying the application124Deploying the application
115-------------------------125-------------------------
116126
117The wsgi script to deploy the application is present.
118Point your webserver like Apache, or Nginx to point to the swtstore.wsgi
127SwtStore is deployed as a WSGI application server.
128
129The wsgi script to deploy the application is provided (its called
130`swtstore.wsgi`).
131Point your webserver like Apache, or Nginx to point to the `swtstore.wsgi`
119script.132script.
120133
121See Apache WSGI configuration here:134See Apache WSGI configuration here:
122[http://modwsgi.readthedocs.org/en/latest/configuration-directives/WSGIScriptAlias.html]
123(http://modwsgi.readthedocs.org/en/latest/configuration-directives/WSGIScriptAlias.html)
135[http://modwsgi.readthedocs.org/en/latest/configuration-directives/WSGIScriptAlias.html](http://modwsgi.readthedocs.org/en/latest/configuration-directives/WSGIScriptAlias.html)
124136
137TODO: [insert Nginx WSGI config link]
125138
139
126Help / Feedback140Help / Feedback
127---------------141---------------
128142
129If you need any help, or have any questions, comments or feedback, you can contact at143If you need any help, or have any questions, comments or feedback, you can contact at
130rayanon or arvind or bhanu @servelots.com
144rayanon or arvind or bhanu at servelots.com
131145
132You can also join channel #servelots on freenode network, using your favourite146You can also join channel #servelots on freenode network, using your favourite
133IRC client. We usually hang out at #servelots.147IRC client. We usually hang out at #servelots.

dbsetup.py

1# coding utf-8
1# -*- coding: utf-8 -*-
2# swtstore->dbsetup.py
23
4# Create and setup databases for the first time run of the application
5
3import sys, os6import sys, os
47
8# Get the path to the base directory of the app
5BASE_DIR = os.path.join(os.path.dirname(__file__))9BASE_DIR = os.path.join(os.path.dirname(__file__))
610
11# append the path to the WSGI env path
7sys.path.insert(0, BASE_DIR)12sys.path.insert(0, BASE_DIR)
813
14# Import and create the app; also get the db instance from the current app
9from swtstore import create_app, getDBInstance15from swtstore import create_app, getDBInstance
1016
11app = create_app()17app = create_app()
1218
13db = getDBInstance()19db = getDBInstance()
1420
15# Import all modules which represents a SQLAlchemy model
16# They have correspondin tables that are needed to be created
21# Import all modules which represents a SQLAlchemy model;
22# they have corresponding tables that are needed to be created
17from swtstore.classes.models import Sweet, Context, Client23from swtstore.classes.models import Sweet, Context, Client
18from swtstore.classes.models.um import User, Group, Membership24from swtstore.classes.models.um import User, Group, Membership
1925
26# Create them!
20db.create_all()27db.create_all()

runserver.py

1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2# swtstore->runserver.py
3
2# Script to run the application server in development mode4# Script to run the application server in development mode
35
4import sys, os6import sys, os
5from swtstore import create_app, getDBInstance
67
7# Get the path to the base directory of the app8# Get the path to the base directory of the app
8BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__)))9BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__)))
11# append the path to the WSGI env path11# append the path to the WSGI env path
12sys.path.insert(0, BASE_DIR)12sys.path.insert(0, BASE_DIR)
1313
14# Import and create the app
15from swtstore import create_app
16
14app = create_app()17app = create_app()
1518
19# Run the server if this script is directly executed
20# Presumably, this is development mode
16if __name__ == '__main__':21if __name__ == '__main__':
17 app.run(debug=True, host='0.0.0.0', port=5001)22 app.run(debug=True, host='0.0.0.0', port=5001)

setup.py

11swtstore11swtstore
12--------12--------
1313
14The sweet store for decentralized, semantic, social web tweets a.k.a SWeeTs!!
14The store for decentralized, semantic, social web tweets a.k.a SWeeTs!!
1515
16"""16"""
17from setuptools import setup17from setuptools import setup
30 version='0.1 - alpha',30 version='0.1 - alpha',
31 url='https://git.pantoto.org/sweet-web',31 url='https://git.pantoto.org/sweet-web',
32 license='BSD',32 license='BSD',
33 author='Halwai',
34 author_email='rayanon@servelots.com',
33 author='swtstore authors',
34 author_email='rayanon004@gmail.com',
35 description='Server-side store for decentralized, semantic, social, web\35 description='Server-side store for decentralized, semantic, social, web\
36 tweets',36 tweets',
37 long_description=__doc__,37 long_description=__doc__,

swtstore/application.py

9from classes.database import db9from classes.database import db
10from config import DefaultConfig10from config import DefaultConfig
11from classes import views11from classes import views
12#from something import oauth
13
14#from classes import models12#from classes import models
13from classes import oauth
1514
16__all__ = ['create_app', 'getDBInstance']15__all__ = ['create_app', 'getDBInstance']
1716
21 (views.api, '/api'),21 (views.api, '/api'),
22 (views.user, '/users'),22 (views.user, '/users'),
23 (views.context, '/contexts'),23 (views.context, '/contexts'),
24 (views.app, '/apps')
24 (views.app, '/apps'),
25 (views.Oauth, '/oauth')
25)26)
2627
2728
65def configure_extensions(app):65def configure_extensions(app):
66 db.init_app(app)66 db.init_app(app)
67 db.app = app67 db.app = app
68 #oauth.init_app(app)
68 oauth.init_app(app)
6969
70# return the current db instance70# return the current db instance
71# TODO: is this needed so much?71# TODO: is this needed so much?

swtstore/classes/models/client.py

2929
30 _is_private = db.Column(db.Boolean)30 _is_private = db.Column(db.Boolean)
3131
32 _host_url = db.Column(db.String(60))
33
32 _redirect_uris = db.Column(db.Text)34 _redirect_uris = db.Column(db.Text)
33 _default_scopes = db.Column(db.Text)35 _default_scopes = db.Column(db.Text)
3436
3537
36 @property38 @property
39 def client_id(self):
40 return self.id
41
42 @property
37 def client_type(self):43 def client_type(self):
38 if self._is_private:44 if self._is_private:
39 return 'private'45 return 'private'
40 return 'public'46 return 'public'
4147
42 @property48 @property
49 def host_url(self):
50 return self._host_url
51
52 @property
43 def redirect_uris(self):53 def redirect_uris(self):
44 if self._redirect_uris:54 if self._redirect_uris:
45 return self._redirect_uris.split()55 return self._redirect_uris.split()
66 return []66 return []
6767
68 def __repr__(self):68 def __repr__(self):
69 return '[Client: <%s> : <%s>]' % (self.id, self.name)
69 return '<Client: %s :: ID: %s>' % (self.name, self.id)
7070
71 def __str__(self):71 def __str__(self):
72 return '[Client: <%s> : <%s>]' % (self.id, self.name)
72 return '<Client: %s :: ID: %s>' % (self.name, self.id)
7373
7474
75 # create and persist the client to the database75 # create and persist the client to the database
124 """124 """
125 The final token to be used by a client125 The final token to be used by a client
126 """126 """
127
128 __tablename__ = 'tokens'
129
127 id = db.Column(db.Integer, primary_key=True)130 id = db.Column(db.Integer, primary_key=True)
128131
129 client_id = db.Column(db.String(40), db.ForeignKey('clients.id'),132 client_id = db.Column(db.String(40), db.ForeignKey('clients.id'),
156156
157@oauth.clientgetter157@oauth.clientgetter
158def loadClient(client_id):158def loadClient(client_id):
159 return Client.query.filter_by(id=client_id).first()
159 print '@oauth.clientgetter'
160 #return Client.query.filter_by(id=client_id).first()
161 return Client.query.get(client_id)
160162
161@oauth.grantgetter163@oauth.grantgetter
162def loadGrant(client_id, code):164def loadGrant(client_id, code):
165 print '@oauth.grantgetter'
163 return Grant.query.filter_by(client_id=client_id, code=code).first()166 return Grant.query.filter_by(client_id=client_id, code=code).first()
164167
165@oauth.grantsetter168@oauth.grantsetter
166def saveGrant(client_id, code, request, *args, **kwargs):169def saveGrant(client_id, code, request, *args, **kwargs):
170 print '@oauth.grantsetter'
167 expires = datetime.utcnow() + timedelta(seconds=100)171 expires = datetime.utcnow() + timedelta(seconds=100)
168 grant = Grant(172 grant = Grant(
169 client_id = client_id,173 client_id = client_id,
183183
184@oauth.tokengetter184@oauth.tokengetter
185def loadToken(access_token=None, refresh_token=None):185def loadToken(access_token=None, refresh_token=None):
186 print '@oauth.tokengetter'
186 if access_token:187 if access_token:
187 return Token.query.filter_by(access_token=access_token).first()188 return Token.query.filter_by(access_token=access_token).first()
188 elif refresh_token:189 elif refresh_token:
191191
192@oauth.tokensetter192@oauth.tokensetter
193def saveToken(token, request, *args, **kwargs):193def saveToken(token, request, *args, **kwargs):
194 print '@oauth.tokensetter'
195
194 toks = Token.query.filter_by(client_id=request.client.id,196 toks = Token.query.filter_by(client_id=request.client.id,
195 user_id=request.user.id)197 user_id=request.user.id)
196 # make sure that every client has only one token connected to a user198 # make sure that every client has only one token connected to a user
197 for t in toks:199 for t in toks:
198 db.session.delete(t)200 db.session.delete(t)
201
199 expires_in = token.pop('expires_in')202 expires_in = token.pop('expires_in')
200 expires = datetime.utcnow() + timedelta(seconds=expires_in)203 expires = datetime.utcnow() + timedelta(seconds=expires_in)
201204
206 access_token = token['access_token'],206 access_token = token['access_token'],
207 refresh_token = token['refresh_token'],207 refresh_token = token['refresh_token'],
208 token_type = token['token_type'],208 token_type = token['token_type'],
209 _scopes = token['scopes'],
209 _scopes = token['scope'],
210 expires = expires,210 expires = expires,
211 client_id = request.client.id,211 client_id = request.client.id,
212 user = request.user.id
212 user = request.user
213 )213 )
214 db.session.add(tok)214 db.session.add(tok)
215 db.session.commit()215 db.session.commit()
216 return tok216 return tok
217
218@oauth.usergetter
219def getUser():
220 return User.getCurrentUser()

swtstore/classes/models/sweet.py

50 print self.created50 print self.created
51 return {51 return {
52 'id': self.id,52 'id': self.id,
53 'who': self.who,
53 'who': self.who.username,
54 'user_id': self.user_id,
54 'what': self.what.name,55 'what': self.what.name,
55 'context_id': self.context_id,56 'context_id': self.context_id,
56 'where': self.where,57 'where': self.where,

swtstore/classes/utils/httputils.py

11
22
3def make_cross_origin_headers(response):
4 response.headers['Access-Control-Allow-Origin'] = 'http://localhost:5000'
5 response.headers['Access-Control-Allow-Origin'] = 'http://localhost'
6 response.headers['Access-Control-Max-Age'] = '20days'
7 response.headers['Access-Control-Allow-Headers'] = 'Origin,\
8 X-Requested-With, Content-Type, Accept'
3def make_cross_origin_headers(response, host_url):
4 print 'client\'s host_url: %s' % host_url
5 response.headers['Access-Control-Allow-Origin'] = host_url
6 response.headers['Access-Control-Max-Age'] = '3600'
7 response.headers['Access-Control-Allow-Credentials'] = 'true'
8 response.headers['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept'
9
10 print 'Updated headers'
11 print response.headers
912
10 return response13 return response

swtstore/classes/views/__init__.py

3from .user import user3from .user import user
4from .context import context4from .context import context
5from .app import app5from .app import app
6from .oauth import Oauth

swtstore/classes/views/api.py

1from flask import Module, jsonify, request, make_response, abort, g
2import json
1from flask import Module, jsonify, request, make_response, abort, g, json
2#import json
3from sqlalchemy.exc import IntegrityError3from sqlalchemy.exc import IntegrityError
44
5from swtstore.classes.models import Context5from swtstore.classes.models import Context
7from swtstore.classes.exceptions import AlreadyExistsError7from swtstore.classes.exceptions import AlreadyExistsError
8from swtstore.classes.utils import urlnorm # normalize URLs8from swtstore.classes.utils import urlnorm # normalize URLs
9from swtstore.classes.utils.httputils import make_cross_origin_headers9from swtstore.classes.utils.httputils import make_cross_origin_headers
10from swtstore.classes import oauth
1011
1112
12api = Module(__name__)13api = Module(__name__)
1314
1415
15
16# Get a specific sweet16# Get a specific sweet
17@api.route('/sweets/<int:id>', methods=['GET'])17@api.route('/sweets/<int:id>', methods=['GET'])
18def getSweetById(id):18def getSweetById(id):
3030
31# Post a sweet to the sweet store31# Post a sweet to the sweet store
32@api.route('/sweets', methods=['OPTIONS', 'POST'])32@api.route('/sweets', methods=['OPTIONS', 'POST'])
33def createSweet():
33@oauth.require_oauth('email', 'sweet')
34def createSweet(oauth_request):
3435
35 response = make_response()36 response = make_response()
3637
38 client = oauth_request.client
39
37 #TODO: check if response is coming from a registered client40 #TODO: check if response is coming from a registered client
38 response = make_cross_origin_headers(response)
41 response = make_cross_origin_headers(response, client.host_url)
3942
40 if request.method == 'OPTIONS':43 if request.method == 'OPTIONS':
41 response.status_code = 20044 response.status_code = 200
56 print 'new sweet payload recvd..'56 print 'new sweet payload recvd..'
57 print payload57 print payload
5858
59 if 'who' not in payload and 'what' not in payload and 'where' not in\
60 payload and 'how' not in payload:
59 # the payload has to be a list; a list of swts
60 for each in payload:
61 if 'what' not in each and 'where' not in\
62 each and 'how' not in each:
6163
62 print 'Invalid Request..'
63 abort(400)
64 print 'Invalid Request..'
65 abort(400)
6466
65 #who = User.getUserByName(payload['who'])
67 # all ok. create swts from the list now
6668
67 what = Context.query.filter_by(name=payload['what']).first()
69 swts = []
70 for each in payload:
6871
69 if what is None:
70 print 'Context doesn\'t exist'
71 g.error = 'Context doesn\'t exist'
72 abort(400) # this context doesn't exist!
72 what = Context.query.filter_by(name=each['what']).first()
7373
74 print 'SWEET DATA'
75 print '------------'
76 print payload['who']
77 print what
78 print payload['where']
79 print payload['how']
80 print '-------------'
74 if what is None:
75 print 'Context doesn\'t exist'
76 g.error = 'Context doesn\'t exist'
77 abort(400) # this context doesn't exist!
8178
82 new_sweet = Sweet(payload['who'], what, payload['where'], payload['how'])
79 # Get the authenticated user from the oauth request object.
80 # Older swtr clients sending `who` in string will be ignored.
81 who = oauth_request.user
8382
84 print new_sweet
85 new_sweet.persist()
83 print 'SWEET DATA'
84 print '------------'
85 print who
86 print what
87 print each['where']
88 print each['how']
89 print '-------------'
8690
91 new_sweet = Sweet(who, what, each['where'], each['how'])
92
93 new_sweet.persist()
94 print new_sweet
95 swts.append(new_sweet.id)
96
87 response.status_code = 20097 response.status_code = 200
98 response.data = json.dumps(swts)
88 return response99 return response
89100
90101
91
92# The Sweet query API: /sweets/q?who=<>&what=<>&where=<>102# The Sweet query API: /sweets/q?who=<>&what=<>&where=<>
93# args: who, what, where103# args: who, what, where
94@api.route('/sweets/q', methods=['GET'])
95def querySweets():
104@api.route('/sweets/q', methods=['GET', 'OPTIONS'])
105@oauth.require_oauth('sweet')
106def querySweets(oauth_request):
96107
108 response = make_response()
109 response = make_cross_origin_headers(response,
110 oauth_request.client.host_url)
111
112 if request.method == 'OPTIONS':
113 reponse.status_code = 200
114 return response
115
97 args = request.args116 args = request.args
98117
99 # if no arguments are passed, its an invalid request118 # if no arguments are passed, its an invalid request
135 print 'recvd params'135 print 'recvd params'
136 print params136 print params
137137
138 response = make_response()
139138
140 sweets = Sweet.query.filter_by(**params).all()139 sweets = Sweet.query.filter_by(**params).all()
141140
142 if len(sweets) == 0:141 if len(sweets) == 0:
142 print 'No sweets found to satisfy query..'
143 abort(404)143 abort(404)
144144
145 swts = [i.to_dict() for i in sweets]145 swts = [i.to_dict() for i in sweets]
176176
177177
178# Create a new Sweet Context178# Create a new Sweet Context
179@oauth.require_oauth('email', 'context')
179@api.route('/contexts', methods=['POST'])180@api.route('/contexts', methods=['POST'])
180def createContext():181def createContext():
181182
213 res = new_context.persist()213 res = new_context.persist()
214214
215 response.status_code = 200215 response.status_code = 200
216 return response
217
218
219# Send back logged in user data
220@api.route('/users/me', methods=['GET', 'OPTIONS'])
221@oauth.require_oauth('email')
222def getCurrentUser(oauth_request):
223 response = make_response()
224 response = make_cross_origin_headers(response,
225 oauth_request.client.host_url)
226 response.status_code = 200
227
228 if request.method == 'OPTIONS':
229 return response
230
231 response.data = json.dumps(oauth_request.user.to_dict())
216 return response232 return response

swtstore/classes/views/app.py

3# classes/views/apps.py3# classes/views/apps.py
44
55
6from flask import Module, jsonify, request, render_template, redirect, url_for
6from flask import Module, jsonify, request, render_template, redirect,\
7 url_for, flash, abort
8
7from hashlib import md59from hashlib import md5
8from werkzeug.security import gen_salt10from werkzeug.security import gen_salt
911
10from swtstore.classes.models import Client12from swtstore.classes.models import Client
11from swtstore.classes.models.um import User13from swtstore.classes.models.um import User
14from swtstore.classes.utils import urlnorm
1215
1316
14app = Module(__name__)17app = Module(__name__)
21def list():21def list():
22 current_user = User.getCurrentUser()22 current_user = User.getCurrentUser()
23 if current_user is None:23 if current_user is None:
24 return redirect(url_for('index'))
24 return redirect(url_for('frontend.index'))
2525
26 her_apps = Client.getClientsByCreator(current_user.id)26 her_apps = Client.getClientsByCreator(current_user.id)
27 print 'her_apps'27 print 'her_apps'
34def register():34def register():
35 current_user = User.getCurrentUser()35 current_user = User.getCurrentUser()
36 if current_user is None:36 if current_user is None:
37 return redirect(url_for('index'))
37 return redirect(url_for('frontend.index'))
3838
39 user = current_user.to_dict()39 user = current_user.to_dict()
4040
42 return render_template('register_app.html', user=user)42 return render_template('register_app.html', user=user)
4343
44 elif request.method == 'POST':44 elif request.method == 'POST':
45 if not request.form.get('name'):
46 abort(400)
45 req_fields = ['name', 'host_url', 'redirect_uris', 'scopes']
46 for field in req_fields:
47 if not request.form.get(field):
48 abort(404)
4749
48 new_app = Client(50 new_app = Client(
49 id = gen_salt(40),51 id = gen_salt(40),
53 name = request.form.get('name'),53 name = request.form.get('name'),
54 description = request.form.get('desc'),54 description = request.form.get('desc'),
55 user_id = current_user.id,55 user_id = current_user.id,
56 _redirect_uris = request.form.get('redirect_uris'),
57 _default_scopes = request.form.get('scopes'),
56 _host_url = request.form.get('host_url'),
57 _redirect_uris = urlnorm(request.form.get('redirect_uris')),
58 _default_scopes = ' '.join(request.form.get('scopes').split(',')),
58 _is_private = False59 _is_private = False
59 )60 )
60 new_app.persist()61 new_app.persist()

swtstore/classes/views/oauth.py

1# -*- coding utf-8 -*-
2# classes/views/oauth.py
3
4from flask import Module, jsonify, request, render_template, redirect, url_for
5import json
6
7from swtstore.classes import oauth
8from swtstore.classes.models.um import User
9from swtstore.classes.models import Client
10
11
12Oauth = Module(__name__)
13
14@Oauth.route('/authorize', methods=['GET', 'POST'])
15@oauth.authorize_handler
16def authorize(*args, **kwargs):
17 current_user = User.getCurrentUser()
18 if current_user is None:
19 return render_template('oauth_login.html')
20
21 if request.method == 'GET':
22 client_id = kwargs.get('client_id')
23 client = Client.query.get(client_id)
24 print '/authorize: '
25 print client
26 kwargs['client'] = client
27 kwargs['user'] = current_user
28 print kwargs
29 return render_template('authorize.html', **kwargs)
30
31 confirm = request.form.get('confirm', 'no')
32 print confirm
33 return confirm == 'yes'
34
35@Oauth.route('/token', methods=['GET', 'POST'])
36@oauth.token_handler
37def access_token():
38 #print request.form
39 print 'access token touched..'
40 return None
41
42@Oauth.route('/errors')
43def error():
44 return jsonify(error=request.args.get('error'))

swtstore/classes/views/user.py

21def login():21def login():
2222
23 response = make_response()23 response = make_response()
24 response = make_cross_origin_headers(response)
24 #response = make_cross_origin_headers(response)
2525
26 if 'assertion' not in request.form:26 if 'assertion' not in request.form:
27 response.status_code = 40027 response.status_code = 400
2929
30 print request.remote_addr30 print request.remote_addr
31 data = {'assertion': request.form['assertion'], 'audience':31 data = {'assertion': request.form['assertion'], 'audience':
32 'http://localhost:5001'}
32 config.SWTSTORE_URL}
3333
34 resp = requests.post(config.MOZ_PERSONA_VERIFIER, data=data, verify=True)34 resp = requests.post(config.MOZ_PERSONA_VERIFIER, data=data, verify=True)
35 print resp.status_code35 print resp.status_code
64def logout():64def logout():
6565
66 response = make_response()66 response = make_response()
67 response = make_cross_origin_headers(response)
67 #response = make_cross_origin_headers(response)
6868
69 if 'email' in session:69 if 'email' in session:
70 print 'logging out '70 print 'logging out '

swtstore/sample_config.py

2929
30 # The Mozilla Persona Verifier Host. Leave it as it is.30 # The Mozilla Persona Verifier Host. Leave it as it is.
31 MOZ_PERSONA_VERIFIER = 'https://verifier.login.persona.org/verify'31 MOZ_PERSONA_VERIFIER = 'https://verifier.login.persona.org/verify'
32
33 # The URL at which this app, swtstore, is deployed.
34 SWTSTORE_URL = 'http://demo.swtr.us'
35
36 # Bearer token expiry
37 OAUTH2_PROVIDER_TOKEN_EXPIRES_IN = 3600

swtstore/static/img/glyphicons-halflings-white.png

1PNG
2
3 IHDRӳ{PLTEmmmⰰᒒttt󻻻bbbeeeggg𶶶xxx󛛛Ƽ몪֢UUU鿿rOtRNS#_
4/oS?C kDOS_6>4!~a @1_'onҋM3BQjp&%!l"Xqr; A[<`am}43/0IPCM!6(*gK&YQGDP,`{VP-x)h7e1]W$1bzSܕcO]U;Zi<N# ) 86pV:h#0ZQJNEDT~^-IDATx^읇#Ǚb' 4A$Ah )p3<MF9Y9X,riھ|st9s޿X kjv@l_I*~h>'y"؆K 64Y*.v@c.};tN%DI !ZЏ5LH26 ɯ" -bE,,)ʏ B>mn6pmRO
5wm@V#?'CȑZ#qb|$:)/E%nRqChn%i̓}lm
6?idd",`H"r.z~(bQU&)5X#EMR<*p[[%.Ọk7lIoJF lV!̡ăuH`&,zRk$|$lXbjߪdU?Σ$HW$U'HE3*խU\}(
7zhVk}guRk$%|T|ck獳"D_W+.Q)@ƽHbslTDR2Xm#a 3lYzj㒚#! 4J8(cvt]aT D ΅Q?^-_^$:\V $N|=(vZ'q6Z׆B5V!y3K㱿bv4xR]al!IoP@tVyL٪mlڿIUb|[*lke'*WddDӝ}\W_WߝrN?vޫ۲X%0uoui*JVƦb%}i5IYlNE-wςf_W3mI-mQ)S kTC7m<"܌bT|'$ҘR&>O p6tSN\ׯLm\r@3uT
8b7t.5.q3r0=8TiJ\6uF
9R32^'ŪxI F8O{%8kJMSȴdBEdWCYO:/ON/I_=xFE! =i:o~ y?''[͓[͓[͓[͓[ͭ.U>$PƦc%]\c:| ,eSZ,oXrX!R@Zv 0>?* <|N60;{ad2v+D^t[q!۞V}fۨϏYeॗ)Vyl|" fUq@Ǽ4Y-Y-!6aB:o%JIUQ|UKO`=\ :0x Pau@!KPdxhw1>$j΍vZdxSUA&[URd7øzk/rU^w:I.VǮc>q.!zSr&2)Wg R -iQ 8Pa\ОU%iݡU_=p Lu(N?0?Æ:]άtB%U|NsorNf ,P !v"
10Y6hL_@@bscqgv4||0lϟ$S9bʱj#~?o}}7sAPm:IV=n
11!{{hEࢪ8suoLT$;VscqD3 ༂3.DBB4&V' T `D6Ϸqyj8V*X%@s\jrN$|=5Ά 'mUiKi%CI:ssaƅ`*`=l)>u՘MeuSI_OL_}o&jzp{lu:O)s%Q@$<]f xO%PCbhr2PKpf5Në3^o]eJiB464^tuٲU֌:G4'22YpuG'/Py4?.SBP_>I 1t3ΓBɭɭɭɭVVVVVs]!67(g y@ 4>Q VF}^Xׇڼje26 L%YGh lC})<
12!EEPZWZV+@†R 5{@ouɐ4&H6ey V݀VťcqZޒrJyByFzFN$Hb*+jՏqэ ګkݿUXle1d0d^-B%} {Y%r*j5Ak5u",:~ҸY~
13hSA~6 fulՇf{ȵQtATHZkƭ/_Sn
14u']b]|m`BāJ,O$du]Zs FL:aǙT4o~by?wpj滥A(x]†f~an֧/^dڲcՇ,!1i&xi_VK@ip̓9Vi%a; L?0J*Ū5U'x^6V[^ {eU|:0=0d۫o*Jq%[YN.sQLud[29I:WnmXlڃ6!lNlVէKUjV\J%UߊBLcKfb>a=b~R]aG%[js@<i[Х*^.d;UIR+OD2eܶ QN34"1g0u\I}wFV4y/Djjjn5On5On5On5On5h,ҷUr]]L^%JDiɭGԝ ߴ/%='qå):Q<X.'[@Pv/ɼ>/9MطݘU>yɲX@} Ftg^vO\Ӹwvpz3K5i!$P>ā'VƛL2r@UMKZ6tw맟¦bm1h||]}~0MjA(JJP68C&yr׉e}j_cJ?I0k>šW |Bޝ."TEXd 8!cw*E(J)![W"j_ТeX_XB;oO0~?:PC (.[!Wq%*leY)E<^KZT60.#A\5;Rmtkd/8)5~^0 #Ckgey)ͶԺ6ĥ<(?&uAVm0^h.txR*a':,H|ō l5z;8+e#b'#|}2w(|KcJ l6 w^Տoi3H R ̔9,YgPְ:N [5SR![)]i}`mN4Хv`|;f(FltL8÷Z#AO%Y)NU5YedJE3dZذ<xɝe @PڧFTR 2S·Φ/uZ~C3XzUx\2se DD.fBO&en'iR%?FyVsS~$umw()ro0*Di!3:On[B!sʇBp>ݣHT1 ;8MjnʏӤqp 1h^<<<jjjjjnq(qpOk}I?TY8HmhyK̝u5ItenQBޗ`R`EP ڦx>>yt{?|'j)}YUU{@V/J1F+7䀉[OWO[ yUY!?BD%DWj>-Ai6xz)U R7 d@g\so)a4zf[W+> P>
15|qLG8vȣlj2Zt+VA6g</Q HSrΣd}Yqg]sY];]FC@5YՖ5C38o)k1'd6>T *ʆUz(m)CD `He/.:zN9pgo &NC׃އ>Wհ_Hj)Xe6F7pm-`'c.AZ=^e8F;<J1{+8'ɪ'և\A*[ R$UY)V AyɃw)Ec#<T\vW<U1IؘCDoYo]wmaw:B :'Z+v}|0q1P΃*uT7 F39A}$f+o[I5ʰ޽x(&iʼY:cPp*b¸JjV7ljtsNkv[fy3g]u鲱gJE0)Viłù\vW<Ugte~B[AHJ'.n& 1Ԕ o%gͱ_N
165.W3y/Ddyr<<<<<jܪ{waw:6dJ;&3p tlasW_UT_'9{?aԬl/0dHgqllc8Rym=ˢ_ͺ[Է71x""SIfVrx33y)h hՠ0?r5x_ -j чoO:$XBXJѣ1#ֈu7`zu2{\;uܗ9@0V$2XS&Ba[O~jN2ߠȪ/jz_nA~ uh@GLOeɵ ?Tf<Ve@*-}e@ 0Zt /~Xm0**H'\uSEmLֻ6;+{l5۽?u*_ Ni-:I@,;]WY` *߀nSO~nWP.cZTuPo^ǃ7wBRBW_mdjB6:*H]dQ>{Rtn(z!S7o
17Iew3]bܗ85|iϠRJkʱZRO+8U&:]ZieR<I~|d,j릟{;7U݌XB`[u5~=zq굵Ű޹ebc5o{;ߩ@;n*T ĵ2$ܨ0'Y-?
18j[Zjӭvi-*rD{mL-,L=ymxc:WevұoÏń
19"dF8[T}ӵF-IVlV[P)DV C8ݪ}|kZ {Y|xrrxaG_>(JMޗ7Z@5a^\GzsρU*rMezT^:ɬͦX=>$
20bi>U&XQoybbGk8
21Ҙn).Սo ^MmdZi$soo*{4eLbLٳ""mx:`:mk[geTެ)'0*TB{!I ''''[͓[͓[͓[͓[]Zj Q.e '/yvQ71(Z&X?(_Z){tڀmZWϏ)-C jqn,̋"IvUL!h꛿skAcrN佚фVE40yX~4zʸV㳰%,)fqtpu~  *^0:ܲ33JO(ZB?K^ v]unlWi0p6[착C_5X#[wX3b廫R{NKAe Se|wxso>P\儔ԕ6;nVmfI$V͓J-J%֌0UwYЎSnum藮xz˗VƫIvnW_qLZ"_Xz 8]Ap?C543zw({7e*Ȳ`۰!AQ:KUnz]1yVGaCm0PY
22ٚUx6TT&hV9V
23ӬzÑ 1[XzZ9erqJND/gX*9oN6D` {I%Mz9—TQ7f\"j_3~xB'ܷY]*KЌ%"5"qxq~ƕ=jS>jV&~]2xzF1X_yD<#NRB}K/iy !V^˿eJ}/FkA7 S+.(ecJ:zWZ몖wQ~ä́p6,e5,+,tv%O^OO}ן -O7>ekC6wa_C |9*WA)UJg8<Zx^?2uY*^?ڇKCZ[0.C@m$-/~|Y[eweQ׶&cO4s|cJwsX8/6/ ڼ;'FLN^8]eadZ 1'^LsBd%+M`SK8פ*)gl#3"gъS qtcxx|H>=:mjUvqysܒLglC6+[FSWg9wV31A ND<$5e(s[ ۨbaF.]KIENDB`

swtstore/static/img/glyphicons-halflings.png

1PNG
2
3 IHDRtEXtSoftwareAdobe ImageReadyqe<1IDATx}ml\EW^ɺD$|nw';vю8m0k<f8ـ<h3$  b,mn ғ0L Y`6s'>QSnSV;1KGsԩ>UoTU1cƖYuּca&#C,pؚ>kں ULW
4-sn3Vq~NocI~L{- H8%_M£wB6EW,ĢpY2+(Y@&A/3kXhߍ-aA<>P'\J;(}#Qz:4%m?nfntK*l9J+DIYu1YZ^(]YYEf@ОlXz]Ut u &5-PW}@t|#LY=s܂,w#+R+?Ƌax X0"ea)tG*ԡwVwV^rf%xB(qּ4>WG#lWU<ЁXJVѶlR$kDVrI7:X<s>%X1NEzw;y9z9O%~~uɗ*=Ixcy}Y(ou
5±N$^j e\iX񝜬];Y-rѲ&>!zlYaVHVN԰9=]=mRMdOUC JUiT}rWW'ڹu)ʢF"YU#P׾&ܑЅROwyzm$Os? +^FTIEq%&~ >M}]ԖwA?
6[Nteexn(措BdMTpʥnqqS?bWXmW6x*{V_!VjΧsVL^j XkQjU6sk̩n~[qǸ-` O:G7l"ksRe2vQ=QƼJUX`gQy~ ďKȰE]#P:td\T/u;س:Jc-%'e q
7?j"/yh48Zi1|JUu>_N;hxwNU JQU7\j̮bT:B?6oJ1Ί%I
8UY-Ii4{=rǤ7@)HKJ+f4X8Cd?'j1 N< 39E<w߬VzE}^_e檴 pt붾39,?glYO<xx|a؎UeF 1;{ EF0`DR+UYiD4?Y`|Bs2yipIq>Wo VTGzg#
9%D0#ܠ3[tiآ( U,]125|Ṋfw7w u+Š]Db]K xbW ՛7|ВX㕛{UcGXk¬|(h)IUa)lp 3luPU]D)/7~4Wt5J}V
10X0z VM;>Gԙ^|gF:jaZ^)74C#jwr,еSlGu;1vm><)}<VZ ue۠D+jyJ6V{j K>ZQՖ&mZ:1UMB~
11a:/᜗:KWWOҠ&Y2f7cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘g*3fF5LbN2#Tf=C`!ZGUe꣇e2V<1mkS4iϗ*.{N8Xaj~ڀnAx,%fE:|YDVj
12¢lg6(:k~MM5?4 ]WO>诋WZiG|QGJeK[YcյpmjE\f/ǎ8&OQ3 .3tt2'-V8pXSrY#J!Q ",ub@FK:u^iy[]<.Cw+W\)b
13kr-.MtڀMqʄ۰#$^X$"V`T4m~w%Pp1|+&UxY8*r8:k7QЃҀT$Ўƙ
14S>~Sjs:5q.w&_Z.X=:ވbw` _kd{'0:ds#qi!224nq\9-KUTsSUuVo@;Uz>^=Np>oPO
15@I@'Gj5o*U>^*ew>ͫʧ᫠Q5 ̈́<$#5Jٻj6e)_ d]2B:^(*:8JYS鬆Kݗ ]U4_rj{5ׇaǑ/yV?GtGb@xPU7O3|鍪 IQ5QGw *(;wf0*PUU<YƔvbt5{2!,}Ҧ:)j2OkΪ' ֊0I.q\(%ojQĖՇa<ԍexAgt'[d;׸`rcdjPFU$UeJI6T&Z}z(z vfuz {}ۿߝݞlxUZ謊.Y岟b%nw@ǩS9|źs%>_o#9\EU~/ځt(r[QZuOo;!MrU]0TcpDő?.cPuF;L_Sb}R/J_+h2$ai UǩS9>Є}76rzu~国4oĨ
161J
17^̘~iC޸55<Pھr/GYk૵5mK
182姪Ϊ5,?1'jÓQpT뾺
19*~I?Hם):\J:3ѴUGo)X.Ë*j\?}㉎G~A{Y#W/3鬶!ʼ=Cgu *u_ޮ+Qe5w:UK?UW1j\S5/<z7P^<,SjUU8v,2__i뻊^R5^vNl>G׹]gwsn zTuO=?/zƲc>Οb#7ֻcgkޛTUj*-T=]uu}>ݨNЭ [ ]:%/_ Sz]6D.mD7Uƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1c>J4hPP+A;'G_XKmL5I.},wFFum$S-E-;Õ
20C3I-`BRx1ғTJݕ;hΊ8 DYJo;Yš5MKɰM;%Pd9KhnD[zgVh,'C
21p!^M(WK2X>UQ%^p8 ˽^#Ζ؄+.@gCz%ɔ-Pr
22KX
23n>=ՔѨeSvRLz5%9UQS \WիK'hp)ô
24Jrh M0F (f_R5///G+x 1"eS 5
25:Tf=+7Qɧ\TEs༬rYs8&k#pSՊ5MTbD܊[Ng5Q\s5PB@[8ɨV1&4Wsy[Ǿ
26wU2V77jމd^~YfC_h;a.&M
27i UWpzs`>/"'OI۲y:BzdTq£=йb:"m/-/PWDQǴ͐57m`H%AV!Hԛ׿@"Qzދ|ߒT-*OU^Ҧ6!Cwk|h&Hd5LEYy'ƣ7%*<C'@lb!wLWW(%C43\x*QFҨ<m߃g?߉^)D}{U֘|Q=C'@|uwLׂQE=?x+x "gS OҨj׈ .fqj[YGͤC焓m>{=)Z%ٝP *G]/8Lw$?8M)\į/#7Ufd7'6\h1
28vIfEIr=1w\WKVZHKgZ͡$mx % `j}TuTQJZ*H>*QxkLFTyU-)ôbiA|q`F'+ 4^Qy xH)#t^?@]^`ARSqjgB:r<h̆RnzPΦ)[+n MXH!0Ir sKϡէUR2T XgƴڳEcƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1cnjǴyƌIxQq7fM4EF.34<.i;eВi1c%9K ͠2JCnw E¤cF`5v6%˿]3Ty`~a[[J>K۷l<2-4YKhgQLxVwP~M Φ0l 3ƅaŊITȀhwJmxIMչ|U7xˆS~2ߕ?kW1kC3];YnSґAeXYz8,'x< k7Kx]$x$vgT#w;o@ z_Vmn|HֵhZg-^TAn- )@4[*9xKƋj>!,Vt:eqn8%ohS(2\Q^aigF3vTUDVlQꅧWc%Ueq4ҝº/U $_Q!>t| ,țG<tC[xTXmf|<OڡMT|(w:_Xj7wt
29AXͦp$^xZRjx`3=^ll+˗eQ8g8V +9M/o14snb tX܍svEl+@\e,,cѮ<(iHVYrQ O7aI>Q%d#jUՆ|; H[bά#,Ws7NT1~m&ǻ{' \㟾 bBKJo8%!$Qj:/RX)$Sy޳ 䍧RDUg_D軦J\jN֖SU;~?Ohssdƣ}6(T <_4b5 ^N N%8QejF7toMyө`)g[/|?өJuGL坕/=CTܠhdifHcǞG4,`D՞{'xG_p/5@m +$jVH3a"*ũ,,HJҵȸT^Qyo&IÉJUVwWLeM~3tA6rwɤ6տ \0HL%LX5c@HHÃZ|NV+7WM{cig*ȸU7iÉбzd * ?gtX8̝OX:]2ɍ]p^++>AVڛE{ DB.&/56ArxY#ܕy)cKQtȪ~! ;C}ʃtf{6$NVsj wupZ)zŁ|-wg+nMVj/d+U~ͯi:_ix whqr>駃-x뼬)ݷyR=! ì:J/lIkV@n74758Z KJ(Uxz1w)^\ԣzȪ󲦨c2f؍v+6f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘2N oC\F1ִ UZJV̚\4Mgq1z{&YT
30,HX~D u\g}x>+YdN̮ol ZX+F[/j+S~2/jV8Jr^ԉ]J}J*ۏ<2԰&JݣjOM@ѯ#0O[SXB^ uze\]dd./xXE
31f'vO_H${%;kt7ށmő|d{aފ^ǛڎE5ʋBr]W=_SAf(0 oU5q ,_\luz˪uz㻲o=Yi~|
320+=VJت /ލzM\zCL[U:|k*^8"\Wٚ\ .XTjX5 SkFu\1 q'mģ/QUؕ*AɽDNZ׮?_[# ˍ4:^j|5LG ||øBW{6[uQF.1$qF9IHg)\5>C#uXZ$#*<ߐsRv1Tj>Jm>*#(
33[Fhsש5*jQʼ&&&P犛L[Q1* ;X}Iΰ[Q?qQZ Hݙ֞VEsBCZ9JTK tup˷ /O,.kUdsOHMg4=-)+ؿh2Nw/r|WQn=GIU;'j,vfdzpe$V GTYsBZO1pj:r"nTUSCgr veAۘ˜FC+Ֆ#[JTe'v9-3 Dmӻuuz?0 o hxuY &_54=f07kלU0]D:jdw/+PGUVS<\2uatc^zYRąmC+7#,|:iNw*|^sm|X>Ъ^1\#͹ &%{,2U>ݎ.c05z#
34ogNO+Q쓭 ,˗-%K\[S_`y+b_94"U+Ύap}I[M,B.NtwHj漬E L߀ 0DX(kڵ NoU{gquz
35RwkէRx'uZ[3'zyyד%<UhN[tzx1 cc]F ݯB"]a[JDս[cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3Ves{L+3VH ]YPA >sƕ3jYF\s=m1&VAɼ?k\+]6yモ1gtOIW7al|1 >$]e 7؝WIe?ަL#>|
36ҭ]
37pM5MUdI61ԠeǼYGhOn3խR:^k_'Yuuq#p#
38J2xl>OjcY馃!ڡ+sZ/ D}2AY mpc#<'xSKx`*W[,e|6BH)㶤kjpDU(2qzx9*tqa/,
39 Z[ 0>Ө֜xN)fă@qըFU՝w(a;ˋ>|Tc|w2eiT]*!_\WG{
40 ]^݅Z5t|6oYHaO@= my^akE.uz]#٥hWv(:,6A߉JFa\ wWex>v<?|&i_qz ] eR_7|& c*kր4f,J U_h\1Au\-L\Ϝ^~Phr*tqa0fT:MU;q>etuMYA>).,;ɦCbwjE)W Fӫ@s4e6^Q9oI}4x<.B?B߫#$Hx.x9,a!RTpgd5xBe.L7@*
41AsduttSVUaRU|I xG߃$T񭟬#_IFMŒ_X@foQIDII?|%$r {ENĸwޕqq?Dؽ}}o/`ӣCTi <QR{\yYFQJkh^?Us:E|]V)Z|HjsW |H'|o=d|߼j #T%O W!N#w1[iH(SVs[=Ɉ71ȳT]A G換3CT׻lRݕCV9Q\V#ܛNӏjˇ1/slR %^s1nUj,x}fW|JuKwpSm,<7<
42Ȼ[R<&p?',Й\;5bH$3#Q4\_>/ywO rD 9YUD] Ή@s]+'UaL} hrU'7:sU|k)H@hNq#ϵ8y˭Xű#w
431!흉R'7fuד0p!WÖW+Nmp\-ioD$g٠˅%%ÐmV]̱rw*Z}y+L
44Nouj}xt)lStuqxmNyKUOnDbhf}k>6ufT%{ <񐮸mjFcmUïc;w8@dGFUA& =nq5]iP}z:k⼶-ʓ Κl*'UzaxWFdZzTNRs+# wzgi:MBqtM l#^'Gߣ*^t{=rERnQ$adJl02%Tڊ^<~g?Of*U^?:N+o[PUs|QR']V-L)H K䐞 mYn\4}YVD hR;g-'3aסM Dh}1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌk*Ț4`L$b U4\dt'>HȄ|.+Y+/Gy2OCWv3v,'kia W O6߯E=Hv
45$LlxI躍/}^]x\3 ɮ5 QT&G9Ay^i}O[5ޱwq4,s JJI.myE^%'VB~dׯ}*j* ~uTk\fKЬ*Y]_v'I˨鑩6Xo'j&uɧngT]oڌ9\*wVHӖ| >:5EF'J ɝ`!A
46e~_;5ױϊ镋m_&OVi<}"靍hW9X6KPƣ G"ƭ?/O^hCHLciPj)}QQզ#tMg9 xGw~d;_J+RỲ<;e 5/Qs/5N[!a+NPb+ѺI}-t_qU=MKʞY5no*vvbʊ{]| ~ Z{-끇^FVviϵ3Ya=6ndS;-ʹ^;uꪪ^ |=_w+"i&4l#wir|W3U$"J~O@]~tRJVMHw:̦@?>O?vdrtS*$&~1>Z}^nL(]f*&*QaIꝄ|3*O?r?*4Gyz[k/tkQϖWCCKk/x5|S*`ϹγQEwy
47o KYqTb$-/PtsZNKQ*>ݢU@Џ"JQ;¹&
48Lx;+T /+O赟> (T?ķD^N*'p$IW֐W~ =J|_UTe7ְP`;CYjk=sU[mߙ-;};2|wo1p0~>0m
49@Jrǟcٷ4͜?q\UUIV?2L/+Шꄾ< ܇^T ?tj\JrҀB*=km X,n}aՒIadp׷ll{\6v8RꅟҲf1F|Տ;e=\D ,D:ψrxQT◎*|{nS
509~=}ӕG~%j:Dj<ឫ:jO%
51$T8!jvm|'OЗ¹➱z\vsIv`Ȕʨj-^$-^G Q{m`T#c֞㸝|n.ߪN$O JUVʼt,jg-mסּNV z:(Ι*|1Ux=Yk*t MNNDUhK ؞X(刄Rv!#B_cxRŹoE5Dg>?fXQQ˔|@"աMveC>mO$H#]Y I=)_`k*
52:a>!X!W^wҒl'<;vwgIt_?Jh`#E:fdx=6Wu<Ӌd2di˂c#h¬c4?<HFYoVpN;ݷJ\ >` (t3{>⦊;;qFx4YcS$w.da*k|Q,+xs^K߫P^nO֮L5mIwl?-.ʲJ8 F B.-:2Ȕ!/A#b_m%I($|PZ[1G{^#o>3mw?'cx[^:Wk/`'=~֥W(gQbfv7UzM3+؍K:4|GCtA+Kʨ{@Ɩ [05E|yn4MIENDB`

swtstore/templates/authorize.html

1{% extends "layout.html" %}
2{% block body %}
3<h4> Allow Access ?</h4>
4<p class="text-muted">
5 The following application wants to get permission to do stuff(?) on the <i>swt
6 web</i> platform on your behalf.
7</p>
8
9<form role="form" method="POST" action="{{ url_for('oauth.authorize') }}">
10 <div class="form-group">
11 <p class="form-control-static text-primary">
12 {{ client.name }} at {{ client.host_url }} wants
13 to get permission to post data to swt store
14 </p>
15 </div>
16 <div class="form-group">
17 <label class="control-label">Scopes</label>
18 <div class="">
19 <p class="form-control-static">{{ scopes|join(',') }}</p>
20 </div>
21 </div>
22 <input type="hidden" name="client_id" value="{{ client.id }}">
23 <input type="hidden" name="scope" value="{{ scopes|join(' ') }}">
24 <input type="hidden" name="response_type" value="{{ response_type }}">
25 {% if state %}
26 <input type="hidden" name="state" value="{{ state }}">
27 {% endif %}
28 <button type="submit" name="confirm" value="yes" class="btn btn-primary">Allow</button>
29 <button type="submit" name="confirm" value="no" class="btn btn-default">Deny</button>
30</form>
31
32{% endblock %}
33
34{% block scripts %}
35
36<script>
37 var created = new Date("{{ user.created }}");
38 window.onload = function() {
39 $('#user-created').html(created.toString());
40 };
41</script>
42
43{% endblock %}

swtstore/templates/errors/403.html

1<!doctype html>
2<body>
3 <em> Oops! Looks like you are not authorized to make this request!!</em>
4</body>

swtstore/templates/index.html

10 {% for sweet in sweets %}10 {% for sweet in sweets %}
11 <li>11 <li>
12 <span class="who">12 <span class="who">
13 <a href="#"> @{{ sweet.who }} </a>
13 <a href="#">
14 @{{ sweet.who.username }}
15 </a>
14 </span>16 </span>
15 <span class="what">17 <span class="what">
16 #{{ sweet.what.name }}
18 <b> #{{ sweet.what.name }} </b>
17 </span>19 </span>
18 <span class="where">20 <span class="where">
19 {{ sweet.where }}21 {{ sweet.where }}

swtstore/templates/layout.html

4 <meta charset="utf-8">4 <meta charset="utf-8">
5 <meta http-equiv="X-UA-Compatible" content="IE=edge">5 <meta http-equiv="X-UA-Compatible" content="IE=edge">
6 <meta name="viewport" content="width=device-width, initial-scale=1">6 <meta name="viewport" content="width=device-width, initial-scale=1">
7 <title>SWeeT Store</title>
7 <title>SWT Store</title>
8 <link rel=stylesheet type=text/css href="{{ url_for('.static', filename='css/bootstrap.min.css') }}">8 <link rel=stylesheet type=text/css href="{{ url_for('.static', filename='css/bootstrap.min.css') }}">
9 <link rel=stylesheet type=text/css href="{{ url_for('.static', filename='css/bootstrap-theme.min.css') }}">9 <link rel=stylesheet type=text/css href="{{ url_for('.static', filename='css/bootstrap-theme.min.css') }}">
10 <link rel=stylesheet type=text/css href="{{ url_for('.static', filename='css/style.css') }}">10 <link rel=stylesheet type=text/css href="{{ url_for('.static', filename='css/style.css') }}">
52 </small></a>52 </small></a>
53 </li>53 </li>
54 <li class="divider"></li>54 <li class="divider"></li>
55 <li> <a href="#" id="logout">Logout</a> </li>
55 <li> <a href="#" id="logout"> Logout </a> </li>
56 </ul>56 </ul>
57 </li>57 </li>
58 {% endif %}58 {% endif %}
61 </div>61 </div>
62 <div class="notif">62 <div class="notif">
63 {% for message in get_flashed_messages() %}63 {% for message in get_flashed_messages() %}
64 <div class="flash">{{ message }}</div>
64 <div class="alert alert-danger">{{ message }}</div>
65 {% endfor %}65 {% endfor %}
66 </div>66 </div>
67 <div class="content">67 <div class="content">

swtstore/templates/list_apps.html

8 <div class="pull-right">8 <div class="pull-right">
9 <span class="glyphicon glyphicon-trash"></span>9 <span class="glyphicon glyphicon-trash"></span>
10 </div>10 </div>
11 <div> {{ app.name }} </div>
12 <div> {{ app.description }} </div>
11 <div><h4> {{ app.name }} </h4></div>
12 <div><h5> {{ app.description }} </h5></div>
13 <div>APP ID: {{ app.id }} </div>13 <div>APP ID: {{ app.id }} </div>
14 <div> APP Secret: {{ app.client_secret }} </div>14 <div> APP Secret: {{ app.client_secret }} </div>
15 <div>Host URL: {{ app.host_url }} </div>
16 <div>Redirect URL: {{ app.redirect_uris|join(', ') }} </div>
17 <div>Scopes: {{ app.default_scopes|join(', ') }} </div>
15 </div>18 </div>
16 {% endfor %}19 {% endfor %}
17 </ul>20 </ul>

swtstore/templates/register_app.html

15 </textarea>15 </textarea>
16 </div>16 </div>
17 <div class="form-group">17 <div class="form-group">
18 <label for="host_url">Host URL</label>
19 <p class="help-block">URL where your app is deployed</p>
20 <input type="text" class="form-control" name="host_url" id="host_url" placeholder="host URL">
21 </div>
22 <div class="form-group">
18 <label for="redirect_uris">Redirect URLs</label>23 <label for="redirect_uris">Redirect URLs</label>
19 <p class="help-block">Your app URL where it should be redirected once the user is authorized.</p>24 <p class="help-block">Your app URL where it should be redirected once the user is authorized.</p>
20 <input type="text" class="form-control" name="redirect_uris" id="redirect_uris" placeholder="redirect URLs">25 <input type="text" class="form-control" name="redirect_uris" id="redirect_uris" placeholder="redirect URLs">
30 </div>30 </div>
31 <button type="submit" class="btn btn-primary">Register</button>31 <button type="submit" class="btn btn-primary">Register</button>
32</form>32</form>
33{% endblock %}
34{% block scripts %}
35<script>
36 // form validation
37 /*$('form').on('submit', function(event) {
38 console.log('here..');
39 if(!$('#name').val()) {
40 event.preventDefault();
41 $('#name').parent().addClass('has-feedback');
42 $('#name').parent().addClass('has-error');
43 return false;
44 }
45 if(!$('#host_url').val()) {
46 event.preventDefault();
47 $('#host_url').parent().addClass('has-feedback');
48 $('#name').parent().addClass('has-error');
49 return false;
50 }
51 if(!$('#redirect_uris').val()) {
52 event.preventDefault();
53 $('#redirect_uris').parent().addClass('has-feedback');
54 $('#name').parent().addClass('has-error');
55 return false;
56 }
57 if(!$('#scopes').val()) {
58 event.preventDefault();
59 $('#scopes').parent().addClass('has-feedback');
60 $('#name').parent().addClass('has-error');
61 return false;
62 }
63 });*/
64</script>
33{% endblock %}65{% endblock %}