Source code for ringo.model.user

import logging
import json
from pyramid.security import Allow
import sqlalchemy as sa
from datetime import datetime
from ringo.lib.alchemy import get_prop_from_instance
from ringo.model import Base
from ringo.model.base import BaseItem, BaseFactory
from ringo.model.mixins import Owned


log = logging.getLogger(__name__)


password_reset_requests = sa.Table(
    'password_reset_requests', Base.metadata,
    sa.Column('id', sa.Integer, primary_key=True),
    sa.Column('uid', sa.Integer, sa.ForeignKey('users.id')),
    sa.Column('created', sa.DateTime),
    sa.Column('token', sa.String)
)

# NM-Table definitions
nm_user_roles = sa.Table(
    'nm_user_roles', Base.metadata,
    sa.Column('uid', sa.Integer, sa.ForeignKey('users.id')),
    sa.Column('rid', sa.Integer, sa.ForeignKey('roles.id')),
    sa.UniqueConstraint('uid', 'rid')
)

nm_user_usergroups = sa.Table(
    'nm_user_usergroups', Base.metadata,
    sa.Column('uid', sa.Integer, sa.ForeignKey('users.id')),
    sa.Column('gid', sa.Integer, sa.ForeignKey('usergroups.id')),
    sa.UniqueConstraint('uid', 'gid')
)

nm_action_roles = sa.Table(
    'nm_action_roles', Base.metadata,
    sa.Column('aid', sa.Integer, sa.ForeignKey('actions.id')),
    sa.Column('rid', sa.Integer, sa.ForeignKey('roles.id')),
    sa.UniqueConstraint('aid', 'rid')
)


class Login(Base):
    """Class to for logins of a user. Attempts to login into the
    application are stored in a table in the database. Each attemp is
    stored with the datetime, the user and the state of the login.

    This information can later be used to give information on suspicious
    logins."""
    __tablename__ = "user_logins"

    id = sa.Column(sa.Integer, primary_key=True)
    datetime = sa.Column(sa.DateTime, default=datetime.utcnow)
    success = sa.Column(sa.Boolean)
    uid = sa.Column(sa.Integer, sa.ForeignKey("users.id"))
    user = sa.orm.relationship("User", backref="logins")

    def __init__(self, user, success):
        self.success = success
        user.logins.append(self)


class PasswordResetRequest(Base):
    __tablename__ = 'password_reset_requests'
    user = sa.orm.relationship("User", backref='reset_tokens')

    def __init__(self, token):
        self.token = token
        self.created = datetime.now()

    def __str__(self):
        return self.token


class UserSetting(Base):
    __tablename__ = 'user_settings'

    id = sa.Column(sa.Integer, primary_key=True)
    settings = sa.Column(sa.Text, nullable=False, default="{}")

    def get(self, key, default):
        log.debug("Getting usersetting")
        settings = json.loads(self.settings)
        return settings.get(key, default)

    def set(self, key, value):
        settings = json.loads(self.settings)
        section = settings.get(key)
        section = value
        settings[key] = section
        dump = json.dumps(settings)
        log.debug("Setting usersetting for %s: %s" % (key, dump))
        self.settings = dump


class UserFactory(BaseFactory):

    def create(self, user, values):

        # Delete the gid which sets the default group when creating a
        # new user. The default group when creating a new user is always
        # the usergroup which gets automatically created on user
        # creation. So this value can (and must) be ignored.
        # Not removing the "gid" will otherwise cause an
        # IntegrityError while flushing the new user to the DB as the
        # usergroup with the given id is not persistent in the DB.
        if "gid" in values:
            del values["gid"]
        # Delete the sid which sets the default settings for the same
        # reasons than deleting the "gid" attribute.
        if "sid" in values:
            del values["sid"]

        new_user = BaseFactory.create(self, user, values)

        # Now create a a new Profile
        profile_property = get_prop_from_instance(new_user, "profile",
                                                  include_relations=True)
        profile_class = profile_property.mapper.class_
        profile_factory = BaseFactory(profile_class)
        profile = profile_factory.create(user, {})
        new_user.profile.append(profile)

        # Create settings
        settings_factory = BaseFactory(UserSetting)
        settings = settings_factory.create(user, {})
        new_user.settings = settings

        # A usergroup
        usergroup_property = get_prop_from_instance(new_user, "usergroup",
                                                    include_relations=True)
        usergroup_class = usergroup_property.mapper.class_
        usergroup_factory = BaseFactory(usergroup_class)
        usergroup = usergroup_factory.create(None, {})
        usergroup.name = new_user.login
        usergroup.members.append(new_user)
        # If no default group is set, then set the users group as
        # default group
        new_user.usergroup = values.get("usergroup") or usergroup
        return new_user


