Picture-Puzzle-website/venv/Lib/site-packages/flask_security/datastore.py

472 lines
15 KiB
Python

# -*- coding: utf-8 -*-
"""
flask_security.datastore
~~~~~~~~~~~~~~~~~~~~~~~~
This module contains an user datastore classes.
:copyright: (c) 2012 by Matt Wright.
:license: MIT, see LICENSE for more details.
"""
from .utils import get_identity_attributes, string_types
class Datastore(object):
def __init__(self, db):
self.db = db
def commit(self):
pass
def put(self, model):
raise NotImplementedError
def delete(self, model):
raise NotImplementedError
class SQLAlchemyDatastore(Datastore):
def commit(self):
self.db.session.commit()
def put(self, model):
self.db.session.add(model)
return model
def delete(self, model):
self.db.session.delete(model)
class MongoEngineDatastore(Datastore):
def put(self, model):
model.save()
return model
def delete(self, model):
model.delete()
class PeeweeDatastore(Datastore):
def put(self, model):
model.save()
return model
def delete(self, model):
model.delete_instance(recursive=True)
def with_pony_session(f):
from functools import wraps
@wraps(f)
def decorator(*args, **kwargs):
from pony.orm import db_session
from pony.orm.core import local
from flask import after_this_request, current_app, has_app_context, \
has_request_context
from flask.signals import appcontext_popped
register = local.db_context_counter == 0
if register and (has_app_context() or has_request_context()):
db_session.__enter__()
result = f(*args, **kwargs)
if register:
if has_request_context():
@after_this_request
def pop(request):
db_session.__exit__()
return request
elif has_app_context():
@appcontext_popped.connect_via(
current_app._get_current_object()
)
def pop(sender, *args, **kwargs):
while local.db_context_counter:
db_session.__exit__()
else:
raise RuntimeError('Needs app or request context')
return result
return decorator
class PonyDatastore(Datastore):
def commit(self):
self.db.commit()
@with_pony_session
def put(self, model):
return model
@with_pony_session
def delete(self, model):
model.delete()
class UserDatastore(object):
"""Abstracted user datastore.
:param user_model: A user model class definition
:param role_model: A role model class definition
"""
def __init__(self, user_model, role_model):
self.user_model = user_model
self.role_model = role_model
def _prepare_role_modify_args(self, user, role):
if isinstance(user, string_types):
user = self.find_user(email=user)
if isinstance(role, string_types):
role = self.find_role(role)
return user, role
def _prepare_create_user_args(self, **kwargs):
kwargs.setdefault('active', True)
roles = kwargs.get('roles', [])
for i, role in enumerate(roles):
rn = role.name if isinstance(role, self.role_model) else role
# see if the role exists
roles[i] = self.find_role(rn)
kwargs['roles'] = roles
return kwargs
def get_user(self, id_or_email):
"""Returns a user matching the specified ID or email address."""
raise NotImplementedError
def find_user(self, *args, **kwargs):
"""Returns a user matching the provided parameters."""
raise NotImplementedError
def find_role(self, *args, **kwargs):
"""Returns a role matching the provided name."""
raise NotImplementedError
def add_role_to_user(self, user, role):
"""Adds a role to a user.
:param user: The user to manipulate
:param role: The role to add to the user
"""
user, role = self._prepare_role_modify_args(user, role)
if role not in user.roles:
user.roles.append(role)
self.put(user)
return True
return False
def remove_role_from_user(self, user, role):
"""Removes a role from a user.
:param user: The user to manipulate
:param role: The role to remove from the user
"""
rv = False
user, role = self._prepare_role_modify_args(user, role)
if role in user.roles:
rv = True
user.roles.remove(role)
self.put(user)
return rv
def toggle_active(self, user):
"""Toggles a user's active status. Always returns True."""
user.active = not user.active
return True
def deactivate_user(self, user):
"""Deactivates a specified user. Returns `True` if a change was made.
:param user: The user to deactivate
"""
if user.active:
user.active = False
return True
return False
def activate_user(self, user):
"""Activates a specified user. Returns `True` if a change was made.
:param user: The user to activate
"""
if not user.active:
user.active = True
return True
return False
def create_role(self, **kwargs):
"""Creates and returns a new role from the given parameters."""
role = self.role_model(**kwargs)
return self.put(role)
def find_or_create_role(self, name, **kwargs):
"""Returns a role matching the given name or creates it with any
additionally provided parameters.
"""
kwargs["name"] = name
return self.find_role(name) or self.create_role(**kwargs)
def create_user(self, **kwargs):
"""Creates and returns a new user from the given parameters."""
kwargs = self._prepare_create_user_args(**kwargs)
user = self.user_model(**kwargs)
return self.put(user)
def delete_user(self, user):
"""Deletes the specified user.
:param user: The user to delete
"""
self.delete(user)
class SQLAlchemyUserDatastore(SQLAlchemyDatastore, UserDatastore):
"""A SQLAlchemy datastore implementation for Flask-Security that assumes the
use of the Flask-SQLAlchemy extension.
"""
def __init__(self, db, user_model, role_model):
SQLAlchemyDatastore.__init__(self, db)
UserDatastore.__init__(self, user_model, role_model)
def get_user(self, identifier):
from sqlalchemy import func as alchemyFn
if self._is_numeric(identifier):
return self.user_model.query.get(identifier)
for attr in get_identity_attributes():
query = alchemyFn.lower(getattr(self.user_model, attr)) \
== alchemyFn.lower(identifier)
rv = self.user_model.query.filter(query).first()
if rv is not None:
return rv
def _is_numeric(self, value):
try:
int(value)
except (TypeError, ValueError):
return False
return True
def find_user(self, **kwargs):
return self.user_model.query.filter_by(**kwargs).first()
def find_role(self, role):
return self.role_model.query.filter_by(name=role).first()
class SQLAlchemySessionUserDatastore(SQLAlchemyUserDatastore,
SQLAlchemyDatastore):
"""A SQLAlchemy datastore implementation for Flask-Security that assumes the
use of the flask_sqlalchemy_session extension.
"""
def __init__(self, session, user_model, role_model):
class PretendFlaskSQLAlchemyDb(object):
""" This is a pretend db object, so we can just pass in a session.
"""
def __init__(self, session):
self.session = session
SQLAlchemyUserDatastore.__init__(self,
PretendFlaskSQLAlchemyDb(session),
user_model,
role_model)
def commit(self):
# Old flask-sqlalchemy adds this weird attribute for tracking
# to Session. flask-sqlalchemy 2.0 does things more nicely.
try:
super(SQLAlchemySessionUserDatastore, self).commit()
except AttributeError:
import sqlalchemy
sqlalchemy.orm.Session._model_changes = {}
super(SQLAlchemySessionUserDatastore, self).commit()
class MongoEngineUserDatastore(MongoEngineDatastore, UserDatastore):
"""A MongoEngine datastore implementation for Flask-Security that assumes
the use of the Flask-MongoEngine extension.
"""
def __init__(self, db, user_model, role_model):
MongoEngineDatastore.__init__(self, db)
UserDatastore.__init__(self, user_model, role_model)
def get_user(self, identifier):
from mongoengine import ValidationError
try:
return self.user_model.objects(id=identifier).first()
except (ValidationError, ValueError):
pass
for attr in get_identity_attributes():
query_key = '%s__iexact' % attr
query = {query_key: identifier}
rv = self.user_model.objects(**query).first()
if rv is not None:
return rv
def find_user(self, **kwargs):
try:
from mongoengine.queryset import Q, QCombination
except ImportError:
from mongoengine.queryset.visitor import Q, QCombination
from mongoengine.errors import ValidationError
queries = map(lambda i: Q(**{i[0]: i[1]}), kwargs.items())
query = QCombination(QCombination.AND, queries)
try:
return self.user_model.objects(query).first()
except ValidationError: # pragma: no cover
return None
def find_role(self, role):
return self.role_model.objects(name=role).first()
# TODO: Not sure why this was added but tests pass without it
# def add_role_to_user(self, user, role):
# rv = super(MongoEngineUserDatastore, self).add_role_to_user(
# user, role)
# if rv:
# self.put(user)
# return rv
class PeeweeUserDatastore(PeeweeDatastore, UserDatastore):
"""A PeeweeD datastore implementation for Flask-Security that assumes
the use of the Flask-Peewee extension.
:param user_model: A user model class definition
:param role_model: A role model class definition
:param role_link: A model implementing the many-to-many user-role relation
"""
def __init__(self, db, user_model, role_model, role_link):
PeeweeDatastore.__init__(self, db)
UserDatastore.__init__(self, user_model, role_model)
self.UserRole = role_link
def get_user(self, identifier):
from peewee import fn as peeweeFn
try:
return self.user_model.get(self.user_model.id == identifier)
except ValueError:
pass
for attr in get_identity_attributes():
column = getattr(self.user_model, attr)
try:
return self.user_model.get(
peeweeFn.Lower(column) == peeweeFn.Lower(identifier))
except self.user_model.DoesNotExist:
pass
def find_user(self, **kwargs):
try:
return self.user_model.filter(**kwargs).get()
except self.user_model.DoesNotExist:
return None
def find_role(self, role):
try:
return self.role_model.filter(name=role).get()
except self.role_model.DoesNotExist:
return None
def create_user(self, **kwargs):
"""Creates and returns a new user from the given parameters."""
roles = kwargs.pop('roles', [])
user = self.user_model(**self._prepare_create_user_args(**kwargs))
user = self.put(user)
for role in roles:
self.add_role_to_user(user, role)
self.put(user)
return user
def add_role_to_user(self, user, role):
"""Adds a role to a user.
:param user: The user to manipulate
:param role: The role to add to the user
"""
user, role = self._prepare_role_modify_args(user, role)
result = self.UserRole.select().where(
self.UserRole.user == user.id,
self.UserRole.role == role.id,
)
if result.count():
return False
else:
self.put(self.UserRole.create(user=user.id, role=role.id))
return True
def remove_role_from_user(self, user, role):
"""Removes a role from a user.
:param user: The user to manipulate
:param role: The role to remove from the user
"""
user, role = self._prepare_role_modify_args(user, role)
result = self.UserRole.select().where(
self.UserRole.user == user,
self.UserRole.role == role,
)
if result.count():
query = self.UserRole.delete().where(
self.UserRole.user == user, self.UserRole.role == role)
query.execute()
return True
else:
return False
class PonyUserDatastore(PonyDatastore, UserDatastore):
"""A Pony ORM datastore implementation for Flask-Security.
Code primarily from https://github.com/ET-CS but taken over after
being abandoned.
"""
def __init__(self, db, user_model, role_model):
PonyDatastore.__init__(self, db)
UserDatastore.__init__(self, user_model, role_model)
@with_pony_session
def get_user(self, identifier):
if self._is_numeric(identifier):
return self.user_model[identifier]
for attr in get_identity_attributes():
# this is a nightmare, tl;dr we need to get the thing that
# corresponds to email (usually)
user = self.user_model.get(**{attr: identifier})
if user is not None:
return user
def _is_numeric(self, value):
try:
int(value)
except ValueError:
return False
return True
@with_pony_session
def find_user(self, **kwargs):
return self.user_model.get(**kwargs)
@with_pony_session
def find_role(self, role):
return self.role_model.get(name=role)
@with_pony_session
def add_role_to_user(self, *args, **kwargs):
return super(PonyUserDatastore, self).add_role_to_user(*args, **kwargs)
@with_pony_session
def create_user(self, **kwargs):
return super(PonyUserDatastore, self).create_user(**kwargs)
@with_pony_session
def create_role(self, **kwargs):
return super(PonyUserDatastore, self).create_role(**kwargs)