From 594aec6929519399307c057a7bedfd448e7d26be Mon Sep 17 00:00:00 2001 From: Cédric Bonhomme Date: Wed, 30 Oct 2013 23:44:56 +0100 Subject: Added authentication with Flask-Login. --- pyaggr3g470r/__init__.py | 9 ++++- pyaggr3g470r/forms.py | 18 +++++++++ pyaggr3g470r/models.py | 6 ++- pyaggr3g470r/templates/layout.html | 15 +++++--- pyaggr3g470r/templates/login.html | 29 +++++++++++++++ pyaggr3g470r/views.py | 75 +++++++++++++++++++++++++++++++++++--- 6 files changed, 138 insertions(+), 14 deletions(-) create mode 100644 pyaggr3g470r/templates/login.html diff --git a/pyaggr3g470r/__init__.py b/pyaggr3g470r/__init__.py index 3d63d9de..64b25b96 100644 --- a/pyaggr3g470r/__init__.py +++ b/pyaggr3g470r/__init__.py @@ -5,7 +5,7 @@ import os from flask import Flask, session, g from flask.ext.mongoengine import MongoEngine -#from flask.ext.login import AnonymousUserMixin +from flask.ext.login import LoginManager, AnonymousUserMixin from flask.ext.admin import Admin from flask.ext.admin.contrib.mongoengine import ModelView @@ -13,7 +13,7 @@ from flask.ext.admin.contrib.mongoengine import ModelView import conf from models import * -# Create Flask applicatio +# Create Flask application app = Flask(__name__) app.debug = True @@ -37,6 +37,10 @@ mail.init_app(app) # Administration panel admin = Admin(app, name='pyAggr3g470r') # Add administrative views here +class UserView(ModelView): + column_filters = ['firstname', 'lastname'] + + column_searchable_list = ('firstname', 'lastname') class FeedView(ModelView): column_filters = ['title', 'link'] @@ -47,6 +51,7 @@ class ArticleView(ModelView): column_searchable_list = ('title', 'link') admin.add_view(FeedView(Feed)) admin.add_view(ArticleView(Article)) +admin.add_view(UserView(User)) from pyaggr3g470r import views \ No newline at end of file diff --git a/pyaggr3g470r/forms.py b/pyaggr3g470r/forms.py index 72bb3909..c7054b85 100644 --- a/pyaggr3g470r/forms.py +++ b/pyaggr3g470r/forms.py @@ -6,3 +6,21 @@ from wtforms import TextField, TextAreaField, PasswordField, SubmitField, valida import models +class SigninForm(Form): + email = TextField("Email", [validators.Required("Please enter your email address."), validators.Email("Please enter your email address.")]) + password = PasswordField('Password', [validators.Required("Please enter a password.")]) + submit = SubmitField("Sign In") + + def __init__(self, *args, **kwargs): + Form.__init__(self, *args, **kwargs) + + def validate(self): + if not Form.validate(self): + return False + + user = models.User.objects(email = self.email.data).first() + if user and user.check_password(self.password.data): + return True + else: + self.email.errors.append("Invalid e-mail or password") + return False \ No newline at end of file diff --git a/pyaggr3g470r/models.py b/pyaggr3g470r/models.py index dfc40b28..23222909 100644 --- a/pyaggr3g470r/models.py +++ b/pyaggr3g470r/models.py @@ -12,8 +12,12 @@ class User(Document, UserMixin): lastname = StringField(required = True) email = EmailField(required=True, unique=True) pwdhash = StringField(required=True) + #feeds = ListField(DocumentField('Article')) created_at = DateTimeField(required=True, default=datetime.now) + def get_id(self): + return self.email + def set_password(self, password): self.pwdhash = generate_password_hash(password) @@ -22,7 +26,7 @@ class User(Document, UserMixin): #required for administrative interface def __unicode__(self): - return self.nickname + return self.email class Feed(Document): title = StringField(required=True) diff --git a/pyaggr3g470r/templates/layout.html b/pyaggr3g470r/templates/layout.html index c6d26e50..659c573c 100644 --- a/pyaggr3g470r/templates/layout.html +++ b/pyaggr3g470r/templates/layout.html @@ -38,12 +38,15 @@ diff --git a/pyaggr3g470r/templates/login.html b/pyaggr3g470r/templates/login.html new file mode 100644 index 00000000..fce29054 --- /dev/null +++ b/pyaggr3g470r/templates/login.html @@ -0,0 +1,29 @@ +{% extends "layout.html" %} + +{% block content %} +
+

Log In

