different roles

This commit is contained in:
Oleg Podsadny 2013-06-23 15:12:29 +02:00
parent d8ff1466c3
commit 89c7f2f955
14 changed files with 167 additions and 65 deletions

View File

@ -1,7 +1,6 @@
import views
from .decorators import public_endpoint
from .blueprint import auth
from .security import user_datastore
__all__ = ['auth', 'public_endpoint', 'views']
__all__ = ['auth', 'public_endpoint', 'user_datastore']

View File

@ -1,11 +1,21 @@
from flask import Blueprint
from flask.ext.security import Security
from .manager import login_manager, check_valid_login
from .security import user_datastore
from .forms import LoginForm
auth = Blueprint('auth', __name__, template_folder='../')
@auth.record_once
def on_registered(state):
state.app.config['SECURITY_LOGIN_USER_TEMPLATE'] = 'auth/login.html'
Security(
state.app,
user_datastore,
login_form=LoginForm,
)
login_manager.init_app(state.app)
state.app.before_request(check_valid_login)

View File

@ -1,9 +1,47 @@
from flask.ext.wtf import Form
from wtforms import TextField, PasswordField
from wtforms import TextField, PasswordField, BooleanField
from wtforms.validators import Required
from flask.ext.security.forms import NextFormMixin
from flask.ext.security.utils import verify_and_update_password, get_message
from flask.ext.security.confirmable import requires_confirmation
from .security import user_datastore
class LoginForm(Form):
class LoginForm(Form, NextFormMixin):
username = TextField('Username', [Required()])
password = PasswordField('Password', [Required()])
remember = BooleanField('Remember me')
def validate(self):
if not super(LoginForm, self).validate():
return False
username = self.username.data.strip()
password = self.password.data.strip()
if username == '':
self.username.errors.append(get_message('EMAIL_NOT_PROVIDED')[0])
return False
if password == '':
self.password.errors.append(get_message('PASSWORD_NOT_PROVIDED')[0])
return False
self.user = user_datastore.find_user(username=username)
if self.user is None:
self.username.errors.append(get_message('USER_DOES_NOT_EXIST')[0])
return False
if not verify_and_update_password(password, self.user):
self.password.errors.append(get_message('INVALID_PASSWORD')[0])
return False
if requires_confirmation(self.user):
self.username.errors.append(get_message('CONFIRMATION_REQUIRED')[0])
return False
if not self.user.is_active():
self.username.errors.append(get_message('DISABLED_ACCOUNT')[0])
return False
return True

View File

@ -2,15 +2,21 @@ from flask import request, current_app
from flask.ext.login import LoginManager, current_user
from .models import User
from .security import user_datastore
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
login_manager.login_view = 'security.login'
PUBLIC_ENDPOINTS = [
'static',
'security.login',
]
def check_valid_login():
if 'static' == request.endpoint:
if request.endpoint in PUBLIC_ENDPOINTS:
return
if getattr(current_app.view_functions.get(request.endpoint), 'is_public', False):
@ -22,4 +28,4 @@ def check_valid_login():
@login_manager.user_loader
def load_user(userid):
return User.query.get(userid)
return user_datastore.find_user(id=userid)

View File

@ -1,12 +1,15 @@
from flask import current_app
from flask.ext.security import UserMixin
from flask.ext.security import UserMixin, RoleMixin
db = current_app.extensions['sqlalchemy'].db
ROLE_USER = 0
ROLE_ADMIN = 1
class Role(db.Model, RoleMixin):
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(80), unique=True)
description = db.Column(db.String(255))
class User(db.Model, UserMixin):
@ -14,9 +17,14 @@ class User(db.Model, UserMixin):
username = db.Column(db.String(64), unique=True)
email = db.Column(db.String(120), unique=True)
password = db.Column(db.String(20))
role = db.Column(db.SmallInteger, default=ROLE_USER)
active = db.Column(db.Boolean, default=False)
roles = db.relationship(
Role,
secondary=lambda: roles_users,
backref=db.backref('users', lazy='dynamic'),
)
def get_id(self):
return unicode(self.id)
@ -25,3 +33,10 @@ class User(db.Model, UserMixin):
def is_anonymous(self):
return False
roles_users = db.Table(
'roles_users',
db.Column('user_id', db.Integer(), db.ForeignKey(User.id)),
db.Column('role_id', db.Integer(), db.ForeignKey(Role.id)),
)

View File

@ -0,0 +1,4 @@
from flask.ext.security import SQLAlchemyUserDatastore
from .models import db, User, Role
user_datastore = SQLAlchemyUserDatastore(db, User, Role)

View File