class User(BaseItem, Owned, Base):
    __tablename__ = 'users'
    _modul_id = 3
    _sql_eager_loads = ['roles', 'groups', 'profile', 'settings']
    id = sa.Column(sa.Integer, primary_key=True)
    login = sa.Column(sa.String, unique=True, nullable=False)
    password = sa.Column(sa.String, nullable=False)
    activated = sa.Column(sa.Boolean, default=True)
    activation_token = sa.Column(sa.String, nullable=False, default='')
    default_gid = sa.Column(sa.Integer, sa.ForeignKey('usergroups.id'))
    sid = sa.Column(sa.Integer, sa.ForeignKey('user_settings.id'))
    last_login = sa.Column(sa.DateTime)

    # Relations
    profile = sa.orm.relation("Profile", cascade="all, delete-orphan")
    roles = sa.orm.relationship("Role",
                                secondary=nm_user_roles,
                                backref='users')
    groups = sa.orm.relationship("Usergroup",
                                 secondary=nm_user_usergroups,
                                 backref='members')
    usergroup = sa.orm.relationship("Usergroup", uselist=False,
                                    cascade="delete, all",
                                    foreign_keys=[default_gid])
    settings = sa.orm.relationship("UserSetting", uselist=False,
                                   cascade="all,delete")

    @classmethod
    def get_item_factory(cls):
        return UserFactory(cls)

    def has_role(self, role):
        """Return True if the user has the given role. Else False"
        :user: User instance
        :returns: True or False
        """
        roles = [r.name for r in self.roles]
        return role in roles

ADMIN_GROUP_ID = 1
"""Role ID your the system administration group"""
USER_GROUP_ID = 2
"""Role ID your the system user group"""
USER_ROLE_ID = 1
"""Role ID your the system user role"""


class Usergroup(BaseItem, Owned, Base):
    __tablename__ = 'usergroups'
    _modul_id = 4
    id = sa.Column(sa.Integer, primary_key=True)
    name = sa.Column(sa.String, unique=True, nullable=False)
    description = sa.Column(sa.String, nullable=False, default='')

    def __unicode__(self):
        return self.name

    @classmethod
    def _get_permissions(cls, modul, item, request):

        # Default ACL. Direct access
        permissions = BaseItem._get_permissions(modul, item, request)

        # A usergoups can be linked by all members of the group
        if isinstance(item, BaseItem):
            for user in item.members:
                permissions.append((Allow, 'uid:{}'.format(user.id), 'link'))
        return permissions


[docs]class Role(BaseItem, Owned, Base): """Roles are used to configure which actions on a specific modul are permitted to users. Roles are important during the permission checks to determine what the user can so which an item of a certain modul. However a role does not determine if the user has access to the item at all. This is left to the ownership checks. However a role can be configured to be a 'administration role'. This means every user who has this role will pass the ownership check during the permission checks even if the user is not the owner or in the group of the item he wants to. The specific actions a user is allowed to do on a item are stored in a internal list of ActionsItems. A user will be allowed to call all actions assigned to the role he is equipped with. """ __tablename__ = 'roles' _modul_id = 5 id = sa.Column(sa.Integer, primary_key=True) label = sa.Column(sa.String, unique=True, nullable=False) name = sa.Column(sa.String, unique=True, nullable=False) description = sa.Column(sa.Text, nullable=False, default='') admin = sa.Column(sa.Boolean, default=False) """Flag to set the role as administration role which means that the user will gain the assigned permissions irrespective from checking the ownership""" permissions = sa.orm.relation("ActionItem", secondary=nm_action_roles, backref='roles')
class Profile(BaseItem, Owned, Base): __tablename__ = 'profiles' _modul_id = 6 id = sa.Column(sa.Integer, primary_key=True) first_name = sa.Column(sa.String, nullable=True, default='') last_name = sa.Column(sa.String, nullable=True, default='') gender = sa.Column(sa.Integer) birthday = sa.Column(sa.Date) address = sa.Column(sa.Text, nullable=True, default='') phone = sa.Column(sa.String, nullable=True, default='') email = sa.Column(sa.String, nullable=True) web = sa.Column(sa.String, nullable=True, default='') # The foreign key to the user is injected from the Owned mixin. user = sa.orm.relation("User", cascade="all, delete", single_parent=True, uselist=False)