auth.py 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. # -*- coding: utf-8 -*-
  2. """
  3. Auth* related model.
  4. This is where the models used by the authentication stack are defined.
  5. It's perfectly fine to re-use this definition in the pod application,
  6. though.
  7. """
  8. import os
  9. from datetime import datetime
  10. from hashlib import sha256
  11. from sqlalchemy.sql.functions import session_user
  12. __all__ = ['User', 'Group', 'Permission']
  13. from sqlalchemy import Table, ForeignKey, Column
  14. from sqlalchemy.types import Unicode, Integer, DateTime, Boolean
  15. from sqlalchemy.orm import relation, relationship, synonym
  16. from pod.model import DeclarativeBase, metadata, DBSession
  17. # This is the association table for the many-to-many relationship between
  18. # groups and permissions.
  19. group_permission_table = Table('pod_group_permission', metadata,
  20. Column('group_id', Integer, ForeignKey('pod_group.group_id',
  21. onupdate="CASCADE", ondelete="CASCADE"), primary_key=True),
  22. Column('permission_id', Integer, ForeignKey('pod_permission.permission_id',
  23. onupdate="CASCADE", ondelete="CASCADE"), primary_key=True)
  24. )
  25. # This is the association table for the many-to-many relationship between
  26. # groups and members - this is, the memberships.
  27. user_group_table = Table('pod_user_group', metadata,
  28. Column('user_id', Integer, ForeignKey('pod_user.user_id',
  29. onupdate="CASCADE", ondelete="CASCADE"), primary_key=True),
  30. Column('group_id', Integer, ForeignKey('pod_group.group_id',
  31. onupdate="CASCADE", ondelete="CASCADE"), primary_key=True)
  32. )
  33. class Rights(DeclarativeBase):
  34. READ_ACCESS = 1
  35. WRITE_ACCESS = 2
  36. __tablename__ = 'pod_group_node'
  37. group_id = Column(Integer, ForeignKey('pod_group.group_id'), primary_key=True)
  38. node_id = Column(Integer, ForeignKey('pod_nodes.node_id'), primary_key=True)
  39. rights = Column(Integer)
  40. def hasReadAccess(self):
  41. return self.rights & Rights.READ_ACCESS
  42. def hasWriteAccess(self):
  43. return self.rights & Rights.WRITE_ACCESS
  44. class Group(DeclarativeBase):
  45. GROUP_ID_ALL_USERS = 2
  46. GROUP_ID_MANAGERS = 1
  47. __tablename__ = 'pod_group'
  48. group_id = Column(Integer, autoincrement=True, primary_key=True)
  49. group_name = Column(Unicode(16), unique=True, nullable=False)
  50. display_name = Column(Unicode(255))
  51. created = Column(DateTime, default=datetime.now)
  52. personnal_group = Column(Boolean)
  53. users = relationship('User', secondary=user_group_table, backref='groups')
  54. _lRights = relationship('Rights', backref='_oGroup', cascade = "all, delete-orphan")
  55. def __repr__(self):
  56. return '<Group: name=%s>' % repr(self.group_name)
  57. def __unicode__(self):
  58. return self.group_name
  59. @classmethod
  60. def by_group_name(cls, group_name):
  61. """Return the user object whose email address is ``email``."""
  62. return DBSession.query(cls).filter_by(group_name=group_name).first()
  63. def getDisplayName(self) -> str:
  64. if self.group_id<0:
  65. # FIXME - D.A. - 2014-05-19 - MAKE THIS CODE CLEANER,
  66. try:
  67. return self.users[0].getDisplayName()
  68. except:
  69. print('ERROR GROUP =>', self.group_id)
  70. return self.display_name
  71. @property
  72. def rights(self):
  73. return self._lRights
  74. def hasSomeAccess(self, poNode):
  75. for loRight in self._lRights:
  76. if loRight.node_id == poNode.node_id and loRight.rights>0:
  77. return True
  78. return False
  79. class User(DeclarativeBase):
  80. """
  81. User definition.
  82. This is the user definition used by :mod:`repoze.who`, which requires at
  83. least the ``email_address`` column.
  84. """
  85. __tablename__ = 'pod_user'
  86. user_id = Column(Integer, autoincrement=True, primary_key=True)
  87. email_address = Column(Unicode(255), unique=True, nullable=False)
  88. display_name = Column(Unicode(255))
  89. _password = Column('password', Unicode(128))
  90. created = Column(DateTime, default=datetime.now)
  91. def __repr__(self):
  92. return '<User: email=%s, display=%s>' % (
  93. repr(self.email_address), repr(self.display_name))
  94. def __unicode__(self):
  95. return self.display_name or self.email_address
  96. @property
  97. def permissions(self):
  98. """Return a set with all permissions granted to the user."""
  99. perms = set()
  100. for g in self.groups:
  101. perms = perms | set(g.permissions)
  102. return perms
  103. @classmethod
  104. def by_email_address(cls, email):
  105. """Return the user object whose email address is ``email``."""
  106. return DBSession.query(cls).filter_by(email_address=email).first()
  107. @classmethod
  108. def by_user_name(cls, username):
  109. """Return the user object whose user name is ``username``."""
  110. return DBSession.query(cls).filter_by(email_address=username).first()
  111. @classmethod
  112. def _hash_password(cls, password):
  113. salt = sha256()
  114. salt.update(os.urandom(60))
  115. salt = salt.hexdigest()
  116. hash = sha256()
  117. # Make sure password is a str because we cannot hash unicode objects
  118. hash.update((password + salt).encode('utf-8'))
  119. hash = hash.hexdigest()
  120. password = salt + hash
  121. # Make sure the hashed password is a unicode object at the end of the
  122. # process because SQLAlchemy _wants_ unicode objects for Unicode cols
  123. # FIXME - D.A. - 2013-11-20 - The following line has been removed since using python3. Is this normal ?!
  124. # password = password.decode('utf-8')
  125. return password
  126. def _set_password(self, password):
  127. """Hash ``password`` on the fly and store its hashed version."""
  128. self._password = self._hash_password(password)
  129. def _get_password(self):
  130. """Return the hashed version of the password."""
  131. return self._password
  132. password = synonym('_password', descriptor=property(_get_password,
  133. _set_password))
  134. def validate_password(self, password):
  135. """
  136. Check the password against existing credentials.
  137. :param password: the password that was provided by the user to
  138. try and authenticate. This is the clear text version that we will
  139. need to match against the hashed one in the database.
  140. :type password: unicode object.
  141. :return: Whether the password is valid.
  142. :rtype: bool
  143. """
  144. hash = sha256()
  145. hash.update((password + self.password[:64]).encode('utf-8'))
  146. return self.password[64:] == hash.hexdigest()
  147. def getDisplayName(self):
  148. if self.display_name!=None and self.display_name!='':
  149. return self.display_name
  150. else:
  151. return self.email_address
  152. class Permission(DeclarativeBase):
  153. """
  154. Permission definition.
  155. Only the ``permission_name`` column is required.
  156. """
  157. __tablename__ = 'pod_permission'
  158. permission_id = Column(Integer, autoincrement=True, primary_key=True)
  159. permission_name = Column(Unicode(63), unique=True, nullable=False)
  160. description = Column(Unicode(255))
  161. groups = relation(Group, secondary=group_permission_table,
  162. backref='permissions')
  163. def __repr__(self):
  164. return '<Permission: name=%s>' % repr(self.permission_name)
  165. def __unicode__(self):
  166. return self.permission_name