@ -1,25 +0,0 @@
from flask import request, url_for, redirect, render_template
from flask.ext.login import login_user
from .blueprint import auth
from .decorators import public_endpoint
from .forms import LoginForm
from .models import User
@auth.route('/login/', methods=['GET', 'POST'])
@public_endpoint
def login():
form = LoginForm()
if form.validate_on_submit():
user = (
User.query
.filter(User.username == form.username.data)
.first()
)
#TODO: if user is not found
login_user(user)
return redirect(request.args.get('next') or url_for('index'))
return render_template('auth/login.html', form=form,)

View File

@ -1,6 +1,37 @@
from flask.ext.admin import Admin
from flask import current_app
from flask.ext.admin.contrib.sqlamodel import ModelView
from pytest_bdd_example.book import db, Book
admin = Admin()
admin.add_view(ModelView(Book, db.session, 'books', endpoint='books'))
from flask.ext.principal import Permission, RoleNeed
from pytest_bdd_example.book import db, Book, Author
admin = current_app.extensions['admin'][0]
class AuthorView(ModelView):
def is_visible(self):
return Permission(RoleNeed('admin')).can()
admin.add_view(
AuthorView(
Author,
db.session,
'authors',
endpoint='authors',
)
)
admin.add_view(
ModelView(
Book,
db.session,
'books',
endpoint='books',
)
)

View File

@ -1,10 +1,7 @@
from flask import Blueprint
from .admin import admin
book = Blueprint('book', __name__)
@book.record_once
def on_registered(state):
admin.init_app(state.app)
__all__ = ['book', 'admin']

View File

@ -0,0 +1,14 @@
from flask.ext.admin import Admin
from flask.ext.admin.base import MenuLink
admin = Admin(
name='Dashboard',
url='/',
)
admin.add_link(
MenuLink(
name='Logout',
endpoint='security.logout',
)
)

View File

@ -3,6 +3,7 @@ from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from pytest_bdd_example.dashboard import settings
from .admin import admin
app = Flask(
__name__,
@ -13,6 +14,7 @@ app = Flask(
app.config.from_object('pytest_bdd_example.dashboard.settings')
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
admin.init_app(app)
db = SQLAlchemy(app)
@ -23,6 +25,5 @@ with app.app_context():
app.register_blueprint(auth, url_prefix='/auth')
app.register_blueprint(book)
# Register the views
from .views import *

View File

@ -1,8 +0,0 @@
from flask import redirect, url_for
from .app import app
@app.route('/')
def index():
return redirect(url_for('admin.index'))

View File

@ -34,21 +34,23 @@
{% block menu %}{% endblock %}
{% block content %}
<form class="form-signin" method='POST'>
{{ form.csrf_token }}
{{ login_user_form.csrf_token }}
<h2 class="form-signin-heading">Please sign in</h2>
<div class="control-group">
<div class="controls">
{{ form.username(placeholder=form.username.label.text, class="input-block-level") }}
<ul class="errors">{% for error in form.username.errors %}<li>{{ error|e }}</li>{% endfor %}</ul>
{{ login_user_form.username(placeholder=login_user_form.username.label.text, class="input-block-level") }}
{% for error in login_user_form.username.errors %}<div class="alert alert-error">{{ error|e }}</div>{% endfor %}
</div>
</div>
<div class="control-group">
<div class="controls">
{{ form.password(placeholder=form.password.label.text, class="input-block-level") }}
<ul class="errors">{% for error in form.password.errors %}<li>{{ error|e }}</li>{% endfor %}</ul>
{{ login_user_form.password(placeholder=login_user_form.password.label.text, class="input-block-level") }}
{% for error in login_user_form.password.errors %}<div class="alert alert-error">{{ error|e }}</div>{% endfor %}
</div>
</div>

View File

@ -1,20 +1,38 @@
from flask import current_app
from pytest_bdd_example.auth.models import User
from pytest_bdd_example.auth import user_datastore
def create_initial_data():
print 'Populating the initial data...'
db = current_app.extensions['sqlalchemy'].db
print 'Dashboard user: admin/asdasd'
admin = User(
print 'Creating roles...'
admin_role = user_datastore.create_role(
name='admin',
description='Administrators',
)
author_role = user_datastore.create_role(
name='author',
description='Book authors',
)
print 'Admin user: admin/asdasd'
admin = user_datastore.create_user(
username='admin',
password='asdasd',
active=True,
)
db.session.add(admin)
user_datastore.add_role_to_user(admin, admin_role)
print 'Author user: author/asdasd'
author = user_datastore.create_user(
username='author',
password='asdasd',
active=True,
)
user_datastore.add_role_to_user(author, author_role)
#TODO: add other data here...
db.session.commit()