Skip to content

Commit v0.30

kwmccabe edited this page Apr 17, 2018 · 7 revisions

v0.30 - API Module: Token-Based Auth, upgrade /api/item and /api/user


Files changed (5)

File web/app/api/authentication.py MODIFIED

  • Extend verify_password() to call UserModel.verify_auth_token(login) if password is empty.
  • Create route /token to return 1-hour authorization tokens.
-from flask import g
+from flask import g, jsonify

...

 @auth.verify_password
-def verify_password(email,password):
-    if not email:
+def verify_password(login,password):
+    if not login:
         return False
-    user = UserModel.query.filter_by(user_email=email).first()
+    if not password:
+        g.current_user = UserModel.verify_auth_token(login)
+        g.token_used = True
+        return g.current_user is not None
+    user = UserModel.query.filter_by(user_email=login).first()
     if not user:
         return False
     g.current_user = user
+    g.token_used = False
     return user.verify_password(password)

...

+@api.route('/token')
+def get_auth_token():
+    if g.token_used:
+        return unauthorized('Invalid Credentials')
+    return jsonify({'token': g.current_user.generate_auth_token(3600).decode('utf-8'), 'expires': 3600})

File web/app/api/item.py MODIFIED

  • Base route /api/item/ returns items owned by the current_user.
  • New route /api/item/list/ returns all items, regardless of owner.
  • New routes /api/item/<int:id>/owner/ and /api/item/<int:id>/editor/ return the item's related users.
-from flask import jsonify
+from flask import g, jsonify
 from .. import db
 from ..item.models import ItemModel
+from ..user.models import UserModel
 from . import api

...

-def get_items():
+def get_profile_items():
+    user = UserModel.query.get_or_404( g.current_user.id )
+    return jsonify({ 'item': [item.to_json() for item in user.items] })
+
+@api.route('/item/count/')
+def get_item_count():
+    cnt = db.session.query(ItemModel).count()
+    return jsonify({ 'item': cnt })
+
+@api.route('/item/list/')
+def get_item_list():
     rows = db.session.query(ItemModel)
     return jsonify({ 'item': [item.to_json() for item in rows] })
 
 @api.route('/item/<int:id>/')
 def get_item(id):
     item = ItemModel.query.get_or_404(id)
-    return jsonify(item.to_json())
+    return jsonify({ 'item': [item.to_json()] })
+
+@api.route('/item/<int:id>/owner/')
+def get_item_owner(id):
+    item = ItemModel.query.get_or_404(id)
+    #return jsonify(item.owner.to_json())
+    return jsonify({ 'user': [item.owner.to_json()] })
+
+@api.route('/item/<int:id>/editor/')
+def get_item_editor(id):
+    item = ItemModel.query.get_or_404(id)
+    return jsonify({ 'user': [iu.user.to_json() for iu in item.item_users] })

File web/app/api/user.py MODIFIED

  • Base route /api/user/ returns the current_user.
  • New route /api/user/list/ returns all users.
  • New route /api/user/<int:id>/item/owner/ returns items owned by this user.
  • New route /api/user/<int:id>/item/editor/ returns items editable by this user.
-from flask import jsonify
+from flask import g, jsonify
 from .. import db
+from ..item.models import ItemModel
 from ..user.models import UserModel
 from . import api
 
 
 @api.route('/user/')
-def get_users():
+def get_profile():
+    user = UserModel.query.get_or_404( g.current_user.id )
+    return jsonify({ 'user': [user.to_json()] })
+
+@api.route('/user/count/')
+def get_user_count():
+    cnt = db.session.query(UserModel).count()
+    return jsonify({ 'user': cnt })
+
+@api.route('/user/list/')
+def get_user_list():
     rows = db.session.query(UserModel)
     return jsonify({ 'user': [user.to_json() for user in rows] })
 
 @api.route('/user/<int:id>/')
 def get_user(id):
     user = UserModel.query.get_or_404(id)
-    return jsonify(user.to_json())
+    return jsonify({ 'user': [user.to_json()] })
+
+@api.route('/user/<int:id>/item/')
+@api.route('/user/<int:id>/item/owner/')
+def get_user_item_owner(id):
+    user = UserModel.query.get_or_404(id)
+    return jsonify({ 'item': [item.to_json() for item in user.items] })
+
+@api.route('/user/<int:id>/item/editor/')
+def get_user_item_editor(id):
+    user = UserModel.query.get_or_404(id)
+    return jsonify({ 'item': [iu.item.to_json() for iu in user.user_items] })
+

File web/app/item/models.py MODIFIED

  • Return absolute paths in JSON urls via the _external=True option to url_for().
     def to_json(self):
         json_item = {
-            'url': url_for('api.get_item', id=self.id),
+            'url': url_for('api.get_item', id=self.id, _external=True),

...

-            'owner_url': url_for('api.get_user', id=self.owner_id),
+            'url_owner' : url_for('api.get_item_owner', id=self.id, _external=True),
             'owner_id'  : self.owner_id,
-            #'users_url': url_for('api.get_item_users', id=self.id),
-            'users_count': len(self.item_users)
+            'url_editor': url_for('api.get_item_editor', id=self.id, _external=True),
+            'editor_id' : [iu.user_id for iu in self.item_users]
         }
         return json_item

File web/app/user/models.py MODIFIED

  • Create method generate_auth_token() using TimedJSONWebSignatureSerializer.
  • Create reciprocal method verify_auth_token().
  • Return absolute paths in JSON urls via the _external=True option to url_for().
 import logging
-from flask import url_for
+from flask import current_app, url_for
 from datetime import datetime
+from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
 from werkzeug.security import generate_password_hash, check_password_hash

...

+    def generate_auth_token(self, expires):
+        logging.debug( "generate_auth_token(%s)" %  expires)
+        s = Serializer(current_app.config['SECRET_KEY'],expires_in=expires)
+        return s.dumps({'id': self.id})
+
+    @staticmethod
+    def verify_auth_token(token):
+        s = Serializer(current_app.config['SECRET_KEY'])
+        try:
+            data = s.loads(token)
+        except:
+            return None
+        return UserModel.query.get(data['id'])

...

     def to_json(self):
         json_user = {
-            'url': url_for('api.get_user', id=self.id),
+            'url': url_for('api.get_user', id=self.id, _external=True),

...

-            #'items_url': url_for('api.get_user_items', id=self.id),
-            'items_count': len(self.user_items)
+            'url_item_owner'   : url_for('api.get_user_item_owner', id=self.id, _external=True),
+            'count_item_owner' : len(self.items),
+            'url_item_editor'  : url_for('api.get_user_item_editor', id=self.id, _external=True),
+            'count_item_editor': len(self.user_items)
         }
         return json_user

Commit-v0.29 | Commit-v0.30 | Commit-v0.31

Clone this wiki locally