+ + {% for message in form.email.errors %} +
{{ message }}
+ {% endfor %} + + {% for message in form.password.errors %} +
{{ message }}
+ {% endfor %} + +
+ {{ form.hidden_tag() }} + + {{ form.email.label }} + {{ form.email }} +
+ {{ form.password.label }} + {{ form.password }} + +
+ {{ form.submit(class_="btn") }} +
+
+ +{% endblock %} diff --git a/pyaggr3g470r/views.py b/pyaggr3g470r/views.py index 1a0408ee..359ae15d 100644 --- a/pyaggr3g470r/views.py +++ b/pyaggr3g470r/views.py @@ -1,47 +1,109 @@ #! /usr/bin/env python # -*- coding: utf-8 -*- -from flask import render_template, request, flash, session, url_for, redirect +from flask import render_template, request, flash, session, url_for, redirect, g from wtforms import TextField, PasswordField, SubmitField, validators from flask.ext.mail import Message, Mail +from flask.ext.login import LoginManager, login_user, logout_user, login_required, current_user, AnonymousUserMixin from collections import defaultdict -#from forms import ContactForm, SignupForm, SigninForm +from forms import SigninForm from pyaggr3g470r import app, db + import feedgetter import models mail = Mail() +login_manager = LoginManager() +login_manager.init_app(app) + +@app.before_request +def before_request(): + g.user = current_user + if g.user.is_authenticated(): + pass + #g.user.last_seen = datetime.utcnow() + #db.session.add(g.user) + #db.session.commit() + +@app.errorhandler(403) +def authentication_failed(e): + flash('Authenticated failed.') + return redirect(url_for('login')) + +@app.errorhandler(401) +def authentication_failed(e): + flash('Authenticated required.') + return redirect(url_for('login')) + +@login_manager.user_loader +def load_user(email): + # Return an instance of the User model + return models.User.objects(email=email).first() + +@app.route('/login/', methods=['GET', 'POST']) +def login(): + g.user = AnonymousUserMixin() + form = SigninForm() + + if form.validate_on_submit(): + user = models.User.objects(email=form.email.data).first() + login_user(user) + g.user = user + flash("Logged in successfully.") + return redirect(url_for('home')) + return render_template('login.html', form=form) + +@app.route('/logout/') +@login_required +def logout(): + """ + Remove the user information from the session. + """ + logout_user() + return redirect(url_for('home')) + + + + + @app.route('/') +@login_required def home(): #feeds = models.Feed.objects().order_by('title').fields(slice__articles=[0,9]) + user = g.user feeds = models.Feed.objects().fields(slice__articles=9) - return render_template('home.html', feeds=feeds) + return render_template('home.html', user=user, feeds=feeds) @app.route('/fetch/', methods=['GET']) +@login_required def fetch(): feed_getter = feedgetter.FeedGetter() feed_getter.retrieve_feed() return redirect(url_for('home')) @app.route('/about/', methods=['GET']) +@login_required def about(): return render_template('about.html') @app.route('/feeds/', methods=['GET']) +@login_required def feeds(): feeds = models.Feed.objects() return render_template('feeds.html', feeds=feeds) @app.route('/feed/', methods=['GET']) +@login_required def feed(feed_id=None): feed = models.Article.objects(id=feed_id).first() return render_template('feed.html', feed=feed) @app.route('/article/', methods=['GET']) +@login_required def article(article_id=None): article = models.Article.objects(id=article_id).first() if not article.readed: @@ -50,6 +112,7 @@ def article(article_id=None): return render_template('article.html', article=article) @app.route('/delete/', methods=['GET']) +@login_required def delete(article_id=None): article = models.Article.objects(id=article_id).first() article.delete() @@ -57,12 +120,14 @@ def delete(article_id=None): return redirect(url_for('home')) @app.route('/articles/', methods=['GET']) +@login_required def articles(feed_id=None): feed = models.Feed.objects(id=feed_id).first() #feed.articles = sorted(feed.articles, key=lambda t: t.date, reverse=True) return render_template('articles.html', feed=feed) @app.route('/favorites/', methods=['GET']) +@login_required def favorites(): favorites = defaultdict(list) for feed in models.Feed.objects(): @@ -73,15 +138,15 @@ def favorites(): return render_template('favorites.html', favorites=favorites) @app.route('/unread/', methods=['GET']) +@login_required def unread(): feeds = models.Feed.objects().filter(articles__readed=False) - unread_articles = models.Article.objects.filter(readed = False) - print len(feeds) return render_template('unread.html', feeds=feeds) @app.route('/management/', methods=['GET']) +@login_required def management(): nb_article = models.Article.objects().count() return render_template('management.html', nb_article=nb_article) \ No newline at end of file -- cgit