pyramid-fullauth 0.4.0__py3.4.egg

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. EGG-INFO/PKG-INFO +184 -0
  2. EGG-INFO/SOURCES.txt +93 -0
  3. EGG-INFO/dependency_links.txt +1 -0
  4. EGG-INFO/not-zip-safe +1 -0
  5. EGG-INFO/requires.txt +13 -0
  6. EGG-INFO/top_level.txt +2 -0
  7. pyramid_fullauth/__init__.py +106 -0
  8. pyramid_fullauth/__pycache__/__init__.cpython-34.pyc +0 -0
  9. pyramid_fullauth/__pycache__/auth.cpython-34.pyc +0 -0
  10. pyramid_fullauth/__pycache__/events.cpython-34.pyc +0 -0
  11. pyramid_fullauth/__pycache__/exceptions.cpython-34.pyc +0 -0
  12. pyramid_fullauth/__pycache__/request.cpython-34.pyc +0 -0
  13. pyramid_fullauth/__pycache__/tools.cpython-34.pyc +0 -0
  14. pyramid_fullauth/auth.py +67 -0
  15. pyramid_fullauth/config/default.yaml +26 -0
  16. pyramid_fullauth/events.py +281 -0
  17. pyramid_fullauth/exceptions.py +47 -0
  18. pyramid_fullauth/models/__init__.py +190 -0
  19. pyramid_fullauth/models/__pycache__/__init__.cpython-34.pyc +0 -0
  20. pyramid_fullauth/models/extensions/__init__.py +39 -0
  21. pyramid_fullauth/models/extensions/__pycache__/__init__.cpython-34.pyc +0 -0
  22. pyramid_fullauth/models/mixins/__init__.py +9 -0
  23. pyramid_fullauth/models/mixins/__pycache__/__init__.cpython-34.pyc +0 -0
  24. pyramid_fullauth/models/mixins/__pycache__/email.cpython-34.pyc +0 -0
  25. pyramid_fullauth/models/mixins/__pycache__/password.cpython-34.pyc +0 -0
  26. pyramid_fullauth/models/mixins/email.py +128 -0
  27. pyramid_fullauth/models/mixins/password.py +141 -0
  28. pyramid_fullauth/request.py +70 -0
  29. pyramid_fullauth/resources/helpers/social.mako +17 -0
  30. pyramid_fullauth/resources/locale/pyramid_fullauth.pot +216 -0
  31. pyramid_fullauth/resources/templates/403.mako +11 -0
  32. pyramid_fullauth/resources/templates/_form.login.mako +44 -0
  33. pyramid_fullauth/resources/templates/_form.register.mako +47 -0
  34. pyramid_fullauth/resources/templates/_social.login.mako +17 -0
  35. pyramid_fullauth/resources/templates/activate.mako +14 -0
  36. pyramid_fullauth/resources/templates/email_change.mako +33 -0
  37. pyramid_fullauth/resources/templates/layout.mako +13 -0
  38. pyramid_fullauth/resources/templates/login.mako +17 -0
  39. pyramid_fullauth/resources/templates/register.mako +22 -0
  40. pyramid_fullauth/resources/templates/reset.mako +34 -0
  41. pyramid_fullauth/resources/templates/reset.proceed.mako +41 -0
  42. pyramid_fullauth/routing/__init__.py +5 -0
  43. pyramid_fullauth/routing/__pycache__/__init__.cpython-34.pyc +0 -0
  44. pyramid_fullauth/routing/__pycache__/predicates.cpython-34.pyc +0 -0
  45. pyramid_fullauth/routing/predicates.py +86 -0
  46. pyramid_fullauth/tools.py +69 -0
  47. pyramid_fullauth/views/__init__.py +15 -0
  48. pyramid_fullauth/views/__pycache__/__init__.cpython-34.pyc +0 -0
  49. pyramid_fullauth/views/__pycache__/social.cpython-34.pyc +0 -0
  50. pyramid_fullauth/views/basic/__init__.py +5 -0
  51. pyramid_fullauth/views/basic/__pycache__/__init__.cpython-34.pyc +0 -0
  52. pyramid_fullauth/views/basic/__pycache__/activate.cpython-34.pyc +0 -0
  53. pyramid_fullauth/views/basic/__pycache__/forbidden.cpython-34.pyc +0 -0
  54. pyramid_fullauth/views/basic/__pycache__/login.cpython-34.pyc +0 -0
  55. pyramid_fullauth/views/basic/__pycache__/logout.cpython-34.pyc +0 -0
  56. pyramid_fullauth/views/basic/__pycache__/register.cpython-34.pyc +0 -0
  57. pyramid_fullauth/views/basic/activate.py +44 -0
  58. pyramid_fullauth/views/basic/forbidden.py +46 -0
  59. pyramid_fullauth/views/basic/login.py +128 -0
  60. pyramid_fullauth/views/basic/logout.py +28 -0
  61. pyramid_fullauth/views/basic/profile/__init__.py +5 -0
  62. pyramid_fullauth/views/basic/profile/__pycache__/__init__.cpython-34.pyc +0 -0
  63. pyramid_fullauth/views/basic/profile/__pycache__/email.cpython-34.pyc +0 -0
  64. pyramid_fullauth/views/basic/profile/__pycache__/password.cpython-34.pyc +0 -0
  65. pyramid_fullauth/views/basic/profile/email.py +108 -0
  66. pyramid_fullauth/views/basic/profile/password.py +129 -0
  67. pyramid_fullauth/views/basic/register.py +111 -0
  68. pyramid_fullauth/views/social.py +221 -0
  69. tests/__init__.py +1 -0
  70. tests/__pycache__/__init__.cpython-34.pyc +0 -0
  71. tests/__pycache__/conftest.cpython-34.pyc +0 -0
  72. tests/__pycache__/tools.cpython-34.pyc +0 -0
  73. tests/conftest.py +201 -0
  74. tests/tools.py +54 -0
  75. tests/unit/__pycache__/test_default_settings.cpython-34.pyc +0 -0
  76. tests/unit/__pycache__/test_groupfinder.cpython-34.pyc +0 -0
  77. tests/unit/__pycache__/test_social_view.cpython-34.pyc +0 -0
  78. tests/unit/__pycache__/test_tools.cpython-34.pyc +0 -0
  79. tests/unit/__pycache__/test_user_active.cpython-34.pyc +0 -0
  80. tests/unit/__pycache__/test_user_admin.cpython-34.pyc +0 -0
  81. tests/unit/__pycache__/test_user_email.cpython-34.pyc +0 -0
  82. tests/unit/__pycache__/test_user_password.cpython-34.pyc +0 -0
  83. tests/unit/__pycache__/test_user_providers.cpython-34.pyc +0 -0
  84. tests/unit/__pycache__/test_user_repr.cpython-34.pyc +0 -0
  85. tests/unit/__pycache__/test_validators.cpython-34.pyc +0 -0
  86. tests/unit/test_default_settings.py +15 -0
  87. tests/unit/test_groupfinder.py +20 -0
  88. tests/unit/test_social_view.py +69 -0
  89. tests/unit/test_tools.py +24 -0
  90. tests/unit/test_user_active.py +31 -0
  91. tests/unit/test_user_admin.py +61 -0
  92. tests/unit/test_user_email.py +37 -0
  93. tests/unit/test_user_password.py +52 -0
  94. tests/unit/test_user_providers.py +24 -0
  95. tests/unit/test_user_repr.py +29 -0
  96. tests/unit/test_validators.py +44 -0
  97. tests/views/__init__.py +1 -0
  98. tests/views/__pycache__/__init__.cpython-34.pyc +0 -0
  99. tests/views/__pycache__/conftest.cpython-34.pyc +0 -0
  100. tests/views/__pycache__/test_activation.cpython-34.pyc +0 -0
  101. tests/views/__pycache__/test_email.cpython-34.pyc +0 -0
  102. tests/views/__pycache__/test_events.cpython-34.pyc +0 -0
  103. tests/views/__pycache__/test_login.cpython-34.pyc +0 -0
  104. tests/views/__pycache__/test_login_social.cpython-34.pyc +0 -0
  105. tests/views/__pycache__/test_logout.cpython-34.pyc +0 -0
  106. tests/views/__pycache__/test_register.cpython-34.pyc +0 -0
  107. tests/views/__pycache__/test_reset.cpython-34.pyc +0 -0
  108. tests/views/conftest.py +20 -0
  109. tests/views/test_activation.py +86 -0
  110. tests/views/test_email.py +175 -0
  111. tests/views/test_events.py +558 -0
  112. tests/views/test_login.py +172 -0
  113. tests/views/test_login_social.py +289 -0
  114. tests/views/test_logout.py +42 -0
  115. tests/views/test_register.py +138 -0
  116. tests/views/test_reset.py +105 -0
