diff options
-rw-r--r-- | requirements.txt | 1 | ||||
-rw-r--r-- | src/bootstrap.py | 4 | ||||
-rw-r--r-- | src/web/views/__init__.py | 4 | ||||
-rw-r--r-- | src/web/views/api/v3/__init__.py | 3 | ||||
-rw-r--r-- | src/web/views/api/v3/article.py | 30 | ||||
-rw-r--r-- | src/web/views/api/v3/common.py | 49 | ||||
-rw-r--r-- | src/web/views/api/v3/feed.py | 30 |
7 files changed, 119 insertions, 2 deletions
diff --git a/requirements.txt b/requirements.txt index b63daf74..f4cc2bc2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,6 +14,7 @@ Flask-Login Flask-Principal Flask-WTF Flask-RESTful +Flask-Restless Flask-Babel Flask-SSLify Flask-Migrate diff --git a/src/bootstrap.py b/src/bootstrap.py index 6d76d58f..2c5f9951 100644 --- a/src/bootstrap.py +++ b/src/bootstrap.py @@ -6,6 +6,7 @@ import os import conf import logging +import flask.ext.restless from urllib.parse import urlsplit def set_logging(log_path, log_level=logging.INFO, @@ -51,6 +52,9 @@ if not application.config['SECURITY_PASSWORD_SALT']: db = SQLAlchemy(application) +# Create the Flask-Restless API manager. +manager = flask.ext.restless.APIManager(application, flask_sqlalchemy_db=db) + def populate_g(): from flask import g g.db = db diff --git a/src/web/views/__init__.py b/src/web/views/__init__.py index 4af8975b..1ca5c473 100644 --- a/src/web/views/__init__.py +++ b/src/web/views/__init__.py @@ -1,4 +1,4 @@ -from web.views.api import v2 +from web.views.api import v2, v3 from web.views import views, home, session_mgmt from web.views.article import article_bp, articles_bp from web.views.feed import feed_bp, feeds_bp @@ -7,7 +7,7 @@ from web.views.icon import icon_bp from web.views.admin import admin_bp from web.views.user import user_bp, users_bp -__all__ = ['views', 'home', 'session_mgmt', 'v2', +__all__ = ['views', 'home', 'session_mgmt', 'v2', 'v3', 'article_bp', 'articles_bp', 'feed_bp', 'feeds_bp', 'category_bp', 'categories_bp', 'icon_bp', 'admin_bp', 'user_bp', 'users_bp'] diff --git a/src/web/views/api/v3/__init__.py b/src/web/views/api/v3/__init__.py index e69de29b..04dd28ad 100644 --- a/src/web/views/api/v3/__init__.py +++ b/src/web/views/api/v3/__init__.py @@ -0,0 +1,3 @@ +from web.views.api.v3 import article, feed + +__all__ = ['article', 'feed'] diff --git a/src/web/views/api/v3/article.py b/src/web/views/api/v3/article.py new file mode 100644 index 00000000..1f6e757a --- /dev/null +++ b/src/web/views/api/v3/article.py @@ -0,0 +1,30 @@ +from flask.ext.login import current_user +from web import models +from bootstrap import application, manager +from web.controllers import ArticleController +from web.views.api.v3.common import AbstractProcessor +from web.views.api.v3.common import url_prefix, auth_func + +class ArticleProcessor(AbstractProcessor): + def get_single_preprocessor(self, instance_id=None, **kw): + # Check if the user is authorized to modify the specified + # instance of the model. + contr = ArticleController(current_user.id) + article = contr.get(id=instance_id) + if not self.is_authorized_to_modify(current_user, article): + raise ProcessingException(description='Not Authorized', code=401) + + +article_processor = ArticleProcessor() + +blueprint_article = manager.create_api_blueprint(models.Article, + url_prefix=url_prefix, + methods=['GET', 'POST', 'PUT', 'DELETE'], + preprocessors=dict(GET_SINGLE=[auth_func, + article_processor.get_single_preprocessor], + GET_MANY=[auth_func, + article_processor.get_many_preprocessor], + PUT_SINGLE=[auth_func], + POST=[auth_func], + DELETE=[auth_func])) +application.register_blueprint(blueprint_article) diff --git a/src/web/views/api/v3/common.py b/src/web/views/api/v3/common.py new file mode 100644 index 00000000..4234a91a --- /dev/null +++ b/src/web/views/api/v3/common.py @@ -0,0 +1,49 @@ +from flask import request +from flask.ext.login import current_user +from flask.ext.restless import ProcessingException +from werkzeug.exceptions import NotFound +from web.controllers import ArticleController, UserController +from web.views.common import login_user_bundle + +url_prefix = '/api/v3' + +def auth_func(*args, **kw): + if request.authorization: + ucontr = UserController() + try: + user = ucontr.get(nickname=request.authorization.username) + except NotFound: + raise ProcessingException("Couldn't authenticate your user", + code=401) + if not ucontr.check_password(user, request.authorization.password): + raise ProcessingException("Couldn't authenticate your user", + code=401) + if not user.is_active: + raise ProcessingException("User is desactivated", code=401) + login_user_bundle(user) + if not current_user.is_authenticated: + raise ProcessingException(description='Not authenticated!', code=401) + +class AbstractProcessor(): + + def is_authorized_to_modify(self, user, obj): + return user.id == obj.user_id + + def get_single_preprocessor(self, instance_id=None, **kw): + # Check if the user is authorized to modify the specified + # instance of the model. + pass + + def get_many_preprocessor(self, search_params=None, **kw): + """Accepts a single argument, `search_params`, which is a dictionary + containing the search parameters for the request. + """ + filt = dict(name="user_id", + op="eq", + val=current_user.id) + + # Check if there are any filters there already. + if "filters" not in search_params: + search_params["filters"] = [] + + search_params["filters"].append(filt) diff --git a/src/web/views/api/v3/feed.py b/src/web/views/api/v3/feed.py new file mode 100644 index 00000000..ef1b415f --- /dev/null +++ b/src/web/views/api/v3/feed.py @@ -0,0 +1,30 @@ +from flask.ext.login import current_user +from web import models +from bootstrap import application, manager +from web.controllers import FeedController +from web.views.api.v3.common import AbstractProcessor +from web.views.api.v3.common import url_prefix, auth_func + +class FeedProcessor(AbstractProcessor): + def get_single_preprocessor(self, instance_id=None, **kw): + # Check if the user is authorized to modify the specified + # instance of the model. + contr = FeedController(current_user.id) + feed = contr.get(id=instance_id) + if not self.is_authorized_to_modify(current_user, feed): + raise ProcessingException(description='Not Authorized', code=401) + + +feed_processor = FeedProcessor() + +blueprint_feed = manager.create_api_blueprint(models.Feed, + url_prefix=url_prefix, + methods=['GET', 'POST', 'PUT', 'DELETE'], + preprocessors=dict(GET_SINGLE=[auth_func, + feed_processor.get_single_preprocessor], + GET_MANY=[auth_func, + feed_processor.get_many_preprocessor], + PUT_SINGLE=[auth_func], + POST=[auth_func], + DELETE=[auth_func])) +application.register_blueprint(blueprint_feed) |