@@ -0,0 +1,281 @@
1
+ # Copyright (c) 2013 - 2014 by pyramid_fullauth authors and contributors <see AUTHORS file>
2
+ #
3
+ # This module is part of pyramid_fullauth and is released under
4
+ # the MIT License (MIT): http://opensource.org/licenses/MIT
5
+
6
+ """pyramid_fullauth emits these events during whole cycle."""
7
+
8
+
9
+ class _BaseRegisterEvent(object):
10
+
11
+ """
12
+ Base fullauth event.
13
+
14
+ most of the fullauth's event will provide both request and user
15
+ object with some additional data (these will be described then).
16
+ """
17
+
18
+ def __init__(self, request, user):
19
+ """
20
+ Initialize event.
21
+
22
+ :param pyramid.request.Request request: request object
23
+ :param pyramid_fullauth.models.User user: user object
24
+ """
25
+ self.request = request
26
+ self.user = user
27
+
28
+
29
+ class BeforeRegister(_BaseRegisterEvent):
30
+
31
+ """
32
+ Execute custom code at the start of registration process.
33
+
34
+ .. note::
35
+
36
+ User object is not yet in session.
37
+ """
38
+
39
+ def __init__(self, request, user, errors):
40
+ """
41
+ Initialize event.
42
+
43
+ :param pyramid.request.Request request: request object
44
+ :param pyramid_fullauth.models.User user: user object
45
+ :param dict errors: a dictionary with wrong/not submitted fields
46
+ with format - fields for which error occured: error message
47
+ """
48
+ _BaseRegisterEvent.__init__(self, request, user)
49
+ self.errors = errors
50
+
51
+
52
+ class AfterRegister(_BaseRegisterEvent):
53
+
54
+ """
55
+ Add custom post-processing code in registration process.
56
+
57
+ Can be used to add e.g. e-mail sending with registration links.
58
+
59
+ .. note::
60
+ User object is already in a session.
61
+
62
+ .. note::
63
+ Action emitting this event, should catch all HTTPRedirection that might
64
+ be risen in event listener.
65
+
66
+ .. warning::
67
+ If HTTPRedirection is risen from event listener, then response_values
68
+ will not be used!
69
+
70
+ """
71
+
72
+ def __init__(self, request, user, response_values):
73
+ """
74
+ Initialize event.
75
+
76
+ :param pyramid.request.Request request: request object
77
+ :param pyramid_fullauth.models.User user: user object
78
+ :param dict response_values: a dictionary with response values
79
+ """
80
+ _BaseRegisterEvent.__init__(self, request, user)
81
+ self.response_values = response_values
82
+
83
+
84
+ class AfterActivate(_BaseRegisterEvent):
85
+
86
+ """
87
+ Add custom post-processing logic after user gets activated.
88
+
89
+ .. note::
90
+ Action emitting this event, should catch all HTTPRedirection that might
91
+ be risen in event listener.
92
+ """
93
+
94
+ pass
95
+
96
+
97
+ class AfterResetRequest(_BaseRegisterEvent):
98
+
99
+ """
100
+ Add custom post-processing after user sends request to reset password.
101
+
102
+ .. note::
103
+ Action emitting this event, should catch all HTTPRedirection that might
104
+ be risen in event listener.
105
+ """
106
+
107
+ pass
108
+
109
+
110
+ class BeforeReset(_BaseRegisterEvent):
111
+
112
+ """Add custom pre-processing before the actual reset-password process."""
113
+
114
+ pass
115
+
116
+
117
+ class AfterReset(_BaseRegisterEvent):
118
+
119
+ """
120
+ Add custom post-processing after the actual reset-password process.
121
+
122
+ .. note::
123
+ Action emitting this event, should catch all HTTPRedirection that might
124
+ be risen in event listener.
125
+ """
126
+
127
+ pass
128
+
129
+
130
+ class AlreadyLoggedIn(object):
131
+
132
+ """
133
+ Allow execute custom logic, when logged in user tries to log in again.
134
+
135
+ .. note::
136
+ Action emitting this event, should catch all HTTPRedirection that might
137
+ be risen in event listener.
138
+
139
+ """
140
+
141
+ def __init__(self, request):
142
+ """
143
+ Initialize event.
144
+
145
+ :param pyramid.request.Request request: request object
146
+ """
147
+ self.request = request
148
+
149
+
150
+ class BeforeLogIn(_BaseRegisterEvent):
151
+
152
+ """
153
+ Add custom logic before user gets logged in.
154
+
155
+ .. note::
156
+ Action emitting this event, should catch all AttributeError that might
157
+ be risen in event listener.
158
+ User param set to None when user is not found or request method is GET.
159
+ """
160
+
161
+ pass
162
+
163
+
164
+ class AfterLogIn(_BaseRegisterEvent):
165
+
166
+ """Add custom logic after user logs in."""
167
+
168
+ pass
169
+
170
+
171
+ # Social events
172
+ # TODO: extract to sub module
173
+
174
+
175
+ class _BaseSocialRegister(_BaseRegisterEvent):
176
+
177
+ """Base for all social requests."""
178
+
179
+ def __init__(self, request, user, profile):
180
+ """
181
+ Initialize base events.
182
+
183
+ :param pyramid.request.Request request: request object
184
+ :param pyramid_fullauth.models.User user: user object
185
+ :param dict profile: a dictionary with profile data
186
+ """
187
+ _BaseRegisterEvent.__init__(self, request, user)
188
+ self.profile = profile
189
+
190
+
191
+ class BeforeSocialRegister(_BaseSocialRegister):
192
+
193
+ """Adds custom logic before the social login process start."""
194
+
195
+ pass
196
+
197
+
198
+ class AfterSocialRegister(_BaseSocialRegister):
199
+
200
+ """
201
+ Add custom logic after user registers through social network.
202
+
203
+ .. note::
204
+ Action emitting this event, should catch all HTTPRedirection that might
205
+ be risen in event listener.
206
+ """
207
+
208
+ def __init__(self, request, user, profile, response_values):
209
+ """
210
+ Initialize event.
211
+
212
+ :param pyramid.request.Request request: request object
213
+ :param pyramid_fullauth.models.User user: user object
214
+ :param dict profile: a dictionary with profile data
215
+ :param dict response_values: a dictionary with response values
216
+ """
217
+ _BaseSocialRegister.__init__(self, request, user, profile)
218
+ self.response_values = response_values
219
+
220
+
221
+ class AfterSocialLogIn(_BaseSocialRegister):
222
+
223
+ """
224
+ Custom logic after user logs in through social network.
225
+
226
+ .. note::
227
+ Action emitting this event, should catch all HTTPRedirection that might
228
+ be risen in event listener.
229
+ """
230
+
231
+ pass
232
+
233
+
234
+ class SocialAccountAlreadyConnected(_BaseSocialRegister):
235
+
236
+ """
237
+ Event raised when social account is already connected to some other user.
238
+
239
+
240
+ Allow to add custom logic, when someone tries to connect social account to
241
+ second user in application.
242
+
243
+ .. note::
244
+ Action emitting this event, should catch all HTTPRedirection that might
245
+ be risen in event listener.
246
+ """
247
+
248
+ def __init__(self, request, user, profile, response_values):
249
+ """
250
+ Initialize event.
251
+
252
+ :param pyramid.request.Request request: request object
253
+ :param pyramid_fullauth.models.User user: user object
254
+ :param dict profile: a dictionary with profile data
255
+ :param dict response_values: a dictionary with response values
256
+ """
257
+ _BaseSocialRegister.__init__(self, request, user, profile)
258
+ self.response_values = response_values
259
+
260
+ # Email change events.
261
+
262
+
263
+ class BeforeEmailChange(_BaseRegisterEvent):
264
+
265
+ """Allow to add custom validation (like checking password) before email change process."""
266
+
267
+ pass
268
+
269
+
270
+ class AfterEmailChange(_BaseRegisterEvent):
271
+
272
+ """Allow to add some custom post-processing, like e-mail sending, after email change process."""
273
+
274
+ pass
275
+
276
+
277
+ class AfterEmailChangeActivation(_BaseRegisterEvent):
278
+
279
+ """Allow to add custom logic, after changed email had been activated."""
280
+
281
+ pass
@@ -0,0 +1,47 @@
1
+ # Copyright (c) 2013 - 2014 by pyramid_fullauth authors and contributors <see AUTHORS file>
2
+ #
3
+ # This module is part of pyramid_fullauth and is released under
4
+ # the MIT License (MIT): http://opensource.org/licenses/MIT
5
+ """Fullauth's exceptions definition."""
6
+
7
+
8
+ class DeleteException(Exception):
9
+
10
+ """Exception risen when the user can't be deleted."""
11
+
12
+ pass
13
+
14
+
15
+ class ValidateError(ValueError):
16
+
17
+ """Base of every validate error in pyramid_fullauth."""
18
+
19
+ pass
20
+
21
+
22
+ class EmptyError(ValidateError):
23
+
24
+ """Thrown whenever user is trying to set empty value."""
25
+
26
+ pass
27
+
28
+
29
+ class ShortPasswordError(ValidateError):
30
+
31
+ """Thrown when password doesn't meet the length requirement."""
32
+
33
+ pass
34
+
35
+
36
+ class PasswordConfirmMismatchError(ValidateError):
37
+
38
+ """Thrown when there's a mismatch with cpassword_confirm."""
39
+
40
+ pass
41
+
42
+
43
+ class EmailValidationError(ValidateError):
44
+
45
+ """Exception thrown, when there's incorrect email provided."""
46
+
47
+ pass
@@ -0,0 +1,190 @@
1
+ # Copyright (c) 2013 - 2014 by pyramid_fullauth authors and contributors <see AUTHORS file>
2
+ #
3
+ # This module is part of pyramid_fullauth and is released under
4
+ # the MIT License (MIT): http://opensource.org/licenses/MIT
5
+ """Models needed for registration, and user servicing."""
6
+
7
+ import sys
8
+ import uuid
9
+ from datetime import datetime
10
+
11
+ from sqlalchemy import (
12
+ Column, Unicode, String, Integer, Boolean, Sequence, DateTime,
13
+ Table, ForeignKey, UniqueConstraint
14
+ )
15
+ from sqlalchemy.sql import func
16
+ from sqlalchemy.orm import validates, relationship
17
+ from sqlalchemy.orm.session import object_session
18
+ from sqlalchemy.orm.util import has_identity
19
+ from pyramid.compat import text_type
20
+ from pyramid_basemodel import Base
21
+
22
+ from pyramid_fullauth import exceptions
23
+ from pyramid_fullauth.models.mixins import UserPasswordMixin, UserEmailMixin
24
+
25
+
26
+ class User(UserPasswordMixin, UserEmailMixin, Base):
27
+
28
+ """User object."""
29
+
30
+ __tablename__ = 'users'
31
+
32
+ id = Column(Integer, Sequence(__tablename__ + '_sq'), primary_key=True)
33
+ username = Column(Unicode(32), unique=True, nullable=True)
34
+ firstname = Column(Unicode(100), nullable=True)
35
+ lastname = Column(Unicode(100), nullable=True)
36
+ activate_key = Column(String(255), default=lambda: str(uuid.uuid4()), unique=True)
37
+ address_ip = Column(String(15), nullable=False)
38
+
39
+ registered_at = Column(DateTime, default=func.now(), nullable=False)
40
+ logged_at = Column(DateTime, default=func.now(), nullable=False)
41
+ activated_at = Column(DateTime, nullable=True)
42
+ deactivated_at = Column(DateTime, nullable=True)
43
+ deleted_at = Column(DateTime, nullable=True)
44
+
45
+ is_admin = Column(Boolean, default=False, nullable=False)
46
+
47
+ @property
48
+ def is_active(self):
49
+ """
50
+ Check if user is active.
51
+
52
+ :returns: Returns False if user account is not active (or deleted).
53
+ :rtype: bool
54
+
55
+ """
56
+ return not (self.deactivated_at or self.deleted_at or self.activate_key) and bool(self.activated_at)
57
+
58
+ @is_active.setter
59
+ def is_active(self, value):
60
+ """
61
+ Set user as active/inactive.
62
+
63
+ :param bood value:
64
+ True - removes deactivated_at, deleted_at, activate_key and set activated_at to datetime now
65
+ False - set deactivated_at to now and activated_at to None
66
+
67
+ """
68
+ # Allow to modify this only if object is in the persistent state to prevent "default values" errors/bugs.
69
+ # http://stackoverflow.com/questions/3885601/sqlalchemy-get-object-instance-state
70
+ if object_session(self) is not None and has_identity(self):
71
+ if value:
72
+ self.deactivated_at = None
73
+ self.deleted_at = None
74
+ self.activate_key = None
75
+ self.activated_at = datetime.now()
76
+ else:
77
+ self.deactivated_at = datetime.now()
78
+ self.activated_at = None
79
+ else:
80
+ raise AttributeError('User has to be in the persistent state - stored in the DB')
81
+
82
+ def provider_id(self, provider):
83
+ """
84
+ Return provider identification for give user.
85
+
86
+ :param str provider: provider name
87
+ :returns: provider identification
88
+ :rtype: str
89
+
90
+ """
91
+ for user_provider in self.providers:
92
+ if user_provider.provider == provider:
93
+ return user_provider.provider_id
94
+
95
+ def __repr__(self):
96
+ """Object representation."""
97
+ return "<User ('{0}')>".format(self.id)
98
+
99
+ def __unicode__(self):
100
+ """Unicode cast rules."""
101
+ if self.username:
102
+ return self.username
103
+ elif self.email:
104
+ return self.email.split('@')[0] + '@...'
105
+ else:
106
+ return text_type(self.id)
107
+
108
+ def __str__(self): # pragma: no cover
109
+ """String cast rules."""
110
+ if sys.version[0] == '3':
111
+ return self.__unicode__()
112
+ else:
113
+ return self.__unicode__().encode('utf-8')
114
+
115
+ @validates('is_admin')
116
+ def validate_is_admin(self, key, value):
117
+ """
118
+ Validate is_admin value, we forbid the deletion of the last superadmin.
119
+
120
+ .. note::
121
+
122
+ More about simple validators: http://docs.sqlalchemy.org/en/latest/orm/mapper_config.html#simple-validators
123
+
124
+ :raises AttributeError: Information about an error
125
+
126
+ """
127
+ if self.is_admin and not value:
128
+ admin_counter = object_session(self).query(User).filter(
129
+ User.is_admin, User.deleted_at.is_(None)
130
+ ).count()
131
+ if admin_counter and admin_counter <= 1:
132
+ raise AttributeError('Can\'t delete last superadmin!')
133
+ return value
134
+
135
+ def delete(self):
136
+ """
137
+ Perform soft delete action. along with checking if it's super admin, or not.
138
+
139
+ :rises pyramid_fullauth.exceptions.DeleteException: if you try to delete last super admin.
140
+
141
+ .. note::
142
+ You should use this method to delete users
143
+
144
+ """
145
+ if self.is_admin:
146
+ admin_counter = object_session(self).query(User).filter(
147
+ User.is_admin, User.deleted_at.is_(None)
148
+ ).count()
149
+ if admin_counter and admin_counter <= 1:
150
+ raise exceptions.DeleteException('Can\'t delete last superadmin!')
151
+
152
+ self.deleted_at = datetime.now()
153
+
154
+
155
+ class Group(Base):
156
+
157
+ """User group object."""
158
+
159
+ __tablename__ = 'groups'
160
+
161
+ id = Column(Integer, Sequence(__tablename__ + '_sq'), primary_key=True)
162
+ name = Column(Unicode(100), unique=True, nullable=False)
163
+
164
+ #: Relation to User object
165
+ users = relationship(User, secondary=lambda: user_group, backref='groups')
166
+
167
+
168
+ class AuthenticationProvider(Base):
169
+
170
+ """Model to store authentication methods for different providers."""
171
+
172
+ __tablename__ = 'user_authentication_provider'
173
+
174
+ __table_args__ = (
175
+ UniqueConstraint('provider', 'provider_id', name='user_authentication_methods_provider_id'),
176
+ )
177
+
178
+ user_id = Column(Integer, ForeignKey(User.id), primary_key=True)
179
+ provider = Column(Unicode(15), primary_key=True)
180
+ provider_id = Column(String(255), nullable=False)
181
+
182
+ user = relationship(User, backref='providers')
183
+
184
+ #: Association table between User and Group models.
185
+ user_group = Table(
186
+ 'users_groups',
187
+ Base.metadata,
188
+ Column('user_id', Integer, ForeignKey(User.id), primary_key=True),
189
+ Column('group_id', Integer, ForeignKey(Group.id), primary_key=True)
190
+ )
@@ -0,0 +1,39 @@
1
+ # Copyright (c) by SQLAlchemy authors and contributors.
2
+ #
3
+ # This module's code is part of SQLAlchemy's documentation
4
+ # http://docs.sqlalchemy.org/en/rel_0_9/orm/extensions/hybrid.html#hybrid-value-objects
5
+ # and released under
6
+ # the MIT License (MIT): http://opensource.org/licenses/MIT
7
+ """CaseInsensitive comparator for sqlalchemy models."""
8
+
9
+ from sqlalchemy import func
10
+ from sqlalchemy.ext.hybrid import Comparator
11
+ from pyramid.compat import string_types
12
+
13
+
14
+ class CaseInsensitive(Comparator):
15
+
16
+ """Hybrid value representing a lower case representation."""
17
+
18
+ def __init__(self, word):
19
+ """Initialise comparator object."""
20
+ if isinstance(word, string_types):
21
+ self.word = word.lower()
22
+ elif isinstance(word, CaseInsensitive):
23
+ self.word = word.word
24
+ else:
25
+ self.word = func.lower(word)
26
+
27
+ def operate(self, op, other):
28
+ """Operate."""
29
+ if not isinstance(other, CaseInsensitive):
30
+ other = CaseInsensitive(other)
31
+ return op(self.word, other.word)
32
+
33
+ def __clause_element__(self):
34
+ """Get clause element for comparator.."""
35
+ return self.word
36
+
37
+ def __str__(self):
38
+ """Cast element to."""
39
+ return self.word
@@ -0,0 +1,9 @@
1
+ # Copyright (c) 2013 - 2014 by pyramid_fullauth authors and contributors <see AUTHORS file>
2
+ #
3
+ # This module is part of pyramid_fullauth and is released under
4
+ # the MIT License (MIT): http://opensource.org/licenses/MIT
5
+
6
+ from pyramid_fullauth.models.mixins.password import UserPasswordMixin
7
+ from pyramid_fullauth.models.mixins.email import UserEmailMixin
8
+
9
+ __all__ = (UserPasswordMixin, UserEmailMixin)